Updated Map Server namespace. Moved all other data folders (www and sql) to data folder. Renamed boot name to Project Meteor.

This commit is contained in:
Filip Maj
2019-06-19 01:10:15 -04:00
parent 18ef69f3d1
commit 91549bff7a
1823 changed files with 102704 additions and 901 deletions

View File

@@ -0,0 +1,98 @@
/*
===========================================================================
Copyright (C) 2015-2019 Project Meteor Dev Team
This file is part of Project Meteor Server.
Project Meteor Server is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Project Meteor Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
===========================================================================
*/
using System;
using System.Collections.Generic;
using Meteor.Map.Actors;
using Meteor.Map.actors.chara.npc;
namespace Meteor.Map.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;
}
protected List<Character> GetContentGroupCharas()
{
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);
}
}
return contentGroupCharas;
}
//Iterate over players in the group and if they are fighting, assist them
protected override void TryAggro(DateTime tick)
{
//lua.LuaEngine.CallLuaBattleFunction(owner, "tryAggro", owner, GetContentGroupCharas());
foreach(Character chara in GetContentGroupCharas())
{
if(chara.IsPlayer())
{
if(owner.aiContainer.GetTargetFind().CanTarget((Character) chara.target) && chara.target is BattleNpc && ((BattleNpc)chara.target).hateContainer.HasHateForTarget(chara))
{
owner.Engage(chara.target.actorId);
owner.hateContainer.AddBaseHate((Character) chara.target);
break;
}
}
}
//base.TryAggro(tick);
}
// server really likes to hang whenever scripts iterate area's actorlist
protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (contentGroupCharas == null)
{
contentGroupCharas = GetContentGroupCharas();
}
base.DoCombatTick(tick, contentGroupCharas);
}
protected override void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (contentGroupCharas == null)
{
contentGroupCharas = GetContentGroupCharas();
}
base.DoRoamTick(tick, contentGroupCharas);
}
}
}

View File

