Page 1 of 4

AIP Actions

PostPosted: Thu May 30, 2019 4:38 pm
by PurpleYouko
This thread is all about AIP actions, what they do and how to implement them

Starting with

AIP_REWD_000

  1. //Stop all actions
  2. AIP_REWD_000
  3.     entity->CurrentAction->Clear ( )                        //Clear current actions
  4.     entity->Position->destiny = entity->Position->current   //Set position destiny to exactly where I am right now so I stop moving
  5.     return AI_SUCCESS


As stated at the top of the function, this command stops all actions.
First of all we clear current actions in the entity->CurrentAction[b] class. This is a new class that has been added in project 137. It is used to control all skills and actions that a player or monster may be involved in. It works as a state machine in a similar way to the way that [b]Battle does in the standard osrose server but is a lot more streamlined and efficient.
Next we make sure that the entity stops moving completely by setting its destination (destiny) position equal to its present location.

Re: AIP Actions

PostPosted: Thu May 30, 2019 4:46 pm
by PurpleYouko
AIP_REWD_001

  1. //Set Motion
  2. //This action causes the entity to perform an animation by sending an 0x0781 packet to the client containing the action code
  3. AIP_REWD_001
  4.     uint8 cAction       //Byte 0
  5.     CPacket pak     ( 0x0781 )                      //Initialize the packet
  6.     pak.addUINT16   ( cAction )                     //Add the action code
  7.     pak.addUINT16   ( 0 )                           //Don't ask.. it's complicated. Just leave it zero
  8.     pak.addUINT16   ( entity->clientid )            //Add clientid so the client knows who to animate
  9.     GServer->SendToVisible( &pak, entity, NULL )    //Show everybody the animation
  10.     return AI_SUCCESS


Sets an animation for some function. Most often used to animate a monster casting a spell
Here is an example of this REWD function in use
turak_aip.jpg

In this case mr. Turak is summoning a monster to fight at his side

For further information this is a list of possible values that can be used in this function
  1. enum MonsterAnimations {
  2.     MOB_ANI_STOP                = 0,
  3.     MOB_ANI_MOVE                = 1,
  4.     MOB_ANI_ATTACK              = 2,
  5.     MOB_ANI_HIT                 = 3,
  6.     MOB_ANI_DIE                 = 4,
  7.     MOB_ANI_RUN                 = 5,
  8.     MOB_ANI_CASTING01           = 6,
  9.     MOB_ANI_SKILL_ACTION01      = 7,
  10.     MOB_ANI_CASTINGN02          = 8,
  11.     MOB_ANI_SKILL_ACTION02      = 9,
  12.     MOB_ANI_ETC                 = 10,
  13.     MAX_MOB_ANI                 = 11
  14. };


Yes we pretty much made all variables like this into enums.

Re: AIP Actions

PostPosted: Thu May 30, 2019 4:50 pm
by PurpleYouko
AIP_REWD_002

  1. //Say LTB String
  2. AIP_REWD_002
  3.     uint32 iStrID       //Byte 0
  4.     // We don't have to deal with this at all in the server. The client does this on it's own
  5.     return AI_SUCCESS


This one makes the entity say something that has been previously defined in an LTB file in the client.
That's right. It's used 100% client side. The server currently has no way to tell the client what LTB string to use. Possibly in the future we could change this but for now just make sure your AIP files are loaded into both server and client.

Re: AIP Actions

PostPosted: Thu May 30, 2019 5:05 pm
by PurpleYouko
AIP_REWD_003

  1. //Move (walk or run) to a random location within iDistance of my present location
  2. AIP_REWD_003
  3.     uint32 iDistance    //Byte 0
  4.     uint8 cSpeed        //Byte 4
  5.     entity->Position->destiny = Rose::Math::RandInCircle(entity->Position->current, iDistance) //Find a random point in a circle around present location
  6.     entity->thisnpc->stance = cSpeed                //Set the movement speed. Walk or run
  7.     CPacket pak ( 0x0797 )                          //Initialize movement packet
  8.     pak.addUINT16( entity->clientid );              //my id
  9.     pak.addUINT16( 0x0000 );                        //Destination char id. This packet can be used to move to another entity's position
  10.     pak.addUINT16 ( entity->Stats->MoveSpeed )      //PY This is wrong. This should be distance to the target char if it exists. Same for every instance of 797 packet. Come back to this later
  11.     pak.addFloat32( entity->Position->destiny.x * 100 ) //Destination position X
  12.     pak.addFloat32( entity->Position->destiny.y * 100 ) //Destination position Y
  13.     pak.addUINT16 ( 0xcdcd )                        //This is a useless placeholder but if you leave it out it all goes Blehhh. Technically it is the Z position but that isn't actually used so...
  14.     pak.addUINT8( entity->thisnpc->stance )         //Walk, run or whatever. I'm sure you get that by now
  15.     GServer->SendToVisible(&pak, entity)
  16.     return AI_SUCCESS


