aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dep/PackageList.txt2
-rw-r--r--dep/recastnavigation/Detour/Include/DetourNavMesh.h19
-rw-r--r--dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h54
-rw-r--r--dep/recastnavigation/Detour/Include/DetourNode.h19
-rw-r--r--dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp352
-rw-r--r--dep/recastnavigation/Detour/Source/DetourNode.cpp26
-rw-r--r--dep/recastnavigation/Recast/Include/Recast.h17
-rw-r--r--dep/recastnavigation/Recast/Source/RecastContour.cpp535
-rw-r--r--dep/recastnavigation/Recast/Source/RecastLayers.cpp2
-rw-r--r--dep/recastnavigation/Recast/Source/RecastMesh.cpp86
-rw-r--r--dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp279
-rw-r--r--dep/recastnavigation/Recast/Source/RecastRegion.cpp427
-rw-r--r--dep/recastnavigation/recastnavigation.diff3676
-rw-r--r--sql/base/auth_database.sql28
-rw-r--r--sql/updates/auth/2014_06_23_00_auth_action_ip_logger.sql (renamed from sql/updates/auth/2014_05_02_00_action_ip_logger.sql)3
-rw-r--r--sql/updates/world/2014_06_22_00_world_locales_creature.sql (renamed from sql/updates/world/2014_06_22_00_locales_creature.sql)0
-rw-r--r--sql/updates/world/2014_06_22_01_world_gameobject.sql (renamed from sql/updates/world/2014_06_22_01_gameobject.sql)0
-rw-r--r--sql/updates/world/2014_06_24_05_world_misc.sql68
-rw-r--r--sql/updates/world/2014_06_24_06_world_misc.sql12
-rw-r--r--sql/updates/world/2014_06_24_07_world_spell_proc_event.sql5
-rw-r--r--sql/updates/world/2014_06_24_08_world_misc.sql15
-rw-r--r--sql/updates/world/2014_06_24_09_world_misc.sql99
-rw-r--r--sql/updates/world/2014_06_25_00_world_quest.sql2
-rw-r--r--sql/updates/world/2014_06_25_01_world_misc.sql33
-rw-r--r--sql/updates/world/2014_06_25_02_world_creature.sql6
-rw-r--r--sql/updates/world/2014_06_25_03_world_misc.sql40
-rw-r--r--sql/updates/world/2014_06_25_04_world_misc.sql10
-rw-r--r--sql/updates/world/2014_06_25_05_world_misc.sql17
-rw-r--r--sql/updates/world/2014_06_25_06_world_conditions.sql4
-rw-r--r--sql/updates/world/2014_06_25_07_world_conditions.sql5
-rw-r--r--sql/updates/world/2014_06_25_08_world_conditions.sql8
-rw-r--r--sql/updates/world/2014_06_26_00_world_misc.sql232
-rw-r--r--sql/updates/world/2014_06_26_01_world_misc.sql102
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp2
-rw-r--r--src/server/game/DataStores/DBCEnums.h32
-rw-r--r--src/server/game/DataStores/DBCStores.cpp26
-rw-r--r--src/server/game/DataStores/DBCStores.h5
-rw-r--r--src/server/game/DataStores/DBCStructure.h21
-rw-r--r--src/server/game/DataStores/DBCfmt.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp57
-rw-r--r--src/server/game/Entities/Creature/Creature.h8
-rw-r--r--src/server/game/Entities/Item/ItemPrototype.h2
-rw-r--r--src/server/game/Entities/Player/Player.cpp31
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp37
-rw-r--r--src/server/game/Handlers/LootHandler.cpp9
-rw-r--r--src/server/game/Handlers/SkillHandler.cpp3
-rw-r--r--src/server/game/Loot/LootMgr.h3
-rw-r--r--src/server/game/Server/WorldSession.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp11
-rw-r--r--src/server/game/World/World.cpp2
-rw-r--r--src/server/game/World/World.h1
-rw-r--r--src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp18
-rw-r--r--src/server/worldserver/worldserver.conf.dist8
-rw-r--r--src/tools/mmaps_generator/MapBuilder.cpp32
-rw-r--r--src/tools/mmaps_generator/MapBuilder.h20
55 files changed, 2416 insertions, 4098 deletions
diff --git a/dep/PackageList.txt b/dep/PackageList.txt
index 55dac51cad8..3fa0143a584 100644
--- a/dep/PackageList.txt
+++ b/dep/PackageList.txt
@@ -42,4 +42,4 @@ gSOAP (a portable development toolkit for C and C++ XML Web services and XML dat
recastnavigation (Recast is state of the art navigation mesh construction toolset for games)
https://github.com/memononen/recastnavigation
- Version: 740a7ba51600a3c87ce5667ae276a38284a1ce75
+ Version: 42b96b7306d39bb7680ddb0f89d480a8296c83ff
diff --git a/dep/recastnavigation/Detour/Include/DetourNavMesh.h b/dep/recastnavigation/Detour/Include/DetourNavMesh.h
index cdd473f1aff..782ddbc2edd 100644
--- a/dep/recastnavigation/Detour/Include/DetourNavMesh.h
+++ b/dep/recastnavigation/Detour/Include/DetourNavMesh.h
@@ -119,6 +119,25 @@ enum dtStraightPathOptions
DT_STRAIGHTPATH_ALL_CROSSINGS = 0x02, ///< Add a vertex at every polygon edge crossing.
};
+
+/// Options for dtNavMeshQuery::findPath
+enum dtFindPathOptions
+{
+ DT_FINDPATH_LOW_QUALITY_FAR = 0x01, ///< [provisional] trade quality for performance far from the origin. The idea is that by then a new query will be issued
+ DT_FINDPATH_ANY_ANGLE = 0x02, ///< use raycasts during pathfind to "shortcut" (raycast still consider costs)
+};
+
+/// Options for dtNavMeshQuery::raycast
+enum dtRaycastOptions
+{
+ DT_RAYCAST_USE_COSTS = 0x01, ///< Raycast should calculate movement cost along the ray and fill RaycastHit::cost
+};
+
+
+/// Limit raycasting during any angle pahfinding
+/// The limit is given as a multiple of the character radius
+static const float DT_RAY_CAST_LIMIT_PROPORTIONS = 50.0f;
+
/// Flags representing the type of a navigation mesh polygon.
enum dtPolyTypes
{
diff --git a/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h
index 4a5112c9eb9..c7b360dcdc6 100644
--- a/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h
+++ b/dep/recastnavigation/Detour/Include/DetourNavMeshQuery.h
@@ -41,6 +41,10 @@ class dtQueryFilter
public:
dtQueryFilter();
+#ifdef DT_VIRTUAL_QUERYFILTER
+ virtual ~dtQueryFilter() { }
+#endif
+
/// Returns true if the polygon can be visited. (I.e. Is traversable.)
/// @param[in] ref The reference id of the polygon test.
/// @param[in] tile The tile containing the polygon.
@@ -115,6 +119,34 @@ public:
};
+
+
+/// Provides information about raycast hit
+/// filled by dtNavMeshQuery::raycast
+/// @ingroup detour
+struct dtRaycastHit
+{
+ /// The hit parameter. (FLT_MAX if no wall hit.)
+ float t;
+
+ /// hitNormal The normal of the nearest wall hit. [(x, y, z)]
+ float hitNormal[3];
+
+ /// Pointer to an array of reference ids of the visited polygons. [opt]
+ dtPolyRef* path;
+
+ /// The number of visited polygons. [opt]
+ int pathCount;
+
+ /// The maximum number of polygons the @p path array can hold.
+ int maxPath;
+
+ /// The cost of the path until hit.
+ float pathCost;
+};
+
+
+
/// Provides the ability to perform pathfinding related queries against
/// a navigation mesh.
/// @ingroup detour
@@ -179,10 +211,11 @@ public:
/// @param[in] startPos A position within the start polygon. [(x, y, z)]
/// @param[in] endPos A position within the end polygon. [(x, y, z)]
/// @param[in] filter The polygon filter to apply to the query.
+ /// @param[in] options query options (see: #dtFindPathOptions)
/// @returns The status flags for the query.
dtStatus initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
const float* startPos, const float* endPos,
- const dtQueryFilter* filter);
+ const dtQueryFilter* filter, const unsigned int options = 0);
/// Updates an in-progress sliced path query.
/// @param[in] maxIter The maximum number of iterations to perform.
@@ -308,6 +341,7 @@ public:
/// Casts a 'walkability' ray along the surface of the navigation mesh from
/// the start position toward the end position.
+ /// @note A wrapper around raycast(..., RaycastHit*). Retained for backward compatibility.
/// @param[in] startRef The reference id of the start polygon.
/// @param[in] startPos A position within the start polygon representing
/// the start of the ray. [(x, y, z)]
@@ -323,6 +357,22 @@ public:
const dtQueryFilter* filter,
float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const;
+ /// Casts a 'walkability' ray along the surface of the navigation mesh from
+ /// the start position toward the end position.
+ /// @param[in] startRef The reference id of the start polygon.
+ /// @param[in] startPos A position within the start polygon representing
+ /// the start of the ray. [(x, y, z)]
+ /// @param[in] endPos The position to cast the ray toward. [(x, y, z)]
+ /// @param[in] filter The polygon filter to apply to the query.
+ /// @param[in] flags govern how the raycast behaves. See dtRaycastOptions
+ /// @param[out] hit Pointer to a raycast hit structure which will be filled by the results.
+ /// @param[in] prevRef parent of start ref. Used during for cost calculation [opt]
+ /// @returns The status flags for the query.
+ dtStatus raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter, const unsigned int options,
+ dtRaycastHit* hit, dtPolyRef prevRef = 0) const;
+
+
/// Finds the distance from the specified position to the nearest polygon wall.
/// @param[in] startRef The reference id of the polygon containing @p centerPos.
/// @param[in] centerPos The center of the search circle. [(x, y, z)]
@@ -463,6 +513,8 @@ private:
dtPolyRef startRef, endRef;
float startPos[3], endPos[3];
const dtQueryFilter* filter;
+ unsigned int options;
+ float raycastLimitSqr;
};
dtQueryData m_query; ///< Sliced query state.
diff --git a/dep/recastnavigation/Detour/Include/DetourNode.h b/dep/recastnavigation/Detour/Include/DetourNode.h
index b68c922d038..6fefdc8e0f3 100644
--- a/dep/recastnavigation/Detour/Include/DetourNode.h
+++ b/dep/recastnavigation/Detour/Include/DetourNode.h
@@ -25,6 +25,7 @@ enum dtNodeFlags
{
DT_NODE_OPEN = 0x01,
DT_NODE_CLOSED = 0x02,
+ DT_NODE_PARENT_DETACHED = 0x04, // parent of the node is not adjacent. Found using raycast.
};
typedef unsigned short dtNodeIndex;
@@ -35,12 +36,17 @@ struct dtNode
float pos[3]; ///< Position of the node.
float cost; ///< Cost from previous node to current node.
float total; ///< Cost up to the node.
- unsigned int pidx : 30; ///< Index to parent node.
- unsigned int flags : 2; ///< Node flags 0/open/closed.
+ unsigned int pidx : 24; ///< Index to parent node.
+ unsigned int state : 2; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
+ unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
dtPolyRef id; ///< Polygon ref the node corresponds to.
};
+static const int DT_MAX_STATES_PER_NODE = 4; // number of extra states per node. See dtNode::state
+
+
+
class dtNodePool
{
public:
@@ -48,8 +54,12 @@ public:
~dtNodePool();
inline void operator=(const dtNodePool&) {}
void clear();
- dtNode* getNode(dtPolyRef id);
- dtNode* findNode(dtPolyRef id);
+
+ // Get a dtNode by ref and extra state information. If there is none then - allocate
+ // There can be more than one node for the same polyRef but with different extra state information
+ dtNode* getNode(dtPolyRef id, unsigned char state=0);
+ dtNode* findNode(dtPolyRef id, unsigned char state);
+ unsigned int findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes);
inline unsigned int getNodeIdx(const dtNode* node) const
{
@@ -82,6 +92,7 @@ public:
inline int getHashSize() const { return m_hashSize; }
inline dtNodeIndex getFirst(int bucket) const { return m_first[bucket]; }
inline dtNodeIndex getNext(int i) const { return m_next[i]; }
+ inline int getNodeCount() const { return m_nodeCount; }
private:
diff --git a/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp
index f1709dfd4cf..ec3a2946ea5 100644
--- a/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp
+++ b/dep/recastnavigation/Detour/Source/DetourNavMeshQuery.cpp
@@ -1011,7 +1011,13 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
if (!filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
continue;
- dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ // deal explicitly with crossing tile boundaries
+ unsigned char crossSide = 0;
+ if (bestTile->links[i].side != 0xff)
+ crossSide = bestTile->links[i].side >> 1;
+
+ // get the node
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide);
if (!neighbourNode)
{
status |= DT_OUT_OF_NODES;
@@ -1139,7 +1145,7 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
///
dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef,
const float* startPos, const float* endPos,
- const dtQueryFilter* filter)
+ const dtQueryFilter* filter, const unsigned int options)
{
dtAssert(m_nav);
dtAssert(m_nodePool);
@@ -1153,6 +1159,8 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
dtVcopy(m_query.startPos, startPos);
dtVcopy(m_query.endPos, endPos);
m_query.filter = filter;
+ m_query.options = options;
+ m_query.raycastLimitSqr = FLT_MAX;
if (!startRef || !endRef)
return DT_FAILURE | DT_INVALID_PARAM;
@@ -1161,6 +1169,16 @@ dtStatus dtNavMeshQuery::initSlicedFindPath(dtPolyRef startRef, dtPolyRef endRef
if (!m_nav->isValidPolyRef(startRef) || !m_nav->isValidPolyRef(endRef))
return DT_FAILURE | DT_INVALID_PARAM;
+ // trade quality with performance?
+ if (options & DT_FINDPATH_ANY_ANGLE)
+ {
+ // limiting to several times the character radius yields nice results. It is not sensitive
+ // so it is enough to compute it from the first tile.
+ const dtMeshTile* tile = m_nav->getTileByRef(startRef);
+ float agentRadius = tile->header->walkableRadius;
+ m_query.raycastLimitSqr = dtSqr(agentRadius * DT_RAY_CAST_LIMIT_PROPORTIONS);
+ }
+
if (startRef == endRef)
{
m_query.status = DT_SUCCESS;
@@ -1197,6 +1215,9 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
m_query.status = DT_FAILURE;
return DT_FAILURE;
}
+
+ dtRaycastHit rayHit;
+ rayHit.maxPath = 0;
int iter = 0;
while (iter < maxIter && !m_openList->empty())
@@ -1233,15 +1254,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
return m_query.status;
}
- // Get parent poly and tile.
- dtPolyRef parentRef = 0;
+ // Get parent and grand parent poly and tile.
+ dtPolyRef parentRef = 0, grandpaRef = 0;
const dtMeshTile* parentTile = 0;
const dtPoly* parentPoly = 0;
+ dtNode* parentNode = 0;
if (bestNode->pidx)
- parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
+ {
+ parentNode = m_nodePool->getNodeAtIdx(bestNode->pidx);
+ parentRef = parentNode->id;
+ if (parentNode->pidx)
+ grandpaRef = m_nodePool->getNodeAtIdx(parentNode->pidx)->id;
+ }
if (parentRef)
{
- if (dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly)))
+ bool invalidParent = dtStatusFailed(m_nav->getTileAndPolyByRef(parentRef, &parentTile, &parentPoly));
+ if (invalidParent || (grandpaRef && !m_nav->isValidPolyRef(grandpaRef)) )
{
// The polygon has disappeared during the sliced query, fail.
m_query.status = DT_FAILURE;
@@ -1250,6 +1278,14 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
return m_query.status;
}
}
+
+ // decide whether to test raycast to previous nodes
+ bool tryLOS = false;
+ if (m_query.options & DT_FINDPATH_ANY_ANGLE)
+ {
+ if ((parentRef != 0) && (dtVdistSqr(parentNode->pos, bestNode->pos) < m_query.raycastLimitSqr))
+ tryLOS = true;
+ }
for (unsigned int i = bestPoly->firstLink; i != DT_NULL_LINK; i = bestTile->links[i].next)
{
@@ -1268,13 +1304,22 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
if (!m_query.filter->passFilter(neighbourRef, neighbourTile, neighbourPoly))
continue;
- dtNode* neighbourNode = m_nodePool->getNode(neighbourRef);
+ // deal explicitly with crossing tile boundaries
+ unsigned char crossSide = 0;
+ if (bestTile->links[i].side != 0xff)
+ crossSide = bestTile->links[i].side >> 1;
+
+ dtNode* neighbourNode = m_nodePool->getNode(neighbourRef, crossSide);
if (!neighbourNode)
{
m_query.status |= DT_OUT_OF_NODES;
continue;
}
+ // do not expand to nodes that were already visited from the same parent
+ if (neighbourNode->pidx != 0 && neighbourNode->pidx == bestNode->pidx)
+ continue;
+
// If the node is visited the first time, calculate node position.
if (neighbourNode->flags == 0)
{
@@ -1287,30 +1332,44 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
float cost = 0;
float heuristic = 0;
- // Special case for last node.
- if (neighbourRef == m_query.endRef)
+ // raycast parent
+ bool foundShortCut = false;
+ rayHit.pathCost = rayHit.t = 0;
+ if (tryLOS)
{
- // Cost
+ raycast(parentRef, parentNode->pos, neighbourNode->pos, m_query.filter, DT_RAYCAST_USE_COSTS, &rayHit, grandpaRef);
+ foundShortCut = rayHit.t >= 1.0f;
+ }
+
+ // update move cost
+ if (foundShortCut)
+ {
+ // shortcut found using raycast. Using shorter cost instead
+ cost = parentNode->cost + rayHit.pathCost;
+ }
+ else
+ {
+ // No shortcut found.
const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
parentRef, parentTile, parentPoly,
- bestRef, bestTile, bestPoly,
- neighbourRef, neighbourTile, neighbourPoly);
+ bestRef, bestTile, bestPoly,
+ neighbourRef, neighbourTile, neighbourPoly);
+ cost = bestNode->cost + curCost;
+ }
+
+ // Special case for last node.
+ if (neighbourRef == m_query.endRef)
+ {
const float endCost = m_query.filter->getCost(neighbourNode->pos, m_query.endPos,
bestRef, bestTile, bestPoly,
neighbourRef, neighbourTile, neighbourPoly,
0, 0, 0);
- cost = bestNode->cost + curCost + endCost;
+ cost = cost + endCost;
heuristic = 0;
}
else
{
- // Cost
- const float curCost = m_query.filter->getCost(bestNode->pos, neighbourNode->pos,
- parentRef, parentTile, parentPoly,
- bestRef, bestTile, bestPoly,
- neighbourRef, neighbourTile, neighbourPoly);
- cost = bestNode->cost + curCost;
heuristic = dtVdist(neighbourNode->pos, m_query.endPos)*H_SCALE;
}
@@ -1324,11 +1383,13 @@ dtStatus dtNavMeshQuery::updateSlicedFindPath(const int maxIter, int* doneIters)
continue;
// Add or update the node.
- neighbourNode->pidx = m_nodePool->getNodeIdx(bestNode);
+ neighbourNode->pidx = foundShortCut ? bestNode->pidx : m_nodePool->getNodeIdx(bestNode);
neighbourNode->id = neighbourRef;
- neighbourNode->flags = (neighbourNode->flags & ~DT_NODE_CLOSED);
+ neighbourNode->flags = (neighbourNode->flags & ~(DT_NODE_CLOSED | DT_NODE_PARENT_DETACHED));
neighbourNode->cost = cost;
neighbourNode->total = total;
+ if (foundShortCut)
+ neighbourNode->flags = (neighbourNode->flags | DT_NODE_PARENT_DETACHED);
if (neighbourNode->flags & DT_NODE_OPEN)
{
@@ -1392,11 +1453,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount,
dtNode* prev = 0;
dtNode* node = m_query.lastBestNode;
+ int prevRay = 0;
do
{
dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
node->pidx = m_nodePool->getNodeIdx(prev);
prev = node;
+ int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
+ node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
+ prevRay = nextRay;
node = next;
}
while (node);
@@ -1405,13 +1470,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPath(dtPolyRef* path, int* pathCount,
node = prev;
do
{
- path[n++] = node->id;
- if (n >= maxPath)
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ dtStatus status = 0;
+ if (node->flags & DT_NODE_PARENT_DETACHED)
{
- m_query.status |= DT_BUFFER_TOO_SMALL;
+ float t, normal[3];
+ int m;
+ status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
+ n += m;
+ // raycast ends on poly boundary and the path might include the next poly boundary.
+ if (path[n-1] == next->id)
+ n--; // remove to avoid duplicates
+ }
+ else
+ {
+ path[n++] = node->id;
+ if (n >= maxPath)
+ status = DT_BUFFER_TOO_SMALL;
+ }
+
+ if (status & DT_STATUS_DETAIL_MASK)
+ {
+ m_query.status |= status & DT_STATUS_DETAIL_MASK;
break;
}
- node = m_nodePool->getNodeAtIdx(node->pidx);
+ node = next;
}
while (node);
}
@@ -1457,7 +1540,7 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing
dtNode* node = 0;
for (int i = existingSize-1; i >= 0; --i)
{
- node = m_nodePool->findNode(existing[i]);
+ m_nodePool->findNodes(existing[i], &node, 1);
if (node)
break;
}
@@ -1470,11 +1553,15 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing
}
// Reverse the path.
+ int prevRay = 0;
do
{
dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
node->pidx = m_nodePool->getNodeIdx(prev);
prev = node;
+ int nextRay = node->flags & DT_NODE_PARENT_DETACHED; // keep track of whether parent is not adjacent (i.e. due to raycast shortcut)
+ node->flags = (node->flags & ~DT_NODE_PARENT_DETACHED) | prevRay; // and store it in the reversed path's node
+ prevRay = nextRay;
node = next;
}
while (node);
@@ -1483,13 +1570,31 @@ dtStatus dtNavMeshQuery::finalizeSlicedFindPathPartial(const dtPolyRef* existing
node = prev;
do
{
- path[n++] = node->id;
- if (n >= maxPath)
+ dtNode* next = m_nodePool->getNodeAtIdx(node->pidx);
+ dtStatus status = 0;
+ if (node->flags & DT_NODE_PARENT_DETACHED)
+ {
+ float t, normal[3];
+ int m;
+ status = raycast(node->id, node->pos, next->pos, m_query.filter, &t, normal, path+n, &m, maxPath-n);
+ n += m;
+ // raycast ends on poly boundary and the path might include the next poly boundary.
+ if (path[n-1] == next->id)
+ n--; // remove to avoid duplicates
+ }
+ else
+ {
+ path[n++] = node->id;
+ if (n >= maxPath)
+ status = DT_BUFFER_TOO_SMALL;
+ }
+
+ if (status & DT_STATUS_DETAIL_MASK)
{
- m_query.status |= DT_BUFFER_TOO_SMALL;
+ m_query.status |= status & DT_STATUS_DETAIL_MASK;
break;
}
- node = m_nodePool->getNodeAtIdx(node->pidx);
+ node = next;
}
while (node);
}
@@ -2161,6 +2266,8 @@ dtStatus dtNavMeshQuery::getEdgeMidPoint(dtPolyRef from, const dtPoly* fromPoly,
return DT_SUCCESS;
}
+
+
/// @par
///
/// This method is meant to be used for quick, short distance checks.
@@ -2203,73 +2310,144 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
const dtQueryFilter* filter,
float* t, float* hitNormal, dtPolyRef* path, int* pathCount, const int maxPath) const
{
- dtAssert(m_nav);
+ dtRaycastHit hit;
+ hit.path = path;
+ hit.maxPath = maxPath;
+
+ dtStatus status = raycast(startRef, startPos, endPos, filter, 0, &hit);
- *t = 0;
+ *t = hit.t;
+ if (hitNormal)
+ dtVcopy(hitNormal, hit.hitNormal);
if (pathCount)
- *pathCount = 0;
+ *pathCount = hit.pathCount;
+
+ return status;
+}
+
+
+/// @par
+///
+/// This method is meant to be used for quick, short distance checks.
+///
+/// If the path array is too small to hold the result, it will be filled as
+/// far as possible from the start postion toward the end position.
+///
+/// <b>Using the Hit Parameter t of RaycastHit</b>
+///
+/// If the hit parameter is a very high value (FLT_MAX), then the ray has hit
+/// the end position. In this case the path represents a valid corridor to the
+/// end position and the value of @p hitNormal is undefined.
+///
+/// If the hit parameter is zero, then the start position is on the wall that
+/// was hit and the value of @p hitNormal is undefined.
+///
+/// If 0 < t < 1.0 then the following applies:
+///
+/// @code
+/// distanceToHitBorder = distanceToEndPosition * t
+/// hitPoint = startPos + (endPos - startPos) * t
+/// @endcode
+///
+/// <b>Use Case Restriction</b>
+///
+/// The raycast ignores the y-value of the end position. (2D check.) This
+/// places significant limits on how it can be used. For example:
+///
+/// Consider a scene where there is a main floor with a second floor balcony
+/// that hangs over the main floor. So the first floor mesh extends below the
+/// balcony mesh. The start position is somewhere on the first floor. The end
+/// position is on the balcony.
+///
+/// The raycast will search toward the end position along the first floor mesh.
+/// If it reaches the end position's xz-coordinates it will indicate FLT_MAX
+/// (no wall hit), meaning it reached the end position. This is one example of why
+/// this method is meant for short distance checks.
+///
+dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, const float* endPos,
+ const dtQueryFilter* filter, const unsigned int options,
+ dtRaycastHit* hit, dtPolyRef prevRef) const
+{
+ dtAssert(m_nav);
+ hit->t = 0;
+ hit->pathCount = 0;
+ hit->pathCost = 0;
+
// Validate input
if (!startRef || !m_nav->isValidPolyRef(startRef))
return DT_FAILURE | DT_INVALID_PARAM;
+ if (prevRef && !m_nav->isValidPolyRef(prevRef))
+ return DT_FAILURE | DT_INVALID_PARAM;
- dtPolyRef curRef = startRef;
- float verts[DT_VERTS_PER_POLYGON*3];
+ float dir[3], curPos[3], lastPos[3];
+ float verts[DT_VERTS_PER_POLYGON*3+3];
int n = 0;
-
- hitNormal[0] = 0;
- hitNormal[1] = 0;
- hitNormal[2] = 0;
-
+
+ dtVcopy(curPos, startPos);
+ dtVsub(dir, endPos, startPos);
+ dtVset(hit->hitNormal, 0, 0, 0);
+
dtStatus status = DT_SUCCESS;
-
+
+ const dtMeshTile* prevTile, *tile, *nextTile;
+ const dtPoly* prevPoly, *poly, *nextPoly;
+ dtPolyRef curRef, nextRef;
+
+ // The API input has been checked already, skip checking internal data.
+ nextRef = curRef = startRef;
+ tile = 0;
+ poly = 0;
+ m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
+ nextTile = prevTile = tile;
+ nextPoly = prevPoly = poly;
+ if (prevRef)
+ m_nav->getTileAndPolyByRefUnsafe(prevRef, &prevTile, &prevPoly);
+
while (curRef)
{
// Cast ray against current polygon.
- // The API input has been cheked already, skip checking internal data.
- const dtMeshTile* tile = 0;
- const dtPoly* poly = 0;
- m_nav->getTileAndPolyByRefUnsafe(curRef, &tile, &poly);
-
// Collect vertices.
int nv = 0;
for (int i = 0; i < (int)poly->vertCount; ++i)
{
dtVcopy(&verts[nv*3], &tile->verts[poly->verts[i]*3]);
nv++;
- }
+ }
float tmin, tmax;
int segMin, segMax;
if (!dtIntersectSegmentPoly2D(startPos, endPos, verts, nv, tmin, tmax, segMin, segMax))
{
// Could not hit the polygon, keep the old t and report hit.
- if (pathCount)
- *pathCount = n;
+ hit->pathCount = n;
return status;
}
// Keep track of furthest t so far.
- if (tmax > *t)
- *t = tmax;
+ if (tmax > hit->t)
+ hit->t = tmax;
// Store visited polygons.
- if (n < maxPath)
- path[n++] = curRef;
+ if (n < hit->maxPath)
+ hit->path[n++] = curRef;
else
status |= DT_BUFFER_TOO_SMALL;
-
+
// Ray end is completely inside the polygon.
if (segMax == -1)
{
- *t = FLT_MAX;
- if (pathCount)
- *pathCount = n;
+ hit->t = FLT_MAX;
+ hit->pathCount = n;
+
+ // add the cost
+ if (options & DT_RAYCAST_USE_COSTS)
+ hit->pathCost += filter->getCost(curPos, endPos, prevRef, prevTile, prevPoly, curRef, tile, poly, curRef, tile, poly);
return status;
}
-
+
// Follow neighbours.
- dtPolyRef nextRef = 0;
+ nextRef = 0;
for (unsigned int i = poly->firstLink; i != DT_NULL_LINK; i = tile->links[i].next)
{
@@ -2280,8 +2458,8 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
continue;
// Get pointer to the next polygon.
- const dtMeshTile* nextTile = 0;
- const dtPoly* nextPoly = 0;
+ nextTile = 0;
+ nextPoly = 0;
m_nav->getTileAndPolyByRefUnsafe(link->ref, &nextTile, &nextPoly);
// Skip off-mesh connections.
@@ -2349,6 +2527,24 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
}
}
+ // add the cost
+ if (options & DT_RAYCAST_USE_COSTS)
+ {
+ // compute the intersection point at the furthest end of the polygon
+ // and correct the height (since the raycast moves in 2d)
+ dtVcopy(lastPos, curPos);
+ dtVmad(curPos, startPos, dir, hit->t);
+ float* e1 = &verts[segMax*3];
+ float* e2 = &verts[((segMax+1)%nv)*3];
+ float eDir[3], diff[3];
+ dtVsub(eDir, e2, e1);
+ dtVsub(diff, curPos, e1);
+ float s = dtSqr(eDir[0]) > dtSqr(eDir[2]) ? diff[0] / eDir[0] : diff[2] / eDir[2];
+ curPos[1] = e1[1] + eDir[1] * s;
+
+ hit->pathCost += filter->getCost(lastPos, curPos, prevRef, prevTile, prevPoly, curRef, tile, poly, nextRef, nextTile, nextPoly);
+ }
+
if (!nextRef)
{
// No neighbour, we hit a wall.
@@ -2360,22 +2556,25 @@ dtStatus dtNavMeshQuery::raycast(dtPolyRef startRef, const float* startPos, cons
const float* vb = &verts[b*3];
const float dx = vb[0] - va[0];
const float dz = vb[2] - va[2];
- hitNormal[0] = dz;
- hitNormal[1] = 0;
- hitNormal[2] = -dx;
- dtVnormalize(hitNormal);
+ hit->hitNormal[0] = dz;
+ hit->hitNormal[1] = 0;
+ hit->hitNormal[2] = -dx;
+ dtVnormalize(hit->hitNormal);
- if (pathCount)
- *pathCount = n;
+ hit->pathCount = n;
return status;
}
-
+
// No hit, advance to neighbour polygon.
+ prevRef = curRef;
curRef = nextRef;
+ prevTile = tile;
+ tile = nextTile;
+ prevPoly = poly;
+ poly = nextPoly;
}
- if (pathCount)
- *pathCount = n;
+ hit->pathCount = n;
return status;
}
@@ -3333,6 +3532,15 @@ bool dtNavMeshQuery::isValidPolyRef(dtPolyRef ref, const dtQueryFilter* filter)
bool dtNavMeshQuery::isInClosedList(dtPolyRef ref) const
{
if (!m_nodePool) return false;
- const dtNode* node = m_nodePool->findNode(ref);
- return node && node->flags & DT_NODE_CLOSED;
+
+ dtNode* nodes[DT_MAX_STATES_PER_NODE];
+ int n= m_nodePool->findNodes(ref, nodes, DT_MAX_STATES_PER_NODE);
+
+ for (int i=0; i<n; i++)
+ {
+ if (nodes[i]->flags & DT_NODE_CLOSED)
+ return true;
+ }
+
+ return false;
}
diff --git a/dep/recastnavigation/Detour/Source/DetourNode.cpp b/dep/recastnavigation/Detour/Source/DetourNode.cpp
index 4c8215e20d0..1d1897708f4 100644
--- a/dep/recastnavigation/Detour/Source/DetourNode.cpp
+++ b/dep/recastnavigation/Detour/Source/DetourNode.cpp
@@ -71,27 +71,46 @@ void dtNodePool::clear()
m_nodeCount = 0;
}
-dtNode* dtNodePool::findNode(dtPolyRef id)
+unsigned int dtNodePool::findNodes(dtPolyRef id, dtNode** nodes, const int maxNodes)
{
+ int n = 0;
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
while (i != DT_NULL_IDX)
{
if (m_nodes[i].id == id)
+ {
+ if (n >= maxNodes)
+ return n;
+ nodes[n++] = &m_nodes[i];
+ }
+ i = m_next[i];
+ }
+
+ return n;
+}
+
+dtNode* dtNodePool::findNode(dtPolyRef id, unsigned char state)
+{
+ unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
+ dtNodeIndex i = m_first[bucket];
+ while (i != DT_NULL_IDX)
+ {
+ if (m_nodes[i].id == id && m_nodes[i].state == state)
return &m_nodes[i];
i = m_next[i];
}
return 0;
}
-dtNode* dtNodePool::getNode(dtPolyRef id)
+dtNode* dtNodePool::getNode(dtPolyRef id, unsigned char state)
{
unsigned int bucket = dtHashRef(id) & (m_hashSize-1);
dtNodeIndex i = m_first[bucket];
dtNode* node = 0;
while (i != DT_NULL_IDX)
{
- if (m_nodes[i].id == id)
+ if (m_nodes[i].id == id && m_nodes[i].state == state)
return &m_nodes[i];
i = m_next[i];
}
@@ -108,6 +127,7 @@ dtNode* dtNodePool::getNode(dtPolyRef id)
node->cost = 0;
node->total = 0;
node->id = id;
+ node->state = state;
node->flags = 0;
m_next[i] = m_first[bucket];
diff --git a/dep/recastnavigation/Recast/Include/Recast.h b/dep/recastnavigation/Recast/Include/Recast.h
index 3f4ae96d1c9..66974cdbcc3 100644
--- a/dep/recastnavigation/Recast/Include/Recast.h
+++ b/dep/recastnavigation/Recast/Include/Recast.h
@@ -338,7 +338,7 @@ struct rcHeightfieldLayer
int maxy; ///< The maximum y-bounds of usable data. (Along the z-axis.)
int hmin; ///< The minimum height bounds of usable data. (Along the y-axis.)
int hmax; ///< The maximum height bounds of usable data. (Along the y-axis.)
- unsigned char* heights; ///< The heightfield. [Size: (width - borderSize*2) * (h - borderSize*2)]
+ unsigned char* heights; ///< The heightfield. [Size: width * height]
unsigned char* areas; ///< Area ids. [Size: Same as #heights]
unsigned char* cons; ///< Packed neighbor connection information. [Size: Same as #heights]
};
@@ -969,7 +969,7 @@ void rcMarkCylinderArea(rcContext* ctx, const float* pos,
/// @returns True if the operation completed successfully.
bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
-/// Builds region data for the heightfield using watershed partitioning.
+/// Builds region data for the heightfield using watershed partitioning.
/// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation.
/// @param[in,out] chf A populated compact heightfield.
@@ -983,6 +983,18 @@ bool rcBuildDistanceField(rcContext* ctx, rcCompactHeightfield& chf);
bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
const int borderSize, const int minRegionArea, const int mergeRegionArea);
+/// Builds region data for the heightfield by partitioning the heightfield in non-overlapping layers.
+/// @ingroup recast
+/// @param[in,out] ctx The build context to use during the operation.
+/// @param[in,out] chf A populated compact heightfield.
+/// @param[in] borderSize The size of the non-navigable border around the heightfield.
+/// [Limit: >=0] [Units: vx]
+/// @param[in] minRegionArea The minimum number of cells allowed to form isolated island areas.
+/// [Limit: >=0] [Units: vx].
+/// @returns True if the operation completed successfully.
+bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea);
+
/// Builds region data for the heightfield using simple monotone partitioning.
/// @ingroup recast
/// @param[in,out] ctx The build context to use during the operation.
@@ -997,7 +1009,6 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
const int borderSize, const int minRegionArea, const int mergeRegionArea);
-
/// Sets the neighbor connection data for the specified direction.
/// @param[in] s The span to update.
/// @param[in] dir The direction to set. [Limits: 0 <= value < 4]
diff --git a/dep/recastnavigation/Recast/Source/RecastContour.cpp b/dep/recastnavigation/Recast/Source/RecastContour.cpp
index 5c324bcedfe..8aa9d1d92a1 100644
--- a/dep/recastnavigation/Recast/Source/RecastContour.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastContour.cpp
@@ -20,6 +20,7 @@
#include <math.h>
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
#include "Recast.h"
#include "RecastAlloc.h"
#include "RecastAssert.h"
@@ -36,7 +37,7 @@ static int getCornerHeight(int x, int y, int i, int dir,
unsigned int regs[4] = {0,0,0,0};
// Combine region and area codes in order to prevent
- // border vertices which are in between two areas to be removed.
+ // border vertices which are in between two areas to be removed.
regs[0] = chf.spans[i].reg | (chf.areas[i] << 16);
if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
@@ -187,27 +188,6 @@ static float distancePtSeg(const int x, const int z,
const int px, const int pz,
const int qx, const int qz)
{
-/* float pqx = (float)(qx - px);
- float pqy = (float)(qy - py);
- float pqz = (float)(qz - pz);
- float dx = (float)(x - px);
- float dy = (float)(y - py);
- float dz = (float)(z - pz);
- float d = pqx*pqx + pqy*pqy + pqz*pqz;
- float t = pqx*dx + pqy*dy + pqz*dz;
- if (d > 0)
- t /= d;
- if (t < 0)
- t = 0;
- else if (t > 1)
- t = 1;
-
- dx = px + t*pqx - x;
- dy = py + t*pqy - y;
- dz = pz + t*pqz - z;
-
- return dx*dx + dy*dy + dz*dz;*/
-
float pqx = (float)(qx - px);
float pqz = (float)(qz - pz);
float dx = (float)(x - px);
@@ -257,13 +237,13 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
simplified.push(points[i*4+2]);
simplified.push(i);
}
- }
+ }
}
if (simplified.size() == 0)
{
// If there is no connections at all,
- // create some initial points for the simplification process.
+ // create some initial points for the simplification process.
// Find lower-left and upper-right vertices of the contour.
int llx = points[0];
int lly = points[1];
@@ -311,19 +291,19 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
{
int ii = (i+1) % (simplified.size()/4);
- const int ax = simplified[i*4+0];
- const int az = simplified[i*4+2];
- const int ai = simplified[i*4+3];
-
- const int bx = simplified[ii*4+0];
- const int bz = simplified[ii*4+2];
- const int bi = simplified[ii*4+3];
+ int ax = simplified[i*4+0];
+ int az = simplified[i*4+2];
+ int ai = simplified[i*4+3];
+
+ int bx = simplified[ii*4+0];
+ int bz = simplified[ii*4+2];
+ int bi = simplified[ii*4+3];
// Find maximum deviation from the segment.
float maxd = 0;
int maxi = -1;
int ci, cinc, endi;
-
+
// Traverse the segment in lexilogical order so that the
// max deviation is calculated similarly when traversing
// opposite segments.
@@ -338,6 +318,8 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
cinc = pn-1;
ci = (bi+cinc) % pn;
endi = ai;
+ rcSwap(ax, bx);
+ rcSwap(az, bz);
}
// Tessellate only outer edges or edges between areas.
@@ -397,11 +379,11 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
const int bx = simplified[ii*4+0];
const int bz = simplified[ii*4+2];
const int bi = simplified[ii*4+3];
-
+
// Find maximum deviation from the segment.
int maxi = -1;
int ci = (ai+1) % pn;
-
+
// Tessellate only outer edges or edges between areas.
bool tess = false;
// Wall edges.
@@ -469,32 +451,6 @@ static void simplifyContour(rcIntArray& points, rcIntArray& simplified,
}
-static void removeDegenerateSegments(rcIntArray& simplified)
-{
- // Remove adjacent vertices which are equal on xz-plane,
- // or else the triangulator will get confused.
- for (int i = 0; i < simplified.size()/4; ++i)
- {
- int ni = i+1;
- if (ni >= (simplified.size()/4))
- ni = 0;
-
- if (simplified[i*4+0] == simplified[ni*4+0] &&
- simplified[i*4+2] == simplified[ni*4+2])
- {
- // Degenerate segment, remove.
- for (int j = i; j < simplified.size()/4-1; ++j)
- {
- simplified[j*4+0] = simplified[(j+1)*4+0];
- simplified[j*4+1] = simplified[(j+1)*4+1];
- simplified[j*4+2] = simplified[(j+1)*4+2];
- simplified[j*4+3] = simplified[(j+1)*4+3];
- }
- simplified.resize(simplified.size()-4);
- }
- }
-}
-
static int calcAreaOfPolygon2D(const int* verts, const int nverts)
{
int area = 0;
@@ -507,54 +463,155 @@ static int calcAreaOfPolygon2D(const int* verts, const int nverts)
return (area+1) / 2;
}
-inline bool ileft(const int* a, const int* b, const int* c)
+// TODO: these are the same as in RecastMesh.cpp, consider using the same.
+
+inline int prev(int i, int n) { return i-1 >= 0 ? i-1 : n-1; }
+inline int next(int i, int n) { return i+1 < n ? i+1 : 0; }
+
+inline int area2(const int* a, const int* b, const int* c)
+{
+ return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]);
+}
+
+// Exclusive or: true iff exactly one argument is true.
+// The arguments are negated to ensure that they are 0/1
+// values. Then the bitwise Xor operator may apply.
+// (This idea is due to Michael Baldwin.)
+inline bool xorb(bool x, bool y)
+{
+ return !x ^ !y;
+}
+
+// Returns true iff c is strictly to the left of the directed
+// line through a to b.
+inline bool left(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) < 0;
+}
+
+inline bool leftOn(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) <= 0;
+}
+
+inline bool collinear(const int* a, const int* b, const int* c)
+{
+ return area2(a, b, c) == 0;
+}
+
+// Returns true iff ab properly intersects cd: they share
+// a point interior to both segments. The properness of the
+// intersection is ensured by using strict leftness.
+static bool intersectProp(const int* a, const int* b, const int* c, const int* d)
+{
+ // Eliminate improper cases.
+ if (collinear(a,b,c) || collinear(a,b,d) ||
+ collinear(c,d,a) || collinear(c,d,b))
+ return false;
+
+ return xorb(left(a,b,c), left(a,b,d)) && xorb(left(c,d,a), left(c,d,b));
+}
+
+// Returns T iff (a,b,c) are collinear and point c lies
+// on the closed segement ab.
+static bool between(const int* a, const int* b, const int* c)
+{
+ if (!collinear(a, b, c))
+ return false;
+ // If ab not vertical, check betweenness on x; else on y.
+ if (a[0] != b[0])
+ return ((a[0] <= c[0]) && (c[0] <= b[0])) || ((a[0] >= c[0]) && (c[0] >= b[0]));
+ else
+ return ((a[2] <= c[2]) && (c[2] <= b[2])) || ((a[2] >= c[2]) && (c[2] >= b[2]));
+}
+
+// Returns true iff segments ab and cd intersect, properly or improperly.
+static bool intersect(const int* a, const int* b, const int* c, const int* d)
+{
+ if (intersectProp(a, b, c, d))
+ return true;
+ else if (between(a, b, c) || between(a, b, d) ||
+ between(c, d, a) || between(c, d, b))
+ return true;
+ else
+ return false;
+}
+
+static bool vequal(const int* a, const int* b)
+{
+ return a[0] == b[0] && a[2] == b[2];
+}
+
+static bool intersectSegCountour(const int* d0, const int* d1, int i, int n, const int* verts)
{
- return (b[0] - a[0]) * (c[2] - a[2]) - (c[0] - a[0]) * (b[2] - a[2]) <= 0;
+ // For each edge (k,k+1) of P
+ for (int k = 0; k < n; k++)
+ {
+ int k1 = next(k, n);
+ // Skip edges incident to i.
+ if (i == k || i == k1)
+ continue;
+ const int* p0 = &verts[k * 4];
+ const int* p1 = &verts[k1 * 4];
+ if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+ continue;
+
+ if (intersect(d0, d1, p0, p1))
+ return true;
+ }
+ return false;
}
-static void getClosestIndices(const int* vertsa, const int nvertsa,
- const int* vertsb, const int nvertsb,
- int& ia, int& ib)
+static bool inCone(int i, int n, const int* verts, const int* pj)
{
- int closestDist = 0xfffffff;
- ia = -1, ib = -1;
- for (int i = 0; i < nvertsa; ++i)
+ const int* pi = &verts[i * 4];
+ const int* pi1 = &verts[next(i, n) * 4];
+ const int* pin1 = &verts[prev(i, n) * 4];
+
+ // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+ if (leftOn(pin1, pi, pi1))
+ return left(pi, pj, pin1) && left(pj, pi, pi1);
+ // Assume (i-1,i,i+1) not collinear.
+ // else P[i] is reflex.
+ return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+
+static void removeDegenerateSegments(rcIntArray& simplified)
+{
+ // Remove adjacent vertices which are equal on xz-plane,
+ // or else the triangulator will get confused.
+ int npts = simplified.size()/4;
+ for (int i = 0; i < npts; ++i)
{
- const int in = (i+1) % nvertsa;
- const int ip = (i+nvertsa-1) % nvertsa;
- const int* va = &vertsa[i*4];
- const int* van = &vertsa[in*4];
- const int* vap = &vertsa[ip*4];
+ int ni = next(i, npts);
- for (int j = 0; j < nvertsb; ++j)
+ if (vequal(&simplified[i*4], &simplified[ni*4]))
{
- const int* vb = &vertsb[j*4];
- // vb must be "infront" of va.
- if (ileft(vap,va,vb) && ileft(va,van,vb))
+ // Degenerate segment, remove.
+ for (int j = i; j < simplified.size()/4-1; ++j)
{
- const int dx = vb[0] - va[0];
- const int dz = vb[2] - va[2];
- const int d = dx*dx + dz*dz;
- if (d < closestDist)
- {
- ia = i;
- ib = j;
- closestDist = d;
- }
+ simplified[j*4+0] = simplified[(j+1)*4+0];
+ simplified[j*4+1] = simplified[(j+1)*4+1];
+ simplified[j*4+2] = simplified[(j+1)*4+2];
+ simplified[j*4+3] = simplified[(j+1)*4+3];
}
+ simplified.resize(simplified.size()-4);
+ npts--;
}
}
}
+
static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
{
const int maxVerts = ca.nverts + cb.nverts + 2;
int* verts = (int*)rcAlloc(sizeof(int)*maxVerts*4, RC_ALLOC_PERM);
if (!verts)
return false;
-
+
int nv = 0;
-
+
// Copy contour A.
for (int i = 0; i <= ca.nverts; ++i)
{
@@ -582,7 +639,7 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
rcFree(ca.verts);
ca.verts = verts;
ca.nverts = nv;
-
+
rcFree(cb.verts);
cb.verts = 0;
cb.nverts = 0;
@@ -590,18 +647,179 @@ static bool mergeContours(rcContour& ca, rcContour& cb, int ia, int ib)
return true;
}
+struct rcContourHole
+{
+ rcContour* contour;
+ int minx, minz, leftmost;
+};
+
+struct rcContourRegion
+{
+ rcContour* outline;
+ rcContourHole* holes;
+ int nholes;
+};
+
+struct rcPotentialDiagonal
+{
+ int vert;
+ int dist;
+};
+
+// Finds the lowest leftmost vertex of a contour.
+static void findLeftMostVertex(rcContour* contour, int* minx, int* minz, int* leftmost)
+{
+ *minx = contour->verts[0];
+ *minz = contour->verts[2];
+ *leftmost = 0;
+ for (int i = 1; i < contour->nverts; i++)
+ {
+ const int x = contour->verts[i*4+0];
+ const int z = contour->verts[i*4+2];
+ if (x < *minx || (x == *minx && z < *minz))
+ {
+ *minx = x;
+ *minz = z;
+ *leftmost = i;
+ }
+ }
+}
+
+static int compareHoles(const void* va, const void* vb)
+{
+ const rcContourHole* a = (const rcContourHole*)va;
+ const rcContourHole* b = (const rcContourHole*)vb;
+ if (a->minx == b->minx)
+ {
+ if (a->minz < b->minz)
+ return -1;
+ if (a->minz > b->minz)
+ return 1;
+ }
+ else
+ {
+ if (a->minx < b->minx)
+ return -1;
+ if (a->minx > b->minx)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int compareDiagDist(const void* va, const void* vb)
+{
+ const rcPotentialDiagonal* a = (const rcPotentialDiagonal*)va;
+ const rcPotentialDiagonal* b = (const rcPotentialDiagonal*)vb;
+ if (a->dist < b->dist)
+ return -1;
+ if (a->dist > b->dist)
+ return 1;
+ return 0;
+}
+
+
+static void mergeRegionHoles(rcContext* ctx, rcContourRegion& region)
+{
+ // Sort holes from left to right.
+ for (int i = 0; i < region.nholes; i++)
+ findLeftMostVertex(region.holes[i].contour, &region.holes[i].minx, &region.holes[i].minz, &region.holes[i].leftmost);
+
+ qsort(region.holes, region.nholes, sizeof(rcContourHole), compareHoles);
+
+ int maxVerts = region.outline->nverts;
+ for (int i = 0; i < region.nholes; i++)
+ maxVerts += region.holes[i].contour->nverts;
+
+ rcScopedDelete<rcPotentialDiagonal> diags = (rcPotentialDiagonal*)rcAlloc(sizeof(rcPotentialDiagonal)*maxVerts, RC_ALLOC_TEMP);
+ if (!diags)
+ {
+ ctx->log(RC_LOG_WARNING, "mergeRegionHoles: Failed to allocated diags %d.", maxVerts);
+ return;
+ }
+
+ rcContour* outline = region.outline;
+
+ // Merge holes into the outline one by one.
+ for (int i = 0; i < region.nholes; i++)
+ {
+ rcContour* hole = region.holes[i].contour;
+
+ int index = -1;
+ int bestVertex = region.holes[i].leftmost;
+ for (int iter = 0; iter < hole->nverts; iter++)
+ {
+ // Find potential diagonals.
+ // The 'best' vertex must be in the cone described by 3 cosequtive vertices of the outline.
+ // ..o j-1
+ // |
+ // | * best
+ // |
+ // j o-----o j+1
+ // :
+ int ndiags = 0;
+ const int* corner = &hole->verts[bestVertex*4];
+ for (int j = 0; j < outline->nverts; j++)
+ {
+ if (inCone(j, outline->nverts, outline->verts, corner))
+ {
+ int dx = outline->verts[j*4+0] - corner[0];
+ int dz = outline->verts[j*4+2] - corner[2];
+ diags[ndiags].vert = j;
+ diags[ndiags].dist = dx*dx + dz*dz;
+ ndiags++;
+ }
+ }
+ // Sort potential diagonals by distance, we want to make the connection as short as possible.
+ qsort(diags, ndiags, sizeof(rcPotentialDiagonal), compareDiagDist);
+
+ // Find a diagonal that is not intersecting the outline not the remaining holes.
+ index = -1;
+ for (int j = 0; j < ndiags; j++)
+ {
+ const int* pt = &outline->verts[diags[j].vert*4];
+ bool intersect = intersectSegCountour(pt, corner, diags[i].vert, outline->nverts, outline->verts);
+ for (int k = i; k < region.nholes && !intersect; k++)
+ intersect |= intersectSegCountour(pt, corner, -1, region.holes[k].contour->nverts, region.holes[k].contour->verts);
+ if (!intersect)
+ {
+ index = diags[j].vert;
+ break;
+ }
+ }
+ // If found non-intersecting diagonal, stop looking.
+ if (index != -1)
+ break;
+ // All the potential diagonals for the current vertex were intersecting, try next vertex.
+ bestVertex = (bestVertex + 1) % hole->nverts;
+ }
+
+ if (index == -1)
+ {
+ ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to find merge points for %p and %p.", region.outline, hole);
+ continue;
+ }
+ if (!mergeContours(*region.outline, *hole, index, bestVertex))
+ {
+ ctx->log(RC_LOG_WARNING, "mergeHoles: Failed to merge contours %p and %p.", region.outline, hole);
+ continue;
+ }
+ }
+}
+
+
/// @par
///
/// The raw contours will match the region outlines exactly. The @p maxError and @p maxEdgeLen
/// parameters control how closely the simplified contours will match the raw contours.
///
-/// Simplified contours are generated such that the vertices for portals between areas match up.
+/// Simplified contours are generated such that the vertices for portals between areas match up.
/// (They are considered mandatory vertices.)
///
/// Setting @p maxEdgeLength to zero will disabled the edge length feature.
-///
+///
/// See the #rcConfig documentation for more information on the configuration parameters.
-///
+///
/// @see rcAllocContourSet, rcCompactHeightfield, rcContourSet, rcConfig
bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
const float maxError, const int maxEdgeLen,
@@ -704,17 +922,17 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
verts.resize(0);
simplified.resize(0);
-
+
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
walkContour(x, y, i, chf, flags, verts);
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_TRACE);
-
+
ctx->startTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
simplifyContour(verts, simplified, maxError, maxEdgeLen, buildFlags);
removeDegenerateSegments(simplified);
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS_SIMPLIFY);
-
+
// Store region->contour remap info.
// Create contour.
if (simplified.size()/4 >= 3)
@@ -722,7 +940,7 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
if (cset.nconts >= maxContours)
{
// Allocate more contours.
- // This can happen when there are tiny holes in the heightfield.
+ // This happens when a region has holes.
const int oldMax = maxContours;
maxContours *= 2;
rcContour* newConts = (rcContour*)rcAlloc(sizeof(rcContour)*maxContours, RC_ALLOC_PERM);
@@ -735,10 +953,10 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
}
rcFree(cset.conts);
cset.conts = newConts;
-
+
ctx->log(RC_LOG_WARNING, "rcBuildContours: Expanding max contours from %d to %d.", oldMax, maxContours);
}
-
+
rcContour* cont = &cset.conts[cset.nconts++];
cont->nverts = simplified.size()/4;
@@ -779,17 +997,6 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
}
}
-/* cont->cx = cont->cy = cont->cz = 0;
- for (int i = 0; i < cont->nverts; ++i)
- {
- cont->cx += cont->verts[i*4+0];
- cont->cy += cont->verts[i*4+1];
- cont->cz += cont->verts[i*4+2];
- }
- cont->cx /= cont->nverts;
- cont->cy /= cont->nverts;
- cont->cz /= cont->nverts;*/
-
cont->reg = reg;
cont->area = area;
}
@@ -797,52 +1004,100 @@ bool rcBuildContours(rcContext* ctx, rcCompactHeightfield& chf,
}
}
- // Check and merge droppings.
- // Sometimes the previous algorithms can fail and create several contours
- // per area. This pass will try to merge the holes into the main region.
- for (int i = 0; i < cset.nconts; ++i)
+ // Merge holes if needed.
+ if (cset.nconts > 0)
{
- rcContour& cont = cset.conts[i];
- // Check if the contour is would backwards.
- if (calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0)
+ // Calculate winding of all polygons.
+ rcScopedDelete<char> winding = (char*)rcAlloc(sizeof(char)*cset.nconts, RC_ALLOC_TEMP);
+ if (!winding)
{
- // Find another contour which has the same region ID.
- int mergeIdx = -1;
- for (int j = 0; j < cset.nconts; ++j)
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'hole' (%d).", cset.nconts);
+ return false;
+ }
+ int nholes = 0;
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+ // If the contour is wound backwards, it is a hole.
+ winding[i] = calcAreaOfPolygon2D(cont.verts, cont.nverts) < 0 ? -1 : 1;
+ if (winding[i] < 0)
+ nholes++;
+ }
+
+ if (nholes > 0)
+ {
+ // Collect outline contour and holes contours per region.
+ // We assume that there is one outline and multiple holes.
+ const int nregions = chf.maxRegions+1;
+ rcScopedDelete<rcContourRegion> regions = (rcContourRegion*)rcAlloc(sizeof(rcContourRegion)*nregions, RC_ALLOC_TEMP);
+ if (!regions)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'regions' (%d).", nregions);
+ return false;
+ }
+ memset(regions, 0, sizeof(rcContourRegion)*nregions);
+
+ rcScopedDelete<rcContourHole> holes = (rcContourHole*)rcAlloc(sizeof(rcContourHole)*cset.nconts, RC_ALLOC_TEMP);
+ if (!holes)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Out of memory 'holes' (%d).", cset.nconts);
+ return false;
+ }
+ memset(holes, 0, sizeof(rcContourHole)*cset.nconts);
+
+ for (int i = 0; i < cset.nconts; ++i)
{
- if (i == j) continue;
- if (cset.conts[j].nverts && cset.conts[j].reg == cont.reg)
+ rcContour& cont = cset.conts[i];
+ // Positively would contours are outlines, negative holes.
+ if (winding[i] > 0)
{
- // Make sure the polygon is correctly oriented.
- if (calcAreaOfPolygon2D(cset.conts[j].verts, cset.conts[j].nverts))
- {
- mergeIdx = j;
- break;
- }
+ if (regions[cont.reg].outline)
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Multiple outlines for region %d.", cont.reg);
+ regions[cont.reg].outline = &cont;
+ }
+ else
+ {
+ regions[cont.reg].nholes++;
}
}
- if (mergeIdx == -1)
+ int index = 0;
+ for (int i = 0; i < nregions; i++)
{
- ctx->log(RC_LOG_WARNING, "rcBuildContours: Could not find merge target for bad contour %d.", i);
+ if (regions[i].nholes > 0)
+ {
+ regions[i].holes = &holes[index];
+ index += regions[i].nholes;
+ regions[i].nholes = 0;
+ }
}
- else
+ for (int i = 0; i < cset.nconts; ++i)
+ {
+ rcContour& cont = cset.conts[i];
+ rcContourRegion& reg = regions[cont.reg];
+ if (winding[i] < 0)
+ reg.holes[reg.nholes++].contour = &cont;
+ }
+
+ // Finally merge each regions holes into the outline.
+ for (int i = 0; i < nregions; i++)
{
- rcContour& mcont = cset.conts[mergeIdx];
- // Merge by closest points.
- int ia = 0, ib = 0;
- getClosestIndices(mcont.verts, mcont.nverts, cont.verts, cont.nverts, ia, ib);
- if (ia == -1 || ib == -1)
+ rcContourRegion& reg = regions[i];
+ if (!reg.nholes) continue;
+
+ if (reg.outline)
{
- ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to find merge points for %d and %d.", i, mergeIdx);
- continue;
+ mergeRegionHoles(ctx, reg);
}
- if (!mergeContours(mcont, cont, ia, ib))
+ else
{
- ctx->log(RC_LOG_WARNING, "rcBuildContours: Failed to merge contours %d and %d.", i, mergeIdx);
- continue;
+ // The region does not have an outline.
+ // This can happen if the contour becaomes selfoverlapping because of
+ // too aggressive simplification settings.
+ ctx->log(RC_LOG_ERROR, "rcBuildContours: Bad outline for region %d, contour simplification is likely too aggressive.", i);
}
}
}
+
}
ctx->stopTimer(RC_TIMER_BUILD_CONTOURS);
diff --git a/dep/recastnavigation/Recast/Source/RecastLayers.cpp b/dep/recastnavigation/Recast/Source/RecastLayers.cpp
index 204f72e8cb2..cb1a39f4bda 100644
--- a/dep/recastnavigation/Recast/Source/RecastLayers.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastLayers.cpp
@@ -38,7 +38,7 @@ struct rcLayerRegion
unsigned char layerId; // Layer ID
unsigned char nlayers; // Layer count
unsigned char nneis; // Neighbour count
- unsigned char base; // Flag indicating if the region is hte base of merged regions.
+ unsigned char base; // Flag indicating if the region is the base of merged regions.
};
diff --git a/dep/recastnavigation/Recast/Source/RecastMesh.cpp b/dep/recastnavigation/Recast/Source/RecastMesh.cpp
index 8af609b79fb..e4f9c4b3629 100644
--- a/dep/recastnavigation/Recast/Source/RecastMesh.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastMesh.cpp
@@ -288,6 +288,53 @@ static bool diagonal(int i, int j, int n, const int* verts, int* indices)
return inCone(i, j, n, verts, indices) && diagonalie(i, j, n, verts, indices);
}
+
+static bool diagonalieLoose(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* d0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* d1 = &verts[(indices[j] & 0x0fffffff) * 4];
+
+ // For each edge (k,k+1) of P
+ for (int k = 0; k < n; k++)
+ {
+ int k1 = next(k, n);
+ // Skip edges incident to i or j
+ if (!((k == i) || (k1 == i) || (k == j) || (k1 == j)))
+ {
+ const int* p0 = &verts[(indices[k] & 0x0fffffff) * 4];
+ const int* p1 = &verts[(indices[k1] & 0x0fffffff) * 4];
+
+ if (vequal(d0, p0) || vequal(d1, p0) || vequal(d0, p1) || vequal(d1, p1))
+ continue;
+
+ if (intersectProp(d0, d1, p0, p1))
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool inConeLoose(int i, int j, int n, const int* verts, int* indices)
+{
+ const int* pi = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* pj = &verts[(indices[j] & 0x0fffffff) * 4];
+ const int* pi1 = &verts[(indices[next(i, n)] & 0x0fffffff) * 4];
+ const int* pin1 = &verts[(indices[prev(i, n)] & 0x0fffffff) * 4];
+
+ // If P[i] is a convex vertex [ i+1 left or on (i-1,i) ].
+ if (leftOn(pin1, pi, pi1))
+ return leftOn(pi, pj, pin1) && leftOn(pj, pi, pi1);
+ // Assume (i-1,i,i+1) not collinear.
+ // else P[i] is reflex.
+ return !(leftOn(pi, pj, pi1) && leftOn(pj, pi, pin1));
+}
+
+static bool diagonalLoose(int i, int j, int n, const int* verts, int* indices)
+{
+ return inConeLoose(i, j, n, verts, indices) && diagonalieLoose(i, j, n, verts, indices);
+}
+
+
static int triangulate(int n, const int* verts, int* indices, int* tris)
{
int ntris = 0;
@@ -328,14 +375,41 @@ static int triangulate(int n, const int* verts, int* indices, int* tris)
if (mini == -1)
{
- // Should not happen.
-/* printf("mini == -1 ntris=%d n=%d\n", ntris, n);
+ // We might get here because the contour has overlapping segments, like this:
+ //
+ // A o-o=====o---o B
+ // / |C D| \
+ // o o o o
+ // : : : :
+ // We'll try to recover by loosing up the inCone test a bit so that a diagonal
+ // like A-B or C-D can be found and we can continue.
+ minLen = -1;
+ mini = -1;
for (int i = 0; i < n; i++)
{
- printf("%d ", indices[i] & 0x0fffffff);
+ int i1 = next(i, n);
+ int i2 = next(i1, n);
+ if (diagonalLoose(i, i2, n, verts, indices))
+ {
+ const int* p0 = &verts[(indices[i] & 0x0fffffff) * 4];
+ const int* p2 = &verts[(indices[next(i2, n)] & 0x0fffffff) * 4];
+ int dx = p2[0] - p0[0];
+ int dy = p2[2] - p0[2];
+ int len = dx*dx + dy*dy;
+
+ if (minLen < 0 || len < minLen)
+ {
+ minLen = len;
+ mini = i;
+ }
+ }
+ }
+ if (mini == -1)
+ {
+ // The contour is messed up. This sometimes happens
+ // if the contour simplification is too aggressive.
+ return -ntris;
}
- printf("\n");*/
- return -ntris;
}
int i = mini;
@@ -1463,7 +1537,7 @@ bool rcCopyPolyMesh(rcContext* ctx, const rcPolyMesh& src, rcPolyMesh& dst)
ctx->log(RC_LOG_ERROR, "rcCopyPolyMesh: Out of memory 'dst.flags' (%d).", src.npolys);
return false;
}
- memcpy(dst.flags, src.flags, sizeof(unsigned char)*src.npolys);
+ memcpy(dst.flags, src.flags, sizeof(unsigned short)*src.npolys);
return true;
}
diff --git a/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp
index 8325b883707..5cc2adf0320 100644
--- a/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastMeshDetail.cpp
@@ -56,7 +56,7 @@ inline float vdist2(const float* p, const float* q)
}
inline float vcross2(const float* p1, const float* p2, const float* p3)
-{
+{
const float u1 = p2[0] - p1[0];
const float v1 = p2[2] - p1[2];
const float u2 = p3[0] - p1[0];
@@ -68,21 +68,27 @@ static bool circumCircle(const float* p1, const float* p2, const float* p3,
float* c, float& r)
{
static const float EPS = 1e-6f;
+ // Calculate the circle relative to p1, to avoid some precision issues.
+ const float v1[3] = {0,0,0};
+ float v2[3], v3[3];
+ rcVsub(v2, p2,p1);
+ rcVsub(v3, p3,p1);
- const float cp = vcross2(p1, p2, p3);
+ const float cp = vcross2(v1, v2, v3);
if (fabsf(cp) > EPS)
{
- const float p1Sq = vdot2(p1,p1);
- const float p2Sq = vdot2(p2,p2);
- const float p3Sq = vdot2(p3,p3);
- c[0] = (p1Sq*(p2[2]-p3[2]) + p2Sq*(p3[2]-p1[2]) + p3Sq*(p1[2]-p2[2])) / (2*cp);
- c[2] = (p1Sq*(p3[0]-p2[0]) + p2Sq*(p1[0]-p3[0]) + p3Sq*(p2[0]-p1[0])) / (2*cp);
- r = vdist2(c, p1);
+ const float v1Sq = vdot2(v1,v1);
+ const float v2Sq = vdot2(v2,v2);
+ const float v3Sq = vdot2(v3,v3);
+ c[0] = (v1Sq*(v2[2]-v3[2]) + v2Sq*(v3[2]-v1[2]) + v3Sq*(v1[2]-v2[2])) / (2*cp);
+ c[1] = 0;
+ c[2] = (v1Sq*(v3[0]-v2[0]) + v2Sq*(v1[0]-v3[0]) + v3Sq*(v2[0]-v1[0])) / (2*cp);
+ r = vdist2(c, v1);
+ rcVadd(c, c, p1);
return true;
}
-
- c[0] = p1[0];
- c[2] = p1[2];
+
+ rcVcopy(c, p1);
r = 0;
return false;
}
@@ -93,7 +99,7 @@ static float distPtTri(const float* p, const float* a, const float* b, const flo
rcVsub(v0, c,a);
rcVsub(v1, b,a);
rcVsub(v2, p,a);
-
+
const float dot00 = vdot2(v0, v0);
const float dot01 = vdot2(v0, v1);
const float dot02 = vdot2(v0, v2);
@@ -178,7 +184,7 @@ static float distToTriMesh(const float* p, const float* verts, const int /*nvert
static float distToPoly(int nvert, const float* verts, const float* p)
{
-
+
float dmin = FLT_MAX;
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++)
@@ -216,22 +222,13 @@ static unsigned short getHeight(const float fx, const float fy, const float fz,
if (nx < 0 || nz < 0 || nx >= hp.width || nz >= hp.height) continue;
const unsigned short nh = hp.data[nx+nz*hp.width];
if (nh == RC_UNSET_HEIGHT) continue;
-
+
const float d = fabsf(nh*ch - fy);
if (d < dmin)
{
h = nh;
dmin = d;
}
-
-/* const float dx = (nx+0.5f)*cs - fx;
- const float dz = (nz+0.5f)*cs - fz;
- const float d = dx*dx+dz*dz;
- if (d < dmin)
- {
- h = nh;
- dmin = d;
- } */
}
}
return h;
@@ -263,7 +260,7 @@ static int addEdge(rcContext* ctx, int* edges, int& nedges, const int maxEdges,
return UNDEF;
}
- // Add edge if not already in the triangulation.
+ // Add edge if not already in the triangulation.
int e = findEdge(edges, nedges, s, t);
if (e == UNDEF)
{
@@ -286,7 +283,7 @@ static void updateLeftFace(int* e, int s, int t, int f)
e[2] = f;
else if (e[1] == s && e[0] == t && e[3] == UNDEF)
e[3] = f;
-}
+}
static int overlapSegSeg2d(const float* a, const float* b, const float* c, const float* d)
{
@@ -298,7 +295,7 @@ static int overlapSegSeg2d(const float* a, const float* b, const float* c, const
float a4 = a3 + a2 - a1;
if (a3 * a4 < 0.0f)
return 1;
- }
+ }
return 0;
}
@@ -320,7 +317,7 @@ static bool overlapEdges(const float* pts, const int* edges, int nedges, int s1,
static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges, int& nedges, const int maxEdges, int& nfaces, int e)
{
static const float EPS = 1e-5f;
-
+
int* edge = &edges[e*4];
// Cache s and t.
@@ -337,11 +334,11 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges
}
else
{
- // Edge already completed.
+ // Edge already completed.
return;
}
- // Find best point on left of edge.
+ // Find best point on left of edge.
int pt = npts;
float c[3] = {0,0,0};
float r = -1;
@@ -385,20 +382,20 @@ static void completeFacet(rcContext* ctx, const float* pts, int npts, int* edges
}
}
- // Add new triangle or update edge info if s-t is on hull.
+ // Add new triangle or update edge info if s-t is on hull.
if (pt < npts)
{
- // Update face information of edge being completed.
+ // Update face information of edge being completed.
updateLeftFace(&edges[e*4], s, t, nfaces);
- // Add new edge or update face info of old edge.
+ // Add new edge or update face info of old edge.
e = findEdge(edges, nedges, pt, s);
if (e == UNDEF)
addEdge(ctx, edges, nedges, maxEdges, pt, s, nfaces, UNDEF);
else
updateLeftFace(&edges[e*4], pt, s, nfaces);
- // Add new edge or update face info of old edge.
+ // Add new edge or update face info of old edge.
e = findEdge(edges, nedges, t, pt);
if (e == UNDEF)
addEdge(ctx, edges, nedges, maxEdges, t, pt, nfaces, UNDEF);
@@ -434,7 +431,7 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
completeFacet(ctx, pts, npts, &edges[0], nedges, maxEdges, nfaces, currentEdge);
currentEdge++;
}
-
+
// Create tris
tris.resize(nfaces*4);
for (int i = 0; i < nfaces*4; ++i)
@@ -489,6 +486,103 @@ static void delaunayHull(rcContext* ctx, const int npts, const float* pts,
}
}
+// Calculate minimum extend of the polygon.
+static float polyMinExtent(const float* verts, const int nverts)
+{
+ float minDist = FLT_MAX;
+ for (int i = 0; i < nverts; i++)
+ {
+ const int ni = (i+1) % nverts;
+ const float* p1 = &verts[i*3];
+ const float* p2 = &verts[ni*3];
+ float maxEdgeDist = 0;
+ for (int j = 0; j < nverts; j++)
+ {
+ if (j == i || j == ni) continue;
+ float d = distancePtSeg2d(&verts[j*3], p1,p2);
+ maxEdgeDist = rcMax(maxEdgeDist, d);
+ }
+ minDist = rcMin(minDist, maxEdgeDist);
+ }
+ return rcSqrt(minDist);
+}
+
+inline int next(int i, int n)
+{
+ return (i+1) % n;
+}
+
+inline int prev(int i, int n)
+{
+ return (i + n-1) % n;
+}
+
+static void triangulateHull(const int nverts, const float* verts, const int nhull, const int* hull, rcIntArray& tris)
+{
+ int start = 0, left = 1, right = nhull-1;
+
+ // Start from an ear with shortest perimeter.
+ // This tends to favor well formed triangles as starting point.
+ float dmin = 0;
+ for (int i = 0; i < nhull; i++)
+ {
+ int pi = prev(i, nhull);
+ int ni = next(i, nhull);
+ const float* pv = &verts[hull[pi]*3];
+ const float* cv = &verts[hull[i]*3];
+ const float* nv = &verts[hull[ni]*3];
+ const float d = vdist2(pv,cv) + vdist2(cv,nv) + vdist2(nv,pv);
+ if (d < dmin)
+ {
+ start = i;
+ left = ni;
+ right = pi;
+ dmin = d;
+ }
+ }
+
+ // Add first triangle
+ tris.push(hull[start]);
+ tris.push(hull[left]);
+ tris.push(hull[right]);
+ tris.push(0);
+
+ // Triangulate the polygon by moving left or right,
+ // depending on which triangle has shorter perimeter.
+ // This heuristic was chose emprically, since it seems
+ // handle tesselated straight edges well.
+ while (next(left, nhull) != right)
+ {
+ // Check to see if se should advance left or right.
+ int nleft = next(left, nhull);
+ int nright = prev(right, nhull);
+
+ const float* cvleft = &verts[hull[left]*3];
+ const float* nvleft = &verts[hull[nleft]*3];
+ const float* cvright = &verts[hull[right]*3];
+ const float* nvright = &verts[hull[nright]*3];
+ const float dleft = vdist2(cvleft, nvleft) + vdist2(nvleft, cvright);
+ const float dright = vdist2(cvright, nvright) + vdist2(cvleft, nvright);
+
+ if (dleft < dright)
+ {
+ tris.push(hull[left]);
+ tris.push(hull[nleft]);
+ tris.push(hull[right]);
+ tris.push(0);
+ left = nleft;
+ }
+ else
+ {
+ tris.push(hull[left]);
+ tris.push(hull[nright]);
+ tris.push(hull[right]);
+ tris.push(0);
+ right = nright;
+ }
+ }
+}
+
inline float getJitterX(const int i)
{
@@ -512,16 +606,22 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
float edge[(MAX_VERTS_PER_EDGE+1)*3];
int hull[MAX_VERTS];
int nhull = 0;
-
+
nverts = 0;
-
+
for (int i = 0; i < nin; ++i)
rcVcopy(&verts[i*3], &in[i*3]);
nverts = nin;
+ edges.resize(0);
+ tris.resize(0);
+
const float cs = chf.cs;
const float ics = 1.0f/cs;
+ // Calculate minimum extents of the polygon based on input data.
+ float minExtent = polyMinExtent(verts, nverts);
+
// Tessellate outlines.
// This is done in separate pass in order to ensure
// seamless height values across the ply boundaries.
@@ -628,27 +728,26 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
}
}
-
+ // If the polygon minimum extent is small (sliver or small triangle), do not try to add internal points.
+ if (minExtent < sampleDist*2)
+ {
+ triangulateHull(nverts, verts, nhull, hull, tris);
+ return true;
+ }
+
// Tessellate the base mesh.
- edges.resize(0);
- tris.resize(0);
-
- delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
+ // We're using the triangulateHull instead of delaunayHull as it tends to
+ // create a bit better triangulation for long thing triangles when there
+ // are no internal points.
+ triangulateHull(nverts, verts, nhull, hull, tris);
if (tris.size() == 0)
{
// Could not triangulate the poly, make sure there is some valid data there.
- ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon, adding default data.");
- for (int i = 2; i < nverts; ++i)
- {
- tris.push(0);
- tris.push(i-1);
- tris.push(i);
- tris.push(0);
- }
+ ctx->log(RC_LOG_WARNING, "buildPolyDetail: Could not triangulate polygon (%d verts).", nverts);
return true;
}
-
+
if (sampleDist > 0)
{
// Create sample locations in a grid.
@@ -681,7 +780,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
samples.push(0); // Not added
}
}
-
+
// Add the samples starting from the one that has the most
// error. The procedure stops when all samples are added
// or when the max error is within treshold.
@@ -690,7 +789,7 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
{
if (nverts >= MAX_VERTS)
break;
-
+
// Find sample with most error.
float bestpt[3] = {0,0,0};
float bestd = 0;
@@ -728,24 +827,24 @@ static bool buildPolyDetail(rcContext* ctx, const float* in, const int nin,
edges.resize(0);
tris.resize(0);
delaunayHull(ctx, nverts, verts, nhull, hull, tris, edges);
- }
+ }
}
-
+
const int ntris = tris.size()/4;
if (ntris > MAX_TRIS)
{
tris.resize(MAX_TRIS*4);
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Shrinking triangle count from %d to max %d.", ntris, MAX_TRIS);
}
-
+
return true;
}
static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
- const unsigned short* poly, const int npoly,
- const unsigned short* verts, const int bs,
- rcHeightPatch& hp, rcIntArray& stack)
+ const unsigned short* poly, const int npoly,
+ const unsigned short* verts, const int bs,
+ rcHeightPatch& hp, rcIntArray& stack)
{
// Floodfill the heightfield to get 2D height data,
// starting at vertex locations as seeds.
@@ -849,7 +948,7 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
continue;
const int ai = (int)chf.cells[(ax+bs)+(ay+bs)*chf.width].index + rcGetCon(cs, dir);
-
+
int idx = ax-hp.xmin+(ay-hp.ymin)*hp.width;
hp.data[idx] = 1;
@@ -858,9 +957,9 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
stack.push(ai);
}
}
-
+
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
-
+
// Mark start locations.
for (int i = 0; i < stack.size(); i += 3)
{
@@ -870,8 +969,8 @@ static void getHeightDataSeedsFromVertices(const rcCompactHeightfield& chf,
int idx = cx-hp.xmin+(cy-hp.ymin)*hp.width;
const rcCompactSpan& cs = chf.spans[ci];
hp.data[idx] = cs.y;
-
- // getHeightData seeds are given in coordinates with borders
+
+ // getHeightData seeds are given in coordinates with borders
stack[i+0] += bs;
stack[i+1] += bs;
}
@@ -888,12 +987,12 @@ static void getHeightData(const rcCompactHeightfield& chf,
{
// Note: Reads to the compact heightfield are offset by border size (bs)
// since border size offset is already removed from the polymesh vertices.
-
+
stack.resize(0);
memset(hp.data, 0xff, sizeof(unsigned short)*hp.width*hp.height);
bool empty = true;
-
+
// Copy the height from the same region, and mark region borders
// as seed points to fill the rest.
for (int hy = 0; hy < hp.height; hy++)
@@ -911,7 +1010,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
// Store height
hp.data[hx + hy*hp.width] = s.y;
empty = false;
-
+
// If any of the neighbours is not in same region,
// add the current location as flood fill start
bool border = false;
@@ -940,8 +1039,8 @@ static void getHeightData(const rcCompactHeightfield& chf,
}
}
}
- }
-
+ }
+
// if the polygon does not contian any points from the current region (rare, but happens)
// then use the cells closest to the polygon vertices as seeds to fill the height field
if (empty)
@@ -963,7 +1062,7 @@ static void getHeightData(const rcCompactHeightfield& chf,
memmove(&stack[0], &stack[RETRACT_SIZE*3], sizeof(int)*(stack.size()-RETRACT_SIZE*3));
stack.resize(stack.size()-RETRACT_SIZE*3);
}
-
+
const rcCompactSpan& cs = chf.spans[ci];
for (int dir = 0; dir < 4; ++dir)
{
@@ -982,9 +1081,9 @@ static void getHeightData(const rcCompactHeightfield& chf,
const int ai = (int)chf.cells[ax + ay*chf.width].index + rcGetCon(cs, dir);
const rcCompactSpan& as = chf.spans[ai];
-
+
hp.data[hx + hy*hp.width] = as.y;
-
+
stack.push(ax);
stack.push(ay);
stack.push(ai);
@@ -999,7 +1098,7 @@ static unsigned char getEdgeFlags(const float* va, const float* vb,
static const float thrSqr = rcSqr(0.001f);
for (int i = 0, j = npoly-1; i < npoly; j=i++)
{
- if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr &&
+ if (distancePtSeg2d(va, &vpoly[j*3], &vpoly[i*3]) < thrSqr &&
distancePtSeg2d(vb, &vpoly[j*3], &vpoly[i*3]) < thrSqr)
return 1;
}
@@ -1028,7 +1127,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
rcAssert(ctx);
ctx->startTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
-
+
if (mesh.nverts == 0 || mesh.npolys == 0)
return true;
@@ -1107,10 +1206,10 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.meshes' (%d).", dmesh.nmeshes*4);
return false;
}
-
+
int vcap = nPolyVerts+nPolyVerts/2;
int tcap = vcap*2;
-
+
dmesh.nverts = 0;
dmesh.verts = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
if (!dmesh.verts)
@@ -1119,7 +1218,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
return false;
}
dmesh.ntris = 0;
- dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char*)*tcap*4, RC_ALLOC_PERM);
+ dmesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*tcap*4, RC_ALLOC_PERM);
if (!dmesh.tris)
{
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", tcap*4);
@@ -1158,7 +1257,7 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
{
return false;
}
-
+
// Move detail verts to world space.
for (int j = 0; j < nverts; ++j)
{
@@ -1173,21 +1272,21 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
poly[j*3+1] += orig[1];
poly[j*3+2] += orig[2];
}
-
+
// Store detail submesh.
const int ntris = tris.size()/4;
-
+
dmesh.meshes[i*4+0] = (unsigned int)dmesh.nverts;
dmesh.meshes[i*4+1] = (unsigned int)nverts;
dmesh.meshes[i*4+2] = (unsigned int)dmesh.ntris;
- dmesh.meshes[i*4+3] = (unsigned int)ntris;
+ dmesh.meshes[i*4+3] = (unsigned int)ntris;
// Store vertices, allocate more memory if necessary.
if (dmesh.nverts+nverts > vcap)
{
while (dmesh.nverts+nverts > vcap)
vcap += 256;
-
+
float* newv = (float*)rcAlloc(sizeof(float)*vcap*3, RC_ALLOC_PERM);
if (!newv)
{
@@ -1233,9 +1332,9 @@ bool rcBuildPolyMeshDetail(rcContext* ctx, const rcPolyMesh& mesh, const rcCompa
dmesh.ntris++;
}
}
-
+
ctx->stopTimer(RC_TIMER_BUILD_POLYMESHDETAIL);
-
+
return true;
}
@@ -1245,11 +1344,11 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
rcAssert(ctx);
ctx->startTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
-
+
int maxVerts = 0;
int maxTris = 0;
int maxMeshes = 0;
-
+
for (int i = 0; i < nmeshes; ++i)
{
if (!meshes[i]) continue;
@@ -1257,7 +1356,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
maxTris += meshes[i]->ntris;
maxMeshes += meshes[i]->nmeshes;
}
-
+
mesh.nmeshes = 0;
mesh.meshes = (unsigned int*)rcAlloc(sizeof(unsigned int)*maxMeshes*4, RC_ALLOC_PERM);
if (!mesh.meshes)
@@ -1265,7 +1364,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'pmdtl.meshes' (%d).", maxMeshes*4);
return false;
}
-
+
mesh.ntris = 0;
mesh.tris = (unsigned char*)rcAlloc(sizeof(unsigned char)*maxTris*4, RC_ALLOC_PERM);
if (!mesh.tris)
@@ -1273,7 +1372,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
ctx->log(RC_LOG_ERROR, "rcBuildPolyMeshDetail: Out of memory 'dmesh.tris' (%d).", maxTris*4);
return false;
}
-
+
mesh.nverts = 0;
mesh.verts = (float*)rcAlloc(sizeof(float)*maxVerts*3, RC_ALLOC_PERM);
if (!mesh.verts)
@@ -1297,7 +1396,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
dst[3] = src[3];
mesh.nmeshes++;
}
-
+
for (int k = 0; k < dm->nverts; ++k)
{
rcVcopy(&mesh.verts[mesh.nverts*3], &dm->verts[k*3]);
@@ -1312,7 +1411,7 @@ bool rcMergePolyMeshDetails(rcContext* ctx, rcPolyMeshDetail** meshes, const int
mesh.ntris++;
}
}
-
+
ctx->stopTimer(RC_TIMER_MERGE_POLYMESHDETAIL);
return true;
diff --git a/dep/recastnavigation/Recast/Source/RecastRegion.cpp b/dep/recastnavigation/Recast/Source/RecastRegion.cpp
index 589fac29203..38bc4ff476f 100644
--- a/dep/recastnavigation/Recast/Source/RecastRegion.cpp
+++ b/dep/recastnavigation/Recast/Source/RecastRegion.cpp
@@ -315,6 +315,7 @@ static bool floodRegion(int x, int y, int i,
srcReg[ci] = 0;
continue;
}
+
count++;
// Expand neighbours.
@@ -516,7 +517,11 @@ struct rcRegion
id(i),
areaType(0),
remap(false),
- visited(false)
+ visited(false),
+ overlap(false),
+ connectsToBorder(false),
+ ymin(0xffff),
+ ymax(0)
{}
int spanCount; // Number of spans belonging to this region
@@ -524,6 +529,9 @@ struct rcRegion
unsigned char areaType; // Are type.
bool remap;
bool visited;
+ bool overlap;
+ bool connectsToBorder;
+ unsigned short ymin, ymax;
rcIntArray connections;
rcIntArray floors;
};
@@ -768,10 +776,11 @@ static void walkContour(int x, int y, int i, int dir,
}
}
-static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
- unsigned short& maxRegionId,
- rcCompactHeightfield& chf,
- unsigned short* srcReg)
+
+static bool mergeAndFilterRegions(rcContext* ctx, int minRegionArea, int mergeRegionSize,
+ unsigned short& maxRegionId,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, rcIntArray& overlaps)
{
const int w = chf.width;
const int h = chf.height;
@@ -780,7 +789,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
if (!regions)
{
- ctx->log(RC_LOG_ERROR, "filterSmallRegions: Out of memory 'regions' (%d).", nreg);
+ ctx->log(RC_LOG_ERROR, "mergeAndFilterRegions: Out of memory 'regions' (%d).", nreg);
return false;
}
@@ -803,7 +812,6 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
rcRegion& reg = regions[r];
reg.spanCount++;
-
// Update floors.
for (int j = (int)c.index; j < ni; ++j)
{
@@ -811,6 +819,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
unsigned short floorId = srcReg[j];
if (floorId == 0 || floorId >= nreg)
continue;
+ if (floorId == r)
+ reg.overlap = true;
addUniqueFloorRegion(reg, floorId);
}
@@ -906,7 +916,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
}
}
}
-
+
// Merge too small regions to neighbour regions.
int mergeCount = 0 ;
do
@@ -916,7 +926,9 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
{
rcRegion& reg = regions[i];
if (reg.id == 0 || (reg.id & RC_BORDER_REG))
- continue;
+ continue;
+ if (reg.overlap)
+ continue;
if (reg.spanCount == 0)
continue;
@@ -933,7 +945,7 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
{
if (reg.connections[j] & RC_BORDER_REG) continue;
rcRegion& mreg = regions[reg.connections[j]];
- if (mreg.id == 0 || (mreg.id & RC_BORDER_REG)) continue;
+ if (mreg.id == 0 || (mreg.id & RC_BORDER_REG) || mreg.overlap) continue;
if (mreg.spanCount < smallest &&
canMergeWithRegion(reg, mreg) &&
canMergeWithRegion(mreg, reg))
@@ -1003,6 +1015,224 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
if ((srcReg[i] & RC_BORDER_REG) == 0)
srcReg[i] = regions[srcReg[i]].id;
}
+
+ // Return regions that we found to be overlapping.
+ for (int i = 0; i < nreg; ++i)
+ if (regions[i].overlap)
+ overlaps.push(regions[i].id);
+
+ for (int i = 0; i < nreg; ++i)
+ regions[i].~rcRegion();
+ rcFree(regions);
+
+
+ return true;
+}
+
+
+static void addUniqueConnection(rcRegion& reg, int n)
+{
+ for (int i = 0; i < reg.connections.size(); ++i)
+ if (reg.connections[i] == n)
+ return;
+ reg.connections.push(n);
+}
+
+static bool mergeAndFilterLayerRegions(rcContext* ctx, int minRegionArea,
+ unsigned short& maxRegionId,
+ rcCompactHeightfield& chf,
+ unsigned short* srcReg, rcIntArray& overlaps)
+{
+ const int w = chf.width;
+ const int h = chf.height;
+
+ const int nreg = maxRegionId+1;
+ rcRegion* regions = (rcRegion*)rcAlloc(sizeof(rcRegion)*nreg, RC_ALLOC_TEMP);
+ if (!regions)
+ {
+ ctx->log(RC_LOG_ERROR, "mergeAndFilterLayerRegions: Out of memory 'regions' (%d).", nreg);
+ return false;
+ }
+
+ // Construct regions
+ for (int i = 0; i < nreg; ++i)
+ new(&regions[i]) rcRegion((unsigned short)i);
+
+ // Find region neighbours and overlapping regions.
+ rcIntArray lregs(32);
+ for (int y = 0; y < h; ++y)
+ {
+ for (int x = 0; x < w; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ lregs.resize(0);
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ const unsigned short ri = srcReg[i];
+ if (ri == 0 || ri >= nreg) continue;
+ rcRegion& reg = regions[ri];
+
+ reg.spanCount++;
+
+ reg.ymin = rcMin(reg.ymin, s.y);
+ reg.ymax = rcMax(reg.ymax, s.y);
+
+ // Collect all region layers.
+ lregs.push(ri);
+
+ // Update neighbours
+ for (int dir = 0; dir < 4; ++dir)
+ {
+ if (rcGetCon(s, dir) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(dir);
+ const int ay = y + rcGetDirOffsetY(dir);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, dir);
+ const unsigned short rai = srcReg[ai];
+ if (rai > 0 && rai < nreg && rai != ri)
+ addUniqueConnection(reg, rai);
+ if (rai & RC_BORDER_REG)
+ reg.connectsToBorder = true;
+ }
+ }
+
+ }
+
+ // Update overlapping regions.
+ for (int i = 0; i < lregs.size()-1; ++i)
+ {
+ for (int j = i+1; j < lregs.size(); ++j)
+ {
+ if (lregs[i] != lregs[j])
+ {
+ rcRegion& ri = regions[lregs[i]];
+ rcRegion& rj = regions[lregs[j]];
+ addUniqueFloorRegion(ri, lregs[j]);
+ addUniqueFloorRegion(rj, lregs[i]);
+ }
+ }
+ }
+
+ }
+ }
+
+ // Create 2D layers from regions.
+ unsigned short layerId = 1;
+
+ for (int i = 0; i < nreg; ++i)
+ regions[i].id = 0;
+
+ // Merge montone regions to create non-overlapping areas.
+ rcIntArray stack(32);
+ for (int i = 1; i < nreg; ++i)
+ {
+ rcRegion& root = regions[i];
+ // Skip already visited.
+ if (root.id != 0)
+ continue;
+
+ // Start search.
+ root.id = layerId;
+
+ stack.resize(0);
+ stack.push(i);
+
+ while (stack.size() > 0)
+ {
+ // Pop front
+ rcRegion& reg = regions[stack[0]];
+ for (int j = 0; j < stack.size()-1; ++j)
+ stack[j] = stack[j+1];
+ stack.resize(stack.size()-1);
+
+ const int ncons = (int)reg.connections.size();
+ for (int j = 0; j < ncons; ++j)
+ {
+ const int nei = reg.connections[j];
+ rcRegion& regn = regions[nei];
+ // Skip already visited.
+ if (regn.id != 0)
+ continue;
+ // Skip if the neighbour is overlapping root region.
+ bool overlap = false;
+ for (int k = 0; k < root.floors.size(); k++)
+ {
+ if (root.floors[k] == nei)
+ {
+ overlap = true;
+ break;
+ }
+ }
+ if (overlap)
+ continue;
+
+ // Deepen
+ stack.push(nei);
+
+ // Mark layer id
+ regn.id = layerId;
+ // Merge current layers to root.
+ for (int k = 0; k < regn.floors.size(); ++k)
+ addUniqueFloorRegion(root, regn.floors[k]);
+ root.ymin = rcMin(root.ymin, regn.ymin);
+ root.ymax = rcMax(root.ymax, regn.ymax);
+ root.spanCount += regn.spanCount;
+ regn.spanCount = 0;
+ root.connectsToBorder = root.connectsToBorder || regn.connectsToBorder;
+ }
+ }
+
+ layerId++;
+ }
+
+ // Remove small regions
+ for (int i = 0; i < nreg; ++i)
+ {
+ if (regions[i].spanCount > 0 && regions[i].spanCount < minRegionArea && !regions[i].connectsToBorder)
+ {
+ unsigned short reg = regions[i].id;
+ for (int j = 0; j < nreg; ++j)
+ if (regions[j].id == reg)
+ regions[j].id = 0;
+ }
+ }
+
+ // Compress region Ids.
+ for (int i = 0; i < nreg; ++i)
+ {
+ regions[i].remap = false;
+ if (regions[i].id == 0) continue; // Skip nil regions.
+ if (regions[i].id & RC_BORDER_REG) continue; // Skip external regions.
+ regions[i].remap = true;
+ }
+
+ unsigned short regIdGen = 0;
+ for (int i = 0; i < nreg; ++i)
+ {
+ if (!regions[i].remap)
+ continue;
+ unsigned short oldId = regions[i].id;
+ unsigned short newId = ++regIdGen;
+ for (int j = i; j < nreg; ++j)
+ {
+ if (regions[j].id == oldId)
+ {
+ regions[j].id = newId;
+ regions[j].remap = false;
+ }
+ }
+ }
+ maxRegionId = regIdGen;
+
+ // Remap regions.
+ for (int i = 0; i < chf.spanCount; ++i)
+ {
+ if ((srcReg[i] & RC_BORDER_REG) == 0)
+ srcReg[i] = regions[srcReg[i]].id;
+ }
for (int i = 0; i < nreg; ++i)
regions[i].~rcRegion();
@@ -1011,6 +1241,8 @@ static bool filterSmallRegions(rcContext* ctx, int minRegionArea, int mergeRegio
return true;
}
+
+
/// @par
///
/// This is usually the second to the last step in creating a fully built
@@ -1256,13 +1488,17 @@ bool rcBuildRegionsMonotone(rcContext* ctx, rcCompactHeightfield& chf,
}
}
+
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
- // Filter out small regions.
+ // Merge regions and filter out small regions.
+ rcIntArray overlaps;
chf.maxRegions = id;
- if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps))
return false;
+ // Monotone partitioning does not generate overlapping regions.
+
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
// Store the result out.
@@ -1407,11 +1643,18 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
- // Filter out small regions.
+ // Merge regions and filter out smalle regions.
+ rcIntArray overlaps;
chf.maxRegions = regionId;
- if (!filterSmallRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg))
+ if (!mergeAndFilterRegions(ctx, minRegionArea, mergeRegionArea, chf.maxRegions, chf, srcReg, overlaps))
return false;
-
+
+ // If overlapping regions were found during merging, split those regions.
+ if (overlaps.size() > 0)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegions: %d overlapping regions.", overlaps.size());
+ }
+
ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
// Write the result out.
@@ -1424,3 +1667,157 @@ bool rcBuildRegions(rcContext* ctx, rcCompactHeightfield& chf,
}
+bool rcBuildLayerRegions(rcContext* ctx, rcCompactHeightfield& chf,
+ const int borderSize, const int minRegionArea)
+{
+ rcAssert(ctx);
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS);
+
+ const int w = chf.width;
+ const int h = chf.height;
+ unsigned short id = 1;
+
+ rcScopedDelete<unsigned short> srcReg = (unsigned short*)rcAlloc(sizeof(unsigned short)*chf.spanCount, RC_ALLOC_TEMP);
+ if (!srcReg)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'src' (%d).", chf.spanCount);
+ return false;
+ }
+ memset(srcReg,0,sizeof(unsigned short)*chf.spanCount);
+
+ const int nsweeps = rcMax(chf.width,chf.height);
+ rcScopedDelete<rcSweepSpan> sweeps = (rcSweepSpan*)rcAlloc(sizeof(rcSweepSpan)*nsweeps, RC_ALLOC_TEMP);
+ if (!sweeps)
+ {
+ ctx->log(RC_LOG_ERROR, "rcBuildRegionsMonotone: Out of memory 'sweeps' (%d).", nsweeps);
+ return false;
+ }
+
+
+ // Mark border regions.
+ if (borderSize > 0)
+ {
+ // Make sure border will not overflow.
+ const int bw = rcMin(w, borderSize);
+ const int bh = rcMin(h, borderSize);
+ // Paint regions
+ paintRectRegion(0, bw, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(w-bw, w, 0, h, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, 0, bh, id|RC_BORDER_REG, chf, srcReg); id++;
+ paintRectRegion(0, w, h-bh, h, id|RC_BORDER_REG, chf, srcReg); id++;
+
+ chf.borderSize = borderSize;
+ }
+
+ rcIntArray prev(256);
+
+ // Sweep one line at a time.
+ for (int y = borderSize; y < h-borderSize; ++y)
+ {
+ // Collect spans from this row.
+ prev.resize(id+1);
+ memset(&prev[0],0,sizeof(int)*id);
+ unsigned short rid = 1;
+
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ const rcCompactSpan& s = chf.spans[i];
+ if (chf.areas[i] == RC_NULL_AREA) continue;
+
+ // -x
+ unsigned short previd = 0;
+ if (rcGetCon(s, 0) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(0);
+ const int ay = y + rcGetDirOffsetY(0);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 0);
+ if ((srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ previd = srcReg[ai];
+ }
+
+ if (!previd)
+ {
+ previd = rid++;
+ sweeps[previd].rid = previd;
+ sweeps[previd].ns = 0;
+ sweeps[previd].nei = 0;
+ }
+
+ // -y
+ if (rcGetCon(s,3) != RC_NOT_CONNECTED)
+ {
+ const int ax = x + rcGetDirOffsetX(3);
+ const int ay = y + rcGetDirOffsetY(3);
+ const int ai = (int)chf.cells[ax+ay*w].index + rcGetCon(s, 3);
+ if (srcReg[ai] && (srcReg[ai] & RC_BORDER_REG) == 0 && chf.areas[i] == chf.areas[ai])
+ {
+ unsigned short nr = srcReg[ai];
+ if (!sweeps[previd].nei || sweeps[previd].nei == nr)
+ {
+ sweeps[previd].nei = nr;
+ sweeps[previd].ns++;
+ prev[nr]++;
+ }
+ else
+ {
+ sweeps[previd].nei = RC_NULL_NEI;
+ }
+ }
+ }
+
+ srcReg[i] = previd;
+ }
+ }
+
+ // Create unique ID.
+ for (int i = 1; i < rid; ++i)
+ {
+ if (sweeps[i].nei != RC_NULL_NEI && sweeps[i].nei != 0 &&
+ prev[sweeps[i].nei] == (int)sweeps[i].ns)
+ {
+ sweeps[i].id = sweeps[i].nei;
+ }
+ else
+ {
+ sweeps[i].id = id++;
+ }
+ }
+
+ // Remap IDs
+ for (int x = borderSize; x < w-borderSize; ++x)
+ {
+ const rcCompactCell& c = chf.cells[x+y*w];
+
+ for (int i = (int)c.index, ni = (int)(c.index+c.count); i < ni; ++i)
+ {
+ if (srcReg[i] > 0 && srcReg[i] < rid)
+ srcReg[i] = sweeps[srcReg[i]].id;
+ }
+ }
+ }
+
+
+ ctx->startTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+ // Merge monotone regions to layers and remove small regions.
+ rcIntArray overlaps;
+ chf.maxRegions = id;
+ if (!mergeAndFilterLayerRegions(ctx, minRegionArea, chf.maxRegions, chf, srcReg, overlaps))
+ return false;
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS_FILTER);
+
+
+ // Store the result out.
+ for (int i = 0; i < chf.spanCount; ++i)
+ chf.spans[i].reg = srcReg[i];
+
+ ctx->stopTimer(RC_TIMER_BUILD_REGIONS);
+
+ return true;
+}
diff --git a/dep/recastnavigation/recastnavigation.diff b/dep/recastnavigation/recastnavigation.diff
index 68e976c955e..42bab6474e3 100644
--- a/dep/recastnavigation/recastnavigation.diff
+++ b/dep/recastnavigation/recastnavigation.diff
@@ -1,25 +1,21 @@
- Detour/Include/DetourNavMesh.h | 76 +-
- Detour/Include/DetourNavMeshQuery.h | 0
- Detour/Include/DetourNode.h | 0
- Detour/Source/DetourCommon.cpp | 4 +-
- Detour/Source/DetourNavMesh.cpp | 32 +-
- Detour/Source/DetourNavMeshBuilder.cpp | 6 +-
- Detour/Source/DetourNavMeshQuery.cpp | 9 +-
- Detour/Source/DetourNode.cpp | 29 +-
- DetourCrowd/Include/DetourObstacleAvoidance.h | 0
- DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +-
- Recast/Include/Recast.h | 8 +-
- Recast/Source/RecastContour.cpp | 0
- Recast/Source/RecastLayers.cpp | 0
- Recast/Source/RecastMesh.cpp | 0
- Recast/Source/RecastMeshDetail.cpp | 0
- Recast/Source/RecastRegion.cpp | 0
- RecastDemo/Contrib/stb_truetype.h | 3612 ++++++++++++------------
- RecastDemo/Include/NavMeshPruneTool.h | 0
- 18 files changed, 1865 insertions(+), 1917 deletions(-)
+From b84e9ffbd1d1e1fb2f5d78cc53d2bb7b56c3fce3 Mon Sep 17 00:00:00 2001
+From: jackpoz <giacomopoz@gmail.com>
+Date: Fri, 20 Jun 2014 23:15:04 +0200
+Subject: [PATCH] Add custom trinitycore changes
+
+---
+ Detour/Include/DetourNavMesh.h | 76 ++++++++------------------
+ Detour/Source/DetourCommon.cpp | 4 +-
+ Detour/Source/DetourNavMesh.cpp | 32 ++++-------
+ Detour/Source/DetourNavMeshBuilder.cpp | 6 +-
+ Detour/Source/DetourNavMeshQuery.cpp | 9 +--
+ Detour/Source/DetourNode.cpp | 29 +++-------
+ DetourCrowd/Source/DetourObstacleAvoidance.cpp | 6 +-
+ Recast/Include/Recast.h | 8 +--
+ 8 files changed, 59 insertions(+), 111 deletions(-)
diff --git a/Detour/Include/DetourNavMesh.h b/Detour/Include/DetourNavMesh.h
-index 95a63e4..cdd473f 100644
+index 1060845..782ddbc 100644
--- a/Detour/Include/DetourNavMesh.h
+++ b/Detour/Include/DetourNavMesh.h
@@ -22,39 +22,35 @@
@@ -93,7 +89,7 @@ index 95a63e4..cdd473f 100644
/// Tile flags used for various functions and fields.
/// For an example, see dtNavMesh::addTile().
enum dtTileFlags
-@@ -492,11 +494,7 @@ public:
+@@ -511,11 +513,7 @@ public:
/// @param[in] ip The index of the polygon within the tile.
inline dtPolyRef encodePolyId(unsigned int salt, unsigned int it, unsigned int ip) const
{
@@ -105,7 +101,7 @@ index 95a63e4..cdd473f 100644
}
/// Decodes a standard polygon reference.
-@@ -508,21 +506,12 @@ public:
+@@ -527,21 +525,12 @@ public:
/// @see #encodePolyId
inline void decodePolyId(dtPolyRef ref, unsigned int& salt, unsigned int& it, unsigned int& ip) const
{
@@ -127,7 +123,7 @@ index 95a63e4..cdd473f 100644
}
/// Extracts a tile's salt value from the specified polygon reference.
-@@ -531,13 +520,8 @@ public:
+@@ -550,13 +539,8 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdSalt(dtPolyRef ref) const
{
@@ -141,7 +137,7 @@ index 95a63e4..cdd473f 100644
}
/// Extracts the tile's index from the specified polygon reference.
-@@ -546,13 +530,8 @@ public:
+@@ -565,13 +549,8 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdTile(dtPolyRef ref) const
{
@@ -155,7 +151,7 @@ index 95a63e4..cdd473f 100644
}
/// Extracts the polygon's index (within its tile) from the specified polygon reference.
-@@ -561,13 +540,8 @@ public:
+@@ -580,13 +559,8 @@ public:
/// @see #encodePolyId
inline unsigned int decodePolyIdPoly(dtPolyRef ref) const
{
@@ -169,7 +165,7 @@ index 95a63e4..cdd473f 100644
}
/// @}
-@@ -626,11 +600,9 @@ private:
+@@ -645,11 +619,9 @@ private:
dtMeshTile* m_nextFree; ///< Freelist of tiles.
dtMeshTile* m_tiles; ///< List of tiles.
@@ -305,7 +301,7 @@ index 1bf271b..9d8471b 100644
int curNode = 0;
diff --git a/Detour/Source/DetourNavMeshQuery.cpp b/Detour/Source/DetourNavMeshQuery.cpp
-index 2e30464..f1709df 100644
+index 9debb4d..ec3a294 100644
--- a/Detour/Source/DetourNavMeshQuery.cpp
+++ b/Detour/Source/DetourNavMeshQuery.cpp
@@ -16,13 +16,13 @@
@@ -335,7 +331,7 @@ index 2e30464..f1709df 100644
dtNavMeshQuery* dtAllocNavMeshQuery()
-@@ -3305,7 +3306,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
+@@ -3504,7 +3505,7 @@ dtStatus dtNavMeshQuery::findDistanceToWall(dtPolyRef startRef, const float* cen
dtVsub(hitNormal, centerPos, hitPos);
dtVnormalize(hitNormal);
@@ -345,7 +341,7 @@ index 2e30464..f1709df 100644
return status;
}
diff --git a/Detour/Source/DetourNode.cpp b/Detour/Source/DetourNode.cpp
-index 57cb206..4c8215e 100644
+index 5cf6548..1d18977 100644
--- a/Detour/Source/DetourNode.cpp
+++ b/Detour/Source/DetourNode.cpp
@@ -22,30 +22,17 @@
@@ -422,7 +418,7 @@ index 0fad9ef..d3f90b7 100644
// Always add sample at zero
pat[npat*2+0] = 0;
diff --git a/Recast/Include/Recast.h b/Recast/Include/Recast.h
-index 336837e..3f4ae96 100644
+index 83ca606..66974cd 100644
--- a/Recast/Include/Recast.h
+++ b/Recast/Include/Recast.h
@@ -243,7 +243,7 @@ struct rcConfig
@@ -447,3620 +443,6 @@ index 336837e..3f4ae96 100644
rcSpan* next; ///< The next span higher up in column.
};
-diff --git a/RecastDemo/Contrib/stb_truetype.h b/RecastDemo/Contrib/stb_truetype.h
-index fd72578..92dc8c2 100644
---- a/RecastDemo/Contrib/stb_truetype.h
-+++ b/RecastDemo/Contrib/stb_truetype.h
-@@ -1,1806 +1,1806 @@
--// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools
--//
--// This library processes TrueType files:
--// parse files
--// extract glyph metrics
--// extract glyph shapes
--// render glyphs to one-channel bitmaps with antialiasing (box filter)
--//
--// Todo:
--// non-MS cmaps
--// crashproof on bad data
--// hinting
--// subpixel positioning when rendering bitmap
--// cleartype-style AA
--//
--// ADDITIONAL CONTRIBUTORS
--//
--// Mikko Mononen: compound shape support, more cmap formats
--//
--// VERSIONS
--//
--// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
--// userdata, malloc-from-userdata, non-zero fill (STB)
--// 0.2 (2009-03-11) Fix unsigned/signed char warnings
--// 0.1 (2009-03-09) First public release
--//
--// USAGE
--//
--// Include this file in whatever places neeed to refer to it. In ONE C/C++
--// file, write:
--// #define STB_TRUETYPE_IMPLEMENTATION
--// before the #include of this file. This expands out the actual
--// implementation into that C/C++ file.
--//
--// Look at the header-file sections below for the API, but here's a quick skim:
--//
--// Simple 3D API (don't ship this, but it's fine for tools and quick start,
--// and you can cut and paste from it to move to more advanced)
--// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
--// stbtt_GetBakedQuad() -- compute quad to draw for a given char
--//
--// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
--// stbtt_InitFont()
--// stbtt_GetFontOffsetForIndex() -- use for TTC font collections
--//
--// Render a unicode codepoint to a bitmap
--// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
--// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
--// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
--//
--// Character advance/positioning
--// stbtt_GetCodepointHMetrics()
--// stbtt_GetFontVMetrics()
--//
--// NOTES
--//
--// The system uses the raw data found in the .ttf file without changing it
--// and without building auxiliary data structures. This is a bit inefficient
--// on little-endian systems (the data is big-endian), but assuming you're
--// caching the bitmaps or glyph shapes this shouldn't be a big deal.
--//
--// It appears to be very hard to programmatically determine what font a
--// given file is in a general way. I provide an API for this, but I don't
--// recommend it.
--//
--//
--// SOURCE STATISTICS (based on v0.3, 1800 LOC)
--//
--// Documentation & header file 350 LOC \___ 500 LOC documentation
--// Sample code 140 LOC /
--// Truetype parsing 580 LOC ---- 600 LOC TrueType
--// Software rasterization 240 LOC \ .
--// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation
--// Bitmap management 70 LOC /
--// Baked bitmap interface 70 LOC /
--// Font name matching & access 150 LOC ---- 150
--// C runtime library abstraction 60 LOC ---- 60
--
--
--//////////////////////////////////////////////////////////////////////////////
--//////////////////////////////////////////////////////////////////////////////
--////
--//// SAMPLE PROGRAMS
--////
--//
--// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
--//
--#if 0
--#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
--#include "stb_truetype.h"
--
--char ttf_buffer[1<<20];
--unsigned char temp_bitmap[512*512];
--
--stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs
--GLstbtt_uint ftex;
--
--void my_stbtt_initfont(void)
--{
-- fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
-- stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
-- // can free ttf_buffer at this point
-- glGenTextures(1, &ftex);
-- glBindTexture(GL_TEXTURE_2D, ftex);
-- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
-- // can free temp_bitmap at this point
-- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
--}
--
--void my_stbtt_print(float x, float y, char *text)
--{
-- // assume orthographic projection with units = screen pixels, origin at top left
-- glBindTexture(GL_TEXTURE_2D, ftex);
-- glBegin(GL_QUADS);
-- while (*text) {
-- if (*text >= 32 && *text < 128) {
-- stbtt_aligned_quad q;
-- stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d
-- glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
-- glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
-- glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
-- glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
-- }
-- ++text;
-- }
-- glEnd();
--}
--#endif
--//
--//
--//////////////////////////////////////////////////////////////////////////////
--//
--// Complete program (this compiles): get a single bitmap, print as ASCII art
--//
--#if 0
--#include <stdio.h>
--#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
--#include "stb_truetype.h"
--
--char ttf_buffer[1<<25];
--
--int main(int argc, char **argv)
--{
-- stbtt_fontinfo font;
-- unsigned char *bitmap;
-- int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
--
-- fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
--
-- stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
-- bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
--
-- for (j=0; j < h; ++j) {
-- for (i=0; i < w; ++i)
-- putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
-- putchar('\n');
-- }
-- return 0;
--}
--#endif
--//
--// Output:
--//
--// .ii.
--// @@@@@@.
--// V@Mio@@o
--// :i. V@V
--// :oM@@M
--// :@@@MM@M
--// @@o o@M
--// :@@. M@M
--// @@@o@@@@
--// :M@@V:@@.
--//
--//////////////////////////////////////////////////////////////////////////////
--//
--// Complete program: print "Hello World!" banner, with bugs
--//
--#if 0
--int main(int arg, char **argv)
--{
-- unsigned char screen[20][79];
-- int i,j, pos=0;
-- float scale;
-- char *text = "Heljo World!";
--
-- fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
-- stbtt_InitFont(&font, buffer, 0);
--
-- scale = stbtt_ScaleForPixelHeight(&font, 16);
-- memset(screen, 0, sizeof(screen));
--
-- while (*text) {
-- int advance,lsb,x0,y0,x1,y1, newpos, baseline=13;
-- stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb);
-- stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1);
-- newpos = pos + (int) (lsb * scale) + x0;
-- stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text);
-- // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
-- // because this API is really for baking character bitmaps into textures
-- pos += (int) (advance * scale);
-- ++text;
-- }
--
-- for (j=0; j < 20; ++j) {
-- for (i=0; i < 79; ++i)
-- putchar(" .:ioVM@"[screen[j][i]>>5]);
-- putchar('\n');
-- }
--
-- return 0;
--}
--#endif
--
--
--//////////////////////////////////////////////////////////////////////////////
--//////////////////////////////////////////////////////////////////////////////
--////
--//// INTEGRATION WITH RUNTIME LIBRARIES
--////
--
--#ifdef STB_TRUETYPE_IMPLEMENTATION
-- // #define your own (u)stbtt_int8/16/32 before including to override this
-- #ifndef stbtt_uint8
-- typedef unsigned char stbtt_uint8;
-- typedef signed char stbtt_int8;
-- typedef unsigned short stbtt_uint16;
-- typedef signed short stbtt_int16;
-- typedef unsigned int stbtt_uint32;
-- typedef signed int stbtt_int32;
-- #endif
--
-- typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
-- typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
--
-- // #define your own STBTT_sort() to override this to avoid qsort
-- #ifndef STBTT_sort
-- #include <stdlib.h>
-- #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func)
-- #endif
--
-- // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
-- #ifndef STBTT_ifloor
-- #include <math.h>
-- #define STBTT_ifloor(x) ((int) floor(x))
-- #define STBTT_iceil(x) ((int) ceil(x))
-- #endif
--
-- // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
-- #ifndef STBTT_malloc
-- #include <malloc.h>
-- #define STBTT_malloc(x,u) malloc(x)
-- #define STBTT_free(x,u) free(x)
-- #endif
--
-- #ifndef STBTT_assert
-- #include <assert.h>
-- #define STBTT_assert(x) assert(x)
-- #endif
--
-- #ifndef STBTT_strlen
-- #include <string.h>
-- #define STBTT_strlen(x) strlen(x)
-- #endif
--
-- #ifndef STBTT_memcpy
-- #include <memory.h>
-- #define STBTT_memcpy memcpy
-- #define STBTT_memset memset
-- #endif
--#endif
--
--///////////////////////////////////////////////////////////////////////////////
--///////////////////////////////////////////////////////////////////////////////
--////
--//// INTERFACE
--////
--////
--
--#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
--#define __STB_INCLUDE_STB_TRUETYPE_H__
--
--#ifdef __cplusplus
--extern "C" {
--#endif
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// TEXTURE BAKING API
--//
--// If you use this API, you only have to call two functions ever.
--//
--
--typedef struct
--{
-- unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
-- float xoff,yoff,xadvance;
--} stbtt_bakedchar;
--
--extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-- float pixel_height, // height of font in pixels
-- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-- int first_char, int num_chars, // characters to bake
-- stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
--// if return is positive, the first unused row of the bitmap
--// if return is negative, returns the negative of the number of characters that fit
--// if return is 0, no characters fit and no rows were used
--// This uses a very crappy packing.
--
--typedef struct
--{
-- float x0,y0,s0,t0; // top-left
-- float x1,y1,s1,t1; // bottom-right
--} stbtt_aligned_quad;
--
--extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
-- int char_index, // character to display
-- float *xpos, float *ypos, // pointers to current position in screen pixel space
-- stbtt_aligned_quad *q, // output: quad to draw
-- int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
--// Call GetBakedQuad with char_index = 'character - first_char', and it
--// creates the quad you need to draw and advances the current position.
--// It's inefficient; you might want to c&p it and optimize it.
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// FONT LOADING
--//
--//
--
--extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
--// Each .ttf file may have more than one font. Each has a sequential index
--// number starting from 0. Call this function to get the font offset for a
--// given index; it returns -1 if the index is out of range. A regular .ttf
--// file will only define one font and it always be at offset 0, so it will
--// return '0' for index 0, and -1 for all other indices. You can just skip
--// this step if you know it's that kind of font.
--
--
--// The following structure is defined publically so you can declare one on
--// the stack or as a global or etc.
--typedef struct
--{
-- void *userdata;
-- unsigned char *data; // pointer to .ttf file
-- int fontstart; // offset of start of font
--
-- int numGlyphs; // number of glyphs, needed for range checking
--
-- int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf
-- int index_map; // a cmap mapping for our chosen character encoding
-- int indexToLocFormat; // format needed to map from glyph index to glyph
--} stbtt_fontinfo;
--
--extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
--// Given an offset into the file that defines a font, this function builds
--// the necessary cached info for the rest of the system. You must allocate
--// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
--// need to do anything special to free it, because the contents are a pure
--// cache with no additional data structures. Returns 0 on failure.
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// CHARACTER TO GLYPH-INDEX CONVERSIOn
--
--int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
--// If you're going to perform multiple operations on the same character
--// and you want a speed-up, call this function with the character you're
--// going to process, then use glyph-based functions instead of the
--// codepoint-based functions.
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// CHARACTER PROPERTIES
--//
--
--extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
--// computes a scale factor to produce a font whose "height" is 'pixels' tall.
--// Height is measured as the distance from the highest ascender to the lowest
--// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
--// and computing:
--// scale = pixels / (ascent - descent)
--// so if you prefer to measure height by the ascent only, use a similar calculation.
--
--extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
--// ascent is the coordinate above the baseline the font extends; descent
--// is the coordinate below the baseline the font extends (i.e. it is typically negative)
--// lineGap is the spacing between one row's descent and the next row's ascent...
--// so you should advance the vertical position by "*ascent - *descent + *lineGap"
--// these are expressed in unscaled coordinates
--
--extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
--// leftSideBearing is the offset from the current horizontal position to the left edge of the character
--// advanceWidth is the offset from the current horizontal position to the next horizontal position
--// these are expressed in unscaled coordinates
--
--extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
--// an additional amount to add to the 'advance' value between ch1 and ch2
--// @TODO; for now always returns 0!
--
--extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
--// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
--
--extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
--extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
--extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
--// as above, but takes one or more glyph indices for greater efficiency
--
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// GLYPH SHAPES (you probably don't need these, but they have to go before
--// the bitmaps for C declaration-order reasons)
--//
--
--#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
-- enum {
-- STBTT_vmove=1,
-- STBTT_vline,
-- STBTT_vcurve
-- };
--#endif
--
--#ifndef stbtt_vertex // you can predefine this to use different values
-- // (we share this with other code at RAD)
-- #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
-- typedef struct
-- {
-- stbtt_vertex_type x,y,cx,cy;
-- unsigned char type,padding;
-- } stbtt_vertex;
--#endif
--
--extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
--extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
--// returns # of vertices and fills *vertices with the pointer to them
--// these are expressed in "unscaled" coordinates
--
--extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
--// frees the data allocated above
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// BITMAP RENDERING
--//
--
--extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
--// frees the bitmap allocated below
--
--extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
--// allocates a large-enough single-channel 8bpp bitmap and renders the
--// specified character/glyph at the specified scale into it, with
--// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
--// *width & *height are filled out with the width & height of the bitmap,
--// which is stored left-to-right, top-to-bottom.
--//
--// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
--
--extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
--// the same as above, but you pass in storage for the bitmap in the form
--// of 'output', with row spacing of 'out_stride' bytes. the bitmap is
--// clipped to out_w/out_h bytes. call the next function to get the
--// height and width and positioning info
--
--extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
--// get the bbox of the bitmap centered around the glyph origin; so the
--// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
--// the bitmap top left is (leftSideBearing*scale,iy0).
--// (Note that the bitmap uses y-increases-down, but the shape uses
--// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
--
--extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
--extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
--extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
--
--//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
--
--// @TODO: don't expose this structure
--typedef struct
--{
-- int w,h,stride;
-- unsigned char *pixels;
--} stbtt__bitmap;
--
--extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata);
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// Finding the right font...
--//
--// You should really just solve this offline, keep your own tables
--// of what font is what, and don't try to get it out of the .ttf file.
--// That's because getting it out of the .ttf file is really hard, because
--// the names in the file can appear in many possible encodings, in many
--// possible languages, and e.g. if you need a case-insensitive comparison,
--// the details of that depend on the encoding & language in a complex way
--// (actually underspecified in truetype, but also gigantic).
--//
--// But you can use the provided functions in two possible ways:
--// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
--// unicode-encoded names to try to find the font you want;
--// you can run this before calling stbtt_InitFont()
--//
--// stbtt_GetFontNameString() lets you get any of the various strings
--// from the file yourself and do your own comparisons on them.
--// You have to have called stbtt_InitFont() first.
--
--
--extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
--// returns the offset (not index) of the font that matches, or -1 if none
--// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
--// if you use any other flag, use a font name like "Arial"; this checks
--// the 'macStyle' header field; i don't know if fonts set this consistently
--#define STBTT_MACSTYLE_DONTCARE 0
--#define STBTT_MACSTYLE_BOLD 1
--#define STBTT_MACSTYLE_ITALIC 2
--#define STBTT_MACSTYLE_UNDERSCORE 4
--#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
--
--extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
--// returns 1/0 whether the first string interpreted as utf8 is identical to
--// the second string interpreted as big-endian utf16... useful for strings from next func
--
--extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
--// returns the string (which may be big-endian double byte, e.g. for unicode)
--// and puts the length in bytes in *length.
--//
--// some of the values for the IDs are below; for more see the truetype spec:
--// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
--// http://www.microsoft.com/typography/otspec/name.htm
--
--enum { // platformID
-- STBTT_PLATFORM_ID_UNICODE =0,
-- STBTT_PLATFORM_ID_MAC =1,
-- STBTT_PLATFORM_ID_ISO =2,
-- STBTT_PLATFORM_ID_MICROSOFT =3
--};
--
--enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
-- STBTT_UNICODE_EID_UNICODE_1_0 =0,
-- STBTT_UNICODE_EID_UNICODE_1_1 =1,
-- STBTT_UNICODE_EID_ISO_10646 =2,
-- STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
-- STBTT_UNICODE_EID_UNICODE_2_0_FULL=4,
--};
--
--enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
-- STBTT_MS_EID_SYMBOL =0,
-- STBTT_MS_EID_UNICODE_BMP =1,
-- STBTT_MS_EID_SHIFTJIS =2,
-- STBTT_MS_EID_UNICODE_FULL =10,
--};
--
--enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
-- STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
-- STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
-- STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
-- STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7,
--};
--
--enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
-- // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
-- STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
-- STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
-- STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
-- STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
-- STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
-- STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D,
--};
--
--enum { // languageID for STBTT_PLATFORM_ID_MAC
-- STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
-- STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
-- STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
-- STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
-- STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
-- STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
-- STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19,
--};
--
--#ifdef __cplusplus
--}
--#endif
--
--#endif // __STB_INCLUDE_STB_TRUETYPE_H__
--
--///////////////////////////////////////////////////////////////////////////////
--///////////////////////////////////////////////////////////////////////////////
--////
--//// IMPLEMENTATION
--////
--////
--
--#ifdef STB_TRUETYPE_IMPLEMENTATION
--
--//////////////////////////////////////////////////////////////////////////
--//
--// accessors to parse data from file
--//
--
--// on platforms that don't allow misaligned reads, if we want to allow
--// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
--
--#define ttBYTE(p) (* (stbtt_uint8 *) (p))
--#define ttCHAR(p) (* (stbtt_int8 *) (p))
--#define ttFixed(p) ttLONG(p)
--
--#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)
--
-- #define ttUSHORT(p) (* (stbtt_uint16 *) (p))
-- #define ttSHORT(p) (* (stbtt_int16 *) (p))
-- #define ttULONG(p) (* (stbtt_uint32 *) (p))
-- #define ttLONG(p) (* (stbtt_int32 *) (p))
--
--#else
--
-- stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-- stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-- stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-- stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
--
--#endif
--
--#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
--#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
--
--static int stbtt__isfont(const stbtt_uint8 *font)
--{
-- // check the version number
-- if (stbtt_tag(font, "1")) return 1; // TrueType 1
-- if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
-- if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
-- if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
-- return 0;
--}
--
--// @OPTIMIZE: binary search
--static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
--{
-- stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
-- stbtt_uint32 tabledir = fontstart + 12;
-- stbtt_int32 i;
-- for (i=0; i < num_tables; ++i) {
-- stbtt_uint32 loc = tabledir + 16*i;
-- if (stbtt_tag(data+loc+0, tag))
-- return ttULONG(data+loc+8);
-- }
-- return 0;
--}
--
--int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
--{
-- // if it's just a font, there's only one valid index
-- if (stbtt__isfont(font_collection))
-- return index == 0 ? 0 : -1;
--
-- // check if it's a TTC
-- if (stbtt_tag(font_collection, "ttcf")) {
-- // version 1?
-- if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
-- stbtt_int32 n = ttLONG(font_collection+8);
-- if (index >= n)
-- return -1;
-- return ttULONG(font_collection+12+index*14);
-- }
-- }
-- return -1;
--}
--
--int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
--{
-- stbtt_uint8 *data = (stbtt_uint8 *) data2;
-- stbtt_uint32 cmap, t;
-- stbtt_int32 i,numTables;
--
-- info->data = data;
-- info->fontstart = fontstart;
--
-- cmap = stbtt__find_table(data, fontstart, "cmap");
-- info->loca = stbtt__find_table(data, fontstart, "loca");
-- info->head = stbtt__find_table(data, fontstart, "head");
-- info->glyf = stbtt__find_table(data, fontstart, "glyf");
-- info->hhea = stbtt__find_table(data, fontstart, "hhea");
-- info->hmtx = stbtt__find_table(data, fontstart, "hmtx");
-- if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
-- return 0;
--
-- t = stbtt__find_table(data, fontstart, "maxp");
-- if (t)
-- info->numGlyphs = ttUSHORT(data+t+4);
-- else
-- info->numGlyphs = 0xffff;
--
-- // find a cmap encoding table we understand *now* to avoid searching
-- // later. (todo: could make this installable)
-- // the same regardless of glyph.
-- numTables = ttUSHORT(data + cmap + 2);
-- info->index_map = 0;
-- for (i=0; i < numTables; ++i) {
-- stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
-- // find an encoding we understand:
-- switch(ttUSHORT(data+encoding_record)) {
-- case STBTT_PLATFORM_ID_MICROSOFT:
-- switch (ttUSHORT(data+encoding_record+2)) {
-- case STBTT_MS_EID_UNICODE_BMP:
-- case STBTT_MS_EID_UNICODE_FULL:
-- // MS/Unicode
-- info->index_map = cmap + ttULONG(data+encoding_record+4);
-- break;
-- }
-- break;
-- }
-- }
-- if (info->index_map == 0)
-- return 0;
--
-- info->indexToLocFormat = ttUSHORT(data+info->head + 50);
-- return 1;
--}
--
--int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
--{
-- stbtt_uint8 *data = info->data;
-- stbtt_uint32 index_map = info->index_map;
--
-- stbtt_uint16 format = ttUSHORT(data + index_map + 0);
-- if (format == 0) { // apple byte encoding
-- stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
-- if (unicode_codepoint < bytes-6)
-- return ttBYTE(data + index_map + 6 + unicode_codepoint);
-- return 0;
-- } else if (format == 6) {
-- stbtt_uint32 first = ttUSHORT(data + index_map + 6);
-- stbtt_uint32 count = ttUSHORT(data + index_map + 8);
-- if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
-- return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
-- return 0;
-- } else if (format == 2) {
-- STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
-- return 0;
-- } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
-- stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
-- stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
-- stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
-- stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
-- stbtt_uint16 item, offset, start, end;
--
-- // do a binary search of the segments
-- stbtt_uint32 endCount = index_map + 14;
-- stbtt_uint32 search = endCount;
--
-- if (unicode_codepoint > 0xffff)
-- return 0;
--
-- // they lie from endCount .. endCount + segCount
-- // but searchRange is the nearest power of two, so...
-- if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
-- search += rangeShift*2;
--
-- // now decrement to bias correctly to find smallest
-- search -= 2;
-- while (entrySelector) {
-- stbtt_uint16 start, end;
-- searchRange >>= 1;
-- start = ttUSHORT(data + search + 2 + segcount*2 + 2);
-- end = ttUSHORT(data + search + 2);
-- start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2);
-- end = ttUSHORT(data + search + searchRange*2);
-- if (unicode_codepoint > end)
-- search += searchRange*2;
-- --entrySelector;
-- }
-- search += 2;
--
-- item = (stbtt_uint16) ((search - endCount) >> 1);
--
-- STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
-- start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
-- end = ttUSHORT(data + index_map + 14 + 2 + 2*item);
-- if (unicode_codepoint < start)
-- return 0;
--
-- offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
-- if (offset == 0)
-- return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item);
--
-- return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
-- } else if (format == 12) {
-- stbtt_uint16 ngroups = ttUSHORT(data+index_map+6);
-- stbtt_int32 low,high;
-- low = 0; high = (stbtt_int32)ngroups;
-- // Binary search the right group.
-- while (low <= high) {
-- stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
-- stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
-- stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
-- if ((stbtt_uint32) unicode_codepoint < start_char)
-- high = mid-1;
-- else if ((stbtt_uint32) unicode_codepoint > end_char)
-- low = mid+1;
-- else {
-- stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
-- return start_glyph + unicode_codepoint-start_char;
-- }
-- }
-- return 0; // not found
-- }
-- // @TODO
-- STBTT_assert(0);
-- return 0;
--}
--
--int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
--{
-- return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
--}
--
--static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy)
--{
-- v->type = type;
-- v->x = x;
-- v->y = y;
-- v->cx = cx;
-- v->cy = cy;
--}
--
--static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
--{
-- int g1,g2;
--
-- if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
-- if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
--
-- if (info->indexToLocFormat == 0) {
-- g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
-- g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
-- } else {
-- g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
-- g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
-- }
--
-- return g1==g2 ? -1 : g1; // if length is 0, return -1
--}
--
--int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
--{
-- int g = stbtt__GetGlyfOffset(info, glyph_index);
-- if (g < 0) return 0;
--
-- if (x0) *x0 = ttSHORT(info->data + g + 2);
-- if (y0) *y0 = ttSHORT(info->data + g + 4);
-- if (x1) *x1 = ttSHORT(info->data + g + 6);
-- if (y1) *y1 = ttSHORT(info->data + g + 8);
-- return 1;
--}
--
--int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
--{
-- return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
--}
--
--int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
--{
-- stbtt_int16 numberOfContours;
-- stbtt_uint8 *endPtsOfContours;
-- stbtt_uint8 *data = info->data;
-- stbtt_vertex *vertices=0;
-- int num_vertices=0;
-- int g = stbtt__GetGlyfOffset(info, glyph_index);
--
-- *pvertices = NULL;
--
-- if (g < 0) return 0;
--
-- numberOfContours = ttSHORT(data + g);
--
-- if (numberOfContours > 0) {
-- stbtt_uint8 flags=0,flagcount;
-- stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off;
-- stbtt_int16 x,y,cx,cy,sx,sy;
-- stbtt_uint8 *points;
-- endPtsOfContours = (data + g + 10);
-- ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
-- points = data + g + 10 + numberOfContours * 2 + 2 + ins;
--
-- n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
--
-- m = n + numberOfContours; // a loose bound on how many vertices we might need
-- vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
-- if (vertices == 0)
-- return 0;
--
-- next_move = 0;
-- flagcount=0;
--
-- // in first pass, we load uninterpreted data into the allocated array
-- // above, shifted to the end of the array so we won't overwrite it when
-- // we create our final data starting from the front
--
-- off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
--
-- // first load flags
--
-- for (i=0; i < n; ++i) {
-- if (flagcount == 0) {
-- flags = *points++;
-- if (flags & 8)
-- flagcount = *points++;
-- } else
-- --flagcount;
-- vertices[off+i].type = flags;
-- }
--
-- // now load x coordinates
-- x=0;
-- for (i=0; i < n; ++i) {
-- flags = vertices[off+i].type;
-- if (flags & 2) {
-- stbtt_int16 dx = *points++;
-- x += (flags & 16) ? dx : -dx; // ???
-- } else {
-- if (!(flags & 16)) {
-- x = x + (stbtt_int16) (points[0]*256 + points[1]);
-- points += 2;
-- }
-- }
-- vertices[off+i].x = x;
-- }
--
-- // now load y coordinates
-- y=0;
-- for (i=0; i < n; ++i) {
-- flags = vertices[off+i].type;
-- if (flags & 4) {
-- stbtt_int16 dy = *points++;
-- y += (flags & 32) ? dy : -dy; // ???
-- } else {
-- if (!(flags & 32)) {
-- y = y + (stbtt_int16) (points[0]*256 + points[1]);
-- points += 2;
-- }
-- }
-- vertices[off+i].y = y;
-- }
--
-- // now convert them to our format
-- num_vertices=0;
-- sx = sy = cx = cy = 0;
-- for (i=0; i < n; ++i) {
-- flags = vertices[off+i].type;
-- x = (stbtt_int16) vertices[off+i].x;
-- y = (stbtt_int16) vertices[off+i].y;
-- if (next_move == i) {
-- // when we get to the end, we have to close the shape explicitly
-- if (i != 0) {
-- if (was_off)
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-- else
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-- }
--
-- // now start the new one
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0);
-- next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
-- ++j;
-- was_off = 0;
-- sx = x;
-- sy = y;
-- } else {
-- if (!(flags & 1)) { // if it's a curve
-- if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
-- cx = x;
-- cy = y;
-- was_off = 1;
-- } else {
-- if (was_off)
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
-- else
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
-- was_off = 0;
-- }
-- }
-- }
-- if (i != 0) {
-- if (was_off)
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-- else
-- stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-- }
-- } else if (numberOfContours == -1) {
-- // Compound shapes.
-- int more = 1;
-- stbtt_uint8 *comp = data + g + 10;
-- num_vertices = 0;
-- vertices = 0;
-- while (more) {
-- stbtt_uint16 flags, gidx;
-- int comp_num_verts = 0, i;
-- stbtt_vertex *comp_verts = 0, *tmp = 0;
-- float mtx[6] = {1,0,0,1,0,0}, m, n;
--
-- flags = ttSHORT(comp); comp+=2;
-- gidx = ttSHORT(comp); comp+=2;
--
-- if (flags & 2) { // XY values
-- if (flags & 1) { // shorts
-- mtx[4] = ttSHORT(comp); comp+=2;
-- mtx[5] = ttSHORT(comp); comp+=2;
-- } else {
-- mtx[4] = ttCHAR(comp); comp+=1;
-- mtx[5] = ttCHAR(comp); comp+=1;
-- }
-- }
-- else {
-- // @TODO handle matching point
-- STBTT_assert(0);
-- }
-- if (flags & (1<<3)) { // WE_HAVE_A_SCALE
-- mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[1] = mtx[2] = 0;
-- } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
-- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[1] = mtx[2] = 0;
-- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-- } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
-- mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
-- mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-- }
--
-- // Find transformation scales.
-- m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
-- n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
--
-- // Get indexed glyph.
-- comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
-- if (comp_num_verts > 0) {
-- // Transform vertices.
-- for (i = 0; i < comp_num_verts; ++i) {
-- stbtt_vertex* v = &comp_verts[i];
-- stbtt_vertex_type x,y;
-- x=v->x; y=v->y;
-- v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-- v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-- x=v->cx; y=v->cy;
-- v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-- v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-- }
-- // Append vertices.
-- tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
-- if (!tmp) {
-- if (vertices) STBTT_free(vertices, info->userdata);
-- if (comp_verts) STBTT_free(comp_verts, info->userdata);
-- return 0;
-- }
-- if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
-- memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
-- if (vertices) STBTT_free(vertices, info->userdata);
-- vertices = tmp;
-- STBTT_free(comp_verts, info->userdata);
-- num_vertices += comp_num_verts;
-- }
-- // More components ?
-- more = flags & (1<<5);
-- }
-- } else if (numberOfContours < 0) {
-- // @TODO other compound variations?
-- STBTT_assert(0);
-- } else {
-- // numberOfCounters == 0, do nothing
-- }
--
-- *pvertices = vertices;
-- return num_vertices;
--}
--
--void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
--{
-- stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
-- if (glyph_index < numOfLongHorMetrics) {
-- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
-- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
-- } else {
-- if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
-- if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
-- }
--}
--
--int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/)
--{
-- return 0;
--}
--
--int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/)
--{
-- return 0;
--}
--
--void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
--{
-- stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
--}
--
--void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
--{
-- if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
-- if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
-- if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
--}
--
--float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
--{
-- int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
-- return (float) height / fheight;
--}
--
--void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
--{
-- STBTT_free(v, info->userdata);
--}
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// antialiasing software rasterizer
--//
--
--void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
--{
-- int x0,y0,x1,y1;
-- if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1))
-- x0=y0=x1=y1=0; // e.g. space character
-- // now move to integral bboxes (treating pixels as little squares, what pixels get touched)?
-- if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x);
-- if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y);
-- if (ix1) *ix1 = STBTT_iceil (x1 * scale_x);
-- if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y);
--}
--
--void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
--{
-- stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1);
--}
--
--typedef struct stbtt__edge {
-- float x0,y0, x1,y1;
-- int invert;
--} stbtt__edge;
--
--typedef struct stbtt__active_edge
--{
-- int x,dx;
-- float ey;
-- struct stbtt__active_edge *next;
-- int valid;
--} stbtt__active_edge;
--
--#define FIXSHIFT 10
--#define FIX (1 << FIXSHIFT)
--#define FIXMASK (FIX-1)
--
--static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata)
--{
-- stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!!
-- float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
-- STBTT_assert(e->y0 <= start_point);
-- if (!z) return z;
-- // round dx down to avoid going too far
-- if (dxdy < 0)
-- z->dx = -STBTT_ifloor(FIX * -dxdy);
-- else
-- z->dx = STBTT_ifloor(FIX * dxdy);
-- z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0)));
-- z->x -= off_x * FIX;
-- z->ey = e->y1;
-- z->next = 0;
-- z->valid = e->invert ? 1 : -1;
-- return z;
--}
--
--// note: this routine clips fills that extend off the edges... ideally this
--// wouldn't happen, but it could happen if the truetype glyph bounding boxes
--// are wrong, or if the user supplies a too-small bitmap
--static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
--{
-- // non-zero winding fill
-- int x0=0, w=0;
--
-- while (e) {
-- if (w == 0) {
-- // if we're currently at zero, we need to record the edge start point
-- x0 = e->x; w += e->valid;
-- } else {
-- int x1 = e->x; w += e->valid;
-- // if we went to zero, we need to draw
-- if (w == 0) {
-- int i = x0 >> FIXSHIFT;
-- int j = x1 >> FIXSHIFT;
--
-- if (i < len && j >= 0) {
-- if (i == j) {
-- // x0,x1 are the same pixel, so compute combined coverage
-- scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT);
-- } else {
-- if (i >= 0) // add antialiasing for x0
-- scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT);
-- else
-- i = -1; // clip
--
-- if (j < len) // add antialiasing for x1
-- scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT);
-- else
-- j = len; // clip
--
-- for (++i; i < j; ++i) // fill pixels between x0 and x1
-- scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
-- }
-- }
-- }
-- }
--
-- e = e->next;
-- }
--}
--
--static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
--{
-- stbtt__active_edge *active = NULL;
-- int y,j=0;
-- int max_weight = (255 / vsubsample); // weight per vertical scanline
-- int s; // vertical subsample index
-- unsigned char scanline_data[512], *scanline;
--
-- if (result->w > 512)
-- scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
-- else
-- scanline = scanline_data;
--
-- y = off_y * vsubsample;
-- e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
--
-- while (j < result->h) {
-- STBTT_memset(scanline, 0, result->w);
-- for (s=0; s < vsubsample; ++s) {
-- // find center of pixel for this scanline
-- float scan_y = y + 0.5f;
-- stbtt__active_edge **step = &active;
--
-- // update all active edges;
-- // remove all active edges that terminate before the center of this scanline
-- while (*step) {
-- stbtt__active_edge * z = *step;
-- if (z->ey <= scan_y) {
-- *step = z->next; // delete from list
-- STBTT_assert(z->valid);
-- z->valid = 0;
-- STBTT_free(z, userdata);
-- } else {
-- z->x += z->dx; // advance to position for current scanline
-- step = &((*step)->next); // advance through list
-- }
-- }
--
-- // resort the list if needed
-- for(;;) {
-- int changed=0;
-- step = &active;
-- while (*step && (*step)->next) {
-- if ((*step)->x > (*step)->next->x) {
-- stbtt__active_edge *t = *step;
-- stbtt__active_edge *q = t->next;
--
-- t->next = q->next;
-- q->next = t;
-- *step = q;
-- changed = 1;
-- }
-- step = &(*step)->next;
-- }
-- if (!changed) break;
-- }
--
-- // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
-- while (e->y0 <= scan_y) {
-- if (e->y1 > scan_y) {
-- stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata);
-- // find insertion point
-- if (active == NULL)
-- active = z;
-- else if (z->x < active->x) {
-- // insert at front
-- z->next = active;
-- active = z;
-- } else {
-- // find thing to insert AFTER
-- stbtt__active_edge *p = active;
-- while (p->next && p->next->x < z->x)
-- p = p->next;
-- // at this point, p->next->x is NOT < z->x
-- z->next = p->next;
-- p->next = z;
-- }
-- }
-- ++e;
-- }
--
-- // now process all active edges in XOR fashion
-- if (active)
-- stbtt__fill_active_edges(scanline, result->w, active, max_weight);
--
-- ++y;
-- }
-- STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
-- ++j;
-- }
--
-- while (active) {
-- stbtt__active_edge *z = active;
-- active = active->next;
-- STBTT_free(z, userdata);
-- }
--
-- if (scanline != scanline_data)
-- STBTT_free(scanline, userdata);
--}
--
--static int stbtt__edge_compare(const void *p, const void *q)
--{
-- stbtt__edge *a = (stbtt__edge *) p;
-- stbtt__edge *b = (stbtt__edge *) q;
--
-- if (a->y0 < b->y0) return -1;
-- if (a->y0 > b->y0) return 1;
-- return 0;
--}
--
--typedef struct
--{
-- float x,y;
--} stbtt__point;
--
--static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata)
--{
-- float y_scale_inv = invert ? -scale_y : scale_y;
-- stbtt__edge *e;
-- int n,i,j,k,m;
-- int vsubsample = result->h < 8 ? 15 : 5;
-- // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
--
-- // now we have to blow out the windings into explicit edge lists
-- n = 0;
-- for (i=0; i < windings; ++i)
-- n += wcount[i];
--
-- e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
-- if (e == 0) return;
-- n = 0;
--
-- m=0;
-- for (i=0; i < windings; ++i) {
-- stbtt__point *p = pts + m;
-- m += wcount[i];
-- j = wcount[i]-1;
-- for (k=0; k < wcount[i]; j=k++) {
-- int a=k,b=j;
-- // skip the edge if horizontal
-- if (p[j].y == p[k].y)
-- continue;
-- // add edge from j to k to the list
-- e[n].invert = 0;
-- if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
-- e[n].invert = 1;
-- a=j,b=k;
-- }
-- e[n].x0 = p[a].x * scale_x;
-- e[n].y0 = p[a].y * y_scale_inv * vsubsample;
-- e[n].x1 = p[b].x * scale_x;
-- e[n].y1 = p[b].y * y_scale_inv * vsubsample;
-- ++n;
-- }
-- }
--
-- // now sort the edges by their highest point (should snap to integer, and then by x)
-- STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
--
-- // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
-- stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
--
-- STBTT_free(e, userdata);
--}
--
--static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
--{
-- if (!points) return; // during first pass, it's unallocated
-- points[n].x = x;
-- points[n].y = y;
--}
--
--// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
--static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
--{
-- // midpoint
-- float mx = (x0 + 2*x1 + x2)/4;
-- float my = (y0 + 2*y1 + y2)/4;
-- // versus directly drawn line
-- float dx = (x0+x2)/2 - mx;
-- float dy = (y0+y2)/2 - my;
-- if (n > 16) // 65536 segments on one curve better be enough!
-- return 1;
-- if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
-- stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
-- stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
-- } else {
-- stbtt__add_point(points, *num_points,x2,y2);
-- *num_points = *num_points+1;
-- }
-- return 1;
--}
--
--// returns number of contours
--stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
--{
-- stbtt__point *points=0;
-- int num_points=0;
--
-- float objspace_flatness_squared = objspace_flatness * objspace_flatness;
-- int i,n=0,start=0, pass;
--
-- // count how many "moves" there are to get the contour count
-- for (i=0; i < num_verts; ++i)
-- if (vertices[i].type == STBTT_vmove)
-- ++n;
--
-- *num_contours = n;
-- if (n == 0) return 0;
--
-- *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
--
-- if (*contour_lengths == 0) {
-- *num_contours = 0;
-- return 0;
-- }
--
-- // make two passes through the points so we don't need to realloc
-- for (pass=0; pass < 2; ++pass) {
-- float x=0,y=0;
-- if (pass == 1) {
-- points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
-- if (points == NULL) goto error;
-- }
-- num_points = 0;
-- n= -1;
-- for (i=0; i < num_verts; ++i) {
-- switch (vertices[i].type) {
-- case STBTT_vmove:
-- // start the next contour
-- if (n >= 0)
-- (*contour_lengths)[n] = num_points - start;
-- ++n;
-- start = num_points;
--
-- x = vertices[i].x, y = vertices[i].y;
-- stbtt__add_point(points, num_points++, x,y);
-- break;
-- case STBTT_vline:
-- x = vertices[i].x, y = vertices[i].y;
-- stbtt__add_point(points, num_points++, x, y);
-- break;
-- case STBTT_vcurve:
-- stbtt__tesselate_curve(points, &num_points, x,y,
-- vertices[i].cx, vertices[i].cy,
-- vertices[i].x, vertices[i].y,
-- objspace_flatness_squared, 0);
-- x = vertices[i].x, y = vertices[i].y;
-- break;
-- }
-- }
-- (*contour_lengths)[n] = num_points - start;
-- }
--
-- return points;
--error:
-- STBTT_free(points, userdata);
-- STBTT_free(*contour_lengths, userdata);
-- *contour_lengths = 0;
-- *num_contours = 0;
-- return NULL;
--}
--
--void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata)
--{
-- float scale = scale_x > scale_y ? scale_y : scale_x;
-- int winding_count, *winding_lengths;
-- stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
-- if (windings) {
-- stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata);
-- STBTT_free(winding_lengths, userdata);
-- STBTT_free(windings, userdata);
-- }
--}
--
--void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
--{
-- STBTT_free(bitmap, userdata);
--}
--
--unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
--{
-- int ix0,iy0,ix1,iy1;
-- stbtt__bitmap gbm;
-- stbtt_vertex *vertices;
-- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
--
-- if (scale_x == 0) scale_x = scale_y;
-- if (scale_y == 0) {
-- if (scale_x == 0) return NULL;
-- scale_y = scale_x;
-- }
--
-- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1);
--
-- // now we get the size
-- gbm.w = (ix1 - ix0);
-- gbm.h = (iy1 - iy0);
-- gbm.pixels = NULL; // in case we error
--
-- if (width ) *width = gbm.w;
-- if (height) *height = gbm.h;
-- if (xoff ) *xoff = ix0;
-- if (yoff ) *yoff = iy0;
--
-- if (gbm.w && gbm.h) {
-- gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
-- if (gbm.pixels) {
-- gbm.stride = gbm.w;
--
-- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata);
-- }
-- }
-- STBTT_free(vertices, info->userdata);
-- return gbm.pixels;
--}
--
--void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
--{
-- int ix0,iy0;
-- stbtt_vertex *vertices;
-- int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-- stbtt__bitmap gbm;
--
-- stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0);
-- gbm.pixels = output;
-- gbm.w = out_w;
-- gbm.h = out_h;
-- gbm.stride = out_stride;
--
-- if (gbm.w && gbm.h)
-- stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata);
--
-- STBTT_free(vertices, info->userdata);
--}
--
--unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
--{
-- return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
--}
--
--void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
--{
-- stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint));
--}
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// bitmap baking
--//
--// This is SUPER-SHITTY packing to keep source code small
--
--extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-- float pixel_height, // height of font in pixels
-- unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-- int first_char, int num_chars, // characters to bake
-- stbtt_bakedchar *chardata)
--{
-- float scale;
-- int x,y,bottom_y, i;
-- stbtt_fontinfo f;
-- stbtt_InitFont(&f, data, offset);
-- STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
-- x=y=1;
-- bottom_y = 1;
--
-- scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
--
-- for (i=0; i < num_chars; ++i) {
-- int advance, lsb, x0,y0,x1,y1,gw,gh;
-- int g = stbtt_FindGlyphIndex(&f, first_char + i);
-- stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
-- stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
-- gw = x1-x0;
-- gh = y1-y0;
-- if (x + gw + 1 >= pw)
-- y = bottom_y, x = 1; // advance to next row
-- if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
-- return -i;
-- STBTT_assert(x+gw < pw);
-- STBTT_assert(y+gh < ph);
-- stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
-- chardata[i].x0 = (stbtt_int16) x;
-- chardata[i].y0 = (stbtt_int16) y;
-- chardata[i].x1 = (stbtt_int16) (x + gw);
-- chardata[i].y1 = (stbtt_int16) (y + gh);
-- chardata[i].xadvance = scale * advance;
-- chardata[i].xoff = (float) x0;
-- chardata[i].yoff = (float) y0;
-- x = x + gw + 2;
-- if (y+gh+2 > bottom_y)
-- bottom_y = y+gh+2;
-- }
-- return bottom_y;
--}
--
--void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
--{
-- float d3d_bias = opengl_fillrule ? 0 : -0.5f;
-- float ipw = 1.0f / pw, iph = 1.0f / ph;
-- stbtt_bakedchar *b = chardata + char_index;
-- int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5);
-- int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5);
--
-- q->x0 = round_x + d3d_bias;
-- q->y0 = round_y + d3d_bias;
-- q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
-- q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
--
-- q->s0 = b->x0 * ipw;
-- q->t0 = b->y0 * ipw;
-- q->s1 = b->x1 * iph;
-- q->t1 = b->y1 * iph;
--
-- *xpos += b->xadvance;
--}
--
--//////////////////////////////////////////////////////////////////////////////
--//
--// font name matching -- recommended not to use this
--//
--
--// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
--static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
--{
-- stbtt_int32 i=0;
--
-- // convert utf16 to utf8 and compare the results while converting
-- while (len2) {
-- stbtt_uint16 ch = s2[0]*256 + s2[1];
-- if (ch < 0x80) {
-- if (i >= len1) return -1;
-- if (s1[i++] != ch) return -1;
-- } else if (ch < 0x800) {
-- if (i+1 >= len1) return -1;
-- if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
-- if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
-- } else if (ch >= 0xd800 && ch < 0xdc00) {
-- stbtt_uint32 c;
-- stbtt_uint16 ch2 = s2[2]*256 + s2[3];
-- if (i+3 >= len1) return -1;
-- c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
-- if (s1[i++] != 0xf0 + (c >> 18)) return -1;
-- if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
-- if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
-- if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
-- s2 += 2; // plus another 2 below
-- len2 -= 2;
-- } else if (ch >= 0xdc00 && ch < 0xe000) {
-- return -1;
-- } else {
-- if (i+2 >= len1) return -1;
-- if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
-- if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
-- if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
-- }
-- s2 += 2;
-- len2 -= 2;
-- }
-- return i;
--}
--
--int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
--{
-- return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
--}
--
--// returns results in whatever encoding you request... but note that 2-byte encodings
--// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
--char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
--{
-- stbtt_int32 i,count,stringOffset;
-- stbtt_uint8 *fc = font->data;
-- stbtt_uint32 offset = font->fontstart;
-- stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
-- if (!nm) return NULL;
--
-- count = ttUSHORT(fc+nm+2);
-- stringOffset = nm + ttUSHORT(fc+nm+4);
-- for (i=0; i < count; ++i) {
-- stbtt_uint32 loc = nm + 6 + 12 * i;
-- if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
-- && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
-- *length = ttUSHORT(fc+loc+8);
-- return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
-- }
-- }
-- return NULL;
--}
--
--static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
--{
-- stbtt_int32 i;
-- stbtt_int32 count = ttUSHORT(fc+nm+2);
-- stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
--
-- for (i=0; i < count; ++i) {
-- stbtt_uint32 loc = nm + 6 + 12 * i;
-- stbtt_int32 id = ttUSHORT(fc+loc+6);
-- if (id == target_id) {
-- // find the encoding
-- stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
--
-- // is this a Unicode encoding?
-- if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
-- stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10);
--
-- // check if there's a prefix match
-- stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
-- if (matchlen >= 0) {
-- // check for target_id+1 immediately following, with same encoding & language
-- if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
-- stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10);
-- if (slen == 0) {
-- if (matchlen == nlen)
-- return 1;
-- } else if (matchlen < nlen && name[matchlen] == ' ') {
-- ++matchlen;
-- if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
-- return 1;
-- }
-- } else {
-- // if nothing immediately following
-- if (matchlen == nlen)
-- return 1;
-- }
-- }
-- }
--
-- // @TODO handle other encodings
-- }
-- }
-- return 0;
--}
--
--static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
--{
-- stbtt_int32 nlen = STBTT_strlen((char *) name);
-- stbtt_uint32 nm,hd;
-- if (!stbtt__isfont(fc+offset)) return 0;
--
-- // check italics/bold/underline flags in macStyle...
-- if (flags) {
-- hd = stbtt__find_table(fc, offset, "head");
-- if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
-- }
--
-- nm = stbtt__find_table(fc, offset, "name");
-- if (!nm) return 0;
--
-- if (flags) {
-- // if we checked the macStyle flags, then just check the family and ignore the subfamily
-- if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-- } else {
-- if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
-- if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-- }
--
-- return 0;
--}
--
--int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
--{
-- stbtt_int32 i;
-- for (i=0;;++i) {
-- stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
-- if (off < 0) return off;
-- if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
-- return off;
-- }
--}
--
--#endif // STB_TRUETYPE_IMPLEMENTATION
-+// stb_truetype.h - v0.3 - public domain - 2009 Sean Barrett / RAD Game Tools
-+//
-+// This library processes TrueType files:
-+// parse files
-+// extract glyph metrics
-+// extract glyph shapes
-+// render glyphs to one-channel bitmaps with antialiasing (box filter)
-+//
-+// Todo:
-+// non-MS cmaps
-+// crashproof on bad data
-+// hinting
-+// subpixel positioning when rendering bitmap
-+// cleartype-style AA
-+//
-+// ADDITIONAL CONTRIBUTORS
-+//
-+// Mikko Mononen: compound shape support, more cmap formats
-+//
-+// VERSIONS
-+//
-+// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM)
-+// userdata, malloc-from-userdata, non-zero fill (STB)
-+// 0.2 (2009-03-11) Fix unsigned/signed char warnings
-+// 0.1 (2009-03-09) First public release
-+//
-+// USAGE
-+//
-+// Include this file in whatever places neeed to refer to it. In ONE C/C++
-+// file, write:
-+// #define STB_TRUETYPE_IMPLEMENTATION
-+// before the #include of this file. This expands out the actual
-+// implementation into that C/C++ file.
-+//
-+// Look at the header-file sections below for the API, but here's a quick skim:
-+//
-+// Simple 3D API (don't ship this, but it's fine for tools and quick start,
-+// and you can cut and paste from it to move to more advanced)
-+// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture
-+// stbtt_GetBakedQuad() -- compute quad to draw for a given char
-+//
-+// "Load" a font file from a memory buffer (you have to keep the buffer loaded)
-+// stbtt_InitFont()
-+// stbtt_GetFontOffsetForIndex() -- use for TTC font collections
-+//
-+// Render a unicode codepoint to a bitmap
-+// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap
-+// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide
-+// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be
-+//
-+// Character advance/positioning
-+// stbtt_GetCodepointHMetrics()
-+// stbtt_GetFontVMetrics()
-+//
-+// NOTES
-+//
-+// The system uses the raw data found in the .ttf file without changing it
-+// and without building auxiliary data structures. This is a bit inefficient
-+// on little-endian systems (the data is big-endian), but assuming you're
-+// caching the bitmaps or glyph shapes this shouldn't be a big deal.
-+//
-+// It appears to be very hard to programmatically determine what font a
-+// given file is in a general way. I provide an API for this, but I don't
-+// recommend it.
-+//
-+//
-+// SOURCE STATISTICS (based on v0.3, 1800 LOC)
-+//
-+// Documentation & header file 350 LOC \___ 500 LOC documentation
-+// Sample code 140 LOC /
-+// Truetype parsing 580 LOC ---- 600 LOC TrueType
-+// Software rasterization 240 LOC \ .
-+// Curve tesselation 120 LOC \__ 500 LOC Bitmap creation
-+// Bitmap management 70 LOC /
-+// Baked bitmap interface 70 LOC /
-+// Font name matching & access 150 LOC ---- 150
-+// C runtime library abstraction 60 LOC ---- 60
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//////////////////////////////////////////////////////////////////////////////
-+////
-+//// SAMPLE PROGRAMS
-+////
-+//
-+// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless
-+//
-+#if 0
-+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-+#include "stb_truetype.h"
-+
-+char ttf_buffer[1<<20];
-+unsigned char temp_bitmap[512*512];
-+
-+stbtt_chardata cdata[96]; // ASCII 32..126 is 95 glyphs
-+GLstbtt_uint ftex;
-+
-+void my_stbtt_initfont(void)
-+{
-+ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb"));
-+ stbtt_BakeFontBitmap(data,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits!
-+ // can free ttf_buffer at this point
-+ glGenTextures(1, &ftex);
-+ glBindTexture(GL_TEXTURE_2D, ftex);
-+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
-+ // can free temp_bitmap at this point
-+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-+}
-+
-+void my_stbtt_print(float x, float y, char *text)
-+{
-+ // assume orthographic projection with units = screen pixels, origin at top left
-+ glBindTexture(GL_TEXTURE_2D, ftex);
-+ glBegin(GL_QUADS);
-+ while (*text) {
-+ if (*text >= 32 && *text < 128) {
-+ stbtt_aligned_quad q;
-+ stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl,0=old d3d
-+ glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0);
-+ glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0);
-+ glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1);
-+ glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1);
-+ }
-+ ++text;
-+ }
-+ glEnd();
-+}
-+#endif
-+//
-+//
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// Complete program (this compiles): get a single bitmap, print as ASCII art
-+//
-+#if 0
-+#include <stdio.h>
-+#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation
-+#include "stb_truetype.h"
-+
-+char ttf_buffer[1<<25];
-+
-+int main(int argc, char **argv)
-+{
-+ stbtt_fontinfo font;
-+ unsigned char *bitmap;
-+ int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20);
-+
-+ fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb"));
-+
-+ stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0));
-+ bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0);
-+
-+ for (j=0; j < h; ++j) {
-+ for (i=0; i < w; ++i)
-+ putchar(" .:ioVM@"[bitmap[j*w+i]>>5]);
-+ putchar('\n');
-+ }
-+ return 0;
-+}
-+#endif
-+//
-+// Output:
-+//
-+// .ii.
-+// @@@@@@.
-+// V@Mio@@o
-+// :i. V@V
-+// :oM@@M
-+// :@@@MM@M
-+// @@o o@M
-+// :@@. M@M
-+// @@@o@@@@
-+// :M@@V:@@.
-+//
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// Complete program: print "Hello World!" banner, with bugs
-+//
-+#if 0
-+int main(int arg, char **argv)
-+{
-+ unsigned char screen[20][79];
-+ int i,j, pos=0;
-+ float scale;
-+ char *text = "Heljo World!";
-+
-+ fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb"));
-+ stbtt_InitFont(&font, buffer, 0);
-+
-+ scale = stbtt_ScaleForPixelHeight(&font, 16);
-+ memset(screen, 0, sizeof(screen));
-+
-+ while (*text) {
-+ int advance,lsb,x0,y0,x1,y1, newpos, baseline=13;
-+ stbtt_GetCodepointHMetrics(&font, *text, &advance, &lsb);
-+ stbtt_GetCodepointBitmapBox(&font, *text, scale,scale, &x0,&y0,&x1,&y1);
-+ newpos = pos + (int) (lsb * scale) + x0;
-+ stbtt_MakeCodepointBitmap(&font, &screen[baseline + y0][newpos], x1-x0,y1-y0, 79, scale,scale, *text);
-+ // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong
-+ // because this API is really for baking character bitmaps into textures
-+ pos += (int) (advance * scale);
-+ ++text;
-+ }
-+
-+ for (j=0; j < 20; ++j) {
-+ for (i=0; i < 79; ++i)
-+ putchar(" .:ioVM@"[screen[j][i]>>5]);
-+ putchar('\n');
-+ }
-+
-+ return 0;
-+}
-+#endif
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//////////////////////////////////////////////////////////////////////////////
-+////
-+//// INTEGRATION WITH RUNTIME LIBRARIES
-+////
-+
-+#ifdef STB_TRUETYPE_IMPLEMENTATION
-+ // #define your own (u)stbtt_int8/16/32 before including to override this
-+ #ifndef stbtt_uint8
-+ typedef unsigned char stbtt_uint8;
-+ typedef signed char stbtt_int8;
-+ typedef unsigned short stbtt_uint16;
-+ typedef signed short stbtt_int16;
-+ typedef unsigned int stbtt_uint32;
-+ typedef signed int stbtt_int32;
-+ #endif
-+
-+ typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1];
-+ typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1];
-+
-+ // #define your own STBTT_sort() to override this to avoid qsort
-+ #ifndef STBTT_sort
-+ #include <stdlib.h>
-+ #define STBTT_sort(data,num_items,item_size,compare_func) qsort(data,num_items,item_size,compare_func)
-+ #endif
-+
-+ // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h
-+ #ifndef STBTT_ifloor
-+ #include <math.h>
-+ #define STBTT_ifloor(x) ((int) floor(x))
-+ #define STBTT_iceil(x) ((int) ceil(x))
-+ #endif
-+
-+ // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h
-+ #ifndef STBTT_malloc
-+ #include <malloc.h>
-+ #define STBTT_malloc(x,u) malloc(x)
-+ #define STBTT_free(x,u) free(x)
-+ #endif
-+
-+ #ifndef STBTT_assert
-+ #include <assert.h>
-+ #define STBTT_assert(x) assert(x)
-+ #endif
-+
-+ #ifndef STBTT_strlen
-+ #include <string.h>
-+ #define STBTT_strlen(x) strlen(x)
-+ #endif
-+
-+ #ifndef STBTT_memcpy
-+ #include <memory.h>
-+ #define STBTT_memcpy memcpy
-+ #define STBTT_memset memset
-+ #endif
-+#endif
-+
-+///////////////////////////////////////////////////////////////////////////////
-+///////////////////////////////////////////////////////////////////////////////
-+////
-+//// INTERFACE
-+////
-+////
-+
-+#ifndef __STB_INCLUDE_STB_TRUETYPE_H__
-+#define __STB_INCLUDE_STB_TRUETYPE_H__
-+
-+#ifdef __cplusplus
-+extern "C" {
-+#endif
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// TEXTURE BAKING API
-+//
-+// If you use this API, you only have to call two functions ever.
-+//
-+
-+typedef struct
-+{
-+ unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap
-+ float xoff,yoff,xadvance;
-+} stbtt_bakedchar;
-+
-+extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-+ float pixel_height, // height of font in pixels
-+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-+ int first_char, int num_chars, // characters to bake
-+ stbtt_bakedchar *chardata); // you allocate this, it's num_chars long
-+// if return is positive, the first unused row of the bitmap
-+// if return is negative, returns the negative of the number of characters that fit
-+// if return is 0, no characters fit and no rows were used
-+// This uses a very crappy packing.
-+
-+typedef struct
-+{
-+ float x0,y0,s0,t0; // top-left
-+ float x1,y1,s1,t1; // bottom-right
-+} stbtt_aligned_quad;
-+
-+extern void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above
-+ int char_index, // character to display
-+ float *xpos, float *ypos, // pointers to current position in screen pixel space
-+ stbtt_aligned_quad *q, // output: quad to draw
-+ int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier
-+// Call GetBakedQuad with char_index = 'character - first_char', and it
-+// creates the quad you need to draw and advances the current position.
-+// It's inefficient; you might want to c&p it and optimize it.
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// FONT LOADING
-+//
-+//
-+
-+extern int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index);
-+// Each .ttf file may have more than one font. Each has a sequential index
-+// number starting from 0. Call this function to get the font offset for a
-+// given index; it returns -1 if the index is out of range. A regular .ttf
-+// file will only define one font and it always be at offset 0, so it will
-+// return '0' for index 0, and -1 for all other indices. You can just skip
-+// this step if you know it's that kind of font.
-+
-+
-+// The following structure is defined publically so you can declare one on
-+// the stack or as a global or etc.
-+typedef struct
-+{
-+ void *userdata;
-+ unsigned char *data; // pointer to .ttf file
-+ int fontstart; // offset of start of font
-+
-+ int numGlyphs; // number of glyphs, needed for range checking
-+
-+ int loca,head,glyf,hhea,hmtx; // table locations as offset from start of .ttf
-+ int index_map; // a cmap mapping for our chosen character encoding
-+ int indexToLocFormat; // format needed to map from glyph index to glyph
-+} stbtt_fontinfo;
-+
-+extern int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset);
-+// Given an offset into the file that defines a font, this function builds
-+// the necessary cached info for the rest of the system. You must allocate
-+// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
-+// need to do anything special to free it, because the contents are a pure
-+// cache with no additional data structures. Returns 0 on failure.
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// CHARACTER TO GLYPH-INDEX CONVERSIOn
-+
-+int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint);
-+// If you're going to perform multiple operations on the same character
-+// and you want a speed-up, call this function with the character you're
-+// going to process, then use glyph-based functions instead of the
-+// codepoint-based functions.
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// CHARACTER PROPERTIES
-+//
-+
-+extern float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels);
-+// computes a scale factor to produce a font whose "height" is 'pixels' tall.
-+// Height is measured as the distance from the highest ascender to the lowest
-+// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
-+// and computing:
-+// scale = pixels / (ascent - descent)
-+// so if you prefer to measure height by the ascent only, use a similar calculation.
-+
-+extern void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap);
-+// ascent is the coordinate above the baseline the font extends; descent
-+// is the coordinate below the baseline the font extends (i.e. it is typically negative)
-+// lineGap is the spacing between one row's descent and the next row's ascent...
-+// so you should advance the vertical position by "*ascent - *descent + *lineGap"
-+// these are expressed in unscaled coordinates
-+
-+extern void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing);
-+// leftSideBearing is the offset from the current horizontal position to the left edge of the character
-+// advanceWidth is the offset from the current horizontal position to the next horizontal position
-+// these are expressed in unscaled coordinates
-+
-+extern int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2);
-+// an additional amount to add to the 'advance' value between ch1 and ch2
-+// @TODO; for now always returns 0!
-+
-+extern int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1);
-+// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
-+
-+extern void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing);
-+extern int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2);
-+extern int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1);
-+// as above, but takes one or more glyph indices for greater efficiency
-+
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// GLYPH SHAPES (you probably don't need these, but they have to go before
-+// the bitmaps for C declaration-order reasons)
-+//
-+
-+#ifndef STBTT_vmove // you can predefine these to use different values (but why?)
-+ enum {
-+ STBTT_vmove=1,
-+ STBTT_vline,
-+ STBTT_vcurve
-+ };
-+#endif
-+
-+#ifndef stbtt_vertex // you can predefine this to use different values
-+ // (we share this with other code at RAD)
-+ #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file
-+ typedef struct
-+ {
-+ stbtt_vertex_type x,y,cx,cy;
-+ unsigned char type,padding;
-+ } stbtt_vertex;
-+#endif
-+
-+extern int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices);
-+extern int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices);
-+// returns # of vertices and fills *vertices with the pointer to them
-+// these are expressed in "unscaled" coordinates
-+
-+extern void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices);
-+// frees the data allocated above
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// BITMAP RENDERING
-+//
-+
-+extern void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata);
-+// frees the bitmap allocated below
-+
-+extern unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff);
-+// allocates a large-enough single-channel 8bpp bitmap and renders the
-+// specified character/glyph at the specified scale into it, with
-+// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
-+// *width & *height are filled out with the width & height of the bitmap,
-+// which is stored left-to-right, top-to-bottom.
-+//
-+// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
-+
-+extern void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint);
-+// the same as above, but you pass in storage for the bitmap in the form
-+// of 'output', with row spacing of 'out_stride' bytes. the bitmap is
-+// clipped to out_w/out_h bytes. call the next function to get the
-+// height and width and positioning info
-+
-+extern void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-+// get the bbox of the bitmap centered around the glyph origin; so the
-+// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
-+// the bitmap top left is (leftSideBearing*scale,iy0).
-+// (Note that the bitmap uses y-increases-down, but the shape uses
-+// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
-+
-+extern unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff);
-+extern void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-+extern void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph);
-+
-+//extern void stbtt_get_true_bbox(stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1);
-+
-+// @TODO: don't expose this structure
-+typedef struct
-+{
-+ int w,h,stride;
-+ unsigned char *pixels;
-+} stbtt__bitmap;
-+
-+extern void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata);
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// Finding the right font...
-+//
-+// You should really just solve this offline, keep your own tables
-+// of what font is what, and don't try to get it out of the .ttf file.
-+// That's because getting it out of the .ttf file is really hard, because
-+// the names in the file can appear in many possible encodings, in many
-+// possible languages, and e.g. if you need a case-insensitive comparison,
-+// the details of that depend on the encoding & language in a complex way
-+// (actually underspecified in truetype, but also gigantic).
-+//
-+// But you can use the provided functions in two possible ways:
-+// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
-+// unicode-encoded names to try to find the font you want;
-+// you can run this before calling stbtt_InitFont()
-+//
-+// stbtt_GetFontNameString() lets you get any of the various strings
-+// from the file yourself and do your own comparisons on them.
-+// You have to have called stbtt_InitFont() first.
-+
-+
-+extern int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags);
-+// returns the offset (not index) of the font that matches, or -1 if none
-+// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
-+// if you use any other flag, use a font name like "Arial"; this checks
-+// the 'macStyle' header field; i don't know if fonts set this consistently
-+#define STBTT_MACSTYLE_DONTCARE 0
-+#define STBTT_MACSTYLE_BOLD 1
-+#define STBTT_MACSTYLE_ITALIC 2
-+#define STBTT_MACSTYLE_UNDERSCORE 4
-+#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0
-+
-+extern int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2);
-+// returns 1/0 whether the first string interpreted as utf8 is identical to
-+// the second string interpreted as big-endian utf16... useful for strings from next func
-+
-+extern char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID);
-+// returns the string (which may be big-endian double byte, e.g. for unicode)
-+// and puts the length in bytes in *length.
-+//
-+// some of the values for the IDs are below; for more see the truetype spec:
-+// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
-+// http://www.microsoft.com/typography/otspec/name.htm
-+
-+enum { // platformID
-+ STBTT_PLATFORM_ID_UNICODE =0,
-+ STBTT_PLATFORM_ID_MAC =1,
-+ STBTT_PLATFORM_ID_ISO =2,
-+ STBTT_PLATFORM_ID_MICROSOFT =3
-+};
-+
-+enum { // encodingID for STBTT_PLATFORM_ID_UNICODE
-+ STBTT_UNICODE_EID_UNICODE_1_0 =0,
-+ STBTT_UNICODE_EID_UNICODE_1_1 =1,
-+ STBTT_UNICODE_EID_ISO_10646 =2,
-+ STBTT_UNICODE_EID_UNICODE_2_0_BMP=3,
-+ STBTT_UNICODE_EID_UNICODE_2_0_FULL=4,
-+};
-+
-+enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT
-+ STBTT_MS_EID_SYMBOL =0,
-+ STBTT_MS_EID_UNICODE_BMP =1,
-+ STBTT_MS_EID_SHIFTJIS =2,
-+ STBTT_MS_EID_UNICODE_FULL =10,
-+};
-+
-+enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes
-+ STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4,
-+ STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5,
-+ STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6,
-+ STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7,
-+};
-+
-+enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID...
-+ // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
-+ STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410,
-+ STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411,
-+ STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412,
-+ STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419,
-+ STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409,
-+ STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D,
-+};
-+
-+enum { // languageID for STBTT_PLATFORM_ID_MAC
-+ STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11,
-+ STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23,
-+ STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32,
-+ STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 ,
-+ STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 ,
-+ STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33,
-+ STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19,
-+};
-+
-+#ifdef __cplusplus
-+}
-+#endif
-+
-+#endif // __STB_INCLUDE_STB_TRUETYPE_H__
-+
-+///////////////////////////////////////////////////////////////////////////////
-+///////////////////////////////////////////////////////////////////////////////
-+////
-+//// IMPLEMENTATION
-+////
-+////
-+
-+#ifdef STB_TRUETYPE_IMPLEMENTATION
-+
-+//////////////////////////////////////////////////////////////////////////
-+//
-+// accessors to parse data from file
-+//
-+
-+// on platforms that don't allow misaligned reads, if we want to allow
-+// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE
-+
-+#define ttBYTE(p) (* (stbtt_uint8 *) (p))
-+#define ttCHAR(p) (* (stbtt_int8 *) (p))
-+#define ttFixed(p) ttLONG(p)
-+
-+#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE)
-+
-+ #define ttUSHORT(p) (* (stbtt_uint16 *) (p))
-+ #define ttSHORT(p) (* (stbtt_int16 *) (p))
-+ #define ttULONG(p) (* (stbtt_uint32 *) (p))
-+ #define ttLONG(p) (* (stbtt_int32 *) (p))
-+
-+#else
-+
-+ stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-+ stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; }
-+ stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-+ stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; }
-+
-+#endif
-+
-+#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3))
-+#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3])
-+
-+static int stbtt__isfont(const stbtt_uint8 *font)
-+{
-+ // check the version number
-+ if (stbtt_tag(font, "1")) return 1; // TrueType 1
-+ if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this!
-+ if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF
-+ if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0
-+ return 0;
-+}
-+
-+// @OPTIMIZE: binary search
-+static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag)
-+{
-+ stbtt_int32 num_tables = ttUSHORT(data+fontstart+4);
-+ stbtt_uint32 tabledir = fontstart + 12;
-+ stbtt_int32 i;
-+ for (i=0; i < num_tables; ++i) {
-+ stbtt_uint32 loc = tabledir + 16*i;
-+ if (stbtt_tag(data+loc+0, tag))
-+ return ttULONG(data+loc+8);
-+ }
-+ return 0;
-+}
-+
-+int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index)
-+{
-+ // if it's just a font, there's only one valid index
-+ if (stbtt__isfont(font_collection))
-+ return index == 0 ? 0 : -1;
-+
-+ // check if it's a TTC
-+ if (stbtt_tag(font_collection, "ttcf")) {
-+ // version 1?
-+ if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) {
-+ stbtt_int32 n = ttLONG(font_collection+8);
-+ if (index >= n)
-+ return -1;
-+ return ttULONG(font_collection+12+index*14);
-+ }
-+ }
-+ return -1;
-+}
-+
-+int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart)
-+{
-+ stbtt_uint8 *data = (stbtt_uint8 *) data2;
-+ stbtt_uint32 cmap, t;
-+ stbtt_int32 i,numTables;
-+
-+ info->data = data;
-+ info->fontstart = fontstart;
-+
-+ cmap = stbtt__find_table(data, fontstart, "cmap");
-+ info->loca = stbtt__find_table(data, fontstart, "loca");
-+ info->head = stbtt__find_table(data, fontstart, "head");
-+ info->glyf = stbtt__find_table(data, fontstart, "glyf");
-+ info->hhea = stbtt__find_table(data, fontstart, "hhea");
-+ info->hmtx = stbtt__find_table(data, fontstart, "hmtx");
-+ if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx)
-+ return 0;
-+
-+ t = stbtt__find_table(data, fontstart, "maxp");
-+ if (t)
-+ info->numGlyphs = ttUSHORT(data+t+4);
-+ else
-+ info->numGlyphs = 0xffff;
-+
-+ // find a cmap encoding table we understand *now* to avoid searching
-+ // later. (todo: could make this installable)
-+ // the same regardless of glyph.
-+ numTables = ttUSHORT(data + cmap + 2);
-+ info->index_map = 0;
-+ for (i=0; i < numTables; ++i) {
-+ stbtt_uint32 encoding_record = cmap + 4 + 8 * i;
-+ // find an encoding we understand:
-+ switch(ttUSHORT(data+encoding_record)) {
-+ case STBTT_PLATFORM_ID_MICROSOFT:
-+ switch (ttUSHORT(data+encoding_record+2)) {
-+ case STBTT_MS_EID_UNICODE_BMP:
-+ case STBTT_MS_EID_UNICODE_FULL:
-+ // MS/Unicode
-+ info->index_map = cmap + ttULONG(data+encoding_record+4);
-+ break;
-+ }
-+ break;
-+ }
-+ }
-+ if (info->index_map == 0)
-+ return 0;
-+
-+ info->indexToLocFormat = ttUSHORT(data+info->head + 50);
-+ return 1;
-+}
-+
-+int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint)
-+{
-+ stbtt_uint8 *data = info->data;
-+ stbtt_uint32 index_map = info->index_map;
-+
-+ stbtt_uint16 format = ttUSHORT(data + index_map + 0);
-+ if (format == 0) { // apple byte encoding
-+ stbtt_int32 bytes = ttUSHORT(data + index_map + 2);
-+ if (unicode_codepoint < bytes-6)
-+ return ttBYTE(data + index_map + 6 + unicode_codepoint);
-+ return 0;
-+ } else if (format == 6) {
-+ stbtt_uint32 first = ttUSHORT(data + index_map + 6);
-+ stbtt_uint32 count = ttUSHORT(data + index_map + 8);
-+ if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count)
-+ return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2);
-+ return 0;
-+ } else if (format == 2) {
-+ STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean
-+ return 0;
-+ } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges
-+ stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1;
-+ stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1;
-+ stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10);
-+ stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1;
-+ stbtt_uint16 item, offset, start, end;
-+
-+ // do a binary search of the segments
-+ stbtt_uint32 endCount = index_map + 14;
-+ stbtt_uint32 search = endCount;
-+
-+ if (unicode_codepoint > 0xffff)
-+ return 0;
-+
-+ // they lie from endCount .. endCount + segCount
-+ // but searchRange is the nearest power of two, so...
-+ if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2))
-+ search += rangeShift*2;
-+
-+ // now decrement to bias correctly to find smallest
-+ search -= 2;
-+ while (entrySelector) {
-+ stbtt_uint16 start, end;
-+ searchRange >>= 1;
-+ start = ttUSHORT(data + search + 2 + segcount*2 + 2);
-+ end = ttUSHORT(data + search + 2);
-+ start = ttUSHORT(data + search + searchRange*2 + segcount*2 + 2);
-+ end = ttUSHORT(data + search + searchRange*2);
-+ if (unicode_codepoint > end)
-+ search += searchRange*2;
-+ --entrySelector;
-+ }
-+ search += 2;
-+
-+ item = (stbtt_uint16) ((search - endCount) >> 1);
-+
-+ STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item));
-+ start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item);
-+ end = ttUSHORT(data + index_map + 14 + 2 + 2*item);
-+ if (unicode_codepoint < start)
-+ return 0;
-+
-+ offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item);
-+ if (offset == 0)
-+ return unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item);
-+
-+ return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item);
-+ } else if (format == 12) {
-+ stbtt_uint16 ngroups = ttUSHORT(data+index_map+6);
-+ stbtt_int32 low,high;
-+ low = 0; high = (stbtt_int32)ngroups;
-+ // Binary search the right group.
-+ while (low <= high) {
-+ stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high
-+ stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12);
-+ stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4);
-+ if ((stbtt_uint32) unicode_codepoint < start_char)
-+ high = mid-1;
-+ else if ((stbtt_uint32) unicode_codepoint > end_char)
-+ low = mid+1;
-+ else {
-+ stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8);
-+ return start_glyph + unicode_codepoint-start_char;
-+ }
-+ }
-+ return 0; // not found
-+ }
-+ // @TODO
-+ STBTT_assert(0);
-+ return 0;
-+}
-+
-+int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices)
-+{
-+ return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices);
-+}
-+
-+static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int16 x, stbtt_int16 y, stbtt_int16 cx, stbtt_int16 cy)
-+{
-+ v->type = type;
-+ v->x = x;
-+ v->y = y;
-+ v->cx = cx;
-+ v->cy = cy;
-+}
-+
-+static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index)
-+{
-+ int g1,g2;
-+
-+ if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range
-+ if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format
-+
-+ if (info->indexToLocFormat == 0) {
-+ g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2;
-+ g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2;
-+ } else {
-+ g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4);
-+ g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4);
-+ }
-+
-+ return g1==g2 ? -1 : g1; // if length is 0, return -1
-+}
-+
-+int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1)
-+{
-+ int g = stbtt__GetGlyfOffset(info, glyph_index);
-+ if (g < 0) return 0;
-+
-+ if (x0) *x0 = ttSHORT(info->data + g + 2);
-+ if (y0) *y0 = ttSHORT(info->data + g + 4);
-+ if (x1) *x1 = ttSHORT(info->data + g + 6);
-+ if (y1) *y1 = ttSHORT(info->data + g + 8);
-+ return 1;
-+}
-+
-+int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1)
-+{
-+ return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1);
-+}
-+
-+int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices)
-+{
-+ stbtt_int16 numberOfContours;
-+ stbtt_uint8 *endPtsOfContours;
-+ stbtt_uint8 *data = info->data;
-+ stbtt_vertex *vertices=0;
-+ int num_vertices=0;
-+ int g = stbtt__GetGlyfOffset(info, glyph_index);
-+
-+ *pvertices = NULL;
-+
-+ if (g < 0) return 0;
-+
-+ numberOfContours = ttSHORT(data + g);
-+
-+ if (numberOfContours > 0) {
-+ stbtt_uint8 flags=0,flagcount;
-+ stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off;
-+ stbtt_int16 x,y,cx,cy,sx,sy;
-+ stbtt_uint8 *points;
-+ endPtsOfContours = (data + g + 10);
-+ ins = ttUSHORT(data + g + 10 + numberOfContours * 2);
-+ points = data + g + 10 + numberOfContours * 2 + 2 + ins;
-+
-+ n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2);
-+
-+ m = n + numberOfContours; // a loose bound on how many vertices we might need
-+ vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata);
-+ if (vertices == 0)
-+ return 0;
-+
-+ next_move = 0;
-+ flagcount=0;
-+
-+ // in first pass, we load uninterpreted data into the allocated array
-+ // above, shifted to the end of the array so we won't overwrite it when
-+ // we create our final data starting from the front
-+
-+ off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated
-+
-+ // first load flags
-+
-+ for (i=0; i < n; ++i) {
-+ if (flagcount == 0) {
-+ flags = *points++;
-+ if (flags & 8)
-+ flagcount = *points++;
-+ } else
-+ --flagcount;
-+ vertices[off+i].type = flags;
-+ }
-+
-+ // now load x coordinates
-+ x=0;
-+ for (i=0; i < n; ++i) {
-+ flags = vertices[off+i].type;
-+ if (flags & 2) {
-+ stbtt_int16 dx = *points++;
-+ x += (flags & 16) ? dx : -dx; // ???
-+ } else {
-+ if (!(flags & 16)) {
-+ x = x + (stbtt_int16) (points[0]*256 + points[1]);
-+ points += 2;
-+ }
-+ }
-+ vertices[off+i].x = x;
-+ }
-+
-+ // now load y coordinates
-+ y=0;
-+ for (i=0; i < n; ++i) {
-+ flags = vertices[off+i].type;
-+ if (flags & 4) {
-+ stbtt_int16 dy = *points++;
-+ y += (flags & 32) ? dy : -dy; // ???
-+ } else {
-+ if (!(flags & 32)) {
-+ y = y + (stbtt_int16) (points[0]*256 + points[1]);
-+ points += 2;
-+ }
-+ }
-+ vertices[off+i].y = y;
-+ }
-+
-+ // now convert them to our format
-+ num_vertices=0;
-+ sx = sy = cx = cy = 0;
-+ for (i=0; i < n; ++i) {
-+ flags = vertices[off+i].type;
-+ x = (stbtt_int16) vertices[off+i].x;
-+ y = (stbtt_int16) vertices[off+i].y;
-+ if (next_move == i) {
-+ // when we get to the end, we have to close the shape explicitly
-+ if (i != 0) {
-+ if (was_off)
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-+ else
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-+ }
-+
-+ // now start the new one
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,x,y,0,0);
-+ next_move = 1 + ttUSHORT(endPtsOfContours+j*2);
-+ ++j;
-+ was_off = 0;
-+ sx = x;
-+ sy = y;
-+ } else {
-+ if (!(flags & 1)) { // if it's a curve
-+ if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy);
-+ cx = x;
-+ cy = y;
-+ was_off = 1;
-+ } else {
-+ if (was_off)
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy);
-+ else
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0);
-+ was_off = 0;
-+ }
-+ }
-+ }
-+ if (i != 0) {
-+ if (was_off)
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy);
-+ else
-+ stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0);
-+ }
-+ } else if (numberOfContours == -1) {
-+ // Compound shapes.
-+ int more = 1;
-+ stbtt_uint8 *comp = data + g + 10;
-+ num_vertices = 0;
-+ vertices = 0;
-+ while (more) {
-+ stbtt_uint16 flags, gidx;
-+ int comp_num_verts = 0, i;
-+ stbtt_vertex *comp_verts = 0, *tmp = 0;
-+ float mtx[6] = {1,0,0,1,0,0}, m, n;
-+
-+ flags = ttSHORT(comp); comp+=2;
-+ gidx = ttSHORT(comp); comp+=2;
-+
-+ if (flags & 2) { // XY values
-+ if (flags & 1) { // shorts
-+ mtx[4] = ttSHORT(comp); comp+=2;
-+ mtx[5] = ttSHORT(comp); comp+=2;
-+ } else {
-+ mtx[4] = ttCHAR(comp); comp+=1;
-+ mtx[5] = ttCHAR(comp); comp+=1;
-+ }
-+ }
-+ else {
-+ // @TODO handle matching point
-+ STBTT_assert(0);
-+ }
-+ if (flags & (1<<3)) { // WE_HAVE_A_SCALE
-+ mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[1] = mtx[2] = 0;
-+ } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE
-+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[1] = mtx[2] = 0;
-+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-+ } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO
-+ mtx[0] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[1] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[2] = ttSHORT(comp)/16384.0f; comp+=2;
-+ mtx[3] = ttSHORT(comp)/16384.0f; comp+=2;
-+ }
-+
-+ // Find transformation scales.
-+ m = (float) sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]);
-+ n = (float) sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]);
-+
-+ // Get indexed glyph.
-+ comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts);
-+ if (comp_num_verts > 0) {
-+ // Transform vertices.
-+ for (i = 0; i < comp_num_verts; ++i) {
-+ stbtt_vertex* v = &comp_verts[i];
-+ stbtt_vertex_type x,y;
-+ x=v->x; y=v->y;
-+ v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-+ v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-+ x=v->cx; y=v->cy;
-+ v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4]));
-+ v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5]));
-+ }
-+ // Append vertices.
-+ tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata);
-+ if (!tmp) {
-+ if (vertices) STBTT_free(vertices, info->userdata);
-+ if (comp_verts) STBTT_free(comp_verts, info->userdata);
-+ return 0;
-+ }
-+ if (num_vertices > 0) memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex));
-+ memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex));
-+ if (vertices) STBTT_free(vertices, info->userdata);
-+ vertices = tmp;
-+ STBTT_free(comp_verts, info->userdata);
-+ num_vertices += comp_num_verts;
-+ }
-+ // More components ?
-+ more = flags & (1<<5);
-+ }
-+ } else if (numberOfContours < 0) {
-+ // @TODO other compound variations?
-+ STBTT_assert(0);
-+ } else {
-+ // numberOfCounters == 0, do nothing
-+ }
-+
-+ *pvertices = vertices;
-+ return num_vertices;
-+}
-+
-+void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing)
-+{
-+ stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34);
-+ if (glyph_index < numOfLongHorMetrics) {
-+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index);
-+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2);
-+ } else {
-+ if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1));
-+ if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics));
-+ }
-+}
-+
-+int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo * /*info*/, int /*glyph1*/, int /*glyph2*/)
-+{
-+ return 0;
-+}
-+
-+int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo * /*info*/, int /*ch1*/, int /*ch2*/)
-+{
-+ return 0;
-+}
-+
-+void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing)
-+{
-+ stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing);
-+}
-+
-+void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap)
-+{
-+ if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4);
-+ if (descent) *descent = ttSHORT(info->data+info->hhea + 6);
-+ if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8);
-+}
-+
-+float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height)
-+{
-+ int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6);
-+ return (float) height / fheight;
-+}
-+
-+void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v)
-+{
-+ STBTT_free(v, info->userdata);
-+}
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// antialiasing software rasterizer
-+//
-+
-+void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-+{
-+ int x0,y0,x1,y1;
-+ if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1))
-+ x0=y0=x1=y1=0; // e.g. space character
-+ // now move to integral bboxes (treating pixels as little squares, what pixels get touched)?
-+ if (ix0) *ix0 = STBTT_ifloor(x0 * scale_x);
-+ if (iy0) *iy0 = -STBTT_iceil (y1 * scale_y);
-+ if (ix1) *ix1 = STBTT_iceil (x1 * scale_x);
-+ if (iy1) *iy1 = -STBTT_ifloor(y0 * scale_y);
-+}
-+
-+void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1)
-+{
-+ stbtt_GetGlyphBitmapBox(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y, ix0,iy0,ix1,iy1);
-+}
-+
-+typedef struct stbtt__edge {
-+ float x0,y0, x1,y1;
-+ int invert;
-+} stbtt__edge;
-+
-+typedef struct stbtt__active_edge
-+{
-+ int x,dx;
-+ float ey;
-+ struct stbtt__active_edge *next;
-+ int valid;
-+} stbtt__active_edge;
-+
-+#define FIXSHIFT 10
-+#define FIX (1 << FIXSHIFT)
-+#define FIXMASK (FIX-1)
-+
-+static stbtt__active_edge *new_active(stbtt__edge *e, int off_x, float start_point, void *userdata)
-+{
-+ stbtt__active_edge *z = (stbtt__active_edge *) STBTT_malloc(sizeof(*z), userdata); // @TODO: make a pool of these!!!
-+ float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0);
-+ STBTT_assert(e->y0 <= start_point);
-+ if (!z) return z;
-+ // round dx down to avoid going too far
-+ if (dxdy < 0)
-+ z->dx = -STBTT_ifloor(FIX * -dxdy);
-+ else
-+ z->dx = STBTT_ifloor(FIX * dxdy);
-+ z->x = STBTT_ifloor(FIX * (e->x0 + dxdy * (start_point - e->y0)));
-+ z->x -= off_x * FIX;
-+ z->ey = e->y1;
-+ z->next = 0;
-+ z->valid = e->invert ? 1 : -1;
-+ return z;
-+}
-+
-+// note: this routine clips fills that extend off the edges... ideally this
-+// wouldn't happen, but it could happen if the truetype glyph bounding boxes
-+// are wrong, or if the user supplies a too-small bitmap
-+static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight)
-+{
-+ // non-zero winding fill
-+ int x0=0, w=0;
-+
-+ while (e) {
-+ if (w == 0) {
-+ // if we're currently at zero, we need to record the edge start point
-+ x0 = e->x; w += e->valid;
-+ } else {
-+ int x1 = e->x; w += e->valid;
-+ // if we went to zero, we need to draw
-+ if (w == 0) {
-+ int i = x0 >> FIXSHIFT;
-+ int j = x1 >> FIXSHIFT;
-+
-+ if (i < len && j >= 0) {
-+ if (i == j) {
-+ // x0,x1 are the same pixel, so compute combined coverage
-+ scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> FIXSHIFT);
-+ } else {
-+ if (i >= 0) // add antialiasing for x0
-+ scanline[i] = scanline[i] + (stbtt_uint8) (((FIX - (x0 & FIXMASK)) * max_weight) >> FIXSHIFT);
-+ else
-+ i = -1; // clip
-+
-+ if (j < len) // add antialiasing for x1
-+ scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & FIXMASK) * max_weight) >> FIXSHIFT);
-+ else
-+ j = len; // clip
-+
-+ for (++i; i < j; ++i) // fill pixels between x0 and x1
-+ scanline[i] = scanline[i] + (stbtt_uint8) max_weight;
-+ }
-+ }
-+ }
-+ }
-+
-+ e = e->next;
-+ }
-+}
-+
-+static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata)
-+{
-+ stbtt__active_edge *active = NULL;
-+ int y,j=0;
-+ int max_weight = (255 / vsubsample); // weight per vertical scanline
-+ int s; // vertical subsample index
-+ unsigned char scanline_data[512], *scanline;
-+
-+ if (result->w > 512)
-+ scanline = (unsigned char *) STBTT_malloc(result->w, userdata);
-+ else
-+ scanline = scanline_data;
-+
-+ y = off_y * vsubsample;
-+ e[n].y0 = (off_y + result->h) * (float) vsubsample + 1;
-+
-+ while (j < result->h) {
-+ STBTT_memset(scanline, 0, result->w);
-+ for (s=0; s < vsubsample; ++s) {
-+ // find center of pixel for this scanline
-+ float scan_y = y + 0.5f;
-+ stbtt__active_edge **step = &active;
-+
-+ // update all active edges;
-+ // remove all active edges that terminate before the center of this scanline
-+ while (*step) {
-+ stbtt__active_edge * z = *step;
-+ if (z->ey <= scan_y) {
-+ *step = z->next; // delete from list
-+ STBTT_assert(z->valid);
-+ z->valid = 0;
-+ STBTT_free(z, userdata);
-+ } else {
-+ z->x += z->dx; // advance to position for current scanline
-+ step = &((*step)->next); // advance through list
-+ }
-+ }
-+
-+ // resort the list if needed
-+ for(;;) {
-+ int changed=0;
-+ step = &active;
-+ while (*step && (*step)->next) {
-+ if ((*step)->x > (*step)->next->x) {
-+ stbtt__active_edge *t = *step;
-+ stbtt__active_edge *q = t->next;
-+
-+ t->next = q->next;
-+ q->next = t;
-+ *step = q;
-+ changed = 1;
-+ }
-+ step = &(*step)->next;
-+ }
-+ if (!changed) break;
-+ }
-+
-+ // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline
-+ while (e->y0 <= scan_y) {
-+ if (e->y1 > scan_y) {
-+ stbtt__active_edge *z = new_active(e, off_x, scan_y, userdata);
-+ // find insertion point
-+ if (active == NULL)
-+ active = z;
-+ else if (z->x < active->x) {
-+ // insert at front
-+ z->next = active;
-+ active = z;
-+ } else {
-+ // find thing to insert AFTER
-+ stbtt__active_edge *p = active;
-+ while (p->next && p->next->x < z->x)
-+ p = p->next;
-+ // at this point, p->next->x is NOT < z->x
-+ z->next = p->next;
-+ p->next = z;
-+ }
-+ }
-+ ++e;
-+ }
-+
-+ // now process all active edges in XOR fashion
-+ if (active)
-+ stbtt__fill_active_edges(scanline, result->w, active, max_weight);
-+
-+ ++y;
-+ }
-+ STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w);
-+ ++j;
-+ }
-+
-+ while (active) {
-+ stbtt__active_edge *z = active;
-+ active = active->next;
-+ STBTT_free(z, userdata);
-+ }
-+
-+ if (scanline != scanline_data)
-+ STBTT_free(scanline, userdata);
-+}
-+
-+static int stbtt__edge_compare(const void *p, const void *q)
-+{
-+ stbtt__edge *a = (stbtt__edge *) p;
-+ stbtt__edge *b = (stbtt__edge *) q;
-+
-+ if (a->y0 < b->y0) return -1;
-+ if (a->y0 > b->y0) return 1;
-+ return 0;
-+}
-+
-+typedef struct
-+{
-+ float x,y;
-+} stbtt__point;
-+
-+static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, int off_x, int off_y, int invert, void *userdata)
-+{
-+ float y_scale_inv = invert ? -scale_y : scale_y;
-+ stbtt__edge *e;
-+ int n,i,j,k,m;
-+ int vsubsample = result->h < 8 ? 15 : 5;
-+ // vsubsample should divide 255 evenly; otherwise we won't reach full opacity
-+
-+ // now we have to blow out the windings into explicit edge lists
-+ n = 0;
-+ for (i=0; i < windings; ++i)
-+ n += wcount[i];
-+
-+ e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel
-+ if (e == 0) return;
-+ n = 0;
-+
-+ m=0;
-+ for (i=0; i < windings; ++i) {
-+ stbtt__point *p = pts + m;
-+ m += wcount[i];
-+ j = wcount[i]-1;
-+ for (k=0; k < wcount[i]; j=k++) {
-+ int a=k,b=j;
-+ // skip the edge if horizontal
-+ if (p[j].y == p[k].y)
-+ continue;
-+ // add edge from j to k to the list
-+ e[n].invert = 0;
-+ if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) {
-+ e[n].invert = 1;
-+ a=j,b=k;
-+ }
-+ e[n].x0 = p[a].x * scale_x;
-+ e[n].y0 = p[a].y * y_scale_inv * vsubsample;
-+ e[n].x1 = p[b].x * scale_x;
-+ e[n].y1 = p[b].y * y_scale_inv * vsubsample;
-+ ++n;
-+ }
-+ }
-+
-+ // now sort the edges by their highest point (should snap to integer, and then by x)
-+ STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare);
-+
-+ // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule
-+ stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata);
-+
-+ STBTT_free(e, userdata);
-+}
-+
-+static void stbtt__add_point(stbtt__point *points, int n, float x, float y)
-+{
-+ if (!points) return; // during first pass, it's unallocated
-+ points[n].x = x;
-+ points[n].y = y;
-+}
-+
-+// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching
-+static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n)
-+{
-+ // midpoint
-+ float mx = (x0 + 2*x1 + x2)/4;
-+ float my = (y0 + 2*y1 + y2)/4;
-+ // versus directly drawn line
-+ float dx = (x0+x2)/2 - mx;
-+ float dy = (y0+y2)/2 - my;
-+ if (n > 16) // 65536 segments on one curve better be enough!
-+ return 1;
-+ if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA
-+ stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1);
-+ stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1);
-+ } else {
-+ stbtt__add_point(points, *num_points,x2,y2);
-+ *num_points = *num_points+1;
-+ }
-+ return 1;
-+}
-+
-+// returns number of contours
-+stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata)
-+{
-+ stbtt__point *points=0;
-+ int num_points=0;
-+
-+ float objspace_flatness_squared = objspace_flatness * objspace_flatness;
-+ int i,n=0,start=0, pass;
-+
-+ // count how many "moves" there are to get the contour count
-+ for (i=0; i < num_verts; ++i)
-+ if (vertices[i].type == STBTT_vmove)
-+ ++n;
-+
-+ *num_contours = n;
-+ if (n == 0) return 0;
-+
-+ *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata);
-+
-+ if (*contour_lengths == 0) {
-+ *num_contours = 0;
-+ return 0;
-+ }
-+
-+ // make two passes through the points so we don't need to realloc
-+ for (pass=0; pass < 2; ++pass) {
-+ float x=0,y=0;
-+ if (pass == 1) {
-+ points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata);
-+ if (points == NULL) goto error;
-+ }
-+ num_points = 0;
-+ n= -1;
-+ for (i=0; i < num_verts; ++i) {
-+ switch (vertices[i].type) {
-+ case STBTT_vmove:
-+ // start the next contour
-+ if (n >= 0)
-+ (*contour_lengths)[n] = num_points - start;
-+ ++n;
-+ start = num_points;
-+
-+ x = vertices[i].x, y = vertices[i].y;
-+ stbtt__add_point(points, num_points++, x,y);
-+ break;
-+ case STBTT_vline:
-+ x = vertices[i].x, y = vertices[i].y;
-+ stbtt__add_point(points, num_points++, x, y);
-+ break;
-+ case STBTT_vcurve:
-+ stbtt__tesselate_curve(points, &num_points, x,y,
-+ vertices[i].cx, vertices[i].cy,
-+ vertices[i].x, vertices[i].y,
-+ objspace_flatness_squared, 0);
-+ x = vertices[i].x, y = vertices[i].y;
-+ break;
-+ }
-+ }
-+ (*contour_lengths)[n] = num_points - start;
-+ }
-+
-+ return points;
-+error:
-+ STBTT_free(points, userdata);
-+ STBTT_free(*contour_lengths, userdata);
-+ *contour_lengths = 0;
-+ *num_contours = 0;
-+ return NULL;
-+}
-+
-+void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, int x_off, int y_off, int invert, void *userdata)
-+{
-+ float scale = scale_x > scale_y ? scale_y : scale_x;
-+ int winding_count, *winding_lengths;
-+ stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata);
-+ if (windings) {
-+ stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, x_off, y_off, invert, userdata);
-+ STBTT_free(winding_lengths, userdata);
-+ STBTT_free(windings, userdata);
-+ }
-+}
-+
-+void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata)
-+{
-+ STBTT_free(bitmap, userdata);
-+}
-+
-+unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff)
-+{
-+ int ix0,iy0,ix1,iy1;
-+ stbtt__bitmap gbm;
-+ stbtt_vertex *vertices;
-+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-+
-+ if (scale_x == 0) scale_x = scale_y;
-+ if (scale_y == 0) {
-+ if (scale_x == 0) return NULL;
-+ scale_y = scale_x;
-+ }
-+
-+ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,&ix1,&iy1);
-+
-+ // now we get the size
-+ gbm.w = (ix1 - ix0);
-+ gbm.h = (iy1 - iy0);
-+ gbm.pixels = NULL; // in case we error
-+
-+ if (width ) *width = gbm.w;
-+ if (height) *height = gbm.h;
-+ if (xoff ) *xoff = ix0;
-+ if (yoff ) *yoff = iy0;
-+
-+ if (gbm.w && gbm.h) {
-+ gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata);
-+ if (gbm.pixels) {
-+ gbm.stride = gbm.w;
-+
-+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0, iy0, 1, info->userdata);
-+ }
-+ }
-+ STBTT_free(vertices, info->userdata);
-+ return gbm.pixels;
-+}
-+
-+void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph)
-+{
-+ int ix0,iy0;
-+ stbtt_vertex *vertices;
-+ int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices);
-+ stbtt__bitmap gbm;
-+
-+ stbtt_GetGlyphBitmapBox(info, glyph, scale_x, scale_y, &ix0,&iy0,0,0);
-+ gbm.pixels = output;
-+ gbm.w = out_w;
-+ gbm.h = out_h;
-+ gbm.stride = out_stride;
-+
-+ if (gbm.w && gbm.h)
-+ stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, ix0,iy0, 1, info->userdata);
-+
-+ STBTT_free(vertices, info->userdata);
-+}
-+
-+unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff)
-+{
-+ return stbtt_GetGlyphBitmap(info, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff);
-+}
-+
-+void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint)
-+{
-+ stbtt_MakeGlyphBitmap(info, output, out_w, out_h, out_stride, scale_x, scale_y, stbtt_FindGlyphIndex(info,codepoint));
-+}
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// bitmap baking
-+//
-+// This is SUPER-SHITTY packing to keep source code small
-+
-+extern int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf)
-+ float pixel_height, // height of font in pixels
-+ unsigned char *pixels, int pw, int ph, // bitmap to be filled in
-+ int first_char, int num_chars, // characters to bake
-+ stbtt_bakedchar *chardata)
-+{
-+ float scale;
-+ int x,y,bottom_y, i;
-+ stbtt_fontinfo f;
-+ stbtt_InitFont(&f, data, offset);
-+ STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
-+ x=y=1;
-+ bottom_y = 1;
-+
-+ scale = stbtt_ScaleForPixelHeight(&f, pixel_height);
-+
-+ for (i=0; i < num_chars; ++i) {
-+ int advance, lsb, x0,y0,x1,y1,gw,gh;
-+ int g = stbtt_FindGlyphIndex(&f, first_char + i);
-+ stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb);
-+ stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1);
-+ gw = x1-x0;
-+ gh = y1-y0;
-+ if (x + gw + 1 >= pw)
-+ y = bottom_y, x = 1; // advance to next row
-+ if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row
-+ return -i;
-+ STBTT_assert(x+gw < pw);
-+ STBTT_assert(y+gh < ph);
-+ stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g);
-+ chardata[i].x0 = (stbtt_int16) x;
-+ chardata[i].y0 = (stbtt_int16) y;
-+ chardata[i].x1 = (stbtt_int16) (x + gw);
-+ chardata[i].y1 = (stbtt_int16) (y + gh);
-+ chardata[i].xadvance = scale * advance;
-+ chardata[i].xoff = (float) x0;
-+ chardata[i].yoff = (float) y0;
-+ x = x + gw + 2;
-+ if (y+gh+2 > bottom_y)
-+ bottom_y = y+gh+2;
-+ }
-+ return bottom_y;
-+}
-+
-+void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule)
-+{
-+ float d3d_bias = opengl_fillrule ? 0 : -0.5f;
-+ float ipw = 1.0f / pw, iph = 1.0f / ph;
-+ stbtt_bakedchar *b = chardata + char_index;
-+ int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5);
-+ int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5);
-+
-+ q->x0 = round_x + d3d_bias;
-+ q->y0 = round_y + d3d_bias;
-+ q->x1 = round_x + b->x1 - b->x0 + d3d_bias;
-+ q->y1 = round_y + b->y1 - b->y0 + d3d_bias;
-+
-+ q->s0 = b->x0 * ipw;
-+ q->t0 = b->y0 * ipw;
-+ q->s1 = b->x1 * iph;
-+ q->t1 = b->y1 * iph;
-+
-+ *xpos += b->xadvance;
-+}
-+
-+//////////////////////////////////////////////////////////////////////////////
-+//
-+// font name matching -- recommended not to use this
-+//
-+
-+// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string
-+static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2)
-+{
-+ stbtt_int32 i=0;
-+
-+ // convert utf16 to utf8 and compare the results while converting
-+ while (len2) {
-+ stbtt_uint16 ch = s2[0]*256 + s2[1];
-+ if (ch < 0x80) {
-+ if (i >= len1) return -1;
-+ if (s1[i++] != ch) return -1;
-+ } else if (ch < 0x800) {
-+ if (i+1 >= len1) return -1;
-+ if (s1[i++] != 0xc0 + (ch >> 6)) return -1;
-+ if (s1[i++] != 0x80 + (ch & 0x3f)) return -1;
-+ } else if (ch >= 0xd800 && ch < 0xdc00) {
-+ stbtt_uint32 c;
-+ stbtt_uint16 ch2 = s2[2]*256 + s2[3];
-+ if (i+3 >= len1) return -1;
-+ c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000;
-+ if (s1[i++] != 0xf0 + (c >> 18)) return -1;
-+ if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1;
-+ if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1;
-+ if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1;
-+ s2 += 2; // plus another 2 below
-+ len2 -= 2;
-+ } else if (ch >= 0xdc00 && ch < 0xe000) {
-+ return -1;
-+ } else {
-+ if (i+2 >= len1) return -1;
-+ if (s1[i++] != 0xe0 + (ch >> 12)) return -1;
-+ if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1;
-+ if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1;
-+ }
-+ s2 += 2;
-+ len2 -= 2;
-+ }
-+ return i;
-+}
-+
-+int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2)
-+{
-+ return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2);
-+}
-+
-+// returns results in whatever encoding you request... but note that 2-byte encodings
-+// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare
-+char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID)
-+{
-+ stbtt_int32 i,count,stringOffset;
-+ stbtt_uint8 *fc = font->data;
-+ stbtt_uint32 offset = font->fontstart;
-+ stbtt_uint32 nm = stbtt__find_table(fc, offset, "name");
-+ if (!nm) return NULL;
-+
-+ count = ttUSHORT(fc+nm+2);
-+ stringOffset = nm + ttUSHORT(fc+nm+4);
-+ for (i=0; i < count; ++i) {
-+ stbtt_uint32 loc = nm + 6 + 12 * i;
-+ if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2)
-+ && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) {
-+ *length = ttUSHORT(fc+loc+8);
-+ return (char *) (fc+stringOffset+ttUSHORT(fc+loc+10));
-+ }
-+ }
-+ return NULL;
-+}
-+
-+static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id)
-+{
-+ stbtt_int32 i;
-+ stbtt_int32 count = ttUSHORT(fc+nm+2);
-+ stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4);
-+
-+ for (i=0; i < count; ++i) {
-+ stbtt_uint32 loc = nm + 6 + 12 * i;
-+ stbtt_int32 id = ttUSHORT(fc+loc+6);
-+ if (id == target_id) {
-+ // find the encoding
-+ stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4);
-+
-+ // is this a Unicode encoding?
-+ if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) {
-+ stbtt_int32 slen = ttUSHORT(fc+loc+8), off = ttUSHORT(fc+loc+10);
-+
-+ // check if there's a prefix match
-+ stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen);
-+ if (matchlen >= 0) {
-+ // check for target_id+1 immediately following, with same encoding & language
-+ if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) {
-+ stbtt_int32 slen = ttUSHORT(fc+loc+12+8), off = ttUSHORT(fc+loc+12+10);
-+ if (slen == 0) {
-+ if (matchlen == nlen)
-+ return 1;
-+ } else if (matchlen < nlen && name[matchlen] == ' ') {
-+ ++matchlen;
-+ if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen))
-+ return 1;
-+ }
-+ } else {
-+ // if nothing immediately following
-+ if (matchlen == nlen)
-+ return 1;
-+ }
-+ }
-+ }
-+
-+ // @TODO handle other encodings
-+ }
-+ }
-+ return 0;
-+}
-+
-+static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags)
-+{
-+ stbtt_int32 nlen = STBTT_strlen((char *) name);
-+ stbtt_uint32 nm,hd;
-+ if (!stbtt__isfont(fc+offset)) return 0;
-+
-+ // check italics/bold/underline flags in macStyle...
-+ if (flags) {
-+ hd = stbtt__find_table(fc, offset, "head");
-+ if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0;
-+ }
-+
-+ nm = stbtt__find_table(fc, offset, "name");
-+ if (!nm) return 0;
-+
-+ if (flags) {
-+ // if we checked the macStyle flags, then just check the family and ignore the subfamily
-+ if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-+ } else {
-+ if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1;
-+ if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1;
-+ }
-+
-+ return 0;
-+}
-+
-+int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags)
-+{
-+ stbtt_int32 i;
-+ for (i=0;;++i) {
-+ stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i);
-+ if (off < 0) return off;
-+ if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags))
-+ return off;
-+ }
-+}
-+
-+#endif // STB_TRUETYPE_IMPLEMENTATION
+--
+1.9.0.msysgit.0
+
diff --git a/sql/base/auth_database.sql b/sql/base/auth_database.sql
index be64a560798..76cddd7e45d 100644
--- a/sql/base/auth_database.sql
+++ b/sql/base/auth_database.sql
@@ -1,8 +1,8 @@
--- MySQL dump 10.15 Distrib 10.0.10-MariaDB, for Win64 (x86)
+-- MySQL dump 10.15 Distrib 10.0.12-MariaDB, for Win64 (x86)
--
-- Host: localhost Database: auth
-- ------------------------------------------------------
--- Server version 10.0.10-MariaDB
+-- Server version 10.0.12-MariaDB
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
@@ -248,17 +248,17 @@ DROP TABLE IF EXISTS `logs_ip_actions`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `logs_ip_actions` (
-`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier',
-`account_id` INT(10) UNSIGNED NOT NULL COMMENT 'Account ID',
-`character_guid` INT(10) UNSIGNED NOT NULL COMMENT 'Character Guid',
-`type` TINYINT(3) UNSIGNED NOT NULL,
-`ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1',
-`systemnote` TEXT NULL COMMENT 'Notes inserted by system',
-`unixtime` INT(10) UNSIGNED NOT NULL COMMENT 'Unixtime',
-`time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp',
-`comment` TEXT NULL COMMENT 'Allows users to add a comment',
-PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier',
+ `account_id` int(10) unsigned NOT NULL COMMENT 'Account ID',
+ `character_guid` int(10) unsigned NOT NULL COMMENT 'Character Guid',
+ `type` tinyint(3) unsigned NOT NULL,
+ `ip` varchar(15) NOT NULL DEFAULT '127.0.0.1',
+ `systemnote` text COMMENT 'Notes inserted by system',
+ `unixtime` int(10) unsigned NOT NULL COMMENT 'Unixtime',
+ `time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'Timestamp',
+ `comment` text COMMENT 'Allows users to add a comment',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Used to log ips of individual actions';
/*!40101 SET character_set_client = @saved_cs_client */;
--
@@ -471,4 +471,4 @@ UNLOCK TABLES;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
--- Dump completed on 2014-04-28 13:43:54
+-- Dump completed on 2014-06-25 18:37:31
diff --git a/sql/updates/auth/2014_05_02_00_action_ip_logger.sql b/sql/updates/auth/2014_06_23_00_auth_action_ip_logger.sql
index f5603738f54..375b97d35fc 100644
--- a/sql/updates/auth/2014_05_02_00_action_ip_logger.sql
+++ b/sql/updates/auth/2014_06_23_00_auth_action_ip_logger.sql
@@ -1,5 +1,4 @@
-ALTER TABLE `account`
- ADD COLUMN `last_attempt_ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1' AFTER `last_ip`;
+ALTER TABLE `account` ADD COLUMN `last_attempt_ip` VARCHAR(15) NOT NULL DEFAULT '127.0.0.1' AFTER `last_ip`;
CREATE TABLE `logs_ip_actions` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'Unique Identifier',
diff --git a/sql/updates/world/2014_06_22_00_locales_creature.sql b/sql/updates/world/2014_06_22_00_world_locales_creature.sql
index d24a4dd1f33..d24a4dd1f33 100644
--- a/sql/updates/world/2014_06_22_00_locales_creature.sql
+++ b/sql/updates/world/2014_06_22_00_world_locales_creature.sql
diff --git a/sql/updates/world/2014_06_22_01_gameobject.sql b/sql/updates/world/2014_06_22_01_world_gameobject.sql
index 9a180b24c95..9a180b24c95 100644
--- a/sql/updates/world/2014_06_22_01_gameobject.sql
+++ b/sql/updates/world/2014_06_22_01_world_gameobject.sql
diff --git a/sql/updates/world/2014_06_24_05_world_misc.sql b/sql/updates/world/2014_06_24_05_world_misc.sql
new file mode 100644
index 00000000000..71898ee1569
--- /dev/null
+++ b/sql/updates/world/2014_06_24_05_world_misc.sql
@@ -0,0 +1,68 @@
+-- Add support for quest Changing the Wind's Course - ID: 13058 /Horde/
+SET @Stormhoof := 30388;
+SET @VehicleSpell := 56863;
+SET @Ride := 46598;
+SET @Wind := 30474;
+SET @DropHorn := 56892;
+SET @Stun := 62794;
+SET @Guid := 40509; -- Set by TDB
+
+-- Add spawn in phase 256 for The Wind of North
+DELETE FROM `creature` WHERE `guid`=@Guid;
+INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`modelid`,`equipment_id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`currentwaypoint`,`curhealth`,`curmana`,`MovementType`,`npcflag`,`unit_flags`,`dynamicflags`) VALUES
+(@Guid,@Wind,571,1,256,0,0,7942.61,-2723.29,1138,6.09394,60,0,0,63000,19970,0,0,0,0);
+
+-- SAI and spell support for StormHoof
+UPDATE `creature_template` SET `AIName`='SmartAI',`spell1`=56897,`spell2`=61668,`spell3`=56896 WHERE `entry`=30388;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@Stormhoof;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@Stormhoof,0,0,1,54,0,100,0,0,0,0,0,85,56900,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On summoned - Cast power of Lorehammer on invoker /used to store invoker/'),
+(@Stormhoof,0,1,0,61,0,100,0,0,0,0,0,85,@Ride,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Ivoker cast Ride hardcoded on Stormhoof '),
+(@Stormhoof,0,2,0,4,0,100,1,0,0,0,0,20,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On aggro - Disable auto attacks'),
+(@Stormhoof,0,3,0,8,0,100,0,@Ride,0,0,0,2,2141,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On hit by spell Ride - Change faction to hostile towards Wind'),
+(@Stormhoof,0,4,5,38,0,100,0,0,1,0,0,18,8196,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On data set 0 1 - Set unit flags for No move, Silence and Pacified'),
+(@Stormhoof,0,5,0,61,0,100,0,0,0,0,0,1,0,1000,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Say text 1'),
+(@Stormhoof,0,6,7,38,0,100,0,0,2,0,0,75,@Stun,0,0,0,0,0,19,@Wind,30,0,0,0,0,0,'Stormhoof - On Data set 0 2 - Set stun on wind'),
+(@Stormhoof,0,7,8,61,0,100,0,0,0,0,0,37,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Die'),
+(@Stormhoof,0,8,0,61,0,100,0,0,0,0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linked with previous event - Set unseen'),
+(@Stormhoof,0,9,10,6,0,100,0,0,0,0,0,19,8196,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - On death - Remove unit_flags'),
+(@Stormhoof,0,10,0,61,0,100,0,0,0,0,0,2,35,0,0,0,0,0,1,0,0,0,0,0,0,0,'Stormhoof - Linkedw with previous event - Change faction to 35');
+
+-- Add SAI support for North Wind
+UPDATE `creature_template` SET `mindmg`=327,`maxdmg`=362,`faction`=16,`AIName`='SmartAI' WHERE `entry`=@Wind;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@Wind;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@Wind,0,0,0,0,0,100,0,1000,3000,8000,11000,11,61662,0,0,0,0,0,2,0,0,0,0,0,0,0,'North Wind - IC - Cast Cyclone'),
+(@Wind,0,1,0,0,0,100,0,1000,8000,12000,16000,11,61663,0,0,0,0,0,2,0,0,0,0,0,0,0,'North Wind - IC - Gust of Wind'),
+(@Wind,0,2,3,2,0,100,1,10,20,20000,20000,11,@DropHorn,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Between 10 and 20% HP - Drop Horn'),
+(@Wind,0,3,4,61,0,100,0,0,0,0,0,18,139270,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Set unit flags for No move, Silence and Pacified'),
+(@Wind,0,4,0,61,0,100,0,0,0,0,0,1,0,1200,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Say text 0'),
+(@Wind,0,5,6,52,0,100,0,0,@Wind,0,0,45,0,1,0,0,0,0,19,@Stormhoof,30,0,0,0,0,0,'North Wind - On text 0 over - Set data 0 1 on Stormhoof'),
+(@Wind,0,6,0,61,0,100,0,0,0,0,0,1,1,4000,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Say text 1'),
+(@Wind,0,7,0,52,0,100,0,1,@Wind,0,0,45,0,2,0,0,0,0,19,@Stormhoof,30,0,0,0,0,0,'North Wind - On text over 1 - Data set 0 2 on Stormhoof'),
+(@Wind,0,8,9,38,0,100,1,0,2,0,0,1,2,1000,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - On data set 0 2 - Say text 2'),
+(@Wind,0,9,0,61,0,100,0,0,0,0,0,37,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - Linked with previous event - Die'),
+(@Wind,0,10,0,6,0,100,0,0,0,0,0,19,139270,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - On death - Remove unit_flags'),
+(@Wind,0,11,0,4,0,100,0,0,0,0,0,20,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'North Wind - On Agro - Disable Auto Attack');
+
+-- Texts
+DELETE FROM `creature_text` WHERE `entry` IN (@Wind,@Stormhoof);
+
+INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`, `BroadcastTextID`) VALUES
+(@Stormhoof, 0, 0, 'The horn! Use the horn on it while it''s weak!', 14, 0, 100, 1, 1000, 0, 'Stormhoof', 31183),
+(@Wind, 2, 0, 'That horn is... MINE!', 14, 0, 100, 1, 2000, 0, 'North Wind', 31195),
+(@Wind, 1, 0, 'I will never allow you to use the Horn of Elemental Fury against us! Die, weakling!', 14, 0, 100, 1, 3000, 0, 'North Wind', 31181),
+(@Wind, 0, 0, 'Fool! You will never defeat me!', 14, 0, 100, 1, 1200, 0, 'North Wind', 31180);
+
+-- Add SAI for Horn of Elemental Fury
+SET @GO := 194123;
+UPDATE `gameobject_template` SET `AIName`= 'SmartGameObjectAI' WHERE `entry`=@GO;
+DELETE FROM `smart_scripts` WHERE `source_type`=1 AND `entryorguid`=@GO;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@GO,1,0,0,70,0,100,0,2,0,0,0,45,0,2,0,0,0,0,19,@Wind,30,0,0,0,0,0,'Horn of Elemental Fury - On state changed - Data set 0 2 on The Winf of North');
+
+-- Add Npc_spell_click data
+UPDATE `creature_template` SET `npcflag`=16777216 WHERE `entry`=@Stormhoof;
+DELETE FROM `npc_spellclick_spells` WHERE `npc_entry`=@Stormhoof;
+INSERT INTO `npc_spellclick_spells` (`npc_entry`,`spell_id`,`cast_flags`,`user_type`) VALUES
+(@Stormhoof,@Ride,1,0);
diff --git a/sql/updates/world/2014_06_24_06_world_misc.sql b/sql/updates/world/2014_06_24_06_world_misc.sql
new file mode 100644
index 00000000000..0c01fade351
--- /dev/null
+++ b/sql/updates/world/2014_06_24_06_world_misc.sql
@@ -0,0 +1,12 @@
+--
+SET @GRICKSBONESAW :=43286;
+SET @DERANGEDEXPLORER :=23967;
+
+DELETE FROM `conditions` WHERE `SourceEntry`=@GRICKSBONESAW AND `SourceTypeOrReferenceId`=17;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(17,0,@GRICKSBONESAW ,0,0,36,1,0,0,0,1,0,'', 'Gricks Bonesaw can target only a corpse'),
+(17,0,@GRICKSBONESAW ,0,0,31,1,3,@DERANGEDEXPLORER,0,0,0,'', 'Gricks Bonesaw can target only Deranged Explorer');
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry` = @DERANGEDEXPLORER;
+DELETE FROM `smart_scripts` WHERE `entryorguid` = @DERANGEDEXPLORER AND `source_type`=0;
+INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
+(@DERANGEDEXPLORER,0,0,1,8,0,100,1,@GRICKSBONESAW,0,0,0,41,1000,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Deranged Explorer - On Spellhit Despawn after 1 second');
diff --git a/sql/updates/world/2014_06_24_07_world_spell_proc_event.sql b/sql/updates/world/2014_06_24_07_world_spell_proc_event.sql
new file mode 100644
index 00000000000..5eafb052acb
--- /dev/null
+++ b/sql/updates/world/2014_06_24_07_world_spell_proc_event.sql
@@ -0,0 +1,5 @@
+-- Improved Mana Gems
+DELETE FROM `spell_proc_event` WHERE `entry` IN (37447, 61062);
+INSERT INTO `spell_proc_event` (`entry`,`SchoolMask`,`SpellFamilyName`,`SpellFamilyMask0`,`SpellFamilyMask1`,`SpellFamilyMask2`,`procFlags`,`procEx`,`ppmRate`,`CustomChance`,`Cooldown`) VALUES
+(37447, 0, 3, 0, 0x00000100, 0, 0x04000, 0, 0, 0, 0), -- Serpent-Coil Braid
+(61062, 0, 3, 0, 0x00000100, 0, 0x04000, 0, 0, 0, 0); -- 2/5 Frostfire Garb
diff --git a/sql/updates/world/2014_06_24_08_world_misc.sql b/sql/updates/world/2014_06_24_08_world_misc.sql
new file mode 100644
index 00000000000..9dd8f8f4691
--- /dev/null
+++ b/sql/updates/world/2014_06_24_08_world_misc.sql
@@ -0,0 +1,15 @@
+-- Disable Midsummer Fire Festival quests that were removed in patch 2.4.0
+DELETE FROM `disables` WHERE `sourceType`=1 AND `entry` IN (9367,9368,9319,9386,9322,9323,9388,9389);
+INSERT INTO `disables` (`sourceType`,`entry`,`flags`,`params_0`,`params_1`,`comment`) VALUES
+(1,9367,0,0,0,'Quest: The Festival of Fire {A}, removed in patch 2.4.0'),
+(1,9368,0,0,0,'Quest: The Festival of Fire {H}, removed in patch 2.4.0'),
+(1,9319,0,0,0,'Quest: A Light in Dark Places, removed in patch 2.4.0'),
+(1,9386,0,0,0,'Quest: A Light in Dark Places {Daily}, removed in patch 2.4.0'),
+(1,9322,0,0,0,'Quest: Wild Fires in Kalimdor, removed in patch 2.4.0'),
+(1,9323,0,0,0,'Quest: Wild Fires in the Eastern Kingdoms, removed in patch 2.4.0'),
+(1,9388,0,0,0,'Quest: Flickering Flames in Kalimdor, removed in patch 2.4.0'),
+(1,9389,0,0,0,'Quest: Flickering Flames in the Eastern Kingdoms, removed in patch 2.4.0');
+
+-- Remove above mentioned quests from QuestStarters and QuestEnders
+DELETE FROM `creature_queststarter` WHERE `quest` IN (9367,9368,9319,9386,9322,9323,9388,9389);
+DELETE FROM `creature_questender` WHERE `quest` IN (9367,9368,9319,9386,9322,9323,9388,9389);
diff --git a/sql/updates/world/2014_06_24_09_world_misc.sql b/sql/updates/world/2014_06_24_09_world_misc.sql
new file mode 100644
index 00000000000..fc092e08108
--- /dev/null
+++ b/sql/updates/world/2014_06_24_09_world_misc.sql
@@ -0,0 +1,99 @@
+--
+SET @CGuid := 71997;
+
+UPDATE `gameobject_template` SET `AIName`='SmartGameObjectAI', `ScriptName`='' WHERE `entry`=164911;
+UPDATE `creature_template` SET `AIName`= 'SmartAI',`unit_flags`=0 WHERE `entry` IN(9537,9541);
+
+DELETE FROM `smart_scripts` WHERE `source_type`=1 AND `entryorguid`=164911;
+DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid`IN(9537,-@CGuid,-@CGuid-1,-@CGuid-2);
+INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
+(164911, 1, 0 ,0, 70, 0, 100, 0, 2, 0, 0,0,45,1,1,0,0,0,0,13,164911,0,200,0,0,0,0, 'Thunderbrew Lager Keg - On State Changed - Set Data'),
+(164911, 1, 1 ,0, 70, 2, 100, 0, 2, 0, 0,0,45,1,1,0,0,0,0,9,9537,0,200,0,0,0,0, 'Thunderbrew Lager Keg - On State Changed - Set Data'),
+(164911, 1, 2 ,0, 38, 0, 100, 0, 1, 1, 0,0,23,1,0,0,0,0,0,9,9537,0,200,0,0,0,0, 'Thunderbrew Lager Keg - On Data Set - Increment Phase'),
+--
+(9537, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Spawn - Set Unit Flags'),
+(9537, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Invisible'),
+(9537, 0, 3 ,4, 38, 0, 100, 1, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Data Set - Set Visible'),
+(9537, 0, 4 ,6, 61, 0, 100, 0, 0, 0, 0,0,45,1,1,0,0,0,0,9,9541,0,200,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Data'),
+(9537, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Say'),
+(9537, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9537,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Start WP'),
+(9537, 0, 8 ,9, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Reached WP 3 - Set Home Position'),
+(9537, 0, 9 ,10, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Unit Flags'),
+(9537, 0, 10 ,15, 61, 0, 100, 0, 0, 0, 0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Say'),
+(9537, 0, 11 ,0, 2, 0, 100, 0, 0, 30, 30000,45000,11,14872,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Less than 30% HP - Cast Drunken Rage'),
+(9537, 0, 12 ,0, 9, 0, 100, 0, 0, 5, 10000,15000,11,9573,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Hurley Blackbreath - On Range - Cast Flame Breath'),
+(9537, 0, 13 ,0, 0, 0, 100, 0, 5000, 6000, 10000,14000,11,26211,0,0,0,0,0,2,0,0,0,0,0,0,0, 'Hurley Blackbreath - IC - Cast Hamstring'),
+(9537, 0, 14 ,0, 0, 0, 100, 0, 5000, 8000, 8000,13000,11,16856,0,0,0,0,0,2,0,0,0,0,0,0,0, 'Hurley Blackbreath - IC - Cast Mortal Strike'),
+(9537, 0, 15 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Set Random Movement'),
+(9537, 0, 16 ,17, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Hurley Blackbreath - On Agro - Set Set Data'),
+(9537, 0, 17 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Attack'),
+(9537, 0, 18 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Hurley Blackbreath - On Data Set - Attack'),
+--
+(-@CGuid, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Spawn - Set Unit Flags'),
+(-@CGuid, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Invisible'),
+(-@CGuid, 0, 2 ,4, 38, 0, 100, 0, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Set visible'),
+(-@CGuid, 0, 4 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9541,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Start WP'),
+(-@CGuid, 0, 5 ,6, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Reached WP 5 - Set Home Position'),
+(-@CGuid, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Unit Flags'),
+(-@CGuid, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Random Movement'),
+(-@CGuid, 0, 8, 0, 0, 0, 100, 0, 1000, 3000, 4000, 7000, 11, 15581, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Sinister Strike'),
+(-@CGuid, 0, 9, 0, 0, 0, 100, 0, 2000, 2000, 11000, 12000, 11, 15583, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Rupture'),
+(-@CGuid, 0, 10 ,11, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Hurley Blackbreath - On Agro - Set Set Data'),
+(-@CGuid, 0, 11 ,12,61, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9537,0,200,0,0,0,0, 'Hurley Blackbreath - On Agro - Set Set Data'),
+(-@CGuid, 0, 12 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Hurley Blackbreath - Linked with Previous Event - Attack'),
+(-@CGuid, 0, 13 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Hurley Blackbreath - On Data Set - Attack'),
+--
+(-@CGuid-1, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Spawn - Set Unit Flags'),
+(-@CGuid-1, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Invisible'),
+(-@CGuid-1, 0, 2 ,4, 38, 0, 100, 0, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Set visible'),
+(-@CGuid-1, 0, 4 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9541000,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Start WP'),
+(-@CGuid-1, 0, 5 ,6, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Reached WP 5 - Set Home Position'),
+(-@CGuid-1, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Unit Flags'),
+(-@CGuid-1, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Random Movement'),
+(-@CGuid-1, 0, 8, 0, 0, 0, 100, 0, 1000, 3000, 4000, 7000, 11, 15581, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Sinister Strike'),
+(-@CGuid-1, 0, 9, 0, 0, 0, 100, 0, 2000, 2000, 11000, 12000, 11, 15583, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Rupture'),
+(-@CGuid-1, 0, 10 ,11, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'),
+(-@CGuid-1, 0, 11 ,12,61, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9537,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'),
+(-@CGuid-1, 0, 12 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Attack'),
+(-@CGuid-1, 0, 13 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Attack'),
+--
+(-@CGuid-2, 0, 0 ,1, 11, 0, 100, 0, 0, 0, 0,0,18,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Spawn - Set Unit Flags'),
+(-@CGuid-2, 0, 1 ,0, 61, 0, 100, 0, 0, 0, 0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Invisible'),
+(-@CGuid-2, 0, 2 ,4, 38, 0, 100, 0, 1, 1, 0,0,47,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Set visible'),
+(-@CGuid-2, 0, 4 ,0, 61, 0, 100, 0, 0, 0, 0,0,53,0,9541001,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Start WP'),
+(-@CGuid-2, 0, 5 ,6, 40, 0, 100, 0, 3, 0, 0,0,101,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - On Reached WP 5 - Set Home Position'),
+(-@CGuid-2, 0, 6 ,7, 61, 0, 100, 0, 0, 0, 0,0,19,320,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Unit Flags'),
+(-@CGuid-2, 0, 7 ,0, 61, 0, 100, 0, 0, 0, 0,0,89,3,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Set Random Movement'),
+(-@CGuid-2, 0, 8, 0, 0, 0, 100, 0, 2000, 2000, 11000, 12000, 11, 15581, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Sinister Strike'),
+(-@CGuid-2, 0, 9, 0, 0, 0, 100, 0, 3000, 5000, 15000, 20000, 11, 15583, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - IC - Cast Rupture'),
+(-@CGuid-2, 0, 10 ,11, 4, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9541,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'),
+(-@CGuid-2, 0, 11 ,12,61, 0, 100, 0, 0, 0, 0,0,45,1,2,0,0,0,0,9,9537,0,200,0,0,0,0, 'Blackbreath Crony - On Agro - Set Set Data'),
+(-@CGuid-2, 0, 12 ,0, 61, 0, 100, 0, 0, 0, 0,0,49,0,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Blackbreath Crony - Linked with Previous Event - Attack'),
+(-@CGuid-2, 0, 13 ,0, 38, 0, 100, 0, 1, 2, 0,0,49,0,0,0,0,0,0,21,200,0,0,0,0,0,0, 'Blackbreath Crony - On Data Set - Attack');
+
+DELETE FROM `creature_text` WHERE `entry` =9537;
+INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`, `BroadcastTextID`) VALUES
+(9537, 1, 0, 'You''ll pay for that!', 12, 0, 100, 0, 0, 0, 'Hurley Blackbreath', 4936),
+(9537, 0, 0, 'Get away from those kegs!', 14, 0, 100, 0, 0, 0, 'Hurley Blackbreath', 4934);
+
+DELETE FROM `waypoints` WHERE `entry`IN (9537,9541,9541000,9541001);
+INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `point_comment`) VALUES
+(9537, 1, 886.916870,-149.176147,-49.760010, 'Hurley Blackbreath'),
+(9537, 2, 897.698730,-137.324112,-49.752193, 'Hurley Blackbreath'),
+(9537, 3, 898.569946,-141.830872,-49.755016, 'Hurley Blackbreath'),
+(9541, 1, 885.926453,-145.907608,-49.760010, 'Blackbreath Crony'),
+(9541, 2, 891.983093,-138.613907,-49.754261, 'Blackbreath Crony'),
+(9541, 3, 896.412170,-144.160172,-49.757446, 'Blackbreath Crony'),
+(9541000, 1, 888.984070,-152.598846,-49.760010, 'Blackbreath Crony'),
+(9541000, 2, 895.560303,-134.140274,-49.748451, 'Blackbreath Crony'),
+(9541000, 3, 897.199036,-139.512802,-49.755379, 'Blackbreath Crony'),
+(9541001, 1, 886.916870,-149.176147,-49.760010, 'Blackbreath Crony'),
+(9541001, 2, 899.913330,-133.534821,-49.747944, 'Blackbreath Crony'),
+(9541001, 3, 901.009399,-139.603699,-49.755054, 'Blackbreath Crony');
+
+DELETE FROM `creature` WHERE `id` IN(9537,9541);
+INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `phaseMask`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`) VALUES
+(@CGuid, 9541, 230, 1, 1, 0, 1, 879.018, -155.273, -49.7591, 0.57177, 300, 0, 0, 7359, 0, 0, 0, 0, 0),
+(@CGuid+1, 9541, 230, 1, 1, 0, 1, 876.961, -151.796, -49.7591, 0.499513, 300, 0, 0, 7359, 0, 0, 0, 0, 0),
+(@CGuid+2, 9541, 230, 1, 1, 0, 1, 876.535, -154.334, -49.7586, 0.339292, 300, 0, 0, 7359, 0, 0, 0, 0, 0),
+(@CGuid+3, 9537, 230, 1, 1, 0, 1, 878.121, -153.074, -49.76, 0.527783, 300, 0, 0, 13070, 0, 0, 0, 0, 0);
diff --git a/sql/updates/world/2014_06_25_00_world_quest.sql b/sql/updates/world/2014_06_25_00_world_quest.sql
new file mode 100644
index 00000000000..77f5bf05471
--- /dev/null
+++ b/sql/updates/world/2014_06_25_00_world_quest.sql
@@ -0,0 +1,2 @@
+--
+UPDATE `quest_template` SET `SpecialFlags`=0 WHERE `Id`=8490;
diff --git a/sql/updates/world/2014_06_25_01_world_misc.sql b/sql/updates/world/2014_06_25_01_world_misc.sql
new file mode 100644
index 00000000000..88e47f6510d
--- /dev/null
+++ b/sql/updates/world/2014_06_25_01_world_misc.sql
@@ -0,0 +1,33 @@
+--
+SET @CGUID := 127340;
+INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `MovementType`) VALUES
+(@CGUID+0, 32801, 571, 1, 1, 4131.279, 5394.604, 26.09047, 3.595378, 120, 0, 0), -- 32801
+(@CGUID+1, 32809, 571, 1, 1, 4454.667, 5623.537, 56.91562, 4.13643, 120, 0, 0), -- 32809
+(@CGUID+2, 32810, 571, 1, 1, 5499.812, 4878.976, -197.8652, 3.176499, 120, 0, 0), -- 32810
+(@CGUID+3, 32802, 571, 1, 1, 5360.002, 4834.393, -196.3981, 5.550147, 120, 0, 0), -- 32802
+(@CGUID+4, 32811, 571, 1, 1, 3762.387, 1481.287, 92.8882, 3.438299, 120, 0, 0), -- 32811
+(@CGUID+5, 32803, 571, 1, 1, 3936.924, -595.4437, 241.1534, 5.951573, 120, 0, 0), -- 32803
+(@CGUID+6, 32812, 571, 1, 1, 2586.682, -4337.068, 276.0698, 4.13643, 120, 0, 0), -- 32812
+(@CGUID+7, 32804, 571, 1, 1, 2466.965, -4892.675, 262.5474, 2.303835, 120, 0, 0), -- 32804
+(@CGUID+8, 32813, 571, 1, 1, 3376.009, -2124.778, 124.664, 0.1396263, 120, 0, 0), -- 32813
+(@CGUID+9, 32805, 571, 1, 1, 3400.944, -2890.375, 201.4968, 2.303835, 120, 0, 0), -- 32805
+(@CGUID+10, 32814, 571, 1, 1, 6150.945, -1023.05, 408.3642, 1.43117, 120, 0, 0), -- 32814
+(@CGUID+11, 32806, 571, 1, 1, 6087.442, -1105.523, 418.2672, 1.099557, 120, 0, 0), -- 32806
+(@CGUID+12, 32815, 571, 1, 1, 5536.516, -733.7185, 149.6217, 3.124139, 120, 0, 0), -- 32815
+(@CGUID+13, 32807, 571, 1, 1, 5141.776, -685.0027, 170.2738, 5.951573, 120, 0, 0), -- 32807
+(@CGUID+14, 32816, 571, 1, 1, 5280.307, -2766.146, 292.5019, 2.303835, 120, 0, 0), -- 32816
+(@CGUID+15, 32808, 571, 1, 1, 5627.932, -2616.493, 292.5019, 1.518436, 120, 0, 0), -- 32808
+(@CGUID+16, 25892, 0, 1, 1, -8258.798, -2618.704, 133.3752, 1.343904, 120, 0, 0), -- 25892
+(@CGUID+17, 25899, 1, 1, 1, -4391.495, 2187.865, 12.22644, 5.864306, 120, 0, 0), -- 25899
+(@CGUID+18, 25936, 1, 1, 1, -2321.744, -619.7491, -9.089149, 5.67232, 120, 0, 0), -- 25936
+(@CGUID+19, 25888, 530, 1, 1, -4219.219, -12310.55, 2.439188, 5.934119, 120, 0, 0), -- 25888
+(@CGUID+20, 25891, 530, 1, 1, -2234.83, -11895.94, 26.62421, 4.694936, 120, 0, 0), -- 25891
+(@CGUID+21, 25907, 530, 1, 1, -2998.854, 4164.042, 5.169014, 0.5934119, 120, 0, 0), -- 25907
+(@CGUID+22, 25938, 530, 1, 1, -3067.278, 2387.903, 62.19349, 3.368485, 120, 0, 0), -- 25938
+(@CGUID+23, 25905, 530, 1, 1, -3951.305, 2045.65, 95.14804, 4.520403, 120, 0, 0), -- 25905
+(@CGUID+24, 25912, 530, 1, 1, 193.7309, 6016.713, 22.73373, 6.161012, 120, 0, 0), -- 25912
+(@CGUID+25, 25903, 530, 1, 1, -2517.064, 7555.085, -1.793157, 5.375614, 120, 0, 0), -- 25903
+(@CGUID+26, 25889, 530, 1, 1, 2024.906, 6590.532, 135.3214, 5.934119, 120, 0, 0), -- 25889
+(@CGUID+27, 25926, 530, 1, 1, 2286.707, 6137.618, 136.3056, 1.570796, 120, 0, 0), -- 25926
+(@CGUID+28, 25913, 530, 1, 1, 3127.551, 3743.148, 141.7706, 6.161012, 120, 0, 0), -- 25913
+(@CGUID+29, 25918, 530, 1, 1, 2917.222, 3693.411, 143.7703, 2.932153, 120, 0, 0); -- 25918
diff --git a/sql/updates/world/2014_06_25_02_world_creature.sql b/sql/updates/world/2014_06_25_02_world_creature.sql
new file mode 100644
index 00000000000..8c43786c8e8
--- /dev/null
+++ b/sql/updates/world/2014_06_25_02_world_creature.sql
@@ -0,0 +1,6 @@
+--
+SET @CGUID := 72776;
+INSERT INTO `creature` (`guid`, `id`, `map`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `MovementType`) VALUES
+(@CGUID+0, 23108, 530, 1, 1, -493.0895, 7448.848, 178.6083, 4.83718, 120, 5, 1), -- 23108 (Area: 0)
+(@CGUID+1, 23107, 530, 1, 1, -475.5646, 7463.805, 181.9694, 1.496497, 120, 5, 1), -- 23107 (Area: 0)
+(@CGUID+2, 23106, 530, 1, 1, -511.7721, 7500.889, 183.1165, 5.096361, 120, 0, 0); -- 23106 (Area: 0)
diff --git a/sql/updates/world/2014_06_25_03_world_misc.sql b/sql/updates/world/2014_06_25_03_world_misc.sql
new file mode 100644
index 00000000000..3ddd98aa8df
--- /dev/null
+++ b/sql/updates/world/2014_06_25_03_world_misc.sql
@@ -0,0 +1,40 @@
+-- Add support for {Q} A Tangled Skein ID: 12555
+-- rewritten from sniff: thx Pitcrawler
+SET @Sprayer := 28274;
+SET @Thrower := 51165; -- Throws a web
+SET @Summon := 51314; -- Summons Broken Sprayer GO
+SET @Explosion := 53236; -- Explosion on fall
+SET @Credit := 28289; -- Kill Credit
+SET @Trigger := 29457; -- Trigger to permit explosion only when Sprayer reach him
+SET @SpellTrigger := 51173; -- A Tangled Skein: Encasing Webs - Effect that procs from @Thrower
+SET @EncasingWebs := 51168; -- The visual that A Tangled Skein: Encasing Webs - Effect should apply
+SET @SummonTrigger := 54496;
+SET @Script := 2827400;
+-- Add SAI support for Plague Sprayer
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=@Sprayer; -- they shouldn't be attackable, but unit_flag 2 cause evade
+UPDATE `creature_template` SET `AIName`='SmartAI',`InhabitType`=1 WHERE `entry`=@Trigger; -- need this to make trigger (while guardian) stay at ground
+DELETE FROM `smart_scripts` WHERE `entryorguid` IN (@Sprayer,@Trigger) AND `source_type`=0;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@Script AND `source_type`=9;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`,`event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@Trigger,0,0,0,54,0,100,0,0,0,0,0,75,@EncasingWebs,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Plagued Sprayer - On just summoned - Cast Encasing Webs'),
+(@Sprayer,0,0,0,25,0,100,0,0,0,0,0,8,0,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - On spawn/reset - Set react state passive'),
+(@Sprayer,0,1,0,8,0,100,1,@Thrower,0,0,0,64,1,0,0,0,0,0,7,0,0,0,0,0,0,0, 'Plagued Sprayer - On spell hit by player with thrower - Store invoker as target'),
+(@Sprayer,0,2,3,8,0,100,1,@SpellTrigger,0,0,0,11,@SummonTrigger,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - On spell hit by item spell - Cast summon trigger'),
+(@Sprayer,0,3,0,61,0,100,1,0,0,0,0,59,1,0,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - Linked with previous event - Set run on'),
+(@Sprayer,0,4,0,23,0,100,1,@EncasingWebs,1,0,0,69,1,0,0,0,0,0,19,@Trigger,20,0,0,0,0,0, 'Plagued Sprayer - On creature has aura Encasing Webs - Move to closest trigger in 20 yards'),
+(@Sprayer,0,5,0,34,0,100,1,8,1,0,0,80,@Script,2,0,0,0,0,1,0,0,0,0,0,0,0, 'Plagued Sprayer - On point 1 reached - Start action list'),
+-- Script
+(@Script,9,0,0,0,0,100,0,0,0,0,0,11,@Explosion,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 0 - Cast huge explosion on self'),
+(@Script,9,1,0,0,0,100,0,300,300,0,0,33,@Credit,0,0,0,0,0,12,1,0,0,0,0,0,0,'Plagued Sprayer - Action 1 - Give credit to stored invoker'),
+(@Script,9,2,0,0,0,100,0,0,0,0,0,11,@Summon,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 2 - Cast summon broken GO sprayer on self'), -- need small delay for the explosion to be displayed
+(@Script,9,3,0,0,0,100,0,0,0,0,0,37,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 3 - Die'),
+(@Script,9,4,0,0,0,100,0,0,0,0,0,47,0,0,0,0,0,0,1,0,0,0,0,0,0,0,'Plagued Sprayer - Action 4 - Set unseen');
+-- Conditions
+DELETE FROM `conditions` WHERE `SourceEntry`=@Thrower AND `SourceTypeOrReferenceId`=17;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(17,0,@Thrower,0,0,31,1,3,@Sprayer,0,0,0,'', 'Thrower can target only Plague Sprayer');
+-- Delete wrong, nonexistant Trigger spawn
+DELETE FROM `creature` WHERE `id`=@Trigger;
+-- Update creature data for plague sprayers they should have movement type 0
+UPDATE `creature` SET `spawndist`=0,`MovementType`=0 WHERE `id`=@Sprayer;
+UPDATE `creature_template` SET `InhabitType`=4 WHERE `entry`=28274;
diff --git a/sql/updates/world/2014_06_25_04_world_misc.sql b/sql/updates/world/2014_06_25_04_world_misc.sql
new file mode 100644
index 00000000000..3bd4bbaafe2
--- /dev/null
+++ b/sql/updates/world/2014_06_25_04_world_misc.sql
@@ -0,0 +1,10 @@
+-- Fix for ... and a Batch of Ooze (4294) and ... and a Batch of Ooze (4293)
+UPDATE `quest_template` SET `PrevQuestId`=0 WHERE `Id`=4661; -- Testing for Impurities - Un'Goro Crater
+UPDATE `quest_template` SET `PrevQuestId`=0 WHERE `Id`=4561; -- Testing for Corruption - Felwood
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` IN(19,20) AND `SourceEntry` IN (4661,4561);
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(19, 0, 4561, 0, 0, 9, 0, 4294, 0, 0, 0, 0, 0, '', 'player needs to be on quest ... and a Batch of Ooze to see Testing for Impurities - UnGoro Crater'),
+(19, 0, 4661, 0, 0, 9, 0, 4293, 0, 0, 0, 0, 0, '', 'player needs to be on quest and a batch of slime to see Testing for Corruption - Felwood'),
+(20, 0, 4561, 0, 0, 9, 0, 4294, 0, 0, 0, 0, 0, '', 'player needs to be on quest ... and a Batch of Ooze to see Testing for Impurities - UnGoro Crater'),
+(20, 0, 4661, 0, 0, 9, 0, 4293, 0, 0, 0, 0, 0, '', 'player needs to be on quest and a batch of slime to see Testing for Corruption - Felwood');
diff --git a/sql/updates/world/2014_06_25_05_world_misc.sql b/sql/updates/world/2014_06_25_05_world_misc.sql
new file mode 100644
index 00000000000..e253e445e01
--- /dev/null
+++ b/sql/updates/world/2014_06_25_05_world_misc.sql
@@ -0,0 +1,17 @@
+UPDATE `creature_template` SET `AIName`= 'SmartAI',`ScriptName`='' WHERE `entry` =17542;
+DELETE FROM `smart_scripts` WHERE `entryorguid` =17542;
+
+INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES
+(17542,0,0,1,8,0,100,0,8593,0,0,0,33,17542,0,0,0,0,0,7,0,0,0,0,0,0,0,'Young Furbolg Shaman - On Spell Hit - Give Kill Credit'),
+(17542,0,1,2,61,0,100,0,0,0,0,0,28,29266,0,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - Linked with Previous Event - Remove permament Feign Death'),
+(17542,0,2,0,61,0,100,0,0,0,0,0,23,2,0,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - Linked with Previous Event - Set Phase 2'),
+
+(17542,0,4,5, 1,2,100,0,60000,60000,60000,60000,11,29266,2,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - OOC - Cast Permament Feign Death'),
+(17542,0,5,0,61,2,100,0,0,0,0,0,23,1,0,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - Linked with Previous Event - Set Phase 1'),
+(17542,0,6,0,25,0,100,0,0,0,0,0,11,29266,2,0,0,0,0,1,0,0,0,0,0,0,0,'Young Furbolg Shaman - On Spawn - Cast Permament Feign Death');
+
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=8593;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(13, 1, 8593, 0, 0, 31, 0, 3, 17542, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Young Furbolg Shaman'),
+(13, 1, 8593, 0, 1, 31, 0, 3, 6172, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Henze Faulk'),
+(13, 1, 8593, 0, 2, 31, 0, 3, 6177, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Narm Faulk');
diff --git a/sql/updates/world/2014_06_25_06_world_conditions.sql b/sql/updates/world/2014_06_25_06_world_conditions.sql
new file mode 100644
index 00000000000..8bd179e360b
--- /dev/null
+++ b/sql/updates/world/2014_06_25_06_world_conditions.sql
@@ -0,0 +1,4 @@
+--
+DELETE FROM `conditions` WHERE `SourceEntry`=28700 AND `SourceTypeOrReferenceId`=17;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `Comment`) VALUES
+(17,0,28700,0,0,30,0,181433,15,0,0,0,0,'Disperse Neutralizing Agent (28700) can be used only near the Irradiated Power Crystal (181433)');
diff --git a/sql/updates/world/2014_06_25_07_world_conditions.sql b/sql/updates/world/2014_06_25_07_world_conditions.sql
new file mode 100644
index 00000000000..fd1c5a2b681
--- /dev/null
+++ b/sql/updates/world/2014_06_25_07_world_conditions.sql
@@ -0,0 +1,5 @@
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=8593;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(13, 1, 8593, 0, 0, 31, 0, 3, 17542, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Young Furbolg Shaman'),
+(13, 1, 8593, 0, 1, 31, 0, 3, 6172, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Henze Faulk'),
+(13, 1, 8593, 0, 2, 31, 0, 3, 6177, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Narm Faulk');
diff --git a/sql/updates/world/2014_06_25_08_world_conditions.sql b/sql/updates/world/2014_06_25_08_world_conditions.sql
new file mode 100644
index 00000000000..e2e904ae063
--- /dev/null
+++ b/sql/updates/world/2014_06_25_08_world_conditions.sql
@@ -0,0 +1,8 @@
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=8593 OR `SourceTypeOrReferenceId`=17 AND `SourceEntry`=8593;
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES
+(17, 0, 8593, 0, 0, 29, 0, 17542, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards of Young Furbolg Shaman'),
+(17, 0, 8593, 0, 1, 29, 0, 6172, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'),
+(17, 0, 8593, 0, 2, 29, 0, 6177, 40, 0, 0, 0, 0, '', 'Symbol of Life can be casted within 40 yards Henze Faulk'),
+(13, 1, 8593, 0, 0, 31, 0, 3, 17542, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Young Furbolg Shaman'),
+(13, 1, 8593, 0, 1, 31, 0, 3, 6172, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Henze Faulk'),
+(13, 1, 8593, 0, 2, 31, 0, 3, 6177, 0, 0, 0, 0, '', 'Symbol of Life can be casted on Narm Faulk');
diff --git a/sql/updates/world/2014_06_26_00_world_misc.sql b/sql/updates/world/2014_06_26_00_world_misc.sql
new file mode 100644
index 00000000000..b116a05979a
--- /dev/null
+++ b/sql/updates/world/2014_06_26_00_world_misc.sql
@@ -0,0 +1,232 @@
+/* [Q] A Suitable Disguise
+
+SUMMARY:
+Basically we are running a random script (4 in total) every 12 seconds after gossip select. Every script has a different subject. For example when we run script @ENTRY*100+1 it will require you to throw a bucket of water into the tub. If you don't do this on time, the event will fail and you'll have to start it over again.
+The gossip flags are turned off on gossip select (@ENTRY*100+0) because we don't want the same event to be ran more than once at a time.
+As you might have noticed we are using phases a LOT. This helps us to basically add conditions to some lines so they won't occur all the time, yet we can easily time them OOC.
+We are also using Data Set event and action types a lot. As you can see when a bunny is hit by the spell which belongs to it, it will set data to Shandy which makes Shandy say something like 'Well done'.
+The way the gameobjects are handled is maybe pretty much a work-around but it will need a LOT of core work to make this work like it works on Blizzlike. It should be different objects if you're either horde or alliance. Right now I've just spawned them on-top of each other, because in the end you can only use the object which belongs to your faction.
+
+
+PROBLEMS:
+Shandy Glossgleam should start a waypoint after completing script. (wtb sniffs)
+Aquanos' evocation spell seems to have a bugged effect.
+
+RESOURCES:
+http://www.youtube.com/watch?v=Fe4mRWO20Hw
+http://www.youtube.com/watch?v=tqBbFr_BhC4
+http://www.youtube.com/watch?v=OFBRam2MgUw
+http://www.wowwiki.com/Quest:A_Suitable_Disguise_(Alliance)
+
+PHASES:
+Phase 0: when event did not start yet
+Phase 1: when event is happening
+Phase 2: when event failed
+Phase 4: resetting everything */
+
+-- Shandy Glossgleam SAI
+SET @ENTRY := 36856;
+SET @QUEST_A := 20438;
+SET @QUEST_H := 24556;
+SET @OGUID := 56249;
+SET @GOSSIP := 10854;
+
+UPDATE `creature_template` SET `AIName`='SmartAI',`gossip_menu_id`=@GOSSIP WHERE `entry`=@ENTRY;
+
+DELETE FROM `smart_scripts` WHERE `entryorguid` IN (@ENTRY,@ENTRY*100+0,@ENTRY*100+1,@ENTRY*100+2,@ENTRY*100+3,@ENTRY*100+4,@ENTRY*100+5,@ENTRY*100+6,@ENTRY*100+7);
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@ENTRY,0,0,0,62,0,100,0,@GOSSIP,0,0,0,80,@ENTRY*100+0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Gossip Select - Run script"),
+(@ENTRY,0,1,0,1,1,100,0,8000,8000,12000,12000,88,@ENTRY*100+1,@ENTRY*100+4,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Run Random Script"),
+(@ENTRY,0,2,0,38,1,100,0,1,1,0,0,80,@ENTRY*100+5,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Data Set 1 1 - Run Task Successful Script"),
+(@ENTRY,0,3,0,1,1,100,0,120000,120000,120000,120000,80,@ENTRY*100+7,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Run Complete Script"),
+(@ENTRY,0,4,0,1,2,100,0,0,0,0,0,80,@ENTRY*100+6,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Run Failure Script"),
+(@ENTRY,0,5,6,1,4,100,0,30000,30000,30000,30000,81,3,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Set Back Gossip & Quest Flags"), -- This can only occur in phase 4 so no need to time it (P4 is set in completion script)
+(@ENTRY,0,6,7,61,4,100,0,0,0,0,0,22,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Set Phase 0"),
+(@ENTRY,0,7,0,61,4,100,0,0,0,0,0,78,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Timed - Reset Scripts"),
+(@ENTRY,0,8,0,40,0,100,0,16,@ENTRY,0,0,41,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Reached WP 16 - Despawn"),
+(@ENTRY,0,9,0,62,0,100,0,@GOSSIP,1,0,0,80,@ENTRY*100+0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - On Gossip Select - Run script"),
+
+-- Start script
+(@ENTRY*100+0,9,0,0,0,0,100,0,0,0,0,0,72,0,0,0,0,0,0,7,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Close Gossip"),
+(@ENTRY*100+0,9,1,0,0,0,100,0,0,0,0,0,81,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Turn of Gossip & Questgiver flags"),
+(@ENTRY*100+0,9,2,0,0,0,100,0,1000,1000,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Say Line 0"),
+(@ENTRY*100+0,9,3,0,0,0,100,0,5000,5000,0,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Say Line 1"),
+(@ENTRY*100+0,9,4,0,0,0,100,0,0,0,0,0,22,1,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 0 - Set Phase 1"),
+-- Wants Water
+(@ENTRY*100+1,9,0,0,0,0,100,0,0,0,0,0,1,4,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 1 - Say Line 4"),
+(@ENTRY*100+1,9,1,0,0,0,100,0,0,0,0,0,12,36947,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 1 - Summon Wants Water"),
+(@ENTRY*100+1,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 1 - Set Phase 2"),
+-- Wants Pants
+(@ENTRY*100+2,9,0,0,0,0,100,0,0,0,0,0,1,5,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 2 - Say Line 5"),
+(@ENTRY*100+2,9,1,0,0,0,100,0,0,0,0,0,12,36945,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 2 - Summon Wants Pants"),
+(@ENTRY*100+2,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 2 - Set Phase 2"),
+-- Wants Unmentionables
+(@ENTRY*100+3,9,0,0,0,0,100,0,0,0,0,0,1,3,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 3 - Say Line 3"),
+(@ENTRY*100+3,9,1,0,0,0,100,0,0,0,0,0,12,36946,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 3 - Summon Wants Unmentionables"),
+(@ENTRY*100+3,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 3 - Set Phase 2"),
+-- Wants Shirts
+(@ENTRY*100+4,9,0,0,0,0,100,0,0,0,0,0,1,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 4 - Say Line 2"),
+(@ENTRY*100+4,9,1,0,0,0,100,0,0,0,0,0,12,36944,1,10000,0,0,0,8,0,0,0,5796.970215,693.942993,658.351990,0,"Shandy Glossgleam - Script 4 - Summon Wants Shirts"),
+(@ENTRY*100+4,9,2,0,0,0,100,0,10000,10000,0,0,22,2,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 4 - Set Phase 2"),
+
+-- Task successful
+(@ENTRY*100+5,9,0,0,0,0,100,0,0,0,0,0,1,6,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 6 - Say Line 6 (random)"),
+-- End failure
+(@ENTRY*100+6,9,0,0,0,0,100,0,0,0,0,0,1,7,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 7 - Say Line 7"),
+(@ENTRY*100+6,9,1,0,0,0,100,0,0,0,0,0,22,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 7 - Set Phase 0"),
+(@ENTRY*100+6,9,2,0,0,0,100,0,0,0,0,0,81,3,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 7 - Set Gossip Flag"),
+-- Completion script
+(@ENTRY*100+7,9,0,0,0,0,100,0,0,0,0,0,78,0,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Stop Current Scripts"),
+(@ENTRY*100+7,9,1,0,0,0,100,0,2000,2000,0,0,1,8,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Say Line 8"),
+(@ENTRY*100+7,9,2,0,0,0,100,0,0,0,0,0,66,0,0,0,0,0,0,19,36851,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Face Aquanos"),
+(@ENTRY*100+7,9,3,0,0,0,100,0,8000,8000,0,0,1,9,0,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Say Line 9"),
+(@ENTRY*100+7,9,4,0,0,0,100,0,0,0,0,0,50,201384,30000,0,0,0,0,8,0,0,0,5797.147461,696.602417,657.949463,6.090852,"Shandy Glossgleam - Script 8 - Summon Clean Laundry"),
+(@ENTRY*100+7,9,5,0,0,0,100,0,3000,3000,0,0,53,0,@ENTRY,0,0,0,0,1,0,0,0,0,0,0,0,"Shandy Glossgleam - Script 8 - Set Phase 4");
+
+-- Texts
+-- They all emote ONESHOT_TALK apart from request texts (as seen in videos)
+DELETE FROM `creature_text` WHERE `entry`=@ENTRY;
+INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`comment`,`BroadcastTextID`) VALUES
+-- Start
+(@ENTRY,0,0,"You're in luck. I've got just what you need in the load I'm about to wash.",12,0,100,0,0,0,"Shandy Glossgleam",36761),
+(@ENTRY,1,0,"See the piles of laundry and the bucket of water? I'll run out what I need next, and you put it in the tub. Ready?",12,0,100,0,0,0,"Shandy Glossgleam",36762),
+-- Requests
+(@ENTRY,2,0,"Quick, add some shirts to the laundry!",12,0,100,0,0,0,"Shandy Glossgleam",36788), -- Emote ONESHOT_POINT
+(@ENTRY,3,0,"Add the unmentionables... uh, I mean, the 'delicates'!",12,0,100,25,0,0,"Shandy Glossgleam",36789), -- Emote ONESHOT_POINT
+(@ENTRY,4,0,"The tub needs more water!",12,0,100,25,0,0,"Shandy Glossgleam",36790), -- Emote ONESHOT_POINT
+(@ENTRY,5,0,"Toss some pants in to the tub!",12,0,100,25,0,0,"Shandy Glossgleam",36787), -- Emote ONESHOT_POINT
+-- Correct
+(@ENTRY,6,0,"I should keep you around.",12,0,100,0,0,0,"Shandy Glossgleam",37989),
+(@ENTRY,6,1,"Well done!",12,0,100,0,0,0,"Shandy Glossgleam",36764),
+(@ENTRY,6,2,"That's how it's done!",12,0,100,0,0,0,"Shandy Glossgleam",37986),
+(@ENTRY,6,3,"Clean and tidy!",12,0,100,0,0,0,"Shandy Glossgleam",37985),
+(@ENTRY,6,4,"Nice. I don't want to know what that stain was.",12,0,100,0,0,0,"Shandy Glossgleam",37987),
+(@ENTRY,6,5,"Aquanos can hardly keep up!",12,0,40,0,0,0,"Shandy Glossgleam",37988),
+-- Incorrect
+(@ENTRY,7,0,"Oh, no! That wasn't right. Now I'll have to go get more detergent so we can start over!",12,0,100,0,0,0,"Shandy Glossgleam",36763),
+-- End
+(@ENTRY,8,0,"Aquanos, stop sending the clothes so high! You didn't have to see the look on Aethas Sunreaver's face when he found his pants in the fountain!",12,0,100,0,0,0,"Shandy Glossgleam",36817),
+(@ENTRY,9,0,"See how easy that was with everyone working together? Just take what you need from the clean laundry here, but don't forget to return it when you're finished.",12,0,100,0,0,0,"Shandy Glossgleam",36791);
+
+-- Give Clean Laundry quest item loot
+DELETE FROM `gameobject_loot_template` WHERE `entry`=27725;
+INSERT INTO `gameobject_loot_template` (`entry`,`item`,`ChanceOrQuestChance`,`lootmode`,`groupid`,`mincountOrRef`,`maxcount`) VALUES
+(27725,49648,-100,1,0,1,1);
+
+-- Summon quest gameobjects
+DELETE FROM `gameobject` WHERE `id` IN (201295,201931,201301,201296,201300,201932,201936,201933,201299,201855,201298,201297,201934,201935) AND `guid` BETWEEN @OGUID AND @OGUID+13;
+INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`) VALUES
+-- Game objects for Alliance quest 20438
+-- Water Bucket
+(@OGUID, 201298, 571, 1, 1, 5807.094, 690.5868, 659.1125, 0, 0, 0, 0, 1, 120, 255, 1), -- 201298 (Area: 4613)
+-- Pants
+(@OGUID+1, 201295, 571, 1, 1, 5806.047, 694.5938, 657.949, 1.710422, 0, 0, 0, 1, 120, 255, 1), -- 201295 (Area: 4613)
+(@OGUID+2, 201931, 571, 1, 1, 5806.068, 694.5799, 658.4567, 0, 0, 0, 0, 1, 120, 255, 1), -- 201931 (Area: 4613)
+-- Shirts
+(@OGUID+3, 201296, 571, 1, 1, 5805.592, 691.0816, 657.9492, 0, 0, 0, 0, 1, 120, 255, 1), -- 201296 (Area: 4613)
+(@OGUID+4, 201932, 571, 1, 1, 5805.556, 691.1024, 658.3948, 0, 0, 0, 0, 1, 120, 255, 1), -- 201932 (Area: 4613)
+-- Unmentionalables
+(@OGUID+5, 201933, 571, 1, 1, 5805.316, 697.5625, 658.3061, 0, 0, 0, 0, 1, 120, 255, 1), -- 201933 (Area: 4613)
+(@OGUID+6, 201297, 571, 1, 1, 5805.338, 697.5729, 657.9791, 1.640607, 0, 0, 0, 1, 120, 255, 1), -- 201297 (Area: 4613)
+-- Game objects for Horde quest 24556
+-- Water Bucket
+(@OGUID+7, 201855, 571, 1, 1, 5807.094, 690.5868, 659.1125, 0, 0, 0, 0, 1, 120, 255, 1), -- 201855 (Area: 4613)
+-- Pants
+(@OGUID+8, 201301, 571, 1, 1, 5806.068, 694.5799, 658.4575, 0, 0, 0, 0, 1, 120, 255, 1), -- 201301 (Area: 4613)
+(@OGUID+9, 201934, 571, 1, 1, 5806.015, 694.6285, 657.949, 1.710422, 0, 0, 0, 1, 120, 255, 1), -- 201934 (Area: 4613)
+-- Shirts
+(@OGUID+10, 201300, 571, 1, 1, 5805.579, 691.0868, 658.3615, 0, 0, 0, 0, 1, 120, 255, 1), -- 201300 (Area: 4613)
+(@OGUID+11, 201935, 571, 1, 1, 5805.587, 691.0608, 657.9492, 0, 0, 0, 0, 1, 120, 255, 1), -- 201935 (Area: 4613)
+-- Unmentionalables
+(@OGUID+12, 201936, 571, 1, 1, 5805.326, 697.5555, 657.949, 1.640607, 0, 0, 0, 1, 120, 255, 1), -- 201936 (Area: 4613)
+(@OGUID+13, 201299, 571, 1, 1, 5805.316, 697.5625, 658.3046, 0, 0, 0, 0, 1, 120, 255, 1); -- 201299 (Area: 4613)
+
+-- Insert option menu
+DELETE FROM `gossip_menu_option` WHERE `menu_id`=@GOSSIP;
+INSERT INTO `gossip_menu_option` (`menu_id`,`id`,`option_icon`,`option_text`,`option_id`,`npc_option_npcflag`,`BoxBroadcastTextID`) VALUES
+(@GOSSIP,0,0,'Arcanist Tybalin said you might be able to lend me a certain tabard.',1,1,36760),
+(@GOSSIP,1,0,'Magister Hathorel said you might be able to lend me a certain tabard.',1,1,37552);
+
+-- Add conditions for gossip - as you see we are using ElseGroup (logical 'OR')
+DELETE FROM `conditions` WHERE `SourceGroup` IN (@GOSSIP) AND `ConditionValue1` IN (@QUEST_A,@QUEST_H);
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES
+(15,@GOSSIP,0,0,9,@QUEST_A,0,0,0,'',"Only show first gossip if player is on quest A Suitable Disguise (A)"),
+(15,@GOSSIP,1,0,9,@QUEST_H,0,0,0,'',"Only show first gossip if player is on quest A Suitable Disguise (H)");
+
+-- Aquanos SAI
+SET @ENTRY := 36851;
+SET @SPELL_EVOCATION := 69659;
+UPDATE `creature_template` SET `AIName`='SmartAI' WHERE `entry`=@ENTRY;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@ENTRY,0,0,0,38,0,100,0,2,2,0,0,11,@SPELL_EVOCATION,1,0,0,0,0,1,0,0,0,0,0,0,0,"Aquanos - On Data Set 2 2 - Cast Evocation (Visual Only)");
+
+-- Wants Water SAI
+SET @ENTRY := 36947;
+SET @SPELL_WATER := 69614;
+UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@ENTRY,0,0,1,8,0,100,1,@SPELL_WATER,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Water - On Spellhit - Set Data Shandy Glossgleam"),
+(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Water - On Spellhit - Set Data Aquanos");
+
+-- Wants Pants SAI
+SET @ENTRY := 36945;
+SET @SPELL_PANTS := 69600;
+UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@ENTRY,0,0,1,8,0,100,1,@SPELL_PANTS,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Pants - On Spellhit - Set Data Shandy Glossgleam"),
+(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Pants - On Spellhit - Set Data Aquanos");
+
+-- Wants Unmentionables SAI
+SET @ENTRY := 36946;
+SET @SPELL_UNMENTIONABLES := 69601;
+UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@ENTRY,0,0,1,8,0,100,1,@SPELL_UNMENTIONABLES,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Unmentionables - On Spellhit - Set Data Shandy Glossgleam"),
+(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Unmentionables - On Spellhit - Set Data Aquanos");
+
+-- Wants Shirts SAI
+SET @ENTRY := 36944;
+SET @SPELL_SHIRT := 69593;
+UPDATE `creature_template` SET `AIName`='SmartAI',`minlevel`=70,`maxlevel`=70,`exp`=2,`unit_class`=2,`unit_flags`=`unit_flags`|33554432,`flags_extra`=`flags_extra`|128 WHERE `entry`=@ENTRY;
+DELETE FROM `smart_scripts` WHERE `entryorguid`=@ENTRY;
+INSERT INTO `smart_scripts` (`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,`event_param1`,`event_param2`,`event_param3`, `event_param4`,`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`, `target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
+(@ENTRY,0,0,1,8,0,100,1,@SPELL_SHIRT,0,0,0,45,1,1,0,0,0,0,19,36856,0,0,0,0,0,0,"Wants Shirts - On Spellhit - Set Data Shandy Glossgleam"),
+(@ENTRY,0,1,0,61,0,100,0,0,0,0,0,45,2,2,0,0,0,0,19,36851,0,0,0,0,0,0,"Wants Shirts - On Spellhit - Set Data Aquanos");
+
+-- The conditions are made this way because the PLAYER should throw the 'item'. (water, shirt, pant, etc.) Basically when you click the object it casts a trigger spell on you. This trigger spell can only target players and will make the player cast 'Toss XX', which has direct conditions to one of the Wants XX imps.
+DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (69593,69600,69601,69614,69548,69542,69544,69543);
+INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`comment`) VALUES
+(13,1,69593,31,3,36944, 'Toss Shirts requires target Wants Shirts'),
+(13,1,69600,31,3,36945, 'Toss Pants requires target Wants Pants'),
+(13,1,69601,31,3,36946, 'Toss Unmentionables requires target Wants Unmentionables'),
+(13,1,69614,31,3,36947, 'Toss Water requires target Wants Water'),
+-- other conditions are unneeded, and having them causes problems.
+-- These are erroring still, but the quest works with this change:
+(13,1,69548,32,128,0,'Trigger Throw Water requires target player'),
+(13,1,69542,32,128,0,'Trigger Throw Pants requires target player'),
+(13,1,69544,32,128,0,'Trigger Throw Unmentionables requires target player'),
+(13,1,69543,32,128,0,'Trigger Throw Shirt requires target player');
+
+-- Waypoints
+DELETE FROM `waypoints` WHERE `entry`=36856;
+INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `point_comment`) VALUES
+(36856, 1, 5800.057, 691.5624, 658.0007, 'Shandy Glossgleam WP 1'),
+(36856, 2, 5802.057, 691.5624, 658.0007, 'Shandy Glossgleam WP 2'),
+(36856, 3, 5802.307, 690.0624, 658.0007, 'Shandy Glossgleam WP 3'),
+(36856, 4, 5802.557, 688.8124, 658.0007, 'Shandy Glossgleam WP 4'),
+(36856, 5, 5802.824, 688.4631, 657.9935, 'Shandy Glossgleam WP 5'),
+(36856, 6, 5803.074, 686.4631, 658.2435, 'Shandy Glossgleam WP 6'),
+(36856, 7, 5803.574, 681.9631, 658.2435, 'Shandy Glossgleam WP 7'),
+(36856, 8, 5804.324, 677.7131, 658.2435, 'Shandy Glossgleam WP 8'),
+(36856, 9, 5804.842, 673.8136, 658.0798, 'Shandy Glossgleam WP 9'),
+(36856, 10, 5802.092, 671.8136, 658.3298, 'Shandy Glossgleam WP 10'),
+(36856, 11, 5801.806, 671.6563, 658.1652, 'Shandy Glossgleam WP 11'),
+(36856, 12, 5801.306, 671.1563, 658.1652, 'Shandy Glossgleam WP 12'),
+(36856, 13, 5800.306, 671.9063, 658.4152, 'Shandy Glossgleam WP 13'),
+(36856, 14, 5799.306, 672.9063, 657.4152, 'Shandy Glossgleam WP 14'),
+(36856, 15, 5798.056, 673.4063, 656.4152, 'Shandy Glossgleam WP 15'),
+(36856, 16, 5795.953, 674.7413, 654.7663, 'Shandy Glossgleam WP 16 - Despawn');
diff --git a/sql/updates/world/2014_06_26_01_world_misc.sql b/sql/updates/world/2014_06_26_01_world_misc.sql
new file mode 100644
index 00000000000..aea1adbf3ce
--- /dev/null
+++ b/sql/updates/world/2014_06_26_01_world_misc.sql
@@ -0,0 +1,102 @@
+-- A lot of missing spawns. Objects and creatures
+SET @CGUID := 134649; -- 34 needed
+SET @OGUID := 59171; -- 21 needed
+SET @EVENT := 24;
+-- Berfest Reveler
+DELETE FROM `creature` WHERE `guid` BETWEEN @CGUID+0 AND @CGUID+33;
+INSERT INTO `creature` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`modelid`,`equipment_id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`currentwaypoint`,`curhealth`,`curmana`,`MovementType`,`npcflag`,`unit_flags`,`dynamicflags`) VALUES
+-- Barleybrew Apprentice <Barleybrew Brew Vendor>
+(@CGUID+0,23482,530,1,1,22395,0,-4016.6,-11833,0.106771,4.04165,300,0,0,2215,0,0,0,0,0),
+-- Brewfest Reveler - Exodar
+(@CGUID+1,24484,530,1,1,21843,86,-4004.32,-11859.4,0.177081,0.860784,300,0,0,2215,0,0,0,0,0),
+(@CGUID+2,24484,530,1,1,21844,86,-4001.38,-11855.2,0.211436,4.18773,300,0,0,2215,0,0,0,0,0),
+(@CGUID+3,24484,530,1,1,21843,86,-4010.47,-11852.8,0.118782,4.25763,300,0,0,2215,0,0,0,0,0),
+(@CGUID+4,24484,530,1,1,21842,86,-4009.89,-11854.7,0.121793,3.64502,300,0,0,2215,0,0,0,0,0),
+(@CGUID+5,24484,530,1,1,21844,86,-4012.09,-11839.5,0.131949,0.507355,300,0,0,2215,0,0,0,0,0),
+(@CGUID+6,24484,530,1,1,21845,86,-4005.04,-11840.8,0.203372,2.26665,300,0,0,2215,0,0,0,0,0),
+(@CGUID+7,24484,530,1,1,21842,86,-4017.34,-11851.2,0.047381,1.00608,300,0,0,2215,0,0,0,0,0),
+(@CGUID+8,24484,530,1,1,21845,86,-4013.17,-11842.1,0.114192,4.16731,300,0,0,2215,0,0,0,0,0),
+(@CGUID+9,24484,530,1,1,21844,86,-4012.1,-11847.4,0.111304,2.65935,300,0,0,2215,0,0,0,0,0),
+(@CGUID+10,24484,530,1,1,21843,86,-4017.5,-11845,0.0595054,5.9266,300,0,0,2215,0,0,0,0,0),
+-- Brewfest Reveler - Eversong Woods
+(@CGUID+11,24484,530,1,1,21841,86,9318.1,-7278.19,13.3383,0.709729,300,0,0,2215,0,0,0,0,0),
+(@CGUID+12,24484,530,1,1,21838,86,9318.04,-7274.55,13.3571,5.13466,300,0,0,2215,0,0,0,0,0),
+(@CGUID+13,24484,530,1,1,21840,86,9322.69,-7278.57,13.2085,3.03844,300,0,0,2215,0,0,0,0,0),
+(@CGUID+14,24484,530,1,1,21839,86,9323.46,-7274.86,13.2653,3.31333,300,0,0,2215,0,0,0,0,0),
+(@CGUID+15,24484,530,1,1,21840,86,9333.88,-7275.82,13.514,5.39384,300,0,0,2215,0,0,0,0,0),
+(@CGUID+16,24484,530,1,1,21841,86,9335.93,-7281.15,13.695,2.03234,300,0,0,2215,0,0,0,0,0),
+(@CGUID+17,24484,530,1,1,21838,86,9340.67,-7275.46,14.0555,4.97837,300,0,0,2215,0,0,0,0,0),
+(@CGUID+18,24484,530,1,1,21839,86,9343.15,-7280.24,14.3778,2.35828,300,0,0,2215,0,0,0,0,0),
+(@CGUID+19,24484,530,1,1,21840,86,9334.47,-7296.27,13.7759,3.58271,300,0,0,2215,0,0,0,0,0),
+(@CGUID+20,24484,530,1,1,21839,86,9329.98,-7295.05,13.7486,5.6758,300,0,0,2215,0,0,0,0,0),
+(@CGUID+21,24484,530,1,1,21841,86,9340.93,-7269.42,14.5058,0.837748,300,0,0,2215,0,0,0,0,0),
+-- Troll Commoner
+(@CGUID+22,19177,1,1,1,21876,0,1748.94,-4497.53,28.793,5.47104,300,0,0,42,0,0,0,0,0),
+(@CGUID+23,19177,1,1,1,21877,0,1752.58,-4499.43,28.2911,2.89886,300,0,0,42,0,0,0,0,0),
+-- Orc Commoner
+(@CGUID+24,19175,1,1,1,21868,0,1609.47,-4323.21,2.33176,3.34811,300,0,0,42,0,0,0,0,0),
+(@CGUID+25,19175,1,1,1,21869,0,1605.95,-4324.2,2.19915,0.0101724,300,0,0,42,0,0,0,0,0),
+-- Gnome Commoner
+(@CGUID+26,19172,0,1,1,21850,0,-4831.74,-1172.58,502.278,6.19592,2,0,0,42,0,0,3,0,0),
+(@CGUID+27,19172,0,1,1,21853,0,-4829.88,-1173.25,502.277,2.49582,2,0,0,42,0,0,3,0,0),
+-- Bok Dropcertain
+(@CGUID+28,24527,1,1,1,0,0,795.14,-4522.59,6.31052,5.20636,300,0,0,1004,0,0,0,0,0),
+-- Drohn's Distillery Apprentice
+(@CGUID+29,24501,530,1,1,22491,0,9333.7,-7269.67,13.7715,4.52047,300,0,0,2215,0,0,0,0,0),
+-- Gordok Brew Apprentice <Gordok Brew Vendor>
+(@CGUID+30,23511,530,1,1,0,0,-4020.13,-11855.6,0.0061873,1.0273,300,0,0,2215,0,0,0,0,0),
+(@CGUID+31,23511,530,1,1,0,0,9313.83,-7281.3,13.8523,0.903714,300,0,0,2215,0,0,0,0,0),
+-- T'chali's Voodoo Brewery Apprentice <Voodoo Brew Vendor>
+(@CGUID+32,23533,530,1,1,22493,0,9330.11,-7285.87,13.7137,1.37103,300,0,0,2215,0,0,0,0,0),
+-- Thunderbrew Apprentice
+(@CGUID+33,23510,530,1,1,0,0,-4021.3,-11842.9,0.0212859,5.9439,300,0,0,2215,0,0,0,0,0);
+
+-- Make the above spawns only happen if Brewfest is active
+DELETE FROM `game_event_creature` WHERE `guid` BETWEEN @CGUID+0 AND @CGUID+33;
+INSERT INTO `game_event_creature` (`eventEntry`,`guid`) VALUES
+(@EVENT,@CGUID+0),(@EVENT,@CGUID+1),(@EVENT,@CGUID+2),(@EVENT,@CGUID+3),
+(@EVENT,@CGUID+4),(@EVENT,@CGUID+5),(@EVENT,@CGUID+6),(@EVENT,@CGUID+7),
+(@EVENT,@CGUID+8),(@EVENT,@CGUID+9),(@EVENT,@CGUID+10),(@EVENT,@CGUID+11),
+(@EVENT,@CGUID+12),(@EVENT,@CGUID+13),(@EVENT,@CGUID+14),(@EVENT,@CGUID+15),
+(@EVENT,@CGUID+16),(@EVENT,@CGUID+17),(@EVENT,@CGUID+18),(@EVENT,@CGUID+19),
+(@EVENT,@CGUID+20),(@EVENT,@CGUID+21),(@EVENT,@CGUID+22),(@EVENT,@CGUID+23),
+(@EVENT,@CGUID+24),(@EVENT,@CGUID+25),(@EVENT,@CGUID+26),(@EVENT,@CGUID+27),
+(@EVENT,@CGUID+28),(@EVENT,@CGUID+29),(@EVENT,@CGUID+30),(@EVENT,@CGUID+31),
+(@EVENT,@CGUID+32),(@EVENT,@CGUID+33);
+
+DELETE FROM `gameobject` WHERE `guid` BETWEEN @OGUID+0 AND @OGUID+20;
+INSERT INTO `gameobject` (`guid`,`id`,`map`,`spawnMask`,`phaseMask`,`position_x`,`position_y`,`position_z`,`orientation`,`rotation0`,`rotation1`,`rotation2`,`rotation3`,`spawntimesecs`,`animprogress`,`state`) VALUES
+-- Exodar
+(@OGUID+0,178666,530,1,1,-4015.97,-11829.2,0.146814,2.80549,0,0,0.985913,0.167262,300,0,1),
+(@OGUID+1,178666,530,1,1,-4024.94,-11840.7,-0.0137537,4.17208,0,0,0.870173,-0.492747,300,0,1),
+(@OGUID+2,186259,530,1,1,-4029.42,-11849.1,-0.0817791,3.05053,0,0,0.998964,0.0455147,300,0,1),
+(@OGUID+3,186260,530,1,1,-4008.1,-11835.5,0.186614,1.27632,0,0,0.595718,0.803194,300,0,1),
+(@OGUID+4,186257,530,1,1,-4016.1,-11859.2,0.0441619,4.26476,0,0,0.846413,-0.532527,300,0,1),
+(@OGUID+5,186221,530,1,1,-4008.42,-11850.7,0.144533,2.04601,0,0,0.853677,0.520803,300,0,1),
+(@OGUID+6,186717,530,1,1,-4008.29,-11857.1,0.135722,5.47893,0,0,0.391378,-0.92023,300,0,1),
+(@OGUID+7,186717,530,1,1,-4002.56,-11851,0.205429,5.13728,0,0,0.542116,-0.840304,300,0,1),
+-- Darnassus
+(@OGUID+8,186717,1,1,1,9962.87,2218.32,1329.82,1.77101,0,0,0.774236,0.632897,300,0,1),
+(@OGUID+9,186717,1,1,1,9963.64,2192.59,1328.52,4.74374,0,0,0.695937,-0.718103,300,0,1),
+-- Eversong Woods
+(@OGUID+10,178666,530,1,1,9333.24,-7265.71,13.7149,3.15074,0,0,0.99999,-0.00457361,300,0,1),
+(@OGUID+11,178666,530,1,1,9330.88,-7289.79,13.7388,0.18586,0,0,0.0927961,0.995685,300,0,1),
+(@OGUID+12,186717,530,1,1,9319.23,-7265.19,13.7578,2.02761,0,0,0.84885,0.528633,300,0,1),
+(@OGUID+13,186717,530,1,1,9310.25,-7291.41,14.4908,2.53027,0,0,0.953648,0.300923,300,0,1),
+(@OGUID+14,186221,530,1,1,9318.17,-7291.17,14.4236,0.649241,0,0,0.318949,0.947772,300,0,1),
+(@OGUID+15,186257,530,1,1,9318.61,-7289.66,14.436,0.782759,0,0,0.381464,0.924384,300,0,1),
+(@OGUID+16,186256,530,1,1,9341.19,-7288.21,14.3979,4.71368,0,0,0.70665,-0.707563,300,0,1),
+(@OGUID+17,186255,530,1,1,9343.07,-7265.55,14.691,1.48569,0,0,0.676387,0.736546,300,0,1),
+(@OGUID+18,180037,530,1,1,9316.77,-7283.36,13.7084,3.63768,0,0,0.969394,-0.24551,300,0,1),
+(@OGUID+19,180037,530,1,1,9313.62,-7285.06,13.9541,3.38636,0,0,0.992521,-0.122077,300,0,1),
+(@OGUID+20,180037,530,1,1,9308.97,-7280.39,14.447,5.40091,0,0,0.426971,-0.904265,300,0,1);
+
+-- Make the above game objects only spawn if Brewfest is active
+DELETE FROM `game_event_gameobject` WHERE `guid` BETWEEN @OGUID+0 AND @OGUID+20;
+INSERT INTO `game_event_gameobject` (`eventEntry`,`guid`) VALUES
+(@EVENT,@OGUID+0),(@EVENT,@OGUID+1),(@EVENT,@OGUID+2),(@EVENT,@OGUID+3),
+(@EVENT,@OGUID+4),(@EVENT,@OGUID+5),(@EVENT,@OGUID+6),(@EVENT,@OGUID+7),
+(@EVENT,@OGUID+8),(@EVENT,@OGUID+9),(@EVENT,@OGUID+10),(@EVENT,@OGUID+11),
+(@EVENT,@OGUID+12),(@EVENT,@OGUID+13),(@EVENT,@OGUID+14),(@EVENT,@OGUID+15),
+(@EVENT,@OGUID+16),(@EVENT,@OGUID+17),(@EVENT,@OGUID+18),(@EVENT,@OGUID+19),
+(@EVENT,@OGUID+20);
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index e36433dd8c0..af47b52f500 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -537,9 +537,9 @@ void SmartAI::JustRespawned()
me->SetVisible(true);
if (me->getFaction() != me->GetCreatureTemplate()->faction)
me->RestoreFaction();
- GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
mJustReset = true;
JustReachedHome();
+ GetScript()->ProcessEventsFor(SMART_EVENT_RESPAWN);
mFollowGuid = 0;//do not reset follower on Reset(), we need it after combat evade
mFollowDist = 0;
mFollowAngle = 0;
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index 8e186e9d094..10b7c25bb1f 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -349,21 +349,20 @@ enum ItemLimitCategoryMode
ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1 // limit applied to amount equipped items (including used gems)
};
-enum SpellCategoryFlags
+enum SkillRaceClassInfoFlags
{
- SPELL_CATEGORY_FLAG_COOLDOWN_SCALES_WITH_WEAPON_SPEED = 0x01, // unused
- SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT = 0x04
+ SKILL_FLAG_NO_SKILLUP_MESSAGE = 0x2,
+ SKILL_FLAG_ALWAYS_MAX_VALUE = 0x10,
+ SKILL_FLAG_UNLEARNABLE = 0x20, // Skill can be unlearned
+ SKILL_FLAG_INCLUDE_IN_SORT = 0x80, // Spells belonging to a skill with this flag will additionally compare skill ids when sorting spellbook in client
+ SKILL_FLAG_NOT_TRAINABLE = 0x100,
+ SKILL_FLAG_MONO_VALUE = 0x400 // Skill always has value 1
};
-enum TotemCategoryType
+enum SpellCategoryFlags
{
- TOTEM_CATEGORY_TYPE_KNIFE = 1,
- TOTEM_CATEGORY_TYPE_TOTEM = 2,
- TOTEM_CATEGORY_TYPE_ROD = 3,
- TOTEM_CATEGORY_TYPE_PICK = 21,
- TOTEM_CATEGORY_TYPE_STONE = 22,
- TOTEM_CATEGORY_TYPE_HAMMER = 23,
- TOTEM_CATEGORY_TYPE_SPANNER = 24
+ SPELL_CATEGORY_FLAG_COOLDOWN_SCALES_WITH_WEAPON_SPEED = 0x01, // unused
+ SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT = 0x04
};
// SummonProperties.dbc, col 1
@@ -398,6 +397,17 @@ enum SummonPropFlags
SUMMON_PROP_FLAG_UNK16 = 0x00008000 // Light/Dark Bullet, Soul/Fiery Consumption, Twisted Visage, Twilight Whelp. Phase related?
};
+enum TotemCategoryType
+{
+ TOTEM_CATEGORY_TYPE_KNIFE = 1,
+ TOTEM_CATEGORY_TYPE_TOTEM = 2,
+ TOTEM_CATEGORY_TYPE_ROD = 3,
+ TOTEM_CATEGORY_TYPE_PICK = 21,
+ TOTEM_CATEGORY_TYPE_STONE = 22,
+ TOTEM_CATEGORY_TYPE_HAMMER = 23,
+ TOTEM_CATEGORY_TYPE_SPANNER = 24
+};
+
enum VehicleSeatFlags
{
VEHICLE_SEAT_FLAG_HAS_LOWER_ANIM_FOR_ENTER = 0x00000001,
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 44f03b6978d..92d00b20645 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -151,6 +151,9 @@ DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore(ScalingStatValuesfmt
DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt);
DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt);
+DBCStorage <SkillRaceClassInfoEntry> sSkillRaceClassInfoStore(SkillRaceClassInfofmt);
+SkillRaceClassInfoMap SkillRaceClassInfoBySkill;
+DBCStorage <SkillTiersEntry> sSkillTiersStore(SkillTiersfmt);
DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt);
@@ -409,6 +412,13 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sScalingStatValuesStore, dbcPath, "ScalingStatValues.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sSkillLineStore, dbcPath, "SkillLine.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sSkillLineAbilityStore, dbcPath, "SkillLineAbility.dbc");
+ LoadDBC(availableDbcLocales, bad_dbc_files, sSkillRaceClassInfoStore, dbcPath, "SkillRaceClassInfo.dbc");
+ for (uint32 i = 0; i < sSkillRaceClassInfoStore.GetNumRows(); ++i)
+ if (SkillRaceClassInfoEntry const* entry = sSkillRaceClassInfoStore.LookupEntry(i))
+ if (sSkillLineStore.LookupEntry(entry->SkillId))
+ SkillRaceClassInfoBySkill.emplace(entry->SkillId, entry);
+
+ LoadDBC(availableDbcLocales, bad_dbc_files, sSkillTiersStore, dbcPath, "SkillTiers.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sSoundEntriesStore, dbcPath, "SoundEntries.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc", &CustomSpellEntryfmt, &CustomSpellEntryIndex);
for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
@@ -965,3 +975,19 @@ uint32 GetDefaultMapLight(uint32 mapId)
return 0;
}
+
+SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_)
+{
+ SkillRaceClassInfoBounds bounds = SkillRaceClassInfoBySkill.equal_range(skill);
+ for (SkillRaceClassInfoMap::iterator itr = bounds.first; itr != bounds.second; ++itr)
+ {
+ if (itr->second->RaceMask && !(itr->second->RaceMask & (1 << (race - 1))))
+ continue;
+ if (itr->second->ClassMask && !(itr->second->ClassMask & (1 << (class_ - 1))))
+ continue;
+
+ return itr->second;
+ }
+
+ return NULL;
+}
diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h
index fe775dfda19..8b89a86fafe 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -74,6 +74,10 @@ LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty);
uint32 GetDefaultMapLight(uint32 mapId);
+typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRaceClassInfoMap;
+typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds;
+SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_);
+
extern DBCStorage <AchievementEntry> sAchievementStore;
extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions
@@ -150,6 +154,7 @@ extern DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore;
extern DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore;
extern DBCStorage <SkillLineEntry> sSkillLineStore;
extern DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore;
+extern DBCStorage <SkillTiersEntry> sSkillTiersStore;
extern DBCStorage <SoundEntriesEntry> sSoundEntriesStore;
extern DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore;
extern DBCStorage <SpellCategoryEntry> sSpellCategoryStore;
diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h
index 5d6c8c7aa89..2da166fb049 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -1584,6 +1584,27 @@ struct SkillLineAbilityEntry
//uint32 characterPoints[2]; // 12-13 m_characterPoints[2]
};
+struct SkillRaceClassInfoEntry
+{
+ //uint32 Id; // 0
+ uint32 SkillId; // 1
+ uint32 RaceMask; // 2
+ uint32 ClassMask; // 3
+ uint32 Flags; // 4
+ //uint32 MinLevel; // 5
+ uint32 SkillTier; // 6
+ //uint32 SkillCostType; // 7
+};
+
+#define MAX_SKILL_STEP 16
+
+struct SkillTiersEntry
+{
+ uint32 Id; // 0
+ //uint32 StepCost[MAX_SKILL_STEP]; // 1-16
+ uint32 MaxSkill[MAX_SKILL_STEP]; // 17-32
+};
+
struct SoundEntriesEntry
{
uint32 Id; // 0 m_ID
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index 222353467f4..a90cc48c5af 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -98,6 +98,8 @@ char const ScalingStatDistributionfmt[] = "niiiiiiiiiiiiiiiiiiiii";
char const ScalingStatValuesfmt[] = "iniiiiiiiiiiiiiiiiiiiiii";
char const SkillLinefmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi";
char const SkillLineAbilityfmt[] = "niiiixxiiiiixx";
+char const SkillRaceClassInfofmt[] = "diiiixix";
+char const SkillTiersfmt[] = "nxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiii";
char const SoundEntriesfmt[] = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char const SpellCastTimefmt[] = "nixx";
char const SpellCategoryfmt[] = "ni";
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index c81ba409495..9b2cd013802 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -142,8 +142,8 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
}
Creature::Creature(bool isWorldObject): Unit(isWorldObject), MapObject(),
-lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLowGUID(0),
-m_PlayerDamageReq(0), m_lootRecipient(0), m_lootRecipientGroup(0), m_corpseRemoveTime(0), m_respawnTime(0),
+_pickpocketLootRestore(0), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0),
+m_lootRecipient(0), m_lootRecipientGroup(0), _skinner(0), m_corpseRemoveTime(0), m_respawnTime(0),
m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_reactState(REACT_AGGRESSIVE),
m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
@@ -547,6 +547,15 @@ void Creature::Update(uint32 diff)
if (!IsAlive())
break;
+ time_t now = time(NULL);
+
+ // Check if we should refill the pickpocketing loot
+ if (loot.loot_type == LOOT_PICKPOCKETING && _pickpocketLootRestore && _pickpocketLootRestore <= now)
+ {
+ loot.clear();
+ _pickpocketLootRestore = 0;
+ }
+
if (m_regenTimer > 0)
{
if (diff >= m_regenTimer)
@@ -1463,11 +1472,6 @@ void Creature::setDeathState(DeathState s)
setActive(false);
- if (!IsPet() && GetCreatureTemplate()->SkinLootId)
- if (LootTemplates_Skinning.HaveLootFor(GetCreatureTemplate()->SkinLootId))
- if (hasLootRecipient())
- SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
-
if (HasSearchedAssistance())
{
SetNoSearchAssistance(false);
@@ -1527,9 +1531,8 @@ void Creature::Respawn(bool force)
TC_LOG_DEBUG("entities.unit", "Respawning creature %s (GuidLow: %u, Full GUID: " UI64FMTD " Entry: %u)",
GetName().c_str(), GetGUIDLow(), GetGUID(), GetEntry());
m_respawnTime = 0;
- lootForPickPocketed = false;
- lootForBody = false;
-
+ _pickpocketLootRestore = 0;
+ loot.clear();
if (m_originalEntry != GetEntry())
UpdateEntry(m_originalEntry);
@@ -2268,26 +2271,23 @@ void Creature::GetRespawnPosition(float &x, float &y, float &z, float* ori, floa
void Creature::AllLootRemovedFromCorpse()
{
- if (!HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE))
- {
- time_t now = time(NULL);
- if (m_corpseRemoveTime <= now)
- return;
+ if (loot.loot_type != LOOT_SKINNING && !IsPet() && GetCreatureTemplate()->SkinLootId && hasLootRecipient())
+ if (LootTemplates_Skinning.HaveLootFor(GetCreatureTemplate()->SkinLootId))
+ SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
- float decayRate;
- CreatureTemplate const* cinfo = GetCreatureTemplate();
+ time_t now = time(NULL);
+ if (m_corpseRemoveTime <= now)
+ return;
- decayRate = sWorld->getRate(RATE_CORPSE_DECAY_LOOTED);
- uint32 diff = uint32((m_corpseRemoveTime - now) * decayRate);
+ float decayRate = sWorld->getRate(RATE_CORPSE_DECAY_LOOTED);
- m_respawnTime -= diff;
+ // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
+ if (loot.loot_type == LOOT_SKINNING)
+ m_corpseRemoveTime = time(NULL);
+ else
+ m_corpseRemoveTime = now + m_corpseDelay * decayRate;
- // corpse skinnable, but without skinning flag, and then skinned, corpse will despawn next update
- if (cinfo && cinfo->SkinLootId)
- m_corpseRemoveTime = time(NULL);
- else
- m_corpseRemoveTime -= diff;
- }
+ m_respawnTime = m_corpseRemoveTime + m_respawnTime;
}
uint8 Creature::getLevelForTarget(WorldObject const* target) const
@@ -2712,3 +2712,8 @@ void Creature::ReleaseFocus(Spell const* focusSpell)
ClearUnitState(UNIT_STATE_ROTATING);
}
+void Creature::StartPickPocketRefillTimer()
+{
+ _pickpocketLootRestore = time(NULL) + sWorld->getIntConfig(CONFIG_CREATURE_PICKPOCKET_REFILL);
+}
+
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 9cc08e3b71d..ca536e44e43 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -551,8 +551,10 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
virtual void DeleteFromDB(); // overriden in Pet
Loot loot;
- bool lootForPickPocketed;
- bool lootForBody;
+ void StartPickPocketRefillTimer();
+ void ResetPickPocketRefillTimer() { _pickpocketLootRestore = 0; }
+ void SetSkinner(uint64 guid) { _skinner = guid; }
+ uint64 GetSkinner() const { return _skinner; } // Returns the player who skinned this creature
Player* GetLootRecipient() const;
Group* GetLootRecipientGroup() const;
bool hasLootRecipient() const { return m_lootRecipient || m_lootRecipientGroup; }
@@ -688,8 +690,10 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
uint64 m_lootRecipient;
uint32 m_lootRecipientGroup;
+ uint64 _skinner;
/// Timers
+ time_t _pickpocketLootRestore;
time_t m_corpseRemoveTime; // (msecs)timer for death or corpse disappearance
time_t m_respawnTime; // (secs) time of next respawn
uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
diff --git a/src/server/game/Entities/Item/ItemPrototype.h b/src/server/game/Entities/Item/ItemPrototype.h
index cc477c5bd37..bdf956f8921 100644
--- a/src/server/game/Entities/Item/ItemPrototype.h
+++ b/src/server/game/Entities/Item/ItemPrototype.h
@@ -734,7 +734,7 @@ struct ItemTemplate
default:
break;
}
- return itemLevel;
+ return std::max<float>(0.f, itemLevel);
}
bool IsPotion() const { return Class == ITEM_CLASS_CONSUMABLE && SubClass == ITEM_SUBCLASS_POTION; }
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 9733c0f2b52..8d0223d2247 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2920,11 +2920,10 @@ void Player::UninviteFromGroup()
void Player::RemoveFromGroup(Group* group, uint64 guid, RemoveMethod method /* = GROUP_REMOVEMETHOD_DEFAULT*/, uint64 kicker /* = 0 */, const char* reason /* = NULL */)
{
- if (group)
- {
- group->RemoveMember(guid, method, kicker, reason);
- group = NULL;
- }
+ if (!group)
+ return;
+
+ group->RemoveMember(guid, method, kicker, reason);
}
void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 BonusXP, bool recruitAFriend, float /*group_rate*/)
@@ -8970,9 +8969,9 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
if (loot_type == LOOT_PICKPOCKETING)
{
- if (!creature->lootForPickPocketed)
+ if (loot->loot_type != LOOT_PICKPOCKETING)
{
- creature->lootForPickPocketed = true;
+ creature->StartPickPocketRefillTimer();
loot->clear();
if (uint32 lootid = creature->GetCreatureTemplate()->pickpocketLootId)
@@ -8992,12 +8991,9 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
if (!recipient)
return;
- if (!creature->lootForBody)
+ if (loot->loot_type == LOOT_NONE)
{
- creature->lootForBody = true;
-
// for creature, loot is filled when creature is killed.
-
if (Group* group = recipient->GetGroup())
{
switch (group->GetLootMethod())
@@ -9018,11 +9014,17 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
}
}
- // possible only if creature->lootForBody && loot->empty() at spell cast check
- if (loot_type == LOOT_SKINNING)
+ // if loot is already skinning loot then don't do anything else
+ if (loot->loot_type == LOOT_SKINNING)
+ {
+ loot_type = LOOT_SKINNING;
+ permission = creature->GetSkinner() == GetGUID() ? OWNER_PERMISSION : NONE_PERMISSION;
+ }
+ else if (loot_type == LOOT_SKINNING)
{
loot->clear();
loot->FillLoot(creature->GetCreatureTemplate()->SkinLootId, LootTemplates_Skinning, this, true);
+ creature->SetSkinner(GetGUID());
permission = OWNER_PERMISSION;
}
// set group rights only for loot_type != LOOT_SKINNING
@@ -17831,6 +17833,9 @@ bool Player::isAllowedToLoot(const Creature* creature)
if (loot->isLooted()) // nothing to loot or everything looted.
return false;
+ if (loot->loot_type == LOOT_SKINNING)
+ return creature->GetSkinner() == GetGUID();
+
Group* thisGroup = GetGroup();
if (!thisGroup)
return this == creature->GetLootRecipient();
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 492197db64e..363738f96ef 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -15271,8 +15271,8 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
if (creature)
{
Loot* loot = &creature->loot;
- if (creature->lootForPickPocketed)
- creature->lootForPickPocketed = false;
+ if (creature->loot.loot_type == LOOT_PICKPOCKETING)
+ creature->ResetPickPocketRefillTimer();
loot->clear();
if (uint32 lootid = creature->GetCreatureTemplate()->lootid)
@@ -15391,9 +15391,12 @@ void Unit::Kill(Unit* victim, bool durabilityLoss)
if (!creature->IsPet())
{
creature->DeleteThreatList();
- CreatureTemplate const* cInfo = creature->GetCreatureTemplate();
- if (cInfo && (cInfo->lootid || cInfo->maxgold > 0))
+
+ // must be after setDeathState which resets dynamic flags
+ if (!creature->loot.empty())
creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+ else
+ creature->AllLootRemovedFromCorpse();
}
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
@@ -16378,19 +16381,25 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update)
}
}
- WorldObject::SetPhaseMask(newPhaseMask, update);
+ // Phase player, dont update
+ WorldObject::SetPhaseMask(newPhaseMask, false);
- if (!IsInWorld())
- return;
+ // Phase pets and summons
+ if (IsInWorld())
+ {
+ for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
+ if ((*itr)->GetTypeId() == TYPEID_UNIT)
+ (*itr)->SetPhaseMask(newPhaseMask, true);
- for (ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
- if ((*itr)->GetTypeId() == TYPEID_UNIT)
- (*itr)->SetPhaseMask(newPhaseMask, true);
+ for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
+ if (m_SummonSlot[i])
+ if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i]))
+ summon->SetPhaseMask(newPhaseMask, true);
+ }
- for (uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
- if (m_SummonSlot[i])
- if (Creature* summon = GetMap()->GetCreature(m_SummonSlot[i]))
- summon->SetPhaseMask(newPhaseMask, true);
+ // Update visibility after phasing pets and summons so they wont despawn
+ if (update)
+ UpdateObjectVisibility();
}
void Unit::UpdateObjectVisibility(bool forced)
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index 61f0b9afce2..b9c6f349ac3 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -81,7 +81,7 @@ void WorldSession::HandleAutostoreLootItemOpcode(WorldPacket& recvData)
{
Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
- bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
+ bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
{
@@ -148,7 +148,7 @@ void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/)
case HIGHGUID_VEHICLE:
{
Creature* creature = player->GetMap()->GetCreature(guid);
- bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
+ bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
if (lootAllowed && creature->IsWithinDistInMap(player, INTERACTION_DISTANCE))
{
loot = &creature->loot;
@@ -348,18 +348,19 @@ void WorldSession::DoLootRelease(uint64 lguid)
{
Creature* creature = GetPlayer()->GetMap()->GetCreature(lguid);
- bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->lootForPickPocketed);
+ bool lootAllowed = creature && creature->IsAlive() == (player->getClass() == CLASS_ROGUE && creature->loot.loot_type == LOOT_PICKPOCKETING);
if (!lootAllowed || !creature->IsWithinDistInMap(_player, INTERACTION_DISTANCE))
return;
loot = &creature->loot;
if (loot->isLooted())
{
+ creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+
// skip pickpocketing loot for speed, skinning timer reduction is no-op in fact
if (!creature->IsAlive())
creature->AllLootRemovedFromCorpse();
- creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
loot->clear();
}
else
diff --git a/src/server/game/Handlers/SkillHandler.cpp b/src/server/game/Handlers/SkillHandler.cpp
index f90dfef2684..8a94753b692 100644
--- a/src/server/game/Handlers/SkillHandler.cpp
+++ b/src/server/game/Handlers/SkillHandler.cpp
@@ -98,7 +98,8 @@ void WorldSession::HandleUnlearnSkillOpcode(WorldPacket& recvData)
uint32 skillId;
recvData >> skillId;
- if (!IsPrimaryProfessionSkill(skillId))
+ SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skillId, GetPlayer()->getRace(), GetPlayer()->getClass());
+ if (!rcEntry || !(rcEntry->Flags & SKILL_FLAG_UNLEARNABLE))
return;
GetPlayer()->SetSkill(skillId, 0, 0, 0);
diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h
index 9be745e622d..cb3b9082c20 100644
--- a/src/server/game/Loot/LootMgr.h
+++ b/src/server/game/Loot/LootMgr.h
@@ -76,6 +76,8 @@ enum PermissionTypes
enum LootType
{
+ LOOT_NONE = 0,
+
LOOT_CORPSE = 1,
LOOT_PICKPOCKETING = 2,
LOOT_FISHING = 3,
@@ -341,6 +343,7 @@ struct Loot
gold = 0;
unlootedCount = 0;
roundRobinPlayer = 0;
+ loot_type = LOOT_NONE;
i_LootValidatorRefManager.clearReferences();
}
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 3422934fcd9..c8c81a415e1 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -444,7 +444,6 @@ class WorldSession
void HandleForceSpeedChangeAck(WorldPacket& recvData);
void HandlePingOpcode(WorldPacket& recvPacket);
- void HandleAuthSessionOpcode(WorldPacket& recvPacket);
void HandleRepopRequestOpcode(WorldPacket& recvPacket);
void HandleAutostoreLootItemOpcode(WorldPacket& recvPacket);
void HandleLootMoneyOpcode(WorldPacket& recvPacket);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 9cf0e1ae45c..606851bf8f1 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -2608,8 +2608,16 @@ void Spell::EffectLearnSkill(SpellEffIndex effIndex)
return;
uint32 skillid = m_spellInfo->Effects[effIndex].MiscValue;
+ SkillRaceClassInfoEntry const* rcEntry = GetSkillRaceClassInfo(skillid, unitTarget->getRace(), unitTarget->getClass());
+ if (!rcEntry)
+ return;
+
+ SkillTiersEntry const* tier = sSkillTiersStore.LookupEntry(rcEntry->SkillTier);
+ if (!tier)
+ return;
+
uint16 skillval = unitTarget->ToPlayer()->GetPureSkillValue(skillid);
- unitTarget->ToPlayer()->SetSkill(skillid, m_spellInfo->Effects[effIndex].CalcValue(), skillval?skillval:1, damage*75);
+ unitTarget->ToPlayer()->SetSkill(skillid, m_spellInfo->Effects[effIndex].CalcValue(), std::max<uint16>(skillval, 1), tier->MaxSkill[damage - 1]);
}
void Spell::EffectAddHonor(SpellEffIndex /*effIndex*/)
@@ -4704,6 +4712,7 @@ void Spell::EffectSkinning(SpellEffIndex /*effIndex*/)
m_caster->ToPlayer()->SendLoot(creature->GetGUID(), LOOT_SKINNING);
creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+ creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
int32 reqValue = targetLevel < 10 ? 0 : targetLevel < 20 ? (targetLevel-10)*10 : targetLevel*5;
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index b643d127c04..62603cce950 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -1044,6 +1044,8 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfigMgr->GetBoolDefault("OffhandCheckAtSpellUnlearn", true);
+ m_int_configs[CONFIG_CREATURE_PICKPOCKET_REFILL] = sConfigMgr->GetIntDefault("Creature.PickPocketRefillDelay", 10 * MINUTE);
+
if (int32 clientCacheId = sConfigMgr->GetIntDefault("ClientCacheVersion", 0))
{
// overwrite DB/old value
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index efd7570992a..61acdc37b07 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -333,6 +333,7 @@ enum WorldIntConfigs
CONFIG_BG_REWARD_LOSER_HONOR_FIRST,
CONFIG_BG_REWARD_LOSER_HONOR_LAST,
CONFIG_BIRTHDAY_TIME,
+ CONFIG_CREATURE_PICKPOCKET_REFILL,
INT_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp b/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp
index 8c612a11621..bc4fff4da7b 100644
--- a/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp
+++ b/src/server/scripts/EasternKingdoms/zone_eversong_woods.cpp
@@ -190,6 +190,9 @@ enum InfusedCrystal
// Quest
QUEST_POWERING_OUR_DEFENSES = 8490,
+ // Quest Credit
+ QUEST_POD_CREDIT = 16364,
+
// Says
EMOTE = 0,
@@ -266,24 +269,17 @@ public:
summoned->AI()->AttackStart(me);
}
- void JustDied(Unit* /*killer*/) override
- {
- if (PlayerGUID && !Completed)
- if (Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID))
- player->FailQuest(QUEST_POWERING_OUR_DEFENSES);
- }
-
void UpdateAI(uint32 diff) override
{
if (EndTimer < diff && Progress)
{
- Talk(EMOTE);
Completed = true;
if (PlayerGUID)
if (Player* player = ObjectAccessor::GetPlayer(*me, PlayerGUID))
- player->CompleteQuest(QUEST_POWERING_OUR_DEFENSES);
-
- me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+ {
+ Talk(EMOTE, player);
+ player->KilledMonsterCredit(QUEST_POD_CREDIT);
+ }
me->RemoveCorpse();
} else EndTimer -= diff;
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index f71b7bb8150..5a3ea2bb4e1 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -1353,6 +1353,14 @@ Rate.Creature.Elite.RAREELITE.HP = 1
Rate.Creature.Elite.WORLDBOSS.HP = 1
#
+# Creature.PickPocketRefillDelay
+# Description: Time in seconds that the server will wait before refilling the pickpocket loot
+# for a creature
+# Default: 600
+
+Creature.PickPocketRefillDelay = 600
+
+#
# ListenRange.Say
# Description: Distance in which players can read say messages from creatures or
# gameobjects.
diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp
index cc24035b05e..131041e0cd2 100644
--- a/src/tools/mmaps_generator/MapBuilder.cpp
+++ b/src/tools/mmaps_generator/MapBuilder.cpp
@@ -77,8 +77,8 @@ namespace MMAP
{
for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
{
- (*it).second->clear();
- delete (*it).second;
+ (*it).m_tiles->clear();
+ delete (*it).m_tiles;
}
delete m_terrainBuilder;
@@ -97,9 +97,9 @@ namespace MMAP
for (uint32 i = 0; i < files.size(); ++i)
{
mapID = uint32(atoi(files[i].substr(0,3).c_str()));
- if (m_tiles.find(mapID) == m_tiles.end())
+ if (std::find(m_tiles.begin(), m_tiles.end(), mapID) == m_tiles.end())
{
- m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
+ m_tiles.emplace_back(MapTiles(mapID, new std::set<uint32>));
count++;
}
}
@@ -109,8 +109,11 @@ namespace MMAP
for (uint32 i = 0; i < files.size(); ++i)
{
mapID = uint32(atoi(files[i].substr(0,3).c_str()));
- m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, new std::set<uint32>));
- count++;
+ if (std::find(m_tiles.begin(), m_tiles.end(), mapID) == m_tiles.end())
+ {
+ m_tiles.emplace_back(MapTiles(mapID, new std::set<uint32>));
+ count++;
+ }
}
printf("found %u.\n", count);
@@ -118,8 +121,8 @@ namespace MMAP
printf("Discovering tiles... ");
for (TileList::iterator itr = m_tiles.begin(); itr != m_tiles.end(); ++itr)
{
- std::set<uint32>* tiles = (*itr).second;
- mapID = (*itr).first;
+ std::set<uint32>* tiles = (*itr).m_tiles;
+ mapID = (*itr).m_mapId;
sprintf(filter, "%03u*.vmtile", mapID);
files.clear();
@@ -153,12 +156,12 @@ namespace MMAP
/**************************************************************************/
std::set<uint32>* MapBuilder::getTileList(uint32 mapID)
{
- TileList::iterator itr = m_tiles.find(mapID);
+ TileList::iterator itr = std::find(m_tiles.begin(), m_tiles.end(), mapID);
if (itr != m_tiles.end())
- return (*itr).second;
+ return (*itr).m_tiles;
std::set<uint32>* tiles = new std::set<uint32>();
- m_tiles.insert(std::pair<uint32, std::set<uint32>*>(mapID, tiles));
+ m_tiles.emplace_back(MapTiles(mapID, tiles));
return tiles;
}
@@ -169,9 +172,14 @@ namespace MMAP
BuilderThreadPool* pool = threads > 0 ? new BuilderThreadPool() : NULL;
+ m_tiles.sort([](MapTiles a, MapTiles b)
+ {
+ return a.m_tiles->size() > b.m_tiles->size();
+ });
+
for (TileList::iterator it = m_tiles.begin(); it != m_tiles.end(); ++it)
{
- uint32 mapID = it->first;
+ uint32 mapID = it->m_mapId;
if (!shouldSkipMap(mapID))
{
if (threads > 0)
diff --git a/src/tools/mmaps_generator/MapBuilder.h b/src/tools/mmaps_generator/MapBuilder.h
index 86a6db2077c..08b87324d01 100644
--- a/src/tools/mmaps_generator/MapBuilder.h
+++ b/src/tools/mmaps_generator/MapBuilder.h
@@ -22,6 +22,7 @@
#include <vector>
#include <set>
#include <map>
+#include <list>
#include "TerrainBuilder.h"
#include "IntermediateValues.h"
@@ -39,7 +40,24 @@ using namespace VMAP;
namespace MMAP
{
- typedef std::map<uint32, std::set<uint32>*> TileList;
+ struct MapTiles
+ {
+ MapTiles() : m_mapId(uint32(-1)), m_tiles(NULL) {}
+
+ MapTiles(uint32 id, std::set<uint32>* tiles) : m_mapId(id), m_tiles(tiles) {}
+ ~MapTiles() {}
+
+ uint32 m_mapId;
+ std::set<uint32>* m_tiles;
+
+ bool operator==(uint32 id)
+ {
+ return m_mapId == id;
+ }
+ };
+
+ typedef std::list<MapTiles> TileList;
+
struct Tile
{
Tile() : chf(NULL), solid(NULL), cset(NULL), pmesh(NULL), dmesh(NULL) {}