The damage/heal information is packed into array of integers that expands with larger data (Supposedly to save bandwidth, as this is a very common packet). Original server emulator code done by Dyox does not have correct algorithm to encode damage information.
(Here is example of each field controlled individually)

Here is the algorithm (Can be improved for speed).
Code: Select all
public static List<byte> Pack(List<int> values)
{
List<byte> bytes = new List<byte>();
foreach (int val in values)
{
if (val == 0)
{
bytes.Add(0);
continue;
}
else if (val == 1)
{
bytes.Add(1);
continue;
}
else if (val == -1)
{
bytes.Add(2);
continue;
}
int signedValue = val;
if (val != 1)
{
if (val > 0)
signedValue = signedValue - 1;
else
signedValue = -signedValue;
}
int bitCount = BitCount(signedValue);
int bitCountRemaning = bitCount;
int octetCount = 0;
int c = 0;
while (bitCountRemaning > 0)
{
if (c == 0)
bitCountRemaning -= 6;
else
bitCountRemaning -= 7;
octetCount++;
}
int offset = 0;
int shift = 6;
for (int i = 0; i < octetCount; i++)
{
int mask = CreateMask(offset, offset + shift);
int octet = (mask & signedValue) >> offset;
if (i == 0)
{
octet = octet << 1; //LSB is the sign bit on first octet
if (val > 0)//if value was positive, we need to set sign flag
octet |= 1 << 0; //1 for positive
else
octet &= ~(1 << 0); //0 for negative
}
if (i != (octetCount - 1)) //if not on last octet, set expansion bit
octet |= 0x80;
bytes.Add((byte)octet);
offset += shift;
if (i == 0) //first octet we write 6 bits, then 7 bits on rest
shift = 7;
}
}
return bytes;
}
public void SendAbilityEffect(Client client, Ability ability, AbilityResult abilityResult, Character from, Character target,
int? hitAmount, int? absorbedAmount, int? mitigatedAmount, int? overhealedAmount)
{
MemoryStream stream = client.GetWriteBuffer();
PacketUtil.WriteByte(stream, (byte)Opcode.F_CAST_PLAYER_EFFECT);
PacketUtil.WriteUInt16(stream, (ushort)from.Client.CharacterID);
PacketUtil.WriteUInt16(stream, (ushort)target.Client.CharacterID);
PacketUtil.WriteUInt16(stream, (ushort)ability.ID);
if (ability.IsHeal)
PacketUtil.WriteByte(stream, (byte)0);
else
PacketUtil.WriteByte(stream, (byte)6);
PacketUtil.WriteByte(stream, (byte)abilityResult); // BLOCK = 4, PARRY = 5, EVADE = 6, DISRUPT = 7, MISS = 8, CRITICAL = 9
if (ability.IsHeal)
PacketUtil.WriteByte(stream, (byte)2);
else
{
if (absorbedAmount.HasValue && mitigatedAmount.HasValue)
if (hitAmount.Value == 0)
PacketUtil.WriteByte(stream, (byte)50);
else
PacketUtil.WriteByte(stream, (byte)43);
else if (absorbedAmount.HasValue && !mitigatedAmount.HasValue)
PacketUtil.WriteByte(stream, (byte)111);
else if (!absorbedAmount.HasValue && mitigatedAmount.HasValue)
PacketUtil.WriteByte(stream, (byte)122);
else
PacketUtil.WriteByte(stream, (byte)195);
}
List<int> numsToSend = new List<int>();
if (hitAmount.HasValue)
{
if (ability.IsHeal)
numsToSend.Add(-hitAmount.Value);
else
numsToSend.Add(hitAmount.Value);
if (ability.IsHeal && overhealedAmount.HasValue)
numsToSend.Add(-overhealedAmount.Value);
else
{
if (mitigatedAmount.HasValue && absorbedAmount.HasValue)
{
numsToSend.Add(-mitigatedAmount.Value);
numsToSend.Add(-absorbedAmount.Value);
}
else if (mitigatedAmount.HasValue && !absorbedAmount.HasValue)
{
numsToSend.Add(-mitigatedAmount.Value);
}
else if (!mitigatedAmount.HasValue && absorbedAmount.HasValue)
{
numsToSend.Add(0);
numsToSend.Add(-absorbedAmount.Value);
}
}
}
foreach (byte octet in PacketUtil.Pack(numsToSend))
PacketUtil.WriteByte(stream, octet);
PacketUtil.WriteByte(stream, 0x00);
client.SendPacket("F_CAST_PLAYER_EFFECT", stream, false, false);
}
private static int BitCount(int value)
{
int bitCount = 0;
int r = value;
while (r > 0)
{
r /= 2;
bitCount++;
}
return bitCount;
}
private static int CreateMask(int a, int b)
{
int r = 0;
for (int i = a; i <= b; i++)
r |= 1 << i;
return r;
}