/* Rose Online Server Emulator Copyright (C) 2006,2007 OSRose Team http://www.osrose.net 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. developed with Main erose/hrose source server + some change from the original eich source */ #include "character.h" #include "worldserver.h" // return true if this character is monster bool CCharacter::IsMonster( ) { if(CharType == 2 || CharType == 3) return true; return false; } // return true if this character is player bool CCharacter::IsPlayer( ) { if(CharType == 1) return true; return false; } //return true if this character is an NPC bool CCharacter::IsNPC( ) { if(CharType == 4) return true; return false; } // return true if HP is lower to 1 bool CCharacter::IsDead( ) { if(Stats->HP <= 0) return true; return false; } // return true if this character is summon bool CCharacter::IsSummon( ) { if(this == NULL) return false; if(CharType == 3)// this line is erroring. No idea why return true; return false; } // Virtual function [return item drop] CDrop* CCharacter::GetDrop( ) { return NULL; } // start action [attack] void CCharacter::StartAction( CCharacter* Target, BYTE action, UINT skillid) { BEGINPACKET( pak, 0 ); switch(action) { case NORMAL_ATTACK: { //Log( MSG_INFO, "case NORMAL_ATTACK"); if(Target == NULL)return; RESETPACKET( pak, 0x798 ); ADDWORD ( pak, clientid ); ADDWORD ( pak, Target->clientid ); ADDWORD ( pak, Stats->Move_Speed ); ADDFLOAT ( pak, Target->Position->current.x * 100 ); ADDFLOAT ( pak, Target->Position->current.y * 100 ); Battle->target = Target->clientid; Battle->atktarget = Target->clientid; Battle->atktype = action; Battle->skilltarget = 0; Battle->bufftarget = 0; Battle->skillid = 0; Position->destiny = Target->Position->current; Position->lastMoveTime = clock(); } break; case SKILL_ATTACK: //Log( MSG_INFO, "case SKILL_ATTACK"); case SKILL_BUFF: { if(Target == NULL)return; RESETPACKET( pak, 0x7b3 ); ADDWORD ( pak, clientid ); ADDWORD ( pak, Target->clientid ); ADDWORD ( pak, skillid ); ADDWORD ( pak, 0x0000 ); ADDFLOAT ( pak, Target->Position->current.x * 100 ); ADDFLOAT ( pak, Target->Position->current.y * 100 ); Battle->target = Target->clientid; if(action == SKILL_ATTACK) { //Log( MSG_INFO, "case SKILL_ATTACK"); Battle->skilltarget = Target->clientid; Battle->bufftarget = 0; } else { //Log( MSG_INFO, "case SKILL_BUFF"); Battle->bufftarget = Target->clientid; Battle->skilltarget = 0; } Battle->atktype = action; Position->destiny = Target->Position->current; Battle->skillid = skillid; Position->lastMoveTime = clock(); } break; case SKILL_AOE://Log( MSG_INFO, "case SKILL_AOE"); case BUFF_SELF://Log( MSG_INFO, "case BUFF_SELF"); case BUFF_AOE: { //Log( MSG_INFO, "case BUFF_AOE"); RESETPACKET( pak, 0x7b2); ADDWORD ( pak, clientid ); ADDWORD ( pak, skillid ); Battle->atktype = action; Battle->skillid = skillid; //Log( MSG_INFO, "BUFF_AOE packet sent"); } break; case AOE_TARGET: { //Log( MSG_INFO, "case AOE_TARGET"); //Target is NULL for this function when cast by a player //Target is NOT NULL when it comes from a monster if(Target != NULL) //monster attack { Position->skilltargetpos.x = Target->Position->current.x; Position->skilltargetpos.y = Target->Position->current.y; } RESETPACKET( pak, 0x7b4 ); ADDWORD ( pak, clientid ); ADDWORD ( pak, skillid ); ADDFLOAT ( pak, Position->skilltargetpos.x * 100 ); ADDFLOAT ( pak, Position->skilltargetpos.y * 100 ); Battle->atktype = action; Battle->skillid = skillid; Battle->skilltarget = 0; Battle->bufftarget = 0; } break; default: return; break; } GServer->SendToVisible( &pak, this ); Battle->contatk = true; //Log(MSG_INFO,"Battle Attacktype = %i",Battle->atktype); return; } bool CCharacter::IsOnBattle( ) { if(Battle->atktype == 0) { if(Battle->iscasting == 1) { clock_t etime = clock() - Battle->lastAtkTime; if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed ) { //Log(MSG_DEBUG,"Iscasting is true while NOT in battle. Reset to false."); Battle->iscasting = 0; return false; } } return false; } switch(Battle->atktype) { case NORMAL_ATTACK: { if(Battle->atktarget != 0) return true; } break; case SKILL_ATTACK: case AOE_TARGET: { if(Battle->skilltarget != 0 && Battle->skillid != 0) return true; } break; case SKILL_BUFF: { if(Battle->bufftarget != 0 && Battle->skillid != 0) return true; } break; case SKILL_AOE: //case SKILL_SELF: //no such skill type case BUFF_SELF: case BUFF_AOE: { if(Battle->skillid != 0) return true; } break; default: return false; } } bool CCharacter::CanAttack( ) { clock_t etime = clock() - Battle->lastAtkTime; if (Stats->Attack_Speed < 1) { Log( MSG_INFO, "weapon with no attackspeed."); return false; } if(Battle->iscasting == 1) { if( etime < CLOCKS_PER_SEC * 25 / Stats->Attack_Speed ) //should make the delay 4 times longer { //if(IsPlayer()) // Log(MSG_INFO,"Cannot use magic attack yet. Last attack time = %i",Battle->lastAtkTime); return false; } else { Battle->iscasting = 0; //if(IsPlayer()) // Log(MSG_INFO,"OK Now we can use a new (magic or normal) attack."); return true; } } else //normal attack { if( etime < CLOCKS_PER_SEC * 100 / Stats->Attack_Speed ) { //if(IsPlayer()) // Log(MSG_INFO,"Cannot attack yet. Last attack time = %i",Battle->lastAtkTime); return false; } //if(IsPlayer()) // Log(MSG_INFO,"OK Now we can use a normal attack"); } return true; } // return true if target is reached bool CCharacter::IsTargetReached( CCharacter* Enemy, CSkills* skill ) { CMap* map = GServer->MapList.Index[Position->Map]; if(map == NULL)return false; float distance = GServer->distance( Position->current, Enemy->Position->current ); if(skill == NULL) //it's a normal attack { if(distance <= Stats->Attack_Distance) { return true; } else { Position->destiny = Enemy->Position->current; } } else { if(distance <= skill->range) { return true; } else { Position->destiny = Enemy->Position->current; } } //Log(MSG_INFO,"Monster cannot attack. Too far away"); return false; } // return true if the character is moving bool CCharacter::IsMoving( ) { if(Position->current.x == Position->destiny.x && Position->current.y == Position->destiny.y ) return false; return true; } // VIRTUAL [add player damage] void CCharacter::AddDamage( CCharacter* enemy, long int hitpower) { } // return character target CCharacter* CCharacter::GetCharTarget( ) { CCharacter* thistarget = GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->target ); if (thistarget != NULL) return thistarget; //if I don't currently have a target then return the character who just hit me return GServer->MapList.Index[Position->Map]->GetCharInMap( Battle->hitby ); } // VIRTUAL [reduce ammon/bullets/cannons] void CCharacter::ReduceABC( ) { } // VIRTUAL [get extra buff value] int CCharacter::GetEVBuff( ) { return 0; } // VIRTUAL [return intelligence] unsigned int CCharacter::GetInt( ) { return 15; } // Check For Debuffs void CCharacter::RefreshBuff( ) { bool bflag = false; for( UINT i=0;i<30;i++) { if(MagicStatus[i].Buff == 0) continue; clock_t etime = clock() - MagicStatus[i].BuffTime; if( etime >= MagicStatus[i].Duration * CLOCKS_PER_SEC ) { switch(MagicStatus[i].Status) { case 18: // attack power up case 19: // attack power down case 48: // attack power up if(i<15) Status->Attack_up = 0xff; else Status->Attack_down = 0xff; Stats->Attack_Power = GetAttackPower( ); break; case 20: // def up case 21: // def down case 49: // def up if(i<15) Status->Defense_up= 0xff; else Status->Defense_down = 0xff; Stats->Defense = GetDefense( ); break; case 24: //accuracy up case 25: //accuracy down case 51: //attack accuracy up. if(i<15) Status->Accury_up= 0xff; else Status->Accury_down = 0xff; Stats->Accury = GetAccury( ); break; case 22: // macic resistance up case 23: // magic resistance down case 50: // magic resistance up if(i<15) Status->Magic_Defense_up = 0xff; else Status->Magic_Defense_down = 0xff; Stats->Magic_Defense = GetMagicDefense( ); break; case 28: //dodge up case 29: //dodge down case 53: //dodge rate up if(i<15) Status->Dodge_up = 0xff; else Status->Dodge_down = 0xff; Stats->Dodge = GetDodge( ); break; case 14: //dash case 15: //slow case 46: //movement speed increased if(i<15) Status->Dash_up = 0xff; else Status->Dash_down = 0xff; Stats->Move_Speed = GetMoveSpeed( ); break; case 16: // haste attack case 17: // slow attack case 47: // attack speed up if(i<15) Status->Haste_up = 0xff; else Status->Haste_down = 0xff; Stats->Attack_Speed = GetAttackSpeed( ); break; case 26: // crit up case 27: // crit down case 52: // crit up if(i<15) Status->Critical_up = 0xff; else Status->Critical_down = 0xff; Stats->Critical = GetCritical( ); break; case 12: // max HP up case 44: // max HP up if(i<15) Status->HP_up = 0xff; else Status->HP_down = 0xff; Stats->MaxHP = GetMaxHP( ); break; case 13: // max MP up case 45: // max MP up if(i<15) Status->MP_up = 0xff; else Status->MP_down = 0xff; Stats->MaxMP = GetMaxMP( ); break; case 32: // faint Status->Stuned = 0xff; Status->CanAttack = true; //printf("removing stun\n"); break; case 7: case 8: case 9: case 10: case 11: //poisoned Status->Poisoned = 0xff; //printf("removing poison\n"); break; case 30: // muted Status->Muted = 0xff; Status->CanCastSkill = true; break; case 31: // sleep May need to be fixed later to accomodate multiple status effects. Status->Sleep = 0xff; Status->CanMove = true; Status->CanCastSkill = true; Status->CanAttack = true; break; case 54: //A_GMExtra_Damage: case 36: //A_Extra_Damage: if(i<15) { Status->ExtraDamage_up = 0xff; Stats->ExtraDamage = 0xff; } else { Status->ExtraDamage_down = 0xff; Stats->ExtraDamage = 0xff; } break; case 56: // Taunt Status->Taunt = 0xff; break; } MagicStatus[i].Status = 0; MagicStatus[i].Buff = 0; MagicStatus[i].BuffTime = 0; MagicStatus[i].Duration = 0; MagicStatus[i].Value = 0; bflag = true; } else if (MagicStatus[i].Status >= 7 && MagicStatus[i].Status <= 11 && etime > 1*CLOCKS_PER_SEC) //Do poison dmg every 1.5 seconds { Stats->HP -= MagicStatus[i].Status; //Actually take 7, 8, 9, 10 or 11 from the health. Based on the Status itself MagicStatus[i].BuffTime += 1*CLOCKS_PER_SEC; MagicStatus[i].Duration -= 1; //printf("did %i poison dmg to the player, still %i seconds and %i HP remain \n", MagicStatus[i].Status, MagicStatus[i].Duration, Stats->HP); //A bunch of messy code to send dmg packet BEGINPACKET( pak, 0x7b6 ); ADDWORD ( pak, clientid ); ADDWORD ( pak, 0 ); ADDDWORD ( pak, 0x000007f8 ); ADDBYTE ( pak, 0x00 ); ADDDWORD ( pak, MagicStatus[i].Status ); //If Enemy is killed if( IsDead()) { //printf("char died\n"); CDrop* thisdrop = NULL; ADDDWORD ( pak, 16 ); if( !IsSummon( ) && !IsPlayer( )) { thisdrop = GetDrop( ); if( thisdrop!=NULL) { ADDFLOAT ( pak, thisdrop->pos.x*100 ); ADDFLOAT ( pak, thisdrop->pos.y*100 ); if( thisdrop->type==1) { ADDDWORD( pak, 0xccccccdf ); ADDDWORD( pak, thisdrop->amount ); } else { ADDDWORD ( pak, GServer->BuildItemHead( thisdrop->item ) ); ADDDWORD ( pak, GServer->BuildItemData( thisdrop->item ) ); } ADDWORD ( pak, thisdrop->clientid ); ADDWORD ( pak, thisdrop->owner ); CMap* map = GServer->MapList.Index[thisdrop->posMap]; map->AddDrop( thisdrop ); } } GServer->SendToVisible( &pak, this, thisdrop ); } //If enemy is still alive else { ADDDWORD ( pak, 4 ); GServer->SendToVisible( &pak, this ); } } } if(bflag) { BEGINPACKET( pak,0x7b7 ); ADDWORD ( pak, clientid ); ADDDWORD ( pak, GServer->BuildBuffs( this ) ); GServer->SendToVisible( &pak, this ); } } // VIRTUAL [return party pointer] CParty* CCharacter::GetParty( ) { return NULL; } // VIRTUAL [return clan pointer] CLAN* CCharacter::GetClan( ) { return NULL; } bool CCharacter::IsAttacking( ) { if(IsOnBattle( )) { switch(Battle->atktype) { case SKILL_BUFF: case BUFF_SELF: case BUFF_AOE: return false; } } else return false; return true; }