| @@ -0,0 +1,597 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_GAME_H_3EC96D67DD024E6093B3BAC29B7A6D7F | |
| 21 | + | #define FS_GAME_H_3EC96D67DD024E6093B3BAC29B7A6D7F | |
| 22 | + | ||
| 23 | + | #include "account.h" | |
| 24 | + | #include "combat.h" | |
| 25 | + | #include "groups.h" | |
| 26 | + | #include "map.h" | |
| 27 | + | #include "position.h" | |
| 28 | + | #include "item.h" | |
| 29 | + | #include "container.h" | |
| 30 | + | #include "player.h" | |
| 31 | + | #include "runeeffects.h" | |
| 32 | + | #include "raids.h" | |
| 33 | + | #include "npc.h" | |
| 34 | + | #include "wildcardtree.h" | |
| 35 | + | #include "quests.h" | |
| 36 | + | #include "shaders.h" | |
| 37 | + | ||
| 38 | + | class ServiceManager; | |
| 39 | + | class Creature; | |
| 40 | + | class Monster; | |
| 41 | + | class Npc; | |
| 42 | + | class CombatInfo; | |
| 43 | + | ||
| 44 | + | enum stackPosType_t { | |
| 45 | + | STACKPOS_MOVE, | |
| 46 | + | STACKPOS_LOOK, | |
| 47 | + | STACKPOS_TOPDOWN_ITEM, | |
| 48 | + | STACKPOS_USEITEM, | |
| 49 | + | STACKPOS_USETARGET, | |
| 50 | + | }; | |
| 51 | + | ||
| 52 | + | enum WorldType_t { | |
| 53 | + | WORLD_TYPE_NO_PVP = 1, | |
| 54 | + | WORLD_TYPE_PVP = 2, | |
| 55 | + | WORLD_TYPE_PVP_ENFORCED = 3, | |
| 56 | + | }; | |
| 57 | + | ||
| 58 | + | enum GameState_t { | |
| 59 | + | GAME_STATE_STARTUP, | |
| 60 | + | GAME_STATE_INIT, | |
| 61 | + | GAME_STATE_NORMAL, | |
| 62 | + | GAME_STATE_CLOSED, | |
| 63 | + | GAME_STATE_SHUTDOWN, | |
| 64 | + | GAME_STATE_CLOSING, | |
| 65 | + | GAME_STATE_MAINTAIN, | |
| 66 | + | }; | |
| 67 | + | ||
| 68 | + | enum LightState_t { | |
| 69 | + | LIGHT_STATE_DAY, | |
| 70 | + | LIGHT_STATE_NIGHT, | |
| 71 | + | LIGHT_STATE_SUNSET, | |
| 72 | + | LIGHT_STATE_SUNRISE, | |
| 73 | + | }; | |
| 74 | + | ||
| 75 | + | static constexpr int32_t EVENT_LIGHTINTERVAL = 10000; | |
| 76 | + | static constexpr int32_t EVENT_DECAYINTERVAL = 250; | |
| 77 | + | static constexpr int32_t EVENT_DECAY_BUCKETS = 4; | |
| 78 | + | ||
| 79 | + | /** | |
| 80 | + | * Main Game class. | |
| 81 | + | * This class is responsible to control everything that happens | |
| 82 | + | */ | |
| 83 | + | ||
| 84 | + | class Game | |
| 85 | + | { | |
| 86 | + | public: | |
| 87 | + | Game(); | |
| 88 | + | ~Game(); | |
| 89 | + | ||
| 90 | + | // non-copyable | |
| 91 | + | Game(const Game&) = delete; | |
| 92 | + | Game& operator=(const Game&) = delete; | |
| 93 | + | ||
| 94 | + | void start(ServiceManager* manager); | |
| 95 | + | ||
| 96 | + | void forceAddCondition(uint32_t creatureId, Condition* condition); | |
| 97 | + | void forceRemoveCondition(uint32_t creatureId, ConditionType_t type); | |
| 98 | + | ||
| 99 | + | bool loadMainMap(const std::string& filename); | |
| 100 | + | void loadMap(const std::string& path); | |
| 101 | + | ||
| 102 | + | /** | |
| 103 | + | * Get the map size - info purpose only | |
| 104 | + | * \param width width of the map | |
| 105 | + | * \param height height of the map | |
| 106 | + | */ | |
| 107 | + | void getMapDimensions(uint32_t& width, uint32_t& height) const { | |
| 108 | + | width = map.width; | |
| 109 | + | height = map.height; | |
| 110 | + | } | |
| 111 | + | ||
| 112 | + | void setWorldType(WorldType_t type); | |
| 113 | + | WorldType_t getWorldType() const { | |
| 114 | + | return worldType; | |
| 115 | + | } | |
| 116 | + | ||
| 117 | + | Cylinder* internalGetCylinder(Player* player, const Position& pos) const; | |
| 118 | + | Thing* internalGetThing(Player* player, const Position& pos, int32_t index, | |
| 119 | + | uint32_t spriteId, stackPosType_t type) const; | |
| 120 | + | static void internalGetPosition(Item* item, Position& pos, uint8_t& stackpos); | |
| 121 | + | ||
| 122 | + | static std::string getTradeErrorDescription(ReturnValue ret, Item* item); | |
| 123 | + | ||
| 124 | + | /** | |
| 125 | + | * Returns a creature based on the unique creature identifier | |
| 126 | + | * \param id is the unique creature id to get a creature pointer to | |
| 127 | + | * \returns A Creature pointer to the creature | |
| 128 | + | */ | |
| 129 | + | Creature* getCreatureByID(uint32_t id); | |
| 130 | + | ||
| 131 | + | /** | |
| 132 | + | * Returns a monster based on the unique creature identifier | |
| 133 | + | * \param id is the unique monster id to get a monster pointer to | |
| 134 | + | * \returns A Monster pointer to the monster | |
| 135 | + | */ | |
| 136 | + | Monster* getMonsterByID(uint32_t id); | |
| 137 | + | ||
| 138 | + | /** | |
| 139 | + | * Returns a npc based on the unique creature identifier | |
| 140 | + | * \param id is the unique npc id to get a npc pointer to | |
| 141 | + | * \returns A NPC pointer to the npc | |
| 142 | + | */ | |
| 143 | + | Npc* getNpcByID(uint32_t id); | |
| 144 | + | ||
| 145 | + | /** | |
| 146 | + | * Returns a player based on the unique creature identifier | |
| 147 | + | * \param id is the unique player id to get a player pointer to | |
| 148 | + | * \returns A Pointer to the player | |
| 149 | + | */ | |
| 150 | + | Player* getPlayerByID(uint32_t id); | |
| 151 | + | ||
| 152 | + | /** | |
| 153 | + | * Returns a creature based on a string name identifier | |
| 154 | + | * \param s is the name identifier | |
| 155 | + | * \returns A Pointer to the creature | |
| 156 | + | */ | |
| 157 | + | Creature* getCreatureByName(const std::string& s); | |
| 158 | + | ||
| 159 | + | /** | |
| 160 | + | * Returns a npc based on a string name identifier | |
| 161 | + | * \param s is the name identifier | |
| 162 | + | * \returns A Pointer to the npc | |
| 163 | + | */ | |
| 164 | + | Npc* getNpcByName(const std::string& s); | |
| 165 | + | ||
| 166 | + | /** | |
| 167 | + | * Returns a player based on a string name identifier | |
| 168 | + | * \param s is the name identifier | |
| 169 | + | * \returns A Pointer to the player | |
| 170 | + | */ | |
| 171 | + | Player* getPlayerByName(const std::string& s); | |
| 172 | + | ||
| 173 | + | /** | |
| 174 | + | * Returns a player based on guid | |
| 175 | + | * \returns A Pointer to the player | |
| 176 | + | */ | |
| 177 | + | Player* getPlayerByGUID(const uint32_t& guid); | |
| 178 | + | ||
| 179 | + | /** | |
| 180 | + | * Returns a player based on a string name identifier, with support for the "~" wildcard. | |
| 181 | + | * \param s is the name identifier, with or without wildcard | |
| 182 | + | * \param player will point to the found player (if any) | |
| 183 | + | * \return "RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE" or "RETURNVALUE_NAMEISTOOAMBIGIOUS" | |
| 184 | + | */ | |
| 185 | + | ReturnValue getPlayerByNameWildcard(const std::string& s, Player*& player); | |
| 186 | + | ||
| 187 | + | /** | |
| 188 | + | * Returns a player based on an account number identifier | |
| 189 | + | * \param acc is the account identifier | |
| 190 | + | * \returns A Pointer to the player | |
| 191 | + | */ | |
| 192 | + | Player* getPlayerByAccount(uint32_t acc); | |
| 193 | + | ||
| 194 | + | /* Place Creature on the map without sending out events to the surrounding. | |
| 195 | + | * \param creature Creature to place on the map | |
| 196 | + | * \param pos The position to place the creature | |
| 197 | + | * \param extendedPos If true, the creature will in first-hand be placed 2 tiles away | |
| 198 | + | * \param forced If true, placing the creature will not fail because of obstacles (creatures/items) | |
| 199 | + | */ | |
| 200 | + | bool internalPlaceCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool forced = false); | |
| 201 | + | ||
| 202 | + | /** | |
| 203 | + | * Place Creature on the map. | |
| 204 | + | * \param creature Creature to place on the map | |
| 205 | + | * \param pos The position to place the creature | |
| 206 | + | * \param extendedPos If true, the creature will in first-hand be placed 2 tiles away | |
| 207 | + | * \param force If true, placing the creature will not fail because of obstacles (creatures/items) | |
| 208 | + | */ | |
| 209 | + | bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool forced = false); | |
| 210 | + | ||
| 211 | + | /** | |
| 212 | + | * Remove Creature from the map. | |
| 213 | + | * Removes the Creature the map | |
| 214 | + | * \param c Creature to remove | |
| 215 | + | */ | |
| 216 | + | bool removeCreature(Creature* creature, bool isLogout = true); | |
| 217 | + | ||
| 218 | + | void addCreatureCheck(Creature* creature); | |
| 219 | + | static void removeCreatureCheck(Creature* creature); | |
| 220 | + | ||
| 221 | + | size_t getPlayersOnline() const { | |
| 222 | + | return players.size(); | |
| 223 | + | } | |
| 224 | + | size_t getMonstersOnline() const { | |
| 225 | + | return monsters.size(); | |
| 226 | + | } | |
| 227 | + | size_t getNpcsOnline() const { | |
| 228 | + | return npcs.size(); | |
| 229 | + | } | |
| 230 | + | uint32_t getPlayersRecord() const { | |
| 231 | + | return playersRecord; | |
| 232 | + | } | |
| 233 | + | ||
| 234 | + | LightInfo getWorldLightInfo() const; | |
| 235 | + | ||
| 236 | + | ReturnValue internalMoveCreature(Creature* creature, Direction direction, uint32_t flags = 0); | |
| 237 | + | ReturnValue internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags = 0); | |
| 238 | + | ||
| 239 | + | ReturnValue internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index, | |
| 240 | + | Item* item, uint32_t count, Item** _moveItem, uint32_t flags = 0, Creature* actor = nullptr, Item* tradeItem = nullptr, const Position* fromPos = nullptr, const Position* toPos = nullptr); | |
| 241 | + | ||
| 242 | + | ReturnValue internalAddItem(Cylinder* toCylinder, Item* item, int32_t index = INDEX_WHEREEVER, | |
| 243 | + | uint32_t flags = 0, bool test = false); | |
| 244 | + | ReturnValue internalAddItem(Cylinder* toCylinder, Item* item, int32_t index, | |
| 245 | + | uint32_t flags, bool test, uint32_t& remainderCount); | |
| 246 | + | ReturnValue internalRemoveItem(Item* item, int32_t count = -1, bool test = false, uint32_t flags = 0); | |
| 247 | + | ||
| 248 | + | ReturnValue internalPlayerAddItem(Player* player, Item* item, bool dropOnMap = true, slots_t slot = CONST_SLOT_WHEREEVER); | |
| 249 | + | ||
| 250 | + | /** | |
| 251 | + | * Find an item of a certain type | |
| 252 | + | * \param cylinder to search the item | |
| 253 | + | * \param itemId is the item to remove | |
| 254 | + | * \param subType is the extra type an item can have such as charges/fluidtype, default is -1 | |
| 255 | + | * meaning it's not used | |
| 256 | + | * \param depthSearch if true it will check child containers aswell | |
| 257 | + | * \returns A pointer to the item to an item and nullptr if not found | |
| 258 | + | */ | |
| 259 | + | Item* findItemOfType(Cylinder* cylinder, uint16_t itemId, | |
| 260 | + | bool depthSearch = true, int32_t subType = -1) const; | |
| 261 | + | ||
| 262 | + | /** | |
| 263 | + | * Remove/Add item(s) with a monetary value | |
| 264 | + | * \param cylinder to remove the money from | |
| 265 | + | * \param money is the amount to remove | |
| 266 | + | * \param flags optional flags to modifiy the default behaviour | |
| 267 | + | * \returns true if the removal was successful | |
| 268 | + | */ | |
| 269 | + | bool removeMoney(Cylinder* cylinder, uint64_t money, uint32_t flags = 0); | |
| 270 | + | ||
| 271 | + | /** | |
| 272 | + | * Add item(s) with monetary value | |
| 273 | + | * \param cylinder which will receive money | |
| 274 | + | * \param money the amount to give | |
| 275 | + | * \param flags optional flags to modify default behavior | |
| 276 | + | */ | |
| 277 | + | void addMoney(Cylinder* cylinder, uint64_t money, uint32_t flags = 0); | |
| 278 | + | ||
| 279 | + | /** | |
| 280 | + | * Transform one item to another type/count | |
| 281 | + | * \param item is the item to transform | |
| 282 | + | * \param newId is the new itemid | |
| 283 | + | * \param newCount is the new count value, use default value (-1) to not change it | |
| 284 | + | * \returns true if the tranformation was successful | |
| 285 | + | */ | |
| 286 | + | Item* transformItem(Item* item, uint16_t newId, int32_t newCount = -1); | |
| 287 | + | ||
| 288 | + | /** | |
| 289 | + | * Teleports an object to another position | |
| 290 | + | * \param thing is the object to teleport | |
| 291 | + | * \param newPos is the new position | |
| 292 | + | * \param pushMove force teleport if false | |
| 293 | + | * \param flags optional flags to modify default behavior | |
| 294 | + | * \returns true if the teleportation was successful | |
| 295 | + | */ | |
| 296 | + | ReturnValue internalTeleport(Thing* thing, const Position& newPos, bool pushMove = true, uint32_t flags = 0); | |
| 297 | + | ||
| 298 | + | /** | |
| 299 | + | * Turn a creature to a different direction. | |
| 300 | + | * \param creature Creature to change the direction | |
| 301 | + | * \param dir Direction to turn to | |
| 302 | + | */ | |
| 303 | + | bool internalCreatureTurn(Creature* creature, Direction dir); | |
| 304 | + | ||
| 305 | + | /** | |
| 306 | + | * Creature wants to say something. | |
| 307 | + | * \param creature Creature pointer | |
| 308 | + | * \param type Type of message | |
| 309 | + | * \param text The text to say | |
| 310 | + | */ | |
| 311 | + | bool internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, | |
| 312 | + | bool ghostMode, SpectatorVec* spectatorsPtr = nullptr, const Position* pos = nullptr); | |
| 313 | + | ||
| 314 | + | void loadPlayersRecord(); | |
| 315 | + | void checkPlayersRecord(); | |
| 316 | + | ||
| 317 | + | void sendGuildMotd(uint32_t playerId); | |
| 318 | + | void kickPlayer(uint32_t playerId, bool displayEffect); | |
| 319 | + | void playerReportBug(uint32_t playerId, const std::string& message, const Position& position, uint8_t category); | |
| 320 | + | void playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment); | |
| 321 | + | void playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, uint8_t button, uint8_t choice); | |
| 322 | + | void playerReportRuleViolation(uint32_t playerId, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation); | |
| 323 | + | ||
| 324 | + | bool internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem); | |
| 325 | + | void internalCloseTrade(Player* player); | |
| 326 | + | bool playerBroadcastMessage(Player* player, const std::string& text) const; | |
| 327 | + | void broadcastMessage(const std::string& text, MessageClasses type) const; | |
| 328 | + | ||
| 329 | + | //Implementation of player invoked events | |
| 330 | + | void playerMoveThing(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, | |
| 331 | + | const Position& toPos, uint8_t count); | |
| 332 | + | void playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, const Position& movingCreatureOrigPos, const Position& toPos); | |
| 333 | + | void playerMoveCreature(Player* player, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile); | |
| 334 | + | void playerMoveItemByPlayerID(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count); | |
| 335 | + | void playerMoveItem(Player* player, const Position& fromPos, | |
| 336 | + | uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count, Item* item, Cylinder* toCylinder); | |
| 337 | + | void playerEquipItem(uint32_t playerId, uint16_t spriteId); | |
| 338 | + | void playerMove(uint32_t playerId, Direction direction); | |
| 339 | + | void playerCreatePrivateChannel(uint32_t playerId); | |
| 340 | + | void playerChannelInvite(uint32_t playerId, const std::string& name); | |
| 341 | + | void playerChannelExclude(uint32_t playerId, const std::string& name); | |
| 342 | + | void playerRequestChannels(uint32_t playerId); | |
| 343 | + | void playerOpenChannel(uint32_t playerId, uint16_t channelId); | |
| 344 | + | void playerCloseChannel(uint32_t playerId, uint16_t channelId); | |
| 345 | + | void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver); | |
| 346 | + | void playerCloseNpcChannel(uint32_t playerId); | |
| 347 | + | void playerReceivePing(uint32_t playerId); | |
| 348 | + | void playerReceivePingBack(uint32_t playerId); | |
| 349 | + | void playerReceiveNewPing(uint32_t playerId, uint16_t ping, uint16_t fps); | |
| 350 | + | void playerAutoWalk(uint32_t playerId, const std::list<Direction>& listDir); | |
| 351 | + | void playerNewWalk(uint32_t playerId, Position pos, uint8_t flags, std::list<Direction> listDir); | |
| 352 | + | void playerStopAutoWalk(uint32_t playerId); | |
| 353 | + | void playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, | |
| 354 | + | uint16_t fromSpriteId, const Position& toPos, uint8_t toStackPos, uint16_t toSpriteId); | |
| 355 | + | void playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPos, uint8_t index, uint16_t spriteId); | |
| 356 | + | void playerUseWithCreature(uint32_t playerId, const Position& fromPos, uint8_t fromStackPos, uint32_t creatureId, uint16_t spriteId); | |
| 357 | + | void playerCloseContainer(uint32_t playerId, uint8_t cid); | |
| 358 | + | void playerMoveUpContainer(uint32_t playerId, uint8_t cid); | |
| 359 | + | void playerUpdateContainer(uint32_t playerId, uint8_t cid); | |
| 360 | + | void playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stackPos, const uint16_t spriteId); | |
| 361 | + | void playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text); | |
| 362 | + | void playerBrowseField(uint32_t playerId, const Position& pos); | |
| 363 | + | void playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index); | |
| 364 | + | void playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text); | |
| 365 | + | void playerWrapItem(uint32_t playerId, const Position& position, uint8_t stackPos, const uint16_t spriteId); | |
| 366 | + | void playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t stackPos, | |
| 367 | + | uint32_t tradePlayerId, uint16_t spriteId); | |
| 368 | + | void playerAcceptTrade(uint32_t playerId); | |
| 369 | + | void playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index); | |
| 370 | + | void playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, | |
| 371 | + | bool ignoreCap = false, bool inBackpacks = false); | |
| 372 | + | void playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, | |
| 373 | + | uint8_t amount, bool ignoreEquipped = false); | |
| 374 | + | void playerCloseShop(uint32_t playerId); | |
| 375 | + | void playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count); | |
| 376 | + | void playerCloseTrade(uint32_t playerId); | |
| 377 | + | void playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId); | |
| 378 | + | void playerFollowCreature(uint32_t playerId, uint32_t creatureId); | |
| 379 | + | void playerCancelAttackAndFollow(uint32_t playerId); | |
| 380 | + | void playerSetFightModes(uint32_t playerId, fightMode_t fightMode, bool chaseMode, bool secureMode); | |
| 381 | + | void playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos); | |
| 382 | + | void playerLookInBattleList(uint32_t playerId, uint32_t creatureId); | |
| 383 | + | void playerRequestAddVip(uint32_t playerId, const std::string& name); | |
| 384 | + | void playerRequestRemoveVip(uint32_t playerId, uint32_t guid); | |
| 385 | + | void playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string& description, uint32_t icon, bool notify); | |
| 386 | + | void playerTurn(uint32_t playerId, Direction dir); | |
| 387 | + | void playerRequestOutfit(uint32_t playerId); | |
| 388 | + | void playerShowQuestLog(uint32_t playerId); | |
| 389 | + | void playerShowQuestLine(uint32_t playerId, uint16_t questId); | |
| 390 | + | void playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, | |
| 391 | + | const std::string& receiver, const std::string& text); | |
| 392 | + | void playerChangeOutfit(uint32_t playerId, Outfit_t outfit); | |
| 393 | + | void playerInviteToParty(uint32_t playerId, uint32_t invitedId); | |
| 394 | + | void playerJoinParty(uint32_t playerId, uint32_t leaderId); | |
| 395 | + | void playerRevokePartyInvitation(uint32_t playerId, uint32_t invitedId); | |
| 396 | + | void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId); | |
| 397 | + | void playerLeaveParty(uint32_t playerId); | |
| 398 | + | void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive); | |
| 399 | + | void playerToggleOutfitExtension(uint32_t playerId, int mount, int wings, int aura, int shader, int runeeffect); | |
| 400 | + | void playerLeaveMarket(uint32_t playerId); | |
| 401 | + | void playerBrowseMarket(uint32_t playerId, uint16_t spriteId); | |
| 402 | + | void playerBrowseMarketOwnOffers(uint32_t playerId); | |
| 403 | + | void playerBrowseMarketOwnHistory(uint32_t playerId); | |
| 404 | + | void playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t spriteId, uint16_t amount, uint32_t price, bool anonymous); | |
| 405 | + | void playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter); | |
| 406 | + | void playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount); | |
| 407 | + | ||
| 408 | + | void parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer); | |
| 409 | + | ||
| 410 | + | std::forward_list<Item*> getMarketItemList(uint16_t wareId, uint16_t sufficientCount, DepotChest* depotChest, Inbox* inbox); | |
| 411 | + | ||
| 412 | + | static void updatePremium(Account& account); | |
| 413 | + | ||
| 414 | + | void cleanup(); | |
| 415 | + | void shutdown(); | |
| 416 | + | void ReleaseCreature(Creature* creature); | |
| 417 | + | void ReleaseItem(Item* item); | |
| 418 | + | ||
| 419 | + | bool canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight = true, | |
| 420 | + | int32_t rangex = Map::maxClientViewportX, int32_t rangey = Map::maxClientViewportY) const; | |
| 421 | + | bool isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck) const; | |
| 422 | + | ||
| 423 | + | void changeSpeed(Creature* creature, int32_t varSpeedDelta); | |
| 424 | + | void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit); | |
| 425 | + | void internalCreatureChangeVisible(Creature* creature, bool visible); | |
| 426 | + | void changeLight(const Creature* creature); | |
| 427 | + | void updateCreatureSkull(const Creature* creature); | |
| 428 | + | void updatePlayerShield(Player* player); | |
| 429 | + | void updatePlayerHelpers(const Player& player); | |
| 430 | + | void updateCreatureType(Creature* creature); | |
| 431 | + | void updateCreatureWalkthrough(const Creature* creature); | |
| 432 | + | ||
| 433 | + | GameState_t getGameState() const; | |
| 434 | + | void setGameState(GameState_t newState); | |
| 435 | + | void saveGameState(); | |
| 436 | + | ||
| 437 | + | //Events | |
| 438 | + | void checkCreatureWalk(uint32_t creatureId); | |
| 439 | + | void updateCreatureWalk(uint32_t creatureId); | |
| 440 | + | void checkCreatureAttack(uint32_t creatureId); | |
| 441 | + | void checkCreatures(size_t index); | |
| 442 | + | void checkLight(); | |
| 443 | + | ||
| 444 | + | bool combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* target, bool checkDefense, bool checkArmor, bool field); | |
| 445 | + | ||
| 446 | + | void combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect); | |
| 447 | + | ||
| 448 | + | bool combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage); | |
| 449 | + | bool combatChangeMana(Creature* attacker, Creature* target, CombatDamage& damage); | |
| 450 | + | ||
| 451 | + | //animation help functions | |
| 452 | + | void addCreatureHealth(const Creature* target); | |
| 453 | + | static void addCreatureHealth(const SpectatorVec& spectators, const Creature* target); | |
| 454 | + | void addMagicEffect(const Position& pos, uint16_t effect); // Cambiado de uint8_t a uint16_t | |
| 455 | + | static void addMagicEffect(const SpectatorVec& spectators, const Position& pos, uint16_t effect); // Cambiado de uint8_t a uint16_t | |
| 456 | + | void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect); | |
| 457 | + | static void addDistanceEffect(const SpectatorVec& spectators, const Position& fromPos, const Position& toPos, uint8_t effect); | |
| 458 | + | ||
| 459 | + | void startDecay(Item* item); | |
| 460 | + | int32_t getLightHour() const { | |
| 461 | + | return lightHour; | |
| 462 | + | } | |
| 463 | + | ||
| 464 | + | bool loadExperienceStages(); | |
| 465 | + | uint64_t getExperienceStage(uint32_t level); | |
| 466 | + | ||
| 467 | + | void loadMotdNum(); | |
| 468 | + | void saveMotdNum() const; | |
| 469 | + | const std::string& getMotdHash() const { return motdHash; } | |
| 470 | + | uint32_t getMotdNum() const { return motdNum; } | |
| 471 | + | void incrementMotdNum() { motdNum++; } | |
| 472 | + | ||
| 473 | + | void sendOfflineTrainingDialog(Player* player); | |
| 474 | + | ||
| 475 | + | const std::unordered_map<uint32_t, Player*>& getPlayers() const { return players; } | |
| 476 | + | const std::map<uint32_t, Npc*>& getNpcs() const { return npcs; } | |
| 477 | + | ||
| 478 | + | void addPlayer(Player* player); | |
| 479 | + | void removePlayer(Player* player); | |
| 480 | + | ||
| 481 | + | void addNpc(Npc* npc); | |
| 482 | + | void removeNpc(Npc* npc); | |
| 483 | + | ||
| 484 | + | void addMonster(Monster* monster); | |
| 485 | + | void removeMonster(Monster* monster); | |
| 486 | + | ||
| 487 | + | Guild* getGuild(uint32_t id) const; | |
| 488 | + | void addGuild(Guild* guild); | |
| 489 | + | void removeGuild(uint32_t guildId); | |
| 490 | + | void decreaseBrowseFieldRef(const Position& pos); | |
| 491 | + | ||
| 492 | + | std::unordered_map<Tile*, Container*> browseFields; | |
| 493 | + | ||
| 494 | + | void internalRemoveItems(std::vector<Item*> itemList, uint32_t amount, bool stackable); | |
| 495 | + | ||
| 496 | + | BedItem* getBedBySleeper(uint32_t guid) const; | |
| 497 | + | void setBedSleeper(BedItem* bed, uint32_t guid); | |
| 498 | + | void removeBedSleeper(uint32_t guid); | |
| 499 | + | ||
| 500 | + | Item* getUniqueItem(uint16_t uniqueId); | |
| 501 | + | bool addUniqueItem(uint16_t uniqueId, Item* item); | |
| 502 | + | void removeUniqueItem(uint16_t uniqueId); | |
| 503 | + | ||
| 504 | + | bool reload(ReloadTypes_t reloadType); | |
| 505 | + | ||
| 506 | + | Auras auras; | |
| 507 | + | Groups groups; | |
| 508 | + | Map map; | |
| 509 | + | Mounts mounts; | |
| 510 | + | Raids raids; | |
| 511 | + | RuneEffects runeEffects; | |
| 512 | + | Quests quests; | |
| 513 | + | Wings wings; | |
| 514 | + | Shaders shaders; | |
| 515 | + | ||
| 516 | + | std::forward_list<Item*> toDecayItems; | |
| 517 | + | ||
| 518 | + | std::unordered_set<Tile*> getTilesToClean() const { | |
| 519 | + | return tilesToClean; | |
| 520 | + | } | |
| 521 | + | void addTileToClean(Tile* tile) { | |
| 522 | + | tilesToClean.emplace(tile); | |
| 523 | + | } | |
| 524 | + | void removeTileToClean(Tile* tile) { | |
| 525 | + | tilesToClean.erase(tile); | |
| 526 | + | } | |
| 527 | + | void clearTilesToClean() { | |
| 528 | + | tilesToClean.clear(); | |
| 529 | + | } | |
| 530 | + | ||
| 531 | + | private: | |
| 532 | + | bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text); | |
| 533 | + | void playerWhisper(Player* player, const std::string& text); | |
| 534 | + | bool playerYell(Player* player, const std::string& text); | |
| 535 | + | bool playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, const std::string& text); | |
| 536 | + | void playerSpeakToNpc(Player* player, const std::string& text); | |
| 537 | + | ||
| 538 | + | void checkDecay(); | |
| 539 | + | void internalDecayItem(Item* item); | |
| 540 | + | ||
| 541 | + | std::unordered_map<uint32_t, Player*> players; | |
| 542 | + | std::unordered_map<std::string, Player*> mappedPlayerNames; | |
| 543 | + | std::unordered_map<uint32_t, Player*> mappedPlayerGuids; | |
| 544 | + | std::unordered_map<uint32_t, Guild*> guilds; | |
| 545 | + | std::unordered_map<uint16_t, Item*> uniqueItems; | |
| 546 | + | std::map<uint32_t, uint32_t> stages; | |
| 547 | + | ||
| 548 | + | std::list<Item*> decayItems[EVENT_DECAY_BUCKETS]; | |
| 549 | + | std::list<Creature*> checkCreatureLists[EVENT_CREATURECOUNT]; | |
| 550 | + | ||
| 551 | + | std::vector<Creature*> ToReleaseCreatures; | |
| 552 | + | std::vector<Item*> ToReleaseItems; | |
| 553 | + | ||
| 554 | + | size_t lastBucket = 0; | |
| 555 | + | ||
| 556 | + | WildcardTreeNode wildcardTree { false }; | |
| 557 | + | ||
| 558 | + | std::map<uint32_t, Npc*> npcs; | |
| 559 | + | std::map<uint32_t, Monster*> monsters; | |
| 560 | + | ||
| 561 | + | //list of items that are in trading state, mapped to the player | |
| 562 | + | std::map<Item*, uint32_t> tradeItems; | |
| 563 | + | ||
| 564 | + | std::map<uint32_t, BedItem*> bedSleepersMap; | |
| 565 | + | ||
| 566 | + | std::unordered_set<Tile*> tilesToClean; | |
| 567 | + | ||
| 568 | + | ModalWindow offlineTrainingWindow { std::numeric_limits<uint32_t>::max(), "Choose a Skill", "Please choose a skill:" }; | |
| 569 | + | ||
| 570 | + | static constexpr int32_t LIGHT_LEVEL_DAY = 250; | |
| 571 | + | static constexpr int32_t LIGHT_LEVEL_NIGHT = 40; | |
| 572 | + | static constexpr int32_t SUNSET = 1305; | |
| 573 | + | static constexpr int32_t SUNRISE = 430; | |
| 574 | + | ||
| 575 | + | GameState_t gameState = GAME_STATE_NORMAL; | |
| 576 | + | WorldType_t worldType = WORLD_TYPE_PVP; | |
| 577 | + | ||
| 578 | + | LightState_t lightState = LIGHT_STATE_DAY; | |
| 579 | + | uint8_t lightLevel = LIGHT_LEVEL_DAY; | |
| 580 | + | int32_t lightHour = SUNRISE + (SUNSET - SUNRISE) / 2; | |
| 581 | + | // (1440 minutes/day)/(3600 seconds/day)*10 seconds event interval | |
| 582 | + | int32_t lightHourDelta = 1400 * 10 / 3600; | |
| 583 | + | ||
| 584 | + | ServiceManager* serviceManager = nullptr; | |
| 585 | + | ||
| 586 | + | void updatePlayersRecord() const; | |
| 587 | + | uint32_t playersRecord = 0; | |
| 588 | + | ||
| 589 | + | std::string motdHash; | |
| 590 | + | uint32_t motdNum = 0; | |
| 591 | + | ||
| 592 | + | uint32_t lastStageLevel = 0; | |
| 593 | + | bool stagesEnabled = false; | |
| 594 | + | bool useLastStageLevel = false; | |
| 595 | + | }; | |
| 596 | + | ||
| 597 | + | #endif |
| @@ -0,0 +1,371 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #include "otpch.h" | |
| 21 | + | ||
| 22 | + | #include "configmanager.h" | |
| 23 | + | #include "globalevent.h" | |
| 24 | + | #include "tools.h" | |
| 25 | + | #include "scheduler.h" | |
| 26 | + | #include "pugicast.h" | |
| 27 | + | ||
| 28 | + | extern ConfigManager g_config; | |
| 29 | + | ||
| 30 | + | GlobalEvents::GlobalEvents() : | |
| 31 | + | scriptInterface("GlobalEvent Interface") | |
| 32 | + | { | |
| 33 | + | scriptInterface.initState(); | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | GlobalEvents::~GlobalEvents() | |
| 37 | + | { | |
| 38 | + | clear(false); | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | void GlobalEvents::clearMap(GlobalEventMap& map, bool fromLua) | |
| 42 | + | { | |
| 43 | + | for (auto it = map.begin(); it != map.end(); ) { | |
| 44 | + | if (fromLua == it->second.fromLua) { | |
| 45 | + | it = map.erase(it); | |
| 46 | + | } else { | |
| 47 | + | ++it; | |
| 48 | + | } | |
| 49 | + | } | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | void GlobalEvents::clear(bool fromLua) | |
| 53 | + | { | |
| 54 | + | g_scheduler.stopEvent(thinkEventId); | |
| 55 | + | thinkEventId = 0; | |
| 56 | + | g_scheduler.stopEvent(timerEventId); | |
| 57 | + | timerEventId = 0; | |
| 58 | + | ||
| 59 | + | clearMap(thinkMap, fromLua); | |
| 60 | + | clearMap(serverMap, fromLua); | |
| 61 | + | clearMap(timerMap, fromLua); | |
| 62 | + | ||
| 63 | + | reInitState(fromLua); | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | Event_ptr GlobalEvents::getEvent(const std::string& nodeName) | |
| 67 | + | { | |
| 68 | + | if (strcasecmp(nodeName.c_str(), "globalevent") != 0) { | |
| 69 | + | return nullptr; | |
| 70 | + | } | |
| 71 | + | return Event_ptr(new GlobalEvent(&scriptInterface)); | |
| 72 | + | } | |
| 73 | + | ||
| 74 | + | bool GlobalEvents::registerEvent(Event_ptr event, const pugi::xml_node&) | |
| 75 | + | { | |
| 76 | + | GlobalEvent_ptr globalEvent{static_cast<GlobalEvent*>(event.release())}; //event is guaranteed to be a GlobalEvent | |
| 77 | + | if (globalEvent->getEventType() == GLOBALEVENT_TIMER) { | |
| 78 | + | auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent)); | |
| 79 | + | if (result.second) { | |
| 80 | + | if (timerEventId == 0) { | |
| 81 | + | timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this))); | |
| 82 | + | } | |
| 83 | + | return true; | |
| 84 | + | } | |
| 85 | + | } else if (globalEvent->getEventType() != GLOBALEVENT_NONE) { | |
| 86 | + | auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent)); | |
| 87 | + | if (result.second) { | |
| 88 | + | return true; | |
| 89 | + | } | |
| 90 | + | } else { // think event | |
| 91 | + | auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent)); | |
| 92 | + | if (result.second) { | |
| 93 | + | if (thinkEventId == 0) { | |
| 94 | + | thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this))); | |
| 95 | + | } | |
| 96 | + | return true; | |
| 97 | + | } | |
| 98 | + | } | |
| 99 | + | ||
| 100 | + | std::cout << "[Warning - GlobalEvents::configureEvent] Duplicate registered globalevent with name: " << globalEvent->getName() << std::endl; | |
| 101 | + | return false; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | bool GlobalEvents::registerLuaEvent(GlobalEvent* event) | |
| 105 | + | { | |
| 106 | + | GlobalEvent_ptr globalEvent{ event }; | |
| 107 | + | if (globalEvent->getEventType() == GLOBALEVENT_TIMER) { | |
| 108 | + | auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent)); | |
| 109 | + | if (result.second) { | |
| 110 | + | if (timerEventId == 0) { | |
| 111 | + | timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this))); | |
| 112 | + | } | |
| 113 | + | return true; | |
| 114 | + | } | |
| 115 | + | } else if (globalEvent->getEventType() != GLOBALEVENT_NONE) { | |
| 116 | + | auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent)); | |
| 117 | + | if (result.second) { | |
| 118 | + | return true; | |
| 119 | + | } | |
| 120 | + | } else { // think event | |
| 121 | + | auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent)); | |
| 122 | + | if (result.second) { | |
| 123 | + | if (thinkEventId == 0) { | |
| 124 | + | thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this))); | |
| 125 | + | } | |
| 126 | + | return true; | |
| 127 | + | } | |
| 128 | + | } | |
| 129 | + | ||
| 130 | + | std::cout << "[Warning - GlobalEvents::configureEvent] Duplicate registered globalevent with name: " << globalEvent->getName() << std::endl; | |
| 131 | + | return false; | |
| 132 | + | } | |
| 133 | + | ||
| 134 | + | void GlobalEvents::startup() const | |
| 135 | + | { | |
| 136 | + | execute(GLOBALEVENT_STARTUP); | |
| 137 | + | } | |
| 138 | + | ||
| 139 | + | void GlobalEvents::timer() | |
| 140 | + | { | |
| 141 | + | time_t now = time(nullptr); | |
| 142 | + | ||
| 143 | + | int64_t nextScheduledTime = std::numeric_limits<int64_t>::max(); | |
| 144 | + | ||
| 145 | + | auto it = timerMap.begin(); | |
| 146 | + | while (it != timerMap.end()) { | |
| 147 | + | GlobalEvent& globalEvent = it->second; | |
| 148 | + | ||
| 149 | + | int64_t nextExecutionTime = globalEvent.getNextExecution() - now; | |
| 150 | + | if (nextExecutionTime > 0) { | |
| 151 | + | if (nextExecutionTime < nextScheduledTime) { | |
| 152 | + | nextScheduledTime = nextExecutionTime; | |
| 153 | + | } | |
| 154 | + | ||
| 155 | + | ++it; | |
| 156 | + | continue; | |
| 157 | + | } | |
| 158 | + | ||
| 159 | + | if (!globalEvent.executeEvent()) { | |
| 160 | + | it = timerMap.erase(it); | |
| 161 | + | continue; | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | nextExecutionTime = 86400; | |
| 165 | + | if (nextExecutionTime < nextScheduledTime) { | |
| 166 | + | nextScheduledTime = nextExecutionTime; | |
| 167 | + | } | |
| 168 | + | ||
| 169 | + | globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); | |
| 170 | + | ||
| 171 | + | ++it; | |
| 172 | + | } | |
| 173 | + | ||
| 174 | + | if (nextScheduledTime != std::numeric_limits<int64_t>::max()) { | |
| 175 | + | timerEventId = g_scheduler.addEvent(createSchedulerTask(std::max<int64_t>(1000, nextScheduledTime * 1000), | |
| 176 | + | std::bind(&GlobalEvents::timer, this))); | |
| 177 | + | } | |
| 178 | + | } | |
| 179 | + | ||
| 180 | + | void GlobalEvents::think() | |
| 181 | + | { | |
| 182 | + | int64_t now = OTSYS_TIME(); | |
| 183 | + | ||
| 184 | + | int64_t nextScheduledTime = std::numeric_limits<int64_t>::max(); | |
| 185 | + | for (auto& it : thinkMap) { | |
| 186 | + | GlobalEvent& globalEvent = it.second; | |
| 187 | + | ||
| 188 | + | int64_t nextExecutionTime = globalEvent.getNextExecution() - now; | |
| 189 | + | if (nextExecutionTime > 0) { | |
| 190 | + | if (nextExecutionTime < nextScheduledTime) { | |
| 191 | + | nextScheduledTime = nextExecutionTime; | |
| 192 | + | } | |
| 193 | + | continue; | |
| 194 | + | } | |
| 195 | + | ||
| 196 | + | if (!globalEvent.executeEvent()) { | |
| 197 | + | std::cout << "[Error - GlobalEvents::think] Failed to execute event: " << globalEvent.getName() << std::endl; | |
| 198 | + | } | |
| 199 | + | ||
| 200 | + | nextExecutionTime = globalEvent.getInterval(); | |
| 201 | + | if (nextExecutionTime < nextScheduledTime) { | |
| 202 | + | nextScheduledTime = nextExecutionTime; | |
| 203 | + | } | |
| 204 | + | ||
| 205 | + | globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); | |
| 206 | + | } | |
| 207 | + | ||
| 208 | + | if (nextScheduledTime != std::numeric_limits<int64_t>::max()) { | |
| 209 | + | thinkEventId = g_scheduler.addEvent(createSchedulerTask(nextScheduledTime, std::bind(&GlobalEvents::think, this))); | |
| 210 | + | } | |
| 211 | + | } | |
| 212 | + | ||
| 213 | + | void GlobalEvents::execute(GlobalEvent_t type) const | |
| 214 | + | { | |
| 215 | + | for (const auto& it : serverMap) { | |
| 216 | + | const GlobalEvent& globalEvent = it.second; | |
| 217 | + | if (globalEvent.getEventType() == type) { | |
| 218 | + | globalEvent.executeEvent(); | |
| 219 | + | } | |
| 220 | + | } | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) | |
| 224 | + | { | |
| 225 | + | // TODO: This should be better implemented. Maybe have a map for every type. | |
| 226 | + | switch (type) { | |
| 227 | + | case GLOBALEVENT_NONE: return thinkMap; | |
| 228 | + | case GLOBALEVENT_TIMER: return timerMap; | |
| 229 | + | case GLOBALEVENT_STARTUP: | |
| 230 | + | case GLOBALEVENT_SHUTDOWN: | |
| 231 | + | case GLOBALEVENT_RECORD: { | |
| 232 | + | GlobalEventMap retMap; | |
| 233 | + | for (const auto& it : serverMap) { | |
| 234 | + | if (it.second.getEventType() == type) { | |
| 235 | + | retMap.emplace(it.first, it.second); | |
| 236 | + | } | |
| 237 | + | } | |
| 238 | + | return retMap; | |
| 239 | + | } | |
| 240 | + | default: return GlobalEventMap(); | |
| 241 | + | } | |
| 242 | + | } | |
| 243 | + | ||
| 244 | + | GlobalEvent::GlobalEvent(LuaScriptInterface* interface) : Event(interface) {} | |
| 245 | + | ||
| 246 | + | bool GlobalEvent::configureEvent(const pugi::xml_node& node) | |
| 247 | + | { | |
| 248 | + | pugi::xml_attribute nameAttribute = node.attribute("name"); | |
| 249 | + | if (!nameAttribute) { | |
| 250 | + | std::cout << "[Error - GlobalEvent::configureEvent] Missing name for a globalevent" << std::endl; | |
| 251 | + | return false; | |
| 252 | + | } | |
| 253 | + | ||
| 254 | + | name = nameAttribute.as_string(); | |
| 255 | + | eventType = GLOBALEVENT_NONE; | |
| 256 | + | ||
| 257 | + | pugi::xml_attribute attr; | |
| 258 | + | if ((attr = node.attribute("time"))) { | |
| 259 | + | std::vector<int32_t> params = vectorAtoi(explodeString(attr.as_string(), ":")); | |
| 260 | + | ||
| 261 | + | int32_t hour = params.front(); | |
| 262 | + | if (hour < 0 || hour > 23) { | |
| 263 | + | std::cout << "[Error - GlobalEvent::configureEvent] Invalid hour \"" << attr.as_string() << "\" for globalevent with name: " << name << std::endl; | |
| 264 | + | return false; | |
| 265 | + | } | |
| 266 | + | ||
| 267 | + | interval |= hour << 16; | |
| 268 | + | ||
| 269 | + | int32_t min = 0; | |
| 270 | + | int32_t sec = 0; | |
| 271 | + | if (params.size() > 1) { | |
| 272 | + | min = params[1]; | |
| 273 | + | if (min < 0 || min > 59) { | |
| 274 | + | std::cout << "[Error - GlobalEvent::configureEvent] Invalid minute \"" << attr.as_string() << "\" for globalevent with name: " << name << std::endl; | |
| 275 | + | return false; | |
| 276 | + | } | |
| 277 | + | ||
| 278 | + | if (params.size() > 2) { | |
| 279 | + | sec = params[2]; | |
| 280 | + | if (sec < 0 || sec > 59) { | |
| 281 | + | std::cout << "[Error - GlobalEvent::configureEvent] Invalid second \"" << attr.as_string() << "\" for globalevent with name: " << name << std::endl; | |
| 282 | + | return false; | |
| 283 | + | } | |
| 284 | + | } | |
| 285 | + | } | |
| 286 | + | ||
| 287 | + | time_t current_time = time(nullptr); | |
| 288 | + | tm* timeinfo = localtime(¤t_time); | |
| 289 | + | timeinfo->tm_hour = hour; | |
| 290 | + | timeinfo->tm_min = min; | |
| 291 | + | timeinfo->tm_sec = sec; | |
| 292 | + | ||
| 293 | + | time_t difference = static_cast<time_t>(difftime(mktime(timeinfo), current_time)); | |
| 294 | + | if (difference < 0) { | |
| 295 | + | difference += 86400; | |
| 296 | + | } | |
| 297 | + | ||
| 298 | + | nextExecution = current_time + difference; | |
| 299 | + | eventType = GLOBALEVENT_TIMER; | |
| 300 | + | } else if ((attr = node.attribute("type"))) { | |
| 301 | + | const char* value = attr.value(); | |
| 302 | + | if (strcasecmp(value, "startup") == 0) { | |
| 303 | + | eventType = GLOBALEVENT_STARTUP; | |
| 304 | + | } else if (strcasecmp(value, "shutdown") == 0) { | |
| 305 | + | eventType = GLOBALEVENT_SHUTDOWN; | |
| 306 | + | } else if (strcasecmp(value, "record") == 0) { | |
| 307 | + | eventType = GLOBALEVENT_RECORD; | |
| 308 | + | } else { | |
| 309 | + | std::cout << "[Error - GlobalEvent::configureEvent] No valid type \"" << attr.as_string() << "\" for globalevent with name " << name << std::endl; | |
| 310 | + | return false; | |
| 311 | + | } | |
| 312 | + | } else if ((attr = node.attribute("interval"))) { | |
| 313 | + | interval = std::max<int32_t>(SCHEDULER_MINTICKS, pugi::cast<int32_t>(attr.value())); | |
| 314 | + | nextExecution = OTSYS_TIME() + interval; | |
| 315 | + | } else { | |
| 316 | + | std::cout << "[Error - GlobalEvent::configureEvent] No interval for globalevent with name " << name << std::endl; | |
| 317 | + | return false; | |
| 318 | + | } | |
| 319 | + | return true; | |
| 320 | + | } | |
| 321 | + | ||
| 322 | + | std::string GlobalEvent::getScriptEventName() const | |
| 323 | + | { | |
| 324 | + | switch (eventType) { | |
| 325 | + | case GLOBALEVENT_STARTUP: return "onStartup"; | |
| 326 | + | case GLOBALEVENT_SHUTDOWN: return "onShutdown"; | |
| 327 | + | case GLOBALEVENT_RECORD: return "onRecord"; | |
| 328 | + | case GLOBALEVENT_TIMER: return "onTime"; | |
| 329 | + | default: return "onThink"; | |
| 330 | + | } | |
| 331 | + | } | |
| 332 | + | ||
| 333 | + | bool GlobalEvent::executeRecord(uint32_t current, uint32_t old) | |
| 334 | + | { | |
| 335 | + | //onRecord(current, old) | |
| 336 | + | if (!scriptInterface->reserveScriptEnv()) { | |
| 337 | + | std::cout << "[Error - GlobalEvent::executeRecord] Call stack overflow" << std::endl; | |
| 338 | + | return false; | |
| 339 | + | } | |
| 340 | + | ||
| 341 | + | ScriptEnvironment* env = scriptInterface->getScriptEnv(); | |
| 342 | + | env->setScriptId(scriptId, scriptInterface); | |
| 343 | + | ||
| 344 | + | lua_State* L = scriptInterface->getLuaState(); | |
| 345 | + | scriptInterface->pushFunction(scriptId); | |
| 346 | + | ||
| 347 | + | lua_pushnumber(L, current); | |
| 348 | + | lua_pushnumber(L, old); | |
| 349 | + | return scriptInterface->callFunction(2); | |
| 350 | + | } | |
| 351 | + | ||
| 352 | + | bool GlobalEvent::executeEvent() const | |
| 353 | + | { | |
| 354 | + | if (!scriptInterface->reserveScriptEnv()) { | |
| 355 | + | std::cout << "[Error - GlobalEvent::executeEvent] Call stack overflow" << std::endl; | |
| 356 | + | return false; | |
| 357 | + | } | |
| 358 | + | ||
| 359 | + | ScriptEnvironment* env = scriptInterface->getScriptEnv(); | |
| 360 | + | env->setScriptId(scriptId, scriptInterface); | |
| 361 | + | lua_State* L = scriptInterface->getLuaState(); | |
| 362 | + | scriptInterface->pushFunction(scriptId); | |
| 363 | + | ||
| 364 | + | int32_t params = 0; | |
| 365 | + | if (eventType == GLOBALEVENT_NONE || eventType == GLOBALEVENT_TIMER) { | |
| 366 | + | lua_pushnumber(L, interval); | |
| 367 | + | params = 1; | |
| 368 | + | } | |
| 369 | + | ||
| 370 | + | return scriptInterface->callFunction(params); | |
| 371 | + | } |
| @@ -0,0 +1,126 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_GLOBALEVENT_H_B3FB9B848EA3474B9AFC326873947E3C | |
| 21 | + | #define FS_GLOBALEVENT_H_B3FB9B848EA3474B9AFC326873947E3C | |
| 22 | + | #include "baseevents.h" | |
| 23 | + | ||
| 24 | + | #include "const.h" | |
| 25 | + | ||
| 26 | + | enum GlobalEvent_t { | |
| 27 | + | GLOBALEVENT_NONE, | |
| 28 | + | GLOBALEVENT_TIMER, | |
| 29 | + | ||
| 30 | + | GLOBALEVENT_STARTUP, | |
| 31 | + | GLOBALEVENT_SHUTDOWN, | |
| 32 | + | GLOBALEVENT_RECORD, | |
| 33 | + | }; | |
| 34 | + | ||
| 35 | + | class GlobalEvent; | |
| 36 | + | using GlobalEvent_ptr = std::unique_ptr<GlobalEvent>; | |
| 37 | + | using GlobalEventMap = std::map<std::string, GlobalEvent>; | |
| 38 | + | ||
| 39 | + | class GlobalEvents final : public BaseEvents | |
| 40 | + | { | |
| 41 | + | public: | |
| 42 | + | GlobalEvents(); | |
| 43 | + | ~GlobalEvents(); | |
| 44 | + | ||
| 45 | + | // non-copyable | |
| 46 | + | GlobalEvents(const GlobalEvents&) = delete; | |
| 47 | + | GlobalEvents& operator=(const GlobalEvents&) = delete; | |
| 48 | + | ||
| 49 | + | void startup() const; | |
| 50 | + | ||
| 51 | + | void timer(); | |
| 52 | + | void think(); | |
| 53 | + | void execute(GlobalEvent_t type) const; | |
| 54 | + | ||
| 55 | + | GlobalEventMap getEventMap(GlobalEvent_t type); | |
| 56 | + | static void clearMap(GlobalEventMap& map, bool fromLua); | |
| 57 | + | ||
| 58 | + | bool registerLuaEvent(GlobalEvent* event); | |
| 59 | + | void clear(bool fromLua) override final; | |
| 60 | + | ||
| 61 | + | private: | |
| 62 | + | std::string getScriptBaseName() const override { | |
| 63 | + | return "globalevents"; | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | Event_ptr getEvent(const std::string& nodeName) override; | |
| 67 | + | bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; | |
| 68 | + | ||
| 69 | + | LuaScriptInterface& getScriptInterface() override { | |
| 70 | + | return scriptInterface; | |
| 71 | + | } | |
| 72 | + | LuaScriptInterface scriptInterface; | |
| 73 | + | ||
| 74 | + | GlobalEventMap thinkMap, serverMap, timerMap; | |
| 75 | + | int32_t thinkEventId = 0, timerEventId = 0; | |
| 76 | + | }; | |
| 77 | + | ||
| 78 | + | class GlobalEvent final : public Event | |
| 79 | + | { | |
| 80 | + | public: | |
| 81 | + | explicit GlobalEvent(LuaScriptInterface* interface); | |
| 82 | + | ||
| 83 | + | bool configureEvent(const pugi::xml_node& node) override; | |
| 84 | + | ||
| 85 | + | bool executeRecord(uint32_t current, uint32_t old); | |
| 86 | + | bool executeEvent() const; | |
| 87 | + | ||
| 88 | + | GlobalEvent_t getEventType() const { | |
| 89 | + | return eventType; | |
| 90 | + | } | |
| 91 | + | void setEventType(GlobalEvent_t type) { | |
| 92 | + | eventType = type; | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | const std::string& getName() const { | |
| 96 | + | return name; | |
| 97 | + | } | |
| 98 | + | void setName(std::string eventName) { | |
| 99 | + | name = eventName; | |
| 100 | + | } | |
| 101 | + | ||
| 102 | + | uint32_t getInterval() const { | |
| 103 | + | return interval; | |
| 104 | + | } | |
| 105 | + | void setInterval(uint32_t eventInterval) { | |
| 106 | + | interval |= eventInterval; | |
| 107 | + | } | |
| 108 | + | ||
| 109 | + | int64_t getNextExecution() const { | |
| 110 | + | return nextExecution; | |
| 111 | + | } | |
| 112 | + | void setNextExecution(int64_t time) { | |
| 113 | + | nextExecution = time; | |
| 114 | + | } | |
| 115 | + | ||
| 116 | + | private: | |
| 117 | + | GlobalEvent_t eventType = GLOBALEVENT_NONE; | |
| 118 | + | ||
| 119 | + | std::string getScriptEventName() const override; | |
| 120 | + | ||
| 121 | + | std::string name; | |
| 122 | + | int64_t nextExecution = 0; | |
| 123 | + | uint32_t interval = 0; | |
| 124 | + | }; | |
| 125 | + | ||
| 126 | + | #endif |
| @@ -0,0 +1,112 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #include "otpch.h" | |
| 21 | + | ||
| 22 | + | #include "groups.h" | |
| 23 | + | ||
| 24 | + | #include "pugicast.h" | |
| 25 | + | #include "tools.h" | |
| 26 | + | ||
| 27 | + | const std::unordered_map<std::string, PlayerFlags> ParsePlayerFlagMap = { | |
| 28 | + | {"cannotusecombat", PlayerFlag_CannotUseCombat}, | |
| 29 | + | {"cannotattackplayer", PlayerFlag_CannotAttackPlayer}, | |
| 30 | + | {"cannotattackmonster", PlayerFlag_CannotAttackMonster}, | |
| 31 | + | {"cannotbeattacked", PlayerFlag_CannotBeAttacked}, | |
| 32 | + | {"canconvinceall", PlayerFlag_CanConvinceAll}, | |
| 33 | + | {"cansummonall", PlayerFlag_CanSummonAll}, | |
| 34 | + | {"canillusionall", PlayerFlag_CanIllusionAll}, | |
| 35 | + | {"cansenseinvisibility", PlayerFlag_CanSenseInvisibility}, | |
| 36 | + | {"ignoredbymonsters", PlayerFlag_IgnoredByMonsters}, | |
| 37 | + | {"notgaininfight", PlayerFlag_NotGainInFight}, | |
| 38 | + | {"hasinfinitemana", PlayerFlag_HasInfiniteMana}, | |
| 39 | + | {"hasinfinitesoul", PlayerFlag_HasInfiniteSoul}, | |
| 40 | + | {"hasnoexhaustion", PlayerFlag_HasNoExhaustion}, | |
| 41 | + | {"cannotusespells", PlayerFlag_CannotUseSpells}, | |
| 42 | + | {"cannotpickupitem", PlayerFlag_CannotPickupItem}, | |
| 43 | + | {"canalwayslogin", PlayerFlag_CanAlwaysLogin}, | |
| 44 | + | {"canbroadcast", PlayerFlag_CanBroadcast}, | |
| 45 | + | {"canedithouses", PlayerFlag_CanEditHouses}, | |
| 46 | + | {"cannotbebanned", PlayerFlag_CannotBeBanned}, | |
| 47 | + | {"cannotbepushed", PlayerFlag_CannotBePushed}, | |
| 48 | + | {"hasinfinitecapacity", PlayerFlag_HasInfiniteCapacity}, | |
| 49 | + | {"cannotpushallcreatures", PlayerFlag_CanPushAllCreatures}, | |
| 50 | + | {"cantalkredprivate", PlayerFlag_CanTalkRedPrivate}, | |
| 51 | + | {"cantalkredchannel", PlayerFlag_CanTalkRedChannel}, | |
| 52 | + | {"talkorangehelpchannel", PlayerFlag_TalkOrangeHelpChannel}, | |
| 53 | + | {"notgainexperience", PlayerFlag_NotGainExperience}, | |
| 54 | + | {"notgainmana", PlayerFlag_NotGainMana}, | |
| 55 | + | {"notgainhealth", PlayerFlag_NotGainHealth}, | |
| 56 | + | {"notgainskill", PlayerFlag_NotGainSkill}, | |
| 57 | + | {"setmaxspeed", PlayerFlag_SetMaxSpeed}, | |
| 58 | + | {"specialvip", PlayerFlag_SpecialVIP}, | |
| 59 | + | {"notgenerateloot", PlayerFlag_NotGenerateLoot}, | |
| 60 | + | {"cantalkredchannelanonymous", PlayerFlag_CanTalkRedChannelAnonymous}, | |
| 61 | + | {"ignoreprotectionzone", PlayerFlag_IgnoreProtectionZone}, | |
| 62 | + | {"ignorespellcheck", PlayerFlag_IgnoreSpellCheck}, | |
| 63 | + | {"ignoreweaponcheck", PlayerFlag_IgnoreWeaponCheck}, | |
| 64 | + | {"cannotbemuted", PlayerFlag_CannotBeMuted}, | |
| 65 | + | {"isalwayspremium", PlayerFlag_IsAlwaysPremium} | |
| 66 | + | }; | |
| 67 | + | ||
| 68 | + | bool Groups::load() | |
| 69 | + | { | |
| 70 | + | pugi::xml_document doc; | |
| 71 | + | pugi::xml_parse_result result = doc.load_file("data/XML/groups.xml"); | |
| 72 | + | if (!result) { | |
| 73 | + | printXMLError("Error - Groups::load", "data/XML/groups.xml", result); | |
| 74 | + | return false; | |
| 75 | + | } | |
| 76 | + | ||
| 77 | + | for (auto groupNode : doc.child("groups").children()) { | |
| 78 | + | Group group; | |
| 79 | + | group.id = pugi::cast<uint32_t>(groupNode.attribute("id").value()); | |
| 80 | + | group.name = groupNode.attribute("name").as_string(); | |
| 81 | + | group.access = groupNode.attribute("access").as_bool(); | |
| 82 | + | group.maxDepotItems = pugi::cast<uint32_t>(groupNode.attribute("maxdepotitems").value()); | |
| 83 | + | group.maxVipEntries = pugi::cast<uint32_t>(groupNode.attribute("maxvipentries").value()); | |
| 84 | + | group.flags = pugi::cast<uint64_t>(groupNode.attribute("flags").value()); | |
| 85 | + | if (pugi::xml_node node = groupNode.child("flags")) { | |
| 86 | + | for (auto flagNode : node.children()) { | |
| 87 | + | pugi::xml_attribute attr = flagNode.first_attribute(); | |
| 88 | + | if (!attr || (attr && !attr.as_bool())) { | |
| 89 | + | continue; | |
| 90 | + | } | |
| 91 | + | ||
| 92 | + | auto parseFlag = ParsePlayerFlagMap.find(attr.name()); | |
| 93 | + | if (parseFlag != ParsePlayerFlagMap.end()) { | |
| 94 | + | group.flags |= parseFlag->second; | |
| 95 | + | } | |
| 96 | + | } | |
| 97 | + | } | |
| 98 | + | ||
| 99 | + | groups.push_back(group); | |
| 100 | + | } | |
| 101 | + | return true; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | Group* Groups::getGroup(uint16_t id) | |
| 105 | + | { | |
| 106 | + | for (Group& group : groups) { | |
| 107 | + | if (group.id == id) { | |
| 108 | + | return &group; | |
| 109 | + | } | |
| 110 | + | } | |
| 111 | + | return nullptr; | |
| 112 | + | } |
| @@ -0,0 +1,41 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_GROUPS_H_EE39438337D148E1983FB79D936DD8F3 | |
| 21 | + | #define FS_GROUPS_H_EE39438337D148E1983FB79D936DD8F3 | |
| 22 | + | ||
| 23 | + | struct Group { | |
| 24 | + | std::string name; | |
| 25 | + | uint64_t flags; | |
| 26 | + | uint32_t maxDepotItems; | |
| 27 | + | uint32_t maxVipEntries; | |
| 28 | + | uint16_t id; | |
| 29 | + | bool access; | |
| 30 | + | }; | |
| 31 | + | ||
| 32 | + | class Groups { | |
| 33 | + | public: | |
| 34 | + | bool load(); | |
| 35 | + | Group* getGroup(uint16_t id); | |
| 36 | + | ||
| 37 | + | private: | |
| 38 | + | std::vector<Group> groups; | |
| 39 | + | }; | |
| 40 | + | ||
| 41 | + | #endif |
| @@ -0,0 +1,83 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #include "otpch.h" | |
| 21 | + | ||
| 22 | + | #include "guild.h" | |
| 23 | + | ||
| 24 | + | #include "game.h" | |
| 25 | + | ||
| 26 | + | extern Game g_game; | |
| 27 | + | ||
| 28 | + | void Guild::addMember(Player* player) | |
| 29 | + | { | |
| 30 | + | membersOnline.push_back(player); | |
| 31 | + | for (Player* member : membersOnline) { | |
| 32 | + | g_game.updatePlayerHelpers(*member); | |
| 33 | + | } | |
| 34 | + | } | |
| 35 | + | ||
| 36 | + | void Guild::removeMember(Player* player) | |
| 37 | + | { | |
| 38 | + | membersOnline.remove(player); | |
| 39 | + | for (Player* member : membersOnline) { | |
| 40 | + | g_game.updatePlayerHelpers(*member); | |
| 41 | + | } | |
| 42 | + | g_game.updatePlayerHelpers(*player); | |
| 43 | + | ||
| 44 | + | if (membersOnline.empty()) { | |
| 45 | + | g_game.removeGuild(id); | |
| 46 | + | delete this; | |
| 47 | + | } | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | GuildRank_ptr Guild::getRankById(uint32_t rankId) | |
| 51 | + | { | |
| 52 | + | for (auto rank : ranks) { | |
| 53 | + | if (rank->id == rankId) { | |
| 54 | + | return rank; | |
| 55 | + | } | |
| 56 | + | } | |
| 57 | + | return nullptr; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | GuildRank_ptr Guild::getRankByName(const std::string& name) const | |
| 61 | + | { | |
| 62 | + | for (auto rank : ranks) { | |
| 63 | + | if (rank->name == name) { | |
| 64 | + | return rank; | |
| 65 | + | } | |
| 66 | + | } | |
| 67 | + | return nullptr; | |
| 68 | + | } | |
| 69 | + | ||
| 70 | + | GuildRank_ptr Guild::getRankByLevel(uint8_t level) const | |
| 71 | + | { | |
| 72 | + | for (auto rank : ranks) { | |
| 73 | + | if (rank->level == level) { | |
| 74 | + | return rank; | |
| 75 | + | } | |
| 76 | + | } | |
| 77 | + | return nullptr; | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | void Guild::addRank(uint32_t rankId, const std::string& rankName, uint8_t level) | |
| 81 | + | { | |
| 82 | + | ranks.emplace_back(std::make_shared<GuildRank>(rankId, rankName, level)); | |
| 83 | + | } |
| @@ -0,0 +1,84 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_GUILD_H_C00F0A1D732E4BA88FF62ACBE74D76BC | |
| 21 | + | #define FS_GUILD_H_C00F0A1D732E4BA88FF62ACBE74D76BC | |
| 22 | + | ||
| 23 | + | class Player; | |
| 24 | + | ||
| 25 | + | struct GuildRank { | |
| 26 | + | uint32_t id; | |
| 27 | + | std::string name; | |
| 28 | + | uint8_t level; | |
| 29 | + | ||
| 30 | + | GuildRank(uint32_t id, std::string name, uint8_t level) : | |
| 31 | + | id(id), name(std::move(name)), level(level) {} | |
| 32 | + | }; | |
| 33 | + | ||
| 34 | + | using GuildRank_ptr = std::shared_ptr<GuildRank>; | |
| 35 | + | ||
| 36 | + | class Guild | |
| 37 | + | { | |
| 38 | + | public: | |
| 39 | + | Guild(uint32_t id, std::string name) : name(std::move(name)), id(id) {} | |
| 40 | + | ||
| 41 | + | void addMember(Player* player); | |
| 42 | + | void removeMember(Player* player); | |
| 43 | + | ||
| 44 | + | uint32_t getId() const { | |
| 45 | + | return id; | |
| 46 | + | } | |
| 47 | + | const std::string& getName() const { | |
| 48 | + | return name; | |
| 49 | + | } | |
| 50 | + | const std::list<Player*>& getMembersOnline() const { | |
| 51 | + | return membersOnline; | |
| 52 | + | } | |
| 53 | + | uint32_t getMemberCount() const { | |
| 54 | + | return memberCount; | |
| 55 | + | } | |
| 56 | + | void setMemberCount(uint32_t count) { | |
| 57 | + | memberCount = count; | |
| 58 | + | } | |
| 59 | + | ||
| 60 | + | const std::vector<GuildRank_ptr>& getRanks() const { | |
| 61 | + | return ranks; | |
| 62 | + | } | |
| 63 | + | GuildRank_ptr getRankById(uint32_t rankId); | |
| 64 | + | GuildRank_ptr getRankByName(const std::string& name) const; | |
| 65 | + | GuildRank_ptr getRankByLevel(uint8_t level) const; | |
| 66 | + | void addRank(uint32_t rankId, const std::string& rankName, uint8_t level); | |
| 67 | + | ||
| 68 | + | const std::string& getMotd() const { | |
| 69 | + | return motd; | |
| 70 | + | } | |
| 71 | + | void setMotd(const std::string& motd) { | |
| 72 | + | this->motd = motd; | |
| 73 | + | } | |
| 74 | + | ||
| 75 | + | private: | |
| 76 | + | std::list<Player*> membersOnline; | |
| 77 | + | std::vector<GuildRank_ptr> ranks; | |
| 78 | + | std::string name; | |
| 79 | + | std::string motd; | |
| 80 | + | uint32_t id; | |
| 81 | + | uint32_t memberCount = 0; | |
| 82 | + | }; | |
| 83 | + | ||
| 84 | + | #endif |
| @@ -0,0 +1,754 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #include "otpch.h" | |
| 21 | + | ||
| 22 | + | #include "pugicast.h" | |
| 23 | + | ||
| 24 | + | #include "house.h" | |
| 25 | + | #include "iologindata.h" | |
| 26 | + | #include "game.h" | |
| 27 | + | #include "configmanager.h" | |
| 28 | + | #include "bed.h" | |
| 29 | + | ||
| 30 | + | extern ConfigManager g_config; | |
| 31 | + | extern Game g_game; | |
| 32 | + | ||
| 33 | + | House::House(uint32_t houseId) : id(houseId) {} | |
| 34 | + | ||
| 35 | + | void House::addTile(HouseTile* tile) | |
| 36 | + | { | |
| 37 | + | tile->setFlag(TILESTATE_PROTECTIONZONE); | |
| 38 | + | houseTiles.push_back(tile); | |
| 39 | + | } | |
| 40 | + | ||
| 41 | + | void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* player/* = nullptr*/) | |
| 42 | + | { | |
| 43 | + | if (updateDatabase && owner != guid) { | |
| 44 | + | Database& db = Database::getInstance(); | |
| 45 | + | ||
| 46 | + | std::ostringstream query; | |
| 47 | + | query << "UPDATE `houses` SET `owner` = " << guid << ", `bid` = 0, `bid_end` = 0, `last_bid` = 0, `highest_bidder` = 0 WHERE `id` = " << id; | |
| 48 | + | db.executeQuery(query.str()); | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | if (isLoaded && owner == guid) { | |
| 52 | + | return; | |
| 53 | + | } | |
| 54 | + | ||
| 55 | + | isLoaded = true; | |
| 56 | + | ||
| 57 | + | if (owner != 0) { | |
| 58 | + | //send items to depot | |
| 59 | + | if (player) { | |
| 60 | + | transferToDepot(player); | |
| 61 | + | } else { | |
| 62 | + | transferToDepot(); | |
| 63 | + | } | |
| 64 | + | ||
| 65 | + | for (HouseTile* tile : houseTiles) { | |
| 66 | + | if (const CreatureVector* creatures = tile->getCreatures()) { | |
| 67 | + | for (int32_t i = creatures->size(); --i >= 0;) { | |
| 68 | + | kickPlayer(nullptr, (*creatures)[i]->getPlayer()); | |
| 69 | + | } | |
| 70 | + | } | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | // Remove players from beds | |
| 74 | + | for (BedItem* bed : bedsList) { | |
| 75 | + | if (bed->getSleeper() != 0) { | |
| 76 | + | bed->wakeUp(nullptr); | |
| 77 | + | } | |
| 78 | + | } | |
| 79 | + | ||
| 80 | + | //clean access lists | |
| 81 | + | owner = 0; | |
| 82 | + | ownerAccountId = 0; | |
| 83 | + | setAccessList(SUBOWNER_LIST, ""); | |
| 84 | + | setAccessList(GUEST_LIST, ""); | |
| 85 | + | ||
| 86 | + | for (Door* door : doorSet) { | |
| 87 | + | door->setAccessList(""); | |
| 88 | + | } | |
| 89 | + | } else { | |
| 90 | + | std::string strRentPeriod = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD)); | |
| 91 | + | time_t currentTime = time(nullptr); | |
| 92 | + | if (strRentPeriod == "yearly") { | |
| 93 | + | currentTime += 24 * 60 * 60 * 365; | |
| 94 | + | } else if (strRentPeriod == "monthly") { | |
| 95 | + | currentTime += 24 * 60 * 60 * 30; | |
| 96 | + | } else if (strRentPeriod == "weekly") { | |
| 97 | + | currentTime += 24 * 60 * 60 * 7; | |
| 98 | + | } else if (strRentPeriod == "daily") { | |
| 99 | + | currentTime += 24 * 60 * 60; | |
| 100 | + | } else { | |
| 101 | + | currentTime = 0; | |
| 102 | + | } | |
| 103 | + | ||
| 104 | + | paidUntil = currentTime; | |
| 105 | + | } | |
| 106 | + | ||
| 107 | + | rentWarnings = 0; | |
| 108 | + | ||
| 109 | + | if (guid != 0) { | |
| 110 | + | std::string name = IOLoginData::getNameByGuid(guid); | |
| 111 | + | if (!name.empty()) { | |
| 112 | + | owner = guid; | |
| 113 | + | ownerName = name; | |
| 114 | + | ownerAccountId = IOLoginData::getAccountIdByPlayerName(name); | |
| 115 | + | } | |
| 116 | + | } | |
| 117 | + | ||
| 118 | + | updateDoorDescription(); | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | void House::updateDoorDescription() const | |
| 122 | + | { | |
| 123 | + | std::ostringstream ss; | |
| 124 | + | if (owner != 0) { | |
| 125 | + | ss << "It belongs to house '" << houseName << "'. " << ownerName << " owns this house."; | |
| 126 | + | } else { | |
| 127 | + | ss << "It belongs to house '" << houseName << "'. Nobody owns this house."; | |
| 128 | + | ||
| 129 | + | const int32_t housePrice = g_config.getNumber(ConfigManager::HOUSE_PRICE); | |
| 130 | + | if (housePrice != -1 && g_config.getBoolean(ConfigManager::HOUSE_DOOR_SHOW_PRICE)) { | |
| 131 | + | ss << " It costs " << (houseTiles.size() * housePrice) << " gold coins."; | |
| 132 | + | } | |
| 133 | + | } | |
| 134 | + | ||
| 135 | + | for (const auto& it : doorSet) { | |
| 136 | + | it->setSpecialDescription(ss.str()); | |
| 137 | + | } | |
| 138 | + | } | |
| 139 | + | ||
| 140 | + | AccessHouseLevel_t House::getHouseAccessLevel(const Player* player) | |
| 141 | + | { | |
| 142 | + | if (!player) { | |
| 143 | + | return HOUSE_OWNER; | |
| 144 | + | } | |
| 145 | + | ||
| 146 | + | if (g_config.getBoolean(ConfigManager::HOUSE_OWNED_BY_ACCOUNT)) { | |
| 147 | + | if (ownerAccountId == player->getAccount()) { | |
| 148 | + | return HOUSE_OWNER; | |
| 149 | + | } | |
| 150 | + | } | |
| 151 | + | ||
| 152 | + | if (player->hasFlag(PlayerFlag_CanEditHouses)) { | |
| 153 | + | return HOUSE_OWNER; | |
| 154 | + | } | |
| 155 | + | ||
| 156 | + | if (player->getGUID() == owner) { | |
| 157 | + | return HOUSE_OWNER; | |
| 158 | + | } | |
| 159 | + | ||
| 160 | + | if (subOwnerList.isInList(player)) { | |
| 161 | + | return HOUSE_SUBOWNER; | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | if (guestList.isInList(player)) { | |
| 165 | + | return HOUSE_GUEST; | |
| 166 | + | } | |
| 167 | + | ||
| 168 | + | return HOUSE_NOT_INVITED; | |
| 169 | + | } | |
| 170 | + | ||
| 171 | + | bool House::kickPlayer(Player* player, Player* target) | |
| 172 | + | { | |
| 173 | + | if (!target) { | |
| 174 | + | return false; | |
| 175 | + | } | |
| 176 | + | ||
| 177 | + | HouseTile* houseTile = dynamic_cast<HouseTile*>(target->getTile()); | |
| 178 | + | if (!houseTile || houseTile->getHouse() != this) { | |
| 179 | + | return false; | |
| 180 | + | } | |
| 181 | + | ||
| 182 | + | if (getHouseAccessLevel(player) < getHouseAccessLevel(target) || target->hasFlag(PlayerFlag_CanEditHouses)) { | |
| 183 | + | return false; | |
| 184 | + | } | |
| 185 | + | ||
| 186 | + | Position oldPosition = target->getPosition(); | |
| 187 | + | if (g_game.internalTeleport(target, getEntryPosition()) == RETURNVALUE_NOERROR) { | |
| 188 | + | g_game.addMagicEffect(oldPosition, CONST_ME_POFF); | |
| 189 | + | g_game.addMagicEffect(getEntryPosition(), CONST_ME_TELEPORT); | |
| 190 | + | } | |
| 191 | + | return true; | |
| 192 | + | } | |
| 193 | + | ||
| 194 | + | void House::setAccessList(uint32_t listId, const std::string& textlist) | |
| 195 | + | { | |
| 196 | + | if (listId == GUEST_LIST) { | |
| 197 | + | guestList.parseList(textlist); | |
| 198 | + | } else if (listId == SUBOWNER_LIST) { | |
| 199 | + | subOwnerList.parseList(textlist); | |
| 200 | + | } else { | |
| 201 | + | Door* door = getDoorByNumber(listId); | |
| 202 | + | if (door) { | |
| 203 | + | door->setAccessList(textlist); | |
| 204 | + | } | |
| 205 | + | ||
| 206 | + | // We dont have kick anyone | |
| 207 | + | return; | |
| 208 | + | } | |
| 209 | + | ||
| 210 | + | //kick uninvited players | |
| 211 | + | for (HouseTile* tile : houseTiles) { | |
| 212 | + | if (CreatureVector* creatures = tile->getCreatures()) { | |
| 213 | + | for (int32_t i = creatures->size(); --i >= 0;) { | |
| 214 | + | Player* player = (*creatures)[i]->getPlayer(); | |
| 215 | + | if (player && !isInvited(player)) { | |
| 216 | + | kickPlayer(nullptr, player); | |
| 217 | + | } | |
| 218 | + | } | |
| 219 | + | } | |
| 220 | + | } | |
| 221 | + | } | |
| 222 | + | ||
| 223 | + | bool House::transferToDepot() const | |
| 224 | + | { | |
| 225 | + | if (townId == 0 || owner == 0) { | |
| 226 | + | return false; | |
| 227 | + | } | |
| 228 | + | ||
| 229 | + | Player* player = g_game.getPlayerByGUID(owner); | |
| 230 | + | if (player) { | |
| 231 | + | transferToDepot(player); | |
| 232 | + | } else { | |
| 233 | + | Player tmpPlayer(nullptr); | |
| 234 | + | if (!IOLoginData::loadPlayerById(&tmpPlayer, owner)) { | |
| 235 | + | return false; | |
| 236 | + | } | |
| 237 | + | ||
| 238 | + | transferToDepot(&tmpPlayer); | |
| 239 | + | IOLoginData::savePlayer(&tmpPlayer); | |
| 240 | + | } | |
| 241 | + | return true; | |
| 242 | + | } | |
| 243 | + | ||
| 244 | + | bool House::transferToDepot(Player* player) const | |
| 245 | + | { | |
| 246 | + | if (townId == 0 || owner == 0) { | |
| 247 | + | return false; | |
| 248 | + | } | |
| 249 | + | ||
| 250 | + | ItemList moveItemList; | |
| 251 | + | for (HouseTile* tile : houseTiles) { | |
| 252 | + | if (const TileItemVector* items = tile->getItemList()) { | |
| 253 | + | for (Item* item : *items) { | |
| 254 | + | if (item->isPickupable()) { | |
| 255 | + | moveItemList.push_back(item); | |
| 256 | + | } else { | |
| 257 | + | Container* container = item->getContainer(); | |
| 258 | + | if (container) { | |
| 259 | + | for (Item* containerItem : container->getItemList()) { | |
| 260 | + | moveItemList.push_back(containerItem); | |
| 261 | + | } | |
| 262 | + | } | |
| 263 | + | } | |
| 264 | + | } | |
| 265 | + | } | |
| 266 | + | } | |
| 267 | + | ||
| 268 | + | for (Item* item : moveItemList) { | |
| 269 | + | g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT); | |
| 270 | + | } | |
| 271 | + | return true; | |
| 272 | + | } | |
| 273 | + | ||
| 274 | + | bool House::getAccessList(uint32_t listId, std::string& list) const | |
| 275 | + | { | |
| 276 | + | if (listId == GUEST_LIST) { | |
| 277 | + | guestList.getList(list); | |
| 278 | + | return true; | |
| 279 | + | } else if (listId == SUBOWNER_LIST) { | |
| 280 | + | subOwnerList.getList(list); | |
| 281 | + | return true; | |
| 282 | + | } | |
| 283 | + | ||
| 284 | + | Door* door = getDoorByNumber(listId); | |
| 285 | + | if (!door) { | |
| 286 | + | return false; | |
| 287 | + | } | |
| 288 | + | ||
| 289 | + | return door->getAccessList(list); | |
| 290 | + | } | |
| 291 | + | ||
| 292 | + | bool House::isInvited(const Player* player) | |
| 293 | + | { | |
| 294 | + | return getHouseAccessLevel(player) != HOUSE_NOT_INVITED; | |
| 295 | + | } | |
| 296 | + | ||
| 297 | + | void House::addDoor(Door* door) | |
| 298 | + | { | |
| 299 | + | door->incrementReferenceCounter(); | |
| 300 | + | doorSet.insert(door); | |
| 301 | + | door->setHouse(this); | |
| 302 | + | updateDoorDescription(); | |
| 303 | + | } | |
| 304 | + | ||
| 305 | + | void House::removeDoor(Door* door) | |
| 306 | + | { | |
| 307 | + | auto it = doorSet.find(door); | |
| 308 | + | if (it != doorSet.end()) { | |
| 309 | + | door->decrementReferenceCounter(); | |
| 310 | + | doorSet.erase(it); | |
| 311 | + | } | |
| 312 | + | } | |
| 313 | + | ||
| 314 | + | void House::addBed(BedItem* bed) | |
| 315 | + | { | |
| 316 | + | bedsList.push_back(bed); | |
| 317 | + | bed->setHouse(this); | |
| 318 | + | } | |
| 319 | + | ||
| 320 | + | Door* House::getDoorByNumber(uint32_t doorId) const | |
| 321 | + | { | |
| 322 | + | for (Door* door : doorSet) { | |
| 323 | + | if (door->getDoorId() == doorId) { | |
| 324 | + | return door; | |
| 325 | + | } | |
| 326 | + | } | |
| 327 | + | return nullptr; | |
| 328 | + | } | |
| 329 | + | ||
| 330 | + | Door* House::getDoorByPosition(const Position& pos) | |
| 331 | + | { | |
| 332 | + | for (Door* door : doorSet) { | |
| 333 | + | if (door->getPosition() == pos) { | |
| 334 | + | return door; | |
| 335 | + | } | |
| 336 | + | } | |
| 337 | + | return nullptr; | |
| 338 | + | } | |
| 339 | + | ||
| 340 | + | bool House::canEditAccessList(uint32_t listId, const Player* player) | |
| 341 | + | { | |
| 342 | + | switch (getHouseAccessLevel(player)) { | |
| 343 | + | case HOUSE_OWNER: | |
| 344 | + | return true; | |
| 345 | + | ||
| 346 | + | case HOUSE_SUBOWNER: | |
| 347 | + | return listId == GUEST_LIST; | |
| 348 | + | ||
| 349 | + | default: | |
| 350 | + | return false; | |
| 351 | + | } | |
| 352 | + | } | |
| 353 | + | ||
| 354 | + | HouseTransferItem* House::getTransferItem() | |
| 355 | + | { | |
| 356 | + | if (transferItem != nullptr) { | |
| 357 | + | return nullptr; | |
| 358 | + | } | |
| 359 | + | ||
| 360 | + | transfer_container.setParent(nullptr); | |
| 361 | + | transferItem = HouseTransferItem::createHouseTransferItem(this); | |
| 362 | + | transfer_container.addThing(transferItem); | |
| 363 | + | return transferItem; | |
| 364 | + | } | |
| 365 | + | ||
| 366 | + | void House::resetTransferItem() | |
| 367 | + | { | |
| 368 | + | if (transferItem) { | |
| 369 | + | Item* tmpItem = transferItem; | |
| 370 | + | transferItem = nullptr; | |
| 371 | + | transfer_container.setParent(nullptr); | |
| 372 | + | ||
| 373 | + | transfer_container.removeThing(tmpItem, tmpItem->getItemCount()); | |
| 374 | + | g_game.ReleaseItem(tmpItem); | |
| 375 | + | } | |
| 376 | + | } | |
| 377 | + | ||
| 378 | + | HouseTransferItem* HouseTransferItem::createHouseTransferItem(House* house) | |
| 379 | + | { | |
| 380 | + | HouseTransferItem* transferItem = new HouseTransferItem(house); | |
| 381 | + | transferItem->incrementReferenceCounter(); | |
| 382 | + | transferItem->setID(ITEM_DOCUMENT_RO); | |
| 383 | + | transferItem->setSubType(1); | |
| 384 | + | std::ostringstream ss; | |
| 385 | + | ss << "It is a house transfer document for '" << house->getName() << "'."; | |
| 386 | + | transferItem->setSpecialDescription(ss.str()); | |
| 387 | + | return transferItem; | |
| 388 | + | } | |
| 389 | + | ||
| 390 | + | void HouseTransferItem::onTradeEvent(TradeEvents_t event, Player* owner) | |
| 391 | + | { | |
| 392 | + | if (event == ON_TRADE_TRANSFER) { | |
| 393 | + | if (house) { | |
| 394 | + | house->executeTransfer(this, owner); | |
| 395 | + | } | |
| 396 | + | ||
| 397 | + | g_game.internalRemoveItem(this, 1); | |
| 398 | + | } else if (event == ON_TRADE_CANCEL) { | |
| 399 | + | if (house) { | |
| 400 | + | house->resetTransferItem(); | |
| 401 | + | } | |
| 402 | + | } | |
| 403 | + | } | |
| 404 | + | ||
| 405 | + | bool House::executeTransfer(HouseTransferItem* item, Player* newOwner) | |
| 406 | + | { | |
| 407 | + | if (transferItem != item) { | |
| 408 | + | return false; | |
| 409 | + | } | |
| 410 | + | ||
| 411 | + | setOwner(newOwner->getGUID()); | |
| 412 | + | transferItem = nullptr; | |
| 413 | + | return true; | |
| 414 | + | } | |
| 415 | + | ||
| 416 | + | void AccessList::parseList(const std::string& list) | |
| 417 | + | { | |
| 418 | + | playerList.clear(); | |
| 419 | + | guildRankList.clear(); | |
| 420 | + | allowEveryone = false; | |
| 421 | + | this->list = list; | |
| 422 | + | if (list.empty()) { | |
| 423 | + | return; | |
| 424 | + | } | |
| 425 | + | ||
| 426 | + | std::istringstream listStream(list); | |
| 427 | + | std::string line; | |
| 428 | + | ||
| 429 | + | int lineNo = 1; | |
| 430 | + | while (getline(listStream, line)) { | |
| 431 | + | if (++lineNo > 100) { | |
| 432 | + | break; | |
| 433 | + | } | |
| 434 | + | ||
| 435 | + | trimString(line); | |
| 436 | + | trim_left(line, '\t'); | |
| 437 | + | trim_right(line, '\t'); | |
| 438 | + | trimString(line); | |
| 439 | + | ||
| 440 | + | if (line.empty() || line.front() == '#' || line.length() > 100) { | |
| 441 | + | continue; | |
| 442 | + | } | |
| 443 | + | ||
| 444 | + | toLowerCaseString(line); | |
| 445 | + | ||
| 446 | + | std::string::size_type at_pos = line.find("@"); | |
| 447 | + | if (at_pos != std::string::npos) { | |
| 448 | + | if (at_pos == 0) { | |
| 449 | + | addGuild(line.substr(1)); | |
| 450 | + | } else { | |
| 451 | + | addGuildRank(line.substr(0, at_pos - 1), line.substr(at_pos + 1)); | |
| 452 | + | } | |
| 453 | + | } else if (line == "*") { | |
| 454 | + | allowEveryone = true; | |
| 455 | + | } else if (line.find("!") != std::string::npos || line.find("*") != std::string::npos || line.find("?") != std::string::npos) { | |
| 456 | + | continue; // regexp no longer supported | |
| 457 | + | } else { | |
| 458 | + | addPlayer(line); | |
| 459 | + | } | |
| 460 | + | } | |
| 461 | + | } | |
| 462 | + | ||
| 463 | + | void AccessList::addPlayer(const std::string& name) | |
| 464 | + | { | |
| 465 | + | Player* player = g_game.getPlayerByName(name); | |
| 466 | + | if (player) { | |
| 467 | + | playerList.insert(player->getGUID()); | |
| 468 | + | } else { | |
| 469 | + | uint32_t guid = IOLoginData::getGuidByName(name); | |
| 470 | + | if (guid != 0) { | |
| 471 | + | playerList.insert(guid); | |
| 472 | + | } | |
| 473 | + | } | |
| 474 | + | } | |
| 475 | + | ||
| 476 | + | namespace { | |
| 477 | + | ||
| 478 | + | const Guild* getGuildByName(const std::string& name) | |
| 479 | + | { | |
| 480 | + | uint32_t guildId = IOGuild::getGuildIdByName(name); | |
| 481 | + | if (guildId == 0) { | |
| 482 | + | return nullptr; | |
| 483 | + | } | |
| 484 | + | ||
| 485 | + | const Guild* guild = g_game.getGuild(guildId); | |
| 486 | + | if (guild) { | |
| 487 | + | return guild; | |
| 488 | + | } | |
| 489 | + | ||
| 490 | + | return IOGuild::loadGuild(guildId); | |
| 491 | + | } | |
| 492 | + | ||
| 493 | + | } | |
| 494 | + | ||
| 495 | + | void AccessList::addGuild(const std::string& name) | |
| 496 | + | { | |
| 497 | + | const Guild* guild = getGuildByName(name); | |
| 498 | + | if (guild) { | |
| 499 | + | for (auto rank : guild->getRanks()) { | |
| 500 | + | guildRankList.insert(rank->id); | |
| 501 | + | } | |
| 502 | + | } | |
| 503 | + | } | |
| 504 | + | ||
| 505 | + | void AccessList::addGuildRank(const std::string& name, const std::string& rankName) | |
| 506 | + | { | |
| 507 | + | const Guild* guild = getGuildByName(name); | |
| 508 | + | if (guild) { | |
| 509 | + | GuildRank_ptr rank = guild->getRankByName(rankName); | |
| 510 | + | if (rank) { | |
| 511 | + | guildRankList.insert(rank->id); | |
| 512 | + | } | |
| 513 | + | } | |
| 514 | + | } | |
| 515 | + | ||
| 516 | + | bool AccessList::isInList(const Player* player) | |
| 517 | + | { | |
| 518 | + | if (allowEveryone) { | |
| 519 | + | return true; | |
| 520 | + | } | |
| 521 | + | ||
| 522 | + | auto playerIt = playerList.find(player->getGUID()); | |
| 523 | + | if (playerIt != playerList.end()) { | |
| 524 | + | return true; | |
| 525 | + | } | |
| 526 | + | ||
| 527 | + | GuildRank_ptr rank = player->getGuildRank(); | |
| 528 | + | return rank && guildRankList.find(rank->id) != guildRankList.end(); | |
| 529 | + | } | |
| 530 | + | ||
| 531 | + | void AccessList::getList(std::string& list) const | |
| 532 | + | { | |
| 533 | + | list = this->list; | |
| 534 | + | } | |
| 535 | + | ||
| 536 | + | Door::Door(uint16_t type) : Item(type) {} | |
| 537 | + | ||
| 538 | + | Attr_ReadValue Door::readAttr(AttrTypes_t attr, PropStream& propStream) | |
| 539 | + | { | |
| 540 | + | if (attr == ATTR_HOUSEDOORID) { | |
| 541 | + | uint8_t doorId; | |
| 542 | + | if (!propStream.read<uint8_t>(doorId)) { | |
| 543 | + | return ATTR_READ_ERROR; | |
| 544 | + | } | |
| 545 | + | ||
| 546 | + | setDoorId(doorId); | |
| 547 | + | return ATTR_READ_CONTINUE; | |
| 548 | + | } | |
| 549 | + | return Item::readAttr(attr, propStream); | |
| 550 | + | } | |
| 551 | + | ||
| 552 | + | void Door::setHouse(House* house) | |
| 553 | + | { | |
| 554 | + | if (this->house != nullptr) { | |
| 555 | + | return; | |
| 556 | + | } | |
| 557 | + | ||
| 558 | + | this->house = house; | |
| 559 | + | ||
| 560 | + | if (!accessList) { | |
| 561 | + | accessList.reset(new AccessList()); | |
| 562 | + | } | |
| 563 | + | } | |
| 564 | + | ||
| 565 | + | bool Door::canUse(const Player* player) | |
| 566 | + | { | |
| 567 | + | if (!house) { | |
| 568 | + | return true; | |
| 569 | + | } | |
| 570 | + | ||
| 571 | + | if (house->getHouseAccessLevel(player) >= HOUSE_SUBOWNER) { | |
| 572 | + | return true; | |
| 573 | + | } | |
| 574 | + | ||
| 575 | + | return accessList->isInList(player); | |
| 576 | + | } | |
| 577 | + | ||
| 578 | + | void Door::setAccessList(const std::string& textlist) | |
| 579 | + | { | |
| 580 | + | if (!accessList) { | |
| 581 | + | accessList.reset(new AccessList()); | |
| 582 | + | } | |
| 583 | + | ||
| 584 | + | accessList->parseList(textlist); | |
| 585 | + | } | |
| 586 | + | ||
| 587 | + | bool Door::getAccessList(std::string& list) const | |
| 588 | + | { | |
| 589 | + | if (!house) { | |
| 590 | + | return false; | |
| 591 | + | } | |
| 592 | + | ||
| 593 | + | accessList->getList(list); | |
| 594 | + | return true; | |
| 595 | + | } | |
| 596 | + | ||
| 597 | + | void Door::onRemoved() | |
| 598 | + | { | |
| 599 | + | Item::onRemoved(); | |
| 600 | + | ||
| 601 | + | if (house) { | |
| 602 | + | house->removeDoor(this); | |
| 603 | + | } | |
| 604 | + | } | |
| 605 | + | ||
| 606 | + | House* Houses::getHouseByPlayerId(uint32_t playerId) | |
| 607 | + | { | |
| 608 | + | for (const auto& it : houseMap) { | |
| 609 | + | if (it.second->getOwner() == playerId) { | |
| 610 | + | return it.second; | |
| 611 | + | } | |
| 612 | + | } | |
| 613 | + | return nullptr; | |
| 614 | + | } | |
| 615 | + | ||
| 616 | + | bool Houses::loadHousesXML(const std::string& filename) | |
| 617 | + | { | |
| 618 | + | pugi::xml_document doc; | |
| 619 | + | pugi::xml_parse_result result = doc.load_file(filename.c_str()); | |
| 620 | + | if (!result) { | |
| 621 | + | printXMLError("Error - Houses::loadHousesXML", filename, result); | |
| 622 | + | return false; | |
| 623 | + | } | |
| 624 | + | ||
| 625 | + | for (auto houseNode : doc.child("houses").children()) { | |
| 626 | + | pugi::xml_attribute houseIdAttribute = houseNode.attribute("houseid"); | |
| 627 | + | if (!houseIdAttribute) { | |
| 628 | + | return false; | |
| 629 | + | } | |
| 630 | + | ||
| 631 | + | int32_t houseId = pugi::cast<int32_t>(houseIdAttribute.value()); | |
| 632 | + | ||
| 633 | + | House* house = getHouse(houseId); | |
| 634 | + | if (!house) { | |
| 635 | + | std::cout << "Error: [Houses::loadHousesXML] Unknown house, id = " << houseId << std::endl; | |
| 636 | + | return false; | |
| 637 | + | } | |
| 638 | + | ||
| 639 | + | house->setName(houseNode.attribute("name").as_string()); | |
| 640 | + | ||
| 641 | + | Position entryPos( | |
| 642 | + | pugi::cast<uint16_t>(houseNode.attribute("entryx").value()), | |
| 643 | + | pugi::cast<uint16_t>(houseNode.attribute("entryy").value()), | |
| 644 | + | pugi::cast<uint16_t>(houseNode.attribute("entryz").value()) | |
| 645 | + | ); | |
| 646 | + | if (entryPos.x == 0 && entryPos.y == 0 && entryPos.z == 0) { | |
| 647 | + | std::cout << "[Warning - Houses::loadHousesXML] House entry not set" | |
| 648 | + | << " - Name: " << house->getName() | |
| 649 | + | << " - House id: " << houseId << std::endl; | |
| 650 | + | } | |
| 651 | + | house->setEntryPos(entryPos); | |
| 652 | + | ||
| 653 | + | house->setRent(pugi::cast<uint32_t>(houseNode.attribute("rent").value())); | |
| 654 | + | house->setTownId(pugi::cast<uint32_t>(houseNode.attribute("townid").value())); | |
| 655 | + | ||
| 656 | + | house->setOwner(0, false); | |
| 657 | + | } | |
| 658 | + | return true; | |
| 659 | + | } | |
| 660 | + | ||
| 661 | + | void Houses::payHouses(RentPeriod_t rentPeriod) const | |
| 662 | + | { | |
| 663 | + | if (rentPeriod == RENTPERIOD_NEVER) { | |
| 664 | + | return; | |
| 665 | + | } | |
| 666 | + | ||
| 667 | + | time_t currentTime = time(nullptr); | |
| 668 | + | for (const auto& it : houseMap) { | |
| 669 | + | House* house = it.second; | |
| 670 | + | if (house->getOwner() == 0) { | |
| 671 | + | continue; | |
| 672 | + | } | |
| 673 | + | ||
| 674 | + | const uint32_t rent = house->getRent(); | |
| 675 | + | if (rent == 0 || house->getPaidUntil() > currentTime) { | |
| 676 | + | continue; | |
| 677 | + | } | |
| 678 | + | ||
| 679 | + | const uint32_t ownerId = house->getOwner(); | |
| 680 | + | Town* town = g_game.map.towns.getTown(house->getTownId()); | |
| 681 | + | if (!town) { | |
| 682 | + | continue; | |
| 683 | + | } | |
| 684 | + | ||
| 685 | + | Player player(nullptr); | |
| 686 | + | if (!IOLoginData::loadPlayerById(&player, ownerId)) { | |
| 687 | + | // Player doesn't exist, reset house owner | |
| 688 | + | house->setOwner(0); | |
| 689 | + | continue; | |
| 690 | + | } | |
| 691 | + | ||
| 692 | + | if (player.getBankBalance() >= rent) { | |
| 693 | + | player.setBankBalance(player.getBankBalance() - rent); | |
| 694 | + | ||
| 695 | + | time_t paidUntil = currentTime; | |
| 696 | + | switch (rentPeriod) { | |
| 697 | + | case RENTPERIOD_DAILY: | |
| 698 | + | paidUntil += 24 * 60 * 60; | |
| 699 | + | break; | |
| 700 | + | case RENTPERIOD_WEEKLY: | |
| 701 | + | paidUntil += 24 * 60 * 60 * 7; | |
| 702 | + | break; | |
| 703 | + | case RENTPERIOD_MONTHLY: | |
| 704 | + | paidUntil += 24 * 60 * 60 * 30; | |
| 705 | + | break; | |
| 706 | + | case RENTPERIOD_YEARLY: | |
| 707 | + | paidUntil += 24 * 60 * 60 * 365; | |
| 708 | + | break; | |
| 709 | + | default: | |
| 710 | + | break; | |
| 711 | + | } | |
| 712 | + | ||
| 713 | + | house->setPaidUntil(paidUntil); | |
| 714 | + | } else { | |
| 715 | + | if (house->getPayRentWarnings() < 7) { | |
| 716 | + | int32_t daysLeft = 7 - house->getPayRentWarnings(); | |
| 717 | + | ||
| 718 | + | Item* letter = Item::CreateItem(ITEM_LETTER_STAMPED); | |
| 719 | + | std::string period; | |
| 720 | + | ||
| 721 | + | switch (rentPeriod) { | |
| 722 | + | case RENTPERIOD_DAILY: | |
| 723 | + | period = "daily"; | |
| 724 | + | break; | |
| 725 | + | ||
| 726 | + | case RENTPERIOD_WEEKLY: | |
| 727 | + | period = "weekly"; | |
| 728 | + | break; | |
| 729 | + | ||
| 730 | + | case RENTPERIOD_MONTHLY: | |
| 731 | + | period = "monthly"; | |
| 732 | + | break; | |
| 733 | + | ||
| 734 | + | case RENTPERIOD_YEARLY: | |
| 735 | + | period = "annual"; | |
| 736 | + | break; | |
| 737 | + | ||
| 738 | + | default: | |
| 739 | + | break; | |
| 740 | + | } | |
| 741 | + | ||
| 742 | + | std::ostringstream ss; | |
| 743 | + | ss << "Warning! \nThe " << period << " rent of " << house->getRent() << " gold for your house \"" << house->getName() << "\" is payable. Have it within " << daysLeft << " days or you will lose this house."; | |
| 744 | + | letter->setText(ss.str()); | |
| 745 | + | g_game.internalAddItem(player.getInbox(), letter, INDEX_WHEREEVER, FLAG_NOLIMIT); | |
| 746 | + | house->setPayRentWarnings(house->getPayRentWarnings() + 1); | |
| 747 | + | } else { | |
| 748 | + | house->setOwner(0, true, &player); | |
| 749 | + | } | |
| 750 | + | } | |
| 751 | + | ||
| 752 | + | IOLoginData::savePlayer(&player); | |
| 753 | + | } | |
| 754 | + | } |
| @@ -0,0 +1,315 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_HOUSE_H_EB9732E7771A438F9CD0EFA8CB4C58C4 | |
| 21 | + | #define FS_HOUSE_H_EB9732E7771A438F9CD0EFA8CB4C58C4 | |
| 22 | + | ||
| 23 | + | #include <set> | |
| 24 | + | #include <unordered_set> | |
| 25 | + | ||
| 26 | + | #include "container.h" | |
| 27 | + | #include "housetile.h" | |
| 28 | + | #include "position.h" | |
| 29 | + | ||
| 30 | + | class House; | |
| 31 | + | class BedItem; | |
| 32 | + | class Player; | |
| 33 | + | ||
| 34 | + | class AccessList | |
| 35 | + | { | |
| 36 | + | public: | |
| 37 | + | void parseList(const std::string& list); | |
| 38 | + | void addPlayer(const std::string& name); | |
| 39 | + | void addGuild(const std::string& name); | |
| 40 | + | void addGuildRank(const std::string& name, const std::string& rankName); | |
| 41 | + | ||
| 42 | + | bool isInList(const Player* player); | |
| 43 | + | ||
| 44 | + | void getList(std::string& list) const; | |
| 45 | + | ||
| 46 | + | private: | |
| 47 | + | std::string list; | |
| 48 | + | std::unordered_set<uint32_t> playerList; | |
| 49 | + | std::unordered_set<uint32_t> guildRankList; | |
| 50 | + | bool allowEveryone = false; | |
| 51 | + | }; | |
| 52 | + | ||
| 53 | + | class Door final : public Item | |
| 54 | + | { | |
| 55 | + | public: | |
| 56 | + | explicit Door(uint16_t type); | |
| 57 | + | ||
| 58 | + | // non-copyable | |
| 59 | + | Door(const Door&) = delete; | |
| 60 | + | Door& operator=(const Door&) = delete; | |
| 61 | + | ||
| 62 | + | Door* getDoor() override { | |
| 63 | + | return this; | |
| 64 | + | } | |
| 65 | + | const Door* getDoor() const override { | |
| 66 | + | return this; | |
| 67 | + | } | |
| 68 | + | ||
| 69 | + | House* getHouse() { | |
| 70 | + | return house; | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | //serialization | |
| 74 | + | Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; | |
| 75 | + | void serializeAttr(PropWriteStream&) const override {} | |
| 76 | + | ||
| 77 | + | void setDoorId(uint32_t doorId) { | |
| 78 | + | setIntAttr(ITEM_ATTRIBUTE_DOORID, doorId); | |
| 79 | + | } | |
| 80 | + | uint32_t getDoorId() const { | |
| 81 | + | return getIntAttr(ITEM_ATTRIBUTE_DOORID); | |
| 82 | + | } | |
| 83 | + | ||
| 84 | + | bool canUse(const Player* player); | |
| 85 | + | ||
| 86 | + | void setAccessList(const std::string& textlist); | |
| 87 | + | bool getAccessList(std::string& list) const; | |
| 88 | + | ||
| 89 | + | void onRemoved() override; | |
| 90 | + | ||
| 91 | + | private: | |
| 92 | + | void setHouse(House* house); | |
| 93 | + | ||
| 94 | + | House* house = nullptr; | |
| 95 | + | std::unique_ptr<AccessList> accessList; | |
| 96 | + | friend class House; | |
| 97 | + | }; | |
| 98 | + | ||
| 99 | + | enum AccessList_t { | |
| 100 | + | GUEST_LIST = 0x100, | |
| 101 | + | SUBOWNER_LIST = 0x101, | |
| 102 | + | }; | |
| 103 | + | ||
| 104 | + | enum AccessHouseLevel_t { | |
| 105 | + | HOUSE_NOT_INVITED = 0, | |
| 106 | + | HOUSE_GUEST = 1, | |
| 107 | + | HOUSE_SUBOWNER = 2, | |
| 108 | + | HOUSE_OWNER = 3, | |
| 109 | + | }; | |
| 110 | + | ||
| 111 | + | using HouseTileList = std::list<HouseTile*>; | |
| 112 | + | using HouseBedItemList = std::list<BedItem*>; | |
| 113 | + | ||
| 114 | + | class HouseTransferItem final : public Item | |
| 115 | + | { | |
| 116 | + | public: | |
| 117 | + | static HouseTransferItem* createHouseTransferItem(House* house); | |
| 118 | + | ||
| 119 | + | explicit HouseTransferItem(House* house) : Item(0), house(house) {} | |
| 120 | + | ||
| 121 | + | void onTradeEvent(TradeEvents_t event, Player* owner) override; | |
| 122 | + | bool canTransform() const override { | |
| 123 | + | return false; | |
| 124 | + | } | |
| 125 | + | ||
| 126 | + | private: | |
| 127 | + | House* house; | |
| 128 | + | }; | |
| 129 | + | ||
| 130 | + | class House | |
| 131 | + | { | |
| 132 | + | public: | |
| 133 | + | explicit House(uint32_t houseId); | |
| 134 | + | ||
| 135 | + | void addTile(HouseTile* tile); | |
| 136 | + | void updateDoorDescription() const; | |
| 137 | + | ||
| 138 | + | bool canEditAccessList(uint32_t listId, const Player* player); | |
| 139 | + | // listId special values: | |
| 140 | + | // GUEST_LIST guest list | |
| 141 | + | // SUBOWNER_LIST subowner list | |
| 142 | + | void setAccessList(uint32_t listId, const std::string& textlist); | |
| 143 | + | bool getAccessList(uint32_t listId, std::string& list) const; | |
| 144 | + | ||
| 145 | + | bool isInvited(const Player* player); | |
| 146 | + | ||
| 147 | + | AccessHouseLevel_t getHouseAccessLevel(const Player* player); | |
| 148 | + | bool kickPlayer(Player* player, Player* target); | |
| 149 | + | ||
| 150 | + | void setEntryPos(Position pos) { | |
| 151 | + | posEntry = pos; | |
| 152 | + | } | |
| 153 | + | const Position& getEntryPosition() const { | |
| 154 | + | return posEntry; | |
| 155 | + | } | |
| 156 | + | ||
| 157 | + | void setName(std::string houseName) { | |
| 158 | + | this->houseName = houseName; | |
| 159 | + | } | |
| 160 | + | const std::string& getName() const { | |
| 161 | + | return houseName; | |
| 162 | + | } | |
| 163 | + | ||
| 164 | + | void setOwner(uint32_t guid, bool updateDatabase = true, Player* player = nullptr); | |
| 165 | + | uint32_t getOwner() const { | |
| 166 | + | return owner; | |
| 167 | + | } | |
| 168 | + | ||
| 169 | + | void setPaidUntil(time_t paid) { | |
| 170 | + | paidUntil = paid; | |
| 171 | + | } | |
| 172 | + | time_t getPaidUntil() const { | |
| 173 | + | return paidUntil; | |
| 174 | + | } | |
| 175 | + | ||
| 176 | + | void setRent(uint32_t rent) { | |
| 177 | + | this->rent = rent; | |
| 178 | + | } | |
| 179 | + | uint32_t getRent() const { | |
| 180 | + | return rent; | |
| 181 | + | } | |
| 182 | + | ||
| 183 | + | void setPayRentWarnings(uint32_t warnings) { | |
| 184 | + | rentWarnings = warnings; | |
| 185 | + | } | |
| 186 | + | uint32_t getPayRentWarnings() const { | |
| 187 | + | return rentWarnings; | |
| 188 | + | } | |
| 189 | + | ||
| 190 | + | void setTownId(uint32_t townId) { | |
| 191 | + | this->townId = townId; | |
| 192 | + | } | |
| 193 | + | uint32_t getTownId() const { | |
| 194 | + | return townId; | |
| 195 | + | } | |
| 196 | + | ||
| 197 | + | uint32_t getId() const { | |
| 198 | + | return id; | |
| 199 | + | } | |
| 200 | + | ||
| 201 | + | void addDoor(Door* door); | |
| 202 | + | void removeDoor(Door* door); | |
| 203 | + | Door* getDoorByNumber(uint32_t doorId) const; | |
| 204 | + | Door* getDoorByPosition(const Position& pos); | |
| 205 | + | ||
| 206 | + | HouseTransferItem* getTransferItem(); | |
| 207 | + | void resetTransferItem(); | |
| 208 | + | bool executeTransfer(HouseTransferItem* item, Player* newOwner); | |
| 209 | + | ||
| 210 | + | const HouseTileList& getTiles() const { | |
| 211 | + | return houseTiles; | |
| 212 | + | } | |
| 213 | + | ||
| 214 | + | const std::set<Door*>& getDoors() const { | |
| 215 | + | return doorSet; | |
| 216 | + | } | |
| 217 | + | ||
| 218 | + | void addBed(BedItem* bed); | |
| 219 | + | const HouseBedItemList& getBeds() const { | |
| 220 | + | return bedsList; | |
| 221 | + | } | |
| 222 | + | uint32_t getBedCount() { | |
| 223 | + | return static_cast<uint32_t>(std::ceil(bedsList.size() / 2.)); //each bed takes 2 sqms of space, ceil is just for bad maps | |
| 224 | + | } | |
| 225 | + | ||
| 226 | + | private: | |
| 227 | + | bool transferToDepot() const; | |
| 228 | + | bool transferToDepot(Player* player) const; | |
| 229 | + | ||
| 230 | + | AccessList guestList; | |
| 231 | + | AccessList subOwnerList; | |
| 232 | + | ||
| 233 | + | Container transfer_container{ITEM_LOCKER1}; | |
| 234 | + | ||
| 235 | + | HouseTileList houseTiles; | |
| 236 | + | std::set<Door*> doorSet; | |
| 237 | + | HouseBedItemList bedsList; | |
| 238 | + | ||
| 239 | + | std::string houseName; | |
| 240 | + | std::string ownerName; | |
| 241 | + | ||
| 242 | + | HouseTransferItem* transferItem = nullptr; | |
| 243 | + | ||
| 244 | + | time_t paidUntil = 0; | |
| 245 | + | ||
| 246 | + | uint32_t id; | |
| 247 | + | uint32_t owner = 0; | |
| 248 | + | uint32_t ownerAccountId = 0; | |
| 249 | + | uint32_t rentWarnings = 0; | |
| 250 | + | uint32_t rent = 0; | |
| 251 | + | uint32_t townId = 0; | |
| 252 | + | ||
| 253 | + | Position posEntry = {}; | |
| 254 | + | ||
| 255 | + | bool isLoaded = false; | |
| 256 | + | }; | |
| 257 | + | ||
| 258 | + | using HouseMap = std::map<uint32_t, House*>; | |
| 259 | + | ||
| 260 | + | enum RentPeriod_t { | |
| 261 | + | RENTPERIOD_DAILY, | |
| 262 | + | RENTPERIOD_WEEKLY, | |
| 263 | + | RENTPERIOD_MONTHLY, | |
| 264 | + | RENTPERIOD_YEARLY, | |
| 265 | + | RENTPERIOD_NEVER, | |
| 266 | + | }; | |
| 267 | + | ||
| 268 | + | class Houses | |
| 269 | + | { | |
| 270 | + | public: | |
| 271 | + | Houses() = default; | |
| 272 | + | ~Houses() { | |
| 273 | + | for (const auto& it : houseMap) { | |
| 274 | + | delete it.second; | |
| 275 | + | } | |
| 276 | + | } | |
| 277 | + | ||
| 278 | + | // non-copyable | |
| 279 | + | Houses(const Houses&) = delete; | |
| 280 | + | Houses& operator=(const Houses&) = delete; | |
| 281 | + | ||
| 282 | + | House* addHouse(uint32_t id) { | |
| 283 | + | auto it = houseMap.find(id); | |
| 284 | + | if (it != houseMap.end()) { | |
| 285 | + | return it->second; | |
| 286 | + | } | |
| 287 | + | ||
| 288 | + | House* house = new House(id); | |
| 289 | + | houseMap[id] = house; | |
| 290 | + | return house; | |
| 291 | + | } | |
| 292 | + | ||
| 293 | + | House* getHouse(uint32_t houseId) { | |
| 294 | + | auto it = houseMap.find(houseId); | |
| 295 | + | if (it == houseMap.end()) { | |
| 296 | + | return nullptr; | |
| 297 | + | } | |
| 298 | + | return it->second; | |
| 299 | + | } | |
| 300 | + | ||
| 301 | + | House* getHouseByPlayerId(uint32_t playerId); | |
| 302 | + | ||
| 303 | + | bool loadHousesXML(const std::string& filename); | |
| 304 | + | ||
| 305 | + | void payHouses(RentPeriod_t rentPeriod) const; | |
| 306 | + | ||
| 307 | + | const HouseMap& getHouses() const { | |
| 308 | + | return houseMap; | |
| 309 | + | } | |
| 310 | + | ||
| 311 | + | private: | |
| 312 | + | HouseMap houseMap; | |
| 313 | + | }; | |
| 314 | + | ||
| 315 | + | #endif |
| @@ -0,0 +1,122 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #include "otpch.h" | |
| 21 | + | ||
| 22 | + | #include "housetile.h" | |
| 23 | + | #include "house.h" | |
| 24 | + | #include "game.h" | |
| 25 | + | ||
| 26 | + | extern Game g_game; | |
| 27 | + | ||
| 28 | + | HouseTile::HouseTile(int32_t x, int32_t y, int32_t z, House* house) : | |
| 29 | + | DynamicTile(x, y, z), house(house) {} | |
| 30 | + | ||
| 31 | + | void HouseTile::addThing(int32_t index, Thing* thing) | |
| 32 | + | { | |
| 33 | + | Tile::addThing(index, thing); | |
| 34 | + | ||
| 35 | + | if (!thing->getParent()) { | |
| 36 | + | return; | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | if (Item* item = thing->getItem()) { | |
| 40 | + | updateHouse(item); | |
| 41 | + | } | |
| 42 | + | } | |
| 43 | + | ||
| 44 | + | void HouseTile::internalAddThing(uint32_t index, Thing* thing) | |
| 45 | + | { | |
| 46 | + | Tile::internalAddThing(index, thing); | |
| 47 | + | ||
| 48 | + | if (!thing->getParent()) { | |
| 49 | + | return; | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | if (Item* item = thing->getItem()) { | |
| 53 | + | updateHouse(item); | |
| 54 | + | } | |
| 55 | + | } | |
| 56 | + | ||
| 57 | + | void HouseTile::updateHouse(Item* item) | |
| 58 | + | { | |
| 59 | + | if (item->getParent() != this) { | |
| 60 | + | return; | |
| 61 | + | } | |
| 62 | + | ||
| 63 | + | Door* door = item->getDoor(); | |
| 64 | + | if (door) { | |
| 65 | + | if (door->getDoorId() != 0) { | |
| 66 | + | house->addDoor(door); | |
| 67 | + | } | |
| 68 | + | } else { | |
| 69 | + | BedItem* bed = item->getBed(); | |
| 70 | + | if (bed) { | |
| 71 | + | house->addBed(bed); | |
| 72 | + | } | |
| 73 | + | } | |
| 74 | + | } | |
| 75 | + | ||
| 76 | + | ReturnValue HouseTile::queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor/* = nullptr*/) const | |
| 77 | + | { | |
| 78 | + | if (const Creature* creature = thing.getCreature()) { | |
| 79 | + | if (const Player* player = creature->getPlayer()) { | |
| 80 | + | if (!house->isInvited(player)) { | |
| 81 | + | return RETURNVALUE_PLAYERISNOTINVITED; | |
| 82 | + | } | |
| 83 | + | } else { | |
| 84 | + | return RETURNVALUE_NOTPOSSIBLE; | |
| 85 | + | } | |
| 86 | + | } else if (thing.getItem() && actor) { | |
| 87 | + | Player* actorPlayer = actor->getPlayer(); | |
| 88 | + | if (!house->isInvited(actorPlayer)) { | |
| 89 | + | return RETURNVALUE_CANNOTTHROW; | |
| 90 | + | } | |
| 91 | + | } | |
| 92 | + | return Tile::queryAdd(index, thing, count, flags, actor); | |
| 93 | + | } | |
| 94 | + | ||
| 95 | + | Tile* HouseTile::queryDestination(int32_t& index, const Thing& thing, Item** destItem, uint32_t& flags) | |
| 96 | + | { | |
| 97 | + | if (const Creature* creature = thing.getCreature()) { | |
| 98 | + | if (const Player* player = creature->getPlayer()) { | |
| 99 | + | if (!house->isInvited(player)) { | |
| 100 | + | const Position& entryPos = house->getEntryPosition(); | |
| 101 | + | Tile* destTile = g_game.map.getTile(entryPos); | |
| 102 | + | if (!destTile) { | |
| 103 | + | std::cout << "Error: [HouseTile::queryDestination] House entry not correct" | |
| 104 | + | << " - Name: " << house->getName() | |
| 105 | + | << " - House id: " << house->getId() | |
| 106 | + | << " - Tile not found: " << entryPos << std::endl; | |
| 107 | + | ||
| 108 | + | destTile = g_game.map.getTile(player->getTemplePosition()); | |
| 109 | + | if (!destTile) { | |
| 110 | + | destTile = &(Tile::nullptr_tile); | |
| 111 | + | } | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | index = -1; | |
| 115 | + | *destItem = nullptr; | |
| 116 | + | return destTile; | |
| 117 | + | } | |
| 118 | + | } | |
| 119 | + | } | |
| 120 | + | ||
| 121 | + | return Tile::queryDestination(index, thing, destItem, flags); | |
| 122 | + | } |
| @@ -0,0 +1,52 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_HOUSETILE_H_57D59BEC1CE741D9B142BFC54634505B | |
| 21 | + | #define FS_HOUSETILE_H_57D59BEC1CE741D9B142BFC54634505B | |
| 22 | + | ||
| 23 | + | #include "tile.h" | |
| 24 | + | ||
| 25 | + | class House; | |
| 26 | + | ||
| 27 | + | class HouseTile final : public DynamicTile | |
| 28 | + | { | |
| 29 | + | public: | |
| 30 | + | HouseTile(int32_t x, int32_t y, int32_t z, House* house); | |
| 31 | + | ||
| 32 | + | //cylinder implementations | |
| 33 | + | ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, | |
| 34 | + | uint32_t flags, Creature* actor = nullptr) const override; | |
| 35 | + | ||
| 36 | + | Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem, | |
| 37 | + | uint32_t& flags) override; | |
| 38 | + | ||
| 39 | + | void addThing(int32_t index, Thing* thing) override; | |
| 40 | + | void internalAddThing(uint32_t index, Thing* thing) override; | |
| 41 | + | ||
| 42 | + | House* getHouse() { | |
| 43 | + | return house; | |
| 44 | + | } | |
| 45 | + | ||
| 46 | + | private: | |
| 47 | + | void updateHouse(Item* item); | |
| 48 | + | ||
| 49 | + | House* house; | |
| 50 | + | }; | |
| 51 | + | ||
| 52 | + | #endif |
| @@ -0,0 +1,72 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #include "otpch.h" | |
| 21 | + | ||
| 22 | + | #include "inbox.h" | |
| 23 | + | #include "tools.h" | |
| 24 | + | ||
| 25 | + | Inbox::Inbox(uint16_t type) : Container(type, 30, false, true) {} | |
| 26 | + | ||
| 27 | + | ReturnValue Inbox::queryAdd(int32_t, const Thing& thing, uint32_t, | |
| 28 | + | uint32_t flags, Creature*) const | |
| 29 | + | { | |
| 30 | + | if (!hasBitSet(FLAG_NOLIMIT, flags)) { | |
| 31 | + | return RETURNVALUE_CONTAINERNOTENOUGHROOM; | |
| 32 | + | } | |
| 33 | + | ||
| 34 | + | const Item* item = thing.getItem(); | |
| 35 | + | if (!item) { | |
| 36 | + | return RETURNVALUE_NOTPOSSIBLE; | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | if (item == this) { | |
| 40 | + | return RETURNVALUE_THISISIMPOSSIBLE; | |
| 41 | + | } | |
| 42 | + | ||
| 43 | + | if (!item->isPickupable()) { | |
| 44 | + | return RETURNVALUE_CANNOTPICKUP; | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | return RETURNVALUE_NOERROR; | |
| 48 | + | } | |
| 49 | + | ||
| 50 | + | void Inbox::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t) | |
| 51 | + | { | |
| 52 | + | Cylinder* parent = getParent(); | |
| 53 | + | if (parent != nullptr) { | |
| 54 | + | parent->postAddNotification(thing, oldParent, index, LINK_PARENT); | |
| 55 | + | } | |
| 56 | + | } | |
| 57 | + | ||
| 58 | + | void Inbox::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t) | |
| 59 | + | { | |
| 60 | + | Cylinder* parent = getParent(); | |
| 61 | + | if (parent != nullptr) { | |
| 62 | + | parent->postRemoveNotification(thing, newParent, index, LINK_PARENT); | |
| 63 | + | } | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | Cylinder* Inbox::getParent() const | |
| 67 | + | { | |
| 68 | + | if (parent) { | |
| 69 | + | return parent->getParent(); | |
| 70 | + | } | |
| 71 | + | return nullptr; | |
| 72 | + | } |
| @@ -0,0 +1,49 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_INBOX_H_C3EF10190329447883B9C3479234EE5C | |
| 21 | + | #define FS_INBOX_H_C3EF10190329447883B9C3479234EE5C | |
| 22 | + | ||
| 23 | + | #include "container.h" | |
| 24 | + | ||
| 25 | + | class Inbox final : public Container | |
| 26 | + | { | |
| 27 | + | public: | |
| 28 | + | explicit Inbox(uint16_t type); | |
| 29 | + | ||
| 30 | + | //cylinder implementations | |
| 31 | + | ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, | |
| 32 | + | uint32_t flags, Creature* actor = nullptr) const override; | |
| 33 | + | ||
| 34 | + | void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; | |
| 35 | + | void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; | |
| 36 | + | ||
| 37 | + | //overrides | |
| 38 | + | bool canRemove() const override { | |
| 39 | + | return false; | |
| 40 | + | } | |
| 41 | + | ||
| 42 | + | Cylinder* getParent() const override; | |
| 43 | + | Cylinder* getRealParent() const override { | |
| 44 | + | return parent; | |
| 45 | + | } | |
| 46 | + | }; | |
| 47 | + | ||
| 48 | + | #endif | |
| 49 | + |
| @@ -0,0 +1,79 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #include "otpch.h" | |
| 21 | + | ||
| 22 | + | #include "database.h" | |
| 23 | + | #include "guild.h" | |
| 24 | + | #include "ioguild.h" | |
| 25 | + | ||
| 26 | + | Guild* IOGuild::loadGuild(uint32_t guildId) | |
| 27 | + | { | |
| 28 | + | Database& db = Database::getInstance(); | |
| 29 | + | std::ostringstream query; | |
| 30 | + | query << "SELECT `name` FROM `guilds` WHERE `id` = " << guildId; | |
| 31 | + | if (DBResult_ptr result = db.storeQuery(query.str())) { | |
| 32 | + | Guild* guild = new Guild(guildId, result->getString("name")); | |
| 33 | + | ||
| 34 | + | query.str(std::string()); | |
| 35 | + | query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = " << guildId; | |
| 36 | + | ||
| 37 | + | if ((result = db.storeQuery(query.str()))) { | |
| 38 | + | do { | |
| 39 | + | guild->addRank(result->getNumber<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("level")); | |
| 40 | + | } while (result->next()); | |
| 41 | + | } | |
| 42 | + | return guild; | |
| 43 | + | } | |
| 44 | + | return nullptr; | |
| 45 | + | } | |
| 46 | + | ||
| 47 | + | uint32_t IOGuild::getGuildIdByName(const std::string& name) | |
| 48 | + | { | |
| 49 | + | Database& db = Database::getInstance(); | |
| 50 | + | ||
| 51 | + | std::ostringstream query; | |
| 52 | + | query << "SELECT `id` FROM `guilds` WHERE `name` = " << db.escapeString(name); | |
| 53 | + | ||
| 54 | + | DBResult_ptr result = db.storeQuery(query.str()); | |
| 55 | + | if (!result) { | |
| 56 | + | return 0; | |
| 57 | + | } | |
| 58 | + | return result->getNumber<uint32_t>("id"); | |
| 59 | + | } | |
| 60 | + | ||
| 61 | + | void IOGuild::getWarList(uint32_t guildId, GuildWarVector& guildWarVector) | |
| 62 | + | { | |
| 63 | + | std::ostringstream query; | |
| 64 | + | query << "SELECT `guild1`, `guild2` FROM `guild_wars` WHERE (`guild1` = " << guildId << " OR `guild2` = " << guildId << ") AND `ended` = 0 AND `status` = 1"; | |
| 65 | + | ||
| 66 | + | DBResult_ptr result = Database::getInstance().storeQuery(query.str()); | |
| 67 | + | if (!result) { | |
| 68 | + | return; | |
| 69 | + | } | |
| 70 | + | ||
| 71 | + | do { | |
| 72 | + | uint32_t guild1 = result->getNumber<uint32_t>("guild1"); | |
| 73 | + | if (guildId != guild1) { | |
| 74 | + | guildWarVector.push_back(guild1); | |
| 75 | + | } else { | |
| 76 | + | guildWarVector.push_back(result->getNumber<uint32_t>("guild2")); | |
| 77 | + | } | |
| 78 | + | } while (result->next()); | |
| 79 | + | } |
| @@ -0,0 +1,34 @@ | |||
| 1 | + | /** | |
| 2 | + | * The Forgotten Server - a free and open-source MMORPG server emulator | |
| 3 | + | * Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> | |
| 4 | + | * | |
| 5 | + | * This program is free software; you can redistribute it and/or modify | |
| 6 | + | * it under the terms of the GNU General Public License as published by | |
| 7 | + | * the Free Software Foundation; either version 2 of the License, or | |
| 8 | + | * (at your option) any later version. | |
| 9 | + | * | |
| 10 | + | * This program is distributed in the hope that it will be useful, | |
| 11 | + | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 12 | + | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 13 | + | * GNU General Public License for more details. | |
| 14 | + | * | |
| 15 | + | * You should have received a copy of the GNU General Public License along | |
| 16 | + | * with this program; if not, write to the Free Software Foundation, Inc., | |
| 17 | + | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
| 18 | + | */ | |
| 19 | + | ||
| 20 | + | #ifndef FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF | |
| 21 | + | #define FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF | |
| 22 | + | ||
| 23 | + | class Guild; | |
| 24 | + | using GuildWarVector = std::vector<uint32_t>; | |
| 25 | + | ||
| 26 | + | class IOGuild | |
| 27 | + | { | |
| 28 | + | public: | |
| 29 | + | static Guild* loadGuild(uint32_t guildId); | |
| 30 | + | static uint32_t getGuildIdByName(const std::string& name); | |
| 31 | + | static void getWarList(uint32_t guildId, GuildWarVector& guildWarVector); | |
| 32 | + | }; | |
| 33 | + | ||
| 34 | + | #endif |