2 * Copyright (C) 2005-2008 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Database/DatabaseEnv.h"
20 #include "GridDefines.h"
21 #include "Policies/SingletonImp.h"
22 #include "WaypointManager.h"
23 #include "ProgressBar.h"
24 #include "MapManager.h"
25 #include "ObjectMgr.h"
27 INSTANTIATE_SINGLETON_1(WaypointManager
);
29 bool WaypointBehavior::isEmpty()
31 if (emote
|| spell
|| model1
|| model2
)
34 for(int i
= 0; i
< MAX_WAYPOINT_TEXT
; ++i
)
41 WaypointBehavior::WaypointBehavior(const WaypointBehavior
&b
)
47 for(int i
=0; i
< MAX_WAYPOINT_TEXT
; ++i
)
48 textid
[i
] = b
.textid
[i
];
51 void WaypointManager::Load()
55 uint32 total_paths
= 0;
56 uint32 total_nodes
= 0;
57 uint32 total_behaviors
= 0;
59 QueryResult
*result
= WorldDatabase
.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id");
62 total_paths
= result
->GetRowCount();
63 barGoLink
bar( total_paths
);
66 Field
*fields
= result
->Fetch();
67 uint32 id
= fields
[0].GetUInt32();
68 uint32 count
= fields
[1].GetUInt32();
69 m_pathMap
[id
].resize(count
);
73 } while( result
->NextRow() );
77 result
= WorldDatabase
.Query("SELECT position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, textid1, textid2, textid3, textid4, textid5, id, point FROM creature_movement");
80 barGoLink
bar( result
->GetRowCount() );
83 Field
*fields
= result
->Fetch();
84 uint32 point
= fields
[15].GetUInt32();
85 uint32 id
= fields
[14].GetUInt32();
87 WaypointPath
&path
= m_pathMap
[id
];
88 // the cleanup queries make sure the following is true
89 assert(point
>= 1 && point
<= path
.size());
90 WaypointNode
&node
= path
[point
-1];
92 node
.x
= fields
[0].GetFloat();
93 node
.y
= fields
[1].GetFloat();
94 node
.z
= fields
[2].GetFloat();
95 node
.orientation
= fields
[3].GetFloat();
96 node
.delay
= fields
[6].GetUInt16();
98 // prevent using invalid coordinates
99 if(!MaNGOS::IsValidMapCoord(node
.x
, node
.y
, node
.z
, node
.orientation
))
101 QueryResult
*result1
= WorldDatabase
.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id
);
103 sLog
.outErrorDb("ERROR: Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
104 id
, result1
->Fetch()[0].GetUInt32(), point
, node
.x
, node
.y
);
106 sLog
.outErrorDb("ERROR: Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
107 id
, point
, node
.x
, node
.y
);
109 MaNGOS::NormalizeMapCoord(node
.x
);
110 MaNGOS::NormalizeMapCoord(node
.y
);
113 node
.z
= MapManager::Instance ().GetBaseMap(result1
->Fetch()[1].GetUInt32())->GetHeight(node
.x
, node
.y
, node
.z
);
116 WorldDatabase
.PExecute("UPDATE creature_movement SET position_x = '%f', position_y = '%f', position_z = '%f' WHERE id = '%u' AND point = '%u'", node
.x
, node
.y
, node
.z
, id
, point
);
120 be
.model1
= fields
[4].GetUInt32();
121 be
.model2
= fields
[5].GetUInt32();
122 be
.emote
= fields
[7].GetUInt32();
123 be
.spell
= fields
[8].GetUInt32();
125 for(int i
= 0; i
< MAX_WAYPOINT_TEXT
; ++i
)
127 be
.textid
[i
] = fields
[9+i
].GetUInt32();
130 if (be
.textid
[i
] < MIN_DB_SCRIPT_STRING_ID
|| be
.textid
[i
] >= MAX_DB_SCRIPT_STRING_ID
)
132 sLog
.outErrorDb( "Table `db_script_string` not have string id %u", be
.textid
[i
]);
138 // save memory by not storing empty behaviors
141 node
.behavior
= new WaypointBehavior(be
);
145 node
.behavior
= NULL
;
147 } while( result
->NextRow() );
150 sLog
.outString( ">> Loaded %u paths, %u nodes and %u behaviors", total_paths
, total_nodes
, total_behaviors
);
153 void WaypointManager::Cleanup()
155 // check if points need to be renumbered and do it
156 if(QueryResult
*result
= WorldDatabase
.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1"))
159 WorldDatabase
.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement");
160 WorldDatabase
.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement");
161 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY");
162 WorldDatabase
.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)");
163 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)");
164 WorldDatabase
.DirectExecute("DROP TABLE temp");
165 assert(!(result
= WorldDatabase
.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1")));
169 void WaypointManager::Unload()
171 for(WaypointPathMap::iterator itr
= m_pathMap
.begin(); itr
!= m_pathMap
.end(); ++itr
)
172 _clearPath(itr
->second
);
176 void WaypointManager::_clearPath(WaypointPath
&path
)
178 for(WaypointPath::iterator itr
= path
.begin(); itr
!= path
.end(); ++itr
)
180 delete itr
->behavior
;
184 /// - Insert after the last point
185 void WaypointManager::AddLastNode(uint32 id
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
187 _addNode(id
, GetLastPoint(id
, 0) + 1, x
, y
, z
, o
, delay
, wpGuid
);
190 /// - Insert after a certain point
191 void WaypointManager::AddAfterNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
193 for(uint32 i
= GetLastPoint(id
, 0); i
> point
; i
--)
194 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id
, i
);
196 _addNode(id
, point
+ 1, x
, y
, z
, o
, delay
, wpGuid
);
199 /// - Insert without checking for collision
200 void WaypointManager::_addNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
202 if(point
== 0) return; // counted from 1 in the DB
203 WorldDatabase
.PExecuteLog("INSERT INTO creature_movement (id,point,position_x,position_y,position_z,orientation,wpguid,waittime) VALUES ('%u','%u','%f', '%f', '%f', '%f', '%d', '%d')", id
, point
, x
, y
, z
, o
, wpGuid
, delay
);
204 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
205 if(itr
== m_pathMap
.end())
206 itr
= m_pathMap
.insert(WaypointPathMap::value_type(id
, WaypointPath())).first
;
207 itr
->second
.insert(itr
->second
.begin() + (point
- 1), WaypointNode(x
, y
, z
, o
, delay
, NULL
));
210 uint32
WaypointManager::GetLastPoint(uint32 id
, uint32 default_notfound
)
212 uint32 point
= default_notfound
;
213 /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id);
216 point = (*result)[0].GetUInt32()+1;
219 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
220 if(itr
!= m_pathMap
.end() && itr
->second
.size() != 0)
221 point
= itr
->second
.size();
225 void WaypointManager::DeleteNode(uint32 id
, uint32 point
)
227 if(point
== 0) return; // counted from 1 in the DB
228 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id
, point
);
229 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id
, point
);
230 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
231 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
232 itr
->second
.erase(itr
->second
.begin() + (point
-1));
235 void WaypointManager::DeletePath(uint32 id
)
237 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id
);
238 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
239 if(itr
!= m_pathMap
.end())
240 _clearPath(itr
->second
);
241 // the path is not removed from the map, just cleared
242 // WMGs have pointers to the path, so deleting them would crash
243 // this wastes some memory, but these functions are
244 // only meant to be called by GM commands
247 void WaypointManager::SetNodePosition(uint32 id
, uint32 point
, float x
, float y
, float z
)
249 if(point
== 0) return; // counted from 1 in the DB
250 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", x
, y
, z
, id
, point
);
251 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
252 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
254 itr
->second
[point
-1].x
= x
;
255 itr
->second
[point
-1].y
= y
;
256 itr
->second
[point
-1].z
= z
;
260 void WaypointManager::SetNodeText(uint32 id
, uint32 point
, const char *text_field
, const char *text
)
262 if(point
== 0) return; // counted from 1 in the DB
263 if(!text_field
) return;
264 std::string field
= text_field
;
265 WorldDatabase
.escape_string(field
);
269 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field
.c_str(), id
, point
);
273 std::string text2
= text
;
274 WorldDatabase
.escape_string(text2
);
275 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field
.c_str(), text2
.c_str(), id
, point
);
278 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
279 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
281 WaypointNode
&node
= itr
->second
[point
-1];
282 if(!node
.behavior
) node
.behavior
= new WaypointBehavior();
284 // if(field == "text1") node.behavior->text[0] = text ? text : "";
285 // if(field == "text2") node.behavior->text[1] = text ? text : "";
286 // if(field == "text3") node.behavior->text[2] = text ? text : "";
287 // if(field == "text4") node.behavior->text[3] = text ? text : "";
288 // if(field == "text5") node.behavior->text[4] = text ? text : "";
289 if(field
== "emote") node
.behavior
->emote
= text
? atoi(text
) : 0;
290 if(field
== "spell") node
.behavior
->spell
= text
? atoi(text
) : 0;
291 if(field
== "model1") node
.behavior
->model1
= text
? atoi(text
) : 0;
292 if(field
== "model2") node
.behavior
->model2
= text
? atoi(text
) : 0;
296 void WaypointManager::CheckTextsExistance(std::set
<int32
>& ids
)
298 WaypointPathMap::iterator pmItr
= m_pathMap
.begin();
299 for ( ; pmItr
!= m_pathMap
.end(); ++pmItr
)
301 for (int i
= 0; i
< pmItr
->second
.size(); ++i
)
303 if (!pmItr
->second
[i
].behavior
)
306 // Now we check text existence and put all zero texts ids to the end of array
308 // Counting leading zeros for futher textid shift
310 for (int j
= 0; j
< MAX_WAYPOINT_TEXT
; ++j
)
312 if (!pmItr
->second
[i
].behavior
->textid
[j
])
319 if (!objmgr
.GetMangosStringLocale(pmItr
->second
[i
].behavior
->textid
[j
]))
321 sLog
.outErrorDb("ERROR: Some waypoint has textid%u with not existing %u text.", j
, pmItr
->second
[i
].behavior
->textid
[j
]);
322 pmItr
->second
[i
].behavior
->textid
[j
] = 0;
327 ids
.erase(pmItr
->second
[i
].behavior
->textid
[j
]);
332 // Correct textid but some zeros leading, so move it forward.
333 pmItr
->second
[i
].behavior
->textid
[j
-zeroCount
] = pmItr
->second
[i
].behavior
->textid
[j
];
334 pmItr
->second
[i
].behavior
->textid
[j
] = 0;