PDA

View Full Version : Faction PvP Source Edits


Shin Noir
09-30-2009, 06:35 AM
I was working on a Good vs. Evil server and did some work on making a faction-based PvP system. I utilized a lot of the old VZTZ code to make it happen, and some of those changes will be included here.

This code is primarily about implementing faction based immunity mobs, and allowing players to attack each other if they're on a specific opposing faction. (If you are ally to this faction's mob, you can't hurt it). If I add or edit other info not related I'll note it.

On my code, I create it so that faction 500 is goodly, and faction 501 is evil. You can take this example and create more faction based PvP rules (like 3 factions etc).

I'm mainly sharing so it may be used somewhere, some time. It's based roughly on the latest revision as of this post (rev 999?) and some things may not work 100%.

aggro.cpp Ln 520

else if(our_owner && our_owner == target)
return false;

+ //CastToClient()->Message(15, "ATTACKFAIL" ); +//Shin: Debug line
+ if (IsClient() && target->IsNPC())
+ { //Shin: Can't attack ally faction monsters.
if ((target->GetPrimaryFaction() == 500 && CastToClient()->GetCharacterFactionLevel(500) > 1000)
||(target->GetPrimaryFaction() == 501 && CastToClient()->GetCharacterFactionLevel(501) > 1000))
{
CastToClient()->Message(10, "You can't attack this ally NPC!");
return false;
}
}
//Pet Check.

if (our_owner && our_owner->IsClient() && target->IsNPC())
{ //Shin: Pets can't attackeither.
if ((target->GetPrimaryFaction() == 500 && our_owner->CastToClient()->GetCharacterFactionLevel(500) > 1000)
||(target->GetPrimaryFaction() == 501 && our_owner->CastToClient()->GetCharacterFactionLevel(501) > 1000))
{
//our_owner->CastToClient()->Message(10, "Your pet can't attack this ally NPC!");
return false;
}
}

line 552 change from:
c1 = mob1->CastToClient();
c2 = mob2->CastToClient();

if // if both are pvp they can fight
(
c1->GetPVP() &&
c2->GetPVP()
)
return true;
To:

c1 = mob1->CastToClient();
c2 = mob2->CastToClient();
//Shin: vars for +/- 10 checks
int c1_level = c1->GetLevel(); // Level of Client 1
int c2_level = c2->GetLevel(); // Level of Client 2

int c1_guild = c1->GuildID();
int c2_guild = c2->GuildID();
//printf("c1: %i c2: %i", c1->GetCharacterFactionLevel(500),c2->GetCharacterFactionLevel(500)); //Shin: Debug line

if // TheLieka: If they are: Flagged for PvP; +/- 10 level; level 7+; not in the same guild: Allow Fight.
(
((c1_level + 10) >= c2_level) &&
((c2_level + 10) >= c1_level) &&
((c1_level >= 7) && (c2_level >= 7)) &&
//((c1_guild != c2_guild) || (c1_guild == GUILD_NONE) || (c1_guild == 0) || (c2_guild == GUILD_NONE) || (c2_guild == 0)
((c1->GetCharacterFactionLevel(500) > 1000 && c2->GetCharacterFactionLevel(501) > 1000) || (c1->GetCharacterFactionLevel(501) > 1000 && c2->GetCharacterFactionLevel(500) > 1000))
//&& c1->GetPVP() && //Shin: PvP status not needed for my server. Blue or Red Named you going to PVP.
//c2->GetPVP()
)
return true;

line 719 from:

c1 = mob1->CastToClient();
c2 = mob2->CastToClient();

if(c1->GetPVP() == c2->GetPVP())
return true;
to:

c1 = mob1->CastToClient();
c2 = mob2->CastToClient();
//int c1_guild = c1->GuildID(); //Shin: Spell +/- 10 lvl and >7 check code. TODO: Add a check to allow for non-heal beneficials?
//int c2_guild = c2->GuildID();
//int c1_level = c1->GetLevel(); // Level of Client 1
//int c2_level = c2->GetLevel(); // Level of Client 2
if ( //Lieka Begin Edit: Allow beneficial spells if Your target is in your guild or you are within 10 levels of your target.
((c1->GetCharacterFactionLevel(500) > 1000 && c2->GetCharacterFactionLevel(500) > 1000) || (c1->GetCharacterFactionLevel(501) > 1000 && c2->GetCharacterFactionLevel(501) > 1000))
//((c1_guild == c2_guild) && (c1_guild != GUILD_NONE) && (c1_guild != 0) && (c2_guild != GUILD_NONE) && (c2_guild != 0)) ||
//(((c1_level + 10) >= c2_level) && ((c2_level + 10) >= c1_level)) // && c1->GetPVP() == c2->GetPVP()) //Shin: No PVP flag needed
)
return true;

