void Monster::onCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); if(creature == this) { if(isSummon()) isMasterInRange = canSee(master->getPosition()); updateTargetList(); updateIdleStatus(); } else { bool canSeeNewPos = canSee(newPos), canSeeOldPos = canSee(oldPos); if(canSeeNewPos && !canSeeOldPos) onCreatureEnter(const_cast<Creature*>(creature)); else if(!canSeeNewPos && canSeeOldPos) onCreatureLeave(const_cast<Creature*>(creature)); if(isSummon() && master == creature && canSeeNewPos) //Turn the summon on again isMasterInRange = true; updateIdleStatus(); if(!followCreature && !isSummon() && isOpponent(creature)) //we have no target lets try pick this one selectTarget(const_cast<Creature*>(creature)); } }
bool Actor::selectTarget(Creature* creature) { #ifdef __DEBUG__ std::cout << "Selecting target... " << std::endl; #endif if(!isTarget(creature)){ return false; } CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature); if(it == targetList.end()){ //Target not found in our target list. #ifdef __DEBUG__ std::cout << "Target not found in targetList." << std::endl; #endif return false; } if(isHostile() || isSummon()){ if(setAttackedCreature(creature) && !isSummon()){ g_dispatcher.addTask(createTask( boost::bind(&Game::checkCreatureAttack, &g_game, getID()))); } } return setFollowCreature(creature, true); }
bool Actor::getNextStep(Direction& dir, uint32_t& flags) { if(isIdle || getHealth() <= 0){ //we dont have anyone watching might aswell stop walking eventWalk = 0; return false; } bool result = false; if((!followCreature || !hasFollowPath) && !isSummon()){ if(followCreature){ result = getRandomStep(getPosition(), dir); }else{ if(getTimeSinceLastMove() > 1000){ //choose a random direction result = getRandomStep(getPosition(), dir); } } } else if(isSummon() || followCreature){ result = Creature::getNextStep(dir, flags); if(result){ flags |= FLAG_PATHFINDING; } else{ //target dancing if(attackedCreature && attackedCreature == followCreature){ if(isFleeing()){ result = getDanceStep(getPosition(), dir, false, false); } else if(cType.staticAttackChance() < (uint32_t)random_range(1, 100)){ result = getDanceStep(getPosition(), dir); } } } } if(result && (canPushItems() || canPushCreatures()) ){ const Position& pos = Combat::getCasterPosition(this, dir); Tile* tile = g_game.getParentTile(pos.x, pos.y, pos.z); if(tile){ if(canPushItems()){ pushItems(tile); } if(canPushCreatures()){ pushCreatures(tile); } } #ifdef __DEBUG__ else{ std::cout << "getNextStep - no tile." << std::endl; } #endif } return result; }
bool Monster::getNextStep(Direction& dir, uint32_t& flags) { if(isIdle || getHealth() <= 0) { //we dont have anyone watching might aswell stop walking eventWalk = 0; return false; } bool result = false; if((!followCreature || !hasFollowPath) && !isSummon()) { if(followCreature || getTimeSinceLastMove() > 1000) //choose a random direction result = getRandomStep(getPosition(), dir); } else if(isSummon() || followCreature) { result = Creature::getNextStep(dir, flags); if(!result) { //target dancing if(attackedCreature && attackedCreature == followCreature) { if(isFleeing()) result = getDanceStep(getPosition(), dir, false, false); else if(mType->staticAttackChance < (uint32_t)random_range(1, 100)) result = getDanceStep(getPosition(), dir); } } else flags |= FLAG_PATHFINDING; } if(result && (canPushItems() || canPushCreatures())) { if(Tile* tile = g_game.getTile(Spells::getCasterPosition(this, dir))) { if(canPushItems()) pushItems(tile); if(canPushCreatures()) pushCreatures(tile); } #ifdef __DEBUG__ else std::clog << "[Warning - Monster::getNextStep] no tile found." << std::endl; #endif } return result; }
void Actor::onCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); if(creature == this){ if(isSummon()){ isMasterInRange = canSee(getMaster()->getPosition()); } updateTargetList(); updateIdleStatus(); /* TODO: Optimizations here if(teleport){ //do a full update of the friend/target list } else{ //partial update of the friend/target list } */ } else{ bool canSeeNewPos = canSee(newPos); bool canSeeOldPos = canSee(oldPos); if(canSeeNewPos && !canSeeOldPos){ onCreatureEnter(const_cast<Creature*>(creature)); } else if(!canSeeNewPos && canSeeOldPos){ onCreatureLeave(const_cast<Creature*>(creature)); } if(isSummon() && getMaster() == creature){ if(canSeeNewPos){ //Turn the summon on again isMasterInRange = true; } } updateIdleStatus(); if(!followCreature && !isSummon()){ //we have no target lets try pick this one if(isOpponent(creature)){ selectTarget(const_cast<Creature*>(creature)); } } } }
void Monster::onThinkTarget(uint32_t interval) { if(isSummon() || mType->changeTargetSpeed <= 0) return; bool canChangeTarget = true; if(targetChangeCooldown > 0) { targetChangeCooldown -= interval; if(targetChangeCooldown <= 0) { targetChangeCooldown = 0; targetChangeTicks = (uint32_t)mType->changeTargetSpeed; } else canChangeTarget = false; } if(!canChangeTarget) return; targetChangeTicks += interval; if(targetChangeTicks < (uint32_t)mType->changeTargetSpeed) return; targetChangeTicks = 0; targetChangeCooldown = (uint32_t)mType->changeTargetSpeed; if(mType->changeTargetChance < random_range(1, 100)) return; if(mType->targetDistance <= 1) searchTarget(TARGETSEARCH_RANDOM); else searchTarget(TARGETSEARCH_NEAREST); }
bool Monster::isFriend(const Creature* creature) const { if (isSummon() && getMaster()->getPlayer()) { const Player* masterPlayer = getMaster()->getPlayer(); const Player* tmpPlayer = nullptr; if (creature->getPlayer()) { tmpPlayer = creature->getPlayer(); } else { const Creature* creatureMaster = creature->getMaster(); if (creatureMaster && creatureMaster->getPlayer()) { tmpPlayer = creatureMaster->getPlayer(); } } if (tmpPlayer && (tmpPlayer == getMaster() || masterPlayer->isPartner(tmpPlayer))) { return true; } } else if (creature->getMonster() && !creature->isSummon()) { return true; } return false; }
void Actor::onThinkTarget(uint32_t interval) { if(!isSummon()){ if(cType.changeTargetSpeed() > 0){ bool canChangeTarget = true; if(targetChangeCooldown > 0){ targetChangeCooldown -= interval; if(targetChangeCooldown <= 0){ targetChangeCooldown = 0; targetChangeTicks = (uint32_t)cType.changeTargetSpeed(); } else{ canChangeTarget = false; } } if(canChangeTarget){ targetChangeTicks += interval; if(targetChangeTicks >= (uint32_t)cType.changeTargetSpeed()){ targetChangeTicks = 0; targetChangeCooldown = (uint32_t)cType.changeTargetSpeed(); if(cType.changeTargetChance() >= random_range(1, 100)){ searchTarget(TARGETSEARCH_RANDOM); } } } } } }
void Monster::updateIdleStatus() { //heightMinimum is declared static to avoid //several calls for getNumber (updateIdleStatus is called often) static int heightMinimum = 0; if (heightMinimum <= 0) heightMinimum = std::max(g_config.getNumber(ConfigManager::HEIGHT_MINIMUM_FOR_IDLE),(int64_t)1); bool idle = false; semiIdle = false; if(conditions.empty()){ if(isSummon()){ if(!isMasterInRange){ idle = true; } else if(getMaster()->getMonster() && getMaster()->getMonster()->getIdleStatus()){ idle = true; } } else{ idle = targetList.empty(); semiIdle = !idle; for(CreatureList::iterator it = targetList.begin(); it != targetList.end(); ++it){ if (std::abs((*it)->getPosition().z - getPosition().z) < heightMinimum) { semiIdle = false; break; } } } } setIdle(idle); }
void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const { Creature::getPathSearchParams(creature, fpp); fpp.minTargetDist = 1; fpp.maxTargetDist = mType->targetDistance; if(isSummon()) { if(master == creature) { fpp.maxTargetDist = 2; fpp.fullPathSearch = true; } else if(mType->targetDistance <= 1) fpp.fullPathSearch = true; else fpp.fullPathSearch = !canUseAttack(getPosition(), creature); } else if(isFleeing()) { //Distance should be higher than the client view range (Map::maxClientViewportX/Map::maxClientViewportY) fpp.maxTargetDist = Map::maxViewportX; fpp.clearSight = fpp.fullPathSearch = false; fpp.keepDistance = true; } else if(mType->targetDistance <= 1) fpp.fullPathSearch = true; else fpp.fullPathSearch = !canUseAttack(getPosition(), creature); }
void Actor::onThinkDefense(uint32_t interval) { resetTicks = true; defenseTicks += interval; for(SpellList::const_iterator it = cType.spellDefenseList().begin(), spell_defense_list_end = cType.spellDefenseList().end(); it != spell_defense_list_end; ++it){ if(it->speed > defenseTicks){ resetTicks = false; continue; } if(defenseTicks % it->speed >= interval){ //already used this spell for this round continue; } if((it->chance >= (uint32_t)random_range(1, 100))){ g_game.onActorCastSpell(this, NULL, (*it).name); } } if(!isSummon() && (int32_t)summons.size() < cType.maxSummons()){ for(SummonList::const_iterator it = cType.summonList().begin(), summon_list_end = cType.summonList().end(); it != summon_list_end; ++it){ if(it->speed > defenseTicks){ resetTicks = false; continue; } if((int32_t)summons.size() >= cType.maxSummons()){ continue; } if(defenseTicks % it->speed >= interval){ //already used this spell for this round continue; } if((it->chance >= (uint32_t)random_range(1, 100))){ Actor* summon = Actor::create(it->name); if(summon){ const Position& summonPos = getPosition(); addSummon(summon); if(g_game.placeCreature(summon, summonPos)){ g_game.addMagicEffect(summon->getPosition(), MAGIC_EFFECT_BLUE_BUBBLE); g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_BLUE_BUBBLE); } else{ removeSummon(summon); } } } } } if(resetTicks){ defenseTicks = 0; } }
void Monster::doAttacking(uint32_t interval) { if(!attackedCreature || (isSummon() && attackedCreature == this)) return; bool updateLook = true, outOfRange = true; resetTicks = interval; attackTicks += interval; const Position& myPos = getPosition(); const Position& targetPos = attackedCreature->getPosition(); for(SpellList::iterator it = mType->spellAttackList.begin(); it != mType->spellAttackList.end(); ++it) { if(it->isMelee && isFleeing()) continue; bool inRange = false; if(canUseSpell(myPos, targetPos, *it, interval, inRange)) { if(it->chance >= (uint32_t)random_range(1, 100)) { if(updateLook) { updateLookDirection(); updateLook = false; } double multiplier; if(maxCombatValue > 0) //defense multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_DEFENSE); else //attack multiplier = g_config.getDouble(ConfigManager::RATE_MONSTER_ATTACK); minCombatValue = (int32_t)(it->minCombatValue * multiplier); maxCombatValue = (int32_t)(it->maxCombatValue * multiplier); it->spell->castSpell(this, attackedCreature); if(it->isMelee) extraMeleeAttack = false; #ifdef __DEBUG__ static uint64_t prevTicks = OTSYS_TIME(); std::clog << "doAttacking ticks: " << OTSYS_TIME() - prevTicks << std::endl; prevTicks = OTSYS_TIME(); #endif } } if(inRange) outOfRange = false; else if(it->isMelee) //melee swing out of reach extraMeleeAttack = true; } if(updateLook) updateLookDirection(); if(resetTicks) attackTicks = 0; }
void Actor::onAttackedCreature(Creature* target) { Creature::onAttackedCreature(target); if(isSummon()){ getMaster()->onSummonAttackedCreature(this, target); } }
void Actor::onAttackedCreatureDrainMana(Creature* target, int32_t points) { Creature::onAttackedCreatureDrainMana(target, points); if(isSummon()){ getMaster()->onSummonAttackedCreatureDrainMana(this, target, points); } }
bool Monster::getNextStep(Direction& direction, uint32_t& flags) { if (isIdle || getHealth() <= 0) { //we dont have anyone watching might aswell stop walking eventWalk = 0; return false; } bool result = false; if ((!followCreature || !hasFollowPath) && (!isSummon() || !isMasterInRange)) { if (followCreature || getTimeSinceLastMove() > 1000) { //choose a random direction result = getRandomStep(getPosition(), direction); } } else if ((isSummon() && isMasterInRange) || followCreature) { result = Creature::getNextStep(direction, flags); if (result) { flags |= FLAG_PATHFINDING; } else { //target dancing if (attackedCreature && attackedCreature == followCreature) { if (isFleeing()) { result = getDanceStep(getPosition(), direction, false, false); } else if (mType->info.staticAttackChance < static_cast<uint32_t>(uniform_random(1, 100))) { result = getDanceStep(getPosition(), direction); } } } } if (result && (canPushItems() || canPushCreatures())) { const Position& pos = Spells::getCasterPosition(this, direction); Tile* tile = g_game.map.getTile(pos); if (tile) { if (canPushItems()) { Monster::pushItems(tile); } if (canPushCreatures()) { Monster::pushCreatures(tile); } } } return result; }
void Monster::onAttackedCreatureDrainHealth(Creature* target, int32_t points) { Creature::onAttackedCreatureDrainHealth(target, points); if(isSummon()){ getMaster()->onSummonAttackedCreatureDrainHealth(this, target, points); } }
bool Monster::challengeCreature(Creature* creature) { if(isSummon() || !selectTarget(creature)) return false; targetChangeCooldown = 8000; targetChangeTicks = 0; return true; }
void Monster::onThink(uint32_t interval) { Creature::onThink(interval); if(despawn()) { g_game.internalTeleport(this, masterPos); setIdle(true); } else { updateIdleStatus(); if(!isIdle) { addEventWalk(); if(isSummon()) { if(!attackedCreature) { if(getMaster() && getMaster()->getAttackedCreature()) { ///This happens if the monster is summoned during combat selectTarget(getMaster()->getAttackedCreature()); } else if(getMaster() != followCreature) { //Our master has not ordered us to attack anything, lets follow him around instead. setFollowCreature(getMaster()); } } else if(attackedCreature == this) setFollowCreature(NULL); else if(followCreature != attackedCreature) { //This happens just after a master orders an attack, so lets follow it aswell. setFollowCreature(attackedCreature); } } else if(!targetList.empty()) { if(!followCreature || !hasFollowPath) searchTarget(); else if(isFleeing()) { if(attackedCreature && !canUseAttack(getPosition(), attackedCreature)) searchTarget(TARGETSEARCH_ATTACKRANGE); } } onThinkTarget(interval); onThinkYell(interval); onThinkDefense(interval); } } }
void Monster::onThink(uint32_t interval) { Creature::onThink(interval); if(despawn()) { g_game.removeCreature(this, true); setIdle(true); return; } updateIdleStatus(); if(isIdle) return; if(teleportToMaster && doTeleportToMaster()) teleportToMaster = false; addEventWalk(); if(getMaster()){ if(getPosition().z != getMaster()->getPosition().z){ g_game.internalTeleport(this, getMaster()->getPosition(), false); //g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_SOUND_YELLOW); } } if(isSummon()) { if(!attackedCreature) { std::string strValue; if(getMaster() && getMaster()->getAttackedCreature()) //This happens if the monster is summoned during combat selectTarget(getMaster()->getAttackedCreature()); else{ setFollowCreature((getMaster()->getStorage(500, strValue) && strValue != "-1") ? NULL : getMaster()); } } else if(attackedCreature == this) setFollowCreature(NULL); else if(followCreature != attackedCreature) //This happens just after a master orders an attack, so lets follow it aswell. setFollowCreature(attackedCreature); } else if(!targetList.empty()) { if(!followCreature || !hasFollowPath) searchTarget(); else if(isFleeing() && attackedCreature && !canUseAttack(getPosition(), attackedCreature)) searchTarget(TARGETSEARCH_ATTACKRANGE); } onThinkTarget(interval); onThinkYell(interval); onThinkDefense(interval); }
bool Monster::selectTarget(Creature* creature) { if (!isTarget(creature)) { return false; } auto it = std::find(targetList.begin(), targetList.end(), creature); if (it == targetList.end()) { //Target not found in our target list. return false; } if (isHostile() || isSummon()) { if (setAttackedCreature(creature) && !isSummon()) { g_dispatcher.addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game, getID()))); } } return setFollowCreature(creature); }
void Actor::doAttacking(uint32_t interval) { if(!attackedCreature || (isSummon() && attackedCreature == this)){ return; } // TODO: seems "outOfRange" is being unused. Why? //bool outOfRange = true; bool updateLook = true; resetTicks = interval != 0; attackTicks += interval; const Position& myPos = getPosition(); const Position& targetPos = attackedCreature->getPosition(); for(SpellList::const_iterator it = cType.spellAttackList().begin(), spell_list_end = cType.spellAttackList().end(); it != spell_list_end; ++it){ bool inRange = false; if(canUseSpell(myPos, targetPos, *it, interval, inRange)){ if(it->chance >= (uint32_t)random_range(1, 100)){ if(updateLook){ updateLookDirection(); updateLook = false; } g_game.onActorCastSpell(this, attackedCreature, (*it).name); /* if(it->isMelee){ extraMeleeAttack = false; } */ } } /* if(inRange){ outOfRange = false; } */ /* else if(it->isMelee){ //melee swing out of reach extraMeleeAttack = true; } */ } if(updateLook){ updateLookDirection(); } if(resetTicks){ attackTicks = 0; } }
void Monster::doAttacking(uint32_t interval) { if(!attackedCreature || (isSummon() && attackedCreature == this)) return; bool updateLook = true; resetTicks = interval != 0; attackTicks += interval; const Position& myPos = getPosition(); const Position& targetPos = attackedCreature->getPosition(); for(SpellList::iterator it = mType->spellAttackList.begin(); it != mType->spellAttackList.end(); ++it) { bool inRange = false; if(canUseSpell(myPos, targetPos, *it, interval, inRange)) { if(it->chance >= (uint32_t)random_range(1, 100)) { if(updateLook) { updateLookDirection(); updateLook = false; } minCombatValue = it->minCombatValue; maxCombatValue = it->maxCombatValue; it->spell->castSpell(this, attackedCreature); if(it->isMelee) extraMeleeAttack = false; #ifdef __DEBUG__ static uint64_t prevTicks = OTSYS_TIME(); std::cout << "doAttacking ticks: " << OTSYS_TIME() - prevTicks << std::endl; prevTicks = OTSYS_TIME(); #endif } } if(!inRange && it->isMelee) { //melee swing out of reach extraMeleeAttack = true; } } if(updateLook) updateLookDirection(); if(resetTicks) attackTicks = 0; }
void Monster::updateIdleStatus() { bool idle = false; if (conditions.empty()) { if (!isSummon() && targetList.empty()) { idle = true; } } setIdle(idle); }
bool Monster::challengeCreature(Creature* creature) { if (isSummon()) { return false; } bool result = selectTarget(creature); if (result) { targetChangeCooldown = 8000; targetChangeTicks = 0; } return result; }
bool Monster::convinceCreature(Creature* creature) { Player* player = creature->getPlayer(); if(player && !player->hasFlag(PlayerFlag_CanConvinceAll) && !mType->isConvinceable) return false; Creature* oldMaster = NULL; if(isSummon()) oldMaster = master; if(oldMaster) { if(oldMaster->getPlayer() || oldMaster == creature) return false; oldMaster->removeSummon(this); } setFollowCreature(NULL); setAttackedCreature(NULL); destroySummons(); creature->addSummon(this); updateTargetList(); updateIdleStatus(); //Notify surrounding about the change SpectatorVec list; g_game.getSpectators(list, getPosition(), false, true); g_game.getSpectators(list, creature->getPosition(), true, true); isMasterInRange = true; for(SpectatorVec::iterator it = list.begin(); it != list.end(); ++it) (*it)->onCreatureConvinced(creature, this); if(spawn) { spawn->removeMonster(this); spawn = NULL; masterRadius = -1; } if(raid) { raid->unRef(); raid = NULL; } return true; }
bool Monster::isFriend(const Creature* creature) { if(!isSummon() || !master->getPlayer()) return creature->getMonster() && !creature->isSummon(); const Player* tmpPlayer = NULL; if(creature->getPlayer()) tmpPlayer = creature->getPlayer(); else if(creature->getPlayerMaster()) tmpPlayer = creature->getPlayerMaster(); const Player* masterPlayer = master->getPlayer(); return tmpPlayer && (tmpPlayer == masterPlayer || masterPlayer->isPartner(tmpPlayer)); }
void Monster::onCreatureLeave(Creature* creature) { #ifdef __DEBUG__ std::clog << "onCreatureLeave - " << creature->getName() << std::endl; #endif if(isSummon() && master == creature) { if(!g_config.getBool(ConfigManager::TELEPORT_SUMMONS) && (!master->getPlayer() || !g_config.getBool(ConfigManager::TELEPORT_PLAYER_SUMMONS))) { //Turn the monster off until its master comes back isMasterInRange = false; updateIdleStatus(); } else if(!doTeleportToMaster()) teleportToMaster = true; } //update friendList if(isFriend(creature)) { CreatureList::iterator it = std::find(friendList.begin(), friendList.end(), creature); if(it != friendList.end()) { (*it)->unRef(); friendList.erase(it); } #ifdef __DEBUG__ else std::clog << "Monster: " << creature->getName() << " not found in the friendList." << std::endl; #endif } //update targetList if(isOpponent(creature)) { CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature); if(it != targetList.end()) { (*it)->unRef(); targetList.erase(it); if(targetList.empty()) updateIdleStatus(); } #ifdef __DEBUG__ else std::clog << "Player: " << creature->getName() << " not found in the targetList." << std::endl; #endif } }
void Monster::onCreatureAppear(const Creature* creature) { Creature::onCreatureAppear(creature); if(creature == this) { //We just spawned lets look around to see who is there. if(isSummon()) isMasterInRange = canSee(master->getPosition()); updateTargetList(); updateIdleStatus(); } else onCreatureEnter(const_cast<Creature*>(creature)); }
void Monster::updateIdleStatus() { bool idle = false; if(conditions.empty()) { if(isSummon()) { if(!isMasterInRange || (getMaster()->getMonster() && getMaster()->getMonster()->getIdleStatus())) idle = true; } else if(targetList.empty()) idle = true; } setIdle(idle); }
bool Monster::selectTarget(Creature* creature) { #ifdef __DEBUG__ std::cout << "Selecting target... " << std::endl; #endif if(!isTarget(creature)) return false; if(!isHostile()) return false; std::string value; if(getStorage(505, value) && value != "-1") { if(creature->isSummon()){ if(value != creature->getMaster()->getName()) return false; }else if(value != creature->getName()) //&& value != "0"){ -- se bugar return false; } CreatureList::iterator it = std::find(targetList.begin(), targetList.end(), creature); if(it == targetList.end()) { //Target not found in our target list. #ifdef __DEBUG__ std::cout << "Target not found in targetList." << std::endl; #endif return false; } if((isHostile() || isSummon()) && setAttackedCreature(creature) && !isSummon()) Dispatcher::getInstance().addTask(createTask( boost::bind(&Game::checkCreatureAttack, &g_game, getID()))); return setFollowCreature(creature, true); }