1079 lines
31 KiB
C++
1079 lines
31 KiB
C++
// 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;j<map->MonsterList.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;j<map->PlayerList.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<CMonster*>(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;j<map->MonsterList.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;j<map->PlayerList.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;j<map->MonsterList.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;j<map->PlayerList.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<CMonster*>(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<CNpc*>(entity);
|
|
//thisNpc = thisNpc->SelectedNpc;
|
|
//if(thisNpc == NULL) return AI_FAILURE;
|
|
//if(OperateValues<short>(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<CMonster*>(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<CMonster*>(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<CMonster*>(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<int>(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<CMonster*>(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<int>(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<CMonster*>(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<CMonster*>(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;j<map->MonsterList.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<short>(data->btOp, &chrCount, data->wChrNum)) return AI_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
for(UINT j=0;j<map->PlayerList.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<short>(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<CMonster*>(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<CMonster*>(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;
|
|
}
|
|
|
|
|