attempt to add tutorial fight

- added tempvars which are reset on spawning/zoning
This commit is contained in:
Tahir Akhlaq 2017-09-16 02:50:32 +01:00
parent da621dfc0e
commit ba8184db89
30 changed files with 552 additions and 154 deletions

View file

@ -111,6 +111,9 @@ namespace FFXIVClassic_Map_Server.Actors
{
lock (mActorList)
{
if (actor is Character)
((Character)actor).ResetTempVars();
if (!mActorList.ContainsKey(actor.actorId))
mActorList.Add(actor.actorId, actor);

View file

@ -13,6 +13,7 @@ using FFXIVClassic_Map_Server.packets.send.actor.battle;
using FFXIVClassic_Map_Server.packets.send;
using FFXIVClassic_Map_Server.actors.chara.ai.state;
using FFXIVClassic_Map_Server.actors.chara.ai.utils;
using FFXIVClassic_Map_Server.actors.chara.npc;
namespace FFXIVClassic_Map_Server.Actors
{
@ -25,6 +26,15 @@ namespace FFXIVClassic_Map_Server.Actors
Player
}
enum DamageTakenType
{
None,
Attack,
Magic,
Weaponskill,
Ability
}
class Character : Actor
{
public const int CLASSID_PUG = 2;
@ -102,6 +112,8 @@ namespace FFXIVClassic_Map_Server.Actors
public ushort newMainState;
public float spawnX, spawnY, spawnZ;
protected Dictionary<string, UInt64> tempVars = new Dictionary<string, UInt64>();
public Character(uint actorID) : base(actorID)
{
//Init timer array to "notimer"
@ -392,6 +404,12 @@ namespace FFXIVClassic_Map_Server.Actors
return false;
}
public virtual bool Engage(Character target)
{
aiContainer.Engage(target);
return false;
}
public virtual bool Disengage(ushort newMainState = 0xFFFF)
{
if (newMainState != 0xFFFF)
@ -447,12 +465,12 @@ namespace FFXIVClassic_Map_Server.Actors
public bool IsDead()
{
return aiContainer.IsDead();
return !IsAlive();
}
public bool IsAlive()
{
return !aiContainer.IsDead();
return !aiContainer.IsDead() && GetHP() > 0;
}
public short GetHP()
@ -520,7 +538,7 @@ namespace FFXIVClassic_Map_Server.Actors
// todo: +/- hp and die
// todo: battlenpcs probably have way more hp?
var addHp = charaWork.parameterSave.hp[0] + hp;
addHp = addHp.Clamp(ushort.MinValue, charaWork.parameterSave.hpMax[0]);
addHp = addHp.Clamp((short)GetMod((uint)Modifier.MinimumHpLock), charaWork.parameterSave.hpMax[0]);
charaWork.parameterSave.hp[0] = (short)addHp;
if (charaWork.parameterSave.hp[0] < 1)
@ -615,7 +633,7 @@ namespace FFXIVClassic_Map_Server.Actors
//var packet = BattleActionX01Packet.BuildPacket(owner.actorId, owner.actorId, target.actorId, (uint)0x19001000, (uint)0x8000604, (ushort)0x765D, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, (byte)0x1);
}
target.OnDamageTaken(this, action);
target.OnDamageTaken(this, action, DamageTakenType.Ability);
// todo: call onAttack/onDamageTaken
target.DelHP(action.amount);
if (target is BattleNpc)
@ -629,7 +647,7 @@ namespace FFXIVClassic_Map_Server.Actors
this.DelMP(spell.mpCost); // mpCost can be set in script e.g. if caster has something for free spells
foreach (var action in actions)
zone.FindActorInArea<Character>(action.targetId).OnDamageTaken(this, action);
zone.FindActorInArea<Character>(action.targetId).OnDamageTaken(this, action, DamageTakenType.Magic);
if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
@ -642,7 +660,7 @@ namespace FFXIVClassic_Map_Server.Actors
this.DelTP(skill.tpCost);
foreach (var action in actions)
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action);
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action, DamageTakenType.Weaponskill);
if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
@ -654,7 +672,7 @@ namespace FFXIVClassic_Map_Server.Actors
((BattleNpc)target).lastAttacker = this;
foreach (var action in actions)
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action);
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action, DamageTakenType.Ability);
}
public virtual void OnSpawn()
@ -672,11 +690,64 @@ namespace FFXIVClassic_Map_Server.Actors
}
public virtual void OnDamageTaken(Character attacker, BattleAction action)
public virtual void OnDamageTaken(Character attacker, BattleAction action, DamageTakenType damageTakenType)
{
}
#endregion
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()
{
return this is BattleNpc && !IsAlly();
}
public bool IsPet()
{
return this is Pet;
}
public bool IsAlly()
{
return this is Ally;
}
#endregion lua helpers
#endregion ai stuff
}
}

