• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

TFS 1.X+ TFS 1.4.1 - save not pickupable item in house

lexus21

Active Member
Joined
Dec 14, 2022
Messages
88
Reaction score
25
Hi
How to save in house non pickupable items, grounds etc.
For example i create item /i 2737 (growing wheat) in house and i want to save this

I think this is a code

C++:
bool IOMapSerialize::saveHouseItems()
{
    int64_t start = OTSYS_TIME();
    Database& db = Database::getInstance();

    //Start the transaction
    DBTransaction transaction;
    if (!transaction.begin()) {
        return false;
    }

    //clear old tile data
    if (!db.executeQuery("DELETE FROM `tile_store`")) {
        return false;
    }

    DBInsert stmt("INSERT INTO `tile_store` (`house_id`, `data`) VALUES ");

    PropWriteStream stream;
    for (const auto& it : g_game.map.houses.getHouses()) {
        //save house items
        House* house = it.second;
        for (HouseTile* tile : house->getTiles()) {
            saveTile(stream, tile);

            size_t attributesSize;
            const char* attributes = stream.getStream(attributesSize);
            if (attributesSize > 0) {
                if (!stmt.addRow(fmt::format("{:d}, {:s}", house->getId(), db.escapeBlob(attributes, attributesSize)))) {
                    return false;
                }
                stream.clear();
            }
        }
    }

    if (!stmt.execute()) {
        return false;
    }

    //End the transaction
    bool success = transaction.commit();
    std::cout << "> Saved house items in: " <<
              (OTSYS_TIME() - start) / (1000.) << " s" << std::endl;
    return success;
}


but wher is code resposible for what type of item to save?

EDIT.1
ok i fix this.
In iomapserialize.cpp

C++:
bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent)
{
    uint16_t id;
    if (!propStream.read<uint16_t>(id)) {
        return false;
    }

    Tile* tile = nullptr;
    if (parent->getParent() == nullptr) {
        tile = parent->getTile();
    }

    const ItemType& iType = Item::items[id];
    if (iType.moveable || iType.forceSerialize || !tile) {
        //create a new item
        Item* item = Item::CreateItem(id);
        if (item) {
            if (item->unserializeAttr(propStream)) {
                Container* container = item->getContainer();
                if (container && !loadContainer(propStream, container)) {
                    delete item;
                    return false;
                }

                parent->internalAddThing(item);
                item->startDecaying();
            } else {
                std::cout << "WARNING: Unserialization error in IOMapSerialize::loadItem()" << id << std::endl;
                delete item;
                return false;
            }
        }
    } else {
        // Stationary items like doors/beds/blackboards/bookcases
        Item* item = nullptr;
        if (const TileItemVector* items = tile->getItemList()) {
            for (Item* findItem : *items) {
                if (findItem->getID() == id) {
                    item = findItem;
                    break;
                } else if (iType.isDoor() && findItem->getDoor()) {
                    item = findItem;
                    break;
                } else if (iType.isBed() && findItem->getBed()) {
                    item = findItem;
                    break;
                }
            }
        }

        if (item) {
            if (item->unserializeAttr(propStream)) {
                Container* container = item->getContainer();
                if (container && !loadContainer(propStream, container)) {
                    return false;
                }

                g_game.transformItem(item, id);
            } else {
                std::cout << "WARNING: Unserialization error in IOMapSerialize::loadItem()" << id << std::endl;
            }
        } else {
            //The map changed since the last save, just read the attributes
            std::unique_ptr<Item> dummy(Item::CreateItem(id));
            if (dummy) {
                dummy->unserializeAttr(propStream);
                Container* container = dummy->getContainer();
                if (container) {
                    if (!loadContainer(propStream, container)) {
                        return false;
                    }
                } else if (BedItem* bedItem = dynamic_cast<BedItem*>(dummy.get())) {
                    uint32_t sleeperGUID = bedItem->getSleeper();
                    if (sleeperGUID != 0) {
                        g_game.removeBedSleeper(sleeperGUID);
                    }
                }
            }
        }
    }
    return true;



to



C++:
bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent)
{
    uint16_t id;
    if (!propStream.read<uint16_t>(id)) {
        return false;
    }

    Tile* tile = nullptr;
    if (parent->getParent() == nullptr) {
        tile = parent->getTile();
    }

    const ItemType& iType = Item::items[id];
    if (iType.id) {
        //create a new item
        Item* item = Item::CreateItem(id);
        if (item) {
            if (item->unserializeAttr(propStream)) {
                Container* container = item->getContainer();
                if (container && !loadContainer(propStream, container)) {
                    delete item;
                    return false;
                }

                parent->internalAddThing(item);
                item->startDecaying();
            } else {
                std::cout << "WARNING: Unserialization error in IOMapSerialize::loadItem()" << id << std::endl;
                delete item;
                return false;
            }
        }
    } else {
        // Stationary items like doors/beds/blackboards/bookcases
        Item* item = nullptr;
        if (const TileItemVector* items = tile->getItemList()) {
            for (Item* findItem : *items) {
                if (findItem->getID() == id) {
                    item = findItem;
                    break;
                } else if (iType.isDoor() && findItem->getDoor()) {
                    item = findItem;
                    break;
                } else if (iType.isBed() && findItem->getBed()) {
                    item = findItem;
                    break;
                }
            }
        }

        if (item) {
            if (item->unserializeAttr(propStream)) {
                Container* container = item->getContainer();
                if (container && !loadContainer(propStream, container)) {
                    return false;
                }

                g_game.transformItem(item, id);
            } else {
                std::cout << "WARNING: Unserialization error in IOMapSerialize::loadItem()" << id << std::endl;
            }
        } else {
            //The map changed since the last save, just read the attributes
            std::unique_ptr<Item> dummy(Item::CreateItem(id));
            if (dummy) {
                dummy->unserializeAttr(propStream);
                Container* container = dummy->getContainer();
                if (container) {
                    if (!loadContainer(propStream, container)) {
                        return false;
                    }
                } else if (BedItem* bedItem = dynamic_cast<BedItem*>(dummy.get())) {
                    uint32_t sleeperGUID = bedItem->getSleeper();
                    if (sleeperGUID != 0) {
                        g_game.removeBedSleeper(sleeperGUID);
                    }
                }
            }
        }
    }
    return true;
}


