• 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+ Move creatures onto solid objects or non-walkable grounds (e.g: water)

Snavy

Bakasta
Senator
Joined
Apr 1, 2012
Messages
1,249
Solutions
71
Reaction score
621
Location
Hell
TFS 1.3 - Master branch

I have managed to modify some functions from Game.cpp in order to allow admins push players from far or throw items anywhere without walking to it. however I haven't been able to find the part where it doesn't allow me to push creatures onto solid objects.

How do I allow pushing creatures onto solid objects / non-walkable grounds?

I am suspecting that the part of code might be in Game::playerMoveCreature at the last section which is the following part of code :
C++:
ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
    if (ret != RETURNVALUE_NOERROR) {
        player->sendCancelMessage(ret);
    }

.. But I am not sure if that's the case.

C++:
void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile)
{
    if (!player->canDoAction() && !player->isAccessPlayer()) {
        uint32_t delay = player->getNextActionTime();
        SchedulerTask* task = createSchedulerTask(delay, std::bind(&Game::playerMoveCreatureByID,
            this, player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()));
        player->setNextActionTask(task);
        return;
    }

    player->setNextActionTask(nullptr);

    if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition()) && !player->isAccessPlayer()) {
        //need to walk to the creature first before moving it
        std::forward_list<Direction> listDir;
        if (player->getPathTo(movingCreatureOrigPos, listDir, 0, 1, true, true)) {
            g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk,
                                            this, player->getID(), listDir)));
            SchedulerTask* task = createSchedulerTask(1500, std::bind(&Game::playerMoveCreatureByID, this,
                player->getID(), movingCreature->getID(), movingCreatureOrigPos, toTile->getPosition()));
            player->setNextWalkActionTask(task);
        } else {
            player->sendCancelMessage(RETURNVALUE_THEREISNOWAY);
        }
        return;
    }

    if ((!movingCreature->isPushable() && !player->hasFlag(PlayerFlag_CanPushAllCreatures)) ||
            (movingCreature->isInGhostMode() && !player->isAccessPlayer())) {
        player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE);
        return;
    }

    //check throw distance
    const Position& movingCreaturePos = movingCreature->getPosition();
    const Position& toPos = toTile->getPosition();
    if (
        (
            (Position::getDistanceX(movingCreaturePos, toPos) > movingCreature->getThrowRange())
            || (Position::getDistanceY(movingCreaturePos, toPos) > movingCreature->getThrowRange())
            || (Position::getDistanceZ(movingCreaturePos, toPos) * 4 > movingCreature->getThrowRange())
        )     && !player->isAccessPlayer()) {
        player->sendCancelMessage(RETURNVALUE_DESTINATIONOUTOFREACH);
        return;
    }

    if (player != movingCreature) {
        if (toTile->hasFlag(TILESTATE_BLOCKPATH) && !player->isAccessPlayer()) {
            player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
            return;
        } else if (
                (
                    (movingCreature->getZone() == ZONE_PROTECTION && !toTile->hasFlag(TILESTATE_PROTECTIONZONE))
                    || (movingCreature->getZone() == ZONE_NOPVP && !toTile->hasFlag(TILESTATE_NOPVPZONE))
                )     && !player->isAccessPlayer()) {
            player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
            return;
        } else {
            if (CreatureVector* tileCreatures = toTile->getCreatures()) {
                for (Creature* tileCreature : *tileCreatures) {
                    if (!tileCreature->isInGhostMode() && !player->isAccessPlayer()) {
                        player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
                        return;
                    }
                }
            }

            Npc* movingNpc = movingCreature->getNpc();
            if (movingNpc && !Spawns::isInZone(movingNpc->getMasterPos(), movingNpc->getMasterRadius(), toPos) && !player->isAccessPlayer()) {
                player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
                return;
            }
        }
    }

    if (!g_events->eventPlayerOnMoveCreature(player, movingCreature, movingCreaturePos, toPos)) {
        return;
    }

    ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
    if (ret != RETURNVALUE_NOERROR) {
        player->sendCancelMessage(ret);
    }
}


UPDATE :

After going deeper into the code I decided to add a cout in the function called Game::internalMoveCreature and as I suspected the issue occured in that step.

C++:
ReturnValue Game::internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags /*= 0*/)
{
    //check if we can move the creature to the destination
    ReturnValue ret = toTile.queryAdd(0, creature, 1, flags);
    if (ret != RETURNVALUE_NOERROR) {
        std::cout << "queryAdd returned Error ... " << std::endl;
        return ret;
    }

    ... more cpp blabla
   
}

and I thought of adding !player->isAccessPlayer() .. but player is not even passed onto that function and I am not sure how I would solve this issue.


@Znote @Gesior.pl @Evil Hero
Mah masters! I summon you to guide me on this dark path. Please help.
 
Last edited:
Solution
and the same for items. playerMoveItem function.
fixed this error: Linux - GDB backtrace report - internalMoveCreature (https://otland.net/threads/gdb-backtrace-report-internalmovecreature.263990/#post-2551305)
Since I know someone is going to answer you, I prefer to earn the contribution point easily.
game.cpp
Code:
    if (!g_events->eventPlayerOnMoveCreature(player, movingCreature, movingCreaturePos, toPos)) {
        return;
    }
then add:
Code:
    uint32_t flags = 0;
    if (player->isAccessPlayer()) {
        flags = FLAG_NOLIMIT;
    }

replace
Code:
ReturnValue ret = internalMoveCreature(*movingCreature, *toTile);
for
Code:
ReturnValue ret = internalMoveCreature(*movingCreature, *toTile, flags);

;)
Forget the above.
 
Last edited:
Since I know someone is going to answer you, I prefer to earn the contribution point easily.
tile.cpp
Code:
ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags, Creature*) const

Code:
        if (!hasBitSet(FLAG_IGNOREBLOCKITEM, flags)) {
            //If the FLAG_IGNOREBLOCKITEM bit isn't set we dont have to iterate every single item
then add:
Code:
if (const Player* player = creature->getPlayer()) {
                if (player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) {
                    return RETURNVALUE_NOERROR;
                }
            }

it will look like this:
Code:
if (!hasBitSet(FLAG_IGNOREBLOCKITEM, flags)) {
            //If the FLAG_IGNOREBLOCKITEM bit isn't set we dont have to iterate every single item
            if (const Player* player = creature->getPlayer()) {
                if (player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) {
                    return RETURNVALUE_NOERROR;
                }
            }
            if (hasFlag(TILESTATE_BLOCKSOLID)) {
                return RETURNVALUE_NOTENOUGHROOM;
            }
        }

in you case replace
Code:
if (player->hasCustomFlag(PlayerCustomFlag_CanThrowAnywhere)) {
for
Code:
if (player->isAccessPlayer()) {

;)

This only allows the "thrower" to throw themselves into solid object (accessPlayer ofc). Not able to push someone else into a solid object.


Update:

@Sarah Wesker
I noticed the message I was getting was "NOTENOUGHROOM" so I added some prints whereever I fonud them in queryAdd function.
and this part is where it stopped:

line: 596
Lua:
else if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags)) {
    for (const Creature* tileCreature : *creatures) {
        if (!tileCreature->isInGhostMode()) {
            return RETURNVALUE_NOTENOUGHROOM;
        }
    }
}
 
Last edited:
and the same for items. playerMoveItem function.
fixed this error: Linux - GDB backtrace report - internalMoveCreature (https://otland.net/threads/gdb-backtrace-report-internalmovecreature.263990/#post-2551305)
 
Last edited:
Solution
Back
Top