www.pudn.com > wow.rar > Combat.cpp
// Copyright (C) 2004 Team Python
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "Combat.h"
#include "NetworkInterface.h"
#include "Log.h"
#include "GameClient.h"
#include "Opcodes.h"
#include "Character.h"
#include "WorldServer.h"
#include "UpdateMask.h"
#include "Stats.h"
#include "math.h"
#define world WorldServer::getSingleton()
void CombatHandler::HandleMsg( wowWData & recv_data, GameClient *pClient )
{
wowWData data;
char f[256];
sprintf(f, "WORLD: Combat Opcode 0x%.4X from %u", recv_data.opcode, pClient->getCurrentChar()->getGUID());
Log::getSingleton( ).outString( f );
switch (recv_data.opcode)
{
case CMSG_ATTACKSWING:
{
uint32 mguid[2], pguid;
pguid = pClient->getCurrentChar()->getGUID();
recv_data >> mguid[0] >> mguid[1];
// AttackSwing
Log::getSingleton( ).outString( "WORLD: Recvd CMSG_ATTACKSWING Message" );
Unit *pEnemy;
if(world.mCreatures.find(mguid[0]) == world.mCreatures.end() ||
((pEnemy = world.mCreatures[mguid[0]])->getGUIDHigh() != mguid[1]))
{
Log::getSingleton( ).outString( "WORLD: Can't attack character" );
break; // we do not attack PCs for now
}
pClient->getCurrentChar()->addStateFlag(UF_ATTACKING);
pClient->getCurrentChar()->setCombat(true);
smsg_AttackStart(pClient->getCurrentChar(), pEnemy);
}break;
case CMSG_ATTACKSTOP:
{
uint32 mguid[2];
mguid[0] = *(pClient->getCurrentChar()->getSelectionPtr());
mguid[1] = *(pClient->getCurrentChar()->getSelectionPtr()+1);
smsg_AttackStop((Unit*)pClient->getCurrentChar(), mguid);
pClient->getCurrentChar()->setCombat(false);
pClient->getCurrentChar()->clearStateFlag(UF_ATTACKING);
// pClient->getCurrentChar()->removeUnitFlag(0x00080000);
}break;
}
}
//================================================================================================
// AttackerStateUpdate
// This function determines whether there is a hit, and the resultant damage
//================================================================================================
void CombatHandler::AttackerStateUpdate(Unit *pAttacker, Unit *pVictim, int32 damage)
{
if (pVictim->getUpdateValue(UNIT_FIELD_HEALTH) == 0 ||
pAttacker->getUpdateValue(UNIT_FIELD_HEALTH) == 0 )
return;
pVictim->setCombat(true);
pAttacker->setCombat(true);
wowWData data;
uint32 absorb = 0;
uint32 shield = 0;
uint32 mana_dmg = 0;
uint32 critiq = 0;
uint32 hit_status = 0xe2;
if(damage == 0)
{
damage = CalculateDamage(pAttacker);
// weaker attack for mobs lvl+ player
if (pAttacker->getUpdateValue(PLAYER_NEXT_LEVEL_XP) != NULL && damage) {
//printf("Ataque do PLAYER %u - Dmg: %u\n", pAttacker->getGUID() ,damage);
int lvl_diff = (pVictim->getUpdateValue(UNIT_FIELD_LEVEL) - pAttacker->getUpdateValue(UNIT_FIELD_LEVEL));
if (lvl_diff == 1) {
damage -= 2;
}
else if (lvl_diff == 2) {
damage = damage/2;
}
else if (lvl_diff >=2) {
damage = rand()%3;
}
// set player as mob 'owner'
uint32 hp = pVictim->getUpdateValue(UNIT_FIELD_HEALTH);
uint32 maxhp = pVictim->getUpdateValue(UNIT_FIELD_MAXHEALTH);
if ((hp < maxhp*.8) && (hp > maxhp*.5))
pVictim->setkillerGUID(pAttacker->getGUID());
// printf("\n\nuhuuuuu!!! attackerGUID: %u\n\n", pAttacker->getGUID());
// if (health < 80%) && (health > 50%)
// setkillerGUID(uint32 guid)
}
if (damage < 0) {
damage = 0;
}
//printf("Damage changed to %u\n", damage);
}// get absorb/shield stuff
if(pAttacker->getGUIDHigh() == 0 ) //if player
{
uint8 agi = pAttacker->getUpdateValue(UNIT_FIELD_STAT1);
// if( rand()%(agi) > (rand()%pAttacker->getLevel()+agi/2) ) critiq = pAttacker->getLevel()/2+1;
if ((agi) && ( rand()%(agi) > (rand()%pAttacker->getLevel()+agi/2))) damage *= 1.5;
// printf("Critical changed to %u\n", critiq);
}
if(pVictim->m_absorb == 1)
{
//printf("ok we will absorb some dmg\n");
SpellInformation spellInfo;
DatabaseInterface *dbi = Database::getSingleton().createDatabaseInterface(); //get a hook for the DB
spellInfo = dbi->GetSpellInformation ( pVictim->m_absorbspell ); //returns a SpellInformation object/struct
absorb = spellInfo.DmgPlus1;
Database::getSingleton().removeDatabaseInterface( dbi ); //clean up used resources
}
if(pVictim->m_shield == 1)
{
//printf("ok we will shield some dmg\n");
SpellInformation spellInfo;
DatabaseInterface *dbi = Database::getSingleton().createDatabaseInterface(); //get a hook for the DB
spellInfo = dbi->GetSpellInformation ( pVictim->m_shieldspell ); //returns a SpellInformation object/struct
shield = spellInfo.DmgPlus1;
Database::getSingleton().removeDatabaseInterface( dbi ); //clean up used resources
}
//START OF LINA
if(pVictim->getUpdateValue(UNIT_FIELD_RESISTANCES)>0)
{
uint32 armor=pVictim->getUpdateValue(UNIT_FIELD_RESISTANCES);
absorb += armor;
}
//END OF LINA
// absorb damage if damage absorb is activated
int32 absorbed=0;
if( absorb > 0 )
{
//absorbed = (uint32)((float)damage/float(100)*(float(100)-(float)absorb));
absorbed = (uint32)damage-absorb*absorb/2000;
if(absorbed < 0) //if armor absorbe all damage
{
absorb = damage-1; //absorb all - 1
absorbed = 1; //set receive damage to 1
}
else
{
absorb = damage-absorbed; //set absorb to the correct value
}
}
else //if no armor
{
absorbed=damage; //give full damage to user
}
/*
// absorb damage if damage absorb is activated
uint32 absorbed = (float)damage/float(100)*(float(100)-(float)absorb);
absorb = absorbed-damage;
if(absorbed > damage)
absorbed = 1;
if(absorb < 0)
absorb = 0;
*/
// use mana shield if activated
if(pVictim->m_shield)
{
uint32 mana = pVictim->getUpdateValue(UNIT_FIELD_POWER1);
if(mana < shield)
shield = pVictim->getUpdateValue(UNIT_FIELD_POWER1);
if(absorbed-shield > absorbed)
{// happens if shield is bigger then absorbed and that would make a char unplayable
mana_dmg = absorbed-1;
absorbed = 1;
absorb = mana_dmg;
//printf("#1 mana_dmg is %u and absorbed is %u\n", mana_dmg,absorbed);
}
else
{
absorbed = absorbed-shield;
mana_dmg = shield;
absorb = mana_dmg;
//printf("#2 mana_dmg is %u and absorbed is %u\n", mana_dmg,absorbed);
}
pVictim->setUpdateValue(UNIT_FIELD_POWER1,mana-mana_dmg);// if mana_dmg is >0 then absorb it or leave it as it is
}
/*
uint32 some_value = 0xffffffff;
some_value = 0x0;
*/
data.clear();
data.Initialise(61, SMSG_ATTACKERSTATEUPDATE);
data.writeData( hit_status ); // Attack flags
data.writeData( pAttacker->getGUID() );
data.writeData( pAttacker->getGUIDHigh() );
data.writeData( pVictim->getGUID() );
data.writeData( pVictim->getGUIDHigh() );
data.writeData( absorbed ); // damage
data.writeData( uint8(1) ); // Damage type counter
// for each...
data.writeData( uint32(0) ); // Damage type, // 0 - white font, 1 - yellow
data.writeData( uint32(0x0) ); // damage float
data.writeData( damage ); // Damage amount damage
data.writeData( absorb ); // damage absorbed
data.writeData( uint32(1) ); // new victim state
data.writeData( uint32(0) ); // victim round duration
data.writeData( uint32(0) ); // additional spell damage amount
data.writeData( uint32(0) ); // additional spell damage id
data.writeData( shield ); // Damage amount blocked
pAttacker->SendMessageToSet(&data, true);
//printf("AttackerStateUpdate: %u attacked %u for %u dmg-%u absorb=%u absorbed (shield %u).\n", pAttacker->getGUID(), pVictim->getGUID(), damage, absorb, absorbed, shield);
DealDamage(pAttacker, pVictim, absorbed);
}
void CombatHandler::smsg_AttackStop(Unit* pAttacker, uint32 victim_guid[2])
{
uint32 attacker_guid[2];
attacker_guid[0] = pAttacker->getGUID();
attacker_guid[1] = pAttacker->getGUIDHigh();
wowWData data;
data.Initialise( 20, SMSG_ATTACKSTOP );
data << attacker_guid[0] << attacker_guid[1] << victim_guid[0] << victim_guid[1] << uint32( 0 );
// world.SendUnitAreaMessage(&data, pAttacker);
pAttacker->SendMessageToSet(&data, true);
printf("%u stopped attacking %u\n", attacker_guid[0], victim_guid[0]);
// Changed by nothin
if(pAttacker->getUpdateValue(UNIT_FIELD_SUMMONEDBY) != 0)
{
pAttacker->m_creatureState = STOPPED;
}
}
void CombatHandler::smsg_AttackStart(Unit* pAttacker, Unit* pVictim)
{
//check if victim is close to attacker
float dx, dy, dz, length, reach, radius;
dx = pVictim->getPositionX() - pAttacker->getPositionX();
dy = pVictim->getPositionY() - pAttacker->getPositionY();
dz = pVictim->getPositionZ() - pAttacker->getPositionZ();
length = sqrt((dx*dx) + (dy*dy) + (dz*dz)); //distance between you and mob
radius = pVictim->getUpdateFloatValue(UNIT_FIELD_BOUNDINGRADIUS);
reach = pAttacker->getUpdateFloatValue(UNIT_FIELD_COMBATREACH);
if (length < reach + radius)
{
// Prevent user from ignoring attack speed and stopping and start combat really really fast
if (pAttacker->isAttackReady())
{
AttackerStateUpdate(pAttacker, pVictim, 0);
pAttacker->setAttackTimer();
}
// Send out ATTACKSTART
wowWData data;
data.clear();
data.Initialise( 16, SMSG_ATTACKSTART );
data << pAttacker->getGUID() << pAttacker->getGUIDHigh() << pVictim->getGUID() << pVictim->getGUIDHigh();
// world.SendUnitAreaMessage(&data, pAttacker); //probably global
pAttacker->SendMessageToSet(&data, true);
Log::getSingleton( ).outString( "WORLD: Sent SMSG_ATTACKSTART" );
}
else
{
pAttacker->clearStateFlag(UF_ATTACKING);
pAttacker->setCombat(false);
pVictim->setCombat(false);
}
// FLAGS changed so other players see attack animation
// pAttacker->addUnitFlag(0x00080000);
// pAttacker->setUpdateMaskBit(UNIT_FIELD_FLAGS );
}
void CombatHandler::DealDamage(Unit *pAttacker, Unit *pVictim, uint32 damage)
{
Unit * pPetMaster;
uint32 health = pVictim->getUpdateValue(UNIT_FIELD_HEALTH );
pAttacker->setCombat(true);
pVictim->setCombat(true);
if (health <= damage)
{
// printf("ok now he is dead\n");
pVictim->generateLoot();
// victim died!
pVictim->setDeathState(JUST_DIED);
// Send SMSG_PARTYKILLLOG 0x1e6
// To everyone in the party?
// SMSG_ATTACKSTOP
uint32 aguid[2], vguid[2];
aguid[0] = pAttacker->getGUID();
aguid[1] = pAttacker->getGUIDHigh();
vguid[0] = pVictim->getGUID();
vguid[1] = pVictim->getGUIDHigh();
smsg_AttackStop(pVictim, aguid);
// Send MSG_MOVE_ROOT 0xe7
// if player, dismount
if (pVictim->isPlayer()) {
wowWData data;
pVictim->setUpdateValue(UNIT_FIELD_MOUNTDISPLAYID , 0);
pVictim->removeUnitFlag( 0x003000 );
float dmspeed = 7.5; // Exact value of normal player speed
data.Initialise( 12, SMSG_FORCE_SPEED_CHANGE );
data << pVictim->getUpdateValue( OBJECT_FIELD_GUID );
data << pVictim->getUpdateValue( OBJECT_FIELD_GUID + 1 );
data << dmspeed;
pVictim->SendMessageToSet( &data, true );
}
// Set update values... try flags 917504
// health
pVictim->setUpdateValue(UNIT_FIELD_HEALTH, 0);
// pVictim->setUpdateValue(UNIT_FIELD_POWER1, 0);
// then another update message, sets health to 0, maxhealth to 100, and dynamic flags
pVictim->setUpdateMaskBit(UNIT_FIELD_MAXHEALTH);
// pVictim->setUpdateMaskBit(UNIT_FIELD_MAXPOWER1);
pVictim->removeUnitFlag(0x00080000);
if ( !pVictim->isPlayer() )
pVictim->setUpdateValue(UNIT_DYNAMIC_FLAGS, 1);
//LINA
if( pAttacker->isPet() ) //if pet kill someone, give xp to summoner
{
printf("It's a PET, master %u!\n", pAttacker->getUpdateValue(UNIT_FIELD_SUMMONEDBY) );
pPetMaster = world.GetCharacter( pAttacker->getUpdateValue(UNIT_FIELD_SUMMONEDBY) );
if(!pPetMaster)
{
pPetMaster=pAttacker;
}
smsg_AttackStop(pAttacker, vguid);
pAttacker->removeUnitFlag(0x00080000);
pAttacker->setUpdateMaskBit(UNIT_FIELD_FLAGS );
pAttacker->addStateFlag(UF_TARGET_DIED);
}
else
pPetMaster=pAttacker;
uint32 killerGUID = pVictim->getkillerGUID();
if ((killerGUID != pPetMaster->getGUID()) && killerGUID) {
//printf("Roubo de XP!! Quem ganha eh: %u, nao %u!\n", killerGUID, pPetMaster->getGUID());
WorldServer::ClientSet::iterator itr;
for (itr = world.mClients.begin(); itr != world.mClients.end(); itr++)
{
if (((GameClient*)(*itr))->IsInWorld()) {
if (((GameClient*)(*itr))->getCurrentChar()->getGUID() == killerGUID)
pPetMaster = ((GameClient*)(*itr))->getCurrentChar();
continue;
}
}
}
if (pPetMaster->isPlayer())
{
uint32 xp = CalculateXpToGive(pVictim, pPetMaster);
// check running quests in case this monster belongs to it
uint32 entry = 0;
if (!pVictim->isPlayer())
entry = pVictim->getUpdateValue(OBJECT_FIELD_ENTRY );
// Is this player part of a group? new by LeMMiNGS: only share xp with near party members, also no xp steal by group invite
if (((Character*)pPetMaster)->IsInGroup()) {
Group *pGroup = world.GetGroupByLeader(((Character*)pPetMaster)->GetGroupLeader());
uint32 higherlvl=pPetMaster->getLevel(), num=0;
for (uint32 c=0; c < pGroup->count; c++){
Character *pGroupGuy = world.mCharacters[pGroup->members[c].guid];
if ( (world.CalcDistance( (Unit *)pPetMaster, (Unit *)pGroupGuy) < UPDATE_DISTANCE) &&
(pPetMaster->getMapId() == (pGroupGuy->getMapId()))) {
if (pGroupGuy->getLevel()>higherlvl) {
higherlvl = pGroupGuy->getLevel();
xp = CalculateXpToGive(pVictim, pGroupGuy);
}
num++;
}
}
//xp /= pGroup->count;
xp /= num;
if (num >1)
xp *= 1.2;
for (uint32 c=0; c < pGroup->count; c++){
Character *pGroupGuy = world.mCharacters[pGroup->members[c].guid];
if ( (world.CalcDistance( (Unit *)pPetMaster, (Unit *)pGroupGuy) < UPDATE_DISTANCE) &&
(pPetMaster->getMapId() == (pGroupGuy->getMapId() ))) {
pGroupGuy->giveXP(xp, pVictim->getGUID(), pVictim->getGUIDHigh());
}
if (!pVictim->isPlayer())
pGroupGuy->KilledMonster(entry, pVictim->getGUID());
pGroupGuy->setCombat(false);
}
}
else
{
// update experience
((Character*)pPetMaster)->giveXP(xp, pVictim->getGUID(), pVictim->getGUIDHigh());
if (!pVictim->isPlayer())
((Character*)pPetMaster)->KilledMonster(entry, pVictim->getGUID());
pPetMaster->setCombat(false);
}
}
else
{
smsg_AttackStop(pAttacker, vguid);
pAttacker->removeUnitFlag(0x00080000);
pAttacker->setUpdateMaskBit(UNIT_FIELD_FLAGS );
pAttacker->addStateFlag(UF_TARGET_DIED);
//LINA
if( pVictim->isPet() )//if npc kill a pet, attack summoner
{
pPetMaster = world.GetCharacter( pVictim->getUpdateValue(UNIT_FIELD_SUMMONEDBY) );
if(pPetMaster)
{
pAttacker->AI_ChangeState(ATTACKING); // when attacked mobs stop moving around
pAttacker->AI_AttackReaction(pPetMaster, damage);
}
}
}
}
else
{
pVictim->setUpdateValue(UNIT_FIELD_HEALTH , health - damage);
// Start Change
// this need alot of work.
///////////////// PET ATTACK THE ONE WHO ATTACKS ITS MASTER by nothin/////////
if(pVictim->isPlayer())
{
if(pVictim->getUpdateValue(UNIT_FIELD_SUMMON) != 0)
{
petGUID = pVictim->getUpdateValue(UNIT_FIELD_SUMMON);
Unit * pPet=world.GetCreature(petGUID);
if(pPet)
{
if(pPet->m_pet_state != 0)
{
pPet->AI_ChangeState(ATTACKING);
pPet->AI_AttackReaction(pAttacker, damage);
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
else
{
pVictim->AI_ChangeState(ATTACKING); // when attacked mobs stop moving around
pVictim->AI_AttackReaction(pAttacker, damage);
/*
//uint32 max_health = getUpdateValue(UNIT_FIELD_MAXHEALTH);
//uint32 health_porcent = (max_health*10)/100; // this if for know 10% of total healt,need changes about mobs lvls
pVictim->AI_ChangeState(3); //if mob are attack then they stop moving around
pVictim->AI_AttackReaction(pAttacker, damage);
//well mobs scape if have a movement assignet atm
//if(health<=health_porcent)
{}
*/
}
// Start Change
}
}
/*
void CombatHandler::Heal(Unit *pAttacker, Unit *pVictim, uint32 damage)
{
uint32 health = pVictim->getUpdateValue(UNIT_FIELD_HEALTH );
if (health <= damage)
{
pVictim->generateLoot();
// victim died!
pVictim->setDeathState(JUST_DIED);
// Send SMSG_PARTYKILLLOG 0x1e6
// To everyone in the party?
// SMSG_ATTACKSTOP
uint32 aguid[2], vguid[2];
aguid[0] = pAttacker->getGUID();
aguid[1] = pAttacker->getGUIDHigh();
vguid[0] = pVictim->getGUID();
vguid[1] = pVictim->getGUIDHigh();
smsg_AttackStop(pVictim, aguid);
// Send MSG_MOVE_ROOT 0xe7
// Set update values... try flags 917504
// health
pVictim->setUpdateValue(UNIT_FIELD_HEALTH, 0);
// then another update message, sets health to 0, maxhealth to 100, and dynamic flags
pVictim->setUpdateValue(UNIT_FIELD_HEALTH, 0);
pVictim->setUpdateMaskBit(UNIT_FIELD_MAXHEALTH);
pVictim->removeUnitFlag(0x00080000);
if (!pVictim->isPlayer())
pVictim->setUpdateValue(UNIT_DYNAMIC_FLAGS, 1);
if (pAttacker->isPlayer()){
uint32 xp = CalculateXpToGive(pVictim, pAttacker);
// check running quests in case this monster belongs to it
uint32 entry = pVictim->getUpdateValue(OBJECT_FIELD_ENTRY );
// Is this player part of a group?
Group *pGroup = WorldServer::getSingleton().GetGroupByLeader(((Character*)pAttacker)->GetGroupLeader());
if (pGroup){
xp /= pGroup->count;
for (uint32 c=0; c < pGroup->count; c++){
Character *pGroupGuy = world.mCharacters[pGroup->members[c].guid];
pGroupGuy->giveXP(xp, pVictim->getGUID(), pVictim->getGUIDHigh());
pGroupGuy->KilledMonster(entry, pVictim->getGUID());
}
}
else
{
// update experience
((Character*)pAttacker)->giveXP(xp, pVictim->getGUID(), pVictim->getGUIDHigh());
((Character*)pAttacker)->KilledMonster(entry, pVictim->getGUID());
}
}
else
{
smsg_AttackStop(pAttacker, vguid);
pAttacker->removeUnitFlag(0x00080000);
pAttacker->setUpdateMaskBit(UNIT_FIELD_FLAGS );
pAttacker->addStateFlag(UF_TARGET_DIED);
}
} else {
pVictim->setUpdateValue(UNIT_FIELD_HEALTH , health + damage);
}
}
*/