pretty straight forward move command. Nothing mind boggling here

Re: AIP Actions

PostPosted: Thu May 30, 2019 5:09 pm
by PurpleYouko
AIP_REWD_004

  1. //Move (walk or run) to a random location within iDistance of my spawn location
  2. AIP_REWD_004
  3.     uint32 iDistance    //Byte 0
  4.     uint8 cSpeed        //Byte 4
  5.     entity->Position->destiny = Rose::Math::RandInCircle(entity->Position->source, iDistance) //Find a random point in a circle around source location
  6.     entity->thisnpc->stance = cSpeed                //Set the movement speed. Walk or run
  7.     CPacket pak ( 0x0797 )                          //Initialize movement packet
  8.     pak.addUINT16( entity->clientid );              //my id
  9.     pak.addUINT16( 0x0000 );                        //Destination char id. This packet can be used to move to another entity's position
  10.     pak.addUINT16 ( entity->Stats->MoveSpeed )      //PY This is wrong. This should be distance to the target char if it exists. Same for every instance of 797 packet. Come back to this later
  11.     pak.addFloat32( entity->Position->destiny.x * 100 ) //Destination position X
  12.     pak.addFloat32( entity->Position->destiny.y * 100 ) //Destination position Y
  13.     pak.addUINT16 ( 0xcdcd )                        //This is a useless placeholder but if you leave it out it all goes Blehhh. Technically it is the Z position but that isn't actually used so...
  14.     pak.addUINT8( entity->thisnpc->stance )         //Walk, run or whatever. I'm sure you get that by now
  15.     GServer->SendToVisible(&pak, entity)
  16.     return AI_SUCCESS


Another movement command.
Almost identical to the last one except that this one moves to a random position within a circle centered on the entity's spawn position rather than current position

Re: AIP Actions

PostPosted: Thu May 30, 2019 5:25 pm
by PurpleYouko
AIP_REWD_005

  1. //Move (walk or run) to a random location within iDist of my findChar location
  2. AIP_REWD_005
  3.     //First thing to do is go grab that findChar entity and find out where it is
  4.     //Hereafter to be know as "target"
  5.     CCharacter* target = GServer->GetClientByID ( entity->findChar, entity->Position->Map ) 
  6.     if ( target == NULL )       //Target doesn't exist so return success
  7.         return AI_SUCCESS
  8.    
  9.     uint8 cSpeed = GetUint8     //Byte 0
  10.     //move randomly within 200 of "Target"  because the actual distance isn't specified in the AIP.... LAME
  11.     int iDist = 2                                   //Get it to our coord system!
  12.     entity->Position->destiny = Rose::Math::RandInCircle(target->Position->current, iDist)  //Select a random location
  13.     entity->thisnpc->stance = cSpeed                //Set the movement speed. Walk or run
  14.     CPacket pak ( 0x0797 )                          //Initialize movement packet
  15.     pak.addUINT16( entity->clientid );              //my id
  16.     pak.addUINT16( 0x0000 );                        //Destination char id. This packet can be used to move to another entity's position
  17.     pak.addUINT16 ( entity->Stats->MoveSpeed )      //PY This is wrong. This should be distance to the target char if it exists. Same for every instance of 797 packet. Come back to this later
  18.     pak.addFloat32( entity->Position->destiny.x * 100 ) //Destination position X
  19.     pak.addFloat32( entity->Position->destiny.y * 100 ) //Destination position Y
  20.     pak.addUINT16 ( 0xcdcd )                        //This is a useless placeholder but if you leave it out it all goes Blehhh. Technically it is the Z position but that isn't actually used so...
  21.     pak.addUINT8( entity->thisnpc->stance )         //Walk, run or whatever. I'm sure you get that by now
  22.     GServer->SendToVisible(&pak, entity)
  23.     return AI_SUCCESS


Another movement function
If I remember correctly this function is simply redirected to point at AIP_REWD_002 in standard osrose servers.
Nope!!
This one uses one of the Reference entities that we looked at in the AIP Conditions.
we move to a random position in a circle around "findChar" which we found and set in the conditions file. Isn't that fun??

You might have noticed that in the last 3 actions I have included a comment that using entity->Stats->MoveSpeed in the packet is wrong.
The reason that I have never actually bothered to fix it is that it is largely irrelevent since i have yet to actually find any place in any AIP file where they actually set a destination character. No point bothering to send the distance to a point which doesn't exist since the client is going to completely ignore anything in that slot anyhow.
So basically.... Meh. Whatever. It will get fixed when fixing it isn't a waste of time

