Exemple #1
0
//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;
}
Exemple #2
0
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;
		}
	}
}
Exemple #3
0
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;
		}
	}

}
Exemple #4
0
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;
		}
	}

}
Exemple #5
0
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;
		}
	}
}
Exemple #6
0
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());
}
}
Exemple #7
0
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 );
			}
		}
	}
}
Exemple #8
0
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 ); 
			} 
		} 
	}
}
Exemple #9
0
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());
}
Exemple #10
0
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;
}
Exemple #11
0
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;
			}
		}
	}
}
Exemple #12
0
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;
}
Exemple #13
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;
	}
}
Exemple #14
0
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;
}
Exemple #15
0
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();
}
Exemple #16
0
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;
}
Exemple #17
0
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;
}
Exemple #18
0
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 &currentNode = (*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 &current_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);
	}
}
Exemple #20
0
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);
}
Exemple #21
0
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);
}
Exemple #22
0
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());
		}
	}
}
Exemple #23
0
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);
}
Exemple #24
0
//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;
	}
Exemple #26
0
// 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 &current_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 &current_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);
	}
}
Exemple #28
0
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);
}