/* 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. depeloped with Main erose/hrose source server + some change from the original eich source */ #include "worldmonster.h" // called when a monster is attacked [attack/use atk skill/use buff/run/summon]//Enemy is the one attacking? bool CMonster::OnBeAttacked( CCharacter* Enemy ) { Battle->hitby = Enemy->clientid; if(!IsOnBattle( )) { CMap* map = GServer->MapList.Index[Position->Map]; map->UpdateSpawnCounter( montype, Status->spawnid, 3); // update the counters if(thisnpc->aggresive == 0) //prevent monsters with zero aggresion fighting back. return true; //StartAction( Enemy, NORMAL_ATTACK, 0 ); // temporary removal for testing } return true; } // called when a monster die [give exp/give extra drop] bool CMonster::OnDie( ) { /* Don't need this now. AIP takes care of it. CMap* map = GServer->MapList.Index[Position->Map]; if(map->ghost!=0) { if((map->IsNight( ) || map->ghost==2) && !IsGhost( ) && !IsGhostSeed( ) && !IsSummon( )) { UINT gs = GServer->RandNumber( 0, 100 ); if(gs<30) // 30% / 70% { // spawn a ghost seed [for now will be 701 [level 1 ghost seed] ] map->AddMonster( 701, Position->current, 0, Position->respawn, 0 ); } } }*/ GServer->GiveExp( this ); return true; } // called when we create a new monster [attack players?] bool CMonster::OnSpawn( bool Attack ) { if(IsGhost( ) && Attack ) { CPlayer* player = GetNearPlayer( ); if(player == NULL) // no players or too far return true; StartAction( (CCharacter*)player, NORMAL_ATTACK, 0 ); } return true; } // [not]called when a monster is almost dead [run/use only skills/call a healer? :S xD] bool CMonster::OnAlmostDie( ) { return true; } // called when a enemy is on sight bool CMonster::OnEnemyOnSight( CCharacter* Enemy ) { //return true //enable for AI stuff clock_t etime = clock() - lastSighCheck; if(etime<5000) return true; if(!IsOnBattle( )) { if(thisnpc->aggresive>1) { UINT aggro = GServer->RandNumber(2,15); if(thisnpc->aggresive>=aggro && !IsGhostSeed( )) { StartAction( (CCharacter*) Enemy, NORMAL_ATTACK, 0 ); } else if(IsGhostSeed( ) || thisnpc->aggresive>5) MoveTo( Enemy->Position->current, true ); } lastSighCheck = clock(); } return true; } // called when enemy die bool CMonster::OnEnemyDie( CCharacter* Enemy ) { Position->destiny = Position->source; ClearBattle( Battle ); MoveTo( Position->source ); return true; } bool CMonster::OnFar( ) { if(!IsSummon())return true; Position->destiny = Position->source; ClearBattle( Battle ); MoveTo( Position->source ); return true; } /*bool CMonster::Guardiantree(CMonster* monster,CMap* map) { return true; if(Stats->HP>9000)monster->hitcount=0; switch (monster->hitcount) { case 0: if(Stats->HP<9000) { for(unsigned char i=0;i<5;i++){ fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, 0, 10 ); newmonster->hitcount=0; } monster->hitcount=1; } break; case 1: if(Stats->HP<8000) { /* for(unsigned char i=0;i<5;i++){ fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=0; }*/ /*monster->hitcount=2; } break; case 2: if(Stats->HP<7000) { for(unsigned char i=0;i<5;i++){ fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=0; } monster->hitcount=3; } break; case 3: if(Stats->HP<6000) { /* for(unsigned char i=0;i<5;i++){ fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=0; }*/ /*monster->hitcount=4; } break; case 4: if(Stats->HP<5000) { for(unsigned char i=0;i<5;i++) { fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=0; } monster->hitcount=5; CDrop* thisdrop = new CDrop; assert(thisdrop); thisdrop->clientid = GServer->GetNewClientID(); thisdrop->type = 2; // ITEM thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 ); thisdrop->posMap = monster->Position->Map; thisdrop->droptime = time(NULL); thisdrop->amount = 1; thisdrop->item.count = thisdrop->amount; thisdrop->owner = 0; thisdrop->item.itemtype=10; thisdrop->item.itemnum=rand()%4+10; map->AddDrop( thisdrop ); } break; case 5: if(Stats->HP<4000) { /* for(unsigned char i=0;i<5;i++){ fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=0; }*/ /*monster->hitcount=6; } break; case 6: if(Stats->HP<3000) { for(unsigned char i=0;i<5;i++){ fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=0; } monster->hitcount=7; CDrop* thisdrop = new CDrop; assert(thisdrop); thisdrop->clientid = GServer->GetNewClientID(); thisdrop->type = 2; // ITEM thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 ); thisdrop->posMap = monster->Position->Map; thisdrop->droptime = time(NULL); thisdrop->amount = 1; thisdrop->item.count = thisdrop->amount; thisdrop->owner = 0; thisdrop->item.itemtype=10; thisdrop->item.itemnum=rand()%4+29; map->AddDrop( thisdrop ); } break; case 7: if(Stats->HP<2000) { /* for(unsigned char i=0;i<5;i++){ fPoint position = GServer->RandInCircle( monster->Position->current, 5 ); CMonster* newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=0; }*/ /*monster->hitcount=8; } break; default: monster->hitcount=9; } return true; } */ /*bool CMonster::Scarab(CMonster* monster,CMap* map) { return true; fPoint position; CMonster* othermon; CMonster* newmonster; if (monster->hitcount>5)monster->hitcount=0; switch (monster->hitcount) { case 0: position = GServer->RandInCircle( monster->Position->current, 1 ); newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=1; GServer->ClearClientID( monster->clientid ); for(UINT i=0;iMonsterList.size();i++) { othermon = map->MonsterList.at(i); if(othermon==monster) { map->MonsterList.erase( map->MonsterList.begin()+i ); delete monster; } } break; case 1: position = GServer->RandInCircle( monster->Position->current, 1 ); newmonster=map->AddMonster( 210, position, 0, NULL, NULL, 0, true ); newmonster->hitcount=2; GServer->ClearClientID( monster->clientid ); for(UINT i=0;iMonsterList.size();i++) { othermon = map->MonsterList.at(i); if(othermon==monster) { map->MonsterList.erase( map->MonsterList.begin()+i ); delete monster; } } break; case 2: CDrop* thisdrop = new CDrop; assert(thisdrop); thisdrop->clientid = GServer->GetNewClientID(); thisdrop->type = 2; // ITEM thisdrop->pos = GServer->RandInCircle( monster->Position->current, 3 ); thisdrop->posMap = monster->Position->Map; thisdrop->droptime = time(NULL); thisdrop->amount = rand()%3+1; thisdrop->item.count = thisdrop->amount; thisdrop->owner = 0; thisdrop->item.itemtype=12; thisdrop->item.itemnum=rand()%10+11; map->AddDrop( thisdrop ); GServer->ClearClientID( monster->clientid ); for(UINT i=0;iMonsterList.size();i++) { othermon = map->MonsterList.at(i); if(othermon==monster) { map->MonsterList.erase( map->MonsterList.begin()+i ); delete monster; } } break; } return true; }*/ bool CMonster::SummonUpdate(CMonster* monster, CMap* map, UINT j) { BEGINPACKET( pak, 0x799 ); clock_t etime = clock() - lastDegenTime; CPlayer* ownerclient = monster->GetOwner( ); if( etime >= 4 * CLOCKS_PER_SEC && Stats->HP > 0 ) { Stats->HP -= 4; lastDegenTime = clock(); if (Stats->HP < 1||ownerclient == NULL||ownerclient->IsDead())//orphan check { Log( MSG_INFO, "Deleting orphan summon %i (summonupdate)",monster->montype); ADDWORD ( pak, monster->clientid ); ADDWORD ( pak, monster->clientid ); ADDDWORD ( pak, 0x8005 ); GServer->SendToVisible( &pak, monster ); map->DeleteMonster( monster, true, j ); return true; } else if (montype > 800 && montype < 811) { /* CPlayer* ownerclient = monster->GetOwner( ); float distance = 0x9; for(UINT i=0;iMapList.Index[Position->Map]->PlayerList.size();i++) { CPlayer* thisclient = GServer->MapList.Index[Position->Map]->PlayerList.at(i); float tempdist = GServer->distance( Position->current, thisclient->Position->current ); if(tempdistStats->HP += ((((montype-800)*5)+((ownerclient->Stats->Level)/10))+10); thisclient->Stats->MP += (((montype-800)*5)+((ownerclient->Stats->Level)/25)); BEGINPACKET( pak, 0x7b2); ADDWORD ( pak, monster->clientid ); ADDWORD ( pak, 0x0b9d ); ADDBYTE ( pak, 0x06 ); thisclient->client->SendPacket(&pak); RESETPACKET( pak, 0x7b5); ADDWORD ( pak, thisclient->clientid ); ADDWORD ( pak, monster->clientid ); ADDWORD ( pak, 0x0b9d+0x3000+((ownerclient->Stats->Level%4)*0x4000) ); ADDBYTE ( pak, ownerclient->Stats->Level/4 ); thisclient->client->SendPacket(&pak); RESETPACKET( pak, 0x7b9); ADDWORD ( pak, monster->clientid ); ADDWORD ( pak, 0x0b9d ); thisclient->client->SendPacket(&pak); } }*/ } // SET MONSTER HEALTH //hopefully this will 100% control the health of summons from the server end BEGINPACKET( pak1, 0x79f ); ADDWORD( pak1, clientid ); ADDWORD( pak1, Stats->HP ); ADDWORD( pak1, 0 ); //thisclient->client->SendPacket( &pak ); GServer->SendToVisible( &pak1, monster ); } else if (Stats->HP < 1) { Log( MSG_INFO, "Deleting dead summon %i (summonupdate)",monster->montype); ADDWORD ( pak, monster->clientid ); ADDWORD ( pak, monster->clientid ); ADDDWORD ( pak, 0x8005 ); GServer->SendToVisible( &pak, monster ); map->DeleteMonster( monster, true, j ); } return true; } void CMonster::DoAi(int ainumber,char type)//ainumber is monster->AI type is add=0 idle=1 attacking=2 attacked=3 after killing target=4 hp<1=5 { CAip* script = NULL; int AIWatch = GServer->Config.AIWatch; int aiindex = (ainumber*0x10000)+(type*0x100); if(type == 5) { //Log(MSG_INFO,"Monster died. Activating AI type 5"); } for(unsigned j=0; j < GServer->AipList.size(); j++) { if (GServer->AipList.at(j)->AipID == aiindex) { script = GServer->AipList.at(j); //if(ainumber == AIWatch)Log(MSG_DEBUG, "Record count = %i",script->recordcount[type]); if(ainumber == AIWatch)Log(MSG_DEBUG, "aiCondition type: %i Index: %i condition count %i", type, aiindex, script->ConditionCount); int success = AI_SUCCESS; //needs to be AI_SUCCESS otherwise would not perform conditionless actions int thisaction = 0; for (dword i = 0; i < script->ConditionCount; i++) { int command = script->Conditions[i]->opcode; if (command > 30 || command < 0) continue; success = (*GServer->aiCondFunc[command])(GServer, this, script->Conditions[i]->data); if(ainumber == AIWatch)Log(MSG_DEBUG, "aiCondition %03u returned %d", command, success); if (success == AI_FAILURE) break; } if (success == AI_SUCCESS) { for (dword i = 0; i < script->ActionCount; i++) { int command = script->Actions[i]->opcode; if (command > 38 || command < 0) continue; success = (*GServer->aiActFunc[command])(GServer, this, script->Actions[i]->data); if(ainumber == AIWatch)Log(MSG_DEBUG, "aiAction: %03u returned %d", command, success); } if(success == AI_SUCCESS) return; //automatically return after performing the first successful action } aiindex++; } else if(GServer->AipList.at(j)->AipID > aiindex)return; } return; }