This one is just useful for PvP servers the big hit box of VZTZ:
line 847 from:

// this could still use some work, but for now it's an improvement....

if (size_mod > 29)
size_mod *= size_mod;
else if (size_mod > 19)
size_mod *= size_mod * 2;
else
size_mod *= size_mod * 4;
To:
// this could still use some work, but for now it's an improvement....

//if (size_mod > 29)
// size_mod *= size_mod;
//else if (size_mod > 19)
// size_mod *= size_mod * 2;
if(other->IsClient() && this->IsClient()) //Null: PvP size mod
size_mod *= size_mod * 6; //Shin: Just used the simplified hitbox of VZTZ's source
else
size_mod *= size_mod * 4;


attack.cpp line 41 from:

#include "../common/MiscFunctions.h"
#include "../common/rulesys.h"

#ifdef WIN32
#define snprintf _snprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif

extern EntityList entity_list;

To:

#include "../common/MiscFunctions.h"
#include "../common/rulesys.h"
#include "guild_mgr.h" //Shin: Added so guild ranks can be shown on PvP kills.
#include "../common/misc.h" //Shin: This is for long2ip conversions on guild rank wiretap recording.
#include "worldserver.h" //Shin: For Worldserver Emote Messages on PVP kills.
#include "faction.h" //Shin: For faction checks on alignment with NPCs etc.

#ifdef WIN32
#define snprintf _snprintf
#define strncasecmp _strnicmp
#define strcasecmp _stricmp
#endif

extern WorldServer worldserver; //Shin For Emote Messages.

line 676 from:
if(against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){
return 0;
}

//check to see if our weapons or fists are magical.

to:
if(against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){
return 0;
}

//CastToClient()->Message(15, "WEAPDMG" ); //Shin: Debug line
if (IsClient())
{ //Shin: Can't attack ally faction monsters.
if ((against->GetPrimaryFaction() == 500 && CastToClient()->GetCharacterFactionLevel(500) > 1000)
||(against->GetPrimaryFaction() == 501 && CastToClient()->GetCharacterFactionLevel(501) > 1000))
{
CastToClient()->Message(10, "You can't attack this ally NPC!");
return 0;
}
}
//check to see if our weapons or fists are magical.
line 782 from:
if(against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){
return 0;
}

//check for items being illegally attained
To:
if(against->GetInvul() || against->SpecAttacks[IMMUNE_MELEE]){
return 0;
}

//CastToClient()->Message(15, "WEAPDMGitem" ); //Shin: Debug line
if (IsClient())
{ //Shin: Can't attack ally faction monsters.
if ((against->GetPrimaryFaction() == 500 && CastToClient()->GetCharacterFactionLevel(500) > 1000)
||(against->GetPrimaryFaction() == 501 && CastToClient()->GetCharacterFactionLevel(501) > 1000))
{
CastToClient()->Message(10, "You can't attack this ally NPC!");
return 0;
}
}

//check for items being illegally attained

line 1032 from:
if (GetFeigned())
return false; // Rogean: How can you attack while feigned? Moved up from Aggro Code.


ItemInst* weapon;
To:

if (GetFeigned())
return false; // Rogean: How can you attack while feigned? Moved up from Aggro Code.
if (IsClient())
{ //Shin: Can't attack ally faction monsters.
if ((other->CastToNPC()->GetPrimaryFaction() == 500 && CastToClient()->GetCharacterFactionLevel(500) > 1000)
||(other->CastToNPC()->GetPrimaryFaction() == 501 && CastToClient()->GetCharacterFactionLevel(501) > 1000))
{
CastToClient()->Message(10, "You can't attack this ally NPC!");
return 0;
}
}

ItemInst* weapon;


This one modifies the PVP mitigation damage to be lesser amounts, directly from VZTZ.
line 1295 from:
if(other && other->IsClient() && (other != this) && damage > 0) {
int PvPMitigation = 100;
if(attack_skill == ARCHERY)
PvPMitigation = 80;
else
PvPMitigation = 67;
damage = (damage * PvPMitigation) / 100;
}
To:
if(other && other->IsClient() && (other != this) && damage > 0) {
int PvPMitigation = 75; //Shin: From 100 to 75 on VZTZ
if(attack_skill == ARCHERY)
PvPMitigation = 60; //Shin: From 80 to 60 on VZTZ
//else //Shin: 75 is used for all non-archery.
// PvPMitigation = 67;
//this->Message(15, "PvP Mitigation = %i", PvPMitigation); //Shin: Debug
damage = (damage * PvPMitigation) / 100;
}

