2016-08-22 10:43:04 -04:00
using FFXIVClassic.Common ;
2017-11-11 13:46:12 -05:00
using FFXIVClassic_Map_Server.actors.chara.player ;
2016-12-20 19:17:50 -05:00
using FFXIVClassic_Map_Server.actors.group ;
2016-01-20 23:18:10 -05:00
using FFXIVClassic_Map_Server.Actors.Chara ;
2017-11-11 13:46:12 -05:00
using FFXIVClassic_Map_Server.dataobjects ;
2016-01-02 14:04:45 -05:00
using FFXIVClassic_Map_Server.packets.send.actor ;
2017-12-10 15:13:33 -05:00
using FFXIVClassic_Map_Server.packets.send.actor.inventory ;
2017-04-15 16:33:56 -04:00
using FFXIVClassic_Map_Server.utils ;
2017-06-12 03:50:02 +01:00
using FFXIVClassic_Map_Server.actors.chara.ai ;
2017-06-07 00:46:32 +01:00
using System ;
2017-07-15 19:33:47 +01:00
using System.Collections.Generic ;
2017-08-24 05:12:30 +01:00
using FFXIVClassic_Map_Server.actors.chara ;
using FFXIVClassic_Map_Server.packets.send.actor.battle ;
2017-08-28 04:45:20 +01:00
using FFXIVClassic_Map_Server.actors.chara.ai.state ;
2017-08-29 01:15:12 +01:00
using FFXIVClassic_Map_Server.actors.chara.ai.utils ;
2017-09-16 02:50:32 +01:00
using FFXIVClassic_Map_Server.actors.chara.npc ;
2016-01-02 14:04:45 -05:00
2016-01-20 23:18:10 -05:00
namespace FFXIVClassic_Map_Server.Actors
2016-01-02 14:04:45 -05:00
{
2017-07-11 01:54:15 +01:00
/// <summary> Which Character types am I friendly with </summary>
enum CharacterTargetingAllegiance
{
/// <summary> Friendly to BattleNpcs </summary>
2017-09-05 05:05:25 +01:00
BattleNpcs ,
/// <summary> Friendly to Players </summary>
Player
2017-07-11 01:54:15 +01:00
}
2017-09-16 02:50:32 +01:00
enum DamageTakenType
{
None ,
Attack ,
Magic ,
Weaponskill ,
Ability
}
2017-07-11 01:54:15 +01:00
class Character : Actor
2016-01-02 14:04:45 -05:00
{
2017-08-25 03:52:43 +01:00
public const int CLASSID_PUG = 2 ;
public const int CLASSID_GLA = 3 ;
public const int CLASSID_MRD = 4 ;
public const int CLASSID_ARC = 7 ;
public const int CLASSID_LNC = 8 ;
public const int CLASSID_THM = 22 ;
public const int CLASSID_CNJ = 23 ;
2017-10-11 14:46:24 +01:00
public const int CLASSID_CRP = 29 ;
public const int CLASSID_BSM = 30 ;
public const int CLASSID_ARM = 31 ;
public const int CLASSID_GSM = 32 ;
public const int CLASSID_LTW = 33 ;
public const int CLASSID_WVR = 34 ;
public const int CLASSID_ALC = 35 ;
public const int CLASSID_CUL = 36 ;
public const int CLASSID_MIN = 39 ;
public const int CLASSID_BTN = 40 ;
public const int CLASSID_FSH = 41 ;
2016-01-02 14:04:45 -05:00
public const int SIZE = 0 ;
public const int COLORINFO = 1 ;
public const int FACEINFO = 2 ;
public const int HIGHLIGHT_HAIR = 3 ;
public const int VOICE = 4 ;
2016-03-06 17:55:42 -05:00
public const int MAINHAND = 5 ;
public const int OFFHAND = 6 ;
public const int SPMAINHAND = 7 ;
public const int SPOFFHAND = 8 ;
public const int THROWING = 9 ;
public const int PACK = 10 ;
public const int POUCH = 11 ;
2016-01-02 14:04:45 -05:00
public const int HEADGEAR = 12 ;
public const int BODYGEAR = 13 ;
public const int LEGSGEAR = 14 ;
public const int HANDSGEAR = 15 ;
public const int FEETGEAR = 16 ;
public const int WAISTGEAR = 17 ;
2016-03-06 17:55:42 -05:00
public const int NECKGEAR = 18 ;
public const int L_EAR = 19 ;
public const int R_EAR = 20 ;
public const int R_WRIST = 21 ;
public const int L_WRIST = 22 ;
public const int R_RINGFINGER = 23 ;
public const int L_RINGFINGER = 24 ;
public const int R_INDEXFINGER = 25 ;
public const int L_INDEXFINGER = 26 ;
public const int UNKNOWN = 27 ;
2016-01-02 14:04:45 -05:00
2016-09-24 14:17:31 -04:00
public bool isStatic = false ;
2017-06-07 00:46:32 +01:00
public bool isMovingToSpawn = false ;
2017-08-31 05:56:43 +01:00
public bool isAutoAttackEnabled = true ;
2017-06-07 00:46:32 +01:00
2016-01-09 21:35:45 -05:00
public uint modelId ;
2016-03-06 17:55:42 -05:00
public uint [ ] appearanceIds = new uint [ 28 ] ;
2016-01-02 14:04:45 -05:00
2016-01-02 18:17:03 -05:00
public uint animationId = 0 ;
2017-10-03 07:32:32 +01:00
public uint currentTarget = Actor . INVALID_ACTORID ;
public uint currentLockedTarget = Actor . INVALID_ACTORID ;
2016-01-02 14:04:45 -05:00
2016-01-02 18:17:03 -05:00
public uint currentActorIcon = 0 ;
2016-01-02 14:04:45 -05:00
2016-01-09 23:22:10 -05:00
public Work work = new Work ( ) ;
2016-01-02 14:04:45 -05:00
public CharaWork charaWork = new CharaWork ( ) ;
2016-12-20 19:17:50 -05:00
public Group currentParty = null ;
2017-04-29 20:30:54 -04:00
public ContentGroup currentContentGroup = null ;
2019-05-06 15:59:09 -04:00
2017-08-02 23:06:11 +01:00
//public DateTime lastAiUpdate;
2017-06-09 04:17:08 +01:00
2017-06-12 03:50:02 +01:00
public AIContainer aiContainer ;
2017-07-15 19:33:47 +01:00
public StatusEffectContainer statusEffects ;
2017-06-12 03:50:02 +01:00
2017-07-11 01:54:15 +01:00
public CharacterTargetingAllegiance allegiance ;
2017-08-02 23:06:11 +01:00
public Pet pet ;
2018-04-18 16:06:41 -05:00
private Dictionary < Modifier , double > modifiers = new Dictionary < Modifier , double > ( ) ;
2017-08-25 03:52:43 +01:00
protected ushort hpBase , hpMaxBase , mpBase , mpMaxBase , tpBase ;
protected BattleTemp baseStats = new BattleTemp ( ) ;
public ushort currentJob ;
2017-08-31 05:56:43 +01:00
public ushort newMainState ;
public float spawnX , spawnY , spawnZ ;
2017-08-24 05:12:30 +01:00
2018-02-15 13:20:46 -06:00
//I needed some values I could reuse for random stuff, delete later
public int extraInt ;
public uint extraUint ;
public float extraFloat ;
2017-09-16 02:50:32 +01:00
protected Dictionary < string , UInt64 > tempVars = new Dictionary < string , UInt64 > ( ) ;
2019-05-06 15:59:09 -04:00
2017-11-11 13:46:12 -05:00
//Inventory
2018-04-07 14:48:43 -04:00
protected Dictionary < ushort , ItemPackage > itemPackages = new Dictionary < ushort , ItemPackage > ( ) ;
2019-06-02 16:57:46 -04:00
protected ReferencedItemPackage equipment ;
2017-09-16 02:50:32 +01:00
2017-12-04 22:58:18 -05:00
public Character ( uint actorID )
: base ( actorID )
2017-08-26 17:39:28 +01:00
{
2016-01-10 13:36:36 -05:00
//Init timer array to "notimer"
for ( int i = 0 ; i < charaWork . statusShownTime . Length ; i + + )
2018-02-15 13:20:46 -06:00
charaWork . statusShownTime [ i ] = 0 ;
2017-07-02 20:01:24 +01:00
2017-07-15 19:33:47 +01:00
this . statusEffects = new StatusEffectContainer ( this ) ;
2017-08-02 23:06:11 +01:00
// todo: move this somewhere more appropriate
2017-08-25 03:52:43 +01:00
// todo: base this on equip and shit
SetMod ( ( uint ) Modifier . AttackRange , 3 ) ;
2019-05-27 23:05:20 -07:00
SetMod ( ( uint ) Modifier . Delay , ( Program . Random . Next ( 30 , 60 ) * 100 ) ) ;
SetMod ( ( uint ) Modifier . MovementSpeed , ( uint ) moveSpeeds [ 2 ] ) ;
2017-08-31 05:56:43 +01:00
spawnX = positionX ;
spawnY = positionY ;
spawnZ = positionZ ;
2016-01-02 14:04:45 -05:00
}
2017-06-27 13:52:47 -04:00
public SubPacket CreateAppearancePacket ( )
2016-01-02 14:04:45 -05:00
{
2016-01-09 21:35:45 -05:00
SetActorAppearancePacket setappearance = new SetActorAppearancePacket ( modelId , appearanceIds ) ;
2017-06-27 13:52:47 -04:00
return setappearance . BuildPacket ( actorId ) ;
2016-01-02 18:17:03 -05:00
}
2017-06-27 13:52:47 -04:00
public SubPacket CreateInitStatusPacket ( )
2016-01-02 18:17:03 -05:00
{
2017-08-26 17:39:28 +01:00
return ( SetActorStatusAllPacket . BuildPacket ( actorId , charaWork . status ) ) ;
2016-01-02 14:04:45 -05:00
}
2017-06-27 13:52:47 -04:00
public SubPacket CreateSetActorIconPacket ( )
2016-01-02 14:04:45 -05:00
{
2017-06-27 13:52:47 -04:00
return SetActorIconPacket . BuildPacket ( actorId , currentActorIcon ) ;
2016-01-02 14:04:45 -05:00
}
2018-10-20 13:15:58 -04:00
public SubPacket CreateSubStatePacket ( )
2016-01-02 14:04:45 -05:00
{
2018-10-20 13:15:58 -04:00
return SetActorSubStatePacket . BuildPacket ( actorId , currentSubState ) ;
2016-01-02 14:04:45 -05:00
}
2016-06-14 21:29:10 +01:00
public void SetQuestGraphic ( Player player , int graphicNum )
2016-04-02 17:56:01 -04:00
{
2017-06-27 13:52:47 -04:00
player . QueuePacket ( SetActorQuestGraphicPacket . BuildPacket ( actorId , graphicNum ) ) ;
2016-04-02 17:56:01 -04:00
}
2017-06-25 14:25:54 -04:00
public void SetCurrentContentGroup ( ContentGroup group )
2017-04-15 16:33:56 -04:00
{
2017-04-29 20:30:54 -04:00
if ( group ! = null )
charaWork . currentContentGroup = group . GetTypeId ( ) ;
else
charaWork . currentContentGroup = 0 ;
currentContentGroup = group ;
2017-04-15 16:33:56 -04:00
2017-06-27 16:55:14 -04:00
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/currentContentGroup" , this ) ;
2017-08-26 17:39:28 +01:00
propPacketUtil . AddProperty ( "charaWork.currentContentGroup" ) ;
2017-06-25 14:25:54 -04:00
zone . BroadcastPacketsAroundActor ( this , propPacketUtil . Done ( ) ) ;
2017-07-27 03:58:42 +01:00
}
2018-04-18 16:06:41 -05:00
//This logic isn't correct, order of GetStatusEffects() is not necessarily the same as the actual effects in game. Also sending every time at once isn't needed
2017-07-27 22:19:20 +01:00
public List < SubPacket > GetActorStatusPackets ( )
{
var propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/status" , this ) ;
var i = 0 ;
foreach ( var effect in statusEffects . GetStatusEffects ( ) )
{
2018-02-15 13:20:46 -06:00
if ( ! effect . GetHidden ( ) )
{
2018-10-20 12:08:47 -04:00
propPacketUtil . AddProperty ( String . Format ( "charaWork.statusShownTime[{0}]" , i ) ) ;
2018-02-15 13:20:46 -06:00
propPacketUtil . AddProperty ( String . Format ( "charaWork.statusShownTime[{0}]" , i ) ) ;
i + + ;
}
2017-07-27 22:19:20 +01:00
}
return propPacketUtil . Done ( ) ;
}
2019-05-06 15:59:09 -04:00
2017-06-26 00:14:26 -04:00
public void PlayAnimation ( uint animId , bool onlySelf = false )
2017-08-26 17:39:28 +01:00
{
2017-06-26 00:14:26 -04:00
if ( onlySelf )
{
if ( this is Player )
2017-06-27 13:52:47 -04:00
( ( Player ) this ) . QueuePacket ( PlayAnimationOnActorPacket . BuildPacket ( actorId , animId ) ) ;
2017-06-26 00:14:26 -04:00
}
else
2017-06-27 13:52:47 -04:00
zone . BroadcastPacketAroundActor ( this , PlayAnimationOnActorPacket . BuildPacket ( actorId , animId ) ) ;
2017-05-01 22:30:43 -04:00
}
2019-05-06 15:59:09 -04:00
2017-08-28 21:45:01 -04:00
public void DoBattleAction ( ushort commandId , uint animationId )
2017-08-28 20:26:21 -04:00
{
2019-01-29 00:02:09 -05:00
zone . BroadcastPacketAroundActor ( this , CommandResultX00Packet . BuildPacket ( actorId , animationId , commandId ) ) ;
2017-08-28 21:45:01 -04:00
}
2019-05-04 18:53:00 -04:00
public void DoBattleAction ( ushort commandId , uint animationId , CommandResult result )
2017-08-28 21:45:01 -04:00
{
2019-05-04 18:53:00 -04:00
zone . BroadcastPacketAroundActor ( this , CommandResultX01Packet . BuildPacket ( actorId , animationId , commandId , result ) ) ;
2017-08-28 21:45:01 -04:00
}
2019-05-04 18:53:00 -04:00
public void DoBattleAction ( ushort commandId , uint animationId , CommandResult [ ] results )
2017-08-28 21:45:01 -04:00
{
int currentIndex = 0 ;
2019-05-29 19:46:29 -07:00
2017-08-28 21:45:01 -04:00
while ( true )
{
2019-05-04 18:53:00 -04:00
if ( results . Length - currentIndex > = 10 )
zone . BroadcastPacketAroundActor ( this , CommandResultX18Packet . BuildPacket ( actorId , animationId , commandId , results , ref currentIndex ) ) ;
else if ( results . Length - currentIndex > 1 )
zone . BroadcastPacketAroundActor ( this , CommandResultX10Packet . BuildPacket ( actorId , animationId , commandId , results , ref currentIndex ) ) ;
else if ( results . Length - currentIndex = = 1 )
2017-08-28 21:45:01 -04:00
{
2019-05-04 18:53:00 -04:00
zone . BroadcastPacketAroundActor ( this , CommandResultX01Packet . BuildPacket ( actorId , animationId , commandId , results [ currentIndex ] ) ) ;
2017-08-28 21:45:01 -04:00
currentIndex + + ;
}
else
break ;
}
2017-08-28 20:26:21 -04:00
}
2019-05-04 18:53:00 -04:00
public void DoBattleAction ( ushort commandId , uint animationId , List < CommandResult > results )
2017-08-28 20:26:21 -04:00
{
2018-04-18 16:06:41 -05:00
int currentIndex = 0 ;
2017-08-28 20:26:21 -04:00
while ( true )
{
2019-05-04 18:53:00 -04:00
if ( results . Count - currentIndex > = 10 )
zone . BroadcastPacketAroundActor ( this , CommandResultX18Packet . BuildPacket ( actorId , animationId , commandId , results , ref currentIndex ) ) ;
else if ( results . Count - currentIndex > 1 )
zone . BroadcastPacketAroundActor ( this , CommandResultX10Packet . BuildPacket ( actorId , animationId , commandId , results , ref currentIndex ) ) ;
else if ( results . Count - currentIndex = = 1 )
2017-08-28 20:26:21 -04:00
{
2019-05-04 18:53:00 -04:00
zone . BroadcastPacketAroundActor ( this , CommandResultX01Packet . BuildPacket ( actorId , animationId , commandId , results [ currentIndex ] ) ) ;
2017-08-28 20:26:21 -04:00
currentIndex + + ;
}
else
break ;
2018-04-18 16:06:41 -05:00
//Sending multiple packets at once causes some issues. Setting any combination of these to zero changes what breaks
//animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing.
//commandId = 0;
//sourceActorId = 0;
2017-08-28 20:26:21 -04:00
}
}
2017-08-28 04:45:20 +01:00
#region ai stuff
2017-06-07 00:46:32 +01:00
public void PathTo ( float x , float y , float z , float stepSize = 0.70f , int maxPath = 40 , float polyRadius = 0.0f )
{
2018-10-20 12:08:47 -04:00
if ( aiContainer ! = null & & aiContainer . pathFind ! = null )
aiContainer . pathFind . PreparePath ( x , y , z , stepSize , maxPath , polyRadius ) ;
2017-06-07 00:46:32 +01:00
}
2017-06-11 02:29:08 +01:00
public void FollowTarget ( Actor target , float stepSize = 1.2f , int maxPath = 25 , float radius = 0.0f )
2017-06-07 00:46:32 +01:00
{
2017-10-11 19:23:40 +01:00
if ( target ! = null )
PathTo ( target . positionX , target . positionY , target . positionZ , stepSize , maxPath , radius ) ;
2017-06-07 00:46:32 +01:00
}
2018-04-18 16:06:41 -05:00
public double GetMod ( Modifier modifier )
{
return GetMod ( ( uint ) modifier ) ;
}
public double GetMod ( uint modifier )
2017-08-24 05:12:30 +01:00
{
2018-04-18 16:06:41 -05:00
double res ;
2017-08-25 03:52:43 +01:00
if ( modifiers . TryGetValue ( ( Modifier ) modifier , out res ) )
return res ;
return 0 ;
2017-08-24 05:12:30 +01:00
}
2018-04-18 16:06:41 -05:00
public void SetMod ( uint modifier , double val )
2017-08-24 05:12:30 +01:00
{
if ( modifiers . ContainsKey ( ( Modifier ) modifier ) )
modifiers [ ( Modifier ) modifier ] = val ;
else
modifiers . Add ( ( Modifier ) modifier , val ) ;
2018-04-18 16:06:41 -05:00
2019-05-27 16:26:10 -07:00
if ( modifier > = 3 & & modifier < = 35 )
2018-04-18 16:06:41 -05:00
updateFlags | = ActorUpdateFlags . Stats ;
}
public void AddMod ( Modifier modifier , double val )
{
AddMod ( ( uint ) modifier , val ) ;
}
public void AddMod ( uint modifier , double val )
{
double newVal = GetMod ( modifier ) + val ;
SetMod ( modifier , newVal ) ;
}
public void SubtractMod ( Modifier modifier , double val )
{
AddMod ( ( uint ) modifier , val ) ;
}
public void SubtractMod ( uint modifier , double val )
{
double newVal = GetMod ( modifier ) - val ;
SetMod ( modifier , newVal ) ;
2017-08-24 05:12:30 +01:00
}
2019-05-27 16:26:10 -07:00
public void MultiplyMod ( Modifier modifier , double val )
{
MultiplyMod ( ( uint ) modifier , val ) ;
}
public void MultiplyMod ( uint modifier , double val )
{
double newVal = GetMod ( modifier ) * val ;
SetMod ( modifier , newVal ) ;
}
public void DivideMod ( Modifier modifier , double val )
{
DivideMod ( ( uint ) modifier , val ) ;
}
public void DivideMod ( uint modifier , double val )
{
double newVal = GetMod ( modifier ) / val ;
SetMod ( modifier , newVal ) ;
}
2017-08-02 23:06:11 +01:00
public virtual void OnPath ( Vector3 point )
2017-06-07 00:46:32 +01:00
{
2017-12-08 00:58:39 -06:00
//lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point);
2017-08-02 23:06:11 +01:00
updateFlags | = ActorUpdateFlags . Position ;
this . isAtSpawn = false ;
2017-06-07 00:46:32 +01:00
}
2017-06-18 22:01:55 +01:00
public override void Update ( DateTime tick )
2017-06-07 00:46:32 +01:00
{
2017-06-12 20:13:26 +01:00
2017-08-02 23:06:11 +01:00
}
2017-06-07 00:46:32 +01:00
2017-08-02 23:06:11 +01:00
public override void PostUpdate ( DateTime tick , List < SubPacket > packets = null )
{
if ( updateFlags ! = ActorUpdateFlags . None )
2017-06-07 00:46:32 +01:00
{
2017-08-02 23:06:11 +01:00
packets = packets ? ? new List < SubPacket > ( ) ;
2017-08-26 17:39:28 +01:00
2017-08-02 23:06:11 +01:00
if ( ( updateFlags & ActorUpdateFlags . Appearance ) ! = 0 )
2017-06-07 00:46:32 +01:00
{
2017-08-02 23:06:11 +01:00
packets . Add ( new SetActorAppearancePacket ( modelId , appearanceIds ) . BuildPacket ( actorId ) ) ;
}
2017-06-07 00:46:32 +01:00
2017-08-30 00:14:14 +01:00
if ( ( updateFlags & ActorUpdateFlags . State ) ! = 0 )
{
2018-10-20 13:02:14 -04:00
packets . Add ( SetActorStatePacket . BuildPacket ( actorId , currentMainState , 0x0 ) ) ;
2019-01-29 00:02:09 -05:00
packets . Add ( CommandResultX00Packet . BuildPacket ( actorId , 0x72000062 , 0 ) ) ;
packets . Add ( CommandResultX01Packet . BuildPacket ( actorId , 0x7C000062 , 21001 , new CommandResult ( actorId , 0 , 1 ) ) ) ;
2017-08-31 05:56:43 +01:00
updateFlags & = ~ ActorUpdateFlags . State ;
//DoBattleAction(21001, 0x7C000062, new BattleAction(this.actorId, 0, 1, 0, 0, 1)); //Attack Mode
2017-08-30 00:14:14 +01:00
}
2018-10-20 13:02:14 -04:00
if ( ( updateFlags & ActorUpdateFlags . SubState ) ! = 0 )
{
2019-06-01 02:10:40 -07:00
packets . Add ( SetActorSubStatePacket . BuildPacket ( actorId , currentSubState ) ) ;
2019-05-04 18:53:00 -04:00
//packets.Add(CommandResultX00Packet.BuildPacket(actorId, 0x72000062, 0));
//packets.Add(CommandResultX01Packet.BuildPacket(actorId, 0x7C000062, 21001, new CommandResult(actorId, 0, 1)));
2018-10-20 13:02:14 -04:00
updateFlags & = ~ ActorUpdateFlags . SubState ;
//DoBattleAction(21001, 0x7C000062, new BattleAction(this.actorId, 0, 1, 0, 0, 1)); //Attack Mode
}
2018-04-18 16:06:41 -05:00
if ( ( updateFlags & ActorUpdateFlags . Status ) ! = 0 )
{
2019-05-27 23:05:20 -07:00
2018-04-18 16:06:41 -05:00
List < SubPacket > statusPackets = statusEffects . GetStatusPackets ( ) ;
packets . AddRange ( statusPackets ) ;
statusPackets . Clear ( ) ;
updateFlags & = ~ ActorUpdateFlags . Status ;
}
if ( ( updateFlags & ActorUpdateFlags . StatusTime ) ! = 0 )
{
packets . AddRange ( statusEffects . GetStatusTimerPackets ( ) ) ;
2018-05-27 14:51:39 -05:00
statusEffects . ResetPropPacketUtil ( ) ;
2018-04-18 16:06:41 -05:00
updateFlags & = ~ ActorUpdateFlags . StatusTime ;
}
2017-08-02 23:06:11 +01:00
if ( ( updateFlags & ActorUpdateFlags . HpTpMp ) ! = 0 )
{
2017-12-08 00:58:39 -06:00
var propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/stateAtQuicklyForAll" , this ) ;
2017-12-10 09:20:42 -06:00
propPacketUtil . AddProperty ( "charaWork.parameterSave.hp[0]" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.hpMax[0]" ) ;
2017-08-02 23:06:11 +01:00
propPacketUtil . AddProperty ( "charaWork.parameterSave.mp" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterSave.mpMax" ) ;
propPacketUtil . AddProperty ( "charaWork.parameterTemp.tp" ) ;
packets . AddRange ( propPacketUtil . Done ( ) ) ;
2017-06-07 00:46:32 +01:00
}
2018-04-18 16:06:41 -05:00
2017-08-02 23:06:11 +01:00
base . PostUpdate ( tick , packets ) ;
2017-06-07 00:46:32 +01:00
}
}
2017-08-02 23:06:11 +01:00
2017-08-28 21:45:01 -04:00
public virtual bool IsValidTarget ( Character target , ValidTarget validTarget )
2017-08-26 04:08:26 +01:00
{
2017-10-11 14:46:24 +01:00
return ! target . isStatic ;
2017-08-26 04:08:26 +01:00
}
2017-08-02 23:06:11 +01:00
public virtual bool CanAttack ( )
2017-08-26 04:08:26 +01:00
{
return true ;
}
2019-05-29 19:46:29 -07:00
public virtual bool CanUse ( Character target , BattleCommand skill , CommandResult error = null )
2017-08-26 04:08:26 +01:00
{
return false ;
}
2017-07-07 22:08:48 +01:00
2017-08-02 23:06:11 +01:00
public virtual uint GetAttackDelayMs ( )
{
2019-05-27 23:05:20 -07:00
return ( uint ) GetMod ( ( uint ) Modifier . Delay ) ;
2017-08-25 03:52:43 +01:00
}
public virtual uint GetAttackRange ( )
{
2018-07-03 04:46:34 -05:00
return ( uint ) ( GetMod ( ( uint ) Modifier . AttackRange ) = = 0 ? 3 : GetMod ( ( uint ) Modifier . AttackRange ) ) ;
2017-07-07 22:08:48 +01:00
}
2017-08-31 05:56:43 +01:00
public virtual bool Engage ( uint targid = 0 , ushort newMainState = 0xFFFF )
2017-07-07 22:08:48 +01:00
{
2017-08-02 23:06:11 +01:00
// todo: attack the things
2018-04-18 16:06:41 -05:00
/ * if ( newMainState ! = 0xFFFF )
2017-08-26 04:08:26 +01:00
{
2018-04-18 16:06:41 -05:00
currentMainState = newMainState ; // this.newMainState = newMainState;
updateFlags | = ActorUpdateFlags . State ;
2017-08-26 04:08:26 +01:00
}
2018-04-18 16:06:41 -05:00
else * / if ( aiContainer . CanChangeState ( ) )
2017-08-02 23:06:11 +01:00
{
2017-08-31 05:56:43 +01:00
if ( targid = = 0 )
{
2017-10-03 07:32:32 +01:00
if ( currentTarget ! = Actor . INVALID_ACTORID )
2017-08-31 05:56:43 +01:00
targid = currentTarget ;
2017-10-03 07:32:32 +01:00
else if ( currentLockedTarget ! = Actor . INVALID_ACTORID )
2017-08-31 05:56:43 +01:00
targid = currentLockedTarget ;
}
//if (targid != 0)
{
aiContainer . Engage ( zone . FindActorInArea < Character > ( targid ) ) ;
}
2017-08-02 23:06:11 +01:00
}
2017-10-10 13:32:47 -05:00
2017-08-02 23:06:11 +01:00
return false ;
}
2017-07-07 22:08:48 +01:00
2017-09-16 02:50:32 +01:00
public virtual bool Engage ( Character target )
{
aiContainer . Engage ( target ) ;
return false ;
}
2017-08-31 05:56:43 +01:00
public virtual bool Disengage ( ushort newMainState = 0xFFFF )
2017-08-02 23:06:11 +01:00
{
2018-04-18 16:06:41 -05:00
/ * if ( newMainState ! = 0xFFFF )
2017-08-31 05:56:43 +01:00
{
2018-04-18 16:06:41 -05:00
currentMainState = newMainState ; // this.newMainState = newMainState;
updateFlags | = ActorUpdateFlags . State ;
2017-08-31 05:56:43 +01:00
}
2018-04-18 16:06:41 -05:00
else * / if ( IsEngaged ( ) )
2017-08-02 23:06:11 +01:00
{
aiContainer . Disengage ( ) ;
return true ;
}
return false ;
}
2019-05-06 15:59:09 -04:00
2017-08-31 05:56:43 +01:00
public virtual void Cast ( uint spellId , uint targetId = 0 )
2017-08-25 03:52:43 +01:00
{
2017-08-31 05:56:43 +01:00
if ( aiContainer . CanChangeState ( ) )
aiContainer . Cast ( zone . FindActorInArea < Character > ( targetId = = 0 ? currentTarget : targetId ) , spellId ) ;
2017-08-25 03:52:43 +01:00
}
2017-08-31 05:56:43 +01:00
public virtual void Ability ( uint abilityId , uint targetId = 0 )
2017-08-26 17:39:28 +01:00
{
2017-08-31 05:56:43 +01:00
if ( aiContainer . CanChangeState ( ) )
aiContainer . Ability ( zone . FindActorInArea < Character > ( targetId = = 0 ? currentTarget : targetId ) , abilityId ) ;
2017-08-26 17:39:28 +01:00
}
2017-08-31 05:56:43 +01:00
public virtual void WeaponSkill ( uint skillId , uint targetId = 0 )
2017-08-25 03:52:43 +01:00
{
2017-08-31 05:56:43 +01:00
if ( aiContainer . CanChangeState ( ) )
aiContainer . WeaponSkill ( zone . FindActorInArea < Character > ( targetId = = 0 ? currentTarget : targetId ) , skillId ) ;
2017-08-25 03:52:43 +01:00
}
2017-08-02 23:06:11 +01:00
public virtual void Spawn ( DateTime tick )
{
2017-09-03 01:01:19 +01:00
aiContainer . Reset ( ) ;
2017-08-02 23:06:11 +01:00
// todo: reset hp/mp/tp etc here
2017-08-25 03:52:43 +01:00
ChangeState ( SetActorStatePacket . MAIN_STATE_PASSIVE ) ;
2019-06-03 23:45:21 -07:00
SetHP ( ( uint ) GetMaxHP ( ) ) ;
SetMP ( ( uint ) GetMaxMP ( ) ) ;
2017-08-25 03:52:43 +01:00
RecalculateStats ( ) ;
2017-08-02 23:06:11 +01:00
}
2018-04-18 16:06:41 -05:00
//AdditionalActions is the list of actions that EXP/Chain messages are added to
2019-01-29 00:02:09 -05:00
public virtual void Die ( DateTime tick , CommandResultContainer actionContainer = null )
2017-08-02 23:06:11 +01:00
{
// todo: actual despawn timer
aiContainer . InternalDie ( tick , 10 ) ;
2017-12-08 00:58:39 -06:00
ChangeSpeed ( 0.0f , 0.0f , 0.0f , 0.0f ) ;
2017-07-07 22:08:48 +01:00
}
2017-08-31 05:56:43 +01:00
public virtual void Despawn ( DateTime tick )
2017-07-07 22:08:48 +01:00
{
2017-08-31 05:56:43 +01:00
2017-07-07 22:08:48 +01:00
}
2017-07-09 00:27:15 +01:00
public bool IsDead ( )
{
2017-09-16 02:50:32 +01:00
return ! IsAlive ( ) ;
2017-07-09 00:27:15 +01:00
}
public bool IsAlive ( )
{
2017-10-10 13:32:47 -05:00
return ! aiContainer . IsDead ( ) ; // && GetHP() > 0;
2017-07-09 00:27:15 +01:00
}
2017-08-02 23:06:11 +01:00
2017-08-25 03:52:43 +01:00
public short GetHP ( )
2017-08-02 23:06:11 +01:00
{
// todo:
2017-09-03 01:01:19 +01:00
return charaWork . parameterSave . hp [ 0 ] ;
2017-08-25 03:52:43 +01:00
}
public short GetMaxHP ( )
{
2017-09-03 01:01:19 +01:00
return charaWork . parameterSave . hpMax [ 0 ] ;
2017-08-25 03:52:43 +01:00
}
public short GetMP ( )
{
return charaWork . parameterSave . mp ;
}
public ushort GetTP ( )
{
return tpBase ;
}
public short GetMaxMP ( )
{
return charaWork . parameterSave . mpMax ;
}
public byte GetMPP ( )
{
return ( byte ) ( ( charaWork . parameterSave . mp / charaWork . parameterSave . mpMax ) * 100 ) ;
2017-08-02 23:06:11 +01:00
}
2017-08-25 03:52:43 +01:00
public byte GetTPP ( )
2017-08-02 23:06:11 +01:00
{
2017-08-25 03:52:43 +01:00
return ( byte ) ( ( tpBase / 3000 ) * 100 ) ;
2017-08-02 23:06:11 +01:00
}
2017-08-25 03:52:43 +01:00
public byte GetHPP ( )
2017-08-02 23:06:11 +01:00
{
2018-04-18 16:06:41 -05:00
return ( byte ) ( charaWork . parameterSave . hp [ 0 ] = = 0 ? 0 : ( charaWork . parameterSave . hp [ 0 ] / ( float ) charaWork . parameterSave . hpMax [ 0 ] ) * 100 ) ;
2017-08-02 23:06:11 +01:00
}
2017-09-12 01:24:02 +01:00
public void SetHP ( uint hp )
{
charaWork . parameterSave . hp [ 0 ] = ( short ) hp ;
if ( hp > charaWork . parameterSave . hpMax [ 0 ] )
SetMaxHP ( hp ) ;
updateFlags | = ActorUpdateFlags . HpTpMp ;
}
public void SetMaxHP ( uint hp )
{
charaWork . parameterSave . hpMax [ 0 ] = ( short ) hp ;
updateFlags | = ActorUpdateFlags . HpTpMp ;
}
2017-12-08 00:58:39 -06:00
public void SetMP ( uint mp )
{
2019-06-01 02:10:40 -07:00
charaWork . parameterSave . mp = ( short ) mp ;
2019-05-29 19:46:29 -07:00
if ( mp > charaWork . parameterSave . mpMax )
2017-12-08 00:58:39 -06:00
SetMaxMP ( mp ) ;
updateFlags | = ActorUpdateFlags . HpTpMp ;
}
public void SetMaxMP ( uint mp )
{
charaWork . parameterSave . mp = ( short ) mp ;
updateFlags | = ActorUpdateFlags . HpTpMp ;
}
2019-05-27 17:08:06 -07:00
2017-08-25 03:52:43 +01:00
// todo: the following functions are virtuals since we want to check hidden item bonuses etc on player for certain conditions
2019-05-27 17:08:06 -07:00
public virtual void AddHP ( int hp , CommandResultContainer resultContainer = null )
2017-08-02 23:06:11 +01:00
{
2018-04-18 16:06:41 -05:00
// dont wanna die ded, don't want to send update if hp isn't actually changed
if ( IsAlive ( ) & & hp ! = 0 )
2017-09-03 01:01:19 +01:00
{
// todo: +/- hp and die
// todo: battlenpcs probably have way more hp?
var addHp = charaWork . parameterSave . hp [ 0 ] + hp ;
2017-09-16 02:50:32 +01:00
addHp = addHp . Clamp ( ( short ) GetMod ( ( uint ) Modifier . MinimumHpLock ) , charaWork . parameterSave . hpMax [ 0 ] ) ;
2017-09-03 01:01:19 +01:00
charaWork . parameterSave . hp [ 0 ] = ( short ) addHp ;
2017-08-02 23:06:11 +01:00
2017-09-03 01:01:19 +01:00
updateFlags | = ActorUpdateFlags . HpTpMp ;
2019-05-27 17:08:06 -07:00
if ( charaWork . parameterSave . hp [ 0 ] < 1 )
Die ( Program . Tick , resultContainer ) ;
2017-09-03 01:01:19 +01:00
}
2017-08-02 23:06:11 +01:00
}
2017-12-08 00:58:39 -06:00
public short GetClass ( )
2017-09-12 01:24:02 +01:00
{
return charaWork . parameterSave . state_mainSkill [ 0 ] ;
}
public short GetLevel ( )
{
return charaWork . parameterSave . state_mainSkillLevel ;
}
2017-08-25 03:52:43 +01:00
public void AddMP ( int mp )
{
2018-04-18 16:06:41 -05:00
if ( IsAlive ( ) & & mp ! = 0 )
{
charaWork . parameterSave . mp = ( short ) ( charaWork . parameterSave . mp + mp ) . Clamp ( ushort . MinValue , charaWork . parameterSave . mpMax ) ;
2017-08-25 03:52:43 +01:00
2018-04-18 16:06:41 -05:00
// todo: check hidden effects and shit
2017-08-25 03:52:43 +01:00
2018-04-18 16:06:41 -05:00
updateFlags | = ActorUpdateFlags . HpTpMp ;
}
2017-08-25 03:52:43 +01:00
}
public void AddTP ( int tp )
{
2018-04-18 16:06:41 -05:00
if ( IsAlive ( ) & & tp ! = 0 )
2018-02-15 13:20:46 -06:00
{
2018-04-18 16:06:41 -05:00
var addTp = charaWork . parameterTemp . tp + tp ;
addTp = addTp . Clamp ( ( int ) GetMod ( Modifier . MinimumTpLock ) , 3000 ) ;
charaWork . parameterTemp . tp = ( short ) addTp ;
2018-02-15 13:20:46 -06:00
tpBase = ( ushort ) charaWork . parameterTemp . tp ;
updateFlags | = ActorUpdateFlags . HpTpMp ;
2017-10-10 13:32:47 -05:00
2018-02-15 13:20:46 -06:00
if ( tpBase > = 1000 )
lua . LuaEngine . GetInstance ( ) . OnSignal ( "tpOver1000" ) ;
}
2017-08-25 03:52:43 +01:00
}
2019-05-27 17:08:06 -07:00
public void DelHP ( int hp , CommandResultContainer resultContainer = null )
2017-08-02 23:06:11 +01:00
{
2019-05-27 17:08:06 -07:00
AddHP ( ( short ) - hp , resultContainer ) ;
2017-08-02 23:06:11 +01:00
}
2017-08-25 03:52:43 +01:00
public void DelMP ( int mp )
{
AddMP ( - mp ) ;
}
public void DelTP ( int tp )
{
AddTP ( - tp ) ;
}
2018-02-15 13:20:46 -06:00
virtual public void CalculateBaseStats ( )
2017-08-25 03:52:43 +01:00
{
// todo: apply mods and shit here, get race/level/job and shit
2017-12-08 00:58:39 -06:00
uint hpMod = ( uint ) GetMod ( ( uint ) Modifier . Hp ) ;
if ( hpMod ! = 0 )
2017-08-25 03:52:43 +01:00
{
2017-12-08 00:58:39 -06:00
SetMaxHP ( hpMod ) ;
uint hpp = ( uint ) GetMod ( ( uint ) Modifier . HpPercent ) ;
uint hp = hpMod ;
if ( hpp ! = 0 )
{
hp = ( uint ) Math . Ceiling ( ( ( float ) hpp / 100.0f ) * hpMod ) ;
}
SetHP ( hp ) ;
}
uint mpMod = ( uint ) GetMod ( ( uint ) Modifier . Mp ) ;
if ( mpMod ! = 0 )
{
SetMaxMP ( mpMod ) ;
uint mpp = ( uint ) GetMod ( ( uint ) Modifier . MpPercent ) ;
uint mp = mpMod ;
if ( mpp ! = 0 )
{
mp = ( uint ) Math . Ceiling ( ( ( float ) mpp / 100.0f ) * mpMod ) ;
}
SetMP ( mp ) ;
2017-08-25 03:52:43 +01:00
}
2017-08-02 23:06:11 +01:00
// todo: recalculate stats and crap
updateFlags | = ActorUpdateFlags . HpTpMp ;
2018-02-15 13:20:46 -06:00
SetMod ( ( uint ) Modifier . HitCount , 1 ) ;
}
public void RecalculateStats ( )
{
//CalculateBaseStats();
2017-08-02 23:06:11 +01:00
}
2018-04-18 16:06:41 -05:00
public void SetStat ( uint statId , int val )
2017-09-05 05:05:25 +01:00
{
2018-04-18 16:06:41 -05:00
charaWork . battleTemp . generalParameter [ statId ] = ( short ) val ;
2017-09-05 05:05:25 +01:00
}
2018-04-18 16:06:41 -05:00
public short GetStat ( uint statId )
2017-09-05 05:05:25 +01:00
{
return charaWork . battleTemp . generalParameter [ statId ] ;
}
2017-08-02 23:06:11 +01:00
public virtual float GetSpeed ( )
{
// todo: for battlenpc/player calculate speed
2019-05-27 23:05:20 -07:00
return ( float ) GetMod ( ( uint ) Modifier . MovementSpeed ) ;
2017-08-02 23:06:11 +01:00
}
2017-08-28 04:45:20 +01:00
2019-01-29 00:02:09 -05:00
public virtual void OnAttack ( State state , CommandResult action , ref CommandResult error )
2017-08-28 04:45:20 +01:00
{
2017-08-29 01:15:12 +01:00
var target = state . GetTarget ( ) ;
2018-02-15 13:20:46 -06:00
// todo: change animation based on equipped weapon
2017-08-29 01:15:12 +01:00
// todo: get hitrate and shit, handle protect effect and whatever
2017-08-28 21:45:01 -04:00
if ( BattleUtils . TryAttack ( this , target , action , ref error ) )
2017-08-29 01:15:12 +01:00
{
//var packet = BattleActionX01Packet.BuildPacket(owner.actorId, owner.actorId, target.actorId, (uint)0x19001000, (uint)0x8000604, (ushort)0x765D, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, (byte)0x1);
}
2017-08-31 05:56:43 +01:00
// todo: call onAttack/onDamageTaken
2018-04-18 16:06:41 -05:00
//BattleUtils.DamageTarget(this, target, DamageTakenType.Attack, action);
2018-02-15 13:20:46 -06:00
AddTP ( 200 ) ;
2017-10-10 13:32:47 -05:00
target . AddTP ( 100 ) ;
2017-08-28 04:45:20 +01:00
}
2019-01-29 00:02:09 -05:00
public virtual void OnCast ( State state , CommandResult [ ] actions , BattleCommand spell , ref CommandResult [ ] errors )
2017-08-28 04:45:20 +01:00
{
2017-09-03 01:01:19 +01:00
// damage is handled in script
2018-02-15 13:20:46 -06:00
var spellCost = spell . CalculateMpCost ( this ) ;
2017-12-08 00:58:39 -06:00
this . DelMP ( spellCost ) ; // mpCost can be set in script e.g. if caster has something for free spells
2017-09-03 01:01:19 +01:00
2019-01-29 00:02:09 -05:00
foreach ( CommandResult action in actions )
2018-02-15 13:20:46 -06:00
{
2018-10-20 12:08:47 -04:00
if ( zone . FindActorInArea < Character > ( action . targetId ) is Character )
2018-02-15 13:20:46 -06:00
{
//BattleUtils.HandleHitType(this, chara, action);
2018-04-18 16:06:41 -05:00
//BattleUtils.DoAction(this, chara, action, DamageTakenType.Magic);
2018-02-15 13:20:46 -06:00
}
}
2017-12-08 00:58:39 -06:00
lua . LuaEngine . GetInstance ( ) . OnSignal ( "spellUsed" ) ;
2017-08-28 04:45:20 +01:00
}
2019-01-29 00:02:09 -05:00
public virtual void OnWeaponSkill ( State state , CommandResult [ ] actions , BattleCommand skill , ref CommandResult [ ] errors )
2017-08-28 04:45:20 +01:00
{
2017-09-03 01:01:19 +01:00
// damage is handled in script
2019-01-29 00:02:09 -05:00
foreach ( CommandResult action in actions )
2018-02-15 13:20:46 -06:00
{
//Should we just store the character insteado f having to find it again?
2018-10-20 12:08:47 -04:00
if ( zone . FindActorInArea < Character > ( action . targetId ) is Character )
2018-02-15 13:20:46 -06:00
{
2018-04-18 16:06:41 -05:00
//BattleUtils.DoAction(this, chara, action, DamageTakenType.Weaponskill);
2018-02-15 13:20:46 -06:00
}
}
2017-12-08 00:58:39 -06:00
2018-02-15 13:20:46 -06:00
this . DelTP ( skill . tpCost ) ;
2017-08-28 04:45:20 +01:00
}
2019-01-29 00:02:09 -05:00
public virtual void OnAbility ( State state , CommandResult [ ] actions , BattleCommand ability , ref CommandResult [ ] errors )
2017-09-03 01:01:19 +01:00
{
2017-09-10 03:41:58 +01:00
foreach ( var action in actions )
2018-02-15 13:20:46 -06:00
{
2018-10-20 12:08:47 -04:00
if ( zone . FindActorInArea < Character > ( action . targetId ) is Character )
2018-02-15 13:20:46 -06:00
{
2018-04-18 16:06:41 -05:00
//BattleUtils.DoAction(this, chara, action, DamageTakenType.Ability);
2018-02-15 13:20:46 -06:00
}
}
2017-09-03 01:01:19 +01:00
}
public virtual void OnSpawn ( )
{
}
public virtual void OnDeath ( )
{
}
public virtual void OnDespawn ( )
2017-08-28 04:45:20 +01:00
{
2017-09-10 03:41:58 +01:00
}
2019-05-27 17:31:25 -07:00
public virtual void OnDamageDealt ( Character defender , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
2017-09-16 02:50:32 +01:00
{
2018-04-18 16:06:41 -05:00
switch ( action . hitType )
{
case ( HitType . Miss ) :
2019-05-27 17:31:25 -07:00
OnMiss ( defender , skill , action , actionContainer ) ;
break ;
case ( HitType . Crit ) :
OnCrit ( defender , skill , action , actionContainer ) ;
OnHit ( defender , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
break ;
default :
2019-05-27 17:31:25 -07:00
OnHit ( defender , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
break ;
}
2017-09-16 02:50:32 +01:00
2018-04-18 16:06:41 -05:00
//TP is only gained from autoattacks and abilities
2019-06-01 03:18:23 -07:00
if ( ( action . commandType = = CommandType . AutoAttack | | action . commandType = = CommandType . Ability ) & & action . hitType ! = HitType . Miss )
2018-04-18 16:06:41 -05:00
{
//TP gained on an attack is usually 100 * delay.
//Store TP seems to add .1% per point.
2019-05-27 23:05:20 -07:00
double weaponDelay = GetMod ( Modifier . Delay ) / 1000.0 ;
var storeTPPercent = 1 + ( GetMod ( Modifier . StoreTp ) * 0.001 ) ;
2018-04-18 16:06:41 -05:00
AddTP ( ( int ) ( weaponDelay * 100 * storeTPPercent ) ) ;
}
}
2019-05-27 17:31:25 -07:00
public virtual void OnDamageTaken ( Character attacker , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
2018-04-18 16:06:41 -05:00
{
switch ( action . hitType )
{
case ( HitType . Miss ) :
2019-05-27 17:31:25 -07:00
OnEvade ( attacker , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
break ;
case ( HitType . Parry ) :
2019-05-27 17:31:25 -07:00
OnParry ( attacker , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
break ;
case ( HitType . Block ) :
2019-05-27 17:31:25 -07:00
OnBlock ( attacker , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
break ;
}
2019-05-29 20:19:44 -07:00
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnDamageTaken , "onDamageTaken" , attacker , this , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
//TP gain formula seems to be something like 5 * e ^ ( -0.667 * [defender's level] ) * damage taken, rounded up
//This should be completely accurate at level 50, but isn't totally accurate at lower levels.
//Don't know if store tp impacts this
double tpModifier = 5 * Math . Pow ( Math . E , ( - 0.0667 * GetLevel ( ) ) ) ;
AddTP ( ( int ) Math . Ceiling ( tpModifier * action . amount ) ) ;
2017-09-16 02:50:32 +01:00
}
public UInt64 GetTempVar ( string name )
{
UInt64 retVal = 0 ;
if ( tempVars . TryGetValue ( name , out retVal ) )
return retVal ;
return 0 ;
}
// cause lua is a dick
public void SetTempVar ( string name , uint val )
{
if ( tempVars . ContainsKey ( name ) )
tempVars [ name ] = val ;
}
public void SetTempVar ( string name , UInt64 val )
{
if ( tempVars . ContainsKey ( name ) )
tempVars [ name ] = val ;
}
public void ResetTempVars ( )
{
tempVars . Clear ( ) ;
}
#region lua helpers
public bool IsEngaged ( )
{
return aiContainer . IsEngaged ( ) ;
}
public bool IsPlayer ( )
{
return this is Player ;
}
public bool IsMonster ( )
2017-09-10 03:41:58 +01:00
{
2017-10-11 19:23:40 +01:00
return this is BattleNpc ;
2017-09-16 02:50:32 +01:00
}
2017-09-10 03:41:58 +01:00
2017-09-16 02:50:32 +01:00
public bool IsPet ( )
{
return this is Pet ;
}
public bool IsAlly ( )
{
return this is Ally ;
2017-08-28 04:45:20 +01:00
}
2017-10-11 14:46:24 +01:00
public bool IsDiscipleOfWar ( )
{
2017-12-08 00:58:39 -06:00
return GetClass ( ) < CLASSID_THM ;
2017-10-11 14:46:24 +01:00
}
public bool IsDiscipleOfMagic ( )
{
2017-12-08 00:58:39 -06:00
return GetClass ( ) > = CLASSID_THM & & currentJob < CLASSID_CRP ;
2017-10-11 14:46:24 +01:00
}
public bool IsDiscipleOfHand ( )
{
2017-12-08 00:58:39 -06:00
return GetClass ( ) > = CLASSID_CRP & & currentJob < CLASSID_MIN ;
2017-10-11 14:46:24 +01:00
}
public bool IsDiscipleOfLand ( )
{
2017-12-08 00:58:39 -06:00
return GetClass ( ) > = CLASSID_MIN ;
2017-10-11 14:46:24 +01:00
}
2017-09-16 02:50:32 +01:00
#endregion lua helpers
#endregion ai stuff
2016-01-02 14:04:45 -05:00
2018-02-15 13:20:46 -06:00
//Reset procs. Only send packet if any procs were actually reset.
//This assumes you can't use weaponskills between getting a proc and using the procced ability
public void ResetProcs ( )
{
var propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/timingCommand" , this ) ;
bool shouldSend = false ;
for ( int i = 0 ; i < 4 ; i + + )
{
if ( charaWork . battleTemp . timingCommandFlag [ i ] )
{
shouldSend = true ;
charaWork . battleTemp . timingCommandFlag [ i ] = false ;
2018-10-20 12:08:47 -04:00
propPacketUtil . AddProperty ( String . Format ( "charaWork.battleTemp.timingCommandFlag[{0}]" , i ) ) ;
2018-02-15 13:20:46 -06:00
}
}
2018-10-20 12:08:47 -04:00
if ( shouldSend & & this is Player )
( ( Player ) this ) . QueuePackets ( propPacketUtil . Done ( ) ) ;
2018-02-15 13:20:46 -06:00
}
//Set given proc to true and send packet if this is a player
// todo: hidden status effects for timing when the procs fall off
public void SetProc ( int procId , bool val = true )
{
charaWork . battleTemp . timingCommandFlag [ procId ] = val ;
uint effectId = ( uint ) StatusEffectId . EvadeProc + ( uint ) procId ;
//If a proc just occurred, add a hidden effect effect
if ( val )
{
StatusEffect procEffect = Server . GetWorldManager ( ) . GetStatusEffect ( effectId ) ;
2018-04-18 16:06:41 -05:00
procEffect . SetDuration ( 5 ) ;
2019-05-27 23:05:20 -07:00
statusEffects . AddStatusEffect ( procEffect , this ) ;
2018-02-15 13:20:46 -06:00
}
//Otherwise we're reseting a proc, remove the status
else
{
statusEffects . RemoveStatusEffect ( statusEffects . GetStatusEffectById ( ( uint ) effectId ) ) ;
}
2018-10-20 12:08:47 -04:00
if ( this is Player )
2018-02-15 13:20:46 -06:00
{
var propPacketUtil = new ActorPropertyPacketUtil ( "charaWork/timingCommand" , this ) ;
2018-10-20 12:08:47 -04:00
propPacketUtil . AddProperty ( String . Format ( "charaWork.battleTemp.timingCommandFlag[{0}]" , procId ) ) ;
( ( Player ) this ) . QueuePackets ( propPacketUtil . Done ( ) ) ;
2018-02-15 13:20:46 -06:00
}
}
public HitDirection GetHitDirection ( Actor target )
{
//Get between taget's position and our position
double angle = Vector3 . GetAngle ( target . GetPosAsVector3 ( ) , GetPosAsVector3 ( ) ) ;
//Add to the target's rotation, mod by 2pi. This is the angle relative to where the target is looking
//Actor's rotation is 0 degrees on their left side, rotate it by 45 degrees so that quadrants line up with sides
angle = ( angle + target . rotation - ( . 25 * Math . PI ) ) % ( 2 * Math . PI ) ;
//Make positive
if ( angle < 0 )
angle = angle + ( 2 * Math . PI ) ;
//Get the side we're on. 0 is front, 1 is right, 2 is rear, 3 is left
var side = ( int ) ( angle / ( . 5 * Math . PI ) ) % 4 ;
return ( HitDirection ) ( 1 < < side ) ;
}
//Called when this character evades attacker's action
2019-05-27 17:31:25 -07:00
public void OnEvade ( Character attacker , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
2018-04-18 16:06:41 -05:00
{
SetProc ( ( ushort ) HitType . Evade ) ;
2019-05-29 20:19:44 -07:00
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnEvade , "onEvade" , attacker , this , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
}
//Called when this character blocks attacker's action
2019-05-27 17:31:25 -07:00
public void OnBlock ( Character attacker , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
2018-04-18 16:06:41 -05:00
{
SetProc ( ( ushort ) HitType . Block ) ;
2019-05-29 20:19:44 -07:00
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnBlock , "onBlock" , attacker , this , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
}
//Called when this character parries attacker's action
2019-05-27 17:31:25 -07:00
public void OnParry ( Character attacker , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
2018-04-18 16:06:41 -05:00
{
SetProc ( ( ushort ) HitType . Parry ) ;
2019-05-29 20:19:44 -07:00
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnParry , "onParry" , attacker , this , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
}
//Called when this character misses
2019-05-27 17:31:25 -07:00
public void OnMiss ( Character defender , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
2018-04-18 16:06:41 -05:00
{
SetProc ( ( ushort ) HitType . Miss ) ;
2019-05-29 20:19:44 -07:00
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnMiss , "onMiss" , this , defender , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
}
2019-05-27 17:31:25 -07:00
public void OnHit ( Character defender , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
2018-04-18 16:06:41 -05:00
{
2019-05-29 20:19:44 -07:00
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnHit , "onHit" , this , defender , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
}
2019-05-27 17:31:25 -07:00
public void OnCrit ( Character defender , BattleCommand skill , CommandResult action , CommandResultContainer actionContainer = null )
{
2019-05-29 20:19:44 -07:00
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnCrit , "onCrit" , this , defender , skill , action , actionContainer ) ;
2018-04-18 16:06:41 -05:00
}
//The order of messages that appears after using a command is:
//1. Cast start messages. (ie "You begin casting... ")
//2. Messages from buffs that activate before the command actually starts, like Power Surge or Presence of Mind. (This may be wrong and these could be the same as 4.)
//3. If the command is a multi-hit command, this is where the "You use [command] on [target]" message goes
//Then, for each hit:
//4. Buffs that activate before a command hits, like Blindside
//5. The hit itself. For single hit commands this message is "Your [command] hits [target] for x damage" for multi hits it's "[Target] takes x points of damage"
//6. Stoneskin falling off
//6. Buffs that activate after a command hits, like Aegis Boon and Divine Veil
2019-05-27 17:31:25 -07:00
2018-04-18 16:06:41 -05:00
//After all hits
//7. If it's a multi-hit command there's a "{numhits]fold attack..." message or if all hits miss an "All attacks missed" message
//8. Buffs that fall off after the skill ends, like Excruciate
//For every target defeated:
//8. Defeat message
//9. EXP message
//10. EXP chain message
//folder is probably temporary until move to cached scripts is complete
public void DoBattleCommand ( BattleCommand command , string folder )
2018-02-15 13:20:46 -06:00
{
2018-04-18 16:06:41 -05:00
//List<BattleAction> actions = new List<BattleAction>();
2019-01-29 00:02:09 -05:00
CommandResultContainer actions = new CommandResultContainer ( ) ;
2018-04-18 16:06:41 -05:00
var targets = command . targetFind . GetTargets ( ) ;
bool hitTarget = false ;
if ( targets . Count > 0 )
{
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnCommandStart , "onCommandStart" , this , command , actions ) ;
foreach ( var chara in targets )
{
ushort hitCount = 0 ;
2018-06-25 18:20:20 -05:00
ushort totalDamage = 0 ;
2018-04-18 16:06:41 -05:00
for ( int hitNum = 1 ; hitNum < = command . numHits ; hitNum + + )
{
2019-01-29 00:02:09 -05:00
var action = new CommandResult ( chara . actorId , command , ( byte ) GetHitDirection ( chara ) , ( byte ) hitNum ) ;
2018-04-18 16:06:41 -05:00
//uncached script
lua . LuaEngine . CallLuaBattleCommandFunction ( this , command , folder , "onSkillFinish" , this , chara , command , action , actions ) ;
//cached script
//skill.CallLuaFunction(owner, "onSkillFinish", this, chara, command, action, actions);
2019-05-27 16:52:56 -07:00
if ( action . ActionLanded ( ) )
2018-04-18 16:06:41 -05:00
{
hitTarget = true ;
hitCount + + ;
2018-06-25 18:20:20 -05:00
totalDamage + = action . amount ;
2018-04-18 16:06:41 -05:00
}
}
if ( command . numHits > 1 )
{
2018-06-25 18:20:20 -05:00
//30442: [hitCount]fold Attack! [chara] takes a total of totalDamage points of damage.
//30450: All attacks miss!
ushort textId = ( ushort ) ( hitTarget ? 30442 : 30450 ) ;
2019-01-29 00:02:09 -05:00
actions . AddAction ( new CommandResult ( chara . actorId , textId , 0 , totalDamage , ( byte ) hitCount ) ) ;
2018-04-18 16:06:41 -05:00
}
}
statusEffects . CallLuaFunctionByFlag ( ( uint ) StatusEffectFlags . ActivateOnCommandFinish , "onCommandFinish" , this , command , actions ) ;
}
else
{
2019-01-29 00:02:09 -05:00
actions . AddAction ( new CommandResult ( actorId , 30202 , 0 ) ) ;
2018-04-18 16:06:41 -05:00
}
2019-05-31 23:42:52 -07:00
DelMP ( command . CalculateMpCost ( this ) ) ;
DelTP ( command . CalculateTpCost ( this ) ) ;
2018-04-18 16:06:41 -05:00
//Now that we know if we hit the target we can check if the combo continues
2018-10-20 12:08:47 -04:00
if ( this is Player )
{
2018-04-18 16:06:41 -05:00
if ( command . isCombo & & hitTarget )
2018-10-20 12:08:47 -04:00
( ( Player ) this ) . SetCombos ( command . comboNextCommandId ) ;
2019-05-29 19:46:29 -07:00
//Only reset combo if the command is a spell or weaponskill, since abilities can be used between combo skills
else if ( command . commandType = = CommandType . Spell | | command . commandType = = CommandType . WeaponSkill )
2018-10-20 12:08:47 -04:00
( ( Player ) this ) . SetCombos ( ) ;
}
2019-05-29 19:46:29 -07:00
2018-04-18 16:06:41 -05:00
actions . CombineLists ( ) ;
DoBattleAction ( command . id , command . battleAnimation , actions . GetList ( ) ) ;
2018-02-15 13:20:46 -06:00
}
2018-04-18 16:06:41 -05:00
public List < Character > GetPartyMembersInRange ( uint range )
{
TargetFind targetFind = new TargetFind ( this ) ;
2019-05-29 19:24:58 -07:00
targetFind . SetAOEType ( ValidTarget . Party , TargetFindAOEType . Circle , TargetFindAOETarget . Self , range , 0 , 10 , 0 , 0 ) ;
targetFind . FindWithinArea ( this , ValidTarget . Party , TargetFindAOETarget . Self ) ;
2018-04-18 16:06:41 -05:00
return targetFind . GetTargets ( ) ;
}
2019-05-06 15:59:09 -04:00
#region Inventory
2017-12-10 15:13:33 -05:00
public void SendItemPackage ( Player player , uint id )
{
if ( ! itemPackages . ContainsKey ( ( ushort ) id ) )
return ;
player . QueuePacket ( InventoryBeginChangePacket . BuildPacket ( actorId , true ) ) ;
2019-06-06 01:43:27 -04:00
itemPackages [ ( ushort ) id ] . SendFullPackage ( player ) ;
2017-12-10 15:13:33 -05:00
player . QueuePacket ( InventoryEndChangePacket . BuildPacket ( actorId ) ) ;
}
2017-11-11 13:46:12 -05:00
public void AddItem ( uint catalogID )
{
AddItem ( catalogID , 1 ) ;
}
public void AddItem ( uint catalogID , int quantity )
{
AddItem ( catalogID , quantity , 1 ) ;
}
public void AddItem ( uint catalogID , int quantity , byte quality )
{
ushort itemPackage = GetPackageForItem ( catalogID ) ;
if ( itemPackages . ContainsKey ( itemPackage ) )
{
2017-12-10 22:32:24 -05:00
itemPackages [ itemPackage ] . AddItem ( catalogID , quantity , quality ) ;
2017-12-10 13:38:53 -05:00
}
}
2017-11-11 13:46:12 -05:00
public void AddItem ( InventoryItem item )
{
ushort itemPackage = GetPackageForItem ( item . GetItemData ( ) . catalogID ) ;
if ( itemPackages . ContainsKey ( itemPackage ) )
2017-12-04 22:58:18 -05:00
{
2017-11-11 13:46:12 -05:00
itemPackages [ itemPackage ] . AddItem ( item ) ;
}
}
public void MoveItem ( InventoryItem item , ushort destinationPackage )
{
ushort sourcePackage = item . itemPackage ;
if ( ! itemPackages . ContainsKey ( sourcePackage ) & & ! itemPackages . ContainsKey ( destinationPackage ) )
return ;
2019-06-06 01:43:27 -04:00
itemPackages [ sourcePackage ] . MoveItem ( item , itemPackages [ destinationPackage ] ) ;
2017-11-11 13:46:12 -05:00
}
2017-12-04 22:58:18 -05:00
2017-11-11 13:46:12 -05:00
public void RemoveItem ( uint catalogID )
{
RemoveItem ( catalogID , 1 ) ;
}
public void RemoveItem ( uint catalogID , int quantity )
{
RemoveItem ( catalogID , quantity , 1 ) ;
}
public void RemoveItem ( uint catalogID , int quantity , byte quality )
{
ushort itemPackage = GetPackageForItem ( catalogID ) ;
if ( itemPackages . ContainsKey ( itemPackage ) )
{
2017-12-04 22:58:18 -05:00
itemPackages [ itemPackage ] . RemoveItem ( catalogID , quantity , quality ) ;
2017-11-11 13:46:12 -05:00
}
}
public void RemoveItemAtSlot ( ushort itemPackage , ushort slot )
{
if ( itemPackages . ContainsKey ( itemPackage ) )
{
itemPackages [ itemPackage ] . RemoveItemAtSlot ( slot ) ;
}
}
public void RemoveItem ( InventoryItem item )
{
2019-06-06 01:43:27 -04:00
itemPackages [ item . itemPackage ] . RemoveItem ( item ) ;
2017-11-11 13:46:12 -05:00
}
public bool HasItem ( uint catalogID )
{
return HasItem ( catalogID , 1 ) ;
}
public bool HasItem ( uint catalogID , int minQuantity )
{
return HasItem ( catalogID , minQuantity , 1 ) ;
}
public bool HasItem ( uint catalogID , int minQuantity , byte quality )
{
ushort itemPackage = GetPackageForItem ( catalogID ) ;
if ( itemPackages . ContainsKey ( itemPackage ) )
{
2017-12-10 13:38:53 -05:00
return itemPackages [ itemPackage ] . HasItem ( catalogID , minQuantity , quality ) ;
2017-11-11 13:46:12 -05:00
}
return false ;
}
public bool HasItem ( InventoryItem item )
{
ushort itemPackage = GetPackageForItem ( item . GetItemData ( ) . catalogID ) ;
if ( itemPackages . ContainsKey ( itemPackage ) )
{
//return itemPackages[itemPackage].HasItem(item);
return false ; //TODO FIX
}
2019-05-06 15:59:09 -04:00
else
return false ;
2017-11-11 13:46:12 -05:00
}
public InventoryItem GetItem ( LuaUtils . ItemRefParam reference )
{
if ( reference . actorId ! = actorId )
return null ;
if ( itemPackages . ContainsKey ( reference . itemPackage ) )
{
return itemPackages [ reference . itemPackage ] . GetItemAtSlot ( reference . slot ) ;
}
return null ;
}
2018-04-07 14:48:43 -04:00
public ItemPackage GetItemPackage ( ushort package )
2017-11-11 17:05:07 -05:00
{
if ( itemPackages . ContainsKey ( package ) )
return itemPackages [ package ] ;
else
return null ;
}
2017-11-11 13:46:12 -05:00
public ushort GetPackageForItem ( uint catalogID )
{
ItemData data = Server . GetItemGamedata ( catalogID ) ;
if ( data = = null )
2018-04-07 14:48:43 -04:00
return ItemPackage . NORMAL ;
2017-11-11 13:46:12 -05:00
else
2017-12-04 22:58:18 -05:00
{
2017-11-11 13:46:12 -05:00
if ( data . IsMoney ( ) )
2018-04-07 14:48:43 -04:00
return ItemPackage . CURRENCY_CRYSTALS ;
2017-11-11 13:46:12 -05:00
else if ( data . IsImportant ( ) )
2018-04-07 14:48:43 -04:00
return ItemPackage . KEYITEMS ;
2017-11-11 13:46:12 -05:00
else
2018-04-07 14:48:43 -04:00
return ItemPackage . NORMAL ;
2017-11-11 13:46:12 -05:00
}
}
//public void removeItem(byUniqueId)
//public void removeItem(byUniqueId, quantity)
//public void removeItem(slot)
//public void removeItem(slot, quantity)
2017-04-15 16:33:56 -04:00
2019-05-06 15:59:09 -04:00
#endregion
2016-01-02 14:04:45 -05:00
2018-02-15 13:20:46 -06:00
}
}