PVP
Moderator: Team Members
-
bolly
- Retired
- Posts: 389
- Joined: Mon Sep 21, 2009 3:03 pm
- Location: Leeds, UK
PVP
Hey,
Since there is 1-20 content i've decided to throw up a level 1-20 pvp server. I've got the combat working by commenting out the IsPlayer() checks in attackallowed but I'm not sure where spells decide if it's a player or not.
Anyone know the function involved?
Cheers
Mix
Since there is 1-20 content i've decided to throw up a level 1-20 pvp server. I've got the combat working by commenting out the IsPlayer() checks in attackallowed but I'm not sure where spells decide if it's a player or not.
Anyone know the function involved?
Cheers
Mix
- John Adams
- Retired
- Posts: 9684
- Joined: Thu Jul 26, 2007 6:27 am
- EQ2Emu Server: EQ2Emulator Test Center
- Characters: John
- Location: Arizona
- Contact:
Re: PVP
I probably don't have to say this, but the code is nowhere near ready to support PVP, not properly. Sure you can slash at a player and cast a spell on them... but I don't think that's all there is to proper PVP.
Far as I know, the code decides if it's a player the same way it would decide if you can be attacked at all (not an enemy, or you cannot attack that?) maybe. Scat is gone for the week, so maybe when he gets back he can answer. Not sure if Zcoretri has messed with spells at a C++ level yet.
Far as I know, the code decides if it's a player the same way it would decide if you can be attacked at all (not an enemy, or you cannot attack that?) maybe. Scat is gone for the week, so maybe when he gets back he can answer. Not sure if Zcoretri has messed with spells at a C++ level yet.
- Scatman
- Retired
- Posts: 1688
- Joined: Wed Apr 16, 2008 5:44 am
- EQ2Emu Server: Scatman's Word
- Characters: Scatman
- Location: New Jersey
Re: PVP
I don't officially leave until tomorrow but I'm at a friend's now in Philly since we're leaving from philly airport. Spells and melee are different. Sounds like you got melee under control but spells would be in SpellProcess::ProcessSpell I think it is if I remember correctly. The logic for spells is much more advanced than melee but if you fool around with it you should be able to get it. Like John said, the server has 0 pvp support so you may be one-shotting players, or maybe doing 1 dmg, not sure
May be interesting to find out.
Oh, also check out SpellProcess::GetSpellTargets. I can more easily guide you when I get back.
Oh, also check out SpellProcess::GetSpellTargets. I can more easily guide you when I get back.
-
bolly
- Retired
- Posts: 389
- Joined: Mon Sep 21, 2009 3:03 pm
- Location: Leeds, UK
Re: PVP
Woop, found it in SpellProcess like you said!
I guess I need to do the racial checks now and also remove exp for pvp
But this should be fun for now!
One thing I did wonder about was having pvp as an option in the variables table and wrapping an if around these checks - will need to find where you are doing that atm and pop that in!
I guess I need to do the racial checks now and also remove exp for pvp
One thing I did wonder about was having pvp as an option in the variables table and wrapping an if around these checks - will need to find where you are doing that atm and pop that in!
Code: Select all
bool Combat::AttackAllowed(Entity* attacker, Spawn* victim, bool calculate_distance, bool range_attack){
if(!attacker || !victim || attacker->IsMezzedOrStunned() || attacker->IsDazed())
return false;
/* bolly - for pvp
if((attacker->IsPlayer() && victim->appearance.attackable == 0) || (attacker->IsPlayer() && victim->IsPlayer()))
return false;
*/
else if(victim->GetHP() == 0)
return false;
else if(calculate_distance){
float distance = attacker->GetDistance(victim);
distance -= victim->appearance.pos.collision_radius/10;
distance -= attacker->appearance.pos.collision_radius/10;
if(range_attack){
Item* weapon = 0;
Item* ammo = 0;
if(attacker->IsPlayer()){
weapon = ((Player*)attacker)->GetEquipmentList()->GetItem(EQ2_RANGE_SLOT);
ammo = ((Player*)attacker)->GetEquipmentList()->GetItem(EQ2_AMMO_SLOT);
}
if(weapon && weapon->IsRanged() && ammo && ammo->IsAmmo() && ammo->IsThrown()){
if(weapon->ranged_info->range_low <= distance && (weapon->ranged_info->range_high + ammo->thrown_info->range) >= distance)
return true;
}
}
else{
if(distance <= MAX_COMBAT_RANGE)
return true;
}
}
//todo: add more calculations
return true;
}
Code: Select all
void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell){
if(spell && caster){
Client* client = 0;
int8 target_type = spell->GetSpellData()->target_type;
if(caster->IsPlayer() && zone)
client = zone->GetClientBySpawn(caster);
if (caster->IsMezzedOrStunned() || caster->IsStifled()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST);
return;
}
if(caster->IsPlayer() && !IsReady(spell, caster)){
CheckSpellQueue(spell, caster);
return;
}
if (target_type != SPELL_TARGET_SELF && target_type != SPELL_TARGET_GROUP_AE && target_type != SPELL_TARGET_NONE && spell->GetSpellData()->max_aoe_targets == 0){
if (!target) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
if(caster->GetDistance(target) > spell->GetSpellData()->range) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_FAR_AWAY);
return;
}
if (caster->GetDistance(target) < spell->GetSpellData()->min_range) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_CLOSE);
return;
}
}
if(target_type == SPELL_TARGET_SELF && spell->GetSpellData()->max_aoe_targets == 0) {
if (harvest_spell) {
if (!target || !target->IsGroundSpawn()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
}
else
target = caster;
}
if (target_type == SPELL_TARGET_ENEMY && spell->GetSpellData()->max_aoe_targets == 0) {
if (spell->GetSpellData()->friendly_spell) {
if (!target->IsEntity()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
if (target->appearance.attackable) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
return;
}
}
else {
if (!target->IsEntity()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
if (caster == target || (!target->IsPlayer() && !target->appearance.attackable)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
return;
}
if (!target->Alive()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ALIVE);
return;
}
if (target->GetInvulnerable()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_TARGET_INVULNERABLE);
return;
}
/* bolly - pvp
if (target->IsPlayer() && caster->IsPlayer()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
return;
}
*/
}
}
LuaSpell* lua_spell = 0;
if(lua_interface)
lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
if(!lua_spell){
string lua_script = spell->GetSpellData()->lua_script;
lua_script.append(".lua");
lua_spell = 0;
if(lua_interface)
lua_spell = lua_interface->GetSpell(lua_script.c_str());
if(!lua_spell)
return;
else
spell->GetSpellData()->lua_script = lua_script;
}
lua_spell->caster = caster;
lua_spell->spell = spell;
lua_spell->initial_target = target;
GetSpellTargets(lua_spell);
if (lua_spell->targets.size() == 0 && spell->GetSpellData()->max_aoe_targets == 0) {
LogFile->write(EQEMuLog::Error, "SpellProcess::ProcessSpell Unable to find any spell targets for spell '%s'.", spell->GetName());
safe_delete(lua_spell);
return;
}
if (spell->GetSpellData()->cast_type == SPELL_CAST_TYPE_TOGGLE) {
bool ret_val = DeleteCasterSpell(lua_spell->caster, spell);
if (ret_val) {
int8 actual_concentration = spell->GetSpellData()->req_concentration / 256;
if (actual_concentration > 0) {
caster->GetInfoStruct()->cur_concentration -= actual_concentration;
if (caster->IsPlayer())
caster->GetZone()->TriggerCharSheetTimer();
}
CheckRecast(spell, caster);
SendSpellBookUpdate(client);
safe_delete(lua_spell);
return;
}
}
if(!CheckPower(lua_spell)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_POWER);
safe_delete(lua_spell);
return;
}
if (!CheckHP(lua_spell)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_HEALTH);
safe_delete(lua_spell);
return;
}
if (!CheckConcentration(lua_spell)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_CONC);
safe_delete(lua_spell);
return;
}
LockAllSpells(client);
SendStartCast(lua_spell, client);
if(spell->GetSpellData()->cast_time > 0){
CastTimer* cast_timer = new CastTimer;
cast_timer->entity_command = 0;
cast_timer->spell = lua_spell;
cast_timer->spell->caster = caster;
cast_timer->delete_timer = false;
cast_timer->timer = new Timer(spell->GetSpellData()->cast_time*10);
cast_timer->zone = zone;
cast_timers.Add(cast_timer);
if(caster)
caster->IsCasting(true);
}
else{
if(!CastProcessedSpell(lua_spell)) {
safe_delete(lua_spell);
return;
}
}
if(caster)
caster->GetZone()->SendCastSpellPacket(lua_spell, caster);
}
}- John Adams
- Retired
- Posts: 9684
- Joined: Thu Jul 26, 2007 6:27 am
- EQ2Emu Server: EQ2Emulator Test Center
- Characters: John
- Location: Arizona
- Contact:
Re: PVP
Yeah you can do that for now if you like. My long-term plan is for a Rules System like EQEmu has implemented. I think that'll be the easier solution down the road.
-
bolly
- Retired
- Posts: 389
- Joined: Mon Sep 21, 2009 3:03 pm
- Location: Leeds, UK
Re: PVP
Ok added the variable config - there wasn't a GetStartingCity/SetStartingCity so i added those to playerinfo and also WorldDatabase::loadCharacter but they are not provided here
Melee Checks
Spell Checks
Melee Checks
Code: Select all
bool Combat::AttackAllowed(Entity* attacker, Spawn* victim, bool calculate_distance, bool range_attack){
Variable* var = variables.FindVariable("server_type");
if (var)
{
sint16 ServerType = atoi(var->GetValue());
switch(ServerType)
{
case 2:
// can only attack different starting city players
if (attacker->IsPlayer() && victim->IsPlayer())
{
if (((Player*)attacker)->GetPlayerInfo()->GetStartingCity() == ((Player*)victim)->GetPlayerInfo()->GetStartingCity())
{
// same starting city so can't attack
return false;
}
}
break;
case 1:
// can only attack diff race
if (attacker->IsPlayer() && victim->IsPlayer() && (attacker->GetRace() == victim->GetRace()))
{
return false;
}
break;
// normal operation
default:
if((attacker->IsPlayer() && victim->appearance.attackable == 0) || (attacker->IsPlayer() && victim->IsPlayer()))
return false;
}
} else {
// normal operation (variable table entry missing)
if((attacker->IsPlayer() && victim->appearance.attackable == 0) || (attacker->IsPlayer() && victim->IsPlayer()))
return false;
}
if(!attacker || !victim || attacker->IsMezzedOrStunned() || attacker->IsDazed())
return false;
else if(victim->GetHP() == 0)
return false;
else if(calculate_distance){
float distance = attacker->GetDistance(victim);
distance -= victim->appearance.pos.collision_radius/10;
distance -= attacker->appearance.pos.collision_radius/10;
if(range_attack){
Item* weapon = 0;
Item* ammo = 0;
if(attacker->IsPlayer()){
weapon = ((Player*)attacker)->GetEquipmentList()->GetItem(EQ2_RANGE_SLOT);
ammo = ((Player*)attacker)->GetEquipmentList()->GetItem(EQ2_AMMO_SLOT);
}
if(weapon && weapon->IsRanged() && ammo && ammo->IsAmmo() && ammo->IsThrown()){
if(weapon->ranged_info->range_low <= distance && (weapon->ranged_info->range_high + ammo->thrown_info->range) >= distance)
return true;
}
}
else{
if(distance <= MAX_COMBAT_RANGE)
return true;
}
}
//todo: add more calculations
return true;
}Code: Select all
void SpellProcess::ProcessSpell(ZoneServer* zone, Spell* spell, Entity* caster, Spawn* target, bool lock, bool harvest_spell){
if(spell && caster){
Client* client = 0;
int8 target_type = spell->GetSpellData()->target_type;
if(caster->IsPlayer() && zone)
client = zone->GetClientBySpawn(caster);
if (caster->IsMezzedOrStunned() || caster->IsStifled()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_CANNOT_CAST);
return;
}
if(caster->IsPlayer() && !IsReady(spell, caster)){
CheckSpellQueue(spell, caster);
return;
}
if (target_type != SPELL_TARGET_SELF && target_type != SPELL_TARGET_GROUP_AE && target_type != SPELL_TARGET_NONE && spell->GetSpellData()->max_aoe_targets == 0){
if (!target) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
if(caster->GetDistance(target) > spell->GetSpellData()->range) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_FAR_AWAY);
return;
}
if (caster->GetDistance(target) < spell->GetSpellData()->min_range) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_TOO_CLOSE);
return;
}
}
if(target_type == SPELL_TARGET_SELF && spell->GetSpellData()->max_aoe_targets == 0) {
if (harvest_spell) {
if (!target || !target->IsGroundSpawn()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
}
else
target = caster;
}
if (target_type == SPELL_TARGET_ENEMY && spell->GetSpellData()->max_aoe_targets == 0) {
if (spell->GetSpellData()->friendly_spell) {
if (!target->IsEntity()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
if (target->appearance.attackable) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_A_FRIEND);
return;
}
}
else {
if (!target->IsEntity()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NO_ELIGIBLE_TARGET);
return;
}
if (caster == target || (!target->IsPlayer() && !target->appearance.attackable)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
return;
}
if (!target->Alive()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ALIVE);
return;
}
if (target->GetInvulnerable()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_TARGET_INVULNERABLE);
return;
}
Variable* var = variables.FindVariable("server_type");
if (var)
{
sint16 ServerType = atoi(var->GetValue());
switch(ServerType)
{
case 2:
// can only attack different starting city players
if (caster->IsPlayer() && target->IsPlayer())
{
if (((Player*)caster)->GetPlayerInfo()->GetStartingCity() == ((Player*)target)->GetPlayerInfo()->GetStartingCity())
{
// same starting city so can't attack
return;
}
}
break;
case 1:
// can only attack diff race
if (caster->IsPlayer() && target->IsPlayer() && (caster->GetRace() == target->GetRace()))
{
return;
}
break;
// normal operation
default:
// normal operation (variable table entry missing)
if (target->IsPlayer() && caster->IsPlayer()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
return;
}
}
} else {
// normal operation (variable table entry missing)
if (target->IsPlayer() && caster->IsPlayer()) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_AN_ENEMY);
return;
}
}
}
}
LuaSpell* lua_spell = 0;
if(lua_interface)
lua_spell = lua_interface->GetSpell(spell->GetSpellData()->lua_script.c_str());
if(!lua_spell){
string lua_script = spell->GetSpellData()->lua_script;
lua_script.append(".lua");
lua_spell = 0;
if(lua_interface)
lua_spell = lua_interface->GetSpell(lua_script.c_str());
if(!lua_spell)
return;
else
spell->GetSpellData()->lua_script = lua_script;
}
lua_spell->caster = caster;
lua_spell->spell = spell;
lua_spell->initial_target = target;
GetSpellTargets(lua_spell);
if (lua_spell->targets.size() == 0 && spell->GetSpellData()->max_aoe_targets == 0) {
LogFile->write(EQEMuLog::Error, "SpellProcess::ProcessSpell Unable to find any spell targets for spell '%s'.", spell->GetName());
safe_delete(lua_spell);
return;
}
if (spell->GetSpellData()->cast_type == SPELL_CAST_TYPE_TOGGLE) {
bool ret_val = DeleteCasterSpell(lua_spell->caster, spell);
if (ret_val) {
int8 actual_concentration = spell->GetSpellData()->req_concentration / 256;
if (actual_concentration > 0) {
caster->GetInfoStruct()->cur_concentration -= actual_concentration;
if (caster->IsPlayer())
caster->GetZone()->TriggerCharSheetTimer();
}
CheckRecast(spell, caster);
SendSpellBookUpdate(client);
safe_delete(lua_spell);
return;
}
}
if(!CheckPower(lua_spell)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_POWER);
safe_delete(lua_spell);
return;
}
if (!CheckHP(lua_spell)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_HEALTH);
safe_delete(lua_spell);
return;
}
if (!CheckConcentration(lua_spell)) {
zone->SendSpellFailedPacket(client, SPELL_ERROR_NOT_ENOUGH_CONC);
safe_delete(lua_spell);
return;
}
LockAllSpells(client);
SendStartCast(lua_spell, client);
if(spell->GetSpellData()->cast_time > 0){
CastTimer* cast_timer = new CastTimer;
cast_timer->entity_command = 0;
cast_timer->spell = lua_spell;
cast_timer->spell->caster = caster;
cast_timer->delete_timer = false;
cast_timer->timer = new Timer(spell->GetSpellData()->cast_time*10);
cast_timer->zone = zone;
cast_timers.Add(cast_timer);
if(caster)
caster->IsCasting(true);
}
else{
if(!CastProcessedSpell(lua_spell)) {
safe_delete(lua_spell);
return;
}
}
if(caster)
caster->GetZone()->SendCastSpellPacket(lua_spell, caster);
}
}- John Adams
- Retired
- Posts: 9684
- Joined: Thu Jul 26, 2007 6:27 am
- EQ2Emu Server: EQ2Emulator Test Center
- Characters: John
- Location: Arizona
- Contact:
Re: PVP
If this is something you want committed to the base code, please provide a DIFF file with your changes, and we'll test it out and commit.
If you're interested in becoming a C++ developer "officially"
let me know that, too.
If you're interested in becoming a C++ developer "officially"
-
bolly
- Retired
- Posts: 389
- Joined: Mon Sep 21, 2009 3:03 pm
- Location: Leeds, UK
Re: PVP
add starting_city to PlayerInfo::PlayerInfo(Player* in_player)
Player.h (playerinfo)
void SetStartingCity(int32 id);
int32 GetStartingCity();
Player.cpp (playerinfo)
int32 PlayerInfo::GetStartingCity(){
return starting_city;
}
void PlayerInfo::SetStartingCity(int32 id)
{
starting_city = id;
}
WorldDatabase.cpp (WorldDatabase::loadCharacter)
change query to
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type,instance_id,last_saved, DATEDIFF(curdate(), created_date) as accage, starting_city from characters where name='%s' and account_id=%i", query.escaped_name, account_id);
and add
client->GetPlayer()->GetPlayerInfo()->SetStartingCity(atoi(row[28]));
Player.h (playerinfo)
void SetStartingCity(int32 id);
int32 GetStartingCity();
Player.cpp (playerinfo)
int32 PlayerInfo::GetStartingCity(){
return starting_city;
}
void PlayerInfo::SetStartingCity(int32 id)
{
starting_city = id;
}
WorldDatabase.cpp (WorldDatabase::loadCharacter)
change query to
MYSQL_RES* result = query.RunQuery2(Q_SELECT, "SELECT id, current_zone_id, x, y, z, heading, admin_status, race, model_type, class, deity, level, gender, tradeskill_level, wing_type, hair_type, chest_type, legs_type, soga_wing_type, soga_hair_type, soga_chest_type, soga_legs_type, 0xFFFFFFFF - crc32(name), facial_hair_type, soga_facial_hair_type,instance_id,last_saved, DATEDIFF(curdate(), created_date) as accage, starting_city from characters where name='%s' and account_id=%i", query.escaped_name, account_id);
and add
client->GetPlayer()->GetPlayerInfo()->SetStartingCity(atoi(row[28]));
- John Adams
- Retired
- Posts: 9684
- Joined: Thu Jul 26, 2007 6:27 am
- EQ2Emu Server: EQ2Emulator Test Center
- Characters: John
- Location: Arizona
- Contact:
Re: PVP
Sorry, maybe I should have asked first, do you know what a "diff" file is? Using TortoiseSVN, you can right-click on the World folder, and "Create Patch", and it will compare your work with SVN's current, making a "differential" file with precise changes.
That's what I'm after, so we do not copy/paste or fat-finger code changes by pulling them off a forum post.
That's what I'm after, so we do not copy/paste or fat-finger code changes by pulling them off a forum post.
- John Adams
- Retired
- Posts: 9684
- Joined: Thu Jul 26, 2007 6:27 am
- EQ2Emu Server: EQ2Emulator Test Center
- Characters: John
- Location: Arizona
- Contact:
Re: PVP
Hmm, your own SVN? Is it current with ours? We'll have a look, but that might present other problems since your Rev will not match ours. Post it, we'll see how it goes. At least we can see the exact changes that way clearly.
Who is online
Users browsing this forum: No registered users and 0 guests