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,49 @@
/*
===========================================================================
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/>.
===========================================================================
*/
namespace Meteor.Map.actors.chara.npc
{
class ActorClass
{
public readonly uint actorClassId;
public readonly string classPath;
public readonly uint displayNameId;
public readonly uint propertyFlags;
public readonly string eventConditions;
public readonly ushort pushCommand;
public readonly ushort pushCommandSub;
public readonly byte pushCommandPriority;
public ActorClass(uint id, string classPath, uint nameId, uint propertyFlags, string eventConditions, ushort pushCommand, ushort pushCommandSub, byte pushCommandPriority)
{
this.actorClassId = id;
this.classPath = classPath;
this.displayNameId = nameId;
this.propertyFlags = propertyFlags;
this.eventConditions = eventConditions;
this.pushCommand = pushCommand;
this.pushCommandSub = pushCommandSub;
this.pushCommandPriority = pushCommandPriority;
}
}
}

View file

@ -0,0 +1,41 @@
/*
===========================================================================
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 Meteor.Map.Actors;
using Meteor.Map.actors.chara.ai;
using Meteor.Map.actors.chara.ai.controllers;
namespace Meteor.Map.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;
this.isAutoAttackEnabled = true;
this.isMovingToSpawn = false;
}
}
}

View file

@ -0,0 +1,465 @@
/*
===========================================================================
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.chara.npc;
using Meteor.Map.actors.chara;
using Meteor.Map.actors.chara.ai;
using Meteor.Map.actors.chara.ai.controllers;
using Meteor.Map.actors.chara.ai.state;
using Meteor.Map.utils;
using Meteor.Map.packets.send.actor.battle;
using Meteor.Map.actors.chara.ai.utils;
using Meteor.Map.actors.group;
using Meteor.Map.Actors.Chara;
namespace Meteor.Map.Actors
{
[Flags]
enum DetectionType
{
None = 0x00,
Sight = 0x01,
Scent = 0x02,
Sound = 0x04,
LowHp = 0x08,
IgnoreLevelDifference = 0x10,
Magic = 0x20,
}
enum KindredType
{
Unknown = 0,
Beast = 1,
Plantoid = 2,
Aquan = 3,
Spoken = 4,
Reptilian = 5,
Insect = 6,
Avian = 7,
Undead = 8,
Cursed = 9,
Voidsent = 10,
}
class BattleNpc : Npc
{
public HateContainer hateContainer;
public DetectionType detectionType;
public KindredType kindredType;
public bool neutral;
protected uint despawnTime;
protected uint respawnTime;
protected uint spawnDistance;
protected uint bnpcId;
public Character lastAttacker;
public uint spellListId, skillListId, dropListId;
public Dictionary<uint, BattleCommand> skillList = new Dictionary<uint, BattleCommand>();
public Dictionary<uint, BattleCommand> spellList = new Dictionary<uint, BattleCommand>();
public uint poolId, genusId;
public ModifierList poolMods;
public ModifierList genusMods;
public ModifierList spawnMods;
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)
: base(actorNumber, actorClass, uniqueId, spawnedArea, posX, posY, posZ, rot, actorState, animationId, customDisplayName)
{
this.aiContainer = new AIContainer(this, new BattleNpcController(this), new PathFind(this), new TargetFind(this));
//this.currentSubState = SetActorStatePacket.SUB_STATE_MONSTER;
//this.currentMainState = SetActorStatePacket.MAIN_STATE_ACTIVE;
//charaWork.property[2] = 1;
//npcWork.hateType = 1;
this.hateContainer = new HateContainer(this);
this.allegiance = CharacterTargetingAllegiance.BattleNpcs;
spawnX = posX;
spawnY = posY;
spawnZ = posZ;
despawnTime = 10;
CalculateBaseStats();
}
public override List<SubPacket> GetSpawnPackets(Player player, ushort spawnType)
{
List<SubPacket> subpackets = new List<SubPacket>();
if (IsAlive())
{
subpackets.Add(CreateAddActorPacket());
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(0x0));
subpackets.Add(CreateAppearancePacket());
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateSubStatePacket());
subpackets.Add(CreateInitStatusPacket());
subpackets.Add(CreateSetActorIconPacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket(player));
subpackets.Add(GetHateTypePacket(player));
}
return subpackets;
}
//This might need more work
//I think there migh be something that ties mobs to parties
//and the client checks if any mobs are tied to the current party
//and bases the color on that. Adding mob to party obviously doesn't work
//Based on depictionjudge script:
//HATE_TYPE_NONE is for passive
//HATE_TYPE_ENGAGED is for aggroed mobs
//HATE_TYPE_ENGAGED_PARTY is for claimed mobs, client uses occupancy group to determine if mob is claimed by player's party
//for now i'm just going to assume that occupancygroup will be BattleNpc's currentparties when they're in combat,
//so if party isn't null, they're claimed.
public SubPacket GetHateTypePacket(Player player)
{
npcWork.hateType = NpcWork.HATE_TYPE_NONE;
if (player != null)
{
if (aiContainer.IsEngaged())
{
npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED;
if (this.currentParty != null)
{
npcWork.hateType = NpcWork.HATE_TYPE_ENGAGED_PARTY;
}
}
}
npcWork.hateType = 3;
var propPacketUtil = new ActorPropertyPacketUtil("npcWork/hate", this);
propPacketUtil.AddProperty("npcWork.hateType");
return propPacketUtil.Done()[0];
}
public uint GetDetectionType()
{
return (uint)detectionType;
}
public void SetDetectionType(uint detectionType)
{
this.detectionType = (DetectionType)detectionType;
}
public override void Update(DateTime tick)
{
this.aiContainer.Update(tick);
this.statusEffects.Update(tick);
}
public override void PostUpdate(DateTime tick, List<SubPacket> packets = null)
{
// todo: should probably add another flag for battleTemp since all this uses reflection
packets = new List<SubPacket>();
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
{
var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this);
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkillLevel");
propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[0]");
propPacketUtil.AddProperty("charaWork.battleTemp.castGauge_speed[1]");
packets.AddRange(propPacketUtil.Done());
}
base.PostUpdate(tick, packets);
}
public override bool CanAttack()
{
// todo:
return true;
}
public override bool CanUse(Character target, BattleCommand spell, CommandResult error = null)
{
// todo:
if (target == null)
{
// Target does not exist.
return false;
}
if (Utils.Distance(positionX, positionY, positionZ, target.positionX, target.positionY, target.positionZ) > spell.range)
{
// The target is out of range.
return false;
}
if (!IsValidTarget(target, spell.mainTarget) || !spell.IsValidMainTarget(this, target))
{
// error packet is set in IsValidTarget
return false;
}
return true;
}
public uint GetDespawnTime()
{
return despawnTime;
}
public void SetDespawnTime(uint seconds)
{
despawnTime = seconds;
}
public uint GetRespawnTime()
{
return respawnTime;
}
public void SetRespawnTime(uint seconds)
{
respawnTime = seconds;
}
///<summary> // todo: create an action object? </summary>
public bool OnAttack(AttackState state)
{
return false;
}
public override void Spawn(DateTime tick)
{
if (respawnTime > 0)
{
ForceRespawn();
}
}
public void ForceRespawn()
{
base.Spawn(Program.Tick);
this.isMovingToSpawn = false;
this.hateContainer.ClearHate();
zone.BroadcastPacketsAroundActor(this, GetSpawnPackets(null, 0x01));
zone.BroadcastPacketsAroundActor(this, GetInitPackets());
RecalculateStats();
OnSpawn();
updateFlags |= ActorUpdateFlags.AllNpc;
}
public override void Die(DateTime tick, CommandResultContainer actionContainer = null)
{
if (IsAlive())
{
// todo: does retail
if (lastAttacker is Pet && lastAttacker.aiContainer.GetController<PetController>() != null && lastAttacker.aiContainer.GetController<PetController>().GetPetMaster() is Player)
{
lastAttacker = lastAttacker.aiContainer.GetController<PetController>().GetPetMaster();
}
if (lastAttacker is Player)
{
//I think this is, or should be odne in DoBattleAction. Packet capture had the message in the same packet as an attack
// <actor> defeat/defeats <target>
if (actionContainer != null)
actionContainer.AddEXPAction(new CommandResult(actorId, 30108, 0));
if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party)
{
foreach (var memberId in ((Party)lastAttacker.currentParty).members)
{
var partyMember = zone.FindActorInArea<Character>(memberId);
// onDeath(monster, player, killer)
lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker);
// todo: add actual experience calculation and exp bonus values.
if (partyMember is Player)
BattleUtils.AddBattleBonusEXP((Player)partyMember, this, actionContainer);
}
}
else
{
// onDeath(monster, player, killer)
lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, lastAttacker, lastAttacker);
//((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
}
}
if (positionUpdates != null)
positionUpdates.Clear();
aiContainer.InternalDie(tick, despawnTime);
//this.ResetMoveSpeeds();
// todo: reset cooldowns
lua.LuaEngine.GetInstance().OnSignal("mobkill");
}
else
{
var err = String.Format("[{0}][{1}] {2} {3} {4} {5} tried to die ded", actorId, GetUniqueId(), positionX, positionY, positionZ, GetZone().GetName());
Program.Log.Error(err);
//throw new Exception(err);
}
}
public override void Despawn(DateTime tick)
{
// todo: probably didnt need to make a new state...
aiContainer.InternalDespawn(tick, respawnTime);
lua.LuaEngine.CallLuaBattleFunction(this, "onDespawn", this);
this.isAtSpawn = true;
}
public void OnRoam(DateTime tick)
{
// leash back to spawn
if (!IsCloseToSpawn())
{
if (!isMovingToSpawn)
{
aiContainer.Reset();
isMovingToSpawn = true;
}
else
{
if (target == null && !aiContainer.pathFind.IsFollowingPath())
aiContainer.pathFind.PathInRange(spawnX, spawnY, spawnZ, 1.5f, 15.0f);
}
}
else
{
// recover hp
if (GetHPP() < 100)
{
AddHP(GetMaxHP() / 10);
}
else
{
this.isMovingToSpawn = false;
}
}
}
public bool IsCloseToSpawn()
{
return this.isAtSpawn = Utils.DistanceSquared(positionX, positionY, positionZ, spawnX, spawnY, spawnZ) <= 2500.0f;
}
public override void OnAttack(State state, CommandResult action, ref CommandResult error)
{
base.OnAttack(state, action, ref error);
// todo: move this somewhere else prolly and change based on model/appearance (so maybe in Character.cs instead)
action.animation = 0x11001000; // (temporary) wolf anim
if (GetMobMod((uint)MobModifier.AttackScript) != 0)
lua.LuaEngine.CallLuaBattleFunction(this, "onAttack", this, state.GetTarget(), action.amount);
}
public override void OnCast(State state, CommandResult[] actions, BattleCommand spell, ref CommandResult[] errors)
{
base.OnCast(state, actions, spell, 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, CommandResult[] actions, BattleCommand ability, ref CommandResult[] errors)
{
base.OnAbility(state, actions, ability, 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, CommandResult[] actions, BattleCommand skill, ref CommandResult[] errors)
{
base.OnWeaponSkill(state, actions, skill, 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()
{
base.OnSpawn();
lua.LuaEngine.CallLuaBattleFunction(this, "onSpawn", this);
}
public override void OnDeath()
{
base.OnDeath();
}
public override void OnDespawn()
{
base.OnDespawn();
}
public uint GetBattleNpcId()
{
return bnpcId;
}
public void SetBattleNpcId(uint id)
{
this.bnpcId = id;
}
public Int64 GetMobMod(MobModifier mobMod)
{
return GetMobMod((uint)mobMod);
}
public Int64 GetMobMod(uint mobModId)
{
Int64 res;
if (mobModifiers.TryGetValue((MobModifier)mobModId, out res))
return res;
return 0;
}
public void SetMobMod(uint mobModId, Int64 val)
{
if (mobModifiers.ContainsKey((MobModifier)mobModId))
mobModifiers[(MobModifier)mobModId] = val;
else
mobModifiers.Add((MobModifier)mobModId, val);
}
public override void OnDamageTaken(Character attacker, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
{
if (GetMobMod((uint)MobModifier.DefendScript) != 0)
lua.LuaEngine.CallLuaBattleFunction(this, "onDamageTaken", this, attacker, action.amount);
base.OnDamageTaken(attacker, skill, action, actionContainer);
}
}
}

View file

@ -0,0 +1,55 @@
/*
===========================================================================
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/>.
===========================================================================
*/
namespace Meteor.Map.actors.chara.npc
{
enum MobModifier
{
None = 0,
SpawnLeash = 1, // how far can i move before i deaggro target
SightRange = 2, // how close does target need to be for me to detect by sight
SoundRange = 3, // how close does target need to be for me to detect by sound
BuffChance = 4,
HealChance = 5,
SkillUseChance = 6,
LinkRadius = 7,
MagicDelay = 8,
SpecialDelay = 9,
ExpBonus = 10, //
IgnoreSpawnLeash = 11, // pursue target forever
DrawIn = 12, // do i suck people in around me
HpScale = 13, //
Assist = 14, // gotta call the bois
NoMove = 15, // cant move
ShareTarget = 16, // use this actor's id as target id
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
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
Roams = 24, // Do I walk around?
RoamDelay = 25, // What is the delay between roam ticks
Linked = 26, // Did I get aggroed via linking?
LinkCount = 27 // How many BattleNPCs got linked with me
}
}

