2023-02-18 20:10:02 -03:00
|
|
|
|
// Copyright 2023 The Forgotten Server Authors. All rights reserved.
|
2022-02-22 15:41:21 -03:00
|
|
|
|
// Use of this source code is governed by the GPL-2.0 License that can be found in the LICENSE file.
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
|
|
|
|
|
#include "otpch.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include "tile.h"
|
2015-03-19 00:41:50 +01:00
|
|
|
|
|
|
|
|
|
|
#include "combat.h"
|
2022-03-18 21:20:02 +01:00
|
|
|
|
#include "configmanager.h"
|
|
|
|
|
|
#include "creature.h"
|
2013-07-07 02:30:08 +02:00
|
|
|
|
#include "game.h"
|
2022-03-18 21:20:02 +01:00
|
|
|
|
#include "housetile.h"
|
2015-03-19 00:41:50 +01:00
|
|
|
|
#include "mailbox.h"
|
|
|
|
|
|
#include "monster.h"
|
|
|
|
|
|
#include "movement.h"
|
2022-03-18 21:20:02 +01:00
|
|
|
|
#include "spectators.h"
|
2013-07-07 02:30:08 +02:00
|
|
|
|
#include "teleport.h"
|
|
|
|
|
|
#include "trashholder.h"
|
|
|
|
|
|
|
|
|
|
|
|
extern Game g_game;
|
|
|
|
|
|
extern MoveEvents* g_moveEvents;
|
|
|
|
|
|
|
2015-05-01 14:05:32 +02:00
|
|
|
|
StaticTile real_nullptr_tile(0xFFFF, 0xFFFF, 0xFF);
|
2013-09-22 02:02:19 +02:00
|
|
|
|
Tile& Tile::nullptr_tile = real_nullptr_tile;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-03-30 01:37:55 +02:00
|
|
|
|
bool Tile::hasProperty(ITEMPROPERTY prop) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (ground && ground->hasProperty(prop)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Item* item : *items) {
|
|
|
|
|
|
if (item->hasProperty(prop)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-03-30 01:37:55 +02:00
|
|
|
|
bool Tile::hasProperty(const Item* exclude, ITEMPROPERTY prop) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
assert(exclude);
|
|
|
|
|
|
|
|
|
|
|
|
if (ground && exclude != ground && ground->hasProperty(prop)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Item* item : *items) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (item != exclude && item->hasProperty(prop)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Tile::hasHeight(uint32_t n) const
|
|
|
|
|
|
{
|
|
|
|
|
|
uint32_t height = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (ground) {
|
2014-03-19 14:19:55 -04:00
|
|
|
|
if (ground->hasProperty(CONST_PROP_HASHEIGHT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
++height;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (n == height) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Item* item : *items) {
|
2014-03-19 14:19:55 -04:00
|
|
|
|
if (item->hasProperty(CONST_PROP_HASHEIGHT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
++height;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (n == height) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-09-26 07:28:59 +02:00
|
|
|
|
size_t Tile::getCreatureCount() const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
|
|
|
|
|
return creatures->size();
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-09-26 07:28:59 +02:00
|
|
|
|
size_t Tile::getItemCount() const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2013-09-26 07:28:59 +02:00
|
|
|
|
return items->size();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t Tile::getTopItemCount() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
|
|
|
|
|
return items->getTopItemCount();
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t Tile::getDownItemCount() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
|
|
|
|
|
return items->getDownItemCount();
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Teleport* Tile::getTeleportItem() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!hasFlag(TILESTATE_TELEPORT)) {
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if ((*it)->getTeleport()) {
|
|
|
|
|
|
return (*it)->getTeleport();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MagicField* Tile::getFieldItem() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!hasFlag(TILESTATE_MAGICFIELD)) {
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ground && ground->getMagicField()) {
|
|
|
|
|
|
return ground->getMagicField();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if ((*it)->getMagicField()) {
|
|
|
|
|
|
return (*it)->getMagicField();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TrashHolder* Tile::getTrashHolder() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!hasFlag(TILESTATE_TRASHHOLDER)) {
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ground && ground->getTrashHolder()) {
|
|
|
|
|
|
return ground->getTrashHolder();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if ((*it)->getTrashHolder()) {
|
|
|
|
|
|
return (*it)->getTrashHolder();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Mailbox* Tile::getMailbox() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!hasFlag(TILESTATE_MAILBOX)) {
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ground && ground->getMailbox()) {
|
|
|
|
|
|
return ground->getMailbox();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if ((*it)->getMailbox()) {
|
|
|
|
|
|
return (*it)->getMailbox();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
BedItem* Tile::getBedItem() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!hasFlag(TILESTATE_BED)) {
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ground && ground->getBed()) {
|
|
|
|
|
|
return ground->getBed();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if ((*it)->getBed()) {
|
|
|
|
|
|
return (*it)->getBed();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-05 23:11:37 +01:00
|
|
|
|
Creature* Tile::getTopCreature() const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
|
|
|
|
|
if (!creatures->empty()) {
|
|
|
|
|
|
return *creatures->begin();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Creature* Tile::getBottomCreature() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
|
|
|
|
|
if (!creatures->empty()) {
|
|
|
|
|
|
return *creatures->rbegin();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-11-05 23:11:37 +01:00
|
|
|
|
Creature* Tile::getTopVisibleCreature(const Creature* creature) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
|
|
|
|
|
if (creature) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (Creature* tileCreature : *creatures) {
|
|
|
|
|
|
if (creature->canSeeCreature(tileCreature)) {
|
|
|
|
|
|
return tileCreature;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (Creature* tileCreature : *creatures) {
|
|
|
|
|
|
if (!tileCreature->isInvisible()) {
|
|
|
|
|
|
const Player* player = tileCreature->getPlayer();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (!player || !player->isInGhostMode()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
return tileCreature;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Creature* Tile::getBottomVisibleCreature(const Creature* creature) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
|
|
|
|
|
if (creature) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (creature->canSeeCreature(*it)) {
|
|
|
|
|
|
return *it;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (!(*it)->isInvisible()) {
|
|
|
|
|
|
const Player* player = (*it)->getPlayer();
|
|
|
|
|
|
if (!player || !player->isInGhostMode()) {
|
|
|
|
|
|
return *it;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-03-29 00:08:29 +01:00
|
|
|
|
Item* Tile::getTopDownItem() const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2015-03-29 00:08:29 +01:00
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
|
|
|
|
|
return items->getTopDownItem();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-03-29 00:08:29 +01:00
|
|
|
|
Item* Tile::getTopTopItem() const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2015-03-29 00:08:29 +01:00
|
|
|
|
if (const TileItemVector* items = getItemList()) {
|
|
|
|
|
|
return items->getTopTopItem();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Item* Tile::getItemByTopOrder(int32_t topOrder)
|
|
|
|
|
|
{
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// topOrder:
|
|
|
|
|
|
// 1: borders
|
|
|
|
|
|
// 2: ladders, signs, splashes
|
|
|
|
|
|
// 3: doors etc
|
|
|
|
|
|
// 4: creatures
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (TileItemVector* items = getItemList()) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
for (auto it = ItemVector::const_reverse_iterator(items->getEndTopItem()),
|
|
|
|
|
|
end = ItemVector::const_reverse_iterator(items->getBeginTopItem());
|
|
|
|
|
|
it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (Item::items[(*it)->getID()].alwaysOnTopOrder == topOrder) {
|
|
|
|
|
|
return (*it);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Thing* Tile::getTopVisibleThing(const Creature* creature)
|
|
|
|
|
|
{
|
|
|
|
|
|
Thing* thing = getTopVisibleCreature(creature);
|
|
|
|
|
|
if (thing) {
|
|
|
|
|
|
return thing;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileItemVector* items = getItemList();
|
|
|
|
|
|
if (items) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
for (ItemVector::const_iterator it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end;
|
|
|
|
|
|
++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
const ItemType& iit = Item::items[(*it)->getID()];
|
|
|
|
|
|
if (!iit.lookThrough) {
|
|
|
|
|
|
return (*it);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
for (auto it = ItemVector::const_reverse_iterator(items->getEndTopItem()),
|
|
|
|
|
|
end = ItemVector::const_reverse_iterator(items->getBeginTopItem());
|
|
|
|
|
|
it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
const ItemType& iit = Item::items[(*it)->getID()];
|
|
|
|
|
|
if (!iit.lookThrough) {
|
|
|
|
|
|
return (*it);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return ground;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Tile::onAddTileItem(Item* item)
|
|
|
|
|
|
{
|
2014-03-19 14:19:55 -04:00
|
|
|
|
if (item->hasProperty(CONST_PROP_MOVEABLE) || item->getContainer()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
auto it = g_game.browseFields.find(this);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (it != g_game.browseFields.end()) {
|
2015-03-14 14:40:43 +01:00
|
|
|
|
it->second->addItemBack(item);
|
2021-05-06 09:09:22 -03:00
|
|
|
|
item->setParent(it->second);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
setTileFlags(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2019-10-27 13:41:40 +01:00
|
|
|
|
SpectatorVec spectators;
|
2025-11-28 21:38:42 -03:00
|
|
|
|
g_game.map.getSpectators(spectators, tilePos, true);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// send to client
|
2016-12-30 11:02:17 -02:00
|
|
|
|
for (Creature* spectator : spectators) {
|
2022-11-10 17:56:22 -03:00
|
|
|
|
if (Player* spectatorPlayer = spectator->getPlayer()) {
|
2025-11-28 21:38:42 -03:00
|
|
|
|
spectatorPlayer->sendAddTileItem(this, tilePos, item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 00:48:25 -03:00
|
|
|
|
if ((!hasFlag(TILESTATE_PROTECTIONZONE) || getBoolean(ConfigManager::CLEAN_PROTECTION_ZONES)) &&
|
2022-04-13 14:06:08 +00:00
|
|
|
|
item->isCleanable()) {
|
2025-11-19 04:58:14 -03:00
|
|
|
|
if (!getHouseTile()) {
|
2020-06-15 01:10:57 +02:00
|
|
|
|
g_game.addTileToClean(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Tile::onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType)
|
|
|
|
|
|
{
|
2014-03-19 14:19:55 -04:00
|
|
|
|
if (newItem->hasProperty(CONST_PROP_MOVEABLE) || newItem->getContainer()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
auto it = g_game.browseFields.find(this);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (it != g_game.browseFields.end()) {
|
2015-01-02 20:24:38 -06:00
|
|
|
|
int32_t index = it->second->getThingIndex(oldItem);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (index != -1) {
|
2015-01-02 20:24:38 -06:00
|
|
|
|
it->second->replaceThing(index, newItem);
|
2021-05-06 09:09:22 -03:00
|
|
|
|
newItem->setParent(it->second);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-03-19 14:19:55 -04:00
|
|
|
|
} else if (oldItem->hasProperty(CONST_PROP_MOVEABLE) || oldItem->getContainer()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
auto it = g_game.browseFields.find(this);
|
2013-07-10 21:17:47 +02:00
|
|
|
|
if (it != g_game.browseFields.end()) {
|
2025-11-28 21:38:42 -03:00
|
|
|
|
const auto oldParent = oldItem->getParent();
|
2015-01-02 20:24:38 -06:00
|
|
|
|
it->second->removeThing(oldItem, oldItem->getItemCount());
|
2013-07-10 21:17:47 +02:00
|
|
|
|
oldItem->setParent(oldParent);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2019-10-27 13:41:40 +01:00
|
|
|
|
SpectatorVec spectators;
|
2025-11-28 21:38:42 -03:00
|
|
|
|
g_game.map.getSpectators(spectators, tilePos, true);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// send to client
|
2016-12-30 11:02:17 -02:00
|
|
|
|
for (Creature* spectator : spectators) {
|
2022-11-10 17:56:22 -03:00
|
|
|
|
if (Player* spectatorPlayer = spectator->getPlayer()) {
|
2025-11-28 21:38:42 -03:00
|
|
|
|
spectatorPlayer->sendUpdateTileItem(this, tilePos, newItem);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// event methods
|
2016-12-30 11:02:17 -02:00
|
|
|
|
for (Creature* spectator : spectators) {
|
2025-11-28 21:38:42 -03:00
|
|
|
|
spectator->onUpdateTileItem(this, tilePos, oldItem, oldType, newItem, newType);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2019-10-27 13:41:40 +01:00
|
|
|
|
void Tile::onRemoveTileItem(const SpectatorVec& spectators, const std::vector<int32_t>& oldStackPosVector, Item* item)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2014-03-19 14:19:55 -04:00
|
|
|
|
if (item->hasProperty(CONST_PROP_MOVEABLE) || item->getContainer()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
auto it = g_game.browseFields.find(this);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (it != g_game.browseFields.end()) {
|
2015-01-02 20:24:38 -06:00
|
|
|
|
it->second->removeThing(item, item->getItemCount());
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
resetTileFlags(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
|
|
|
|
|
const ItemType& iType = Item::items[item->getID()];
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// send to client
|
2013-09-13 05:05:09 +02:00
|
|
|
|
size_t i = 0;
|
2016-12-30 11:02:17 -02:00
|
|
|
|
for (Creature* spectator : spectators) {
|
2013-09-13 05:05:09 +02:00
|
|
|
|
if (Player* tmpPlayer = spectator->getPlayer()) {
|
2025-11-28 21:38:42 -03:00
|
|
|
|
tmpPlayer->sendRemoveTileThing(tilePos, oldStackPosVector[i++]);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// event methods
|
2016-12-30 11:02:17 -02:00
|
|
|
|
for (Creature* spectator : spectators) {
|
2025-11-28 21:38:42 -03:00
|
|
|
|
spectator->onRemoveTileItem(this, tilePos, iType, item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2020-06-15 01:10:57 +02:00
|
|
|
|
|
2024-04-27 00:48:25 -03:00
|
|
|
|
if (!hasFlag(TILESTATE_PROTECTIONZONE) || getBoolean(ConfigManager::CLEAN_PROTECTION_ZONES)) {
|
2020-10-17 13:50:20 +02:00
|
|
|
|
auto items = getItemList();
|
|
|
|
|
|
if (!items || items->empty()) {
|
2020-06-15 01:10:57 +02:00
|
|
|
|
g_game.removeTileToClean(this);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ret = false;
|
2020-10-17 13:50:20 +02:00
|
|
|
|
for (auto toCheck : *items) {
|
2020-06-15 01:10:57 +02:00
|
|
|
|
if (toCheck->isCleanable()) {
|
|
|
|
|
|
ret = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!ret) {
|
|
|
|
|
|
g_game.removeTileToClean(this);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags, Creature*) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2015-01-03 22:17:10 -06:00
|
|
|
|
if (const Creature* creature = thing.getCreature()) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (hasBitSet(FLAG_NOLIMIT, flags)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOERROR;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_FLOORCHANGE | TILESTATE_TELEPORT)) {
|
|
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!ground) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const Monster* monster = creature->getMonster()) {
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_PROTECTIONZONE | TILESTATE_FLOORCHANGE | TILESTATE_TELEPORT)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-12-02 11:52:04 +01:00
|
|
|
|
const CreatureVector* creatures = getCreatures();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (monster->canPushCreatures() && !monster->isSummon()) {
|
|
|
|
|
|
if (creatures) {
|
2013-11-06 18:38:58 +01:00
|
|
|
|
for (Creature* tileCreature : *creatures) {
|
2013-08-01 00:33:33 +02:00
|
|
|
|
if (tileCreature->getPlayer() && tileCreature->getPlayer()->isInGhostMode()) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-08-01 00:33:33 +02:00
|
|
|
|
const Monster* creatureMonster = tileCreature->getMonster();
|
|
|
|
|
|
if (!creatureMonster || !tileCreature->isPushable() ||
|
2022-04-13 14:06:08 +00:00
|
|
|
|
(creatureMonster->isSummon() && creatureMonster->getMaster()->getPlayer())) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (creatures && !creatures->empty()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Creature* tileCreature : *creatures) {
|
|
|
|
|
|
if (!tileCreature->isInGhostMode()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
if (hasFlag(TILESTATE_BLOCKSOLID) ||
|
|
|
|
|
|
(hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_NOFIELDBLOCKPATH))) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (!(monster->canPushItems() || hasBitSet(FLAG_IGNOREBLOCKITEM, flags))) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
MagicField* field = getFieldItem();
|
2017-12-23 23:47:38 -02:00
|
|
|
|
if (!field || field->isBlocking() || field->getDamage() == 0) {
|
2017-06-01 01:14:57 -03:00
|
|
|
|
return RETURNVALUE_NOERROR;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CombatType_t combatType = field->getCombatType();
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// There is 3 options for a monster to enter a magic field
|
|
|
|
|
|
// 1) Monster is immune
|
2017-06-01 01:14:57 -03:00
|
|
|
|
if (!monster->isImmune(combatType)) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// 1) Monster is able to walk over field type
|
|
|
|
|
|
// 2) Being attacked while random stepping will make it ignore
|
|
|
|
|
|
// field damages
|
2017-06-01 01:14:57 -03:00
|
|
|
|
if (hasBitSet(FLAG_IGNOREFIELDDAMAGE, flags)) {
|
2017-12-23 23:47:38 -02:00
|
|
|
|
if (!(monster->canWalkOnFieldType(combatType) || monster->isIgnoringFieldDamage())) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2017-06-01 01:14:57 -03:00
|
|
|
|
} else {
|
|
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOERROR;
|
2014-12-02 11:52:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const CreatureVector* creatures = getCreatures();
|
|
|
|
|
|
if (const Player* player = creature->getPlayer()) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags) &&
|
|
|
|
|
|
!player->isAccessPlayer()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Creature* tileCreature : *creatures) {
|
|
|
|
|
|
if (!player->canWalkthrough(tileCreature)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-07 10:47:42 +01:00
|
|
|
|
if (MagicField* field = getFieldItem()) {
|
|
|
|
|
|
if (field->getDamage() != 0 && hasBitSet(FLAG_PATHFINDING, flags) &&
|
|
|
|
|
|
!hasBitSet(FLAG_IGNOREFIELDDAMAGE, flags)) {
|
|
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
|
|
|
|
|
}
|
2022-11-01 09:01:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-22 14:31:04 -03:00
|
|
|
|
if (!player->hasParent() && hasFlag(TILESTATE_NOLOGOUT)) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// player is trying to login to a "no logout" tile
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-03-12 22:12:58 +01:00
|
|
|
|
const Tile* playerTile = player->getTile();
|
|
|
|
|
|
if (playerTile && player->isPzLocked()) {
|
|
|
|
|
|
if (!playerTile->hasFlag(TILESTATE_PVPZONE)) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// player is trying to enter a pvp zone while being pz-locked
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (hasFlag(TILESTATE_PVPZONE)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_PLAYERISPZLOCKEDENTERPVPZONE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2015-03-12 22:12:58 +01:00
|
|
|
|
} else if (!hasFlag(TILESTATE_PVPZONE)) {
|
|
|
|
|
|
// player is trying to leave a pvp zone while being pz-locked
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_PLAYERISPZLOCKEDLEAVEPVPZONE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-03-12 22:12:58 +01:00
|
|
|
|
if ((!playerTile->hasFlag(TILESTATE_NOPVPZONE) && hasFlag(TILESTATE_NOPVPZONE)) ||
|
2022-04-13 14:06:08 +00:00
|
|
|
|
(!playerTile->hasFlag(TILESTATE_PROTECTIONZONE) && hasFlag(TILESTATE_PROTECTIONZONE))) {
|
2015-03-12 22:12:58 +01:00
|
|
|
|
// player is trying to enter a non-pvp/protection zone while being pz-locked
|
2015-03-11 18:38:56 +01:00
|
|
|
|
return RETURNVALUE_PLAYERISPZLOCKED;
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
} else if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags)) {
|
2013-09-24 13:46:58 +02:00
|
|
|
|
for (const Creature* tileCreature : *creatures) {
|
|
|
|
|
|
if (!tileCreature->isInGhostMode()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-05-12 14:57:21 +02:00
|
|
|
|
if (!hasBitSet(FLAG_IGNOREBLOCKITEM, flags)) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// If the FLAG_IGNOREBLOCKITEM bit isn't set we dont have to iterate every single item
|
2015-05-12 14:57:21 +02:00
|
|
|
|
if (hasFlag(TILESTATE_BLOCKSOLID)) {
|
|
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// FLAG_IGNOREBLOCKITEM is set
|
2015-05-12 14:57:21 +02:00
|
|
|
|
if (ground) {
|
|
|
|
|
|
const ItemType& iiType = Item::items[ground->getID()];
|
|
|
|
|
|
if (iiType.blockSolid && (!iiType.moveable || ground->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID))) {
|
|
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2015-05-12 14:57:21 +02:00
|
|
|
|
}
|
2015-05-31 23:38:35 +02:00
|
|
|
|
|
2015-05-12 14:57:21 +02:00
|
|
|
|
if (const auto items = getItemList()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Item* item : *items) {
|
2013-08-01 00:33:33 +02:00
|
|
|
|
const ItemType& iiType = Item::items[item->getID()];
|
2014-03-19 14:36:30 -04:00
|
|
|
|
if (iiType.blockSolid && (!iiType.moveable || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID))) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2015-01-03 22:17:10 -06:00
|
|
|
|
} else if (const Item* item = thing.getItem()) {
|
2014-12-02 11:52:04 +01:00
|
|
|
|
const TileItemVector* items = getItemList();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (items && items->size() >= 0xFFFF) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (hasBitSet(FLAG_NOLIMIT, flags)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOERROR;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-22 11:53:39 +02:00
|
|
|
|
if (item->isStoreItem()) {
|
|
|
|
|
|
return RETURNVALUE_ITEMCANNOTBEMOVEDTHERE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-07 02:30:08 +02:00
|
|
|
|
bool itemIsHangable = item->isHangable();
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!ground && !itemIsHangable) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-12-02 11:52:04 +01:00
|
|
|
|
const CreatureVector* creatures = getCreatures();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (creatures && !creatures->empty() && item->isBlocking() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags)) {
|
2013-09-24 13:46:58 +02:00
|
|
|
|
for (const Creature* tileCreature : *creatures) {
|
|
|
|
|
|
if (!tileCreature->isInGhostMode()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-14 21:06:11 +02:00
|
|
|
|
if (itemIsHangable && hasFlag(TILESTATE_SUPPORTS_HANGABLE)) {
|
|
|
|
|
|
if (items) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Item* tileItem : *items) {
|
|
|
|
|
|
if (tileItem->isHangable()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NEEDEXCHANGE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-07-14 21:06:11 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
if (ground) {
|
|
|
|
|
|
const ItemType& iiType = Item::items[ground->getID()];
|
|
|
|
|
|
if (iiType.blockSolid) {
|
|
|
|
|
|
if (!iiType.allowPickupable || item->isMagicField() || item->isBlocking()) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (!item->isPickupable()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!iiType.hasHeight || iiType.pickupable || iiType.isBed()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-07-14 21:06:11 +02:00
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2013-07-14 21:06:11 +02:00
|
|
|
|
if (items) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Item* tileItem : *items) {
|
|
|
|
|
|
const ItemType& iiType = Item::items[tileItem->getID()];
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (!iiType.blockSolid) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (iiType.allowPickupable && !item->isMagicField() && !item->isBlocking()) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!item->isPickupable()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!iiType.hasHeight || iiType.pickupable || iiType.isBed()) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTENOUGHROOM;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOERROR;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
ReturnValue Tile::queryMaxCount(int32_t, const Thing&, uint32_t count, uint32_t& maxQueryCount, uint32_t) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
maxQueryCount = std::max<uint32_t>(1, count);
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOERROR;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-10-18 23:46:09 +02:00
|
|
|
|
ReturnValue Tile::queryRemove(const Thing& thing, uint32_t count, uint32_t flags, Creature* /*= nullptr */) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2015-01-03 22:17:10 -06:00
|
|
|
|
int32_t index = getThingIndex(&thing);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (index == -1) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-03 22:17:10 -06:00
|
|
|
|
const Item* item = thing.getItem();
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!item) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (count == 0 || (item->isStackable() && count > item->getItemCount())) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTPOSSIBLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-12-26 04:02:00 +01:00
|
|
|
|
if (!item->isMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOTMOVEABLE;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return RETURNVALUE_NOERROR;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-03 22:17:10 -06:00
|
|
|
|
Tile* Tile::queryDestination(int32_t&, const Thing&, Item** destItem, uint32_t& flags)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2013-09-22 02:02:19 +02:00
|
|
|
|
Tile* destTile = nullptr;
|
|
|
|
|
|
*destItem = nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_FLOORCHANGE_DOWN)) {
|
2014-12-25 02:52:40 +01:00
|
|
|
|
uint16_t dx = tilePos.x;
|
|
|
|
|
|
uint16_t dy = tilePos.y;
|
|
|
|
|
|
uint8_t dz = tilePos.z + 1;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-19 07:28:58 -06:00
|
|
|
|
Tile* southDownTile = g_game.map.getTile(dx, dy - 1, dz);
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (southDownTile && southDownTile->hasFlag(TILESTATE_FLOORCHANGE_SOUTH_ALT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
dy -= 2;
|
2015-01-19 07:28:58 -06:00
|
|
|
|
destTile = g_game.map.getTile(dx, dy, dz);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
} else {
|
2015-01-19 07:28:58 -06:00
|
|
|
|
Tile* eastDownTile = g_game.map.getTile(dx - 1, dy, dz);
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (eastDownTile && eastDownTile->hasFlag(TILESTATE_FLOORCHANGE_EAST_ALT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
dx -= 2;
|
2015-01-19 07:28:58 -06:00
|
|
|
|
destTile = g_game.map.getTile(dx, dy, dz);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
} else {
|
2015-01-19 07:28:58 -06:00
|
|
|
|
Tile* downTile = g_game.map.getTile(dx, dy, dz);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (downTile) {
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (downTile->hasFlag(TILESTATE_FLOORCHANGE_NORTH)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
++dy;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (downTile->hasFlag(TILESTATE_FLOORCHANGE_SOUTH)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
--dy;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (downTile->hasFlag(TILESTATE_FLOORCHANGE_SOUTH_ALT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
dy -= 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (downTile->hasFlag(TILESTATE_FLOORCHANGE_EAST)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
--dx;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (downTile->hasFlag(TILESTATE_FLOORCHANGE_EAST_ALT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
dx -= 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (downTile->hasFlag(TILESTATE_FLOORCHANGE_WEST)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
++dx;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-19 07:28:58 -06:00
|
|
|
|
destTile = g_game.map.getTile(dx, dy, dz);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2016-01-22 22:41:54 +01:00
|
|
|
|
} else if (hasFlag(TILESTATE_FLOORCHANGE)) {
|
2014-12-25 02:52:40 +01:00
|
|
|
|
uint16_t dx = tilePos.x;
|
|
|
|
|
|
uint16_t dy = tilePos.y;
|
|
|
|
|
|
uint8_t dz = tilePos.z - 1;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_FLOORCHANGE_NORTH)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
--dy;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_FLOORCHANGE_SOUTH)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
++dy;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_FLOORCHANGE_EAST)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
++dx;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_FLOORCHANGE_WEST)) {
|
2014-01-01 06:46:50 +01:00
|
|
|
|
--dx;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_FLOORCHANGE_SOUTH_ALT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
dy += 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (hasFlag(TILESTATE_FLOORCHANGE_EAST_ALT)) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
dx += 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-19 07:28:58 -06:00
|
|
|
|
destTile = g_game.map.getTile(dx, dy, dz);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!destTile) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
destTile = this;
|
|
|
|
|
|
} else {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
flags |= FLAG_NOLIMIT; // Will ignore that there is blocking items/creatures
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (destTile) {
|
|
|
|
|
|
Thing* destThing = destTile->getTopDownItem();
|
|
|
|
|
|
if (destThing) {
|
|
|
|
|
|
*destItem = destThing->getItem();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return destTile;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
void Tile::addThing(int32_t, Thing* thing)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
Creature* creature = thing->getCreature();
|
|
|
|
|
|
if (creature) {
|
2015-03-22 04:34:08 -05:00
|
|
|
|
g_game.map.clearSpectatorCache();
|
2019-10-30 15:00:45 +01:00
|
|
|
|
if (creature->getPlayer()) {
|
|
|
|
|
|
g_game.map.clearPlayersSpectatorCache();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-07 02:30:08 +02:00
|
|
|
|
creature->setParent(this);
|
|
|
|
|
|
CreatureVector* creatures = makeCreatures();
|
|
|
|
|
|
creatures->insert(creatures->begin(), creature);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Item* item = thing->getItem();
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!item) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return /*RETURNVALUE_NOTPOSSIBLE*/;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileItemVector* items = getItemList();
|
2015-03-15 19:37:04 +01:00
|
|
|
|
if (items && items->size() >= 0xFFFF) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return /*RETURNVALUE_NOTPOSSIBLE*/;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
item->setParent(this);
|
|
|
|
|
|
|
2015-04-13 17:35:02 +02:00
|
|
|
|
const ItemType& itemType = Item::items[item->getID()];
|
|
|
|
|
|
if (itemType.isGroundTile()) {
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!ground) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
ground = item;
|
|
|
|
|
|
onAddTileItem(item);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const ItemType& oldType = Item::items[ground->getID()];
|
|
|
|
|
|
|
|
|
|
|
|
Item* oldGround = ground;
|
2013-09-22 02:02:19 +02:00
|
|
|
|
ground->setParent(nullptr);
|
2013-08-06 01:03:22 +02:00
|
|
|
|
g_game.ReleaseItem(ground);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
ground = item;
|
2015-01-10 00:16:36 +01:00
|
|
|
|
resetTileFlags(oldGround);
|
|
|
|
|
|
setTileFlags(item);
|
2015-04-13 17:35:02 +02:00
|
|
|
|
onUpdateTileItem(oldGround, oldType, item, itemType);
|
2015-03-19 00:41:50 +01:00
|
|
|
|
postRemoveNotification(oldGround, nullptr, 0);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2015-04-13 17:35:02 +02:00
|
|
|
|
} else if (itemType.alwaysOnTop) {
|
|
|
|
|
|
if (itemType.isSplash() && items) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// remove old splash if exists
|
|
|
|
|
|
for (ItemVector::const_iterator it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end;
|
|
|
|
|
|
++it) {
|
2015-04-13 17:35:02 +02:00
|
|
|
|
Item* oldSplash = *it;
|
|
|
|
|
|
if (!Item::items[oldSplash->getID()].isSplash()) {
|
|
|
|
|
|
continue;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2015-04-13 17:35:02 +02:00
|
|
|
|
|
|
|
|
|
|
removeThing(oldSplash, 1);
|
|
|
|
|
|
oldSplash->setParent(nullptr);
|
|
|
|
|
|
g_game.ReleaseItem(oldSplash);
|
|
|
|
|
|
postRemoveNotification(oldSplash, nullptr, 0);
|
|
|
|
|
|
break;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool isInserted = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (items) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// Note: this is different from internalAddThing
|
2025-11-15 00:21:14 -03:00
|
|
|
|
if (itemType.alwaysOnTopOrder < Item::items[(*it)->getID()].alwaysOnTopOrder) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
items->insert(it, item);
|
|
|
|
|
|
isInserted = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
items = makeItemList();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!isInserted) {
|
|
|
|
|
|
items->push_back(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
onAddTileItem(item);
|
|
|
|
|
|
} else {
|
2015-04-13 17:35:02 +02:00
|
|
|
|
if (itemType.isMagicField()) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// remove old field item if exists
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (items) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
for (ItemVector::const_iterator it = items->getBeginDownItem(), end = items->getEndDownItem();
|
|
|
|
|
|
it != end; ++it) {
|
2013-07-13 16:01:23 +02:00
|
|
|
|
MagicField* oldField = (*it)->getMagicField();
|
|
|
|
|
|
if (oldField) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (oldField->isReplaceable()) {
|
2015-01-02 20:24:38 -06:00
|
|
|
|
removeThing(oldField, 1);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2013-09-22 02:02:19 +02:00
|
|
|
|
oldField->setParent(nullptr);
|
2013-08-06 01:03:22 +02:00
|
|
|
|
g_game.ReleaseItem(oldField);
|
2015-03-19 00:41:50 +01:00
|
|
|
|
postRemoveNotification(oldField, nullptr, 0);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
break;
|
|
|
|
|
|
} else {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// This magic field cannot be replaced.
|
2013-09-22 02:02:19 +02:00
|
|
|
|
item->setParent(nullptr);
|
2013-08-06 01:03:22 +02:00
|
|
|
|
g_game.ReleaseItem(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
items = makeItemList();
|
|
|
|
|
|
items->insert(items->getBeginDownItem(), item);
|
2015-08-20 04:45:41 +02:00
|
|
|
|
items->addDownItemCount(1);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
onAddTileItem(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
void Tile::updateThing(Thing* thing, uint16_t itemId, uint32_t count)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2015-01-02 20:24:38 -06:00
|
|
|
|
int32_t index = getThingIndex(thing);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (index == -1) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return /*RETURNVALUE_NOTPOSSIBLE*/;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Item* item = thing->getItem();
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!item) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return /*RETURNVALUE_NOTPOSSIBLE*/;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const ItemType& oldType = Item::items[item->getID()];
|
|
|
|
|
|
const ItemType& newType = Item::items[itemId];
|
2015-01-10 00:16:36 +01:00
|
|
|
|
resetTileFlags(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
item->setID(itemId);
|
|
|
|
|
|
item->setSubType(count);
|
2015-01-10 00:16:36 +01:00
|
|
|
|
setTileFlags(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
onUpdateTileItem(item, oldType, item, newType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
void Tile::replaceThing(uint32_t index, Thing* thing)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
int32_t pos = index;
|
|
|
|
|
|
|
|
|
|
|
|
Item* item = thing->getItem();
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!item) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return /*RETURNVALUE_NOTPOSSIBLE*/;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2013-09-22 02:02:19 +02:00
|
|
|
|
Item* oldItem = nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
bool isInserted = false;
|
|
|
|
|
|
|
|
|
|
|
|
if (ground) {
|
|
|
|
|
|
if (pos == 0) {
|
|
|
|
|
|
oldItem = ground;
|
|
|
|
|
|
ground = item;
|
|
|
|
|
|
isInserted = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
--pos;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileItemVector* items = getItemList();
|
|
|
|
|
|
if (items && !isInserted) {
|
|
|
|
|
|
int32_t topItemSize = getTopItemCount();
|
|
|
|
|
|
if (pos < topItemSize) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
auto it = items->getBeginTopItem();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
it += pos;
|
|
|
|
|
|
|
|
|
|
|
|
oldItem = (*it);
|
|
|
|
|
|
it = items->erase(it);
|
|
|
|
|
|
items->insert(it, item);
|
|
|
|
|
|
isInserted = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pos -= topItemSize;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CreatureVector* creatures = getCreatures();
|
|
|
|
|
|
if (creatures) {
|
2014-10-26 00:39:47 +02:00
|
|
|
|
if (!isInserted && pos < static_cast<int32_t>(creatures->size())) {
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return /*RETURNVALUE_NOTPOSSIBLE*/;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2014-10-26 00:39:47 +02:00
|
|
|
|
pos -= static_cast<uint32_t>(creatures->size());
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (items && !isInserted) {
|
|
|
|
|
|
int32_t downItemSize = getDownItemCount();
|
|
|
|
|
|
if (pos < downItemSize) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
auto it = items->getBeginDownItem() + pos;
|
2013-09-23 03:34:15 +02:00
|
|
|
|
oldItem = *it;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
it = items->erase(it);
|
|
|
|
|
|
items->insert(it, item);
|
|
|
|
|
|
isInserted = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isInserted) {
|
|
|
|
|
|
item->setParent(this);
|
|
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
resetTileFlags(oldItem);
|
|
|
|
|
|
setTileFlags(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
const ItemType& oldType = Item::items[oldItem->getID()];
|
|
|
|
|
|
const ItemType& newType = Item::items[item->getID()];
|
|
|
|
|
|
onUpdateTileItem(oldItem, oldType, item, newType);
|
|
|
|
|
|
|
2013-09-22 02:02:19 +02:00
|
|
|
|
oldItem->setParent(nullptr);
|
2014-10-04 19:30:47 +02:00
|
|
|
|
return /*RETURNVALUE_NOERROR*/;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
void Tile::removeThing(Thing* thing, uint32_t count)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
Creature* creature = thing->getCreature();
|
|
|
|
|
|
if (creature) {
|
|
|
|
|
|
CreatureVector* creatures = getCreatures();
|
|
|
|
|
|
if (creatures) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
auto it = std::find(creatures->begin(), creatures->end(), thing);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (it != creatures->end()) {
|
2015-03-22 04:34:08 -05:00
|
|
|
|
g_game.map.clearSpectatorCache();
|
2019-10-30 15:00:45 +01:00
|
|
|
|
if (creature->getPlayer()) {
|
|
|
|
|
|
g_game.map.clearPlayersSpectatorCache();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-07 02:30:08 +02:00
|
|
|
|
creatures->erase(it);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Item* item = thing->getItem();
|
2013-09-24 04:21:32 +02:00
|
|
|
|
if (!item) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-02 20:24:38 -06:00
|
|
|
|
int32_t index = getThingIndex(item);
|
2013-09-24 04:21:32 +02:00
|
|
|
|
if (index == -1) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item == ground) {
|
|
|
|
|
|
ground->setParent(nullptr);
|
|
|
|
|
|
ground = nullptr;
|
2015-03-22 04:08:59 +01:00
|
|
|
|
|
2019-10-27 13:41:40 +01:00
|
|
|
|
SpectatorVec spectators;
|
2016-12-30 11:02:17 -02:00
|
|
|
|
g_game.map.getSpectators(spectators, getPosition(), true);
|
|
|
|
|
|
onRemoveTileItem(spectators, std::vector<int32_t>(spectators.size(), 0), item);
|
2013-09-24 04:21:32 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileItemVector* items = getItemList();
|
|
|
|
|
|
if (!items) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-13 17:35:02 +02:00
|
|
|
|
const ItemType& itemType = Item::items[item->getID()];
|
|
|
|
|
|
if (itemType.alwaysOnTop) {
|
2013-09-24 04:21:32 +02:00
|
|
|
|
auto it = std::find(items->getBeginTopItem(), items->getEndTopItem(), item);
|
|
|
|
|
|
if (it == items->getEndTopItem()) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-04-09 14:54:27 -04:00
|
|
|
|
std::vector<int32_t> oldStackPosVector;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2019-10-27 13:41:40 +01:00
|
|
|
|
SpectatorVec spectators;
|
2016-12-30 11:02:17 -02:00
|
|
|
|
g_game.map.getSpectators(spectators, getPosition(), true);
|
|
|
|
|
|
for (Creature* spectator : spectators) {
|
2022-11-10 17:56:22 -03:00
|
|
|
|
if (Player* spectatorPlayer = spectator->getPlayer()) {
|
|
|
|
|
|
oldStackPosVector.push_back(getStackposOfItem(spectatorPlayer, item));
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2013-09-24 04:21:32 +02:00
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2013-09-24 04:21:32 +02:00
|
|
|
|
item->setParent(nullptr);
|
|
|
|
|
|
items->erase(it);
|
2016-12-30 11:02:17 -02:00
|
|
|
|
onRemoveTileItem(spectators, oldStackPosVector, item);
|
2013-09-24 04:21:32 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
auto it = std::find(items->getBeginDownItem(), items->getEndDownItem(), item);
|
|
|
|
|
|
if (it == items->getEndDownItem()) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-13 17:35:02 +02:00
|
|
|
|
if (itemType.stackable && count != item->getItemCount()) {
|
2022-04-13 14:06:08 +00:00
|
|
|
|
uint8_t newCount =
|
|
|
|
|
|
static_cast<uint8_t>(std::max<int32_t>(0, static_cast<int32_t>(item->getItemCount() - count)));
|
2013-09-24 04:21:32 +02:00
|
|
|
|
item->setItemCount(newCount);
|
|
|
|
|
|
onUpdateTileItem(item, itemType, item, itemType);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
} else {
|
2014-04-09 14:54:27 -04:00
|
|
|
|
std::vector<int32_t> oldStackPosVector;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2019-10-27 13:41:40 +01:00
|
|
|
|
SpectatorVec spectators;
|
2016-12-30 11:02:17 -02:00
|
|
|
|
g_game.map.getSpectators(spectators, getPosition(), true);
|
|
|
|
|
|
for (Creature* spectator : spectators) {
|
2022-11-10 17:56:22 -03:00
|
|
|
|
if (Player* spectatorPlayer = spectator->getPlayer()) {
|
|
|
|
|
|
oldStackPosVector.push_back(getStackposOfItem(spectatorPlayer, item));
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-09-24 04:21:32 +02:00
|
|
|
|
|
|
|
|
|
|
item->setParent(nullptr);
|
|
|
|
|
|
items->erase(it);
|
2015-08-20 04:45:41 +02:00
|
|
|
|
items->addDownItemCount(-1);
|
2016-12-30 11:02:17 -02:00
|
|
|
|
onRemoveTileItem(spectators, oldStackPosVector, item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-12 22:53:22 -04:00
|
|
|
|
bool Tile::hasCreature(Creature* creature) const
|
|
|
|
|
|
{
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
|
|
|
|
|
return std::find(creatures->begin(), creatures->end(), creature) != creatures->end();
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-03-19 00:41:50 +01:00
|
|
|
|
void Tile::removeCreature(Creature* creature)
|
|
|
|
|
|
{
|
2015-05-01 14:05:32 +02:00
|
|
|
|
g_game.map.getQTNode(tilePos.x, tilePos.y)->removeCreature(creature);
|
2015-03-19 00:41:50 +01:00
|
|
|
|
removeThing(creature, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
int32_t Tile::getThingIndex(const Thing* thing) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2013-07-13 16:01:23 +02:00
|
|
|
|
int32_t n = -1;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (ground) {
|
|
|
|
|
|
if (ground == thing) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
++n;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TileItemVector* items = getItemList();
|
|
|
|
|
|
if (items) {
|
2014-04-09 14:54:27 -04:00
|
|
|
|
const Item* item = thing->getItem();
|
|
|
|
|
|
if (item && item->isAlwaysOnTop()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
++n;
|
2014-04-09 14:54:27 -04:00
|
|
|
|
if (*it == item) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return n;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
n += items->getTopItemCount();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
|
|
|
|
|
if (thing->getCreature()) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (Creature* creature : *creatures) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
++n;
|
2013-09-23 03:34:15 +02:00
|
|
|
|
if (creature == thing) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return n;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
n += creatures->size();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-04-09 14:54:27 -04:00
|
|
|
|
if (items) {
|
|
|
|
|
|
const Item* item = thing->getItem();
|
|
|
|
|
|
if (item && !item->isAlwaysOnTop()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
|
2014-04-09 14:54:27 -04:00
|
|
|
|
++n;
|
|
|
|
|
|
if (*it == item) {
|
|
|
|
|
|
return n;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int32_t Tile::getClientIndexOfCreature(const Player* player, const Creature* creature) const
|
|
|
|
|
|
{
|
|
|
|
|
|
int32_t n;
|
|
|
|
|
|
if (ground) {
|
|
|
|
|
|
n = 1;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
n = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TileItemVector* items = getItemList();
|
|
|
|
|
|
if (items) {
|
|
|
|
|
|
n += items->getTopItemCount();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
2021-10-23 01:56:39 -03:00
|
|
|
|
for (auto it = creatures->rbegin(), end = creatures->rend(); it != end; ++it) {
|
|
|
|
|
|
const Creature* c = (*it);
|
2014-04-09 14:54:27 -04:00
|
|
|
|
if (c == creature) {
|
|
|
|
|
|
return n;
|
|
|
|
|
|
} else if (player->canSeeCreature(c)) {
|
|
|
|
|
|
++n;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-03-12 22:22:17 +01:00
|
|
|
|
int32_t Tile::getStackposOfItem(const Player* player, const Item* item) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2013-09-24 13:46:58 +02:00
|
|
|
|
int32_t n = 0;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (ground) {
|
2015-03-12 22:22:17 +01:00
|
|
|
|
if (ground == item) {
|
2013-09-24 13:46:58 +02:00
|
|
|
|
return n;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
++n;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TileItemVector* items = getItemList();
|
|
|
|
|
|
if (items) {
|
2015-03-12 22:22:17 +01:00
|
|
|
|
if (item->isAlwaysOnTop()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
|
2014-04-09 14:54:27 -04:00
|
|
|
|
if (*it == item) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return n;
|
2023-11-21 20:08:41 -03:00
|
|
|
|
} else if (++n == MAX_STACKPOS) {
|
2014-04-09 14:54:27 -04:00
|
|
|
|
return -1;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
n += items->getTopItemCount();
|
2023-11-21 20:08:41 -03:00
|
|
|
|
if (n >= MAX_STACKPOS) {
|
2014-04-09 14:54:27 -04:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
2015-03-12 22:22:17 +01:00
|
|
|
|
for (const Creature* creature : *creatures) {
|
|
|
|
|
|
if (player->canSeeCreature(creature)) {
|
2023-11-21 20:08:41 -03:00
|
|
|
|
if (++n >= MAX_STACKPOS) {
|
2014-04-09 14:54:27 -04:00
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-13 17:35:02 +02:00
|
|
|
|
if (items && !item->isAlwaysOnTop()) {
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) {
|
2015-04-13 17:35:02 +02:00
|
|
|
|
if (*it == item) {
|
|
|
|
|
|
return n;
|
2023-11-21 20:08:41 -03:00
|
|
|
|
} else if (++n >= MAX_STACKPOS) {
|
2015-04-13 17:35:02 +02:00
|
|
|
|
return -1;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-12-02 10:02:25 +01:00
|
|
|
|
uint32_t Tile::getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
uint32_t count = 0;
|
|
|
|
|
|
if (ground && ground->getID() == itemId) {
|
|
|
|
|
|
count += Item::countByType(ground, subType);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TileItemVector* items = getItemList();
|
|
|
|
|
|
if (items) {
|
2013-09-23 03:34:15 +02:00
|
|
|
|
for (const Item* item : *items) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
if (item->getID() == itemId) {
|
|
|
|
|
|
count += Item::countByType(item, subType);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return count;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-02 20:24:38 -06:00
|
|
|
|
Thing* Tile::getThing(size_t index) const
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (ground) {
|
|
|
|
|
|
if (index == 0) {
|
|
|
|
|
|
return ground;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
--index;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const TileItemVector* items = getItemList();
|
|
|
|
|
|
if (items) {
|
|
|
|
|
|
uint32_t topItemSize = items->getTopItemCount();
|
|
|
|
|
|
if (index < topItemSize) {
|
2015-08-20 04:45:41 +02:00
|
|
|
|
return items->at(items->getDownItemCount() + index);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
index -= topItemSize;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (const CreatureVector* creatures = getCreatures()) {
|
2013-12-23 20:05:20 +01:00
|
|
|
|
if (index < creatures->size()) {
|
|
|
|
|
|
return (*creatures)[index];
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2013-12-23 20:05:20 +01:00
|
|
|
|
index -= creatures->size();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-13 17:35:02 +02:00
|
|
|
|
if (items && index < items->getDownItemCount()) {
|
|
|
|
|
|
return items->at(index);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2013-09-22 02:02:19 +02:00
|
|
|
|
return nullptr;
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-28 21:38:42 -03:00
|
|
|
|
void Tile::postAddNotification(Thing* thing, const Thing* oldParent, int32_t index,
|
|
|
|
|
|
ReceiverLink_t link /*= LINK_OWNER*/)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2019-10-27 13:41:40 +01:00
|
|
|
|
SpectatorVec spectators;
|
2016-12-30 11:02:17 -02:00
|
|
|
|
g_game.map.getSpectators(spectators, getPosition(), true, true);
|
|
|
|
|
|
for (Creature* spectator : spectators) {
|
2022-11-10 17:56:22 -03:00
|
|
|
|
assert(dynamic_cast<Player*>(spectator) != nullptr);
|
|
|
|
|
|
static_cast<Player*>(spectator)->postAddNotification(thing, oldParent, index, LINK_NEAR);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// add a reference to this item, it may be deleted after being added (mailbox for example)
|
2013-08-06 01:03:22 +02:00
|
|
|
|
Creature* creature = thing->getCreature();
|
2013-09-13 05:05:09 +02:00
|
|
|
|
Item* item;
|
2013-08-06 01:03:22 +02:00
|
|
|
|
if (creature) {
|
2015-03-19 00:41:50 +01:00
|
|
|
|
creature->incrementReferenceCounter();
|
2013-09-22 02:02:19 +02:00
|
|
|
|
item = nullptr;
|
2013-08-06 01:03:22 +02:00
|
|
|
|
} else {
|
|
|
|
|
|
item = thing->getItem();
|
|
|
|
|
|
if (item) {
|
2015-03-19 00:41:50 +01:00
|
|
|
|
item->incrementReferenceCounter();
|
2013-08-06 01:03:22 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
|
|
|
|
|
if (link == LINK_OWNER) {
|
|
|
|
|
|
if (hasFlag(TILESTATE_TELEPORT)) {
|
|
|
|
|
|
Teleport* teleport = getTeleportItem();
|
|
|
|
|
|
if (teleport) {
|
2015-01-02 20:24:38 -06:00
|
|
|
|
teleport->addThing(thing);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
} else if (hasFlag(TILESTATE_TRASHHOLDER)) {
|
|
|
|
|
|
TrashHolder* trashholder = getTrashHolder();
|
|
|
|
|
|
if (trashholder) {
|
2015-01-02 20:24:38 -06:00
|
|
|
|
trashholder->addThing(thing);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
} else if (hasFlag(TILESTATE_MAILBOX)) {
|
|
|
|
|
|
Mailbox* mailbox = getMailbox();
|
|
|
|
|
|
if (mailbox) {
|
2015-01-02 20:24:38 -06:00
|
|
|
|
mailbox->addThing(thing);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2014-09-08 22:17:00 +02:00
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// calling movement scripts
|
2017-01-08 15:45:17 -02:00
|
|
|
|
if (creature) {
|
|
|
|
|
|
g_moveEvents->onCreatureMove(creature, this, MOVE_EVENT_STEP_IN);
|
|
|
|
|
|
} else if (item) {
|
2014-09-08 22:17:00 +02:00
|
|
|
|
g_moveEvents->onItemMove(item, this, true);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// release the reference to this item onces we are finished
|
2013-08-06 01:03:22 +02:00
|
|
|
|
if (creature) {
|
|
|
|
|
|
g_game.ReleaseCreature(creature);
|
|
|
|
|
|
} else if (item) {
|
|
|
|
|
|
g_game.ReleaseItem(item);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-28 21:38:42 -03:00
|
|
|
|
void Tile::postRemoveNotification(Thing* thing, const Thing* newParent, int32_t index, ReceiverLink_t)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2025-11-11 14:10:44 -03:00
|
|
|
|
const auto thingCount = getThingCount();
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2025-11-11 14:10:44 -03:00
|
|
|
|
SpectatorVec spectators;
|
|
|
|
|
|
g_game.map.getSpectators(spectators, tilePos, true, true);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2016-12-30 11:02:17 -02:00
|
|
|
|
for (Creature* spectator : spectators) {
|
2022-11-10 17:56:22 -03:00
|
|
|
|
assert(dynamic_cast<Player*>(spectator) != nullptr);
|
2025-11-11 14:10:44 -03:00
|
|
|
|
|
|
|
|
|
|
if (thingCount > TILE_UPDATE_THRESHOLD) {
|
|
|
|
|
|
// If the tile contains more than the defined threshold of things,
|
|
|
|
|
|
// send a full tile update to the player to keep the client’s view in sync
|
|
|
|
|
|
static_cast<Player*>(spectator)->sendUpdateTile(this, tilePos);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-11-10 17:56:22 -03:00
|
|
|
|
static_cast<Player*>(spectator)->postRemoveNotification(thing, newParent, index, LINK_NEAR);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
// calling movement scripts
|
2017-01-08 15:45:17 -02:00
|
|
|
|
Creature* creature = thing->getCreature();
|
|
|
|
|
|
if (creature) {
|
2019-05-12 01:23:17 +02:00
|
|
|
|
g_moveEvents->onCreatureMove(creature, this, MOVE_EVENT_STEP_OUT);
|
2017-01-08 15:45:17 -02:00
|
|
|
|
} else {
|
|
|
|
|
|
Item* item = thing->getItem();
|
|
|
|
|
|
if (item) {
|
|
|
|
|
|
g_moveEvents->onItemMove(item, this, false);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-09 19:59:32 +01:00
|
|
|
|
void Tile::internalAddThing(uint32_t, Thing* thing)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
|
|
|
|
|
thing->setParent(this);
|
|
|
|
|
|
|
|
|
|
|
|
Creature* creature = thing->getCreature();
|
|
|
|
|
|
if (creature) {
|
2015-03-22 04:34:08 -05:00
|
|
|
|
g_game.map.clearSpectatorCache();
|
2019-10-30 15:00:45 +01:00
|
|
|
|
if (creature->getPlayer()) {
|
|
|
|
|
|
g_game.map.clearPlayersSpectatorCache();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2013-07-07 02:30:08 +02:00
|
|
|
|
CreatureVector* creatures = makeCreatures();
|
|
|
|
|
|
creatures->insert(creatures->begin(), creature);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Item* item = thing->getItem();
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!item) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-04-13 17:35:02 +02:00
|
|
|
|
const ItemType& itemType = Item::items[item->getID()];
|
|
|
|
|
|
if (itemType.isGroundTile()) {
|
2021-10-31 13:28:02 -03:00
|
|
|
|
if (!ground) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
ground = item;
|
2015-05-09 14:54:23 +02:00
|
|
|
|
setTileFlags(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2015-05-09 14:54:23 +02:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileItemVector* items = makeItemList();
|
|
|
|
|
|
if (items->size() >= 0xFFFF) {
|
|
|
|
|
|
return /*RETURNVALUE_NOTPOSSIBLE*/;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (itemType.alwaysOnTop) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
bool isInserted = false;
|
2016-10-02 18:52:01 -03:00
|
|
|
|
for (auto it = items->getBeginTopItem(), end = items->getEndTopItem(); it != end; ++it) {
|
2025-11-15 00:21:14 -03:00
|
|
|
|
if (Item::items[(*it)->getID()].alwaysOnTopOrder >= itemType.alwaysOnTopOrder) {
|
2013-07-07 02:30:08 +02:00
|
|
|
|
items->insert(it, item);
|
|
|
|
|
|
isInserted = true;
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!isInserted) {
|
|
|
|
|
|
items->push_back(item);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
items->insert(items->getBeginDownItem(), item);
|
2015-08-20 04:45:41 +02:00
|
|
|
|
items->addDownItemCount(1);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
setTileFlags(item);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
void Tile::setTileFlags(const Item* item)
|
2013-07-07 02:30:08 +02:00
|
|
|
|
{
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (!hasFlag(TILESTATE_FLOORCHANGE)) {
|
2015-04-13 17:35:02 +02:00
|
|
|
|
const ItemType& it = Item::items[item->getID()];
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (it.floorChange != 0) {
|
|
|
|
|
|
setFlag(it.floorChange);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
2015-01-10 00:16:36 +01:00
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID)) {
|
|
|
|
|
|
setFlag(TILESTATE_IMMOVABLEBLOCKSOLID);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_BLOCKPATH)) {
|
|
|
|
|
|
setFlag(TILESTATE_BLOCKPATH);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_NOFIELDBLOCKPATH)) {
|
|
|
|
|
|
setFlag(TILESTATE_NOFIELDBLOCKPATH);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) {
|
|
|
|
|
|
setFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->getTeleport()) {
|
|
|
|
|
|
setFlag(TILESTATE_TELEPORT);
|
|
|
|
|
|
}
|
2013-07-14 21:06:11 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->getMagicField()) {
|
|
|
|
|
|
setFlag(TILESTATE_MAGICFIELD);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->getMailbox()) {
|
|
|
|
|
|
setFlag(TILESTATE_MAILBOX);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->getTrashHolder()) {
|
|
|
|
|
|
setFlag(TILESTATE_TRASHHOLDER);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_BLOCKSOLID)) {
|
|
|
|
|
|
setFlag(TILESTATE_BLOCKSOLID);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->getBed()) {
|
|
|
|
|
|
setFlag(TILESTATE_BED);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
const Container* container = item->getContainer();
|
|
|
|
|
|
if (container && container->getDepotLocker()) {
|
|
|
|
|
|
setFlag(TILESTATE_DEPOT);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) {
|
|
|
|
|
|
setFlag(TILESTATE_SUPPORTS_HANGABLE);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
void Tile::resetTileFlags(const Item* item)
|
|
|
|
|
|
{
|
2015-04-13 17:35:02 +02:00
|
|
|
|
const ItemType& it = Item::items[item->getID()];
|
2016-01-22 22:41:54 +01:00
|
|
|
|
if (it.floorChange != 0) {
|
2015-01-10 00:16:36 +01:00
|
|
|
|
resetFlag(TILESTATE_FLOORCHANGE);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_BLOCKSOLID) && !hasProperty(item, CONST_PROP_BLOCKSOLID)) {
|
|
|
|
|
|
resetFlag(TILESTATE_BLOCKSOLID);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) && !hasProperty(item, CONST_PROP_IMMOVABLEBLOCKSOLID)) {
|
|
|
|
|
|
resetFlag(TILESTATE_IMMOVABLEBLOCKSOLID);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_BLOCKPATH) && !hasProperty(item, CONST_PROP_BLOCKPATH)) {
|
|
|
|
|
|
resetFlag(TILESTATE_BLOCKPATH);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_NOFIELDBLOCKPATH) && !hasProperty(item, CONST_PROP_NOFIELDBLOCKPATH)) {
|
|
|
|
|
|
resetFlag(TILESTATE_NOFIELDBLOCKPATH);
|
|
|
|
|
|
}
|
2013-07-07 02:30:08 +02:00
|
|
|
|
|
2015-01-10 00:16:36 +01:00
|
|
|
|
if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKPATH) && !hasProperty(item, CONST_PROP_IMMOVABLEBLOCKPATH)) {
|
|
|
|
|
|
resetFlag(TILESTATE_IMMOVABLEBLOCKPATH);
|
|
|
|
|
|
}
|
2013-07-14 21:06:11 +02:00
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
if (item->hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH) &&
|
|
|
|
|
|
!hasProperty(item, CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) {
|
2015-01-10 00:16:36 +01:00
|
|
|
|
resetFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item->getTeleport()) {
|
|
|
|
|
|
resetFlag(TILESTATE_TELEPORT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item->getMagicField()) {
|
|
|
|
|
|
resetFlag(TILESTATE_MAGICFIELD);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item->getMailbox()) {
|
|
|
|
|
|
resetFlag(TILESTATE_MAILBOX);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item->getTrashHolder()) {
|
|
|
|
|
|
resetFlag(TILESTATE_TRASHHOLDER);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item->getBed()) {
|
|
|
|
|
|
resetFlag(TILESTATE_BED);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const Container* container = item->getContainer();
|
|
|
|
|
|
if (container && container->getDepotLocker()) {
|
|
|
|
|
|
resetFlag(TILESTATE_DEPOT);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item->hasProperty(CONST_PROP_SUPPORTHANGABLE)) {
|
|
|
|
|
|
resetFlag(TILESTATE_SUPPORTS_HANGABLE);
|
2013-07-07 02:30:08 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-04-13 14:06:08 +00:00
|
|
|
|
bool Tile::isMoveableBlocking() const { return !ground || hasFlag(TILESTATE_BLOCKSOLID); }
|
2015-03-29 00:08:29 +01:00
|
|
|
|
|
2017-12-27 15:39:45 +01:00
|
|
|
|
Item* Tile::getUseItem(int32_t index) const
|
2015-03-29 00:08:29 +01:00
|
|
|
|
{
|
|
|
|
|
|
const TileItemVector* items = getItemList();
|
2024-10-06 20:03:08 +02:00
|
|
|
|
|
|
|
|
|
|
// no items, get ground
|
2015-03-29 00:08:29 +01:00
|
|
|
|
if (!items || items->size() == 0) {
|
|
|
|
|
|
return ground;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-06 20:03:08 +02:00
|
|
|
|
// try getting thing by index
|
2017-12-27 15:39:45 +01:00
|
|
|
|
if (Thing* thing = getThing(index)) {
|
2024-10-06 20:03:08 +02:00
|
|
|
|
Item* thingItem = thing->getItem();
|
|
|
|
|
|
if (thingItem) {
|
|
|
|
|
|
return thingItem;
|
|
|
|
|
|
}
|
2015-03-29 00:08:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-10-06 20:03:08 +02:00
|
|
|
|
// try getting top movable item
|
|
|
|
|
|
Item* topDownItem = getTopDownItem();
|
|
|
|
|
|
if (topDownItem) {
|
|
|
|
|
|
return topDownItem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// try getting door
|
|
|
|
|
|
for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) {
|
|
|
|
|
|
if ((*it)->getDoor()) {
|
|
|
|
|
|
return (*it)->getItem();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return *items->begin();
|
2015-03-29 00:08:29 +01:00
|
|
|
|
}
|