//we need this function to immediately determine, after we receive OP_Fishing, if we can even try to fish, otherwise we have to wait a while to get the failure bool Client::CanFish() { //make sure we still have a fishing pole on: const ItemInst* Pole = m_inv[MainPrimary]; int32 bslot = m_inv.HasItemByUse(ItemTypeFishingBait, 1, invWhereWorn|invWherePersonal); const ItemInst* Bait = nullptr; if (bslot != INVALID_INDEX) Bait = m_inv.GetItem(bslot); if(!Pole || !Pole->IsType(ItemClassCommon) || Pole->GetItem()->ItemType != ItemTypeFishingPole) { if (m_inv.HasItemByUse(ItemTypeFishingPole, 1, invWhereWorn|invWherePersonal|invWhereBank|invWhereSharedBank|invWhereTrading|invWhereCursor)) //We have a fishing pole somewhere, just not equipped Message_StringID(MT_Skills, FISHING_EQUIP_POLE); //You need to put your fishing pole in your primary hand. else //We don't have a fishing pole anywhere Message_StringID(MT_Skills, FISHING_NO_POLE); //You can't fish without a fishing pole, go buy one. return false; } if (!Bait || !Bait->IsType(ItemClassCommon) || Bait->GetItem()->ItemType != ItemTypeFishingBait) { Message_StringID(MT_Skills, FISHING_NO_BAIT); //You can't fish without fishing bait, go buy some. return false; } if(zone->zonemap != nullptr && zone->watermap != nullptr && RuleB(Watermap, CheckForWaterWhenFishing)) { glm::vec3 rodPosition; // Tweak Rod and LineLength if required const float RodLength = RuleR(Watermap, FishingRodLength); const float LineLength = RuleR(Watermap, FishingLineLength); int HeadingDegrees; HeadingDegrees = (int) ((GetHeading()*360)/256); HeadingDegrees = HeadingDegrees % 360; rodPosition.x = m_Position.x + RodLength * sin(HeadingDegrees * M_PI/180.0f); rodPosition.y = m_Position.y + RodLength * cos(HeadingDegrees * M_PI/180.0f); // Do BestZ to find where the line hanging from the rod intersects the water (if it is water). // and go 1 unit into the water. glm::vec3 dest; dest.x = rodPosition.x; dest.y = rodPosition.y; dest.z = m_Position.z+10; rodPosition.z = zone->zonemap->FindBestZ(dest, nullptr) + 4; bool in_lava = zone->watermap->InLava(rodPosition); bool in_water = zone->watermap->InWater(rodPosition) || zone->watermap->InVWater(rodPosition); //Message(0, "Rod is at %4.3f, %4.3f, %4.3f, InWater says %d, InLava says %d", RodX, RodY, RodZ, in_water, in_lava); if (in_lava) { Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something? return false; } if((!in_water) || (m_Position.z-rodPosition.z)>LineLength) { //Didn't hit the water OR the water is too far below us Message_StringID(MT_Skills, FISHING_LAND); //Trying to catch land sharks perhaps? return false; } } return true; }
void Mob::SendToFixZ(float new_x, float new_y, float new_z) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z + 0.1); } x_pos = new_x; y_pos = new_y; z_pos = new_z + 0.1; //fix up pathing Z, this shouldent be needed IF our waypoints //are corrected instead if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo)) { if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr); mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. z_pos = newz + 1; } } }
void NPC::UpdateWaypoint(int wp_index) { if(wp_index >= static_cast<int>(Waypoints.size())) { Log.Out(Logs::Detail, Logs::AI, "Update to waypoint %d failed. Not found.", wp_index); return; } std::vector<wplist>::iterator cur; cur = Waypoints.begin(); cur += wp_index; m_CurrentWayPoint = glm::vec4(cur->x, cur->y, cur->z, cur->heading); cur_wp_pause = cur->pause; Log.Out(Logs::Detail, Logs::AI, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, m_CurrentWayPoint.w); //fix up pathing Z if(zone->HasMap() && RuleB(Map, FixPathingZAtWaypoints) && !IsBoat()) { if(!RuleB(Watermap, CheckForWaterAtWaypoints) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_CurrentWayPoint)))) { glm::vec3 dest(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z); float newz = zone->zonemap->FindBestZ(dest, nullptr); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaWaypoint)) m_CurrentWayPoint.z = newz + 1; } } }
void NPC::UpdateWaypoint(int wp_index) { if(wp_index >= static_cast<int>(Waypoints.size())) { mlog(AI__WAYPOINTS, "Update to waypoint %d failed. Not found.", wp_index); return; } std::vector<wplist>::iterator cur; cur = Waypoints.begin(); cur += wp_index; cur_wp_x = cur->x; cur_wp_y = cur->y; cur_wp_z = cur->z; cur_wp_pause = cur->pause; cur_wp_heading = cur->heading; mlog(AI__WAYPOINTS, "Next waypoint %d: (%.3f, %.3f, %.3f, %.3f)", wp_index, cur_wp_x, cur_wp_y, cur_wp_z, cur_wp_heading); //fix up pathing Z if(zone->HasMap() && RuleB(Map, FixPathingZAtWaypoints)) { if(!RuleB(Watermap, CheckForWaterAtWaypoints) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(cur_wp_x, cur_wp_y, cur_wp_z))) { Map::Vertex dest(cur_wp_x, cur_wp_y, cur_wp_z); float newz = zone->zonemap->FindBestZ(dest, nullptr); if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaWaypoint)) cur_wp_z = newz + 1; } } }
void Mob::SendToFixZ(float new_x, float new_y, float new_z) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z + 0.1); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z + 0.1; //fix up pathing Z, this shouldent be needed IF our waypoints //are corrected instead if(zone->HasMap() && RuleB(Map, FixPathingZOnSendTo)) { if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr); Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x,m_Position.y,m_Position.z); if( (newz > -2000) && std::abs(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check. m_Position.z = newz + 1; } } }
void EQStream::init() { active_users = 0; Session=0; Key=0; MaxLen=0; NextInSeq=0; NextOutSeq=0; NextAckToSend=-1; LastAckSent=-1; MaxSends=5; LastPacket=0; oversize_buffer=NULL; oversize_length=0; oversize_offset=0; RateThreshold=RATEBASE/250; DecayRate=DECAYBASE/250; BytesWritten=0; SequencedBase = 0; NextSequencedSend = 0; #ifdef RETRANSMITS retransmittimer = Timer::GetCurrentTime(); retransmittimeout = 500 * RuleR(EQStream, RetransmitTimeoutMult); //use 500ms as base before we have connection stats #endif OpMgr = NULL; if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { _log(NET__ERROR, _L "init Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { _log(NET__ERROR, _L "init Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } }
void Group::SplitExp(uint32 exp, Mob* other) { if( other->CastToNPC()->MerchantType != 0 ) // Ensure NPC isn't a merchant return; if(other->GetOwner() && other->GetOwner()->IsClient()) // Ensure owner isn't pc return; unsigned int i; uint32 groupexp = exp; uint8 membercount = 0; uint8 maxlevel = 1; for (i = 0; i < MAX_GROUP_MEMBERS; i++) { if (members[i] != nullptr) { if(members[i]->GetLevel() > maxlevel) maxlevel = members[i]->GetLevel(); membercount++; } } float groupmod; if (membercount > 1 && membercount < 6) groupmod = 1 + .2(membercount - 1); //2members=1.2exp, 3=1.4, 4=1.6, 5=1.8 else if (membercount == 6) groupmod = 2.16; else groupmod = 1.0; groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); if(conlevel == CON_GREEN) return; //no exp for greenies... if (membercount == 0) return; for (i = 0; i < MAX_GROUP_MEMBERS; i++) { if (members[i] != nullptr && members[i]->IsClient()) // If Group Member is Client { Client *cmember = members[i]->CastToClient(); // add exp + exp cap int16 diff = cmember->GetLevel() - maxlevel; int16 maxdiff = -(cmember->GetLevel()*15/10 - cmember->GetLevel()); if(maxdiff > -5) maxdiff = -5; if (diff >= (maxdiff)) { /*Instead of person who killed the mob, the person who has the highest level in the group*/ uint32 tmp = (cmember->GetLevel()+3) * (cmember->GetLevel()+3) * 75 * 35 / 10; uint32 tmp2 = groupexp / membercount; cmember->AddEXP( tmp < tmp2 ? tmp : tmp2, conlevel ); } } } }
void Raid::SplitExp(uint32 exp, Mob* other) { if( other->CastToNPC()->MerchantType != 0 ) // Ensure NPC isn't a merchant return; if(other->GetOwner() && other->GetOwner()->IsClient()) // Ensure owner isn't pc return; uint32 groupexp = exp; uint8 membercount = 0; uint8 maxlevel = 1; for (int i = 0; i < MAX_RAID_MEMBERS; i++) { if (members[i].member != NULL) { if(members[i].member->GetLevel() > maxlevel) maxlevel = members[i].member->GetLevel(); membercount++; } } groupexp = (uint32)((float)groupexp * (1.0f-(RuleR(Character, RaidExpMultiplier)))); int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); if(conlevel == CON_GREEN) return; //no exp for greenies... if (membercount == 0) return; for (unsigned int x = 0; x < MAX_RAID_MEMBERS; x++) { if (members[x].member != NULL) // If Group Member is Client { Client *cmember = members[x].member; // add exp + exp cap int16 diff = cmember->GetLevel() - maxlevel; int16 maxdiff = -(cmember->GetLevel()*15/10 - cmember->GetLevel()); if(maxdiff > -5) maxdiff = -5; if (diff >= (maxdiff)) { /*Instead of person who killed the mob, the person who has the highest level in the group*/ uint32 tmp = (cmember->GetLevel()+3) * (cmember->GetLevel()+3) * 75 * 35 / 10; uint32 tmp2 = (groupexp / membercount) + 1; cmember->AddEXP( tmp < tmp2 ? tmp : tmp2, conlevel ); } } } }
float Mob::GetFearSpeed() { if(flee_mode) { //we know ratio < FLEE_HP_RATIO float speed = GetBaseRunspeed(); float ratio = GetHPRatio(); float multiplier = RuleR(Combat, FleeMultiplier); if(GetSnaredAmount() > 40) multiplier = multiplier / 6.0f; speed = speed * ratio * multiplier / 100; //NPC will eventually stop. Snares speeds this up. if(speed < 0.09) speed = 0.0001f; return(speed); } return(GetRunspeed()); }
bool Client::IsInRange(Mob* defender) { float exprange = RuleR(Zone, GroupEXPRange); float t1, t2, t3; t1 = defender->GetX() - GetX(); t2 = defender->GetY() - GetY(); //t3 = defender->GetZ() - GetZ(); if(t1 < 0) abs(t1); if(t2 < 0) abs(t2); //if(t3 < 0) // abs(t3); if(( t1 > exprange) || ( t2 > exprange)) { // || ( t3 > 40) ) { //_log(CLIENT__EXP, "%s is out of range. distances (%.3f,%.3f,%.3f), range %.3f No XP will be awarded.", defender->GetName(), t1, t2, t3, exprange); return false; } else return true; }
void Client::GetExpLoss(Mob* killerMob, uint16 spell, int &exploss) { float loss; uint8 level = GetLevel(); if(level >= 1 && level <= 29) loss = 0.16f; if(level == 30) loss = 0.08f; if(level >= 31 && level <= 34) loss = 0.15f; if(level == 35) loss = 0.075f; if(level >= 36 && level <= 39) loss = 0.14f; if(level == 40) loss = 0.07f; if(level >= 41 && level <= 44) loss = 0.13f; if(level == 45) loss = 0.065f; if(level >= 46 && level <= 50) loss = 0.12f; if(level >= 51) loss = 0.06f; if(RuleB(Character, SmoothEXPLoss)) { if(loss >= 0.12) loss /= 2; } int requiredxp = GetEXPForLevel(level + 1) - GetEXPForLevel(level); exploss=(int)((float)requiredxp * (loss * RuleR(Character, EXPLossMultiplier))); if( (level < RuleI(Character, DeathExpLossLevel)) || (level > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC() ) { exploss = 0; } else if( killerMob ) { if( killerMob->IsClient() ) { exploss = 0; } else if( killerMob->GetOwner() && killerMob->GetOwner()->IsClient() ) { exploss = 0; } } if(spell != SPELL_UNKNOWN) { uint32 buff_count = GetMaxTotalSlots(); for(uint16 buffIt = 0; buffIt < buff_count; buffIt++) { if(buffs[buffIt].spellid == spell && buffs[buffIt].client) { exploss = 0; // no exp loss for pvp dot break; } } } }
int main(int argc, char** argv) { RegisterExecutablePlatform(ExePlatformZone); LogSys.LoadLogSettingsDefaults(); set_exception_handler(); #ifdef USE_MAP_MMFS if (argc == 3 && strcasecmp(argv[1], "convert_map") == 0) { if (!ZoneConfig::LoadConfig()) return 1; Config = ZoneConfig::get(); std::string mapfile = argv[2]; std::transform(mapfile.begin(), mapfile.end(), mapfile.begin(), ::tolower); std::string filename = Config->MapDir; filename += mapfile; auto m = new Map(); auto success = m->Load(filename, true); delete m; std::cout << mapfile.c_str() << " conversion " << (success ? "succeeded" : "failed") << std::endl; return 0; } #endif /*USE_MAP_MMFS*/ QServ = new QueryServ; Log(Logs::General, Logs::Zone_Server, "Loading server configuration.."); if (!ZoneConfig::LoadConfig()) { Log(Logs::General, Logs::Error, "Loading server configuration failed."); return 1; } Config = ZoneConfig::get(); const char *zone_name; uint32 instance_id = 0; std::string z_name; if (argc == 4) { instance_id = atoi(argv[3]); worldserver.SetLauncherName(argv[2]); auto zone_port = SplitString(argv[1], ':'); if (!zone_port.empty()) { z_name = zone_port[0]; } if (zone_port.size() > 1) { std::string p_name = zone_port[1]; Config->SetZonePort(atoi(p_name.c_str())); } worldserver.SetLaunchedName(z_name.c_str()); if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) { zone_name = "."; } else { zone_name = z_name.c_str(); } } else if (argc == 3) { worldserver.SetLauncherName(argv[2]); auto zone_port = SplitString(argv[1], ':'); if (!zone_port.empty()) { z_name = zone_port[0]; } if (zone_port.size() > 1) { std::string p_name = zone_port[1]; Config->SetZonePort(atoi(p_name.c_str())); } worldserver.SetLaunchedName(z_name.c_str()); if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) { zone_name = "."; } else { zone_name = z_name.c_str(); } } else if (argc == 2) { worldserver.SetLauncherName("NONE"); auto zone_port = SplitString(argv[1], ':'); if (!zone_port.empty()) { z_name = zone_port[0]; } if (zone_port.size() > 1) { std::string p_name = zone_port[1]; Config->SetZonePort(atoi(p_name.c_str())); } worldserver.SetLaunchedName(z_name.c_str()); if (strncmp(z_name.c_str(), "dynamic_", 8) == 0) { zone_name = "."; } else { zone_name = z_name.c_str(); } } else { zone_name = "."; worldserver.SetLaunchedName("."); worldserver.SetLauncherName("NONE"); } Log(Logs::General, Logs::Zone_Server, "Connecting to MySQL..."); if (!database.Connect( Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { Log(Logs::General, Logs::Error, "Cannot continue without a database connection."); return 1; } #ifdef BOTS if (!botdb.Connect( Config->DatabaseHost.c_str(), Config->DatabaseUsername.c_str(), Config->DatabasePassword.c_str(), Config->DatabaseDB.c_str(), Config->DatabasePort)) { Log(Logs::General, Logs::Error, "Cannot continue without a bots database connection."); return 1; } #endif /* Register Log System and Settings */ LogSys.OnLogHookCallBackZone(&Zone::GMSayHookCallBackProcess); database.LoadLogSettings(LogSys.log_settings); LogSys.StartFileLogs(); /* Guilds */ guild_mgr.SetDatabase(&database); GuildBanks = nullptr; /** * NPC Scale Manager */ npc_scale_manager = new NpcScaleManager; npc_scale_manager->LoadScaleData(); #ifdef _EQDEBUG _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); #endif Log(Logs::General, Logs::Zone_Server, "CURRENT_VERSION: %s", CURRENT_VERSION); /* * Setup nice signal handlers */ if (signal(SIGINT, CatchSignal) == SIG_ERR) { Log(Logs::General, Logs::Error, "Could not set signal handler"); return 1; } if (signal(SIGTERM, CatchSignal) == SIG_ERR) { Log(Logs::General, Logs::Error, "Could not set signal handler"); return 1; } #ifndef WIN32 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { Log(Logs::General, Logs::Error, "Could not set signal handler"); return 1; } #endif Log(Logs::General, Logs::Zone_Server, "Mapping Incoming Opcodes"); MapOpcodes(); Log(Logs::General, Logs::Zone_Server, "Loading Variables"); database.LoadVariables(); std::string hotfix_name; if (database.GetVariable("hotfix_name", hotfix_name)) { if (!hotfix_name.empty()) { Log(Logs::General, Logs::Zone_Server, "Current hotfix in use: '%s'", hotfix_name.c_str()); } } Log(Logs::General, Logs::Zone_Server, "Loading zone names"); database.LoadZoneNames(); Log(Logs::General, Logs::Zone_Server, "Loading items"); if (!database.LoadItems(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading items FAILED!"); Log(Logs::General, Logs::Error, "Failed. But ignoring error and going on..."); } Log(Logs::General, Logs::Zone_Server, "Loading npc faction lists"); if (!database.LoadNPCFactionLists(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading npcs faction lists FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading loot tables"); if (!database.LoadLoot(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading loot FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading skill caps"); if (!database.LoadSkillCaps(std::string(hotfix_name))) { Log(Logs::General, Logs::Error, "Loading skill caps FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading spells"); if (!database.LoadSpells(hotfix_name, &SPDAT_RECORDS, &spells)) { Log(Logs::General, Logs::Error, "Loading spells FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading base data"); if (!database.LoadBaseData(hotfix_name)) { Log(Logs::General, Logs::Error, "Loading base data FAILED!"); return 1; } Log(Logs::General, Logs::Zone_Server, "Loading guilds"); guild_mgr.LoadGuilds(); Log(Logs::General, Logs::Zone_Server, "Loading factions"); database.LoadFactionData(); Log(Logs::General, Logs::Zone_Server, "Loading titles"); title_manager.LoadTitles(); Log(Logs::General, Logs::Zone_Server, "Loading tributes"); database.LoadTributes(); Log(Logs::General, Logs::Zone_Server, "Loading corpse timers"); database.GetDecayTimes(npcCorpseDecayTimes); Log(Logs::General, Logs::Zone_Server, "Loading profanity list"); if (!EQEmu::ProfanityManager::LoadProfanityList(&database)) Log(Logs::General, Logs::Error, "Loading profanity list FAILED!"); Log(Logs::General, Logs::Zone_Server, "Loading commands"); int retval = command_init(); if (retval<0) Log(Logs::General, Logs::Error, "Command loading FAILED"); else Log(Logs::General, Logs::Zone_Server, "%d commands loaded", retval); //rules: { std::string tmp; if (database.GetVariable("RuleSet", tmp)) { Log(Logs::General, Logs::Zone_Server, "Loading rule set '%s'", tmp.c_str()); if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) { Log(Logs::General, Logs::Error, "Failed to load ruleset '%s', falling back to defaults.", tmp.c_str()); } } else { if (!RuleManager::Instance()->LoadRules(&database, "default", false)) { Log(Logs::General, Logs::Zone_Server, "No rule set configured, using default rules"); } else { Log(Logs::General, Logs::Zone_Server, "Loaded default rule set 'default'", tmp.c_str()); } } EQEmu::InitializeDynamicLookups(); Log(Logs::General, Logs::Zone_Server, "Initialized dynamic dictionary entries"); } #ifdef BOTS Log(Logs::General, Logs::Zone_Server, "Loading bot commands"); int botretval = bot_command_init(); if (botretval<0) Log(Logs::General, Logs::Error, "Bot command loading FAILED"); else Log(Logs::General, Logs::Zone_Server, "%d bot commands loaded", botretval); Log(Logs::General, Logs::Zone_Server, "Loading bot spell casting chances"); if (!botdb.LoadBotSpellCastingChances()) Log(Logs::General, Logs::Error, "Bot spell casting chances loading FAILED"); #endif if (RuleB(TaskSystem, EnableTaskSystem)) { Log(Logs::General, Logs::Tasks, "[INIT] Loading Tasks"); taskmanager = new TaskManager; taskmanager->LoadTasks(); } parse = new QuestParserCollection(); #ifdef LUA_EQEMU parse->RegisterQuestInterface(LuaParser::Instance(), "lua"); #endif #ifdef EMBPERL auto perl_parser = new PerlembParser(); parse->RegisterQuestInterface(perl_parser, "pl"); /* Load Perl Event Export Settings */ parse->LoadPerlEventExportSettings(parse->perl_event_export_settings); #endif //now we have our parser, load the quests Log(Logs::General, Logs::Zone_Server, "Loading quests"); parse->ReloadQuests(); worldserver.Connect(); Timer InterserverTimer(INTERSERVER_TIMER); // does MySQL pings and auto-reconnect #ifdef EQPROFILE #ifdef PROFILE_DUMP_TIME Timer profile_dump_timer(PROFILE_DUMP_TIME * 1000); profile_dump_timer.Start(); #endif #endif if (!strlen(zone_name) || !strcmp(zone_name, ".")) { Log(Logs::General, Logs::Zone_Server, "Entering sleep mode"); } else if (!Zone::Bootup(database.GetZoneID(zone_name), instance_id, true)) { Log(Logs::General, Logs::Error, "Zone Bootup failed :: Zone::Bootup"); zone = 0; } //register all the patches we have avaliable with the stream identifier. EQStreamIdentifier stream_identifier; RegisterAllPatches(stream_identifier); #ifndef WIN32 Log(Logs::Detail, Logs::None, "Main thread running with thread id %d", pthread_self()); #endif Timer quest_timers(100); UpdateWindowTitle(); bool worldwasconnected = worldserver.Connected(); std::shared_ptr<EQStreamInterface> eqss; EQStreamInterface *eqsi; bool eqsf_open = false; std::unique_ptr<EQ::Net::EQStreamManager> eqsm; std::chrono::time_point<std::chrono::system_clock> frame_prev = std::chrono::system_clock::now(); auto loop_fn = [&](EQ::Timer* t) { //Advance the timer to our current point in time Timer::SetCurrentTime(); //Calculate frame time std::chrono::time_point<std::chrono::system_clock> frame_now = std::chrono::system_clock::now(); frame_time = std::chrono::duration_cast<std::chrono::duration<double>>(frame_now - frame_prev).count(); frame_prev = frame_now; if (!eqsf_open && Config->ZonePort != 0) { Log(Logs::General, Logs::Zone_Server, "Starting EQ Network server on port %d", Config->ZonePort); EQ::Net::EQStreamManagerOptions opts(Config->ZonePort, false, true); opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS); opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor); opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS); opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS); eqsm.reset(new EQ::Net::EQStreamManager(opts)); eqsf_open = true; eqsm->OnNewConnection([&stream_identifier](std::shared_ptr<EQ::Net::EQStream> stream) { stream_identifier.AddStream(stream); LogF(Logs::Detail, Logs::World_Server, "New connection from IP {0}:{1}", stream->RemoteEndpoint(), ntohs(stream->GetRemotePort())); }); } //give the stream identifier a chance to do its work.... stream_identifier.Process(); //check the stream identifier for any now-identified streams while ((eqsi = stream_identifier.PopIdentified())) { //now that we know what patch they are running, start up their client object struct in_addr in; in.s_addr = eqsi->GetRemoteIP(); Log(Logs::Detail, Logs::World_Server, "New client from %s:%d", inet_ntoa(in), ntohs(eqsi->GetRemotePort())); auto client = new Client(eqsi); entity_list.AddClient(client); } if (worldserver.Connected()) { worldwasconnected = true; } else { if (worldwasconnected && is_zone_loaded) { entity_list.ChannelMessageFromWorld(0, 0, 6, 0, 0, "WARNING: World server connection lost"); worldwasconnected = false; } } if (is_zone_loaded) { { if (net.group_timer.Enabled() && net.group_timer.Check()) entity_list.GroupProcess(); if (net.door_timer.Enabled() && net.door_timer.Check()) entity_list.DoorProcess(); if (net.object_timer.Enabled() && net.object_timer.Check()) entity_list.ObjectProcess(); if (net.corpse_timer.Enabled() && net.corpse_timer.Check()) entity_list.CorpseProcess(); if (net.trap_timer.Enabled() && net.trap_timer.Check()) entity_list.TrapProcess(); if (net.raid_timer.Enabled() && net.raid_timer.Check()) entity_list.RaidProcess(); entity_list.Process(); entity_list.MobProcess(); entity_list.BeaconProcess(); entity_list.EncounterProcess(); if (zone) { if (!zone->Process()) { Zone::Shutdown(); } } if (quest_timers.Check()) quest_manager.Process(); } } if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); entity_list.UpdateWho(); } }; EQ::Timer process_timer(loop_fn); bool is_boat_zone = strstr(zone_name, "erudnext") != NULL || strstr(zone_name, "freporte") != NULL || strstr(zone_name, "qeynos") != NULL || strstr(zone_name, "oot") != NULL || strstr(zone_name, "timorous") != NULL || strstr(zone_name, "erudsxing") != NULL || strstr(zone_name, "firiona") != NULL || strstr(zone_name, "butcher") != NULL || strstr(zone_name, "overthere") != NULL || strstr(zone_name, "oasis") != NULL || strstr(zone_name, "nro") != NULL || strstr(zone_name, "iceclad") != NULL; if (!is_boat_zone) process_timer.Start(1000, true); else process_timer.Start(100, true); while (RunLoops) { if (!is_boat_zone) { bool previous_loaded = is_zone_loaded && numclients > 0; EQ::EventLoop::Get().Process(); bool current_loaded = is_zone_loaded && numclients > 0; if (previous_loaded && !current_loaded) { process_timer.Stop(); process_timer.Start(1000, true); } else if (!previous_loaded && current_loaded) { process_timer.Stop(); process_timer.Start(32, true); } if (current_loaded) { Sleep(1); } else { Sleep(10); } } else { bool previous_loaded = is_zone_loaded; EQ::EventLoop::Get().Process(); bool current_loaded = is_zone_loaded; if (previous_loaded && !current_loaded) { process_timer.Stop(); process_timer.Start(100, true); if (zone && zone->GetZoneID() && zone->GetInstanceVersion()) { uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion()); zone->StartShutdownTimer(shutdown_timer); } } else if (!previous_loaded && current_loaded) { process_timer.Stop(); process_timer.Start(10, true); } Sleep(1); } } entity_list.Clear(); entity_list.RemoveAllEncounters(); // gotta do it manually or rewrite lots of shit :P parse->ClearInterfaces(); #ifdef EMBPERL safe_delete(perl_parser); #endif safe_delete(Config); if (zone != 0) Zone::Shutdown(true); //Fix for Linux world server problem. safe_delete(taskmanager); command_deinit(); #ifdef BOTS bot_command_deinit(); #endif safe_delete(parse); Log(Logs::General, Logs::Zone_Server, "Proper zone shutdown complete."); LogSys.CloseFileLogs(); return 0; }
void EQStream::ProcessPacket(EQProtocolPacket *p) { uint32 processed=0,subpacket_length=0; if (p == NULL) return; // Raw Application packet if (p->opcode > 0xff) { p->opcode = htons(p->opcode); //byte order is backwards in the protocol packet EQRawApplicationPacket *ap=MakeApplicationPacket(p); if (ap) InboundQueuePush(ap); return; } if (!Session && p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { _log(NET__DEBUG, _L "Session not initialized, packet ignored" __L); _raw(NET__DEBUG, 0xFFFF, p); return; } switch (p->opcode) { case OP_Combined: { processed=0; while(processed < p->size) { subpacket_length=*(p->pBuffer+processed); EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+processed+1,subpacket_length); _log(NET__NET_CREATE, _L "Extracting combined packet of length %d" __L, subpacket_length); _raw(NET__NET_CREATE_HEX, 0xFFFF, subp); subp->copyInfo(p); ProcessPacket(subp); delete subp; processed+=subpacket_length+1; } } break; case OP_AppCombined: { processed=0; while(processed<p->size) { EQRawApplicationPacket *ap=NULL; if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))!=0xff) { _log(NET__NET_CREATE, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); ap=MakeApplicationPacket(p->pBuffer+processed+1,subpacket_length); processed+=subpacket_length+1; } else { subpacket_length=ntohs(*(uint16 *)(p->pBuffer+processed+1)); _log(NET__NET_CREATE, _L "Extracting combined app packet of length %d, short len" __L, subpacket_length); ap=MakeApplicationPacket(p->pBuffer+processed+3,subpacket_length); processed+=subpacket_length+3; } if (ap) { ap->copyInfo(p); InboundQueuePush(ap); } } } break; case OP_Packet: { if(!p->pBuffer || (p->Size() < 4)) { _log(NET__ERROR, _L "Received OP_Packet that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { _log(NET__DEBUG, _L "Future OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); _raw(NET__DEBUG, seq, p); PacketQueue[seq]=p->Copy(); _log(NET__APP_TRACE, _L "OP_Packet Queue size=%d" __L, PacketQueue.size()); //SendOutOfOrderAck(seq); } else if (check == SeqPast) { _log(NET__DEBUG, _L "Duplicate OP_Packet: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); _raw(NET__DEBUG, seq, p); SendOutOfOrderAck(seq); //we already got this packet but it was out of order } else { // In case we did queue one before as well. EQProtocolPacket *qp=RemoveQueue(seq); if (qp) { _log(NET__NET_TRACE, "OP_Packet: Removing older queued packet with sequence %d", seq); delete qp; } SetNextAckToSend(seq); NextInSeq++; // Check for an embedded OP_AppCombinded (protocol level 0x19) if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { EQProtocolPacket *subp=MakeProtocolPacket(p->pBuffer+2,p->size-2); _log(NET__NET_CREATE, _L "seq %d, Extracting combined packet of length %d" __L, seq, subp->size); _raw(NET__NET_CREATE_HEX, seq, subp); subp->copyInfo(p); ProcessPacket(subp); delete subp; } else { EQRawApplicationPacket *ap=MakeApplicationPacket(p->pBuffer+2,p->size-2); if (ap) { ap->copyInfo(p); InboundQueuePush(ap); } } } } break; case OP_Fragment: { if(!p->pBuffer || (p->Size() < 4)) { _log(NET__ERROR, _L "Received OP_Fragment that was of malformed size" __L); break; } uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); SeqOrder check=CompareSequence(NextInSeq,seq); if (check == SeqFuture) { _log(NET__DEBUG, _L "Future OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); _raw(NET__DEBUG, seq, p); PacketQueue[seq]=p->Copy(); _log(NET__APP_TRACE, _L "OP_Fragment Queue size=%d" __L, PacketQueue.size()); //SendOutOfOrderAck(seq); } else if (check == SeqPast) { _log(NET__DEBUG, _L "Duplicate OP_Fragment: Expecting Seq=%d, but got Seq=%d" __L, NextInSeq, seq); _raw(NET__DEBUG, seq, p); SendOutOfOrderAck(seq); } else { // In case we did queue one before as well. EQProtocolPacket *qp=RemoveQueue(seq); if (qp) { _log(NET__NET_TRACE, "OP_Fragment: Removing older queued packet with sequence %d", seq); delete qp; } SetNextAckToSend(seq); NextInSeq++; if (oversize_buffer) { memcpy(oversize_buffer+oversize_offset,p->pBuffer+2,p->size-2); oversize_offset+=p->size-2; _log(NET__NET_TRACE, _L "Fragment of oversized of length %d, seq %d: now at %d/%d" __L, p->size-2, seq, oversize_offset, oversize_length); if (oversize_offset==oversize_length) { if (*(p->pBuffer+2)==0x00 && *(p->pBuffer+3)==0x19) { EQProtocolPacket *subp=MakeProtocolPacket(oversize_buffer,oversize_offset); _log(NET__NET_CREATE, _L "seq %d, Extracting combined oversize packet of length %d" __L, seq, subp->size); //_raw(NET__NET_CREATE_HEX, subp); subp->copyInfo(p); ProcessPacket(subp); delete subp; } else { EQRawApplicationPacket *ap=MakeApplicationPacket(oversize_buffer,oversize_offset); _log(NET__NET_CREATE, _L "seq %d, completed combined oversize packet of length %d" __L, seq, ap->size); if (ap) { ap->copyInfo(p); InboundQueuePush(ap); } } delete[] oversize_buffer; oversize_buffer=NULL; oversize_offset=0; } } else { oversize_length=ntohl(*(uint32 *)(p->pBuffer+2)); oversize_buffer=new unsigned char[oversize_length]; memcpy(oversize_buffer,p->pBuffer+6,p->size-6); oversize_offset=p->size-6; _log(NET__NET_TRACE, _L "First fragment of oversized of seq %d: now at %d/%d" __L, seq, oversize_offset, oversize_length); } } } break; case OP_KeepAlive: { #ifndef COLLECTOR NonSequencedPush(new EQProtocolPacket(p->opcode,p->pBuffer,p->size)); _log(NET__NET_TRACE, _L "Received and queued reply to keep alive" __L); #endif } break; case OP_Ack: { if(!p->pBuffer || (p->Size() < 4)) { _log(NET__ERROR, _L "Received OP_Ack that was of malformed size" __L); break; } #ifndef COLLECTOR uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); AckPackets(seq); #ifdef RETRANSMITS retransmittimer = Timer::GetCurrentTime(); #endif #endif } break; case OP_SessionRequest: { if(p->Size() < sizeof(SessionRequest)) { _log(NET__ERROR, _L "Received OP_SessionRequest that was of malformed size" __L); break; } #ifndef COLLECTOR if (GetState()==ESTABLISHED) { _log(NET__ERROR, _L "Received OP_SessionRequest in ESTABLISHED state (%d)" __L, GetState()); /*RemoveData(); init(); State=UNESTABLISHED;*/ _SendDisconnect(); SetState(CLOSED); break; } #endif //cout << "Got OP_SessionRequest" << endl; init(); OutboundQueueClear(); SessionRequest *Request=(SessionRequest *)p->pBuffer; Session=ntohl(Request->Session); SetMaxLen(ntohl(Request->MaxLength)); _log(NET__NET_TRACE, _L "Received OP_SessionRequest: session %lu, maxlen %d" __L, (unsigned long)Session, MaxLen); SetState(ESTABLISHED); #ifndef COLLECTOR Key=0x11223344; SendSessionResponse(); #endif } break; case OP_SessionResponse: { if(p->Size() < sizeof(SessionResponse)) { _log(NET__ERROR, _L "Received OP_SessionResponse that was of malformed size" __L); break; } init(); OutboundQueueClear(); SessionResponse *Response=(SessionResponse *)p->pBuffer; SetMaxLen(ntohl(Response->MaxLength)); Key=ntohl(Response->Key); NextInSeq=0; SetState(ESTABLISHED); if (!Session) Session=ntohl(Response->Session); compressed=(Response->Format&FLAG_COMPRESSED); encoded=(Response->Format&FLAG_ENCODED); _log(NET__NET_TRACE, _L "Received OP_SessionResponse: session %lu, maxlen %d, key %lu, compressed? %s, encoded? %s" __L, (unsigned long)Session, MaxLen, (unsigned long)Key, compressed?"yes":"no", encoded?"yes":"no"); // Kinda kludgy, but trie for now if (StreamType==UnknownStream) { if (compressed) { if (remote_port==9000 || (remote_port==0 && p->src_port==9000)) { SetStreamType(WorldStream); } else { SetStreamType(ZoneStream); } } else if (encoded) { SetStreamType(ChatOrMailStream); } else { SetStreamType(LoginStream); } } } break; case OP_SessionDisconnect: { //NextInSeq=0; EQStreamState state = GetState(); if(state == ESTABLISHED) { //client initiated disconnect? _log(NET__NET_TRACE, _L "Received unsolicited OP_SessionDisconnect. Treating like a client-initiated disconnect." __L); _SendDisconnect(); SetState(CLOSED); } else if(state == CLOSING) { //we were waiting for this anyways, ignore pending messages, send the reply and be closed. _log(NET__NET_TRACE, _L "Received OP_SessionDisconnect when we have a pending close, they beat us to it. Were happy though." __L); _SendDisconnect(); SetState(CLOSED); } else { //we are expecting this (or have already gotten it, but dont care either way) _log(NET__NET_TRACE, _L "Received expected OP_SessionDisconnect. Moving to closed state." __L); SetState(CLOSED); } } break; case OP_OutOfOrderAck: { if(!p->pBuffer || (p->Size() < 4)) { _log(NET__ERROR, _L "Received OP_OutOfOrderAck that was of malformed size" __L); break; } #ifndef COLLECTOR uint16 seq=ntohs(*(uint16 *)(p->pBuffer)); MOutboundQueue.lock(); if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { _log(NET__ERROR, _L "Pre-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { _log(NET__ERROR, _L "Pre-OOA Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } //if the packet they got out of order is between our last acked packet and the last sent packet, then its valid. if (CompareSequence(SequencedBase,seq) != SeqPast && CompareSequence(NextOutSeq,seq) == SeqPast) { _log(NET__NET_TRACE, _L "Received OP_OutOfOrderAck for sequence %d, starting retransmit at the start of our unacked buffer (seq %d, was %d)." __L, seq, SequencedBase, SequencedBase+NextSequencedSend); #ifdef RETRANSMITS if (!RuleB(EQStream, RetransmitAckedPackets)) { #endif uint16 sqsize = SequencedQueue.size(); uint16 index = seq - SequencedBase; _log(NET__NET_TRACE, _L " OP_OutOfOrderAck marking packet acked in queue (queue index = %d, queue size = %d)." __L, index, sqsize); if (index < sqsize) { deque<EQProtocolPacket *>::iterator sitr; sitr = SequencedQueue.begin(); sitr += index; (*sitr)->acked = true; } #ifdef RETRANSMITS } if (RuleR(EQStream, RetransmitTimeoutMult)) { // only choose new behavior if multiplier is set retransmittimer = Timer::GetCurrentTime(); } #endif NextSequencedSend = 0; } else { _log(NET__NET_TRACE, _L "Received OP_OutOfOrderAck for out-of-window %d. Window (%d->%d)." __L, seq, SequencedBase, NextOutSeq); } if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { _log(NET__ERROR, _L "Post-OOA Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { _log(NET__ERROR, _L "Post-OOA Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } MOutboundQueue.unlock(); #endif } break; case OP_SessionStatRequest: { if(p->Size() < sizeof(SessionStats)) { _log(NET__ERROR, _L "Received OP_SessionStatRequest that was of malformed size" __L); break; } #ifndef COLLECTOR SessionStats *Stats=(SessionStats *)p->pBuffer; _log(NET__NET_TRACE, _L "Received Stats: %lu packets received, %lu packets sent, Deltas: local %lu, (%lu <- %lu -> %lu) remote %lu" __L, (unsigned long)ntohl(Stats->packets_received), (unsigned long)ntohl(Stats->packets_sent), (unsigned long)ntohl(Stats->last_local_delta), (unsigned long)ntohl(Stats->low_delta), (unsigned long)ntohl(Stats->average_delta), (unsigned long)ntohl(Stats->high_delta), (unsigned long)ntohl(Stats->last_remote_delta)); uint64 x=Stats->packets_received; Stats->packets_received=Stats->packets_sent; Stats->packets_sent=x; NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse,p->pBuffer,p->size)); AdjustRates(ntohl(Stats->average_delta)); #ifdef RETRANSMITS if (RuleR(EQStream, RetransmitTimeoutMult) && ntohl(Stats->average_delta)) { //recalculate retransmittimeout using the larger of the last rtt or average rtt, which is multiplied by the rule value if((ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) > (ntohl(Stats->average_delta) * 2)) { retransmittimeout = (ntohl(Stats->last_local_delta) + ntohl(Stats->last_remote_delta)) * RuleR(EQStream, RetransmitTimeoutMult); } else { retransmittimeout = ntohl(Stats->average_delta) * 2 * RuleR(EQStream, RetransmitTimeoutMult); } if(retransmittimeout > RuleI(EQStream, RetransmitTimeoutMax)) retransmittimeout = RuleI(EQStream, RetransmitTimeoutMax); _log(NET__NET_TRACE, _L "Retransmit timeout recalculated to %dms" __L, retransmittimeout); } #endif #endif } break; case OP_SessionStatResponse: { _log(NET__NET_TRACE, _L "Received OP_SessionStatResponse. Ignoring." __L); } break; case OP_OutOfSession: { _log(NET__NET_TRACE, _L "Received OP_OutOfSession. Ignoring." __L); } break; default: EQRawApplicationPacket *ap = MakeApplicationPacket(p); if (ap) InboundQueuePush(ap); break; } }
bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ, bool calcHeading) { if(GetID()==0) return true; float nx = m_Position.x; float ny = m_Position.y; float nz = m_Position.z; // if NPC is rooted if (speed == 0) { SetHeading(CalculateHeadingToTarget(x, y)); if(moved){ SetCurrentSpeed(0); moved=false; } Log.Out(Logs::Detail, Logs::AI, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z); return true; } float old_test_vector=test_vector; m_TargetV.x = x - nx; m_TargetV.y = y - ny; m_TargetV.z = z - nz; if (m_TargetV.x == 0 && m_TargetV.y == 0) return false; SetCurrentSpeed((int8)(speed)); //*NPC_RUNANIM_RATIO); //speed *= NPC_SPEED_MULTIPLIER; Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- test_vector=sqrtf (x*x + y*y + z*z); tar_vector = speed / sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z); m_Position.w = CalculateHeadingToTarget(x, y); if (tar_vector >= 1.0) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } m_Position.x = x; m_Position.y = y; m_Position.z = z; Log.Out(Logs::Detail, Logs::AI, "Close enough, jumping to waypoint"); } else { float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; float new_z = m_Position.z + m_TargetV.z*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; Log.Out(Logs::Detail, Logs::AI, "Next position (%.3f, %.3f, %.3f)", m_Position.x, m_Position.y, m_Position.z); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x,m_Position.y,m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) { if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) m_Position.z = z; else m_Position.z = newz + 1; } else m_Position.z = newz+1; } } } //OP_MobUpdate if((old_test_vector!=test_vector) || tar_ndx>20){ //send update tar_ndx=0; this->SetMoving(true); moved=true; m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); SendPosUpdate(); } tar_ndx++; // now get new heading SetAppearance(eaStanding, false); // make sure they're standing pLastChange = Timer::GetCurrentTime(); return true; }
void NPC::AssignWaypoints(int32 grid) { if (grid == 0) return; // grid ID 0 not supported if (grid < 0) { // Allow setting negative grid values for pausing pathing this->CastToNPC()->SetGrid(grid); return; } Waypoints.clear(); roamer = false; // Retrieve the wander and pause types for this grid std::string query = StringFormat("SELECT `type`, `type2` FROM `grid` WHERE `id` = %i AND `zoneid` = %i", grid, zone->GetZoneID()); auto results = database.QueryDatabase(query); if (!results.Success()) { return; } if (results.RowCount() == 0) return; auto row = results.begin(); wandertype = atoi(row[0]); pausetype = atoi(row[1]); SetGrid(grid); // Assign grid number // Retrieve all waypoints for this grid query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading` " "FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %i " "ORDER BY `number`", grid, zone->GetZoneID()); results = database.QueryDatabase(query); if (!results.Success()) { return; } roamer = true; max_wp = 0; // Initialize it; will increment it for each waypoint successfully added to the list for (auto row = results.begin(); row != results.end(); ++row, ++max_wp) { wplist newwp; newwp.index = max_wp; newwp.x = atof(row[0]); newwp.y = atof(row[1]); newwp.z = atof(row[2]); if(zone->HasMap() && RuleB(Map, FixPathingZWhenLoading) ) { auto positon = glm::vec3(newwp.x,newwp.y,newwp.z); if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(positon))) { glm::vec3 dest(newwp.x, newwp.y, newwp.z); float newz = zone->zonemap->FindBestZ(dest, nullptr); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaLoading)) newwp.z = newz + 1; } } newwp.pause = atoi(row[3]); newwp.heading = atof(row[4]); Waypoints.push_back(newwp); } UpdateWaypoint(0); SetWaypointPause(); if (wandertype == 1 || wandertype == 2 || wandertype == 5) CalculateNewWaypoint(); if (wandertype == 1 || wandertype == 2 || wandertype == 5) CalculateNewWaypoint(); }
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ) { if(GetID()==0) return true; if ((x_pos-x == 0) && (y_pos-y == 0)) {//spawn is at target coords if(z_pos-z != 0) { z_pos = z; mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f): Jumping pure Z.", x, y, z); return true; } mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f) inWater=%d: We are there.", x, y, z, inWater); return false; } else if ((ABS(x_pos - x) < 0.1) && (ABS(y_pos - y) < 0.1)) { mlog(AI__WAYPOINTS, "Calc Position2 (%.3f, %.3f, %.3f): X/Y difference <0.1, Jumping to target.", x, y, z); if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } x_pos = x; y_pos = y; z_pos = z; return true; } int compare_steps = IsBoat() ? 1 : 20; if(tar_ndx < compare_steps && tarx==x && tary==y) { float new_x = x_pos + tar_vx*tar_vector; float new_y = y_pos + tar_vy*tar_vector; float new_z = z_pos + tar_vz*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; mlog(AI__WAYPOINTS, "Calculating new position2 to (%.3f, %.3f, %.3f), old vector (%.3f, %.3f, %.3f)", x, y, z, tar_vx, tar_vy, tar_vz); uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if((ABS(x - x_pos) < 0.5) && (ABS(y - y_pos) < 0.5)) { if(ABS(z-z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) z_pos = z; else z_pos = newz + 1; } else z_pos = newz + 1; } } } tar_ndx++; return true; } if (tar_ndx>50) { tar_ndx--; } else { tar_ndx=0; } tarx=x; tary=y; tarz=z; float nx = this->x_pos; float ny = this->y_pos; float nz = this->z_pos; // float nh = this->heading; tar_vx = x - nx; tar_vy = y - ny; tar_vz = z - nz; //pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); //speed *= NPC_SPEED_MULTIPLIER; mlog(AI__WAYPOINTS, "Calculating new position2 to (%.3f, %.3f, %.3f), new vector (%.3f, %.3f, %.3f) rate %.3f, RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- float mag = sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz); tar_vector = speed / mag; // mob move fix int numsteps = (int) ( mag * 20 / speed) + 1; // mob move fix if (numsteps<20) { if (numsteps>1) { tar_vector=1.0f ; tar_vx = tar_vx/numsteps; tar_vy = tar_vy/numsteps; tar_vz = tar_vz/numsteps; float new_x = x_pos + tar_vx; float new_y = y_pos + tar_vy; float new_z = z_pos + tar_vz; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; tar_ndx=22-numsteps; heading = CalculateHeadingToTarget(x, y); mlog(AI__WAYPOINTS, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps); } else { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } x_pos = x; y_pos = y; z_pos = z; mlog(AI__WAYPOINTS, "Only a single step to get there... jumping."); } } else { tar_vector/=20; float new_x = x_pos + tar_vx*tar_vector; float new_y = y_pos + tar_vy*tar_vector; float new_z = z_pos + tar_vz*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; heading = CalculateHeadingToTarget(x, y); mlog(AI__WAYPOINTS, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", x_pos, y_pos, z_pos, numsteps); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5) { if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) z_pos = z; else z_pos = newz + 1; } else z_pos = newz+1; } } } SetMoving(true); moved=true; delta_x=x_pos-nx; delta_y=y_pos-ny; delta_z=z_pos-nz; delta_heading=0; if (IsClient()) SendPosUpdate(1); else SendPosUpdate(); SetAppearance(eaStanding, false); pLastChange = Timer::GetCurrentTime(); return true; }
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ) { if(GetID()==0) return true; if(speed <= 0) { SetCurrentSpeed(0); return true; } if ((m_Position.x-x == 0) && (m_Position.y-y == 0)) {//spawn is at target coords if(m_Position.z-z != 0) { m_Position.z = z; Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f): Jumping pure Z.", x, y, z); return true; } Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f) inWater=%d: We are there.", x, y, z, inWater); return false; } else if ((std::abs(m_Position.x - x) < 0.1) && (std::abs(m_Position.y - y) < 0.1)) { Log.Out(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f): X/Y difference <0.1, Jumping to target.", x, y, z); if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } m_Position.x = x; m_Position.y = y; m_Position.z = z; return true; } bool send_update = false; int compare_steps = 20; if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) { float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; float new_z = m_Position.z + m_TargetV.z*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), old vector (%.3f, %.3f, %.3f)", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z); uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x,m_Position.y,m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if ((std::abs(x - m_Position.x) < 0.5) && (std::abs(y - m_Position.y) < 0.5)) { if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) m_Position.z = z; else m_Position.z = newz + 1; } else m_Position.z = newz + 1; } } } tar_ndx++; return true; } if (tar_ndx>50) { tar_ndx--; } else { tar_ndx=0; } m_TargetLocation = glm::vec3(x, y, z); float nx = this->m_Position.x; float ny = this->m_Position.y; float nz = this->m_Position.z; // float nh = this->heading; m_TargetV.x = x - nx; m_TargetV.y = y - ny; m_TargetV.z = z - nz; SetCurrentSpeed((int8)speed); pRunAnimSpeed = speed; if(IsClient()) { animation = speed / 2; } //pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); //speed *= NPC_SPEED_MULTIPLIER; Log.Out(Logs::Detail, Logs::AI, "Calculating new position2 to (%.3f, %.3f, %.3f), new vector (%.3f, %.3f, %.3f) rate %.3f, RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- float mag = sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z); tar_vector = (float)speed / mag; // mob move fix int numsteps = (int) ( mag * 16.0f / (float)speed + 0.5f); // mob move fix if (numsteps<20) { if (numsteps>1) { tar_vector=1.0f ; m_TargetV.x = m_TargetV.x/(float)numsteps; m_TargetV.y = m_TargetV.y/(float)numsteps; m_TargetV.z = m_TargetV.z/(float)numsteps; float new_x = m_Position.x + m_TargetV.x; float new_y = m_Position.y + m_TargetV.y; float new_z = m_Position.z + m_TargetV.z; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; m_Position.w = CalculateHeadingToTarget(x, y); tar_ndx = 20 - numsteps; Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", m_Position.x, m_Position.y, m_Position.z, numsteps); } else { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } m_Position.x = x; m_Position.y = y; m_Position.z = z; Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping."); } } else { tar_vector/=16.0f; float dur = Timer::GetCurrentTime() - pLastChange; if(dur < 1.0f) { dur = 1.0f; } tar_vector = (tar_vector * AImovement_duration) / 100.0f; float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector; float new_z = m_Position.z + m_TargetV.z*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } m_Position.x = new_x; m_Position.y = new_y; m_Position.z = new_z; m_Position.w = CalculateHeadingToTarget(x, y); Log.Out(Logs::Detail, Logs::AI, "Next position2 (%.3f, %.3f, %.3f) (%d steps)", m_Position.x, m_Position.y, m_Position.z, numsteps); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position)))) { glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z); float newz = zone->zonemap->FindBestZ(dest, nullptr); Log.Out(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,m_Position.x, m_Position.y, m_Position.z); if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) { if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving)) m_Position.z = z; else m_Position.z = newz + 1; } else m_Position.z = newz+1; } } } SetMoving(true); moved=true; m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); if (IsClient()) { SendPosUpdate(1); CastToClient()->ResetPositionTimer(); } else { SendPosUpdate(); SetAppearance(eaStanding, false); } pLastChange = Timer::GetCurrentTime(); return true; }
bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool checkZ) { if(GetID()==0) return true; float nx = x_pos; float ny = y_pos; float nz = z_pos; // if NPC is rooted if (speed == 0.0) { SetHeading(CalculateHeadingToTarget(x, y)); if(moved){ SendPosition(); SetMoving(false); moved=false; } SetRunAnimSpeed(0); mlog(AI__WAYPOINTS, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z); return true; } float old_test_vector=test_vector; tar_vx = x - nx; tar_vy = y - ny; tar_vz = z - nz; if (tar_vx == 0 && tar_vy == 0) return false; pRunAnimSpeed = (uint8)(speed*NPC_RUNANIM_RATIO); speed *= NPC_SPEED_MULTIPLIER; mlog(AI__WAYPOINTS, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, tar_vx, tar_vy, tar_vz, speed, pRunAnimSpeed); // -------------------------------------------------------------------------- // 2: get unit vector // -------------------------------------------------------------------------- test_vector=sqrtf (x*x + y*y + z*z); tar_vector = speed / sqrtf (tar_vx*tar_vx + tar_vy*tar_vy + tar_vz*tar_vz); heading = CalculateHeadingToTarget(x, y); if (tar_vector >= 1.0) { if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), x, y, z); } x_pos = x; y_pos = y; z_pos = z; mlog(AI__WAYPOINTS, "Close enough, jumping to waypoint"); } else { float new_x = x_pos + tar_vx*tar_vector; float new_y = y_pos + tar_vy*tar_vector; float new_z = z_pos + tar_vz*tar_vector; if(IsNPC()) { entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); } x_pos = new_x; y_pos = new_y; z_pos = new_z; mlog(AI__WAYPOINTS, "Next position (%.3f, %.3f, %.3f)", x_pos, y_pos, z_pos); } uint8 NPCFlyMode = 0; if(IsNPC()) { if(CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2) NPCFlyMode = 1; } //fix up pathing Z if(!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) { if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() || (zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos))) { Map::Vertex dest(x_pos, y_pos, z_pos); float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check. { if(ABS(x - x_pos) < 0.5 && ABS(y - y_pos) < 0.5) { if(ABS(z - z_pos) <= RuleR(Map, FixPathingZMaxDeltaMoving)) z_pos = z; else z_pos = newz + 1; } else z_pos = newz+1; } } } //OP_MobUpdate if((old_test_vector!=test_vector) || tar_ndx>20){ //send update tar_ndx=0; this->SetMoving(true); moved=true; delta_x=(x_pos-nx); delta_y=(y_pos-ny); delta_z=(z_pos-nz); delta_heading=0;//(heading-nh)*8; SendPosUpdate(); } tar_ndx++; // now get new heading SetAppearance(eaStanding, false); // make sure they're standing pLastChange = Timer::GetCurrentTime(); return true; }
void MobMovementManager::UpdatePathGround(Mob * who, float x, float y, float z, MobMovementMode mode) { PathfinderOptions opts; opts.smooth_path = true; opts.step_size = RuleR(Pathing, NavmeshStepSize); opts.offset = who->GetZOffset(); opts.flags = PathingNotDisabled ^ PathingZoneLine; //This is probably pointless since the nav mesh tool currently sets zonelines to disabled anyway auto partial = false; auto stuck = false; auto route = zone->pathing->FindPath( glm::vec3(who->GetX(), who->GetY(), who->GetZ()), glm::vec3(x, y, z), partial, stuck, opts); auto eiter = _impl->Entries.find(who); auto &ent = (*eiter); if (route.size() == 0) { HandleStuckBehavior(who, x, y, z, mode); return; } AdjustRoute(route, who); //avoid doing any processing if the mob is stuck to allow normal stuck code to work. if (!stuck) { //there are times when the routes returned are no differen than where the mob is currently standing. What basically happens //is a mob will get 'stuck' in such a way that it should be moving but the 'moving' place is the exact same spot it is at. //this is a problem and creates an area of ground that if a mob gets to, will stay there forever. If socal this creates a //"Ball of Death" (tm). This code tries to prevent this by simply warping the mob to the requested x/y. Better to have a warp than //have stuck mobs. auto routeNode = route.begin(); bool noValidPath = true; while (routeNode != route.end() && noValidPath == true) { auto ¤tNode = (*routeNode); if (routeNode == route.end()) { continue; } if (!(currentNode.pos.x == who->GetX() && currentNode.pos.y == who->GetY())) { //if one of the nodes to move to, is not our current node, pass it. noValidPath = false; break; } //move to the next node routeNode++; } if (noValidPath) { //we are 'stuck' in a path, lets just get out of this by 'teleporting' to the next position. PushTeleportTo(ent.second, x, y, z, CalculateHeadingAngleBetweenPositions(who->GetX(), who->GetY(), x, y)); return; } } auto iter = route.begin(); glm::vec3 previous_pos(who->GetX(), who->GetY(), who->GetZ()); bool first_node = true; while (iter != route.end()) { auto ¤t_node = (*iter); iter++; if (iter == route.end()) { continue; } previous_pos = current_node.pos; auto &next_node = (*iter); if (first_node) { if (mode == MovementWalking) { auto h = who->CalculateHeadingToTarget(next_node.pos.x, next_node.pos.y); PushRotateTo(ent.second, who, h, mode); } first_node = false; } //move to / teleport to node + 1 if (next_node.teleport && next_node.pos.x != 0.0f && next_node.pos.y != 0.0f) { PushTeleportTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, CalculateHeadingAngleBetweenPositions(current_node.pos.x, current_node.pos.y, next_node.pos.x, next_node.pos.y)); } else { if (zone->watermap->InLiquid(previous_pos)) { PushSwimTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, mode); } else { PushMoveTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, mode); } } } if (stuck) { HandleStuckBehavior(who, x, y, z, mode); } else { PushStopMoving(ent.second); } }
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { this->EVENT_ITEM_ScriptStopReturn(); if(conlevel == CON_GREEN) return; uint32 add_exp = in_add_exp; if(!resexp && (XPRate != 0)) add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f)); if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA=0; // stop exploit with sanity check uint32 add_aaxp; if(resexp) { add_aaxp = 0; } else { //figure out how much of this goes to AAs add_aaxp = add_exp * m_epp.perAA / 100; //take that ammount away from regular exp add_exp -= add_aaxp; float totalmod = 1.0; if(RuleR(Character, ExpMultiplier) >= 0){ totalmod *= RuleR(Character, ExpMultiplier); } float zemmod = 75.0; if(zone->newzone_data.zone_exp_multiplier >= 0){ zemmod = zone->newzone_data.zone_exp_multiplier * 100; } if(zone->IsHotzone()) { totalmod += RuleR(Zone, HotZoneBonus); } // AK had a permanent 20% XP increase. totalmod += 0.20; add_exp = uint32(float(add_exp) * totalmod * zemmod); } float aatotalmod = 1.0; // AK had a permanent 20% XP increase. aatotalmod += 0.20; if(zone->newzone_data.zone_exp_multiplier >= 0){ aatotalmod *= zone->newzone_data.zone_exp_multiplier * 100; } if(RuleB(Zone, LevelBasedEXPMods)){ if(zone->level_exp_mod[GetLevel()].ExpMod){ add_exp *= zone->level_exp_mod[GetLevel()].ExpMod; add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod; } } uint32 requiredxp = GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel()); float xp_cap = (float)requiredxp * 0.13f; //13% of total XP is our cap if(add_exp > xp_cap) add_exp = xp_cap; if(add_aaxp > xp_cap) add_aaxp = xp_cap; uint32 exp = GetEXP() + add_exp; uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod); uint32 had_aaexp = GetAAXP(); aaexp += had_aaexp; if(aaexp < had_aaexp) aaexp = had_aaexp; //watch for wrap uint32 neededxp = GetEXPForLevel(GetLevel()+1) - (GetEXP() + add_exp); if (admin>=100 && GetGM()) { Message(CC_Yellow, "[GM] You have gained %d (%d) AXP and %d (%d) EXP. %d more EXP is needed for Level %d", add_aaxp, GetAAXP() + add_aaxp, add_exp, GetEXP() + add_exp, neededxp, GetLevel()+1); } //Message(CC_Yellow, "AddExp: XP awarded: %i (%i) Required XP is: %i Cap: %0.2f Race: %i Class: %i Zoneid: %i", add_exp, GetEXP() + add_exp, requiredxp, xp_cap, GetBaseRace(), GetClass(), zone->GetZoneID()); SetEXP(exp, aaexp, resexp); }
void EQStream::Write(int eq_fd) { queue<EQProtocolPacket *> ReadyToSend; bool SeqEmpty=false,NonSeqEmpty=false; deque<EQProtocolPacket *>::iterator sitr; // Check our rate to make sure we can send more MRate.lock(); int32 threshold=RateThreshold; MRate.unlock(); if (BytesWritten > threshold) { //cout << "Over threshold: " << BytesWritten << " > " << threshold << endl; return; } // If we got more packets to we need to ack, send an ack on the highest one MAcks.lock(); if (CompareSequence(LastAckSent, NextAckToSend) == SeqFuture) SendAck(NextAckToSend); MAcks.unlock(); // Lock the outbound queues while we process MOutboundQueue.lock(); // Place to hold the base packet t combine into EQProtocolPacket *p=NULL; #ifdef RETRANSMITS // if we have a timeout defined and we have not received an ack recently enough, retransmit from beginning of queue if (RuleR(EQStream, RetransmitTimeoutMult) && !SequencedQueue.empty() && NextSequencedSend && (GetState()==ESTABLISHED) && ((retransmittimer+retransmittimeout) < Timer::GetCurrentTime())) { _log(NET__NET_TRACE, _L "Timeout since last ack received, starting retransmit at the start of our unacked buffer (seq %d, was %d)." __L, SequencedBase, SequencedBase+NextSequencedSend); NextSequencedSend = 0; retransmittimer = Timer::GetCurrentTime(); // don't want to endlessly retransmit the first packet } #endif // Find the next sequenced packet to send from the "queue" sitr = SequencedQueue.begin(); if (sitr!=SequencedQueue.end()) sitr += NextSequencedSend; // Loop until both are empty or MaxSends is reached while(!SeqEmpty || !NonSeqEmpty) { // See if there are more non-sequenced packets left if (!NonSequencedQueue.empty()) { if (!p) { // If we don't have a packet to try to combine into, use this one as the base // And remove it form the queue p = NonSequencedQueue.front(); _log(NET__NET_COMBINE, _L "Starting combined packet with non-seq packet of len %d" __L, p->size); NonSequencedQueue.pop(); } else if (!p->combine(NonSequencedQueue.front())) { // Tryint to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) _log(NET__NET_COMBINE, _L "Combined packet full at len %d, next non-seq packet is len %d" __L, p->size, (NonSequencedQueue.front())->size); ReadyToSend.push(p); BytesWritten+=p->size; p=NULL; if (BytesWritten > threshold) { // Sent enough this round, lets stop to be fair _log(NET__RATES, _L "Exceeded write threshold in nonseq (%d > %d)" __L, BytesWritten, threshold); break; } } else { // Combine worked, so just remove this packet and it's spot in the queue _log(NET__NET_COMBINE, _L "Combined non-seq packet of len %d, yeilding %d combined." __L, (NonSequencedQueue.front())->size, p->size); delete NonSequencedQueue.front(); NonSequencedQueue.pop(); } } else { // No more non-sequenced packets NonSeqEmpty=true; } if (sitr!=SequencedQueue.end()) { //_log(NET__NET_COMBINE, _L "Send Seq with %d seq packets starting at seq %d, next send %d, and %d non-seq packets." __L, // SequencedQueue.size(), SequencedBase, NextSequencedSend, NonSequencedQueue.size()); if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { _log(NET__ERROR, _L "Pre-Send Seq NSS=%d Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, NextSequencedSend, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { _log(NET__ERROR, _L "Pre-Send Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } uint16 seq_send = SequencedBase + NextSequencedSend; //just for logging... if(SequencedQueue.empty()) { _log(NET__ERROR, _L "Tried to write a packet with an empty queue (%d is past next out %d)" __L, seq_send, NextOutSeq); SeqEmpty=true; continue; } /*if(CompareSequence(NextOutSeq, seq_send) == SeqFuture) { _log(NET__ERROR, _L "Tried to write a packet beyond the end of the queue! (%d is past next out %d)" __L, seq_send, NextOutSeq); sitr=SequencedQueue.end(); continue; }*/ #ifdef RETRANSMITS if (!RuleB(EQStream, RetransmitAckedPackets) && (*sitr)->acked) { _log(NET__NET_TRACE, _L "Not retransmitting seq packet %d because already marked as acked" __L, seq_send); sitr++; NextSequencedSend++; } else if (!p) { #else if (!p) { #endif // If we don't have a packet to try to combine into, use this one as the base // Copy it first as it will still live until it is acked p=(*sitr)->Copy(); _log(NET__NET_COMBINE, _L "Starting combined packet with seq packet %d of len %d" __L, seq_send, p->size); sitr++; NextSequencedSend++; } else if (!p->combine(*sitr)) { // Trying to combine this packet with the base didn't work (too big maybe) // So just send the base packet (we'll try this packet again later) _log(NET__NET_COMBINE, _L "Combined packet full at len %d, next seq packet %d is len %d" __L, p->size, seq_send, (*sitr)->size); ReadyToSend.push(p); BytesWritten+=p->size; p=NULL; if (BytesWritten > threshold) { // Sent enough this round, lets stop to be fair _log(NET__RATES, _L "Exceeded write threshold in seq (%d > %d)" __L, BytesWritten, threshold); break; } } else { // Combine worked _log(NET__NET_COMBINE, _L "Combined seq packet %d of len %d, yeilding %d combined." __L, seq_send, (*sitr)->size, p->size); sitr++; NextSequencedSend++; } if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { _log(NET__ERROR, _L "Post send Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); } if(NextSequencedSend > SequencedQueue.size()) { _log(NET__ERROR, _L "Post send Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); } } else { // No more sequenced packets SeqEmpty=true; } } // Unlock the queue MOutboundQueue.unlock(); // We have a packet still, must have run out of both seq and non-seq, so send it if (p) { _log(NET__NET_COMBINE, _L "Final combined packet not full, len %d" __L, p->size); ReadyToSend.push(p); BytesWritten+=p->size; } // Send all the packets we "made" while(!ReadyToSend.empty()) { p = ReadyToSend.front(); WritePacket(eq_fd,p); delete p; ReadyToSend.pop(); } //see if we need to send our disconnect and finish our close if(SeqEmpty && NonSeqEmpty) { //no more data to send if(CheckState(CLOSING)) { _log(NET__DEBUG, _L "All outgoing data flushed, closing stream." __L ); //we are waiting for the queues to empty, now we can do our disconnect. //this packet will not actually go out until the next call to Write(). _SendDisconnect(); SetState(DISCONNECTING); } } } void EQStream::WritePacket(int eq_fd, EQProtocolPacket *p) { uint32 length; sockaddr_in address; address.sin_family = AF_INET; address.sin_addr.s_addr=remote_ip; address.sin_port=remote_port; #ifdef NOWAY uint32 ip=address.sin_addr.s_addr; cout << "Sending to: " << (int)*(unsigned char *)&ip << "." << (int)*((unsigned char *)&ip+1) << "." << (int)*((unsigned char *)&ip+2) << "." << (int)*((unsigned char *)&ip+3) << "," << (int)ntohs(address.sin_port) << "(" << p->size << ")" << endl; p->DumpRaw(); cout << "-------------" << endl; #endif length=p->serialize(buffer); if (p->opcode!=OP_SessionRequest && p->opcode!=OP_SessionResponse) { if (compressed) { uint32 newlen=EQProtocolPacket::Compress(buffer,length, _tempBuffer, 2048); memcpy(buffer,_tempBuffer,newlen); length=newlen; } if (encoded) { EQProtocolPacket::ChatEncode(buffer,length,Key); } *(uint16 *)(buffer+length)=htons(CRC16(buffer,length,Key)); length+=2; } //dump_message_column(buffer,length,"Writer: "); sendto(eq_fd,(char *)buffer,length,0,(sockaddr *)&address,sizeof(address)); AddBytesSent(length); }
void Group::SplitExp(uint32 exp, Mob* other) { if( other->CastToNPC()->MerchantType != 0 ) // Ensure NPC isn't a merchant return; if(other->GetOwner() && other->GetOwner()->IsClient()) // Ensure owner isn't pc return; unsigned int i; int8 membercount = 0; int8 close_membercount = 0; uint8 maxlevel = 1; for (i = 0; i < MAX_GROUP_MEMBERS; i++) { if (members[i] != nullptr && members[i]->IsClient()) { Client *cmember = members[i]->CastToClient(); if(cmember->CastToClient()->GetZoneID() == zone->GetZoneID()) { if(members[i]->GetLevel() > maxlevel) maxlevel = members[i]->GetLevel(); if(cmember->GetLevelCon(other->GetLevel()) != CON_GREEN) { ++membercount; if(cmember->CastToClient()->IsInRange(other)) ++close_membercount; } } } } // If the NPC is green to the whole group or they are all out of the kill zone (wipe?) this will return. if (membercount <= 0 || close_membercount <= 0) return; bool isgreen = false; int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); if(conlevel == CON_GREEN) isgreen = true; if(isgreen && !RuleB(AlKabor, GreensGiveXPToGroup)) return; // The first loop grabs the maxlevel, so we need to adjust the count here checking for level range, // before applying xp in the third and final loop. for (i = 0; i < MAX_GROUP_MEMBERS; i++) { if (members[i] != nullptr && members[i]->IsClient()) // If Group Member is Client { Client *cmember = members[i]->CastToClient(); if (!cmember->IsInLevelRange(maxlevel) && cmember->CastToClient()->GetZoneID() == zone->GetZoneID() && cmember->GetLevelCon(other->GetLevel()) != CON_GREEN) { if(membercount != 0 && close_membercount != 0) { --membercount; if(cmember->CastToClient()->IsInRange(other)) --close_membercount; } else return; } } } if (membercount <= 0 || close_membercount <= 0) return; if(!RuleB(AlKabor, OutOfRangeGroupXPBonus)) membercount = close_membercount; float groupmod = 1.0; if (membercount == 2) groupmod += 0.20; else if(membercount == 3) groupmod += 0.40; if(RuleB(AlKabor, GroupEXPBonuses)) { if(membercount == 4) groupmod += 1.20; else if(membercount > 4) groupmod += 1.60; } else { if(membercount == 4) groupmod += 0.60; else if(membercount > 4) groupmod += 0.80; } uint32 groupexp = (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); // Give XP to all clients in the group who are close to the kill. if(!RuleB(AlKabor, Count6thGroupMember)) { // 6th member is free for division. if(close_membercount == 6) close_membercount = 5; } uint32 splitgroupxp = groupexp / close_membercount; if(splitgroupxp < 1) splitgroupxp = 1; for (i = 0; i < MAX_GROUP_MEMBERS; i++) { if (members[i] != nullptr && members[i]->IsClient()) // If Group Member is Client { Client *cmember = members[i]->CastToClient(); if(cmember->CastToClient()->GetZoneID() == zone->GetZoneID() && cmember->GetLevelCon(other->GetLevel()) != CON_GREEN && cmember->IsInRange(other)) { if (cmember->IsInLevelRange(maxlevel)) { if(isgreen) { // NPCs that are green to some of the group do not split XP. cmember->AddEXP(groupexp, cmember->GetLevelCon(other->GetLevel())); //_log(_GROUP__LOG, "%s gets non-split green XP worth: %i. You lucky dog.", cmember->GetName(), groupexp); } else { cmember->AddEXP(splitgroupxp, conlevel); //_log(_GROUP__LOG, "%s splits %i with the rest of the group. Their share: %i", cmember->GetName(), groupexp, splitgroupxp); //cmember->Message(CC_Yellow, "Group XP awarded is: %i Total XP is: %i for count: %i total count: %i in_exp is: %i", splitgroupxp, groupexp, close_membercount, membercount, exp); } } else Log.Out(Logs::Detail, Logs::Group, "%s is too low in level to gain XP from this group.", cmember->GetName()); } else Log.Out(Logs::Detail, Logs::Group, "%s is not in the kill zone, is out of range, or %s is green to them. They won't recieve group XP.", cmember->GetName(), other->GetCleanName()); } } }
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { uint32 add_exp = in_add_exp; if(!resexp && (XPRate != 0)) add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f)); if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA=0; // stop exploit with sanity check uint32 add_aaxp; if(resexp) { add_aaxp = 0; } else { //figure out how much of this goes to AAs add_aaxp = add_exp * m_epp.perAA / 100; //take that ammount away from regular exp add_exp -= add_aaxp; float totalmod = 1.0; float zemmod = 1.0; //get modifiers if(RuleR(Character, ExpMultiplier) >= 0){ totalmod *= RuleR(Character, ExpMultiplier); } if(zone->newzone_data.zone_exp_multiplier >= 0){ zemmod *= zone->newzone_data.zone_exp_multiplier; } if(RuleB(Character,UseRaceClassExpBonuses)) { if(GetBaseRace() == HALFLING){ totalmod *= 1.05; } if(GetClass() == ROGUE || GetClass() == WARRIOR){ totalmod *= 1.05; } } if(zone->IsHotzone()) { totalmod += RuleR(Zone, HotZoneBonus); } add_exp = uint32(float(add_exp) * totalmod * zemmod); if(RuleB(Character,UseXPConScaling)) { if (conlevel != 0xFF && !resexp) { switch (conlevel) { case CON_GREEN: add_exp = 0; add_aaxp = 0; return; case CON_LIGHTBLUE: add_exp = add_exp * RuleI(Character, LightBlueModifier)/100; add_aaxp = add_aaxp * RuleI(Character, LightBlueModifier)/100; break; case CON_BLUE: add_exp = add_exp * RuleI(Character, BlueModifier)/100; add_aaxp = add_aaxp * RuleI(Character, BlueModifier)/100; break; case CON_WHITE: add_exp = add_exp * RuleI(Character, WhiteModifier)/100; add_aaxp = add_aaxp * RuleI(Character, WhiteModifier)/100; break; case CON_YELLOW: add_exp = add_exp * RuleI(Character, YellowModifier)/100; add_aaxp = add_aaxp * RuleI(Character, YellowModifier)/100; break; case CON_RED: add_exp = add_exp * RuleI(Character, RedModifier)/100; add_aaxp = add_aaxp * RuleI(Character, RedModifier)/100; break; } } } if(IsLeadershipEXPOn() && ((conlevel == CON_BLUE) || (conlevel == CON_WHITE) || (conlevel == CON_YELLOW) || (conlevel == CON_RED))) { add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f); if(GetGroup()) { if((m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())) && (RuleI(Character, KillsPerGroupLeadershipAA) > 0)) { AddLeadershipEXP(GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA), 0); Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); } else Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); } else { if((m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())) && (RuleI(Character, KillsPerRaidLeadershipAA) > 0)) { AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA)); Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP); } else Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS); } } } //end !resexp float aatotalmod = 1.0; if(zone->newzone_data.zone_exp_multiplier >= 0){ aatotalmod *= zone->newzone_data.zone_exp_multiplier; } if(RuleB(Character,UseRaceClassExpBonuses)) { if(GetBaseRace() == HALFLING){ aatotalmod *= 1.05; } if(GetClass() == ROGUE || GetClass() == WARRIOR){ aatotalmod *= 1.05; } } if(RuleB(Zone, LevelBasedEXPMods)){ if(zone->level_exp_mod[GetLevel()].ExpMod){ add_exp *= zone->level_exp_mod[GetLevel()].ExpMod; add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod; } } uint32 exp = GetEXP() + add_exp; uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod); uint32 had_aaexp = GetAAXP(); aaexp += had_aaexp; if(aaexp < had_aaexp) aaexp = had_aaexp; //watch for wrap SetEXP(exp, aaexp, resexp); }
//we need this function to immediately determine, after we receive OP_Fishing, if we can even try to fish, otherwise we have to wait a while to get the failure bool Client::CanFish() { //make sure we still have a fishing pole on: const ItemInst* Pole = m_inv[SlotPrimary]; int32 bslot = m_inv.HasItemByUse(ItemTypeFishingBait, 1, invWhereWorn|invWherePersonal); const ItemInst* Bait = nullptr; if (bslot != INVALID_INDEX) Bait = m_inv.GetItem(bslot); if(!Pole || !Pole->IsType(ItemClassCommon) || Pole->GetItem()->ItemType != ItemTypeFishingPole) { if (m_inv.HasItemByUse(ItemTypeFishingPole, 1, invWhereWorn|invWherePersonal|invWhereBank|invWhereSharedBank|invWhereTrading|invWhereCursor)) //We have a fishing pole somewhere, just not equipped Message_StringID(MT_Skills, FISHING_EQUIP_POLE); //You need to put your fishing pole in your primary hand. else //We don't have a fishing pole anywhere Message_StringID(MT_Skills, FISHING_NO_POLE); //You can't fish without a fishing pole, go buy one. return false; } if (!Bait || !Bait->IsType(ItemClassCommon) || Bait->GetItem()->ItemType != ItemTypeFishingBait) { Message_StringID(MT_Skills, FISHING_NO_BAIT); //You can't fish without fishing bait, go buy some. return false; } if(zone->zonemap != nullptr && zone->watermap != nullptr && RuleB(Watermap, CheckForWaterWhenFishing)) { glm::vec3 rodPosition; // Tweak Rod and LineLength if required const float RodLength = RuleR(Watermap, FishingRodLength); const float LineLength = RuleR(Watermap, FishingLineLength); int HeadingDegrees; HeadingDegrees = (int) ((GetHeading()*360)/256); HeadingDegrees = HeadingDegrees % 360; rodPosition.x = m_Position.x + RodLength * sin(HeadingDegrees * M_PI/180.0f); rodPosition.y = m_Position.y + RodLength * cos(HeadingDegrees * M_PI/180.0f); rodPosition.z = m_Position.z; float bestz = zone->zonemap->FindBestZ(rodPosition, nullptr); float len = m_Position.z - bestz; if(len > LineLength || len < 0.0f) { Message_StringID(MT_Skills, FISHING_LAND); return false; } float step_size = RuleR(Watermap, FishingLineStepSize); for(float i = 0.0f; i < len; i += step_size) { glm::vec3 dest(rodPosition.x, rodPosition.y, m_Position.z - i); bool in_lava = zone->watermap->InLava(dest); bool in_water = zone->watermap->InWater(dest) || zone->watermap->InVWater(dest); if (in_lava) { Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something? return false; } if(in_water) { return true; } } Message_StringID(MT_Skills, FISHING_LAND); return false; } return true; }
virtual bool Process(MobMovementManager *mgr, Mob *m) { if (!m->IsAIControlled()) { return true; } //Send a movement packet when you start moving double current_time = static_cast<double>(Timer::GetCurrentTime()) / 1000.0; int current_speed = 0; if (m_move_to_mode == MovementRunning) { if (m->IsFeared()) { current_speed = m->GetFearSpeed(); } else { current_speed = m->GetRunspeed(); } } else { current_speed = m->GetWalkspeed(); } if (!m_started) { m_started = true; //rotate to the point m->SetMoving(true); m->SetHeading(m->CalculateHeadingToTarget(m_move_to_x, m_move_to_y)); m_last_sent_speed = current_speed; m_last_sent_time = current_time; m_total_h_dist = DistanceNoZ(m->GetPosition(), glm::vec4(m_move_to_x, m_move_to_y, 0.0f, 0.0f)); m_total_v_dist = m_move_to_z - m->GetZ(); mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } //When speed changes if (current_speed != m_last_sent_speed) { if (RuleB(Map, FixZWhenPathing)) { m->FixZ(); } m_distance_moved_since_correction = 0.0; m_last_sent_speed = current_speed; m_last_sent_time = current_time; mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } //If x seconds have passed without sending an update. if (current_time - m_last_sent_time >= 5.0) { if (RuleB(Map, FixZWhenPathing)) { m->FixZ(); } m_distance_moved_since_correction = 0.0; m_last_sent_speed = current_speed; m_last_sent_time = current_time; mgr->SendCommandToClients(m, 0.0, 0.0, 0.0, 0.0, current_speed, ClientRangeCloseMedium); } auto &p = m->GetPosition(); glm::vec2 tar(m_move_to_x, m_move_to_y); glm::vec2 pos(p.x, p.y); double len = glm::distance(pos, tar); if (len == 0) { return true; } m->SetMoved(true); glm::vec2 dir = tar - pos; glm::vec2 ndir = glm::normalize(dir); double distance_moved = frame_time * current_speed * 0.4f * 1.45f; if (distance_moved > len) { if (m->IsNPC()) { entity_list.ProcessMove(m->CastToNPC(), m_move_to_x, m_move_to_y, m_move_to_z); } m->SetPosition(m_move_to_x, m_move_to_y, m_move_to_z); if (RuleB(Map, FixZWhenPathing)) { m->FixZ(); } return true; } else { glm::vec2 npos = pos + (ndir * static_cast<float>(distance_moved)); len -= distance_moved; double total_distance_traveled = m_total_h_dist - len; double start_z = m_move_to_z - m_total_v_dist; double z_at_pos = start_z + (m_total_v_dist * (total_distance_traveled / m_total_h_dist)); if (m->IsNPC()) { entity_list.ProcessMove(m->CastToNPC(), npos.x, npos.y, z_at_pos); } m->SetPosition(npos.x, npos.y, z_at_pos); if (RuleB(Map, FixZWhenPathing)) { m_distance_moved_since_correction += distance_moved; if (m_distance_moved_since_correction > RuleR(Map, DistanceCanTravelBeforeAdjustment)) { m_distance_moved_since_correction = 0.0; m->FixZ(); } } } return false; }
// Split from the basic MakePet to allow backward compatiblity with existing code while also // making it possible for petpower to be retained without the focus item having to // stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus // of a client is searched for and used instead. void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, const char *petname, float in_size) { // Sanity and early out checking first. if(HasPet() || pettype == nullptr) return; int16 act_power = 0; // The actual pet power we'll use. if (petpower == -1) { if (this->IsClient()) { act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);//Client only act_power = CastToClient()->mod_pet_power(act_power, spell_id); } #ifdef BOTS else if (this->IsBot()) act_power = CastToBot()->GetBotFocusEffect(Bot::BotfocusPetPower, spell_id); #endif } else if (petpower > 0) act_power = petpower; // optional rule: classic style variance in pets. Achieve this by // adding a random 0-4 to pet power, since it only comes in increments // of five from focus effects. //lookup our pets table record for this type PetRecord record; if(!database.GetPoweredPetEntry(pettype, act_power, &record)) { Message(13, "Unable to find data for pet %s", pettype); Log.Out(Logs::General, Logs::Error, "Unable to find data for pet %s, check pets table.", pettype); return; } //find the NPC data for the specified NPC type const NPCType *base = database.LoadNPCTypesData(record.npc_type); if(base == nullptr) { Message(13, "Unable to load NPC data for pet %s", pettype); Log.Out(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type); return; } //we copy the npc_type data because we need to edit it a bit auto npc_type = new NPCType; memcpy(npc_type, base, sizeof(NPCType)); // If pet power is set to -1 in the DB, use stat scaling if ((this->IsClient() #ifdef BOTS || this->IsBot() #endif ) && record.petpower == -1) { float scale_power = (float)act_power / 100.0f; if(scale_power > 0) { npc_type->max_hp *= (1 + scale_power); npc_type->cur_hp = npc_type->max_hp; npc_type->AC *= (1 + scale_power); npc_type->level += 1 + ((int)act_power / 25) > npc_type->level + RuleR(Pets, PetPowerLevelCap) ? RuleR(Pets, PetPowerLevelCap) : 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2))); npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2))); npc_type->size = npc_type->size * (1 + (scale_power / 2)) > npc_type->size * 3 ? npc_type->size * 3 : npc_type-> size * (1 + (scale_power / 2)); } record.petpower = act_power; } //Live AA - Elemental Durability int16 MaxHP = aabonuses.PetMaxHP + itembonuses.PetMaxHP + spellbonuses.PetMaxHP; if (MaxHP){ npc_type->max_hp += (npc_type->max_hp*MaxHP)/100; npc_type->cur_hp = npc_type->max_hp; } //TODO: think about regen (engaged vs. not engaged) // Pet naming: // 0 - `s pet // 1 - `s familiar // 2 - `s Warder // 3 - Random name if client, `s pet for others // 4 - Keep DB name if (petname != nullptr) { // Name was provided, use it. strn0cpy(npc_type->name, petname, 64); } else if (record.petnaming == 0) { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } else if (record.petnaming == 1) { strcpy(npc_type->name, this->GetName()); npc_type->name[19] = '\0'; strcat(npc_type->name, "`s_familiar"); } else if (record.petnaming == 2) { strcpy(npc_type->name, this->GetName()); npc_type->name[21] = 0; strcat(npc_type->name, "`s_Warder"); } else if (record.petnaming == 4) { // Keep the DB name } else if (record.petnaming == 3 && IsClient()) { strcpy(npc_type->name, GetRandPetName()); } else { strcpy(npc_type->name, this->GetCleanName()); npc_type->name[25] = '\0'; strcat(npc_type->name, "`s_pet"); } //handle beastlord pet appearance if(record.petnaming == 2) { switch(GetBaseRace()) { case VAHSHIR: npc_type->race = TIGER; npc_type->size *= 0.8f; break; case TROLL: npc_type->race = ALLIGATOR; npc_type->size *= 2.5f; break; case OGRE: npc_type->race = BEAR; npc_type->texture = 3; npc_type->gender = 2; break; case BARBARIAN: npc_type->race = WOLF; npc_type->texture = 2; break; case IKSAR: npc_type->race = WOLF; npc_type->texture = 0; npc_type->gender = 1; npc_type->size *= 2.0f; npc_type->luclinface = 0; break; default: npc_type->race = WOLF; npc_type->texture = 0; } } // handle monster summoning pet appearance if(record.monsterflag) { uint32 monsterid = 0; // get a random npc id from the spawngroups assigned to this zone auto query = StringFormat("SELECT npcID " "FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) " "INNER JOIN npc_types ON npc_types.id = spawnentry.npcID " "WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) " "AND npc_types.race NOT IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 44, " "55, 67, 71, 72, 73, 77, 78, 81, 90, 92, 93, 94, 106, 112, 114, 127, 128, " "130, 139, 141, 183, 236, 237, 238, 239, 254, 266, 329, 330, 378, 379, " "380, 381, 382, 383, 404, 522) " "ORDER BY RAND() LIMIT 1", zone->GetShortName()); auto results = database.QueryDatabase(query); if (!results.Success()) { safe_delete(npc_type); return; } if (results.RowCount() != 0) { auto row = results.begin(); monsterid = atoi(row[0]); } // since we don't have any monsters, just make it look like an earth pet for now if (monsterid == 0) monsterid = 567; // give the summoned pet the attributes of the monster we found const NPCType* monster = database.LoadNPCTypesData(monsterid); if(monster) { npc_type->race = monster->race; npc_type->size = monster->size; npc_type->texture = monster->texture; npc_type->gender = monster->gender; npc_type->luclinface = monster->luclinface; npc_type->helmtexture = monster->helmtexture; npc_type->herosforgemodel = monster->herosforgemodel; } else Log.Out(Logs::General, Logs::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid); } //this takes ownership of the npc_type data auto npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower); // Now that we have an actual object to interact with, load // the base items for the pet. These are always loaded // so that a rank 1 suspend minion does not kill things // like the special back items some focused pets may receive. uint32 petinv[EQEmu::legacy::EQUIPMENT_SIZE]; memset(petinv, 0, sizeof(petinv)); const EQEmu::ItemData *item = 0; if (database.GetBasePetItems(record.equipmentset, petinv)) { for (int i = 0; i < EQEmu::legacy::EQUIPMENT_SIZE; i++) if (petinv[i]) { item = database.GetItem(petinv[i]); npc->AddLootDrop(item, &npc->itemlist, 0, 1, 127, true, true); } } npc->UpdateEquipmentLight(); // finally, override size if one was provided if (in_size > 0.0f) npc->size = in_size; entity_list.AddNPC(npc, true, true); SetPetID(npc->GetID()); // We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet if (record.petcontrol == petTargetLock) { Mob* target = GetTarget(); if (target){ npc->AddToHateList(target, 1); npc->SetPetTargetLockID(target->GetID()); npc->SetSpecialAbility(IMMUNE_AGGRO, 1); } else npc->Kill(); //On live casts spell 892 Unsummon (Kayen - Too limiting to use that for emu since pet can have more than 20k HP) } }
void MobMovementManager::UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode mode) { auto eiter = _impl->Entries.find(who); auto &ent = (*eiter); if (zone->watermap->InLiquid(who->GetPosition()) && zone->watermap->InLiquid(glm::vec3(x, y, z)) && zone->zonemap->CheckLoS(who->GetPosition(), glm::vec3(x, y, z))) { PushSwimTo(ent.second, x, y, z, mode); PushStopMoving(ent.second); return; } PathfinderOptions opts; opts.smooth_path = true; opts.step_size = RuleR(Pathing, NavmeshStepSize); opts.offset = who->GetZOffset(); opts.flags = PathingNotDisabled ^ PathingZoneLine; auto partial = false; auto stuck = false; auto route = zone->pathing->FindPath( glm::vec3(who->GetX(), who->GetY(), who->GetZ()), glm::vec3(x, y, z), partial, stuck, opts); if (route.size() == 0) { HandleStuckBehavior(who, x, y, z, mode); return; } AdjustRoute(route, who); auto iter = route.begin(); glm::vec3 previous_pos(who->GetX(), who->GetY(), who->GetZ()); bool first_node = true; while (iter != route.end()) { auto ¤t_node = (*iter); if (!zone->watermap->InLiquid(current_node.pos)) { stuck = true; while (iter != route.end()) { iter = route.erase(iter); } break; } else { iter++; } } if (route.size() == 0) { HandleStuckBehavior(who, x, y, z, mode); return; } iter = route.begin(); while (iter != route.end()) { auto ¤t_node = (*iter); iter++; if (iter == route.end()) { continue; } previous_pos = current_node.pos; auto &next_node = (*iter); if (first_node) { if (mode == MovementWalking) { auto h = who->CalculateHeadingToTarget(next_node.pos.x, next_node.pos.y); PushRotateTo(ent.second, who, h, mode); } first_node = false; } //move to / teleport to node + 1 if (next_node.teleport && next_node.pos.x != 0.0f && next_node.pos.y != 0.0f) { PushTeleportTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, CalculateHeadingAngleBetweenPositions(current_node.pos.x, current_node.pos.y, next_node.pos.x, next_node.pos.y)); } else { PushSwimTo(ent.second, next_node.pos.x, next_node.pos.y, next_node.pos.z, mode); } } if (stuck) { HandleStuckBehavior(who, x, y, z, mode); } else { PushStopMoving(ent.second); } }
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { this->EVENT_ITEM_ScriptStopReturn(); uint32 add_exp = in_add_exp; if(!resexp && (XPRate != 0)) add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f)); if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA=0; // stop exploit with sanity check uint32 add_aaxp; if(resexp) { add_aaxp = 0; } else { //figure out how much of this goes to AAs add_aaxp = add_exp * m_epp.perAA / 100; //take that ammount away from regular exp add_exp -= add_aaxp; float totalmod = 1.0; float zemmod = 1.0; //get modifiers if(RuleR(Character, ExpMultiplier) >= 0){ totalmod *= RuleR(Character, ExpMultiplier); } if(zone->newzone_data.zone_exp_multiplier >= 0){ zemmod *= zone->newzone_data.zone_exp_multiplier; } if(RuleB(Character,UseRaceClassExpBonuses)) { if(GetBaseRace() == HALFLING){ totalmod *= 1.05; } if(GetClass() == ROGUE || GetClass() == WARRIOR){ totalmod *= 1.05; } } if(zone->IsHotzone()) { totalmod += RuleR(Zone, HotZoneBonus); } add_exp = uint32(float(add_exp) * totalmod * zemmod); if(RuleB(Character,UseXPConScaling)) { if (conlevel != 0xFF && !resexp) { switch (conlevel) { case CON_GREEN: add_exp = 0; add_aaxp = 0; return; case CON_LIGHTBLUE: add_exp = add_exp * RuleI(Character, LightBlueModifier)/100; add_aaxp = add_aaxp * RuleI(Character, LightBlueModifier)/100; break; case CON_BLUE: add_exp = add_exp * RuleI(Character, BlueModifier)/100; add_aaxp = add_aaxp * RuleI(Character, BlueModifier)/100; break; case CON_WHITE: add_exp = add_exp * RuleI(Character, WhiteModifier)/100; add_aaxp = add_aaxp * RuleI(Character, WhiteModifier)/100; break; case CON_YELLOW: add_exp = add_exp * RuleI(Character, YellowModifier)/100; add_aaxp = add_aaxp * RuleI(Character, YellowModifier)/100; break; case CON_RED: add_exp = add_exp * RuleI(Character, RedModifier)/100; add_aaxp = add_aaxp * RuleI(Character, RedModifier)/100; break; } } } if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) { add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f); if (GetGroup()) { if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()) && RuleI(Character, KillsPerGroupLeadershipAA) > 0) { uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA); Client *mentoree = GetGroup()->GetMentoree(); if (GetGroup()->GetMentorPercent() && mentoree && mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) { uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f); exp -= mentor_exp; mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); } if (exp > 0) { // possible if you mentor 100% to the other client AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); } } else { Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); } } else { Raid *raid = GetRaid(); // Raid leaders CAN NOT gain group AA XP, other group leaders can though! if (raid->IsLeader(this)) { if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel()) && RuleI(Character, KillsPerRaidLeadershipAA) > 0) { AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA)); Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP); } else { Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS); } } else { if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()) && RuleI(Character, KillsPerGroupLeadershipAA) > 0) { uint32 group_id = raid->GetGroup(this); uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA); Client *mentoree = raid->GetMentoree(group_id); if (raid->GetMentorPercent(group_id) && mentoree && mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) { uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f); exp -= mentor_exp; mentoree->AddLeadershipEXP(mentor_exp, 0); mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); } if (exp > 0) { AddLeadershipEXP(exp, 0); Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); } } else { Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); } } } } } //end !resexp float aatotalmod = 1.0; if(zone->newzone_data.zone_exp_multiplier >= 0){ aatotalmod *= zone->newzone_data.zone_exp_multiplier; } if(RuleB(Character,UseRaceClassExpBonuses)) { if(GetBaseRace() == HALFLING){ aatotalmod *= 1.05; } if(GetClass() == ROGUE || GetClass() == WARRIOR){ aatotalmod *= 1.05; } } if(RuleB(Zone, LevelBasedEXPMods)){ if(zone->level_exp_mod[GetLevel()].ExpMod){ add_exp *= zone->level_exp_mod[GetLevel()].ExpMod; add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod; } } uint32 exp = GetEXP() + add_exp; uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod); uint32 had_aaexp = GetAAXP(); aaexp += had_aaexp; if(aaexp < had_aaexp) aaexp = had_aaexp; //watch for wrap SetEXP(exp, aaexp, resexp); }