View file

@ -0,0 +1,468 @@
/*
===========================================================================
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 Meteor.Common;
using Meteor.Map.actors;
using Meteor.Map.actors.chara.npc;
using Meteor.Map.Actors.Chara;
using Meteor.Map.lua;
using Meteor.Map.packets.send.actor;
using Meteor.Map.utils;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using Meteor.Map.actors.chara.ai;
namespace Meteor.Map.Actors
{
[Flags]
enum NpcSpawnType : ushort
{
Normal = 0x00,
Scripted = 0x01,
Nighttime = 0x02,
Evening = 0x04,
Daytime = 0x08,
Weather = 0x10,
}
class Npc : Character
{
private uint actorClassId;
private string uniqueIdentifier;
private bool isMapObj = false;
private uint layout, instance;
public NpcWork npcWork = new NpcWork();
public NpcSpawnType npcSpawnType;
public Npc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, ushort actorState, uint animationId, string customDisplayName)
: base((4 << 28 | spawnedArea.actorId << 19 | (uint)actorNumber))
{
this.positionX = posX;
this.positionY = posY;
this.positionZ = posZ;
this.rotation = rot;
this.currentMainState = actorState;
this.animationId = animationId;
this.displayNameId = actorClass.displayNameId;
this.customDisplayName = customDisplayName;
this.uniqueIdentifier = uniqueId;
this.zoneId = spawnedArea.actorId;
this.zone = spawnedArea;
this.actorClassId = actorClass.actorClassId;
this.currentSubState.motionPack = (ushort) animationId;
LoadNpcAppearance(actorClass.actorClassId);
className = actorClass.classPath.Substring(actorClass.classPath.LastIndexOf("/") + 1);
this.classPath = String.Format("{0}/{1}", actorClass.classPath.Substring(0, actorClass.classPath.LastIndexOf('/')).ToLower(), className);
charaWork.battleSave.potencial = 1.0f;
// todo: these really need to be read from db etc
{
charaWork.parameterSave.state_mainSkill[0] = 3;
charaWork.parameterSave.state_mainSkill[2] = 3;
charaWork.parameterSave.state_mainSkillLevel = 1;
charaWork.parameterSave.hp[0] = 80;
charaWork.parameterSave.hpMax[0] = 80;
}
for (int i = 0; i < 32; i++ )
charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1);
npcWork.pushCommand = actorClass.pushCommand;
npcWork.pushCommandSub = actorClass.pushCommandSub;
npcWork.pushCommandPriority = actorClass.pushCommandPriority;
if (actorClassId == 1080078 || actorClassId == 1080079 || actorClassId == 1080080 || (actorClassId >= 1080123 && actorClassId <= 1080135) || (actorClassId >= 5000001 && actorClassId <= 5000090) || (actorClassId >= 5900001 && actorClassId <= 5900038))
{
isMapObj = true;
List<LuaParam> lParams = LuaEngine.GetInstance().CallLuaFunctionForReturn(null, this, "init", false);
if (lParams == null || lParams.Count < 6)
isMapObj = false;
else
{
layout = (uint)(Int32)lParams[4].value;
instance = (uint)(Int32)lParams[5].value;
isStatic = true;
}
}
GenerateActorName((int)actorNumber);
this.aiContainer = new AIContainer(this, null, new PathFind(this), new TargetFind(this));
}
public Npc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, uint layout, uint instance)
: base((4 << 28 | spawnedArea.actorId << 19 | (uint)actorNumber))
{
this.positionX = posX;
this.positionY = posY;
this.positionZ = posZ;
this.rotation = rot;
this.currentMainState = 0;
this.animationId = 0;
this.displayNameId = actorClass.displayNameId;
this.uniqueIdentifier = uniqueId;
this.zoneId = spawnedArea.actorId;
this.zone = spawnedArea;
this.actorClassId = actorClass.actorClassId;
LoadNpcAppearance(actorClass.actorClassId);
this.classPath = actorClass.classPath;
className = classPath.Substring(classPath.LastIndexOf("/") + 1);
for (int i = 0; i < 32; i++)
charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1);
npcWork.pushCommand = actorClass.pushCommand;
npcWork.pushCommandSub = actorClass.pushCommandSub;
npcWork.pushCommandPriority = actorClass.pushCommandPriority;
this.isMapObj = true;
this.layout = layout;
this.instance = instance;
GenerateActorName((int)actorNumber);
this.aiContainer = new AIContainer(this, null, new PathFind(this), new TargetFind(null));
}
public SubPacket CreateAddActorPacket()
{
return AddActorPacket.BuildPacket(actorId, 8);
}
// actorClassId, [], [], numBattleCommon, [battleCommon], numEventCommon, [eventCommon], args for either initForBattle/initForEvent
public override SubPacket CreateScriptBindPacket(Player player)
{
List<LuaParam> lParams;
lParams = LuaEngine.GetInstance().CallLuaFunctionForReturn(player, this, "init", false);
if (lParams != null && lParams.Count >= 3 && lParams[2].typeID == 0 && (int)lParams[2].value == 0)
isStatic = true;
else
{
//charaWork.property[2] = 1;
//npcWork.hateType = 1;
}
if (lParams == null)
{
string classPathFake = "/Chara/Npc/Populace/PopulaceStandard";
string classNameFake = "PopulaceStandard";
lParams = LuaUtils.CreateLuaParamList(classPathFake, false, false, false, false, false, 0xF47F6, false, false, 0, 0);
isStatic = true;
//ActorInstantiatePacket.BuildPacket(actorId, actorName, classNameFake, lParams).DebugPrintSubPacket();
return ActorInstantiatePacket.BuildPacket(actorId, actorName, classNameFake, lParams);
}
else
{
lParams.Insert(0, new LuaParam(2, classPath));
lParams.Insert(1, new LuaParam(4, 4));
lParams.Insert(2, new LuaParam(4, 4));
lParams.Insert(3, new LuaParam(4, 4));
lParams.Insert(4, new LuaParam(4, 4));
lParams.Insert(5, new LuaParam(4, 4));
lParams.Insert(6, new LuaParam(0, (int)actorClassId));
}
//ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams).DebugPrintSubPacket();
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
}
public override List<SubPacket> GetSpawnPackets(Player player, ushort spawnType)
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket());
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(0x0));
if (isMapObj)
subpackets.Add(SetActorBGPropertiesPacket.BuildPacket(actorId, instance, layout));
else
subpackets.Add(CreateAppearancePacket());
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateSubStatePacket());
subpackets.Add(CreateInitStatusPacket());
subpackets.Add(CreateSetActorIconPacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket(player));
return subpackets;
}
public override List<SubPacket> GetInitPackets()
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("/_init", this);
//Potential
propPacketUtil.AddProperty("charaWork.battleSave.potencial");
//Properties
for (int i = 0; i < charaWork.property.Length; i++)
{
if (charaWork.property[i] != 0)
propPacketUtil.AddProperty(String.Format("charaWork.property[{0}]", i));
}
//Parameters
propPacketUtil.AddProperty("charaWork.parameterSave.hp[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.mp");
propPacketUtil.AddProperty("charaWork.parameterSave.mpMax");
propPacketUtil.AddProperty("charaWork.parameterTemp.tp");
if (charaWork.parameterSave.state_mainSkill[0] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[0]");
if (charaWork.parameterSave.state_mainSkill[1] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[1]");
if (charaWork.parameterSave.state_mainSkill[2] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[2]");
if (charaWork.parameterSave.state_mainSkill[3] != 0)
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkill[3]");
propPacketUtil.AddProperty("charaWork.parameterSave.state_mainSkillLevel");
//Status Times
for (int i = 0; i < charaWork.statusShownTime.Length; i++)
{
if (charaWork.statusShownTime[i] != 0)
propPacketUtil.AddProperty(String.Format("charaWork.statusShownTime[{0}]", i));
}
//General Parameters
for (int i = 3; i < charaWork.battleTemp.generalParameter.Length; i++)
{
if (charaWork.battleTemp.generalParameter[i] != 0)
propPacketUtil.AddProperty(String.Format("charaWork.battleTemp.generalParameter[{0}]", i));
}
propPacketUtil.AddProperty("npcWork.hateType");
if (npcWork.pushCommand != 0)
{
propPacketUtil.AddProperty("npcWork.pushCommand");
if (npcWork.pushCommandSub != 0)
propPacketUtil.AddProperty("npcWork.pushCommandSub");
propPacketUtil.AddProperty("npcWork.pushCommandPriority");
}
return propPacketUtil.Done();
}
public string GetUniqueId()
{
return uniqueIdentifier;
}
public uint GetActorClassId()
{
return actorClassId;
}
public void ChangeNpcAppearance(uint id)
{
LoadNpcAppearance(id);
zone.BroadcastPacketAroundActor(this, CreateAppearancePacket());
}
public void LoadNpcAppearance(uint id)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
try
{
conn.Open();
string query = @"
SELECT
base,
size,
hairStyle,
hairHighlightColor,
hairVariation,
faceType,
characteristics,
characteristicsColor,
faceEyebrows,
faceIrisSize,
faceEyeShape,
faceNose,
faceFeatures,
faceMouth,
ears,
hairColor,
skinColor,
eyeColor,
voice,
mainHand,
offHand,
spMainHand,
spOffHand,
throwing,
pack,
pouch,
head,
body,
legs,
hands,
feet,
waist,
neck,
leftEar,
rightEar,
leftIndex,
rightIndex,
leftFinger,
rightFinger
FROM gamedata_actor_appearance
WHERE id = @templateId
";
MySqlCommand cmd = new MySqlCommand(query, conn);
cmd.Parameters.AddWithValue("@templateId", id);
using (MySqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
//Handle Appearance
modelId = reader.GetUInt32(0);
appearanceIds[Character.SIZE] = reader.GetUInt32(1);
appearanceIds[Character.COLORINFO] = (uint)(reader.GetUInt32(16) | (reader.GetUInt32(15) << 10) | (reader.GetUInt32(17) << 20)); //17 - Skin Color, 16 - Hair Color, 18 - Eye Color
appearanceIds[Character.FACEINFO] = PrimitiveConversion.ToUInt32(CharacterUtils.GetFaceInfo(reader.GetByte(6), reader.GetByte(7), reader.GetByte(5), reader.GetByte(14), reader.GetByte(13), reader.GetByte(12), reader.GetByte(11), reader.GetByte(10), reader.GetByte(9), reader.GetByte(8)));
appearanceIds[Character.HIGHLIGHT_HAIR] = (uint)(reader.GetUInt32(3) | reader.GetUInt32(2) << 10); //5- Hair Highlight, 4 - Hair Style
appearanceIds[Character.VOICE] = reader.GetUInt32(17);
appearanceIds[Character.MAINHAND] = reader.GetUInt32(19);
appearanceIds[Character.OFFHAND] = reader.GetUInt32(20);
appearanceIds[Character.SPMAINHAND] = reader.GetUInt32(21);
appearanceIds[Character.SPOFFHAND] = reader.GetUInt32(22);
appearanceIds[Character.THROWING] = reader.GetUInt32(23);
appearanceIds[Character.PACK] = reader.GetUInt32(24);
appearanceIds[Character.POUCH] = reader.GetUInt32(25);
appearanceIds[Character.HEADGEAR] = reader.GetUInt32(26);
appearanceIds[Character.BODYGEAR] = reader.GetUInt32(27);
appearanceIds[Character.LEGSGEAR] = reader.GetUInt32(28);
appearanceIds[Character.HANDSGEAR] = reader.GetUInt32(29);
appearanceIds[Character.FEETGEAR] = reader.GetUInt32(30);
appearanceIds[Character.WAISTGEAR] = reader.GetUInt32(31);
appearanceIds[Character.NECKGEAR] = reader.GetUInt32(32);
appearanceIds[Character.R_EAR] = reader.GetUInt32(33);
appearanceIds[Character.L_EAR] = reader.GetUInt32(34);
appearanceIds[Character.R_INDEXFINGER] = reader.GetUInt32(35);
appearanceIds[Character.L_INDEXFINGER] = reader.GetUInt32(36);
appearanceIds[Character.R_RINGFINGER] = reader.GetUInt32(37);
appearanceIds[Character.L_RINGFINGER] = reader.GetUInt32(38);
}
}
}
catch (MySqlException e)
{ Console.WriteLine(e); }
finally
{
conn.Dispose();
}
}
}
public void LoadEventConditions(string eventConditions)
{
EventList conditions = JsonConvert.DeserializeObject<EventList>(eventConditions);
this.eventConditions = conditions;
}
public void DoOnActorSpawn(Player player)
{
LuaEngine.GetInstance().CallLuaFunction(player, this, "onSpawn", true);
}
public void PlayMapObjAnimation(Player player, string animationName)
{
player.QueuePacket(PlayBGAnimation.BuildPacket(actorId, animationName));
}
public void Despawn()
{
zone.DespawnActor(this);
}
public override void Update(DateTime tick)
{
// todo: can normal npcs have status effects?
aiContainer.Update(tick);
}
public override void PostUpdate(DateTime tick, List<SubPacket> packets = null)
{
packets = packets ?? new List<SubPacket>();
if ((updateFlags & ActorUpdateFlags.Work) != 0)
{
}
base.PostUpdate(tick, packets);
}
public override void OnSpawn()
{
base.OnSpawn();
}
public override void OnDeath()
{
base.OnDeath();
}
public override void OnDespawn()
{
zone.BroadcastPacketAroundActor(this, RemoveActorPacket.BuildPacket(this.actorId));
QueuePositionUpdate(spawnX, spawnY, spawnZ);
LuaEngine.CallLuaBattleFunction(this, "onDespawn", this);
}
//A party member list packet came, set the party
/* public void SetParty(MonsterPartyGroup group)
{
if (group is MonsterPartyGroup)
currentParty = group;
}
*/
}
}