@@ -0,0 +1,430 @@
/*
===========================================================================
Copyright (C) 2015-2019 Project Meteor Dev Team
This file is part of Project Meteor Server.
Project Meteor Server is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Project Meteor Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
===========================================================================
*/
using System;
using System.Collections.Generic;
using Meteor.Common;
using Meteor.Map.Actors;
using Meteor.Map.packets.send.actor;
using Meteor.Map.actors.area;
using Meteor.Map.utils;
using Meteor.Map.actors.chara.ai.state;
using Meteor.Map.actors.chara.npc;
namespace Meteor.Map.actors.chara.ai.controllers
{
class BattleNpcController : Controller
{
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;
protected DateTime lastRoamUpdate;
protected DateTime battleStartTime;
protected new BattleNpc owner;
public BattleNpcController(BattleNpc owner) :
base(owner)
{
this.owner = owner;
this.lastUpdate = DateTime.Now;
this.waitTime = lastUpdate.AddSeconds(5);
}
public override void Update(DateTime tick)
{
lastUpdate = tick;
if (!owner.IsDead())
{
// todo: handle aggro/deaggro and other shit here
if (!owner.aiContainer.IsEngaged())
{
TryAggro(tick);
}
if (owner.aiContainer.IsEngaged())
{
DoCombatTick(tick);
}
//Only move if owner isn't dead and is either too far away from their spawn point or is meant to roam and isn't in combat
else if (!owner.IsDead() && (owner.isMovingToSpawn || owner.GetMobMod((uint)MobModifier.Roams) > 0))
{
DoRoamTick(tick);
}
}
}
public bool TryDeaggro()
{
if (owner.hateContainer.GetMostHatedTarget() == null || !owner.aiContainer.GetTargetFind().CanTarget(owner.target as Character))
{
return true;
}
else if (!owner.IsCloseToSpawn())
{
return true;
}
return false;
}
//If the owner isn't moving to spawn, iterate over nearby enemies and
//aggro the first one that is within 10 levels and can be detected, then engage
protected virtual void TryAggro(DateTime tick)
{
if (tick >= neutralTime && !owner.isMovingToSpawn)
{
if (!owner.neutral && owner.IsAlive())
{
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(owner, 50))
{
if (chara.allegiance == owner.allegiance)
continue;
if (owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None)
{
uint levelDifference = (uint)Math.Abs(owner.GetLevel() - chara.GetLevel());
if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara))
{
owner.hateContainer.AddBaseHate(chara);
break;
}
}
}
}
}
if (owner.hateContainer.GetHateList().Count > 0)
{
Engage(owner.hateContainer.GetMostHatedTarget());
}
}
public override bool Engage(Character target)
{
var canEngage = this.owner.aiContainer.InternalEngage(target);
if (canEngage)
{
//owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
// reset casting
firstSpell = true;
// todo: find a better place to put this?
if (owner.GetState() != SetActorStatePacket.MAIN_STATE_ACTIVE)
owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
lastActionTime = DateTime.Now;
battleStartTime = lastActionTime;
// todo: adjust cooldowns with modifiers
}
return canEngage;
}
protected bool TryEngage(Character target)
{
// todo:
return true;
}
public override void Disengage()
{
var target = owner.target;
base.Disengage();
// todo:
lastActionTime = lastUpdate.AddSeconds(5);
owner.isMovingToSpawn = true;
owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None);
owner.aiContainer.pathFind.PreparePath(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 10);
neutralTime = lastActionTime;
owner.hateContainer.ClearHate();
lua.LuaEngine.CallLuaBattleFunction(owner, "onDisengage", owner, target, Utils.UnixTimeStampUTC(lastUpdate));
}
public override void Cast(Character target, uint spellId)
{
// todo:
if(owner.aiContainer.CanChangeState())
owner.aiContainer.InternalCast(target, spellId);
}
public override void Ability(Character target, uint abilityId)
{
// todo:
if (owner.aiContainer.CanChangeState())
owner.aiContainer.InternalAbility(target, abilityId);
}
public override void RangedAttack(Character target)
{
// todo:
}
public override void MonsterSkill(Character target, uint mobSkillId)
{
// todo:
}
protected virtual void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (tick >= waitTime)
{
neutralTime = tick.AddSeconds(5);
if (owner.aiContainer.pathFind.IsFollowingPath())
{
owner.aiContainer.pathFind.FollowPath();
lastActionTime = tick.AddSeconds(-5);
}
else
{
if (tick >= lastActionTime)
{
}
}
waitTime = tick.AddSeconds(owner.GetMobMod((uint) MobModifier.RoamDelay));
owner.OnRoam(tick);
if (CanMoveForward(0.0f) && !owner.aiContainer.pathFind.IsFollowingPath())
{
// will move on next tick
owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None);
owner.aiContainer.pathFind.PathInRange(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 50.0f);
}
//lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas);
}
if (owner.aiContainer.pathFind.IsFollowingPath() && owner.aiContainer.CanFollowPath())
{
owner.aiContainer.pathFind.FollowPath();
}
}
protected virtual void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
{
HandleHate();
// todo: magic/attack/ws cooldowns etc
if (TryDeaggro())
{
Disengage();
return;
}
owner.SetMod((uint)Modifier.MovementSpeed, 5);
if ((tick - lastCombatTickScript).TotalSeconds > 3)
{
Move();
//if (owner.aiContainer.CanChangeState())
//owner.aiContainer.WeaponSkill(owner.zone.FindActorInArea<Character>(owner.target.actorId), 27155);
//lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas);
lastCombatTickScript = tick;
}
}
protected virtual void Move()
{
if (!owner.aiContainer.CanFollowPath() || owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventMovement))
{
return;
}
if (owner.aiContainer.pathFind.IsFollowingScriptedPath())
{
owner.aiContainer.pathFind.FollowPath();
return;
}
var vecToTarget = owner.target.GetPosAsVector3() - owner.GetPosAsVector3();
vecToTarget /= vecToTarget.Length();
vecToTarget = (Utils.Distance(owner.GetPosAsVector3(), owner.target.GetPosAsVector3()) - owner.GetAttackRange() + 0.2f) * vecToTarget;
var targetPos = vecToTarget + owner.GetPosAsVector3();
var distance = Utils.Distance(owner.GetPosAsVector3(), owner.target.GetPosAsVector3());
if (distance > owner.GetAttackRange() - 0.2f)
{
if (CanMoveForward(distance))
{
if (!owner.aiContainer.pathFind.IsFollowingPath() && distance > 3)
{
// pathfind if too far otherwise jump to target
owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None);
owner.aiContainer.pathFind.PreparePath(targetPos, 1.5f, 5);
}
owner.aiContainer.pathFind.FollowPath();
if (!owner.aiContainer.pathFind.IsFollowingPath())
{
if (owner.target is Player)
{
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(owner, 1))
{
if (chara == owner)
continue;
float mobDistance = Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, chara.positionX, chara.positionY, chara.positionZ);
if (mobDistance < 0.50f && (chara.updateFlags & ActorUpdateFlags.Position) == 0)
{
owner.aiContainer.pathFind.PathInRange(targetPos, 1.3f, chara.GetAttackRange());
break;
}
}
}
FaceTarget();
}
}
}
else
{
FaceTarget();
}
lastRoamUpdate = DateTime.Now;
}
protected void FaceTarget()
{
// todo: check if stunned etc
if (owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventTurn) )
{
}
else
{
owner.LookAt(owner.target);
}
}
protected bool CanMoveForward(float distance)
{
// todo: check spawn leash and stuff
if (!owner.IsCloseToSpawn())
{
return false;
}
if (owner.GetSpeed() == 0)
{
return false;
}
return true;
}
public virtual bool CanAggroTarget(Character target)
{
if (owner.neutral || owner.detectionType == DetectionType.None || owner.IsDead())
{
return false;
}
// todo: can mobs aggro mounted targets?
if (target.IsDead() || target.currentMainState == SetActorStatePacket.MAIN_STATE_MOUNTED)
{
return false;
}
if (owner.aiContainer.IsSpawned() && !owner.aiContainer.IsEngaged() && CanDetectTarget(target))
{
return true;
}
return false;
}
public virtual bool CanDetectTarget(Character target, bool forceSight = false)
{
if (owner.IsDead())
return false;
// todo: this should probably be changed to only allow detection at end of path?
if (owner.aiContainer.pathFind.IsFollowingScriptedPath() || owner.aiContainer.pathFind.IsFollowingPath() && !owner.aiContainer.pathFind.AtPoint())
{
return false;
}
// todo: handle sight/scent/hp etc
if (target.IsDead() || target.currentMainState == SetActorStatePacket.MAIN_STATE_MOUNTED)
return false;
float verticalDistance = Math.Abs(target.positionY - owner.positionY);
if (verticalDistance > 8)
return false;
var distance = Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ);
bool detectSight = forceSight || (owner.detectionType & DetectionType.Sight) != 0;
bool hasSneak = false;
bool hasInvisible = false;
bool isFacing = owner.IsFacing(target);
// use the mobmod sight range before defaulting to 20 yalms
if (detectSight && !hasInvisible && isFacing && distance < owner.GetMobMod((uint)MobModifier.SightRange))
return CanSeePoint(target.positionX, target.positionY, target.positionZ);
// todo: check line of sight and aggroTypes
if (distance > 20)
{
return false;
}
// todo: seems ffxiv doesnt even differentiate between sneak/invis?
{
hasSneak = target.GetMod(Modifier.Stealth) > 0;
hasInvisible = hasSneak;
}
if ((owner.detectionType & DetectionType.Sound) != 0 && !hasSneak && distance < owner.GetMobMod((uint)MobModifier.SoundRange))
return CanSeePoint(target.positionX, target.positionY, target.positionZ);
if ((owner.detectionType & DetectionType.Magic) != 0 && target.aiContainer.IsCurrentState<MagicState>())
return CanSeePoint(target.positionX, target.positionY, target.positionZ);
if ((owner.detectionType & DetectionType.LowHp) != 0 && target.GetHPP() < 75)
return CanSeePoint(target.positionX, target.positionY, target.positionZ);
return false;
}
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);
}
protected virtual void HandleHate()
{
ChangeTarget(owner.hateContainer.GetMostHatedTarget());
}
public override void ChangeTarget(Character target)
{
if (target != owner.target)
{
owner.target = target;
owner.currentLockedTarget = target?.actorId ?? Actor.INVALID_ACTORID;
owner.currentTarget = target?.actorId ?? Actor.INVALID_ACTORID;
foreach (var player in owner.zone.GetActorsAroundActor<Player>(owner, 50))
player.QueuePacket(owner.GetHateTypePacket(player));
base.ChangeTarget(target);
}
}
}
}

