[263] | 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 |
---|
[279] | 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
[263] | 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 |
---|
[279] | 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
---|
[263] | 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" |
---|
[279] | 27 | #include "ObjectMgr.h" |
---|
[263] | 28 | |
---|
| 29 | INSTANTIATE_SINGLETON_1(WaypointManager); |
---|
| 30 | |
---|
| 31 | bool WaypointBehavior::isEmpty() |
---|
| 32 | { |
---|
[279] | 33 | if (emote || spell || model1 || model2) |
---|
| 34 | return false; |
---|
| 35 | |
---|
| 36 | for(int i = 0; i < MAX_WAYPOINT_TEXT; ++i) |
---|
| 37 | if(textid[i]) |
---|
| 38 | return false; |
---|
| 39 | |
---|
| 40 | return true; |
---|
[263] | 41 | } |
---|
| 42 | |
---|
| 43 | WaypointBehavior::WaypointBehavior(const WaypointBehavior &b) |
---|
| 44 | { |
---|
[279] | 45 | emote = b.emote; |
---|
| 46 | spell = b.spell; |
---|
| 47 | model1 = b.model1; |
---|
| 48 | model2 = b.model2; |
---|
| 49 | for(int i=0; i < MAX_WAYPOINT_TEXT; ++i) |
---|
| 50 | textid[i] = b.textid[i]; |
---|
[263] | 51 | } |
---|
| 52 | |
---|
| 53 | void WaypointManager::Load() |
---|
| 54 | { |
---|
| 55 | Cleanup(); |
---|
| 56 | |
---|
| 57 | uint32 total_paths = 0; |
---|
| 58 | uint32 total_nodes = 0; |
---|
| 59 | uint32 total_behaviors = 0; |
---|
| 60 | |
---|
| 61 | QueryResult *result = WorldDatabase.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id"); |
---|
| 62 | if(result) |
---|
| 63 | { |
---|
| 64 | total_paths = result->GetRowCount(); |
---|
| 65 | barGoLink bar( total_paths ); |
---|
| 66 | do |
---|
| 67 | { |
---|
| 68 | Field *fields = result->Fetch(); |
---|
| 69 | uint32 id = fields[0].GetUInt32(); |
---|
| 70 | uint32 count = fields[1].GetUInt32(); |
---|
| 71 | m_pathMap[id].resize(count); |
---|
| 72 | |
---|
| 73 | total_nodes += count; |
---|
| 74 | bar.step(); |
---|
| 75 | } while( result->NextRow() ); |
---|
| 76 | delete result; |
---|
| 77 | } |
---|
| 78 | |
---|
[279] | 79 | 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"); |
---|
[263] | 80 | if(result) |
---|
| 81 | { |
---|
| 82 | barGoLink bar( result->GetRowCount() ); |
---|
| 83 | do |
---|
| 84 | { |
---|
| 85 | Field *fields = result->Fetch(); |
---|
| 86 | uint32 point = fields[15].GetUInt32(); |
---|
| 87 | uint32 id = fields[14].GetUInt32(); |
---|
| 88 | |
---|
| 89 | WaypointPath &path = m_pathMap[id]; |
---|
| 90 | // the cleanup queries make sure the following is true |
---|
| 91 | assert(point >= 1 && point <= path.size()); |
---|
| 92 | WaypointNode &node = path[point-1]; |
---|
| 93 | |
---|
| 94 | node.x = fields[0].GetFloat(); |
---|
| 95 | node.y = fields[1].GetFloat(); |
---|
| 96 | node.z = fields[2].GetFloat(); |
---|
| 97 | node.orientation = fields[3].GetFloat(); |
---|
| 98 | node.delay = fields[6].GetUInt16(); |
---|
| 99 | |
---|
| 100 | // prevent using invalid coordinates |
---|
| 101 | if(!Trinity::IsValidMapCoord(node.x, node.y, node.z, node.orientation)) |
---|
| 102 | { |
---|
| 103 | QueryResult *result1 = WorldDatabase.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id); |
---|
| 104 | if(result1) |
---|
| 105 | sLog.outErrorDb("ERROR: Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).", |
---|
| 106 | id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y); |
---|
| 107 | else |
---|
| 108 | sLog.outErrorDb("ERROR: Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).", |
---|
| 109 | id, point, node.x, node.y); |
---|
| 110 | |
---|
| 111 | Trinity::NormalizeMapCoord(node.x); |
---|
| 112 | Trinity::NormalizeMapCoord(node.y); |
---|
| 113 | if(result1) |
---|
| 114 | { |
---|
| 115 | node.z = MapManager::Instance ().GetBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z); |
---|
| 116 | delete result1; |
---|
| 117 | } |
---|
| 118 | 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); |
---|
| 119 | } |
---|
| 120 | |
---|
| 121 | WaypointBehavior be; |
---|
| 122 | be.model1 = fields[4].GetUInt32(); |
---|
| 123 | be.model2 = fields[5].GetUInt32(); |
---|
| 124 | be.emote = fields[7].GetUInt32(); |
---|
| 125 | be.spell = fields[8].GetUInt32(); |
---|
| 126 | |
---|
[279] | 127 | for(int i = 0; i < MAX_WAYPOINT_TEXT; ++i) |
---|
| 128 | { |
---|
[290] | 129 | be.textid[i] = fields[9+i].GetUInt32(); |
---|
| 130 | if(be.textid[i]) |
---|
[279] | 131 | { |
---|
[290] | 132 | if (be.textid[i] < MIN_DB_SCRIPT_STRING_ID || be.textid[i] >= MAX_DB_SCRIPT_STRING_ID) |
---|
[279] | 133 | { |
---|
[290] | 134 | sLog.outErrorDb( "Table `db_script_string` not have string id %u", be.textid[i]); |
---|
[279] | 135 | continue; |
---|
| 136 | } |
---|
| 137 | } |
---|
| 138 | } |
---|
| 139 | |
---|
[263] | 140 | // save memory by not storing empty behaviors |
---|
| 141 | if(!be.isEmpty()) |
---|
| 142 | { |
---|
| 143 | node.behavior = new WaypointBehavior(be); |
---|
| 144 | ++total_behaviors; |
---|
| 145 | } |
---|
| 146 | else |
---|
| 147 | node.behavior = NULL; |
---|
| 148 | bar.step(); |
---|
| 149 | } while( result->NextRow() ); |
---|
| 150 | delete result; |
---|
| 151 | } |
---|
| 152 | sLog.outString( ">> Loaded %u paths, %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors); |
---|
| 153 | } |
---|
| 154 | |
---|
| 155 | void WaypointManager::Cleanup() |
---|
| 156 | { |
---|
| 157 | // check if points need to be renumbered and do it |
---|
| 158 | 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 | { |
---|
| 160 | delete result; |
---|
| 161 | WorldDatabase.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement"); |
---|
| 162 | WorldDatabase.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement"); |
---|
| 163 | WorldDatabase.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY"); |
---|
| 164 | WorldDatabase.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)"); |
---|
| 165 | WorldDatabase.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)"); |
---|
| 166 | WorldDatabase.DirectExecute("DROP TABLE temp"); |
---|
| 167 | 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"))); |
---|
| 168 | } |
---|
| 169 | } |
---|
| 170 | |
---|
| 171 | void WaypointManager::Unload() |
---|
| 172 | { |
---|
| 173 | for(WaypointPathMap::iterator itr = m_pathMap.begin(); itr != m_pathMap.end(); ++itr) |
---|
| 174 | _clearPath(itr->second); |
---|
| 175 | m_pathMap.clear(); |
---|
| 176 | } |
---|
| 177 | |
---|
| 178 | void WaypointManager::_clearPath(WaypointPath &path) |
---|
| 179 | { |
---|
| 180 | for(WaypointPath::iterator itr = path.begin(); itr != path.end(); ++itr) |
---|
| 181 | if(itr->behavior) |
---|
| 182 | delete itr->behavior; |
---|
| 183 | path.clear(); |
---|
| 184 | } |
---|
| 185 | |
---|
| 186 | /// - Insert after the last point |
---|
| 187 | void WaypointManager::AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
| 188 | { |
---|
| 189 | _addNode(id, GetLastPoint(id, 0) + 1, x, y, z, o, delay, wpGuid); |
---|
| 190 | } |
---|
| 191 | |
---|
| 192 | /// - Insert after a certain point |
---|
| 193 | void WaypointManager::AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
| 194 | { |
---|
| 195 | for(uint32 i = GetLastPoint(id, 0); i > point; i--) |
---|
| 196 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id, i); |
---|
| 197 | |
---|
| 198 | _addNode(id, point + 1, x, y, z, o, delay, wpGuid); |
---|
| 199 | } |
---|
| 200 | |
---|
| 201 | /// - Insert without checking for collision |
---|
| 202 | void WaypointManager::_addNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid) |
---|
| 203 | { |
---|
| 204 | if(point == 0) return; // counted from 1 in the DB |
---|
| 205 | 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); |
---|
| 206 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 207 | if(itr == m_pathMap.end()) |
---|
| 208 | itr = m_pathMap.insert(WaypointPathMap::value_type(id, WaypointPath())).first; |
---|
| 209 | itr->second.insert(itr->second.begin() + (point - 1), WaypointNode(x, y, z, o, delay, NULL)); |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | uint32 WaypointManager::GetLastPoint(uint32 id, uint32 default_notfound) |
---|
| 213 | { |
---|
| 214 | uint32 point = default_notfound; |
---|
| 215 | /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id); |
---|
| 216 | if( result ) |
---|
| 217 | { |
---|
| 218 | point = (*result)[0].GetUInt32()+1; |
---|
| 219 | delete result; |
---|
| 220 | }*/ |
---|
| 221 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 222 | if(itr != m_pathMap.end() && itr->second.size() != 0) |
---|
| 223 | point = itr->second.size(); |
---|
| 224 | return point; |
---|
| 225 | } |
---|
| 226 | |
---|
| 227 | void WaypointManager::DeleteNode(uint32 id, uint32 point) |
---|
| 228 | { |
---|
| 229 | if(point == 0) return; // counted from 1 in the DB |
---|
| 230 | WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id, point); |
---|
| 231 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id, point); |
---|
| 232 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 233 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
| 234 | itr->second.erase(itr->second.begin() + (point-1)); |
---|
| 235 | } |
---|
| 236 | |
---|
| 237 | void WaypointManager::DeletePath(uint32 id) |
---|
| 238 | { |
---|
| 239 | WorldDatabase.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id); |
---|
| 240 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 241 | if(itr != m_pathMap.end()) |
---|
| 242 | _clearPath(itr->second); |
---|
| 243 | // the path is not removed from the map, just cleared |
---|
| 244 | // WMGs have pointers to the path, so deleting them would crash |
---|
| 245 | // this wastes some memory, but these functions are |
---|
| 246 | // only meant to be called by GM commands |
---|
| 247 | } |
---|
| 248 | |
---|
| 249 | void WaypointManager::SetNodePosition(uint32 id, uint32 point, float x, float y, float z) |
---|
| 250 | { |
---|
| 251 | if(point == 0) return; // counted from 1 in the DB |
---|
| 252 | 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); |
---|
| 253 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 254 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
| 255 | { |
---|
| 256 | itr->second[point-1].x = x; |
---|
| 257 | itr->second[point-1].y = y; |
---|
| 258 | itr->second[point-1].z = z; |
---|
| 259 | } |
---|
| 260 | } |
---|
| 261 | |
---|
| 262 | void WaypointManager::SetNodeText(uint32 id, uint32 point, const char *text_field, const char *text) |
---|
| 263 | { |
---|
| 264 | if(point == 0) return; // counted from 1 in the DB |
---|
| 265 | if(!text_field) return; |
---|
| 266 | std::string field = text_field; |
---|
| 267 | WorldDatabase.escape_string(field); |
---|
| 268 | |
---|
| 269 | if(!text) |
---|
| 270 | { |
---|
| 271 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field.c_str(), id, point); |
---|
| 272 | } |
---|
| 273 | else |
---|
| 274 | { |
---|
| 275 | std::string text2 = text; |
---|
| 276 | WorldDatabase.escape_string(text2); |
---|
| 277 | WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field.c_str(), text2.c_str(), id, point); |
---|
| 278 | } |
---|
| 279 | |
---|
| 280 | WaypointPathMap::iterator itr = m_pathMap.find(id); |
---|
| 281 | if(itr != m_pathMap.end() && point <= itr->second.size()) |
---|
| 282 | { |
---|
| 283 | WaypointNode &node = itr->second[point-1]; |
---|
| 284 | if(!node.behavior) node.behavior = new WaypointBehavior(); |
---|
| 285 | |
---|
[279] | 286 | // if(field == "text1") node.behavior->text[0] = text ? text : ""; |
---|
| 287 | // if(field == "text2") node.behavior->text[1] = text ? text : ""; |
---|
| 288 | // if(field == "text3") node.behavior->text[2] = text ? text : ""; |
---|
| 289 | // if(field == "text4") node.behavior->text[3] = text ? text : ""; |
---|
| 290 | // if(field == "text5") node.behavior->text[4] = text ? text : ""; |
---|
[263] | 291 | if(field == "emote") node.behavior->emote = text ? atoi(text) : 0; |
---|
| 292 | if(field == "spell") node.behavior->spell = text ? atoi(text) : 0; |
---|
| 293 | if(field == "model1") node.behavior->model1 = text ? atoi(text) : 0; |
---|
| 294 | if(field == "model2") node.behavior->model2 = text ? atoi(text) : 0; |
---|
| 295 | } |
---|
| 296 | } |
---|
[290] | 297 | |
---|
| 298 | void WaypointManager::CheckTextsExistance(std::set<int32>& ids) |
---|
| 299 | { |
---|
| 300 | WaypointPathMap::iterator pmItr = m_pathMap.begin(); |
---|
| 301 | for ( ; pmItr != m_pathMap.end(); ++pmItr) |
---|
| 302 | { |
---|
| 303 | for (int i = 0; i < pmItr->second.size(); ++i) |
---|
| 304 | { |
---|
| 305 | if (!pmItr->second[i].behavior) |
---|
| 306 | continue; |
---|
| 307 | |
---|
| 308 | // Now we check text existence and put all zero texts ids to the end of array |
---|
| 309 | |
---|
| 310 | // Counting leading zeros for futher textid shift |
---|
| 311 | int zeroCount = 0; |
---|
| 312 | for (int j = 0; j < MAX_WAYPOINT_TEXT; ++j) |
---|
| 313 | { |
---|
| 314 | if (!pmItr->second[i].behavior->textid[j]) |
---|
| 315 | { |
---|
| 316 | ++zeroCount; |
---|
| 317 | continue; |
---|
| 318 | } |
---|
| 319 | else |
---|
| 320 | { |
---|
| 321 | if (!objmgr.GetMangosStringLocale(pmItr->second[i].behavior->textid[j])) |
---|
| 322 | { |
---|
| 323 | sLog.outErrorDb("ERROR: Some waypoint has textid%u with not existing %u text.", j, pmItr->second[i].behavior->textid[j]); |
---|
| 324 | pmItr->second[i].behavior->textid[j] = 0; |
---|
| 325 | ++zeroCount; |
---|
| 326 | continue; |
---|
| 327 | } |
---|
| 328 | else |
---|
| 329 | ids.erase(pmItr->second[i].behavior->textid[j]); |
---|
| 330 | |
---|
| 331 | // Shifting check |
---|
| 332 | if (zeroCount) |
---|
| 333 | { |
---|
| 334 | // Correct textid but some zeros leading, so move it forward. |
---|
| 335 | pmItr->second[i].behavior->textid[j-zeroCount] = pmItr->second[i].behavior->textid[j]; |
---|
| 336 | pmItr->second[i].behavior->textid[j] = 0; |
---|
| 337 | } |
---|
| 338 | } |
---|
| 339 | } |
---|
| 340 | } |
---|
| 341 | } |
---|
| 342 | } |
---|