uint32 CMobEntity::GetRandomGil() { int16 min = getMobMod(MOBMOD_GIL_MIN); int16 max = getMobMod(MOBMOD_GIL_MAX); if(min && max) { // make sure divide won't crash server if(max <= min) { max = min+2; } if(max-min < 2) { max = min+2; ShowWarning("CMobEntity::GetRandomGil Max value is set too low, defauting\n"); } return rand()%(max-min)+min; } float gil = pow(GetMLevel(), 1.05f); if(gil < 1){ gil = 1; } uint16 highGil = (float)(gil) / 3 + 4; if(max) { highGil = max; } if(highGil < 2){ highGil = 2; } // randomize it gil += rand()%highGil; // NMs get more gil if((m_Type & MOBTYPE_NOTORIOUS) == MOBTYPE_NOTORIOUS){ gil *= 10; } // thfs drop more gil if(GetMJob() == JOB_THF){ gil = (float)gil * 1.5; } if(min && gil < min) { gil = min; } return gil; }
bool CMobEntity::CanLink(position_t* pos, int16 superLink) { // handle super linking if (superLink && getMobMod(MOBMOD_SUPERLINK) == superLink) { return true; } // can't link right now if (m_neutral) { return false; } // link only if I see him if (m_Detects & DETECT_SIGHT) { if (!isFaceing(loc.p, *pos, 40)) { return false; } } if (distance(loc.p, *pos) > getMobMod(MOBMOD_LINK_RADIUS)) { return false; } if (!PAI->PathFind->CanSeePoint(*pos)) { return false; } return true; }
uint32 CMobEntity::GetRandomGil() { int16 min = getMobMod(MOBMOD_GIL_MIN); int16 max = getMobMod(MOBMOD_GIL_MAX); if (min && max) { // make sure divide won't crash server if (max <= min) { max = min + 2; } if (max - min < 2) { max = min + 2; ShowWarning("CMobEntity::GetRandomGil Max value is set too low, defauting\n"); } return dsprand::GetRandomNumber(min, max); } float gil = pow(GetMLevel(), 1.05f); if (gil < 1) { gil = 1; } uint16 highGil = (float)(gil) / 3 + 4; if (max) { highGil = max; } if (highGil < 2) { highGil = 2; } // randomize it gil += dsprand::GetRandomNumber(highGil); if (min && gil < min) { gil = min; } if (getMobMod(MOBMOD_GIL_BONUS) != 0) { gil = (float)gil * (getMobMod(MOBMOD_GIL_BONUS) / 100.0f); } return gil; }
void CMobEntity::Spawn() { CBattleEntity::Spawn(); m_giveExp = true; m_HiPCLvl = 0; m_THLvl = 0; m_ItemStolen = false; m_DropItemTime = 1000; animationsub = getMobMod(MOBMOD_SPAWN_ANIMATIONSUB); CallForHelp(false); PEnmityContainer->Clear(); uint8 level = m_minLevel; // Generate a random level between min and max level if (m_maxLevel != m_minLevel) { level += dsprand::GetRandomNumber(0, m_maxLevel - m_minLevel); } SetMLevel(level); SetSLevel(level);//calculated in function delRageMode(); mobutils::CalculateStats(this); mobutils::GetAvailableSpells(this); // spawn somewhere around my point loc.p = m_SpawnPoint; if (m_roamFlags & ROAMFLAG_STEALTH) { HideName(true); Untargetable(true); } // add people to my posse if (getMobMod(MOBMOD_ASSIST)) { for (int8 i = 1; i < getMobMod(MOBMOD_ASSIST) + 1; i++) { CMobEntity* PMob = (CMobEntity*)GetEntity(targid + i, TYPE_MOB); if (PMob != nullptr) { PMob->setMobMod(MOBMOD_SUPERLINK, targid); } } } m_DespawnTimer = time_point::min(); luautils::OnMobSpawn(this); }
bool CMobEntity::CanDropGil() { // smaller than 0 means drop no gil if (getMobMod(MOBMOD_GIL_MAX) < 0) return false; if (getMobMod(MOBMOD_GIL_MIN) > 0 || getMobMod(MOBMOD_GIL_MAX)) { return true; } return getMobMod(MOBMOD_GIL_BONUS) > 0; }
bool CMobEntity::CanDropGil() { // smaller than 0 means drop no gil if(getMobMod(MOBMOD_GIL_MAX) < 0) return false; if(getMobMod(MOBMOD_GIL_MIN) > 0 || getMobMod(MOBMOD_GIL_MAX)) { return true; } return m_EcoSystem == SYSTEM_BEASTMEN; }
bool CMobEntity::CanRoamHome() { if ((speed == 0 && !(m_roamFlags & ROAMFLAG_WORM)) || getMobMod(MOBMOD_NO_MOVE) > 0) return false; if (getMobMod(MOBMOD_NO_DESPAWN) != 0 || map_config.mob_no_despawn) { return true; } return distance(m_SpawnPoint, loc.p) < roam_home_distance; }
bool CMobEntity::OnAttack(CAttackState& state, action_t& action) { static_cast<CMobController*>(PAI->GetController())->TapDeaggroTime(); if (getMobMod(MOBMOD_ATTACK_SKILL_LIST)) { return static_cast<CMobController*>(PAI->GetController())->MobSkill(getMobMod(MOBMOD_ATTACK_SKILL_LIST)); } else { return CBattleEntity::OnAttack(state, action); } }
uint8 CMobEntity::TPUseChance() { auto& MobSkillList = battleutils::GetMobSkillList(getMobMod(MOBMOD_SKILL_LIST)); if (health.tp < 1000 || MobSkillList.empty() == true || !PBattleAI->GetMobAbilityEnabled()) { return 0; } if (health.tp == 3000 || (GetHPP() <= 25 && health.tp >= 1000)) { return 100; } return getMobMod(MOBMOD_TP_USE_CHANCE); }
uint8 CMobEntity::TPUseChance() { auto& MobSkillList = battleutils::GetMobSkillList(getMobMod(MOBMOD_SKILL_LIST)); if (health.tp < 1000 || MobSkillList.empty() == true || !static_cast<CMobController*>(PAI->GetController())->IsWeaponSkillEnabled()) { return 0; } if (health.tp == 3000 || (GetHPP() <= 25 && health.tp >= 1000)) { return 100; } return getMobMod(MOBMOD_TP_USE_CHANCE); }
bool CMobEntity::CanAttack(CBattleEntity* PTarget, std::unique_ptr<CMessageBasicPacket>& errMsg) { auto skill_list_id {getMobMod(MOBMOD_ATTACK_SKILL_LIST)}; if (skill_list_id) { auto attack_range {m_ModelSize}; auto skillList {battleutils::GetMobSkillList(skill_list_id)}; if (!skillList.empty()) { auto skill {battleutils::GetMobSkill(skillList.front())}; if (skill) { attack_range = skill->getDistance(); } } if (distance(loc.p, PTarget->loc.p) > attack_range || !PAI->GetController()->IsAutoAttackEnabled()) { return false; } return true; } else { return CBattleEntity::CanAttack(PTarget, errMsg); } }
void CMobEntity::OnDisengage(CAttackState& state) { PAI->PathFind->Clear(); PEnmityContainer->Clear(); if (getMobMod(MOBMOD_IDLE_DESPAWN)) { SetDespawnTime(std::chrono::milliseconds(getMobMod(MOBMOD_IDLE_DESPAWN))); } // this will let me decide to walk home or despawn m_neutral = true; delRageMode(); m_OwnerID.clean(); CBattleEntity::OnDisengage(state); luautils::OnMobDisengage(this); }
uint8 CMobEntity::TPUseChance() { if(health.tp < 1000) return 0; if(health.tp == 3000 || (GetHPP() <= 25 && health.tp >= 1000)) { return 100; } return getMobMod(MOBMOD_TP_USE_CHANCE); }
bool CMobEntity::CanRoamHome() { if(speed == 0 && !(m_roamFlags & ROAMFLAG_WORM)) return false; if (getMobMod(MOBMOD_NO_DESPAWN) != 0 || map_config.mob_no_despawn) { return true; } return distance(m_SpawnPoint, loc.p) < MOB_ROAM_HOME_DISTANCE; }
bool CMobEntity::CanLink(position_t* pos, int16 superLink) { // handle super linking if(superLink && getMobMod(MOBMOD_SUPERLINK) == superLink) { return true; } // can't link right now if(m_neutral) { return false; } // link only if I see him if ((m_Aggro & AGGRO_DETECT_SIGHT) || (m_Aggro & AGGRO_DETECT_TRUESIGHT)){ if(!isFaceing(loc.p, *pos, 40)) return false; } // link if close enough return distance(loc.p, *pos) <= getMobMod(MOBMOD_LINK_RADIUS); }
uint8 CMobEntity::TPUseChance() { if (!PBattleAI->GetMobAbilityEnabled()) { return 0; } if(health.tp < 1000) return 0; if(health.tp == 3000 || (GetHPP() <= 25 && health.tp >= 1000)) { return 100; } return getMobMod(MOBMOD_TP_USE_CHANCE); }
bool CMobEntity::CanDetectTarget(CBattleEntity* PTarget, bool forceSight) { if (PTarget->isDead() || m_Aggro == AGGRO_NONE || PTarget->animation == ANIMATION_CHOCOBO) return false; float verticalDistance = abs(loc.p.y - PTarget->loc.p.y); if(verticalDistance > 8) { return false; } float currentDistance = distance(PTarget->loc.p, loc.p) + PTarget->getMod(MOD_STEALTH); bool detectSight = (m_Aggro & AGGRO_DETECT_SIGHT) || forceSight; if (detectSight && !PTarget->StatusEffectContainer->HasStatusEffectByFlag(EFFECTFLAG_INVISIBLE) && currentDistance < getMobMod(MOBMOD_SIGHT_RANGE) && isFaceing(loc.p, PTarget->loc.p, 40)) { return true; } if ((m_Aggro & AGGRO_DETECT_TRUESIGHT) && currentDistance < getMobMod(MOBMOD_SIGHT_RANGE) && isFaceing(loc.p, PTarget->loc.p, 40)) { return true; } if ((m_Aggro & AGGRO_DETECT_TRUEHEARING) && currentDistance < getMobMod(MOBMOD_SOUND_RANGE)) { return true; } if ((m_Behaviour & BEHAVIOUR_AGGRO_AMBUSH) && currentDistance < 3 && !PTarget->StatusEffectContainer->HasStatusEffect(EFFECT_SNEAK)) { return true; } if ((m_Aggro & AGGRO_DETECT_HEARING) && currentDistance < getMobMod(MOBMOD_SOUND_RANGE) && !PTarget->StatusEffectContainer->HasStatusEffect(EFFECT_SNEAK)) { return true; } // everything below require distance to be below 20 if(currentDistance > 20) { return false; } if ((m_Aggro & AGGRO_DETECT_LOWHP) && PTarget->GetHPP() < 75) { return true; } if ((m_Aggro & AGGRO_DETECT_MAGIC) && PTarget->PBattleAI->GetCurrentAction() == ACTION_MAGIC_CASTING && PTarget->PBattleAI->GetCurrentSpell()->hasMPCost()) { return true; } if ((m_Aggro & AGGRO_DETECT_WEAPONSKILL) && PTarget->PBattleAI->GetCurrentAction() == ACTION_WEAPONSKILL_FINISH) { return true; } if ((m_Aggro & AGGRO_DETECT_JOBABILITY) && PTarget->PBattleAI->GetCurrentAction() == ACTION_JOBABILITY_FINISH) { return true; } return false; }
bool CMobEntity::CanRoamHome() { if(speed == 0 && !(m_roamFlags & ROAMFLAG_WORM)) return false; return getMobMod(MOBMOD_NO_DESPAWN); }
int32 CMobEntity::getBigMobMod(uint16 type) { PROFILE_FUNC(); return getMobMod(type) * 1000; }
void CMobEntity::DropItems() { CCharEntity* PChar = (CCharEntity*)GetEntity(m_OwnerID.targid, TYPE_PC); if (PChar != nullptr && PChar->id == m_OwnerID.id) { loc.zone->PushPacket(this, CHAR_INRANGE, new CMessageBasicPacket(PChar, this, 0, 0, MSGBASIC_DEFEATS_TARG)); if (!CalledForHelp()) { blueutils::TryLearningSpells(PChar, this); m_UsedSkillIds.clear(); if (m_giveExp) { charutils::DistributeExperiencePoints(PChar, this); } DropList_t* DropList = itemutils::GetDropList(m_DropID); //ShowDebug(CL_CYAN"DropID: %u dropping with TH Level: %u\n" CL_RESET, PMob->m_DropID, PMob->m_THLvl); if (DropList != nullptr && !getMobMod(MOBMOD_NO_DROPS) && DropList->size()) { for (uint8 i = 0; i < DropList->size(); ++i) { //THLvl is the number of 'extra chances' at an item. If the item is obtained, then break out. uint8 tries = 0; uint8 maxTries = 1 + (m_THLvl > 2 ? 2 : m_THLvl); uint8 bonus = (m_THLvl > 2 ? (m_THLvl - 2) * 10 : 0); while (tries < maxTries) { if (dsprand::GetRandomNumber(1000) < DropList->at(i).DropRate * map_config.drop_rate_multiplier + bonus) { PChar->PTreasurePool->AddItem(DropList->at(i).ItemID, this); break; } tries++; } } } // check for gil (beastmen drop gil, some NMs drop gil) if (CanDropGil() || (map_config.all_mobs_gil_bonus > 0 && getMobMod(MOBMOD_GIL_MAX) >= 0)) // Negative value of MOBMOD_GIL_MAX is used to prevent gil drops in Dynamis/Limbus. { charutils::DistributeGil(PChar, this); // TODO: REALISATION MUST BE IN TREASUREPOOL } //check for seal drops /* MobLvl >= 1 = Beastmen Seals ID=1126 >= 50 = Kindred Seals ID=1127 >= 75 = Kindred Crests ID=2955 >= 90 = High Kindred Crests ID=2956 */ uint16 Pzone = PChar->getZone(); bool validZone = ((Pzone > 0 && Pzone < 39) || (Pzone > 42 && Pzone < 134) || (Pzone > 135 && Pzone < 185) || (Pzone > 188 && Pzone < 255)); if (validZone && charutils::GetRealExp(PChar->GetMLevel(), GetMLevel()) > 0) { if (((PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SIGNET) && conquest::GetInfluenceGraphics(PChar->loc.zone->GetRegionID()) < 64) || (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SANCTION) && PChar->loc.zone->GetRegionID() >= 28 && PChar->loc.zone->GetRegionID() <= 32) || (PChar->StatusEffectContainer->HasStatusEffect(EFFECT_SIGIL) && PChar->loc.zone->GetRegionID() >= 33 && PChar->loc.zone->GetRegionID() <= 40)) && m_Element > 0 && dsprand::GetRandomNumber(100) < 20) // Need to move to CRYSTAL_CHANCE constant { PChar->PTreasurePool->AddItem(4095 + m_Element, this); } // Todo: Avatarite and Geode drops during day/weather. Much higher chance during weather than day. // Item element matches day/weather element, not mob crystal. Lv80+ xp mobs can drop Avatarite. // Wiki's have conflicting info on mob lv required for Geodes. One says 50 the other 75. I think 50 is correct. if (dsprand::GetRandomNumber(100) < 20 && PChar->PTreasurePool->CanAddSeal() && !getMobMod(MOBMOD_NO_DROPS)) { //RULES: Only 1 kind may drop per mob if (GetMLevel() >= 75 && luautils::IsExpansionEnabled("ABYSSEA")) //all 4 types { switch (dsprand::GetRandomNumber(4)) { case 0: PChar->PTreasurePool->AddItem(1126, this); break; case 1: PChar->PTreasurePool->AddItem(1127, this); break; case 2: PChar->PTreasurePool->AddItem(2955, this); break; case 3: PChar->PTreasurePool->AddItem(2956, this); break; } } else if (GetMLevel() >= 70 && luautils::IsExpansionEnabled("ABYSSEA")) //b.seal & k.seal & k.crest { switch (dsprand::GetRandomNumber(3)) { case 0: PChar->PTreasurePool->AddItem(1126, this); break; case 1: PChar->PTreasurePool->AddItem(1127, this); break; case 2: PChar->PTreasurePool->AddItem(2955, this); break; } } else if (GetMLevel() >= 50) //b.seal & k.seal only { if (dsprand::GetRandomNumber(2) == 0) { PChar->PTreasurePool->AddItem(1126, this); } else { PChar->PTreasurePool->AddItem(1127, this); } } else { //b.seal only PChar->PTreasurePool->AddItem(1126, this); } } } } PChar->setWeaponSkillKill(false); StatusEffectContainer->KillAllStatusEffect(); // NOTE: this is called for all alliance / party members! luautils::OnMobDeath(this, PChar); } else { luautils::OnMobDeath(this, nullptr); } }
float CMobEntity::GetRoamRate() { return (float)getMobMod(MOBMOD_ROAM_RATE) / 10.0f; }
bool CMobEntity::CanRoam() { return !(m_roamFlags & ROAMFLAG_EVENT) && PMaster == nullptr && (speed > 0 || (m_roamFlags & ROAMFLAG_WORM)) && getMobMod(MOBMOD_NO_MOVE) == 0; }
int32 CMobEntity::getBigMobMod(uint16 type) { return getMobMod(type) * 1000; }
float CMobEntity::GetRoamDistance() { return (float)getMobMod(MOBMOD_ROAM_DISTANCE) / 10.0f; }