View File

@@ -0,0 +1,97 @@
/*
===========================================================================
Copyright (C) 2015-2019 Project Meteor Dev Team
This file is part of Project Meteor Server.
Project Meteor Server is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Project Meteor Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
===========================================================================
*/
using System;
using Meteor.Map.Actors;
namespace Meteor.Map.actors.chara.ai.controllers
{
abstract class Controller
{
protected Character owner;
protected DateTime lastCombatTickScript;
protected DateTime lastUpdate;
public bool canUpdate = true;
protected bool autoAttackEnabled = true;
protected bool castingEnabled = true;
protected bool weaponSkillEnabled = true;
protected PathFind pathFind;
protected TargetFind targetFind;
public Controller(Character owner)
{
this.owner = owner;
}
public abstract void Update(DateTime tick);
public abstract bool Engage(Character target);
public abstract void Cast(Character target, uint spellId);
public virtual void WeaponSkill(Character target, uint weaponSkillId) { }
public virtual void MonsterSkill(Character target, uint mobSkillId) { }
public virtual void UseItem(Character target, uint slot, uint itemId) { }
public abstract void Ability(Character target, uint abilityId);
public abstract void RangedAttack(Character target);
public virtual void Spawn() { }
public virtual void Despawn() { }
public virtual void Disengage()
{
owner.aiContainer.InternalDisengage();
}
public virtual void ChangeTarget(Character target)
{
owner.aiContainer.InternalChangeTarget(target);
}
public bool IsAutoAttackEnabled()
{
return autoAttackEnabled;
}
public void SetAutoAttackEnabled(bool isEnabled)
{
autoAttackEnabled = isEnabled;
}
public bool IsCastingEnabled()
{
return castingEnabled;
}
public void SetCastingEnabled(bool isEnabled)
{
castingEnabled = isEnabled;
}
public bool IsWeaponSkillEnabled()
{
return weaponSkillEnabled;
}
public void SetWeaponSkillEnabled(bool isEnabled)
{
weaponSkillEnabled = isEnabled;
}
}
}

