1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00

Initial implementation of the area system.

This commit is contained in:
Sandu Liviu Catalin 2017-06-19 04:09:35 +03:00
parent 6504f196bb
commit 2fb58f9fbf
16 changed files with 1507 additions and 4 deletions

View File

@ -450,6 +450,8 @@
<Unit filename="../shared/Base/Buffer.hpp" />
<Unit filename="../shared/Base/Plugin.cpp" />
<Unit filename="../shared/Base/Utility.hpp" />
<Unit filename="../source/Areas.cpp" />
<Unit filename="../source/Areas.hpp" />
<Unit filename="../source/Base/AABB.cpp" />
<Unit filename="../source/Base/AABB.hpp" />
<Unit filename="../source/Base/Algo.cpp" />

449
source/Areas.cpp Normal file
View File

@ -0,0 +1,449 @@
// ------------------------------------------------------------------------------------------------
#include "Areas.hpp"
// ------------------------------------------------------------------------------------------------
#include <algorithm>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
SQMODE_DECL_TYPENAME(AreaTypename, _SC("SqArea"))
// ------------------------------------------------------------------------------------------------
AreaManager AreaManager::s_Inst;
// ------------------------------------------------------------------------------------------------
void Area::AddArray(const Sqrat::Array &)
{
//todo...
}
// ------------------------------------------------------------------------------------------------
bool Area::Manage()
{
// Are we connected to any cells?
if (!mCells.empty())
{
STHROWF("The area is already managed");
}
// Is there something to be managed?
else if (mPoints.size() < 3)
{
STHROWF("Areas need at least 3 points to be managed");
}
// We expect this to be called only from the script so that the first parameter in the vm
// is the area instance
LightObj obj(1, DefaultVM::Get());
// Attempt to manage this area
AreaManager::Get().InsertArea(*this, obj);
// Return whether the area is now managed by any cells
return !mCells.empty();
}
// ------------------------------------------------------------------------------------------------
bool Area::Unmanage()
{
// Are we connected to any cells?
if (mCells.empty())
{
return true; // Already unmanaged
}
// Attempt to unmanage this area
AreaManager::Get().RemoveArea(*this);
// Return whether the area is not managed by any cells
return mCells.empty();
}
// ------------------------------------------------------------------------------------------------
bool Area::IsInside(float x, float y) const
{
// Is the an area to test?
if (mPoints.size() < 3)
{
return false; // Can't possibly be in an area that doesn't exist
}
// http://sidvind.com/wiki/Point-in-polygon:_Jordan_Curve_Theorem
// The points creating the polygon
float x1, x2;
// How many times the ray crosses a line segment
int crossings = 0;
// Iterate through each line
for (Uint32 i = 0, n = static_cast< Uint32 >(mPoints.size()); i < n; ++i)
{
Points::const_reference a = mPoints[i];
Points::const_reference b = mPoints[(i + 1) % n];
// This is done to ensure that we get the same result when
// the line goes from left to right and right to left.
if (a.x < b.x)
{
x1 = a.x;
x2 = b.x;
}
else
{
x1 = b.x;
x2 = a.x;
}
// First check if the ray is able to cross the line
if (x > x1 && x <= x2 && (y < a.y || y <= b.y))
{
// Calculate the equation of the line
const float dx = (b.x - a.x);
const float dy = (b.y - a.y);
float k;
if (fabs(dx) < 0.000001f)
{
k = 0xffffffff;
}
else
{
k = (dy / dx);
}
const float m = (a.y - k * a.x);
const float y2 = (k * x + m);
// Does the ray cross the line?
if (y <= y2)
{
++crossings;
}
}
}
// Return if the crossings are not even
return (crossings % 2 == 1);
}
// ------------------------------------------------------------------------------------------------
AreaManager::AreaManager(size_t sz)
: m_Queue(), m_ProcList(), m_Grid{}
{
// Negative half grid size (left)
int l = (-GRIDH * CELLD);
// Positive half grid size minus one cell (bottom)
int b = (abs(l) - CELLD);
// Negative half grid size minus one cell (right)
int r = (l + CELLD);
// Positive half grid size (top)
int t = abs(l);
// Initialize the grid cells
for (int y = 0; y < GRIDN; ++y)
{
for (int x = 0; x < GRIDN; ++x)
{
// Grab a reference to the cell
AreaCell & c = m_Grid[y][x];
// Configure the range of the cell
c.mL = static_cast< float >(l);
c.mB = static_cast< float >(b);
c.mR = static_cast< float >(r);
c.mT = static_cast< float >(t);
// Reserve area memory if requested
c.mAreas.reserve(sz);
// Reset the locks on this area
c.mLocks = 0;
// Advance the left side
l = r;
// Advance the right side
r += CELLD;
// Should we advance to the next row?
if (r > (GRIDH * CELLD))
{
// Reset the left side
l = (-GRIDH * CELLD);
// Reset the right side
r = (l + CELLD);
// Advance the bottom
b -= CELLD;
// Advance the top
t -= CELLD;
}
}
}
// Reserve some space in the queue
m_Queue.reserve(128);
m_ProcList.reserve(128);
}
// ------------------------------------------------------------------------------------------------
void AreaManager::Insert(AreaCell & c, Area & a, LightObj & obj)
{
// Is this cell currently locked?
if (c.mLocks)
{
m_Queue.emplace_back(c, a, obj); // Queue this request for now
}
else
{
c.mAreas.emplace_back(&a, obj);
}
// Associate the area with this cell so it can't be managed again (even while in the queue)
a.mCells.push_back(&c);
}
// ------------------------------------------------------------------------------------------------
void AreaManager::Remove(AreaCell & c, Area & a)
{
// Is this cell currently locked?
if (c.mLocks)
{
m_Queue.emplace_back(c, a); // Queue this request for now
}
else
{
// Attempt to locate this area in the cell
AreaCell::Areas::iterator itr = std::find_if(c.mAreas.begin(), c.mAreas.end(),
[&a](AreaCell::Areas::reference p) -> bool {
return (p.first == &a);
});
// Have we found it?
if (itr != c.mAreas.end())
{
c.mAreas.erase(itr); // Erase it
}
}
// Dissociate the area with this cell so it can be managed again (even while in the queue)
Area::Cells::iterator itr = std::find(a.mCells.begin(), a.mCells.end(), &c);
// Was is associated?
if (itr != a.mCells.end())
{
a.mCells.erase(itr); // Dissociate them
}
}
// ------------------------------------------------------------------------------------------------
void AreaManager::ProcQueue()
{
// Look for actions that can be completed
for (Queue::iterator itr = m_Queue.begin(); itr != m_Queue.end(); ++itr)
{
// Was this cell unlocked in the meantime?
if (itr->mCell->mLocks <= 0)
{
m_ProcList.push_back(itr);
}
}
// Process the actions that are ready
for (auto & itr : m_ProcList)
{
// Was this a remove request?
if (itr->mObj.IsNull())
{
Remove(*(itr->mCell), *(itr->mArea));
}
else
{
Insert(*(itr->mCell), *(itr->mArea), itr->mObj);
}
}
// Remove processed requests
for (auto & itr : m_ProcList)
{
m_Queue.erase(itr);
}
// Actions were processed
m_ProcList.clear();
}
// ------------------------------------------------------------------------------------------------
void AreaManager::Clear()
{
// Clear the cells
for (AreaCell (&row)[GRIDN] : m_Grid)
{
for (AreaCell & c : row)
{
c.mAreas.clear();
}
}
// Clear the queue as well
m_Queue.clear();
m_ProcList.clear();
}
// ------------------------------------------------------------------------------------------------
void AreaManager::InsertArea(Area & a, LightObj & obj)
{
// See if this area is already managed
if (!a.mCells.empty() || a.mPoints.empty())
{
return; // Already managed or nothing to manage
}
// Go through each cell and check if the area touches it
//for (AreaCell (&row)[GRIDN] : m_Grid)
for (int y = 0; y < GRIDN; ++y)
{
//for (AreaCell & c : row)
for (int x = 0; x < GRIDN; ++x)
{
AreaCell & c = m_Grid[y][x];
// Does the bounding box of this cell intersect with the one of the area?
if (a.mL <= c.mR && c.mL <= a.mR && a.mB <= c.mT && c.mB <= a.mT)
{
Insert(c, a, obj); // Attempt to insert the area into this cell
}
}
}
}
// ------------------------------------------------------------------------------------------------
void AreaManager::RemoveArea(Area & a)
{
// Just remove the associated cells
for (auto c : a.mCells)
{
Remove(*c, a);
}
}
// ------------------------------------------------------------------------------------------------
Vector2i AreaManager::LocateCell(float x, float y)
{
// Transform the world coordinates into a cell coordinates
// and cast to integral after rounding the value
int xc = static_cast< int >(std::round(x / CELLD));
int yc = static_cast< int >(std::round(y / CELLD));
// Grab the absolute cell coordinates for range checking
const int xca = std::abs(xc);
const int yca = std::abs(yc);
// Make sure the cell coordinates are within range
if (xca > (GRIDH+1) || yca > (GRIDH+1))
{
return Vector2i(NOCELL, NOCELL); // Out of our scanning area
}
// Clamp the x coordinate if necessary
if (xca >= (GRIDH))
{
xc = xc < 0 ? -(GRIDH-1) : (GRIDH-1);
}
// Clamp the y coordinate if necessary
if (yca >= (GRIDH))
{
yc = xc < 0 ? -(GRIDH-1) : (GRIDH-1);
}
// Return the identified cell row and column
return Vector2i(GRIDH+xc, GRIDH-yc);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPointEx(Object & env, Function & func, float x, float y)
{
// Is the function valid?
if (func.IsNull())
{
STHROWF("Invalid callback object");
}
// Should we use a custom environment?
else if (!env.IsNull())
{
func = Function(env.GetVM(), env, func.GetFunc());
}
// Begin testing
AreaManager::Get().TestPoint([&func](AreaCell::Areas::reference ap) -> void {
func.Execute(ap.second);
}, x, y);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPoint(Object & env, Function & func, const Vector2 & v)
{
Areas_TestPointEx(env, func, v.x, v.y);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPointOnEx(Object & ctx, Object & env, Function & func, float x, float y)
{
// Is the function valid?
if (func.IsNull())
{
STHROWF("Invalid callback object");
}
// Should we use a custom environment?
else if (!env.IsNull())
{
func = Function(env.GetVM(), env, func.GetFunc());
}
// Begin testing
AreaManager::Get().TestPoint([&ctx, &func](AreaCell::Areas::reference ap) -> void {
func.Execute(ctx, ap.second);
}, x, y);
}
// ------------------------------------------------------------------------------------------------
static void Areas_TestPointOn(Object & ctx, Object & env, Function & func, const Vector2 & v)
{
Areas_TestPointOnEx(ctx, env, func, v.x, v.y);
}
// ------------------------------------------------------------------------------------------------
static Vector2i Areas_LocatePointCell(const Vector2 & v)
{
return AreaManager::Get().LocateCell(v.x, v.y);
}
// ------------------------------------------------------------------------------------------------
static Vector2i Areas_LocatePointCellEx(float x, float y)
{
return AreaManager::Get().LocateCell(x, y);
}
// ------------------------------------------------------------------------------------------------
void TerminateAreas()
{
AreaManager::Get().Clear();
}
// ================================================================================================
void Register_Areas(HSQUIRRELVM vm)
{
RootTable(vm).Bind(_SC("SqArea"),
Class< Area >(vm, AreaTypename::Str)
// Constructors
.Ctor()
.FmtCtor()
.FmtCtor< SQInteger >()
.Ctor< const Vector2 &, const Vector2 &, const Vector2 & >()
.FmtCtor< const Vector2 &, const Vector2 &, const Vector2 & >()
.FmtCtor< const Vector2 &, const Vector2 &, const Vector2 &, SQInteger >()
.Ctor< float, float, float, float, float, float >()
.FmtCtor< float, float, float, float, float, float >()
.FmtCtor< float, float, float, float, float, float, SQInteger >()
// Meta-methods
.SquirrelFunc(_SC("_typename"), &AreaTypename::Fn)
.Func(_SC("_tostring"), &Area::ToString)
// Member Properties
.Prop(_SC("Name"), &Area::GetName, &Area::SetName)
.Prop(_SC("ID"), &Area::GetID, &Area::SetID)
.Prop(_SC("Locked"), &Area::IsLocked)
.Prop(_SC("IsLocked"), &Area::IsLocked)
.Prop(_SC("Center"), &Area::GetCenter)
.Prop(_SC("BoundingBox"), &Area::GetBoundingBox)
.Prop(_SC("Empty"), &Area::Empty)
.Prop(_SC("Size"), &Area::Size)
.Prop(_SC("Points"), &Area::Size)
.Prop(_SC("Capacity"), &Area::Capacity)
// Member Methods
.FmtFunc(_SC("SetName"), &Area::ApplyName)
.Func(_SC("SetID"), &Area::ApplyID)
.Func(_SC("Clear"), &Area::Clear)
.Func(_SC("Reserve"), &Area::Reserve)
.Func(_SC("Add"), &Area::AddPoint)
.Func(_SC("AddEx"), &Area::AddPointEx)
.Func(_SC("AddArray"), &Area::AddArray)
.Func(_SC("Test"), &Area::Test)
.Func(_SC("TestEx"), &Area::TestEx)
.Func(_SC("Manage"), &Area::Manage)
.Func(_SC("Unmanage"), &Area::Unmanage)
// Static Functions
.StaticFunc(_SC("GlobalTest"), &Areas_TestPoint)
.StaticFunc(_SC("GlobalTestEx"), &Areas_TestPointEx)
.StaticFunc(_SC("GlobalTestOn"), &Areas_TestPointOn)
.StaticFunc(_SC("GlobalTestOnEx"), &Areas_TestPointOnEx)
.StaticFunc(_SC("LocatePointCell"), &Areas_LocatePointCell)
.StaticFunc(_SC("LocatePointCellEx"), &Areas_LocatePointCellEx)
.StaticFunc(_SC("UnmanageAll"), &TerminateAreas)
);
}
} // Namespace:: SqMod

670
source/Areas.hpp Normal file
View File

@ -0,0 +1,670 @@
#ifndef _AREAS_HPP_
#define _AREAS_HPP_
// ------------------------------------------------------------------------------------------------
#include "Base/Shared.hpp"
#include "Base/Vector2.hpp"
#include "Base/Vector4.hpp"
#include "Base/Vector2i.hpp"
// ------------------------------------------------------------------------------------------------
#include <vector>
#include <utility>
// ------------------------------------------------------------------------------------------------
namespace SqMod {
/* ------------------------------------------------------------------------------------------------
* Various information associated with an area cell.
*/
struct AreaCell
{
// --------------------------------------------------------------------------------------------
typedef std::pair< Area *, LightObj > AreaPair; // A reference to an area object.
typedef std::vector< AreaPair > Areas; // A list of area objects.
// --------------------------------------------------------------------------------------------
float mL, mB, mR, mT; // Left-Bottom, Right-Top components of the cell bounding box.
// --------------------------------------------------------------------------------------------
Areas mAreas; // Areas that intersect with the cell.
// --------------------------------------------------------------------------------------------
int mLocks; // The amount of locks on the cell.
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
AreaCell()
: mL(0), mB(0), mR(0), mT(0), mAreas(0), mLocks(0)
{
//...
}
};
/* ------------------------------------------------------------------------------------------------
* Area implementation used to store area points.
*/
struct Area
{
typedef std::vector< Vector2 > Points;
typedef std::vector< AreaCell * > Cells;
// --------------------------------------------------------------------------------------------
static constexpr float DEF_L = std::numeric_limits< float >::infinity();
static constexpr float DEF_B = std::numeric_limits< float >::infinity();
static constexpr float DEF_R = -std::numeric_limits< float >::infinity();
static constexpr float DEF_T = -std::numeric_limits< float >::infinity();
// --------------------------------------------------------------------------------------------
float mL, mB, mR, mT; // Left-Bottom, Right-Top components of the bounding box.
// --------------------------------------------------------------------------------------------
Points mPoints; // Collection of points that make up the area.
// --------------------------------------------------------------------------------------------
SQInteger mID; // The user identifier given to this area.
// --------------------------------------------------------------------------------------------
Cells mCells; // The cells covered by this area.
// --------------------------------------------------------------------------------------------
String mName; // The user name given to this area.
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Area()
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells(), mName()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Named constructor.
*/
Area(const StackStrF & name)
: Area(16, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Default constructor.
*/
Area(SQInteger sz, const StackStrF & name)
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells()
, mName(name.mPtr, name.mLen <= 0 ? 0 : name.mLen)
{
// Should we reserve some space for points in advance?
if (sz > 0)
{
mPoints.reserve(static_cast< size_t >(sz));
}
}
/* --------------------------------------------------------------------------------------------
* Vector2 constructor.
*/
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c)
: Area(a.x, a.y, b.x, b.y, c.x, c.y, 16, StackStrF())
{
//...
}
/* --------------------------------------------------------------------------------------------
* Vector2 constructor with name.
*/
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c, const StackStrF & name)
: Area(a.x, a.y, b.x, b.y, c.x, c.y, 16, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Vector2 constructor with name and memory to reserve.
*/
Area(const Vector2 & a, const Vector2 & b, const Vector2 & c, SQInteger sz, const StackStrF & name)
: Area(a.x, a.y, b.x, b.y, c.x, c.y, sz, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Extended constructor.
*/
Area(float ax, float ay, float bx, float by, float cx, float cy)
: Area(ax, ay, bx, by, cx, cy, 16, StackStrF())
{
//...
}
/* --------------------------------------------------------------------------------------------
* Extended constructor with name.
*/
Area(float ax, float ay, float bx, float by, float cx, float cy, const StackStrF & name)
: Area(ax, ay, bx, by, cx, cy, 16, name)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
Area(float ax, float ay, float bx, float by, float cx, float cy, SQInteger sz, const StackStrF & name)
: mL(DEF_L), mB(DEF_B), mR(DEF_R), mT(DEF_T), mPoints(), mID(0), mCells()
, mName(name.mPtr, name.mLen <= 0 ? 0 : name.mLen)
{
// Should we reserve some space for points in advance?
if (sz > 0)
{
mPoints.reserve(static_cast< size_t >(sz));
}
// Insert the given points
AddPointEx(ax, ay);
AddPointEx(bx, by);
AddPointEx(cx, cy);
}
/* --------------------------------------------------------------------------------------------
* Copy constructor.
*/
Area(const Area & o)
: mL(o.mL), mB(o.mB), mR(o.mR), mT(o.mT), mPoints(o.mPoints), mID(o.mID), mCells(0), mName(o.mName)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
Area(Area && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~Area()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
Area & operator = (const Area & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
Area & operator = (Area && o) = delete;
/* --------------------------------------------------------------------------------------------
* Used by the script engine to convert this instance to a string.
*/
const String & ToString() const
{
return mName;
}
/* --------------------------------------------------------------------------------------------
* Checks if the area is locked from changes and throws an exception if it is.
*/
void CheckLock()
{
// Are we connected to any cells?
if (!mCells.empty())
{
STHROWF("The area cannot be modified while being managed");
}
}
/* --------------------------------------------------------------------------------------------
* Retrieve the name of this area.
*/
const String & GetName() const
{
return mName;
}
/* --------------------------------------------------------------------------------------------
* Modify the name of this area.
*/
void SetName(const StackStrF & name)
{
if (name.mLen <= 0)
{
mName.clear();
}
else
{
mName.assign(name.mPtr, static_cast< size_t >(name.mLen));
}
}
/* --------------------------------------------------------------------------------------------
* Modify the name of this area. (allows chaining function calls)
*/
Area & ApplyName(const StackStrF & name)
{
SetName(name);
return *this;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the identifier of this area.
*/
SQInteger GetID() const
{
return mID;
}
/* --------------------------------------------------------------------------------------------
* Modify the identifier of this area.
*/
void SetID(SQInteger id)
{
mID = id;
}
/* --------------------------------------------------------------------------------------------
* Modify the identifier of this area. (allows chaining function calls)
*/
Area & ApplyID(SQInteger id)
{
mID = id;
return *this;
}
/* --------------------------------------------------------------------------------------------
* Check if the area is locked from changes
*/
bool IsLocked() const
{
return mCells.empty();
}
/* --------------------------------------------------------------------------------------------
* Retrieve the center of this area.
*/
Vector2 GetCenter() const
{
return Vector2((mL * 0.5f) + (mR * 0.5f), (mB * 0.5f) + (mT * 0.5f));
}
/* --------------------------------------------------------------------------------------------
* Retrieve the bounding box of this area.
*/
Vector4 GetBoundingBox() const
{
return Vector4(mL, mB, mR, mT);
}
/* --------------------------------------------------------------------------------------------
* See whether the area has no points.
*/
bool Empty() const
{
return mPoints.empty();
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of points in this area.
*/
SQInteger Size() const
{
return ConvTo< SQInteger >::From(mPoints.size());
}
/* --------------------------------------------------------------------------------------------
* Retrieve the number of points this area has allocated for.
*/
SQInteger Capacity() const
{
return ConvTo< SQInteger >::From(mPoints.capacity());
}
/* --------------------------------------------------------------------------------------------
* Clear all points in this area.
*/
void Clear()
{
CheckLock();
// Perform the requested action
mPoints.clear();
}
/* --------------------------------------------------------------------------------------------
* Clear all points in this area.
*/
void Reserve(SQInteger sz)
{
// Perform the requested action
if (sz > 0)
{
mPoints.reserve(static_cast< size_t >(sz));
}
}
/* --------------------------------------------------------------------------------------------
* Add a 2D vector to the point list.
*/
void AddPoint(const Vector2 & v)
{
CheckLock();
// Perform the requested action
mPoints.emplace_back(v);
// Update the bounding box
Expand(v.x, v.y);
}
/* --------------------------------------------------------------------------------------------
* Add a point to the point list.
*/
void AddPointEx(float x, float y)
{
CheckLock();
// Perform the requested action
mPoints.emplace_back(x, y);
// Update the bounding box
Expand(x, y);
}
/* --------------------------------------------------------------------------------------------
* Add an array of points to the point list.
*/
void AddArray(const Sqrat::Array & a);
/* --------------------------------------------------------------------------------------------
* Test if a point is inside the bounding box and then the area.
*/
bool Test(const Vector2 & v)
{
// Is the given point in this bounding box at least?
if (mL <= v.x && mR >= v.x && mB <= v.y && mT >= v.y)
{
return IsInside(v.x, v.y);
}
// Not in this area
return false;
}
/* --------------------------------------------------------------------------------------------
* Test if a point is inside the bounding box and then the area.
*/
bool TestEx(float x, float y)
{
// Is the given point in this bounding box at least?
if (mL <= x && mR >= x && mB <= y && mT >= y)
{
return IsInside(x, y);
}
// Not in this area
return false;
}
/* --------------------------------------------------------------------------------------------
* Add this area to the manager to be scanned. MUST BE CALLED ONLY FROM SCRIPT!
*/
bool Manage();
/* --------------------------------------------------------------------------------------------
* Remove this area from the manager to no longer be scanned.
*/
bool Unmanage();
protected:
/* --------------------------------------------------------------------------------------------
* Test if a point is inside the area.
*/
bool IsInside(float x, float y) const;
/* --------------------------------------------------------------------------------------------
* Expand the bounding box area to include the given point.
*/
void Expand(float x, float y)
{
mL = std::fmin(mL, x);
mB = std::fmin(mB, y);
mR = std::fmax(mR, x);
mT = std::fmax(mT, y);
}
};
/* ------------------------------------------------------------------------------------------------
* Manager responsible for storing and partitioning areas.
*/
class AreaManager
{
private:
// --------------------------------------------------------------------------------------------
static AreaManager s_Inst; // Manager instance.
/* --------------------------------------------------------------------------------------------
* Base constructor.
*/
AreaManager(size_t sz = 16);
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
AreaManager(const AreaManager & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor. (disabled)
*/
AreaManager(AreaManager && o) = delete;
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~AreaManager()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
AreaManager & operator = (const AreaManager & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator. (disabled)
*/
AreaManager & operator = (AreaManager && o) = delete;
protected:
/* --------------------------------------------------------------------------------------------
* Helper used to make sure a cell is properly processed after leaving the scope.
*/
struct CellGuard
{
AreaCell & mCell;
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
CellGuard(AreaCell & cell)
: mCell(cell)
{
++(cell.mLocks); // Place a lock on the cell to prevent iterator invalidation
}
/* ----------------------------------------------------------------------------------------
* Destructor.
*/
~CellGuard()
{
// Remove the lock from the cell so it can be processed
--(mCell.mLocks);
// Process requested actions during the lock
AreaManager::Get().ProcQueue();
}
};
// --------------------------------------------------------------------------------------------
static constexpr int GRIDN = 16; // Number of horizontal and vertical number of cells.
static constexpr int GRIDH = GRIDN/2; // Half of the grid horizontal and vertical size.
static constexpr int CELLS = GRIDN*GRIDN; // Total number of cells in the grid.
static constexpr int CELLH = CELLS/2; // Half total number of cells in the grid.
static constexpr int CELLD = 256; // Area covered by a cell in the world.
static constexpr int NOCELL = std::numeric_limits< int >::max(); // Inexistent cell index.
/* --------------------------------------------------------------------------------------------
* Helper used to queue a certain action if the cell is locked.
*/
struct QueueElement
{
// ----------------------------------------------------------------------------------------
AreaCell * mCell; // The cell to be affected.
Area * mArea; // The area that made the request.
LightObj mObj; // Strong reference to the object.
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
QueueElement(AreaCell & cell, Area & area)
: mCell(&cell), mArea(&area), mObj()
{
//...
}
/* ----------------------------------------------------------------------------------------
* Base constructor.
*/
QueueElement(AreaCell & cell, Area & area, LightObj & obj)
: mCell(&cell), mArea(&area), mObj(obj)
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy constructor. (disabled)
*/
QueueElement(const QueueElement & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move constructor.
*/
QueueElement(QueueElement && o)
: mCell(o.mCell), mArea(o.mArea), mObj(std::move(o.mObj))
{
// Take ownership
o.mCell = nullptr;
o.mArea = nullptr;
}
/* --------------------------------------------------------------------------------------------
* Destructor.
*/
~QueueElement()
{
//...
}
/* --------------------------------------------------------------------------------------------
* Copy assignment operator. (disabled)
*/
QueueElement & operator = (const QueueElement & o) = delete;
/* --------------------------------------------------------------------------------------------
* Move assignment operator.
*/
QueueElement & operator = (QueueElement && o)
{
// Avoid self assignment
if (this != &o)
{
// Transfer values
mCell = o.mCell;
mArea = o.mArea;
mObj = std::move(o.mObj);
// Take ownership
o.mCell = nullptr;
o.mArea = nullptr;
}
return *this;
}
};
// --------------------------------------------------------------------------------------------
typedef std::vector< QueueElement > Queue; // Queued actions.
typedef std::vector< Queue::iterator > ProcList; // Elements in the queue redy to process.
/* --------------------------------------------------------------------------------------------
* Attempt to insert an area into a cell or queue the action if not possible.
*/
void Insert(AreaCell & c, Area & a, LightObj & obj);
/* --------------------------------------------------------------------------------------------
* Attempt to remove an area from a cell or queue the action if not possible.
*/
void Remove(AreaCell & c, Area & a);
private:
// --------------------------------------------------------------------------------------------
Queue m_Queue; // Actions currently queued.
ProcList m_ProcList; // Actions ready to be completed.
// --------------------------------------------------------------------------------------------
AreaCell m_Grid[GRIDN][GRIDN]; // A grid of area lists.
public:
/* --------------------------------------------------------------------------------------------
* Retrieve the core instance.
*/
static AreaManager & Get()
{
return s_Inst;
}
/* --------------------------------------------------------------------------------------------
* Attempt to process elements in the queue that can be completed.
*/
void ProcQueue();
/* --------------------------------------------------------------------------------------------
* Clear all cell lists and release any script references.
*/
void Clear();
/* --------------------------------------------------------------------------------------------
* Add an area to be managed.
*/
void InsertArea(Area & a, LightObj & obj);
/* --------------------------------------------------------------------------------------------
* Add an area to be managed.
*/
void RemoveArea(Area & a);
/* --------------------------------------------------------------------------------------------
* Clear all cell lists and release any script references.
*/
Vector2i LocateCell(float x, float y);
/* --------------------------------------------------------------------------------------------
* Test a point to see whether it intersects with any areas
*/
template < typename F > void TestPoint(F && f, float x, float y)
{
// Transform the world coordinates into a cell coordinates
const Vector2i cc(LocateCell(x, y));
// Were these coordinates valid?
if (cc.x == NOCELL)
{
return; // Not our problem
}
// Retrieve a reference to the identified cell
AreaCell & c = m_Grid[cc.y][cc.x];
// Is this cell empty?
if (c.mAreas.empty())
{
return; // Nothing to test
}
// Guard the cell while processing
const CellGuard cg(c);
// Finally, begin processing the areas in this cell
for (auto & a : c.mAreas)
{
if (a.first->TestEx(x, y))
{
f(a);
}
}
}
};
} // Namespace:: SqMod
#endif // _AREAS_HPP_

View File

@ -37,6 +37,7 @@ extern bool RegisterAPI(HSQUIRRELVM vm);
// ------------------------------------------------------------------------------------------------
extern void InitializeTasks();
extern void InitializeRoutines();
extern void TerminateAreas();
extern void TerminateTasks();
extern void TerminateRoutines();
extern void TerminateCommands();
@ -146,6 +147,7 @@ Core::Core()
, m_ReloadPayload()
, m_IncomingNameBuffer(nullptr)
, m_IncomingNameCapacity(0)
, m_AreasEnabled(false)
, m_Debugging(false)
, m_Executed(false)
, m_Shutdown(false)
@ -480,6 +482,8 @@ void Core::Terminate(bool shutdown)
TerminateCommands();
// Release all resources from signals
TerminateSignals();
// Release all managed areas
TerminateAreas();
// In case there's a payload for reload
m_ReloadPayload.Release();
// Release null objects in case any reference to valid objects is stored in them

View File

@ -66,6 +66,9 @@ private:
protected:
// --------------------------------------------------------------------------------------------
typedef std::vector< std::pair< Area *, LightObj > > AreaList; // List of colided areas.
/* --------------------------------------------------------------------------------------------
* Helper structure used to identify a blip entity instance on the server.
*/
@ -449,6 +452,9 @@ protected:
CPlayer * mInst; // Pointer to the actual instance used to interact this entity.
LightObj mObj; // Script object of the instance used to interact this entity.
// ----------------------------------------------------------------------------------------
AreaList mAreas; // Areas the player is currently in.
// ----------------------------------------------------------------------------------------
SQInteger mTrackPosition; // The number of times to track position changes.
SQInteger mTrackHeading; // The number of times to track heading changes.
@ -548,6 +554,8 @@ protected:
SignalPair mOnWantedLevel;
SignalPair mOnImmunity;
SignalPair mOnAlpha;
SignalPair mOnEnterArea;
SignalPair mOnLeaveArea;
};
/* --------------------------------------------------------------------------------------------
@ -600,6 +608,9 @@ protected:
CVehicle * mInst; // Pointer to the actual instance used to interact this entity.
LightObj mObj; // Script object of the instance used to interact this entity.
// ----------------------------------------------------------------------------------------
AreaList mAreas; // Areas the vehicle is currently in.
// ----------------------------------------------------------------------------------------
SQInteger mTrackPosition; // The number of times to track position changes.
SQInteger mTrackRotation; // The number of times to track rotation changes.
@ -637,6 +648,8 @@ protected:
SignalPair mOnDamageData;
SignalPair mOnRadio;
SignalPair mOnHandlingRule;
SignalPair mOnEnterArea;
SignalPair mOnLeaveArea;
};
public:
@ -688,6 +701,7 @@ private:
size_t m_IncomingNameCapacity; // Incoming connection name size.
// --------------------------------------------------------------------------------------------
bool m_AreasEnabled; // Whether area tracking is enabled.
bool m_Debugging; // Enable debugging features, if any.
bool m_Executed; // Whether the scripts were executed.
bool m_Shutdown; // Whether the server currently shutting down.
@ -695,7 +709,6 @@ private:
bool m_LockPostLoadSignal; // Lock post load signal container.
bool m_LockUnloadSignal; // Lock unload signal container.
bool m_EmptyInit; // Whether to initialize without any scripts.
// --------------------------------------------------------------------------------------------
Int32 m_Verbosity; // Restrict the amount of outputted information.
@ -778,6 +791,22 @@ public:
return m_Shutdown;
}
/* --------------------------------------------------------------------------------------------
* See whether area tracking should be enabled on newlly created entities.
*/
bool AreasEnabled() const
{
return m_AreasEnabled;
}
/* --------------------------------------------------------------------------------------------
* Toggle whether area tracking should be enabled on newlly created entities.
*/
void AreasEnabled(bool toggle)
{
m_AreasEnabled = toggle;
}
/* --------------------------------------------------------------------------------------------
* Retrieve the value of the specified option.
*/
@ -1168,6 +1197,8 @@ public:
void EmitPlayerWantedLevel(Int32 player_id, Int32 old_level, Int32 new_level);
void EmitPlayerImmunity(Int32 player_id, Int32 old_immunity, Int32 new_immunity);
void EmitPlayerAlpha(Int32 player_id, Int32 old_alpha, Int32 new_alpha, Int32 fade);
void EmitPlayerEnterArea(Int32 player_id, LightObj & area_obj);
void EmitPlayerLeaveArea(Int32 player_id, LightObj & area_obj);
void EmitVehicleColor(Int32 vehicle_id, Int32 changed);
void EmitVehicleHealth(Int32 vehicle_id, Float32 old_health, Float32 new_health);
void EmitVehiclePosition(Int32 vehicle_id);
@ -1180,6 +1211,8 @@ public:
void EmitVehicleDamageData(Int32 vehicle_id, Uint32 old_data, Uint32 new_data);
void EmitVehicleRadio(Int32 vehicle_id, Int32 old_radio, Int32 new_radio);
void EmitVehicleHandlingRule(Int32 vehicle_id, Int32 rule, Float32 old_data, Float32 new_data);
void EmitVehicleEnterArea(Int32 player_id, LightObj & area_obj);
void EmitVehicleLeaveArea(Int32 player_id, LightObj & area_obj);
void EmitServerOption(Int32 option, bool value, Int32 header, LightObj & payload);
void EmitScriptReload(Int32 header, LightObj & payload);
void EmitScriptLoaded();
@ -1321,6 +1354,8 @@ public:
SignalPair mOnPlayerWantedLevel;
SignalPair mOnPlayerImmunity;
SignalPair mOnPlayerAlpha;
SignalPair mOnPlayerEnterArea;
SignalPair mOnPlayerLeaveArea;
SignalPair mOnVehicleColor;
SignalPair mOnVehicleHealth;
SignalPair mOnVehiclePosition;
@ -1333,6 +1368,8 @@ public:
SignalPair mOnVehicleDamageData;
SignalPair mOnVehicleRadio;
SignalPair mOnVehicleHandlingRule;
SignalPair mOnVehicleEnterArea;
SignalPair mOnVehicleLeaveArea;
SignalPair mOnServerOption;
SignalPair mOnScriptReload;
SignalPair mOnScriptLoaded;

View File

@ -420,6 +420,11 @@ Core::VehicleInst & Core::AllocVehicle(Int32 id, bool owned, Int32 header, Light
{
inst.mFlags ^= ENF_OWNED;
}
// Should we enable area tracking?
if (m_AreasEnabled)
{
inst.mFlags |= ENF_AREA_TRACK;
}
// Initialize the instance events
inst.InitEvents();
// Let the script callbacks know about this entity
@ -795,6 +800,11 @@ void Core::ConnectPlayer(Int32 id, Int32 header, LightObj & payload)
}
// Assign the specified entity identifier
inst.mID = id;
// Should we enable area tracking?
if (m_AreasEnabled)
{
inst.mFlags |= ENF_AREA_TRACK;
}
// Initialize the position
_Func->GetPlayerPosition(id, &inst.mLastPosition.x, &inst.mLastPosition.y, &inst.mLastPosition.z);
// Initialize the remaining attributes

View File

@ -1,5 +1,6 @@
// ------------------------------------------------------------------------------------------------
#include "Core.hpp"
#include "Areas.hpp"
#include "Signal.hpp"
#include "Base/Buffer.hpp"
#include "Library/Utils/Buffer.hpp"
@ -879,6 +880,22 @@ void Core::EmitPlayerAlpha(Int32 player_id, Int32 old_alpha, Int32 new_alpha, In
(*mOnPlayerAlpha.first)(_player.mObj, old_alpha, new_alpha, fade);
}
// ------------------------------------------------------------------------------------------------
void Core::EmitPlayerEnterArea(Int32 player_id, LightObj & area_obj)
{
PlayerInst & _player = m_Players.at(player_id);
(*_player.mOnEnterArea.first)(area_obj);
(*mOnPlayerEnterArea.first)(_player.mObj, area_obj);
}
// ------------------------------------------------------------------------------------------------
void Core::EmitPlayerLeaveArea(Int32 player_id, LightObj & area_obj)
{
PlayerInst & _player = m_Players.at(player_id);
(*_player.mOnLeaveArea.first)(area_obj);
(*mOnPlayerLeaveArea.first)(_player.mObj, area_obj);
}
// ------------------------------------------------------------------------------------------------
void Core::EmitVehicleColor(Int32 vehicle_id, Int32 changed)
{
@ -972,8 +989,24 @@ void Core::EmitVehicleRadio(Int32 vehicle_id, Int32 old_radio, Int32 new_radio)
void Core::EmitVehicleHandlingRule(Int32 vehicle_id, Int32 rule, Float32 old_data, Float32 new_data)
{
VehicleInst & _vehicle = m_Vehicles.at(vehicle_id);
(*mOnVehicleHandlingRule.first)(_vehicle.mObj, rule, old_data, new_data);
(*_vehicle.mOnHandlingRule.first)(rule, old_data, new_data);
(*mOnVehicleHandlingRule.first)(_vehicle.mObj, rule, old_data, new_data);
}
// ------------------------------------------------------------------------------------------------
void Core::EmitVehicleEnterArea(Int32 vehicle_id, LightObj & area_obj)
{
VehicleInst & _vehicle = m_Vehicles.at(vehicle_id);
(*_vehicle.mOnEnterArea.first)(area_obj);
(*mOnVehicleEnterArea.first)(_vehicle.mObj, area_obj);
}
// ------------------------------------------------------------------------------------------------
void Core::EmitVehicleLeaveArea(Int32 vehicle_id, LightObj & area_obj)
{
VehicleInst & _vehicle = m_Vehicles.at(vehicle_id);
(*_vehicle.mOnLeaveArea.first)(area_obj);
(*mOnVehicleLeaveArea.first)(_vehicle.mObj, area_obj);
}
// ------------------------------------------------------------------------------------------------
@ -1150,6 +1183,39 @@ void Core::EmitPlayerUpdate(Int32 player_id, vcmpPlayerUpdate update_type)
// Now emit the event
EmitPlayerPosition(player_id);
}
// Should we check for area collision
if (inst.mFlags & ENF_AREA_TRACK)
{
// Eliminate existing areas first, if the player is not in them anymore
inst.mAreas.erase(std::remove_if(inst.mAreas.begin(), inst.mAreas.end(),
[this, pos, player_id](AreaList::reference ap) -> bool {
// Is this player still in this area?
if (!ap.first->TestEx(pos.x, pos.y))
{
// Emit the script event
this->EmitPlayerLeaveArea(player_id, ap.second);
// Remove this area
return true;
}
// Still in this area
return false;
}), inst.mAreas.end());
// See if the player entered any new areas
AreaManager::Get().TestPoint([this, &inst, player_id](AreaList::reference ap) -> void {
// Was the player in this area before?
if (std::find_if(inst.mAreas.begin(), inst.mAreas.end(),
[a = ap.first](AreaList::reference ap) -> bool {
return (a == ap.first);
}) == inst.mAreas.end())
{
// The player just entered this area so emit the event
this->EmitPlayerEnterArea(player_id, ap.second);
// Now store this area so we know when the player leaves
inst.mAreas.emplace_back(ap);
}
// The player was in this area before so ignore it
}, pos.x, pos.y);
}
// Update the tracked value
inst.mLastPosition = pos;
}
@ -1218,9 +1284,45 @@ void Core::EmitVehicleUpdate(Int32 vehicle_id, vcmpVehicleUpdate update_type)
// Now emit the event
EmitVehiclePosition(vehicle_id);
}
// New vehicle position
Vector3 pos;
// Retrieve the current vehicle position
_Func->GetVehiclePosition(vehicle_id, &pos.x, &pos.y, &pos.z);
// Should we check for area collision
if (inst.mFlags & ENF_AREA_TRACK)
{
// Eliminate existing areas first, if the vehicle is not in them anymore
inst.mAreas.erase(std::remove_if(inst.mAreas.begin(), inst.mAreas.end(),
[this, pos, vehicle_id](AreaList::reference ap) -> bool {
// Is this vehicle still in this area?
if (!ap.first->TestEx(pos.x, pos.y))
{
// Emit the script event
this->EmitVehicleLeaveArea(vehicle_id, ap.second);
// Remove this area
return true;
}
// Still in this area
return false;
}), inst.mAreas.end());
// See if the vehicle entered any new areas
AreaManager::Get().TestPoint([this, &inst, vehicle_id](AreaList::reference ap) -> void {
// Was the vehicle in this area before?
if (std::find_if(inst.mAreas.begin(), inst.mAreas.end(),
[a = ap.first](AreaList::reference ap) -> bool {
return (a == ap.first);
}) == inst.mAreas.end())
{
// The vehicle just entered this area so emit the event
this->EmitVehicleEnterArea(vehicle_id, ap.second);
// Now store this area so we know when the vehicle leaves
inst.mAreas.emplace_back(ap);
}
// The vehicle was in this area before so ignore it
}, pos.x, pos.y);
}
// Update the tracked value
_Func->GetVehiclePosition(vehicle_id, &inst.mLastPosition.x,
&inst.mLastPosition.y, &inst.mLastPosition.z);
inst.mLastPosition = pos;
} break;
case vcmpVehicleUpdateHealth:
{

View File

@ -119,6 +119,18 @@ static void SetState(Int32 value)
return Core::Get().SetState(value);
}
// ------------------------------------------------------------------------------------------------
static bool GetAreasEnabled()
{
return Core::Get().AreasEnabled();
}
// ------------------------------------------------------------------------------------------------
static void SetAreasEnabled(bool toggle)
{
Core::Get().AreasEnabled(toggle);
}
// ------------------------------------------------------------------------------------------------
static CSStr GetOption(CSStr name)
{
@ -306,6 +318,8 @@ void Register_Core(HSQUIRRELVM vm)
.Func(_SC("GetReloadPayload"), &SqGetReloadPayload)
.Func(_SC("GetState"), &GetState)
.Func(_SC("SetState"), &SetState)
.Func(_SC("AreasEnabled"), &GetAreasEnabled)
.Func(_SC("SetAreasEnabled"), &SetAreasEnabled)
.Func(_SC("GetOption"), &GetOption)
.Func(_SC("GetOptionOr"), &GetOptionOr)
.Func(_SC("SetOption"), &SetOption)

View File

@ -357,6 +357,7 @@ void Core::PlayerInst::ResetInstance()
{
mID = -1;
mFlags = ENF_DEFAULT;
mAreas.clear();
mTrackPosition = 0;
mTrackHeading = 0;
mTrackPositionHeader = 0;
@ -376,6 +377,7 @@ void Core::VehicleInst::ResetInstance()
{
mID = -1;
mFlags = ENF_DEFAULT;
mAreas.clear();
mTrackPosition = 0;
mTrackRotation = 0;
mLastPrimaryColor = -1;
@ -642,6 +644,8 @@ void Core::PlayerInst::InitEvents()
InitSignalPair(mOnWantedLevel, mEvents, "WantedLevel");
InitSignalPair(mOnImmunity, mEvents, "Immunity");
InitSignalPair(mOnAlpha, mEvents, "Alpha");
InitSignalPair(mOnEnterArea, mEvents, "EnterArea");
InitSignalPair(mOnLeaveArea, mEvents, "LeaveArea");
}
// ------------------------------------------------------------------------------------------------
@ -718,6 +722,8 @@ void Core::PlayerInst::DropEvents()
ResetSignalPair(mOnWantedLevel);
ResetSignalPair(mOnImmunity);
ResetSignalPair(mOnAlpha);
ResetSignalPair(mOnEnterArea);
ResetSignalPair(mOnLeaveArea);
mEvents.Release();
}
@ -756,6 +762,8 @@ void Core::VehicleInst::InitEvents()
InitSignalPair(mOnDamageData, mEvents, "DamageData");
InitSignalPair(mOnRadio, mEvents, "Radio");
InitSignalPair(mOnHandlingRule, mEvents, "HandlingRule");
InitSignalPair(mOnEnterArea, mEvents, "EnterArea");
InitSignalPair(mOnLeaveArea, mEvents, "LeaveArea");
}
// ------------------------------------------------------------------------------------------------
@ -781,6 +789,8 @@ void Core::VehicleInst::DropEvents()
ResetSignalPair(mOnDamageData);
ResetSignalPair(mOnRadio);
ResetSignalPair(mOnHandlingRule);
ResetSignalPair(mOnEnterArea);
ResetSignalPair(mOnLeaveArea);
mEvents.Release();
}

View File

@ -165,6 +165,8 @@ void Core::InitEvents()
InitSignalPair(mOnPlayerWantedLevel, m_Events, "PlayerWantedLevel");
InitSignalPair(mOnPlayerImmunity, m_Events, "PlayerImmunity");
InitSignalPair(mOnPlayerAlpha, m_Events, "PlayerAlpha");
InitSignalPair(mOnPlayerEnterArea, m_Events, "PlayerEnterArea");
InitSignalPair(mOnPlayerLeaveArea, m_Events, "PlayerLeaveArea");
InitSignalPair(mOnVehicleColor, m_Events, "VehicleColor");
InitSignalPair(mOnVehicleHealth, m_Events, "VehicleHealth");
InitSignalPair(mOnVehiclePosition, m_Events, "VehiclePosition");
@ -177,6 +179,8 @@ void Core::InitEvents()
InitSignalPair(mOnVehicleDamageData, m_Events, "VehicleDamageData");
InitSignalPair(mOnVehicleRadio, m_Events, "VehicleRadio");
InitSignalPair(mOnVehicleHandlingRule, m_Events, "VehicleHandlingRule");
InitSignalPair(mOnVehicleEnterArea, m_Events, "VehicleEnterArea");
InitSignalPair(mOnVehicleLeaveArea, m_Events, "VehicleLeaveArea");
InitSignalPair(mOnServerOption, m_Events, "ServerOption");
InitSignalPair(mOnScriptReload, m_Events, "ScriptReload");
InitSignalPair(mOnScriptLoaded, m_Events, "ScriptLoaded");
@ -293,6 +297,8 @@ void Core::DropEvents()
ResetSignalPair(mOnPlayerWantedLevel);
ResetSignalPair(mOnPlayerImmunity);
ResetSignalPair(mOnPlayerAlpha);
ResetSignalPair(mOnPlayerEnterArea);
ResetSignalPair(mOnPlayerLeaveArea);
ResetSignalPair(mOnVehicleColor);
ResetSignalPair(mOnVehicleHealth);
ResetSignalPair(mOnVehiclePosition);
@ -305,6 +311,8 @@ void Core::DropEvents()
ResetSignalPair(mOnVehicleDamageData);
ResetSignalPair(mOnVehicleRadio);
ResetSignalPair(mOnVehicleHandlingRule);
ResetSignalPair(mOnVehicleEnterArea);
ResetSignalPair(mOnVehicleLeaveArea);
ResetSignalPair(mOnServerOption);
ResetSignalPair(mOnScriptReload);
ResetSignalPair(mOnScriptLoaded);

View File

@ -6,6 +6,7 @@
#include "Base/Vector3.hpp"
#include "Library/Utils/Buffer.hpp"
#include "Core.hpp"
#include "Areas.hpp"
#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
@ -1431,6 +1432,83 @@ LightObj & CPlayer::CreateCheckpoint(Int32 world, bool sphere, const Vector3 & p
color.r, color.g, color.b, color.a, radius, header, payload);
}
// ------------------------------------------------------------------------------------------------
bool CPlayer::GetCollideAreas() const
{
// Validate the managed identifier
Validate();
// Return the requested information
return (Core::Get().GetPlayer(m_ID).mFlags & ENF_AREA_TRACK);
}
void CPlayer::SetCollideAreas(bool toggle) const
{
// Validate the managed identifier
Validate();
// Perform the requested operation
if (toggle)
{
Core::Get().GetPlayer(m_ID).mFlags |= ENF_AREA_TRACK;
}
else
{
// Obtain the actual entity instance
auto & inst = Core::Get().GetPlayer(m_ID);
// Is this option even enabled?
if (!(inst.mFlags & ENF_AREA_TRACK))
{
return; // Not enabled to begin with
}
// Disable the option
inst.mFlags ^= ENF_AREA_TRACK;
// Clear current areas
inst.mAreas.clear();
}
}
void CPlayer::SetAreasCollide(bool toggle) const
{
// Validate the managed identifier
Validate();
// Perform the requested operation
if (toggle)
{
Core::Get().GetPlayer(m_ID).mFlags |= ENF_AREA_TRACK;
}
else
{
// Obtain the actual entity instance
auto & inst = Core::Get().GetPlayer(m_ID);
// Is this option even enabled?
if (!(inst.mFlags & ENF_AREA_TRACK))
{
return; // Not enabled to begin with
}
// Disable the option
inst.mFlags ^= ENF_AREA_TRACK;
// Is the player currently in any areas?
if (inst.mAreas.empty())
{
return; // Nothing to test
}
Vector3 pos;
// Obtain the current position of this instance
_Func->GetPlayerPosition(m_ID, &pos.x, &pos.y, &pos.z);
// Do a final check to see if the player left any area
for (auto & ap : inst.mAreas)
{
// Is the player still in this area?
if (!ap.first->TestEx(pos.x, pos.y))
{
Core::Get().EmitPlayerLeaveArea(m_ID, ap.second); // Emit the script event
}
}
// Clear current areas
inst.mAreas.clear();
}
}
// ------------------------------------------------------------------------------------------------
Int32 CPlayer::GetAuthority() const
{
@ -2423,6 +2501,7 @@ void Register_CPlayer(HSQUIRRELVM vm)
.Prop(_SC("TouchedObject"), &CPlayer::StandingOnObject)
.Prop(_SC("Away"), &CPlayer::IsAway)
.Prop(_SC("Spec"), &CPlayer::GetSpectator, &CPlayer::SetSpectator)
.Prop(_SC("CollideAreas"), &CPlayer::GetCollideAreas, &CPlayer::SetCollideAreas)
.Prop(_SC("Authority"), &CPlayer::GetAuthority, &CPlayer::SetAuthority)
.Prop(_SC("TrackPosition"), &CPlayer::GetTrackPosition, &CPlayer::SetTrackPosition)
.Prop(_SC("TrackHeading"), &CPlayer::GetTrackHeading, &CPlayer::SetTrackHeading)
@ -2472,6 +2551,7 @@ void Register_CPlayer(HSQUIRRELVM vm)
.Func(_SC("Spectate"), &CPlayer::SetSpectator)
.Func(_SC("Redirect"), &CPlayer::Redirect)
.Func(_SC("PlaySound"), &CPlayer::PlaySound)
.Func(_SC("AreasCollide"), &CPlayer::SetAreasCollide)
.Func(_SC("GetMsgPrefix"), &CPlayer::GetMessagePrefix)
.FmtFunc(_SC("SetMsgPrefix"), &CPlayer::SetMessagePrefix)
.Func(_SC("SetTrackPosition"), &CPlayer::SetTrackPositionEx)

View File

@ -765,6 +765,21 @@ public:
LightObj & CreateCheckpoint(Int32 world, bool sphere, const Vector3 & pos, const Color4 & color,
Float32 radius, Int32 header, LightObj & payload) const;
/* --------------------------------------------------------------------------------------------
* See whether the managed player entity collides with user defined areas.
*/
bool GetCollideAreas() const;
/* --------------------------------------------------------------------------------------------
* Set whether the managed player entity can collide with user defined areas.
*/
void SetCollideAreas(bool toggle) const;
/* --------------------------------------------------------------------------------------------
* Set whether the managed player entity can collide with user defined areas (with last test).
*/
void SetAreasCollide(bool toggle) const;
/* --------------------------------------------------------------------------------------------
* Retrieve the authority level of the managed player entity.
*/

View File

@ -5,6 +5,7 @@
#include "Base/Vector2.hpp"
#include "Base/Vector3.hpp"
#include "Core.hpp"
#include "Areas.hpp"
#include "Tasks.hpp"
// ------------------------------------------------------------------------------------------------
@ -1119,6 +1120,83 @@ bool CVehicle::Embark(CPlayer & player, Int32 slot, bool allocate, bool warp) co
!= vcmpErrorRequestDenied);
}
// ------------------------------------------------------------------------------------------------
bool CVehicle::GetCollideAreas() const
{
// Validate the managed identifier
Validate();
// Return the requested information
return (Core::Get().GetVehicle(m_ID).mFlags & ENF_AREA_TRACK);
}
void CVehicle::SetCollideAreas(bool toggle) const
{
// Validate the managed identifier
Validate();
// Perform the requested operation
if (toggle)
{
Core::Get().GetVehicle(m_ID).mFlags |= ENF_AREA_TRACK;
}
else
{
// Obtain the actual entity instance
auto & inst = Core::Get().GetVehicle(m_ID);
// Is this option even enabled?
if (!(inst.mFlags & ENF_AREA_TRACK))
{
return; // Not enabled to begin with
}
// Disable the option
inst.mFlags ^= ENF_AREA_TRACK;
// Clear current areas
inst.mAreas.clear();
}
}
void CVehicle::SetAreasCollide(bool toggle) const
{
// Validate the managed identifier
Validate();
// Perform the requested operation
if (toggle)
{
Core::Get().GetVehicle(m_ID).mFlags |= ENF_AREA_TRACK;
}
else
{
// Obtain the actual entity instance
auto & inst = Core::Get().GetVehicle(m_ID);
// Is this option even enabled?
if (!(inst.mFlags & ENF_AREA_TRACK))
{
return; // Not enabled to begin with
}
// Disable the option
inst.mFlags ^= ENF_AREA_TRACK;
// Is the vehicle currently in any areas?
if (inst.mAreas.empty())
{
return; // Nothing to test
}
Vector3 pos;
// Obtain the current position of this instance
_Func->GetVehiclePosition(m_ID, &pos.x, &pos.y, &pos.z);
// Do a final check to see if the vehicle left any area
for (auto & ap : inst.mAreas)
{
// Is the vehicle still in this area?
if (!ap.first->TestEx(pos.x, pos.y))
{
Core::Get().EmitVehicleLeaveArea(m_ID, ap.second); // Emit the script event
}
}
// Clear current areas
inst.mAreas.clear();
}
}
// ------------------------------------------------------------------------------------------------
SQInteger CVehicle::GetTrackPosition() const
{
@ -1820,6 +1898,7 @@ void Register_CVehicle(HSQUIRRELVM vm)
.Prop(_SC("HorizontalTurretRotation"), &CVehicle::GetHorizontalTurretRotation)
.Prop(_SC("VerTurretRotation"), &CVehicle::GetVerticalTurretRotation)
.Prop(_SC("VerticalTurretRotation"), &CVehicle::GetVerticalTurretRotation)
.Prop(_SC("CollideAreas"), &CVehicle::GetCollideAreas, &CVehicle::SetCollideAreas)
.Prop(_SC("TrackPosition"), &CVehicle::GetTrackPosition, &CVehicle::SetTrackPosition)
.Prop(_SC("TrackRotation"), &CVehicle::GetTrackRotation, &CVehicle::SetTrackRotation)
.Prop(_SC("LastPrimaryColor"), &CVehicle::GetLastPrimaryColor)
@ -1885,6 +1964,7 @@ void Register_CVehicle(HSQUIRRELVM vm)
.Func(_SC("SetHandlingRule"), &CVehicle::SetHandlingRule)
.Func(_SC("ResetHandlingRule"), &CVehicle::ResetHandlingRule)
.Func(_SC("ResetHandlings"), &CVehicle::ResetHandlings)
.Func(_SC("AreasCollide"), &CVehicle::SetAreasCollide)
// Member Overloads
.Overload< void (CVehicle::*)(const Vector3 &, bool) const >
(_SC("SetPos"), &CVehicle::SetPositionEx)

View File

@ -612,6 +612,21 @@ public:
*/
bool Embark(CPlayer & player, Int32 slot, bool allocate, bool warp) const;
/* --------------------------------------------------------------------------------------------
* See whether the managed vehicle entity collides with user defined areas.
*/
bool GetCollideAreas() const;
/* --------------------------------------------------------------------------------------------
* Set whether the managed vehicle entity can collide with user defined areas.
*/
void SetCollideAreas(bool toggle) const;
/* --------------------------------------------------------------------------------------------
* Set whether the managed vehicle entity can collide with user defined areas (with last test).
*/
void SetAreasCollide(bool toggle) const;
/* --------------------------------------------------------------------------------------------
* Retrieve the amount of tracked position changes for the managed vehicle entity.
*/

View File

@ -53,6 +53,7 @@ extern void RegisterTask(HSQUIRRELVM vm);
// ------------------------------------------------------------------------------------------------
extern void Register_Misc(HSQUIRRELVM vm);
extern void Register_Areas(HSQUIRRELVM vm);
extern void Register_Signal(HSQUIRRELVM vm);
// ------------------------------------------------------------------------------------------------
@ -95,6 +96,7 @@ bool RegisterAPI(HSQUIRRELVM vm)
RegisterTask(vm);
Register_Misc(vm);
Register_Areas(vm);
Register_Signal(vm);
return true;

View File

@ -243,6 +243,11 @@ class Core;
class Logger;
class Signal;
// ------------------------------------------------------------------------------------------------
class AreaManager;
struct AreaCell;
struct Area;
// ------------------------------------------------------------------------------------------------
class CmdManager;
class CmdListener;