View file

@ -53,5 +53,6 @@ namespace FFXIVClassic_Map_Server.actors.chara
HarvestRate = 40,
Raise = 41,
MinimumHpLock = 42, // hp cannot fall below this value
}
}

View file

@ -235,5 +235,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
return tpCost;
}
public List<Character> GetTargets()
{
return targetFind?.GetTargets<Character>();
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.actors.chara.npc;
namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{
// todo: this is probably not needed, can do everything in their script
class AllyController : BattleNpcController
{
protected new Ally owner;
public AllyController(Ally owner) :
base(owner)
{
this.owner = owner;
}
// server really likes to hang whenever scripts iterate area's actorlist
protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (owner.currentContentGroup != null)
{
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount());
foreach (var charaId in owner.currentContentGroup.GetMembers())
{
var chara = owner.zone.FindActorInArea<Character>(charaId);
if (chara != null)
contentGroupCharas.Add(chara);
}
}
base.DoCombatTick(tick, contentGroupCharas);
}
}
}

View file

@ -15,19 +15,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{
class BattleNpcController : Controller
{
private DateTime lastActionTime;
private DateTime lastSpellCastTime;
private DateTime lastSkillTime;
private DateTime lastSpecialSkillTime; // todo: i dont think monsters have "2hr" cooldowns like ffxi
private DateTime deaggroTime;
private DateTime neutralTime;
private DateTime waitTime;
protected DateTime lastActionTime;
protected DateTime lastSpellCastTime;
protected DateTime lastSkillTime;
protected DateTime lastSpecialSkillTime; // todo: i dont think monsters have "2hr" cooldowns like ffxi
protected DateTime deaggroTime;
protected DateTime neutralTime;
protected DateTime waitTime;
private bool firstSpell = true;
private DateTime lastRoamUpdate;
private DateTime battleStartTime;
protected DateTime lastRoamUpdate;
protected DateTime battleStartTime;
private new BattleNpc owner;
protected new BattleNpc owner;
public BattleNpcController(BattleNpc owner) :
base(owner)
{
@ -84,7 +84,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return canEngage;
}
private bool TryEngage(Character target)
protected bool TryEngage(Character target)
{
// todo:
return true;
@ -127,7 +127,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
// todo:
}
private void DoRoamTick(DateTime tick)
protected virtual void DoRoamTick(DateTime tick)
{
if (owner.hateContainer.GetHateList().Count > 0)
{
@ -166,15 +166,18 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{
if (!owner.neutral && owner.IsAlive())
{
foreach (var player in owner.zone.GetActorsAroundActor<Player>(owner, 50))
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(owner, 50))
{
if (owner.allegiance == chara.allegiance)
continue;
if (!owner.isMovingToSpawn && owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None)
{
uint levelDifference = (uint)Math.Abs(owner.charaWork.parameterSave.state_mainSkillLevel - player.charaWork.parameterSave.state_mainSkillLevel);
uint levelDifference = (uint)Math.Abs(owner.charaWork.parameterSave.state_mainSkillLevel - chara.charaWork.parameterSave.state_mainSkillLevel);
if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(player))
if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara))
{
owner.hateContainer.AddBaseHate(player);
owner.hateContainer.AddBaseHate(chara);
break;
}
}
@ -188,7 +191,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
}
}
private void DoCombatTick(DateTime tick)
protected virtual void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
{
HandleHate();
@ -200,10 +203,10 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
}
Move();
lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick));
lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas);
}
private void Move()
protected virtual void Move()
{
if (!owner.aiContainer.CanFollowPath())
{
@ -257,7 +260,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
}
}
private void FaceTarget()
protected void FaceTarget()
{
// todo: check if stunned etc
if (owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventAction))
@ -269,7 +272,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
}
}
private bool CanMoveForward(float distance)
protected bool CanMoveForward(float distance)
{
// todo: check spawn leash and stuff
if (!owner.IsCloseToSpawn())
@ -283,7 +286,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return true;
}
public bool CanAggroTarget(Character target)
public virtual bool CanAggroTarget(Character target)
{
if (owner.neutral || owner.detectionType == DetectionType.None || owner.IsDead())
{
@ -303,7 +306,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return false;
}
public bool CanDetectTarget(Character target, bool forceSight = false)
public virtual bool CanDetectTarget(Character target, bool forceSight = false)
{
if (owner.IsDead())
return false;
@ -359,12 +362,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return false;
}
public bool CanSeePoint(float x, float y, float z)
public virtual bool CanSeePoint(float x, float y, float z)
{
return NavmeshUtils.CanSee((Zone)owner.zone, owner.positionX, owner.positionY, owner.positionZ, x, y, z);
}
private void HandleHate()
protected virtual void HandleHate()
{
ChangeTarget(owner.hateContainer.GetMostHatedTarget());
}

View file

@ -24,6 +24,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
{
if (tick >= respawnTime)
{
owner.ResetTempVars();
owner.Spawn(tick);
return true;
}

View file

@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.actors.chara.ai;
using FFXIVClassic_Map_Server.actors.chara.ai.controllers;
namespace FFXIVClassic_Map_Server.actors.chara.npc
{
class Ally : BattleNpc
{
// todo: ally class is probably not necessary
public Ally(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot,
ushort actorState, uint animationId, string customDisplayName)
: base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName)
{
aiContainer = new AIContainer(this, new AllyController(this), new PathFind(this), new TargetFind(this));
this.allegiance = CharacterTargetingAllegiance.Player;
}
}
}

View file

@ -54,10 +54,10 @@ namespace FFXIVClassic_Map_Server.Actors
public DetectionType detectionType;
public KindredType kindredType;
public bool neutral;
private uint despawnTime;
private uint respawnTime;
private uint spawnDistance;
private uint bnpcId;
protected uint despawnTime;
protected uint respawnTime;
protected uint spawnDistance;
protected uint bnpcId;
public Character lastAttacker;
public uint spellListId, skillListId, dropListId;
@ -69,7 +69,7 @@ namespace FFXIVClassic_Map_Server.Actors
public ModifierList genusMods;
public ModifierList spawnMods;
private Dictionary<MobModifier, Int64> mobModifiers = new Dictionary<MobModifier, Int64>();
protected Dictionary<MobModifier, Int64> mobModifiers = new Dictionary<MobModifier, Int64>();
public BattleNpc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot,
ushort actorState, uint animationId, string customDisplayName)
@ -379,16 +379,30 @@ namespace FFXIVClassic_Map_Server.Actors
public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors)
{
base.OnCast(state, actions, ref errors);
if (GetMobMod((uint)MobModifier.SpellScript) != 0)
foreach (var action in actions)
lua.LuaEngine.CallLuaBattleFunction(this, "onCast", this, zone.FindActorInArea<Character>(action.targetId), ((MagicState)state).GetSpell(), action);
}
public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)
{
base.OnAbility(state, actions, ref errors);
/*
if (GetMobMod((uint)MobModifier.AbilityScript) != 0)
foreach (var action in actions)
lua.LuaEngine.CallLuaBattleFunction(this, "onAbility", this, zone.FindActorInArea<Character>(action.targetId), ((AbilityState)state).GetAbility(), action);
*/
}
public override void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors)
{
base.OnWeaponSkill(state, actions, ref errors);
if (GetMobMod((uint)MobModifier.WeaponSkillScript) != 0)
foreach (var action in actions)
lua.LuaEngine.CallLuaBattleFunction(this, "onWeaponSkill", this, zone.FindActorInArea<Character>(action.targetId), ((WeaponSkillState)state).GetWeaponSkill(), action);
}
public override void OnSpawn()
@ -434,10 +448,10 @@ namespace FFXIVClassic_Map_Server.Actors
mobModifiers.Add((MobModifier)mobModId, val);
}
public override void OnDamageTaken(Character attacker, BattleAction action)
public override void OnDamageTaken(Character attacker, BattleAction action, DamageTakenType damageTakenType)
{
if (GetMobMod((uint)MobModifier.DefendScript) != 0)
lua.LuaEngine.CallLuaBattleFunction(this, "onDamageTaken", this, attacker, action.amount);
lua.LuaEngine.CallLuaBattleFunction(this, "onDamageTaken", this, attacker, action.amount, (uint)damageTakenType);
}
}
}

View file

@ -28,7 +28,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc
AttackScript = 17, // call my script's onAttack whenever i attack
DefendScript = 18, // call my script's onDamageTaken whenever i take damage
SpellScript = 19, // call my script's onSpellCast whenever i finish casting
WeaponskillScript = 20, // call my script's onWeaponSkill whenever i finish using a weaponskill
WeaponSkillScript = 20, // call my script's onWeaponSkill whenever i finish using a weaponskill
AbilityScript = 21, // call my script's onAbility whenever i finish using an ability
CallForHelp = 22, // actor with this id outside of target's party with this can attack me
FreeForAll = 23, // any actor can attack me

View file

@ -169,5 +169,9 @@ namespace FFXIVClassic_Map_Server.actors.group
DeleteGroup();
}
public List<uint> GetMembers()
{
return members;
}
}
}