// Props to ExJam for this code :D #include "../WorldServer.h" // extern CRandomMersenne rg; //Stop all actions AIACT(000) { //SetCMD_Stop (Stop all actions i guess?) //entity->StopAll(); CMonster* monster = reinterpret_cast(entity); ClearBattle( monster->Battle ); monster->Position->destiny = monster->Position->current; return AI_SUCCESS; } //Do Action AIACT(001) { //byte cAction; //Pos: 0x00 //CObjMOB::Set_EMOTION (pak 781) GETAIACTDATA(001); BEGINPACKET( pak, 0x781 ); ADDWORD ( pak, data->cAction ); ADDWORD ( pak, 0); ADDWORD ( pak, entity->clientid ); GServer->SendToVisible(&pak, entity,NULL ); return AI_SUCCESS; } //Say LTB String AIACT(002) { //Client side say text //Log(MSG_DEBUG, "Say LTB String"); //dword iStrID; Pos: 0x00 GETAIACTDATA(002); //Think this is crashing the server. Taken out for the time being. //lets have the NPC also say the LTB string over the regular message system. This will show up green ^-^ CMonster* thisMonster = reinterpret_cast(entity); //if(thisMonster->CharType == TNPC) // check if it is an NPC and not a normal monster. //{ BEGINPACKET( pak, 0x0784 ); ADDSTRING( pak, GServer->Ltbstring[data->iStrID].NPCname ); ADDBYTE( pak, 0 ); ADDSTRING( pak, GServer->Ltbstring[data->iStrID].LTBstring ); ADDBYTE( pak, 0 ); GServer->SendToVisible(&pak, thisMonster); //} return AI_SUCCESS; } //Move (walk or run) to a random location within iDistance of my present location AIACT(003) { //dword iDistance; //Pos: 0x00 //byte cSpeed; //Pos: 0x04 //move randomly within iDistance use randincircle GETAIACTDATA(003); CMonster* monster = reinterpret_cast(entity); monster->UpdatePosition( ); int iDist = data->iDistance;// * 2;//Get it to our coord system! entity->Position->destiny = GServer->RandInCircle(entity->Position->current, iDist); //int randDis = rand()%(iDist << 1); //float nX = (entity->Position->current.x + randDis) - iDist; //entity->Position->destiny.x=nX; //randDis = rand()%(iDist << 1); //float nY = (entity->Position->current.y + randDis) - iDist; //entity->Position->destiny.y = nY; monster->Stats->stance = data->cSpeed; monster->SetStats(); entity->Position->lastMoveTime = clock(); entity->Battle->atktype=0; BEGINPACKET( pak, 0x797 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, 0x0000 ); ADDWORD ( pak, entity->Stats->Move_Speed ); //speed ADDFLOAT ( pak, entity->Position->destiny.x*100 ); ADDFLOAT ( pak, entity->Position->destiny.y*100 ); ADDWORD ( pak, 0xcdcd ); ADDBYTE ( pak, monster->Stats->stance ); GServer->SendToVisible(&pak, entity); return AI_SUCCESS; } //Move (walk or run) to a random location within iDistance of my spawn location AIACT(004) { //dword iDistance; //Pos: 0x00 //byte cSpeed; //Pos: 0x04 //move randomly within iDistance of spawn position GETAIACTDATA(004); CMonster* monster = reinterpret_cast(entity); monster->UpdatePosition( ); int iDist = data->iDistance;//Get it to our coord system! entity->Position->destiny = GServer->RandInCircle(entity->Position->source, iDist); //int randDis = rand()%(iDist << 1); //float nX = (monster->Position->source.x + randDis) - iDist; //monster->Position->destiny.x=nX; //randDis = rg.IRandom(0, iDist << 1); //randDis = rand()%(iDist << 1); //float nY = (monster->Position->source.y + randDis) - iDist; //monster->Position->destiny.y=nY; monster->Stats->stance = data->cSpeed; monster->SetStats(); monster->Position->lastMoveTime = clock(); entity->Battle->atktype = 0; BEGINPACKET( pak, 0x797 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, 0x0000 );//??? ADDWORD ( pak, monster->Stats->Move_Speed ); //speed ADDFLOAT ( pak, monster->Position->destiny.x*100 ); ADDFLOAT ( pak, monster->Position->destiny.y*100 ); ADDWORD ( pak, 0xcdcd ); ADDBYTE ( pak, monster->Stats->stance ); GServer->SendToVisible(&pak, entity); return AI_SUCCESS; } //Move (walk or run) to a random location within iDistance of my findChar location AIACT(005) { //byte cSpeed; //Pos: 0x00 //move randomly within 200 of "m_pFindCHAR" GETAIACTDATA(005); if(entity->findChar == NULL) return AI_FAILURE; CMonster* monster = reinterpret_cast(entity); monster->UpdatePosition( ); entity->findChar->UpdatePosition(); int iDist = 2;//Get it to our coord system! entity->Position->destiny = GServer->RandInCircle(entity->findChar->Position->current, iDist); //int randDis = rand()%(iDist << 1); //float nX = (entity->findChar->Position->current.x + randDis) - iDist; //entity->Position->destiny.x=nX; //randDis = rand()%(iDist << 1); //float nY = (entity->findChar->Position->current.y + randDis) - iDist; //entity->Position->destiny.y=nY; monster->Stats->stance = data->cSpeed; monster->SetStats(); entity->Battle->atktype = 0; BEGINPACKET( pak, 0x797 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, 0x0000 );//??? ADDWORD ( pak, monster->Stats->Move_Speed ); //speed ADDFLOAT ( pak, monster->Position->destiny.x*100 ); ADDFLOAT ( pak, monster->Position->destiny.y*100 ); ADDWORD ( pak, 0xcdcd ); ADDBYTE ( pak, monster->Stats->stance ); GServer->SendToVisible(&pak, entity); return AI_SUCCESS; } //Run and Attack aiobj within iDistance that has the lowest or greatest cAbType AIACT(006) { //dword iDistance; //Pos: 0x00 //byte cAbType; //Pos: 0x04 //byte cMoreLess; //Pos: 0x05 //Run and Attack aiobj within iDistance that has the lowest or greatest cAbType //cAbType //0 = Level //1 = Attack //2 = Defense //3 = "Res"? Magic Resistance? //4 = HP //5 = Charm GETAIACTDATA(006); dword eCount = 0; int highestAB = -9999999; // CWorldEntity* highestEntity = NULL; int lowestAB = 9999999; // CWorldEntity* lowestEntity = NULL; CMonster* monster = reinterpret_cast(entity); if(monster->thisnpc->aggresive == 0) return AI_FAILURE; //monster has no agression value so cannot attack a player int aggro = GServer->RandNumber(1,20); if(aggro > monster->thisnpc->aggresive) return AI_FAILURE; // failed aggresiveness check CMap* map= GServer->MapList.Index[entity->Position->Map]; CCharacter* Target = NULL; for(UINT i=0;iPlayerList.size();i++) { CPlayer* player = map->PlayerList.at(i); if(player == NULL)continue; if(GServer->IsMonInCircle( monster->Position->current, player->Position->current, data->iDistance)) { switch(data->cAbType) { case 0: //level if(data->cMoreLess == 0) { if(player->Stats->Level > lowestAB) { lowestAB = player->Stats->Level; Target = player; }else continue; }else{ if(player->Stats->Level < highestAB) { highestAB = player->Stats->Level; Target = player; }else continue; } break; case 1: //Attack power if(data->cMoreLess == 0) { if(player->Stats->Attack_Power > lowestAB) { lowestAB = player->Stats->Attack_Power; Target = player; }else continue; }else{ if(player->Stats->Attack_Power < highestAB) { highestAB = player->Stats->Attack_Power; Target = player; }else continue; } break; case 2: //defense if(data->cMoreLess == 0) { if(player->Stats->Defense> lowestAB) { lowestAB = player->Stats->Defense; Target = player; }else continue; }else{ if(player->Stats->Defense < highestAB) { highestAB = player->Stats->Defense; Target = player; }else continue; } break; case 3: //Magic Def if(data->cMoreLess == 0) { if(player->Stats->Magic_Defense> lowestAB) { lowestAB = player->Stats->Magic_Defense; Target = player; }else continue; }else{ if(player->Stats->Magic_Defense < highestAB) { highestAB = player->Stats->Magic_Defense; Target = player; }else continue; } break; case 4: // HP if(data->cMoreLess == 0) { if(player->Stats->HP> lowestAB) { lowestAB = player->Stats->HP; Target = player; }else continue; }else{ if(player->Stats->HP < highestAB) { highestAB = player->Stats->HP; Target = player; }else continue; } break; case 5: // Charm if(data->cMoreLess == 0) { if(player->Attr->Cha > lowestAB) { lowestAB = player->Attr->Cha; Target = player; }else continue; }else{ if(player->Attr->Cha < highestAB) { highestAB = player->Attr->Cha; Target = player; }else continue; } break; default: break; } } } // OK we found the new Target player. now attack him. monster->Stats->stance = 1; monster->SetStats(); monster->StartAction( Target, NORMAL_ATTACK, 0 ); return AI_SUCCESS; } //Special attack AIACT(007) { //CObjCHAR::Special_ATTACK //Log(MSG_DEBUG, "AIACT(007)"); return AI_SUCCESS; } //Move (4) AIACT(008) { //dword iDistance; //Pos: 0x00 //byte cSpeed; //Pos: 0x04 //move towards target. Stop at a specified precentage away from target //Log(MSG_DEBUG, "AIACT(008)"); GETAIACTDATA(008); CMonster* monster = reinterpret_cast(entity); monster->UpdatePosition( ); CCharacter* target = entity->GetCharTarget( ); if(target == NULL) { return AI_FAILURE; } target->UpdatePosition(); int iDist = data->iDistance;//Get it to our coord system! float xDist = target->Position->current.x - monster->Position->current.x; float yDist = target->Position->current.y - monster->Position->current.y; float fDistance = GServer->distance( entity->Position->current, target->Position->current); float nX = monster->Position->current.x - (iDist * xDist / fDistance); float nY = monster->Position->current.y - (iDist * yDist / fDistance); monster->Position->destiny.x = nX; monster->Position->destiny.y = nY; monster->Stats->stance = data->cSpeed; monster->SetStats(); monster->Position->lastMoveTime = clock(); ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? //entity->Battle->atktype = 0; BEGINPACKET( pak, 0x797 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, 0x0000 );//??? ADDWORD ( pak, monster->Stats->Move_Speed ); //speed ADDFLOAT ( pak, monster->Position->destiny.x*100 ); ADDFLOAT ( pak, monster->Position->destiny.y*100 ); ADDWORD ( pak, 0xcdcd ); ADDBYTE ( pak, monster->Stats->stance ); GServer->SendToVisible(&pak, entity); return AI_SUCCESS; } //Convert Monster to new monster (1) AIACT(009) { //word wMonster; //Pos: 0x00 //Transform into wMonster GETAIACTDATA(009); CMap* map = GServer->MapList.Index[entity->Position->Map]; CMonster* monster = reinterpret_cast(entity); int oldmon = monster->montype; map->ConverToMonster(monster, data->wMonster,1); return AI_SUCCESS; } //Spawn Monster (2) AIACT(010) { //word wMonster; //Pos: 0x00 //Spawn wMonster at current X,Y GETAIACTDATA(010); CMap* map = GServer->MapList.Index[entity->Position->Map]; fPoint position; position = GServer->RandInCircle( entity->Position->current, 1 ); map->AddMonster( data->wMonster, position, 0, 0, 1 ); return AI_SUCCESS; } //Call for backup AIACT(011) { //dword iDistance; //Pos: 0x00 //dword iNumOfMonster; //Pos: 0x04 //force iNumOfMonster of same team within iDistance to attack my target //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); //if(target == NULL) return AI_FAILURE; //Log(MSG_DEBUG, "AIACT(011)"); CCharacter* target = entity->GetCharTarget( ); if(target == NULL) return AI_FAILURE; GETAIACTDATA(011); int chrCount = 0; dword eCount = 0; int searchDistance = data->iDistance; CMap* map = GServer->MapList.Index[entity->Position->Map]; dword entityCount = map->MonsterList.size(); for(UINT j=0;jMonsterList.size();j++) { CMonster* other = map->MonsterList.at(j); if(eCount >= entityCount) break; if(other == NULL) continue; eCount++; if(other == entity) continue; if(other->CharType != TMONSTER) continue; int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; chrCount++; other->Battle->target = entity->Battle->target; other->StartAction( (CCharacter*) target, NORMAL_ATTACK, 0 ); // other->Attack(target); if(chrCount >= data->iNumOfMonster) return AI_SUCCESS; } return AI_SUCCESS; } //start attack against closest player AIACT(012) { //Log(MSG_DEBUG, "AIACT(012)"); //Run and attack "m_pNearCHAR" Nearest Character // entity->Attack(entity->nearChar); // Log(MSG_DEBUG, "AIACT(012)"); if(entity->nearChar == NULL) { Log(MSG_DEBUG,"No target found in entity->nearchar. Now why is that?"); return AI_FAILURE; } //entity->Battle->target=entity->nearChar->clientid; CMonster* monster = reinterpret_cast(entity); int aggro = GServer->RandNumber(1,20); if(aggro > monster->thisnpc->aggresive) return AI_FAILURE; // failed aggresiveness check monster->Stats->stance = 1; monster->SetStats(); monster->StartAction( (CCharacter*) entity->nearChar, NORMAL_ATTACK, 0 ); // Log(MSG_DEBUG, "AIACT(012)%i",entity->nearChar->clientid); return AI_SUCCESS; } //run and attack findChar AIACT(013) { //Log(MSG_DEBUG, "AIACT(013)"); //Run and attack "m_pFindCHAR" Character found from aiobj loop things // entity->Attack(entity->findChar); if(entity->findChar == NULL) { Log(MSG_DEBUG,"No target found in entity->findchar. Now why is that?"); return AI_FAILURE; } //entity->Battle->target=entity->findChar->clientid; CMonster* monster = reinterpret_cast(entity); if(monster->thisnpc->aggresive == 0) return AI_FAILURE; //monster has no agression value so cannot attack a player int aggro = GServer->RandNumber(1,20); if(aggro > monster->thisnpc->aggresive) return AI_FAILURE; // failed aggresiveness check monster->Stats->stance = 1; monster->SetStats(); monster->Battle->target = entity->findChar->clientid; monster->StartAction( (CCharacter*) entity->findChar, NORMAL_ATTACK, 0 ); return AI_SUCCESS; } //get help from other monsters of the same monster type AIACT(014) { //dword iDistance; //Pos: 0x00 //find aiobj within iDistance and force them to attack my target // CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); // if(target == NULL) return AI_FAILURE; GETAIACTDATA(014); CMonster* monster = reinterpret_cast(entity); CMap* map = GServer->MapList.Index[entity->Position->Map]; CCharacter* target = entity->GetCharTarget( ); if(target == NULL) return AI_FAILURE; int monstercount = 0; for(UINT j=0;jMonsterList.size();j++) { CMonster* othermon = map->MonsterList.at(j); if(othermon == NULL) continue; if(othermon == monster) continue; if(othermon->montype != monster->montype) continue; if(GServer->IsMonInCircle( monster->Position->current, othermon->Position->current, data->iDistance)) { othermon->StartAction( target, NORMAL_ATTACK, 0); } } return AI_SUCCESS; } //Retaliate against the last character that hit me AIACT(015) { //Run and attack "m_pDestCHAR" Blah? //Log(MSG_DEBUG, "AIACT(015)"); CMonster* monster = reinterpret_cast(entity); if(monster->thisnpc->aggresive == 0) { //Log(MSG_DEBUG, "AIACT(015) returned false as monster has no aggression"); return AI_FAILURE; //monster has no agression value so cannot attack a player } if(monster->IsOnBattle()) { //already in battle so we don't want it switching all the time. maybe 75% of the time it will do nothing? if(GServer->RandNumber(0,100) > 25) { //Log(MSG_DEBUG, "AIACT(015) returned false as we got a really bad random number %i"); return AI_FAILURE; //denied!!! } if(monster->Battle->hitby == monster->Battle->target) { //Log(MSG_DEBUG, "AIACT(015) returned false as monster is already in combat with this player %i",monster->Battle->target); return AI_FAILURE; //no need to initiate combat. I am already fighting this player } } // otherwise change target to the person who just hit me monster->Stats->stance = 1; monster->SetStats(); monster->Battle->target = monster->Battle->hitby; CCharacter* target = entity->GetCharTarget( ); if(target == NULL) { //Log(MSG_DEBUG, "I seem to have lost my target so I can't attack any more"); return AI_FAILURE; } //Log(MSG_DEBUG, "Starting normal attack against opponent %i",target->clientid); monster->StartAction( target, NORMAL_ATTACK, 0 ); return AI_SUCCESS; } //Run away to a point within distance iDistance of my spawn point AIACT(016) { //dword iDistance; //Run Away! //Log(MSG_DEBUG, "AIACT(016)"); GETAIACTDATA(016); CMonster* monster = reinterpret_cast(entity); monster->UpdatePosition( ); int iDist = data->iDistance;//Get it to our coord system! entity->Position->destiny = GServer->RandInCircle(entity->Position->source, iDist); //float radius = data->iDistance; //float angle = rand()%(360); // float angle = rg.IRandom(0,360); // entity->UpdatePosition(); // entity->MoveTo(entity->basic.pos.x + (radius * cos(angle)), entity->basic.pos.y + (radius * sin(angle))); //float nX = (entity->Position->source.x + (radius * cos(angle))); //entity->Position->destiny.x=nX; //float nY = (entity->Position->source.y + (radius * cos(angle))); //entity->Position->destiny.y=nY; monster->Stats->stance = 1; monster->SetStats(); entity->Position->lastMoveTime = clock(); //ClearBattle( entity->Battle );// this also seems to clear the attackers battle?? entity->Battle->atktype = 0; BEGINPACKET( pak, 0x797 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, 0x0000 );//??? ADDWORD ( pak, entity->Stats->Move_Speed ); //speed ADDFLOAT ( pak, entity->Position->destiny.x*100 ); ADDFLOAT ( pak, entity->Position->destiny.y*100 ); ADDWORD ( pak, 0xcdcd ); ADDBYTE ( pak, 0x01 ); GServer->SendToVisible(&pak, entity); return AI_SUCCESS; } //Drop Item AIACT(017) { //word item0; Pos: 0x00 //word item1; Pos: 0x02 //word item2; Pos: 0x04 //word item3; Pos: 0x06 //word item4; Pos: 0x08 //word item5; pos: 0x0a dummy as a spacer //dword btToOwner; //Pos: 0x0c //Random drop one of item 1-5 GETAIACTDATA(017); //srand(time(NULL)); int itemRand = rand()%(5); //int itemRand = rg.IRandom(0,360); int nItem = data->items[itemRand]; if (nItem == 0)return AI_FAILURE; CMonster* monster = reinterpret_cast(entity); //CItem dropItem; CDrop* newdrop = new (nothrow) CDrop; newdrop->clientid = GServer->GetNewClientID( ); newdrop->posMap = entity->Position->Map; newdrop->pos = GServer->RandInCircle( entity->Position->current, 3 ); newdrop->droptime = time(NULL); if(data->btToOwner == 1) { newdrop->owner = monster->MonsterDrop->firsthit; newdrop->thisparty = monster->thisparty; } else { newdrop->owner = 0; newdrop->thisparty = 0; } ClearItem(newdrop->item); newdrop->amount = 1; newdrop->type = 2; newdrop->item.itemnum = nItem % 1000; newdrop->item.itemtype = nItem / 1000; if(newdrop->item.IsStackable()) { newdrop->item.count = 1; } else { newdrop->item.stats = 0; newdrop->item.durability = 40; newdrop->item.lifespan = 1000; newdrop->item.socketed = 0; newdrop->item.appraised = 1; newdrop->item.refine = 0; } //Log(MSG_DEBUG, "item type= %i item num = %i", newdrop->item.itemtype, newdrop->item.itemnum); newdrop->item.count = 1; CMap* map = GServer->MapList.Index[newdrop->posMap]; map->AddDrop( newdrop ); // entity->DropEntityItem(&dropItem); return AI_SUCCESS; } //Make nearby monsters attack my target AIACT(018) { //word cMonster; //Pos: 0x00 //word wHowMany; //Pos: 0x02 //dword iDistance; //Pos: 0x04 //make wHowMany monsters of type cMonster within iDistance attack my target // CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); // if(target == NULL) return AI_FAILURE; GETAIACTDATA(018); CMonster* monster = reinterpret_cast(entity); CMap* map = GServer->MapList.Index[entity->Position->Map]; CCharacter* target = entity->GetCharTarget( ); if(target == NULL) return AI_FAILURE; int monstercount = 0; for(UINT j=0;jMonsterList.size();j++) { CMonster* othermon = map->MonsterList.at(j); if(othermon == NULL) continue; if(othermon == monster) continue; if(othermon->montype != data->cMonster) continue; if(GServer->IsMonInCircle( monster->Position->current, othermon->Position->current, data->iDistance)) { othermon->StartAction( target, NORMAL_ATTACK, 0); monstercount++; if(monstercount >= data->wHowMany) return AI_SUCCESS; } } return AI_SUCCESS; } //Start attack AIACT(019) { //Run and attack "m_pNearCHAR" //Identical to 012 return F_AI_ACT_012(server, entity, raw); } //Spawn Monster (5) AIACT(020) { //word cMonster; //Pos: 0x00 //byte btPos; //Pos: 0x02 //dword iDistance; //Pos: 0x04 //Spawn cMonster within iDistance of btPos //btPos 0 = my pos //btPos 1 = "m_pDestCHAR" pos //btPos 2 = targets pos GETAIACTDATA(020); CMap* map = GServer->MapList.Index[entity->Position->Map]; CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity if(monster == NULL) return AI_FAILURE; fPoint position; if(data->btPos == 0) { position = GServer->RandInCircle( entity->Position->current, data->iDistance ); map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); } else if(data->btPos == 1) { position = GServer->RandInCircle( entity->Position->current, data->iDistance ); map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); //dest char } else if(data->btPos == 2) { CCharacter* target = entity->GetCharTarget( ); if(target == NULL) return AI_FAILURE; position = GServer->RandInCircle( target->Position->current, data->iDistance ); map->AddMonster( data->cMonster, position, 0, monster->Status->spawnid, 1 ); } //Log(MSG_DEBUG, "AIACT(020)option spawn %i distance %i monstertype %i",data->btPos,data->iDistance,data->cMonster); return AI_SUCCESS; } //Unknown AIACT(021) { //Does absolutely nothing return AI_SUCCESS; } //Unknown AIACT(022) { //Does absolutely nothing return AI_SUCCESS; } //ghostseeds and ghosts and player summons commit suicide AIACT(023) { //Commit suicide entity->Stats->HP = -1; CMonster* thisMonster = reinterpret_cast(entity); CMap* map = GServer->MapList.Index[entity->Position->Map]; BEGINPACKET( pak, 0x799 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, entity->clientid ); ADDDWORD ( pak, 0x8005 ); GServer->SendToVisible( &pak, entity ); //Log(MSG_DEBUG, "AIACT(023) suicide monstertype %i",thisMonster->montype); //thisMonster->DoAi(thisMonster->thisnpc->AI, 5); //now release the id in the monsterlist //map->DeleteMonster( thisMonster); // something weird going on here. Client ids not being relinquished. Deletemonster does not help. it crashes the server. return AI_SUCCESS; } //Do Skill AIACT(024) { //byte btTarget; //Pos: 0x00 //word nSkill; //Pos: 0x02 //word nMotion; //Pos: 0x04 //8 = SKILL_ATTACK 6 = magic //Use nSkill & nMotion? //btTarget 0 = m_pFindCHAR //btTarget 1 = target //btTarget 2 = self skill GETAIACTDATA(024); // Log(MSG_DEBUG, "Do Skill btTarget %i nSkill %i nMotion %i",data->btTarget,data->nSkill,data->nMotion); // Log(MSG_DEBUG, "entity %i",entity->clientid); // Log(MSG_DEBUG, "entity->findChar %i",entity->findChar->clientid); // Log(MSG_DEBUG, "entity->nearChar %i", entity->nearChar->clientid); CSkills* thisskill = GServer->GetSkillByID( data->nSkill ); if(thisskill == NULL)return AI_FAILURE; CCharacter* monster = entity; //if(monster->Battle->atktype != 0) //{ // Log(MSG_DEBUG,"Cannot apply battle attacktype. Monster already has one"); // return AI_FAILURE; //} //Log(MSG_DEBUG,"AIACT(024)skill attack id: %i target: %i type: %i btTarget: %i nMotion: %i", data->nSkill, thisskill->target, thisskill->skilltype, data->btTarget, data->nMotion); switch (thisskill->skilltype) { case 3: //damage action case 6: //magic attack { CCharacter* target = entity->GetCharTarget( ); //Log(MSG_DEBUG,"SKILL_ATTACK or DAMAGE_ACTION selected"); if(target==NULL) { //Log(MSG_DEBUG,"No target can be found for this skill"); return AI_FAILURE; } //Log(MSG_DEBUG,"SKILL_ATTACK selected. target: %i",target->clientid); monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); } break; case 7: //AOE magic attack { CCharacter* target = entity->GetCharTarget( ); //Log(MSG_DEBUG,"AOE_TARGET selected"); if(target == NULL) { //Log(MSG_DEBUG,"No target can be found for this skill"); return AI_FAILURE; } //Log(MSG_DEBUG,"AOE_TARGET selected. target: %i",target->clientid); monster->StartAction( (CCharacter*) target, AOE_TARGET, data->nSkill ); } break; case 8: //self buff { //Log(MSG_DEBUG,"BUFF_SELF selected."); monster->StartAction( NULL, BUFF_SELF, data->nSkill ); } break; case 9: //skill buff case 13://skill buff case 11: //heal { CCharacter* target = entity->GetCharTarget( ); if(target == NULL) //some skills always return a NULL target here { if(entity->nearChar == NULL) // maybe there really is no possible target. { //Log(MSG_DEBUG,"No target can be found for this skill"); return AI_FAILURE; } else //so we are going to have them select a NEW target (if possible) rather than return AI_FAILURE { //Log(MSG_DEBUG,"SKILL_BUFF type 13 selected. NEW target: %i",entity->nearChar->clientid); CCharacter* newtarget = entity->nearChar; if(newtarget == NULL) return AI_FAILURE; monster->StartAction( (CCharacter*) newtarget, SKILL_BUFF, data->nSkill ); } } else { //Log(MSG_DEBUG,"SKILL_BUFF type %i selected. target: %i",thisskill->skilltype, target->clientid); monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); } } break; case 10: //heal AOE centered on self //Log(MSG_DEBUG,"BUFF_AOE selected. Skill id %i", data->nSkill); monster->StartAction( NULL, BUFF_AOE, data->nSkill ); break; case 17: //skill AOE centered on self { //Log(MSG_DEBUG,"SKILL_AOE selected. Skill id %i", data->nSkill); monster->StartAction( NULL, SKILL_AOE, data->nSkill ); } break; default: { //Log(MSG_WARNING,"Skill type not recognized. type: %i",thisskill->skilltype); } break; } /* if (data->btTarget == 0) { //CCharacter* target = entity->GetCharTarget(); //if(target==NULL)return AI_FAILURE; //CCharacter* monster = entity; //defined twice???? Log(MSG_DEBUG,"BUFF_AOE selected"); monster->StartAction( NULL, BUFF_AOE, data->nSkill ); } else if (data->btTarget == 1) { CCharacter* target = entity->GetCharTarget( ); if(target==NULL)return AI_FAILURE; if(data->nMotion == 8) { Log(MSG_DEBUG,"SKILL_ATTACK selected"); monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); } else { Log(MSG_DEBUG,"SKILL_BUFF selected"); monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); } } else if (data->btTarget == 2) { if(thisskill->target == tYourself) { Log(MSG_DEBUG,"BUFF_SELF selected"); monster->StartAction( entity, BUFF_SELF, data->nSkill ); } else if (thisskill->target == tAlly) { Log(MSG_DEBUG,"BUFF_AOE selected"); monster->StartAction( NULL, BUFF_AOE, data->nSkill ); } else if (thisskill->target == tHostileCharacter) { CCharacter* target = entity->GetCharTarget( ); if(target == NULL)return AI_FAILURE; if(data->nMotion == 8) { Log(MSG_DEBUG,"SKILL_ATTACK selected"); monster->StartAction( (CCharacter*) target, SKILL_ATTACK, data->nSkill ); } else { monster->StartAction( (CCharacter*) target, SKILL_BUFF, data->nSkill ); } } }*/ return AI_SUCCESS; } //Set Variable (1) AIACT(025) { // if(entity->_EntityType != ENTITY_NPC) return AI_FAILURE; //byte btVarIDX Pos: 0x00 //dword iValue Pos: 0x04 //byte btOp Pos: 0x08 GETAIACTDATA(025); CMonster* monster = reinterpret_cast(entity); if(monster == NULL) return AI_FAILURE; if(data->btVarIDX > 19) return AI_FAILURE; short tempval = GServer->ObjVar[monster->thisnpc->refNPC][data->btVarIDX]; switch(data->btOp) { case 5: tempval = data->iValue; break; case 6: tempval += data->iValue; break; case 7: tempval -= data->iValue; break; case 9: tempval++; break; default: return AI_FAILURE; break; } if(tempval < 0)tempval = 0; GServer->ObjVar[monster->thisnpc->refNPC][data->btVarIDX] = tempval; if(data->btVarIDX == 0) //changing index 0 changes event variable at client too. Possible others too but for now just 0 { //Log(MSG_DEBUG,"AIACT(025) eventID has changed from %i to %i",monster->thisnpc->eventid,tempval); monster->thisnpc->eventid = tempval; } return AI_SUCCESS; } //Set Variable (2) AIACT(026) { //Set WorldVAR //word nVarNo; //Pos: 0x00 //dword iValue; //Pos: 0x04 //byte btOp; //Pos: 0x08 GETAIACTDATA(026); //PY: we don't know how to use worldVar so here are some fun things we can do with custom AI switch(data->nVarNo) { case 1: //change world exp rate in the config GServer->Config.EXP_RATE = data->iValue; return AI_SUCCESS; break; default: return AI_SUCCESS; break; } } //Set Economy Variable. Could have some fun with tis in the future AIACT(027) { //Set EconomyVAR //word nVarNo; //Pos: 0x00 //dword iValue; //Pos: 0x04 //byte btOp; //Pos: 0x08 return AI_SUCCESS; } //Shout/Ann LTB String AIACT(028) { GETAIACTDATA(028); //byte btMsgType Pos: 0x00 //dword iStrID Pos: 0x04 dword npcId = 0; //if(entity->_EntityType == ENTITY_NPC) CMonster* thisMonster = reinterpret_cast(entity); switch(data->btMsgType) { case 0: //current field // don't think this is possible. No way to get a character to send it to?? break; case 1: //shout to map //Log(MSG_INFO,"AI -- NPC Shouts to map %i",entity->Position->Map); GServer->NPCShout(entity->Position->Map,GServer->Ltbstring[data->iStrID].LTBstring,GServer->Ltbstring[data->iStrID].NPCname); break; case 2: //announce to server. GServer->NPCAnnounce(GServer->Ltbstring[data->iStrID].LTBstring,GServer->Ltbstring[data->iStrID].NPCname); break; default: break; } //npcId = reinterpret_cast(entity)->NpcID; // }else if(entity->_EntityType == ENTITY_MONSTER){ // npcId = reinterpret_cast(entity)->MonID; // }else{ // return AI_FAILURE; // } // std::map::iterator triggerITR = server->NpcNames.find(npcId); // if(triggerITR == server->NpcNames.end()) return AI_FAILURE; // char* npcName = triggerITR->second; // char* sayStr = server->lngAi->Data(data->iStrID, 1); // if(sayStr == NULL) return AI_FAILURE; // if(data->btMsgType == 0){ //Chat // CPacket pakout(0x783); // pakout.Add(entity->_ClientID); // pakout.Add(sayStr); // server->SendPacketToZone(entity, &pakout); // }else if(data->btMsgType == 1){ //Shout // CPacket pakout(0x785); // pakout.Add(npcName); // pakout.Add(sayStr); // server->SendPacketToZone(entity, &pakout); // }else if(data->btMsgType == 2){ //Announce // CPacket pakout(0x702); // pakout.AddBytes((byte*)npcName, strlen(npcName)); // pakout.Add('>'); // pakout.Add(sayStr); // server->SendPacketToZone(entity, &pakout); // } // delete [] sayStr; //Log(MSG_DEBUG, "AIACT(028)"); return AI_SUCCESS; } //Move to my owner's location AIACT(029) { //Move within 20% of the distance of my "CALLER" GETAIACTDATA(029); // if(entity->_EntityType != ENTITY_MONSTER) return AI_FAILURE; // CMonster* thisMonster = reinterpret_cast(entity); // CWorldEntity* caller = thisMonster->thisZone->GetEntity(thisMonster->_CallerID); // if(caller == NULL) return AI_FAILURE; // caller->UpdatePosition(); // thisMonster->UpdatePosition(); if(!entity->IsMonster( )) return AI_FAILURE; CMonster* thisMonster = reinterpret_cast(entity); CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; CCharacter* caller = map->GetCharInMap( thisMonster->owner ); if(caller == NULL) return AI_FAILURE; caller->UpdatePosition(); thisMonster->UpdatePosition(); thisMonster->Stats->stance = 1; thisMonster->SetStats(); thisMonster->Position->source = caller->Position->current; int iDist = GServer->RandNumber(5, 20); //move to between 5% and 20% of owners position float xDist = thisMonster->Position->current.x - thisMonster->Position->source.x; float yDist = thisMonster->Position->current.y - thisMonster->Position->source.y; float nX = thisMonster->Position->source.x + (xDist * iDist / 100); float nY = thisMonster->Position->source.y + (yDist * iDist / 100); thisMonster->Position->destiny.x = nX; thisMonster->Position->destiny.y = nY; thisMonster->Position->lastMoveTime = clock(); ClearBattle( thisMonster->Battle );// this also seems to clear the attackers battle?? //entity->Battle->atktype = 0; BEGINPACKET( pak, 0x797 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, 0x0000 );//??? ADDWORD ( pak, thisMonster->Stats->Move_Speed ); //speed ADDFLOAT ( pak, thisMonster->Position->destiny.x*100 ); ADDFLOAT ( pak, thisMonster->Position->destiny.y*100 ); ADDWORD ( pak, 0xcdcd ); ADDBYTE ( pak, thisMonster->Stats->stance ); GServer->SendToVisible(&pak, entity); return AI_SUCCESS; } //Do Trigger AIACT(030) { //word lenszTrigger; //Pos: 0x00 //char* szTrigger; //Pos: 0x02 GETAIACTDATA(030); char* tempName = reinterpret_cast(&data->szTrigger) - 2; dword hash = MakeStrHash(tempName); //Log(MSG_INFO, "Execute Quest Trigger %s[%d] [%08x]", tempName, data->lenszTrigger, hash); //make a dummy player from this monster CClientSocket* thisclient = new CClientSocket; CPlayer* player = new CPlayer( thisclient ); if(player == NULL) { Log(MSG_INFO, "Player not found in AIACT 30"); return AI_FAILURE; } thisclient->player = (void*) player; player->Position = entity->Position; int quest = player->ExecuteQuestTrigger(hash); if(quest == QUEST_SUCCESS) return AI_SUCCESS; return AI_FAILURE; } //set monster's attack target to the same as the owner AIACT(031) { //Log(MSG_DEBUG, "AIACT(031)"); //Attack my "CALLER"(s target) if he is not an ally (must be caller's target...) if(!entity->IsMonster( )) return AI_FAILURE; CMonster* thisMonster = reinterpret_cast(entity); CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; CCharacter* caller = map->GetCharInMap( thisMonster->owner ); if(caller == NULL) return AI_FAILURE; thisMonster->Position->source = caller->Position->current; CCharacter* target = map->GetCharInMap(caller->Battle->target); if(target == NULL) return AI_FAILURE; // need to prevent the summon from attacking other summons of the same player CMonster* otherMonster = reinterpret_cast(target); if(otherMonster->owner == thisMonster->owner) return AI_FAILURE; thisMonster->StartAction( (CCharacter*) target, NORMAL_ATTACK, 0 ); //Log(MSG_DEBUG, "AIACT(031) summon attacking player's target"); return AI_SUCCESS; } //Set PK flag for specified zone AIACT(032) { //word nZoneNo; //Pos: 0x00 //byte btOnOff; //Pos: 0x02 //Set PK Flag (btOnOff) in nZoneNo GETAIACTDATA(032); //Log(MSG_DEBUG, "AIACT(032) zone (1)"); return AI_SUCCESS; } //Set regen mode for specified zone AIACT(033) { //word nZoneNo //if 0, current map //word nNewValue //if nNewValue == 2 -> toggle //else set regen system nNewValue (0 = off, 1 = on) GETAIACTDATA(033); //Log(MSG_DEBUG, "AIACT(033) zone (2)"); return AI_SUCCESS; } //Item (?) AIACT(034) { //word nItemNum; //Pos: 0x00 //word nCount; //Pos: 0x02 //Give item to "CALLER" GETAIACTDATA(034); if(!entity->IsMonster( )) return AI_SUCCESS; CMonster* thisMonster = reinterpret_cast(entity); CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; CPlayer* caller = map->GetPlayerInMap( thisMonster->owner); if(caller == NULL) return AI_SUCCESS; int nItem = data->nItemNum; if (nItem == 0)return AI_SUCCESS; char itemtypes[15] = {4,0,0,0,0,0,0,0,0,0,1,2,2,4,3}; CItem thisitem; thisitem.Clear(); thisitem.itemtype = nItem / 1000; thisitem.itemnum = nItem % 1000; thisitem.count = data->nCount; if( itemtypes[thisitem.itemtype] == 0 ) { thisitem.appraised = true; thisitem.lifespan = 100; thisitem.gem = 0; thisitem.durability = GServer->STB_ITEM[thisitem.itemtype-1].rows[thisitem.itemnum][29]; } int thisslot = caller->AddItem( thisitem ); //Log(MSG_DEBUG,"Item type = %i item ID = %i Count = %i Slot = %i",thisitem.itemtype,thisitem.itemnum,thisitem.count,thisslot); if (thisslot == 0xffff) // Fail { BEGINPACKET( pak, 0x7a7); ADDWORD(pak, 0x00); ADDBYTE(pak, 0x03); ADDBYTE(pak, 0x00); caller->client->SendPacket(&pak); return AI_SUCCESS; } else // Success { BEGINPACKET( pak, 0x71f); ADDBYTE(pak, 0x01); ADDBYTE(pak, thisslot); ADDWORD(pak, caller->items[thisslot].GetPakHeader( ) ); ADDDWORD(pak, caller->items[thisslot].GetPakData( ) ); caller->client->SendPacket(&pak); } //Log(MSG_DEBUG, "AIACT(034) Item (?)"); return AI_SUCCESS; } //Set Variable (4) AIACT(035) { //Set AiVAR //word nVarIDX; Pos: 0x00 //dword iValue; Pos: 0x04 //byte btOp; Pos: 0x08 GETAIACTDATA(035); CMonster* thisMonster = reinterpret_cast(entity); if(data->nVarIDX >19)return AI_SUCCESS; int tempval = thisMonster->AIVar[data->nVarIDX]; switch(data->btOp) { case 6: tempval += data->iValue; break; case 7: tempval -= data->iValue; if(tempval <= 0) { thisMonster->AIVar[data->nVarIDX] = 0; return AI_SUCCESS; } break; case 9: tempval++; break; default: return AI_SUCCESS; break; } thisMonster->AIVar[data->nVarIDX] = tempval; //thisMonster->thisnpc->Objvar->SetVar(data->btOp, data->iValue); //code not working return AI_SUCCESS; } //Monster (1) AIACT(036) { //word nMonster; //Pos: 0x00 //byte btMaster; //Pos: 0x02 //Spawn nMonster at my position (has btMaster)? GETAIACTDATA(036); // CWorldEntity* master = NULL; // if(data->btMaster) master = entity; CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity CMap* map = GServer->MapList.Index[entity->Position->Map]; int owner = 0; if(data->btMaster == 1) owner = monster->clientid; map->AddMonster( data->nMonster, entity->Position->current, 0, monster->Status->spawnid, 10 ); //Log(MSG_DEBUG, "AIACT(36) monster (1) no code here"); return AI_SUCCESS; } //Monster (2) AIACT(037) { //word nMonster; //Pos: 0x00 //word nPos; //Pos: 0x02 //dword iDistance; //Pos: 0x04 //byte btMaster; //Pos: 0x08 //Spawn nMonster at nPos within iDistance (has btMaster)? //nPos = 0 my pos //nPos = 1 m_pDestCHAR pos //nPos = 2 target pos GETAIACTDATA(037); CMap* map = GServer->MapList.Index[entity->Position->Map]; CMonster* monster = reinterpret_cast(entity); //need this to get the spawnid of the entity if(monster == NULL) return AI_FAILURE; int owner = 0; if(data->btMaster == 1) owner = monster->clientid; fPoint position; if(data->nPos == 0) //spawn monster in a circle with radius iDistance around my current position { position = GServer->RandInCircle( entity->Position->current, data->iDistance ); map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); } else if(data->nPos == 1) //spawn monster in a circle with radius iDistance around my destiny position { position = GServer->RandInCircle( entity->Position->destiny, data->iDistance ); map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); //dest char } else if(data->nPos == 2) //spawn monster in a circle with radius iDistance around my target's current position { CCharacter* target = entity->GetCharTarget( ); if(target == NULL) return AI_FAILURE; position = GServer->RandInCircle( target->Position->current, data->iDistance ); map->AddMonster( data->nMonster, position, 0, monster->Status->spawnid, 10 ); } return AI_SUCCESS; } //TDEF code. move to next waypoint AIACT(038) { //byte cSpeed pos 0x00 GETAIACTDATA(038); CMonster* monster = reinterpret_cast(entity); monster->NextWayPoint ++; monster->Position->destiny.x = GServer->WPList[monster->Position->Map][monster->NextWayPoint].X; monster->Position->destiny.y = GServer->WPList[monster->Position->Map][monster->NextWayPoint].Y; monster->Stats->stance = data->cSpeed; entity->Position->lastMoveTime = clock(); BEGINPACKET( pak, 0x797 ); ADDWORD ( pak, entity->clientid ); ADDWORD ( pak, 0x0000 ); ADDWORD ( pak, entity->Stats->Move_Speed ); //speed ADDFLOAT ( pak, monster->Position->destiny.x*100 ); ADDFLOAT ( pak, monster->Position->destiny.y*100 ); ADDWORD ( pak, 0xcdcd ); ADDBYTE ( pak, monster->Stats->stance ); GServer->SendToVisible(&pak, entity); return AI_SUCCESS; }