bool Map::LineIntersectsNode( NodeRef node_r, VERTEX p1, VERTEX p2, VERTEX *result, FACE **on) const { _ZP(Map_LineIntersectsNode); if( node_r == NODE_NONE || node_r >= m_Nodes) { return(true); //can see through empty nodes, just allow LOS on error... } const PNODE _node = &mNodes[node_r]; if(!(_node->flags & nodeFinal)) { return(true); //not a final node... not sure best action } unsigned long i; PFACE cur; const uint32 *cfl = mFaceLists + _node->faces.offset; for(i = 0; i < _node->faces.count; i++) { if(*cfl > m_Faces) continue; //watch for invalid lists, they seem to happen cur = &mFinalFaces[ *cfl ]; if(LineIntersectsFace(cur,p1, p2, result)) { if(on != NULL) *on = cur; return(true); } cfl++; } //printf("Checked %ld faces and found none in the way.\n", i); return(false); }
tHateEntry *HateList::Find(Mob *ent) { _ZP(HateList_Find); LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { if(iterator.GetData()->ent == ent) return iterator.GetData(); iterator.Advance(); } return NULL; }
Mob* HateList::GetDamageTop(Mob* hater) { _ZP(HateList_GetDamageTop); Mob* current = NULL; Group* grp = NULL; Raid* r = NULL; uint32 dmg_amt = 0; LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { grp = NULL; r = NULL; if(iterator.GetData()->ent && iterator.GetData()->ent->IsClient()){ r = entity_list.GetRaidByClient(iterator.GetData()->ent->CastToClient()); } grp = entity_list.GetGroupByMob(iterator.GetData()->ent); if(iterator.GetData()->ent && r){ if(r->GetTotalRaidDamage(hater) >= dmg_amt) { current = iterator.GetData()->ent; dmg_amt = r->GetTotalRaidDamage(hater); } } else if (iterator.GetData()->ent != NULL && grp != NULL) { if (grp->GetTotalGroupDamage(hater) >= dmg_amt) { current = iterator.GetData()->ent; dmg_amt = grp->GetTotalGroupDamage(hater); } } else if (iterator.GetData()->ent != NULL && (uint32)iterator.GetData()->damage >= dmg_amt) { current = iterator.GetData()->ent; dmg_amt = iterator.GetData()->damage; } iterator.Advance(); } return current; }
Mob *HateList::GetMostHate(){ _ZP(HateList_GetMostHate); Mob* top = NULL; int32 hate = -1; LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { tHateEntry *cur = iterator.GetData(); if(cur->ent != NULL && (cur->hate > hate)) { top = cur->ent; hate = cur->hate; } iterator.Advance(); } return top; }
void HateList::DoFactionHits(int32 nfl_id) { _ZP(HateList_DoFactionHits); if (nfl_id <= 0) return; LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { Client *p; if (iterator.GetData()->ent && iterator.GetData()->ent->IsClient()) p = iterator.GetData()->ent->CastToClient(); else p = NULL; if (p) p->SetFactionLevel(p->CharacterID(), nfl_id, p->GetBaseClass(), p->GetBaseRace(), p->GetDeity()); iterator.Advance(); } }
Mob* HateList::GetClosest(Mob *hater) { _ZP(HateList_GetClosest); Mob* close = NULL; float closedist = 99999.9f; float thisdist; LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { thisdist = iterator.GetData()->ent->DistNoRootNoZ(*hater); if(iterator.GetData()->ent != NULL && thisdist <= closedist) { closedist = thisdist; close = iterator.GetData()->ent; } iterator.Advance(); } if (close == 0 && hater->IsNPC()) close = hater->CastToNPC()->GetHateTop(); return close; }
bool Spawn2::Process() { _ZP(Spawn2_Process); IsDespawned = false; if(!Enabled()) return true; //grab our spawn group SpawnGroup* sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_); if(NPCPointerValid() && (sg->despawn == 0 || condition_id != 0)) return true; if (timer.Check()) { timer.Disable(); _log(SPAWNS__MAIN, "Spawn2 %d: Timer has triggered", spawn2_id); //first check our spawn condition, if this isnt active //then we reset the timer and try again next time. if(condition_id != SC_AlwaysEnabled && !zone->spawn_conditions.Check(condition_id, condition_min_value)) { _log(SPAWNS__CONDITIONS, "Spawn2 %d: spawning prevented by spawn condition %d", spawn2_id, condition_id); Reset(); return(true); } if (sg == NULL) { database.LoadSpawnGroupsByID(spawngroup_id_,&zone->spawn_group_list); sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_); } if (sg == NULL) { _log(SPAWNS__MAIN, "Spawn2 %d: Unable to locate spawn group %d. Disabling.", spawn2_id, spawngroup_id_); return false; } //have the spawn group pick an NPC for us uint32 npcid = sg->GetNPCType(); if (npcid == 0) { _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d did not yeild an NPC! not spawning.", spawn2_id, spawngroup_id_); Reset(); //try again later (why?) return(true); } //try to find our NPC type. const NPCType* tmp = database.GetNPCType(npcid); if (tmp == NULL) { _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", spawn2_id, spawngroup_id_, npcid); Reset(); //try again later return(true); } if(tmp->unique_spawn_by_name) { if(!entity_list.LimitCheckName(tmp->name)) { _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is unique and one already exists.", spawn2_id, spawngroup_id_, npcid); timer.Start(5000); //try again in five seconds. return(true); } } if(tmp->spawn_limit > 0) { if(!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) { _log(SPAWNS__MAIN, "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is over its spawn limit (%d)", spawn2_id, spawngroup_id_, npcid, tmp->spawn_limit); timer.Start(5000); //try again in five seconds. return(true); } } if(sg->despawn != 0 && condition_id == 0) zone->Despawn(spawn2_id); if(IsDespawned) return true; if(spawn2_id) database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0); currentnpcid = npcid; NPC* npc = new NPC(tmp, this, x, y, z, heading, FlyMode3); //DCBOOKMARK npc->mod_prespawn(this); npcthis = npc; npc->AddLootTable(); npc->SetSp2(spawngroup_id_); npc->SaveGuardPointAnim(anim); npc->SetAppearance((EmuAppearance)anim); entity_list.AddNPC(npc); //this limit add must be done after the AddNPC since we need the entity ID. entity_list.LimitAddNPC(npc); if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay) npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay); if(zone->InstantGrids()) { _log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z); LoadGrid(); } else { _log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f). Grid loading delayed.", spawn2_id, spawngroup_id_, tmp->name, npcid, x, y, z); } } return true; }
//looking for any mob with hate > -1 bool HateList::IsEmpty() { _ZP(HateList_IsEmpty); return(list.Count() == 0); }
Mob *HateList::GetTop(Mob *center) { _ZP(HateList_GetTop); Mob* top = NULL; int32 hate = -1; if (RuleB(Aggro,SmartAggroList)){ Mob* topClientInRange = NULL; int32 hateClientInRange = -1; int skipped_count = 0; LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); while(iterator.MoreElements()) { tHateEntry *cur = iterator.GetData(); int16 aggroMod = 0; if(!cur){ iterator.Advance(); continue; } if(!cur->ent){ iterator.Advance(); continue; } if(center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { if(!zone->watermap->InLiquid(cur->ent->GetX(), cur->ent->GetY(), cur->ent->GetZ())) { skipped_count++; iterator.Advance(); continue; } } if(cur->ent->DivineAura() || cur->ent->IsMezzed() || cur->ent->IsFeared()){ if(hate == -1) { top = cur->ent; hate = 0; } iterator.Advance(); continue; } int32 currentHate = cur->hate; if(cur->ent->IsClient()){ if(cur->ent->CastToClient()->IsSitting()){ aggroMod += RuleI(Aggro, SittingAggroMod); } if(center){ if(center->GetTarget() == cur->ent) aggroMod += RuleI(Aggro, CurrentTargetAggroMod); if(RuleI(Aggro, MeleeRangeAggroMod) != 0) { if(center->CombatRange(cur->ent)){ aggroMod += RuleI(Aggro, MeleeRangeAggroMod); if(currentHate > hateClientInRange || cur->bFrenzy){ hateClientInRange = currentHate; topClientInRange = cur->ent; } } } } } else{ if(center){ if(center->GetTarget() == cur->ent) aggroMod += RuleI(Aggro, CurrentTargetAggroMod); if(RuleI(Aggro, MeleeRangeAggroMod) != 0) { if(center->CombatRange(cur->ent)){ aggroMod += RuleI(Aggro, MeleeRangeAggroMod); } } } } if(cur->ent->GetMaxHP() != 0 && ((cur->ent->GetHP()*100/cur->ent->GetMaxHP()) < 20)){ aggroMod += RuleI(Aggro, CriticallyWoundedAggroMod); } if(aggroMod){ currentHate += (currentHate * aggroMod / 100); } if(currentHate > hate || cur->bFrenzy){ hate = currentHate; top = cur->ent; } iterator.Advance(); } if(topClientInRange != NULL && top != NULL) { bool isTopClientType = top->IsClient(); #ifdef BOTS if(!isTopClientType) { if(top->IsBot()) { isTopClientType = true; topClientInRange = top; } } #endif //BOTS if(!isTopClientType) return topClientInRange; return top; } else { if(top == NULL && skipped_count > 0) { return center->GetTarget(); } return top; } } else{ LinkedListIterator<tHateEntry*> iterator(list); iterator.Reset(); int skipped_count = 0; while(iterator.MoreElements()) { tHateEntry *cur = iterator.GetData(); if(center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { if(!zone->watermap->InLiquid(cur->ent->GetX(), cur->ent->GetY(), cur->ent->GetZ())) { skipped_count++; iterator.Advance(); continue; } } if(cur->ent != NULL && ((cur->hate > hate) || cur->bFrenzy )) { top = cur->ent; hate = cur->hate; } iterator.Advance(); } if(top == NULL && skipped_count > 0) { return center->GetTarget(); } return top; } }
// Queries the loottable: adds item & coin to the npc void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) { _ZP(Database_AddLootTableToNPC); //if (loottable_id == 178190) //DebugBreak(); const LootTable_Struct* lts = 0; *copper = 0; *silver = 0; *gold = 0; *plat = 0; lts = database.GetLootTable(loottable_id); if (!lts) return; // do coin if (lts->mincash > lts->maxcash) { std::cerr << "Error in loottable #" << loottable_id << ": mincash > maxcash" << std::endl; } else if (lts->maxcash != 0) { uint32 cash = 0; if (lts->mincash == lts->maxcash) cash = lts->mincash; else cash = MakeRandomInt(lts->mincash, lts->maxcash); if (cash != 0) { if (lts->avgcoin != 0) { //this is some crazy ass stuff... and makes very little sense... dont use it, k? uint32 mincoin = (uint32) (lts->avgcoin * 0.75 + 1); uint32 maxcoin = (uint32) (lts->avgcoin * 1.25 + 1); *copper = MakeRandomInt(mincoin, maxcoin); *silver = MakeRandomInt(mincoin, maxcoin); *gold = MakeRandomInt(mincoin, maxcoin); if(*copper > cash) { *copper = cash; } cash -= *copper; if(*silver>(cash/10)) { *silver = (cash/10); } cash -= *silver*10; if(*gold > (cash/100)) { *gold = (cash/100); } cash -= *gold*100; } if (cash < 0) { cash = 0; } *plat = cash / 1000; cash -= *plat * 1000; uint32 gold2 = cash / 100; cash -= gold2 * 100; uint32 silver2 = cash / 10; cash -= silver2 * 10; *gold += gold2; *silver += silver2; *copper += cash; } } // Do items for (uint32 i=0; i<lts->NumEntries; i++) { for (uint32 k = 1; k <= lts->Entries[i].multiplier; k++) { uint8 droplimit = lts->Entries[i].droplimit; uint8 mindrop = lts->Entries[i].mindrop; //LootTable Entry probability float ltchance = 0.0f; ltchance = lts->Entries[i].probability; float drop_chance = 0.0f; if(ltchance > 0.0 && ltchance < 100.0) { drop_chance = MakeRandomFloat(0.0, 100.0); } if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance < ltchance)) { AddLootDropToNPC(npc,lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop); } } } }
float Map::FindBestZ( NodeRef node_r, VERTEX p1, VERTEX *result, FACE **on) const { _ZP(Map_FindBestZ); p1.z += RuleI(Map, FindBestZHeightAdjust); if(RuleB(Map, UseClosestZ)) return FindClosestZ(p1); if(node_r == GetRoot()) { node_r = SeekNode(node_r, p1.x, p1.y); } if( node_r == NODE_NONE || node_r >= m_Nodes) { return(BEST_Z_INVALID); } const PNODE _node = &mNodes[node_r]; if(!(_node->flags & nodeFinal)) { return(BEST_Z_INVALID); //not a final node... could find the proper node... } VERTEX tmp_result; //dummy placeholder if they do not ask for a result. if(result == NULL) result = &tmp_result; VERTEX p2(p1); p2.z = BEST_Z_INVALID; float best_z = BEST_Z_INVALID; int zAttempt; unsigned long i; PFACE cur; // If we don't find a bestZ on the first attempt, we try again from a position CurrentZ + 10 higher // This is in case the pathing between waypoints temporarily sends the NPC below ground level. // for(zAttempt=1; zAttempt<=2; zAttempt++) { const uint32 *cfl = mFaceLists + _node->faces.offset; #ifdef DEBUG_BEST_Z printf("Start finding best Z...\n"); #endif for(i = 0; i < _node->faces.count; i++) { if(*cfl > m_Faces) continue; //watch for invalid lists, they seem to happen, e.g. in eastwastes.map cur = &mFinalFaces[ *cfl ]; //printf("Intersecting with face %lu\n", *cfl); if(LineIntersectsFace(cur, p1, p2, result)) { #ifdef DEBUG_BEST_Z printf(" %lu (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n", *cfl, cur->a.x, cur->a.y, cur->a.z, cur->b.x, cur->b.y, cur->b.z, cur->c.x, cur->c.y, cur->c.z); printf("Found a z: %.2f\n", result->z); #endif if (result->z > best_z) { if(on != NULL) *on = cur; best_z = result->z; } } cfl++; } if(best_z != BEST_Z_INVALID) return best_z; p1.z = p1.z + 10 ; // If we can't find a best Z, the NPC is probably just under the world. Try again from 10 units higher up. } #ifdef DEBUG_BEST_Z fflush(stdout); printf("Best Z found: %.2f\n", best_z); #endif return best_z; }
bool Map::LineIntersectsZone(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on) const { _ZP(Map_LineIntersectsZone); // Cast a ray from start to end, checking for collisions in each node between the two points. // float stepx, stepy, stepz, curx, cury, curz; curx = start.x; cury = start.y; curz = start.z; VERTEX cur = start; stepx = end.x - start.x; stepy = end.y - start.y; stepz = end.z - start.z; if((stepx == 0) && (stepy == 0) && (stepz == 0)) return false; float factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); stepx = (stepx/factor)*step_mag; stepy = (stepy/factor)*step_mag; stepz = (stepz/factor)*step_mag; NodeRef cnode, lnode, finalnode; lnode = NODE_NONE; //last node visited cnode = SeekNode(GetRoot(), start.x, start.y); finalnode = SeekNode(GetRoot(), end.x, end.y); if(cnode == finalnode) return LineIntersectsNode(cnode, start, end, result, on); do { stepx = (float)end.x - curx; stepy = (float)end.y - cury; stepz = (float)end.z - curz; factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz); stepx = (stepx/factor)*step_mag; stepy = (stepy/factor)*step_mag; stepz = (stepz/factor)*step_mag; cnode = SeekNode(GetRoot(), curx, cury); if(cnode != lnode) { lnode = cnode; if(cnode == NODE_NONE) return false; if(LineIntersectsNode(cnode, start, end, result, on)) return(true); if(cnode == finalnode) return false; } curx += stepx; cury += stepy; curz += stepz; cur.x = curx; cur.y = cury; cur.z = curz; if(ABS(curx - end.x) < step_mag) cur.x = end.x; if(ABS(cury - end.y) < step_mag) cur.y = end.y; if(ABS(curz - end.z) < step_mag) cur.z = end.z; } while(cur.x != end.x || cur.y != end.y || cur.z != end.z); return false; }