View File

@@ -0,0 +1,89 @@
/*
===========================================================================
Copyright (C) 2015-2019 Project Meteor Dev Team
This file is part of Project Meteor Server.
Project Meteor Server is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Project Meteor Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
===========================================================================
*/
using System;
using Meteor.Map.Actors;
namespace Meteor.Map.actors.chara.ai.controllers
{
class PetController : Controller
{
private Character petMaster;
public PetController(Character owner) :
base(owner)
{
this.lastUpdate = Program.Tick;
}
public override void Update(DateTime tick)
{
// todo: handle pet stuff on tick
}
public override void ChangeTarget(Character target)
{
base.ChangeTarget(target);
}
public override bool Engage(Character target)
{
// todo: check distance, last swing time, status effects
return true;
}
public override void Disengage()
{
// todo:
return;
}
public override void Cast(Character target, uint spellId)
{
}
public override void Ability(Character target, uint abilityId)
{
}
public override void RangedAttack(Character target)
{
}
public Character GetPetMaster()
{
return petMaster;
}
public void SetPetMaster(Character master)
{
petMaster = master;
if (master is Player)
owner.allegiance = CharacterTargetingAllegiance.Player;
else
owner.allegiance = CharacterTargetingAllegiance.BattleNpcs;
}
}
}

View File

@@ -0,0 +1,108 @@
/*
===========================================================================
Copyright (C) 2015-2019 Project Meteor Dev Team
This file is part of Project Meteor Server.
Project Meteor Server is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Project Meteor Server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Project Meteor Server. If not, see <https:www.gnu.org/licenses/>.
===========================================================================
*/
using System;
using Meteor.Map.Actors;
namespace Meteor.Map.actors.chara.ai.controllers
{
class PlayerController : Controller
{
private new Player owner;
public PlayerController(Player owner) :
base(owner)
{
this.owner = owner;
this.lastUpdate = DateTime.Now;
}
public override void Update(DateTime tick)
{
/*
if (owner.newMainState != owner.currentMainState)
{
if (owner.newMainState == SetActorStatePacket.MAIN_STATE_ACTIVE)
{
owner.Engage();
}
else
{
owner.Disengage();
}
owner.currentMainState = (ushort)owner.newMainState;
}*/
}
public override void ChangeTarget(Character target)
{
owner.target = target;
base.ChangeTarget(target);
}
public override bool Engage(Character target)
{
var canEngage = this.owner.aiContainer.InternalEngage(target);
if (canEngage)
{
if (owner.statusEffects.HasStatusEffect(StatusEffectId.Sleep))
{
// That command cannot be performed.
owner.SendGameMessage(Server.GetWorldManager().GetActor(), 32553, 0x20);
return false;
}
// todo: adjust cooldowns with modifiers
}
return canEngage;
}
public override void Disengage()
{
// todo:
base.Disengage();
return;
}
public override void Cast(Character target, uint spellId)
{
owner.aiContainer.InternalCast(target, spellId);
}
public override void WeaponSkill(Character target, uint weaponSkillId)
{
owner.aiContainer.InternalWeaponSkill(target, weaponSkillId);
}
public override void Ability(Character target, uint abilityId)
{
owner.aiContainer.InternalAbility(target, abilityId);
}
public override void RangedAttack(Character target)
{
}
public override void UseItem(Character target, uint slot, uint itemId)
{
owner.aiContainer.InternalUseItem(target, slot, itemId);
}
}
}