// Props to ExJam for this code :D #include "../WorldServer.h" //extern CRandomMersenne rg; int AI_GetAbility(CCharacter* entity, byte btAbType) { switch(btAbType) { case 0: return entity->Stats->Level; break; case 1: return entity->Stats->Attack_Power; break; case 2: return entity->Stats->Defense; break; case 3: return entity->Stats->Magic_Defense; break; case 4: return entity->Stats->HP; break; case 5: return 0; //Log(MSG_WARNING, "Don't have monsters charm yet..."); //return -1; break; default: //Log(MSG_ERROR, "Unknown cAbType [%d]", btAbType); return -1; break; } } //Hold or Attack AICOND(000) { //Always False //Log(MSG_DEBUG, "AICOND(000) called"); return AI_FAILURE; } //(Damage > iDamage) == true iDamage cRecvOrGive AICOND(001) { //Log(MSG_DEBUG, "AICOND(001) called"); //dword iDamage //Pos: 0x00 //byte cRecvOrGive //Pos: 0x04 GETAICONDDATA(001); //Log(MSG_DEBUG, "AIcond(001) %i %i %i",data->iDamage,data->cRecvOrGive,entity->damagecounter); if(data->cRecvOrGive == 0) { if(entity->damagecounter > data->iDamage) { entity->damagecounter = 0; //Log(MSG_DEBUG, "AIcond(001) Success "); return AI_SUCCESS; } } else { //PY this is wrong. if data->cRecvOrGive is 1 then it should return FALSE /*if(entity->damagecounter < data->iDamage) { entity->damagecounter = 0; //Log(MSG_DEBUG, "AIcond(001) Success "); return AI_SUCCESS; }*/ return AI_FAILURE; } return AI_FAILURE; } //Check Near (1) AICOND(002) { /* dword iDistance; //Pos: 0x00 byte btIsAllied; //Pos: 0x04 word nLevelDiff; //Pos: 0x06 word nLevelDiff2; //Pos: 0x08 word wChrNum; //Pos: 0x0a */ //count how many aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance //must be >= wChrNum ////Log(MSG_DEBUG, "AICOND(002) called"); GETAICONDDATA(002); dword chrCount = 0; dword eCount = 0; int nearestDistance = 9999999; int searchDistance = data->iDistance; CMap* map = GServer->MapList.Index[entity->Position->Map]; ////Log(MSG_DEBUG, "Check Near (1)iDistance %i btIsAllied %i nLevelDiff %i nLevelDiff2 %i wChrNum %i", data->iDistance, data->btIsAllied, data->nLevelDiff, data->nLevelDiff2, data->wChrNum); if(data->btIsAllied == 1) { 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; int levelDiff = (entity->Stats->Level - other->Stats->Level); if(levelDiff < data->nLevelDiff) continue; if(levelDiff > data->nLevelDiff2) continue; int iDistance = (int) GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; chrCount++; if(iDistance < nearestDistance) { nearestDistance = iDistance; entity->nearChar = other; } if(chrCount >= data->wChrNum) { entity->findChar = other; return AI_SUCCESS; } } } else { dword entityCount = map->PlayerList.size(); ////Log(MSG_DEBUG, "entityCount %i", entityCount); for(UINT j=0;jPlayerList.size();j++) { CPlayer* other = map->PlayerList.at(j); ////Log(MSG_DEBUG, "player %i", other->clientid); if(eCount >= entityCount) break; ////Log(MSG_INFO, "1"); if(other == NULL) continue; ////Log(MSG_INFO, "2"); eCount++; int levelDiff = (entity->Stats->Level - other->Stats->Level); ////Log(MSG_DEBUG, "levelDiff %i < %i", levelDiff,data->nLevelDiff); if(levelDiff < data->nLevelDiff) continue; ////Log(MSG_INFO, "3"); ////Log(MSG_DEBUG, "levelDiff %i > %i", levelDiff,data->nLevelDiff2); if(levelDiff > data->nLevelDiff2) continue; ////Log(MSG_INFO, "4"); int iDistance = (int) GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; ////Log(MSG_INFO, "5"); chrCount++; if(iDistance < nearestDistance) { nearestDistance = iDistance; entity->nearChar = other; ////Log(MSG_INFO, "6"); } if(chrCount >= data->wChrNum) { entity->findChar = other; ////Log(MSG_INFO, "7"); return AI_SUCCESS; } } } return AI_FAILURE; //count how many aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance //must be >= wChrNum //doesn't seem to work that way though //it would seem that LevelDiff is a maximum level and LevelDiff2 is a minimum level. //just subtract each from 65535 to get the level range. No difference involved at all. PY //leveldiff2 often uses only the first byte so it is taken at face value. //Log(MSG_DEBUG, "AICOND(002) called"); /* GETAICONDDATA(002); int maxlevel = 0; int minlevel = 0; CMonster* monster = reinterpret_cast(entity); //Log(MSG_INFO,"Minlevel: %i Maxlevel: %i",data->nLevelDiff2,data->nLevelDiff); if(data->nLevelDiff2 > 255) minlevel = 65535 - data->nLevelDiff2; else minlevel = data->nLevelDiff2; if(data->nLevelDiff > 255) maxlevel = 65535 - data->nLevelDiff; else maxlevel = data->nLevelDiff; if(minlevel > maxlevel) //Sometimes min and max are reversed. After all we just need to know if someting is between the two { int tmpmax = maxlevel; int tmpmin = minlevel; minlevel = tmpmax; maxlevel = tmpmin; } //Log(MSG_INFO,"Minlevel: %i Maxlevel: %i",minlevel,maxlevel); UINT chrCount = 0; UINT eCount = 0; int nearestDistance = 9999999; int searchDistance = data->iDistance; CMap* map = GServer->MapList.Index[entity->Position->Map]; //Log(MSG_DEBUG, "COND(002) Check Near (1)iDistance %i btIsAllied %i level %i to %i wChrNum %i Monid %i", data->iDistance, data->btIsAllied, minlevel, maxlevel, data->wChrNum, monster->montype); if(data->btIsAllied == 1) { 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->Stats->Level > maxlevel) continue; if(other->Stats->Level < minlevel) continue; int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; chrCount++; if(iDistance < nearestDistance) { nearestDistance = iDistance; entity->nearChar = other; } if(chrCount >= data->wChrNum) { entity->findChar = other; return AI_SUCCESS; } } } else { dword entityCount = map->PlayerList.size(); //Log(MSG_DEBUG, "entityCount %i", entityCount); for(UINT j=0;jPlayerList.size();j++) { CPlayer* other = map->PlayerList.at(j); //Log(MSG_DEBUG, "player %i", other->clientid); if(eCount >= entityCount) break; //Log(MSG_INFO, "1"); if(other == NULL) continue; //Log(MSG_INFO, "2"); eCount++; if(other->Stats->Level > maxlevel) continue; if(other->Stats->Level < minlevel) continue; int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; //Log(MSG_INFO, "5"); chrCount++; if(iDistance < nearestDistance) { nearestDistance = iDistance; entity->nearChar = other; //Log(MSG_INFO, "6"); } if(chrCount >= data->wChrNum) { entity->findChar = other; //Log(MSG_INFO, "7"); return AI_SUCCESS; } } } return AI_FAILURE;*/ } //Check Distance (1) AICOND(003) { //dword iDistance; //Pos: 0x00 //CObjCHAR::Get_MoveDISTANCE > iDistance == true //distance from spawn point? //Log(MSG_DEBUG, "AICOND(003) called"); GETAICONDDATA(003); entity->UpdatePosition(); int moveDistance = (int)GServer->distance( entity->Position->current, entity->Position->source ); if(moveDistance > (data->iDistance)) { return AI_SUCCESS; } return AI_FAILURE; } //Check Distance (2) AICOND(004) { //dword iDistance; //Pos: 0x00 //byte cMoreLess; //Pos: 0x04 //distance to target (cMoreLess == 0 then >= else <=) iDistance //Log(MSG_DEBUG, "AICOND(004) called"); GETAICONDDATA(004); //Log(MSG_DEBUG, "AICOND(004) retrieved data"); CCharacter* target = entity->GetCharTarget( ); //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); if(target == NULL) { //Log(MSG_DEBUG, "AICOND(004) found no valid target"); return AI_FAILURE; } target->UpdatePosition(); entity->UpdatePosition(); int distance = (int)GServer->distance( entity->Position->current, target->Position->current ); //int distance = entity->basic.pos.distance(target->basic.pos); if(data->cMoreLess == 0) { if(distance >= data->iDistance) { //Log(MSG_DEBUG, "Check Distance (2) This distance: %i > %i", distance, data->iDistance); return AI_SUCCESS; } } else { if(distance <= data->iDistance) { //Log(MSG_DEBUG, "Check Distance (2) This distance: %i < %i", distance, data->iDistance); return AI_SUCCESS; } } return AI_FAILURE; } //Check Ability of my Target AICOND(005) { /* byte cAbType; //Pos: 0x00 dword iDiff; //Pos: 0x04 byte cMoreLess; //Pos: 0x08 */ //Log(MSG_DEBUG, "AICOND(005) called"); GETAICONDDATA(005); CCharacter* target = entity->GetCharTarget( ); if(target == NULL) { //Log(MSG_DEBUG, "AICOND(004) found no valid target"); return AI_FAILURE; } //cAbType //0 = Level //1 = Attack //2 = Defense //3 = "Res"? Magic Resistance? //4 = HP //5 = Charm //Check if "ability" (cMoreLess == 0 then >= else <=) iDiff //Log(MSG_DEBUG, "Check AB (1)"); int value = AI_GetAbility(target, data->cAbType); //PY This should be checking my target's ability NOT MY OWN. Corrected now //Log(MSG_DEBUG, "Check AB (1) type %i diff %i", data->cAbType, data->iDiff); if(value < 0) return AI_FAILURE; if(data->cMoreLess == 0) { if(value > data->iDiff) { //Log(MSG_DEBUG, "Check AB (1) > %i", data->iDiff); return AI_SUCCESS; } } else { if(value < data->iDiff) { //Log(MSG_DEBUG, "Check AB (1) < %i", data->iDiff); return AI_SUCCESS; } } return AI_FAILURE; } //Check HP AICOND(006) { //dword wHP; //Pos: 0x00 //byte cMoreLess; //Pos: 0x04 //Check if hp% (cMoreLess == 0 then >= else <=) wHP //Log(MSG_DEBUG, "AICOND(000) called"); GETAICONDDATA(006); dword value = (dword)(((float)entity->Stats->HP/ (float)entity->Stats->MaxHP) * 100.0f); //Log(MSG_DEBUG, "Check HP %i %i", data->cMoreLess, value); if(data->cMoreLess == 0) { if(value >= data->wHP) return AI_SUCCESS; } else { if(value <= data->wHP) return AI_SUCCESS; } return AI_FAILURE; } //Random Chance AICOND(007) { //byte cPercent; //Pos: 0x00 //Random number 0->100 if random number < cPercent then return true //Log(MSG_DEBUG, "AICOND(007) called"); GETAICONDDATA(007); //byte rand = rg.IRandom(0, 100) & 0xFF; //srand(time(NULL)); byte brand = rand()%100; //Log(MSG_DEBUG, "brand = %i chance = %i", brand, data->cPercent); if(brand <= data->cPercent) return AI_SUCCESS; return AI_FAILURE; } //Check Near (2) AICOND(008) { //dword iDistance; //Pos: 0x00 //word nLevelDiff; //Pos: 0x04 //word nLevelDiff2; //Pos: 0x06 //byte btIsAllied; //Pos: 0x08 //find atleast 1 aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance //Log(MSG_DEBUG, "AICOND(008) called"); GETAICONDDATA(008); dword eCount = 0; int searchDistance = data->iDistance;// * 10; CMap* map = GServer->MapList.Index[entity->Position->Map]; if(data->btIsAllied == 1) { 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 levelDiff = abs(entity->Stats->Level - other->Stats->Level); if(levelDiff < data->nLevelDiff) continue; if(levelDiff > data->nLevelDiff2) continue; int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; entity->findChar = other; entity->nearChar = other; return AI_SUCCESS; } } else { dword entityCount = map->PlayerList.size(); for(UINT j=0;jPlayerList.size();j++) { CPlayer* other = map->PlayerList.at(j); if(eCount >= entityCount) break; if(other == NULL) continue; eCount++; if(other == entity) continue; if(other->CharType != TPLAYER) continue; int levelDiff = abs(entity->Stats->Level - other->Stats->Level); if(levelDiff < data->nLevelDiff) continue; if(levelDiff > data->nLevelDiff2) continue; int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; entity->findChar = other; entity->nearChar = other; return AI_SUCCESS; } } return AI_FAILURE; } //check target //PY This is actually incorrect. Should be comparing my target with my destination character. TRUE if they are different //To be corrected!!! AICOND(009) { //Do i have a target? //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); //if(target == NULL){entity->_TargetID = 0; return AI_FAILURE;} //return AI_SUCCESS; //TO BE RECODED CCharacter* target = entity->GetCharTarget( ); if(target == NULL) return AI_FAILURE; return AI_SUCCESS; } //Check AB (2) AICOND(010) { //byte cAbType; //Pos: 0x00 //byte cMoreLess; //Pos: 0x01 //Is my ability >= or <= than my targets //PY This is wrong too. Should be comparing ability of my target against the one that just hit me //NOT against MY ability //TO BE RECODED GETAICONDDATA(010); //CWorldEntity* target = entity->thisZone->GetEntity(entity->_TargetID); CCharacter* target = entity->GetCharTarget( ); if(target == NULL) return AI_FAILURE; //Log(MSG_DEBUG, "Check AB (2) type %i ", data->cAbType); int myValue = AI_GetAbility(entity, data->cAbType); if(myValue < 0) return AI_FAILURE; int targetValue = AI_GetAbility(target, data->cAbType); if(targetValue < 0) return AI_FAILURE; if(data->cMoreLess == 0) { if(myValue > targetValue) return AI_SUCCESS; } else { if(myValue < targetValue) return AI_SUCCESS; } return AI_FAILURE; } //Check AB (3) //PY Again this has to be my Attacker's ability. NOT my own or even my Target's AICOND(011) { //byte cAbType; //Pos: 0x00 //dword iValue; //Pos: 0x04 //byte cMoreLess; //Pos: 0x08 //check ability of "m_pDestCHAR" <= or >= iValue GETAICONDDATA(011); dword tValue = data->iValue; //if(tValue == 0)tValue = AI_GetAbility(entity, data->cAbType); CCharacter* target = entity->GetCharTarget( ); if(target == NULL) { //Log(MSG_DEBUG, "AICOND(004) found no valid target"); return AI_FAILURE; } int myValue = AI_GetAbility(target, data->cAbType); //CCharacter* target = entity->GetCharTarget( ); //if(target == NULL) //{ // Log(MSG_DEBUG,"AICOND(011) could not find a valid target"); // return AI_FAILURE; //} //myValue = AI_GetAbility(target, data->cAbType); //Log(MSG_DEBUG, "AICOND(011) myValue: %i <> iValue: %i moreless: %i type: %i", myValue, data->iValue, data->cMoreLess, data->cAbType); if(data->cMoreLess == 0) //0 = more { if(myValue >= data->iValue) { //Log(MSG_DEBUG, "AICOND(011) myValue: %i <= tValue: %i ",myValue,tValue); return AI_SUCCESS; } } else // 1 = less { if(myValue <= data->iValue) { //Log(MSG_DEBUG, "AICOND(011) myValue: %i <= tValue: %i ",myValue,tValue); return AI_SUCCESS; } } return AI_FAILURE; } //Check Time (1) AICOND(012) { //byte cWhen; //Pos: 0x00 //Is it night or day? cWhen: ?1 = night 0 = day? GETAICONDDATA(012); CMap* map = GServer->MapList.Index[entity->Position->Map]; if (data->cWhen == 1) { if (map->CurrentTime > 2) // night return AI_SUCCESS; } else { if (map->CurrentTime < 3) // day return AI_SUCCESS; } return AI_FAILURE; } //Check Target for buffs(1) AICOND(013) { //byte btCheckTarget; //Pos: 0x00 //0 = self 1 = target //byte btStatusType; //Pos: 0x01 // //byte btHave; //Pos: 0x02 //Does my target btHave MagicSTATUS (btStatusType) //btCheckTarget does Get_MagicSTATUS but uses differ AND masks //btCheckTarget = 0 AND EAX,1EAAA84 //btCheckTarget = 1 AND EAX,1E155570 //btCheckTarget = 2 normal CCharacter* target = NULL; UINT buffs=0; GETAICONDDATA(013); if(data->btCheckTarget == 1) //check enemy { target = entity->GetCharTarget( ); if(target == NULL)return AI_FAILURE; } else if(data->btCheckTarget == 0) //check self { target = entity; } else //should never be anything other than 0 or 1 { //Log(MSG_DEBUG, "AICOND(013) Check Target (1) btCheckTarget %i ", data->btCheckTarget); return AI_FAILURE; } if (target == NULL) //target not found { //Log(MSG_DEBUG, "AICOND(013) Check Target (1) not found btCheckTarget %i ", data->btCheckTarget); return AI_FAILURE; } switch(data->btStatusType) { case 0: //debuff buffs = GServer->BuildDeBuffs(target); break; case 1: //buff buffs = GServer->BuildUpBuffs(target); break; case 2: //effect such as burning //not sure how to handle this so i will come back to it. PY break; } //Log(MSG_DEBUG, "Target: %i btStutusType: %i buffs: %i btHave: %i", target->clientid, data->btStatusType, buffs, data->btHave); if (data->btHave == 1) //True if HAS buffs { if(buffs != 0)return AI_SUCCESS; return AI_FAILURE; } else // true if NOT HAS buffs { if(buffs != 0)return AI_FAILURE; return AI_SUCCESS; } //CCharacter* target = entity->GetCharTarget( ); //Log(MSG_DEBUG, "Check Target (1) shouldn't be able to get this far"); return AI_SUCCESS; //return AI_FAILURE; } //Check Variable (1) (ObjVar) AICOND(014) { GETAICONDDATA(014); //Log(MSG_DEBUG,"Checking Object Variable using NPC ref number AICOND(015)"); //Log(MSG_DEBUG,"btVarIDX = %i iValue = %i btOp = %i",data->btVarIDX, data->iValue,data->btOp); CMonster* monster = reinterpret_cast(entity); if(monster == NULL) return AI_FAILURE; if(data->btVarIDX > 19) return AI_FAILURE; int refNPC = monster->thisnpc->refNPC; int ObjvarIndex = data->btVarIDX; int tempval = GServer->ObjVar[refNPC][ObjvarIndex]; //Log(MSG_DEBUG,"ObjVar value [%i][%i] = %i", refNPC,ObjvarIndex,tempval); switch(data->btOp) { case 0: if(tempval == data->iValue) return AI_SUCCESS; break; case 1: if(tempval > data->iValue) return AI_SUCCESS; break; case 2: if(tempval >= data->iValue) return AI_SUCCESS; break; case 3: if(tempval < data->iValue) return AI_SUCCESS; break; case 4: if(tempval <= data->iValue) return AI_SUCCESS; break; case 10: if(tempval != data->iValue) return AI_SUCCESS; break; default: return AI_FAILURE; break; } //if(entity->_EntityType != ENTITY_NPC) return AI_FAILURE; //CNpc* thisNpc = reinterpret_cast(entity); //thisNpc = thisNpc->SelectedNpc; //if(thisNpc == NULL) return AI_FAILURE; //if(OperateValues(data->btOp, &VarValue, data->iValue & 0xFFFF)) return AI_SUCCESS; return AI_FAILURE; } //Check Variable (2) (WorldVAR) //PY should work just like NPC ObjVar. To Be Coded later AICOND(015) { //word nVarIDX; //Pos: 0x00 //byte btOp; //Pos: 0x08 //dword iValue; //Pos: 0x04 //Check WorldVAR return AI_FAILURE; } //Check Variable (3) (EconomyVar) AICOND(016) { //word nVarIDX; //Pos: 0x00 //dword iValue; //Pos: 0x04 //byte btOp; //Pos: 0x08 //Check EconomyVar return AI_FAILURE; } //Select ObjVar reference AICOND(017) { GETAICONDDATA(017); //Log(MSG_DEBUG,"Setting NPC ref number AICOND(017)"); CMonster* monster = reinterpret_cast(entity); if(monster == NULL) return AI_FAILURE; monster->thisnpc->refNPC = data->iNpcNo; // sets the reference variable for the correct ObjVar //Log(MSG_DEBUG,"Set NPC ref number %i successfully", monster->thisnpc->refNPC); return AI_SUCCESS; } //Check Distance (3) AICOND(018) { //dword iDistance; //Pos: 0x00 //byte btOp; //Pos: 0x04 //Distance to "CALLER" ??Possibly the one who summoned the monster?? GETAICONDDATA(018); if(!entity->IsMonster( )) return AI_FAILURE; //Log(MSG_DEBUG, "AICOND(018 1)"); 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; // I think this is wrong. It should return true if there is no owner. Otherwise orphaned bonfires will live forever if(thisMonster->owner == 0) return AI_FAILURE; //Monster has no owner //Log(MSG_DEBUG, "AICOND(018 2)"); //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(); int distance = (int)GServer->distance( caller->Position->current, thisMonster->Position->current ); //int distance = thisMonster->basic.pos.distance(caller->basic.pos); //Log(MSG_DEBUG, "AICOND(018 3)"); if(OperateValues(data->btOp, &distance, data->iDistance)) return AI_SUCCESS; //Log(MSG_DEBUG, "AICOND(018 4)"); return AI_FAILURE; } //Check Time (2) AICOND(019) { //dword ulTime; //Pos: 0x00 //dword ulEndTime; //Pos: 0x04 //Get_ZoneTIME between ulTime and ulEndTime GETAICONDDATA(019); CMonster* thisMonster = reinterpret_cast(entity); CMap* map = GServer->MapList.Index[thisMonster->Position->Map]; int StartTime = int(data->ulTime); int EndTime = int(data->ulEndTime); if(map->ZoneTime >= StartTime && map->ZoneTime <= EndTime) return AI_SUCCESS; return AI_FAILURE; } //Check My AB (4) NOT target's AICOND(020) { //byte btAbType; //Pos: 0x00 //dword iValue; //Pos: 0x04 //byte btOp; //Pos: 0x08 //cAbType //0 = Level //1 = Attack //2 = Defense //3 = "Res"? Magic Resistance? //4 = HP //5 = Charm GETAICONDDATA(020); //Log(MSG_DEBUG, "Check AB (4) type %i iValue %i", data->btAbType, data->iValue); int value = AI_GetAbility(entity, data->btAbType); if(value < 0) return AI_FAILURE; if(OperateValues(data->btOp, &value, data->iValue)) return AI_SUCCESS; //Log(MSG_INFO,"check ab (4) successfull"); return AI_FAILURE; } //Do I have a caller //PY. Needs to return AI_SUCCESS if I have no owner AICOND(021) { //Do i have a "CALLER" ??Possibly the one who summoned the monster?? // more like Am I an orphan? GETAICONDDATA(021); if(!entity->IsMonster())return AI_SUCCESS; CMonster* thisMonster = reinterpret_cast(entity); if(thisMonster->owner == 0) return AI_SUCCESS; //Log(MSG_DEBUG, "AICOND(021) Yes I Have no owner"); return AI_FAILURE; } //Does my caller have a target AICOND(022) { //Does my "CALLER" have a target? //Log(MSG_DEBUG, "AICOND(022) Does my `CALLER` have a target?"); GETAICONDDATA(022); if(!entity->IsMonster( )) return AI_FAILURE; //Log(MSG_DEBUG, "AICOND(022 1)"); 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; //Log(MSG_DEBUG, "AICOND(022 2)"); CCharacter* target = map->GetCharInMap(caller->Battle->target); if(target == entity) return AI_FAILURE; if(target != NULL) return AI_SUCCESS; //Log(MSG_DEBUG, "AICOND(022 3)"); return AI_FAILURE; } //Check Time (3) (World time?) AICOND(023) { //dword ulTime; //Pos: 0x00 //dword ulEndTime; //Pos: 0x04 //Get_WorldTIME between ulTime and ulEndTime return AI_FAILURE; } //Check Date Time (4) AICOND(024) { //Log(MSG_INFO,"AIP Cdt 024 for aipId %i",AipId); GETAICONDDATA(024); //byte btDate Pos: 0x00 //byte btHour1 Pos: 0x01 //byte btMin1 Pos: 0x02 //byte btHour2 Pos: 0x03 //byte btMin2 Pos: 0x04 SYSTEMTIME sTIME; GetLocalTime(&sTIME); if(data->btDate != 0) { if(sTIME.wDay != data->btDate) return AI_FAILURE; } word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); word wFrom = (data->btHour1 * 60) + data->btMin1; word wTo = (data->btHour2 * 60) + data->btMin2; if(wMin >= wFrom && wMin <= wTo) return AI_SUCCESS; return AI_FAILURE; } //Check Weekday Time (5) AICOND(025) { GETAICONDDATA(025); //byte btWweekDay Pos: 0x00 //byte btHour1 Pos: 0x01 //byte btMin1 Pos: 0x02 //byte btHour2 Pos: 0x03 //byte btMin2 Pos: 0x04 SYSTEMTIME sTIME; GetLocalTime(&sTIME); if(data->btWeekDay != sTIME.wDayOfWeek) return AI_FAILURE; word wMin = ((sTIME.wHour * 60) + sTIME.wMinute); word wFrom = (data->btHour1 * 60) + data->btMin1; word wTo = (data->btHour2 * 60) + data->btMin2; if(wMin > wFrom && wMin < wTo) return AI_SUCCESS; return AI_FAILURE; } //Check Server/Channel AICOND(026) { GETAICONDDATA(026); //channel >= min && channel <= max //return AI_SUCCESS; return AI_FAILURE; } //Check Near Character AICOND(027 ){ //dword iDistance; //Pos: 0x00 //byte btIsAllied; //Pos: 0x04 //word nLevelDiff; //Pos: 0x06 //word nLevelDiff2; //Pos: 0x08 //word wChrNum; //Pos: 0x0a //byte btOp; //Pos: 0x0c //count how many aiobj between nLevelDiff and nLevelDiff2 who is btIsAllied within iDistance //must be (btOp) wChrNum GETAICONDDATA(027); short chrCount = 0; dword eCount = 0; int nearestDistance = 9999999; int searchDistance = data->iDistance;// * 10; CMap* map = GServer->MapList.Index[entity->Position->Map]; dword entityCount = map->MonsterList.size(); if(data->btIsAllied == 1) { 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; int levelDiff = (entity->Stats->Level - other->Stats->Level); if(levelDiff < data->nLevelDiff) continue; if(levelDiff > data->nLevelDiff2) continue; int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; chrCount++; if(iDistance < nearestDistance) { nearestDistance = iDistance; entity->nearChar = other; } } entity->findChar = entity->nearChar; if(OperateValues(data->btOp, &chrCount, data->wChrNum)) return AI_SUCCESS; } else { for(UINT j=0;jPlayerList.size();j++) { CPlayer* other = map->PlayerList.at(j); if(eCount >= entityCount) break; if(other == NULL) continue; eCount++; if(other == entity) continue; int levelDiff = (entity->Stats->Level - other->Stats->Level); if(levelDiff < data->nLevelDiff) continue; if(levelDiff > data->nLevelDiff2) continue; int iDistance = (int)GServer->distance( other->Position->current, entity->Position->current ); if(iDistance > searchDistance) continue; chrCount++; if(iDistance < nearestDistance){ nearestDistance = iDistance; entity->nearChar = other; } } entity->findChar = entity->nearChar; if(OperateValues(data->btOp, &chrCount, data->wChrNum)) return AI_SUCCESS; } ////Log(MSG_INFO,"AICOND(027) failure"); return AI_FAILURE; } //Check Variable (4) AICOND(028) { //word nVarIDX; //Pos: 0x00 //byte btOp; //Pos: 0x08 //dword iValue; //Pos: 0x04 //Get_AiVAR GETAICONDDATA(028); CMonster* thisMonster = reinterpret_cast(entity); if(data->nVarIDX >19)return AI_FAILURE; UINT tempval = thisMonster->AIVar[data->nVarIDX]; switch(data->btOp) { case 0: if(tempval == data->iValue) return AI_SUCCESS; break; case 1: if(tempval > data->iValue) return AI_SUCCESS; break; case 2: if(tempval >= data->iValue) return AI_SUCCESS; break; case 3: if(tempval < data->iValue) return AI_SUCCESS; break; case 4: if(tempval <= data->iValue) return AI_SUCCESS; break; case 10: if(tempval != data->iValue) return AI_SUCCESS; break; default: return AI_FAILURE; break; } return AI_FAILURE; } //Check Target (2) AICOND(029) { //byte btTargetType; //Pos: 0x00 //UNKNOWN :@ GETAICONDDATA(029); //Log(MSG_DEBUG,"aicon 029 check target type %i",data->btTargetType); return AI_FAILURE; } //Timer since spawn (seconds). AICOND(030) { GETAICONDDATA(030); //dword Timer; //Pos: 0x00 timer. //Timer is stored in seconds so we need to multiply it by 1000 CMonster* thisMonster = reinterpret_cast(entity); clock_t etime = clock() - thisMonster->SpawnTime; if(etime > (data->Timer * 1000)) return AI_SUCCESS; return AI_FAILURE; } //from here on this is 100% server sided. AIP scripts with these elements in should not be uploaded to the client // TDEF code. Check current position against Destination AICOND(031) { return AI_FAILURE; }