View file

@ -0,0 +1,35 @@
/*
===========================================================================
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/>.
===========================================================================
*/
namespace Meteor.Map.Actors.Chara
{
class NpcWork
{
public static byte HATE_TYPE_NONE = 0;
public static byte HATE_TYPE_ENGAGED = 2;
public static byte HATE_TYPE_ENGAGED_PARTY = 3;
public ushort pushCommand;
public int pushCommandSub;
public byte pushCommandPriority;
public byte hateType = 1;
}
}

View file

@ -0,0 +1,39 @@
/*
===========================================================================
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 Meteor.Map.actors.chara.ai;
using Meteor.Map.actors.chara.ai.controllers;
using Meteor.Map.actors.chara.npc;
namespace Meteor.Map.Actors
{
class Pet : BattleNpc
{
public Pet(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)
{
this.aiContainer = new AIContainer(this, new PetController(this), new PathFind(this), new TargetFind(this));
this.hateContainer = new HateContainer(this);
}
}
}

View file

@ -0,0 +1,58 @@
/*
===========================================================================
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 Meteor.Map.actors.chara.player;
using Meteor.Map.Actors;
using System;
namespace Meteor.Map.actors.chara.npc
{
class Retainer : Npc
{
public const int MAXSIZE_INVENTORY_NORMAL = 150;
public const int MAXSIZE_INVENTORY_CURRANCY = 320;
public const int MAXSIZE_INVENTORY_BAZAAR = 10;
private uint retainerId;
private Player ownerPlayer;
public Retainer(uint retainerId, ActorClass actorClass, Player player, float posX, float posY, float posZ, float rot)
: base(0, actorClass, "myretainer", player.GetZone(), posX, posY, posZ, rot, 0, 0, null)
{
this.retainerId = retainerId;
this.ownerPlayer = player;
this.actorName = String.Format("_rtnre{0:x7}", actorId);
itemPackages[ItemPackage.NORMAL] = new ItemPackage(this, MAXSIZE_INVENTORY_NORMAL, ItemPackage.NORMAL);
itemPackages[ItemPackage.CURRENCY_CRYSTALS] = new ItemPackage(this, MAXSIZE_INVENTORY_CURRANCY, ItemPackage.CURRENCY_CRYSTALS);
itemPackages[ItemPackage.BAZAAR] = new ItemPackage(this, MAXSIZE_INVENTORY_BAZAAR, ItemPackage.BAZAAR);
itemPackages[ItemPackage.NORMAL].InitList(Database.GetItemPackage(this, 0, ItemPackage.NORMAL));
itemPackages[ItemPackage.CURRENCY_CRYSTALS].InitList(Database.GetItemPackage(this, 0, ItemPackage.CURRENCY_CRYSTALS));
itemPackages[ItemPackage.BAZAAR].InitList(Database.GetItemPackage(this, 0, ItemPackage.BAZAAR));
}
public uint GetRetainerId()
{
return retainerId;
}
}
}