void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { if(!sender || !attacker) return; if (sender->GetPrimaryFaction() == 0 ) return; // well, if we dont have a faction set, we're gonna be indiff to everybody LinkedListIterator<NPC*> iterator(npc_list); for(iterator.Reset(); iterator.MoreElements(); iterator.Advance()) { NPC* mob = iterator.GetData(); if(!mob){ continue; } float r = mob->GetAssistRange(); r = r * r; if ( mob != sender && mob != attacker // && !mob->IsCorpse() // && mob->IsAIControlled() && mob->GetPrimaryFaction() != 0 && mob->DistNoRoot(*sender) <= r && !mob->IsEngaged() && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) // If we're a pet we don't react to any calls for help if our owner is a client ) { //if they are in range, make sure we are not green... //then jump in if they are our friend if(attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN) { bool useprimfaction = false; if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID()); if(cf){ if(cf->assistprimaryfaction != 0) useprimfaction = true; } } if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE ) { //attacking someone on same faction, or a friend //Father Nitwit: make sure we can see them. if(mob->CheckLosFN(sender)) { #if (EQDEBUG>=5) LogFile->write(EQEMuLog::Debug, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), mob->DistNoRoot(*sender), fabs(sender->GetZ()+mob->GetZ())); #endif mob->AddToHateList(attacker, 1, 0, false); } } } } } }
Mob* EntityList::AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange) { if (!sender || !sender->IsNPC()) return(nullptr); #ifdef REVERSE_AGGRO //with reverse aggro, npc->client is checked elsewhere, no need to check again auto it = npc_list.begin(); while (it != npc_list.end()) { #else auto it = mob_list.begin(); while (it != mob_list.end()) { #endif Mob *mob = it->second; if (sender->CheckWillAggro(mob)) return mob; ++it; } //LogFile->write(EQEMuLog::Debug, "Check aggro for %s no target.", sender->GetName()); return nullptr; } int EntityList::GetHatedCount(Mob *attacker, Mob *exclude) { // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker if (!attacker) return 0; int Count = 0; for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; if (!mob || (mob == exclude)) continue; if (!mob->IsEngaged()) continue; if (mob->IsFeared() || mob->IsMezzed()) continue; if (attacker->GetLevelCon(mob->GetLevel()) == CON_GREEN) continue; if (!mob->CheckAggro(attacker)) continue; float AggroRange = mob->GetAggroRange(); // Square it because we will be using DistNoRoot AggroRange *= AggroRange; if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) continue; Count++; } return Count; } void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { if(!sender || !attacker) return; if (sender->GetPrimaryFaction() == 0 ) return; // well, if we dont have a faction set, we're gonna be indiff to everybody if (sender->HasAssistAggro()) return; for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; if (!mob) continue; if (mob->CheckAggro(attacker)) continue; if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) break; float r = mob->GetAssistRange(); r = r * r; if ( mob != sender && mob != attacker // && !mob->IsCorpse() // && mob->IsAIControlled() && mob->GetPrimaryFaction() != 0 && DistanceSquared(mob->GetPosition(), sender->GetPosition()) <= r && !mob->IsEngaged() && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) // If we're a pet we don't react to any calls for help if our owner is a client ) { //if they are in range, make sure we are not green... //then jump in if they are our friend if(attacker->GetLevelCon(mob->GetLevel()) != CON_GREEN) { bool useprimfaction = false; if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID()); if(cf){ if(cf->assistprimaryfaction != 0) useprimfaction = true; } } if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE ) { //attacking someone on same faction, or a friend //Father Nitwit: make sure we can see them. if(mob->CheckLosFN(sender)) { #if (EQDEBUG>=5) Log.Out(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), DistanceSquared(mob->GetPosition(), sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); #endif mob->AddToHateList(attacker, 25, 0, false); sender->AddAssistCap(); } } } } } } /* returns false if attack should not be allowed I try to list every type of conflict that's possible here, so it's easy to see how the decision is made. Yea, it could be condensed and made faster, but I'm doing it this way to make it readable and easy to modify */ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) { Mob *mob1, *mob2, *tempmob; Client *c1, *c2, *becomenpc; // NPC *npc1, *npc2; int reverse; if(!zone->CanDoCombat()) return false; // some special cases if(!target) return false; if(this == target) // you can attack yourself return true; if(target->GetSpecialAbility(NO_HARM_FROM_CLIENT)){ return false; } // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); if(target_owner && target_owner == this) return false; else if(our_owner && our_owner == target) return false; // invalidate for swarm pets for later on if their owner is a corpse if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner && our_owner->IsCorpse() && !our_owner->IsPlayerCorpse()) our_owner = nullptr; if (target->IsNPC() && target->CastToNPC()->GetSwarmInfo() && target_owner && target_owner->IsCorpse() && !target_owner->IsPlayerCorpse()) target_owner = nullptr; //cannot hurt untargetable mobs bodyType bt = target->GetBodyType(); if(bt == BT_NoTarget || bt == BT_NoTarget2) { if (RuleB(Pets, UnTargetableSwarmPet)) { if (target->IsNPC()) { if (!target->CastToNPC()->GetSwarmOwner()) { return(false); } } else { return(false); } } else { return(false); } } if(!isSpellAttack) { if(GetClass() == LDON_TREASURE) { return false; } } // the format here is a matrix of mob type vs mob type. // redundant ones are omitted and the reverse is tried if it falls through. // first figure out if we're pets. we always look at the master's flags. // no need to compare pets to anything mob1 = our_owner ? our_owner : this; mob2 = target_owner ? target_owner : target; reverse = 0; do { if(_CLIENT(mob1)) { if(_CLIENT(mob2)) // client vs client { c1 = mob1->CastToClient(); c2 = mob2->CastToClient(); if // if both are pvp they can fight ( c1->GetPVP() && c2->GetPVP() ) return true; else if // if they're dueling they can go at it ( c1->IsDueling() && c2->IsDueling() && c1->GetDuelTarget() == c2->GetID() && c2->GetDuelTarget() == c1->GetID() ) return true; else return false; } else if(_NPC(mob2)) // client vs npc { return true; } else if(_BECOMENPC(mob2)) // client vs becomenpc { c1 = mob1->CastToClient(); becomenpc = mob2->CastToClient(); if(c1->GetLevel() > becomenpc->GetBecomeNPCLevel()) return false; else return true; } else if(_CLIENTCORPSE(mob2)) // client vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client vs npc corpse { return false; } } else if(_NPC(mob1)) { if(_NPC(mob2)) // npc vs npc { /* this says that an NPC can NEVER attack a faction ally... this is stupid... somebody else should check this rule if they want to enforce it, this just says 'can they possibly fight based on their type', in which case, the answer is yes. */ /* npc1 = mob1->CastToNPC(); npc2 = mob2->CastToNPC(); if ( npc1->GetPrimaryFaction() != 0 && npc2->GetPrimaryFaction() != 0 && ( npc1->GetPrimaryFaction() == npc2->GetPrimaryFaction() || npc1->IsFactionListAlly(npc2->GetPrimaryFaction()) ) ) return false; else */ return true; } else if(_BECOMENPC(mob2)) // npc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // npc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // npc vs npc corpse { return false; } } else if(_BECOMENPC(mob1)) { if(_BECOMENPC(mob2)) // becomenpc vs becomenpc { return true; } else if(_CLIENTCORPSE(mob2)) // becomenpc vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // becomenpc vs npc corpse { return false; } } else if(_CLIENTCORPSE(mob1)) { if(_CLIENTCORPSE(mob2)) // client corpse vs client corpse { return false; } else if(_NPCCORPSE(mob2)) // client corpse vs npc corpse { return false; } } else if(_NPCCORPSE(mob1)) { if(_NPCCORPSE(mob2)) // npc corpse vs npc corpse { return false; } } #ifdef BOTS bool HasRuleDefined = false; bool IsBotAttackAllowed = false; IsBotAttackAllowed = Bot::IsBotAttackAllowed(mob1, mob2, HasRuleDefined); if(HasRuleDefined) return IsBotAttackAllowed; #endif //BOTS // we fell through, now we swap the 2 mobs and run through again once more tempmob = mob1; mob1 = mob2; mob2 = tempmob; } while( reverse++ == 0 ); Log.Out(Logs::General, Logs::None, "Mob::IsAttackAllowed: don't have a rule for this - %s vs %s\n", this->GetName(), target->GetName()); return false; }
void Client::GoFish() { //TODO: generate a message if we're already fishing /*if (!fishing_timer.Check()) { //this isn't the right check, may need to add something to the Client class like 'bool is_fishing' Message_StringID(0, ALREADY_FISHING); //You are already fishing! return; }*/ fishing_timer.Disable(); //we're doing this a second time (1st in Client::Handle_OP_Fishing) to make sure that, between when we started fishing & now, we're still able to fish (in case we move, change equip, etc) if (!CanFish()) //if we can't fish here, we don't need to bother with the rest return; //multiple entries yeilds higher probability of dropping... uint32 common_fish_ids[MAX_COMMON_FISH_IDS] = { 1038, // Tattered Cloth Sandals 1038, // Tattered Cloth Sandals 1038, // Tattered Cloth Sandals 13019, // Fresh Fish 13076, // Fish Scales 13076, // Fish Scales 7007, // Rusty Dagger 7007, // Rusty Dagger 7007 // Rusty Dagger }; //success formula is not researched at all int fishing_skill = GetSkill(FISHING); //will take into account skill bonuses on pole & bait //make sure we still have a fishing pole on: int32 bslot = m_inv.HasItemByUse(ItemTypeFishingBait, 1, invWhereWorn|invWherePersonal); const ItemInst* Bait = NULL; if(bslot != SLOT_INVALID) Bait = m_inv.GetItem(bslot); //if the bait isnt equipped, need to add its skill bonus if(bslot >= IDX_INV && Bait->GetItem()->SkillModType == FISHING) { fishing_skill += Bait->GetItem()->SkillModValue; } if (fishing_skill > 100) { fishing_skill = 100+((fishing_skill-100)/2); } if (MakeRandomInt(0,175) < fishing_skill) { uint32 food_id = 0; //25% chance to fish an item. if (MakeRandomInt(0, 399) <= fishing_skill ) { uint32 npc_id = 0; uint8 npc_chance = 0; food_id = database.GetZoneFishing(m_pp.zone_id, fishing_skill, npc_id, npc_chance); //check for add NPC if(npc_chance > 0 && npc_id) { if(npc_chance < MakeRandomInt(0, 99)) { const NPCType* tmp = database.GetNPCType(npc_id); if(tmp != NULL) { NPC* npc = new NPC(tmp, NULL, GetX()+3, GetY(), GetZ(), GetHeading(), FlyMode3); npc->AddLootTable(); npc->AddToHateList(this, 1, 0, false); //no help yelling entity_list.AddNPC(npc); Message(MT_Emote, "You fish up a little more than you bargained for..."); } } } } //consume bait, should we always consume bait on success? DeleteItemInInventory(bslot, 1, true); //do we need client update? if(food_id == 0) { int index = MakeRandomInt(0, MAX_COMMON_FISH_IDS-1); food_id = common_fish_ids[index]; } const Item_Struct* food_item = database.GetItem(food_id); Message_StringID(MT_Skills, FISHING_SUCCESS); const ItemInst* inst = database.CreateItem(food_item, 1); if(inst != NULL) { if(CheckLoreConflict(inst->GetItem())) { this->Message_StringID(0,DUP_LORE); } else { PushItemOnCursor(*inst); // changed from PutItemInInventory(SLOT_CURSOR, *inst); - was additional overhead SendItemPacket(SLOT_CURSOR,inst,ItemPacketSummonItem); if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityFish, food_id); } safe_delete(inst); } parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst != NULL ? inst->GetItem()->ID : 0); } else { //chance to use bait when you dont catch anything... if (MakeRandomInt(0, 4) == 1) { DeleteItemInInventory(bslot, 1, true); //do we need client update? Message_StringID(MT_Skills, FISHING_LOST_BAIT); //You lost your bait! } else { if (MakeRandomInt(0, 15) == 1) //give about a 1 in 15 chance to spill your beer. we could make this a rule, but it doesn't really seem worth it //TODO: check for & consume an alcoholic beverage from inventory when this triggers, and set it as a rule that's disabled by default Message_StringID(MT_Skills, FISHING_SPILL_BEER); //You spill your beer while bringing in your line. else Message_StringID(MT_Skills, FISHING_FAILED); //You didn't catch anything. } parse->EventPlayer(EVENT_FISH_FAILURE, this, "", 0); } //chance to break fishing pole... //this is potentially exploitable in that they can fish //and then swap out items in primary slot... too lazy to fix right now if (MakeRandomInt(0, 49) == 1) { Message_StringID(MT_Skills, FISHING_POLE_BROKE); //Your fishing pole broke! DeleteItemInInventory(13,0,true); } if(CheckIncreaseSkill(FISHING, NULL, 5)) { if(title_manager.IsNewTradeSkillTitleAvailable(FISHING, GetRawSkill(FISHING))) NotifyNewTitlesAvailable(); } }
void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg) { //It might not be a bad idea to put these into the database, eventually.. //Dook- swarms and wards PetRecord record; if(!database.GetPetEntry(spells[spell_id].teleport_zone, &record)) { Log.Out(Logs::General, Logs::Error, "Unknown swarm pet spell id: %d, check pets table", spell_id); Message(13, "Unable to find data for pet %s", spells[spell_id].teleport_zone); return; } AA_SwarmPet pet; pet.count = 1; pet.duration = 1; for(int x = 0; x < MAX_SWARM_PETS; x++) { if(spells[spell_id].effectid[x] == SE_TemporaryPets) { pet.count = spells[spell_id].base[x]; pet.duration = spells[spell_id].max[x]; } } pet.duration += GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000; pet.npc_id = record.npc_type; NPCType *made_npc = nullptr; const NPCType *npc_type = database.LoadNPCTypesData(pet.npc_id); if(npc_type == nullptr) { //log write Log.Out(Logs::General, Logs::Error, "Unknown npc type for swarm pet spell id: %d", spell_id); Message(0,"Unable to find pet!"); return; } if(name_override != nullptr) { //we have to make a custom NPC type for this name change made_npc = new NPCType; memcpy(made_npc, npc_type, sizeof(NPCType)); strcpy(made_npc->name, name_override); npc_type = made_npc; } int summon_count = 0; summon_count = pet.count; if(summon_count > MAX_SWARM_PETS) summon_count = MAX_SWARM_PETS; static const glm::vec2 swarmPetLocations[MAX_SWARM_PETS] = { glm::vec2(5, 5), glm::vec2(-5, 5), glm::vec2(5, -5), glm::vec2(-5, -5), glm::vec2(10, 10), glm::vec2(-10, 10), glm::vec2(10, -10), glm::vec2(-10, -10), glm::vec2(8, 8), glm::vec2(-8, 8), glm::vec2(8, -8), glm::vec2(-8, -8) }; while(summon_count > 0) { int pet_duration = pet.duration; if(duration_override > 0) pet_duration = duration_override; //this is a little messy, but the only way to do it right //it would be possible to optimize out this copy for the last pet, but oh well NPCType *npc_dup = nullptr; if(made_npc != nullptr) { npc_dup = new NPCType; memcpy(npc_dup, made_npc, sizeof(NPCType)); } NPC* npca = new NPC( (npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer 0, GetPosition() + glm::vec4(swarmPetLocations[summon_count], 0.0f, 0.0f), FlyMode3); if (followme) npca->SetFollowID(GetID()); if(!npca->GetSwarmInfo()){ auto nSI = new AA_SwarmPetInfo; npca->SetSwarmInfo(nSI); npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); } else{ npca->GetSwarmInfo()->duration->Start(pet_duration*1000); } //removing this prevents the pet from attacking npca->GetSwarmInfo()->owner_id = GetID(); //give the pets somebody to "love" if(targ != nullptr){ npca->AddToHateList(targ, 1000, 1000); if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) npca->GetSwarmInfo()->target = targ->GetID(); else npca->GetSwarmInfo()->target = 0; } //we allocated a new NPC type object, give the NPC ownership of that memory if(npc_dup != nullptr) npca->GiveNPCTypeData(npc_dup); entity_list.AddNPC(npca, true, true); summon_count--; } //the target of these swarm pets will take offense to being cast on... if(targ != nullptr) targ->AddToHateList(this, 1, 0); // The other pointers we make are handled elsewhere. delete made_npc; }
void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg) { AA_SwarmPet pet; pet.count = 1; pet.duration = 1; pet.npc_id = typesid; NPCType *made_npc = nullptr; const NPCType *npc_type = database.LoadNPCTypesData(typesid); if(npc_type == nullptr) { //log write Log.Out(Logs::General, Logs::Error, "Unknown npc type for swarm pet type id: %d", typesid); Message(0,"Unable to find pet!"); return; } if(name_override != nullptr) { //we have to make a custom NPC type for this name change made_npc = new NPCType; memcpy(made_npc, npc_type, sizeof(NPCType)); strcpy(made_npc->name, name_override); npc_type = made_npc; } int summon_count = 0; summon_count = pet.count; if(summon_count > MAX_SWARM_PETS) summon_count = MAX_SWARM_PETS; static const glm::vec2 swarmPetLocations[MAX_SWARM_PETS] = { glm::vec2(5, 5), glm::vec2(-5, 5), glm::vec2(5, -5), glm::vec2(-5, -5), glm::vec2(10, 10), glm::vec2(-10, 10), glm::vec2(10, -10), glm::vec2(-10, -10), glm::vec2(8, 8), glm::vec2(-8, 8), glm::vec2(8, -8), glm::vec2(-8, -8) };; while(summon_count > 0) { int pet_duration = pet.duration; if(duration_override > 0) pet_duration = duration_override; //this is a little messy, but the only way to do it right //it would be possible to optimize out this copy for the last pet, but oh well NPCType *npc_dup = nullptr; if(made_npc != nullptr) { npc_dup = new NPCType; memcpy(npc_dup, made_npc, sizeof(NPCType)); } NPC* npca = new NPC( (npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer 0, GetPosition() + glm::vec4(swarmPetLocations[summon_count], 0.0f, 0.0f), FlyMode3); if (followme) npca->SetFollowID(GetID()); if(!npca->GetSwarmInfo()){ auto nSI = new AA_SwarmPetInfo; npca->SetSwarmInfo(nSI); npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); } else{ npca->GetSwarmInfo()->duration->Start(pet_duration*1000); } //removing this prevents the pet from attacking npca->GetSwarmInfo()->owner_id = GetID(); //give the pets somebody to "love" if(targ != nullptr){ npca->AddToHateList(targ, 1000, 1000); if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) npca->GetSwarmInfo()->target = targ->GetID(); else npca->GetSwarmInfo()->target = 0; } //we allocated a new NPC type object, give the NPC ownership of that memory if(npc_dup != nullptr) npca->GiveNPCTypeData(npc_dup); entity_list.AddNPC(npca, true, true); summon_count--; } // The other pointers we make are handled elsewhere. delete made_npc; }
void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration) { Corpse *CorpseToUse = nullptr; CorpseToUse = entity_list.GetClosestCorpse(this, nullptr); if (!CorpseToUse) return; //assuming we have pets in our table; we take the first pet as a base type. const NPCType *base_type = database.GetNPCType(500); NPCType *make_npc = new NPCType; memcpy(make_npc, base_type, sizeof(NPCType)); //combat stats make_npc->AC = ((GetLevel() * 7) + 550); make_npc->ATK = GetLevel(); make_npc->max_dmg = (GetLevel() * 4) + 2; make_npc->min_dmg = 1; //base stats make_npc->cur_hp = (GetLevel() * 55); make_npc->max_hp = (GetLevel() * 55); make_npc->STR = 85 + (GetLevel() * 3); make_npc->STA = 85 + (GetLevel() * 3); make_npc->DEX = 85 + (GetLevel() * 3); make_npc->AGI = 85 + (GetLevel() * 3); make_npc->INT = 85 + (GetLevel() * 3); make_npc->WIS = 85 + (GetLevel() * 3); make_npc->CHA = 85 + (GetLevel() * 3); make_npc->MR = 25; make_npc->FR = 25; make_npc->CR = 25; make_npc->DR = 25; make_npc->PR = 25; //level class and gender make_npc->level = GetLevel(); make_npc->class_ = CorpseToUse->class_; make_npc->race = CorpseToUse->race; make_npc->gender = CorpseToUse->gender; make_npc->loottable_id = 0; //name char NewName[64]; sprintf(NewName, "%s`s Animated Corpse", GetCleanName()); strcpy(make_npc->name, NewName); //appearance make_npc->beard = CorpseToUse->beard; make_npc->beardcolor = CorpseToUse->beardcolor; make_npc->eyecolor1 = CorpseToUse->eyecolor1; make_npc->eyecolor2 = CorpseToUse->eyecolor2; make_npc->haircolor = CorpseToUse->haircolor; make_npc->hairstyle = CorpseToUse->hairstyle; make_npc->helmtexture = CorpseToUse->helmtexture; make_npc->luclinface = CorpseToUse->luclinface; make_npc->size = CorpseToUse->size; make_npc->texture = CorpseToUse->texture; //cast stuff.. based off of PEQ's if you want to change //it you'll have to mod this code, but most likely //most people will be using PEQ style for the first //part of their spell list; can't think of any smooth //way to do this //some basic combat mods here too since it's convienent switch (CorpseToUse->class_) { case CLERIC: make_npc->npc_spells_id = 1; break; case WIZARD: make_npc->npc_spells_id = 2; break; case NECROMANCER: make_npc->npc_spells_id = 3; break; case MAGICIAN: make_npc->npc_spells_id = 4; break; case ENCHANTER: make_npc->npc_spells_id = 5; break; case SHAMAN: make_npc->npc_spells_id = 6; break; case DRUID: make_npc->npc_spells_id = 7; break; case PALADIN: //SPECATK_TRIPLE strcpy(make_npc->special_abilities, "6,1"); make_npc->cur_hp = make_npc->cur_hp * 150 / 100; make_npc->max_hp = make_npc->max_hp * 150 / 100; make_npc->npc_spells_id = 8; break; case SHADOWKNIGHT: strcpy(make_npc->special_abilities, "6,1"); make_npc->cur_hp = make_npc->cur_hp * 150 / 100; make_npc->max_hp = make_npc->max_hp * 150 / 100; make_npc->npc_spells_id = 9; break; case RANGER: strcpy(make_npc->special_abilities, "7,1"); make_npc->cur_hp = make_npc->cur_hp * 135 / 100; make_npc->max_hp = make_npc->max_hp * 135 / 100; make_npc->npc_spells_id = 10; break; case BARD: strcpy(make_npc->special_abilities, "6,1"); make_npc->cur_hp = make_npc->cur_hp * 110 / 100; make_npc->max_hp = make_npc->max_hp * 110 / 100; make_npc->npc_spells_id = 11; break; case BEASTLORD: strcpy(make_npc->special_abilities, "7,1"); make_npc->cur_hp = make_npc->cur_hp * 110 / 100; make_npc->max_hp = make_npc->max_hp * 110 / 100; make_npc->npc_spells_id = 12; break; case ROGUE: strcpy(make_npc->special_abilities, "7,1"); make_npc->max_dmg = make_npc->max_dmg * 150 / 100; make_npc->cur_hp = make_npc->cur_hp * 110 / 100; make_npc->max_hp = make_npc->max_hp * 110 / 100; break; case MONK: strcpy(make_npc->special_abilities, "7,1"); make_npc->max_dmg = make_npc->max_dmg * 150 / 100; make_npc->cur_hp = make_npc->cur_hp * 135 / 100; make_npc->max_hp = make_npc->max_hp * 135 / 100; break; case WARRIOR: strcpy(make_npc->special_abilities, "7,1"); make_npc->max_dmg = make_npc->max_dmg * 150 / 100; make_npc->cur_hp = make_npc->cur_hp * 175 / 100; make_npc->max_hp = make_npc->max_hp * 175 / 100; break; default: make_npc->npc_spells_id = 0; break; } make_npc->loottable_id = 0; make_npc->merchanttype = 0; make_npc->d_melee_texture1 = 0; make_npc->d_melee_texture2 = 0; NPC* npca = new NPC(make_npc, 0, GetPosition(), FlyMode3); if (!npca->GetSwarmInfo()){ AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; npca->SetSwarmInfo(nSI); npca->GetSwarmInfo()->duration = new Timer(duration * 1000); } else{ npca->GetSwarmInfo()->duration->Start(duration * 1000); } npca->GetSwarmInfo()->owner_id = GetID(); //give the pet somebody to "love" if (target != nullptr){ npca->AddToHateList(target, 100000); npca->GetSwarmInfo()->target = target->GetID(); } //gear stuff, need to make sure there's //no situation where this stuff can be duped for (int x = EmuConstants::EQUIPMENT_BEGIN; x <= EmuConstants::EQUIPMENT_END; x++) // (< 21) added MainAmmo { uint32 sitem = 0; sitem = CorpseToUse->GetWornItem(x); if (sitem){ const Item_Struct * itm = database.GetItem(sitem); npca->AddLootDrop(itm, &npca->itemlist, 1, 1, 127, true, true); } } //we allocated a new NPC type object, give the NPC ownership of that memory if (make_npc != nullptr) npca->GiveNPCTypeData(make_npc); entity_list.AddNPC(npca, true, true); //the target of these swarm pets will take offense to being cast on... if (target != nullptr) target->AddToHateList(this, 1, 0); }
void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { if(!sender || !attacker) return; if (sender->GetPrimaryFaction() == 0 ) return; // well, if we dont have a faction set, we're gonna be indiff to everybody if (sender->HasAssistAggro()) return; for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { NPC *mob = it->second; if (!mob) continue; if (mob->CheckAggro(attacker)) continue; if (sender->NPCAssistCap() >= RuleI(Combat, NPCAssistCap)) break; float r = mob->GetAssistRange(); r = r * r; if ( mob != sender && mob != attacker // && !mob->IsCorpse() // && mob->IsAIControlled() && mob->GetPrimaryFaction() != 0 && DistanceSquared(mob->GetPosition(), sender->GetPosition()) <= r && !mob->IsEngaged() && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) // If we're a pet we don't react to any calls for help if our owner is a client ) { //if they are in range, make sure we are not green... //then jump in if they are our friend if(mob->GetLevel() >= 50 || attacker->GetLevelCon(mob->GetLevel()) != CON_GRAY) { bool useprimfaction = false; if(mob->GetPrimaryFaction() == sender->CastToNPC()->GetPrimaryFaction()) { const NPCFactionList *cf = database.GetNPCFactionEntry(mob->GetNPCFactionID()); if(cf){ if(cf->assistprimaryfaction != 0) useprimfaction = true; } } if(useprimfaction || sender->GetReverseFactionCon(mob) <= FACTION_AMIABLE ) { //attacking someone on same faction, or a friend //Father Nitwit: make sure we can see them. if(mob->CheckLosFN(sender)) { #if (EQDEBUG>=5) Log(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", sender->GetName(), attacker->GetName(), mob->GetName(), attacker->GetName(), DistanceSquared(mob->GetPosition(), sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); #endif mob->AddToHateList(attacker, 25, 0, false); sender->AddAssistCap(); } } } } } }