Skip to content
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

Merged
merged 62 commits into from Jan 28, 2017
Merged

Conversation

Traesh
Copy link
Contributor

@Traesh Traesh commented Oct 3, 2016

Changes proposed:

  • Basic AreaTrigger Entities handlers.
  • Currently handle non-moving, non-scaling AreaTrigger created by spell effect 179
  • handle type SPHERE, BOX, CYLINDER, POLYGON
  • Added C++ script hooks

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:

  • This is the basic system, ToDo won't be fixed in this PR :
  • SAI Events
  • Moving areatriggers (Spline)
  • Scaling areatriggers
  • Find UNK Flags
  • Implement unhandled flags

(Re-Opening PR due to a fail on previous repo, all old reviews have been fixed)


switch (GetTemplate()->GetType())
{
case AREATRIGGER_TYPE_SPHERE:
Copy link

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())
            ^

Copy link

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?

Copy link
Contributor Author

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
Copy link
Contributor

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?

Copy link
Member

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)

@Krudor
Copy link
Contributor

Krudor commented Oct 14, 2016

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!

@Krudor
Copy link
Contributor

Krudor commented Oct 15, 2016

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
Copy link
Member

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

Copy link
Contributor

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

Copy link
Member

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

Copy link
Contributor Author

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

http://pastebin.com/jrzzmsUi

Copy link
Member

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);
Copy link
Member

@Shauren Shauren Oct 16, 2016

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)

@DDuarte DDuarte changed the base branch from 6.x to master October 16, 2016 16:47
//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;
Copy link

@robens robens Oct 18, 2016

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
Copy link
Contributor

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?

Copy link
Contributor Author

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

Copy link
Contributor

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

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Member

@Shauren Shauren left a 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);
Copy link
Member

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
Copy link
Member

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)
Copy link
Member

@Shauren Shauren Oct 20, 2016

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

Copy link
Contributor Author

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"
Copy link
Member

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));
Copy link
Member

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]);
Copy link
Member

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;
Copy link
Member

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);

Copy link
Contributor Author

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 ?

Copy link
Member

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);
Copy link
Member

@Shauren Shauren Oct 20, 2016

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);
Copy link
Member

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)

Copy link
Contributor Author

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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And use GuidUnorderedSet here

@streetrat
Copy link
Contributor

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) ?

@Krudor
Copy link
Contributor

Krudor commented Oct 20, 2016

I maaay have noticed the same behavior as streetrat described, any thoughts about it Traesh?
It's so responsive on retail, I thought they used a client opcode for sending info whenever a player entered a dynamic areatrigger, much like the static ones, but they don't.

@Traesh
Copy link
Contributor Author

Traesh commented Oct 20, 2016

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;
Copy link
Member

@Shauren Shauren Oct 20, 2016

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

@streetrat
Copy link
Contributor

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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no spell cast?

Copy link
Contributor Author

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.

Copy link
Contributor

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

@Shauren
Copy link
Member

Shauren commented Oct 20, 2016

@streetrat every half a second to be exact


bool AreaTrigger::UnitFitToAuraRequirement(Unit* unit, AreaTriggerAuraTypes targetType) const
{
switch (targetType)
Copy link

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 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing default, again :/

Copy link

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:

@ghost
Copy link

ghost commented Oct 20, 2016

I know it do not matter at the moment, but nevertheless:
On Future:

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;
}

@Traesh
Copy link
Contributor Author

Traesh commented Oct 21, 2016

If player coordinates are updated each 500 ms i don't think i'll be able to be make areatriggers more reactive than that.

@streetrat
Copy link
Contributor

well, g3d library can calculate from 2 different xyz coords of it hits an object or not :)
store the the player position on every update, but before storing check if the last pos + current pos's vector hit the trigger, "simple" :D

Traesh and others added 26 commits January 28, 2017 21:44
Make travis happy
@joschiwald joschiwald merged commit 6ab3877 into TrinityCore:master Jan 28, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

10 participants