bool Tile::removeThing(ThingPtr thing) { if(!thing) return false; bool removed = false; if(thing->isEffect()) { EffectPtr effect = thing->static_self_cast<Effect>(); auto it = std::find(m_effects.begin(), m_effects.end(), effect); if(it != m_effects.end()) { m_effects.erase(it); removed = true; } } else { auto it = std::find(m_things.begin(), m_things.end(), thing); if(it != m_things.end()) { m_things.erase(it); removed = true; } } thing->onDisappear(); if(thing->isTranslucent()) checkTranslucentLight(); return removed; }
bool Map::removeThing(const ThingPtr& thing) { if(!thing) return false; bool ret = false; if(thing->isMissile()) { MissilePtr missile = thing->static_self_cast<Missile>(); int z = missile->getPosition().z; auto it = std::find(m_floorMissiles[z].begin(), m_floorMissiles[z].end(), missile); if(it != m_floorMissiles[z].end()) { m_floorMissiles[z].erase(it); ret = true; } } else if(thing->isAnimatedText()) { AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>(); auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText); if(it != m_animatedTexts.end()) { m_animatedTexts.erase(it); ret = true; } } else if(thing->isStaticText()) { StaticTextPtr staticText = thing->static_self_cast<StaticText>(); auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText); if(it != m_staticTexts.end()) { m_staticTexts.erase(it); ret = true; } } else if(const TilePtr& tile = thing->getTile()) ret = tile->removeThing(thing); notificateTileUpdate(thing->getPosition()); return ret; }
void Game::rotate(const ThingPtr& thing) { if(!canPerformGameAction() || !thing) return; m_protocolGame->sendRotateItem(thing->getPosition(), thing->getId(), thing->getStackpos()); }
ThingPtr Tile::getTopMultiUseThing() { if(isEmpty()) return nullptr; if(CreaturePtr topCreature = getTopCreature()) return topCreature; for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(thing->isForceUse()) return thing; } for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop()) { if(i > 0 && thing->isSplash()) return m_things[i-1]; return thing; } } for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(!thing->isGround() && !thing->isOnTop()) return thing; } return m_things[0]; }
void Map::removeThing(const ThingPtr& thing) { if(!thing) return; if(MissilePtr shot = thing->asMissile()) { auto it = std::find(m_missilesAtFloor[shot->getPos().z].begin(), m_missilesAtFloor[shot->getPos().z].end(), shot); if(it != m_missilesAtFloor[shot->getPos().z].end()) { m_missilesAtFloor[shot->getPos().z].erase(it); } return; } else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) { auto it = std::find(m_animatedTexts.begin(), m_animatedTexts.end(), animatedText); if(it != m_animatedTexts.end()) m_animatedTexts.erase(it); return; } else if(StaticTextPtr staticText = thing->asStaticText()) { auto it = std::find(m_staticTexts.begin(), m_staticTexts.end(), staticText); if(it != m_staticTexts.end()) m_staticTexts.erase(it); return; } if(TilePtr& tile = m_tiles[thing->getPos()]) tile->removeThing(thing); }
CreaturePtr Tile::getTopCreature() { CreaturePtr creature; for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(thing->isLocalPlayer()) // return local player if there is no other creature creature = thing->static_self_cast<Creature>(); else if(thing->isCreature() && !thing->isLocalPlayer()) return thing->static_self_cast<Creature>(); } if(!creature && !m_walkingCreatures.empty()) creature = m_walkingCreatures.back(); // check for walking creatures in tiles around if(!creature) { for(int xi=-1;xi<=1;++xi) { for(int yi=-1;yi<=1;++yi) { Position pos = m_position.translated(xi, yi); if(pos == m_position) continue; const TilePtr& tile = g_map.getTile(pos); if(tile) { for(const CreaturePtr& c : tile->getCreatures()) { if(c->isWalking() && c->getLastStepFromPosition() == m_position && c->getStepProgress() < 0.75f) { creature = c; } } } } } } return creature; }
ItemPtr Tile::getGround() { ThingPtr firstObject = getThing(0); if(!firstObject) return nullptr; if(firstObject->isGround() && firstObject->isItem()) return firstObject->static_self_cast<Item>(); return nullptr; }
void Game::look(const ThingPtr& thing) { if(!canPerformGameAction() || !thing) return; if(thing->isCreature() && m_protocolVersion >= 961) m_protocolGame->sendLookCreature(thing->getId()); else m_protocolGame->sendLook(thing->getPosition(), thing->getId(), thing->getStackPos()); }
void Game::use(const ThingPtr& thing) { if(!canPerformGameAction() || !thing) return; Position pos = thing->getPosition(); if(!pos.isValid()) // virtual item pos = Position(0xFFFF, 0, 0); // means that is a item in inventory m_protocolGame->sendUseItem(pos, thing->getId(), thing->getStackpos(), 0); }
void Game::useInventoryItemWith(int itemId, const ThingPtr& toThing) { if(!canPerformGameAction() || !toThing) return; Position pos = Position(0xFFFF, 0, 0); // means that is a item in inventory if(toThing->isCreature()) m_protocolGame->sendUseOnCreature(pos, itemId, 0, toThing->getId()); else m_protocolGame->sendUseItemWith(pos, itemId, 0, toThing->getPosition(), toThing->getId(), toThing->getStackpos()); }
ThingPtr Tile::getTopLookThing() { if(isEmpty()) return nullptr; for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(!thing->isIgnoreLook() && (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())) return thing; } return m_things[0]; }
void Game::use(const ThingPtr& thing) { if(!canPerformGameAction() || !thing) return; Position pos = thing->getPosition(); if(!pos.isValid()) // virtual item pos = Position(0xFFFF, 0, 0); // inventory item // some items, e.g. parcel, are not set as containers but they are. // always try to use these items in free container slots. m_protocolGame->sendUseItem(pos, thing->getId(), thing->getStackPos(), findEmptyContainerId()); }
void Game::useWith(const ItemPtr& item, const ThingPtr& toThing) { if(!canPerformGameAction() || !item || !toThing) return; Position pos = item->getPosition(); if(!pos.isValid()) // virtual item pos = Position(0xFFFF, 0, 0); // means that is a item in inventory if(toThing->isCreature()) m_protocolGame->sendUseOnCreature(pos, item->getId(), item->getStackpos(), toThing->getId()); else m_protocolGame->sendUseItemWith(pos, item->getId(), item->getStackpos(), toThing->getPosition(), toThing->getId(), toThing->getStackpos()); }
void Game::move(const ThingPtr& thing, const Position& toPos, int count) { if(count <= 0) count = 1; if(!canPerformGameAction() || !thing || thing->getPosition() == toPos) return; uint id = thing->getId(); if(thing->isCreature()) { CreaturePtr creature = thing->static_self_cast<Creature>(); id = Proto::Creature; } m_protocolGame->sendMove(thing->getPosition(), id, thing->getStackpos(), toPos, count); }
void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos) { if(!thing) return; if(MissilePtr shot = thing->asMissile()) { m_missilesAtFloor[shot->getPosition().z].push_back(shot); return; } TilePtr tile = getTile(pos); tile->addThing(thing, stackPos); if(CreaturePtr creature = thing->asCreature()) m_creatures[creature->getId()] = creature; }
void Map::removeThing(const ThingPtr& thing) { if(!thing) return; if(MissilePtr shot = thing->asMissile()) { auto it = std::find(m_missilesAtFloor[shot->getPosition().z].begin(), m_missilesAtFloor[shot->getPosition().z].end(), shot); if(it != m_missilesAtFloor[shot->getPosition().z].end()) { m_missilesAtFloor[shot->getPosition().z].erase(it); } return; } if(TilePtr& tile = m_tiles[thing->getPosition()]) tile->removeThing(thing); }
void Map::removeThingColor(const ThingPtr& thing) { if(!thing) return; if(thing->isItem()) thing->static_self_cast<Item>()->setColor(Color::alpha); else if(thing->isCreature()) { const TilePtr& tile = thing->getTile(); assert(tile); const ThingPtr& topThing = tile->getTopThing(); assert(topThing); topThing->static_self_cast<Item>()->setColor(Color::alpha); } }
void Map::colorizeThing(const ThingPtr& thing, const Color& color) { if(!thing) return; if(thing->isItem()) thing->static_self_cast<Item>()->setColor(color); else if(thing->isCreature()) { const TilePtr& tile = thing->getTile(); assert(tile); const ThingPtr& topThing = tile->getTopThing(); assert(topThing); topThing->static_self_cast<Item>()->setColor(color); } }
void Game::moveToParentContainer(const ThingPtr& thing, int count) { if(!canPerformGameAction() || !thing || count <= 0) return; Position position = thing->getPosition(); move(thing, Position(position.x, position.y, 254), count); }
ThingPtr Tile::getTopMoveThing() { if(isEmpty()) return nullptr; for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop() && !thing->isCreature()) { if(i > 0 && thing->isNotMoveable()) return m_things[i-1]; return thing; } } for(const ThingPtr& thing : m_things) { if(thing->isCreature()) return thing; } return m_things[0]; }
bool Tile::limitsFloorsView(bool isFreeView) { // ground and walls limits the view ThingPtr firstThing = getThing(0); if(isFreeView) { if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || firstThing->isOnBottom())) return true; } else if(firstThing && !firstThing->isDontHide() && (firstThing->isGround() || (firstThing->isOnBottom() && firstThing->blockProjectile()))) return true; return false; }
void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos) { if(!thing) return; Position oldPos = thing->getPos(); bool teleport = false; if(oldPos.isValid() && !oldPos.isInRange(pos,1,1,0)) teleport = true; TilePtr tile = getTile(pos); if(CreaturePtr creature = thing->asCreature()) { tile->addThing(thing, stackPos); m_creatures[creature->getId()] = creature; if(teleport) g_game.processCreatureTeleport(creature); } else if(MissilePtr shot = thing->asMissile()) { m_missilesAtFloor[shot->getPos().z].push_back(shot); } else if(AnimatedTextPtr animatedText = thing->asAnimatedText()) { m_animatedTexts.push_back(animatedText); } else if(StaticTextPtr staticText = thing->asStaticText()) { bool mustAdd = true; for(auto it = m_staticTexts.begin(), end = m_staticTexts.end(); it != end; ++it) { StaticTextPtr cStaticText = *it; if(cStaticText->getPos() == pos) { // try to combine messages if(cStaticText->addMessage(staticText->getName(), staticText->getMessageType(), staticText->getFirstMessage())) { mustAdd = false; break; } else { // must add another message and rearrenge current } } } if(mustAdd) m_staticTexts.push_back(staticText); } else { tile->addThing(thing, stackPos); } thing->start(); thing->setPos(pos); }
void ProtocolGame::parseCreatureMove(InputMessage& msg) { Position oldPos = parsePosition(msg); int oldStackpos = msg.getU8(); Position newPos = parsePosition(msg); ThingPtr thing = g_map.getTile(oldPos)->getThing(oldStackpos); if(!thing) { logTraceError("could not get thing"); return; } CreaturePtr creature = thing->asCreature(); if(!creature) { logTraceError("thing is not a creature"); return; } // update map tiles g_map.removeThing(thing); g_map.addThing(thing, newPos); g_game.processCreatureMove(creature, oldPos, newPos); }
ThingPtr Tile::getTopUseThing() { if(isEmpty()) return nullptr; for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(thing->isForceUse() || (!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop() && !thing->isCreature())) return thing; } for(uint i = 0; i < m_things.size(); ++i) { ThingPtr thing = m_things[i]; if(!thing->isGround() && !thing->isGroundBorder() && !thing->isCreature()) return thing; } return m_things[0]; }
bool Tile::isFullyOpaque() { ThingPtr firstObject = getThing(0); return firstObject && firstObject->isFullGround(); }
void Tile::addThing(const ThingPtr& thing, int stackPos) { if(!thing) return; if(thing->isEffect()) { m_effects.push_back(thing->static_self_cast<Effect>()); } else { // priority 854 // 0 - ground, --> --> // 1 - ground borders --> --> // 2 - bottom (walls), --> --> // 3 - on top (doors) --> --> // 4 - creatures, from top to bottom <-- --> // 5 - items, from top to bottom <-- <-- if(stackPos < 0 || stackPos == 255) { int priority = thing->getStackPriority(); // -1 or 255 => auto detect position // -2 => append bool append; if(stackPos == -2) append = true; else { append = (priority <= 3); // newer protocols does not store creatures in reverse order if(g_game.getProtocolVersion() >= 854 && priority == 4) append = !append; } for(stackPos = 0; stackPos < (int)m_things.size(); ++stackPos) { int otherPriority = m_things[stackPos]->getStackPriority(); if((append && otherPriority > priority) || (!append && otherPriority >= priority)) break; } } else if(stackPos > (int)m_things.size()) stackPos = m_things.size(); m_things.insert(m_things.begin() + stackPos, thing); if(m_things.size() > MAX_THINGS) removeThing(m_things[MAX_THINGS]); /* // check stack priorities // this code exists to find stackpos bugs faster int lastPriority = 0; for(const ThingPtr& thing : m_things) { int priority = thing->getStackPriority(); assert(lastPriority <= priority); lastPriority = priority; } */ } thing->setPosition(m_position); thing->onAppear(); if(thing->isTranslucent()) checkTranslucentLight(); }
void Map::addThing(const ThingPtr& thing, const Position& pos, int stackPos) { if(!thing) return; if(thing->isItem() || thing->isCreature() || thing->isEffect()) { const TilePtr& tile = getOrCreateTile(pos); if(tile) tile->addThing(thing, stackPos); } else { if(thing->isMissile()) { m_floorMissiles[pos.z].push_back(thing->static_self_cast<Missile>()); thing->onAppear(); } else if(thing->isAnimatedText()) { // this code will stack animated texts of the same color AnimatedTextPtr animatedText = thing->static_self_cast<AnimatedText>(); AnimatedTextPtr prevAnimatedText; bool merged = false; for(auto other : m_animatedTexts) { if(other->getPosition() == pos) { prevAnimatedText = other; if(other->merge(animatedText)) { merged = true; break; } } } if(!merged) { if(prevAnimatedText) { Point offset = prevAnimatedText->getOffset(); float t = prevAnimatedText->getTimer().ticksElapsed(); if(t < Otc::ANIMATED_TEXT_DURATION / 4.0) { // didnt move 12 pixels int y = 12 - 48 * t / (float)Otc::ANIMATED_TEXT_DURATION; offset += Point(0, y); } offset.y = std::min<int>(offset.y, 12); animatedText->setOffset(offset); } m_animatedTexts.push_back(animatedText); } } else if(thing->isStaticText()) { StaticTextPtr staticText = thing->static_self_cast<StaticText>(); bool mustAdd = true; for(auto other : m_staticTexts) { // try to combine messages if(other->getPosition() == pos && other->addMessage(staticText->getName(), staticText->getMessageMode(), staticText->getFirstMessage())) { mustAdd = false; break; } } if(mustAdd) m_staticTexts.push_back(staticText); else return; } thing->setPosition(pos); thing->onAppear(); } notificateTileUpdate(pos); }