New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Areatrigger template #18035
Areatrigger template #18035
Conversation
|
||
switch (GetTemplate()->GetType()) | ||
{ | ||
case AREATRIGGER_TYPE_SPHERE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/home/travis/build/TrinityCore/TrinityCore/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp:155:13: fatal error:
enumeration value 'AREATRIGGER_TYPE_NONE' not handled in switch [-Wswitch]
switch (GetTemplate()->GetType())
^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you could add just the 2 lines
case AREATRIGGER_TYPE_NONE:
break;
on top of the existing switches?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
missing default added :)
@@ -662,6 +666,64 @@ class ScriptRegistrySwapHooks<InstanceMapScript, Base> | |||
bool swapped; | |||
}; | |||
|
|||
/// This hook is responsible for swapping SceneScript's |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this belong to this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Its a missed change from previous PR (already merged)
Gonna see if I can make time to test this, in any case it's an awesome PR I've personally been looking forward to! |
Tested with a 292 vertice polygon along with a script and works like a charm, what a joy to see this working, great job! |
SetObjectScale(1); | ||
|
||
SetGuidValue(AREATRIGGER_CASTER, caster->GetGUID()); | ||
SetUInt32Value(AREATRIGGER_SPELLID, spell->Id); | ||
SetUInt32Value(AREATRIGGER_SPELLVISUALID, spellVisual); | ||
SetUInt32Value(AREATRIGGER_DURATION, spell->GetDuration()); | ||
SetUInt32Value(AREATRIGGER_TIME_TO_TARGET_SCALE, spell->GetDuration()); // Todo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the time it takes for the client to play "change scale" animation, you probably should not set it to total duration
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears to be default behavior though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It has no impact on current implementation (because you send final scale before the areatrigger is visible). You would need to modify scale after it is added to world to notice this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is a sniff taked 5 minutes ago on Lei Shen, as you can see by default AREATRIGGER_TIME_TO_TARGET_SCALE should be set to AREATRIGGER_DURATION
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not parse this field with WPP then and store in db?
|
||
Trinity::AnyUnitInObjectRangeCheck check(this, GetTemplate()->MaxSearchRadius); | ||
Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(this, targetList, check); | ||
this->VisitNearbyObject(GetTemplate()->MaxSearchRadius, searcher); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unneeded this-> (in more places, not just here)
//this method uses the ray tracing algorithm to determine if the point is in the polygon | ||
int nPoints = vertices.size(); | ||
int j = -999; | ||
int i = -999; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
any reason to initialize these (i, j) outside of the 'for' scope?
|
||
}; | ||
|
||
class TC_GAME_API AreaTriggerEntityScript : public ScriptObject |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what about using the existing AreaTriggerScript?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bad idea imho, there is no reason to add an "OnUpdate" function in AreaTriggerScript by example. This is two totally different things, even if they have close names
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think he ment inherit AreaTriggerScript
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, as i said, those are two differents things, there is no reason to inherit or use AreaTriggerScript
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that this is a bad idea. Keep it separate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Almost good to go.
@@ -91,4 +91,6 @@ void WorldDatabaseConnection::DoPrepareStatements() | |||
PrepareStatement(WORLD_UPD_CREATURE_ZONE_AREA_DATA, "UPDATE creature SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); | |||
PrepareStatement(WORLD_UPD_GAMEOBJECT_ZONE_AREA_DATA, "UPDATE gameobject SET zoneId = ?, areaId = ? WHERE guid = ?", CONNECTION_ASYNC); | |||
PrepareStatement(WORLD_SEL_GUILD_REWARDS_REQ_ACHIEVEMENTS, "SELECT AchievementRequired FROM guild_rewards_req_achievements WHERE ItemID = ?", CONNECTION_SYNCH); | |||
PrepareStatement(WORLD_SEL_AREATRIGGER_TEMPLATES, "SELECT Id, Flags, MoveCurveId, ScaleCurveId, MorphCurveId, FacingCurveId, Data0, Data1, Data2, Data3, Data4, Data5, ScriptName FROM `areatrigger_template`", CONNECTION_SYNCH); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't use prepared statements for loading templates on startup
SetObjectScale(1); | ||
|
||
SetGuidValue(AREATRIGGER_CASTER, caster->GetGUID()); | ||
SetUInt32Value(AREATRIGGER_SPELLID, spell->Id); | ||
SetUInt32Value(AREATRIGGER_SPELLVISUALID, spellVisual); | ||
SetUInt32Value(AREATRIGGER_DURATION, spell->GetDuration()); | ||
SetUInt32Value(AREATRIGGER_TIME_TO_TARGET_SCALE, spell->GetDuration()); // Todo |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not parse this field with WPP then and store in db?
|
||
void AreaTrigger::HandleUnitEnterExit(std::list<Unit*> newTargetList) | ||
{ | ||
for (Unit* unit : newTargetList) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this would be a better way to handle this:
GuidUnorderedSet exitUnits = _insideUnits;
_insideUnits.clear();
for (Unit* unit : newTargetList)
{
if (exitUnits.erase(unit) == 0) // erase(key_type) returns number of elements erased
{
// handle entering, chat handler and script hook
}
_insideUnits.insert(unit->GetGUID());
}
for (ObjectGuid const& exitUnitGuid : exitUnits)
{
// handle exit, no conditions
}
Making this O(n) average complexity (unordered_set::erase is O(1) in average case) instead of your propsed O(n^2)
And you don't need to check IsAlive() anywhere, AnyUnitInObjectRangeCheck does that so newTargetList
will never contain dead units
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I felt like this was the worst part of my PR, Thanks very much :)
#define TRINITYCORE_AREATRIGGER_TEMPLATE_H | ||
|
||
#include "Object.h" | ||
#include "ObjectAccessor.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't include ObjectAccessor here, you don't need it (do it in .cpp)
} | ||
case AREATRIGGER_TYPE_CYLINDER: | ||
{ | ||
MaxSearchRadius = std::sqrt((CylinderDatas.Radius * CylinderDatas.Radius) + (CylinderDatas.Height * CylinderDatas.Height)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
VisitNearbyObject expects 2D distance for its first argument, also don't turn this into a sphere if it's a cylinder
} | ||
case AREATRIGGER_TYPE_BOX: | ||
{ | ||
MaxSearchRadius = std::max(std::max(BoxDatas.Extents[0], BoxDatas.Extents[1]), BoxDatas.Extents[2]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't take Extents[2] into account here, if the height is large you will make search visit unneccessarily large number of grids.
On top of that the equation should be std::sqrt(BoxDatas.Extents[0] * BoxDatas.Extents[0] / 4 + BoxDatas.Extents[1] * BoxDatas.Extents[1] / 4)
// if (HasScaleCurveID) | ||
// *data << uint32(ScaleCurveID); | ||
if (hasScaleCurveID) | ||
*data << areaTriggerTemplate->ScaleCurveId; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
always explicitly specify the type when appending to packets (ObjectGuid is the only exception)
*data << uint32(areaTriggerTemplate->ScaleCurveId);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May i ask why ? Wouldn't it imply to modify code in 2 location if type change ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And that is a good thing, packet structure should not depend on some local variable having correct type (it also prevents mistakes like using size_t that has different size depending on x86 and x64)
|
||
areaTriggerTemplate.ScriptId = sObjectMgr->GetScriptId(fields[12].GetCString()); | ||
|
||
PreparedStatement* verticesStmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_AREATRIGGER_POLYGON_VERTICES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Select vertices once at the start and cache them in map<areatriggerId, vector<vertice>>
, then just std::move that vector when looping over result from templates
{ | ||
std::list<Unit*> targetList; | ||
|
||
Trinity::AnyUnitInObjectRangeCheck check(this, GetTemplate()->MaxSearchRadius); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will need to extend AnyUnitInObjectRangeCheck constructor to add 3rd argument check3D and pass false to make its inner check IsWithinDistInMap also check only 2D distance here (but it must default to true to keep its current behavior for all other uses in TC)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I updated the box/cylinder/polygon, sphere should check in 3D
std::vector<AreaTriggerPolygonVertice> _polygonVertices; | ||
|
||
AreaTriggerTemplate const* _areaTriggerTemplate; | ||
GuidSet _insideUnits; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And use GuidUnorderedSet here
4ed8bd8
to
5693bb3
Compare
is that normal that you can run thru for example a hunter trap between two update ticks without triggering it if you are fast enough (mounted) ? |
I maaay have noticed the same behavior as streetrat described, any thoughts about it Traesh? |
Weird, AreaTrigger update is called at each update there is no delay. Is there a delay between two player coordinates calculation ? |
@@ -9634,9 +9634,33 @@ void ObjectMgr::LoadAreaTriggerTemplates() | |||
{ | |||
uint32 oldMSTime = getMSTime(); | |||
_areaTriggerTemplateStore.clear(); | |||
std::map< uint32, std::vector<AreaTriggerPolygonVertice> > verticeByAreatrigger; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
dat space padding in template argumets, such stronk c++98
player move hearthbeat is sent from client about once per second if you run straight |
{ | ||
if (Unit* caster = ObjectAccessor::GetUnit(*this, _casterGuid)) | ||
{ | ||
caster->AddAura(aura.AuraId, unit); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no spell cast?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It can be discussed, i choose to use AddAura because caster can be dead, or too far away, etc...
Another solution would be to summon a temp trigger with caster faction, just like gameobjects.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some spells are castable while dead, does the spells have limited range that could case too far away issues?
that gameobject cast is bad, don't look on it
AddAura will do nothing for spells with non apply aura spelleffects
@streetrat every half a second to be exact |
|
||
bool AreaTrigger::UnitFitToAuraRequirement(Unit* unit, AreaTriggerAuraTypes targetType) const | ||
{ | ||
switch (targetType) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/home/travis/build/TrinityCore/TrinityCore/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp:413:13: fatal error:
enumeration value 'AREATRIGGER_AURA_USER_MAX' not handled in switch
[-Wswitch]
switch (targetType)
^
is this caused by another missing default: break;
case switch,
or does it need the actual AREATRIGGER_AURA_USER_MAX
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing default, again :/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I see you have added the default case.
code style suggests that you don't need brackets for 1 single line after default:
I know it do not matter at the moment, but nevertheless: struct
{
uint32 spellId;
uint32 type;
} removeDynamicObjectOrAreatrigger;
case SMART_ACTION_REMOVE_DYNAMIC_OBJECT_OR_AREATRIGGER:
{
if (!me)
break;
if (e.action.removeDynamicObjectOrAreatrigger.type == 0)
{
me->RemoveDynObject(e.action.removeDynamicObjectOrAreatrigger.spellId);
}
else if (e.action.removeDynamicObjectOrAreatrigger.type == 1)
{
me->RemoveAllDynObjects();
}
else if (e.action.removeDynamicObjectOrAreatrigger.type == 2)
{
me->RemoveAreaTrigger(e.action.removeDynamicObjectOrAreatrigger.spellId);
}
else if (e.action.removeDynamicObjectOrAreatrigger.type == 3)
{
me->RemoveAllAreaTriggers();
}
else if (e.action.removeDynamicObjectOrAreatrigger.type == 4)
{
me->RemoveAllDynObjects();
me->RemoveAllAreaTriggers();
}
else
{
TC_LOG_ERROR("sql.sql", "SmartScript: Invalid type for SMART_ACTION_REMOVE_DYNAMIC_OBJECT_OR_AREATRIGGER, skipping");
break;
}
break;
} |
If player coordinates are updated each 500 ms i don't think i'll be able to be make areatriggers more reactive than that. |
well, g3d library can calculate from 2 different xyz coords of it hits an object or not :) |
Make travis happy
…ura, improved spline code
…anent areatriggers
… movement was in progress
009c9ce
to
d3b957c
Compare
Changes proposed:
Target branch(es): 6.x
Issues addressed:
Tests performed: (Does it build, tested in-game, etc)
Builded
Tested ingame with spell 187651 (AreaTrigger 4424), type Cylinder
Tested ingame with spell 163559 (AreaTrigger 2472), type Polygon
Known issues and ToDo list:
SAI Events(Re-Opening PR due to a fail on previous repo, all old reviews have been fixed)