Page 1 of 2
PVP
Posted: Wed May 12, 2010 3:11 am
by bolly
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
Re: PVP
Posted: Wed May 12, 2010 6:29 am
by Eradani
cool
T1 Wardens are OP and T1 SKs suck
hehe
Re: PVP
Posted: Wed May 12, 2010 7:11 am
by John Adams
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.
Re: PVP
Posted: Wed May 12, 2010 12:07 pm
by bolly
okies, no rush, i'm wondering whether its related to the spell type
Re: PVP
Posted: Wed May 12, 2010 2:51 pm
by Scatman
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.
Re: PVP
Posted: Thu May 13, 2010 12:44 am
by bolly
<3!!!
Re: PVP
Posted: Thu May 13, 2010 3:55 am
by bolly
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!
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);
}
}
Re: PVP
Posted: Thu May 13, 2010 6:16 am
by bolly
go for an eqemu approach and use servertype?
I'm thinking:
0 - pve
1 - ffa pvp
2 - team pvp
Re: PVP
Posted: Thu May 13, 2010 6:45 am
by John Adams
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.
Re: PVP
Posted: Fri May 14, 2010 10:16 am
by bolly
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
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;
}
Spell Checks
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);
}
}
Re: PVP
Posted: Fri May 14, 2010 10:41 am
by John Adams
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.
Re: PVP
Posted: Fri May 14, 2010 11:15 am
by bolly
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]));
Re: PVP
Posted: Fri May 14, 2010 11:17 am
by John Adams
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.
Re: PVP
Posted: Fri May 14, 2010 11:22 am
by bolly
Using my own svn but I will run bash's diff against it 2 secs bud. I posted that before I read your reply!
Re: PVP
Posted: Fri May 14, 2010 11:45 am
by John Adams
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.