Re: AIP Actions

PostPosted: Thu May 30, 2019 5:40 pm
by PurpleYouko
AIP_REWD_006

  1. //Run and Attack enemy within iDistance that has the lowest or greatest cAbType
  2. AIP_REWD_006
  3.     //cAbType
  4.     //0 = Level
  5.     //1 = Attack
  6.     //2 = Defense
  7.     //3 = "Res"? Magic Resistance?
  8.     //4 = HP
  9.     //5 = Charm
  10.     uint32 iDistance    //Byte 0
  11.     uint8 cAbType       //Byte 4
  12.     uint8 cMoreLess     //Byte 5
  13.     dword eCount = 0    //Initialize a few variables for use in finding lowest and highest stuff
  14.     int32 highestAB = -999
  15.     int lowestAB = 999
  16.     CMap* map = GServer->MapList.Index[entity->Position->Map]   //Set current map
  17.     CCharacter* Target = NULL                                   //Initialize a NULL target entity
  18.     // TODO later. This should also check for other enemies. Any monster or summon that isn't on the same team
  19.     for( uint32 i = 0; i < map->PlayerList.size(); i++)         //Loop through the player list
  20.         CPlayer* player = map->PlayerList.at(i)
  21.         if(player == NULL)                                      //ignore NULL players
  22.             continue
  23.         if ( player->team == entity->team )                     //on the same team so ignore this player
  24.             continue
  25.         if(GServer->IsMonInCircle( entity->Position->current, player->Position->current, iDistance))
  26.             switch(cAbType)
  27.                 case 0:                                         //level
  28.                     if(cMoreLess == 0)
  29.                         if(player->Stats->Level > lowestAB)
  30.                             lowestAB = player->Stats->Level
  31.                             Target = player
  32.                         else continue
  33.                     else
  34.                         if(player->Stats->Level < highestAB)
  35.                             highestAB = player->Stats->Level
  36.                             Target = player
  37.                         else continue
  38.                 case 1:                                         //Attack power
  39.                     if(cMoreLess == 0)
  40.                         if(player->Stats->AttackPower > lowestAB)
  41.                             lowestAB = player->Stats->AttackPower
  42.                             Target = player
  43.                         else continue
  44.                     else
  45.                         if(player->Stats->AttackPower < highestAB)
  46.                             highestAB = player->Stats->AttackPower
  47.                             Target = player
  48.                         else continue
  49.                 case 2:                                         //defense
  50.                     if(cMoreLess == 0)
  51.                         if(player->Stats->Defense > lowestAB)
  52.                             lowestAB = player->Stats->Defense
  53.                             Target = player
  54.                         else continue
  55.                     else
  56.                         if(player->Stats->Defense < highestAB)
  57.                             highestAB = player->Stats->Defense
  58.                             Target = player
  59.                         else continue
  60.                 case 3:                                         //Magic Def
  61.                     if(cMoreLess == 0)
  62.                         if(player->Stats->MagicDefense> lowestAB)
  63.                             lowestAB = player->Stats->MagicDefense
  64.                             Target = player
  65.                         else continue
  66.                     else
  67.                         if(player->Stats->MagicDefense < (uint32)highestAB)
  68.                             highestAB = player->Stats->MagicDefense
  69.                             Target = player
  70.                         else continue
  71.                 case 4:                                         // HP
  72.                     if(cMoreLess == 0)
  73.                         if(player->Stats->HP> lowestAB)
  74.                             lowestAB = player->Stats->HP
  75.                             Target = player
  76.                         else continue
  77.                     else
  78.                         if(player->Stats->HP < highestAB)
  79.                             highestAB = player->Stats->HP
  80.                             Target = player
  81.                         else continue
  82.                 case 5:                                         // Charm
  83.                     if(cMoreLess == 0)
  84.                         if(player->Attr->Cha > lowestAB)
  85.                             lowestAB = player->Attr->Cha
  86.                             Target = player
  87.                         else continue
  88.                     else
  89.                         if(player->Attr->Cha < highestAB)
  90.                             highestAB = player->Attr->Cha
  91.                             Target = player
  92.                         else continue
  93.                 default:
  94.                     return AI_SUCCESS
  95.     // OK we found the new Target player. now attack him.
  96.     if (Target == NULL)
  97.         return AI_SUCCESS
  98.     if ( !entity->Status->isTaunted)
  99.         entity->CurrentAction->targetID = Target->clientid
  100.         entity->thisnpc->stance = mRUNNING
  101.         entity->StartAction( Target )
  102.     return AI_SUCCESS