This is the PVP score announcement system from VZTZ. Line 1377 from:
dead_timer.Start(5000, true);

if (killerMob != NULL)
{
if (killerMob->IsNPC())
parse->Event(EVENT_SLAY, killerMob->GetNPCTypeID(), 0, killerMob->CastToNPC(), this);

if(killerMob->IsClient() && (IsDueling() || killerMob->CastToClient()->IsDueling())) {
To:

dead_timer.Start(5000, true);
float pvp_points = 6.0; //Shin: PvP score system

if (killerMob != NULL)
{
int linkBuffSlot = killerMob->GetBuffSlotFromType(SE_DamageRedirect);
//if(linkBuffSlot > 0 && linkBuffSlot < 26) { //Shin: TODO: Figure out how to get DropDelayTarget working in 0.8.0
// killerMob->DropDelayTarget(linkBuffSlot);
//}
if (killerMob->IsNPC())
parse->Event(EVENT_SLAY, killerMob->GetNPCTypeID(), 0, killerMob->CastToNPC(), this);
//Lieka Edit: PvP death message
if(killerMob->IsPet() && killerMob->GetOwner()->IsClient()) //Null: display message if pet killed player
killerMob = killerMob->GetOwner();
if(killerMob->IsClient()) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = 0;
if (killerMob->GetName() == this->GetName()) { //Self Death
if (guild_mgr.GetGuildName(this->GuildID()) == "") { //Self Not Guilded
database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO wiretaps(_from, _to, message, from_ip) VALUES ('%s', '%s', 'PvP: %s killed by %s in %s!', '%s');", killerMob->GetName(), this->GetName(), this->GetName(),killerMob->GetName(), zone->GetShortName(), long2ip(killerMob->CastToClient()->GetIP())), errbuf);
worldserver.SendEmoteMessage(0,0,0,15,"PvP News: %s [%d] have killed themselves in %s!",this->GetName(),this->GetLevel(), zone->GetLongName());
} else { //Self Guilded
database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO wiretaps(_from, _to, message, from_ip) VALUES ('%s', '%s', 'PvP: %s <%s> killed by %s <%s> in %s!', '%s');", killerMob->GetName(), this->GetName(), this->GetName(), guild_mgr.GetGuildName(this->GuildID()), killerMob->GetName(),guild_mgr.GetGuildName((killerMob->CastToClient())->GuildID()), zone->GetShortName(), long2ip(killerMob->CastToClient()->GetIP()).c_str()), errbuf);
worldserver.SendEmoteMessage(0,0,0,15,"PvP News: %s <%s> [%d] have killed themselves in %s!",this->GetName(),guild_mgr.GetGuildName(this->GuildID()), this->GetLevel(), zone->GetLongName());
}
} else { //killerMob Killer
if (guild_mgr.GetGuildName(this->GuildID()) == "") { //Self Not Guilded
if (guild_mgr.GetGuildName(killerMob->CastToClient()->GuildID()) == "") { //Killer Not Guilded
database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO wiretaps(_from, _to, message, from_ip) VALUES ('%s', '%s', 'PvP: %s killed by %s in %s!', '%s');", killerMob->GetName(), this->GetName(), this->GetName(),killerMob->GetName(), zone->GetShortName(), long2ip(killerMob->CastToClient()->GetIP()).c_str()), errbuf);
worldserver.SendEmoteMessage(0,0,0,15,"PvP News: %s [%d] has been slain by %s [%d] in %s!",this->GetName(), this->GetLevel(), killerMob->GetName(), killerMob->GetLevel(), zone->GetLongName());
} else { //Killer Guilded
database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO wiretaps(_from, _to, message, from_ip) VALUES ('%s', '%s', 'PvP: %s killed by %s <%s> in %s!', '%s');", killerMob->GetName(), this->GetName(), this->GetName(),killerMob->GetName(),guild_mgr.GetGuildName((killerMob->CastToClient())->GuildID()), zone->GetShortName(), long2ip(killerMob->CastToClient()->GetIP()).c_str()), errbuf);
worldserver.SendEmoteMessage(0,0,0,15,"PvP News: %s [%d] has been slain by %s <%s> [%d] in %s!",this->GetName(),this->GetLevel(), killerMob->GetName(), killerMob->GetLevel(), guild_mgr.GetGuildName((killerMob->CastToClient())->GuildID()),zone->GetLongName());
}
} else { //Self Guilded
if (guild_mgr.GetGuildName(this->GuildID()) == "") { //Killer Not Guilded
database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO wiretaps(_from, _to, message, from_ip) VALUES ('%s', '%s', 'PvP: %s <%s> killed by %s in %s!', '%s');", killerMob->GetName(), this->GetName(), this->GetName(), guild_mgr.GetGuildName(this->GuildID()), killerMob->GetName(), zone->GetShortName(), long2ip(killerMob->CastToClient()->GetIP()).c_str()), errbuf);
worldserver.SendEmoteMessage(0,0,0,15,"PvP News: %s <%s> [%d] has been slain by %s [%d] in %s!",this->GetName(),guild_mgr.GetGuildName(this->GuildID()),this->GetLevel(), killerMob->GetName(), killerMob->GetLevel(), zone->GetLongName());
} else { //Killer Guilded
database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO wiretaps(_from, _to, message, from_ip) VALUES ('%s', '%s', 'PvP: %s <%s> killed by %s <%s> in %s!', '%s');", killerMob->GetName(), this->GetName(), this->GetName(), guild_mgr.GetGuildName(this->GuildID()), killerMob->GetName(),guild_mgr.GetGuildName((killerMob->CastToClient())->GuildID()), zone->GetShortName(), long2ip(killerMob->CastToClient()->GetIP()).c_str()), errbuf);
worldserver.SendEmoteMessage(0,0,0,15,"PvP News: %s <%s> [%d] has been slain by %s <%s> [%d] in %s!",this->GetName(),guild_mgr.GetGuildName(this->GuildID()),this->GetLevel(), killerMob->GetName(),guild_mgr.GetGuildName((killerMob->CastToClient())->GuildID()), killerMob->GetLevel(), zone->GetLongName());
}
}
}
if(this->GetLevel() >= 50 && killerMob->GetLevel() >= 50 && !zone->IsCity())
{ //Shin: Do PvP point update
if (this->GetName() == killerMob->GetName()) {
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET pvp_points = (pvp_points - 6) WHERE id = %i;", this->CharacterID()), errbuf);
}else{
if(killerMob->IsGrouped() && pvp_points != 1) {
Group* pvp_group = entity_list.GetGroupByClient(killerMob->CastToClient());
if(pvp_group != 0) {
pvp_points = pvp_points / pvp_group->GroupCount();
for(int i=0;i<6;i++) { // Doesnt work right, needs work
if(pvp_group->members[i] != NULL) {
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET pvp_points = (pvp_points + %f) WHERE id = %i;", pvp_points, pvp_group->members[i]->CastToClient()->GuildID()), errbuf);
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET pvp_points = (pvp_points + %f) WHERE id = %i;", pvp_points, pvp_group->members[i]->CastToClient()->CharacterID()), errbuf);
}
}

database.RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET pvp_points = (pvp_points - 6) WHERE id = %i;", this->GuildID()), errbuf);
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET pvp_points = (pvp_points - 6) WHERE id = %i;", this->CharacterID()), errbuf);
}
}else{
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET pvp_points = (pvp_points + %f) WHERE id = %i;", pvp_points, killerMob->CastToClient()->GuildID()), errbuf);
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET pvp_points = (pvp_points + %f) WHERE id = %i;",pvp_points, killerMob->CastToClient()->CharacterID()), errbuf);
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET pvp_points = (pvp_points - %f) WHERE id = %i;",pvp_points, this->GuildID()), errbuf);
database.RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET pvp_points = (pvp_points - %f) WHERE id = %i;",pvp_points, this->CharacterID()), errbuf);

}
}
}
safe_delete_array(query);
//End Lieka Edit
} //Shin: This nested bracket may belong somewhere else..! Figure it out!

if(killerMob->IsClient() && (IsDueling() || killerMob->CastToClient()->IsDueling())) {

Antoher PVP mitigation boost from vZTZ line 1761 from:
if(other->IsClient() && IsPet() && GetOwner()->IsClient()) {
//pets do half damage to clients in pvp
damage=damage/2;
}
}
To:
if(other->IsClient() && IsPet() && GetOwner()->IsClient()) {
//Shin: Pets do 66% damage to clients in PvP, not half
damage=damage/1.5;
}
}

Line 2146 from if(IsFamiliar() || SpecAttacks[IMMUNE_AGGRO])
return;

if (other == myowner)
return;

To:
if(IsFamiliar() || SpecAttacks[IMMUNE_AGGRO])
return;

//CastToClient()->Message(15, "AGGROCHECK" ); //Shin: Debug line
if (other->IsClient())
{ //Shin: Can't attack ally faction monsters.
if ((GetPrimaryFaction() == 500 && other->CastToClient()->GetCharacterFactionLevel(500) > 1000)
||(GetPrimaryFaction() == 501 && other->CastToClient()->GetCharacterFactionLevel(501) > 1000))
{
other->CastToClient()->Message(10, "You can't attack this ally NPC!");
return;
}
}
if (other == myowner)
return;

This is a "no anon allowed" fix from VZTZ so players can't hide:
client.cpp line 1372 from:
if (m_pp.anon == 0)
scl->anon = 0;
else if (m_pp.anon == 1)
scl->anon = 1;
else if (m_pp.anon >= 2)
scl->anon = 2;

scl->tellsoff = tellsoff;
To:
if (m_pp.anon == 0)
scl->anon = 0;
else if (m_pp.anon == 1 && (Admin() >= 40))
scl->anon = 1; //Shin: This is a Lieka hack to make anon characters not anon.
else if (m_pp.anon >= 2 && (Admin() >= 40))
scl->anon = 2;
else if (m_pp.anon == 1 && (Admin() < 40))
scl->anon = 0;
else if (m_pp.anon >= 2 && (Admin() < 40))
scl->anon = 0;

scl->tellsoff = tellsoff;

This is a custom con system I wrote, to make allied and enemy faction players con like this: http://img27.imageshack.us/img27/1503/newconsystem.jpg
line 2209 of client_packet.cpp from:

if(tmob->CastToNPC()->IsOnHatelist(this))
con->faction = FACTION_THREATENLY;
}
}