and this :
C++:
void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile)
{
    const TileItemVector* tileItems = tile->getItemList();
    if (!tileItems) {
        return;
    }

    std::forward_list<Item*> items;
    uint16_t count = 0;
    for (Item* item : *tileItems) {
        const ItemType& it = Item::items[item->getID()];

        // Note that these are NEGATED, ie. these are the items that will be saved.
        if (!(it.moveable || it.forceSerialize || item->getDoor() || (item->getContainer() && !item->getContainer()->empty()) || it.canWriteText || item->getBed())) {
            continue;
        }

        items.push_front(item);
        ++count;
    }

    if (!items.empty()) {
        const Position& tilePosition = tile->getPosition();
        stream.write<uint16_t>(tilePosition.x);
        stream.write<uint16_t>(tilePosition.y);
        stream.write<uint8_t>(tilePosition.z);

        stream.write<uint32_t>(count);
        for (const Item* item : items) {
            saveItem(stream, item);
        }
    }
}

to this

C++:
void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile)
{
    const TileItemVector* tileItems = tile->getItemList();
    if (!tileItems) {
        return;
    }

    std::forward_list<Item*> items;
    uint16_t count = 0;
    for (Item* item : *tileItems) {
        const ItemType& it = Item::items[item->getID()];

        // Note that these are NEGATED, ie. these are the items that will be saved.


        items.push_front(item);
        ++count;
    }

    if (!items.empty()) {
        const Position& tilePosition = tile->getPosition();
        stream.write<uint16_t>(tilePosition.x);
        stream.write<uint16_t>(tilePosition.y);
        stream.write<uint8_t>(tilePosition.z);

        stream.write<uint32_t>(count);
        for (const Item* item : items) {
            saveItem(stream, item);
        }
    }
}
 
Last edited:
I eat all day for this.

This is flag

You don't need code in c++, Just -
XML:
<attribute key="forceSerialize" value="1" />

This flag allow to save item in house.
 
Back
Top