mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-10-14 06:34:55 +02:00
Resolve "Merge the best from 7.40 branch"
This commit is contained in:
@@ -277,9 +277,11 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
|
||||
openContainer = container;
|
||||
}
|
||||
|
||||
uint32_t corpseOwner = container->getCorpseOwner();
|
||||
if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
|
||||
return RETURNVALUE_YOUARENOTTHEOWNER;
|
||||
if (g_config.getBoolean(ConfigManager::CORPSE_OWNER_ENABLED)) {
|
||||
uint32_t corpseOwner = container->getCorpseOwner();
|
||||
if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
|
||||
return RETURNVALUE_YOUARENOTTHEOWNER;
|
||||
}
|
||||
}
|
||||
|
||||
//open/close container
|
||||
|
@@ -219,7 +219,7 @@ void BedItem::regeneratePlayer(Player* player) const
|
||||
{
|
||||
const uint32_t sleptTime = time(nullptr) - sleepStart;
|
||||
|
||||
Condition* condition = player->getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
|
||||
/*Condition* condition = player->getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
|
||||
if (condition) {
|
||||
uint32_t regen;
|
||||
if (condition->getTicks() != -1) {
|
||||
@@ -236,7 +236,7 @@ void BedItem::regeneratePlayer(Player* player) const
|
||||
|
||||
player->changeHealth(regen, false);
|
||||
player->changeMana(regen);
|
||||
}
|
||||
}*/
|
||||
|
||||
const int32_t soulRegen = sleptTime / (60 * 15);
|
||||
player->changeSoul(soulRegen);
|
||||
|
@@ -140,6 +140,9 @@ bool BehaviourDatabase::loadConditions(ScriptReader& script, NpcBehaviour* behav
|
||||
} else if (identifier == "premium") {
|
||||
condition->type = BEHAVIOUR_TYPE_ISPREMIUM;
|
||||
searchTerm = true;
|
||||
} else if (identifier == "realpremium") {
|
||||
condition->type = BEHAVIOUR_TYPE_ISREALPREMIUM;
|
||||
searchTerm = true;
|
||||
} else if (identifier == "pvpenforced") {
|
||||
condition->type = BEHAVIOUR_TYPE_PVPENFORCED;
|
||||
searchTerm = true;
|
||||
@@ -801,6 +804,9 @@ bool BehaviourDatabase::checkCondition(const NpcBehaviourCondition* condition, P
|
||||
}
|
||||
break;
|
||||
case BEHAVIOUR_TYPE_ISPREMIUM:
|
||||
|
||||
break;
|
||||
case BEHAVIOUR_TYPE_ISREALPREMIUM:
|
||||
if (!player->isPremium()) {
|
||||
return false;
|
||||
}
|
||||
|
@@ -72,6 +72,7 @@ enum NpcBehaviourType_t
|
||||
BEHAVIOUR_TYPE_KNIGHT, // get/set vocation
|
||||
BEHAVIOUR_TYPE_PALADIN, // get/set vocation
|
||||
BEHAVIOUR_TYPE_ISPREMIUM, // is account premium
|
||||
BEHAVIOUR_TYPE_ISREALPREMIUM, // is REALLY account premium because many isPremium features are for free players also
|
||||
BEHAVIOUR_TYPE_PVPENFORCED, // get world type pvpenforced
|
||||
BEHAVIOUR_TYPE_MALE, // is player male
|
||||
BEHAVIOUR_TYPE_FEMALE, // is player female
|
||||
|
@@ -329,7 +329,7 @@ ChatChannel* Chat::createChannel(const Player& player, uint16_t channelId)
|
||||
|
||||
case CHANNEL_PRIVATE: {
|
||||
//only 1 private channel for each premium player
|
||||
if (!player.isPremium() || getPrivateChannel(player)) {
|
||||
if (getPrivateChannel(player)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -522,7 +522,7 @@ ChannelList Chat::getChannelList(const Player& player)
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasPrivate && player.isPremium()) {
|
||||
if (!hasPrivate) {
|
||||
list.push_front(&dummyPrivate);
|
||||
}
|
||||
return list;
|
||||
|
@@ -80,6 +80,8 @@ bool ConfigManager::load()
|
||||
boolean[TELEPORT_NEWBIES] = getGlobalBoolean(L, "teleportNewbies", true);
|
||||
boolean[STACK_CUMULATIVES] = getGlobalBoolean(L, "autoStackCumulatives", false);
|
||||
boolean[BLOCK_HEIGHT] = getGlobalBoolean(L, "blockHeight", false);
|
||||
boolean[UH_TRAP] = getGlobalBoolean(L, "uhTrap", false);
|
||||
boolean[ROPE_SPOT_BLOCK] = getGlobalBoolean(L, "ropeSpotBlock", false);
|
||||
boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false);
|
||||
boolean[DISTANCE_WEAPONS_DROP_ON_GROUND] = getGlobalBoolean(L, "distanceWeaponsDropOnGround", true);
|
||||
|
||||
@@ -112,6 +114,7 @@ bool ConfigManager::load()
|
||||
integer[DEATH_LOSE_PERCENT] = getGlobalNumber(L, "deathLosePercent", -1);
|
||||
integer[KNIGHT_CLOSE_ATTACK_DAMAGE_INCREASE_PERCENT] = getGlobalNumber(L, "knightCloseAttackDamageIncreasePercent", -1);
|
||||
integer[PALADIN_RANGE_ATTACK_DAMAGE_INCREASE_PERCENT] = getGlobalNumber(L, "paladinRangeAttackDamageIncreasePercent", -1);
|
||||
integer[CORPSE_OWNER_ENABLED] = getGlobalBoolean(L, "corpseOwnerEnabled", true);
|
||||
integer[STATUSQUERY_TIMEOUT] = getGlobalNumber(L, "statusTimeout", 5000);
|
||||
integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60);
|
||||
integer[RED_SKULL_TIME] = getGlobalNumber(L, "redSkullTime", 30 * 24 * 60 * 60);
|
||||
|
@@ -47,9 +47,11 @@ class ConfigManager
|
||||
TELEPORT_NEWBIES,
|
||||
STACK_CUMULATIVES,
|
||||
BLOCK_HEIGHT,
|
||||
UH_TRAP,
|
||||
DROP_ITEMS,
|
||||
DISTANCE_WEAPONS_DROP_ON_GROUND,
|
||||
|
||||
CORPSE_OWNER_ENABLED,
|
||||
ROPE_SPOT_BLOCK,
|
||||
LAST_BOOLEAN_CONFIG /* this must be the last one */
|
||||
};
|
||||
|
||||
|
@@ -242,7 +242,14 @@ enum PartyShields_t : uint8_t {
|
||||
SHIELD_WHITEYELLOW = 1,
|
||||
SHIELD_WHITEBLUE = 2,
|
||||
SHIELD_BLUE = 3,
|
||||
SHIELD_YELLOW = 4
|
||||
SHIELD_YELLOW = 4,
|
||||
SHIELD_BLUE_SHAREDEXP = 5,
|
||||
SHIELD_YELLOW_SHAREDEXP = 6,
|
||||
SHIELD_BLUE_NOSHAREDEXP_BLINK = 7,
|
||||
SHIELD_YELLOW_NOSHAREDEXP_BLINK = 8,
|
||||
SHIELD_BLUE_NOSHAREDEXP = 9,
|
||||
SHIELD_YELLOW_NOSHAREDEXP = 10,
|
||||
SHIELD_GRAY = 11,
|
||||
};
|
||||
|
||||
enum item_t : uint16_t {
|
||||
|
16
src/game.cpp
16
src/game.cpp
@@ -223,7 +223,12 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index
|
||||
if (item && item->isMoveable()) {
|
||||
thing = item;
|
||||
} else {
|
||||
thing = tile->getTopVisibleCreature(player);
|
||||
if (g_config.getBoolean(ConfigManager::UH_TRAP)) {
|
||||
thing = tile->getBottomVisibleCreatureUH(player);
|
||||
}
|
||||
else {
|
||||
thing = tile->getTopVisibleCreature(player);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -239,7 +244,12 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index
|
||||
}
|
||||
|
||||
case STACKPOS_USETARGET: {
|
||||
thing = tile->getTopVisibleCreature(player);
|
||||
if (g_config.getBoolean(ConfigManager::UH_TRAP)) {
|
||||
thing = tile->getBottomCreatureUH();
|
||||
}
|
||||
else {
|
||||
thing = tile->getTopCreature();
|
||||
}
|
||||
if (!thing) {
|
||||
thing = tile->getUseItem(index);
|
||||
}
|
||||
@@ -1715,7 +1725,7 @@ bool Game::playerBroadcastMessage(Player* player, const std::string& text) const
|
||||
void Game::playerCreatePrivateChannel(uint32_t playerId)
|
||||
{
|
||||
Player* player = getPlayerByID(playerId);
|
||||
if (!player || !player->isPremium()) {
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -190,7 +190,7 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
|
||||
bool IOLoginData::loadPlayerById(Player* player, uint32_t id)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `id` = " << id;
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `fake_player` FROM `players` WHERE `id` = " << id;
|
||||
return loadPlayer(player, Database::getInstance()->storeQuery(query.str()));
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ bool IOLoginData::loadPlayerByName(Player* player, const std::string& name)
|
||||
{
|
||||
Database* db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `name` = " << db->escapeString(name);
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `fake_player` FROM `players` WHERE `name` = " << db->escapeString(name);
|
||||
return loadPlayer(player, db->storeQuery(query.str()));
|
||||
}
|
||||
|
||||
@@ -320,6 +320,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
|
||||
player->lastLoginSaved = result->getNumber<time_t>("lastlogin");
|
||||
player->lastLogout = result->getNumber<time_t>("lastlogout");
|
||||
player->isFakePlayer = result->getNumber<int32_t>("fake_player") == 1;
|
||||
|
||||
player->offlineTrainingTime = result->getNumber<int32_t>("offlinetraining_time") * 1000;
|
||||
player->offlineTrainingSkill = result->getNumber<int32_t>("offlinetraining_skill");
|
||||
|
780
src/item.cpp
780
src/item.cpp
@@ -52,19 +52,26 @@ Item* Item::CreateItem(const uint16_t type, uint16_t count /*= 0*/)
|
||||
if (it.id != 0) {
|
||||
if (it.isDepot()) {
|
||||
newItem = new DepotLocker(type);
|
||||
} else if (it.isContainer() || it.isChest()) {
|
||||
}
|
||||
else if (it.isContainer() || it.isChest()) {
|
||||
newItem = new Container(type);
|
||||
} else if (it.isTeleport()) {
|
||||
}
|
||||
else if (it.isTeleport()) {
|
||||
newItem = new Teleport(type);
|
||||
} else if (it.isMagicField()) {
|
||||
}
|
||||
else if (it.isMagicField()) {
|
||||
newItem = new MagicField(type);
|
||||
} else if (it.isDoor()) {
|
||||
}
|
||||
else if (it.isDoor()) {
|
||||
newItem = new Door(type);
|
||||
} else if (it.isMailbox()) {
|
||||
}
|
||||
else if (it.isMailbox()) {
|
||||
newItem = new Mailbox(type);
|
||||
} else if (it.isBed()) {
|
||||
}
|
||||
else if (it.isBed()) {
|
||||
newItem = new BedItem(type);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
newItem = new Item(type, count);
|
||||
}
|
||||
|
||||
@@ -94,36 +101,36 @@ Item* Item::CreateItem(PropStream& propStream)
|
||||
}
|
||||
|
||||
switch (id) {
|
||||
case ITEM_FIREFIELD_PVP_FULL:
|
||||
id = ITEM_FIREFIELD_PERSISTENT_FULL;
|
||||
break;
|
||||
case ITEM_FIREFIELD_PVP_FULL:
|
||||
id = ITEM_FIREFIELD_PERSISTENT_FULL;
|
||||
break;
|
||||
|
||||
case ITEM_FIREFIELD_PVP_MEDIUM:
|
||||
id = ITEM_FIREFIELD_PERSISTENT_MEDIUM;
|
||||
break;
|
||||
case ITEM_FIREFIELD_PVP_MEDIUM:
|
||||
id = ITEM_FIREFIELD_PERSISTENT_MEDIUM;
|
||||
break;
|
||||
|
||||
case ITEM_FIREFIELD_PVP_SMALL:
|
||||
id = ITEM_FIREFIELD_PERSISTENT_SMALL;
|
||||
break;
|
||||
case ITEM_FIREFIELD_PVP_SMALL:
|
||||
id = ITEM_FIREFIELD_PERSISTENT_SMALL;
|
||||
break;
|
||||
|
||||
case ITEM_ENERGYFIELD_PVP:
|
||||
id = ITEM_ENERGYFIELD_PERSISTENT;
|
||||
break;
|
||||
case ITEM_ENERGYFIELD_PVP:
|
||||
id = ITEM_ENERGYFIELD_PERSISTENT;
|
||||
break;
|
||||
|
||||
case ITEM_POISONFIELD_PVP:
|
||||
id = ITEM_POISONFIELD_PERSISTENT;
|
||||
break;
|
||||
case ITEM_POISONFIELD_PVP:
|
||||
id = ITEM_POISONFIELD_PERSISTENT;
|
||||
break;
|
||||
|
||||
case ITEM_MAGICWALL:
|
||||
id = ITEM_MAGICWALL_PERSISTENT;
|
||||
break;
|
||||
case ITEM_MAGICWALL:
|
||||
id = ITEM_MAGICWALL_PERSISTENT;
|
||||
break;
|
||||
|
||||
case ITEM_WILDGROWTH:
|
||||
id = ITEM_WILDGROWTH_PERSISTENT;
|
||||
break;
|
||||
case ITEM_WILDGROWTH:
|
||||
id = ITEM_WILDGROWTH_PERSISTENT;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Item::CreateItem(id, 0);
|
||||
@@ -136,19 +143,24 @@ Item::Item(const uint16_t type, uint16_t count /*= 0*/) :
|
||||
|
||||
if (it.isFluidContainer() || it.isSplash()) {
|
||||
setFluidType(count);
|
||||
} else if (it.stackable) {
|
||||
}
|
||||
else if (it.stackable) {
|
||||
if (count != 0) {
|
||||
setItemCount(count);
|
||||
} else if (it.charges != 0) {
|
||||
}
|
||||
else if (it.charges != 0) {
|
||||
setItemCount(it.charges);
|
||||
}
|
||||
} else if (it.charges != 0) {
|
||||
}
|
||||
else if (it.charges != 0) {
|
||||
if (count != 0) {
|
||||
setCharges(count);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
setCharges(it.charges);
|
||||
}
|
||||
} else if (it.isKey()) {
|
||||
}
|
||||
else if (it.isKey()) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, count);
|
||||
}
|
||||
|
||||
@@ -196,7 +208,8 @@ bool Item::equals(const Item* otherItem) const
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
for (const auto& otherAttribute : otherAttributeList) {
|
||||
if (attribute.type == otherAttribute.type && attribute.value.integer != otherAttribute.value.integer) {
|
||||
return false;
|
||||
@@ -216,7 +229,8 @@ void Item::setDefaultSubtype()
|
||||
if (it.charges != 0) {
|
||||
if (it.stackable) {
|
||||
setItemCount(it.charges);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
setCharges(it.charges);
|
||||
}
|
||||
}
|
||||
@@ -311,9 +325,11 @@ uint16_t Item::getSubType() const
|
||||
const ItemType& it = items[id];
|
||||
if (it.isFluidContainer() || it.isSplash()) {
|
||||
return getFluidType();
|
||||
} else if (it.stackable) {
|
||||
}
|
||||
else if (it.stackable) {
|
||||
return count;
|
||||
} else if (it.charges != 0) {
|
||||
}
|
||||
else if (it.charges != 0) {
|
||||
return getCharges();
|
||||
}
|
||||
return count;
|
||||
@@ -337,11 +353,14 @@ void Item::setSubType(uint16_t n)
|
||||
const ItemType& it = items[id];
|
||||
if (it.isFluidContainer() || it.isSplash()) {
|
||||
setFluidType(n);
|
||||
} else if (it.stackable) {
|
||||
}
|
||||
else if (it.stackable) {
|
||||
setItemCount(n);
|
||||
} else if (it.charges != 0) {
|
||||
}
|
||||
else if (it.charges != 0) {
|
||||
setCharges(n);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
setItemCount(n);
|
||||
}
|
||||
}
|
||||
@@ -349,304 +368,304 @@ void Item::setSubType(uint16_t n)
|
||||
Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream)
|
||||
{
|
||||
switch (attr) {
|
||||
case ATTR_COUNT:
|
||||
case ATTR_RUNE_CHARGES: {
|
||||
uint8_t count;
|
||||
if (!propStream.read<uint8_t>(count)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setSubType(count);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ACTION_ID: {
|
||||
uint16_t actionId;
|
||||
if (!propStream.read<uint16_t>(actionId)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setActionId(actionId);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_MOVEMENT_ID: {
|
||||
uint16_t movementId;
|
||||
if (!propStream.read<uint16_t>(movementId)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setMovementID(movementId);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_TEXT: {
|
||||
std::string text;
|
||||
if (!propStream.readString(text)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setText(text);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_WRITTENDATE: {
|
||||
uint32_t writtenDate;
|
||||
if (!propStream.read<uint32_t>(writtenDate)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setDate(writtenDate);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_WRITTENBY: {
|
||||
std::string writer;
|
||||
if (!propStream.readString(writer)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setWriter(writer);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DESC: {
|
||||
std::string text;
|
||||
if (!propStream.readString(text)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setSpecialDescription(text);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_CHARGES: {
|
||||
uint16_t charges;
|
||||
if (!propStream.read<uint16_t>(charges)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setSubType(charges);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DURATION: {
|
||||
int32_t duration;
|
||||
if (!propStream.read<int32_t>(duration)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setDuration(std::max<int32_t>(0, duration));
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DECAYING_STATE: {
|
||||
uint8_t state;
|
||||
if (!propStream.read<uint8_t>(state)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
if (state != DECAYING_FALSE) {
|
||||
setDecaying(DECAYING_PENDING);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_NAME: {
|
||||
std::string name;
|
||||
if (!propStream.readString(name)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setStrAttr(ITEM_ATTRIBUTE_NAME, name);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ARTICLE: {
|
||||
std::string article;
|
||||
if (!propStream.readString(article)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setStrAttr(ITEM_ATTRIBUTE_ARTICLE, article);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_PLURALNAME: {
|
||||
std::string pluralName;
|
||||
if (!propStream.readString(pluralName)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setStrAttr(ITEM_ATTRIBUTE_PLURALNAME, pluralName);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_WEIGHT: {
|
||||
uint32_t weight;
|
||||
if (!propStream.read<uint32_t>(weight)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_WEIGHT, weight);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ATTACK: {
|
||||
int32_t attack;
|
||||
if (!propStream.read<int32_t>(attack)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_ATTACK, attack);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DEFENSE: {
|
||||
int32_t defense;
|
||||
if (!propStream.read<int32_t>(defense)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DEFENSE, defense);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ARMOR: {
|
||||
int32_t armor;
|
||||
if (!propStream.read<int32_t>(armor)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_ARMOR, armor);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_SHOOTRANGE: {
|
||||
uint8_t shootRange;
|
||||
if (!propStream.read<uint8_t>(shootRange)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE, shootRange);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_KEYNUMBER: {
|
||||
uint16_t keyNumber;
|
||||
if (!propStream.read<uint16_t>(keyNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, keyNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_KEYHOLENUMBER:
|
||||
{
|
||||
uint16_t keyHoleNumber;
|
||||
if (!propStream.read<uint16_t>(keyHoleNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER, keyHoleNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DOORLEVEL:
|
||||
{
|
||||
uint16_t doorLevel;
|
||||
if (!propStream.read<uint16_t>(doorLevel)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORLEVEL, doorLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DOORQUESTNUMBER:
|
||||
{
|
||||
uint16_t doorQuestNumber;
|
||||
if (!propStream.read<uint16_t>(doorQuestNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER, doorQuestNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DOORQUESTVALUE:
|
||||
{
|
||||
uint16_t doorQuestValue;
|
||||
if (!propStream.read<uint16_t>(doorQuestValue)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE, doorQuestValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_CHESTQUESTNUMBER:
|
||||
{
|
||||
uint16_t chestQuestNumber;
|
||||
if (!propStream.read<uint16_t>(chestQuestNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER, chestQuestNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
//these should be handled through derived classes
|
||||
//If these are called then something has changed in the items.xml since the map was saved
|
||||
//just read the values
|
||||
|
||||
//Depot class
|
||||
case ATTR_DEPOT_ID: {
|
||||
if (!propStream.skip(2)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Door class
|
||||
case ATTR_HOUSEDOORID: {
|
||||
if (!propStream.skip(1)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Bed class
|
||||
case ATTR_SLEEPERGUID: {
|
||||
if (!propStream.skip(4)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_SLEEPSTART: {
|
||||
if (!propStream.skip(4)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Teleport class
|
||||
case ATTR_TELE_DEST: {
|
||||
if (!propStream.skip(5)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Container class
|
||||
case ATTR_CONTAINER_ITEMS: {
|
||||
case ATTR_COUNT:
|
||||
case ATTR_RUNE_CHARGES: {
|
||||
uint8_t count;
|
||||
if (!propStream.read<uint8_t>(count)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
default:
|
||||
setSubType(count);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ACTION_ID: {
|
||||
uint16_t actionId;
|
||||
if (!propStream.read<uint16_t>(actionId)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setActionId(actionId);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_MOVEMENT_ID: {
|
||||
uint16_t movementId;
|
||||
if (!propStream.read<uint16_t>(movementId)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setMovementID(movementId);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_TEXT: {
|
||||
std::string text;
|
||||
if (!propStream.readString(text)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setText(text);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_WRITTENDATE: {
|
||||
uint32_t writtenDate;
|
||||
if (!propStream.read<uint32_t>(writtenDate)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setDate(writtenDate);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_WRITTENBY: {
|
||||
std::string writer;
|
||||
if (!propStream.readString(writer)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setWriter(writer);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DESC: {
|
||||
std::string text;
|
||||
if (!propStream.readString(text)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setSpecialDescription(text);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_CHARGES: {
|
||||
uint16_t charges;
|
||||
if (!propStream.read<uint16_t>(charges)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setSubType(charges);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DURATION: {
|
||||
int32_t duration;
|
||||
if (!propStream.read<int32_t>(duration)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setDuration(std::max<int32_t>(0, duration));
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DECAYING_STATE: {
|
||||
uint8_t state;
|
||||
if (!propStream.read<uint8_t>(state)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
if (state != DECAYING_FALSE) {
|
||||
setDecaying(DECAYING_PENDING);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_NAME: {
|
||||
std::string name;
|
||||
if (!propStream.readString(name)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setStrAttr(ITEM_ATTRIBUTE_NAME, name);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ARTICLE: {
|
||||
std::string article;
|
||||
if (!propStream.readString(article)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setStrAttr(ITEM_ATTRIBUTE_ARTICLE, article);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_PLURALNAME: {
|
||||
std::string pluralName;
|
||||
if (!propStream.readString(pluralName)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setStrAttr(ITEM_ATTRIBUTE_PLURALNAME, pluralName);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_WEIGHT: {
|
||||
uint32_t weight;
|
||||
if (!propStream.read<uint32_t>(weight)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_WEIGHT, weight);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ATTACK: {
|
||||
int32_t attack;
|
||||
if (!propStream.read<int32_t>(attack)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_ATTACK, attack);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DEFENSE: {
|
||||
int32_t defense;
|
||||
if (!propStream.read<int32_t>(defense)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DEFENSE, defense);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_ARMOR: {
|
||||
int32_t armor;
|
||||
if (!propStream.read<int32_t>(armor)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_ARMOR, armor);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_SHOOTRANGE: {
|
||||
uint8_t shootRange;
|
||||
if (!propStream.read<uint8_t>(shootRange)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE, shootRange);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_KEYNUMBER: {
|
||||
uint16_t keyNumber;
|
||||
if (!propStream.read<uint16_t>(keyNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, keyNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_KEYHOLENUMBER:
|
||||
{
|
||||
uint16_t keyHoleNumber;
|
||||
if (!propStream.read<uint16_t>(keyHoleNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER, keyHoleNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DOORLEVEL:
|
||||
{
|
||||
uint16_t doorLevel;
|
||||
if (!propStream.read<uint16_t>(doorLevel)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORLEVEL, doorLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DOORQUESTNUMBER:
|
||||
{
|
||||
uint16_t doorQuestNumber;
|
||||
if (!propStream.read<uint16_t>(doorQuestNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER, doorQuestNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_DOORQUESTVALUE:
|
||||
{
|
||||
uint16_t doorQuestValue;
|
||||
if (!propStream.read<uint16_t>(doorQuestValue)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE, doorQuestValue);
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_CHESTQUESTNUMBER:
|
||||
{
|
||||
uint16_t chestQuestNumber;
|
||||
if (!propStream.read<uint16_t>(chestQuestNumber)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER, chestQuestNumber);
|
||||
break;
|
||||
}
|
||||
|
||||
//these should be handled through derived classes
|
||||
//If these are called then something has changed in the items.xml since the map was saved
|
||||
//just read the values
|
||||
|
||||
//Depot class
|
||||
case ATTR_DEPOT_ID: {
|
||||
if (!propStream.skip(2)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Door class
|
||||
case ATTR_HOUSEDOORID: {
|
||||
if (!propStream.skip(1)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Bed class
|
||||
case ATTR_SLEEPERGUID: {
|
||||
if (!propStream.skip(4)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ATTR_SLEEPSTART: {
|
||||
if (!propStream.skip(4)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Teleport class
|
||||
case ATTR_TELE_DEST: {
|
||||
if (!propStream.skip(5)) {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//Container class
|
||||
case ATTR_CONTAINER_ITEMS: {
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
default:
|
||||
return ATTR_READ_ERROR;
|
||||
}
|
||||
|
||||
return ATTR_READ_CONTINUE;
|
||||
@@ -659,7 +678,8 @@ bool Item::unserializeAttr(PropStream& propStream)
|
||||
Attr_ReadValue ret = readAttr(static_cast<AttrTypes_t>(attr_type), propStream);
|
||||
if (ret == ATTR_READ_ERROR) {
|
||||
return false;
|
||||
} else if (ret == ATTR_READ_END) {
|
||||
}
|
||||
else if (ret == ATTR_READ_END) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -803,20 +823,20 @@ bool Item::hasProperty(ITEMPROPERTY prop) const
|
||||
{
|
||||
const ItemType& it = items[id];
|
||||
switch (prop) {
|
||||
case CONST_PROP_BLOCKSOLID: return it.blockSolid;
|
||||
case CONST_PROP_MOVEABLE: return it.moveable;
|
||||
case CONST_PROP_HASHEIGHT: return it.hasHeight;
|
||||
case CONST_PROP_BLOCKPROJECTILE: return it.blockProjectile;
|
||||
case CONST_PROP_BLOCKPATH: return it.blockPathFind;
|
||||
case CONST_PROP_ISVERTICAL: return it.isVertical;
|
||||
case CONST_PROP_ISHORIZONTAL: return it.isHorizontal;
|
||||
case CONST_PROP_IMMOVABLEBLOCKSOLID: return it.blockSolid && !it.moveable;
|
||||
case CONST_PROP_IMMOVABLEBLOCKPATH: return it.blockPathFind && !it.moveable;
|
||||
case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind && !it.moveable;
|
||||
case CONST_PROP_NOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind;
|
||||
case CONST_PROP_SUPPORTHANGABLE: return it.isHorizontal || it.isVertical;
|
||||
case CONST_PROP_UNLAY: return !it.allowPickupable;
|
||||
default: return false;
|
||||
case CONST_PROP_BLOCKSOLID: return it.blockSolid;
|
||||
case CONST_PROP_MOVEABLE: return it.moveable;
|
||||
case CONST_PROP_HASHEIGHT: return it.hasHeight;
|
||||
case CONST_PROP_BLOCKPROJECTILE: return it.blockProjectile;
|
||||
case CONST_PROP_BLOCKPATH: return it.blockPathFind;
|
||||
case CONST_PROP_ISVERTICAL: return it.isVertical;
|
||||
case CONST_PROP_ISHORIZONTAL: return it.isHorizontal;
|
||||
case CONST_PROP_IMMOVABLEBLOCKSOLID: return it.blockSolid && !it.moveable;
|
||||
case CONST_PROP_IMMOVABLEBLOCKPATH: return it.blockPathFind && !it.moveable;
|
||||
case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind && !it.moveable;
|
||||
case CONST_PROP_NOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind;
|
||||
case CONST_PROP_SUPPORTHANGABLE: return it.isHorizontal || it.isVertical;
|
||||
case CONST_PROP_UNLAY: return !it.allowPickupable;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,28 +881,28 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance,
|
||||
}
|
||||
else if (it.weaponType != WEAPON_NONE) {
|
||||
if (it.weaponType == WEAPON_DISTANCE && it.ammoType != AMMO_NONE) {
|
||||
if (it.attack != 0) {
|
||||
s << ", Atk" << std::showpos << it.attack << std::noshowpos;
|
||||
if (item->getAttack() != 0) {
|
||||
s << ", Atk" << std::showpos << item->getAttack() << std::noshowpos;
|
||||
}
|
||||
}
|
||||
else if (it.weaponType != WEAPON_AMMO && it.weaponType != WEAPON_WAND && (it.attack != 0 || it.defense != 0)) {
|
||||
else if (it.weaponType != WEAPON_WAND && (item->getAttack() != 0 || item->getDefense() != 0)) {
|
||||
s << " (";
|
||||
if (it.attack != 0) {
|
||||
s << "Atk:" << static_cast<int>(it.attack);
|
||||
if (item->getAttack() != 0) {
|
||||
s << "Atk:" << static_cast<int>(item->getAttack());
|
||||
}
|
||||
|
||||
if (it.defense != 0) {
|
||||
if (it.attack != 0)
|
||||
if (item->getDefense() != 0) {
|
||||
if (item->getAttack() != 0)
|
||||
s << " ";
|
||||
|
||||
s << "Def:" << static_cast<int>(it.defense);
|
||||
s << "Def:" << static_cast<int>(item->getDefense());
|
||||
}
|
||||
|
||||
s << ")";
|
||||
}
|
||||
s << ".";
|
||||
}
|
||||
else if (it.armor != 0) {
|
||||
else if (item->getArmor() != 0 || (it.abilities && it.abilities->speed != 0)) {
|
||||
if (it.charges > 0) {
|
||||
if (subType > 1) {
|
||||
s << " that has " << static_cast<int32_t>(subType) << " charges left";
|
||||
@@ -892,7 +912,17 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance,
|
||||
}
|
||||
}
|
||||
|
||||
s << " (Arm:" << it.armor << ").";
|
||||
s << " (";
|
||||
if (item->getArmor() > 0) {
|
||||
s << "Arm:" << item->getArmor();
|
||||
}
|
||||
if (it.abilities && it.abilities->speed > 0) {
|
||||
if (item->getArmor() > 0) {
|
||||
s << ", ";
|
||||
}
|
||||
s << "Speed +" << it.abilities->speed;
|
||||
}
|
||||
s << ").";
|
||||
}
|
||||
else if (it.isFluidContainer()) {
|
||||
if (item && item->getFluidType() != 0) {
|
||||
@@ -1083,15 +1113,18 @@ std::string Item::getWeightDescription(const ItemType& it, uint32_t weight, uint
|
||||
std::ostringstream ss;
|
||||
if (it.stackable && count > 1 && it.showCount != 0) {
|
||||
ss << "They weigh ";
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
ss << "It weighs ";
|
||||
}
|
||||
|
||||
if (weight < 10) {
|
||||
ss << "0.0" << weight;
|
||||
} else if (weight < 100) {
|
||||
}
|
||||
else if (weight < 100) {
|
||||
ss << "0." << weight;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
std::string weightString = std::to_string(weight);
|
||||
weightString.insert(weightString.end() - 2, '.');
|
||||
ss << weightString;
|
||||
@@ -1133,17 +1166,17 @@ bool Item::canDecay() const
|
||||
uint32_t Item::getWorth() const
|
||||
{
|
||||
switch (id) {
|
||||
case ITEM_GOLD_COIN:
|
||||
return count;
|
||||
case ITEM_GOLD_COIN:
|
||||
return count;
|
||||
|
||||
case ITEM_PLATINUM_COIN:
|
||||
return count * 100;
|
||||
case ITEM_PLATINUM_COIN:
|
||||
return count * 100;
|
||||
|
||||
case ITEM_CRYSTAL_COIN:
|
||||
return count * 10000;
|
||||
case ITEM_CRYSTAL_COIN:
|
||||
return count * 10000;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1193,7 +1226,8 @@ void ItemAttributes::removeAttribute(itemAttrTypes type)
|
||||
auto prev_it = attributes.cbegin();
|
||||
if ((*prev_it).type == type) {
|
||||
attributes.pop_front();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
auto it = prev_it, end = attributes.cend();
|
||||
while (++it != end) {
|
||||
if ((*it).type == type) {
|
||||
|
@@ -1690,6 +1690,8 @@ void LuaScriptInterface::registerFunctions()
|
||||
registerEnumIn("configKeys", ConfigManager::NEWBIE_TOWN)
|
||||
registerEnumIn("configKeys", ConfigManager::NEWBIE_LEVEL_THRESHOLD)
|
||||
registerEnumIn("configKeys", ConfigManager::BLOCK_HEIGHT)
|
||||
registerEnumIn("configKeys", ConfigManager::UH_TRAP)
|
||||
registerEnumIn("configKeys", ConfigManager::ROPE_SPOT_BLOCK)
|
||||
registerEnumIn("configKeys", ConfigManager::DROP_ITEMS)
|
||||
registerEnumIn("configKeys", ConfigManager::CLIENT_VERSION)
|
||||
|
||||
@@ -2104,6 +2106,7 @@ void LuaScriptInterface::registerFunctions()
|
||||
registerMethod("Player", "save", LuaScriptInterface::luaPlayerSave);
|
||||
|
||||
registerMethod("Player", "isPzLocked", LuaScriptInterface::luaPlayerIsPzLocked);
|
||||
registerMethod("Player", "isFakePlayer", LuaScriptInterface::luaPlayerIsFakePlayer);
|
||||
|
||||
registerMethod("Player", "getClient", LuaScriptInterface::luaPlayerGetClient);
|
||||
registerMethod("Player", "getHouse", LuaScriptInterface::luaPlayerGetHouse);
|
||||
@@ -7110,6 +7113,7 @@ int LuaScriptInterface::luaPlayerIsPlayer(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int LuaScriptInterface::luaPlayerGetGuid(lua_State* L)
|
||||
{
|
||||
// player:getGuid()
|
||||
@@ -8740,6 +8744,19 @@ int LuaScriptInterface::luaPlayerIsPzLocked(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaScriptInterface::luaPlayerIsFakePlayer(lua_State* L)
|
||||
{
|
||||
// player:isFakePlayer()
|
||||
Player* player = getUserdata<Player>(L, 1);
|
||||
if (player) {
|
||||
pushBoolean(L, player->isFakePlayer);
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LuaScriptInterface::luaPlayerGetClient(lua_State* L)
|
||||
{
|
||||
// player:getClient()
|
||||
|
@@ -925,6 +925,7 @@ class LuaScriptInterface
|
||||
static int luaPlayerSave(lua_State* L);
|
||||
|
||||
static int luaPlayerIsPzLocked(lua_State* L);
|
||||
static int luaPlayerIsFakePlayer(lua_State* L);
|
||||
|
||||
static int luaPlayerGetClient(lua_State* L);
|
||||
static int luaPlayerGetHouse(lua_State* L);
|
||||
|
@@ -1072,8 +1072,8 @@ void Monster::onThinkYell(uint32_t)
|
||||
|
||||
int32_t randomResult = rand();
|
||||
if (rand() == 50 * (randomResult / 50)) {
|
||||
int32_t totalVoices = mType->info.voiceVector.size();
|
||||
const voiceBlock_t& voice = mType->info.voiceVector[rand() % totalVoices + 1];
|
||||
uint32_t index = uniform_random(0, mType->info.voiceVector.size() - 1);
|
||||
const voiceBlock_t& voice = mType->info.voiceVector[index];
|
||||
|
||||
if (voice.yellText) {
|
||||
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, voice.text, false);
|
||||
|
@@ -22,12 +22,15 @@
|
||||
|
||||
#include "tile.h"
|
||||
#include "monsters.h"
|
||||
#include "configmanager.h"
|
||||
|
||||
class Creature;
|
||||
class Game;
|
||||
class Spawn;
|
||||
class Combat;
|
||||
|
||||
extern ConfigManager g_config;
|
||||
|
||||
typedef std::unordered_set<Creature*> CreatureHashSet;
|
||||
typedef std::list<Creature*> CreatureList;
|
||||
|
||||
@@ -257,6 +260,13 @@ class Monster final : public Creature
|
||||
return skillLoss ? mType->info.experience : 0;
|
||||
}
|
||||
uint16_t getLookCorpse() const final {
|
||||
if (!g_config.getBoolean(ConfigManager::CORPSE_OWNER_ENABLED)) {
|
||||
const ItemType& itemtype = Item::items[mType->info.lookcorpse];
|
||||
if (itemtype.decayTo != 0) {
|
||||
return itemtype.decayTo;
|
||||
}
|
||||
}
|
||||
|
||||
return mType->info.lookcorpse;
|
||||
}
|
||||
void dropLoot(Container* corpse, Creature* lastHitCreature) final;
|
||||
|
@@ -977,7 +977,8 @@ void Player::onWalk(Direction& dir)
|
||||
{
|
||||
Creature::onWalk(dir);
|
||||
setNextActionTask(nullptr);
|
||||
setNextAction(OTSYS_TIME() + getStepDuration(dir));
|
||||
// TODO: Find out if really nothing brokes. This is for allow players to heal and walk or walk and open backpacks
|
||||
//setNextAction(OTSYS_TIME() + getStepDuration(dir));
|
||||
}
|
||||
|
||||
void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos,
|
||||
@@ -1183,7 +1184,9 @@ void Player::onThink(uint32_t interval)
|
||||
idleTime += interval;
|
||||
const int32_t kickAfterMinutes = g_config.getNumber(ConfigManager::KICK_AFTER_MINUTES);
|
||||
if ((!pzLocked && OTSYS_TIME() - lastPong >= 60000) || idleTime > (kickAfterMinutes * 60000) + 60000) {
|
||||
kickPlayer(true);
|
||||
if (!isFakePlayer) {
|
||||
kickPlayer(true);
|
||||
}
|
||||
} else if (client && idleTime == 60000 * kickAfterMinutes) {
|
||||
std::ostringstream ss;
|
||||
ss << "You have been idle for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if you are still idle then.";
|
||||
@@ -1191,6 +1194,17 @@ void Player::onThink(uint32_t interval)
|
||||
}
|
||||
}
|
||||
|
||||
if (isFakePlayer && idleTime > uniform_random(60000, 120000)) {
|
||||
uint32_t r = uniform_random(0, 1);
|
||||
Direction dir = DIRECTION_NORTH;
|
||||
if (r == 0) {
|
||||
dir = DIRECTION_SOUTH;
|
||||
}
|
||||
|
||||
g_game.internalCreatureTurn(this, dir);
|
||||
resetIdleTime();
|
||||
}
|
||||
|
||||
if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
|
||||
checkSkullTicks();
|
||||
}
|
||||
@@ -1614,7 +1628,12 @@ void Player::dropLoot(Container* corpse, Creature*)
|
||||
|
||||
void Player::death(Creature* lastHitCreature)
|
||||
{
|
||||
loginPosition = town->getTemplePosition();
|
||||
if (isFakePlayer) {
|
||||
loginPosition = g_game.map.towns.getTown(10)->getTemplePosition(); // Isle of solitude
|
||||
}
|
||||
else {
|
||||
loginPosition = town->getTemplePosition();
|
||||
}
|
||||
|
||||
if (skillLoss) {
|
||||
//Magic level loss
|
||||
@@ -3678,10 +3697,34 @@ PartyShields_t Player::getPartyShield(const Player* player) const
|
||||
|
||||
if (party) {
|
||||
if (party->getLeader() == player) {
|
||||
if (party->isSharedExperienceActive()) {
|
||||
if (party->isSharedExperienceEnabled()) {
|
||||
return SHIELD_YELLOW_SHAREDEXP;
|
||||
}
|
||||
|
||||
if (party->canUseSharedExperience(player)) {
|
||||
return SHIELD_YELLOW_NOSHAREDEXP;
|
||||
}
|
||||
|
||||
return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
|
||||
}
|
||||
|
||||
return SHIELD_YELLOW;
|
||||
}
|
||||
|
||||
if (player->party == party) {
|
||||
if (party->isSharedExperienceActive()) {
|
||||
if (party->isSharedExperienceEnabled()) {
|
||||
return SHIELD_BLUE_SHAREDEXP;
|
||||
}
|
||||
|
||||
if (party->canUseSharedExperience(player)) {
|
||||
return SHIELD_BLUE_NOSHAREDEXP;
|
||||
}
|
||||
|
||||
return SHIELD_BLUE_NOSHAREDEXP_BLINK;
|
||||
}
|
||||
|
||||
return SHIELD_BLUE;
|
||||
}
|
||||
|
||||
|
@@ -154,6 +154,8 @@ class Player final : public Creature, public Cylinder
|
||||
return staminaMinutes;
|
||||
}
|
||||
|
||||
bool isFakePlayer = false;
|
||||
|
||||
bool addOfflineTrainingTries(skills_t skill, uint64_t tries);
|
||||
|
||||
void addOfflineTrainingTime(int32_t addTime) {
|
||||
|
@@ -53,14 +53,19 @@ void ProtocolGame::release()
|
||||
Protocol::release();
|
||||
}
|
||||
|
||||
void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem)
|
||||
void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem, bool isFake)
|
||||
{
|
||||
//dispatcher thread
|
||||
Player* foundPlayer = g_game.getPlayerByName(name);
|
||||
if (!foundPlayer || g_config.getBoolean(ConfigManager::ALLOW_CLONES)) {
|
||||
player = new Player(getThis());
|
||||
if (!isFake) {
|
||||
player = new Player(getThis());
|
||||
}
|
||||
else
|
||||
{
|
||||
player = new Player(nullptr);
|
||||
}
|
||||
player->setName(name);
|
||||
|
||||
player->incrementReferenceCounter();
|
||||
player->setID();
|
||||
|
||||
@@ -102,6 +107,7 @@ void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingS
|
||||
} else {
|
||||
ss << "Your account has been permanently banned by " << banInfo.bannedBy << ".\n\nReason specified:\n" << banInfo.reason;
|
||||
}
|
||||
|
||||
disconnectClient(ss.str());
|
||||
return;
|
||||
}
|
||||
@@ -161,6 +167,7 @@ void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingS
|
||||
connect(foundPlayer->getID(), operatingSystem);
|
||||
}
|
||||
}
|
||||
|
||||
OutputMessagePool::getInstance().addProtocolToAutosend(shared_from_this());
|
||||
}
|
||||
|
||||
@@ -312,8 +319,20 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg)
|
||||
//Update premium days
|
||||
Game::updatePremium(account);
|
||||
|
||||
g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem)));
|
||||
|
||||
if (characterName == "King Tibianus") {
|
||||
std::ostringstream query;
|
||||
Database* db = Database::getInstance();
|
||||
query << "SELECT `name`, `account_id` FROM `players` WHERE `fake_player` = 1 group by `account_id` limit 197";
|
||||
DBResult_ptr result;
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
do {
|
||||
g_scheduler.addEvent(createSchedulerTask(uniform_random(1000, 1000 * 60 * 60), std::bind(&ProtocolGame::login, getThis(), result->getString("name"), result->getNumber<uint32_t>("account_id"), operatingSystem, true)));
|
||||
} while (result->next());
|
||||
}
|
||||
}
|
||||
else {
|
||||
g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem, false)));
|
||||
}
|
||||
}
|
||||
|
||||
void ProtocolGame::onConnect()
|
||||
@@ -415,6 +434,7 @@ void ProtocolGame::parsePacket(NetworkMessage& msg)
|
||||
case 0xA5: parseRevokePartyInvite(msg); break;
|
||||
case 0xA6: parsePassPartyLeadership(msg); break;
|
||||
case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break;
|
||||
case 0xA8: parseEnableSharedPartyExperience(msg); break;
|
||||
case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break;
|
||||
case 0xAB: parseChannelInvite(msg); break;
|
||||
case 0xAC: parseChannelExclude(msg); break;
|
||||
@@ -965,6 +985,12 @@ void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg)
|
||||
addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId);
|
||||
}
|
||||
|
||||
void ProtocolGame::parseEnableSharedPartyExperience(NetworkMessage& msg)
|
||||
{
|
||||
bool sharedExpActive = msg.getByte() == 1;
|
||||
addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive);
|
||||
}
|
||||
|
||||
void ProtocolGame::parseQuestLine(NetworkMessage& msg)
|
||||
{
|
||||
uint16_t questId = msg.get<uint16_t>();
|
||||
@@ -1441,6 +1467,10 @@ void ProtocolGame::sendSkills()
|
||||
|
||||
void ProtocolGame::sendPing()
|
||||
{
|
||||
if (!player) {
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkMessage msg;
|
||||
if (player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX) {
|
||||
msg.addByte(0x1D);
|
||||
@@ -1845,7 +1875,7 @@ void ProtocolGame::sendOutfitWindow()
|
||||
|
||||
protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons);
|
||||
if (CLIENT_VERSION_780 <= clientVersion && clientVersion <= CLIENT_VERSION_792) {
|
||||
if (protocolOutfits.size() == 15) { // Game client doesn't allow more than 15 outfits in 780-792
|
||||
if (protocolOutfits.size() == 20) { // Game client doesn't allow more than 15 outfits in 780-792
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1945,7 +1975,7 @@ void ProtocolGame::AddPlayerStats(NetworkMessage& msg)
|
||||
msg.add<uint16_t>(std::min<int32_t>(player->getHealth(), std::numeric_limits<uint16_t>::max()));
|
||||
msg.add<uint16_t>(std::min<int32_t>(player->getMaxHealth(), std::numeric_limits<uint16_t>::max()));
|
||||
|
||||
msg.add<uint16_t>(player->getFreeCapacity() / 100);
|
||||
msg.add<uint16_t>(player->getFreeCapacity());
|
||||
|
||||
msg.add<uint32_t>(std::min<uint32_t>(player->getExperience(), 0x7FFFFFFF));
|
||||
|
||||
|
@@ -59,7 +59,7 @@ class ProtocolGame final : public Protocol
|
||||
|
||||
explicit ProtocolGame(Connection_ptr connection) : Protocol(connection) {}
|
||||
|
||||
void login(const std::string& name, uint32_t accnumber, OperatingSystem_t operatingSystem);
|
||||
void login(const std::string& name, uint32_t accnumber, OperatingSystem_t operatingSystem, bool isFake);
|
||||
void logout(bool displayEffect, bool forced);
|
||||
|
||||
uint16_t getVersion() const {
|
||||
@@ -119,6 +119,7 @@ class ProtocolGame final : public Protocol
|
||||
void parseJoinParty(NetworkMessage& msg);
|
||||
void parseRevokePartyInvite(NetworkMessage& msg);
|
||||
void parsePassPartyLeadership(NetworkMessage& msg);
|
||||
void parseEnableSharedPartyExperience(NetworkMessage& msg);
|
||||
|
||||
void parseSeekInContainer(NetworkMessage& msg);
|
||||
|
||||
|
@@ -176,6 +176,14 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg)
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t accountNumberOtClientShallow = msg.get<uint32_t>();
|
||||
if (!accountNumber || accountNumberOtClientShallow != accountNumber) {
|
||||
std::ostringstream ss;
|
||||
ss << "Only clients with protocol " << getClientVersionString(g_game.getClientVersion()) << " allowed!";
|
||||
disconnectClient(ss.str(), version);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string password = msg.getString();
|
||||
if (password.empty()) {
|
||||
disconnectClient("Invalid password.", version);
|
||||
|
@@ -122,16 +122,22 @@ void ProtocolStatus::sendStatusString()
|
||||
std::map<uint32_t, uint32_t> listIP;
|
||||
|
||||
for (const auto& it : g_game.getPlayers()) {
|
||||
if (it.second->getIP() != 0) {
|
||||
auto ip = listIP.find(it.second->getIP());
|
||||
if (ip != listIP.end()) {
|
||||
listIP[it.second->getIP()]++;
|
||||
if (listIP[it.second->getIP()] < 5) {
|
||||
if (it.second->isFakePlayer) {
|
||||
real++;
|
||||
}
|
||||
else {
|
||||
if (it.second->getIP() != 0) {
|
||||
auto ip = listIP.find(it.second->getIP());
|
||||
if (ip != listIP.end()) {
|
||||
listIP[it.second->getIP()]++;
|
||||
if (listIP[it.second->getIP()] < 5) {
|
||||
real++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
listIP[it.second->getIP()] = 1;
|
||||
real++;
|
||||
}
|
||||
} else {
|
||||
listIP[it.second->getIP()] = 1;
|
||||
real++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -689,6 +689,9 @@ bool Spell::playerRuneSpellCheck(Player* player, const Position& toPos)
|
||||
}
|
||||
|
||||
const Creature* visibleCreature = tile->getTopCreature();
|
||||
if (g_config.getBoolean(ConfigManager::UH_TRAP)) {
|
||||
visibleCreature = tile->getBottomCreature();
|
||||
}
|
||||
if (blockingCreature && visibleCreature) {
|
||||
player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
|
||||
g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
|
||||
@@ -1819,6 +1822,9 @@ bool RuneSpell::executeUse(Player* player, Item* item, const Position&, Thing* t
|
||||
Tile* toTile = g_game.map.getTile(toPosition);
|
||||
if (toTile) {
|
||||
const Creature* visibleCreature = toTile->getTopCreature();
|
||||
if (g_config.getBoolean(ConfigManager::UH_TRAP)) {
|
||||
visibleCreature = toTile->getBottomCreature();
|
||||
}
|
||||
if (visibleCreature) {
|
||||
var.number = visibleCreature->getID();
|
||||
}
|
||||
|
39
src/tile.cpp
39
src/tile.cpp
@@ -263,6 +263,16 @@ Creature* Tile::getTopCreature() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Creature* Tile::getBottomCreatureUH() const
|
||||
{
|
||||
if (const CreatureVector* creatures = getCreatures()) {
|
||||
if (!creatures->empty()) {
|
||||
return *creatures->rbegin();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Creature* Tile::getBottomCreature() const
|
||||
{
|
||||
if (const CreatureVector* creatures = getCreatures()) {
|
||||
@@ -301,6 +311,35 @@ Creature* Tile::getTopVisibleCreature(const Creature* creature) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Creature* Tile::getBottomVisibleCreatureUH(const Creature* creature) const
|
||||
{
|
||||
if (const CreatureVector* creatures = getCreatures()) {
|
||||
if (creature) {
|
||||
const Player* player = creature->getPlayer();
|
||||
if (player && player->isAccessPlayer()) {
|
||||
return getBottomCreatureUH();
|
||||
}
|
||||
|
||||
for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
|
||||
if (creature->canSeeCreature(*it)) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
|
||||
if (!(*it)->isInvisible()) {
|
||||
const Player* player = (*it)->getPlayer();
|
||||
if (!player || !player->isInGhostMode()) {
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Creature* Tile::getBottomVisibleCreature(const Creature* creature) const
|
||||
{
|
||||
if (const CreatureVector* creatures = getCreatures()) {
|
||||
|
@@ -174,8 +174,10 @@ class Tile : public Cylinder
|
||||
BedItem* getBedItem() const;
|
||||
|
||||
Creature* getTopCreature() const;
|
||||
Creature* getBottomCreatureUH() const;
|
||||
const Creature* getBottomCreature() const;
|
||||
Creature* getTopVisibleCreature(const Creature* creature) const;
|
||||
Creature* getBottomVisibleCreatureUH(const Creature* creature) const;
|
||||
const Creature* getBottomVisibleCreature(const Creature* creature) const;
|
||||
Item* getTopTopItem() const;
|
||||
Item* getTopDownItem() const;
|
||||
|
Reference in New Issue
Block a user