| @@ -0,0 +1,1223 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "protocolgame.h" | |
| 24 | + | #include "game.h" | |
| 25 | + | #include "client.h" | |
| 26 | + | #include <framework/core/application.h> | |
| 27 | + | #include <framework/platform/platform.h> | |
| 28 | + | #include <framework/util/crypt.h> | |
| 29 | + | #include <framework/util/extras.h> | |
| 30 | + | ||
| 31 | + | void ProtocolGame::send(const OutputMessagePtr& outputMessage, bool rawPacket) | |
| 32 | + | { | |
| 33 | + | // avoid usage of automated sends (bot modules) | |
| 34 | + | if(!g_game.checkBotProtection()) | |
| 35 | + | return; | |
| 36 | + | Protocol::send(outputMessage, rawPacket); | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | void ProtocolGame::sendExtendedOpcode(uint8 opcode, const std::string& buffer) | |
| 40 | + | { | |
| 41 | + | g_game.enableBotCall(); | |
| 42 | + | if(m_enableSendExtendedOpcode) { | |
| 43 | + | OutputMessagePtr msg(new OutputMessage); | |
| 44 | + | msg->addU8(Proto::ClientExtendedOpcode); | |
| 45 | + | msg->addU8(opcode); | |
| 46 | + | msg->addString(buffer); | |
| 47 | + | send(msg); | |
| 48 | + | } else { | |
| 49 | + | g_logger.error(stdext::format("Unable to send extended opcode %d, extended opcodes are not enabled on this server.", opcode)); | |
| 50 | + | } | |
| 51 | + | g_game.disableBotCall(); | |
| 52 | + | } | |
| 53 | + | ||
| 54 | + | void ProtocolGame::sendWorldName() | |
| 55 | + | { | |
| 56 | + | OutputMessagePtr msg(new OutputMessage); | |
| 57 | + | msg->addRawString(m_worldName + "\n"); | |
| 58 | + | send(msg, true); | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | void ProtocolGame::sendLoginPacket(uint challengeTimestamp, uint8 challengeRandom) | |
| 62 | + | { | |
| 63 | + | OutputMessagePtr msg(new OutputMessage); | |
| 64 | + | ||
| 65 | + | msg->addU8(Proto::ClientPendingGame); | |
| 66 | + | msg->addU16(g_game.getOs()); | |
| 67 | + | msg->addU16(g_game.getCustomProtocolVersion()); | |
| 68 | + | ||
| 69 | + | if (g_game.getFeature(Otc::GameClientVersion)) | |
| 70 | + | msg->addU32(g_game.getClientVersion()); | |
| 71 | + | ||
| 72 | + | if (g_game.getFeature(Otc::GameTibia12Protocol) && g_game.getProtocolVersion() >= 1240) | |
| 73 | + | msg->addString(std::to_string(g_game.getClientVersion())); | |
| 74 | + | ||
| 75 | + | if (g_game.getFeature(Otc::GameContentRevision)) | |
| 76 | + | msg->addU16(g_things.getContentRevision()); | |
| 77 | + | ||
| 78 | + | if (g_game.getFeature(Otc::GamePreviewState)) | |
| 79 | + | msg->addU8(0); | |
| 80 | + | ||
| 81 | + | int offset = msg->getMessageSize(); | |
| 82 | + | // first RSA byte must be 0 | |
| 83 | + | msg->addU8(0); | |
| 84 | + | ||
| 85 | + | if (g_game.getFeature(Otc::GameLoginPacketEncryption)) { | |
| 86 | + | // xtea key | |
| 87 | + | generateXteaKey(); | |
| 88 | + | msg->addU32(m_xteaKey[0]); | |
| 89 | + | msg->addU32(m_xteaKey[1]); | |
| 90 | + | msg->addU32(m_xteaKey[2]); | |
| 91 | + | msg->addU32(m_xteaKey[3]); | |
| 92 | + | msg->addU8(0); // is gm set? | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | if (g_game.getFeature(Otc::GameSessionKey)) { | |
| 96 | + | msg->addString(m_sessionKey); | |
| 97 | + | msg->addString(m_characterName); | |
| 98 | + | } else { | |
| 99 | + | if (g_game.getFeature(Otc::GameAccountNames)) | |
| 100 | + | msg->addString(m_accountName); | |
| 101 | + | else | |
| 102 | + | msg->addU32(stdext::from_string<uint32>(m_accountName)); | |
| 103 | + | ||
| 104 | + | msg->addString(m_characterName); | |
| 105 | + | msg->addString(m_accountPassword); | |
| 106 | + | ||
| 107 | + | if (g_game.getFeature(Otc::GameAuthenticator)) | |
| 108 | + | msg->addString(m_authenticatorToken); | |
| 109 | + | } | |
| 110 | + | ||
| 111 | + | if (g_game.getFeature(Otc::GameChallengeOnLogin)) { | |
| 112 | + | msg->addU32(challengeTimestamp); | |
| 113 | + | msg->addU8(challengeRandom); | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | std::string extended = callLuaField<std::string>("getLoginExtendedData"); | |
| 117 | + | if (!extended.empty()) { | |
| 118 | + | msg->addString(extended); | |
| 119 | + | } else { | |
| 120 | + | msg->addString(std::string("OTCv8")); | |
| 121 | + | std::string version = g_app.getVersion(); | |
| 122 | + | version = stdext::split(version, " ")[0]; | |
| 123 | + | stdext::replace_all(version, ".", ""); | |
| 124 | + | if (version.length() == 2) { | |
| 125 | + | version += "0"; | |
| 126 | + | } | |
| 127 | + | msg->addU16(atoi(version.c_str())); | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | // encrypt with RSA | |
| 131 | + | if (g_game.getFeature(Otc::GameLoginPacketEncryption)) { | |
| 132 | + | int paddingBytes = g_crypt.rsaGetSize() - (msg->getMessageSize() - offset); | |
| 133 | + | VALIDATE(paddingBytes >= 0); | |
| 134 | + | msg->addPaddingBytes(paddingBytes); | |
| 135 | + | msg->encryptRsa(); | |
| 136 | + | } | |
| 137 | + | ||
| 138 | + | if (g_game.getFeature(Otc::GameSendIdentifiers)) { | |
| 139 | + | std::string user = g_platform.getUserName().substr(0, 20); | |
| 140 | + | std::string cpu = g_platform.getCPUName().substr(0, 20); | |
| 141 | + | uint32_t memory = (g_platform.getTotalSystemMemory() / (1024 * 1024)); | |
| 142 | + | auto macs = g_platform.getMacAddresses(); | |
| 143 | + | if (macs.size() > 4) { | |
| 144 | + | macs.resize(4); | |
| 145 | + | } | |
| 146 | + | ||
| 147 | + | offset = msg->getMessageSize(); | |
| 148 | + | msg->addU8(0); // first RSA byte must be 0 | |
| 149 | + | msg->addString(user); // max 22 bytes | |
| 150 | + | msg->addString(cpu); // max 22 bytes | |
| 151 | + | msg->addU32(memory); | |
| 152 | + | msg->addU8(macs.size()); | |
| 153 | + | for (auto& mac : macs) { | |
| 154 | + | msg->addString(mac); // 18 bytes | |
| 155 | + | } | |
| 156 | + | if (g_game.getFeature(Otc::GameLoginPacketEncryption)) { | |
| 157 | + | int paddingBytes = g_crypt.rsaGetSize() - (msg->getMessageSize() - offset); | |
| 158 | + | VALIDATE(paddingBytes >= 0); | |
| 159 | + | msg->addPaddingBytes(paddingBytes); | |
| 160 | + | msg->encryptRsa(); | |
| 161 | + | } | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | if(g_game.getFeature(Otc::GameProtocolChecksum)) | |
| 165 | + | enableChecksum(); | |
| 166 | + | ||
| 167 | + | send(msg); | |
| 168 | + | ||
| 169 | + | if(g_game.getFeature(Otc::GameLoginPacketEncryption)) | |
| 170 | + | enableXteaEncryption(); | |
| 171 | + | ||
| 172 | + | if (g_game.getFeature(Otc::GamePacketCompression)) | |
| 173 | + | enableCompression(); | |
| 174 | + | ||
| 175 | + | if (g_game.getFeature(Otc::GameSequencedPackets)) | |
| 176 | + | enabledSequencedPackets(); | |
| 177 | + | } | |
| 178 | + | ||
| 179 | + | void ProtocolGame::sendEnterGame() | |
| 180 | + | { | |
| 181 | + | OutputMessagePtr msg(new OutputMessage); | |
| 182 | + | msg->addU8(Proto::ClientEnterGame); | |
| 183 | + | send(msg); | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | void ProtocolGame::sendLogout() | |
| 187 | + | { | |
| 188 | + | OutputMessagePtr msg(new OutputMessage); | |
| 189 | + | msg->addU8(Proto::ClientLeaveGame); | |
| 190 | + | send(msg); | |
| 191 | + | } | |
| 192 | + | ||
| 193 | + | void ProtocolGame::sendPing() | |
| 194 | + | { | |
| 195 | + | OutputMessagePtr msg(new OutputMessage); | |
| 196 | + | msg->addU8(Proto::ClientPing); | |
| 197 | + | Protocol::send(msg); | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | void ProtocolGame::sendPingBack() | |
| 201 | + | { | |
| 202 | + | OutputMessagePtr msg(new OutputMessage); | |
| 203 | + | msg->addU8(Proto::ClientPingBack); | |
| 204 | + | send(msg); | |
| 205 | + | } | |
| 206 | + | ||
| 207 | + | void ProtocolGame::sendNewPing(uint32_t pingId, uint16_t localPing, uint16_t fps) | |
| 208 | + | { | |
| 209 | + | OutputMessagePtr msg(new OutputMessage); | |
| 210 | + | msg->addU8(Proto::ClientNewPing); | |
| 211 | + | msg->addU32(pingId); | |
| 212 | + | msg->addU16(localPing); | |
| 213 | + | msg->addU16(fps); | |
| 214 | + | send(msg); | |
| 215 | + | } | |
| 216 | + | ||
| 217 | + | void ProtocolGame::sendAutoWalk(const std::vector<Otc::Direction>& path) | |
| 218 | + | { | |
| 219 | + | OutputMessagePtr msg(new OutputMessage); | |
| 220 | + | msg->addU8(Proto::ClientAutoWalk); | |
| 221 | + | msg->addU8(path.size()); | |
| 222 | + | for(Otc::Direction dir : path) { | |
| 223 | + | uint8 byte; | |
| 224 | + | switch(dir) { | |
| 225 | + | case Otc::East: | |
| 226 | + | byte = 1; | |
| 227 | + | break; | |
| 228 | + | case Otc::NorthEast: | |
| 229 | + | byte = 2; | |
| 230 | + | break; | |
| 231 | + | case Otc::North: | |
| 232 | + | byte = 3; | |
| 233 | + | break; | |
| 234 | + | case Otc::NorthWest: | |
| 235 | + | byte = 4; | |
| 236 | + | break; | |
| 237 | + | case Otc::West: | |
| 238 | + | byte = 5; | |
| 239 | + | break; | |
| 240 | + | case Otc::SouthWest: | |
| 241 | + | byte = 6; | |
| 242 | + | break; | |
| 243 | + | case Otc::South: | |
| 244 | + | byte = 7; | |
| 245 | + | break; | |
| 246 | + | case Otc::SouthEast: | |
| 247 | + | byte = 8; | |
| 248 | + | break; | |
| 249 | + | default: | |
| 250 | + | byte = 0; | |
| 251 | + | break; | |
| 252 | + | } | |
| 253 | + | msg->addU8(byte); | |
| 254 | + | } | |
| 255 | + | send(msg); | |
| 256 | + | } | |
| 257 | + | ||
| 258 | + | void ProtocolGame::sendWalkNorth() | |
| 259 | + | { | |
| 260 | + | OutputMessagePtr msg(new OutputMessage); | |
| 261 | + | msg->addU8(Proto::ClientWalkNorth); | |
| 262 | + | send(msg); | |
| 263 | + | } | |
| 264 | + | ||
| 265 | + | void ProtocolGame::sendWalkEast() | |
| 266 | + | { | |
| 267 | + | OutputMessagePtr msg(new OutputMessage); | |
| 268 | + | msg->addU8(Proto::ClientWalkEast); | |
| 269 | + | send(msg); | |
| 270 | + | } | |
| 271 | + | ||
| 272 | + | void ProtocolGame::sendWalkSouth() | |
| 273 | + | { | |
| 274 | + | OutputMessagePtr msg(new OutputMessage); | |
| 275 | + | msg->addU8(Proto::ClientWalkSouth); | |
| 276 | + | send(msg); | |
| 277 | + | } | |
| 278 | + | ||
| 279 | + | void ProtocolGame::sendWalkWest() | |
| 280 | + | { | |
| 281 | + | OutputMessagePtr msg(new OutputMessage); | |
| 282 | + | msg->addU8(Proto::ClientWalkWest); | |
| 283 | + | send(msg); | |
| 284 | + | } | |
| 285 | + | ||
| 286 | + | void ProtocolGame::sendStop() | |
| 287 | + | { | |
| 288 | + | OutputMessagePtr msg(new OutputMessage); | |
| 289 | + | msg->addU8(Proto::ClientStop); | |
| 290 | + | send(msg); | |
| 291 | + | } | |
| 292 | + | ||
| 293 | + | void ProtocolGame::sendWalkNorthEast() | |
| 294 | + | { | |
| 295 | + | OutputMessagePtr msg(new OutputMessage); | |
| 296 | + | msg->addU8(Proto::ClientWalkNorthEast); | |
| 297 | + | send(msg); | |
| 298 | + | } | |
| 299 | + | ||
| 300 | + | void ProtocolGame::sendWalkSouthEast() | |
| 301 | + | { | |
| 302 | + | OutputMessagePtr msg(new OutputMessage); | |
| 303 | + | msg->addU8(Proto::ClientWalkSouthEast); | |
| 304 | + | send(msg); | |
| 305 | + | } | |
| 306 | + | ||
| 307 | + | void ProtocolGame::sendWalkSouthWest() | |
| 308 | + | { | |
| 309 | + | OutputMessagePtr msg(new OutputMessage); | |
| 310 | + | msg->addU8(Proto::ClientWalkSouthWest); | |
| 311 | + | send(msg); | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | void ProtocolGame::sendWalkNorthWest() | |
| 315 | + | { | |
| 316 | + | OutputMessagePtr msg(new OutputMessage); | |
| 317 | + | msg->addU8(Proto::ClientWalkNorthWest); | |
| 318 | + | send(msg); | |
| 319 | + | } | |
| 320 | + | ||
| 321 | + | void ProtocolGame::sendTurnNorth() | |
| 322 | + | { | |
| 323 | + | OutputMessagePtr msg(new OutputMessage); | |
| 324 | + | msg->addU8(Proto::ClientTurnNorth); | |
| 325 | + | send(msg); | |
| 326 | + | } | |
| 327 | + | ||
| 328 | + | void ProtocolGame::sendTurnEast() | |
| 329 | + | { | |
| 330 | + | OutputMessagePtr msg(new OutputMessage); | |
| 331 | + | msg->addU8(Proto::ClientTurnEast); | |
| 332 | + | send(msg); | |
| 333 | + | } | |
| 334 | + | ||
| 335 | + | void ProtocolGame::sendTurnSouth() | |
| 336 | + | { | |
| 337 | + | OutputMessagePtr msg(new OutputMessage); | |
| 338 | + | msg->addU8(Proto::ClientTurnSouth); | |
| 339 | + | send(msg); | |
| 340 | + | } | |
| 341 | + | ||
| 342 | + | void ProtocolGame::sendTurnWest() | |
| 343 | + | { | |
| 344 | + | OutputMessagePtr msg(new OutputMessage); | |
| 345 | + | msg->addU8(Proto::ClientTurnWest); | |
| 346 | + | send(msg); | |
| 347 | + | } | |
| 348 | + | ||
| 349 | + | void ProtocolGame::sendEquipItem(int itemId, int countOrSubType) | |
| 350 | + | { | |
| 351 | + | OutputMessagePtr msg(new OutputMessage); | |
| 352 | + | msg->addU8(Proto::ClientEquipItem); | |
| 353 | + | msg->addU16(itemId); | |
| 354 | + | if (g_game.getFeature(Otc::GameCountU16)) | |
| 355 | + | msg->addU16(countOrSubType); | |
| 356 | + | else | |
| 357 | + | msg->addU8(countOrSubType); | |
| 358 | + | send(msg); | |
| 359 | + | } | |
| 360 | + | ||
| 361 | + | void ProtocolGame::sendMove(const Position& fromPos, int thingId, int stackpos, const Position& toPos, int count) | |
| 362 | + | { | |
| 363 | + | OutputMessagePtr msg(new OutputMessage); | |
| 364 | + | msg->addU8(Proto::ClientMove); | |
| 365 | + | addPosition(msg, fromPos); | |
| 366 | + | msg->addU16(thingId); | |
| 367 | + | msg->addU8(stackpos); | |
| 368 | + | addPosition(msg, toPos); | |
| 369 | + | if(g_game.getFeature(Otc::GameCountU16)) | |
| 370 | + | msg->addU16(count); | |
| 371 | + | else | |
| 372 | + | msg->addU8(count); | |
| 373 | + | send(msg); | |
| 374 | + | } | |
| 375 | + | ||
| 376 | + | void ProtocolGame::sendInspectNpcTrade(int itemId, int count) | |
| 377 | + | { | |
| 378 | + | OutputMessagePtr msg(new OutputMessage); | |
| 379 | + | msg->addU8(Proto::ClientInspectNpcTrade); | |
| 380 | + | msg->addU16(itemId); | |
| 381 | + | if (g_game.getFeature(Otc::GameCountU16)) | |
| 382 | + | msg->addU16(count); | |
| 383 | + | else | |
| 384 | + | msg->addU8(count); | |
| 385 | + | send(msg); | |
| 386 | + | } | |
| 387 | + | ||
| 388 | + | void ProtocolGame::sendBuyItem(int itemId, int subType, int amount, bool ignoreCapacity, bool buyWithBackpack) | |
| 389 | + | { | |
| 390 | + | OutputMessagePtr msg(new OutputMessage); | |
| 391 | + | msg->addU8(Proto::ClientBuyItem); | |
| 392 | + | msg->addU16(itemId); | |
| 393 | + | msg->addU8(subType); | |
| 394 | + | msg->addU8(amount); | |
| 395 | + | msg->addU8(ignoreCapacity ? 0x01 : 0x00); | |
| 396 | + | msg->addU8(buyWithBackpack ? 0x01 : 0x00); | |
| 397 | + | send(msg); | |
| 398 | + | } | |
| 399 | + | ||
| 400 | + | void ProtocolGame::sendSellItem(int itemId, int subType, int amount, bool ignoreEquipped) | |
| 401 | + | { | |
| 402 | + | OutputMessagePtr msg(new OutputMessage); | |
| 403 | + | msg->addU8(Proto::ClientSellItem); | |
| 404 | + | msg->addU16(itemId); | |
| 405 | + | msg->addU8(subType); | |
| 406 | + | if(g_game.getFeature(Otc::GameDoubleShopSellAmount)) | |
| 407 | + | msg->addU16(amount); | |
| 408 | + | else | |
| 409 | + | msg->addU8(amount); | |
| 410 | + | msg->addU8(ignoreEquipped ? 0x01 : 0x00); | |
| 411 | + | send(msg); | |
| 412 | + | } | |
| 413 | + | ||
| 414 | + | void ProtocolGame::sendCloseNpcTrade() | |
| 415 | + | { | |
| 416 | + | OutputMessagePtr msg(new OutputMessage); | |
| 417 | + | msg->addU8(Proto::ClientCloseNpcTrade); | |
| 418 | + | send(msg); | |
| 419 | + | } | |
| 420 | + | ||
| 421 | + | void ProtocolGame::sendRequestTrade(const Position& pos, int thingId, int stackpos, uint creatureId) | |
| 422 | + | { | |
| 423 | + | OutputMessagePtr msg(new OutputMessage); | |
| 424 | + | msg->addU8(Proto::ClientRequestTrade); | |
| 425 | + | addPosition(msg, pos); | |
| 426 | + | msg->addU16(thingId); | |
| 427 | + | msg->addU8(stackpos); | |
| 428 | + | msg->addU32(creatureId); | |
| 429 | + | send(msg); | |
| 430 | + | } | |
| 431 | + | ||
| 432 | + | void ProtocolGame::sendInspectTrade(bool counterOffer, int index) | |
| 433 | + | { | |
| 434 | + | OutputMessagePtr msg(new OutputMessage); | |
| 435 | + | msg->addU8(Proto::ClientInspectTrade); | |
| 436 | + | msg->addU8(counterOffer ? 0x01 : 0x00); | |
| 437 | + | msg->addU8(index); | |
| 438 | + | send(msg); | |
| 439 | + | } | |
| 440 | + | ||
| 441 | + | void ProtocolGame::sendAcceptTrade() | |
| 442 | + | { | |
| 443 | + | OutputMessagePtr msg(new OutputMessage); | |
| 444 | + | msg->addU8(Proto::ClientAcceptTrade); | |
| 445 | + | send(msg); | |
| 446 | + | } | |
| 447 | + | ||
| 448 | + | void ProtocolGame::sendRejectTrade() | |
| 449 | + | { | |
| 450 | + | OutputMessagePtr msg(new OutputMessage); | |
| 451 | + | msg->addU8(Proto::ClientRejectTrade); | |
| 452 | + | send(msg); | |
| 453 | + | } | |
| 454 | + | ||
| 455 | + | void ProtocolGame::sendUseItem(const Position& position, int itemId, int stackpos, int index) | |
| 456 | + | { | |
| 457 | + | OutputMessagePtr msg(new OutputMessage); | |
| 458 | + | msg->addU8(Proto::ClientUseItem); | |
| 459 | + | addPosition(msg, position); | |
| 460 | + | msg->addU16(itemId); | |
| 461 | + | msg->addU8(stackpos); | |
| 462 | + | msg->addU8(index); | |
| 463 | + | send(msg); | |
| 464 | + | } | |
| 465 | + | ||
| 466 | + | void ProtocolGame::sendUseItemWith(const Position& fromPos, int itemId, int fromStackPos, const Position& toPos, int toThingId, int toStackPos) | |
| 467 | + | { | |
| 468 | + | OutputMessagePtr msg(new OutputMessage); | |
| 469 | + | msg->addU8(Proto::ClientUseItemWith); | |
| 470 | + | addPosition(msg, fromPos); | |
| 471 | + | msg->addU16(itemId); | |
| 472 | + | msg->addU8(fromStackPos); | |
| 473 | + | addPosition(msg, toPos); | |
| 474 | + | msg->addU16(toThingId); | |
| 475 | + | msg->addU8(toStackPos); | |
| 476 | + | send(msg); | |
| 477 | + | } | |
| 478 | + | ||
| 479 | + | void ProtocolGame::sendUseOnCreature(const Position& pos, int thingId, int stackpos, uint creatureId) | |
| 480 | + | { | |
| 481 | + | OutputMessagePtr msg(new OutputMessage); | |
| 482 | + | msg->addU8(Proto::ClientUseOnCreature); | |
| 483 | + | addPosition(msg, pos); | |
| 484 | + | msg->addU16(thingId); | |
| 485 | + | msg->addU8(stackpos); | |
| 486 | + | msg->addU32(creatureId); | |
| 487 | + | send(msg); | |
| 488 | + | } | |
| 489 | + | ||
| 490 | + | void ProtocolGame::sendRotateItem(const Position& pos, int thingId, int stackpos) | |
| 491 | + | { | |
| 492 | + | OutputMessagePtr msg(new OutputMessage); | |
| 493 | + | msg->addU8(Proto::ClientRotateItem); | |
| 494 | + | addPosition(msg, pos); | |
| 495 | + | msg->addU16(thingId); | |
| 496 | + | msg->addU8(stackpos); | |
| 497 | + | send(msg); | |
| 498 | + | } | |
| 499 | + | ||
| 500 | + | void ProtocolGame::sendWrapableItem(const Position& pos, int thingId, int stackpos) | |
| 501 | + | { | |
| 502 | + | OutputMessagePtr msg(new OutputMessage); | |
| 503 | + | msg->addU8(Proto::ClientWrapableItem); | |
| 504 | + | addPosition(msg, pos); | |
| 505 | + | msg->addU16(thingId); | |
| 506 | + | msg->addU8(stackpos); | |
| 507 | + | send(msg); | |
| 508 | + | } | |
| 509 | + | ||
| 510 | + | void ProtocolGame::sendCloseContainer(int containerId) | |
| 511 | + | { | |
| 512 | + | OutputMessagePtr msg(new OutputMessage); | |
| 513 | + | msg->addU8(Proto::ClientCloseContainer); | |
| 514 | + | msg->addU8(containerId); | |
| 515 | + | send(msg); | |
| 516 | + | } | |
| 517 | + | ||
| 518 | + | void ProtocolGame::sendUpContainer(int containerId) | |
| 519 | + | { | |
| 520 | + | OutputMessagePtr msg(new OutputMessage); | |
| 521 | + | msg->addU8(Proto::ClientUpContainer); | |
| 522 | + | msg->addU8(containerId); | |
| 523 | + | send(msg); | |
| 524 | + | } | |
| 525 | + | ||
| 526 | + | void ProtocolGame::sendEditText(uint id, const std::string& text) | |
| 527 | + | { | |
| 528 | + | OutputMessagePtr msg(new OutputMessage); | |
| 529 | + | msg->addU8(Proto::ClientEditText); | |
| 530 | + | msg->addU32(id); | |
| 531 | + | msg->addString(text); | |
| 532 | + | send(msg); | |
| 533 | + | } | |
| 534 | + | ||
| 535 | + | void ProtocolGame::sendEditList(uint id, int doorId, const std::string& text) | |
| 536 | + | { | |
| 537 | + | OutputMessagePtr msg(new OutputMessage); | |
| 538 | + | msg->addU8(Proto::ClientEditList); | |
| 539 | + | msg->addU8(doorId); | |
| 540 | + | msg->addU32(id); | |
| 541 | + | msg->addString(text); | |
| 542 | + | send(msg); | |
| 543 | + | } | |
| 544 | + | ||
| 545 | + | void ProtocolGame::sendLook(const Position& position, int thingId, int stackpos) | |
| 546 | + | { | |
| 547 | + | OutputMessagePtr msg(new OutputMessage); | |
| 548 | + | msg->addU8(Proto::ClientLook); | |
| 549 | + | addPosition(msg, position); | |
| 550 | + | msg->addU16(thingId); | |
| 551 | + | msg->addU8(stackpos); | |
| 552 | + | send(msg); | |
| 553 | + | } | |
| 554 | + | ||
| 555 | + | void ProtocolGame::sendLookCreature(uint32 creatureId) | |
| 556 | + | { | |
| 557 | + | OutputMessagePtr msg(new OutputMessage); | |
| 558 | + | msg->addU8(Proto::ClientLookCreature); | |
| 559 | + | msg->addU32(creatureId); | |
| 560 | + | send(msg); | |
| 561 | + | } | |
| 562 | + | ||
| 563 | + | void ProtocolGame::sendTalk(Otc::MessageMode mode, int channelId, const std::string& receiver, const std::string& message, const Position& pos, Otc::Direction dir) | |
| 564 | + | { | |
| 565 | + | if(message.empty()) | |
| 566 | + | return; | |
| 567 | + | ||
| 568 | + | if(message.length() > 255) { | |
| 569 | + | g_logger.traceError("message too large"); | |
| 570 | + | return; | |
| 571 | + | } | |
| 572 | + | ||
| 573 | + | OutputMessagePtr msg(new OutputMessage); | |
| 574 | + | msg->addU8(Proto::ClientTalk); | |
| 575 | + | msg->addU8(Proto::translateMessageModeToServer(mode)); | |
| 576 | + | ||
| 577 | + | switch(mode) { | |
| 578 | + | case Otc::MessagePrivateTo: | |
| 579 | + | case Otc::MessageGamemasterPrivateTo: | |
| 580 | + | case Otc::MessageRVRAnswer: | |
| 581 | + | msg->addString(receiver); | |
| 582 | + | break; | |
| 583 | + | case Otc::MessageChannel: | |
| 584 | + | case Otc::MessageChannelHighlight: | |
| 585 | + | case Otc::MessageChannelManagement: | |
| 586 | + | case Otc::MessageGamemasterChannel: | |
| 587 | + | msg->addU16(channelId); | |
| 588 | + | break; | |
| 589 | + | default: | |
| 590 | + | break; | |
| 591 | + | } | |
| 592 | + | ||
| 593 | + | msg->addString(message); | |
| 594 | + | ||
| 595 | + | if(g_game.getFeature(Otc::GameNewWalking)) { | |
| 596 | + | // fix for spell direction | |
| 597 | + | addPosition(msg, pos); | |
| 598 | + | uint8 byte; | |
| 599 | + | switch(dir) { | |
| 600 | + | case Otc::East: | |
| 601 | + | case Otc::NorthEast: | |
| 602 | + | case Otc::SouthEast: | |
| 603 | + | byte = 1; | |
| 604 | + | break; | |
| 605 | + | case Otc::North: | |
| 606 | + | byte = 3; | |
| 607 | + | break; | |
| 608 | + | case Otc::SouthWest: | |
| 609 | + | case Otc::NorthWest: | |
| 610 | + | case Otc::West: | |
| 611 | + | byte = 5; | |
| 612 | + | break; | |
| 613 | + | case Otc::South: | |
| 614 | + | byte = 7; | |
| 615 | + | break; | |
| 616 | + | default: | |
| 617 | + | byte = 0; | |
| 618 | + | break; | |
| 619 | + | } | |
| 620 | + | msg->addU8(byte); | |
| 621 | + | } | |
| 622 | + | ||
| 623 | + | send(msg); | |
| 624 | + | } | |
| 625 | + | ||
| 626 | + | void ProtocolGame::sendRequestChannels() | |
| 627 | + | { | |
| 628 | + | OutputMessagePtr msg(new OutputMessage); | |
| 629 | + | msg->addU8(Proto::ClientRequestChannels); | |
| 630 | + | send(msg); | |
| 631 | + | } | |
| 632 | + | ||
| 633 | + | void ProtocolGame::sendJoinChannel(int channelId) | |
| 634 | + | { | |
| 635 | + | OutputMessagePtr msg(new OutputMessage); | |
| 636 | + | msg->addU8(Proto::ClientJoinChannel); | |
| 637 | + | msg->addU16(channelId); | |
| 638 | + | send(msg); | |
| 639 | + | } | |
| 640 | + | ||
| 641 | + | void ProtocolGame::sendLeaveChannel(int channelId) | |
| 642 | + | { | |
| 643 | + | OutputMessagePtr msg(new OutputMessage); | |
| 644 | + | msg->addU8(Proto::ClientLeaveChannel); | |
| 645 | + | msg->addU16(channelId); | |
| 646 | + | send(msg); | |
| 647 | + | } | |
| 648 | + | ||
| 649 | + | void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver) | |
| 650 | + | { | |
| 651 | + | OutputMessagePtr msg(new OutputMessage); | |
| 652 | + | msg->addU8(Proto::ClientOpenPrivateChannel); | |
| 653 | + | msg->addString(receiver); | |
| 654 | + | send(msg); | |
| 655 | + | } | |
| 656 | + | ||
| 657 | + | void ProtocolGame::sendOpenRuleViolation(const std::string& reporter) | |
| 658 | + | { | |
| 659 | + | OutputMessagePtr msg(new OutputMessage); | |
| 660 | + | msg->addU8(Proto::ClientOpenRuleViolation); | |
| 661 | + | msg->addString(reporter); | |
| 662 | + | send(msg); | |
| 663 | + | } | |
| 664 | + | ||
| 665 | + | void ProtocolGame::sendCloseRuleViolation(const std::string& reporter) | |
| 666 | + | { | |
| 667 | + | OutputMessagePtr msg(new OutputMessage); | |
| 668 | + | msg->addU8(Proto::ClientCloseRuleViolation); | |
| 669 | + | msg->addString(reporter); | |
| 670 | + | send(msg); | |
| 671 | + | } | |
| 672 | + | ||
| 673 | + | void ProtocolGame::sendCancelRuleViolation() | |
| 674 | + | { | |
| 675 | + | OutputMessagePtr msg(new OutputMessage); | |
| 676 | + | msg->addU8(Proto::ClientCancelRuleViolation); | |
| 677 | + | send(msg); | |
| 678 | + | } | |
| 679 | + | ||
| 680 | + | void ProtocolGame::sendCloseNpcChannel() | |
| 681 | + | { | |
| 682 | + | OutputMessagePtr msg(new OutputMessage); | |
| 683 | + | msg->addU8(Proto::ClientCloseNpcChannel); | |
| 684 | + | send(msg); | |
| 685 | + | } | |
| 686 | + | ||
| 687 | + | void ProtocolGame::sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseModes chaseMode, bool safeFight, Otc::PVPModes pvpMode) | |
| 688 | + | { | |
| 689 | + | OutputMessagePtr msg(new OutputMessage); | |
| 690 | + | msg->addU8(Proto::ClientChangeFightModes); | |
| 691 | + | msg->addU8(fightMode); | |
| 692 | + | msg->addU8(chaseMode); | |
| 693 | + | msg->addU8(safeFight ? 0x01: 0x00); | |
| 694 | + | if(g_game.getFeature(Otc::GamePVPMode)) | |
| 695 | + | msg->addU8(pvpMode); | |
| 696 | + | send(msg); | |
| 697 | + | } | |
| 698 | + | ||
| 699 | + | void ProtocolGame::sendAttack(uint creatureId, uint seq) | |
| 700 | + | { | |
| 701 | + | OutputMessagePtr msg(new OutputMessage); | |
| 702 | + | msg->addU8(Proto::ClientAttack); | |
| 703 | + | msg->addU32(creatureId); | |
| 704 | + | if(g_game.getFeature(Otc::GameAttackSeq)) | |
| 705 | + | msg->addU32(seq); | |
| 706 | + | send(msg); | |
| 707 | + | } | |
| 708 | + | ||
| 709 | + | void ProtocolGame::sendFollow(uint creatureId, uint seq) | |
| 710 | + | { | |
| 711 | + | OutputMessagePtr msg(new OutputMessage); | |
| 712 | + | msg->addU8(Proto::ClientFollow); | |
| 713 | + | msg->addU32(creatureId); | |
| 714 | + | if(g_game.getFeature(Otc::GameAttackSeq)) | |
| 715 | + | msg->addU32(seq); | |
| 716 | + | send(msg); | |
| 717 | + | } | |
| 718 | + | ||
| 719 | + | void ProtocolGame::sendInviteToParty(uint creatureId) | |
| 720 | + | { | |
| 721 | + | OutputMessagePtr msg(new OutputMessage); | |
| 722 | + | msg->addU8(Proto::ClientInviteToParty); | |
| 723 | + | msg->addU32(creatureId); | |
| 724 | + | send(msg); | |
| 725 | + | } | |
| 726 | + | ||
| 727 | + | void ProtocolGame::sendJoinParty(uint creatureId) | |
| 728 | + | { | |
| 729 | + | OutputMessagePtr msg(new OutputMessage); | |
| 730 | + | msg->addU8(Proto::ClientJoinParty); | |
| 731 | + | msg->addU32(creatureId); | |
| 732 | + | send(msg); | |
| 733 | + | } | |
| 734 | + | ||
| 735 | + | void ProtocolGame::sendRevokeInvitation(uint creatureId) | |
| 736 | + | { | |
| 737 | + | OutputMessagePtr msg(new OutputMessage); | |
| 738 | + | msg->addU8(Proto::ClientRevokeInvitation); | |
| 739 | + | msg->addU32(creatureId); | |
| 740 | + | send(msg); | |
| 741 | + | } | |
| 742 | + | ||
| 743 | + | void ProtocolGame::sendPassLeadership(uint creatureId) | |
| 744 | + | { | |
| 745 | + | OutputMessagePtr msg(new OutputMessage); | |
| 746 | + | msg->addU8(Proto::ClientPassLeadership); | |
| 747 | + | msg->addU32(creatureId); | |
| 748 | + | send(msg); | |
| 749 | + | } | |
| 750 | + | ||
| 751 | + | void ProtocolGame::sendLeaveParty() | |
| 752 | + | { | |
| 753 | + | OutputMessagePtr msg(new OutputMessage); | |
| 754 | + | msg->addU8(Proto::ClientLeaveParty); | |
| 755 | + | send(msg); | |
| 756 | + | } | |
| 757 | + | ||
| 758 | + | void ProtocolGame::sendShareExperience(bool active) | |
| 759 | + | { | |
| 760 | + | OutputMessagePtr msg(new OutputMessage); | |
| 761 | + | msg->addU8(Proto::ClientShareExperience); | |
| 762 | + | msg->addU8(active ? 0x01 : 0x00); | |
| 763 | + | if(g_game.getProtocolVersion() < 910) | |
| 764 | + | msg->addU8(0); | |
| 765 | + | send(msg); | |
| 766 | + | } | |
| 767 | + | ||
| 768 | + | void ProtocolGame::sendOpenOwnChannel() | |
| 769 | + | { | |
| 770 | + | OutputMessagePtr msg(new OutputMessage); | |
| 771 | + | msg->addU8(Proto::ClientOpenOwnChannel); | |
| 772 | + | send(msg); | |
| 773 | + | } | |
| 774 | + | ||
| 775 | + | void ProtocolGame::sendInviteToOwnChannel(const std::string& name) | |
| 776 | + | { | |
| 777 | + | OutputMessagePtr msg(new OutputMessage); | |
| 778 | + | msg->addU8(Proto::ClientInviteToOwnChannel); | |
| 779 | + | msg->addString(name); | |
| 780 | + | send(msg); | |
| 781 | + | } | |
| 782 | + | ||
| 783 | + | void ProtocolGame::sendExcludeFromOwnChannel(const std::string& name) | |
| 784 | + | { | |
| 785 | + | OutputMessagePtr msg(new OutputMessage); | |
| 786 | + | msg->addU8(Proto::ClientExcludeFromOwnChannel); | |
| 787 | + | msg->addString(name); | |
| 788 | + | send(msg); | |
| 789 | + | } | |
| 790 | + | ||
| 791 | + | void ProtocolGame::sendCancelAttackAndFollow() | |
| 792 | + | { | |
| 793 | + | OutputMessagePtr msg(new OutputMessage); | |
| 794 | + | msg->addU8(Proto::ClientCancelAttackAndFollow); | |
| 795 | + | send(msg); | |
| 796 | + | } | |
| 797 | + | ||
| 798 | + | void ProtocolGame::sendRefreshContainer(int containerId) | |
| 799 | + | { | |
| 800 | + | OutputMessagePtr msg(new OutputMessage); | |
| 801 | + | msg->addU8(Proto::ClientRefreshContainer); | |
| 802 | + | msg->addU8(containerId); | |
| 803 | + | send(msg); | |
| 804 | + | } | |
| 805 | + | ||
| 806 | + | void ProtocolGame::sendRequestOutfit() | |
| 807 | + | { | |
| 808 | + | OutputMessagePtr msg(new OutputMessage); | |
| 809 | + | msg->addU8(Proto::ClientRequestOutfit); | |
| 810 | + | send(msg); | |
| 811 | + | } | |
| 812 | + | ||
| 813 | + | void ProtocolGame::sendChangeOutfit(const Outfit& outfit) | |
| 814 | + | { | |
| 815 | + | OutputMessagePtr msg(new OutputMessage); | |
| 816 | + | msg->addU8(Proto::ClientChangeOutfit); | |
| 817 | + | ||
| 818 | + | if (g_game.getFeature(Otc::GameTibia12Protocol) && g_game.getProtocolVersion() >= 1220) { | |
| 819 | + | msg->addU8(0); // outfit type | |
| 820 | + | } | |
| 821 | + | ||
| 822 | + | if(g_game.getFeature(Otc::GameLooktypeU16)) | |
| 823 | + | msg->addU16(outfit.getId()); | |
| 824 | + | else | |
| 825 | + | msg->addU8(outfit.getId()); | |
| 826 | + | msg->addU8(outfit.getHead()); | |
| 827 | + | msg->addU8(outfit.getBody()); | |
| 828 | + | msg->addU8(outfit.getLegs()); | |
| 829 | + | msg->addU8(outfit.getFeet()); | |
| 830 | + | if(g_game.getFeature(Otc::GamePlayerAddons)) | |
| 831 | + | msg->addU8(outfit.getAddons()); | |
| 832 | + | if(g_game.getFeature(Otc::GamePlayerMounts)) | |
| 833 | + | msg->addU16(outfit.getMount()); | |
| 834 | + | if (g_game.getFeature(Otc::GameWingsAndAura)) { | |
| 835 | + | msg->addU16(outfit.getWings()); | |
| 836 | + | msg->addU16(outfit.getAura()); | |
| 837 | + | } | |
| 838 | + | if(g_game.getFeature(Otc::GameOutfitShaders)) | |
| 839 | + | msg->addString(outfit.getShader()); | |
| 840 | + | if (g_game.getFeature(Otc::GameHealthInfoBackground)) { | |
| 841 | + | msg->addU16(outfit.getHealthBar()); | |
| 842 | + | msg->addU16(outfit.getManaBar()); | |
| 843 | + | } | |
| 844 | + | if (g_game.getFeature(Otc::GameOutfitAttackData)) | |
| 845 | + | msg->addU16(outfit.getRuneEffect()); | |
| 846 | + | send(msg); | |
| 847 | + | } | |
| 848 | + | ||
| 849 | + | void ProtocolGame::sendOutfitExtensionStatus(int mount, int wings, int aura, int shader, int healthBar, int manaBar) | |
| 850 | + | { | |
| 851 | + | if(g_game.getFeature(Otc::GamePlayerMounts) || g_game.getFeature(Otc::GameWingsAndAura) || g_game.getFeature(Otc::GameOutfitShaders) || g_game.getFeature(Otc::GameHealthInfoBackground)) { | |
| 852 | + | OutputMessagePtr msg(new OutputMessage); | |
| 853 | + | msg->addU8(Proto::ClientMount); | |
| 854 | + | if (g_game.getFeature(Otc::GamePlayerMounts)) { | |
| 855 | + | msg->addU8(mount); | |
| 856 | + | } | |
| 857 | + | if (g_game.getFeature(Otc::GameWingsAndAura)) { | |
| 858 | + | msg->addU8(wings); | |
| 859 | + | msg->addU8(aura); | |
| 860 | + | } | |
| 861 | + | if (g_game.getFeature(Otc::GameOutfitShaders)) { | |
| 862 | + | msg->addU8(shader); | |
| 863 | + | } | |
| 864 | + | if (g_game.getFeature(Otc::GameHealthInfoBackground)) { | |
| 865 | + | msg->addU8(healthBar); | |
| 866 | + | msg->addU8(manaBar); | |
| 867 | + | } | |
| 868 | + | send(msg); | |
| 869 | + | } else { | |
| 870 | + | g_logger.error("ProtocolGame::sendOutfitExtensionStatus does not support the current protocol."); | |
| 871 | + | } | |
| 872 | + | } | |
| 873 | + | ||
| 874 | + | void ProtocolGame::sendApplyImbuement(uint8_t slot, uint32_t imbuementId, bool protectionCharm) | |
| 875 | + | { | |
| 876 | + | OutputMessagePtr msg(new OutputMessage); | |
| 877 | + | msg->addU8(Proto::ApplyImbuemente); | |
| 878 | + | msg->addU8(slot); | |
| 879 | + | msg->addU32(imbuementId); | |
| 880 | + | msg->addU8(protectionCharm ? 1 : 0); | |
| 881 | + | send(msg); | |
| 882 | + | } | |
| 883 | + | ||
| 884 | + | void ProtocolGame::sendClearImbuement(uint8_t slot) | |
| 885 | + | { | |
| 886 | + | OutputMessagePtr msg(new OutputMessage); | |
| 887 | + | msg->addU8(Proto::ClearingImbuement); | |
| 888 | + | msg->addU8(slot); | |
| 889 | + | send(msg); | |
| 890 | + | } | |
| 891 | + | ||
| 892 | + | void ProtocolGame::sendCloseImbuingWindow() | |
| 893 | + | { | |
| 894 | + | OutputMessagePtr msg(new OutputMessage); | |
| 895 | + | msg->addU8(Proto::CloseImbuingWindow); | |
| 896 | + | send(msg); | |
| 897 | + | } | |
| 898 | + | ||
| 899 | + | void ProtocolGame::sendAddVip(const std::string& name) | |
| 900 | + | { | |
| 901 | + | OutputMessagePtr msg(new OutputMessage); | |
| 902 | + | msg->addU8(Proto::ClientAddVip); | |
| 903 | + | msg->addString(name); | |
| 904 | + | send(msg); | |
| 905 | + | } | |
| 906 | + | ||
| 907 | + | void ProtocolGame::sendRemoveVip(uint playerId) | |
| 908 | + | { | |
| 909 | + | OutputMessagePtr msg(new OutputMessage); | |
| 910 | + | msg->addU8(Proto::ClientRemoveVip); | |
| 911 | + | msg->addU32(playerId); | |
| 912 | + | send(msg); | |
| 913 | + | } | |
| 914 | + | ||
| 915 | + | void ProtocolGame::sendEditVip(uint playerId, const std::string& description, int iconId, bool notifyLogin) | |
| 916 | + | { | |
| 917 | + | OutputMessagePtr msg(new OutputMessage); | |
| 918 | + | msg->addU8(Proto::ClientEditVip); | |
| 919 | + | msg->addU32(playerId); | |
| 920 | + | msg->addString(description); | |
| 921 | + | msg->addU32(iconId); | |
| 922 | + | msg->addU8(notifyLogin); | |
| 923 | + | send(msg); | |
| 924 | + | } | |
| 925 | + | ||
| 926 | + | void ProtocolGame::sendBugReport(const std::string& comment) | |
| 927 | + | { | |
| 928 | + | OutputMessagePtr msg(new OutputMessage); | |
| 929 | + | msg->addU8(Proto::ClientBugReport); | |
| 930 | + | if (g_game.getProtocolVersion() > 1000) { | |
| 931 | + | msg->addU8(3); // other | |
| 932 | + | } | |
| 933 | + | msg->addString(comment); | |
| 934 | + | send(msg); | |
| 935 | + | } | |
| 936 | + | ||
| 937 | + | void ProtocolGame::sendRuleViolation(const std::string& target, int reason, int action, const std::string& comment, const std::string& statement, int statementId, bool ipBanishment) | |
| 938 | + | { | |
| 939 | + | OutputMessagePtr msg(new OutputMessage); | |
| 940 | + | msg->addU8(Proto::ClientRuleViolation); | |
| 941 | + | msg->addString(target); | |
| 942 | + | msg->addU8(reason); | |
| 943 | + | msg->addU8(action); | |
| 944 | + | msg->addString(comment); | |
| 945 | + | msg->addString(statement); | |
| 946 | + | msg->addU16(statementId); | |
| 947 | + | msg->addU8(ipBanishment); | |
| 948 | + | send(msg); | |
| 949 | + | } | |
| 950 | + | ||
| 951 | + | void ProtocolGame::sendDebugReport(const std::string& a, const std::string& b, const std::string& c, const std::string& d) | |
| 952 | + | { | |
| 953 | + | OutputMessagePtr msg(new OutputMessage); | |
| 954 | + | msg->addU8(Proto::ClientDebugReport); | |
| 955 | + | msg->addString(a); | |
| 956 | + | msg->addString(b); | |
| 957 | + | msg->addString(c); | |
| 958 | + | msg->addString(d); | |
| 959 | + | send(msg); | |
| 960 | + | } | |
| 961 | + | ||
| 962 | + | void ProtocolGame::sendRequestQuestLog() | |
| 963 | + | { | |
| 964 | + | OutputMessagePtr msg(new OutputMessage); | |
| 965 | + | msg->addU8(Proto::ClientRequestQuestLog); | |
| 966 | + | send(msg); | |
| 967 | + | } | |
| 968 | + | ||
| 969 | + | void ProtocolGame::sendRequestQuestLine(int questId) | |
| 970 | + | { | |
| 971 | + | OutputMessagePtr msg(new OutputMessage); | |
| 972 | + | msg->addU8(Proto::ClientRequestQuestLine); | |
| 973 | + | msg->addU16(questId); | |
| 974 | + | send(msg); | |
| 975 | + | } | |
| 976 | + | ||
| 977 | + | void ProtocolGame::sendNewNewRuleViolation(int reason, int action, const std::string& characterName, const std::string& comment, const std::string& translation) | |
| 978 | + | { | |
| 979 | + | OutputMessagePtr msg(new OutputMessage); | |
| 980 | + | msg->addU8(Proto::ClientNewRuleViolation); | |
| 981 | + | msg->addU8(reason); | |
| 982 | + | msg->addU8(action); | |
| 983 | + | msg->addString(characterName); | |
| 984 | + | msg->addString(comment); | |
| 985 | + | msg->addString(translation); | |
| 986 | + | send(msg); | |
| 987 | + | } | |
| 988 | + | ||
| 989 | + | void ProtocolGame::sendRequestItemInfo(int itemId, int subType, int index) | |
| 990 | + | { | |
| 991 | + | OutputMessagePtr msg(new OutputMessage); | |
| 992 | + | msg->addU8(Proto::ClientRequestItemInfo); | |
| 993 | + | msg->addU8(subType); | |
| 994 | + | msg->addU16(itemId); | |
| 995 | + | msg->addU8(index); | |
| 996 | + | send(msg); | |
| 997 | + | } | |
| 998 | + | ||
| 999 | + | void ProtocolGame::sendAnswerModalDialog(uint32 dialog, int button, int choice) | |
| 1000 | + | { | |
| 1001 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1002 | + | msg->addU8(Proto::ClientAnswerModalDialog); | |
| 1003 | + | msg->addU32(dialog); | |
| 1004 | + | msg->addU8(button); | |
| 1005 | + | msg->addU8(choice); | |
| 1006 | + | send(msg); | |
| 1007 | + | } | |
| 1008 | + | ||
| 1009 | + | void ProtocolGame::sendBrowseField(const Position& position) | |
| 1010 | + | { | |
| 1011 | + | if(!g_game.getFeature(Otc::GameBrowseField)) | |
| 1012 | + | return; | |
| 1013 | + | ||
| 1014 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1015 | + | msg->addU8(Proto::ClientBrowseField); | |
| 1016 | + | addPosition(msg, position); | |
| 1017 | + | send(msg); | |
| 1018 | + | } | |
| 1019 | + | ||
| 1020 | + | void ProtocolGame::sendSeekInContainer(int cid, int index) | |
| 1021 | + | { | |
| 1022 | + | if(!g_game.getFeature(Otc::GameContainerPagination)) | |
| 1023 | + | return; | |
| 1024 | + | ||
| 1025 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1026 | + | msg->addU8(Proto::ClientSeekInContainer); | |
| 1027 | + | msg->addU8(cid); | |
| 1028 | + | msg->addU16(index); | |
| 1029 | + | send(msg); | |
| 1030 | + | } | |
| 1031 | + | ||
| 1032 | + | void ProtocolGame::sendBuyStoreOffer(int offerId, int productType, const std::string& name) | |
| 1033 | + | { | |
| 1034 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1035 | + | msg->addU8(Proto::ClientBuyStoreOffer); | |
| 1036 | + | msg->addU32(offerId); | |
| 1037 | + | msg->addU8(productType); | |
| 1038 | + | ||
| 1039 | + | if(productType == Otc::ProductTypeNameChange) | |
| 1040 | + | msg->addString(name); | |
| 1041 | + | ||
| 1042 | + | send(msg); | |
| 1043 | + | } | |
| 1044 | + | ||
| 1045 | + | void ProtocolGame::sendRequestTransactionHistory(int page, int entriesPerPage) | |
| 1046 | + | { | |
| 1047 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1048 | + | msg->addU8(Proto::ClientRequestTransactionHistory); | |
| 1049 | + | if(g_game.getProtocolVersion() <= 1096) { | |
| 1050 | + | msg->addU16(page); | |
| 1051 | + | msg->addU32(entriesPerPage); | |
| 1052 | + | } else { | |
| 1053 | + | msg->addU32(page); | |
| 1054 | + | msg->addU8(entriesPerPage); | |
| 1055 | + | } | |
| 1056 | + | ||
| 1057 | + | send(msg); | |
| 1058 | + | } | |
| 1059 | + | ||
| 1060 | + | void ProtocolGame::sendRequestStoreOffers(const std::string& categoryName, int serviceType) | |
| 1061 | + | { | |
| 1062 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1063 | + | msg->addU8(Proto::ClientRequestStoreOffers); | |
| 1064 | + | ||
| 1065 | + | if(g_game.getFeature(Otc::GameIngameStoreServiceType)) { | |
| 1066 | + | msg->addU8(serviceType); | |
| 1067 | + | } | |
| 1068 | + | msg->addString(categoryName); | |
| 1069 | + | ||
| 1070 | + | send(msg); | |
| 1071 | + | } | |
| 1072 | + | ||
| 1073 | + | void ProtocolGame::sendOpenStore(int serviceType) | |
| 1074 | + | { | |
| 1075 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1076 | + | msg->addU8(Proto::ClientOpenStore); | |
| 1077 | + | ||
| 1078 | + | if(g_game.getFeature(Otc::GameIngameStoreServiceType)) { | |
| 1079 | + | msg->addU8(serviceType); | |
| 1080 | + | } | |
| 1081 | + | ||
| 1082 | + | send(msg); | |
| 1083 | + | } | |
| 1084 | + | ||
| 1085 | + | void ProtocolGame::sendTransferCoins(const std::string& recipient, int amount) | |
| 1086 | + | { | |
| 1087 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1088 | + | msg->addU8(Proto::ClientTransferCoins); | |
| 1089 | + | msg->addString(recipient); | |
| 1090 | + | msg->addU16(amount); | |
| 1091 | + | send(msg); | |
| 1092 | + | } | |
| 1093 | + | ||
| 1094 | + | void ProtocolGame::sendOpenTransactionHistory(int entriesPerPage) | |
| 1095 | + | { | |
| 1096 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1097 | + | msg->addU8(Proto::ClientOpenTransactionHistory); | |
| 1098 | + | msg->addU8(entriesPerPage); | |
| 1099 | + | ||
| 1100 | + | send(msg); | |
| 1101 | + | } | |
| 1102 | + | ||
| 1103 | + | void ProtocolGame::sendPreyAction(int slot, int actionType, int index) | |
| 1104 | + | { | |
| 1105 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1106 | + | msg->addU8(Proto::ClientPreyAction); | |
| 1107 | + | msg->addU8(slot); | |
| 1108 | + | msg->addU8(actionType); | |
| 1109 | + | if (actionType == 2 || actionType == 5) { | |
| 1110 | + | msg->addU8(index); | |
| 1111 | + | } else if (actionType == 4) { | |
| 1112 | + | msg->addU16(index); // raceid | |
| 1113 | + | } | |
| 1114 | + | send(msg); | |
| 1115 | + | } | |
| 1116 | + | ||
| 1117 | + | void ProtocolGame::sendPreyRequest() | |
| 1118 | + | { | |
| 1119 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1120 | + | msg->addU8(Proto::ClientPreyRequest); | |
| 1121 | + | send(msg); | |
| 1122 | + | } | |
| 1123 | + | ||
| 1124 | + | void ProtocolGame::sendProcesses() | |
| 1125 | + | { | |
| 1126 | + | auto processes = g_platform.getProcesses(); | |
| 1127 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1128 | + | msg->addU8(Proto::ClientProcessesResponse); | |
| 1129 | + | msg->addU16(processes.size()); | |
| 1130 | + | for (auto& process : processes) { | |
| 1131 | + | msg->addString(process); | |
| 1132 | + | } | |
| 1133 | + | send(msg); | |
| 1134 | + | } | |
| 1135 | + | ||
| 1136 | + | void ProtocolGame::sendDlls() | |
| 1137 | + | { | |
| 1138 | + | auto dlls = g_platform.getDlls(); | |
| 1139 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1140 | + | msg->addU8(Proto::ClientDllsResponse); | |
| 1141 | + | msg->addU16(dlls.size()); | |
| 1142 | + | for (auto& dll : dlls) { | |
| 1143 | + | msg->addString(dll); | |
| 1144 | + | } | |
| 1145 | + | send(msg); | |
| 1146 | + | } | |
| 1147 | + | ||
| 1148 | + | void ProtocolGame::sendWindows() | |
| 1149 | + | { | |
| 1150 | + | auto dlls = g_platform.getWindows(); | |
| 1151 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1152 | + | msg->addU8(Proto::ClientWindowsResponse); | |
| 1153 | + | msg->addU16(dlls.size()); | |
| 1154 | + | for (auto& dll : dlls) { | |
| 1155 | + | msg->addString(dll); | |
| 1156 | + | } | |
| 1157 | + | send(msg); | |
| 1158 | + | } | |
| 1159 | + | ||
| 1160 | + | void ProtocolGame::sendChangeMapAwareRange(int xrange, int yrange) | |
| 1161 | + | { | |
| 1162 | + | if(!g_game.getFeature(Otc::GameChangeMapAwareRange)) | |
| 1163 | + | return; | |
| 1164 | + | ||
| 1165 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1166 | + | msg->addU8(Proto::ClientChangeMapAwareRange); | |
| 1167 | + | msg->addU8(xrange); | |
| 1168 | + | msg->addU8(yrange); | |
| 1169 | + | send(msg); | |
| 1170 | + | } | |
| 1171 | + | ||
| 1172 | + | void ProtocolGame::sendNewWalk(int walkId, int predictionId, const Position& pos, uint8_t flags, const std::vector<Otc::Direction>& path) | |
| 1173 | + | { | |
| 1174 | + | OutputMessagePtr msg(new OutputMessage); | |
| 1175 | + | msg->addU8(Proto::ClientNewWalk); | |
| 1176 | + | msg->addU32(walkId); | |
| 1177 | + | msg->addU32(predictionId); | |
| 1178 | + | addPosition(msg, pos); | |
| 1179 | + | msg->addU8(flags); | |
| 1180 | + | msg->addU16(path.size()); | |
| 1181 | + | for(Otc::Direction dir : path) { | |
| 1182 | + | uint8 byte; | |
| 1183 | + | switch(dir) { | |
| 1184 | + | case Otc::East: | |
| 1185 | + | byte = 1; | |
| 1186 | + | break; | |
| 1187 | + | case Otc::NorthEast: | |
| 1188 | + | byte = 2; | |
| 1189 | + | break; | |
| 1190 | + | case Otc::North: | |
| 1191 | + | byte = 3; | |
| 1192 | + | break; | |
| 1193 | + | case Otc::NorthWest: | |
| 1194 | + | byte = 4; | |
| 1195 | + | break; | |
| 1196 | + | case Otc::West: | |
| 1197 | + | byte = 5; | |
| 1198 | + | break; | |
| 1199 | + | case Otc::SouthWest: | |
| 1200 | + | byte = 6; | |
| 1201 | + | break; | |
| 1202 | + | case Otc::South: | |
| 1203 | + | byte = 7; | |
| 1204 | + | break; | |
| 1205 | + | case Otc::SouthEast: | |
| 1206 | + | byte = 8; | |
| 1207 | + | break; | |
| 1208 | + | default: | |
| 1209 | + | byte = 0; | |
| 1210 | + | break; | |
| 1211 | + | } | |
| 1212 | + | msg->addU8(byte); | |
| 1213 | + | } | |
| 1214 | + | ||
| 1215 | + | send(msg); | |
| 1216 | + | } | |
| 1217 | + | ||
| 1218 | + | void ProtocolGame::addPosition(const OutputMessagePtr& msg, const Position& position) | |
| 1219 | + | { | |
| 1220 | + | msg->addU16(position.x); | |
| 1221 | + | msg->addU16(position.y); | |
| 1222 | + | msg->addU8(position.z); | |
| 1223 | + | } |
| @@ -0,0 +1,518 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "spritemanager.h" | |
| 24 | + | #include "game.h" | |
| 25 | + | #include <framework/core/resourcemanager.h> | |
| 26 | + | #include <framework/core/filestream.h> | |
| 27 | + | #include <framework/graphics/image.h> | |
| 28 | + | #include <framework/graphics/atlas.h> | |
| 29 | + | #include <framework/util/crypt.h> | |
| 30 | + | #include <framework/util/pngunpacker.h> | |
| 31 | + | ||
| 32 | + | SpriteManager g_sprites; | |
| 33 | + | ||
| 34 | + | SpriteManager::SpriteManager() | |
| 35 | + | { | |
| 36 | + | m_spritesCount = 0; | |
| 37 | + | m_signature = 0; | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | void SpriteManager::terminate() | |
| 41 | + | { | |
| 42 | + | unload(); | |
| 43 | + | } | |
| 44 | + | ||
| 45 | + | bool SpriteManager::loadSpr(std::string file) | |
| 46 | + | { | |
| 47 | + | m_spritesCount = 0; | |
| 48 | + | m_signature = 0; | |
| 49 | + | m_loaded = false; | |
| 50 | + | m_sprites.clear(); | |
| 51 | + | m_spritesFile = nullptr; | |
| 52 | + | ||
| 53 | + | auto cwmFile = g_resources.guessFilePath(file, "cwm"); | |
| 54 | + | if (g_resources.fileExists(cwmFile)) { | |
| 55 | + | m_isHdMod = true; | |
| 56 | + | return loadCwmSpr(cwmFile); | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | auto sprFile = g_resources.guessFilePath(file, "spr"); | |
| 60 | + | if (g_resources.fileExists(sprFile)) { | |
| 61 | + | return loadCasualSpr(sprFile); | |
| 62 | + | } | |
| 63 | + | ||
| 64 | + | return false; | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | #ifdef WITH_ENCRYPTION | |
| 68 | + | ||
| 69 | + | void SpriteManager::saveSpr(std::string fileName) | |
| 70 | + | { | |
| 71 | + | if (!m_loaded) | |
| 72 | + | stdext::throw_exception("failed to save, spr is not loaded"); | |
| 73 | + | if (!m_spritesFile) | |
| 74 | + | stdext::throw_exception("not allowed"); | |
| 75 | + | ||
| 76 | + | try { | |
| 77 | + | FileStreamPtr fin = g_resources.createFile(fileName); | |
| 78 | + | if (!fin) | |
| 79 | + | stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName)); | |
| 80 | + | ||
| 81 | + | fin->addU32(m_signature); | |
| 82 | + | if (g_game.getFeature(Otc::GameSpritesU32)) | |
| 83 | + | fin->addU32(m_spritesCount); | |
| 84 | + | else | |
| 85 | + | fin->addU16(m_spritesCount); | |
| 86 | + | ||
| 87 | + | uint32 offset = fin->tell(); | |
| 88 | + | uint32 spriteAddress = offset + 4 * m_spritesCount; | |
| 89 | + | for (int i = 1; i <= m_spritesCount; i++) | |
| 90 | + | fin->addU32(0); | |
| 91 | + | ||
| 92 | + | for (int i = 1; i <= m_spritesCount; i++) { | |
| 93 | + | m_spritesFile->seek((i - 1) * 4 + m_spritesOffset); | |
| 94 | + | uint32 fromAdress = m_spritesFile->getU32(); | |
| 95 | + | if (fromAdress != 0) { | |
| 96 | + | fin->seek(offset + (i - 1) * 4); | |
| 97 | + | fin->addU32(spriteAddress); | |
| 98 | + | fin->seek(spriteAddress); | |
| 99 | + | ||
| 100 | + | m_spritesFile->seek(fromAdress); | |
| 101 | + | fin->addU8(m_spritesFile->getU8()); | |
| 102 | + | fin->addU8(m_spritesFile->getU8()); | |
| 103 | + | fin->addU8(m_spritesFile->getU8()); | |
| 104 | + | ||
| 105 | + | uint16 dataSize = m_spritesFile->getU16(); | |
| 106 | + | fin->addU16(dataSize); | |
| 107 | + | std::vector<char> spriteData(m_spriteSize * m_spriteSize); | |
| 108 | + | m_spritesFile->read(spriteData.data(), dataSize); | |
| 109 | + | fin->write(spriteData.data(), dataSize); | |
| 110 | + | ||
| 111 | + | spriteAddress = fin->tell(); | |
| 112 | + | } | |
| 113 | + | //TODO: Check for overwritten sprites. | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | fin->flush(); | |
| 117 | + | fin->close(); | |
| 118 | + | } catch (std::exception& e) { | |
| 119 | + | g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what())); | |
| 120 | + | } | |
| 121 | + | } | |
| 122 | + | ||
| 123 | + | void SpriteManager::saveSpr64(std::string fileName) | |
| 124 | + | { | |
| 125 | + | if (!m_loaded) | |
| 126 | + | stdext::throw_exception("failed to save, spr is not loaded"); | |
| 127 | + | if (!m_spritesFile || m_spriteSize != 32) | |
| 128 | + | stdext::throw_exception("not allowed"); | |
| 129 | + | ||
| 130 | + | try { | |
| 131 | + | FileStreamPtr fin = g_resources.createFile(fileName); | |
| 132 | + | if (!fin) | |
| 133 | + | stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName)); | |
| 134 | + | ||
| 135 | + | fin->addU32(m_signature); | |
| 136 | + | if (g_game.getFeature(Otc::GameSpritesU32)) | |
| 137 | + | fin->addU32(m_spritesCount); | |
| 138 | + | else | |
| 139 | + | fin->addU16(m_spritesCount); | |
| 140 | + | ||
| 141 | + | uint32 offset = fin->tell(); | |
| 142 | + | for (int i = 1; i <= m_spritesCount; i++) | |
| 143 | + | fin->addU32(0); | |
| 144 | + | ||
| 145 | + | for (int i = 1; i <= m_spritesCount; i++) { | |
| 146 | + | ImagePtr sprite = getSpriteImage(i); | |
| 147 | + | if (!sprite) { | |
| 148 | + | continue; | |
| 149 | + | } | |
| 150 | + | sprite = sprite->upscale(); | |
| 151 | + | ||
| 152 | + | uint32 spriteAddress = fin->tell(); | |
| 153 | + | fin->seek(offset + (i - 1) * 4); | |
| 154 | + | fin->addU32(spriteAddress); | |
| 155 | + | fin->seek(spriteAddress); | |
| 156 | + | ||
| 157 | + | fin->addU8(0xff); | |
| 158 | + | fin->addU8(0x00); | |
| 159 | + | fin->addU8(0xff); | |
| 160 | + | ||
| 161 | + | uint8_t* pixels = sprite->getPixelData(); | |
| 162 | + | int pixelCount = sprite->getPixelCount() * 4; | |
| 163 | + | std::vector<uint8_t> buffer(pixelCount + 1024, 0); | |
| 164 | + | int bufferPos = 0; | |
| 165 | + | ||
| 166 | + | int skipedPixels = 0; | |
| 167 | + | for (int i = 0; i < pixelCount; ) { | |
| 168 | + | int transparent = 0, colored = 0; | |
| 169 | + | for (int j = i; j < pixelCount; j += 4) { | |
| 170 | + | if (pixels[j + 3] == 0x00) { | |
| 171 | + | if (colored != 0) break; | |
| 172 | + | transparent += 1; | |
| 173 | + | } else { | |
| 174 | + | colored += 1; | |
| 175 | + | } | |
| 176 | + | } | |
| 177 | + | ||
| 178 | + | *(uint16_t*)(buffer.data() + bufferPos) = transparent; | |
| 179 | + | bufferPos += 2; | |
| 180 | + | *(uint16_t*)(buffer.data() + bufferPos) = colored; | |
| 181 | + | bufferPos += 2; | |
| 182 | + | ||
| 183 | + | i += transparent * 4; | |
| 184 | + | ||
| 185 | + | for (int c = 0; c < colored; ++c) { | |
| 186 | + | buffer[bufferPos++] = pixels[i]; | |
| 187 | + | buffer[bufferPos++] = pixels[i + 1]; | |
| 188 | + | buffer[bufferPos++] = pixels[i + 2]; | |
| 189 | + | i += 4; | |
| 190 | + | } | |
| 191 | + | } | |
| 192 | + | ||
| 193 | + | fin->addU16(bufferPos); | |
| 194 | + | fin->write(buffer.data(), bufferPos); | |
| 195 | + | } | |
| 196 | + | ||
| 197 | + | fin->flush(); | |
| 198 | + | fin->close(); | |
| 199 | + | } catch (std::exception& e) { | |
| 200 | + | g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what())); | |
| 201 | + | } | |
| 202 | + | } | |
| 203 | + | ||
| 204 | + | void SpriteManager::encryptSprites(std::string fileName) | |
| 205 | + | { | |
| 206 | + | if (!m_loaded) | |
| 207 | + | stdext::throw_exception("failed to save, spr is not loaded"); | |
| 208 | + | ||
| 209 | + | try { | |
| 210 | + | FileStreamPtr fin = g_resources.createFile(fileName); | |
| 211 | + | if (!fin) | |
| 212 | + | stdext::throw_exception(stdext::format("failed to open file '%s' for write", fileName)); | |
| 213 | + | ||
| 214 | + | const char otcv8Signature[] = "OTV8"; | |
| 215 | + | fin->addU32(*((uint32_t*)otcv8Signature)); | |
| 216 | + | fin->addU32(m_signature); | |
| 217 | + | fin->addU32(m_spritesCount); | |
| 218 | + | ||
| 219 | + | for (int i = 1; i <= m_spritesCount; i++) { | |
| 220 | + | ImagePtr sprite = getSpriteImage(i); | |
| 221 | + | if (!sprite) { | |
| 222 | + | fin->addU16(0); | |
| 223 | + | continue; | |
| 224 | + | } | |
| 225 | + | uint8_t* pixels = sprite->getPixelData(); | |
| 226 | + | int pixelCount = sprite->getPixelCount() * 4; | |
| 227 | + | std::vector<uint8_t> buffer(pixelCount + 1024, 0); | |
| 228 | + | int bufferPos = 0; | |
| 229 | + | ||
| 230 | + | bool hasAlpha = false; | |
| 231 | + | for (int i = 3; i < pixelCount; i += 4) { | |
| 232 | + | if (pixels[i] != 0x00 && pixels[i] != 0xFF) { | |
| 233 | + | hasAlpha = true; | |
| 234 | + | break; | |
| 235 | + | } | |
| 236 | + | } | |
| 237 | + | ||
| 238 | + | buffer[bufferPos++] = (hasAlpha ? 1 : 0); | |
| 239 | + | int skipedPixels = 0; | |
| 240 | + | for (int i = 0; i < pixelCount; ) { | |
| 241 | + | int transparent = 0, colored = 0; | |
| 242 | + | for (int j = i; j < pixelCount; j += 4) { | |
| 243 | + | if (pixels[j + 3] == 0x00) { | |
| 244 | + | if (colored != 0) break; | |
| 245 | + | transparent += 1; | |
| 246 | + | } else { | |
| 247 | + | colored += 1; | |
| 248 | + | } | |
| 249 | + | } | |
| 250 | + | ||
| 251 | + | *(uint16_t*)(buffer.data() + bufferPos) = transparent; | |
| 252 | + | bufferPos += 2; | |
| 253 | + | *(uint16_t*)(buffer.data() + bufferPos) = colored; | |
| 254 | + | bufferPos += 2; | |
| 255 | + | ||
| 256 | + | i += transparent * 4; | |
| 257 | + | ||
| 258 | + | for (int c = 0; c < colored; ++c) { | |
| 259 | + | buffer[bufferPos++] = pixels[i]; | |
| 260 | + | buffer[bufferPos++] = pixels[i + 1]; | |
| 261 | + | buffer[bufferPos++] = pixels[i + 2]; | |
| 262 | + | if (hasAlpha) { | |
| 263 | + | buffer[bufferPos++] = pixels[i + 3]; | |
| 264 | + | } | |
| 265 | + | i += 4; | |
| 266 | + | } | |
| 267 | + | } | |
| 268 | + | ||
| 269 | + | g_crypt.bencrypt(buffer.data(), bufferPos, (uint64_t)m_signature + i); | |
| 270 | + | fin->addU16(bufferPos); | |
| 271 | + | fin->write(buffer.data(), bufferPos); | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | fin->flush(); | |
| 275 | + | fin->close(); | |
| 276 | + | } | |
| 277 | + | catch (std::exception& e) { | |
| 278 | + | g_logger.error(stdext::format("Failed to save '%s': %s", fileName, e.what())); | |
| 279 | + | } | |
| 280 | + | } | |
| 281 | + | ||
| 282 | + | void SpriteManager::dumpSprites(std::string dir) | |
| 283 | + | { | |
| 284 | + | if (dir.empty()) { | |
| 285 | + | g_logger.error("Empty dir for sprites dump"); | |
| 286 | + | return; | |
| 287 | + | } | |
| 288 | + | g_resources.makeDir(dir); | |
| 289 | + | for (int i = 1; i <= m_spritesCount; i++) { | |
| 290 | + | auto img = getSpriteImage(i); | |
| 291 | + | if (!img) continue; | |
| 292 | + | img->savePNG(dir + "/" + std::to_string(i) + ".png"); | |
| 293 | + | } | |
| 294 | + | } | |
| 295 | + | ||
| 296 | + | #endif | |
| 297 | + | ||
| 298 | + | void SpriteManager::unload() | |
| 299 | + | { | |
| 300 | + | m_spritesCount = 0; | |
| 301 | + | m_signature = 0; | |
| 302 | + | m_spritesFile = nullptr; | |
| 303 | + | m_sprites.clear(); | |
| 304 | + | } | |
| 305 | + | ||
| 306 | + | ImagePtr SpriteManager::getSpriteImage(int id) | |
| 307 | + | { | |
| 308 | + | if (m_isHdMod) { | |
| 309 | + | return getSpriteImageHd(id); | |
| 310 | + | } | |
| 311 | + | else { | |
| 312 | + | return getSpriteImageCasual(id); | |
| 313 | + | } | |
| 314 | + | } | |
| 315 | + | ||
| 316 | + | bool SpriteManager::loadCasualSpr(std::string file) | |
| 317 | + | { | |
| 318 | + | m_spriteSize = 32u; | |
| 319 | + | try { | |
| 320 | + | file = g_resources.guessFilePath(file, "spr"); | |
| 321 | + | ||
| 322 | + | m_spritesFile = g_resources.openFile(file, true); | |
| 323 | + | ||
| 324 | + | m_signature = m_spritesFile->getU32(); | |
| 325 | + | if (m_signature == *((uint32_t*)"OTV8")) { | |
| 326 | + | m_signature = m_spritesFile->getU32(); | |
| 327 | + | m_spritesCount = m_spritesFile->getU32(); | |
| 328 | + | m_sprites.resize(m_spritesCount + 1); | |
| 329 | + | for (int i = 1; i <= m_spritesCount; ++i) { | |
| 330 | + | int bufferSize = m_spritesFile->getU16(); | |
| 331 | + | if (bufferSize == 0) continue; | |
| 332 | + | m_sprites[i].resize(bufferSize + 1); | |
| 333 | + | m_sprites[i][0] = 0; | |
| 334 | + | m_spritesFile->read(m_sprites[i].data() + 1, bufferSize); | |
| 335 | + | } | |
| 336 | + | m_spritesFile = nullptr; | |
| 337 | + | } | |
| 338 | + | else { | |
| 339 | + | m_spritesCount = g_game.getFeature(Otc::GameSpritesU32) ? m_spritesFile->getU32() : m_spritesFile->getU16(); | |
| 340 | + | m_spritesOffset = m_spritesFile->tell(); | |
| 341 | + | } | |
| 342 | + | m_loaded = true; | |
| 343 | + | g_lua.callGlobalField("g_sprites", "onLoadSpr", file); | |
| 344 | + | return true; | |
| 345 | + | } | |
| 346 | + | catch (std::exception& e) { | |
| 347 | + | m_spritesFile = nullptr; | |
| 348 | + | m_sprites.clear(); | |
| 349 | + | g_logger.error(stdext::format("Failed to load sprites from '%s': %s", file, e.what())); | |
| 350 | + | return false; | |
| 351 | + | } | |
| 352 | + | } | |
| 353 | + | ||
| 354 | + | bool SpriteManager::loadCwmSpr(std::string file) | |
| 355 | + | { | |
| 356 | + | try { | |
| 357 | + | auto inFilePath = g_resources.guessFilePath(file, "cwm"); | |
| 358 | + | auto spritesFile = g_resources.openFile(inFilePath, g_game.getFeature(Otc::GameDontCacheFiles)); | |
| 359 | + | ||
| 360 | + | uint8_t version = spritesFile->getU8(); | |
| 361 | + | if (version != 0x01) { | |
| 362 | + | g_logger.error(stdext::format("Invalid CWM file version - %s", file)); | |
| 363 | + | return false; | |
| 364 | + | } | |
| 365 | + | ||
| 366 | + | m_spriteSize = spritesFile->getU16(); | |
| 367 | + | m_cachedData = std::move(PngUnpacker::unpack(spritesFile)); | |
| 368 | + | m_spritesCount = m_cachedData.size(); | |
| 369 | + | ||
| 370 | + | if (m_spritesCount == 0) { | |
| 371 | + | g_logger.error(stdext::format("Failed to load sprites from '%s' - no sprites", file)); | |
| 372 | + | return false; | |
| 373 | + | } | |
| 374 | + | ||
| 375 | + | m_loaded = true; | |
| 376 | + | return true; | |
| 377 | + | } | |
| 378 | + | catch (stdext::exception& e) { | |
| 379 | + | g_logger.error(stdext::format("Failed to load sprites from '%s': %s", file, e.what())); | |
| 380 | + | return false; | |
| 381 | + | } | |
| 382 | + | ||
| 383 | + | return false; | |
| 384 | + | } | |
| 385 | + | ||
| 386 | + | ImagePtr SpriteManager::getSpriteImageCasual(int id) | |
| 387 | + | { | |
| 388 | + | try { | |
| 389 | + | int spriteDataSize = m_spriteSize * m_spriteSize * 4; | |
| 390 | + | ||
| 391 | + | if (!m_sprites.empty()) { | |
| 392 | + | if (id >= (int)m_sprites.size()) | |
| 393 | + | return nullptr; | |
| 394 | + | auto& buffer = m_sprites[id]; | |
| 395 | + | if (buffer.size() < 5) | |
| 396 | + | return nullptr; | |
| 397 | + | if (buffer[0] == 0) { | |
| 398 | + | buffer[0] = 1; | |
| 399 | + | g_crypt.bdecrypt(buffer.data() + 1, buffer.size() - 1, (uint64_t)m_signature + id); | |
| 400 | + | } | |
| 401 | + | ||
| 402 | + | if (buffer[1] > 1) { | |
| 403 | + | stdext::throw_exception("Invalid sprite encryption"); | |
| 404 | + | } | |
| 405 | + | ||
| 406 | + | bool hasAlpha = (buffer[1] == 1); | |
| 407 | + | ||
| 408 | + | ImagePtr image(new Image(Size(m_spriteSize, m_spriteSize))); | |
| 409 | + | uint8* pixels = image->getPixelData(); | |
| 410 | + | int writePos = 0; | |
| 411 | + | ||
| 412 | + | size_t bufferPos = 2; | |
| 413 | + | while (bufferPos != buffer.size()) { | |
| 414 | + | uint16_t transparentPixels = *(uint16_t*)(&buffer[bufferPos]); | |
| 415 | + | bufferPos += 2; | |
| 416 | + | uint16_t coloredPixels = *(uint16_t*)(&buffer[bufferPos]); | |
| 417 | + | bufferPos += 2; | |
| 418 | + | ||
| 419 | + | writePos += transparentPixels * 4; | |
| 420 | + | for (int i = 0; i < coloredPixels; ++i) { | |
| 421 | + | pixels[writePos++] = buffer[bufferPos++]; | |
| 422 | + | pixels[writePos++] = buffer[bufferPos++]; | |
| 423 | + | pixels[writePos++] = buffer[bufferPos++]; | |
| 424 | + | if (hasAlpha) { | |
| 425 | + | pixels[writePos] = buffer[bufferPos++]; | |
| 426 | + | } | |
| 427 | + | else { | |
| 428 | + | pixels[writePos] = 0xFF; | |
| 429 | + | } | |
| 430 | + | writePos += 1; | |
| 431 | + | } | |
| 432 | + | } | |
| 433 | + | ||
| 434 | + | return image; | |
| 435 | + | } | |
| 436 | + | ||
| 437 | + | if (id == 0 || !m_spritesFile) | |
| 438 | + | return nullptr; | |
| 439 | + | ||
| 440 | + | m_spritesFile->seek(((id - 1) * 4) + m_spritesOffset); | |
| 441 | + | ||
| 442 | + | uint32 spriteAddress = m_spritesFile->getU32(); | |
| 443 | + | ||
| 444 | + | // no sprite? return an empty texture | |
| 445 | + | if (spriteAddress == 0) | |
| 446 | + | return nullptr; | |
| 447 | + | ||
| 448 | + | m_spritesFile->seek(spriteAddress); | |
| 449 | + | ||
| 450 | + | // color key | |
| 451 | + | m_spritesFile->getU8(); | |
| 452 | + | m_spritesFile->getU8(); | |
| 453 | + | m_spritesFile->getU8(); | |
| 454 | + | ||
| 455 | + | uint16 pixelDataSize = m_spritesFile->getU16(); | |
| 456 | + | ||
| 457 | + | ImagePtr image(new Image(Size(m_spriteSize, m_spriteSize))); | |
| 458 | + | ||
| 459 | + | uint8* pixels = image->getPixelData(); | |
| 460 | + | int writePos = 0; | |
| 461 | + | bool useAlpha = g_game.getFeature(Otc::GameSpritesAlphaChannel); | |
| 462 | + | ||
| 463 | + | std::vector<uint8> data(pixelDataSize); | |
| 464 | + | if (pixelDataSize > 0) | |
| 465 | + | m_spritesFile->read(data.data(), pixelDataSize); | |
| 466 | + | ||
| 467 | + | // decompress pixels | |
| 468 | + | size_t read = 0; | |
| 469 | + | while (read + 4 <= data.size() && writePos < spriteDataSize) { | |
| 470 | + | uint16 transparentPixels = data[read] | (data[read + 1] << 8); | |
| 471 | + | uint16 coloredPixels = data[read + 2] | (data[read + 3] << 8); | |
| 472 | + | read += 4; | |
| 473 | + | ||
| 474 | + | writePos += transparentPixels * 4; | |
| 475 | + | if (writePos >= spriteDataSize) | |
| 476 | + | break; | |
| 477 | + | ||
| 478 | + | if (useAlpha) { | |
| 479 | + | size_t count = std::min<size_t>({ static_cast<size_t>(coloredPixels) * 4, static_cast<size_t>(spriteDataSize - writePos), data.size() - read }); | |
| 480 | + | memcpy(&pixels[writePos], &data[read], count); | |
| 481 | + | writePos += coloredPixels * 4; | |
| 482 | + | read += static_cast<size_t>(coloredPixels) * 4; | |
| 483 | + | } | |
| 484 | + | else { | |
| 485 | + | for (int i = 0; i < coloredPixels && writePos < spriteDataSize && read + 3 <= data.size(); i++) { | |
| 486 | + | pixels[writePos + 0] = data[read]; | |
| 487 | + | pixels[writePos + 1] = data[read + 1]; | |
| 488 | + | pixels[writePos + 2] = data[read + 2]; | |
| 489 | + | pixels[writePos + 3] = 0xFF; | |
| 490 | + | writePos += 4; | |
| 491 | + | read += 3; | |
| 492 | + | } | |
| 493 | + | } | |
| 494 | + | } | |
| 495 | + | ||
| 496 | + | return image; | |
| 497 | + | } | |
| 498 | + | catch (stdext::exception& e) { | |
| 499 | + | g_logger.error(stdext::format("Failed to get sprite id %d: %s", id, e.what())); | |
| 500 | + | return nullptr; | |
| 501 | + | } | |
| 502 | + | } | |
| 503 | + | ||
| 504 | + | ImagePtr SpriteManager::getSpriteImageHd(int id) | |
| 505 | + | { | |
| 506 | + | if (id == 0 || !m_loaded) | |
| 507 | + | return nullptr; | |
| 508 | + | ||
| 509 | + | if (m_cachedData.find(id) == m_cachedData.end()) | |
| 510 | + | { | |
| 511 | + | return nullptr; | |
| 512 | + | } | |
| 513 | + | ||
| 514 | + | try { | |
| 515 | + | return Image::loadPNG(m_cachedData[id].data(), m_cachedData[id].size()); | |
| 516 | + | } catch (...) {} | |
| 517 | + | return nullptr; | |
| 518 | + | } |
| @@ -0,0 +1,77 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef SPRITEMANAGER_H | |
| 24 | + | #define SPRITEMANAGER_H | |
| 25 | + | ||
| 26 | + | #include "const.h" | |
| 27 | + | #include <framework/core/declarations.h> | |
| 28 | + | #include <framework/graphics/declarations.h> | |
| 29 | + | ||
| 30 | + | //@bindsingleton g_sprites | |
| 31 | + | class SpriteManager | |
| 32 | + | { | |
| 33 | + | public: | |
| 34 | + | SpriteManager(); | |
| 35 | + | ||
| 36 | + | void terminate(); | |
| 37 | + | ||
| 38 | + | bool loadSpr(std::string file); | |
| 39 | + | void unload(); | |
| 40 | + | ||
| 41 | + | #ifdef WITH_ENCRYPTION | |
| 42 | + | void saveSpr(std::string fileName); | |
| 43 | + | void saveSpr64(std::string fileName); | |
| 44 | + | void encryptSprites(std::string fileName); | |
| 45 | + | void dumpSprites(std::string dir); | |
| 46 | + | #endif | |
| 47 | + | ||
| 48 | + | uint32 getSignature() { return m_signature; } | |
| 49 | + | int getSpritesCount() { return m_spritesCount; } | |
| 50 | + | ||
| 51 | + | ImagePtr getSpriteImage(int id); | |
| 52 | + | bool isLoaded() { return m_loaded; } | |
| 53 | + | ||
| 54 | + | int spriteSize() { return m_spriteSize; } | |
| 55 | + | float getOffsetFactor() const { return static_cast<float>(m_spriteSize) / 32.0f; } | |
| 56 | + | bool isHdMod() const { return m_isHdMod; } | |
| 57 | + | ||
| 58 | + | private: | |
| 59 | + | bool loadCasualSpr(std::string file); | |
| 60 | + | bool loadCwmSpr(std::string file); | |
| 61 | + | ||
| 62 | + | ImagePtr getSpriteImageCasual(int id); | |
| 63 | + | ImagePtr getSpriteImageHd(int id); | |
| 64 | + | bool m_loaded = false; | |
| 65 | + | bool m_isHdMod = false; | |
| 66 | + | uint32 m_signature; | |
| 67 | + | int m_spritesCount; | |
| 68 | + | int m_spritesOffset; | |
| 69 | + | int m_spriteSize; | |
| 70 | + | FileStreamPtr m_spritesFile; | |
| 71 | + | std::vector<std::vector<uint8_t>> m_sprites; | |
| 72 | + | std::unordered_map<uint32, std::string> m_cachedData; | |
| 73 | + | }; | |
| 74 | + | ||
| 75 | + | extern SpriteManager g_sprites; | |
| 76 | + | ||
| 77 | + | #endif |
| @@ -0,0 +1,169 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "statictext.h" | |
| 24 | + | #include "map.h" | |
| 25 | + | #include "spritemanager.h" | |
| 26 | + | #include <framework/core/clock.h> | |
| 27 | + | #include <framework/core/eventdispatcher.h> | |
| 28 | + | #include <framework/graphics/graphics.h> | |
| 29 | + | #include <framework/graphics/fontmanager.h> | |
| 30 | + | ||
| 31 | + | StaticText::StaticText() | |
| 32 | + | { | |
| 33 | + | m_mode = Otc::MessageNone; | |
| 34 | + | m_color = Color::white; | |
| 35 | + | m_cachedText.setFont(g_fonts.getFont("verdana-11px-rounded")); | |
| 36 | + | m_cachedText.setAlign(Fw::AlignCenter); | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | void StaticText::drawText(const Point& dest, const Rect& parentRect) | |
| 40 | + | { | |
| 41 | + | Size textSize = m_cachedText.getTextSize(); | |
| 42 | + | Rect rect = Rect(dest - Point(textSize.width() / 2, textSize.height()) + Point(20, 5), textSize); | |
| 43 | + | Rect boundRect = rect; | |
| 44 | + | boundRect.bind(parentRect); | |
| 45 | + | ||
| 46 | + | // draw only if the real center is not too far from the parent center, or its a yell | |
| 47 | + | //if(g_map.isAwareOfPosition(m_position) || isYell()) { | |
| 48 | + | m_cachedText.draw(boundRect, m_color); | |
| 49 | + | //} | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | void StaticText::setFont(const std::string& fontName) | |
| 53 | + | { | |
| 54 | + | m_cachedText.setFont(g_fonts.getFont(fontName)); | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | void StaticText::setText(const std::string& text) | |
| 58 | + | { | |
| 59 | + | m_cachedText.setText(text); | |
| 60 | + | } | |
| 61 | + | ||
| 62 | + | bool StaticText::addMessage(const std::string& name, Otc::MessageMode mode, const std::string& text) | |
| 63 | + | { | |
| 64 | + | return addColoredMessage(name, mode, { text, "" }); | |
| 65 | + | } | |
| 66 | + | ||
| 67 | + | bool StaticText::addColoredMessage(const std::string& name, Otc::MessageMode mode, const std::vector<std::string>& texts) | |
| 68 | + | { | |
| 69 | + | if (texts.empty() || texts.size() % 2 != 0) | |
| 70 | + | return false; | |
| 71 | + | //TODO: this could be moved to lua | |
| 72 | + | // first message | |
| 73 | + | if (m_messages.size() == 0) { | |
| 74 | + | m_name = name; | |
| 75 | + | m_mode = mode; | |
| 76 | + | } | |
| 77 | + | // check if we can really own the message | |
| 78 | + | else if (m_name != name || m_mode != mode) { | |
| 79 | + | return false; | |
| 80 | + | } | |
| 81 | + | // too many messages | |
| 82 | + | else if (m_messages.size() > 10) { | |
| 83 | + | m_messages.pop_front(); | |
| 84 | + | m_updateEvent->cancel(); | |
| 85 | + | m_updateEvent = nullptr; | |
| 86 | + | } | |
| 87 | + | ||
| 88 | + | size_t len = 0; | |
| 89 | + | for (size_t i = 0; i < texts.size(); i += 2) { | |
| 90 | + | len += texts[i].length(); | |
| 91 | + | } | |
| 92 | + | ||
| 93 | + | int delay = std::max<int>(Otc::STATIC_DURATION_PER_CHARACTER * len, Otc::MIN_STATIC_TEXT_DURATION); | |
| 94 | + | if (isYell()) | |
| 95 | + | delay *= 2; | |
| 96 | + | ||
| 97 | + | m_messages.push_back(StaticTextMessage{ texts, g_clock.millis() + delay }); | |
| 98 | + | compose(); | |
| 99 | + | ||
| 100 | + | if (!m_updateEvent) | |
| 101 | + | scheduleUpdate(); | |
| 102 | + | return true; | |
| 103 | + | } | |
| 104 | + | ||
| 105 | + | void StaticText::update() | |
| 106 | + | { | |
| 107 | + | m_messages.pop_front(); | |
| 108 | + | if(m_messages.empty()) { | |
| 109 | + | // schedule removal | |
| 110 | + | auto self = asStaticText(); | |
| 111 | + | g_dispatcher.addEvent([self]() { g_map.removeThing(self); }); | |
| 112 | + | } else { | |
| 113 | + | compose(); | |
| 114 | + | scheduleUpdate(); | |
| 115 | + | } | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | void StaticText::scheduleUpdate() | |
| 119 | + | { | |
| 120 | + | int delay = std::max<int>(m_messages.front().time - g_clock.millis(), 0); | |
| 121 | + | ||
| 122 | + | auto self = asStaticText(); | |
| 123 | + | m_updateEvent = g_dispatcher.scheduleEvent([self]() { | |
| 124 | + | self->m_updateEvent = nullptr; | |
| 125 | + | self->update(); | |
| 126 | + | }, delay); | |
| 127 | + | } | |
| 128 | + | ||
| 129 | + | void StaticText::compose() | |
| 130 | + | { | |
| 131 | + | //TODO: this could be moved to lua | |
| 132 | + | std::vector<std::string> texts; | |
| 133 | + | ||
| 134 | + | if(m_mode == Otc::MessageSay) { | |
| 135 | + | texts.push_back(m_name + " says:\n"); | |
| 136 | + | texts.push_back("#EFEF00"); | |
| 137 | + | m_color = Color(239, 239, 0); | |
| 138 | + | } else if(m_mode == Otc::MessageWhisper) { | |
| 139 | + | texts.push_back(m_name + " whispers:\n"); | |
| 140 | + | texts.push_back("#EFEF00"); | |
| 141 | + | m_color = Color(239, 239, 0); | |
| 142 | + | } else if(m_mode == Otc::MessageYell) { | |
| 143 | + | texts.push_back(m_name + " yells:\n"); | |
| 144 | + | texts.push_back("#EFEF00"); | |
| 145 | + | m_color = Color(239, 239, 0); | |
| 146 | + | } else if(m_mode == Otc::MessageMonsterSay || m_mode == Otc::MessageMonsterYell || m_mode == Otc::MessageSpell | |
| 147 | + | || m_mode == Otc::MessageBarkLow || m_mode == Otc::MessageBarkLoud) { | |
| 148 | + | m_color = Color(254, 101, 0); | |
| 149 | + | } else if(m_mode == Otc::MessageNpcFrom || m_mode == Otc::MessageNpcFromStartBlock) { | |
| 150 | + | texts.push_back(m_name + " says:\n"); | |
| 151 | + | texts.push_back("#5FF7F7"); | |
| 152 | + | m_color = Color(95, 247, 247); | |
| 153 | + | } else { | |
| 154 | + | g_logger.warning(stdext::format("Unknown speak type: %d", m_mode)); | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | for(uint i = 0; i < m_messages.size(); ++i) { | |
| 158 | + | for (size_t j = 0; j < m_messages[i].texts.size() - 1; j += 2) { | |
| 159 | + | texts.push_back(m_messages[i].texts[j]); | |
| 160 | + | texts.push_back(m_messages[i].texts[j + 1].empty() ? m_color.toHex() : m_messages[i].texts[j + 1]); | |
| 161 | + | } | |
| 162 | + | if (texts.size() >= 2 && i < m_messages.size() - 1) { | |
| 163 | + | texts[texts.size() - 2] += "\n"; | |
| 164 | + | } | |
| 165 | + | } | |
| 166 | + | ||
| 167 | + | m_cachedText.setColoredText(texts); | |
| 168 | + | m_cachedText.wrapText(275); | |
| 169 | + | } |
| @@ -0,0 +1,77 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef STATICTEXT_H | |
| 24 | + | #define STATICTEXT_H | |
| 25 | + | ||
| 26 | + | #include "thing.h" | |
| 27 | + | #include <framework/graphics/cachedtext.h> | |
| 28 | + | #include <framework/core/timer.h> | |
| 29 | + | ||
| 30 | + | struct StaticTextMessage { | |
| 31 | + | std::vector<std::string> texts; | |
| 32 | + | ticks_t time; | |
| 33 | + | }; | |
| 34 | + | ||
| 35 | + | // @bindclass | |
| 36 | + | class StaticText : public Thing | |
| 37 | + | { | |
| 38 | + | public: | |
| 39 | + | StaticText(); | |
| 40 | + | ||
| 41 | + | void drawText(const Point& dest, const Rect& parentRect); | |
| 42 | + | ||
| 43 | + | std::string getName() { return m_name; } | |
| 44 | + | std::string getText() { return m_cachedText.getText(); } | |
| 45 | + | Otc::MessageMode getMessageMode() { return m_mode; } | |
| 46 | + | std::vector<std::string> getFirstMessage() { return m_messages[0].texts; } | |
| 47 | + | ||
| 48 | + | bool isYell() { return m_mode == Otc::MessageYell || m_mode == Otc::MessageMonsterYell || m_mode == Otc::MessageBarkLoud; } | |
| 49 | + | ||
| 50 | + | void setText(const std::string& text); | |
| 51 | + | void setFont(const std::string& fontName); | |
| 52 | + | bool addMessage(const std::string& name, Otc::MessageMode mode, const std::string& text); | |
| 53 | + | bool addColoredMessage(const std::string& name, Otc::MessageMode mode, const std::vector<std::string>& texts); | |
| 54 | + | StaticTextPtr asStaticText() { return static_self_cast<StaticText>(); } | |
| 55 | + | bool isStaticText() { return true; } | |
| 56 | + | ||
| 57 | + | void setColor(const Color& color) { m_color = color; } | |
| 58 | + | Color getColor() { return m_color; } | |
| 59 | + | ||
| 60 | + | CachedText& getCachedText() { return m_cachedText; } | |
| 61 | + | bool hasText() { return m_cachedText.hasText(); } | |
| 62 | + | ||
| 63 | + | private: | |
| 64 | + | void update(); | |
| 65 | + | void scheduleUpdate(); | |
| 66 | + | void compose(); | |
| 67 | + | ||
| 68 | + | stdext::boolean<false> m_yell; | |
| 69 | + | std::deque<StaticTextMessage> m_messages; | |
| 70 | + | std::string m_name; | |
| 71 | + | Otc::MessageMode m_mode; | |
| 72 | + | Color m_color; | |
| 73 | + | CachedText m_cachedText; | |
| 74 | + | ScheduledEventPtr m_updateEvent; | |
| 75 | + | }; | |
| 76 | + | ||
| 77 | + | #endif |
| @@ -0,0 +1,114 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #include "thing.h" | |
| 24 | + | #include "spritemanager.h" | |
| 25 | + | #include "thingtypemanager.h" | |
| 26 | + | #include <framework/graphics/graphics.h> | |
| 27 | + | #include "map.h" | |
| 28 | + | #include "tile.h" | |
| 29 | + | #include "game.h" | |
| 30 | + | #include <framework/util/stats.h> | |
| 31 | + | ||
| 32 | + | Thing::Thing() : | |
| 33 | + | m_datId(0) | |
| 34 | + | { | |
| 35 | + | g_stats.addThing(); | |
| 36 | + | } | |
| 37 | + | ||
| 38 | + | Thing::~Thing() | |
| 39 | + | { | |
| 40 | + | g_stats.removeThing(); | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | void Thing::setPosition(const Position& position) | |
| 44 | + | { | |
| 45 | + | if(m_position == position) | |
| 46 | + | return; | |
| 47 | + | ||
| 48 | + | Position oldPos = m_position; | |
| 49 | + | m_position = position; | |
| 50 | + | onPositionChange(position, oldPos); | |
| 51 | + | } | |
| 52 | + | ||
| 53 | + | int Thing::getStackPriority() | |
| 54 | + | { | |
| 55 | + | // bug fix for old versions | |
| 56 | + | if (g_game.getClientVersion() <= 800 && isSplash()) { | |
| 57 | + | return 1; | |
| 58 | + | } | |
| 59 | + | if(isGround()) | |
| 60 | + | return 0; | |
| 61 | + | else if(isGroundBorder()) | |
| 62 | + | return 1; | |
| 63 | + | else if(isOnBottom()) | |
| 64 | + | return 2; | |
| 65 | + | else if(isOnTop()) | |
| 66 | + | return 3; | |
| 67 | + | else if(isCreature()) | |
| 68 | + | return 4; | |
| 69 | + | else // common items | |
| 70 | + | return 5; | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | const TilePtr& Thing::getTile() | |
| 74 | + | { | |
| 75 | + | return g_map.getTile(m_position); | |
| 76 | + | } | |
| 77 | + | ||
| 78 | + | ContainerPtr Thing::getParentContainer() | |
| 79 | + | { | |
| 80 | + | if(m_position.x == 0xffff && m_position.y & 0x40) { | |
| 81 | + | int containerId = m_position.y ^ 0x40; | |
| 82 | + | return g_game.getContainer(containerId); | |
| 83 | + | } | |
| 84 | + | return nullptr; | |
| 85 | + | } | |
| 86 | + | ||
| 87 | + | int Thing::getStackPos() | |
| 88 | + | { | |
| 89 | + | if(m_position.x == 65535 && isItem()) // is inside a container | |
| 90 | + | return m_position.z; | |
| 91 | + | else if(const TilePtr& tile = getTile()) | |
| 92 | + | return tile->getThingStackPos(static_self_cast<Thing>()); | |
| 93 | + | else { | |
| 94 | + | g_logger.traceError("got a thing with invalid stackpos"); | |
| 95 | + | return -1; | |
| 96 | + | } | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | const ThingTypePtr& Thing::getThingType() | |
| 100 | + | { | |
| 101 | + | return g_things.getNullThingType(); | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | ThingType* Thing::rawGetThingType() | |
| 105 | + | { | |
| 106 | + | return g_things.getNullThingType().get(); | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | Color Thing::updatedMarkedColor() { | |
| 110 | + | if (!m_marked) | |
| 111 | + | return Color::white; | |
| 112 | + | m_markedColor.setAlpha(0.1f + std::abs(500 - g_clock.millis() % 1000) / 1000.0f); // 0.1-0.6 | |
| 113 | + | return m_markedColor; | |
| 114 | + | } |
| @@ -0,0 +1,157 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef THING_H | |
| 24 | + | #define THING_H | |
| 25 | + | ||
| 26 | + | #include "declarations.h" | |
| 27 | + | #include "thingtype.h" | |
| 28 | + | #include "thingtypemanager.h" | |
| 29 | + | #include <framework/luaengine/luaobject.h> | |
| 30 | + | #include <framework/graphics/drawqueue.h> | |
| 31 | + | ||
| 32 | + | // @bindclass | |
| 33 | + | #pragma pack(push,1) // disable memory alignment | |
| 34 | + | class Thing : public LuaObject | |
| 35 | + | { | |
| 36 | + | public: | |
| 37 | + | Thing(); | |
| 38 | + | virtual ~Thing(); | |
| 39 | + | ||
| 40 | + | virtual void draw(const Point& dest, bool animate = true, LightView* lightView = nullptr) { } | |
| 41 | + | ||
| 42 | + | virtual void setId(uint32 id) { } | |
| 43 | + | void setPosition(const Position& position); | |
| 44 | + | ||
| 45 | + | virtual uint32 getId() { return 0; } | |
| 46 | + | Position getPosition() { return m_position; } | |
| 47 | + | int getStackPriority(); | |
| 48 | + | virtual const TilePtr& getTile(); | |
| 49 | + | ContainerPtr getParentContainer(); | |
| 50 | + | int getStackPos(); | |
| 51 | + | ||
| 52 | + | void setMarked(const std::string& color) { | |
| 53 | + | if (color.empty()) { | |
| 54 | + | m_marked = false; | |
| 55 | + | return; | |
| 56 | + | } | |
| 57 | + | m_marked = true; | |
| 58 | + | m_markedColor = Color(color); | |
| 59 | + | } | |
| 60 | + | Color updatedMarkedColor(); | |
| 61 | + | ||
| 62 | + | virtual bool isItem() { return false; } | |
| 63 | + | virtual bool isEffect() { return false; } | |
| 64 | + | virtual bool isMissile() { return false; } | |
| 65 | + | virtual bool isCreature() { return false; } | |
| 66 | + | virtual bool isNpc() { return false; } | |
| 67 | + | virtual bool isMonster() { return false; } | |
| 68 | + | virtual bool isPlayer() { return false; } | |
| 69 | + | virtual bool isLocalPlayer() { return false; } | |
| 70 | + | virtual bool isAnimatedText() { return false; } | |
| 71 | + | virtual bool isStaticText() { return false; } | |
| 72 | + | ||
| 73 | + | // type shortcuts | |
| 74 | + | virtual const ThingTypePtr& getThingType(); | |
| 75 | + | virtual ThingType *rawGetThingType(); | |
| 76 | + | Size getSize() { return rawGetThingType()->getSize(); } | |
| 77 | + | int getWidth() { return rawGetThingType()->getWidth(); } | |
| 78 | + | int getHeight() { return rawGetThingType()->getHeight(); } | |
| 79 | + | virtual Point getDisplacement() { return rawGetThingType()->getDisplacement(); } | |
| 80 | + | virtual int getDisplacementX() { return rawGetThingType()->getDisplacementX(); } | |
| 81 | + | virtual int getDisplacementY() { return rawGetThingType()->getDisplacementY(); } | |
| 82 | + | virtual int getExactSize(int layer, int xPattern, int yPattern, int zPattern, int animationPhase) { return rawGetThingType()->getExactSize(layer, xPattern, yPattern, zPattern, animationPhase); } | |
| 83 | + | int getLayers() { return rawGetThingType()->getLayers(); } | |
| 84 | + | int getNumPatternX() { return rawGetThingType()->getNumPatternX(); } | |
| 85 | + | int getNumPatternY() { return rawGetThingType()->getNumPatternY(); } | |
| 86 | + | int getNumPatternZ() { return rawGetThingType()->getNumPatternZ(); } | |
| 87 | + | int getAnimationPhases() { return rawGetThingType()->getAnimationPhases(); } | |
| 88 | + | AnimatorPtr getAnimator() { return rawGetThingType()->getAnimator(); } | |
| 89 | + | AnimatorPtr getIdleAnimator() { return rawGetThingType()->getIdleAnimator(); } | |
| 90 | + | int getGroundSpeed() { return rawGetThingType()->getGroundSpeed(); } | |
| 91 | + | int getMaxTextLength() { return rawGetThingType()->getMaxTextLength(); } | |
| 92 | + | Light getLight() { return rawGetThingType()->getLight(); } | |
| 93 | + | int getMinimapColor() { return rawGetThingType()->getMinimapColor(); } | |
| 94 | + | int getLensHelp() { return rawGetThingType()->getLensHelp(); } | |
| 95 | + | int getClothSlot() { return rawGetThingType()->getClothSlot(); } | |
| 96 | + | int getElevation() { return rawGetThingType()->getElevation(); } | |
| 97 | + | bool isGround() { return rawGetThingType()->isGround(); } | |
| 98 | + | bool isGroundBorder() { return rawGetThingType()->isGroundBorder(); } | |
| 99 | + | bool isOnBottom() { return rawGetThingType()->isOnBottom(); } | |
| 100 | + | bool isOnTop() { return rawGetThingType()->isOnTop(); } | |
| 101 | + | bool isContainer() { return rawGetThingType()->isContainer(); } | |
| 102 | + | bool isStackable() { return rawGetThingType()->isStackable(); } | |
| 103 | + | bool isForceUse() { return rawGetThingType()->isForceUse(); } | |
| 104 | + | bool isMultiUse() { return rawGetThingType()->isMultiUse(); } | |
| 105 | + | bool isWritable() { return rawGetThingType()->isWritable(); } | |
| 106 | + | bool isChargeable() { return rawGetThingType()->isChargeable(); } | |
| 107 | + | bool isWritableOnce() { return rawGetThingType()->isWritableOnce(); } | |
| 108 | + | bool isFluidContainer() { return rawGetThingType()->isFluidContainer(); } | |
| 109 | + | bool isSplash() { return rawGetThingType()->isSplash(); } | |
| 110 | + | bool isNotWalkable() { return rawGetThingType()->isNotWalkable(); } | |
| 111 | + | bool isNotMoveable() { return rawGetThingType()->isNotMoveable(); } | |
| 112 | + | bool blockProjectile() { return rawGetThingType()->blockProjectile(); } | |
| 113 | + | bool isNotPathable() { return rawGetThingType()->isNotPathable(); } | |
| 114 | + | bool isPickupable() { return rawGetThingType()->isPickupable(); } | |
| 115 | + | bool isHangable() { return rawGetThingType()->isHangable(); } | |
| 116 | + | bool isHookSouth() { return rawGetThingType()->isHookSouth(); } | |
| 117 | + | bool isHookEast() { return rawGetThingType()->isHookEast(); } | |
| 118 | + | bool isRotateable() { return rawGetThingType()->isRotateable(); } | |
| 119 | + | bool hasLight() { return rawGetThingType()->hasLight(); } | |
| 120 | + | bool isDontHide() { return rawGetThingType()->isDontHide(); } | |
| 121 | + | bool isTranslucent() { return rawGetThingType()->isTranslucent(); } | |
| 122 | + | bool hasDisplacement() { return rawGetThingType()->hasDisplacement(); } | |
| 123 | + | bool hasElevation() { return rawGetThingType()->hasElevation(); } | |
| 124 | + | bool isLyingCorpse() { return rawGetThingType()->isLyingCorpse(); } | |
| 125 | + | bool isAnimateAlways() { return rawGetThingType()->isAnimateAlways(); } | |
| 126 | + | bool hasMiniMapColor() { return rawGetThingType()->hasMiniMapColor(); } | |
| 127 | + | bool hasLensHelp() { return rawGetThingType()->hasLensHelp(); } | |
| 128 | + | bool isFullGround() { return rawGetThingType()->isFullGround(); } | |
| 129 | + | bool isIgnoreLook() { return rawGetThingType()->isIgnoreLook(); } | |
| 130 | + | bool isCloth() { return rawGetThingType()->isCloth(); } | |
| 131 | + | bool isMarketable() { return rawGetThingType()->isMarketable(); } | |
| 132 | + | bool isUsable() { return rawGetThingType()->isUsable(); } | |
| 133 | + | bool isWrapable() { return rawGetThingType()->isWrapable(); } | |
| 134 | + | bool isUnwrapable() { return rawGetThingType()->isUnwrapable(); } | |
| 135 | + | bool isTopEffect() { return rawGetThingType()->isTopEffect(); } | |
| 136 | + | MarketData getMarketData() { return rawGetThingType()->getMarketData(); } | |
| 137 | + | ||
| 138 | + | void hide() { m_hidden = true; } | |
| 139 | + | void show() { m_hidden = false; } | |
| 140 | + | void setHidden(bool value) { m_hidden = value; } | |
| 141 | + | bool isHidden() { return m_hidden; } | |
| 142 | + | ||
| 143 | + | virtual void onPositionChange(const Position& newPos, const Position& oldPos) { } | |
| 144 | + | virtual void onAppear() { } | |
| 145 | + | virtual void onDisappear() { } | |
| 146 | + | ||
| 147 | + | protected: | |
| 148 | + | Position m_position; | |
| 149 | + | uint16 m_datId; | |
| 150 | + | bool m_marked = false; | |
| 151 | + | bool m_hidden = false; | |
| 152 | + | Color m_markedColor; | |
| 153 | + | }; | |
| 154 | + | #pragma pack(pop) | |
| 155 | + | ||
| 156 | + | #endif | |
| 157 | + |
| @@ -0,0 +1,67 @@ | |||
| 1 | + | /* | |
| 2 | + | * Copyright (c) 2010-2017 OTClient <https://github.com/edubart/otclient> | |
| 3 | + | * | |
| 4 | + | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
| 5 | + | * of this software and associated documentation files (the "Software"), to deal | |
| 6 | + | * in the Software without restriction, including without limitation the rights | |
| 7 | + | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| 8 | + | * copies of the Software, and to permit persons to whom the Software is | |
| 9 | + | * furnished to do so, subject to the following conditions: | |
| 10 | + | * | |
| 11 | + | * The above copyright notice and this permission notice shall be included in | |
| 12 | + | * all copies or substantial portions of the Software. | |
| 13 | + | * | |
| 14 | + | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| 15 | + | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| 16 | + | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| 17 | + | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| 18 | + | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| 19 | + | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
| 20 | + | * THE SOFTWARE. | |
| 21 | + | */ | |
| 22 | + | ||
| 23 | + | #ifndef DATMANAGER_H | |
| 24 | + | #define DATMANAGER_H | |
| 25 | + | ||
| 26 | + | #include <framework/global.h> | |
| 27 | + | #include <framework/core/declarations.h> | |
| 28 | + | #include "thingtype.h" | |
| 29 | + | ||
| 30 | + | //@bindsingleton g_thingsType | |
| 31 | + | class ThingsType | |
| 32 | + | { | |
| 33 | + | public: | |
| 34 | + | ||
| 35 | + | enum Categories { | |
| 36 | + | Item = 0, | |
| 37 | + | Creature, | |
| 38 | + | Effect, | |
| 39 | + | Missile, | |
| 40 | + | LastCategory | |
| 41 | + | }; | |
| 42 | + | ||
| 43 | + | bool load(const std::string& file); | |
| 44 | + | void unload(); | |
| 45 | + | ||
| 46 | + | bool parseThingType(const FileStreamPtr& fin, ThingType& thingType); | |
| 47 | + | ||
| 48 | + | ThingType *getEmptyThingType() { return &m_emptyThingType; } | |
| 49 | + | ThingType *getThingType(uint16 id, Categories category); | |
| 50 | + | ||
| 51 | + | uint32 getSignature() { return m_signature; } | |
| 52 | + | bool isLoaded() { return m_loaded; } | |
| 53 | + | ||
| 54 | + | uint16 getFirstItemId() { return 100; } | |
| 55 | + | uint16 getMaxItemid() { return m_things[Item].size() + 99; } | |
| 56 | + | bool isValidItemId(int id) { return id >= getFirstItemId() && id <= getMaxItemid(); } | |
| 57 | + | ||
| 58 | + | private: | |
| 59 | + | uint32 m_signature; | |
| 60 | + | stdext::boolean<false> m_loaded; | |
| 61 | + | ThingTypeList m_things[LastCategory]; | |
| 62 | + | static ThingType m_emptyThingType; | |
| 63 | + | }; | |
| 64 | + | ||
| 65 | + | extern ThingsType g_thingsType; | |
| 66 | + | ||
| 67 | + | #endif |