Runs and attacks the ENEMY within a specified range who has the highest/lowest ability as defined in the AIP data array.
i think I might have a couple of errors in this code
Firstly Why the hell am I scanning only the player list? other monsters could be valid targets.
then there is this
if(GServer->IsMonInCircle( entity->Position->current, player->Position->current, iDistance))
IsMonInCircle ????
This code is specifically trying to find a monster in a list of players. Good luck with that

Also it would make the whole thing a lot more efficient to use the EntityList class to create a filtered list based on the defined criteria rather than do all these ifs and elses.
I wrote this code before that class was implemented and apparently while high on something because the logic is shit. lol :?

Re: AIP Actions

PostPosted: Thu May 30, 2019 5:47 pm
by PurpleYouko
AIP_REWD_007

  1. //Special attack
  2. AIP_REWD_007
  3.     return AI_SUCCESS


One of the biggest mysteries of my life is how special attacks work.
Anybody remember the Rackie Fart attack?
You are busy fighting a Rackie then it turns it's back on you and "Farts in your general direction". You see a cloud of poison gas and you get the poison debuff.
That's a special attack and I have never seen it work on any private server EVER.
AIP contains no useful code for it. Just this crap listed above. As far as I can tell AIP_REWD_007 is never even used in any AIP file
I've scoured the SHO server, the ROSE client, all the STBs and every other file format that i can get my hands on. Came up empty at every turn.
The SHO server contains no code for it. No packets, no instructions. NOTHING!

Re: AIP Actions

PostPosted: Thu May 30, 2019 8:26 pm
by PurpleYouko
AIP_REWD_008

  1. //move towards target. Stop at a specified precentage away from target
  2. AIP_REWD_008
  3.     uint32 iDist            //Byte 0
  4.     uint8 cSpeed            //Byte 4
  5.     CCharacter* target = entity->getCharTarget( )   //get my current target
  6.     if(target == NULL)
  7.         return AI_SUCCESS
  8.     float xDist = target->Position->current.x - entity->Position->current.x                 //Calculate distance in the X plane
  9.     float yDist = target->Position->current.y - entity->Position->current.y                 //Calculate distance in the Y plane
  10.     float nX = entity->Position->source.x + (xDist * iDist / 100)                           //Calculate iDist % of xDist
  11.     float nY = entity->Position->source.y + (yDist * iDist / 100)                           //Calculate iDist % of yDist
  12.     entity->Position->destiny.x = nX                    //Set destination position
  13.     entity->Position->destiny.y = nY
  14.     entity->thisnpc->stance = cSpeed                    //Set movement type. Run or Walk
  15.     entity->CurrentAction->Clear ( )                    //Clear current actions
  16.  
  17.     CPacket pak ( 0x0797 )                          //Initialize movement packet
  18.     pak.addUINT16( entity->clientid );              //my id
  19.     pak.addUINT16( 0x0000 );                        //Destination char id. This packet can be used to move to another entity's position
  20.     pak.addUINT16 ( entity->Stats->MoveSpeed )      //PY This is wrong. This should be distance to the target char if it exists. Same for every instance of 797 packet. Come back to this later
  21.     pak.addFloat32( entity->Position->destiny.x * 100 ) //Destination position X
  22.     pak.addFloat32( entity->Position->destiny.y * 100 ) //Destination position Y
  23.     pak.addUINT16 ( 0xcdcd )                        //This is a useless placeholder but if you leave it out it all goes Blehhh. Technically it is the Z position but that isn't actually used so...
  24.     pak.addUINT8( entity->thisnpc->stance )         //Walk, run or whatever. I'm sure you get that by now
  25.     GServer->SendToVisible(&pak, entity)
  26.     return AI_SUCCESS


More movement commands.
This time the entity moves in a straight line towards its target and stops iDist % of the way along that line.
eg. if distance is 100 and iDist is 30 then he would stop at a distance of 30 from the target

Re: AIP Actions

PostPosted: Thu May 30, 2019 8:30 pm
by PurpleYouko
AIP_REWD_009

  1. //Convert Monster to new monster
  2. AIP_REWD_009
  3.     uint16 wMonster         //Byte 0
  4.     CMap* map= GServer->MapList.Index[entity->Position->Map]    //Get current map because converting monsters is a MAP function
  5.     map->ConverToMonster(entity, wMonster, 1)                   //Convert monster type to the new value
  6.     return AI_SUCCESS


Morph one monster into another while leaving its client id intact.
Used for such things as turning a ghost seed into an actual ghost