1 | /* |
---|
2 | * Copyright (C) 2005-2008 MaNGOS <http://www.mangosproject.org/> |
---|
3 | * |
---|
4 | * Copyright (C) 2008 Trinity <http://www.trinitycore.org/> |
---|
5 | * |
---|
6 | * This program is free software; you can redistribute it and/or modify |
---|
7 | * it under the terms of the GNU General Public License as published by |
---|
8 | * the Free Software Foundation; either version 2 of the License, or |
---|
9 | * (at your option) any later version. |
---|
10 | * |
---|
11 | * This program is distributed in the hope that it will be useful, |
---|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
14 | * GNU General Public License for more details. |
---|
15 | * |
---|
16 | * You should have received a copy of the GNU General Public License |
---|
17 | * along with this program; if not, write to the Free Software |
---|
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
19 | */ |
---|
20 | |
---|
21 | #include "Database/DatabaseEnv.h" |
---|
22 | #include "GridDefines.h" |
---|
23 | #include "Policies/SingletonImp.h" |
---|
24 | #include "WaypointManager.h" |
---|
25 | #include "ProgressBar.h" |
---|
26 | #include "MapManager.h" |
---|
27 | |
---|
28 | INSTANTIATE_SINGLETON_1(WaypointManager); |
---|
29 | |
---|
30 | bool WaypointBehavior::isEmpty() |
---|
31 | { |
---|
32 | return emote == 0 && spell == 0 && model1 == 0 && model2 == 0 && text[0].empty() && |
---|
33 | text[1].empty() && text[2].empty() && text[3].empty() && text[4].empty(); |
---|
34 | } |
---|
35 | |
---|
36 | WaypointBehavior::WaypointBehavior(const WaypointBehavior &b) |
---|
37 | { |
---|
38 | emote = b.emote; spell = b.spell; model1 = b.model1; model2 = b.model2; |
---|
39 | text[0] = b.text[0]; text[1] = b.text[1]; text[2] = b.text[2]; |
---|
40 | text[3] = b.text[3]; text[4] = b.text[4]; |
---|
41 | } |
---|
42 | |
---|
43 | void WaypointManager::Load() |
---|
44 | { |
---|
45 | Cleanup(); |
---|
46 | |
---|
47 | uint32 total_paths = 0; |
---|
48 | uint32 total_nodes = 0; |
---|
49 | uint32 total_behaviors = 0; |
---|
50 | |
---|
51 | QueryResult *result = WorldDatabase.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id"); |
---|
52 | if(result) |
---|
53 | { |
---|
54 | total_paths = result->GetRowCount(); |
---|
55 | barGoLink bar( total_paths ); |
---|
56 | do |
---|
57 | { |
---|
58 | Field *fields = result->Fetch(); |
---|
59 | uint32 id = fields[0].GetUInt32(); |
---|
60 | uint32 count = fields[1].GetUInt32(); |
---|
61 | m_pathMap[id].resize(count); |
---|
62 | |
---|
63 | total_nodes += count; |
---|
64 | bar.step(); |
---|
65 | } while( result->NextRow() ); |
---|
66 | delete result; |
---|
67 | } |
---|
68 | |
---|
69 | result = WorldDatabase.Query("SELECT position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id, point FROM creature_movement"); |
---|
70 | if(result) |
---|
71 | { |
---|
72 | barGoLink bar( result->GetRowCount() ); |
---|
73 | do |
---|
74 | { |
---|
75 | Field *fields = result->Fetch(); |
---|
76 | uint32 point = fields[15].GetUInt32(); |
---|
77 | uint32 id = fields[14].GetUInt32(); |
---|
78 | |
---|
79 | WaypointPath &path = m_pathMap[id]; |
---|
80 | // the cleanup queries make sure the following is true |
---|
81 | assert(point >= 1 && point <= path.size()); |
---|
82 | WaypointNode &node = path[point-1]; |
---|
83 | |
---|
84 | node.x = fields[0].GetFloat(); |
---|
85 | node.y = fields[1].GetFloat(); |
---|
86 | node.z = fields[2].GetFloat(); |
---|
87 | node.orientation = fields[3].GetFloat(); |
---|
88 | node.delay = fields[6].GetUInt16(); |
---|
89 | |
---|
90 | // prevent using invalid coordinates |
---|
91 | if(!Trinity::IsValidMapCoord(node.x, node.y, node.z, node.orientation)) |
---|
92 | { |
---|
93 | QueryResult *result1 = WorldDatabase.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id); |
---|
94 | if(result1) sLog.outErrorDb("ERROR: Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y); |
---|
95 | else sLog.outErrorDb("ERROR: Waypoint path %d, have invalid coordinates in his waypoint %d (X: %d, Y: %d).", id, point, node.x, node.y); |
---|
96 | |
---|
97 | Trinity::NormalizeMapCoord(node.x); |
---|
98 | Trinity::NormalizeMapCoord(node.y); |
---|
99 | if(result1) |
---|
100 | { |
---|
101 | node.z = MapManager::Instance ().GetBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z); |
---|
102 | delete result1; |
---|
103 | } |
---|
104 | 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); |
---|
105 | } |
---|
106 | |
---|
107 | WaypointBehavior be; |
---|
108 | be.model1 = fields[4].GetUInt32(); |
---|
109 | be.model2 = fields[5].GetUInt32(); |
---|
110 | be.emote = fields[7].GetUInt32(); |
---|
111 | be.spell = fields[8].GetUInt32(); |
---|
112 | be.text[0] = fields[9].GetCppString(); |
---|
113 | be.text[1] = fields[10].GetCppString(); |
---|
114 | be.text[2] = fields[11].GetCppString(); |
---|
115 | be.text[3] = fields[12].GetCppString(); |
---|
116 | be.text[4] = fields[13].GetCppString(); |
---|
117 | |
---|
118 | // save memory by not storing empty behaviors |
---|
119 | if(!be.isEmpty()) |
---|
120 | { |
---|
121 | node.behavior = new WaypointBehavior(be); |
---|
122 | ++total_behaviors; |
---|
123 | } |
---|
124 | else |
---|
125 | node.behavior = NULL; |
---|
126 | bar.step(); |
---|
127 | } while( result->NextRow() ); |
---|
128 | delete result; |
---|
129 | } |
---|
130 | sLog.outString( ">> Loaded %u paths, %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors); |
---|
131 | } |
---|
132 | |
---|
133 | void WaypointManager::Cleanup() |
---|
134 | { |
---|
135 | // check if points need to be renumbered and do it |
---|
136 | 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")) |
---|
137 | { |
---|
138 | delete result; |
---|
139 | WorldDatabase.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement"); |
---|
140 | WorldDatabase.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement"); |
---|
141 | WorldDatabase.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY"); |
---|
142 | WorldDatabase.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)"); |
---|
143 | WorldDatabase.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)"); |
---|
144 | WorldDatabase.DirectExecute("DROP TABLE temp"); |
---|
145 | 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"))); |
---|
146 | } |
---|
147 | } |
---|
148 | |
---|
149 | void WaypointManager::Unload() |
---|
150 | { |
---|
151 | for(WaypointPathMap::iterator itr = m_pathMap.begin(); itr != m_pathMap.end(); ++itr) |
---|
152 | _clearPath(itr->second); |
---|
153 | m_pathMap.clear(); |
---|
154 | } |
---|
155 | |
---|
156 | void WaypointManager::_clearPath(WaypointPath &path) |
---|
157 | { |
---|
158 | for(WaypointPath::iterator itr = path.begin(); itr != path.end(); ++itr) |
---|
159 | if(itr->behavior) |
---|
160 | delete itr->behavior; |
---|
161 | path.clear(); |
---|
162 | } |
---|
163 | |
---|
164 | /// - Insert after the last point |
---|
165 | void WaypointManager::AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
166 | { |
---|
167 | _addNode(id, GetLastPoint(id, 0) + 1, x, y, z, o, delay, wpGuid); |
---|
168 | } |
---|
169 | |
---|
170 | /// - Insert after a certain point |
---|
171 | void WaypointManager::AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
172 | { |
---|
173 | for(uint32 i = GetLastPoint(id, 0); i > point; i--) |
---|
174 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id, i); |
---|
175 | |
---|
176 | _addNode(id, point + 1, x, y, z, o, delay, wpGuid); |
---|
177 | } |
---|
178 | |
---|
179 | /// - Insert without checking for collision |
---|
180 | void WaypointManager::_addNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
181 | { |
---|
182 | if(point == 0) return; // counted from 1 in the DB |
---|
183 | 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); |
---|
184 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
185 | if(itr == m_pathMap.end()) |
---|
186 | itr = m_pathMap.insert(WaypointPathMap::value_type(id, WaypointPath())).first; |
---|
187 | itr->second.insert(itr->second.begin() + (point - 1), WaypointNode(x, y, z, o, delay, NULL)); |
---|
188 | } |
---|
189 | |
---|
190 | uint32 WaypointManager::GetLastPoint(uint32 id, uint32 default_notfound) |
---|
191 | { |
---|
192 | uint32 point = default_notfound; |
---|
193 | /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id); |
---|
194 | if( result ) |
---|
195 | { |
---|
196 | point = (*result)[0].GetUInt32()+1; |
---|
197 | delete result; |
---|
198 | }*/ |
---|
199 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
200 | if(itr != m_pathMap.end() && itr->second.size() != 0) |
---|
201 | point = itr->second.size(); |
---|
202 | return point; |
---|
203 | } |
---|
204 | |
---|
205 | void WaypointManager::DeleteNode(uint32 id, uint32 point) |
---|
206 | { |
---|
207 | if(point == 0) return; // counted from 1 in the DB |
---|
208 | WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id, point); |
---|
209 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id, point); |
---|
210 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
211 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
212 | itr->second.erase(itr->second.begin() + (point-1)); |
---|
213 | } |
---|
214 | |
---|
215 | void WaypointManager::DeletePath(uint32 id) |
---|
216 | { |
---|
217 | WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id); |
---|
218 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
219 | if(itr != m_pathMap.end()) |
---|
220 | _clearPath(itr->second); |
---|
221 | // the path is not removed from the map, just cleared |
---|
222 | // WMGs have pointers to the path, so deleting them would crash |
---|
223 | // this wastes some memory, but these functions are |
---|
224 | // only meant to be called by GM commands |
---|
225 | } |
---|
226 | |
---|
227 | void WaypointManager::SetNodePosition(uint32 id, uint32 point, float x, float y, float z) |
---|
228 | { |
---|
229 | if(point == 0) return; // counted from 1 in the DB |
---|
230 | 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); |
---|
231 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
232 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
233 | { |
---|
234 | itr->second[point-1].x = x; |
---|
235 | itr->second[point-1].y = y; |
---|
236 | itr->second[point-1].z = z; |
---|
237 | } |
---|
238 | } |
---|
239 | |
---|
240 | void WaypointManager::SetNodeText(uint32 id, uint32 point, const char *text_field, const char *text) |
---|
241 | { |
---|
242 | if(point == 0) return; // counted from 1 in the DB |
---|
243 | if(!text_field) return; |
---|
244 | std::string field = text_field; |
---|
245 | WorldDatabase.escape_string(field); |
---|
246 | |
---|
247 | if(!text) |
---|
248 | { |
---|
249 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field.c_str(), id, point); |
---|
250 | } |
---|
251 | else |
---|
252 | { |
---|
253 | std::string text2 = text; |
---|
254 | WorldDatabase.escape_string(text2); |
---|
255 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field.c_str(), text2.c_str(), id, point); |
---|
256 | } |
---|
257 | |
---|
258 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
259 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
260 | { |
---|
261 | WaypointNode &node = itr->second[point-1]; |
---|
262 | if(!node.behavior) node.behavior = new WaypointBehavior(); |
---|
263 | |
---|
264 | if(field == "text1") node.behavior->text[0] = text ? text : ""; |
---|
265 | if(field == "text2") node.behavior->text[1] = text ? text : ""; |
---|
266 | if(field == "text3") node.behavior->text[2] = text ? text : ""; |
---|
267 | if(field == "text4") node.behavior->text[3] = text ? text : ""; |
---|
268 | if(field == "text5") node.behavior->text[4] = text ? text : ""; |
---|
269 | if(field == "emote") node.behavior->emote = text ? atoi(text) : 0; |
---|
270 | if(field == "spell") node.behavior->spell = text ? atoi(text) : 0; |
---|
271 | if(field == "model1") node.behavior->model1 = text ? atoi(text) : 0; |
---|
272 | if(field == "model2") node.behavior->model2 = text ? atoi(text) : 0; |
---|
273 | } |
---|
274 | } |
---|