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

@ -85,6 +85,7 @@
<Compile Include="actors\area\PrivateAreaContent.cs" />
<Compile Include="actors\area\SpawnLocation.cs" />
<Compile Include="actors\area\Zone.cs" />
<Compile Include="actors\chara\ai\controllers\AllyController.cs" />
<Compile Include="actors\chara\ai\helpers\ActionQueue.cs" />
<Compile Include="actors\chara\ai\AIContainer.cs" />
<Compile Include="actors\chara\ai\controllers\Controller.cs" />
@ -110,6 +111,7 @@
<Compile Include="actors\chara\Modifier.cs" />
<Compile Include="actors\chara\ModifierList.cs" />
<Compile Include="actors\chara\npc\ActorClass.cs" />
<Compile Include="actors\chara\npc\Ally.cs" />
<Compile Include="actors\chara\npc\BattleNpc.cs" />
<Compile Include="actors\chara\npc\MobModifier.cs" />
<Compile Include="actors\chara\npc\NpcWork.cs" />

View file

@ -436,13 +436,13 @@ namespace FFXIVClassic_Map_Server
conn.Open();
var query = @"
SELECT bsl.bnpcId, bsl.groupId, bsl.positionX, bsl.positionY, bsl.positionZ, bsl.rotation,
bgr.groupId, bgr.poolId, bgr.actorClassId, bgr.scriptName, bgr.minLevel, bgr.maxLevel, bgr.respawnTime, bgr.hp, bgr.mp,
bgr.groupId, bgr.poolId, bgr.scriptName, bgr.minLevel, bgr.maxLevel, bgr.respawnTime, bgr.hp, bgr.mp,
bgr.dropListId, bgr.allegiance, bgr.spawnType, bgr.animationId, bgr.actorState, bgr.privateAreaName, bgr.privateAreaLevel, bgr.zoneId,
bpo.poolId, bpo.genusId, bpo.currentJob, bpo.combatSkill, bpo.combatDelay, bpo.combatDmgMult, bpo.aggroType,
bpo.poolId, bpo.genusId, bpo.actorClassId, bpo.currentJob, bpo.combatSkill, bpo.combatDelay, bpo.combatDmgMult, bpo.aggroType,
bpo.immunity, bpo.linkType, bpo.skillListId, bpo.spellListId,
bge.genusId, bge.modelSize, bge.speed, bge.kindredId, bge.detection, bge.hpp, bge.mpp, bge.tpp, bge.str, bge.vit, bge.dex,
bge.int, bge.mnd, bge.pie, bge.att, bge.acc, bge.def, bge.eva, bge.slash, bge.pierce, bge.h2h, bge.blunt,
bge.fire, bge.ice, bge.wind, bge.lightning, bge.earth, bge.water
bge.fire, bge.ice, bge.wind, bge.lightning, bge.earth, bge.water, bge.element
FROM server_battlenpc_spawn_locations bsl
INNER JOIN server_battlenpc_groups bgr ON bsl.groupId = bgr.groupId
INNER JOIN server_battlenpc_pools bpo ON bgr.poolId = bpo.poolId
@ -517,8 +517,12 @@ namespace FFXIVClassic_Map_Server
//battleNpc.SetMod((uint)Modifier.ResistFire, )
zone.AddActorToZone(battleNpc);
count++;
// todo: this is dumb
if (battleNpc.npcSpawnType == NpcSpawnType.Normal)
{
zone.AddActorToZone(battleNpc);
count++;
}
}
}
}
@ -552,13 +556,13 @@ namespace FFXIVClassic_Map_Server
conn.Open();
var query = @"
SELECT bsl.bnpcId, bsl.groupId, bsl.positionX, bsl.positionY, bsl.positionZ, bsl.rotation,
bgr.groupId, bgr.poolId, bgr.actorClassId, bgr.scriptName, bgr.minLevel, bgr.maxLevel, bgr.respawnTime, bgr.hp, bgr.mp,
bgr.groupId, bgr.poolId, bgr.scriptName, bgr.minLevel, bgr.maxLevel, bgr.respawnTime, bgr.hp, bgr.mp,
bgr.dropListId, bgr.allegiance, bgr.spawnType, bgr.animationId, bgr.actorState, bgr.privateAreaName, bgr.privateAreaLevel, bgr.zoneId,
bpo.poolId, bpo.genusId, bpo.currentJob, bpo.combatSkill, bpo.combatDelay, bpo.combatDmgMult, bpo.aggroType,
bpo.poolId, bpo.genusId, bpo.actorClassId, bpo.currentJob, bpo.combatSkill, bpo.combatDelay, bpo.combatDmgMult, bpo.aggroType,
bpo.immunity, bpo.linkType, bpo.skillListId, bpo.spellListId,
bge.genusId, bge.modelSize, bge.speed, bge.kindredId, bge.detection, bge.hpp, bge.mpp, bge.tpp, bge.str, bge.vit, bge.dex,
bge.int, bge.mnd, bge.pie, bge.att, bge.acc, bge.def, bge.eva, bge.slash, bge.pierce, bge.h2h, bge.blunt,
bge.fire, bge.ice, bge.wind, bge.lightning, bge.earth, bge.water
bge.fire, bge.ice, bge.wind, bge.lightning, bge.earth, bge.water, bge.element
FROM server_battlenpc_spawn_locations bsl
INNER JOIN server_battlenpc_groups bgr ON bsl.groupId = bgr.groupId
INNER JOIN server_battlenpc_pools bpo ON bgr.poolId = bpo.poolId

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;
}
}
}