Resolve "Merge the best from 7.40 branch"

This commit is contained in:
Erikas Kontenis
2022-04-06 14:58:52 +00:00
parent 3bd1a6f07e
commit 155da3573c
928 changed files with 43723 additions and 1221 deletions

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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);

View File

@@ -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 */
};

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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()

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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) {

View File

@@ -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));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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++;
}
}
}

View File

@@ -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();
}

View File

@@ -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()) {

View File

@@ -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;