QueuePacket(outapp);
to:
if(tmob->CastToNPC()->IsOnHatelist(this))
con->faction = FACTION_THREATENLY;
}
}

if (tmob->IsClient() && IsClient())
{ //Shin: Allies and Enemies of Players con differently, use ally and glare and con system +/-10.
printf("Faction of tmob %i and source mob %i\n", tmob->CastToClient()->GetCharacterFactionLevel(500), GetCharacterFactionLevel(500));
if ((tmob->CastToClient()->GetCharacterFactionLevel(500) > 1000 && GetCharacterFactionLevel(500) > 1000)
||(tmob->CastToClient()->GetCharacterFactionLevel(501) > 1000 && GetCharacterFactionLevel(501) > 1000))
{ //Ally
if (tmob->GetLevel() < 8) //Green con for non-PvP players.
Message(clientMessageLoot, "%s regards you a sworn ally -- Their class is %s.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() > (GetLevel()+10)) //Red con (over 10 levels)
Message(clientMessageError, "%s regards you a sworn ally -- Their class is %s.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() < (GetLevel()-10)) //Green con (<-10)
Message(clientMessageLoot, "%s regards you a sworn ally -- Their class is %s.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() > GetLevel()) //Yellow barely above con (<11, > same)
Message(clientMessageYellow, "%s regards you a sworn ally -- Their class is %s.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() < GetLevel()) //Blue in range con
Message(clientMessageTradeskill, "%s regards you a sworn ally -- Their class is %s.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else //Otherwise just be white.
Message(clientMessageWhite0, "%s regards you a sworn ally -- Their class is %s.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
}
else
{ //Enemy
if (tmob->GetLevel() < 8) //Green con for non-PvP players
Message(clientMessageLoot, "%s regards you a sworn enemy -- They are too low to battle with.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() > (GetLevel()+10)) //Red con
Message(clientMessageError, "%s regards you a sworn enemy -- They are too high to battle with.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() < (GetLevel()-10)) //Green con
Message(clientMessageLoot, "%s regards you a sworn enemy -- They are too low to battle with.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() > GetLevel()) //Yellow barely above con
Message(clientMessageYellow, "%s regards you a sworn enemy -- looks like quite a gamble.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else if (tmob->GetLevel() < GetLevel()) //Blue in range con
Message(clientMessageTradeskill, "%s regards you a sworn enemy -- You could probably win this fight.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
else //Otherwise just be white.
Message(clientMessageWhite0, "%s regards you a sworn enemy -- looks like a perfect match.", tmob->GetCleanName(), GetEQClassName(tmob->GetClass()));
}
safe_delete(outapp);
return;
}
QueuePacket(outapp);

I think that's about it. I may have missed some code above, if so let me know. I have a rev here as latest but it has a lot of other play around code (I was trying to add a custom class etc):
http://code.google.com/p/provztz/updates/list

I was also adding a red and blue "name" system and it worked, but I needed to finish up the spawn packet sending data to make it 100%.
Anyways, enjoy. Let me know if it works for you.