mirror of
https://bitbucket.org/Ioncannon/project-meteor-server.git
synced 2025-04-02 19:42:05 -04:00
Removing the duplicate bugs after renaming folders to upper case (pls don't break).
This commit is contained in:
parent
ae3a6f1b27
commit
e4d43d952b
@ -1,787 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using Meteor.Map.packets.send.actor.events;
|
||||
using Meteor.Common;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Meteor.Map.actors.area;
|
||||
using System.Reflection;
|
||||
using System.ComponentModel;
|
||||
using Meteor.Map.actors.chara;
|
||||
|
||||
namespace Meteor.Map.Actors
|
||||
{
|
||||
[Flags]
|
||||
enum ActorUpdateFlags
|
||||
{
|
||||
None = 0x00,
|
||||
Position = 0x01,
|
||||
HpTpMp = 0x02,
|
||||
State = 0x04,
|
||||
SubState = 0x08,
|
||||
Combat = 0x0F,
|
||||
Name = 0x10,
|
||||
Appearance = 0x20,
|
||||
Speed = 0x40,
|
||||
Work = 0x80,
|
||||
Stats = 0x100,
|
||||
Status = 0x200,
|
||||
StatusTime = 0x400,
|
||||
Hotbar = 0x800,
|
||||
|
||||
AllNpc = 0xDF,
|
||||
AllPlayer = 0x13F
|
||||
}
|
||||
|
||||
class Actor
|
||||
{
|
||||
public static uint INVALID_ACTORID = 0xC0000000;
|
||||
public uint actorId;
|
||||
public string actorName;
|
||||
|
||||
public uint displayNameId = 0xFFFFFFFF;
|
||||
public string customDisplayName;
|
||||
|
||||
public ushort currentMainState = SetActorStatePacket.MAIN_STATE_PASSIVE;
|
||||
|
||||
public SubState currentSubState = new SubState();
|
||||
|
||||
public float positionX, positionY, positionZ, rotation;
|
||||
public float oldPositionX, oldPositionY, oldPositionZ, oldRotation;
|
||||
public ushort moveState, oldMoveState;
|
||||
public float[] moveSpeeds = new float[4];
|
||||
|
||||
public uint zoneId, zoneId2;
|
||||
public string privateArea;
|
||||
public uint privateAreaType;
|
||||
public Area zone = null;
|
||||
public Area zone2 = null;
|
||||
public bool isZoning = false;
|
||||
|
||||
public bool spawnedFirstTime = false;
|
||||
|
||||
public string classPath;
|
||||
public string className;
|
||||
public List<LuaParam> classParams;
|
||||
|
||||
public List<Vector3> positionUpdates;
|
||||
protected DateTime lastUpdateScript;
|
||||
protected DateTime lastUpdate;
|
||||
public Actor target;
|
||||
|
||||
public bool isAtSpawn = true;
|
||||
|
||||
public ActorUpdateFlags updateFlags;
|
||||
|
||||
public EventList eventConditions;
|
||||
|
||||
public Actor(uint actorId)
|
||||
{
|
||||
this.actorId = actorId;
|
||||
}
|
||||
|
||||
public Actor(uint actorId, string actorName, string className, List<LuaParam> classParams)
|
||||
{
|
||||
this.actorId = actorId;
|
||||
this.actorName = actorName;
|
||||
this.className = className;
|
||||
this.classParams = classParams;
|
||||
|
||||
this.moveSpeeds[0] = SetActorSpeedPacket.DEFAULT_STOP;
|
||||
this.moveSpeeds[1] = SetActorSpeedPacket.DEFAULT_WALK;
|
||||
this.moveSpeeds[2] = SetActorSpeedPacket.DEFAULT_RUN;
|
||||
this.moveSpeeds[3] = SetActorSpeedPacket.DEFAULT_ACTIVE;
|
||||
positionUpdates = new List<Vector3>();
|
||||
}
|
||||
|
||||
public void SetPushCircleRange(string triggerName, float size)
|
||||
{
|
||||
if (eventConditions == null || eventConditions.pushWithCircleEventConditions == null)
|
||||
return;
|
||||
|
||||
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
|
||||
{
|
||||
if (condition.conditionName.Equals(triggerName))
|
||||
{
|
||||
condition.radius = size;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ResetMoveSpeeds()
|
||||
{
|
||||
this.moveSpeeds[0] = SetActorSpeedPacket.DEFAULT_STOP;
|
||||
this.moveSpeeds[1] = SetActorSpeedPacket.DEFAULT_WALK;
|
||||
this.moveSpeeds[2] = SetActorSpeedPacket.DEFAULT_RUN;
|
||||
this.moveSpeeds[3] = SetActorSpeedPacket.DEFAULT_ACTIVE;
|
||||
|
||||
this.moveState = this.oldMoveState;
|
||||
this.updateFlags |= ActorUpdateFlags.Speed;
|
||||
}
|
||||
|
||||
public SubPacket CreateAddActorPacket(byte val)
|
||||
{
|
||||
return AddActorPacket.BuildPacket(actorId, val);
|
||||
}
|
||||
|
||||
public SubPacket CreateNamePacket()
|
||||
{
|
||||
return SetActorNamePacket.BuildPacket(actorId, customDisplayName != null ? 0 : displayNameId, displayNameId == 0xFFFFFFFF | displayNameId == 0x0 | customDisplayName != null ? customDisplayName : "");
|
||||
}
|
||||
|
||||
public SubPacket CreateSpeedPacket()
|
||||
{
|
||||
return SetActorSpeedPacket.BuildPacket(actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2], moveSpeeds[3]);
|
||||
}
|
||||
|
||||
public SubPacket CreateSpawnPositonPacket(ushort spawnType)
|
||||
{
|
||||
return CreateSpawnPositonPacket(null, spawnType);
|
||||
}
|
||||
|
||||
public SubPacket CreateSpawnPositonPacket(Player player, ushort spawnType)
|
||||
{
|
||||
//TODO: FIX THIS IF
|
||||
uint playerActorId = player == null ? 0 : player.actorId; //Get Rid
|
||||
SubPacket spawnPacket;
|
||||
if (!spawnedFirstTime && playerActorId == actorId)
|
||||
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0, positionX, positionY, positionZ, rotation, 0x1, false);
|
||||
else if (playerActorId == actorId)
|
||||
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, true);
|
||||
else
|
||||
{
|
||||
if (this is Player)
|
||||
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0, positionX, positionY, positionZ, rotation, spawnType, false);
|
||||
else
|
||||
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, actorId, positionX, positionY, positionZ, rotation, spawnType, false);
|
||||
}
|
||||
|
||||
//return SetActorPositionPacket.BuildPacket(actorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
|
||||
spawnedFirstTime = true;
|
||||
|
||||
return spawnPacket;
|
||||
}
|
||||
|
||||
public SubPacket CreateSpawnTeleportPacket(ushort spawnType)
|
||||
{
|
||||
SubPacket spawnPacket;
|
||||
|
||||
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, false);
|
||||
|
||||
//return SetActorPositionPacket.BuildPacket(actorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
|
||||
|
||||
//spawnPacket.DebugPrintSubPacket();
|
||||
|
||||
return spawnPacket;
|
||||
}
|
||||
|
||||
public SubPacket CreatePositionUpdatePacket()
|
||||
{
|
||||
return MoveActorToPositionPacket.BuildPacket(actorId, positionX, positionY, positionZ, rotation, moveState);
|
||||
}
|
||||
|
||||
public SubPacket CreateStatePacket()
|
||||
{
|
||||
return SetActorStatePacket.BuildPacket(actorId, currentMainState, 0);
|
||||
}
|
||||
|
||||
public List<SubPacket> GetEventConditionPackets()
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
|
||||
//Return empty list
|
||||
if (eventConditions == null)
|
||||
return subpackets;
|
||||
|
||||
if (eventConditions.talkEventConditions != null)
|
||||
{
|
||||
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
|
||||
subpackets.Add(SetTalkEventCondition.BuildPacket(actorId, condition));
|
||||
}
|
||||
|
||||
if (eventConditions.noticeEventConditions != null)
|
||||
{
|
||||
foreach (EventList.NoticeEventCondition condition in eventConditions.noticeEventConditions)
|
||||
subpackets.Add(SetNoticeEventCondition.BuildPacket(actorId, condition));
|
||||
}
|
||||
|
||||
if (eventConditions.emoteEventConditions != null)
|
||||
{
|
||||
foreach (EventList.EmoteEventCondition condition in eventConditions.emoteEventConditions)
|
||||
subpackets.Add(SetEmoteEventCondition.BuildPacket(actorId, condition));
|
||||
}
|
||||
|
||||
if (eventConditions.pushWithCircleEventConditions != null)
|
||||
{
|
||||
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
|
||||
subpackets.Add(SetPushEventConditionWithCircle.BuildPacket(actorId, condition));
|
||||
}
|
||||
|
||||
if (eventConditions.pushWithFanEventConditions != null)
|
||||
{
|
||||
foreach (EventList.PushFanEventCondition condition in eventConditions.pushWithFanEventConditions)
|
||||
subpackets.Add(SetPushEventConditionWithFan.BuildPacket(actorId, condition));
|
||||
}
|
||||
|
||||
if (eventConditions.pushWithBoxEventConditions != null)
|
||||
{
|
||||
foreach (EventList.PushBoxEventCondition condition in eventConditions.pushWithBoxEventConditions)
|
||||
subpackets.Add(SetPushEventConditionWithTriggerBox.BuildPacket(actorId, condition));
|
||||
}
|
||||
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
public List<SubPacket> GetSetEventStatusPackets()
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
|
||||
//Return empty list
|
||||
if (eventConditions == null)
|
||||
return subpackets;
|
||||
|
||||
if (eventConditions.talkEventConditions != null)
|
||||
{
|
||||
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
|
||||
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 1, condition.conditionName));
|
||||
}
|
||||
|
||||
if (eventConditions.noticeEventConditions != null)
|
||||
{
|
||||
foreach (EventList.NoticeEventCondition condition in eventConditions.noticeEventConditions)
|
||||
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 1, condition.conditionName));
|
||||
}
|
||||
|
||||
if (eventConditions.emoteEventConditions != null)
|
||||
{
|
||||
foreach (EventList.EmoteEventCondition condition in eventConditions.emoteEventConditions)
|
||||
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 3, condition.conditionName));
|
||||
}
|
||||
|
||||
if (eventConditions.pushWithCircleEventConditions != null)
|
||||
{
|
||||
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
|
||||
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
|
||||
}
|
||||
|
||||
if (eventConditions.pushWithFanEventConditions != null)
|
||||
{
|
||||
foreach (EventList.PushFanEventCondition condition in eventConditions.pushWithFanEventConditions)
|
||||
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
|
||||
}
|
||||
|
||||
if (eventConditions.pushWithBoxEventConditions != null)
|
||||
{
|
||||
foreach (EventList.PushBoxEventCondition condition in eventConditions.pushWithBoxEventConditions)
|
||||
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
|
||||
}
|
||||
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
public SubPacket CreateIsZoneingPacket()
|
||||
{
|
||||
return SetActorIsZoningPacket.BuildPacket(actorId, false);
|
||||
}
|
||||
|
||||
public virtual SubPacket CreateScriptBindPacket(Player player)
|
||||
{
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, classParams);
|
||||
}
|
||||
|
||||
public virtual SubPacket CreateScriptBindPacket()
|
||||
{
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, classParams);
|
||||
}
|
||||
|
||||
public virtual List<SubPacket> GetSpawnPackets(Player player, ushort spawnType)
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
subpackets.Add(CreateAddActorPacket(8));
|
||||
subpackets.AddRange(GetEventConditionPackets());
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpawnPositonPacket(player, spawnType));
|
||||
subpackets.Add(CreateNamePacket());
|
||||
subpackets.Add(CreateStatePacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.Add(CreateScriptBindPacket(player));
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
public virtual List<SubPacket> GetSpawnPackets()
|
||||
{
|
||||
return GetSpawnPackets(0x1);
|
||||
}
|
||||
|
||||
public virtual List<SubPacket> GetSpawnPackets(ushort spawnType)
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
subpackets.Add(CreateAddActorPacket(8));
|
||||
subpackets.AddRange(GetEventConditionPackets());
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpawnPositonPacket(null, spawnType));
|
||||
subpackets.Add(CreateNamePacket());
|
||||
subpackets.Add(CreateStatePacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.Add(CreateScriptBindPacket());
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
public virtual List<SubPacket> GetInitPackets()
|
||||
{
|
||||
List<SubPacket> packets = new List<SubPacket>();
|
||||
SetActorPropetyPacket initProperties = new SetActorPropetyPacket("/_init");
|
||||
initProperties.AddByte(0xE14B0CA8, 1);
|
||||
initProperties.AddByte(0x2138FD71, 1);
|
||||
initProperties.AddByte(0xFBFBCFB1, 1);
|
||||
initProperties.AddTarget();
|
||||
packets.Add(initProperties.BuildPacket(actorId));
|
||||
return packets;
|
||||
}
|
||||
|
||||
public override bool Equals(Object obj)
|
||||
{
|
||||
Actor actorObj = obj as Actor;
|
||||
if (actorObj == null)
|
||||
return false;
|
||||
else
|
||||
return actorId == actorObj.actorId;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return actorName;
|
||||
}
|
||||
|
||||
public string GetClassName()
|
||||
{
|
||||
return className;
|
||||
}
|
||||
|
||||
public ushort GetState()
|
||||
{
|
||||
return currentMainState;
|
||||
}
|
||||
|
||||
public List<LuaParam> GetLuaParams()
|
||||
{
|
||||
return classParams;
|
||||
}
|
||||
|
||||
//character's newMainState kind of messes with this
|
||||
public void ChangeState(ushort newState)
|
||||
{
|
||||
if (newState != currentMainState)
|
||||
{
|
||||
currentMainState = newState;
|
||||
|
||||
updateFlags |= (ActorUpdateFlags.State | ActorUpdateFlags.Position);
|
||||
}
|
||||
}
|
||||
|
||||
public SubState GetSubState()
|
||||
{
|
||||
return currentSubState;
|
||||
}
|
||||
|
||||
public void SubstateModified()
|
||||
{
|
||||
updateFlags |= (ActorUpdateFlags.SubState);
|
||||
}
|
||||
|
||||
public void ModifySpeed(float mod)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
moveSpeeds[i] *= mod;
|
||||
}
|
||||
updateFlags |= ActorUpdateFlags.Speed;
|
||||
}
|
||||
|
||||
public void ChangeSpeed(int type, float value)
|
||||
{
|
||||
moveSpeeds[type] = value;
|
||||
updateFlags |= ActorUpdateFlags.Speed;
|
||||
}
|
||||
|
||||
public void ChangeSpeed(float speedStop, float speedWalk, float speedRun, float speedActive)
|
||||
{
|
||||
moveSpeeds[0] = speedStop;
|
||||
moveSpeeds[1] = speedWalk;
|
||||
moveSpeeds[2] = speedRun;
|
||||
moveSpeeds[3] = speedActive;
|
||||
updateFlags |= ActorUpdateFlags.Speed;
|
||||
}
|
||||
|
||||
public virtual void Update(DateTime tick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void PostUpdate(DateTime tick, List<SubPacket> packets = null)
|
||||
{
|
||||
if (updateFlags != ActorUpdateFlags.None)
|
||||
{
|
||||
packets = packets ?? new List<SubPacket>();
|
||||
if ((updateFlags & ActorUpdateFlags.Position) != 0)
|
||||
{
|
||||
if (positionUpdates != null && positionUpdates.Count > 0)
|
||||
{
|
||||
var pos = positionUpdates[0];
|
||||
if (pos != null)
|
||||
{
|
||||
oldPositionX = positionX;
|
||||
oldPositionY = positionY;
|
||||
oldPositionZ = positionZ;
|
||||
oldRotation = rotation;
|
||||
|
||||
positionX = pos.X;
|
||||
positionY = pos.Y;
|
||||
positionZ = pos.Z;
|
||||
|
||||
zone.UpdateActorPosition(this);
|
||||
|
||||
//Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates)
|
||||
}
|
||||
positionUpdates.Remove(pos);
|
||||
|
||||
}
|
||||
packets.Add(CreatePositionUpdatePacket());
|
||||
}
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.Speed) != 0)
|
||||
{
|
||||
packets.Add(SetActorSpeedPacket.BuildPacket(actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2], moveSpeeds[3]));
|
||||
}
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.Name) != 0)
|
||||
{
|
||||
packets.Add(SetActorNamePacket.BuildPacket(actorId, displayNameId, customDisplayName));
|
||||
}
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.State) != 0)
|
||||
{
|
||||
packets.Add(SetActorStatePacket.BuildPacket(actorId, currentMainState, 0x3B));
|
||||
}
|
||||
|
||||
if ((updateFlags & ActorUpdateFlags.SubState) != 0)
|
||||
{
|
||||
packets.Add(SetActorSubStatePacket.BuildPacket(actorId, currentSubState));
|
||||
}
|
||||
|
||||
updateFlags = ActorUpdateFlags.None;
|
||||
}
|
||||
zone.BroadcastPacketsAroundActor(this, packets);
|
||||
}
|
||||
|
||||
public void GenerateActorName(int actorNumber)
|
||||
{
|
||||
//Format Class Name
|
||||
string className = this.className.Replace("Populace", "Ppl")
|
||||
.Replace("Monster", "Mon")
|
||||
.Replace("Crowd", "Crd")
|
||||
.Replace("MapObj", "Map")
|
||||
.Replace("Object", "Obj")
|
||||
.Replace("Retainer", "Rtn")
|
||||
.Replace("Standard", "Std");
|
||||
className = Char.ToLowerInvariant(className[0]) + className.Substring(1);
|
||||
|
||||
//Format Zone Name
|
||||
string zoneName = zone.zoneName.Replace("Field", "Fld")
|
||||
.Replace("Dungeon", "Dgn")
|
||||
.Replace("Town", "Twn")
|
||||
.Replace("Battle", "Btl")
|
||||
.Replace("Test", "Tes")
|
||||
.Replace("Event", "Evt")
|
||||
.Replace("Ship", "Shp")
|
||||
.Replace("Office", "Ofc");
|
||||
if (zone is PrivateArea)
|
||||
{
|
||||
//Check if "normal"
|
||||
zoneName = zoneName.Remove(zoneName.Length - 1, 1) + "P";
|
||||
}
|
||||
zoneName = Char.ToLowerInvariant(zoneName[0]) + zoneName.Substring(1);
|
||||
|
||||
try
|
||||
{
|
||||
className = className.Substring(0, 20 - zoneName.Length);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{ }
|
||||
|
||||
//Convert actor number to base 63
|
||||
string classNumber = Utils.ToStringBase63(actorNumber);
|
||||
|
||||
//Get stuff after @
|
||||
uint zoneId = zone.actorId;
|
||||
uint privLevel = 0;
|
||||
if (zone is PrivateArea)
|
||||
privLevel = ((PrivateArea)zone).GetPrivateAreaType();
|
||||
|
||||
actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel);
|
||||
}
|
||||
|
||||
public bool SetWorkValue(Player player, string name, string uiFunc, object value)
|
||||
{
|
||||
string[] split = name.Split('.');
|
||||
int arrayIndex = 0;
|
||||
|
||||
if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork")))
|
||||
return false;
|
||||
|
||||
Object parentObj = null;
|
||||
Object curObj = this;
|
||||
for (int i = 0; i < split.Length; i++)
|
||||
{
|
||||
//For arrays
|
||||
if (split[i].Contains("["))
|
||||
{
|
||||
if (split[i].LastIndexOf(']') - split[i].IndexOf('[') <= 0)
|
||||
return false;
|
||||
|
||||
arrayIndex = Convert.ToInt32(split[i].Substring(split[i].IndexOf('[') + 1, split[i].LastIndexOf(']') - split[i].LastIndexOf('[') - 1));
|
||||
split[i] = split[i].Substring(0, split[i].IndexOf('['));
|
||||
}
|
||||
|
||||
FieldInfo field = curObj.GetType().GetField(split[i]);
|
||||
if (field == null)
|
||||
return false;
|
||||
|
||||
if (i == split.Length - 1)
|
||||
parentObj = curObj;
|
||||
curObj = field.GetValue(curObj);
|
||||
if (curObj == null)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (curObj == null)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
//Array, we actually care whats inside
|
||||
if (curObj.GetType().IsArray)
|
||||
{
|
||||
if (((Array)curObj).Length <= arrayIndex)
|
||||
return false;
|
||||
|
||||
if (value.GetType() == ((Array)curObj).GetType().GetElementType() || TypeDescriptor.GetConverter(value.GetType()).CanConvertTo(((Array)curObj).GetType().GetElementType()))
|
||||
{
|
||||
if (value.GetType() == ((Array)curObj).GetType().GetElementType())
|
||||
((Array)curObj).SetValue(value, arrayIndex);
|
||||
else
|
||||
((Array)curObj).SetValue(TypeDescriptor.GetConverter(value.GetType()).ConvertTo(value, curObj.GetType().GetElementType()), arrayIndex);
|
||||
|
||||
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket(uiFunc);
|
||||
changeProperty.AddProperty(this, name);
|
||||
changeProperty.AddTarget();
|
||||
SubPacket subpacket = changeProperty.BuildPacket(player.actorId);
|
||||
player.playerSession.QueuePacket(subpacket);
|
||||
subpacket.DebugPrintSubPacket();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (value.GetType() == curObj.GetType() || TypeDescriptor.GetConverter(value.GetType()).CanConvertTo(curObj.GetType()))
|
||||
{
|
||||
if (value.GetType() == curObj.GetType())
|
||||
parentObj.GetType().GetField(split[split.Length - 1]).SetValue(parentObj, value);
|
||||
else
|
||||
parentObj.GetType().GetField(split[split.Length - 1]).SetValue(parentObj, TypeDescriptor.GetConverter(value.GetType()).ConvertTo(value, curObj.GetType()));
|
||||
|
||||
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket(uiFunc);
|
||||
changeProperty.AddProperty(this, name);
|
||||
changeProperty.AddTarget();
|
||||
SubPacket subpacket = changeProperty.BuildPacket(player.actorId);
|
||||
player.playerSession.QueuePacket(subpacket);
|
||||
subpacket.DebugPrintSubPacket();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#region positioning
|
||||
public List<float> GetPos()
|
||||
{
|
||||
List<float> pos = new List<float>();
|
||||
|
||||
pos.Add(positionX);
|
||||
pos.Add(positionY);
|
||||
pos.Add(positionZ);
|
||||
pos.Add(rotation);
|
||||
pos.Add(zoneId);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
public Vector3 GetPosAsVector3()
|
||||
{
|
||||
return new Vector3(positionX, positionY, positionZ);
|
||||
}
|
||||
|
||||
public void SetPos(float x, float y, float z, float rot = 0, uint zoneId = 0)
|
||||
{
|
||||
oldPositionX = positionX;
|
||||
oldPositionY = positionY;
|
||||
oldPositionZ = positionZ;
|
||||
oldRotation = rotation;
|
||||
|
||||
positionX = x;
|
||||
positionY = y;
|
||||
positionZ = z;
|
||||
rotation = rot;
|
||||
|
||||
// todo: handle zone?
|
||||
zone.BroadcastPacketAroundActor(this, MoveActorToPositionPacket.BuildPacket(actorId, x, y, z, rot, moveState));
|
||||
}
|
||||
|
||||
public Area GetZone()
|
||||
{
|
||||
return zone;
|
||||
}
|
||||
|
||||
public uint GetZoneID()
|
||||
{
|
||||
return zoneId;
|
||||
}
|
||||
|
||||
public void LookAt(Actor actor)
|
||||
{
|
||||
if (actor != null)
|
||||
{
|
||||
LookAt(actor.positionX, actor.positionZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Log.Error("[{0}][{1}] Actor.LookAt() unable to find actor!", actorId, actorName);
|
||||
}
|
||||
}
|
||||
|
||||
public void LookAt(Vector3 pos)
|
||||
{
|
||||
if (pos != null)
|
||||
{
|
||||
LookAt(pos.X, pos.Z);
|
||||
}
|
||||
}
|
||||
|
||||
public void LookAt(float x, float z)
|
||||
{
|
||||
//Don't rotate if the lookat position is same as our current position
|
||||
if (positionX != x || positionZ != z)
|
||||
{
|
||||
var rot1 = this.rotation;
|
||||
|
||||
var dX = this.positionX - x;
|
||||
var dY = this.positionZ - z;
|
||||
var rot2 = Math.Atan2(dY, dX);
|
||||
var dRot = Math.PI - rot2 + Math.PI / 2;
|
||||
|
||||
// pending move, dont need to unset it
|
||||
this.updateFlags |= ActorUpdateFlags.Position;
|
||||
rotation = (float)dRot;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: is this legit?
|
||||
public bool IsFacing(float x, float z, float angle = 90.0f)
|
||||
{
|
||||
angle = (float)(Math.PI * angle / 180);
|
||||
var a = Vector3.GetAngle(positionX, positionZ, x, z);
|
||||
return new Vector3(x, 0, z).IsWithinCone(GetPosAsVector3(), rotation, angle);
|
||||
}
|
||||
|
||||
public bool IsFacing(Actor target, float angle = 40.0f)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
Program.Log.Error("[{0}][{1}] IsFacing no target!", actorId, actorName);
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsFacing(target.positionX, target.positionZ, angle);
|
||||
}
|
||||
|
||||
public void QueuePositionUpdate(Vector3 pos)
|
||||
{
|
||||
if (positionUpdates == null)
|
||||
positionUpdates = new List<Vector3>();
|
||||
|
||||
positionUpdates.Add(pos);
|
||||
this.updateFlags |= ActorUpdateFlags.Position;
|
||||
}
|
||||
|
||||
public void QueuePositionUpdate(float x, float y, float z)
|
||||
{
|
||||
QueuePositionUpdate(new Vector3(x, y, z));
|
||||
}
|
||||
|
||||
public void ClearPositionUpdates()
|
||||
{
|
||||
positionUpdates.Clear();
|
||||
}
|
||||
|
||||
public Vector3 FindRandomPoint(float x, float y, float z, float minRadius, float maxRadius)
|
||||
{
|
||||
var angle = Program.Random.NextDouble() * Math.PI * 2;
|
||||
var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius;
|
||||
|
||||
return new Vector3(x + (float)(radius * Math.Cos(angle)), y, z + (float)(radius * Math.Sin(angle)));
|
||||
}
|
||||
|
||||
public Vector3 FindRandomPointAroundTarget(Actor target, float minRadius, float maxRadius)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
Program.Log.Error(String.Format("[{0} {1}] FindRandomPointAroundTarget: no target found!", this.actorId, this.customDisplayName));
|
||||
return GetPosAsVector3();
|
||||
}
|
||||
return FindRandomPoint(target.positionX, target.positionY, target.positionZ, minRadius, maxRadius);
|
||||
}
|
||||
|
||||
public Vector3 FindRandomPointAroundActor(float minRadius, float maxRadius)
|
||||
{
|
||||
return FindRandomPoint(positionX, positionY, positionZ, minRadius, maxRadius);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (className != null)
|
||||
{
|
||||
return string.Format("{0} [0x{1:X}]", className, actorId);
|
||||
}
|
||||
else
|
||||
{
|
||||
return string.Format("Unknown [0x{0:X}]", actorId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,88 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors
|
||||
{
|
||||
class EventList
|
||||
{
|
||||
public List<TalkEventCondition> talkEventConditions;
|
||||
public List<NoticeEventCondition> noticeEventConditions;
|
||||
public List<EmoteEventCondition> emoteEventConditions;
|
||||
public List<PushCircleEventCondition> pushWithCircleEventConditions;
|
||||
public List<PushFanEventCondition> pushWithFanEventConditions;
|
||||
public List<PushBoxEventCondition> pushWithBoxEventConditions;
|
||||
|
||||
public class TalkEventCondition
|
||||
{
|
||||
public byte unknown1;
|
||||
public bool isDisabled = false;
|
||||
public string conditionName;
|
||||
}
|
||||
|
||||
public class NoticeEventCondition
|
||||
{
|
||||
public byte unknown1;
|
||||
public byte unknown2;
|
||||
public string conditionName;
|
||||
|
||||
public NoticeEventCondition(string name, byte unk1, byte unk2)
|
||||
{
|
||||
conditionName = name;
|
||||
unknown1 = unk1;
|
||||
unknown2 = unk2;
|
||||
}
|
||||
}
|
||||
|
||||
public class EmoteEventCondition
|
||||
{
|
||||
public byte unknown1;
|
||||
public byte unknown2;
|
||||
public byte emoteId;
|
||||
public string conditionName;
|
||||
}
|
||||
|
||||
public class PushCircleEventCondition
|
||||
{
|
||||
public string conditionName = "";
|
||||
public float radius = 30.0f;
|
||||
public bool outwards = false;
|
||||
public bool silent = true;
|
||||
}
|
||||
|
||||
public class PushFanEventCondition
|
||||
{
|
||||
public string conditionName;
|
||||
public float radius = 30.0f;
|
||||
public bool outwards = false;
|
||||
public bool silent = true;
|
||||
}
|
||||
|
||||
public class PushBoxEventCondition
|
||||
{
|
||||
public string conditionName = "";
|
||||
public float size = 30.0f;
|
||||
public bool outwards = false;
|
||||
public bool silent = true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Meteor.Map.Actors
|
||||
{
|
||||
class StaticActors
|
||||
{
|
||||
private Dictionary<uint, Actor> mStaticActors = new Dictionary<uint, Actor>();
|
||||
|
||||
public StaticActors(string path)
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(path);
|
||||
|
||||
if (data[0] == 's' && data[1] == 'a' && data[2] == 'n' && data[3] == 'e')
|
||||
data = DecryptStaticActorsFile(data);
|
||||
|
||||
LoadStaticActors(data);
|
||||
}
|
||||
|
||||
private byte[] DecryptStaticActorsFile(byte[] encoded)
|
||||
{
|
||||
byte[] decoded = new byte[encoded.Length - 13];
|
||||
|
||||
MemoryStream sIn = new MemoryStream(encoded);
|
||||
MemoryStream sOut = new MemoryStream(decoded);
|
||||
|
||||
BinaryReader binReader = new BinaryReader(sIn);
|
||||
BinaryWriter binWriter = new BinaryWriter(sOut);
|
||||
|
||||
binReader.BaseStream.Seek(13, SeekOrigin.Begin);
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
byte byteIn = binReader.ReadByte();
|
||||
byte byteOut = (Byte)(byteIn ^ 0x73);
|
||||
binWriter.Write((Byte)byteOut);
|
||||
}
|
||||
catch (EndOfStreamException) { break; }
|
||||
}
|
||||
|
||||
binReader.Close();
|
||||
binWriter.Close();
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
private bool LoadStaticActors(byte[] data)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (MemoryStream s = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(s))
|
||||
{
|
||||
|
||||
while (binReader.BaseStream.Position != binReader.BaseStream.Length)
|
||||
{
|
||||
uint id = Utils.SwapEndian(binReader.ReadUInt32()) | 0xA0F00000;
|
||||
|
||||
List<byte> list = new List<byte>();
|
||||
byte readByte;
|
||||
|
||||
while ((readByte = binReader.ReadByte()) != 0)
|
||||
list.Add(readByte);
|
||||
|
||||
string output = Encoding.UTF8.GetString(list.ToArray());
|
||||
string actorType = output.Split('/')[1];
|
||||
string actorName = output.Substring(1 + output.LastIndexOf("/"));
|
||||
|
||||
if (actorType.Equals("Command"))
|
||||
mStaticActors.Add(id, new Command(id, actorName));
|
||||
else if (actorType.Equals("Quest"))
|
||||
mStaticActors.Add(id, new Quest(id, actorName));
|
||||
//else if (actorType.Equals("Status"))
|
||||
//mStaticActors.Add(id, new Status(id, actorName));
|
||||
else if (actorType.Equals("Judge"))
|
||||
mStaticActors.Add(id, new Judge(id, actorName));
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(FileNotFoundException)
|
||||
{ Program.Log.Error("Could not find staticactors file."); return false; }
|
||||
|
||||
Program.Log.Info("Loaded {0} static actors.", mStaticActors.Count());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool Exists(uint actorId)
|
||||
{
|
||||
return mStaticActors[actorId] != null;
|
||||
}
|
||||
|
||||
public Actor FindStaticActor(string name)
|
||||
{
|
||||
foreach (Actor a in mStaticActors.Values)
|
||||
{
|
||||
if (a.actorName.Equals(name))
|
||||
return a;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Actor GetActor(uint actorId)
|
||||
{
|
||||
if (mStaticActors.ContainsKey(actorId))
|
||||
return mStaticActors[actorId];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,696 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.area;
|
||||
using Meteor.Map.actors.chara.npc;
|
||||
using Meteor.Map.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Meteor.Map.packets.send;
|
||||
using Meteor.Map.actors.director;
|
||||
|
||||
namespace Meteor.Map.Actors
|
||||
{
|
||||
class Area : Actor
|
||||
{
|
||||
public string zoneName;
|
||||
public ushort regionId;
|
||||
public bool isIsolated, canStealth, isInn, canRideChocobo, isInstanceRaid;
|
||||
public ushort weatherNormal, weatherCommon, weatherRare;
|
||||
public ushort bgmDay, bgmNight, bgmBattle;
|
||||
|
||||
protected string classPath;
|
||||
|
||||
public int boundingGridSize = 50;
|
||||
public int minX = -5000, minY = -5000, maxX = 5000, maxY = 5000;
|
||||
protected int numXBlocks, numYBlocks;
|
||||
protected int halfWidth, halfHeight;
|
||||
|
||||
private Dictionary<uint, Director> currentDirectors = new Dictionary<uint, Director>();
|
||||
private Object directorLock = new Object();
|
||||
private uint directorIdCount = 0;
|
||||
|
||||
protected Director mWeatherDirector;
|
||||
|
||||
protected List<SpawnLocation> mSpawnLocations = new List<SpawnLocation>();
|
||||
protected Dictionary<uint, Actor> mActorList = new Dictionary<uint, Actor>();
|
||||
protected List<Actor>[,] mActorBlock;
|
||||
|
||||
LuaScript areaScript;
|
||||
|
||||
public Area(uint id, string zoneName, ushort regionId, string classPath, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid)
|
||||
: base(id)
|
||||
{
|
||||
|
||||
this.zoneName = zoneName;
|
||||
this.regionId = regionId;
|
||||
this.canStealth = canStealth;
|
||||
this.isIsolated = isIsolated;
|
||||
this.isInn = isInn;
|
||||
this.canRideChocobo = canRideChocobo;
|
||||
this.isInstanceRaid = isInstanceRaid;
|
||||
|
||||
this.bgmDay = bgmDay;
|
||||
this.bgmNight = bgmNight;
|
||||
this.bgmBattle = bgmBattle;
|
||||
|
||||
this.displayNameId = 0;
|
||||
this.customDisplayName = "_areaMaster";
|
||||
this.actorName = String.Format("_areaMaster@{0:X5}", id << 8);
|
||||
|
||||
this.classPath = classPath;
|
||||
this.className = classPath.Substring(classPath.LastIndexOf("/") + 1);
|
||||
|
||||
numXBlocks = (maxX - minX) / boundingGridSize;
|
||||
numYBlocks = (maxY - minY) / boundingGridSize;
|
||||
mActorBlock = new List<Actor>[numXBlocks, numYBlocks];
|
||||
halfWidth = numXBlocks / 2;
|
||||
halfHeight = numYBlocks / 2;
|
||||
|
||||
for (int y = 0; y < numYBlocks; y++)
|
||||
{
|
||||
for (int x = 0; x < numXBlocks; x++)
|
||||
{
|
||||
mActorBlock[x, y] = new List<Actor>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override SubPacket CreateScriptBindPacket()
|
||||
{
|
||||
List<LuaParam> lParams;
|
||||
lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, "/Area/Zone/ZoneDefault", -1, (byte)1, true, false, false, false, false, false, false, false);
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, "ZoneDefault", lParams);
|
||||
}
|
||||
|
||||
public override List<SubPacket> GetSpawnPackets()
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
subpackets.Add(CreateAddActorPacket(0));
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpawnPositonPacket(0x1));
|
||||
subpackets.Add(CreateNamePacket());
|
||||
subpackets.Add(CreateStatePacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.Add(CreateScriptBindPacket());
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
// todo: handle instance areas in derived class? (see virtuals)
|
||||
#region Actor Management
|
||||
|
||||
public void AddActorToZone(Actor actor)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
if (actor is Character)
|
||||
((Character)actor).ResetTempVars();
|
||||
|
||||
if (!mActorList.ContainsKey(actor.actorId))
|
||||
mActorList.Add(actor.actorId, actor);
|
||||
|
||||
|
||||
int gridX = (int)actor.positionX / boundingGridSize;
|
||||
int gridY = (int)actor.positionZ / boundingGridSize;
|
||||
|
||||
gridX += halfWidth;
|
||||
gridY += halfHeight;
|
||||
|
||||
//Boundries
|
||||
if (gridX < 0)
|
||||
gridX = 0;
|
||||
if (gridX >= numXBlocks)
|
||||
gridX = numXBlocks - 1;
|
||||
if (gridY < 0)
|
||||
gridY = 0;
|
||||
if (gridY >= numYBlocks)
|
||||
gridY = numYBlocks - 1;
|
||||
|
||||
lock (mActorBlock)
|
||||
mActorBlock[gridX, gridY].Add(actor);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveActorFromZone(Actor actor)
|
||||
{
|
||||
if (actor != null)
|
||||
lock (mActorList)
|
||||
{
|
||||
mActorList.Remove(actor.actorId);
|
||||
|
||||
int gridX = (int)actor.positionX / boundingGridSize;
|
||||
int gridY = (int)actor.positionZ / boundingGridSize;
|
||||
|
||||
gridX += halfWidth;
|
||||
gridY += halfHeight;
|
||||
|
||||
//Boundries
|
||||
if (gridX < 0)
|
||||
gridX = 0;
|
||||
if (gridX >= numXBlocks)
|
||||
gridX = numXBlocks - 1;
|
||||
if (gridY < 0)
|
||||
gridY = 0;
|
||||
if (gridY >= numYBlocks)
|
||||
gridY = numYBlocks - 1;
|
||||
|
||||
lock (mActorBlock)
|
||||
mActorBlock[gridX, gridY].Remove(actor);
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateActorPosition(Actor actor)
|
||||
{
|
||||
int gridX = (int)actor.positionX / boundingGridSize;
|
||||
int gridY = (int)actor.positionZ / boundingGridSize;
|
||||
|
||||
gridX += halfWidth;
|
||||
gridY += halfHeight;
|
||||
|
||||
//Boundries
|
||||
if (gridX < 0)
|
||||
gridX = 0;
|
||||
if (gridX >= numXBlocks)
|
||||
gridX = numXBlocks - 1;
|
||||
if (gridY < 0)
|
||||
gridY = 0;
|
||||
if (gridY >= numYBlocks)
|
||||
gridY = numYBlocks - 1;
|
||||
|
||||
int gridOldX = (int)actor.oldPositionX / boundingGridSize;
|
||||
int gridOldY = (int)actor.oldPositionZ / boundingGridSize;
|
||||
|
||||
gridOldX += halfWidth;
|
||||
gridOldY += halfHeight;
|
||||
|
||||
//Boundries
|
||||
if (gridOldX < 0)
|
||||
gridOldX = 0;
|
||||
if (gridOldX >= numXBlocks)
|
||||
gridOldX = numXBlocks - 1;
|
||||
if (gridOldY < 0)
|
||||
gridOldY = 0;
|
||||
if (gridOldY >= numYBlocks)
|
||||
gridOldY = numYBlocks - 1;
|
||||
|
||||
//Still in same block
|
||||
if (gridX == gridOldX && gridY == gridOldY)
|
||||
return;
|
||||
|
||||
lock (mActorBlock)
|
||||
{
|
||||
mActorBlock[gridOldX, gridOldY].Remove(actor);
|
||||
mActorBlock[gridX, gridY].Add(actor);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual List<T> GetActorsAroundPoint<T>(float x, float y, int checkDistance) where T : Actor
|
||||
{
|
||||
checkDistance /= boundingGridSize;
|
||||
|
||||
int gridX = (int)x / boundingGridSize;
|
||||
int gridY = (int)y / boundingGridSize;
|
||||
|
||||
gridX += halfWidth;
|
||||
gridY += halfHeight;
|
||||
|
||||
//Boundries
|
||||
if (gridX < 0)
|
||||
gridX = 0;
|
||||
if (gridX >= numXBlocks)
|
||||
gridX = numXBlocks - 1;
|
||||
if (gridY < 0)
|
||||
gridY = 0;
|
||||
if (gridY >= numYBlocks)
|
||||
gridY = numYBlocks - 1;
|
||||
|
||||
List<T> result = new List<T>();
|
||||
|
||||
lock (mActorBlock)
|
||||
{
|
||||
for (int gx = gridX - checkDistance; gx <= gridX + checkDistance; gx++)
|
||||
{
|
||||
for (int gy = gridY - checkDistance; gy <= gridY + checkDistance; gy++)
|
||||
{
|
||||
result.AddRange(mActorBlock[gx, gy].OfType<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Remove players if isolation zone
|
||||
if (isIsolated)
|
||||
{
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
if (result[i] is Player)
|
||||
result.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public virtual List<Actor> GetActorsAroundPoint(float x, float y, int checkDistance)
|
||||
{
|
||||
return GetActorsAroundPoint<Actor>(x, y, checkDistance);
|
||||
}
|
||||
|
||||
public virtual List<Actor> GetActorsAroundActor(Actor actor, int checkDistance)
|
||||
{
|
||||
return GetActorsAroundActor<Actor>(actor, checkDistance);
|
||||
}
|
||||
|
||||
public virtual List<T> GetActorsAroundActor<T>(Actor actor, int checkDistance) where T : Actor
|
||||
{
|
||||
checkDistance /= boundingGridSize;
|
||||
|
||||
int gridX = (int)actor.positionX / boundingGridSize;
|
||||
int gridY = (int)actor.positionZ / boundingGridSize;
|
||||
|
||||
gridX += halfWidth;
|
||||
gridY += halfHeight;
|
||||
|
||||
//Boundries
|
||||
if (gridX < 0)
|
||||
gridX = 0;
|
||||
if (gridX >= numXBlocks)
|
||||
gridX = numXBlocks - 1;
|
||||
if (gridY < 0)
|
||||
gridY = 0;
|
||||
if (gridY >= numYBlocks)
|
||||
gridY = numYBlocks - 1;
|
||||
|
||||
var result = new List<T>();
|
||||
|
||||
lock (mActorBlock)
|
||||
{
|
||||
for (int gy = ((gridY - checkDistance) < 0 ? 0 : (gridY - checkDistance)); gy <= ((gridY + checkDistance) >= numYBlocks ? numYBlocks - 1 : (gridY + checkDistance)); gy++)
|
||||
{
|
||||
for (int gx = ((gridX - checkDistance) < 0 ? 0 : (gridX - checkDistance)); gx <= ((gridX + checkDistance) >= numXBlocks ? numXBlocks - 1 : (gridX + checkDistance)); gx++)
|
||||
{
|
||||
result.AddRange(mActorBlock[gx, gy].OfType<T>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Remove players if isolation zone
|
||||
if (isIsolated)
|
||||
{
|
||||
for (int i = 0; i < result.Count; i++)
|
||||
{
|
||||
if (result[i] is Player)
|
||||
result.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public Actor FindActorInArea(uint id)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
if (!mActorList.ContainsKey(id))
|
||||
return null;
|
||||
return mActorList[id];
|
||||
}
|
||||
}
|
||||
|
||||
public T FindActorInArea<T>(uint id) where T : Actor
|
||||
{
|
||||
return FindActorInArea(id) as T;
|
||||
}
|
||||
|
||||
public Actor FindActorInZoneByUniqueID(string uniqueId)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
foreach (Actor a in mActorList.Values)
|
||||
{
|
||||
if (a is Npc)
|
||||
{
|
||||
if (((Npc)a).GetUniqueId().ToLower().Equals(uniqueId))
|
||||
return a;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Player FindPCInZone(string name)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
foreach (Player player in mActorList.Values.OfType<Player>())
|
||||
{
|
||||
if (player.customDisplayName.ToLower().Equals(name.ToLower()))
|
||||
return player;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Player FindPCInZone(uint id)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
if (!mActorList.ContainsKey(id))
|
||||
return null;
|
||||
return (Player)mActorList[id];
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
//Clear All
|
||||
mActorList.Clear();
|
||||
lock (mActorBlock)
|
||||
{
|
||||
for (int y = 0; y < numYBlocks; y++)
|
||||
{
|
||||
for (int x = 0; x < numXBlocks; x++)
|
||||
{
|
||||
mActorBlock[x, y].Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// todo: for zones override this to search contentareas (assuming flag is passed)
|
||||
public virtual List<T> GetAllActors<T>() where T : Actor
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
List<T> actorList = new List<T>(mActorList.Count);
|
||||
actorList.AddRange(mActorList.Values.OfType<T>());
|
||||
return actorList;
|
||||
}
|
||||
}
|
||||
|
||||
public int GetActorCount()
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
return mActorList.Count;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual List<Actor> GetAllActors()
|
||||
{
|
||||
return GetAllActors<Actor>();
|
||||
}
|
||||
|
||||
public virtual List<Player> GetPlayers()
|
||||
{
|
||||
return GetAllActors<Player>();
|
||||
}
|
||||
|
||||
public virtual List<BattleNpc> GetMonsters()
|
||||
{
|
||||
return GetAllActors<BattleNpc>();
|
||||
}
|
||||
|
||||
public virtual List<Ally> GetAllies()
|
||||
{
|
||||
return GetAllActors<Ally>();
|
||||
}
|
||||
|
||||
public void BroadcastPacketsAroundActor(Actor actor, List<SubPacket> packets)
|
||||
{
|
||||
foreach (SubPacket packet in packets)
|
||||
BroadcastPacketAroundActor(actor, packet);
|
||||
}
|
||||
|
||||
public void BroadcastPacketAroundActor(Actor actor, SubPacket packet)
|
||||
{
|
||||
if (isIsolated)
|
||||
return;
|
||||
|
||||
List<Actor> aroundActor = GetActorsAroundActor(actor, 50);
|
||||
foreach (Actor a in aroundActor)
|
||||
{
|
||||
if (a is Player)
|
||||
{
|
||||
if (isIsolated)
|
||||
continue;
|
||||
|
||||
SubPacket clonedPacket = new SubPacket(packet, a.actorId);
|
||||
Player p = (Player)a;
|
||||
p.QueuePacket(clonedPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SpawnActor(SpawnLocation location)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
ActorClass actorClass = Server.GetWorldManager().GetActorClass(location.classId);
|
||||
|
||||
if (actorClass == null)
|
||||
return;
|
||||
|
||||
uint zoneId;
|
||||
|
||||
if (this is PrivateArea)
|
||||
zoneId = ((PrivateArea)this).GetParentZone().actorId;
|
||||
else
|
||||
zoneId = actorId;
|
||||
|
||||
Npc npc = new Npc(mActorList.Count + 1, actorClass, location.uniqueId, this, location.x, location.y, location.z, location.rot, location.state, location.animId, null);
|
||||
|
||||
|
||||
npc.LoadEventConditions(actorClass.eventConditions);
|
||||
|
||||
AddActorToZone(npc);
|
||||
}
|
||||
}
|
||||
|
||||
public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, float rot = 0, ushort state = 0, uint animId = 0, bool isMob = false)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
ActorClass actorClass = Server.GetWorldManager().GetActorClass(classId);
|
||||
|
||||
if (actorClass == null)
|
||||
return null;
|
||||
|
||||
uint zoneId;
|
||||
if (this is PrivateArea)
|
||||
zoneId = ((PrivateArea)this).GetParentZone().actorId;
|
||||
else
|
||||
zoneId = actorId;
|
||||
|
||||
Npc npc;
|
||||
if (isMob)
|
||||
npc = new BattleNpc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null);
|
||||
else
|
||||
npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null);
|
||||
|
||||
npc.LoadEventConditions(actorClass.eventConditions);
|
||||
npc.SetMaxHP(100);
|
||||
npc.SetHP(100);
|
||||
npc.ResetMoveSpeeds();
|
||||
|
||||
AddActorToZone(npc);
|
||||
|
||||
return npc;
|
||||
}
|
||||
}
|
||||
|
||||
public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, uint regionId, uint layoutId)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
ActorClass actorClass = Server.GetWorldManager().GetActorClass(classId);
|
||||
|
||||
if (actorClass == null)
|
||||
return null;
|
||||
|
||||
uint zoneId;
|
||||
|
||||
if (this is PrivateArea)
|
||||
zoneId = ((PrivateArea)this).GetParentZone().actorId;
|
||||
else
|
||||
zoneId = actorId;
|
||||
|
||||
Npc npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, 0, regionId, layoutId);
|
||||
|
||||
npc.LoadEventConditions(actorClass.eventConditions);
|
||||
|
||||
AddActorToZone(npc);
|
||||
|
||||
return npc;
|
||||
}
|
||||
}
|
||||
|
||||
public BattleNpc GetBattleNpcById(uint id)
|
||||
{
|
||||
foreach (var bnpc in GetAllActors<BattleNpc>())
|
||||
{
|
||||
if (bnpc.GetBattleNpcId() == id)
|
||||
return bnpc;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void DespawnActor(string uniqueId)
|
||||
{
|
||||
RemoveActorFromZone(FindActorInZoneByUniqueID(uniqueId));
|
||||
}
|
||||
|
||||
public void DespawnActor(Actor actor)
|
||||
{
|
||||
RemoveActorFromZone(actor);
|
||||
}
|
||||
|
||||
public Director GetWeatherDirector()
|
||||
{
|
||||
return mWeatherDirector;
|
||||
}
|
||||
|
||||
public void ChangeWeather(ushort weather, ushort transitionTime, Player player, bool zoneWide = false)
|
||||
{
|
||||
weatherNormal = weather;
|
||||
|
||||
if (player != null && !zoneWide)
|
||||
{
|
||||
player.QueuePacket(SetWeatherPacket.BuildPacket(player.actorId, weather, transitionTime));
|
||||
}
|
||||
if (zoneWide)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
foreach (var actor in mActorList)
|
||||
{
|
||||
if (actor.Value is Player)
|
||||
{
|
||||
player = ((Player)actor.Value);
|
||||
player.QueuePacket(SetWeatherPacket.BuildPacket(player.actorId, weather, transitionTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Director CreateDirector(string path, bool hasContentGroup, params object[] args)
|
||||
{
|
||||
lock (directorLock)
|
||||
{
|
||||
Director director = new Director(directorIdCount, this, path, hasContentGroup, args);
|
||||
currentDirectors.Add(director.actorId, director);
|
||||
directorIdCount++;
|
||||
return director;
|
||||
}
|
||||
}
|
||||
|
||||
public Director CreateGuildleveDirector(uint glid, byte difficulty, Player owner, params object[] args)
|
||||
{
|
||||
String directorScriptPath = "";
|
||||
|
||||
uint type = Server.GetGuildleveGamedata(glid).plateId;
|
||||
|
||||
if (glid == 10801 || glid == 12401 || glid == 11601)
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleTutorial";
|
||||
else
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case 20021:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleSweepNormal";
|
||||
break;
|
||||
case 20022:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleChaseNormal";
|
||||
break;
|
||||
case 20023:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleOrbNormal";
|
||||
break;
|
||||
case 20024:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleHuntNormal";
|
||||
break;
|
||||
case 20025:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleGatherNormal";
|
||||
break;
|
||||
case 20026:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleRoundNormal";
|
||||
break;
|
||||
case 20027:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleSurviveNormal";
|
||||
break;
|
||||
case 20028:
|
||||
directorScriptPath = "Guildleve/PrivateGLBattleDetectNormal";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lock (directorLock)
|
||||
{
|
||||
GuildleveDirector director = new GuildleveDirector(directorIdCount, this, directorScriptPath, glid, difficulty, owner, args);
|
||||
currentDirectors.Add(director.actorId, director);
|
||||
directorIdCount++;
|
||||
return director;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteDirector(uint id)
|
||||
{
|
||||
lock (directorLock)
|
||||
{
|
||||
if (currentDirectors.ContainsKey(id))
|
||||
{
|
||||
if (!currentDirectors[id].IsDeleted())
|
||||
currentDirectors[id].EndDirector();
|
||||
currentDirectors.Remove(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Director GetDirectorById(uint id)
|
||||
{
|
||||
if (currentDirectors.ContainsKey(id))
|
||||
return currentDirectors[id];
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Update(DateTime tick)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
foreach (Actor a in mActorList.Values.ToList())
|
||||
a.Update(tick);
|
||||
|
||||
if ((tick - lastUpdateScript).TotalMilliseconds > 1500)
|
||||
{
|
||||
//LuaEngine.GetInstance().CallLuaFunctionForReturn(LuaEngine.GetScriptPath(this), "onUpdate", true, this, tick);
|
||||
lastUpdateScript = tick;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.area
|
||||
{
|
||||
class PrivateArea : Area
|
||||
{
|
||||
private Zone parentZone;
|
||||
private string privateAreaName;
|
||||
private uint privateAreaType;
|
||||
|
||||
public PrivateArea(Zone parent, uint id, string classPath, string privateAreaName, uint privateAreaType, ushort bgmDay, ushort bgmNight, ushort bgmBattle)
|
||||
: base(id, parent.zoneName, parent.regionId, classPath, bgmDay, bgmNight, bgmBattle, parent.isIsolated, parent.isInn, parent.canRideChocobo, parent.canStealth, true)
|
||||
{
|
||||
this.parentZone = parent;
|
||||
this.zoneName = parent.zoneName;
|
||||
this.privateAreaName = privateAreaName;
|
||||
this.privateAreaType = privateAreaType;
|
||||
}
|
||||
|
||||
public string GetPrivateAreaName()
|
||||
{
|
||||
return privateAreaName;
|
||||
}
|
||||
|
||||
public uint GetPrivateAreaType()
|
||||
{
|
||||
return privateAreaType;
|
||||
}
|
||||
|
||||
public Zone GetParentZone()
|
||||
{
|
||||
return parentZone;
|
||||
}
|
||||
|
||||
public override SubPacket CreateScriptBindPacket()
|
||||
{
|
||||
List<LuaParam> lParams;
|
||||
|
||||
string path = className;
|
||||
|
||||
string realClassName = className.Substring(className.LastIndexOf("/") + 1);
|
||||
|
||||
lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, privateAreaName, privateAreaType, canRideChocobo ? (byte)1 : (byte)0, canStealth, isInn, false, false, false, false, false, false);
|
||||
ActorInstantiatePacket.BuildPacket(actorId, actorName, realClassName, lParams).DebugPrintSubPacket();
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, realClassName, lParams);
|
||||
}
|
||||
|
||||
|
||||
public void AddSpawnLocation(SpawnLocation spawn)
|
||||
{
|
||||
mSpawnLocations.Add(spawn);
|
||||
}
|
||||
|
||||
public void SpawnAllActors()
|
||||
{
|
||||
foreach (SpawnLocation spawn in mSpawnLocations)
|
||||
SpawnActor(spawn);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.director;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.lua;
|
||||
using System;
|
||||
|
||||
namespace Meteor.Map.actors.area
|
||||
{
|
||||
|
||||
class PrivateAreaContent : PrivateArea
|
||||
{
|
||||
private Director currentDirector;
|
||||
private bool isContentFinished = false;
|
||||
|
||||
public static PrivateAreaContent CreateContentArea(String scriptPath)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public PrivateAreaContent(Zone parent, string classPath, string privateAreaName, uint privateAreaType, Director director, Player contentStarter) //TODO: Make it a list
|
||||
: base(parent, parent.actorId, classPath, privateAreaName, privateAreaType, 0, 0, 0)
|
||||
{
|
||||
currentDirector = director;
|
||||
LuaEngine.GetInstance().CallLuaFunction(contentStarter, this, "onCreate", false, currentDirector);
|
||||
}
|
||||
|
||||
public Director GetContentDirector()
|
||||
{
|
||||
return currentDirector;
|
||||
}
|
||||
|
||||
public void ContentFinished()
|
||||
{
|
||||
isContentFinished = true;
|
||||
}
|
||||
|
||||
public void CheckDestroy()
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
if (isContentFinished)
|
||||
{
|
||||
bool noPlayersLeft = true;
|
||||
foreach (Actor a in mActorList.Values)
|
||||
{
|
||||
if (a is Player)
|
||||
noPlayersLeft = false;
|
||||
}
|
||||
if (noPlayersLeft)
|
||||
GetParentZone().DeleteContentArea(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.area
|
||||
{
|
||||
class SpawnLocation
|
||||
{
|
||||
public uint classId;
|
||||
public string uniqueId;
|
||||
public uint zoneId;
|
||||
public string privAreaName;
|
||||
public uint privAreaLevel;
|
||||
public float x;
|
||||
public float y;
|
||||
public float z;
|
||||
public float rot;
|
||||
public ushort state;
|
||||
public uint animId;
|
||||
|
||||
public SpawnLocation(uint classId, string uniqueId, uint zoneId, string privAreaName, uint privAreaLevel, float x, float y, float z, float rot, ushort state, uint animId)
|
||||
{
|
||||
this.classId = classId;
|
||||
this.uniqueId = uniqueId;
|
||||
this.zoneId = zoneId;
|
||||
this.privAreaName = privAreaName;
|
||||
this.privAreaLevel = privAreaLevel;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.rot = rot;
|
||||
this.state = state;
|
||||
this.animId = animId;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,220 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Meteor.Map.actors.director;
|
||||
|
||||
namespace Meteor.Map.actors.area
|
||||
{
|
||||
class Zone : Area
|
||||
{
|
||||
Dictionary<string, Dictionary<uint, PrivateArea>> privateAreas = new Dictionary<string, Dictionary<uint, PrivateArea>>();
|
||||
Dictionary<string, List<PrivateAreaContent>> contentAreas = new Dictionary<string, List<PrivateAreaContent>>();
|
||||
Object contentAreasLock = new Object();
|
||||
|
||||
public SharpNav.TiledNavMesh tiledNavMesh;
|
||||
public SharpNav.NavMeshQuery navMeshQuery;
|
||||
|
||||
public Int64 pathCalls;
|
||||
public Int64 prevPathCalls = 0;
|
||||
public Int64 pathCallTime;
|
||||
|
||||
public Zone(uint id, string zoneName, ushort regionId, string classPath, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid, bool loadNavMesh = false)
|
||||
: base(id, zoneName, regionId, classPath, bgmDay, bgmNight, bgmBattle, isIsolated, isInn, canRideChocobo, canStealth, isInstanceRaid)
|
||||
{
|
||||
if (loadNavMesh)
|
||||
{
|
||||
try
|
||||
{
|
||||
tiledNavMesh = utils.NavmeshUtils.LoadNavmesh(tiledNavMesh, zoneName + ".snb");
|
||||
navMeshQuery = new SharpNav.NavMeshQuery(tiledNavMesh, 100);
|
||||
|
||||
if (tiledNavMesh != null && tiledNavMesh.Tiles[0].PolyCount > 0)
|
||||
Program.Log.Info($"Loaded navmesh for {zoneName}");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddPrivateArea(PrivateArea pa)
|
||||
{
|
||||
if (privateAreas.ContainsKey(pa.GetPrivateAreaName()))
|
||||
privateAreas[pa.GetPrivateAreaName()][pa.GetPrivateAreaType()] = pa;
|
||||
else
|
||||
{
|
||||
privateAreas[pa.GetPrivateAreaName()] = new Dictionary<uint, PrivateArea>();
|
||||
privateAreas[pa.GetPrivateAreaName()][pa.GetPrivateAreaType()] = pa;
|
||||
}
|
||||
}
|
||||
|
||||
public PrivateArea GetPrivateArea(string type, uint number)
|
||||
{
|
||||
if (privateAreas.ContainsKey(type))
|
||||
{
|
||||
Dictionary<uint, PrivateArea> instances = privateAreas[type];
|
||||
if (instances.ContainsKey(number))
|
||||
return instances[number];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public override SubPacket CreateScriptBindPacket()
|
||||
{
|
||||
bool isEntranceDesion = false;
|
||||
|
||||
List<LuaParam> lParams;
|
||||
lParams = LuaUtils.CreateLuaParamList(classPath, false, true, zoneName, "", -1, canRideChocobo ? (byte)1 : (byte)0, canStealth, isInn, false, false, false, true, isInstanceRaid, isEntranceDesion);
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
|
||||
}
|
||||
|
||||
public void AddSpawnLocation(SpawnLocation spawn)
|
||||
{
|
||||
//Is it in a private area?
|
||||
if (!spawn.privAreaName.Equals(""))
|
||||
{
|
||||
if (privateAreas.ContainsKey(spawn.privAreaName))
|
||||
{
|
||||
Dictionary<uint, PrivateArea> levels = privateAreas[spawn.privAreaName];
|
||||
if (levels.ContainsKey(spawn.privAreaLevel))
|
||||
levels[spawn.privAreaLevel].AddSpawnLocation(spawn);
|
||||
else
|
||||
Program.Log.Error("Tried to add a spawn location to non-existing private area level \"{0}\" in area {1} in zone {2}", spawn.privAreaName, spawn.privAreaLevel, zoneName);
|
||||
}
|
||||
else
|
||||
Program.Log.Error("Tried to add a spawn location to non-existing private area \"{0}\" in zone {1}", spawn.privAreaName, zoneName);
|
||||
}
|
||||
else
|
||||
mSpawnLocations.Add(spawn);
|
||||
}
|
||||
|
||||
public void SpawnAllActors(bool doPrivAreas)
|
||||
{
|
||||
foreach (SpawnLocation spawn in mSpawnLocations)
|
||||
SpawnActor(spawn);
|
||||
|
||||
if (doPrivAreas)
|
||||
{
|
||||
foreach (Dictionary<uint, PrivateArea> areas in privateAreas.Values)
|
||||
{
|
||||
foreach (PrivateArea pa in areas.Values)
|
||||
pa.SpawnAllActors();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Actor FindActorInZone(uint id)
|
||||
{
|
||||
lock (mActorList)
|
||||
{
|
||||
if (!mActorList.ContainsKey(id))
|
||||
{
|
||||
foreach (Dictionary<uint, PrivateArea> paList in privateAreas.Values)
|
||||
{
|
||||
foreach (PrivateArea pa in paList.Values)
|
||||
{
|
||||
Actor actor = pa.FindActorInArea(id);
|
||||
if (actor != null)
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (List<PrivateAreaContent> paList in contentAreas.Values)
|
||||
{
|
||||
foreach (PrivateArea pa in paList)
|
||||
{
|
||||
Actor actor = pa.FindActorInArea(id);
|
||||
if (actor != null)
|
||||
return actor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
else
|
||||
return mActorList[id];
|
||||
}
|
||||
}
|
||||
|
||||
public PrivateAreaContent CreateContentArea(Player starterPlayer, string areaClassPath, string contentScript, string areaName, string directorName, params object[] args)
|
||||
{
|
||||
lock (contentAreasLock)
|
||||
{
|
||||
Director director = CreateDirector(directorName, true, args);
|
||||
|
||||
if (director == null)
|
||||
return null;
|
||||
|
||||
if (!contentAreas.ContainsKey(areaName))
|
||||
contentAreas.Add(areaName, new List<PrivateAreaContent>());
|
||||
PrivateAreaContent contentArea = new PrivateAreaContent(this, classPath, areaName, 1, director, starterPlayer);
|
||||
contentAreas[areaName].Add(contentArea);
|
||||
|
||||
return contentArea;
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteContentArea(PrivateAreaContent area)
|
||||
{
|
||||
if (contentAreas.ContainsKey(area.GetPrivateAreaName()))
|
||||
{
|
||||
contentAreas[area.GetPrivateAreaName()].Remove(area);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(DateTime tick)
|
||||
{
|
||||
base.Update(tick);
|
||||
|
||||
foreach (var a in privateAreas.Values)
|
||||
foreach(var b in a.Values)
|
||||
b.Update(tick);
|
||||
|
||||
foreach (var a in contentAreas.Values)
|
||||
foreach (var b in a)
|
||||
b.Update(tick);
|
||||
|
||||
// todo: again, this is retarded but debug stuff
|
||||
var diffTime = tick - lastUpdate;
|
||||
|
||||
if (diffTime.TotalSeconds >= 10)
|
||||
{
|
||||
if (this.pathCalls > 0)
|
||||
{
|
||||
Program.Log.Debug("Number of pathfinding calls {0} average time {1}ms. {2} this tick", pathCalls, (float)(pathCallTime / pathCalls), pathCalls - prevPathCalls);
|
||||
prevPathCalls = pathCalls;
|
||||
}
|
||||
lastUpdate = tick;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 AetheryteWork
|
||||
{
|
||||
public int iconGil;
|
||||
public short guildleveId;
|
||||
public short clearTime;
|
||||
public int missionBonus;
|
||||
public int difficultyBonus;
|
||||
|
||||
public byte factionNumber;
|
||||
public int factionBonus;
|
||||
public byte factionCredit;
|
||||
|
||||
public int glRewardItem;
|
||||
public int glRewardNumber;
|
||||
public int glRewardSubItem;
|
||||
public int glRewardSubNumber;
|
||||
|
||||
public byte difficulty;
|
||||
}
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 BattleSave
|
||||
{
|
||||
public float potencial = 6.6f;
|
||||
public short[] skillLevel = new short[52];
|
||||
public short[] skillLevelCap = new short[52];
|
||||
public int[] skillPoint = new int[52];
|
||||
|
||||
public short physicalLevel;
|
||||
public int physicalExp;
|
||||
|
||||
public bool[] negotiationFlag= new bool[2];
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 BattleTemp
|
||||
{
|
||||
//Are these right?
|
||||
public const uint NAMEPLATE_SHOWN = 0;
|
||||
public const uint TARGETABLE = 1;
|
||||
public const uint NAMEPLATE_SHOWN2 = 2;
|
||||
//public const uint NAMEPLATE_SHOWN2 = 3;
|
||||
|
||||
public const uint STAT_STRENGTH = 3;
|
||||
public const uint STAT_VITALITY = 4;
|
||||
public const uint STAT_DEXTERITY = 5;
|
||||
public const uint STAT_INTELLIGENCE = 6;
|
||||
public const uint STAT_MIND = 7;
|
||||
public const uint STAT_PIETY = 8;
|
||||
|
||||
public const uint STAT_RESISTANCE_FIRE = 9;
|
||||
public const uint STAT_RESISTANCE_ICE = 10;
|
||||
public const uint STAT_RESISTANCE_WIND = 11;
|
||||
public const uint STAT_RESISTANCE_LIGHTNING = 12;
|
||||
public const uint STAT_RESISTANCE_EARTH = 13;
|
||||
public const uint STAT_RESISTANCE_WATER = 14;
|
||||
|
||||
public const uint STAT_ATTACK = 17;
|
||||
public const uint STAT_ACCURACY = 15;
|
||||
public const uint STAT_NORMALDEFENSE = 18;
|
||||
public const uint STAT_EVASION = 16;
|
||||
public const uint STAT_ATTACK_MAGIC = 23;
|
||||
public const uint STAT_HEAL_MAGIC = 24;
|
||||
public const uint STAT_ENCHANCEMENT_MAGIC_POTENCY = 25;
|
||||
public const uint STAT_ENFEEBLING_MAGIC_POTENCY = 26;
|
||||
|
||||
public const uint STAT_MAGIC_ACCURACY = 27;
|
||||
public const uint STAT_MAGIC_EVASION = 28;
|
||||
|
||||
public const uint STAT_CRAFT_PROCESSING = 30;
|
||||
public const uint STAT_CRAFT_MAGIC_PROCESSING = 31;
|
||||
public const uint STAT_CRAFT_PROCESS_CONTROL = 32;
|
||||
|
||||
public const uint STAT_HARVEST_POTENCY = 33;
|
||||
public const uint STAT_HARVEST_LIMIT = 34;
|
||||
public const uint STAT_HARVEST_RATE = 35;
|
||||
|
||||
public float[] castGauge_speed = { 1.0f, 0.25f};
|
||||
public bool[] timingCommandFlag = new bool[4];
|
||||
public short[] generalParameter = new short[35];
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 CharaWork
|
||||
{
|
||||
public uint PROPERTY_NAMEPLATE_VISIBLE = 1;
|
||||
public uint PROPERTY_NAMEPLATE_VISIBLE2 = 5;
|
||||
|
||||
public ParameterSave parameterSave = new ParameterSave();
|
||||
public ParameterTemp parameterTemp = new ParameterTemp();
|
||||
public BattleSave battleSave = new BattleSave();
|
||||
public BattleTemp battleTemp = new BattleTemp();
|
||||
public EventSave eventSave = new EventSave();
|
||||
public EventTemp eventTemp = new EventTemp();
|
||||
|
||||
public bool gameParameter = false;
|
||||
|
||||
|
||||
public byte[] property = new byte[32];
|
||||
|
||||
public ushort[] status = new ushort[20];
|
||||
public uint[] statusShownTime = new uint[20];
|
||||
|
||||
public uint[] command = new uint[64]; //ACTORS
|
||||
public byte[] commandCategory = new byte[64];
|
||||
public byte commandBorder = 0x20;
|
||||
public bool[] commandAcquired = new bool[4096];
|
||||
public bool[] additionalCommandAcquired = new bool[36];
|
||||
|
||||
public uint currentContentGroup;
|
||||
public uint depictionJudge = 0xa0f50911;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,30 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 EventSave
|
||||
{
|
||||
public bool bazaar;
|
||||
public byte bazaarTax;
|
||||
public byte repairType;
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 EventTemp
|
||||
{
|
||||
public bool bazaarRetail = false;
|
||||
public bool bazaarRepair = false;
|
||||
public bool bazaarMateria = false;
|
||||
|
||||
public ushort[] linshellIcon = new ushort[4];
|
||||
}
|
||||
}
|
@ -1,930 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.chara.npc;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.dataobjects;
|
||||
using Meteor.Map.packets.send.actor.inventory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Meteor.Map.actors.chara.player
|
||||
{
|
||||
class ItemPackage
|
||||
{
|
||||
public const ushort NORMAL = 0; //Max 0xC8
|
||||
public const ushort UNKNOWN = 1; //Max 0x96
|
||||
public const ushort LOOT = 4; //Max 0xA
|
||||
public const ushort MELDREQUEST = 5; //Max 0x04
|
||||
public const ushort BAZAAR = 7; //Max 0x0A
|
||||
public const ushort CURRENCY_CRYSTALS = 99; //Max 0x140
|
||||
public const ushort KEYITEMS = 100; //Max 0x500
|
||||
public const ushort EQUIPMENT = 0x00FE; //Max 0x23
|
||||
public const ushort TRADE = 0x00FD; //Max 0x04
|
||||
public const ushort EQUIPMENT_OTHERPLAYER = 0x00F9; //Max 0x23
|
||||
|
||||
public const ushort MAXSIZE_NORMAL = 200;
|
||||
public const ushort MAXSIZE_CURRANCY = 320;
|
||||
public const ushort MAXSIZE_KEYITEMS = 500;
|
||||
public const ushort MAXSIZE_LOOT = 10;
|
||||
public const ushort MAXSIZE_TRADE = 4;
|
||||
public const ushort MAXSIZE_MELDREQUEST = 4;
|
||||
public const ushort MAXSIZE_BAZAAR = 10;
|
||||
public const ushort MAXSIZE_EQUIPMENT = 35;
|
||||
public const ushort MAXSIZE_EQUIPMENT_OTHERPLAYER = 0x23;
|
||||
|
||||
public const int ERROR_SUCCESS = 0;
|
||||
public const int ERROR_FULL = 1;
|
||||
public const int ERROR_HAS_UNIQUE = 2;
|
||||
public const int ERROR_SYSTEM = 3;
|
||||
|
||||
private Character owner;
|
||||
private ushort itemPackageCapacity;
|
||||
private ushort itemPackageCode;
|
||||
private bool isTemporary;
|
||||
private InventoryItem[] list;
|
||||
private bool[] isDirty;
|
||||
private bool holdingUpdates = false;
|
||||
|
||||
private int endOfListIndex = 0;
|
||||
|
||||
public ItemPackage(Character ownerPlayer, ushort capacity, ushort code, bool temporary = false)
|
||||
{
|
||||
owner = ownerPlayer;
|
||||
itemPackageCapacity = capacity;
|
||||
itemPackageCode = code;
|
||||
isTemporary = temporary;
|
||||
list = new InventoryItem[capacity];
|
||||
isDirty = new bool[capacity];
|
||||
}
|
||||
|
||||
#region Inventory Management
|
||||
public void InitList(List<InventoryItem> itemsFromDB)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (InventoryItem item in itemsFromDB)
|
||||
{
|
||||
item.SetOwner(owner, itemPackageCode, (ushort) i);
|
||||
list[i++] = item;
|
||||
}
|
||||
endOfListIndex = i;
|
||||
}
|
||||
|
||||
public InventoryItem GetItemAtSlot(ushort slot)
|
||||
{
|
||||
if (slot < list.Length)
|
||||
return list[slot];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public InventoryItem GetItemByUniqueId(ulong uniqueItemId)
|
||||
{
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
{
|
||||
InventoryItem item = list[i];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
if (item.uniqueId == uniqueItemId)
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public InventoryItem GetItemByCatelogId(ulong catelogId)
|
||||
{
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
{
|
||||
InventoryItem item = list[i];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
if (item.itemId == catelogId)
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int AddItem(uint itemId)
|
||||
{
|
||||
return AddItem(itemId, 1, 1);
|
||||
}
|
||||
|
||||
public int AddItem(uint itemId, int quantity)
|
||||
{
|
||||
return AddItem(itemId, quantity, 1);
|
||||
}
|
||||
|
||||
public int AddItem(uint itemId, int quantity, byte quality)
|
||||
{
|
||||
if (!IsSpaceForAdd(itemId, quantity, quality))
|
||||
return ERROR_FULL;
|
||||
|
||||
ItemData gItem = Server.GetItemGamedata(itemId);
|
||||
|
||||
//If it's unique, abort
|
||||
if (HasItem(itemId) && gItem.isExclusive)
|
||||
return ERROR_HAS_UNIQUE;
|
||||
|
||||
if (gItem == null)
|
||||
{
|
||||
Program.Log.Error("Inventory.AddItem: unable to find item %u", itemId);
|
||||
return ERROR_SYSTEM;
|
||||
}
|
||||
|
||||
//Check if item id exists
|
||||
int quantityCount = quantity;
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
{
|
||||
InventoryItem item = list[i];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack)
|
||||
{
|
||||
int oldQuantity = item.quantity;
|
||||
item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack);
|
||||
isDirty[i] = true;
|
||||
quantityCount -= (gItem.maxStack - oldQuantity);
|
||||
|
||||
DoDatabaseQuantity(item.uniqueId, item.quantity);
|
||||
|
||||
if (quantityCount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//New item that spilled over
|
||||
while (quantityCount > 0)
|
||||
{
|
||||
InventoryItem.ItemModifier modifiers = null;
|
||||
if (gItem.durability != 0)
|
||||
{
|
||||
modifiers = new InventoryItem.ItemModifier();
|
||||
modifiers.durability = (uint)gItem.durability;
|
||||
}
|
||||
|
||||
InventoryItem addedItem = Database.CreateItem(itemId, Math.Min(quantityCount, gItem.maxStack), quality, modifiers);
|
||||
addedItem.SetOwner(owner, itemPackageCode, (ushort)endOfListIndex);
|
||||
isDirty[endOfListIndex] = true;
|
||||
list[endOfListIndex++] = addedItem;
|
||||
quantityCount -= gItem.maxStack;
|
||||
|
||||
DoDatabaseAdd(addedItem);
|
||||
}
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
public int AddItems(uint[] itemIds, uint[] quantity = null, byte[] quality = null)
|
||||
{
|
||||
//Check if has space
|
||||
if (!CanAdd(itemIds, quantity, quality))
|
||||
return ERROR_FULL;
|
||||
|
||||
for (int i = 0; i < itemIds.Length; i++)
|
||||
{
|
||||
ItemData gItem = Server.GetItemGamedata(itemIds[i]);
|
||||
|
||||
//If it's unique, abort
|
||||
if (HasItem(itemIds[i]) && gItem.isExclusive)
|
||||
return ERROR_HAS_UNIQUE;
|
||||
|
||||
if (gItem == null)
|
||||
{
|
||||
Program.Log.Error("Inventory.AddItem: unable to find item %u", itemIds[i]);
|
||||
return ERROR_SYSTEM;
|
||||
}
|
||||
|
||||
//Check if item id exists
|
||||
uint setQuantity = quantity != null ? quantity[i] : 1;
|
||||
int quantityCount = (int)setQuantity;
|
||||
for (int j = 0; j < endOfListIndex; j++)
|
||||
{
|
||||
InventoryItem item = list[j];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
byte setQuality = quality != null ? quality[i] : (byte)1;
|
||||
|
||||
if (item.itemId == itemIds[i] && item.quality == setQuality && item.quantity < gItem.maxStack)
|
||||
{
|
||||
int oldQuantity = item.quantity;
|
||||
item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack);
|
||||
isDirty[j] = true;
|
||||
quantityCount -= (gItem.maxStack - oldQuantity);
|
||||
|
||||
DoDatabaseQuantity(item.uniqueId, item.quantity);
|
||||
|
||||
if (quantityCount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//New item that spilled over
|
||||
while (quantityCount > 0)
|
||||
{
|
||||
InventoryItem.ItemModifier modifiers = null;
|
||||
if (gItem.durability != 0)
|
||||
{
|
||||
modifiers = new InventoryItem.ItemModifier();
|
||||
modifiers.durability = (uint)gItem.durability;
|
||||
}
|
||||
|
||||
byte setQuality = quality != null ? quality[i] : (byte)1;
|
||||
|
||||
InventoryItem addedItem = Database.CreateItem(itemIds[i], Math.Min(quantityCount, gItem.maxStack), setQuality, modifiers);
|
||||
addedItem.SetOwner(owner, itemPackageCode, (ushort)endOfListIndex);
|
||||
isDirty[endOfListIndex] = true;
|
||||
list[endOfListIndex++] = addedItem;
|
||||
quantityCount -= gItem.maxStack;
|
||||
|
||||
DoDatabaseAdd(addedItem);
|
||||
}
|
||||
}
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
public int AddItem(InventoryItem itemRef)
|
||||
{
|
||||
//If it isn't a single item (ie: armor) just add like normal (not valid for BAZAAR)
|
||||
if (itemPackageCode != BAZAAR && itemRef.GetItemData().maxStack > 1)
|
||||
return AddItem(itemRef.itemId, itemRef.quantity, itemRef.quality);
|
||||
|
||||
if (!IsSpaceForAdd(itemRef.itemId, itemRef.quantity, itemRef.quality))
|
||||
return ERROR_FULL;
|
||||
|
||||
ItemData gItem = Server.GetItemGamedata(itemRef.itemId);
|
||||
|
||||
if (gItem == null)
|
||||
{
|
||||
Program.Log.Error("Inventory.AddItem: unable to find item %u", itemRef.itemId);
|
||||
return ERROR_SYSTEM;
|
||||
}
|
||||
|
||||
itemRef.SetOwner(owner, itemPackageCode, (ushort)endOfListIndex);
|
||||
|
||||
isDirty[endOfListIndex] = true;
|
||||
list[endOfListIndex++] = itemRef;
|
||||
DoDatabaseAdd(itemRef);
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
public int MoveItem(ushort position, ItemPackage destinationPackage)
|
||||
{
|
||||
InventoryItem item = GetItemAtSlot(position);
|
||||
|
||||
if (destinationPackage.CanAdd(item))
|
||||
{
|
||||
RemoveItemAtSlot(position);
|
||||
destinationPackage.AddItem(item);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
return ERROR_FULL;
|
||||
}
|
||||
|
||||
public int MoveItem(InventoryItem item, ItemPackage destinationPackage)
|
||||
{
|
||||
if (destinationPackage == null || item == null)
|
||||
return ERROR_SYSTEM;
|
||||
|
||||
if (destinationPackage.CanAdd(item))
|
||||
{
|
||||
RemoveItem(item);
|
||||
destinationPackage.AddItem(item);
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
return ERROR_FULL;
|
||||
}
|
||||
|
||||
public void RemoveItem(uint itemId)
|
||||
{
|
||||
RemoveItem(itemId, 1);
|
||||
}
|
||||
|
||||
public void RemoveItem(uint itemId, int quantity)
|
||||
{
|
||||
RemoveItem(itemId, quantity, 1);
|
||||
}
|
||||
|
||||
public void RemoveItem(uint itemId, int quantity, int quality)
|
||||
{
|
||||
if (!HasItem(itemId, quantity, quality))
|
||||
return;
|
||||
|
||||
List<ushort> slotsToUpdate = new List<ushort>();
|
||||
List<InventoryItem> itemsToRemove = new List<InventoryItem>();
|
||||
List<ushort> slotsToRemove = new List<ushort>();
|
||||
List<SubPacket> AddItemPackets = new List<SubPacket>();
|
||||
|
||||
//Remove as we go along
|
||||
int quantityCount = quantity;
|
||||
ushort lowestSlot = 0;
|
||||
for (int i = endOfListIndex - 1; i >= 0; i--)
|
||||
{
|
||||
InventoryItem item = list[i];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
if (item.itemId == itemId && item.quality == quality)
|
||||
{
|
||||
int oldQuantity = item.quantity;
|
||||
//Stack nomnomed
|
||||
if (item.quantity - quantityCount <= 0)
|
||||
{
|
||||
DoDatabaseRemove(list[i].uniqueId);
|
||||
list[i] = null;
|
||||
}
|
||||
//Stack reduced
|
||||
else
|
||||
{
|
||||
item.quantity -= quantityCount;
|
||||
DoDatabaseQuantity(list[i].uniqueId, list[i].quantity);}
|
||||
|
||||
isDirty[i] = true;
|
||||
|
||||
quantityCount -= oldQuantity;
|
||||
lowestSlot = item.slot;
|
||||
|
||||
if (quantityCount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoRealign();
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveItem(InventoryItem item)
|
||||
{
|
||||
if (itemPackageCode == item.itemPackage)
|
||||
RemoveItemAtSlot(item.slot);
|
||||
}
|
||||
|
||||
public void RemoveItemByUniqueId(ulong itemDBId, int quantity)
|
||||
{
|
||||
ushort slot = 0;
|
||||
InventoryItem toDelete = null;
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
{
|
||||
InventoryItem item = list[i];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
if (item.uniqueId == itemDBId)
|
||||
{
|
||||
toDelete = item;
|
||||
break;
|
||||
}
|
||||
slot++;
|
||||
}
|
||||
|
||||
if (toDelete == null)
|
||||
return;
|
||||
|
||||
if (quantity >= toDelete.quantity)
|
||||
{
|
||||
DoDatabaseRemove(toDelete.uniqueId);
|
||||
list[slot].ClearOwner();
|
||||
list[slot] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
list[slot].quantity -= quantity;
|
||||
DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity);
|
||||
}
|
||||
|
||||
isDirty[slot] = true;
|
||||
|
||||
DoRealign();
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveItemAtSlot(ushort slot)
|
||||
{
|
||||
if (slot >= endOfListIndex)
|
||||
return;
|
||||
|
||||
DoDatabaseRemove(list[slot].uniqueId);
|
||||
|
||||
list[slot].ClearOwner();
|
||||
list[slot] = null;
|
||||
isDirty[slot] = true;
|
||||
|
||||
DoRealign();
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveItemAtSlot(ushort slot, int quantity)
|
||||
{
|
||||
if (slot >= endOfListIndex)
|
||||
return;
|
||||
|
||||
if (list[slot] != null)
|
||||
{
|
||||
list[slot].quantity -= quantity;
|
||||
|
||||
if (list[slot].quantity <= 0)
|
||||
{
|
||||
DoDatabaseRemove(list[slot].uniqueId);
|
||||
|
||||
list[slot].ClearOwner();
|
||||
list[slot] = null;
|
||||
DoRealign();
|
||||
}
|
||||
else
|
||||
DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity);
|
||||
|
||||
isDirty[slot] = true;
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
{
|
||||
list[i].ClearOwner();
|
||||
list[i] = null;
|
||||
isDirty[i] = true;
|
||||
}
|
||||
endOfListIndex = 0;
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
(owner as Player).QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
(owner as Player).QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanAdd(InventoryItem item)
|
||||
{
|
||||
return itemPackageCapacity - GetCount() > 0;
|
||||
}
|
||||
|
||||
public bool CanAdd(uint[] itemIds, uint[] quantity, byte[] quality)
|
||||
{
|
||||
int tempInvSize = GetCount();
|
||||
|
||||
for (int i = 0; i < itemIds.Length; i++)
|
||||
{
|
||||
ItemData gItem = Server.GetItemGamedata(itemIds[i]);
|
||||
//Check if item id exists and fill up til maxstack
|
||||
int quantityCount = (int)(quantity != null ? quantity[i] : 1);
|
||||
for (int j = 0; j < endOfListIndex; j++)
|
||||
{
|
||||
InventoryItem item = list[j];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
if (item.itemId == itemIds[i] && item.quality == (quality != null ? quality[i] : 1) && item.quantity < gItem.maxStack)
|
||||
{
|
||||
quantityCount -= (gItem.maxStack - item.quantity);
|
||||
if (quantityCount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//New items that spilled over creating new stacks
|
||||
while (quantityCount > 0)
|
||||
{
|
||||
quantityCount -= gItem.maxStack;
|
||||
tempInvSize++;
|
||||
}
|
||||
|
||||
//If the new stacks push us over capacity, can't add these items
|
||||
if (tempInvSize > itemPackageCapacity)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void MarkDirty(InventoryItem item)
|
||||
{
|
||||
if (item.itemPackage != itemPackageCode || list[item.slot] == null)
|
||||
return;
|
||||
|
||||
isDirty[item.slot] = true;
|
||||
}
|
||||
|
||||
public void MarkDirty(ushort slot)
|
||||
{
|
||||
isDirty[slot] = true;
|
||||
}
|
||||
|
||||
public InventoryItem[] GetRawList()
|
||||
{
|
||||
return list;
|
||||
}
|
||||
|
||||
public void ChangeDurability(uint slot, uint durabilityChange)
|
||||
{
|
||||
isDirty[slot] = true;
|
||||
}
|
||||
|
||||
public void ChangeSpiritBind(uint slot, uint spiritBindChange)
|
||||
{
|
||||
isDirty[slot] = true;
|
||||
}
|
||||
|
||||
public void ChangeMateria(uint slot, byte materiaSlot, byte materiaId)
|
||||
{
|
||||
isDirty[slot] = true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Packet Functions
|
||||
public void SendFullPackage(Player player)
|
||||
{
|
||||
player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode));
|
||||
SendItemPackets(player, 0);
|
||||
player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
public void SendUpdate()
|
||||
{
|
||||
if (owner is Player && !holdingUpdates)
|
||||
{
|
||||
SendUpdate((Player)owner);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendUpdate(Player player)
|
||||
{
|
||||
List<InventoryItem> items = new List<InventoryItem>();
|
||||
List<ushort> slotsToRemove = new List<ushort>();
|
||||
|
||||
for (int i = 0; i < list.Length; i++)
|
||||
{
|
||||
if (i == endOfListIndex)
|
||||
break;
|
||||
if (isDirty[i])
|
||||
items.Add(list[i]);
|
||||
}
|
||||
|
||||
for (int i = endOfListIndex; i < list.Length; i++)
|
||||
{
|
||||
if (isDirty[i])
|
||||
slotsToRemove.Add((ushort)i);
|
||||
}
|
||||
|
||||
if (!holdingUpdates)
|
||||
Array.Clear(isDirty, 0, isDirty.Length);
|
||||
|
||||
player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode));
|
||||
//Send Updated Slots
|
||||
SendItemPackets(player, items);
|
||||
//Send Remove packets for tail end
|
||||
SendItemPackets(player, slotsToRemove);
|
||||
player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
|
||||
//If player is updating their normal inventory, we need to send
|
||||
//an equip update as well to resync the slots.
|
||||
if (player.Equals(owner) && itemPackageCode == NORMAL)
|
||||
player.GetEquipment().SendUpdate();
|
||||
}
|
||||
|
||||
private void SendItemPackets(Player player, InventoryItem item)
|
||||
{
|
||||
player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, item));
|
||||
}
|
||||
|
||||
private void SendItemPackets(Player player, List<InventoryItem> items)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (items.Count - currentIndex >= 64)
|
||||
player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex >= 32)
|
||||
player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex >= 16)
|
||||
player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex > 1)
|
||||
player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex == 1)
|
||||
{
|
||||
player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex]));
|
||||
currentIndex++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendItemPackets(Player player, int startOffset)
|
||||
{
|
||||
int currentIndex = startOffset;
|
||||
|
||||
List<InventoryItem> lst = new List<InventoryItem>();
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
lst.Add(list[i]);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (endOfListIndex - currentIndex >= 64)
|
||||
player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, lst, ref currentIndex));
|
||||
else if (endOfListIndex - currentIndex >= 32)
|
||||
player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, lst, ref currentIndex));
|
||||
else if (endOfListIndex - currentIndex >= 16)
|
||||
player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, lst, ref currentIndex));
|
||||
else if (endOfListIndex - currentIndex > 1)
|
||||
player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, lst, ref currentIndex));
|
||||
else if (endOfListIndex - currentIndex == 1)
|
||||
{
|
||||
player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex]));
|
||||
currentIndex++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendItemPackets(Player player, ushort index)
|
||||
{
|
||||
player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, index));
|
||||
}
|
||||
|
||||
private void SendItemPackets(Player player, List<ushort> indexes)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (indexes.Count - currentIndex >= 64)
|
||||
player.QueuePacket(InventoryRemoveX64Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
|
||||
else if (indexes.Count - currentIndex >= 32)
|
||||
player.QueuePacket(InventoryRemoveX32Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
|
||||
else if (indexes.Count - currentIndex >= 16)
|
||||
player.QueuePacket(InventoryRemoveX16Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
|
||||
else if (indexes.Count - currentIndex > 1)
|
||||
player.QueuePacket(InventoryRemoveX08Packet.BuildPacket(owner.actorId, indexes, ref currentIndex));
|
||||
else if (indexes.Count - currentIndex == 1)
|
||||
{
|
||||
player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, indexes[currentIndex]));
|
||||
currentIndex++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Automatic Client and DB Updating
|
||||
|
||||
private void DoDatabaseAdd(InventoryItem addedItem)
|
||||
{
|
||||
if (isTemporary)
|
||||
return;
|
||||
|
||||
Database.AddItem(owner, addedItem, itemPackageCode, addedItem.slot);
|
||||
}
|
||||
|
||||
private void DoDatabaseQuantity(ulong itemDBId, int quantity)
|
||||
{
|
||||
if (isTemporary)
|
||||
return;
|
||||
|
||||
Database.SetQuantity(itemDBId, quantity);
|
||||
}
|
||||
|
||||
private void DoDatabaseRemove(ulong itemDBId)
|
||||
{
|
||||
if (isTemporary)
|
||||
return;
|
||||
|
||||
Database.RemoveItem(owner, itemDBId);
|
||||
}
|
||||
|
||||
public void StartSendUpdate()
|
||||
{
|
||||
holdingUpdates = true;
|
||||
}
|
||||
|
||||
public void DoneSendUpdate()
|
||||
{
|
||||
holdingUpdates = false;
|
||||
SendUpdate();
|
||||
Array.Clear(isDirty, 0, isDirty.Length);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Inventory Utils
|
||||
|
||||
public bool IsFull()
|
||||
{
|
||||
return endOfListIndex >= itemPackageCapacity;
|
||||
}
|
||||
|
||||
public int GetFreeSlots()
|
||||
{
|
||||
return itemPackageCapacity - endOfListIndex;
|
||||
}
|
||||
|
||||
public bool IsSpaceForAdd(uint itemId, int quantity, int quality)
|
||||
{
|
||||
int quantityCount = quantity;
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
{
|
||||
InventoryItem item = list[i];
|
||||
ItemData gItem = Server.GetItemGamedata(item.itemId);
|
||||
if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack)
|
||||
{
|
||||
quantityCount -= (gItem.maxStack - item.quantity);
|
||||
if (quantityCount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return quantityCount <= 0 || (quantityCount > 0 && !IsFull());
|
||||
}
|
||||
|
||||
public bool HasItem(uint itemId)
|
||||
{
|
||||
return HasItem(itemId, 1);
|
||||
}
|
||||
|
||||
public bool HasItem(uint itemId, int minQuantity)
|
||||
{
|
||||
return HasItem(itemId, minQuantity, 1);
|
||||
}
|
||||
|
||||
public bool HasItem(uint itemId, int minQuantity, int quality)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
for (int i = endOfListIndex - 1; i >= 0; i--)
|
||||
{
|
||||
InventoryItem item = list[i];
|
||||
|
||||
Debug.Assert(item != null, "Item slot was null!!!");
|
||||
|
||||
if (item.itemId == itemId && item.quality == quality)
|
||||
count += item.quantity;
|
||||
|
||||
if (count >= minQuantity)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public int GetNextEmptySlot()
|
||||
{
|
||||
return endOfListIndex;
|
||||
}
|
||||
|
||||
private void DoRealign()
|
||||
{
|
||||
List<InventoryItem> positionUpdate = new List<InventoryItem>();
|
||||
|
||||
int lastNullSlot = -1;
|
||||
|
||||
for (int i = 0; i < endOfListIndex; i++)
|
||||
{
|
||||
if (list[i] == null && lastNullSlot == -1)
|
||||
{
|
||||
lastNullSlot = i;
|
||||
continue;
|
||||
}
|
||||
else if (list[i] != null && lastNullSlot != -1)
|
||||
{
|
||||
list[lastNullSlot] = list[i];
|
||||
if (list[lastNullSlot].GetOfferedTo() != null)
|
||||
{
|
||||
list[lastNullSlot].UpdateOfferedSlot((ushort)(list[lastNullSlot].slot - lastNullSlot));
|
||||
}
|
||||
list[lastNullSlot].slot = (ushort)lastNullSlot;
|
||||
positionUpdate.Add(list[lastNullSlot]);
|
||||
list[i] = null;
|
||||
isDirty[lastNullSlot] = true;
|
||||
isDirty[i] = true;
|
||||
lastNullSlot++;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastNullSlot != -1)
|
||||
endOfListIndex = lastNullSlot;
|
||||
|
||||
Database.UpdateItemPositions(positionUpdate);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public int GetCount()
|
||||
{
|
||||
return endOfListIndex;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
string packageName;
|
||||
switch (itemPackageCode)
|
||||
{
|
||||
case NORMAL:
|
||||
packageName = "Inventory";
|
||||
break;
|
||||
case LOOT:
|
||||
packageName = "Loot";
|
||||
break;
|
||||
case MELDREQUEST:
|
||||
packageName = "Meld Request";
|
||||
break;
|
||||
case BAZAAR:
|
||||
packageName = "Bazaar";
|
||||
break;
|
||||
case CURRENCY_CRYSTALS:
|
||||
packageName = "Currency";
|
||||
break;
|
||||
case KEYITEMS:
|
||||
packageName = "KeyItems";
|
||||
break;
|
||||
case EQUIPMENT:
|
||||
packageName = "Equipment";
|
||||
break;
|
||||
case TRADE:
|
||||
packageName = "Trade";
|
||||
break;
|
||||
case EQUIPMENT_OTHERPLAYER:
|
||||
packageName = "CheckEquip";
|
||||
break;
|
||||
default:
|
||||
packageName = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
return string.Format("{0} Package", packageName);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,184 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
|
||||
namespace Meteor.Map.actors.chara
|
||||
{
|
||||
//These will need to be redone at some point. remember to update tables in db.
|
||||
//Consider using text_paramname sheet. that matches up with the stats on armor, but some things will need special handling
|
||||
//Also, 0-35 should probably match with up BattleTemp
|
||||
enum Modifier : UInt32
|
||||
{
|
||||
//These line up with ParamNames starting at 15001 and appear on gear
|
||||
//Health
|
||||
Hp = 0, //Max HP
|
||||
Mp = 1, //Max MP
|
||||
Tp = 2, //Max TP
|
||||
|
||||
//Main stats
|
||||
Strength = 3,
|
||||
Vitality = 4,
|
||||
Dexterity = 5,
|
||||
Intelligence = 6,
|
||||
Mind = 7,
|
||||
Piety = 8,
|
||||
|
||||
//Elemental Resistances
|
||||
FireResistance = 9, //Lowers Fire damage taken
|
||||
IceResistance = 10, //Lowers Ice damage taken
|
||||
WindResistance = 11, //Lowers Wind damage taken
|
||||
EarthResistance = 12, //Lowers Earth damage taken
|
||||
LightningResistance = 13, //Lowers Lightning damage taken
|
||||
WaterResistance = 14, //Lowers Water damage taken
|
||||
|
||||
//Physical Secondary stats
|
||||
Accuracy = 15, //Increases chance to hit with physical attacks
|
||||
Evasion = 16, //Decreases chance to be hit by physical attacks
|
||||
Attack = 17, //Increases damage done with physical attacks
|
||||
Defense = 18, //Decreases damage taken from physical attacks
|
||||
|
||||
//Physical crit stats
|
||||
CriticalHitRating = 19, //Increases chance to crit with physical attacks
|
||||
CriticalHitEvasion = 20, //Decreases chance to be crit by physical attacks
|
||||
CriticalHitAttackPower = 21, //Increases damage done by critical physical attacks
|
||||
CriticalHitResilience = 22, //Decreases damage taken from critical physical attacks
|
||||
|
||||
//Magic secondary stats
|
||||
AttackMagicPotency = 23, //Increases damage done with magical attacks
|
||||
HealingMagicPotency = 24, //Increases healing done with magic healing
|
||||
EnhancementMagicPotency = 25, //Increases effect of enhancement magic
|
||||
EnfeeblingMagicPotency = 26, //Increases effect of enfeebling magic
|
||||
MagicAccuracy = 27, //Decreases chance for magic to be evaded
|
||||
MagicEvasion = 28, //Increases chance to evade magic
|
||||
|
||||
//Crafting stats
|
||||
Craftsmanship = 29,
|
||||
MagicCraftsmanship = 30,
|
||||
Control = 31,
|
||||
Gathering = 32,
|
||||
Output = 33,
|
||||
Perception = 34,
|
||||
|
||||
//Magic crit stats
|
||||
MagicCriticalHitRating = 35, //Increases chance to crit with magical attacks
|
||||
MagicCriticalHitEvasion = 36, //Decreases chance to be crit by magical attacks
|
||||
MagicCriticalHitPotency = 37, //Increases damage done by critical magical attacks
|
||||
MagicCriticalHitResilience = 38, //Decreases damage taken from critical magical attacks
|
||||
|
||||
//Blocking stats
|
||||
Parry = 39, //Increases chance to parry
|
||||
BlockRate = 40, //Increases chance to block
|
||||
Block = 41, //Reduces damage taken from blocked attacks
|
||||
|
||||
//Elemental Potencies
|
||||
FireMagicPotency = 42, //Increases damage done by Fire Magic
|
||||
IceMagicPotency = 43, //Increases damage done by Ice Magic
|
||||
WindMagicPotency = 44, //Increases damage done by Wind Magic
|
||||
EarthMagicPotency = 45, //Increases damage done by Earth Magic
|
||||
LightningMagicPotency = 46, //Increases damage done by Lightning Magic
|
||||
WaterMagicPotency = 47, //Increases damage done by Water Magic
|
||||
|
||||
//Miscellaneous
|
||||
Regen = 48, //Restores health over time
|
||||
Refresh = 49, //Restores MP over time
|
||||
StoreTp = 50, //Increases TP gained by auto attacks and damaging abiltiies
|
||||
Enmity = 51, //Increases enmity gained from actions
|
||||
Spikes = 52, //Deals damage or status to attacker when hit
|
||||
Haste = 53, //Increases attack speed
|
||||
//54 and 55 didn't have names and seem to be unused
|
||||
ReducedDurabilityLoss = 56, //Reduces durability loss
|
||||
IncreasedSpiritbondGain = 57, //Increases rate of spiritbonding
|
||||
Damage = 58, //Increases damage of auto attacks
|
||||
Delay = 59, //Increases rate of auto attacks
|
||||
Fastcast = 60, //Increases speed of casts
|
||||
MovementSpeed = 61, //Increases movement speed
|
||||
Exp = 62, //Increases experience gained
|
||||
RestingHp = 63, //?
|
||||
RestingMp = 64, //?
|
||||
|
||||
//Attack property resistances
|
||||
SlashingResistance = 65, //Reduces damage taken by slashing attacks
|
||||
PiercingResistance = 66, //Reduces damage taken by piercing attacks
|
||||
BluntResistance = 67, //Reduces damage taken by blunt attacks
|
||||
ProjectileResistance = 68, //Reduces damage taken by projectile attacks
|
||||
SonicResistance = 69, //Reduces damage taken by sonic attacks
|
||||
BreathResistance = 70, //Reduces damage taken by breath attacks
|
||||
PhysicalResistance = 71, //Reduces damage taken by physical attacks
|
||||
MagicResistance = 72, //Reduces damage taken by magic attacks
|
||||
|
||||
//Status resistances
|
||||
SlowResistance = 73, //Reduces chance to be inflicted with slow by status magic
|
||||
PetrificationResistance = 74, //Reduces chance to be inflicted with petrification by status magic
|
||||
ParalysisResistance = 75, //Reduces chance to be inflicted with paralysis by status magic
|
||||
SilenceResistance = 76, //Reduces chance to be inflicted with silence by status magic
|
||||
BlindResistance = 77, //Reduces chance to be inflicted with blind by status magic
|
||||
PoisonResistance = 78, //Reduces chance to be inflicted with poison by status magic
|
||||
StunResistance = 79, //Reduces chance to be inflicted with stun by status magic
|
||||
SleepResistance = 80, //Reduces chance to be inflicted with sleep by status magic
|
||||
BindResistance = 81, //Reduces chance to be inflicted with bind by status magic
|
||||
HeavyResistance = 82, //Reduces chance to be inflicted with heavy by status magic
|
||||
DoomResistance = 83, //Reduces chance to be inflicted with doom by status magic
|
||||
|
||||
//84-101 didn't have names and seem to be unused
|
||||
//Miscellaneous
|
||||
ConserveMp = 101, //Chance to reduce mp used by actions
|
||||
SpellInterruptResistance = 102, //Reduces chance to be interrupted by damage while casting
|
||||
DoubleDownOdds = 103, //Increases double down odds
|
||||
HqDiscoveryRate = 104,
|
||||
|
||||
|
||||
//Non-gear mods
|
||||
None = 105,
|
||||
NAMEPLATE_SHOWN = 106,
|
||||
TARGETABLE = 107,
|
||||
NAMEPLATE_SHOWN2 = 108,
|
||||
|
||||
HpPercent = 109,
|
||||
MpPercent = 110,
|
||||
TpPercent = 111,
|
||||
|
||||
AttackRange = 112, //How far away in yalms this character can attack from (probably won't need this when auto attack skills are done)
|
||||
|
||||
Raise = 113,
|
||||
MinimumHpLock = 114, //Stops HP from falling below this value
|
||||
MinimumMpLock = 115, //Stops MP from falling below this value
|
||||
MinimumTpLock = 116, //Stops TP from falling below this value
|
||||
AttackType = 117, //Attack property of auto attacks (might not need this when auto attack skills are done, unsure)
|
||||
CanBlock = 118, //Whether the character can block attacks. (For players this is only true when they have a shield)
|
||||
HitCount = 119, //Amount of hits in an auto attack. Usually 1, 2 for h2h, 3 with spinning heel
|
||||
|
||||
//Flat percent increases to these rates. Might not need these?
|
||||
RawEvadeRate = 120,
|
||||
RawParryRate = 121,
|
||||
RawBlockRate = 122,
|
||||
RawResistRate = 123,
|
||||
RawHitRate = 124,
|
||||
RawCritRate = 125,
|
||||
|
||||
DamageTakenDown = 126, //Percent damage taken down
|
||||
Regain = 127, //TP regen, should be -90 out of combat, Invigorate sets to 100+ depending on traits
|
||||
RegenDown = 128, //Damage over time effects. Separate from normal Regen because of how they are displayed in game
|
||||
Stoneskin = 129, //Nullifies damage
|
||||
KnockbackImmune = 130, //Immune to knockback effects when above 0
|
||||
Stealth = 131, //Not visisble when above 0
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
|
||||
namespace Meteor.Map.actors.chara
|
||||
{
|
||||
class ModifierListEntry
|
||||
{
|
||||
public uint id;
|
||||
public Int64 value;
|
||||
|
||||
public ModifierListEntry(uint id, Int64 value)
|
||||
{
|
||||
this.id = id;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
class ModifierList
|
||||
{
|
||||
public Dictionary<uint, ModifierListEntry> modList;
|
||||
public Dictionary<uint, ModifierListEntry> mobModList;
|
||||
|
||||
public ModifierList(uint id)
|
||||
{
|
||||
modList = new Dictionary<uint, ModifierListEntry>();
|
||||
mobModList = new Dictionary<uint, ModifierListEntry>();
|
||||
}
|
||||
|
||||
public void AddModifier(uint id, Int64 val, bool isMobMod)
|
||||
{
|
||||
var list = isMobMod ? mobModList : modList;
|
||||
list.Add(id, new ModifierListEntry(id, val));
|
||||
}
|
||||
|
||||
public void SetModifier(uint id, Int64 val, bool isMobMod)
|
||||
{
|
||||
var list = isMobMod ? mobModList : modList;
|
||||
if (list.ContainsKey(id))
|
||||
list[id].value = val;
|
||||
else
|
||||
list.Add(id, new ModifierListEntry(id, val));
|
||||
}
|
||||
|
||||
public Int64 GetModifier(uint id, bool isMobMod)
|
||||
{
|
||||
ModifierListEntry retVal;
|
||||
var list = isMobMod ? mobModList : modList;
|
||||
if (!list.TryGetValue(id, out retVal))
|
||||
return 0;
|
||||
|
||||
return retVal.value;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 ParameterSave
|
||||
{
|
||||
public short[] hp = new short[8];
|
||||
public short[] hpMax = new short[8];
|
||||
public short mp;
|
||||
public short mpMax;
|
||||
|
||||
public byte[] state_mainSkill = new byte[4];
|
||||
public short state_mainSkillLevel;
|
||||
|
||||
public byte[] state_boostPointForSkill = new byte[4];
|
||||
|
||||
public uint[] commandSlot_recastTime = new uint[40];
|
||||
public bool[] commandSlot_compatibility = new bool[40];
|
||||
|
||||
public ushort[] giftCommandSlot_commandId = new ushort[10];
|
||||
|
||||
public ushort[] constanceCommandSlot_commandId = new ushort[10];
|
||||
|
||||
public byte abilityCostPoint_used;
|
||||
public byte abilityCostPoint_max;
|
||||
|
||||
public byte giftCostPoint_used;
|
||||
public byte giftCostPoint_max;
|
||||
|
||||
public byte constanceCostPoint_used;
|
||||
public byte constanceCostPoint_max;
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 ParameterTemp
|
||||
{
|
||||
public short tp = 0;
|
||||
|
||||
public int targetInformation = 0;
|
||||
|
||||
public ushort[] maxCommandRecastTime = new ushort[40];
|
||||
|
||||
public float[] forceControl_float_forClientSelf = { 1.0f, 1.0f, 0.0f, 0.0f};
|
||||
public short[] forceControl_int16_forClientSelf = { -1, -1 };
|
||||
|
||||
public byte[] otherClassAbilityCount = new byte[2];
|
||||
public byte[] giftCount = new byte[2];
|
||||
}
|
||||
}
|
@ -1,316 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 Meteor.Map.dataobjects;
|
||||
using Meteor.Map.packets.send.actor.inventory;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Meteor.Map.actors.chara
|
||||
{
|
||||
|
||||
class ReferencedItemPackage
|
||||
{
|
||||
const uint EMPTY = 0xFFFFFFFF;
|
||||
|
||||
private readonly Player owner;
|
||||
private readonly InventoryItem[] referenceList;
|
||||
private readonly ushort itemPackageCode;
|
||||
private readonly ushort itemPackageCapacity;
|
||||
private bool writeToDB = false;
|
||||
|
||||
public ReferencedItemPackage(Player owner, ushort capacity, ushort code)
|
||||
{
|
||||
this.owner = owner;
|
||||
itemPackageCode = code;
|
||||
itemPackageCapacity = capacity;
|
||||
referenceList = new InventoryItem[capacity];
|
||||
|
||||
if (code == ItemPackage.EQUIPMENT)
|
||||
writeToDB = true;
|
||||
}
|
||||
|
||||
public void ToggleDBWrite(bool flag)
|
||||
{
|
||||
writeToDB = flag;
|
||||
}
|
||||
|
||||
#region Package Management
|
||||
public void SetList(InventoryItem[] toSet)
|
||||
{
|
||||
Debug.Assert(referenceList.Length == itemPackageCapacity);
|
||||
toSet.CopyTo(referenceList, 0);
|
||||
}
|
||||
|
||||
public void Set(ushort[] positions, ushort[] itemSlots, ushort itemPackage)
|
||||
{
|
||||
Debug.Assert(positions.Length == itemSlots.Length);
|
||||
|
||||
for (int i = 0; i < positions.Length; i++)
|
||||
{
|
||||
InventoryItem item = owner.GetItemPackage(itemPackage)?.GetItemAtSlot(itemSlots[i]);
|
||||
|
||||
if (item == null)
|
||||
continue;
|
||||
|
||||
Database.EquipItem(owner, positions[i], item.uniqueId);
|
||||
referenceList[positions[i]] = item;
|
||||
}
|
||||
|
||||
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
SendUpdate();
|
||||
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
public void Set(ushort position, ushort itemPackagePosition, ushort itemPackageCode)
|
||||
{
|
||||
InventoryItem item = owner.GetItemPackage(itemPackageCode).GetItemAtSlot(itemPackagePosition);
|
||||
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
Set(position, item);
|
||||
}
|
||||
|
||||
public void Set(ushort position, InventoryItem item)
|
||||
{
|
||||
if (position >= referenceList.Length)
|
||||
return;
|
||||
|
||||
if (writeToDB)
|
||||
Database.EquipItem(owner, position, item.uniqueId);
|
||||
|
||||
ItemPackage newPackage = owner.GetItemPackage(item.itemPackage);
|
||||
ItemPackage oldPackage = null;
|
||||
|
||||
if (referenceList[position] != null)
|
||||
{
|
||||
oldPackage = owner.GetItemPackage(referenceList[position].itemPackage);
|
||||
InventoryItem oldItem = referenceList[position];
|
||||
oldPackage.MarkDirty(oldItem);
|
||||
}
|
||||
|
||||
newPackage.MarkDirty(item);
|
||||
|
||||
referenceList[position] = item;
|
||||
|
||||
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
if (oldPackage != null)
|
||||
oldPackage.SendUpdate();
|
||||
newPackage.SendUpdate();
|
||||
SendSingleUpdate(position);
|
||||
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
public void Clear(ushort position)
|
||||
{
|
||||
if (position >= referenceList.Length)
|
||||
return;
|
||||
|
||||
if (writeToDB)
|
||||
Database.UnequipItem(owner, position);
|
||||
|
||||
ItemPackage oldItemPackage = owner.GetItemPackage(referenceList[position].itemPackage);
|
||||
|
||||
oldItemPackage.MarkDirty(referenceList[position]);
|
||||
referenceList[position] = null;
|
||||
|
||||
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
oldItemPackage.SendUpdate();
|
||||
SendSingleUpdate(position);
|
||||
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
List<ItemPackage> packagesToRefresh = new List<ItemPackage>();
|
||||
|
||||
for (int i = 0; i < referenceList.Length; i++)
|
||||
{
|
||||
if (referenceList[i] == null)
|
||||
continue;
|
||||
|
||||
if (writeToDB)
|
||||
Database.UnequipItem(owner, (ushort)i);
|
||||
|
||||
ItemPackage package = owner.GetItemPackage(referenceList[i].itemPackage);
|
||||
package.MarkDirty(referenceList[i]);
|
||||
packagesToRefresh.Add(package);
|
||||
|
||||
referenceList[i] = null;
|
||||
}
|
||||
|
||||
owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId));
|
||||
for (int i = 0; i < packagesToRefresh.Count; i++)
|
||||
packagesToRefresh[i].SendUpdate();
|
||||
SendUpdate();
|
||||
owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Send Update Functions
|
||||
public void SendSingleUpdate(ushort position)
|
||||
{
|
||||
owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode));
|
||||
SendSingleLinkedItemPacket(owner, position);
|
||||
owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
public void SendUpdate()
|
||||
{
|
||||
SendUpdate(owner);
|
||||
}
|
||||
|
||||
public void SendUpdate(Player targetPlayer)
|
||||
{
|
||||
List<ushort> slotsToUpdate = new List<ushort>();
|
||||
|
||||
for (ushort i = 0; i < referenceList.Length; i++)
|
||||
{
|
||||
if (referenceList[i] != null)
|
||||
slotsToUpdate.Add(i);
|
||||
}
|
||||
|
||||
targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode));
|
||||
SendLinkedItemPackets(targetPlayer, slotsToUpdate);
|
||||
targetPlayer.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
|
||||
}
|
||||
|
||||
public void SendUpdateAsItemPackage(Player targetPlayer)
|
||||
{
|
||||
SendUpdateAsItemPackage(targetPlayer, itemPackageCapacity, itemPackageCode);
|
||||
}
|
||||
|
||||
public void SendUpdateAsItemPackage(Player targetPlayer, ushort destinationCapacity, ushort destinationCode)
|
||||
{
|
||||
List<InventoryItem> items = new List<InventoryItem>();
|
||||
|
||||
for (ushort i = 0; i < referenceList.Length; i++)
|
||||
{
|
||||
if (referenceList[i] == null)
|
||||
continue;
|
||||
|
||||
InventoryItem item = referenceList[i];
|
||||
item.linkSlot = i; //We have to set the linkSlot as this is the position in the Referenced IP, not the original IP it's linked from.
|
||||
items.Add(referenceList[i]);
|
||||
}
|
||||
|
||||
targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, destinationCapacity, destinationCode));
|
||||
SendItemPackets(targetPlayer, items);
|
||||
targetPlayer.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId));
|
||||
|
||||
//Clean Up linkSlots
|
||||
for (ushort i = 0; i < referenceList.Length; i++)
|
||||
{
|
||||
if (referenceList[i] == null)
|
||||
continue;
|
||||
InventoryItem item = referenceList[i];
|
||||
item.linkSlot = 0xFFFF;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Packet Functions (Private)
|
||||
private void SendSingleLinkedItemPacket(Player targetPlayer, ushort position)
|
||||
{
|
||||
if (referenceList[position] == null)
|
||||
targetPlayer.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, position));
|
||||
else
|
||||
targetPlayer.QueuePacket(LinkedItemListX01Packet.BuildPacket(owner.actorId, position, referenceList[position]));
|
||||
}
|
||||
|
||||
private void SendLinkedItemPackets(Player targetPlayer, List<ushort> slotsToUpdate)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (slotsToUpdate.Count - currentIndex >= 64)
|
||||
targetPlayer.QueuePacket(LinkedItemListX64Packet.BuildPacket(owner.actorId, referenceList, slotsToUpdate, ref currentIndex));
|
||||
else if (slotsToUpdate.Count - currentIndex >= 32)
|
||||
targetPlayer.QueuePacket(LinkedItemListX32Packet.BuildPacket(owner.actorId, referenceList, slotsToUpdate, ref currentIndex));
|
||||
else if (slotsToUpdate.Count - currentIndex >= 16)
|
||||
targetPlayer.QueuePacket(LinkedItemListX16Packet.BuildPacket(owner.actorId, referenceList, slotsToUpdate, ref currentIndex));
|
||||
else if (slotsToUpdate.Count - currentIndex > 1)
|
||||
targetPlayer.QueuePacket(LinkedItemListX08Packet.BuildPacket(owner.actorId, referenceList, slotsToUpdate, ref currentIndex));
|
||||
else if (slotsToUpdate.Count - currentIndex == 1)
|
||||
{
|
||||
targetPlayer.QueuePacket(LinkedItemListX01Packet.BuildPacket(owner.actorId, slotsToUpdate[currentIndex], referenceList[slotsToUpdate[currentIndex]]));
|
||||
currentIndex++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendItemPackets(Player player, List<InventoryItem> items)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (items.Count - currentIndex >= 64)
|
||||
player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex >= 32)
|
||||
player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex >= 16)
|
||||
player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex > 1)
|
||||
player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex));
|
||||
else if (items.Count - currentIndex == 1)
|
||||
{
|
||||
player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex]));
|
||||
currentIndex++;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Getters/Setters
|
||||
public ushort GetCode()
|
||||
{
|
||||
return itemPackageCode;
|
||||
}
|
||||
|
||||
public int GetCapacity()
|
||||
{
|
||||
return itemPackageCapacity;
|
||||
}
|
||||
|
||||
public Player GetOwner()
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
public InventoryItem GetItemAtSlot(ushort position)
|
||||
{
|
||||
if (position < referenceList.Length)
|
||||
return referenceList[position];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 SubState
|
||||
{
|
||||
public byte breakage = 0;
|
||||
public byte chantId = 0;
|
||||
public byte guard = 0;
|
||||
public byte waste = 0;
|
||||
public byte mode = 0;
|
||||
public ushort motionPack = 0;
|
||||
|
||||
public void toggleBreak(int index, bool toggle)
|
||||
{
|
||||
if (index > 7 || index < 0)
|
||||
return;
|
||||
|
||||
if (toggle)
|
||||
breakage = (byte)(breakage | (1 << index));
|
||||
else
|
||||
breakage = (byte)(breakage & ~(1 << index));
|
||||
}
|
||||
|
||||
public void setChant(byte chant) {
|
||||
chantId = chant;
|
||||
}
|
||||
|
||||
public void setGuard(byte guard)
|
||||
{
|
||||
if (guard >= 0 && guard <= 3)
|
||||
this.guard = guard;
|
||||
}
|
||||
|
||||
public void setWaste(byte waste)
|
||||
{
|
||||
if (waste >= 0 && waste <= 3)
|
||||
this.waste = waste;
|
||||
}
|
||||
|
||||
public void setMode(byte bitfield)
|
||||
{
|
||||
mode = bitfield;
|
||||
}
|
||||
|
||||
public void setMotionPack(ushort mp)
|
||||
{
|
||||
motionPack = mp;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 Work
|
||||
{
|
||||
public ushort[] guildleveId = new ushort[16];
|
||||
public bool[] guildleveDone = new bool[16];
|
||||
public bool[] guildleveChecked = new bool[16];
|
||||
|
||||
public bool betacheck = false;
|
||||
|
||||
public bool[] event_achieve_aetheryte = new bool[128];
|
||||
}
|
||||
}
|
@ -1,400 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.ai.state;
|
||||
using Meteor.Map.actors.chara.ai.controllers;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
|
||||
// port of ai code in dsp by kjLotus (https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai)
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
class AIContainer
|
||||
{
|
||||
private Character owner;
|
||||
private Controller controller;
|
||||
private Stack<State> states;
|
||||
private DateTime latestUpdate;
|
||||
private DateTime prevUpdate;
|
||||
public readonly PathFind pathFind;
|
||||
private TargetFind targetFind;
|
||||
private ActionQueue actionQueue;
|
||||
private DateTime lastActionTime;
|
||||
|
||||
public AIContainer(Character actor, Controller controller, PathFind pathFind, TargetFind targetFind)
|
||||
{
|
||||
this.owner = actor;
|
||||
this.states = new Stack<State>();
|
||||
this.controller = controller;
|
||||
this.pathFind = pathFind;
|
||||
this.targetFind = targetFind;
|
||||
latestUpdate = DateTime.Now;
|
||||
prevUpdate = latestUpdate;
|
||||
actionQueue = new ActionQueue(owner);
|
||||
}
|
||||
|
||||
public void UpdateLastActionTime(uint delay = 0)
|
||||
{
|
||||
lastActionTime = DateTime.Now.AddSeconds(delay);
|
||||
}
|
||||
|
||||
public DateTime GetLastActionTime()
|
||||
{
|
||||
return lastActionTime;
|
||||
}
|
||||
|
||||
public void Update(DateTime tick)
|
||||
{
|
||||
prevUpdate = latestUpdate;
|
||||
latestUpdate = tick;
|
||||
|
||||
// todo: trigger listeners
|
||||
|
||||
if (controller == null && pathFind != null)
|
||||
{
|
||||
pathFind.FollowPath();
|
||||
}
|
||||
|
||||
// todo: action queues
|
||||
if (controller != null && controller.canUpdate)
|
||||
controller.Update(tick);
|
||||
|
||||
State top;
|
||||
|
||||
while (states.Count > 0 && (top = states.Peek()).Update(tick))
|
||||
{
|
||||
if (top == GetCurrentState())
|
||||
{
|
||||
states.Pop().Cleanup();
|
||||
}
|
||||
}
|
||||
owner.PostUpdate(tick);
|
||||
}
|
||||
|
||||
public void CheckCompletedStates()
|
||||
{
|
||||
while (states.Count > 0 && states.Peek().IsCompleted())
|
||||
{
|
||||
states.Peek().Cleanup();
|
||||
states.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
public void InterruptStates()
|
||||
{
|
||||
while (states.Count > 0 && states.Peek().CanInterrupt())
|
||||
{
|
||||
states.Peek().SetInterrupted(true);
|
||||
states.Peek().Cleanup();
|
||||
states.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
public void InternalUseItem(Character target, uint slot, uint itemId)
|
||||
{
|
||||
// todo: can allies use items?
|
||||
if (owner is Player)
|
||||
{
|
||||
if (CanChangeState())
|
||||
{
|
||||
ChangeState(new ItemState((Player)owner, target, (ushort)slot, itemId));
|
||||
}
|
||||
else
|
||||
{
|
||||
// You cannot use that item now.
|
||||
((Player)owner).SendGameMessage(Server.GetWorldManager().GetActor(), 32544, 0x20, itemId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearStates()
|
||||
{
|
||||
while (states.Count > 0)
|
||||
{
|
||||
states.Peek().Cleanup();
|
||||
states.Pop();
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeController(Controller controller)
|
||||
{
|
||||
this.controller = controller;
|
||||
}
|
||||
|
||||
public T GetController<T>() where T : Controller
|
||||
{
|
||||
return controller as T;
|
||||
}
|
||||
|
||||
public TargetFind GetTargetFind()
|
||||
{
|
||||
return targetFind;
|
||||
}
|
||||
|
||||
public bool CanFollowPath()
|
||||
{
|
||||
return pathFind != null && (GetCurrentState() == null || GetCurrentState().CanChangeState());
|
||||
}
|
||||
|
||||
public bool CanChangeState()
|
||||
{
|
||||
return GetCurrentState() == null || states.Peek().CanChangeState();
|
||||
}
|
||||
|
||||
public void ChangeTarget(Character target)
|
||||
{
|
||||
if (controller != null)
|
||||
{
|
||||
controller.ChangeTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeState(State state)
|
||||
{
|
||||
if (CanChangeState())
|
||||
{
|
||||
if (states.Count <= 10)
|
||||
{
|
||||
CheckCompletedStates();
|
||||
states.Push(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("shit");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ForceChangeState(State state)
|
||||
{
|
||||
if (states.Count <= 10)
|
||||
{
|
||||
CheckCompletedStates();
|
||||
states.Push(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("force shit");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCurrentState<T>() where T : State
|
||||
{
|
||||
return GetCurrentState() is T;
|
||||
}
|
||||
|
||||
public State GetCurrentState()
|
||||
{
|
||||
return states.Count > 0 ? states.Peek() : null;
|
||||
}
|
||||
|
||||
public DateTime GetLatestUpdate()
|
||||
{
|
||||
return latestUpdate;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
// todo: reset cooldowns and stuff here too?
|
||||
targetFind?.Reset();
|
||||
pathFind?.Clear();
|
||||
ClearStates();
|
||||
InternalDisengage();
|
||||
}
|
||||
|
||||
public bool IsSpawned()
|
||||
{
|
||||
return !IsDead();
|
||||
}
|
||||
|
||||
public bool IsEngaged()
|
||||
{
|
||||
return owner.currentMainState == SetActorStatePacket.MAIN_STATE_ACTIVE;
|
||||
}
|
||||
|
||||
public bool IsDead()
|
||||
{
|
||||
return owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD ||
|
||||
owner.currentMainState == SetActorStatePacket.MAIN_STATE_DEAD2;
|
||||
}
|
||||
|
||||
public bool IsRoaming()
|
||||
{
|
||||
// todo: check mounted?
|
||||
return owner.currentMainState == SetActorStatePacket.MAIN_STATE_PASSIVE;
|
||||
}
|
||||
|
||||
public void Engage(Character target)
|
||||
{
|
||||
if (controller != null)
|
||||
controller.Engage(target);
|
||||
else
|
||||
InternalEngage(target);
|
||||
}
|
||||
|
||||
public void Disengage()
|
||||
{
|
||||
if (controller != null)
|
||||
controller.Disengage();
|
||||
else
|
||||
InternalDisengage();
|
||||
}
|
||||
|
||||
public void Ability(Character target, uint abilityId)
|
||||
{
|
||||
if (controller != null)
|
||||
controller.Ability(target, abilityId);
|
||||
else
|
||||
InternalAbility(target, abilityId);
|
||||
}
|
||||
|
||||
public void Cast(Character target, uint spellId)
|
||||
{
|
||||
if (controller != null)
|
||||
controller.Cast(target, spellId);
|
||||
else
|
||||
InternalCast(target, spellId);
|
||||
}
|
||||
|
||||
public void WeaponSkill(Character target, uint weaponSkillId)
|
||||
{
|
||||
if (controller != null)
|
||||
controller.WeaponSkill(target, weaponSkillId);
|
||||
else
|
||||
InternalWeaponSkill(target, weaponSkillId);
|
||||
}
|
||||
|
||||
public void MobSkill(Character target, uint mobSkillId)
|
||||
{
|
||||
if (controller != null)
|
||||
controller.MonsterSkill(target, mobSkillId);
|
||||
else
|
||||
InternalMobSkill(target, mobSkillId);
|
||||
}
|
||||
|
||||
public void UseItem(Character target, uint slot, uint itemId)
|
||||
{
|
||||
if (controller != null)
|
||||
controller.UseItem(target, slot, itemId);
|
||||
}
|
||||
|
||||
public void InternalChangeTarget(Character target)
|
||||
{
|
||||
// targets are changed in the controller
|
||||
if (IsEngaged() || target == null)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Engage(target);
|
||||
}
|
||||
}
|
||||
|
||||
public bool InternalEngage(Character target)
|
||||
{
|
||||
if (IsEngaged())
|
||||
{
|
||||
if (this.owner.target != target)
|
||||
{
|
||||
ChangeTarget(target);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (CanChangeState() || (GetCurrentState() != null && GetCurrentState().IsCompleted()))
|
||||
{
|
||||
ForceChangeState(new AttackState(owner, target));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void InternalDisengage()
|
||||
{
|
||||
pathFind?.Clear();
|
||||
GetTargetFind()?.Reset();
|
||||
|
||||
owner.updateFlags |= ActorUpdateFlags.HpTpMp;
|
||||
ChangeTarget(null);
|
||||
|
||||
if (owner.currentMainState == SetActorStatePacket.MAIN_STATE_ACTIVE)
|
||||
owner.ChangeState(SetActorStatePacket.MAIN_STATE_PASSIVE);
|
||||
|
||||
ClearStates();
|
||||
}
|
||||
|
||||
public void InternalAbility(Character target, uint abilityId)
|
||||
{
|
||||
if (CanChangeState())
|
||||
{
|
||||
ChangeState(new AbilityState(owner, target, (ushort)abilityId));
|
||||
}
|
||||
}
|
||||
|
||||
public void InternalCast(Character target, uint spellId)
|
||||
{
|
||||
if (CanChangeState())
|
||||
{
|
||||
ChangeState(new MagicState(owner, target, (ushort)spellId));
|
||||
}
|
||||
}
|
||||
|
||||
public void InternalWeaponSkill(Character target, uint weaponSkillId)
|
||||
{
|
||||
if (CanChangeState())
|
||||
{
|
||||
ChangeState(new WeaponSkillState(owner, target, (ushort)weaponSkillId));
|
||||
}
|
||||
}
|
||||
|
||||
public void InternalMobSkill(Character target, uint mobSkillId)
|
||||
{
|
||||
if (CanChangeState())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void InternalDie(DateTime tick, uint fadeoutTimerSeconds)
|
||||
{
|
||||
pathFind?.Clear();
|
||||
ClearStates();
|
||||
ForceChangeState(new DeathState(owner, tick, fadeoutTimerSeconds));
|
||||
}
|
||||
|
||||
public void InternalDespawn(DateTime tick, uint respawnTimerSeconds)
|
||||
{
|
||||
ClearStates();
|
||||
Disengage();
|
||||
ForceChangeState(new DespawnState(owner, respawnTimerSeconds));
|
||||
}
|
||||
|
||||
public void InternalRaise(Character target)
|
||||
{
|
||||
// todo: place at target
|
||||
// ForceChangeState(new RaiseState(target));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,412 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 System;
|
||||
using System.Collections.Generic;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
using Meteor.Map.actors.chara.ai.utils;
|
||||
using MoonSharp.Interpreter;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
|
||||
public enum BattleCommandRequirements : ushort
|
||||
{
|
||||
None,
|
||||
DiscipleOfWar = 0x01,
|
||||
DiscipeOfMagic = 0x02,
|
||||
HandToHand = 0x04,
|
||||
Sword = 0x08,
|
||||
Shield = 0x10,
|
||||
Axe = 0x20,
|
||||
Archery = 0x40,
|
||||
Polearm = 0x80,
|
||||
Thaumaturgy = 0x100,
|
||||
Conjury = 0x200
|
||||
}
|
||||
|
||||
public enum BattleCommandPositionBonus : byte
|
||||
{
|
||||
None,
|
||||
Front = 0x01,
|
||||
Rear = 0x02,
|
||||
Flank = 0x04
|
||||
}
|
||||
|
||||
public enum BattleCommandProcRequirement : byte
|
||||
{
|
||||
None,
|
||||
Miss,
|
||||
Evade,
|
||||
Parry,
|
||||
Block
|
||||
}
|
||||
|
||||
public enum BattleCommandValidUser : byte
|
||||
{
|
||||
All,
|
||||
Player,
|
||||
Monster
|
||||
}
|
||||
|
||||
public enum BattleCommandCastType : ushort
|
||||
{
|
||||
None,
|
||||
Weaponskill = 1,
|
||||
Weaponskill2 = 2,
|
||||
BlackMagic = 3,
|
||||
WhiteMagic = 4,
|
||||
SongMagic = 8
|
||||
}
|
||||
|
||||
|
||||
//What type of command it is
|
||||
[Flags]
|
||||
public enum CommandType : ushort
|
||||
{
|
||||
//Type of action
|
||||
None = 0,
|
||||
AutoAttack = 1,
|
||||
WeaponSkill = 2,
|
||||
Ability =3,
|
||||
Spell = 4
|
||||
}
|
||||
|
||||
public enum KnockbackType : ushort
|
||||
{
|
||||
None = 0,
|
||||
Level1 = 1,
|
||||
Level2 = 2,
|
||||
Level3 = 3,
|
||||
Level4 = 4,
|
||||
Level5 = 5,
|
||||
Clockwise1 = 6,
|
||||
Clockwise2 = 7,
|
||||
CounterClockwise1 = 8,
|
||||
CounterClockwise2 = 9,
|
||||
DrawIn = 10
|
||||
}
|
||||
|
||||
class BattleCommand
|
||||
{
|
||||
public ushort id;
|
||||
public string name;
|
||||
public byte job;
|
||||
public byte level;
|
||||
public BattleCommandRequirements requirements;
|
||||
public ValidTarget mainTarget; //what the skill has to be used on. ie self for flare, enemy for ring of talons even though both are self-centere aoe
|
||||
public ValidTarget validTarget; //what type of character the skill can hit
|
||||
public TargetFindAOEType aoeType; //shape of aoe
|
||||
public TargetFindAOETarget aoeTarget; //where the center of the aoe is (target/self)
|
||||
public byte numHits; //amount of hits in the skill
|
||||
public BattleCommandPositionBonus positionBonus; //bonus for front/flank/rear
|
||||
public BattleCommandProcRequirement procRequirement;//if the skill requires a block/parry/evade before using
|
||||
public float range; //maximum distance to target to be able to use this skill
|
||||
public float minRange; //Minimum distance to target to be able to use this skill
|
||||
|
||||
public uint statusId; //id of statuseffect that the skill might inflict
|
||||
public uint statusDuration; //duration of statuseffect in milliseconds
|
||||
public float statusChance; //percent chance of status landing, 0-1.0. Usually 1.0 for buffs
|
||||
public byte castType; //casting animation, 2 for blm, 3 for whm, 8 for brd
|
||||
public uint castTimeMs; //cast time in milliseconds
|
||||
public uint recastTimeMs; //recast time in milliseconds
|
||||
public uint maxRecastTimeSeconds; //maximum recast time in seconds
|
||||
public short mpCost; //short in case these casts can have negative cost
|
||||
public short tpCost; //short because there are certain cases where we want weaponskills to have negative costs (such as Feint)
|
||||
public byte animationType;
|
||||
public ushort effectAnimation;
|
||||
public ushort modelAnimation;
|
||||
public ushort animationDurationSeconds;
|
||||
public uint battleAnimation;
|
||||
public ushort worldMasterTextId;
|
||||
public float aoeRange; //Radius for circle and cone aoes, length for box aoes
|
||||
public float aoeMinRange; //Minimum range of aoe effect for things like Lunar Dynamo or Arrow Helix
|
||||
public float aoeConeAngle; //Angle of aoe cones
|
||||
public float aoeRotateAngle; //Amount aoes are rotated about the target position (usually the user's position)
|
||||
public float rangeHeight; //Total height a skill can be used against target above or below user
|
||||
public float rangeWidth; //Width of box aoes
|
||||
public int[] comboNextCommandId = new int[2]; //next two skills in a combo
|
||||
public short comboStep; //Where in a combo string this skill is
|
||||
public CommandType commandType;
|
||||
public ActionProperty actionProperty;
|
||||
public ActionType actionType;
|
||||
|
||||
|
||||
public byte statusTier; //tier of status to put on target
|
||||
public double statusMagnitude = 0; //magnitude of status to put on target
|
||||
public ushort basePotency; //damage variable
|
||||
public float enmityModifier; //multiples by damage done to get final enmity
|
||||
public float accuracyModifier; //modifies accuracy
|
||||
public float bonusCritRate; //extra crit rate
|
||||
public bool isCombo;
|
||||
public bool comboEffectAdded = false; //If the combo effect is added to multiple hiteffects it plays multiple times, so this keeps track of that
|
||||
public bool isRanged = false;
|
||||
|
||||
public bool actionCrit; //Whether any actions were critical hits, used for Excruciate
|
||||
|
||||
public lua.LuaScript script; //cached script
|
||||
|
||||
public TargetFind targetFind;
|
||||
public BattleCommandValidUser validUser;
|
||||
|
||||
public BattleCommand(ushort id, string name)
|
||||
{
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.range = 0;
|
||||
this.enmityModifier = 1;
|
||||
this.accuracyModifier = 0;
|
||||
this.statusTier = 1;
|
||||
this.statusChance = 50;
|
||||
this.recastTimeMs = (uint) maxRecastTimeSeconds * 1000;
|
||||
this.isCombo = false;
|
||||
}
|
||||
|
||||
public BattleCommand Clone()
|
||||
{
|
||||
return (BattleCommand)MemberwiseClone();
|
||||
}
|
||||
|
||||
public int CallLuaFunction(Character chara, string functionName, params object[] args)
|
||||
{
|
||||
if (script != null && !script.Globals.Get(functionName).IsNil())
|
||||
{
|
||||
DynValue res = new DynValue();
|
||||
res = script.Call(script.Globals.Get(functionName), args);
|
||||
if (res != null)
|
||||
return (int)res.Number;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public bool IsSpell()
|
||||
{
|
||||
return mpCost != 0 || castTimeMs != 0;
|
||||
}
|
||||
|
||||
public bool IsInstantCast()
|
||||
{
|
||||
return castTimeMs == 0;
|
||||
}
|
||||
|
||||
//Checks whether the skill can be used on the given targets, uses error to return specific text ids for errors
|
||||
public bool IsValidMainTarget(Character user, Character target, CommandResult error = null)
|
||||
{
|
||||
targetFind = new TargetFind(user, target);
|
||||
|
||||
if (aoeType == TargetFindAOEType.Box)
|
||||
{
|
||||
targetFind.SetAOEBox(validTarget, aoeTarget, aoeRange, rangeWidth, aoeRotateAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetFind.SetAOEType(validTarget, aoeType, aoeTarget, aoeRange, aoeMinRange, rangeHeight, aoeRotateAngle, aoeConeAngle);
|
||||
}
|
||||
|
||||
/*
|
||||
worldMasterTextId
|
||||
32511 Target does not exist
|
||||
32512 cannot be performed on a KO'd target.
|
||||
32513 can only be performed on a KO'd target.
|
||||
32514 cannot be performed on yourself.
|
||||
32515 can only be performed on yourself.
|
||||
32516 cannot be performed on a friendly target.
|
||||
32517 can only be performed on a friendly target.
|
||||
32518 cannot be performed on an enemy.
|
||||
32519 can only be performed on an enemy.
|
||||
32547 That command cannot be performed on the current target.
|
||||
32548 That command cannot be performed on a party member
|
||||
*/
|
||||
if (target == null)
|
||||
{
|
||||
error?.SetTextId(32511);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill can't be used on a corpse and target is dead
|
||||
if ((mainTarget & ValidTarget.Corpse) == 0 && target.IsDead())
|
||||
{
|
||||
error?.SetTextId(32512);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill must be used on a corpse and target is alive
|
||||
if ((mainTarget & ValidTarget.CorpseOnly) != 0 && target.IsAlive())
|
||||
{
|
||||
error?.SetTextId(32513);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill can't be used on self and target is self
|
||||
if ((mainTarget & ValidTarget.Self) == 0 && target == user)
|
||||
{
|
||||
error?.SetTextId(32514);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill must be used on self and target isn't self
|
||||
if ((mainTarget & ValidTarget.SelfOnly) != 0 && target != user)
|
||||
{
|
||||
error?.SetTextId(32515);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill can't be used on an ally and target is an ally
|
||||
if ((mainTarget & ValidTarget.Ally) == 0 && target.allegiance == user.allegiance)
|
||||
{
|
||||
error?.SetTextId(32516);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill must be used on an ally and target is not an ally
|
||||
if ((mainTarget & ValidTarget.AllyOnly) != 0 && target.allegiance != user.allegiance)
|
||||
{
|
||||
error?.SetTextId(32517);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill can't be used on an enemu and target is an enemy
|
||||
if ((mainTarget & ValidTarget.Enemy) == 0 && target.allegiance != user.allegiance)
|
||||
{
|
||||
error?.SetTextId(32518);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill must be used on an enemy and target is an ally
|
||||
if ((mainTarget & ValidTarget.EnemyOnly) != 0 && target.allegiance == user.allegiance)
|
||||
{
|
||||
error?.SetTextId(32519);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill can't be used on party members and target is a party member
|
||||
if ((mainTarget & ValidTarget.Party) == 0 && target.currentParty == user.currentParty)
|
||||
{
|
||||
error?.SetTextId(32548);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill must be used on party members and target is not a party member
|
||||
if ((mainTarget & ValidTarget.PartyOnly) != 0 && target.currentParty != user.currentParty)
|
||||
{
|
||||
error?.SetTextId(32547);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill can't be used on NPCs and target is an npc
|
||||
if ((mainTarget & ValidTarget.NPC) == 0 && target.isStatic)
|
||||
{
|
||||
error?.SetTextId(32547);
|
||||
return false;
|
||||
}
|
||||
|
||||
//This skill must be used on NPCs and target is not an npc
|
||||
if ((mainTarget & ValidTarget.NPCOnly) != 0 && !target.isStatic)
|
||||
{
|
||||
error?.SetTextId(32547);
|
||||
return false;
|
||||
}
|
||||
|
||||
// todo: why is player always zoning?
|
||||
// cant target if zoning
|
||||
if (target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
{
|
||||
user.aiContainer.ChangeTarget(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.zone != user.zone)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public ushort CalculateMpCost(Character user)
|
||||
{
|
||||
// todo: use precalculated costs instead
|
||||
var level = user.GetLevel();
|
||||
ushort cost = 0;
|
||||
if (level <= 10)
|
||||
cost = (ushort)(100 + level * 10);
|
||||
else if (level <= 20)
|
||||
cost = (ushort)(200 + (level - 10) * 20);
|
||||
else if (level <= 30)
|
||||
cost = (ushort)(400 + (level - 20) * 40);
|
||||
else if (level <= 40)
|
||||
cost = (ushort)(800 + (level - 30) * 70);
|
||||
else if (level <= 50)
|
||||
cost = (ushort)(1500 + (level - 40) * 130);
|
||||
else if (level <= 60)
|
||||
cost = (ushort)(2800 + (level - 50) * 200);
|
||||
else if (level <= 70)
|
||||
cost = (ushort)(4800 + (level - 60) * 320);
|
||||
else
|
||||
cost = (ushort)(8000 + (level - 70) * 500);
|
||||
|
||||
//scale the mpcost by level
|
||||
cost = (ushort)Math.Ceiling((cost * mpCost * 0.001));
|
||||
|
||||
//if user is player, check if spell is a part of combo
|
||||
if (user is Player)
|
||||
{
|
||||
var player = user as Player;
|
||||
if (player.playerWork.comboNextCommandId[0] == id || player.playerWork.comboNextCommandId[1] == id)
|
||||
cost = (ushort)Math.Ceiling(cost * (1 - player.playerWork.comboCostBonusRate));
|
||||
}
|
||||
|
||||
return mpCost != 0 ? cost : (ushort)0;
|
||||
}
|
||||
|
||||
//Calculate TP cost taking into considerating the combo bonus rate for players
|
||||
//Should this set tpCost or should it be called like CalculateMp where it gets calculated each time?
|
||||
//Might cause issues with the delay between starting and finishing a WS
|
||||
public short CalculateTpCost(Character user)
|
||||
{
|
||||
short tp = tpCost;
|
||||
//Calculate tp cost
|
||||
if (user is Player)
|
||||
{
|
||||
var player = user as Player;
|
||||
if (player.playerWork.comboNextCommandId[0] == id || player.playerWork.comboNextCommandId[1] == id)
|
||||
tp = (short)Math.Ceiling((float)tpCost * (1 - player.playerWork.comboCostBonusRate));
|
||||
}
|
||||
|
||||
return tp;
|
||||
}
|
||||
|
||||
public List<Character> GetTargets()
|
||||
{
|
||||
return targetFind?.GetTargets<Character>();
|
||||
}
|
||||
|
||||
public ushort GetCommandType()
|
||||
{
|
||||
return (ushort) commandType;
|
||||
}
|
||||
|
||||
public ushort GetActionType()
|
||||
{
|
||||
return (ushort) actionType;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.ai
|
||||
{
|
||||
class BattleTrait
|
||||
{
|
||||
public ushort id;
|
||||
public string name;
|
||||
public byte job;
|
||||
public byte level;
|
||||
public uint modifier;
|
||||
public int bonus;
|
||||
|
||||
public BattleTrait(ushort id, string name, byte job, byte level, uint modifier, int bonus)
|
||||
{
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.job = job;
|
||||
this.level = level;
|
||||
this.modifier = modifier;
|
||||
this.bonus = bonus;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.Collections.Generic;
|
||||
using Meteor.Map.Actors;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
// todo: actually implement enmity properly
|
||||
class HateEntry
|
||||
{
|
||||
public Character actor;
|
||||
public uint cumulativeEnmity;
|
||||
public uint volatileEnmity;
|
||||
public bool isActive;
|
||||
|
||||
public HateEntry(Character actor, uint cumulativeEnmity = 0, uint volatileEnmity = 0, bool isActive = false)
|
||||
{
|
||||
this.actor = actor;
|
||||
this.cumulativeEnmity = cumulativeEnmity;
|
||||
this.volatileEnmity = volatileEnmity;
|
||||
this.isActive = isActive;
|
||||
}
|
||||
}
|
||||
|
||||
class HateContainer
|
||||
{
|
||||
private Dictionary<Character, HateEntry> hateList;
|
||||
private Character owner;
|
||||
|
||||
public HateContainer(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.hateList = new Dictionary<Character, HateEntry>();
|
||||
}
|
||||
|
||||
public void AddBaseHate(Character target)
|
||||
{
|
||||
if (!HasHateForTarget(target))
|
||||
hateList.Add(target, new HateEntry(target, 1, 0, true));
|
||||
}
|
||||
|
||||
public void UpdateHate(Character target, int damage)
|
||||
{
|
||||
AddBaseHate(target);
|
||||
//hateList[target].volatileEnmity += (uint)damage;
|
||||
hateList[target].cumulativeEnmity += (uint)damage;
|
||||
}
|
||||
|
||||
public void ClearHate(Character target = null)
|
||||
{
|
||||
if (target != null)
|
||||
hateList.Remove(target);
|
||||
else
|
||||
hateList.Clear();
|
||||
}
|
||||
|
||||
private void UpdateHate(HateEntry entry)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public Dictionary<Character, HateEntry> GetHateList()
|
||||
{
|
||||
// todo: return unmodifiable collection?
|
||||
return hateList;
|
||||
}
|
||||
|
||||
public bool HasHateForTarget(Character target)
|
||||
{
|
||||
return hateList.ContainsKey(target);
|
||||
}
|
||||
|
||||
public Character GetMostHatedTarget()
|
||||
{
|
||||
uint enmity = 0;
|
||||
Character target = null;
|
||||
|
||||
foreach(var entry in hateList.Values)
|
||||
{
|
||||
if (entry.cumulativeEnmity > enmity && entry.isActive)
|
||||
{
|
||||
enmity = entry.cumulativeEnmity;
|
||||
target = entry.actor;
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,723 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
using System;
|
||||
using MoonSharp.Interpreter;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
enum StatusEffectId : uint
|
||||
{
|
||||
RageofHalone = 221021,
|
||||
|
||||
Quick = 223001,
|
||||
Haste = 223002,
|
||||
Slow = 223003,
|
||||
Petrification = 223004,
|
||||
Paralysis = 223005,
|
||||
Silence = 223006,
|
||||
Blind = 223007,
|
||||
Mute = 223008,
|
||||
Slowcast = 223009,
|
||||
Glare = 223010,
|
||||
Poison = 223011,
|
||||
Transfixion = 223012,
|
||||
Pacification = 223013,
|
||||
Amnesia = 223014,
|
||||
Stun = 223015,
|
||||
Daze = 223016,
|
||||
ExposedFront = 223017,
|
||||
ExposedRight = 223018,
|
||||
ExposedRear = 223019,
|
||||
ExposedLeft = 223020,
|
||||
Incapacitation = 223021,
|
||||
Incapacitation2 = 223022,
|
||||
Incapacitation3 = 223023,
|
||||
Incapacitation4 = 223024,
|
||||
Incapacitation5 = 223025,
|
||||
Incapacitation6 = 223026,
|
||||
Incapacitation7 = 223027,
|
||||
Incapacitation8 = 223028,
|
||||
HPBoost = 223029,
|
||||
HPPenalty = 223030,
|
||||
MPBoost = 223031,
|
||||
MPPenalty = 223032,
|
||||
AttackUp = 223033,
|
||||
AttackDown = 223034,
|
||||
AccuracyUp = 223035,
|
||||
AccuracyDown = 223036,
|
||||
DefenseUp = 223037,
|
||||
DefenseDown = 223038,
|
||||
EvasionUp = 223039,
|
||||
EvasionDown = 223040,
|
||||
MagicPotencyUp = 223041,
|
||||
MagicPotencyDown = 223042,
|
||||
MagicAccuracyUp = 223043,
|
||||
MagicAccuracyDown = 223044,
|
||||
MagicDefenseUp = 223045,
|
||||
MagicDefenseDown = 223046,
|
||||
MagicResistanceUp = 223047,
|
||||
MagicResistanceDown = 223048,
|
||||
CombatFinesse = 223049,
|
||||
CombatHindrance = 223050,
|
||||
MagicFinesse = 223051,
|
||||
MagicHindrance = 223052,
|
||||
CombatResilience = 223053,
|
||||
CombatVulnerability = 223054,
|
||||
MagicVulnerability = 223055,
|
||||
MagicResilience = 223056,
|
||||
Inhibited = 223057,
|
||||
AegisBoon = 223058,
|
||||
Deflection = 223059,
|
||||
Outmaneuver = 223060,
|
||||
Provoked = 223061,
|
||||
Sentinel = 223062,
|
||||
Cover = 223063,
|
||||
Rampart = 223064,
|
||||
StillPrecision = 223065,
|
||||
Cadence = 223066,
|
||||
DiscerningEye = 223067,
|
||||
TemperedWill = 223068,
|
||||
Obsess = 223069,
|
||||
Ambidexterity = 223070,
|
||||
BattleCalm = 223071,
|
||||
MasterofArms = 223072,
|
||||
Taunted = 223073,
|
||||
Blindside = 223074,
|
||||
Featherfoot = 223075,
|
||||
PresenceofMind = 223076,
|
||||
CoeurlStep = 223077,
|
||||
EnduringMarch = 223078,
|
||||
MurderousIntent = 223079,
|
||||
Entrench = 223080,
|
||||
Bloodbath = 223081,
|
||||
Retaliation = 223082,
|
||||
Foresight = 223083,
|
||||
Defender = 223084,
|
||||
Rampage = 223085, //old effect
|
||||
Enraged = 223086,
|
||||
Warmonger = 223087,
|
||||
Disorientx1 = 223088,
|
||||
Disorientx2 = 223089,
|
||||
Disorientx3 = 223090,
|
||||
KeenFlurry = 223091,
|
||||
ComradeinArms = 223092,
|
||||
Ferocity = 223093,
|
||||
Invigorate = 223094,
|
||||
LineofFire = 223095,
|
||||
Jump = 223096,
|
||||
Collusion = 223097,
|
||||
Diversion = 223098,
|
||||
SpeedSurge = 223099,
|
||||
LifeSurge = 223100,
|
||||
SpeedSap = 223101,
|
||||
LifeSap = 223102,
|
||||
Farshot = 223103,
|
||||
QuellingStrike = 223104,
|
||||
RagingStrike = 223105, //old effect
|
||||
HawksEye = 223106,
|
||||
SubtleRelease = 223107,
|
||||
Decoy = 223108, //Untraited
|
||||
Profundity = 223109,
|
||||
TranceChant = 223110,
|
||||
RoamingSoul = 223111,
|
||||
Purge = 223112,
|
||||
Spiritsong = 223113,
|
||||
Resonance = 223114, //Old Resonance? Both have the same icons and description
|
||||
Soughspeak = 223115,
|
||||
PresenceofMind2 = 223116,
|
||||
SanguineRite = 223117, //old effect
|
||||
PunishingBarbs = 223118,
|
||||
DarkSeal = 223119, //old effect
|
||||
Emulate = 223120,
|
||||
ParadigmShift = 223121,
|
||||
ConcussiveBlowx1 = 223123,
|
||||
ConcussiveBlowx2 = 223124,
|
||||
ConcussiveBlowx3 = 223125,
|
||||
SkullSunder = 223126,
|
||||
Bloodletter = 223127, //comboed effect
|
||||
Levinbolt = 223128,
|
||||
Protect = 223129, //untraited protect
|
||||
Shell = 223130, //old shell
|
||||
Reraise = 223131,
|
||||
ShockSpikes = 223132,
|
||||
Stoneskin = 223133,
|
||||
Scourge = 223134,
|
||||
Bio = 223135,
|
||||
Dia = 223136,
|
||||
Banish = 223137,
|
||||
StygianSpikes = 223138,
|
||||
ATKAbsorbed = 223139,
|
||||
DEFAbsorbed = 223140,
|
||||
ACCAbsorbed = 223141,
|
||||
EVAAbsorbed = 223142,
|
||||
AbsorbATK = 223143,
|
||||
AbsorbDEF = 223144,
|
||||
AbsorbACC = 223145,
|
||||
AbsorbEVA = 223146,
|
||||
SoulWard = 223147,
|
||||
Burn = 223148,
|
||||
Frost = 223149,
|
||||
Shock = 223150,
|
||||
Drown = 223151,
|
||||
Choke = 223152,
|
||||
Rasp = 223153,
|
||||
Flare = 223154,
|
||||
Freeze = 223155,
|
||||
Burst = 223156,
|
||||
Flood = 223157,
|
||||
Tornado = 223158,
|
||||
Quake = 223159,
|
||||
Berserk = 223160,
|
||||
RegimenofRuin = 223161,
|
||||
RegimenofTrauma = 223162,
|
||||
RegimenofDespair = 223163,
|
||||
RegimenofConstraint = 223164,
|
||||
Weakness = 223165,
|
||||
Scavenge = 223166,
|
||||
Fastcast = 223167,
|
||||
MidnightHowl = 223168,
|
||||
Outlast = 223169,
|
||||
Steadfast = 223170,
|
||||
DoubleNock = 223171,
|
||||
TripleNock = 223172,
|
||||
Covered = 223173,
|
||||
PerfectDodge = 223174,
|
||||
ExpertMining = 223175,
|
||||
ExpertLogging = 223176,
|
||||
ExpertHarvesting = 223177,
|
||||
ExpertFishing = 223178,
|
||||
ExpertSpearfishing = 223179,
|
||||
Regen = 223180,
|
||||
Refresh = 223181,
|
||||
Regain = 223182,
|
||||
TPBleed = 223183,
|
||||
Empowered = 223184,
|
||||
Imperiled = 223185,
|
||||
Adept = 223186,
|
||||
Inept = 223187,
|
||||
Quick2 = 223188,
|
||||
Quick3 = 223189,
|
||||
WristFlick = 223190,
|
||||
Glossolalia = 223191,
|
||||
SonorousBlast = 223192,
|
||||
Comradery = 223193,
|
||||
StrengthinNumbers = 223194,
|
||||
|
||||
BrinkofDeath = 223197,
|
||||
CraftersGrace = 223198,
|
||||
GatherersGrace = 223199,
|
||||
Rebirth = 223200,
|
||||
Stealth = 223201,
|
||||
StealthII = 223202,
|
||||
StealthIII = 223203,
|
||||
StealthIV = 223204,
|
||||
Combo = 223205,
|
||||
GoringBlade = 223206,
|
||||
Berserk2 = 223207, //new effect
|
||||
Rampage2 = 223208, //new effect
|
||||
FistsofFire = 223209,
|
||||
FistsofEarth = 223210,
|
||||
FistsofWind = 223211,
|
||||
PowerSurgeI = 223212,
|
||||
PowerSurgeII = 223213,
|
||||
PowerSurgeIII = 223214,
|
||||
LifeSurgeI = 223215,
|
||||
LifeSurgeII = 223216,
|
||||
LifeSurgeIII = 223217,
|
||||
DreadSpike = 223218,
|
||||
BloodforBlood = 223219,
|
||||
Barrage = 223220,
|
||||
RagingStrike2 = 223221,
|
||||
|
||||
Swiftsong = 223224,
|
||||
SacredPrism = 223225,
|
||||
ShroudofSaints = 223226,
|
||||
ClericStance = 223227,
|
||||
BlissfulMind = 223228,
|
||||
DarkSeal2 = 223229, //new effect
|
||||
Resonance2 = 223230,
|
||||
Excruciate = 223231,
|
||||
Necrogenesis = 223232,
|
||||
Parsimony = 223233,
|
||||
SanguineRite2 = 223234, //untraited effect
|
||||
Aero = 223235,
|
||||
Outmaneuver2 = 223236,
|
||||
Blindside2 = 223237,
|
||||
Decoy2 = 223238, //Traited
|
||||
Protect2 = 223239, //Traited
|
||||
SanguineRite3 = 223240, //Traited
|
||||
Bloodletter2 = 223241, //uncomboed effect
|
||||
FullyBlissfulMind = 223242,
|
||||
MagicEvasionDown = 223243,
|
||||
HundredFists = 223244,
|
||||
SpinningHeel = 223245,
|
||||
DivineVeil = 223248,
|
||||
HallowedGround = 223249,
|
||||
Vengeance = 223250,
|
||||
Antagonize = 223251,
|
||||
MightyStrikes = 223252,
|
||||
BattleVoice = 223253,
|
||||
BalladofMagi = 223254,
|
||||
PaeonofWar = 223255,
|
||||
MinuetofRigor = 223256,
|
||||
GoldLung = 223258,
|
||||
Goldbile = 223259,
|
||||
AurumVeil = 223260,
|
||||
AurumVeilII = 223261,
|
||||
Flare2 = 223262,
|
||||
Resting = 223263,
|
||||
DivineRegen = 223264,
|
||||
DefenseAndEvasionUp = 223265,
|
||||
MagicDefenseAndEvasionUp = 223266,
|
||||
AttackUp2 = 223267,
|
||||
MagicPotencyUp2 = 223268,
|
||||
DefenseAndEvasionDown = 223269,
|
||||
MagicDefenseAndEvasionDown = 223270,
|
||||
Poison2 = 223271,
|
||||
DeepBurn = 223272,
|
||||
LunarCurtain = 223273,
|
||||
DefenseUp2 = 223274,
|
||||
AttackDown2 = 223275,
|
||||
Sanction = 223992,
|
||||
IntactPodlingToting = 223993,
|
||||
RedRidingHooded = 223994,
|
||||
Medicated = 223998,
|
||||
WellFed = 223999,
|
||||
|
||||
Sleep = 228001,
|
||||
Bind = 228011,
|
||||
Fixation = 228012,
|
||||
Bind2 = 228013,
|
||||
Heavy = 228021,
|
||||
Charm = 228031,
|
||||
Flee = 228041,
|
||||
Doom = 228051,
|
||||
SynthesisSupport = 230001,
|
||||
WoodyardAccess = 230002,
|
||||
SmithsForgeAccess = 230003,
|
||||
ArmorersForgeAccess = 230004,
|
||||
GemmaryAccess = 230005,
|
||||
TanneryAccess = 230006,
|
||||
ClothshopAccess = 230007,
|
||||
LaboratoryAccess = 230008,
|
||||
CookeryAccess = 230009,
|
||||
MinersSupport = 230010,
|
||||
BotanistsSupport = 230011,
|
||||
FishersSupport = 230012,
|
||||
GearChange = 230013,
|
||||
GearDamage = 230014,
|
||||
HeavyGearDamage = 230015,
|
||||
Lamed = 230016,
|
||||
Lamed2 = 230017,
|
||||
Lamed3 = 230018,
|
||||
Poison3 = 231002,
|
||||
Envenom = 231003,
|
||||
Berserk4 = 231004,
|
||||
GuardiansAspect = 253002,
|
||||
|
||||
|
||||
// custom effects here
|
||||
// status for having procs fall off
|
||||
EvadeProc = 300000,
|
||||
BlockProc = 300001,
|
||||
ParryProc = 300002,
|
||||
MissProc = 300003,
|
||||
EXPChain = 300004
|
||||
}
|
||||
|
||||
[Flags]
|
||||
enum StatusEffectFlags : uint
|
||||
{
|
||||
None = 0,
|
||||
|
||||
//Loss flags - Do we need loseonattacking/caststart? Could just be done with activate flags
|
||||
LoseOnDeath = 1 << 0, // effects removed on death
|
||||
LoseOnZoning = 1 << 1, // effects removed on zoning
|
||||
LoseOnEsuna = 1 << 2, // effects which can be removed with esuna (debuffs)
|
||||
LoseOnDispel = 1 << 3, // some buffs which player might be able to dispel from mob
|
||||
LoseOnLogout = 1 << 4, // effects removed on logging out
|
||||
LoseOnAttacking = 1 << 5, // effects removed when owner attacks another entity
|
||||
LoseOnCastStart = 1 << 6, // effects removed when owner starts casting
|
||||
LoseOnAggro = 1 << 7, // effects removed when owner gains enmity (swiftsong)
|
||||
LoseOnClassChange = 1 << 8, //Effect falls off whhen changing class
|
||||
|
||||
//Activate flags
|
||||
ActivateOnCastStart = 1 << 9, //Activates when a cast starts.
|
||||
ActivateOnCommandStart = 1 << 10, //Activates when a command is used, before iterating over targets. Used for things like power surge, excruciate.
|
||||
ActivateOnCommandFinish = 1 << 11, //Activates when the command is finished, after all targets have been iterated over. Used for things like Excruciate and Resonance falling off.
|
||||
ActivateOnPreactionTarget = 1 << 12, //Activates after initial rates are calculated for an action against owner
|
||||
ActivateOnPreactionCaster = 1 << 13, //Activates after initial rates are calculated for an action by owner
|
||||
ActivateOnDamageTaken = 1 << 14,
|
||||
ActivateOnHealed = 1 << 15,
|
||||
|
||||
//Should these be rolled into DamageTaken?
|
||||
ActivateOnMiss = 1 << 16, //Activates when owner misses
|
||||
ActivateOnEvade = 1 << 17, //Activates when owner evades
|
||||
ActivateOnParry = 1 << 18, //Activates when owner parries
|
||||
ActivateOnBlock = 1 << 19, //Activates when owner evades
|
||||
ActivateOnHit = 1 << 20, //Activates when owner hits
|
||||
ActivateOnCrit = 1 << 21, //Activates when owner crits
|
||||
|
||||
//Prevent flags. Sleep/stun/petrify/etc combine these
|
||||
PreventSpell = 1 << 22, // effects which prevent using spells, such as silence
|
||||
PreventWeaponSkill = 1 << 23, // effects which prevent using weaponskills, such as pacification
|
||||
PreventAbility = 1 << 24, // effects which prevent using abilities, such as amnesia
|
||||
PreventAttack = 1 << 25, // effects which prevent basic attacks
|
||||
PreventMovement = 1 << 26, // effects which prevent movement such as bind, still allows turning in place
|
||||
PreventTurn = 1 << 27, // effects which prevent turning, such as stun
|
||||
PreventUntarget = 1 << 28, // effects which prevent changing targets, such as fixation
|
||||
Stance = 1 << 29 // effects that do not have a timer
|
||||
}
|
||||
|
||||
enum StatusEffectOverwrite : byte
|
||||
{
|
||||
None,
|
||||
Always,
|
||||
GreaterOrEqualTo,
|
||||
GreaterOnly,
|
||||
}
|
||||
|
||||
class StatusEffect
|
||||
{
|
||||
// todo: probably use get;set;
|
||||
private Character owner;
|
||||
private Character source;
|
||||
private StatusEffectId id;
|
||||
private string name; // name of this effect
|
||||
private DateTime startTime; // when was this effect added
|
||||
private DateTime endTime; // when this status falls off
|
||||
private DateTime lastTick; // when did this effect last tick
|
||||
private uint duration; // how long should this effect last in seconds
|
||||
private uint tickMs; // how often should this effect proc
|
||||
private double magnitude; // a value specified by scripter which is guaranteed to be used by all effects
|
||||
private byte tier; // same effect with higher tier overwrites this
|
||||
private double extra; // optional value
|
||||
private StatusEffectFlags flags; // death/erase/dispel etc
|
||||
private StatusEffectOverwrite overwrite; // how to handle adding an effect with same id (see StatusEfectOverwrite)
|
||||
private bool silentOnGain = false; //Whether a message is sent when the status is gained
|
||||
private bool silentOnLoss = false; //Whether a message is sent when the status is lost
|
||||
private bool hidden = false; //Whether this status is shown. Used for things that aren't really status effects like exp chains and procs
|
||||
private ushort statusGainTextId = 30328; //The text id used when the status is gained. 30328: [Command] grants you the effect of [status] (Used for buffs)
|
||||
private ushort statusLossTextId = 30331; //The text id used when the status effect falls off when its time runs out. 30331: You are no longer under the effect of [status] (Used for buffs)
|
||||
public LuaScript script;
|
||||
|
||||
HitEffect animationEffect;
|
||||
|
||||
public StatusEffect(Character owner, uint id, double magnitude, uint tickMs, uint duration, byte tier = 0)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.source = owner;
|
||||
this.id = (StatusEffectId)id;
|
||||
this.magnitude = magnitude;
|
||||
this.tickMs = tickMs;
|
||||
this.duration = duration;
|
||||
this.tier = tier;
|
||||
|
||||
this.startTime = DateTime.Now;
|
||||
this.lastTick = startTime;
|
||||
}
|
||||
|
||||
public StatusEffect(Character owner, StatusEffect effect)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.source = owner;
|
||||
this.id = effect.id;
|
||||
this.magnitude = effect.magnitude;
|
||||
this.tickMs = effect.tickMs;
|
||||
this.duration = effect.duration;
|
||||
this.tier = effect.tier;
|
||||
this.startTime = effect.startTime;
|
||||
this.lastTick = effect.lastTick;
|
||||
|
||||
this.name = effect.name;
|
||||
this.flags = effect.flags;
|
||||
this.overwrite = effect.overwrite;
|
||||
this.statusGainTextId = effect.statusGainTextId;
|
||||
this.statusLossTextId = effect.statusLossTextId;
|
||||
this.extra = effect.extra;
|
||||
this.script = effect.script;
|
||||
this.silentOnGain = effect.silentOnGain;
|
||||
this.silentOnLoss = effect.silentOnLoss;
|
||||
this.hidden = effect.hidden;
|
||||
}
|
||||
|
||||
public StatusEffect(uint id, string name, uint flags, uint overwrite, uint tickMs, bool hidden, bool silentOnGain, bool silentOnLoss, ushort statusGainTextId, ushort statusLossTextId)
|
||||
{
|
||||
this.id = (StatusEffectId)id;
|
||||
this.name = name;
|
||||
this.flags = (StatusEffectFlags)flags;
|
||||
this.overwrite = (StatusEffectOverwrite)overwrite;
|
||||
this.tickMs = tickMs;
|
||||
this.hidden = hidden;
|
||||
this.silentOnGain = silentOnGain;
|
||||
this.silentOnLoss = silentOnLoss;
|
||||
this.statusGainTextId = statusGainTextId;
|
||||
this.statusLossTextId = statusLossTextId;
|
||||
}
|
||||
|
||||
// return true when duration has elapsed
|
||||
public bool Update(DateTime tick, CommandResultContainer resultContainer = null)
|
||||
{
|
||||
if (tickMs != 0 && (tick - lastTick).TotalMilliseconds >= tickMs)
|
||||
{
|
||||
lastTick = tick;
|
||||
if (LuaEngine.CallLuaStatusEffectFunction(this.owner, this, "onTick", this.owner, this, resultContainer) > 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (duration >= 0 && tick >= endTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int CallLuaFunction(Character chara, string functionName, params object[] args)
|
||||
{
|
||||
|
||||
DynValue res = new DynValue();
|
||||
|
||||
return lua.LuaEngine.CallLuaStatusEffectFunction(chara, this, functionName, args);
|
||||
if (!script.Globals.Get(functionName).IsNil())
|
||||
{
|
||||
res = script.Call(script.Globals.Get(functionName), args);
|
||||
if (res != null)
|
||||
return (int)res.Number;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Character GetOwner()
|
||||
{
|
||||
return owner;
|
||||
}
|
||||
|
||||
public Character GetSource()
|
||||
{
|
||||
return source ?? owner;
|
||||
}
|
||||
|
||||
public uint GetStatusEffectId()
|
||||
{
|
||||
return (uint)id;
|
||||
}
|
||||
|
||||
public ushort GetStatusId()
|
||||
{
|
||||
return (ushort)(id - 200000);
|
||||
}
|
||||
|
||||
public DateTime GetStartTime()
|
||||
{
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public DateTime GetEndTime()
|
||||
{
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
public uint GetDuration()
|
||||
{
|
||||
return duration;
|
||||
}
|
||||
|
||||
public uint GetTickMs()
|
||||
{
|
||||
return tickMs;
|
||||
}
|
||||
|
||||
public double GetMagnitude()
|
||||
{
|
||||
return magnitude;
|
||||
}
|
||||
|
||||
public byte GetTier()
|
||||
{
|
||||
return tier;
|
||||
}
|
||||
|
||||
public double GetExtra()
|
||||
{
|
||||
return extra;
|
||||
}
|
||||
|
||||
public uint GetFlags()
|
||||
{
|
||||
return (uint)flags;
|
||||
}
|
||||
|
||||
public byte GetOverwritable()
|
||||
{
|
||||
return (byte)overwrite;
|
||||
}
|
||||
|
||||
public bool GetSilentOnGain()
|
||||
{
|
||||
return silentOnGain;
|
||||
}
|
||||
|
||||
public bool GetSilentOnLoss()
|
||||
{
|
||||
return silentOnLoss;
|
||||
}
|
||||
|
||||
public bool GetHidden()
|
||||
{
|
||||
return hidden;
|
||||
}
|
||||
|
||||
public ushort GetStatusGainTextId()
|
||||
{
|
||||
return statusGainTextId;
|
||||
}
|
||||
|
||||
public ushort GetStatusLossTextId()
|
||||
{
|
||||
return statusLossTextId;
|
||||
}
|
||||
|
||||
public void SetStartTime(DateTime time)
|
||||
{
|
||||
this.startTime = time;
|
||||
this.lastTick = time;
|
||||
}
|
||||
|
||||
public void SetEndTime(DateTime time)
|
||||
{
|
||||
//If it's a stance, just set endtime to highest number possible for XIV
|
||||
if ((flags & StatusEffectFlags.Stance) != 0)
|
||||
{
|
||||
endTime = Utils.UnixTimeStampToDateTime(4294967295);
|
||||
}
|
||||
else
|
||||
{
|
||||
endTime = time;
|
||||
}
|
||||
}
|
||||
|
||||
//Refresh the status, updating the end time based on the duration of the status and broadcasts the new time
|
||||
public void RefreshTime()
|
||||
{
|
||||
endTime = DateTime.Now.AddSeconds(GetDuration());
|
||||
int index = Array.IndexOf(owner.charaWork.status, GetStatusId());
|
||||
|
||||
if (index >= 0)
|
||||
owner.statusEffects.SetTimeAtIndex(index, (uint) Utils.UnixTimeStampUTC(endTime));
|
||||
}
|
||||
|
||||
public void SetOwner(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public void SetSource(Character source)
|
||||
{
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public void SetName(string name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void SetMagnitude(double magnitude)
|
||||
{
|
||||
this.magnitude = magnitude;
|
||||
}
|
||||
|
||||
public void SetDuration(uint duration)
|
||||
{
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
public void SetTickMs(uint tickMs)
|
||||
{
|
||||
this.tickMs = tickMs;
|
||||
}
|
||||
|
||||
public void SetTier(byte tier)
|
||||
{
|
||||
this.tier = tier;
|
||||
}
|
||||
|
||||
public void SetExtra(double val)
|
||||
{
|
||||
this.extra = val;
|
||||
}
|
||||
|
||||
public void SetFlags(uint flags)
|
||||
{
|
||||
this.flags = (StatusEffectFlags)flags;
|
||||
}
|
||||
|
||||
public void SetOverwritable(byte overwrite)
|
||||
{
|
||||
this.overwrite = (StatusEffectOverwrite)overwrite;
|
||||
}
|
||||
|
||||
public void SetSilentOnGain(bool silent)
|
||||
{
|
||||
this.silentOnGain = silent;
|
||||
}
|
||||
|
||||
public void SetSilentOnLoss(bool silent)
|
||||
{
|
||||
this.silentOnLoss = silent;
|
||||
}
|
||||
|
||||
public void SetHidden(bool hidden)
|
||||
{
|
||||
this.hidden = hidden;
|
||||
}
|
||||
|
||||
public void SetStatusGainTextId(ushort textId)
|
||||
{
|
||||
this.statusGainTextId = textId;
|
||||
}
|
||||
|
||||
public void SetStatusLossTextId(ushort textId)
|
||||
{
|
||||
this.statusLossTextId = textId;
|
||||
}
|
||||
|
||||
|
||||
public void SetAnimation(uint hitEffect)
|
||||
{
|
||||
animationEffect = (HitEffect)hitEffect;
|
||||
}
|
||||
|
||||
public uint GetAnimation()
|
||||
{
|
||||
return (uint)animationEffect;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,445 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 System.Linq;
|
||||
using Meteor.Common;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
using Meteor.Map.utils;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
class StatusEffectContainer
|
||||
{
|
||||
private Character owner;
|
||||
private readonly Dictionary<uint, StatusEffect> effects;
|
||||
public static readonly int MAX_EFFECTS = 20;
|
||||
private bool sendUpdate = false;
|
||||
private DateTime lastTick;// Do all effects tick at the same time like regen?
|
||||
private List<SubPacket> statusSubpackets;
|
||||
private ActorPropertyPacketUtil statusTimerPropPacketUtil;
|
||||
|
||||
public StatusEffectContainer(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.effects = new Dictionary<uint, StatusEffect>();
|
||||
statusSubpackets = new List<SubPacket>();
|
||||
statusTimerPropPacketUtil = new ActorPropertyPacketUtil("charawork/Status", owner);
|
||||
}
|
||||
|
||||
public void Update(DateTime tick)
|
||||
{
|
||||
CommandResultContainer resultContainer = new CommandResultContainer();
|
||||
|
||||
//Regen/Refresh/Regain effects tick every 3 seconds
|
||||
if ((DateTime.Now - lastTick).Seconds >= 3)
|
||||
{
|
||||
RegenTick(tick, resultContainer);
|
||||
lastTick = DateTime.Now;
|
||||
}
|
||||
|
||||
// list of effects to remove
|
||||
var removeEffects = new List<StatusEffect>();
|
||||
var effectsList = effects.Values;
|
||||
for (int i = effectsList.Count - 1; i >= 0; i--)
|
||||
{
|
||||
// effect's update function returns true if effect has completed
|
||||
if (effectsList.ElementAt(i).Update(tick, resultContainer))
|
||||
removeEffects.Add(effectsList.ElementAt(i));
|
||||
}
|
||||
|
||||
// remove effects from this list
|
||||
foreach (var effect in removeEffects)
|
||||
{
|
||||
RemoveStatusEffect(effect, resultContainer, effect.GetStatusLossTextId());
|
||||
}
|
||||
|
||||
resultContainer.CombineLists();
|
||||
|
||||
if (resultContainer.GetList().Count > 0)
|
||||
{
|
||||
owner.DoBattleAction(0, 0x7c000062, resultContainer.GetList());
|
||||
}
|
||||
}
|
||||
|
||||
//regen/refresh/regain
|
||||
public void RegenTick(DateTime tick, CommandResultContainer resultContainer)
|
||||
{
|
||||
ushort dotTick = (ushort) owner.GetMod(Modifier.RegenDown);
|
||||
ushort regenTick = (ushort) owner.GetMod(Modifier.Regen);
|
||||
ushort refreshtick = (ushort) owner.GetMod(Modifier.Refresh);
|
||||
short regainTick = (short) owner.GetMod(Modifier.Regain);
|
||||
|
||||
//DoTs tick before regen and the full dot damage is displayed, even if some or all of it is nullified by regen. Only effects like stoneskin actually alter the number shown
|
||||
if (dotTick > 0)
|
||||
{
|
||||
//Unsure why 10105 is the textId used
|
||||
//Also unsure why magicshield is used
|
||||
CommandResult action = new CommandResult(owner.actorId, 10105, (uint)(HitEffect.MagicEffectType | HitEffect.MagicShield | HitEffect.NoResist), dotTick);
|
||||
utils.BattleUtils.HandleStoneskin(owner, action);
|
||||
// todo: figure out how to make red numbers appear for enemies getting hurt by dots
|
||||
resultContainer.AddAction(action);
|
||||
owner.DelHP(action.amount, resultContainer);
|
||||
}
|
||||
|
||||
//DoTs are the only effect to show numbers, so that doesnt need to be handled for these
|
||||
if (regenTick != 0)
|
||||
owner.AddHP(regenTick);
|
||||
|
||||
if (refreshtick != 0)
|
||||
owner.AddMP(refreshtick);
|
||||
|
||||
if (regainTick != 0)
|
||||
owner.AddTP(regainTick);
|
||||
}
|
||||
|
||||
public bool HasStatusEffect(uint id)
|
||||
{
|
||||
return effects.ContainsKey(id);
|
||||
}
|
||||
|
||||
public bool HasStatusEffect(StatusEffectId id)
|
||||
{
|
||||
return effects.ContainsKey((uint)id);
|
||||
}
|
||||
|
||||
public bool AddStatusEffect(uint id, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328)
|
||||
{
|
||||
var se = Server.GetWorldManager().GetStatusEffect(id);
|
||||
|
||||
if (se != null)
|
||||
{
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
|
||||
return AddStatusEffect(se, owner, actionContainer, worldmasterTextId);
|
||||
}
|
||||
|
||||
public bool AddStatusEffect(uint id, byte tier, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328)
|
||||
{
|
||||
var se = Server.GetWorldManager().GetStatusEffect(id);
|
||||
|
||||
if (se != null)
|
||||
{
|
||||
se.SetTier(tier);
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
|
||||
return AddStatusEffect(se, owner, actionContainer, worldmasterTextId);
|
||||
}
|
||||
|
||||
public bool AddStatusEffect(uint id, byte tier, double magnitude, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328)
|
||||
{
|
||||
var se = Server.GetWorldManager().GetStatusEffect(id);
|
||||
|
||||
if (se != null)
|
||||
{
|
||||
se.SetMagnitude(magnitude);
|
||||
se.SetTier(tier);
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
|
||||
return AddStatusEffect(se, owner, actionContainer, worldmasterTextId);
|
||||
}
|
||||
|
||||
public bool AddStatusEffect(uint id, byte tier, double magnitude, uint duration, int tickMs, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328)
|
||||
{
|
||||
var se = Server.GetWorldManager().GetStatusEffect(id);
|
||||
|
||||
if (se != null)
|
||||
{
|
||||
se.SetDuration(duration);
|
||||
se.SetOwner(owner);
|
||||
worldmasterTextId = se.GetStatusGainTextId();
|
||||
}
|
||||
return AddStatusEffect(se ?? new StatusEffect(this.owner, id, magnitude, 3000, duration, tier), owner, actionContainer, worldmasterTextId);
|
||||
}
|
||||
|
||||
public bool AddStatusEffect(StatusEffect newEffect, Character source, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30328)
|
||||
{
|
||||
/*
|
||||
worldMasterTextId
|
||||
32001 [@2B([@IF($E4($EB(1),$EB(2)),you,[@IF($E9(7),[@SHEETEN(xtx/displayName,2,$E9(7),1,1)],$EB(2))])])] [@IF($E4($EB(1),$EB(2)),resist,resists)] the effect of [@SHEET(xtx/status,$E8(11),3)].
|
||||
32002 [@SHEET(xtx/status,$E8(11),3)] fails to take effect.
|
||||
*/
|
||||
|
||||
var effect = GetStatusEffectById(newEffect.GetStatusEffectId());
|
||||
|
||||
bool canOverwrite = false;
|
||||
if (effect != null)
|
||||
{
|
||||
var overwritable = effect.GetOverwritable();
|
||||
canOverwrite = (overwritable == (uint)StatusEffectOverwrite.Always) ||
|
||||
(overwritable == (uint)StatusEffectOverwrite.GreaterOnly && (effect.GetDuration() < newEffect.GetDuration() || effect.GetMagnitude() < newEffect.GetMagnitude())) ||
|
||||
(overwritable == (uint)StatusEffectOverwrite.GreaterOrEqualTo && (effect.GetDuration() <= newEffect.GetDuration() || effect.GetMagnitude() <= newEffect.GetMagnitude()));
|
||||
}
|
||||
|
||||
if (canOverwrite || effect == null)
|
||||
{
|
||||
// send packet to client with effect added message
|
||||
if (newEffect != null && !newEffect.GetSilentOnGain())
|
||||
{
|
||||
if (actionContainer != null)
|
||||
actionContainer.AddAction(new CommandResult(owner.actorId, worldmasterTextId, newEffect.GetStatusEffectId() | (uint)HitEffect.StatusEffectType));
|
||||
}
|
||||
|
||||
// wont send a message about losing effect here
|
||||
if (canOverwrite)
|
||||
effects.Remove(newEffect.GetStatusEffectId());
|
||||
|
||||
newEffect.SetStartTime(DateTime.Now);
|
||||
newEffect.SetEndTime(DateTime.Now.AddSeconds(newEffect.GetDuration()));
|
||||
newEffect.SetOwner(owner);
|
||||
|
||||
if (effects.Count < MAX_EFFECTS)
|
||||
{
|
||||
newEffect.CallLuaFunction(this.owner, "onGain", this.owner, newEffect, actionContainer);
|
||||
|
||||
effects.Add(newEffect.GetStatusEffectId(), newEffect);
|
||||
|
||||
if (!newEffect.GetHidden())
|
||||
{
|
||||
int index = 0;
|
||||
|
||||
//If effect is already in the list of statuses, get that index, otherwise find the first open index
|
||||
if (owner.charaWork.status.Contains(newEffect.GetStatusId()))
|
||||
index = Array.IndexOf(owner.charaWork.status, newEffect.GetStatusId());
|
||||
else
|
||||
index = Array.IndexOf(owner.charaWork.status, (ushort) 0);
|
||||
|
||||
SetStatusAtIndex(index, newEffect.GetStatusId());
|
||||
//Stance statuses need their time set to an extremely high number so their icon doesn't flash
|
||||
//Adding getduration with them doesn't work because it overflows
|
||||
uint time = (newEffect.GetFlags() & (uint) StatusEffectFlags.Stance) == 0 ? Utils.UnixTimeStampUTC(newEffect.GetEndTime()) : 0xFFFFFFFF;
|
||||
SetTimeAtIndex(index, time);
|
||||
}
|
||||
owner.RecalculateStats();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//playEffect determines whether the effect of the animation that's going to play with actionContainer is going to play on owner
|
||||
//Generally, for abilities removing an effect, this is true and for effects removing themselves it's false.
|
||||
public bool RemoveStatusEffect(StatusEffect effect, CommandResultContainer actionContainer = null, ushort worldmasterTextId = 30331, bool playEffect = true)
|
||||
{
|
||||
bool removedEffect = false;
|
||||
if (effect != null && effects.ContainsKey(effect.GetStatusEffectId()))
|
||||
{
|
||||
// send packet to client with effect remove message
|
||||
if (!effect.GetSilentOnLoss())
|
||||
{
|
||||
//Only send a message if we're using an actioncontainer and the effect normally sends a message when it's lost
|
||||
if (actionContainer != null)
|
||||
actionContainer.AddAction(new CommandResult(owner.actorId, worldmasterTextId, effect.GetStatusEffectId() | (playEffect ? 0 : (uint)HitEffect.StatusLossType)));
|
||||
}
|
||||
|
||||
//hidden effects not in charawork
|
||||
var index = Array.IndexOf(owner.charaWork.status, effect.GetStatusId());
|
||||
if (!effect.GetHidden() && index != -1)
|
||||
{
|
||||
SetStatusAtIndex(index, 0);
|
||||
SetTimeAtIndex(index, 0);
|
||||
}
|
||||
|
||||
// function onLose(actor, effect)
|
||||
effects.Remove(effect.GetStatusEffectId());
|
||||
effect.CallLuaFunction(owner, "onLose", owner, effect, actionContainer);
|
||||
owner.RecalculateStats();
|
||||
removedEffect = true;
|
||||
}
|
||||
|
||||
return removedEffect;
|
||||
}
|
||||
|
||||
public bool RemoveStatusEffect(uint effectId, CommandResultContainer resultContainer = null, ushort worldmasterTextId = 30331, bool playEffect = true)
|
||||
{
|
||||
return RemoveStatusEffect(GetStatusEffectById(effectId), resultContainer, worldmasterTextId, playEffect);
|
||||
}
|
||||
|
||||
public StatusEffect CopyEffect(StatusEffect effect)
|
||||
{
|
||||
var newEffect = new StatusEffect(owner, effect);
|
||||
newEffect.SetOwner(owner);
|
||||
// todo: should source be copied too?
|
||||
return AddStatusEffect(newEffect, effect.GetSource()) ? newEffect : null;
|
||||
}
|
||||
|
||||
public bool RemoveStatusEffectsByFlags(uint flags, CommandResultContainer resultContainer = null)
|
||||
{
|
||||
// build list of effects to remove
|
||||
var removeEffects = GetStatusEffectsByFlag(flags);
|
||||
|
||||
// remove effects from main list
|
||||
foreach (var effect in removeEffects)
|
||||
RemoveStatusEffect(effect, resultContainer, effect.GetStatusLossTextId(), true);
|
||||
|
||||
// removed an effect with one of these flags
|
||||
return removeEffects.Count > 0;
|
||||
}
|
||||
|
||||
public StatusEffect GetStatusEffectById(uint id, byte tier = 0xFF)
|
||||
{
|
||||
StatusEffect effect;
|
||||
|
||||
if (effects.TryGetValue(id, out effect) && effect.GetStatusEffectId() == id && (tier != 0xFF ? effect.GetTier() == tier : true))
|
||||
return effect;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<StatusEffect> GetStatusEffectsByFlag(uint flag)
|
||||
{
|
||||
var list = new List<StatusEffect>();
|
||||
foreach (var effect in effects.Values)
|
||||
if ((effect.GetFlags() & flag) != 0)
|
||||
list.Add(effect);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public StatusEffect GetRandomEffectByFlag(uint flag)
|
||||
{
|
||||
var list = GetStatusEffectsByFlag(flag);
|
||||
|
||||
if (list.Count > 0)
|
||||
return list[Program.Random.Next(list.Count)];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// todo: why the fuck cant c# convert enums/
|
||||
public bool HasStatusEffectsByFlag(StatusEffectFlags flags)
|
||||
{
|
||||
return HasStatusEffectsByFlag((uint)flags);
|
||||
}
|
||||
|
||||
public bool HasStatusEffectsByFlag(uint flag)
|
||||
{
|
||||
foreach (var effect in effects.Values)
|
||||
{
|
||||
if ((effect.GetFlags() & flag) != 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public IEnumerable<StatusEffect> GetStatusEffects()
|
||||
{
|
||||
return effects.Values;
|
||||
}
|
||||
|
||||
void SaveStatusEffectsToDatabase(StatusEffectFlags removeEffectFlags = StatusEffectFlags.None)
|
||||
{
|
||||
if (owner is Player)
|
||||
{
|
||||
Database.SavePlayerStatusEffects((Player)owner);
|
||||
}
|
||||
}
|
||||
|
||||
public void CallLuaFunctionByFlag(uint flag, string function, params object[] args)
|
||||
{
|
||||
var effects = GetStatusEffectsByFlag(flag);
|
||||
|
||||
object[] argsWithEffect = new object[args.Length + 1];
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
argsWithEffect[i + 1] = args[i];
|
||||
|
||||
foreach (var effect in effects)
|
||||
{
|
||||
argsWithEffect[0] = effect;
|
||||
effect.CallLuaFunction(owner, function, argsWithEffect);
|
||||
}
|
||||
}
|
||||
|
||||
//Sets the status id at an index.
|
||||
//Changing a status to another doesn't seem to work. If updating an index that already has an effect, set it to 0 first then to the correct status
|
||||
public void SetStatusAtIndex(int index, ushort statusId)
|
||||
{
|
||||
owner.charaWork.status[index] = statusId;
|
||||
|
||||
statusSubpackets.Add(SetActorStatusPacket.BuildPacket(owner.actorId, (ushort)index, statusId));
|
||||
owner.updateFlags |= ActorUpdateFlags.Status;
|
||||
}
|
||||
|
||||
public void SetTimeAtIndex(int index, uint time)
|
||||
{
|
||||
owner.charaWork.statusShownTime[index] = time;
|
||||
statusTimerPropPacketUtil.AddProperty($"charaWork.statusShownTime[{index}]");
|
||||
owner.updateFlags |= ActorUpdateFlags.StatusTime;
|
||||
}
|
||||
|
||||
public List<SubPacket> GetStatusPackets()
|
||||
{
|
||||
return statusSubpackets;
|
||||
}
|
||||
|
||||
public List<SubPacket> GetStatusTimerPackets()
|
||||
{
|
||||
return statusTimerPropPacketUtil.Done();
|
||||
}
|
||||
|
||||
public void ResetPropPacketUtil()
|
||||
{
|
||||
statusTimerPropPacketUtil = new ActorPropertyPacketUtil("charaWork/status", owner);
|
||||
}
|
||||
|
||||
//Overwrites effectToBeReplaced with a new status effect
|
||||
//Returns the message of the new effect being added
|
||||
//Doing this instead of simply calling remove then add so that the new effect is in the same slot as the old one
|
||||
//There should be a better way to do this
|
||||
//Currently causes the icons to blink whenb eing rpelaced
|
||||
public CommandResult ReplaceEffect(StatusEffect effectToBeReplaced, uint newEffectId, byte tier, double magnitude, uint duration)
|
||||
{
|
||||
StatusEffect newEffect = Server.GetWorldManager().GetStatusEffect(newEffectId);
|
||||
newEffect.SetTier(tier);
|
||||
newEffect.SetMagnitude(magnitude);
|
||||
newEffect.SetDuration(duration);
|
||||
newEffect.SetOwner(effectToBeReplaced.GetOwner());
|
||||
effectToBeReplaced.CallLuaFunction(owner, "onLose", owner, effectToBeReplaced);
|
||||
newEffect.CallLuaFunction(owner, "onGain", owner, newEffect);
|
||||
effects.Remove(effectToBeReplaced.GetStatusEffectId());
|
||||
|
||||
newEffect.SetStartTime(DateTime.Now);
|
||||
newEffect.SetEndTime(DateTime.Now.AddSeconds(newEffect.GetDuration()));
|
||||
uint time = (newEffect.GetFlags() & (uint)StatusEffectFlags.Stance) == 0 ? Utils.UnixTimeStampUTC(newEffect.GetEndTime()) : 0xFFFFFFFF;
|
||||
int index = Array.IndexOf(owner.charaWork.status, effectToBeReplaced.GetStatusId());
|
||||
|
||||
//owner.charaWork.status[index] = newEffect.GetStatusId();
|
||||
owner.charaWork.statusShownTime[index] = time;
|
||||
effects[newEffectId] = newEffect;
|
||||
|
||||
SetStatusAtIndex(index, 0);
|
||||
|
||||
//charawork/status
|
||||
SetStatusAtIndex(index, (ushort) (newEffectId - 200000));
|
||||
SetTimeAtIndex(index, time);
|
||||
|
||||
return new CommandResult(owner.actorId, 30330, (uint) HitEffect.StatusEffectType | newEffectId);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,430 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,97 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,108 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,73 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
class Action
|
||||
{
|
||||
public DateTime startTime;
|
||||
public uint durationMs;
|
||||
public bool checkState;
|
||||
// todo: lua function
|
||||
LuaScript script;
|
||||
}
|
||||
|
||||
class ActionQueue
|
||||
{
|
||||
private Character owner;
|
||||
private Queue<Action> actionQueue;
|
||||
private Queue<Action> timerQueue;
|
||||
|
||||
public bool IsEmpty { get { return actionQueue.Count > 0 || timerQueue.Count > 0; } }
|
||||
|
||||
public ActionQueue(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
actionQueue = new Queue<Action>();
|
||||
timerQueue = new Queue<Action>();
|
||||
}
|
||||
|
||||
public void PushAction(Action action)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Update(DateTime tick)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void HandleAction(Action action)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void CheckAction(DateTime tick)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,227 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.utils;
|
||||
using Meteor.Common;
|
||||
using Meteor.Map.actors.area;
|
||||
|
||||
// port of https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai/helpers/pathfind.h
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
// todo: check for obstacles, los, etc
|
||||
public enum PathFindFlags
|
||||
{
|
||||
None,
|
||||
Scripted = 0x01,
|
||||
IgnoreNav = 0x02,
|
||||
}
|
||||
class PathFind
|
||||
{
|
||||
private Character owner;
|
||||
private List<Vector3> path;
|
||||
private bool canFollowPath;
|
||||
float distanceFromPoint;
|
||||
|
||||
private PathFindFlags pathFlags;
|
||||
|
||||
public PathFind(Character owner)
|
||||
{
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public void PreparePath(Vector3 dest, float stepSize = 1.25f, int maxPath = 40, float polyRadius = 0.0f)
|
||||
{
|
||||
PreparePath(dest.X, dest.Y, dest.Z, stepSize, maxPath, polyRadius);
|
||||
}
|
||||
|
||||
public void PreparePath(float x, float y, float z, float stepSize = 1.25f, int maxPath = 40, float polyRadius = 0.0f)
|
||||
{
|
||||
var pos = new Vector3(owner.positionX, owner.positionY, owner.positionZ);
|
||||
var dest = new Vector3(x, y, z);
|
||||
|
||||
Zone zone;
|
||||
if (owner.GetZone() is PrivateArea || owner.GetZone() is PrivateAreaContent)
|
||||
zone = (Zone)((PrivateArea)owner.GetZone()).GetParentZone();
|
||||
else
|
||||
zone = (Zone)owner.GetZone();
|
||||
|
||||
var sw = new System.Diagnostics.Stopwatch();
|
||||
sw.Start();
|
||||
|
||||
if ((pathFlags & PathFindFlags.IgnoreNav) != 0)
|
||||
path = new List<Vector3>(1) { new Vector3(x, y, z) };
|
||||
else
|
||||
path = NavmeshUtils.GetPath(zone, pos, dest, stepSize, maxPath, polyRadius);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
if (owner.oldPositionX == 0.0f && owner.oldPositionY == 0.0f && owner.oldPositionZ == 0.0f)
|
||||
{
|
||||
owner.oldPositionX = owner.positionX;
|
||||
owner.oldPositionY = owner.positionY;
|
||||
owner.oldPositionZ = owner.positionZ;
|
||||
}
|
||||
|
||||
// todo: something went wrong
|
||||
if (path.Count == 0)
|
||||
{
|
||||
owner.positionX = owner.oldPositionX;
|
||||
owner.positionY = owner.oldPositionY;
|
||||
owner.positionZ = owner.oldPositionZ;
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
zone.pathCalls++;
|
||||
zone.pathCallTime += sw.ElapsedMilliseconds;
|
||||
|
||||
//if (path.Count == 1)
|
||||
// Program.Log.Info($"mypos: {owner.positionX} {owner.positionY} {owner.positionZ} | targetPos: {x} {y} {z} | step {stepSize} | maxPath {maxPath} | polyRadius {polyRadius}");
|
||||
|
||||
//Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", owner.actorId, owner.actorName, path.Count, sw.ElapsedMilliseconds);
|
||||
}
|
||||
}
|
||||
|
||||
public void PathInRange(Vector3 dest, float minRange, float maxRange)
|
||||
{
|
||||
PathInRange(dest.X, dest.Y, dest.Z, minRange, maxRange);
|
||||
}
|
||||
|
||||
public void PathInRange(float x, float y, float z, float minRange, float maxRange = 5.0f)
|
||||
{
|
||||
var dest = owner.FindRandomPoint(x, y, z, minRange, maxRange);
|
||||
// todo: this is dumb..
|
||||
distanceFromPoint = owner.GetAttackRange();
|
||||
PreparePath(dest.X, dest.Y, dest.Z);
|
||||
}
|
||||
|
||||
|
||||
public void SetPathFlags(PathFindFlags flags)
|
||||
{
|
||||
this.pathFlags = flags;
|
||||
}
|
||||
|
||||
public bool IsFollowingPath()
|
||||
{
|
||||
return path?.Count > 0;
|
||||
}
|
||||
|
||||
public bool IsFollowingScriptedPath()
|
||||
{
|
||||
return (pathFlags & PathFindFlags.Scripted) != 0;
|
||||
}
|
||||
|
||||
public void FollowPath()
|
||||
{
|
||||
if (path?.Count > 0)
|
||||
{
|
||||
var point = path[0];
|
||||
|
||||
StepTo(point);
|
||||
|
||||
if (AtPoint(point))
|
||||
{
|
||||
path.Remove(point);
|
||||
owner.OnPath(point);
|
||||
//Program.Log.Error($"{owner.actorName} arrived at point {point.X} {point.Y} {point.Z}");
|
||||
}
|
||||
|
||||
if (path.Count == 0 && owner.target != null)
|
||||
owner.LookAt(owner.target);
|
||||
}
|
||||
}
|
||||
|
||||
public bool AtPoint(Vector3 point = null)
|
||||
{
|
||||
if (point == null && path != null && path.Count > 0)
|
||||
{
|
||||
point = path[path.Count - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
distanceFromPoint = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (distanceFromPoint == 0)
|
||||
return owner.positionX == point.X && owner.positionZ == point.Z;
|
||||
else
|
||||
return Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, point.X, point.Y, point.Z) <= (distanceFromPoint + 4.5f);
|
||||
}
|
||||
|
||||
public void StepTo(Vector3 point, bool run = false)
|
||||
{
|
||||
float speed = GetSpeed();
|
||||
|
||||
float stepDistance = speed / 3;
|
||||
float distanceTo = Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, point.X, point.Y, point.Z);
|
||||
|
||||
owner.LookAt(point);
|
||||
|
||||
if (distanceTo <= distanceFromPoint + stepDistance)
|
||||
{
|
||||
if (distanceFromPoint <= owner.GetAttackRange())
|
||||
{
|
||||
owner.QueuePositionUpdate(point);
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = owner.positionX - (float)Math.Cos(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
float z = owner.positionZ + (float)Math.Sin(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
|
||||
owner.QueuePositionUpdate(x, owner.positionY, z);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float x = owner.positionX - (float)Math.Cos(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
float z = owner.positionZ + (float)Math.Sin(owner.rotation + (float)(Math.PI / 2)) * (distanceTo - distanceFromPoint);
|
||||
|
||||
owner.QueuePositionUpdate(x, owner.positionY, z);
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
path?.Clear();
|
||||
pathFlags = PathFindFlags.None;
|
||||
distanceFromPoint = 0.0f;
|
||||
}
|
||||
|
||||
private float GetSpeed()
|
||||
{
|
||||
float baseSpeed = owner.GetSpeed();
|
||||
|
||||
if (!(owner is Player))
|
||||
{
|
||||
if (owner is BattleNpc)
|
||||
{
|
||||
//owner.ChangeSpeed(0.0f, SetActorSpeedPacket.DEFAULT_WALK - 2.0f, SetActorSpeedPacket.DEFAULT_RUN - 2.0f, SetActorSpeedPacket.DEFAULT_ACTIVE - 2.0f);
|
||||
}
|
||||
// baseSpeed += ConfigConstants.NPC_SPEED_MOD;
|
||||
}
|
||||
return baseSpeed;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,509 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 System.Linq;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Common;
|
||||
using Meteor.Map.actors.chara.ai.controllers;
|
||||
using Meteor.Map.actors.group;
|
||||
|
||||
// port of dsp's ai code https://github.com/DarkstarProject/darkstar/blob/master/src/map/ai/
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai
|
||||
{
|
||||
[Flags]
|
||||
public enum ValidTarget : ushort
|
||||
{
|
||||
None = 0x00,
|
||||
Self = 0x01, //Can be used on self (if this flag isn't set and target is self, return false)
|
||||
SelfOnly = 0x02, //Must be used on self (if this flag is set and target isn't self, return false)
|
||||
Party = 0x4, //Can be used on party members
|
||||
PartyOnly = 0x8, //Must be used on party members
|
||||
Ally = 0x10, //Can be used on allies
|
||||
AllyOnly = 0x20, //Must be used on allies
|
||||
NPC = 0x40, //Can be used on static NPCs
|
||||
NPCOnly = 0x80, //Must be used on static NPCs
|
||||
Enemy = 0x100, //Can be used on enemies
|
||||
EnemyOnly = 0x200, //Must be used on enemies
|
||||
Object = 0x400, //Can be used on objects
|
||||
ObjectOnly = 0x800, //Must be used on objects
|
||||
Corpse = 0x1000, //Can be used on corpses
|
||||
CorpseOnly = 0x2000, //Must be used on corpses
|
||||
|
||||
//These are only used for ValidTarget, not MainTarget
|
||||
MainTargetParty = 0x4000, //Can be used on main target's party (This will basically always be true.)
|
||||
MainTargetPartyOnly = 0x8000, //Must be used on main target's party (This is for Protect basically.)
|
||||
}
|
||||
|
||||
/// <summary> Targeting from/to different entity types </summary>
|
||||
enum TargetFindCharacterType : byte
|
||||
{
|
||||
None,
|
||||
/// <summary> Player can target all <see cref="Player">s in party </summary>
|
||||
PlayerToPlayer,
|
||||
/// <summary> Player can target all <see cref="BattleNpc"/>s (excluding player owned <see cref="Pet"/>s) </summary>
|
||||
PlayerToBattleNpc,
|
||||
/// <summary> BattleNpc can target other <see cref="BattleNpc"/>s </summary>
|
||||
BattleNpcToBattleNpc,
|
||||
/// <summary> BattleNpc can target <see cref="Player"/>s and their <see cref="Pet"/>s </summary>
|
||||
BattleNpcToPlayer,
|
||||
}
|
||||
|
||||
/// <summary> Type of AOE region to create </summary>
|
||||
enum TargetFindAOEType : byte
|
||||
{
|
||||
None,
|
||||
/// <summary> Really a cylinder, uses maxDistance parameter in SetAOEType </summary>
|
||||
Circle,
|
||||
/// <summary> Create a cone with param in radians </summary>
|
||||
Cone,
|
||||
/// <summary> Box using self/target coords and </summary>
|
||||
Box
|
||||
}
|
||||
|
||||
/// <summary> Set AOE around self or target </summary>
|
||||
enum TargetFindAOETarget : byte
|
||||
{
|
||||
/// <summary> Set AOE's origin at target's position </summary>
|
||||
Target,
|
||||
/// <summary> Set AOE's origin to own position. </summary>
|
||||
Self
|
||||
}
|
||||
|
||||
/// <summary> Target finding helper class </summary>
|
||||
class TargetFind
|
||||
{
|
||||
private Character owner;
|
||||
private Character mainTarget; //This is the target that the skill is being used on
|
||||
private Character masterTarget; //If mainTarget is a pet, this is the owner
|
||||
private TargetFindCharacterType findType;
|
||||
private ValidTarget validTarget;
|
||||
private TargetFindAOETarget aoeTarget;
|
||||
private TargetFindAOEType aoeType;
|
||||
private Vector3 aoeTargetPosition; //This is the center of circle of cone AOEs and the position where line aoes come out. If we have mainTarget this might not be needed?
|
||||
private float aoeTargetRotation; //This is the direction the aoe target is facing
|
||||
private float maxDistance; //Radius for circle and cone AOEs, length for line AOEs
|
||||
private float minDistance; //Minimum distance to that target must be to be able to be hit
|
||||
private float width; //Width of line AOEs
|
||||
private float height; //All AoEs are boxes or cylinders. Height is usually 10y regardless of maxDistance, but some commands have different values. Height is total height, so targets can be at most half this distance away on Y axis
|
||||
private float aoeRotateAngle; //This is the angle that cones and line aoes are rotated about aoeTargetPosition for skills that come out of a side other than the front
|
||||
private float coneAngle; //The angle of the cone itself in Pi Radians
|
||||
private float param;
|
||||
private List<Character> targets;
|
||||
|
||||
public TargetFind(Character owner, Character mainTarget = null)
|
||||
{
|
||||
Reset();
|
||||
this.owner = owner;
|
||||
this.mainTarget = mainTarget == null ? owner : mainTarget;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
this.mainTarget = owner;
|
||||
this.findType = TargetFindCharacterType.None;
|
||||
this.validTarget = ValidTarget.Enemy;
|
||||
this.aoeType = TargetFindAOEType.None;
|
||||
this.aoeTarget = TargetFindAOETarget.Target;
|
||||
this.aoeTargetPosition = null;
|
||||
this.aoeTargetRotation = 0;
|
||||
this.maxDistance = 0.0f;
|
||||
this.minDistance = 0.0f;
|
||||
this.width = 0.0f;
|
||||
this.height = 0.0f;
|
||||
this.aoeRotateAngle = 0.0f;
|
||||
this.coneAngle = 0.0f;
|
||||
this.param = 0.0f;
|
||||
this.targets = new List<Character>();
|
||||
}
|
||||
|
||||
public List<T> GetTargets<T>() where T : Character
|
||||
{
|
||||
return new List<T>(targets.OfType<T>());
|
||||
}
|
||||
|
||||
public List<Character> GetTargets()
|
||||
{
|
||||
return targets;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this before <see cref="FindWithinArea"/> <para/>
|
||||
/// </summary>
|
||||
/// <param name="maxDistance">
|
||||
/// <see cref="TargetFindAOEType.Circle"/> - radius of circle <para/>
|
||||
/// <see cref="TargetFindAOEType.Cone"/> - height of cone <para/>
|
||||
/// <see cref="TargetFindAOEType.Box"/> - width of box / 2 (todo: set box length not just between user and target)
|
||||
/// </param>
|
||||
/// <param name="param"> param in degrees of cone (todo: probably use radians and forget converting at runtime) </param>
|
||||
public void SetAOEType(ValidTarget validTarget, TargetFindAOEType aoeType, TargetFindAOETarget aoeTarget, float maxDistance, float minDistance, float height, float aoeRotate, float coneAngle, float param = 0.0f)
|
||||
{
|
||||
this.validTarget = validTarget;
|
||||
this.aoeType = aoeType;
|
||||
this.maxDistance = maxDistance;
|
||||
this.minDistance = minDistance;
|
||||
this.param = param;
|
||||
this.height = height;
|
||||
this.aoeRotateAngle = aoeRotate;
|
||||
this.coneAngle = coneAngle;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call this to prepare Box AOE
|
||||
/// </summary>
|
||||
/// <param name="validTarget"></param>
|
||||
/// <param name="aoeTarget"></param>
|
||||
/// <param name="length"></param>
|
||||
/// <param name="width"></param>
|
||||
public void SetAOEBox(ValidTarget validTarget, TargetFindAOETarget aoeTarget, float length, float width, float aoeRotateAngle)
|
||||
{
|
||||
this.validTarget = validTarget;
|
||||
this.aoeType = TargetFindAOEType.Box;
|
||||
this.aoeTarget = aoeTarget;
|
||||
this.aoeRotateAngle = aoeRotateAngle;
|
||||
float x = owner.positionX - (float)Math.Cos(owner.rotation + (float)(Math.PI / 2)) * (length);
|
||||
float z = owner.positionZ + (float)Math.Sin(owner.rotation + (float)(Math.PI / 2)) * (length);
|
||||
this.maxDistance = length;
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find and try to add a single target to target list
|
||||
/// </summary>
|
||||
public void FindTarget(Character target, ValidTarget flags)
|
||||
{
|
||||
validTarget = flags;
|
||||
// todo: maybe this should only be set if successfully added?
|
||||
AddTarget(target, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <para> Call SetAOEType before calling this </para>
|
||||
/// Find targets within area set by <see cref="SetAOEType"/>
|
||||
/// </summary>
|
||||
public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget)
|
||||
{
|
||||
targets.Clear();
|
||||
validTarget = flags;
|
||||
// are we creating aoe circles around target or self
|
||||
if (aoeTarget == TargetFindAOETarget.Self)
|
||||
{
|
||||
this.aoeTargetPosition = owner.GetPosAsVector3();
|
||||
this.aoeTargetRotation = owner.rotation + (float) (aoeRotateAngle * Math.PI);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.aoeTargetPosition = target.GetPosAsVector3();
|
||||
this.aoeTargetRotation = target.rotation + (float) (aoeRotateAngle * Math.PI);
|
||||
}
|
||||
|
||||
masterTarget = TryGetMasterTarget(target) ?? target;
|
||||
|
||||
// todo: this is stupid
|
||||
bool withPet = (flags & ValidTarget.Ally) != 0 || masterTarget.allegiance != owner.allegiance;
|
||||
|
||||
if (masterTarget != null && CanTarget(masterTarget))
|
||||
targets.Add(masterTarget);
|
||||
|
||||
if (aoeType != TargetFindAOEType.None)
|
||||
{
|
||||
AddAllInRange(target, withPet);
|
||||
}
|
||||
|
||||
//if (targets.Count > 8)
|
||||
//targets.RemoveRange(8, targets.Count - 8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find targets within a box using owner's coordinates and target's coordinates as length
|
||||
/// with corners being `maxDistance` yalms to either side of self and target
|
||||
/// </summary>
|
||||
private bool IsWithinBox(Character target, bool withPet)
|
||||
{
|
||||
Vector3 vec = target.GetPosAsVector3() - aoeTargetPosition;
|
||||
Vector3 relativePos = new Vector3();
|
||||
|
||||
//Get target's position relative to owner's position where owner's front is facing positive z axis
|
||||
relativePos.X = (float)(vec.X * Math.Cos(aoeTargetRotation) - vec.Z * Math.Sin(aoeTargetRotation));
|
||||
relativePos.Z = (float)(vec.X * Math.Sin(aoeTargetRotation) + vec.Z * Math.Cos(aoeTargetRotation));
|
||||
|
||||
float halfHeight = height / 2;
|
||||
float halfWidth = width / 2;
|
||||
|
||||
Vector3 closeBottomLeft = new Vector3(-halfWidth, -halfHeight, minDistance);
|
||||
Vector3 farTopRight = new Vector3(halfWidth, halfHeight, maxDistance);
|
||||
|
||||
return relativePos.IsWithinBox(closeBottomLeft, farTopRight);
|
||||
}
|
||||
|
||||
private bool IsWithinCone(Character target, bool withPet)
|
||||
{
|
||||
double distance = Utils.XZDistance(aoeTargetPosition, target.GetPosAsVector3());
|
||||
|
||||
//Make sure target is within the correct range first
|
||||
if (!IsWithinCircle(target))
|
||||
return false;
|
||||
|
||||
//This might not be 100% right or the most optimal way to do this
|
||||
//Get between taget's position and our position
|
||||
return target.GetPosAsVector3().IsWithinCone(aoeTargetPosition, aoeTargetRotation, coneAngle);
|
||||
}
|
||||
|
||||
private void AddTarget(Character target, bool withPet)
|
||||
{
|
||||
if (CanTarget(target))
|
||||
{
|
||||
// todo: add pets too
|
||||
targets.Add(target);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInParty(Character target, bool withPet)
|
||||
{
|
||||
var party = target.currentParty as Party;
|
||||
if (party != null)
|
||||
{
|
||||
foreach (var actorId in party.members)
|
||||
{
|
||||
AddTarget(owner.zone.FindActorInArea<Character>(actorId), withPet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInAlliance(Character target, bool withPet)
|
||||
{
|
||||
// todo:
|
||||
AddAllInParty(target, withPet);
|
||||
}
|
||||
|
||||
private void AddAllBattleNpcs(Character target, bool withPet)
|
||||
{
|
||||
int dist = (int)maxDistance;
|
||||
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(target, dist);
|
||||
|
||||
foreach (BattleNpc actor in actors)
|
||||
{
|
||||
AddTarget(actor, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInZone(Character target, bool withPet)
|
||||
{
|
||||
var actors = owner.zone.GetAllActors<Character>();
|
||||
foreach (Character actor in actors)
|
||||
{
|
||||
AddTarget(actor, withPet);
|
||||
}
|
||||
}
|
||||
|
||||
private void AddAllInRange(Character target, bool withPet)
|
||||
{
|
||||
int dist = (int)maxDistance;
|
||||
var actors = owner.zone.GetActorsAroundActor<Character>(target, dist);
|
||||
|
||||
foreach (Character actor in actors)
|
||||
{
|
||||
AddTarget(actor, false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void AddAllInHateList()
|
||||
{
|
||||
if (!(owner is BattleNpc))
|
||||
{
|
||||
Program.Log.Error($"TargetFind.AddAllInHateList() owner [{owner.actorId}] {owner.customDisplayName} {owner.actorName} is not a BattleNpc");
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var hateEntry in ((BattleNpc)owner).hateContainer.GetHateList())
|
||||
{
|
||||
AddTarget(hateEntry.Value.actor, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanTarget(Character target, bool withPet = false, bool retarget = false, bool ignoreAOE = false)
|
||||
{
|
||||
// already targeted, dont target again
|
||||
if (target == null || !retarget && targets.Contains(target))
|
||||
return false;
|
||||
|
||||
if (target == null)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on a corpse and target is dead
|
||||
if ((validTarget & ValidTarget.Corpse) == 0 && target.IsDead())
|
||||
return false;
|
||||
|
||||
//This skill must be used on a corpse and target is alive
|
||||
if ((validTarget & ValidTarget.CorpseOnly) != 0 && target.IsAlive())
|
||||
return false;
|
||||
|
||||
//This skill can't be used on self and target is self
|
||||
if ((validTarget & ValidTarget.Self) == 0 && target == owner)
|
||||
return false;
|
||||
|
||||
//This skill must be used on self and target isn't self
|
||||
if ((validTarget & ValidTarget.SelfOnly) != 0 && target != owner)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on an ally and target is an ally
|
||||
if ((validTarget & ValidTarget.Ally) == 0 && target.allegiance == owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill must be used on an ally and target is not an ally
|
||||
if ((validTarget & ValidTarget.AllyOnly) != 0 && target.allegiance != owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on an enemu and target is an enemy
|
||||
if ((validTarget & ValidTarget.Enemy) == 0 && target.allegiance != owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill must be used on an enemy and target is an ally
|
||||
if ((validTarget & ValidTarget.EnemyOnly) != 0 && target.allegiance == owner.allegiance)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on party members and target is a party member
|
||||
if ((validTarget & ValidTarget.Party) == 0 && target.currentParty == owner.currentParty)
|
||||
return false;
|
||||
|
||||
//This skill must be used on party members and target is not a party member
|
||||
if ((validTarget & ValidTarget.PartyOnly) != 0 && target.currentParty != owner.currentParty)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on NPCs and target is an npc
|
||||
if ((validTarget & ValidTarget.NPC) == 0 && target.isStatic)
|
||||
return false;
|
||||
|
||||
//This skill must be used on NPCs and target is not an npc
|
||||
if ((validTarget & ValidTarget.NPCOnly) != 0 && !target.isStatic)
|
||||
return false;
|
||||
|
||||
// todo: why is player always zoning?
|
||||
// cant target if zoning
|
||||
if (target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
{
|
||||
owner.aiContainer.ChangeTarget(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/*target.isZoning || owner.isZoning || */target.zone != owner.zone)
|
||||
return false;
|
||||
|
||||
if (validTarget == ValidTarget.Self && aoeType == TargetFindAOEType.None && owner != target)
|
||||
return false;
|
||||
|
||||
//This skill can't be used on main target's party members and target is a party member of main target
|
||||
if ((validTarget & ValidTarget.MainTargetParty) == 0 && target.currentParty == mainTarget.currentParty)
|
||||
return false;
|
||||
|
||||
//This skill must be used on main target's party members and target is not a party member of main target
|
||||
if ((validTarget & ValidTarget.MainTargetPartyOnly) != 0 && target.currentParty != mainTarget.currentParty)
|
||||
return false;
|
||||
|
||||
|
||||
// this is fuckin retarded, think of a better way l8r
|
||||
if (!ignoreAOE)
|
||||
{
|
||||
// hit everything within zone or within aoe region
|
||||
if (param == -1.0f || aoeType == TargetFindAOEType.Circle && !IsWithinCircle(target))
|
||||
return false;
|
||||
|
||||
if (aoeType == TargetFindAOEType.Cone && !IsWithinCone(target, withPet))
|
||||
return false;
|
||||
|
||||
if (aoeType == TargetFindAOEType.Box && !IsWithinBox(target, withPet))
|
||||
return false;
|
||||
|
||||
if (aoeType == TargetFindAOEType.None && targets.Count != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool IsWithinCircle(Character target)
|
||||
{
|
||||
//Check if XZ is in circle and that y difference isn't larger than half height
|
||||
return target.GetPosAsVector3().IsWithinCircle(aoeTargetPosition, maxDistance, minDistance) && Math.Abs(owner.positionY - target.positionY) <= (height / 2);
|
||||
}
|
||||
|
||||
private bool IsPlayer(Character target)
|
||||
{
|
||||
if (target is Player)
|
||||
return true;
|
||||
|
||||
// treat player owned pets as players too
|
||||
return TryGetMasterTarget(target) is Player;
|
||||
}
|
||||
|
||||
private Character TryGetMasterTarget(Character target)
|
||||
{
|
||||
// if character is a player owned pet, treat as a player
|
||||
if (target.aiContainer != null)
|
||||
{
|
||||
var controller = target.aiContainer.GetController<PetController>();
|
||||
if (controller != null)
|
||||
return controller.GetPetMaster();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool IsBattleNpcOwner(Character target)
|
||||
{
|
||||
// i know i copied this from dsp but what even
|
||||
if (!(owner is Player) || target is Player)
|
||||
return true;
|
||||
|
||||
// todo: check hate list
|
||||
if (owner is BattleNpc && ((BattleNpc)owner).hateContainer.GetMostHatedTarget() != target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Character GetValidTarget(Character target, ValidTarget findFlags)
|
||||
{
|
||||
if (target == null || target is Player && ((Player)target).playerSession.isUpdatesLocked)
|
||||
return null;
|
||||
|
||||
if ((findFlags & ValidTarget.Ally) != 0)
|
||||
{
|
||||
return owner.pet;
|
||||
}
|
||||
|
||||
// todo: this is beyond retarded
|
||||
var oldFlags = this.validTarget;
|
||||
this.validTarget = findFlags;
|
||||
if (CanTarget(target, false, true))
|
||||
{
|
||||
this.validTarget = oldFlags;
|
||||
return target;
|
||||
}
|
||||
this.validTarget = oldFlags;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,169 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.Common;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class AbilityState : State
|
||||
{
|
||||
|
||||
private BattleCommand skill;
|
||||
|
||||
public AbilityState(Character owner, Character target, ushort skillId) :
|
||||
base(owner, target)
|
||||
{
|
||||
this.startTime = DateTime.Now;
|
||||
this.skill = Server.GetWorldManager().GetBattleCommand(skillId);
|
||||
var returnCode = skill.CallLuaFunction(owner, "onAbilityPrepare", owner, target, skill);
|
||||
|
||||
this.target = (skill.mainTarget & ValidTarget.SelfOnly) != 0 ? owner : target;
|
||||
|
||||
errorResult = new CommandResult(owner.actorId, 32553, 0);
|
||||
if (returnCode == 0)
|
||||
{
|
||||
OnStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorResult = null;
|
||||
interrupt = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var returnCode = skill.CallLuaFunction(owner, "onAbilityStart", owner, target, skill);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
interrupt = true;
|
||||
errorResult = new CommandResult(owner.actorId, (ushort)(returnCode == -1 ? 32558 : returnCode), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!skill.IsInstantCast())
|
||||
{
|
||||
float castTime = skill.castTimeMs;
|
||||
|
||||
// command casting duration
|
||||
if (owner is Player)
|
||||
{
|
||||
// todo: modify spellSpeed based on modifiers and stuff
|
||||
((Player)owner).SendStartCastbar(skill.id, Utils.UnixTimeStampUTC(DateTime.Now.AddMilliseconds(castTime)));
|
||||
}
|
||||
owner.GetSubState().chantId = 0xf0;
|
||||
owner.SubstateModified();
|
||||
//You ready [skill] (6F000002: BLM, 6F000003: WHM, 0x6F000008: BRD)
|
||||
owner.DoBattleAction(skill.id, (uint)0x6F000000 | skill.castType, new CommandResult(target.actorId, 30126, 1, 0, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Update(DateTime tick)
|
||||
{
|
||||
if (skill != null)
|
||||
{
|
||||
TryInterrupt();
|
||||
|
||||
if (interrupt)
|
||||
{
|
||||
OnInterrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
// todo: check weapon delay/haste etc and use that
|
||||
var actualCastTime = skill.castTimeMs;
|
||||
|
||||
if ((tick - startTime).TotalMilliseconds >= skill.castTimeMs)
|
||||
{
|
||||
OnComplete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnInterrupt()
|
||||
{
|
||||
// todo: send paralyzed/sleep message etc.
|
||||
if (errorResult != null)
|
||||
{
|
||||
owner.DoBattleAction(skill.id, errorResult.animation, errorResult);
|
||||
errorResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnComplete()
|
||||
{
|
||||
owner.LookAt(target);
|
||||
bool hitTarget = false;
|
||||
|
||||
skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget);
|
||||
isCompleted = true;
|
||||
|
||||
owner.DoBattleCommand(skill, "ability");
|
||||
}
|
||||
|
||||
public override void TryInterrupt()
|
||||
{
|
||||
if (interrupt)
|
||||
return;
|
||||
|
||||
if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAbility))
|
||||
{
|
||||
// todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack
|
||||
var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAbility);
|
||||
uint effectId = 0;
|
||||
if (list.Count > 0)
|
||||
{
|
||||
// todo: actually check proc rate/random chance of whatever effect
|
||||
effectId = list[0].GetStatusEffectId();
|
||||
}
|
||||
interrupt = true;
|
||||
return;
|
||||
}
|
||||
|
||||
interrupt = !CanUse();
|
||||
}
|
||||
|
||||
private bool CanUse()
|
||||
{
|
||||
return skill.IsValidMainTarget(owner, target);
|
||||
}
|
||||
|
||||
public BattleCommand GetWeaponSkill()
|
||||
{
|
||||
return skill;
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
owner.GetSubState().chantId = 0x0;
|
||||
owner.SubstateModified();
|
||||
owner.aiContainer.UpdateLastActionTime(skill.animationDurationSeconds);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,232 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.Common;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class AttackState : State
|
||||
{
|
||||
private DateTime attackTime;
|
||||
|
||||
public AttackState(Character owner, Character target) :
|
||||
base(owner, target)
|
||||
{
|
||||
this.canInterrupt = false;
|
||||
this.startTime = DateTime.Now;
|
||||
|
||||
owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
|
||||
ChangeTarget(target);
|
||||
attackTime = startTime;
|
||||
owner.aiContainer.pathFind?.Clear();
|
||||
}
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override bool Update(DateTime tick)
|
||||
{
|
||||
if ((target == null || owner.target != target || owner.target?.actorId != owner.currentLockedTarget) && owner.isAutoAttackEnabled)
|
||||
owner.aiContainer.ChangeTarget(target = owner.zone.FindActorInArea<Character>(owner.currentTarget));
|
||||
|
||||
if (target == null || target.IsDead())
|
||||
{
|
||||
if (owner.IsMonster() || owner.IsAlly())
|
||||
target = ((BattleNpc)owner).hateContainer.GetMostHatedTarget();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsAttackReady())
|
||||
{
|
||||
if (CanAttack())
|
||||
{
|
||||
TryInterrupt();
|
||||
|
||||
// todo: check weapon delay/haste etc and use that
|
||||
if (!interrupt)
|
||||
{
|
||||
OnComplete();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
SetInterrupted(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: handle interrupt/paralyze etc
|
||||
}
|
||||
attackTime = DateTime.Now.AddMilliseconds(owner.GetAttackDelayMs());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void OnInterrupt()
|
||||
{
|
||||
// todo: send paralyzed/sleep message etc.
|
||||
if (errorResult != null)
|
||||
{
|
||||
owner.zone.BroadcastPacketAroundActor(owner, CommandResultX01Packet.BuildPacket(errorResult.targetId, errorResult.animation, 0x765D, errorResult));
|
||||
errorResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnComplete()
|
||||
{
|
||||
//BattleAction action = new BattleAction(target.actorId, 0x765D, (uint) HitEffect.Hit, 0, (byte) HitDirection.None);
|
||||
errorResult = null;
|
||||
|
||||
// todo: implement auto attack damage bonus in Character.OnAttack
|
||||
/*
|
||||
≪Auto-attack Damage Bonus≫
|
||||
Class Bonus 1 Bonus 2
|
||||
Pugilist Intelligence Strength
|
||||
Gladiator Mind Strength
|
||||
Marauder Vitality Strength
|
||||
Archer Dexterity Piety
|
||||
Lancer Piety Strength
|
||||
Conjurer Mind Piety
|
||||
Thaumaturge Mind Piety
|
||||
* The above damage bonus also applies to “Shot” attacks by archers.
|
||||
*/
|
||||
// handle paralyze/intimidate/sleep/whatever in Character.OnAttack
|
||||
|
||||
|
||||
// todo: Change this to use a BattleCommand like the other states
|
||||
|
||||
//List<BattleAction> actions = new List<BattleAction>();
|
||||
CommandResultContainer actions = new CommandResultContainer();
|
||||
|
||||
//This is all temporary until the skill sheet is finishd and the different auto attacks are added to the database
|
||||
//Some mobs have multiple unique auto attacks that they switch between as well as ranged auto attacks, so we'll need a way to handle that
|
||||
//For now, just use a temporary hardcoded BattleCommand that's the same for everyone.
|
||||
BattleCommand attackCommand = new BattleCommand(22104, "Attack");
|
||||
attackCommand.range = 5;
|
||||
attackCommand.rangeHeight = 10;
|
||||
attackCommand.worldMasterTextId = 0x765D;
|
||||
attackCommand.mainTarget = (ValidTarget)768;
|
||||
attackCommand.validTarget = (ValidTarget)17152;
|
||||
attackCommand.commandType = CommandType.AutoAttack;
|
||||
attackCommand.numHits = (byte)owner.GetMod(Modifier.HitCount);
|
||||
attackCommand.basePotency = 100;
|
||||
ActionProperty property = (owner.GetMod(Modifier.AttackType) != 0) ? (ActionProperty)owner.GetMod(Modifier.AttackType) : ActionProperty.Slashing;
|
||||
attackCommand.actionProperty = property;
|
||||
attackCommand.actionType = ActionType.Physical;
|
||||
|
||||
uint anim = (17 << 24 | 1 << 12);
|
||||
|
||||
if (owner is Player)
|
||||
anim = (25 << 24 | 1 << 12);
|
||||
|
||||
attackCommand.battleAnimation = anim;
|
||||
|
||||
if (owner.CanUse(target, attackCommand))
|
||||
{
|
||||
attackCommand.targetFind.FindWithinArea(target, attackCommand.validTarget, attackCommand.aoeTarget);
|
||||
owner.DoBattleCommand(attackCommand, "autoattack");
|
||||
}
|
||||
}
|
||||
|
||||
public override void TryInterrupt()
|
||||
{
|
||||
if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventAttack))
|
||||
{
|
||||
// todo: sometimes paralyze can let you attack, calculate proc rate
|
||||
var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventAttack);
|
||||
uint statusId = 0;
|
||||
if (list.Count > 0)
|
||||
{
|
||||
statusId = list[0].GetStatusId();
|
||||
}
|
||||
interrupt = true;
|
||||
return;
|
||||
}
|
||||
|
||||
interrupt = !CanAttack();
|
||||
}
|
||||
|
||||
private bool IsAttackReady()
|
||||
{
|
||||
// todo: this enforced delay should really be changed if it's not retail..
|
||||
return Program.Tick >= attackTime && Program.Tick >= owner.aiContainer.GetLastActionTime();
|
||||
}
|
||||
|
||||
private bool CanAttack()
|
||||
{
|
||||
if (!owner.isAutoAttackEnabled || target.allegiance == owner.allegiance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!owner.IsFacing(target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// todo: shouldnt need to check if owner is dead since all states would be cleared
|
||||
if (owner.IsDead() || target.IsDead())
|
||||
{
|
||||
if (owner.IsMonster() || owner.IsAlly())
|
||||
((BattleNpc)owner).hateContainer.ClearHate(target);
|
||||
|
||||
owner.aiContainer.ChangeTarget(null);
|
||||
return false;
|
||||
}
|
||||
else if (!owner.IsValidTarget(target, ValidTarget.Enemy) || !owner.aiContainer.GetTargetFind().CanTarget(target, false, true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// todo: use a mod for melee range
|
||||
else if (Utils.Distance(owner.positionX, owner.positionY, owner.positionZ, target.positionX, target.positionY, target.positionZ) > owner.GetAttackRange())
|
||||
{
|
||||
if (owner is Player)
|
||||
{
|
||||
//The target is too far away
|
||||
((Player)owner).SendGameMessage(Server.GetWorldManager().GetActor(), 32537, 0x20);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
if (owner.IsDead())
|
||||
owner.Disengage();
|
||||
}
|
||||
|
||||
public override bool CanChangeState()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class DeathState : State
|
||||
{
|
||||
DateTime despawnTime;
|
||||
public DeathState(Character owner, DateTime tick, uint timeToFadeOut)
|
||||
: base(owner, null)
|
||||
{
|
||||
owner.Disengage();
|
||||
owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD);
|
||||
owner.statusEffects.RemoveStatusEffectsByFlags((uint)StatusEffectFlags.LoseOnDeath);
|
||||
//var deathStatePacket = SetActorStatePacket.BuildPacket(owner.actorId, SetActorStatePacket.MAIN_STATE_DEAD2, owner.currentSubState);
|
||||
//owner.zone.BroadcastPacketAroundActor(owner, deathStatePacket);
|
||||
canInterrupt = false;
|
||||
startTime = tick;
|
||||
despawnTime = startTime.AddSeconds(timeToFadeOut);
|
||||
}
|
||||
|
||||
public override bool Update(DateTime tick)
|
||||
{
|
||||
// todo: set a flag on chara for accept raise, play animation and spawn
|
||||
if (owner.GetMod((uint)Modifier.Raise) > 0)
|
||||
{
|
||||
owner.Spawn(tick);
|
||||
return true;
|
||||
}
|
||||
|
||||
// todo: handle raise etc
|
||||
if (tick >= despawnTime)
|
||||
{
|
||||
// todo: for players, return them to homepoint
|
||||
owner.Despawn(tick);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class DespawnState : State
|
||||
{
|
||||
private DateTime respawnTime;
|
||||
public DespawnState(Character owner, uint respawnTimeSeconds) :
|
||||
base(owner, null)
|
||||
{
|
||||
startTime = Program.Tick;
|
||||
respawnTime = startTime.AddSeconds(respawnTimeSeconds);
|
||||
owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2);
|
||||
owner.OnDespawn();
|
||||
}
|
||||
|
||||
public override bool Update(DateTime tick)
|
||||
{
|
||||
if (tick >= respawnTime)
|
||||
{
|
||||
owner.ResetTempVars();
|
||||
owner.Spawn(tick);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.state
|
||||
{
|
||||
class InactiveState : State
|
||||
{
|
||||
private DateTime endTime;
|
||||
private uint durationMs;
|
||||
public InactiveState(Character owner, uint durationMs, bool canChangeState) :
|
||||
base(owner, null)
|
||||
{
|
||||
if (!canChangeState)
|
||||
owner.aiContainer.InterruptStates();
|
||||
this.durationMs = durationMs;
|
||||
endTime = DateTime.Now.AddMilliseconds(durationMs);
|
||||
}
|
||||
|
||||
public override bool Update(DateTime tick)
|
||||
{
|
||||
if (durationMs == 0)
|
||||
{
|
||||
if (owner.IsDead())
|
||||
return true;
|
||||
|
||||
if (!owner.statusEffects.HasStatusEffectsByFlag(StatusEffectFlags.PreventMovement))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (durationMs != 0 && tick > endTime)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class ItemState : State
|
||||
{
|
||||
ItemData item;
|
||||
new Player owner;
|
||||
public ItemState(Player owner, Character target, ushort slot, uint itemId) :
|
||||
base(owner, target)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.target = target;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,213 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.battle;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class MagicState : State
|
||||
{
|
||||
|
||||
private BattleCommand spell;
|
||||
private Vector3 startPos;
|
||||
|
||||
public MagicState(Character owner, Character target, ushort spellId) :
|
||||
base(owner, target)
|
||||
{
|
||||
this.startPos = owner.GetPosAsVector3();
|
||||
this.startTime = DateTime.Now;
|
||||
this.spell = Server.GetWorldManager().GetBattleCommand(spellId);
|
||||
var returnCode = spell.CallLuaFunction(owner, "onMagicPrepare", owner, target, spell);
|
||||
|
||||
//modify skill based on status effects
|
||||
//Do this here to allow buffs like Resonance to increase range before checking CanCast()
|
||||
owner.statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnCastStart, "onMagicCast", owner, spell);
|
||||
|
||||
this.target = (spell.mainTarget & ValidTarget.SelfOnly) != 0 ? owner : target;
|
||||
|
||||
errorResult = new CommandResult(owner.actorId, 32553, 0);
|
||||
if (returnCode == 0 && owner.CanUse(this.target, spell, errorResult))
|
||||
{
|
||||
OnStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
interrupt = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var returnCode = spell.CallLuaFunction(owner, "onMagicStart", owner, target, spell);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
interrupt = true;
|
||||
errorResult = new CommandResult(target.actorId, (ushort)(returnCode == -1 ? 32553 : returnCode), 0, 0, 0, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: check within attack range
|
||||
float[] baseCastDuration = { 1.0f, 0.25f };
|
||||
|
||||
//There are no positional spells, so just check onCombo, need to check first because certain spells change aoe type/accuracy
|
||||
//If owner is a player and the spell being used is part of the current combo
|
||||
if (owner is Player && ((Player)owner).GetClass() == spell.job)
|
||||
{
|
||||
Player p = (Player)owner;
|
||||
if (spell.comboStep == 1 || ((p.playerWork.comboNextCommandId[0] == spell.id || p.playerWork.comboNextCommandId[1] == spell.id)))
|
||||
{
|
||||
spell.CallLuaFunction(owner, "onCombo", owner, target, spell);
|
||||
spell.isCombo = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Check combo stuff here because combos can impact spell cast times
|
||||
|
||||
float spellSpeed = spell.castTimeMs;
|
||||
|
||||
if (!spell.IsInstantCast())
|
||||
{
|
||||
// command casting duration
|
||||
if (owner is Player)
|
||||
{
|
||||
// todo: modify spellSpeed based on modifiers and stuff
|
||||
((Player)owner).SendStartCastbar(spell.id, Utils.UnixTimeStampUTC(DateTime.Now.AddMilliseconds(spellSpeed)));
|
||||
}
|
||||
owner.GetSubState().chantId = 0xf0;
|
||||
owner.SubstateModified();
|
||||
owner.DoBattleAction(spell.id, (uint) 0x6F000000 | spell.castType, new CommandResult(target.actorId, 30128, 1, 0, 1)); //You begin casting (6F000002: BLM, 6F000003: WHM, 0x6F000008: BRD)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Update(DateTime tick)
|
||||
{
|
||||
if (spell != null)
|
||||
{
|
||||
TryInterrupt();
|
||||
|
||||
if (interrupt)
|
||||
{
|
||||
OnInterrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
// todo: check weapon delay/haste etc and use that
|
||||
var actualCastTime = spell.castTimeMs;
|
||||
|
||||
if ((tick - startTime).TotalMilliseconds >= spell.castTimeMs)
|
||||
{
|
||||
OnComplete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnInterrupt()
|
||||
{
|
||||
// todo: send paralyzed/sleep message etc.
|
||||
if (errorResult != null)
|
||||
{
|
||||
owner.GetSubState().chantId = 0x0;
|
||||
owner.SubstateModified();
|
||||
owner.DoBattleAction(spell.id, errorResult.animation, errorResult);
|
||||
errorResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnComplete()
|
||||
{
|
||||
//How do combos/hitdirs work for aoe abilities or does that not matter for aoe?
|
||||
HitDirection hitDir = owner.GetHitDirection(target);
|
||||
bool hitTarget = false;
|
||||
|
||||
spell.targetFind.FindWithinArea(target, spell.validTarget, spell.aoeTarget);
|
||||
isCompleted = true;
|
||||
var targets = spell.targetFind.GetTargets();
|
||||
|
||||
owner.DoBattleCommand(spell, "magic");
|
||||
}
|
||||
|
||||
public override void TryInterrupt()
|
||||
{
|
||||
if (interrupt)
|
||||
return;
|
||||
|
||||
if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventSpell))
|
||||
{
|
||||
// todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack
|
||||
var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventSpell);
|
||||
uint effectId = 0;
|
||||
if (list.Count > 0)
|
||||
{
|
||||
// todo: actually check proc rate/random chance of whatever effect
|
||||
effectId = list[0].GetStatusEffectId();
|
||||
}
|
||||
interrupt = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (HasMoved())
|
||||
{
|
||||
errorResult = new CommandResult(owner.actorId, 30211, 0);
|
||||
errorResult.animation = 0x7F000002;
|
||||
interrupt = true;
|
||||
return;
|
||||
}
|
||||
|
||||
interrupt = !CanCast();
|
||||
}
|
||||
|
||||
private bool CanCast()
|
||||
{
|
||||
return owner.CanUse(target, spell);
|
||||
}
|
||||
|
||||
private bool HasMoved()
|
||||
{
|
||||
return (owner.GetPosAsVector3() != startPos);
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
owner.GetSubState().chantId = 0x0;
|
||||
owner.SubstateModified();
|
||||
|
||||
if (owner is Player)
|
||||
{
|
||||
((Player)owner).SendEndCastbar();
|
||||
}
|
||||
owner.aiContainer.UpdateLastActionTime(spell.animationDurationSeconds);
|
||||
}
|
||||
|
||||
public BattleCommand GetSpell()
|
||||
{
|
||||
return spell;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class State
|
||||
{
|
||||
protected Character owner;
|
||||
protected Character target;
|
||||
|
||||
protected bool canInterrupt;
|
||||
protected bool interrupt = false;
|
||||
|
||||
protected DateTime startTime;
|
||||
|
||||
protected CommandResult errorResult;
|
||||
|
||||
protected bool isCompleted;
|
||||
|
||||
public State(Character owner, Character target)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.target = target;
|
||||
this.canInterrupt = true;
|
||||
this.interrupt = false;
|
||||
}
|
||||
|
||||
public virtual bool Update(DateTime tick) { return true; }
|
||||
public virtual void OnStart() { }
|
||||
public virtual void OnInterrupt() { }
|
||||
public virtual void OnComplete() { isCompleted = true; }
|
||||
public virtual bool CanChangeState() { return false; }
|
||||
public virtual void TryInterrupt() { }
|
||||
|
||||
public virtual void Cleanup() { }
|
||||
|
||||
public bool CanInterrupt()
|
||||
{
|
||||
return canInterrupt;
|
||||
}
|
||||
|
||||
public void SetInterrupted(bool interrupt)
|
||||
{
|
||||
this.interrupt = interrupt;
|
||||
}
|
||||
|
||||
public bool IsCompleted()
|
||||
{
|
||||
return isCompleted;
|
||||
}
|
||||
|
||||
public void ChangeTarget(Character target)
|
||||
{
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Character GetTarget()
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,199 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.Common;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.state
|
||||
{
|
||||
class WeaponSkillState : State
|
||||
{
|
||||
|
||||
private BattleCommand skill;
|
||||
private HitDirection hitDirection;
|
||||
public WeaponSkillState(Character owner, Character target, ushort skillId) :
|
||||
base(owner, target)
|
||||
{
|
||||
this.startTime = DateTime.Now;
|
||||
this.skill = Server.GetWorldManager().GetBattleCommand(skillId);
|
||||
|
||||
var returnCode = skill.CallLuaFunction(owner, "onSkillPrepare", owner, target, skill);
|
||||
|
||||
this.target = (skill.mainTarget & ValidTarget.SelfOnly) != 0 ? owner : target;
|
||||
|
||||
errorResult = new CommandResult(owner.actorId, 32553, 0);
|
||||
if (returnCode == 0 && owner.CanUse(this.target, skill, errorResult))
|
||||
{
|
||||
OnStart();
|
||||
}
|
||||
else
|
||||
{
|
||||
errorResult = null;
|
||||
interrupt = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnStart()
|
||||
{
|
||||
var returnCode = skill.CallLuaFunction(owner, "onSkillStart", owner, target, skill);
|
||||
|
||||
if (returnCode != 0)
|
||||
{
|
||||
interrupt = true;
|
||||
errorResult = new CommandResult(owner.actorId, (ushort)(returnCode == -1 ? 32558 : returnCode), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hitDirection = owner.GetHitDirection(target);
|
||||
|
||||
//Do positionals and combo effects first because these can influence accuracy and amount of targets/numhits, which influence the rest of the steps
|
||||
//If there is no positon required or if the position bonus should be activated
|
||||
if ((skill.positionBonus & utils.BattleUtils.ConvertHitDirToPosition(hitDirection)) == skill.positionBonus)
|
||||
{
|
||||
//If there is a position bonus
|
||||
if (skill.positionBonus != BattleCommandPositionBonus.None)
|
||||
skill.CallLuaFunction(owner, "weaponskill", "onPositional", owner, target, skill);
|
||||
|
||||
//Combo stuff
|
||||
if (owner is Player)
|
||||
{
|
||||
Player p = (Player)owner;
|
||||
//If skill is part of owner's class/job, it can be used in a combo
|
||||
if (skill.job == p.GetClass() || skill.job == p.GetCurrentClassOrJob())
|
||||
{
|
||||
//If owner is a player and the skill being used is part of the current combo
|
||||
if (p.playerWork.comboNextCommandId[0] == skill.id || p.playerWork.comboNextCommandId[1] == skill.id)
|
||||
{
|
||||
skill.CallLuaFunction(owner, "onCombo", owner, target, skill);
|
||||
skill.isCombo = true;
|
||||
}
|
||||
//or if this just the start of a combo
|
||||
else if (skill.comboStep == 1)
|
||||
skill.isCombo = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skill.IsInstantCast())
|
||||
{
|
||||
float castTime = skill.castTimeMs;
|
||||
|
||||
// command casting duration
|
||||
if (owner is Player)
|
||||
{
|
||||
// todo: modify spellSpeed based on modifiers and stuff
|
||||
((Player)owner).SendStartCastbar(skill.id, Utils.UnixTimeStampUTC(DateTime.Now.AddMilliseconds(castTime)));
|
||||
}
|
||||
owner.GetSubState().chantId = 0xf0;
|
||||
owner.SubstateModified();
|
||||
//You ready [skill] (6F000002: BLM, 6F000003: WHM, 0x6F000008: BRD)
|
||||
owner.DoBattleAction(skill.id, (uint)0x6F000000 | skill.castType, new CommandResult(target.actorId, 30126, 1, 0, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Update(DateTime tick)
|
||||
{
|
||||
if (skill != null)
|
||||
{
|
||||
TryInterrupt();
|
||||
|
||||
if (interrupt)
|
||||
{
|
||||
OnInterrupt();
|
||||
return true;
|
||||
}
|
||||
|
||||
// todo: check weapon delay/haste etc and use that
|
||||
var actualCastTime = skill.castTimeMs;
|
||||
|
||||
if ((tick - startTime).TotalMilliseconds >= skill.castTimeMs)
|
||||
{
|
||||
OnComplete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnInterrupt()
|
||||
{
|
||||
// todo: send paralyzed/sleep message etc.
|
||||
if (errorResult != null)
|
||||
{
|
||||
owner.DoBattleAction(skill.id, errorResult.animation, errorResult);
|
||||
errorResult = null;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnComplete()
|
||||
{
|
||||
owner.LookAt(target);
|
||||
skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget);
|
||||
isCompleted = true;
|
||||
|
||||
owner.DoBattleCommand(skill, "weaponskill");
|
||||
owner.statusEffects.RemoveStatusEffectsByFlags((uint) StatusEffectFlags.LoseOnAttacking);
|
||||
|
||||
lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed");
|
||||
}
|
||||
|
||||
public override void TryInterrupt()
|
||||
{
|
||||
if (interrupt)
|
||||
return;
|
||||
|
||||
if (owner.statusEffects.HasStatusEffectsByFlag((uint)StatusEffectFlags.PreventWeaponSkill))
|
||||
{
|
||||
// todo: sometimes paralyze can let you attack, get random percentage of actually letting you attack
|
||||
var list = owner.statusEffects.GetStatusEffectsByFlag((uint)StatusEffectFlags.PreventWeaponSkill);
|
||||
uint effectId = 0;
|
||||
if (list.Count > 0)
|
||||
{
|
||||
// todo: actually check proc rate/random chance of whatever effect
|
||||
effectId = list[0].GetStatusEffectId();
|
||||
}
|
||||
interrupt = true;
|
||||
return;
|
||||
}
|
||||
|
||||
interrupt = !CanUse();
|
||||
}
|
||||
|
||||
private bool CanUse()
|
||||
{
|
||||
return owner.CanUse(target, skill);
|
||||
}
|
||||
|
||||
public BattleCommand GetWeaponSkill()
|
||||
{
|
||||
return skill;
|
||||
}
|
||||
|
||||
public override void Cleanup()
|
||||
{
|
||||
owner.aiContainer.UpdateLastActionTime(skill.animationDurationSeconds);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
namespace Meteor.Map.actors.chara.ai.utils
|
||||
{
|
||||
static class AttackUtils
|
||||
{
|
||||
public static int CalculateDamage(Character attacker, Character defender)
|
||||
{
|
||||
int dmg = CalculateBaseDamage(attacker, defender);
|
||||
|
||||
return dmg;
|
||||
}
|
||||
|
||||
public static int CalculateBaseDamage(Character attacker, Character defender)
|
||||
{
|
||||
// todo: actually calculate damage
|
||||
return Program.Random.Next(10) * 10;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,924 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 System.Linq;
|
||||
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.packets.send.actor.battle;
|
||||
using Meteor.Map.actors.chara.npc;
|
||||
using Meteor.Common;
|
||||
|
||||
namespace Meteor.Map.actors.chara.ai.utils
|
||||
{
|
||||
static class BattleUtils
|
||||
{
|
||||
|
||||
public static Dictionary<HitType, ushort> PhysicalHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||
{
|
||||
{ HitType.Miss, 30311 },
|
||||
{ HitType.Evade, 30310 },
|
||||
{ HitType.Parry, 30308 },
|
||||
{ HitType.Block, 30306 },
|
||||
{ HitType.Hit, 30301 },
|
||||
{ HitType.Crit, 30302 }
|
||||
};
|
||||
|
||||
public static Dictionary<HitType, ushort> MagicalHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||
{
|
||||
{ HitType.SingleResist,30318 },
|
||||
{ HitType.DoubleResist,30317 },
|
||||
{ HitType.TripleResist, 30316 },//Triple Resists seem to use the same text ID as full resists
|
||||
{ HitType.FullResist,30316 },
|
||||
{ HitType.Hit, 30319 },
|
||||
{ HitType.Crit, 30392 } //Unsure why crit is separated from the rest of the ids
|
||||
};
|
||||
|
||||
public static Dictionary<HitType, ushort> MultiHitTypeTextIds = new Dictionary<HitType, ushort>()
|
||||
{
|
||||
{ HitType.Miss, 30449 }, //The attack misses.
|
||||
{ HitType.Evade, 0 }, //Evades were removed before multi hit skills got their own messages, so this doesnt exist
|
||||
{ HitType.Parry, 30448 }, //[Target] parries, taking x points of damage.
|
||||
{ HitType.Block, 30447 }, //[Target] blocks, taking x points of damage.
|
||||
{ HitType.Hit, 30443 }, //[Target] tales x points of damage
|
||||
{ HitType.Crit, 30444 } //Critical! [Target] takes x points of damage.
|
||||
};
|
||||
|
||||
public static Dictionary<HitType, HitEffect> HitTypeEffectsPhysical = new Dictionary<HitType, HitEffect>()
|
||||
{
|
||||
{ HitType.Miss, 0 },
|
||||
{ HitType.Evade, HitEffect.Evade },
|
||||
{ HitType.Parry, HitEffect.Parry },
|
||||
{ HitType.Block, HitEffect.Block },
|
||||
{ HitType.Hit, HitEffect.Hit },
|
||||
{ HitType.Crit, HitEffect.Crit | HitEffect.CriticalHit }
|
||||
};
|
||||
|
||||
//Magic attacks can't miss, be blocked, or parried. Resists are technically evades
|
||||
public static Dictionary<HitType, HitEffect> HitTypeEffectsMagical = new Dictionary<HitType, HitEffect>()
|
||||
{
|
||||
{ HitType.SingleResist, HitEffect.WeakResist },
|
||||
{ HitType.DoubleResist, HitEffect.WeakResist },
|
||||
{ HitType.TripleResist, HitEffect.WeakResist },
|
||||
{ HitType.FullResist, HitEffect.FullResist },
|
||||
{ HitType.Hit, HitEffect.NoResist },
|
||||
{ HitType.Crit, HitEffect.Crit }
|
||||
};
|
||||
|
||||
public static Dictionary<KnockbackType, HitEffect> KnockbackEffects = new Dictionary<KnockbackType, HitEffect>()
|
||||
{
|
||||
{ KnockbackType.None, 0 },
|
||||
{ KnockbackType.Level1, HitEffect.KnockbackLv1 },
|
||||
{ KnockbackType.Level2, HitEffect.KnockbackLv2 },
|
||||
{ KnockbackType.Level3, HitEffect.KnockbackLv3 },
|
||||
{ KnockbackType.Level4, HitEffect.KnockbackLv4 },
|
||||
{ KnockbackType.Level5, HitEffect.KnockbackLv5 },
|
||||
{ KnockbackType.Clockwise1, HitEffect.KnockbackClockwiseLv1 },
|
||||
{ KnockbackType.Clockwise2, HitEffect.KnockbackClockwiseLv2 },
|
||||
{ KnockbackType.CounterClockwise1, HitEffect.KnockbackCounterClockwiseLv1 },
|
||||
{ KnockbackType.CounterClockwise2, HitEffect.KnockbackCounterClockwiseLv2 },
|
||||
{ KnockbackType.DrawIn, HitEffect.DrawIn }
|
||||
};
|
||||
|
||||
public static Dictionary<byte, ushort> ClassExperienceTextIds = new Dictionary<byte, ushort>()
|
||||
{
|
||||
{ 2, 33934 }, //Pugilist
|
||||
{ 3, 33935 }, //Gladiator
|
||||
{ 4, 33936 }, //Marauder
|
||||
{ 7, 33937 }, //Archer
|
||||
{ 8, 33938 }, //Lancer
|
||||
{ 10, 33939 }, //Sentinel, this doesn't exist anymore but it's still in the files so may as well put it here just in case
|
||||
{ 22, 33940 }, //Thaumaturge
|
||||
{ 23, 33941 }, //Conjurer
|
||||
{ 29, 33945 }, //Carpenter, for some reason there's a a few different messages between 33941 and 33945
|
||||
{ 30, 33946 }, //Blacksmith
|
||||
{ 31, 33947 }, //Armorer
|
||||
{ 32, 33948 }, //Goldsmith
|
||||
{ 33, 33949 }, //Leatherworker
|
||||
{ 34, 33950 }, //Weaver
|
||||
{ 35, 33951 }, //Alchemist
|
||||
{ 36, 33952 }, //Culinarian
|
||||
{ 39, 33953 }, //Miner
|
||||
{ 40, 33954 }, //Botanist
|
||||
{ 41, 33955 } //Fisher
|
||||
};
|
||||
|
||||
//Most of these numbers I'm fairly certain are correct. The repeated numbers at levels 23 and 48 I'm less sure about but they do match some weird spots in the EXP graph
|
||||
|
||||
public static ushort[] BASEEXP = {150, 150, 150, 150, 150, 150, 150, 150, 150, 150, //Level <= 10
|
||||
150, 150, 150, 150, 150, 150, 150, 150, 160, 170, //Level <= 20
|
||||
180, 190, 190, 200, 210, 220, 230, 240, 250, 260, //Level <= 30
|
||||
270, 280, 290, 300, 310, 320, 330, 340, 350, 360, //Level <= 40
|
||||
370, 380, 380, 390, 400, 410, 420, 430, 430, 440}; //Level <= 50
|
||||
|
||||
public static bool TryAttack(Character attacker, Character defender, CommandResult action, ref CommandResult error)
|
||||
{
|
||||
// todo: get hit rate, hit count, set hit effect
|
||||
//action.effectId |= (uint)(HitEffect.RecoilLv2 | HitEffect.Hit | HitEffect.HitVisual1);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static double CalculateDlvlModifier(short dlvl)
|
||||
{
|
||||
//this is just a really, really simplified version of the graph from http://kanican.livejournal.com/55915.html
|
||||
//actual formula is definitely more complicated
|
||||
//I'm going to assum these formulas are linear, and they're clamped so the modifier never goes below 0.
|
||||
double modifier = 0;
|
||||
|
||||
|
||||
if (dlvl >= 0)
|
||||
modifier = (.35 * dlvl) + .225;
|
||||
else
|
||||
modifier = (.01 * dlvl) + .25;
|
||||
|
||||
return modifier.Clamp(0, 1);
|
||||
}
|
||||
|
||||
//Damage calculations
|
||||
//Calculate damage of action
|
||||
//We could probably just do this when determining the action's hit type
|
||||
public static void CalculatePhysicalDamageTaken(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
short dlvl = (short)(defender.GetLevel() - attacker.GetLevel());
|
||||
|
||||
// todo: physical resistances
|
||||
|
||||
//dlvl, Defense, and Vitality all effect how much damage is taken after hittype takes effect
|
||||
//player attacks cannot do more than 9999 damage.
|
||||
//VIT is turned into Defense at a 3:2 ratio in calculatestats, so don't need to do that here
|
||||
double damageTakenPercent = 1 - (defender.GetMod(Modifier.DamageTakenDown) / 100.0);
|
||||
action.amount = (ushort)(action.amount - CalculateDlvlModifier(dlvl) * (defender.GetMod((uint)Modifier.Defense))).Clamp(0, 9999);
|
||||
action.amount = (ushort)(action.amount * damageTakenPercent).Clamp(0, 9999);
|
||||
}
|
||||
|
||||
|
||||
public static void CalculateSpellDamageTaken(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
short dlvl = (short)(defender.GetLevel() - attacker.GetLevel());
|
||||
|
||||
// todo: elemental resistances
|
||||
//Patch 1.19:
|
||||
//Magic Defense has been abolished and no longer appears in equipment attributes.
|
||||
//The effect of elemental attributes has been changed to that of reducing damage from element-based attacks.
|
||||
|
||||
//http://kanican.livejournal.com/55370.html:
|
||||
//elemental resistance stats are not actually related to resists (except for status effects), instead they impact damage taken
|
||||
|
||||
|
||||
//dlvl, Defense, and Vitality all effect how much damage is taken after hittype takes effect
|
||||
//player attacks cannot do more than 9999 damage.
|
||||
double damageTakenPercent = 1 - (defender.GetMod(Modifier.DamageTakenDown) / 100.0);
|
||||
action.amount = (ushort)(action.amount - CalculateDlvlModifier(dlvl) * (defender.GetMod((uint)Modifier.Defense) + 0.67 * defender.GetMod((uint)Modifier.Vitality))).Clamp(0, 9999);
|
||||
action.amount = (ushort)(action.amount * damageTakenPercent).Clamp(0, 9999);
|
||||
}
|
||||
|
||||
|
||||
public static void CalculateBlockDamage(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
double percentBlocked;
|
||||
|
||||
//Aegis boon forces a full block
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.AegisBoon))
|
||||
percentBlocked = 1.0;
|
||||
else
|
||||
{
|
||||
//Is this a case where VIT gives Block?
|
||||
percentBlocked = defender.GetMod((uint)Modifier.Block) * 0.002;//Every point of Block adds .2% to how much is blocked
|
||||
percentBlocked += defender.GetMod((uint)Modifier.Vitality) * 0.001;//Every point of vitality adds .1% to how much is blocked
|
||||
}
|
||||
|
||||
action.amountMitigated = (ushort)(action.amount * percentBlocked);
|
||||
action.amount = (ushort)(action.amount * (1.0 - percentBlocked));
|
||||
}
|
||||
|
||||
//don't know exact crit bonus formula
|
||||
public static void CalculateCritDamage(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
short dlvl = (short)(defender.GetLevel() - attacker.GetLevel());
|
||||
double bonus = (.04 * (dlvl * dlvl)) - 2 * dlvl;
|
||||
bonus += 1.20;
|
||||
double potencyModifier = (-.075 * dlvl) + 1.73;
|
||||
|
||||
// + potency bonus
|
||||
//bonus += attacker.GetMod((uint) Modifier.CriticalPotency) * potencyModifier;
|
||||
// - Crit resilience
|
||||
//bonus -= attacker.GetMod((uint)Modifier.CriticalResilience) * potencyModifier;
|
||||
|
||||
//need to add something for bonus potency as a part of skill (ie thundara, which breaks the cap)
|
||||
action.amount = (ushort)(action.amount * bonus.Clamp(1.15, 1.75));//min bonus of 115, max bonus of 175
|
||||
}
|
||||
|
||||
public static void CalculateParryDamage(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
double percentParry = 0.75;
|
||||
|
||||
action.amountMitigated = (ushort)(action.amount * (1 - percentParry));
|
||||
action.amount = (ushort)(action.amount * percentParry);
|
||||
}
|
||||
|
||||
//There are 3 or 4 tiers of resist that are flat 25% decreases in damage.
|
||||
//It's possible we could just calculate the damage at the same time as we determine the hit type (the same goes for the rest of the hit types)
|
||||
//Or we could have HitTypes for DoubleResist, TripleResist, and FullResist that get used here.
|
||||
public static void CalculateResistDamage(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
//Every tier of resist is a 25% reduction in damage. ie SingleResist is 25% damage taken down, Double is 50% damage taken down, etc
|
||||
double percentResist = 0.25 * (action.hitType - HitType.SingleResist + 1);
|
||||
|
||||
action.amountMitigated = (ushort)(action.amount * (1 - percentResist));
|
||||
action.amount = (ushort)(action.amount * percentResist);
|
||||
}
|
||||
|
||||
//It's weird that stoneskin is handled in C# and all other buffs are in scripts right now
|
||||
//But it's because stoneskin acts like both a preaction and postaction buff in that it falls off after damage is dealt but impacts how much damage is dealt
|
||||
public static void HandleStoneskin(Character defender, CommandResult action)
|
||||
{
|
||||
var mitigation = Math.Min(action.amount, defender.GetMod(Modifier.Stoneskin));
|
||||
|
||||
action.amount = (ushort) (action.amount - mitigation).Clamp(0, 9999);
|
||||
defender.SubtractMod((uint)Modifier.Stoneskin, mitigation);
|
||||
}
|
||||
|
||||
public static void DamageTarget(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer= null)
|
||||
{
|
||||
if (defender != null)
|
||||
{
|
||||
|
||||
//Bugfix, mobs that instantly died were insta disappearing due to lastAttacker == null.
|
||||
if (defender is BattleNpc)
|
||||
{
|
||||
var bnpc = defender as BattleNpc;
|
||||
if (bnpc.lastAttacker == null)
|
||||
bnpc.lastAttacker = attacker;
|
||||
}
|
||||
|
||||
defender.DelHP((short)action.amount, actionContainer);
|
||||
attacker.OnDamageDealt(defender, skill, action, actionContainer);
|
||||
defender.OnDamageTaken(attacker, skill, action, actionContainer);
|
||||
|
||||
// todo: other stuff too
|
||||
if (defender is BattleNpc)
|
||||
{
|
||||
var bnpc = defender as BattleNpc;
|
||||
if (!bnpc.hateContainer.HasHateForTarget(attacker))
|
||||
{
|
||||
bnpc.hateContainer.AddBaseHate(attacker);
|
||||
}
|
||||
bnpc.hateContainer.UpdateHate(attacker, action.enmity);
|
||||
bnpc.lastAttacker = attacker;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void HealTarget(Character caster, Character target, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
if (target != null)
|
||||
{
|
||||
target.AddHP(action.amount, actionContainer);
|
||||
|
||||
target.statusEffects.CallLuaFunctionByFlag((uint)StatusEffectFlags.ActivateOnHealed, "onHealed", caster, target, skill, action, actionContainer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Rate Functions
|
||||
|
||||
//How is accuracy actually calculated?
|
||||
public static double GetHitRate(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
double hitRate = 80.0;
|
||||
|
||||
//Add raw hit rate buffs, subtract raw evade buffs, take into account skill's accuracy modifier.
|
||||
double hitBuff = attacker.GetMod(Modifier.RawHitRate);
|
||||
double evadeBuff = defender.GetMod(Modifier.RawEvadeRate);
|
||||
float modifier = skill != null ? skill.accuracyModifier : 0;
|
||||
hitRate += (hitBuff + modifier).Clamp(0, 100.0);
|
||||
hitRate -= evadeBuff;
|
||||
return hitRate.Clamp(0, 100.0);
|
||||
}
|
||||
|
||||
//Whats the parry formula?
|
||||
public static double GetParryRate(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
//Can't parry with shield, can't parry rear attacks
|
||||
if (defender.GetMod((uint)Modifier.CanBlock) != 0 || action.param == (byte) HitDirection.Rear)
|
||||
return 0;
|
||||
|
||||
double parryRate = 10.0;
|
||||
|
||||
parryRate += defender.GetMod(Modifier.Parry) * 0.1;//.1% rate for every point of Parry
|
||||
|
||||
return parryRate + (defender.GetMod(Modifier.RawParryRate));
|
||||
}
|
||||
|
||||
public static double GetCritRate(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
if (action.actionType == ActionType.Status)
|
||||
return 0.0;
|
||||
|
||||
//using 10.0 for now since gear isn't working
|
||||
double critRate = 10.0;// 0.16 * attacker.GetMod((uint)Modifier.CritRating);//Crit rating adds .16% per point
|
||||
|
||||
//Add additional crit rate from skill
|
||||
//Should this be a raw percent or a flat crit raitng? the wording on skills/buffs isn't clear.
|
||||
critRate += 0.16 * (skill != null ? skill.bonusCritRate : 0);
|
||||
|
||||
return critRate + attacker.GetMod(Modifier.RawCritRate);
|
||||
}
|
||||
|
||||
//http://kanican.livejournal.com/55370.html
|
||||
// todo: figure that out
|
||||
public static double GetResistRate(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
// todo: add elemental stuff
|
||||
//Can only resist spells?
|
||||
if (action.commandType != CommandType.Spell && action.actionProperty <= ActionProperty.Projectile)
|
||||
return 0.0;
|
||||
|
||||
return 15.0 + defender.GetMod(Modifier.RawResistRate);
|
||||
}
|
||||
|
||||
//Block Rate follows 4 simple rules:
|
||||
//(1) Every point in DEX gives +0.1% rate
|
||||
//(2) Every point in "Block Rate" gives +0.2% rate
|
||||
//(3) True block proc rate is capped at 75%. No clue on a possible floor.
|
||||
//(4) The baseline rate is based on dLVL only(mob stats play no role). The baseline rate is summarized in this raw data sheet: https://imgbox.com/aasLyaJz
|
||||
public static double GetBlockRate(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
//Shields are required to block and can't block from rear.
|
||||
if (defender.GetMod((uint)Modifier.CanBlock) == 0 || action.param == (byte)HitDirection.Rear)
|
||||
return 0;
|
||||
|
||||
short dlvl = (short)(defender.GetLevel() - attacker.GetLevel());
|
||||
double blockRate = (2.5 * dlvl) + 5; // Base block rate
|
||||
|
||||
//Is this one of those thing where DEX gives block rate and this would be taking DEX into account twice?
|
||||
blockRate += defender.GetMod((uint)Modifier.Dexterity) * 0.1;// .1% for every dex
|
||||
blockRate += defender.GetMod((uint)Modifier.BlockRate) * 0.2;// .2% for every block rate
|
||||
|
||||
return Math.Min(blockRate, 25.0) + defender.GetMod((uint)Modifier.RawBlockRate);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static bool TryCrit(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
if ((Program.Random.NextDouble() * 100) <= action.critRate)
|
||||
{
|
||||
action.hitType = HitType.Crit;
|
||||
CalculateCritDamage(attacker, defender, skill, action);
|
||||
|
||||
if(skill != null)
|
||||
skill.actionCrit = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//This probably isn't totally correct but it's close enough for now.
|
||||
//Full Resists seem to be calculated in a different way because the resist rates don't seem to line up with kanikan's testing (their tests didn't show any full resists)
|
||||
//Non-spells with elemental damage can be resisted, it just doesnt say in the chat that they were. As far as I can tell, all mob-specific attacks are considered not to be spells
|
||||
public static bool TryResist(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
//The rate degrades for each check. Meaning with 100% resist, the attack will always be resisted, but it won't necessarily be a triple or full resist
|
||||
//Rates beyond 100 still increase the chance for higher resist tiers though
|
||||
double rate = action.resistRate;
|
||||
|
||||
int i = -1;
|
||||
|
||||
while ((Program.Random.NextDouble() * 100) <= rate && i < 4)
|
||||
{
|
||||
rate /= 2;
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != -1)
|
||||
{
|
||||
action.hitType = (HitType) ((int) HitType.SingleResist + i);
|
||||
CalculateResistDamage(attacker, defender, skill, action);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryBlock(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
if ((Program.Random.NextDouble() * 100) <= action.blockRate)
|
||||
{
|
||||
action.hitType = HitType.Block;
|
||||
CalculateBlockDamage(attacker, defender, skill, action);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool TryParry(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
if ((Program.Random.NextDouble() * 100) <= action.parryRate)
|
||||
{
|
||||
action.hitType = HitType.Parry;
|
||||
CalculateParryDamage(attacker, defender, skill, action);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//TryMiss instead of tryHit because hits are the default and don't change damage
|
||||
public static bool TryMiss(Character attacker, Character defender, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
if ((Program.Random.NextDouble() * 100) >= GetHitRate(attacker, defender, skill, action))
|
||||
{
|
||||
action.hitType = (ushort)HitType.Miss;
|
||||
//On misses, the entire amount is considered mitigated
|
||||
action.amountMitigated = action.amount;
|
||||
action.amount = 0;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Hit Effecthelpers. Different types of hit effects hits use some flags for different things, so they're split into physical, magical, heal, and status
|
||||
*/
|
||||
public static void DoAction(Character caster, Character target, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
switch (action.actionType)
|
||||
{
|
||||
case (ActionType.Physical):
|
||||
FinishActionPhysical(caster, target, skill, action, actionContainer);
|
||||
break;
|
||||
case (ActionType.Magic):
|
||||
FinishActionSpell(caster, target, skill, action, actionContainer);
|
||||
break;
|
||||
case (ActionType.Heal):
|
||||
FinishActionHeal(caster, target, skill, action, actionContainer);
|
||||
break;
|
||||
case (ActionType.Status):
|
||||
FinishActionStatus(caster, target, skill, action, actionContainer);
|
||||
break;
|
||||
default:
|
||||
action.effectId = (uint) HitEffect.AnimationEffectType;
|
||||
actionContainer.AddAction(action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Determine the hit type, set the hit effect, modify damage based on stoneskin and hit type, hit target
|
||||
public static void FinishActionPhysical(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
//Figure out the hit type and change damage depending on hit type
|
||||
if (!TryMiss(attacker, defender, skill, action))
|
||||
{
|
||||
//Handle Stoneskin here because it seems like stoneskin mitigates damage done before taking into consideration crit/block/parry damage reductions.
|
||||
//This is based on the fact that a 0 damage attack due to stoneskin will heal for 0 with Aegis Boon, meaning Aegis Boon didn't mitigate any damage
|
||||
HandleStoneskin(defender, action);
|
||||
|
||||
//Crits can't be blocked (is this true for Aegis Boon and Divine Veil?) or parried so they are checked first.
|
||||
if (!TryCrit(attacker, defender, skill, action))
|
||||
//Block and parry order don't really matter because if you can block you can't parry and vice versa
|
||||
if (!TryBlock(attacker, defender, skill, action))
|
||||
if(!TryParry(attacker, defender, skill, action))
|
||||
//Finally if it's none of these, the attack was a hit
|
||||
action.hitType = HitType.Hit;
|
||||
}
|
||||
|
||||
//Actions have different text ids depending on whether they're a part of a multi-hit ws or not.
|
||||
Dictionary<HitType, ushort> textIds = PhysicalHitTypeTextIds;
|
||||
|
||||
//If this is the first hit of a multi hit command, add the "You use [command] on [target]" action
|
||||
//Needs to be done here because certain buff messages appear before it.
|
||||
if (skill != null && skill.numHits > 1)
|
||||
{
|
||||
if (action.hitNum == 1)
|
||||
actionContainer?.AddAction(new CommandResult(attacker.actorId, 30441, 0));
|
||||
|
||||
textIds = MultiHitTypeTextIds;
|
||||
}
|
||||
|
||||
//Set the correct textId
|
||||
action.worldMasterTextId = textIds[action.hitType];
|
||||
|
||||
//Set the hit effect
|
||||
SetHitEffectPhysical(attacker, defender, skill, action, actionContainer);
|
||||
|
||||
//Modify damage based on defender's stats
|
||||
CalculatePhysicalDamageTaken(attacker, defender, skill, action);
|
||||
|
||||
actionContainer.AddAction(action);
|
||||
action.enmity = (ushort) (action.enmity * (skill != null ? skill.enmityModifier : 1));
|
||||
|
||||
//Damage the target
|
||||
DamageTarget(attacker, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public static void FinishActionSpell(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
//I'm assuming that like physical attacks stoneskin is taken into account before mitigation
|
||||
HandleStoneskin(defender, action);
|
||||
|
||||
//Determine the hit type of the action
|
||||
//Spells don't seem to be able to miss, instead magic acc/eva is used for resists (which are generally called evades in game)
|
||||
//Unlike blocks and parries, crits do not go through resists.
|
||||
if (!TryResist(attacker, defender, skill, action))
|
||||
{
|
||||
if (!TryCrit(attacker, defender, skill, action))
|
||||
action.hitType = HitType.Hit;
|
||||
}
|
||||
|
||||
//There are no multi-hit spells, so we don't need to take that into account
|
||||
action.worldMasterTextId = MagicalHitTypeTextIds[action.hitType];
|
||||
|
||||
//Set the hit effect
|
||||
SetHitEffectSpell(attacker, defender, skill, action);
|
||||
|
||||
HandleStoneskin(defender, action);
|
||||
|
||||
CalculateSpellDamageTaken(attacker, defender, skill, action);
|
||||
|
||||
actionContainer.AddAction(action);
|
||||
|
||||
DamageTarget(attacker, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public static void FinishActionHeal(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
//Set the hit effect
|
||||
SetHitEffectHeal(attacker, defender, skill, action);
|
||||
|
||||
actionContainer.AddAction(action);
|
||||
|
||||
HealTarget(attacker, defender, skill, action, actionContainer);
|
||||
}
|
||||
|
||||
public static void FinishActionStatus(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
//Set the hit effect
|
||||
SetHitEffectStatus(attacker, defender, skill, action);
|
||||
|
||||
TryStatus(attacker, defender, skill, action, actionContainer, false);
|
||||
|
||||
actionContainer.AddAction(action);
|
||||
}
|
||||
|
||||
public static void SetHitEffectPhysical(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer)
|
||||
{
|
||||
var hitEffect = HitEffect.HitEffectType;
|
||||
HitType hitType = action.hitType;
|
||||
|
||||
//Don't know what recoil is actually based on, just guessing
|
||||
//Crit is 2 and 3 together
|
||||
if (hitType == HitType.Crit)
|
||||
hitEffect |= HitEffect.CriticalHit;
|
||||
else
|
||||
{
|
||||
//It's not clear what recoil level is based on for physical attacks
|
||||
double percentDealt = (100.0 * (action.amount / defender.GetMaxHP()));
|
||||
if (percentDealt > 5.0)
|
||||
hitEffect |= HitEffect.RecoilLv2;
|
||||
else if (percentDealt > 10)
|
||||
hitEffect |= HitEffect.RecoilLv3;
|
||||
}
|
||||
|
||||
hitEffect |= HitTypeEffectsPhysical[hitType];
|
||||
|
||||
//For combos that land, add the combo effect
|
||||
if (skill != null && skill.isCombo && action.ActionLanded() && !skill.comboEffectAdded)
|
||||
{
|
||||
hitEffect |= (HitEffect)(skill.comboStep << 15);
|
||||
skill.comboEffectAdded = true;
|
||||
}
|
||||
|
||||
//if attack hit the target, take into account protective status effects
|
||||
if (hitType >= HitType.Parry)
|
||||
{
|
||||
//Protect / Shell only show on physical/ magical attacks respectively.
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Protect) || defender.statusEffects.HasStatusEffect(StatusEffectId.Protect2))
|
||||
if (action != null)
|
||||
hitEffect |= HitEffect.Protect;
|
||||
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Stoneskin))
|
||||
if (action != null)
|
||||
hitEffect |= HitEffect.Stoneskin;
|
||||
}
|
||||
|
||||
action.effectId = (uint)hitEffect;
|
||||
}
|
||||
|
||||
public static void SetHitEffectSpell(Character attacker, Character defender, BattleCommand skill, CommandResult action, CommandResultContainer actionContainer = null)
|
||||
{
|
||||
var hitEffect = HitEffect.MagicEffectType;
|
||||
HitType hitType = action.hitType;
|
||||
|
||||
|
||||
hitEffect |= HitTypeEffectsMagical[hitType];
|
||||
|
||||
if (skill != null && skill.isCombo && !skill.comboEffectAdded)
|
||||
{
|
||||
hitEffect |= (HitEffect)(skill.comboStep << 15);
|
||||
skill.comboEffectAdded = true;
|
||||
}
|
||||
|
||||
//if attack hit the target, take into account protective status effects
|
||||
if (action.ActionLanded())
|
||||
{
|
||||
//Protect / Shell only show on physical/ magical attacks respectively.
|
||||
//The magic hit effect category only has a flag for shell (and another shield effect that seems unused)
|
||||
//Even though traited protect gives magic defense, the shell effect doesn't play on attacks
|
||||
//This also means stoneskin doesnt show, but it does reduce damage
|
||||
if (defender.statusEffects.HasStatusEffect(StatusEffectId.Shell))
|
||||
if (action != null)
|
||||
hitEffect |= HitEffect.MagicShell;
|
||||
}
|
||||
action.effectId = (uint)hitEffect;
|
||||
}
|
||||
|
||||
|
||||
public static void SetHitEffectHeal(Character caster, Character receiver, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
var hitEffect = HitEffect.MagicEffectType | HitEffect.Heal;
|
||||
//Heals use recoil levels in some way as well. Possibly for very low health clutch heals or based on percentage of current health healed (not max health).
|
||||
// todo: figure recoil levels out for heals
|
||||
hitEffect |= HitEffect.RecoilLv3;
|
||||
//do heals crit?
|
||||
|
||||
action.effectId = (uint)hitEffect;
|
||||
}
|
||||
|
||||
public static void SetHitEffectStatus(Character caster, Character receiver, BattleCommand skill, CommandResult action)
|
||||
{
|
||||
var hitEffect = (uint)HitEffect.StatusEffectType | skill.statusId;
|
||||
action.effectId = hitEffect;
|
||||
|
||||
action.hitType = HitType.Hit;
|
||||
}
|
||||
|
||||
public static uint CalculateSpellCost(Character caster, Character target, BattleCommand spell)
|
||||
{
|
||||
var scaledCost = spell.CalculateMpCost(caster);
|
||||
|
||||
// todo: calculate cost for mob/player
|
||||
if (caster is BattleNpc)
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
return scaledCost;
|
||||
}
|
||||
|
||||
|
||||
//IsAdditional is needed because additional actions may be required for some actions' effects
|
||||
//For instance, Goring Blade's bleed effect requires another action so the first action can still show damage numbers
|
||||
//Sentinel doesn't require an additional action because it doesn't need to show those numbers
|
||||
//this is stupid
|
||||
public static void TryStatus(Character caster, Character target, BattleCommand skill, CommandResult action, CommandResultContainer results, bool isAdditional = true)
|
||||
{
|
||||
double rand = Program.Random.NextDouble();
|
||||
|
||||
//Statuses only land for non-resisted attacks and attacks that hit
|
||||
if (skill != null && skill.statusId != 0 && (action.ActionLanded()) && rand < skill.statusChance)
|
||||
{
|
||||
StatusEffect effect = Server.GetWorldManager().GetStatusEffect(skill.statusId);
|
||||
//Because combos might change duration or tier
|
||||
if (effect != null)
|
||||
{
|
||||
effect.SetDuration(skill.statusDuration);
|
||||
effect.SetTier(skill.statusTier);
|
||||
effect.SetMagnitude(skill.statusMagnitude);
|
||||
effect.SetOwner(target);
|
||||
effect.SetSource(caster);
|
||||
|
||||
if (target.statusEffects.AddStatusEffect(effect, caster))
|
||||
{
|
||||
//If we need an extra action to show the status text
|
||||
if (isAdditional)
|
||||
results.AddAction(target.actorId, effect.GetStatusGainTextId(), skill.statusId | (uint) HitEffect.StatusEffectType);
|
||||
}
|
||||
else
|
||||
action.worldMasterTextId = 32002;//Is this right?
|
||||
}
|
||||
else
|
||||
{
|
||||
//until all effects are scripted and added to db just doing this
|
||||
if (target.statusEffects.AddStatusEffect(skill.statusId, skill.statusTier, skill.statusMagnitude, skill.statusDuration, 3000))
|
||||
{
|
||||
//If we need an extra action to show the status text
|
||||
if (isAdditional)
|
||||
results.AddAction(target.actorId, 30328, skill.statusId | (uint) HitEffect.StatusEffectType);
|
||||
}
|
||||
else
|
||||
action.worldMasterTextId = 32002;//Is this right?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Convert a HitDirection to a BattleCommandPositionBonus. Basically just combining left/right into flank
|
||||
public static BattleCommandPositionBonus ConvertHitDirToPosition(HitDirection hitDir)
|
||||
{
|
||||
BattleCommandPositionBonus position = BattleCommandPositionBonus.None;
|
||||
|
||||
switch (hitDir)
|
||||
{
|
||||
case (HitDirection.Front):
|
||||
position = BattleCommandPositionBonus.Front;
|
||||
break;
|
||||
case (HitDirection.Right):
|
||||
case (HitDirection.Left):
|
||||
position = BattleCommandPositionBonus.Flank;
|
||||
break;
|
||||
case (HitDirection.Rear):
|
||||
position = BattleCommandPositionBonus.Rear;
|
||||
break;
|
||||
}
|
||||
return position;
|
||||
}
|
||||
|
||||
|
||||
#region experience helpers
|
||||
//See 1.19 patch notes for exp info.
|
||||
public static ushort GetBaseEXP(Player player, BattleNpc mob)
|
||||
{
|
||||
//The way EXP seems to work for most enemies is that it gets the lower character's level, gets the base exp for that level, then uses dlvl to modify that exp
|
||||
//Less than -19 dlvl gives 0 exp and no message is sent.
|
||||
//This equation doesn't seem to work for certain bosses or NMs.
|
||||
//Some enemies might give less EXP? Unsure on this. It seems like there might have been a change in base exp amounts after 1.19
|
||||
|
||||
//Example:
|
||||
//Level 50 in a party kills a level 45 enemy
|
||||
//Base exp is 400, as that's the base EXP for level 45
|
||||
//That's multiplied by the dlvl modifier for -5, which is 0.5625, which gives 225
|
||||
//That's then multiplied by the party modifier, which seems to be 0.667 regardless of party size, which gives 150
|
||||
//150 is then modified by bonus experience from food, rested exp, links, and chains
|
||||
|
||||
int dlvl = mob.GetLevel() - player.GetLevel();
|
||||
if (dlvl <= -20)
|
||||
return 0;
|
||||
|
||||
int baseLevel = Math.Min(player.GetLevel(), mob.GetLevel());
|
||||
ushort baseEXP = BASEEXP[baseLevel - 1];
|
||||
|
||||
double dlvlModifier = 1.0;
|
||||
|
||||
//There's 2 functions depending on if the dlvl is positive or negative.
|
||||
if (dlvl >= 0)
|
||||
//I'm not sure if this caps out at some point. This is correct up to at least +9 dlvl though.
|
||||
dlvlModifier += 0.2 * dlvl;
|
||||
else
|
||||
//0.1x + 0.0025x^2
|
||||
dlvlModifier += 0.1 * dlvl + 0.0025 * (dlvl * dlvl);
|
||||
|
||||
//The party modifier isn't clear yet. It seems like it might just be 0.667 for any number of members in a group, but the 1.19 notes say it's variable
|
||||
//There also seem to be some cases where it simply doesn't apply but it isn't obvious if that's correct or when it applies if it is correct
|
||||
double partyModifier = player.currentParty.GetMemberCount() == 1 ? 1.0 : 0.667;
|
||||
|
||||
baseEXP = (ushort) (baseEXP * dlvlModifier * partyModifier);
|
||||
|
||||
return baseEXP;
|
||||
}
|
||||
|
||||
//Gets the EXP bonus when enemies link
|
||||
public static byte GetLinkBonus(ushort linkCount)
|
||||
{
|
||||
byte bonus = 0;
|
||||
|
||||
switch (linkCount)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (1):
|
||||
bonus = 25;
|
||||
break;
|
||||
case (2):
|
||||
bonus = 50;
|
||||
break;
|
||||
case (3):
|
||||
bonus = 75;
|
||||
break;
|
||||
case (4):
|
||||
default:
|
||||
bonus = 100;
|
||||
break;
|
||||
}
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
//Gets EXP chain bonus for Attacker fighting Defender
|
||||
//Official text on EXP Chains: An EXP Chain occurs when players consecutively defeat enemies of equal or higher level than themselves within a specific amount of time.
|
||||
//Assuming this means that there is no bonus for enemies below player's level and EXP chains are specific to the person, not party
|
||||
public static byte GetChainBonus(ushort tier)
|
||||
{
|
||||
byte bonus = 0;
|
||||
|
||||
switch (tier)
|
||||
{
|
||||
case (0):
|
||||
break;
|
||||
case (1):
|
||||
bonus = 20;
|
||||
break;
|
||||
case (2):
|
||||
bonus = 25;
|
||||
break;
|
||||
case (3):
|
||||
bonus = 30;
|
||||
break;
|
||||
case (4):
|
||||
bonus = 40;
|
||||
break;
|
||||
default:
|
||||
bonus = 50;
|
||||
break;
|
||||
}
|
||||
return bonus;
|
||||
}
|
||||
|
||||
public static byte GetChainTimeLimit(ushort tier)
|
||||
{
|
||||
byte timeLimit = 0;
|
||||
|
||||
switch (tier)
|
||||
{
|
||||
case (0):
|
||||
timeLimit = 100;
|
||||
break;
|
||||
case (1):
|
||||
timeLimit = 80;
|
||||
break;
|
||||
case (2):
|
||||
timeLimit = 60;
|
||||
break;
|
||||
case (3):
|
||||
timeLimit = 20;
|
||||
break;
|
||||
default:
|
||||
timeLimit = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
return timeLimit;
|
||||
}
|
||||
|
||||
//Calculates bonus EXP for Links and Chains
|
||||
public static void AddBattleBonusEXP(Player attacker, BattleNpc defender, CommandResultContainer actionContainer)
|
||||
{
|
||||
ushort baseExp = GetBaseEXP(attacker, defender);
|
||||
|
||||
//Only bother calculating the rest if there's actually exp to be gained.
|
||||
//0 exp sends no message
|
||||
if (baseExp > 0)
|
||||
{
|
||||
int totalBonus = 0;//GetMod(Modifier.bonusEXP)
|
||||
|
||||
var linkCount = defender.GetMobMod(MobModifier.LinkCount);
|
||||
totalBonus += GetLinkBonus((byte)Math.Min(linkCount, 255));
|
||||
|
||||
StatusEffect effect = attacker.statusEffects.GetStatusEffectById((uint)StatusEffectId.EXPChain);
|
||||
ushort expChainNumber = 0;
|
||||
uint timeLimit = 100;
|
||||
if (effect != null)
|
||||
{
|
||||
expChainNumber = effect.GetTier();
|
||||
timeLimit = (uint)(GetChainTimeLimit(expChainNumber));
|
||||
actionContainer?.AddEXPAction(new CommandResult(attacker.actorId, 33919, 0, expChainNumber, (byte)timeLimit));
|
||||
}
|
||||
|
||||
totalBonus += GetChainBonus(expChainNumber);
|
||||
|
||||
StatusEffect newChain = Server.GetWorldManager().GetStatusEffect((uint)StatusEffectId.EXPChain);
|
||||
newChain.SetDuration(timeLimit);
|
||||
newChain.SetTier((byte)(Math.Min(expChainNumber + 1, 255)));
|
||||
attacker.statusEffects.AddStatusEffect(newChain, attacker);
|
||||
|
||||
actionContainer?.AddEXPActions(attacker.AddExp(baseExp, (byte)attacker.GetClass(), (byte)(totalBonus.Min(255))));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,465 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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
|
||||
}
|
||||
}
|
@ -1,468 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,58 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects.chara
|
||||
{
|
||||
class PlayerWork
|
||||
{
|
||||
public byte tribe;
|
||||
public byte guardian;
|
||||
public byte birthdayMonth;
|
||||
public byte birthdayDay;
|
||||
public byte initialTown;
|
||||
|
||||
public float restBonusExpRate = 1.5f;
|
||||
|
||||
public uint[] questScenario = new uint[16];
|
||||
public uint[] questGuildleve = new uint[8];
|
||||
|
||||
public bool[] questScenarioComplete = new bool[2048];
|
||||
public bool[] questGuildleveComplete = new bool[2048];
|
||||
|
||||
public bool isContentsCommand;
|
||||
|
||||
public uint castCommandClient;
|
||||
public uint castEndClient;
|
||||
|
||||
public int[] comboNextCommandId = new int[2];
|
||||
public float comboCostBonusRate;
|
||||
|
||||
public bool isRemainBonusPoint;
|
||||
|
||||
public bool[] npcLinkshellChatCalling = new bool[64];
|
||||
public bool[] npcLinkshellChatExtra = new bool[64];
|
||||
|
||||
public int variableCommandConfirmWarp;
|
||||
public string variableCommandConfirmWarpSender;
|
||||
public int variableCommandConfirmWarpSenderByID;
|
||||
public byte variableCommandConfirmWarpSenderSex;
|
||||
public int variableCommandConfirmWarpPlace;
|
||||
|
||||
public int variableCommandConfirmRaise;
|
||||
public string variableCommandConfirmRaiseSender;
|
||||
public int variableCommandConfirmRaiseSenderByID;
|
||||
public byte variableCommandConfirmRaiseSenderSex;
|
||||
public int variableCommandConfirmRaisePlace;
|
||||
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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
|
||||
{
|
||||
class Command : Actor
|
||||
{
|
||||
|
||||
public Command(uint actorID, string name) : base(actorID)
|
||||
{
|
||||
actorName = name;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.Actors
|
||||
{
|
||||
class DebugProg : Actor
|
||||
{
|
||||
|
||||
public DebugProg()
|
||||
: base(0x5FF80002)
|
||||
{
|
||||
this.displayNameId = 0;
|
||||
this.customDisplayName = "debug";
|
||||
|
||||
this.actorName = "debug";
|
||||
this.className = "Debug";
|
||||
}
|
||||
|
||||
public override SubPacket CreateScriptBindPacket()
|
||||
{
|
||||
List<LuaParam> lParams;
|
||||
lParams = LuaUtils.CreateLuaParamList("/System/Debug.prog", false, false, false, false, true, 0xC51F, true, true);
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
|
||||
}
|
||||
|
||||
public override List<SubPacket> GetSpawnPackets()
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
subpackets.Add(CreateAddActorPacket(0));
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpawnPositonPacket(0x1));
|
||||
subpackets.Add(CreateNamePacket());
|
||||
subpackets.Add(CreateStatePacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.Add(CreateScriptBindPacket());
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,342 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.area;
|
||||
using Meteor.Map.actors.group;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using MoonSharp.Interpreter;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.director
|
||||
{
|
||||
class Director : Actor
|
||||
{
|
||||
private uint directorId;
|
||||
private string directorScriptPath;
|
||||
private List<Actor> members = new List<Actor>();
|
||||
protected ContentGroup contentGroup;
|
||||
private bool isCreated = false;
|
||||
private bool isDeleted = false;
|
||||
private bool isDeleting = false;
|
||||
|
||||
private Script directorScript;
|
||||
private Coroutine currentCoroutine;
|
||||
|
||||
public Director(uint id, Area zone, string directorPath, bool hasContentGroup, params object[] args)
|
||||
: base((6 << 28 | zone.actorId << 19 | (uint)id))
|
||||
{
|
||||
directorId = id;
|
||||
this.zone = zone;
|
||||
this.zoneId = zone.actorId;
|
||||
directorScriptPath = directorPath;
|
||||
|
||||
LoadLuaScript();
|
||||
|
||||
if (hasContentGroup)
|
||||
contentGroup = Server.GetWorldManager().CreateContentGroup(this, GetMembers());
|
||||
|
||||
eventConditions = new EventList();
|
||||
eventConditions.noticeEventConditions = new List<EventList.NoticeEventCondition>();
|
||||
eventConditions.noticeEventConditions.Add(new EventList.NoticeEventCondition("noticeEvent", 0xE,0x0));
|
||||
eventConditions.noticeEventConditions.Add(new EventList.NoticeEventCondition("noticeRequest", 0x0, 0x1));
|
||||
eventConditions.noticeEventConditions.Add(new EventList.NoticeEventCondition("reqForChild", 0x0, 0x1));
|
||||
}
|
||||
|
||||
public override SubPacket CreateScriptBindPacket()
|
||||
{
|
||||
List<LuaParam> actualLParams = new List<LuaParam>();
|
||||
actualLParams.Insert(0, new LuaParam(2, classPath));
|
||||
actualLParams.Insert(1, new LuaParam(4, 4));
|
||||
actualLParams.Insert(2, new LuaParam(4, 4));
|
||||
actualLParams.Insert(3, new LuaParam(4, 4));
|
||||
actualLParams.Insert(4, new LuaParam(4, 4));
|
||||
actualLParams.Insert(5, new LuaParam(4, 4));
|
||||
|
||||
List<LuaParam> lparams = LuaEngine.GetInstance().CallLuaFunctionForReturn(null, this, "init", false);
|
||||
for (int i = 1; i < lparams.Count; i++)
|
||||
actualLParams.Add(lparams[i]);
|
||||
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, actualLParams);
|
||||
}
|
||||
|
||||
public override List<SubPacket> GetSpawnPackets(ushort spawnType = 1)
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
subpackets.Add(CreateAddActorPacket(0));
|
||||
subpackets.AddRange(GetEventConditionPackets());
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpawnPositonPacket(0));
|
||||
subpackets.Add(CreateNamePacket());
|
||||
subpackets.Add(CreateStatePacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.Add(CreateScriptBindPacket());
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
public override List<SubPacket> GetInitPackets()
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
SetActorPropetyPacket initProperties = new SetActorPropetyPacket("/_init");
|
||||
initProperties.AddTarget();
|
||||
subpackets.Add(initProperties.BuildPacket(actorId));
|
||||
return subpackets;
|
||||
}
|
||||
|
||||
public void OnTalkEvent(Player player, Npc npc)
|
||||
{
|
||||
LuaEngine.GetInstance().CallLuaFunction(player, this, "onTalkEvent", false, npc);
|
||||
}
|
||||
|
||||
public void OnCommandEvent(Player player, Command command)
|
||||
{
|
||||
LuaEngine.GetInstance().CallLuaFunction(player, this, "onCommandEvent", false, command);
|
||||
}
|
||||
|
||||
public void StartDirector(bool spawnImmediate, params object[] args)
|
||||
{
|
||||
object[] args2 = new object[args.Length + 1];
|
||||
args2[0] = this;
|
||||
Array.Copy(args, 0, args2, 1, args.Length);
|
||||
|
||||
List<LuaParam> lparams = CallLuaScript("init", args2);
|
||||
|
||||
if (lparams != null && lparams.Count >= 1 && lparams[0].value is string)
|
||||
{
|
||||
classPath = (string)lparams[0].value;
|
||||
className = classPath.Substring(classPath.LastIndexOf("/") + 1);
|
||||
GenerateActorName((int)directorId);
|
||||
isCreated = true;
|
||||
}
|
||||
|
||||
if (isCreated && spawnImmediate)
|
||||
{
|
||||
if (contentGroup != null)
|
||||
contentGroup.Start();
|
||||
|
||||
foreach (Player p in GetPlayerMembers())
|
||||
{
|
||||
p.QueuePackets(GetSpawnPackets());
|
||||
p.QueuePackets(GetInitPackets());
|
||||
}
|
||||
}
|
||||
|
||||
if (this is GuildleveDirector)
|
||||
{
|
||||
((GuildleveDirector)this).LoadGuildleve();
|
||||
}
|
||||
|
||||
CallLuaScript("main", this, contentGroup);
|
||||
}
|
||||
|
||||
public void StartContentGroup()
|
||||
{
|
||||
if (contentGroup != null)
|
||||
contentGroup.Start();
|
||||
}
|
||||
|
||||
public void EndDirector()
|
||||
{
|
||||
isDeleting = true;
|
||||
|
||||
if (contentGroup != null)
|
||||
contentGroup.DeleteGroup();
|
||||
|
||||
if (this is GuildleveDirector)
|
||||
((GuildleveDirector)this).EndGuildleveDirector();
|
||||
|
||||
List<Actor> players = GetPlayerMembers();
|
||||
foreach (Actor player in players)
|
||||
((Player)player).RemoveDirector(this);
|
||||
members.Clear();
|
||||
isDeleted = true;
|
||||
Server.GetWorldManager().GetZone(zoneId).DeleteDirector(actorId);
|
||||
}
|
||||
|
||||
public void AddMember(Actor actor)
|
||||
{
|
||||
if (!members.Contains(actor))
|
||||
{
|
||||
members.Add(actor);
|
||||
|
||||
if (actor is Player)
|
||||
((Player)actor).AddDirector(this);
|
||||
|
||||
if (contentGroup != null)
|
||||
contentGroup.AddMember(actor);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveMember(Actor actor)
|
||||
{
|
||||
if (members.Contains(actor))
|
||||
members.Remove(actor);
|
||||
if (contentGroup != null)
|
||||
contentGroup.RemoveMember(actor.actorId);
|
||||
if (GetPlayerMembers().Count == 0 && !isDeleting)
|
||||
EndDirector();
|
||||
}
|
||||
|
||||
public List<Actor> GetMembers()
|
||||
{
|
||||
return members;
|
||||
}
|
||||
|
||||
public List<Actor> GetPlayerMembers()
|
||||
{
|
||||
return members.FindAll(s => s is Player);
|
||||
}
|
||||
|
||||
public List<Actor> GetNpcMembers()
|
||||
{
|
||||
return members.FindAll(s => s is Npc);
|
||||
}
|
||||
|
||||
public bool IsCreated()
|
||||
{
|
||||
return isCreated;
|
||||
}
|
||||
|
||||
public bool IsDeleted()
|
||||
{
|
||||
return isDeleted;
|
||||
}
|
||||
|
||||
public bool HasContentGroup()
|
||||
{
|
||||
return contentGroup != null;
|
||||
}
|
||||
|
||||
public ContentGroup GetContentGroup()
|
||||
{
|
||||
return contentGroup;
|
||||
}
|
||||
|
||||
public void GenerateActorName(int actorNumber)
|
||||
{
|
||||
//Format Class Name
|
||||
string className = this.className;
|
||||
|
||||
className = Char.ToLowerInvariant(className[0]) + className.Substring(1);
|
||||
|
||||
//Format Zone Name
|
||||
string zoneName = zone.zoneName.Replace("Field", "Fld")
|
||||
.Replace("Dungeon", "Dgn")
|
||||
.Replace("Town", "Twn")
|
||||
.Replace("Battle", "Btl")
|
||||
.Replace("Test", "Tes")
|
||||
.Replace("Event", "Evt")
|
||||
.Replace("Ship", "Shp")
|
||||
.Replace("Office", "Ofc");
|
||||
if (zone is PrivateArea)
|
||||
{
|
||||
//Check if "normal"
|
||||
zoneName = zoneName.Remove(zoneName.Length - 1, 1) + "P";
|
||||
}
|
||||
zoneName = Char.ToLowerInvariant(zoneName[0]) + zoneName.Substring(1);
|
||||
|
||||
try
|
||||
{
|
||||
className = className.Substring(0, 20 - zoneName.Length);
|
||||
}
|
||||
catch (ArgumentOutOfRangeException)
|
||||
{ }
|
||||
|
||||
//Convert actor number to base 63
|
||||
string classNumber = Utils.ToStringBase63(actorNumber);
|
||||
|
||||
//Get stuff after @
|
||||
uint zoneId = zone.actorId;
|
||||
uint privLevel = 0;
|
||||
if (zone is PrivateArea)
|
||||
privLevel = ((PrivateArea)zone).GetPrivateAreaType();
|
||||
|
||||
actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel);
|
||||
}
|
||||
|
||||
public string GetScriptPath()
|
||||
{
|
||||
return directorScriptPath;
|
||||
}
|
||||
|
||||
private void LoadLuaScript()
|
||||
{
|
||||
string luaPath = String.Format(LuaEngine.FILEPATH_DIRECTORS, GetScriptPath());
|
||||
directorScript = LuaEngine.LoadScript(luaPath);
|
||||
if (directorScript == null)
|
||||
Program.Log.Error("Could not find script for director {0}.", GetName());
|
||||
}
|
||||
|
||||
private List<LuaParam> CallLuaScript(string funcName, params object[] args)
|
||||
{
|
||||
if (directorScript != null)
|
||||
{
|
||||
directorScript = LuaEngine.LoadScript(String.Format(LuaEngine.FILEPATH_DIRECTORS, directorScriptPath));
|
||||
if (!directorScript.Globals.Get(funcName).IsNil())
|
||||
{
|
||||
DynValue result = directorScript.Call(directorScript.Globals[funcName], args);
|
||||
List<LuaParam> lparams = LuaUtils.CreateLuaParamList(result);
|
||||
return lparams;
|
||||
}
|
||||
else
|
||||
Program.Log.Error("Could not find script for director {0}.", GetName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private List<LuaParam> StartCoroutine(string funcName, params object[] args)
|
||||
{
|
||||
if (directorScript != null)
|
||||
{
|
||||
if (!directorScript.Globals.Get(funcName).IsNil())
|
||||
{
|
||||
currentCoroutine = directorScript.CreateCoroutine(directorScript.Globals[funcName]).Coroutine;
|
||||
DynValue value = currentCoroutine.Resume(args);
|
||||
LuaEngine.GetInstance().ResolveResume(null, currentCoroutine, value);
|
||||
}
|
||||
else
|
||||
Program.Log.Error("Could not find script for director {0}.", GetName());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void OnEventStart(Player player, object[] args)
|
||||
{
|
||||
object[] args2 = new object[args.Length + (player == null ? 1 : 2)];
|
||||
Array.Copy(args, 0, args2, (player == null ? 1 : 2), args.Length);
|
||||
if (player != null)
|
||||
{
|
||||
args2[0] = player;
|
||||
args2[1] = this;
|
||||
}
|
||||
else
|
||||
args2[0] = this;
|
||||
|
||||
Coroutine coroutine = directorScript.CreateCoroutine(directorScript.Globals["onEventStarted"]).Coroutine;
|
||||
DynValue value = coroutine.Resume(args2);
|
||||
LuaEngine.GetInstance().ResolveResume(player, coroutine, value);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,272 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.director.Work;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.dataobjects;
|
||||
using Meteor.Map.utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.director
|
||||
{
|
||||
class GuildleveDirector : Director
|
||||
{
|
||||
public uint guildleveId;
|
||||
public Player guildleveOwner;
|
||||
public byte selectedDifficulty;
|
||||
|
||||
public GuildleveData guildleveData;
|
||||
public GuildleveWork guildleveWork = new GuildleveWork();
|
||||
|
||||
public bool isEnded = false;
|
||||
public uint completionTime = 0;
|
||||
|
||||
public GuildleveDirector(uint id, Area zone, string directorPath, uint guildleveId, byte selectedDifficulty, Player guildleveOwner, params object[] args)
|
||||
: base(id, zone, directorPath, true, args)
|
||||
{
|
||||
this.guildleveId = guildleveId;
|
||||
this.selectedDifficulty = selectedDifficulty;
|
||||
this.guildleveData = Server.GetGuildleveGamedata(guildleveId);
|
||||
this.guildleveOwner = guildleveOwner;
|
||||
|
||||
guildleveWork.aimNum[0] = guildleveData.aimNum[0];
|
||||
guildleveWork.aimNum[1] = guildleveData.aimNum[1];
|
||||
guildleveWork.aimNum[2] = guildleveData.aimNum[2];
|
||||
guildleveWork.aimNum[3] = guildleveData.aimNum[3];
|
||||
|
||||
if (guildleveWork.aimNum[0] != 0)
|
||||
guildleveWork.uiState[0] = 1;
|
||||
if (guildleveWork.aimNum[1] != 0)
|
||||
guildleveWork.uiState[1] = 1;
|
||||
if (guildleveWork.aimNum[2] != 0)
|
||||
guildleveWork.uiState[2] = 1;
|
||||
if (guildleveWork.aimNum[3] != 0)
|
||||
guildleveWork.uiState[3] = 1;
|
||||
|
||||
guildleveWork.aimNumNow[0] = guildleveWork.aimNumNow[1] = guildleveWork.aimNumNow[2] = guildleveWork.aimNumNow[3] = 0;
|
||||
}
|
||||
|
||||
public void LoadGuildleve()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void StartGuildleve()
|
||||
{
|
||||
foreach (Actor p in GetPlayerMembers())
|
||||
{
|
||||
Player player = (Player) p;
|
||||
|
||||
//Set music
|
||||
if (guildleveData.location == 1)
|
||||
player.ChangeMusic(22);
|
||||
else if (guildleveData.location == 2)
|
||||
player.ChangeMusic(14);
|
||||
else if (guildleveData.location == 3)
|
||||
player.ChangeMusic(26);
|
||||
else if (guildleveData.location == 4)
|
||||
player.ChangeMusic(16);
|
||||
|
||||
//Show Start Messages
|
||||
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50022, 0x20, guildleveId, selectedDifficulty);
|
||||
player.SendDataPacket("attention", Server.GetWorldManager().GetActor(), "", 50022, guildleveId, selectedDifficulty);
|
||||
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50026, 0x20, (object)(int)guildleveData.timeLimit);
|
||||
}
|
||||
|
||||
guildleveWork.startTime = Utils.UnixTimeStampUTC();
|
||||
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/start", this);
|
||||
propertyBuilder.AddProperty("guildleveWork.startTime");
|
||||
SendPacketsToPlayers(propertyBuilder.Done());
|
||||
}
|
||||
|
||||
public void EndGuildleve(bool wasCompleted)
|
||||
{
|
||||
if (isEnded)
|
||||
return;
|
||||
isEnded = true;
|
||||
|
||||
completionTime = Utils.UnixTimeStampUTC() - guildleveWork.startTime;
|
||||
|
||||
if (wasCompleted)
|
||||
{
|
||||
foreach (Actor a in GetPlayerMembers())
|
||||
{
|
||||
Player player = (Player)a;
|
||||
player.MarkGuildleve(guildleveId, true, true);
|
||||
player.PlayAnimation(0x02000002, true);
|
||||
player.ChangeMusic(81);
|
||||
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50023, 0x20, (object)(int)guildleveId);
|
||||
player.SendDataPacket("attention", Server.GetWorldManager().GetActor(), "", 50023, (object)(int)guildleveId);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Actor a in GetNpcMembers())
|
||||
{
|
||||
Npc npc = (Npc)a;
|
||||
npc.Despawn();
|
||||
RemoveMember(a);
|
||||
}
|
||||
|
||||
guildleveWork.startTime = 0;
|
||||
guildleveWork.signal = -1;
|
||||
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/signal", this);
|
||||
propertyBuilder.AddProperty("guildleveWork.signal");
|
||||
propertyBuilder.NewTarget("guildleveWork/start");
|
||||
propertyBuilder.AddProperty("guildleveWork.startTime");
|
||||
SendPacketsToPlayers(propertyBuilder.Done());
|
||||
|
||||
if (wasCompleted)
|
||||
{
|
||||
Npc aetheryteNode = zone.SpawnActor(1200040, String.Format("{0}:warpExit", guildleveOwner.actorName), guildleveOwner.positionX, guildleveOwner.positionY, guildleveOwner.positionZ);
|
||||
AddMember(aetheryteNode);
|
||||
|
||||
foreach (Actor a in GetPlayerMembers())
|
||||
{
|
||||
Player player = (Player)a;
|
||||
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50029, 0x20);
|
||||
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50032, 0x20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AbandonGuildleve()
|
||||
{
|
||||
foreach (Actor p in GetPlayerMembers())
|
||||
{
|
||||
Player player = (Player)p;
|
||||
player.SendGameMessage(Server.GetWorldManager().GetActor(), 50147, 0x20, (object)guildleveId);
|
||||
player.MarkGuildleve(guildleveId, true, false);
|
||||
}
|
||||
|
||||
EndGuildleve(false);
|
||||
EndDirector();
|
||||
}
|
||||
|
||||
//Delete ContentGroup, change music back
|
||||
public void EndGuildleveDirector()
|
||||
{
|
||||
foreach (Actor p in GetPlayerMembers())
|
||||
{
|
||||
Player player = (Player)p;
|
||||
player.ChangeMusic(player.GetZone().bgmDay);
|
||||
}
|
||||
}
|
||||
|
||||
public void SyncAllInfo()
|
||||
{
|
||||
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/infoVariable", this);
|
||||
|
||||
if (guildleveWork.aimNum[0] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNum[0]");
|
||||
if (guildleveWork.aimNum[1] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNum[1]");
|
||||
if (guildleveWork.aimNum[2] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNum[2]");
|
||||
if (guildleveWork.aimNum[3] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNum[3]");
|
||||
|
||||
if (guildleveWork.aimNumNow[0] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNumNow[0]");
|
||||
if (guildleveWork.aimNumNow[1] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNumNow[1]");
|
||||
if (guildleveWork.aimNumNow[2] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNumNow[2]");
|
||||
if (guildleveWork.aimNumNow[3] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.aimNumNow[3]");
|
||||
|
||||
if (guildleveWork.uiState[0] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.uiState[0]");
|
||||
if (guildleveWork.uiState[1] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.uiState[1]");
|
||||
if (guildleveWork.uiState[2] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.uiState[2]");
|
||||
if (guildleveWork.uiState[3] != 0)
|
||||
propertyBuilder.AddProperty("guildleveWork.uiState[3]");
|
||||
|
||||
SendPacketsToPlayers(propertyBuilder.Done());
|
||||
}
|
||||
|
||||
public void UpdateAimNumNow(int index, sbyte value)
|
||||
{
|
||||
guildleveWork.aimNumNow[index] = value;
|
||||
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/infoVariable", this);
|
||||
propertyBuilder.AddProperty(String.Format("guildleveWork.aimNumNow[{0}]", index));
|
||||
SendPacketsToPlayers(propertyBuilder.Done());
|
||||
}
|
||||
|
||||
public void UpdateUiState(int index, sbyte value)
|
||||
{
|
||||
guildleveWork.uiState[index] = value;
|
||||
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/infoVariable", this);
|
||||
propertyBuilder.AddProperty(String.Format("guildleveWork.uiState[{0}]", index));
|
||||
SendPacketsToPlayers(propertyBuilder.Done());
|
||||
}
|
||||
|
||||
public void UpdateMarkers(int markerIndex, float x, float y, float z)
|
||||
{
|
||||
guildleveWork.markerX[markerIndex] = x;
|
||||
guildleveWork.markerY[markerIndex] = y;
|
||||
guildleveWork.markerZ[markerIndex] = z;
|
||||
ActorPropertyPacketUtil propertyBuilder = new ActorPropertyPacketUtil("guildleveWork/marker", this);
|
||||
propertyBuilder.AddProperty(String.Format("guildleveWork.markerX[{0}]", markerIndex));
|
||||
propertyBuilder.AddProperty(String.Format("guildleveWork.markerY[{0}]", markerIndex));
|
||||
propertyBuilder.AddProperty(String.Format("guildleveWork.markerZ[{0}]", markerIndex));
|
||||
SendPacketsToPlayers(propertyBuilder.Done());
|
||||
}
|
||||
|
||||
public void SendPacketsToPlayers(List<SubPacket> packets)
|
||||
{
|
||||
List<Actor> players = GetPlayerMembers();
|
||||
foreach (Actor p in players)
|
||||
{
|
||||
((Player)p).QueuePackets(packets);
|
||||
}
|
||||
}
|
||||
|
||||
public static uint GlBorderIconIDToAnimID(uint iconId)
|
||||
{
|
||||
return iconId - 20000;
|
||||
}
|
||||
|
||||
public static uint GlPlateIconIDToAnimID(uint iconId)
|
||||
{
|
||||
return iconId - 20020;
|
||||
}
|
||||
|
||||
public static uint GetGLStartAnimationFromSheet(uint border, uint plate, bool isBoost)
|
||||
{
|
||||
return GetGLStartAnimation(GlBorderIconIDToAnimID(border), GlPlateIconIDToAnimID(plate), isBoost);
|
||||
}
|
||||
|
||||
public static uint GetGLStartAnimation(uint border, uint plate, bool isBoost)
|
||||
{
|
||||
uint borderBits = border;
|
||||
uint plateBits = plate << 7;
|
||||
|
||||
uint boostBits = isBoost ? (uint)0x8000 : (uint) 0;
|
||||
|
||||
return 0x0B000000 | boostBits | plateBits | borderBits;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.director.Work
|
||||
{
|
||||
|
||||
class GuildleveWork
|
||||
{
|
||||
public uint startTime = 0;
|
||||
public sbyte[] aimNum = new sbyte[4];
|
||||
public sbyte[] aimNumNow = new sbyte[4];
|
||||
public sbyte[] uiState = new sbyte[4];
|
||||
public float[] markerX = new float[3];
|
||||
public float[] markerY = new float[3];
|
||||
public float[] markerZ = new float[3];
|
||||
public sbyte signal;
|
||||
}
|
||||
|
||||
}
|
@ -1,194 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.director;
|
||||
using Meteor.Map.actors.group.Work;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.dataobjects;
|
||||
using Meteor.Map.packets.send.group;
|
||||
using Meteor.Map.packets.send.groups;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class ContentGroup : Group
|
||||
{
|
||||
public ContentGroupWork contentGroupWork = new ContentGroupWork();
|
||||
private Director director;
|
||||
private List<uint> members = new List<uint>();
|
||||
private bool isStarted = false;
|
||||
|
||||
public ContentGroup(ulong groupIndex, Director director, uint[] initialMembers) : base(groupIndex)
|
||||
{
|
||||
if (initialMembers != null)
|
||||
{
|
||||
for (int i = 0; i < initialMembers.Length; i++)
|
||||
{
|
||||
Session s = Server.GetServer().GetSession(initialMembers[i]);
|
||||
if (s != null)
|
||||
s.GetActor().SetCurrentContentGroup(this);
|
||||
|
||||
members.Add(initialMembers[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.director = director;
|
||||
contentGroupWork._globalTemp.director = (ulong)director.actorId << 32;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
isStarted = true;
|
||||
|
||||
SendGroupPacketsAll(members);
|
||||
}
|
||||
|
||||
public void AddMember(Actor actor)
|
||||
{
|
||||
if (actor == null)
|
||||
return;
|
||||
|
||||
if(!members.Contains(actor.actorId))
|
||||
members.Add(actor.actorId);
|
||||
|
||||
if (actor is Character)
|
||||
((Character)actor).SetCurrentContentGroup(this);
|
||||
|
||||
if (isStarted)
|
||||
SendGroupPacketsAll(members);
|
||||
}
|
||||
|
||||
public void RemoveMember(uint memberId)
|
||||
{
|
||||
members.Remove(memberId);
|
||||
if (isStarted)
|
||||
SendGroupPacketsAll(members);
|
||||
CheckDestroy();
|
||||
}
|
||||
|
||||
public override List<GroupMember> BuildMemberList(uint id)
|
||||
{
|
||||
List<GroupMember> groupMembers = new List<GroupMember>();
|
||||
groupMembers.Add(new GroupMember(id, -1, 0, false, true, ""));
|
||||
foreach (uint charaId in members)
|
||||
{
|
||||
if (charaId != id)
|
||||
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, ""));
|
||||
}
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
public override int GetMemberCount()
|
||||
{
|
||||
return members.Count;
|
||||
}
|
||||
|
||||
public override void SendInitWorkValues(Session session)
|
||||
{
|
||||
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
|
||||
groupWork.addProperty(this, "contentGroupWork._globalTemp.director");
|
||||
groupWork.addByte(Utils.MurmurHash2("contentGroupWork.property[0]", 0), 1);
|
||||
groupWork.setTarget("/_init");
|
||||
|
||||
SubPacket test = groupWork.buildPacket(session.id);
|
||||
test.DebugPrintSubPacket();
|
||||
session.QueuePacket(test);
|
||||
}
|
||||
|
||||
public override void SendGroupPackets(Session session)
|
||||
{
|
||||
ulong time = Utils.MilisUnixTimeStampUTC();
|
||||
List<GroupMember> members = BuildMemberList(session.id);
|
||||
|
||||
session.QueuePacket(GroupHeaderPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
|
||||
session.QueuePacket(GroupMembersBeginPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (GetMemberCount() - currentIndex >= 64)
|
||||
session.QueuePacket(ContentMembersX64Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex >= 32)
|
||||
session.QueuePacket(ContentMembersX32Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex >= 16)
|
||||
session.QueuePacket(ContentMembersX16Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (GetMemberCount() - currentIndex > 0)
|
||||
session.QueuePacket(ContentMembersX08Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
|
||||
}
|
||||
|
||||
public override uint GetTypeId()
|
||||
{
|
||||
return Group.ContentGroup_SimpleContentGroup24B;
|
||||
}
|
||||
|
||||
|
||||
public void SendAll()
|
||||
{
|
||||
SendGroupPacketsAll(members);
|
||||
}
|
||||
|
||||
public void DeleteGroup()
|
||||
{
|
||||
SendDeletePackets(members);
|
||||
for (int i = 0; i < members.Count; i++)
|
||||
{
|
||||
Session s = Server.GetServer().GetSession(members[i]);
|
||||
if (s != null)
|
||||
s.GetActor().SetCurrentContentGroup(null);
|
||||
Actor a = director.GetZone().FindActorInArea(members[i]);
|
||||
if (a is Npc)
|
||||
((Npc)a).Despawn();
|
||||
members.Remove(members[i]);
|
||||
i--;
|
||||
}
|
||||
Server.GetWorldManager().DeleteContentGroup(groupIndex);
|
||||
}
|
||||
|
||||
public void CheckDestroy()
|
||||
{
|
||||
bool foundSession = false;
|
||||
foreach (uint memberId in members)
|
||||
{
|
||||
Session session = Server.GetServer().GetSession(memberId);
|
||||
if (session != null)
|
||||
{
|
||||
foundSession = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundSession)
|
||||
DeleteGroup();
|
||||
}
|
||||
|
||||
public List<uint> GetMembers()
|
||||
{
|
||||
return members;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.director;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class GLContentGroup : ContentGroup
|
||||
{
|
||||
public GLContentGroup(ulong groupIndex, Director director, uint[] initialMembers)
|
||||
: base(groupIndex, director, initialMembers)
|
||||
{
|
||||
}
|
||||
|
||||
public override uint GetTypeId()
|
||||
{
|
||||
return Group.ContentGroup_GuildleveGroup;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,179 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects;
|
||||
using Meteor.Map.packets.send.group;
|
||||
using Meteor.Map.packets.send.groups;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class Group
|
||||
{
|
||||
public const uint PlayerPartyGroup = 10001;
|
||||
public const uint CompanyGroup = 20002;
|
||||
|
||||
public const uint GroupInvitationRelationGroup = 50001;
|
||||
public const uint TradeRelationGroup = 50002;
|
||||
public const uint BazaarBuyItemRelationGroup = 50009;
|
||||
|
||||
public const uint RetainerGroup = 80001;
|
||||
|
||||
public const uint MonsterPartyGroup = 10002;
|
||||
|
||||
public const uint ContentGroup_GuildleveGroup = 30001;
|
||||
public const uint ContentGroup_PublicPopGroup = 30002;
|
||||
public const uint ContentGroup_SimpleContentGroup24A = 30003;
|
||||
public const uint ContentGroup_SimpleContentGroup32A = 30004;
|
||||
public const uint ContentGroup_SimpleContentGroup128 = 30005;
|
||||
public const uint ContentGroup_SimpleContentGroup24B = 30006;
|
||||
public const uint ContentGroup_SimpleContentGroup32B = 30007;
|
||||
public const uint ContentGroup_RetainerAccessGroup = 30008;
|
||||
public const uint ContentGroup_SimpleContentGroup99999 = 30009;
|
||||
public const uint ContentGroup_SimpleContentGroup512 = 30010;
|
||||
public const uint ContentGroup_SimpleContentGroup64A = 30011;
|
||||
public const uint ContentGroup_SimpleContentGroup64B = 30012;
|
||||
public const uint ContentGroup_SimpleContentGroup64C = 30013;
|
||||
public const uint ContentGroup_SimpleContentGroup64D = 30014;
|
||||
public const uint ContentGroup_SimpleContentGroup64E = 30015;
|
||||
public const uint ContentGroup_SimpleContentGroup64F = 30016;
|
||||
public const uint ContentGroup_SimpleContentGroup64G = 30017;
|
||||
public const uint ContentGroup_SimpleContentGroup24C = 30018;
|
||||
|
||||
public readonly ulong groupIndex;
|
||||
|
||||
public Group(ulong groupIndex)
|
||||
{
|
||||
this.groupIndex = groupIndex;
|
||||
}
|
||||
|
||||
public virtual int GetMemberCount()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual uint GetTypeId()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public virtual string GetGroupName()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public virtual int GetGroupLocalizedName()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public virtual List<GroupMember> BuildMemberList(uint id)
|
||||
{
|
||||
return new List<GroupMember>();
|
||||
}
|
||||
|
||||
public void SendGroupPacketsAll(params uint[] ids)
|
||||
{
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
{
|
||||
Session session = Server.GetServer().GetSession(ids[i]);
|
||||
|
||||
if (session != null)
|
||||
SendGroupPackets(session);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendGroupPacketsAll(List<uint> ids)
|
||||
{
|
||||
for (int i = 0; i < ids.Count; i++)
|
||||
{
|
||||
Session session = Server.GetServer().GetSession(ids[i]);
|
||||
|
||||
if (session != null)
|
||||
SendGroupPackets(session);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendDeletePackets(params uint[] ids)
|
||||
{
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
{
|
||||
Session session = Server.GetServer().GetSession(ids[i]);
|
||||
|
||||
if (session != null)
|
||||
SendDeletePacket(session);
|
||||
}
|
||||
}
|
||||
|
||||
public void SendDeletePackets(List<uint> ids)
|
||||
{
|
||||
for (int i = 0; i < ids.Count; i++)
|
||||
{
|
||||
Session session = Server.GetServer().GetSession(ids[i]);
|
||||
|
||||
if (session != null)
|
||||
SendDeletePacket(session);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SendGroupPackets(Session session)
|
||||
{
|
||||
ulong time = Utils.MilisUnixTimeStampUTC();
|
||||
List<GroupMember> members = BuildMemberList(session.id);
|
||||
|
||||
session.QueuePacket(GroupHeaderPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
|
||||
session.QueuePacket(GroupMembersBeginPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
|
||||
|
||||
int currentIndex = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
int memberCount = Math.Min(GetMemberCount(), members.Count);
|
||||
if (memberCount - currentIndex >= 64)
|
||||
session.QueuePacket(GroupMembersX64Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (memberCount - currentIndex >= 32)
|
||||
session.QueuePacket(GroupMembersX32Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (memberCount - currentIndex >= 16)
|
||||
session.QueuePacket(GroupMembersX16Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else if (memberCount - currentIndex > 0)
|
||||
session.QueuePacket(GroupMembersX08Packet.buildPacket(session.id, session.GetActor().zoneId, time, members, ref currentIndex));
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
|
||||
|
||||
}
|
||||
|
||||
public void SendDeletePacket(Session session)
|
||||
{
|
||||
if (session != null)
|
||||
session.QueuePacket(DeleteGroupPacket.buildPacket(session.id, this));
|
||||
}
|
||||
|
||||
public virtual void SendInitWorkValues(Session session)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects;
|
||||
using Meteor.Map.packets.send.group;
|
||||
using Meteor.Map.packets.send.groups;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class MonsterParty : Group
|
||||
{
|
||||
private List<uint> monsterMembers = new List<uint>();
|
||||
|
||||
public MonsterParty(ulong groupIndex, uint[] initialMonsterMembers)
|
||||
: base(groupIndex)
|
||||
{
|
||||
if(initialMonsterMembers != null)
|
||||
for (int i = 0; i < initialMonsterMembers.Length; i++)
|
||||
monsterMembers.Add(initialMonsterMembers[i]);
|
||||
}
|
||||
|
||||
public void AddMember(uint memberId)
|
||||
{
|
||||
monsterMembers.Add(memberId);
|
||||
SendGroupPacketsAll(monsterMembers);
|
||||
}
|
||||
|
||||
public void RemoveMember(uint memberId)
|
||||
{
|
||||
monsterMembers.Remove(memberId);
|
||||
SendGroupPacketsAll(monsterMembers);
|
||||
}
|
||||
|
||||
public override List<GroupMember> BuildMemberList(uint id)
|
||||
{
|
||||
List<GroupMember> groupMembers = new List<GroupMember>();
|
||||
groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName));
|
||||
foreach (uint charaId in monsterMembers)
|
||||
{
|
||||
if (charaId != id)
|
||||
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(charaId).customDisplayName));
|
||||
}
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
public override void SendInitWorkValues(Session session)
|
||||
{
|
||||
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
|
||||
groupWork.setTarget("/_init");
|
||||
|
||||
SubPacket test = groupWork.buildPacket(session.id);
|
||||
session.QueuePacket(test);
|
||||
}
|
||||
|
||||
public override uint GetTypeId()
|
||||
{
|
||||
return Group.MonsterPartyGroup;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,103 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work;
|
||||
using Meteor.Map.packets.send.group;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class Party : Group
|
||||
{
|
||||
public PartyWork partyGroupWork = new PartyWork();
|
||||
public List<uint> members = new List<uint>();
|
||||
|
||||
public Party(ulong groupId, uint leaderCharaId) : base(groupId)
|
||||
{
|
||||
partyGroupWork._globalTemp.owner = (ulong)(((ulong)leaderCharaId << 32) | 0xB36F92);
|
||||
members.Add(leaderCharaId);
|
||||
}
|
||||
|
||||
public void SetLeader(uint actorId)
|
||||
{
|
||||
partyGroupWork._globalTemp.owner = (ulong)(((ulong)actorId << 32) | 0xB36F92);
|
||||
}
|
||||
|
||||
public uint GetLeader()
|
||||
{
|
||||
return (uint)(((ulong)partyGroupWork._globalTemp.owner >> 32) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
public uint GetIdForName(string name)
|
||||
{
|
||||
for (int i = 0; i < members.Count; i++)
|
||||
{
|
||||
if (Server.GetWorldManager().GetActorInWorld(members[i]).customDisplayName.Equals(name))
|
||||
{
|
||||
return members[i];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public bool IsInParty(uint charaId)
|
||||
{
|
||||
return members.Contains(charaId);
|
||||
}
|
||||
|
||||
public override int GetMemberCount()
|
||||
{
|
||||
return members.Count;
|
||||
}
|
||||
|
||||
public override uint GetTypeId()
|
||||
{
|
||||
return Group.PlayerPartyGroup;
|
||||
}
|
||||
|
||||
public override List<GroupMember> BuildMemberList(uint id)
|
||||
{
|
||||
List<GroupMember> groupMembers = new List<GroupMember>();
|
||||
groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName));
|
||||
foreach (uint charaId in members)
|
||||
{
|
||||
var chara = Server.GetWorldManager().GetActorInWorld(charaId);
|
||||
if (charaId != id && chara != null)
|
||||
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, chara.customDisplayName));
|
||||
}
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
public void AddMember(uint memberId)
|
||||
{
|
||||
members.Add(memberId);
|
||||
SendGroupPacketsAll(members);
|
||||
}
|
||||
|
||||
public void RemoveMember(uint memberId)
|
||||
{
|
||||
members.Remove(memberId);
|
||||
SendGroupPacketsAll(members);
|
||||
if (members.Count == 0)
|
||||
Server.GetWorldManager().NoMembersInParty(this);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work;
|
||||
using Meteor.Map.dataobjects;
|
||||
using Meteor.Map.packets.send.group;
|
||||
using Meteor.Map.packets.send.groups;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class RelationGroup : Group
|
||||
{
|
||||
public RelationWork work = new RelationWork();
|
||||
private uint charaOther;
|
||||
private ulong topicGroup;
|
||||
|
||||
public RelationGroup(ulong groupIndex, uint host, uint other, uint command, ulong topicGroup) : base (groupIndex)
|
||||
{
|
||||
this.charaOther = other;
|
||||
work._globalTemp.host = ((ulong)host << 32) | (0xc17909);
|
||||
work._globalTemp.variableCommand = command;
|
||||
this.topicGroup = topicGroup;
|
||||
}
|
||||
|
||||
public uint GetHost()
|
||||
{
|
||||
return (uint)(((ulong)work._globalTemp.host >> 32) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
public uint GetOther()
|
||||
{
|
||||
return charaOther;
|
||||
}
|
||||
|
||||
public override int GetMemberCount()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public override uint GetTypeId()
|
||||
{
|
||||
return Group.GroupInvitationRelationGroup;
|
||||
}
|
||||
|
||||
public ulong GetTopicGroupIndex()
|
||||
{
|
||||
return topicGroup;
|
||||
}
|
||||
|
||||
public override List<GroupMember> BuildMemberList(uint id)
|
||||
{
|
||||
List<GroupMember> groupMembers = new List<GroupMember>();
|
||||
|
||||
uint hostId = (uint)((work._globalTemp.host >> 32) & 0xFFFFFFFF);
|
||||
|
||||
groupMembers.Add(new GroupMember(hostId, -1, 0, false, Server.GetServer().GetSession(hostId) != null, Server.GetWorldManager().GetActorInWorld(hostId).customDisplayName));
|
||||
groupMembers.Add(new GroupMember(charaOther, -1, 0, false, Server.GetServer().GetSession(charaOther) != null, Server.GetWorldManager().GetActorInWorld(charaOther).customDisplayName));
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
public override void SendInitWorkValues(Session session)
|
||||
{
|
||||
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
|
||||
groupWork.addProperty(this, "work._globalTemp.host");
|
||||
groupWork.addProperty(this, "work._globalTemp.variableCommand");
|
||||
groupWork.setTarget("/_init");
|
||||
|
||||
SubPacket test = groupWork.buildPacket(session.id);
|
||||
test.DebugPrintSubPacket();
|
||||
session.QueuePacket(test);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.chara.npc;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.dataobjects;
|
||||
using Meteor.Map.packets.send.group;
|
||||
using Meteor.Map.packets.send.groups;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class RetainerMeetingRelationGroup : Group
|
||||
{
|
||||
Player player;
|
||||
Retainer retainer;
|
||||
|
||||
public RetainerMeetingRelationGroup(ulong groupIndex, Player player, Retainer retainer)
|
||||
: base(groupIndex)
|
||||
{
|
||||
this.player = player;
|
||||
this.retainer = retainer;
|
||||
}
|
||||
|
||||
public override int GetMemberCount()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public override List<GroupMember> BuildMemberList(uint id)
|
||||
{
|
||||
List<GroupMember> groupMembers = new List<GroupMember>();
|
||||
|
||||
groupMembers.Add(new GroupMember(player.actorId, -1, 0x83, false, true, player.customDisplayName));
|
||||
groupMembers.Add(new GroupMember(retainer.actorId, -1, 0x83, false, true, retainer.customDisplayName));
|
||||
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
public override uint GetTypeId()
|
||||
{
|
||||
return 50003;
|
||||
}
|
||||
|
||||
public override void SendInitWorkValues(Session session)
|
||||
{
|
||||
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
|
||||
groupWork.setTarget("/_init");
|
||||
|
||||
SubPacket test = groupWork.buildPacket(session.id);
|
||||
test.DebugPrintSubPacket();
|
||||
session.QueuePacket(test);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work;
|
||||
using Meteor.Map.dataobjects;
|
||||
using Meteor.Map.packets.send.group;
|
||||
using Meteor.Map.packets.send.groups;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.actors.group
|
||||
{
|
||||
class TradeGroup : Group
|
||||
{
|
||||
public RelationWork work = new RelationWork();
|
||||
private uint charaOther;
|
||||
private ulong topicGroup;
|
||||
|
||||
public TradeGroup(ulong groupIndex, uint host, uint other)
|
||||
: base(groupIndex)
|
||||
{
|
||||
this.charaOther = other;
|
||||
work._globalTemp.host = ((ulong)host << 32) | (0xc17909);
|
||||
work._globalTemp.variableCommand = 30001;
|
||||
}
|
||||
|
||||
public uint GetHost()
|
||||
{
|
||||
return (uint)(((ulong)work._globalTemp.host >> 32) & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
public uint GetOther()
|
||||
{
|
||||
return charaOther;
|
||||
}
|
||||
|
||||
public override int GetMemberCount()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
public override uint GetTypeId()
|
||||
{
|
||||
return Group.TradeRelationGroup;
|
||||
}
|
||||
|
||||
public ulong GetTopicGroupIndex()
|
||||
{
|
||||
return topicGroup;
|
||||
}
|
||||
|
||||
public override List<GroupMember> BuildMemberList(uint id)
|
||||
{
|
||||
List<GroupMember> groupMembers = new List<GroupMember>();
|
||||
|
||||
uint hostId = (uint)((work._globalTemp.host >> 32) & 0xFFFFFFFF);
|
||||
|
||||
groupMembers.Add(new GroupMember(hostId, -1, 0, false, Server.GetServer().GetSession(hostId) != null, Server.GetWorldManager().GetActorInWorld(hostId).customDisplayName));
|
||||
groupMembers.Add(new GroupMember(charaOther, -1, 0, false, Server.GetServer().GetSession(charaOther) != null, Server.GetWorldManager().GetActorInWorld(charaOther).customDisplayName));
|
||||
return groupMembers;
|
||||
}
|
||||
|
||||
public override void SendInitWorkValues(Session session)
|
||||
{
|
||||
SynchGroupWorkValuesPacket groupWork = new SynchGroupWorkValuesPacket(groupIndex);
|
||||
groupWork.addProperty(this, "work._globalTemp.host");
|
||||
groupWork.addProperty(this, "work._globalTemp.variableCommand");
|
||||
groupWork.setTarget("/_init");
|
||||
|
||||
SubPacket test = groupWork.buildPacket(session.id);
|
||||
test.DebugPrintSubPacket();
|
||||
session.QueuePacket(test);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work
|
||||
{
|
||||
class ContentGroupWork
|
||||
{
|
||||
public GlobalTemp _globalTemp = new GlobalTemp();
|
||||
public bool[] property = new bool[32];
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work
|
||||
{
|
||||
class GlobalTemp
|
||||
{
|
||||
public ulong director;
|
||||
}
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work
|
||||
{
|
||||
class GroupGlobalSave
|
||||
{
|
||||
public ulong master;
|
||||
public ushort[] crestIcon = new ushort[4];
|
||||
public byte rank = 1;
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work
|
||||
{
|
||||
class GroupGlobalTemp
|
||||
{
|
||||
public ulong owner;
|
||||
|
||||
//For content group
|
||||
public ulong director;
|
||||
|
||||
//For relation group
|
||||
public ulong host;
|
||||
public uint variableCommand;
|
||||
}
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work
|
||||
{
|
||||
class GroupMemberSave
|
||||
{
|
||||
//For LS
|
||||
public byte rank;
|
||||
|
||||
//For Retainers
|
||||
public byte cdIDOffset;
|
||||
public ushort placeName;
|
||||
public byte conditions;
|
||||
public byte level;
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work
|
||||
{
|
||||
class PartyWork
|
||||
{
|
||||
public GroupGlobalTemp _globalTemp = new GroupGlobalTemp();
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.group.Work
|
||||
{
|
||||
class RelationWork
|
||||
{
|
||||
public GroupGlobalTemp _globalTemp = new GroupGlobalTemp();
|
||||
}
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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
|
||||
{
|
||||
class Judge : Actor
|
||||
{
|
||||
public Judge(uint actorID, string name) : base(actorID)
|
||||
{
|
||||
actorName = name;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,165 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.Actors
|
||||
{
|
||||
class Quest : Actor
|
||||
{
|
||||
private Player owner;
|
||||
private uint currentPhase = 0;
|
||||
private uint questFlags = 0;
|
||||
private Dictionary<string, Object> questData = new Dictionary<string, object>();
|
||||
|
||||
public Quest(uint actorID, string name)
|
||||
: base(actorID)
|
||||
{
|
||||
actorName = name;
|
||||
}
|
||||
|
||||
public Quest(Player owner, uint actorID, string name, string questDataJson, uint questFlags, uint currentPhase)
|
||||
: base(actorID)
|
||||
{
|
||||
this.owner = owner;
|
||||
actorName = name;
|
||||
this.questFlags = questFlags;
|
||||
|
||||
if (questDataJson != null)
|
||||
this.questData = JsonConvert.DeserializeObject<Dictionary<string, Object>>(questDataJson);
|
||||
else
|
||||
questData = null;
|
||||
|
||||
if (questData == null)
|
||||
questData = new Dictionary<string, object>();
|
||||
|
||||
this.currentPhase = currentPhase;
|
||||
}
|
||||
|
||||
public void SetQuestData(string dataName, object data)
|
||||
{
|
||||
questData[dataName] = data;
|
||||
|
||||
//Inform update
|
||||
}
|
||||
|
||||
public uint GetQuestId()
|
||||
{
|
||||
return actorId & 0xFFFFF;
|
||||
}
|
||||
|
||||
public object GetQuestData(string dataName)
|
||||
{
|
||||
if (questData.ContainsKey(dataName))
|
||||
return questData[dataName];
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public void ClearQuestData()
|
||||
{
|
||||
questData.Clear();
|
||||
}
|
||||
|
||||
public void ClearQuestFlags()
|
||||
{
|
||||
questFlags = 0;
|
||||
}
|
||||
|
||||
public void SetQuestFlag(int bitIndex, bool value)
|
||||
{
|
||||
if (bitIndex >= 32)
|
||||
{
|
||||
Program.Log.Error("Tried to access bit flag >= 32 for questId: {0}", actorId);
|
||||
return;
|
||||
}
|
||||
|
||||
int mask = 1 << bitIndex;
|
||||
|
||||
if (value)
|
||||
questFlags |= (uint)(1 << bitIndex);
|
||||
else
|
||||
questFlags &= (uint)~(1 << bitIndex);
|
||||
|
||||
DoCompletionCheck();
|
||||
}
|
||||
|
||||
public bool GetQuestFlag(int bitIndex)
|
||||
{
|
||||
if (bitIndex >= 32)
|
||||
{
|
||||
Program.Log.Error("Tried to access bit flag >= 32 for questId: {0}", actorId);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return (questFlags & (1 << bitIndex)) == (1 << bitIndex);
|
||||
}
|
||||
|
||||
public uint GetPhase()
|
||||
{
|
||||
return currentPhase;
|
||||
}
|
||||
|
||||
public void NextPhase(uint phaseNumber)
|
||||
{
|
||||
currentPhase = phaseNumber;
|
||||
owner.SendGameMessage(Server.GetWorldManager().GetActor(), 25116, 0x20, (object)GetQuestId());
|
||||
SaveData();
|
||||
|
||||
DoCompletionCheck();
|
||||
}
|
||||
|
||||
public uint GetQuestFlags()
|
||||
{
|
||||
return questFlags;
|
||||
}
|
||||
|
||||
public string GetSerializedQuestData()
|
||||
{
|
||||
return JsonConvert.SerializeObject(questData, Formatting.Indented);
|
||||
}
|
||||
|
||||
public void SaveData()
|
||||
{
|
||||
Database.SaveQuest(owner, this);
|
||||
}
|
||||
|
||||
public void DoCompletionCheck()
|
||||
{
|
||||
List<LuaParam> returned = LuaEngine.GetInstance().CallLuaFunctionForReturn(owner, this, "isObjectivesComplete", true);
|
||||
if (returned != null && returned.Count >= 1 && returned[0].typeID == 3)
|
||||
{
|
||||
owner.SendDataPacket("attention", Server.GetWorldManager().GetActor(), "", 25225, (object)GetQuestId());
|
||||
owner.SendGameMessage(Server.GetWorldManager().GetActor(), 25225, 0x20, (object)GetQuestId());
|
||||
}
|
||||
}
|
||||
|
||||
public void DoAbandon()
|
||||
{
|
||||
LuaEngine.GetInstance().CallLuaFunctionForReturn(owner, this, "onAbandonQuest", true);
|
||||
owner.SendGameMessage(owner, Server.GetWorldManager().GetActor(), 25236, 0x20, (object)GetQuestId());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using Meteor.Map.packets.send.actor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Meteor.Map.Actors
|
||||
{
|
||||
class WorldMaster : Actor
|
||||
{
|
||||
public WorldMaster() : base(0x5FF80001)
|
||||
{
|
||||
this.displayNameId = 0;
|
||||
this.customDisplayName = "worldMaster";
|
||||
|
||||
this.actorName = "worldMaster";
|
||||
this.className = "WorldMaster";
|
||||
}
|
||||
|
||||
public override SubPacket CreateScriptBindPacket()
|
||||
{
|
||||
List<LuaParam> lParams;
|
||||
lParams = LuaUtils.CreateLuaParamList("/World/WorldMaster_event", false, false, false, false, false, null);
|
||||
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, lParams);
|
||||
}
|
||||
|
||||
public override List<SubPacket> GetSpawnPackets()
|
||||
{
|
||||
List<SubPacket> subpackets = new List<SubPacket>();
|
||||
subpackets.Add(CreateAddActorPacket(0));
|
||||
subpackets.Add(CreateSpeedPacket());
|
||||
subpackets.Add(CreateSpawnPositonPacket(0x1));
|
||||
subpackets.Add(CreateNamePacket());
|
||||
subpackets.Add(CreateStatePacket());
|
||||
subpackets.Add(CreateIsZoneingPacket());
|
||||
subpackets.Add(CreateScriptBindPacket());
|
||||
return subpackets;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 MySql.Data.MySqlClient;
|
||||
|
||||
namespace Meteor.Map.dataobjects
|
||||
{
|
||||
class GuildleveData
|
||||
{
|
||||
public readonly uint id;
|
||||
public readonly uint classType;
|
||||
public readonly uint location;
|
||||
public readonly ushort factionCreditRequired;
|
||||
public readonly ushort level;
|
||||
public readonly uint aetheryte;
|
||||
public readonly uint plateId;
|
||||
public readonly uint borderId;
|
||||
public readonly uint objective;
|
||||
public readonly byte timeLimit;
|
||||
public readonly uint skill;
|
||||
public readonly byte favorCount;
|
||||
|
||||
public readonly sbyte[] aimNum = new sbyte[4];
|
||||
public readonly uint[] itemTarget = new uint[4];
|
||||
public readonly uint[] mobTarget = new uint[4];
|
||||
|
||||
public GuildleveData(MySqlDataReader reader)
|
||||
{
|
||||
id = reader.GetUInt32("id");
|
||||
classType = reader.GetUInt32("classType");
|
||||
location = reader.GetUInt32("location");
|
||||
factionCreditRequired = reader.GetUInt16("factionCreditRequired");
|
||||
level = reader.GetUInt16("level");
|
||||
aetheryte = reader.GetUInt32("aetheryte");
|
||||
plateId = reader.GetUInt32("plateId");
|
||||
borderId = reader.GetUInt32("borderId");
|
||||
objective = reader.GetUInt32("objective");
|
||||
timeLimit = reader.GetByte("timeLimit");
|
||||
skill = reader.GetUInt32("skill");
|
||||
favorCount = reader.GetByte("favorCount");
|
||||
|
||||
aimNum[0] = reader.GetSByte("aimNum1");
|
||||
aimNum[1] = reader.GetSByte("aimNum2");
|
||||
aimNum[2] = reader.GetSByte("aimNum3");
|
||||
aimNum[3] = reader.GetSByte("aimNum4");
|
||||
|
||||
itemTarget[0] = reader.GetUInt32("item1");
|
||||
itemTarget[1] = reader.GetUInt32("item2");
|
||||
itemTarget[2] = reader.GetUInt32("item3");
|
||||
itemTarget[3] = reader.GetUInt32("item4");
|
||||
|
||||
mobTarget[0] = reader.GetUInt32("mob1");
|
||||
mobTarget[1] = reader.GetUInt32("mob2");
|
||||
mobTarget[2] = reader.GetUInt32("mob3");
|
||||
mobTarget[3] = reader.GetUInt32("mob4");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,436 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 MySql.Data.MySqlClient;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Meteor.Map.dataobjects
|
||||
{
|
||||
class InventoryItem
|
||||
{
|
||||
public const byte DEALINGMODE_NONE = 0;
|
||||
public const byte DEALINGMODE_REFERENCED = 1;
|
||||
public const byte DEALINGMODE_PRICED = 2;
|
||||
|
||||
public const byte TAG_EXCLUSIVE = 0x3;
|
||||
public const byte TAG_DEALING = 0xC9;
|
||||
public const byte TAG_ATTACHED = 0xCA;
|
||||
|
||||
public const byte MODE_SELL_SINGLE = 11; //0xB
|
||||
public const byte MODE_SELL_PSTACK = 12; //0xC
|
||||
public const byte MODE_SELL_FSTACK = 13; //0xD
|
||||
public const byte MODE_SEEK_ITEM = 20; //0x14
|
||||
public const byte MODE_SEEK_REPAIR = 30; //0x1E
|
||||
public const byte MODE_SEEK_MELD = 40; //0x28
|
||||
|
||||
public ulong uniqueId;
|
||||
public uint itemId;
|
||||
public int quantity = 1;
|
||||
|
||||
private byte dealingVal = 0;
|
||||
private byte dealingMode = DEALINGMODE_NONE;
|
||||
private int dealingAttached1 = 0;
|
||||
private int dealingAttached2 = 0;
|
||||
private int dealingAttached3 = 0;
|
||||
|
||||
private byte[] tags = new byte[4];
|
||||
private byte[] tagValues = new byte[4];
|
||||
|
||||
public byte quality = 1;
|
||||
|
||||
public ItemModifier modifiers;
|
||||
|
||||
public readonly ItemData itemData;
|
||||
public Character owner = null;
|
||||
public ushort slot = 0xFFFF;
|
||||
public ushort linkSlot = 0xFFFF;
|
||||
public ushort itemPackage = 0xFFFF;
|
||||
|
||||
public class ItemModifier
|
||||
{
|
||||
public uint durability = 0;
|
||||
public ushort use = 0;
|
||||
public uint materiaId = 0;
|
||||
public uint materiaLife = 0;
|
||||
public byte mainQuality = 0;
|
||||
public byte[] subQuality = new byte[3];
|
||||
public uint polish = 0;
|
||||
public uint param1 = 0;
|
||||
public uint param2 = 0;
|
||||
public uint param3 = 0;
|
||||
public ushort spiritbind = 0;
|
||||
public byte[] materiaType = new byte[5];
|
||||
public byte[] materiaGrade = new byte[5];
|
||||
|
||||
public ItemModifier()
|
||||
{
|
||||
}
|
||||
|
||||
public ItemModifier(MySql.Data.MySqlClient.MySqlDataReader reader)
|
||||
{
|
||||
durability = reader.GetUInt32("durability");
|
||||
mainQuality = reader.GetByte("mainQuality");
|
||||
subQuality[0] = reader.GetByte("subQuality1");
|
||||
subQuality[1] = reader.GetByte("subQuality2");
|
||||
subQuality[2] = reader.GetByte("subQuality3");
|
||||
param1 = reader.GetUInt32("param1");
|
||||
param2 = reader.GetUInt32("param2");
|
||||
param3 = reader.GetUInt32("param3");
|
||||
spiritbind = reader.GetUInt16("spiritbind");
|
||||
|
||||
ushort materia1 = reader.GetUInt16("materia1");
|
||||
ushort materia2 = reader.GetUInt16("materia2");
|
||||
ushort materia3 = reader.GetUInt16("materia3");
|
||||
ushort materia4 = reader.GetUInt16("materia4");
|
||||
ushort materia5 = reader.GetUInt16("materia5");
|
||||
|
||||
materiaType[0] = (byte)(materia1 & 0xFF);
|
||||
materiaGrade[0] = (byte)((materia1 >> 8) & 0xFF);
|
||||
materiaType[1] = (byte)(materia2 & 0xFF);
|
||||
materiaGrade[1] = (byte)((materia2 >> 8) & 0xFF);
|
||||
materiaType[2] = (byte)(materia3 & 0xFF);
|
||||
materiaGrade[2] = (byte)((materia3 >> 8) & 0xFF);
|
||||
materiaType[3] = (byte)(materia4 & 0xFF);
|
||||
materiaGrade[3] = (byte)((materia4 >> 8) & 0xFF);
|
||||
materiaType[4] = (byte)(materia5 & 0xFF);
|
||||
materiaGrade[4] = (byte)((materia5 >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
public void WriteBytes(BinaryWriter binWriter)
|
||||
{
|
||||
binWriter.Write((UInt32) durability);
|
||||
binWriter.Write((UInt16) use);
|
||||
binWriter.Write((UInt32) materiaId);
|
||||
binWriter.Write((UInt32) materiaLife);
|
||||
binWriter.Write((Byte) mainQuality);
|
||||
binWriter.Write((Byte) subQuality[0]);
|
||||
binWriter.Write((Byte) subQuality[1]);
|
||||
binWriter.Write((Byte) subQuality[2]);
|
||||
binWriter.Write((UInt32) polish);
|
||||
binWriter.Write((UInt32) param1);
|
||||
binWriter.Write((UInt32) param2);
|
||||
binWriter.Write((UInt32) param3);
|
||||
binWriter.Write((UInt16) spiritbind);
|
||||
binWriter.Write((Byte) materiaType[0]);
|
||||
binWriter.Write((Byte) materiaType[1]);
|
||||
binWriter.Write((Byte) materiaType[2]);
|
||||
binWriter.Write((Byte) materiaType[3]);
|
||||
binWriter.Write((Byte) materiaType[4]);
|
||||
binWriter.Write((Byte) materiaGrade[0]);
|
||||
binWriter.Write((Byte) materiaGrade[1]);
|
||||
binWriter.Write((Byte) materiaGrade[2]);
|
||||
binWriter.Write((Byte) materiaGrade[3]);
|
||||
binWriter.Write((Byte) materiaGrade[4]);
|
||||
}
|
||||
}
|
||||
|
||||
//For loading already existing items
|
||||
public InventoryItem(MySqlDataReader reader)
|
||||
{
|
||||
uniqueId = reader.GetUInt32("serverItemId");
|
||||
itemId = reader.GetUInt32("itemId");
|
||||
itemData = Server.GetItemGamedata(itemId);
|
||||
quantity = reader.GetInt32("quantity");
|
||||
quality = reader.GetByte("quality");
|
||||
|
||||
bool hasDealing = !reader.IsDBNull(reader.GetOrdinal("bazaarMode"));
|
||||
if (hasDealing)
|
||||
{
|
||||
dealingVal = reader.GetByte("dealingValue");
|
||||
dealingMode = reader.GetByte("dealingMode");
|
||||
dealingAttached1 = reader.GetInt32("dealingAttached1");
|
||||
dealingAttached2 = reader.GetInt32("dealingAttached2");
|
||||
dealingAttached3 = reader.GetInt32("dealingAttached3");
|
||||
tags[0] = reader.GetByte("dealingTag");
|
||||
tagValues[0] = reader.GetByte("bazaarMode");
|
||||
}
|
||||
|
||||
bool hasModifiers = !reader.IsDBNull(reader.GetOrdinal("modifierId"));
|
||||
if (hasModifiers)
|
||||
modifiers = new InventoryItem.ItemModifier(reader);
|
||||
|
||||
tags[1] = itemData.isExclusive ? TAG_EXCLUSIVE : (byte)0;
|
||||
}
|
||||
|
||||
//For creating new items (only should be called by the DB)
|
||||
public InventoryItem(uint uniqueId, uint itemId, int quantity, byte qualityNumber, ItemModifier modifiers = null)
|
||||
{
|
||||
this.uniqueId = uniqueId;
|
||||
this.itemId = itemId;
|
||||
this.itemData = Server.GetItemGamedata(itemId);
|
||||
this.quantity = quantity;
|
||||
this.quality = qualityNumber;
|
||||
this.modifiers = modifiers;
|
||||
|
||||
tags[1] = itemData.isExclusive ? TAG_EXCLUSIVE : (byte)0;
|
||||
}
|
||||
|
||||
public void SaveDealingInfo(MySqlCommand cmd)
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@serverItemId", uniqueId);
|
||||
cmd.Parameters.AddWithValue("@dealingValue", dealingVal);
|
||||
cmd.Parameters.AddWithValue("@dealingMode", dealingMode);
|
||||
cmd.Parameters.AddWithValue("@dealingAttached1", dealingAttached1);
|
||||
cmd.Parameters.AddWithValue("@dealingAttached2", dealingAttached2);
|
||||
cmd.Parameters.AddWithValue("@dealingAttached3", dealingAttached3);
|
||||
cmd.Parameters.AddWithValue("@dealingTag", tags[0]);
|
||||
cmd.Parameters.AddWithValue("@bazaarMode", tagValues[0]);
|
||||
}
|
||||
|
||||
public byte[] ToPacketBytes()
|
||||
{
|
||||
byte[] data = new byte[0x70];
|
||||
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryWriter binWriter = new BinaryWriter(mem))
|
||||
{
|
||||
binWriter.Write((UInt64)uniqueId);
|
||||
binWriter.Write((Int32)quantity);
|
||||
binWriter.Write((UInt32)itemId);
|
||||
|
||||
if (linkSlot == 0xFFFF)
|
||||
binWriter.Write((UInt16)slot);
|
||||
else
|
||||
binWriter.Write((UInt16)linkSlot);
|
||||
linkSlot = 0xFFFF;
|
||||
|
||||
binWriter.Write((Byte)dealingVal);
|
||||
binWriter.Write((Byte)dealingMode);
|
||||
|
||||
binWriter.Write((UInt32)dealingAttached1);
|
||||
binWriter.Write((UInt32)dealingAttached2);
|
||||
binWriter.Write((UInt32)dealingAttached3);
|
||||
|
||||
for (int i = 0; i < tags.Length; i++)
|
||||
binWriter.Write((Byte) tags[i]);
|
||||
for (int i = 0; i < tagValues.Length; i++)
|
||||
binWriter.Write((Byte) tagValues[i]);
|
||||
|
||||
binWriter.Write((Byte)quality);
|
||||
|
||||
if (modifiers != null)
|
||||
{
|
||||
binWriter.Write((Byte)0x01);
|
||||
modifiers.WriteBytes(binWriter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public void SetQuantity(uint quantity)
|
||||
{
|
||||
lock (owner.GetItemPackage(itemPackage))
|
||||
{
|
||||
this.quantity = (int)quantity;
|
||||
if (quantity < 0)
|
||||
quantity = 0;
|
||||
Database.SetQuantity(uniqueId, this.quantity);
|
||||
|
||||
if (owner != null)
|
||||
owner.GetItemPackage(itemPackage).MarkDirty(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void ChangeQuantity(int quantityDelta)
|
||||
{
|
||||
lock (owner.GetItemPackage(itemPackage))
|
||||
{
|
||||
this.quantity += quantityDelta;
|
||||
if (quantity < 0)
|
||||
quantity = 0;
|
||||
|
||||
if (quantity == 0)
|
||||
{
|
||||
owner.RemoveItem(this);
|
||||
return;
|
||||
}
|
||||
|
||||
Database.SetQuantity(uniqueId, this.quantity);
|
||||
|
||||
if (owner != null)
|
||||
owner.GetItemPackage(itemPackage).MarkDirty(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOwner(Character owner, ushort itemPackage, ushort slot)
|
||||
{
|
||||
this.owner = owner;
|
||||
this.itemPackage = itemPackage;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
public void ClearOwner()
|
||||
{
|
||||
owner = null;
|
||||
itemPackage = 0xFFFF;
|
||||
slot = 0xFFFF;
|
||||
}
|
||||
|
||||
public void SetNormal()
|
||||
{
|
||||
if (dealingMode != 0 || tags[0] == TAG_ATTACHED)
|
||||
Database.ClearDealingInfo(this);
|
||||
|
||||
tags[0] = 0;
|
||||
tagValues[0] = 0;
|
||||
dealingVal = 0;
|
||||
dealingMode = 0;
|
||||
dealingAttached1 = 0;
|
||||
dealingAttached2 = 0;
|
||||
dealingAttached3 = 0;
|
||||
|
||||
if (owner != null)
|
||||
owner.GetItemPackage(itemPackage).MarkDirty(this);
|
||||
}
|
||||
|
||||
public void SetSelling(byte mode, int price)
|
||||
{
|
||||
tags[0] = TAG_DEALING;
|
||||
tagValues[0] = mode;
|
||||
|
||||
dealingVal = 0;
|
||||
dealingMode = DEALINGMODE_PRICED;
|
||||
dealingAttached1 = 0;
|
||||
dealingAttached2 = price;
|
||||
dealingAttached3 = 0;
|
||||
|
||||
if (owner != null)
|
||||
owner.GetItemPackage(itemPackage).MarkDirty(this);
|
||||
|
||||
Database.SetDealingInfo(this);
|
||||
}
|
||||
|
||||
public void SetAsOfferTo(byte mode, InventoryItem seeked)
|
||||
{
|
||||
tags[0] = TAG_DEALING;
|
||||
tagValues[0] = mode;
|
||||
|
||||
dealingVal = 0;
|
||||
dealingMode = DEALINGMODE_REFERENCED;
|
||||
dealingAttached1 = ((seeked.itemPackage << 16) | seeked.slot);
|
||||
dealingAttached2 = 0;
|
||||
dealingAttached3 = 0;
|
||||
|
||||
seeked.SetSeeking();
|
||||
|
||||
if (owner != null)
|
||||
owner.GetItemPackage(itemPackage).MarkDirty(this);
|
||||
|
||||
Database.SetDealingInfo(this);
|
||||
}
|
||||
|
||||
public void UpdateOfferedSlot(ushort delta)
|
||||
{
|
||||
if (dealingMode == DEALINGMODE_REFERENCED)
|
||||
{
|
||||
ushort attachedItemPackage = (ushort)((dealingAttached1 >> 16) & 0xFF);
|
||||
ushort attachedSlot = (ushort)(dealingAttached1 & 0xFF);
|
||||
attachedSlot -= delta;
|
||||
dealingAttached1 = ((attachedItemPackage << 16) | attachedSlot);
|
||||
Database.SetDealingInfo(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void SetSeeking()
|
||||
{
|
||||
tags[0] = TAG_ATTACHED;
|
||||
tagValues[0] = 0;
|
||||
|
||||
dealingVal = 0;
|
||||
dealingMode = DEALINGMODE_NONE;
|
||||
dealingAttached1 = 0;
|
||||
dealingAttached2 = 0;
|
||||
dealingAttached3 = 0;
|
||||
|
||||
if (owner != null)
|
||||
owner.GetItemPackage(itemPackage).MarkDirty(this);
|
||||
|
||||
Database.SetDealingInfo(this);
|
||||
}
|
||||
|
||||
public void SetTradeQuantity(int quantity)
|
||||
{
|
||||
tags[0] = 0;
|
||||
tagValues[0] = 0;
|
||||
|
||||
dealingVal = 0;
|
||||
dealingMode = DEALINGMODE_NONE;
|
||||
dealingAttached1 = 0;
|
||||
dealingAttached2 = 0;
|
||||
dealingAttached3 = quantity;
|
||||
|
||||
if (owner != null)
|
||||
owner.GetItemPackage(itemPackage).MarkDirty(this);
|
||||
}
|
||||
|
||||
public int GetTradeQuantity()
|
||||
{
|
||||
return dealingAttached3;
|
||||
}
|
||||
|
||||
public InventoryItem GetOfferedTo()
|
||||
{
|
||||
if (dealingMode != DEALINGMODE_REFERENCED)
|
||||
return null;
|
||||
|
||||
ushort attachedItemPackage = (ushort)((dealingAttached1 >> 16) & 0xFF);
|
||||
ushort attachedSlot = (ushort)(dealingAttached1 & 0xFF);
|
||||
return owner.GetItemPackage(attachedItemPackage).GetItemAtSlot(attachedSlot);
|
||||
}
|
||||
|
||||
public bool IsSelling()
|
||||
{
|
||||
return GetBazaarMode() == MODE_SELL_SINGLE || GetBazaarMode() == MODE_SELL_PSTACK || GetBazaarMode() == MODE_SELL_FSTACK;
|
||||
}
|
||||
|
||||
public byte GetBazaarMode()
|
||||
{
|
||||
if (tags[0] == 0xC9)
|
||||
return tagValues[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
public ItemData GetItemData()
|
||||
{
|
||||
return itemData;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (itemData != null)
|
||||
{
|
||||
if (quantity <= 1)
|
||||
return string.Format("{0}+{1} ({2}/{3})", itemData.name, quality-1, quantity, itemData.maxStack);
|
||||
else
|
||||
return string.Format("{0} ({1}/{2})", itemData.name, quantity, itemData.maxStack);
|
||||
}
|
||||
else
|
||||
return "Invalid Item";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,617 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 MySql.Data.MySqlClient;
|
||||
using System;
|
||||
|
||||
namespace Meteor.Map.dataobjects
|
||||
{
|
||||
class ItemData
|
||||
{
|
||||
//Basic
|
||||
public readonly uint catalogID;
|
||||
public readonly string name;
|
||||
|
||||
//_item sheet
|
||||
public readonly string category;
|
||||
public readonly int maxStack;
|
||||
public readonly bool isRare;
|
||||
public readonly bool isExclusive;
|
||||
|
||||
//itemData sheet
|
||||
public readonly int durability;
|
||||
public readonly int sellPrice;
|
||||
public readonly int icon;
|
||||
public readonly int kind;
|
||||
public readonly int rarity;
|
||||
public readonly int isUseable;
|
||||
public readonly int mainSkill;
|
||||
public readonly int subSkill;
|
||||
public readonly int levelType;
|
||||
public readonly int level;
|
||||
public readonly int compatibility;
|
||||
public readonly float effectMagnitude;
|
||||
public readonly float effectRate;
|
||||
public readonly float shieldBlocking;
|
||||
public readonly float effectDuration;
|
||||
public readonly float recastTime;
|
||||
public readonly byte recastGroup;
|
||||
public readonly int repairSkill;
|
||||
public readonly int repairItem;
|
||||
public readonly int repairItemNum;
|
||||
public readonly int repairLevel;
|
||||
public readonly int repairLicense;
|
||||
|
||||
public ItemData(MySqlDataReader reader)
|
||||
{
|
||||
catalogID = reader.GetUInt32("catalogID");
|
||||
name = reader.GetString("name");
|
||||
|
||||
category = reader.GetString("category");
|
||||
maxStack = reader.GetInt32("maxStack");
|
||||
isRare = reader.GetBoolean("isRare");
|
||||
isExclusive = reader.GetBoolean("isExclusive");
|
||||
|
||||
durability = reader.GetInt32("durability");
|
||||
sellPrice = reader.GetInt32("sellPrice");
|
||||
|
||||
icon = reader.GetInt32("icon");
|
||||
kind = reader.GetInt32("kind");
|
||||
rarity = reader.GetInt32("rarity");
|
||||
isUseable = reader.GetInt32("isUseable");
|
||||
mainSkill = reader.GetInt32("mainSkill");
|
||||
subSkill = reader.GetInt32("subSkill");
|
||||
levelType = reader.GetInt32("levelType");
|
||||
level = reader.GetInt32("level");
|
||||
compatibility = reader.GetInt32("compatibility");
|
||||
effectMagnitude = reader.GetFloat("effectMagnitude");
|
||||
effectRate = reader.GetFloat("effectRate");
|
||||
shieldBlocking = reader.GetFloat("shieldBlocking");
|
||||
effectDuration = reader.GetFloat("effectDuration");
|
||||
recastTime = reader.GetFloat("recastTime");
|
||||
recastGroup = reader.GetByte("recastGroup");
|
||||
repairSkill = reader.GetInt32("repairSkill");
|
||||
repairItem = reader.GetInt32("repairItem");
|
||||
repairItemNum = reader.GetInt32("repairItemNum");
|
||||
repairLevel = reader.GetInt32("repairLevel");
|
||||
repairLicense = reader.GetInt32("repairLicense");
|
||||
}
|
||||
|
||||
#region Utility Functions
|
||||
public bool IsMoney()
|
||||
{
|
||||
return catalogID >= 1000000 && catalogID <= 1999999;
|
||||
}
|
||||
|
||||
public bool IsImportant()
|
||||
{
|
||||
return catalogID >= 2000001 && catalogID <= 2002048;
|
||||
}
|
||||
|
||||
public bool IsFood()
|
||||
{
|
||||
return catalogID >= 3010000 && catalogID <= 3019999;
|
||||
}
|
||||
|
||||
public bool IsDrink()
|
||||
{
|
||||
return catalogID >= 3010600 && catalogID <= 3010699;
|
||||
}
|
||||
|
||||
public bool IsPotion()
|
||||
{
|
||||
return catalogID >= 3020000 && catalogID <= 3029999;
|
||||
}
|
||||
|
||||
public bool IsEquipment()
|
||||
{
|
||||
return catalogID >= 3900000 && catalogID <= 9999999;
|
||||
}
|
||||
|
||||
public bool IsWeapon()
|
||||
{
|
||||
return catalogID >= 3900000 && catalogID <= 7999999;
|
||||
}
|
||||
|
||||
public static bool IsWeapon(uint catalogID)
|
||||
{
|
||||
return catalogID >= 3900000 && catalogID <= 7999999;
|
||||
}
|
||||
|
||||
public bool IsBattleWeapon()
|
||||
{
|
||||
return catalogID >= 3900000 && catalogID <= 5999999;
|
||||
}
|
||||
|
||||
public bool IsAttackWeapon()
|
||||
{
|
||||
return catalogID >= 4020000 && catalogID <= 4999999;
|
||||
}
|
||||
|
||||
public bool IsNailWeapon()
|
||||
{
|
||||
return catalogID >= 4020000 && catalogID <= 4029999;
|
||||
}
|
||||
|
||||
public bool IsSwordWeapon()
|
||||
{
|
||||
return catalogID >= 4030000 && catalogID <= 4039999;
|
||||
}
|
||||
|
||||
public bool IsAxeWeapon()
|
||||
{
|
||||
return catalogID >= 4040000 && catalogID <= 4049999;
|
||||
}
|
||||
|
||||
public bool IsRapierWeapon()
|
||||
{
|
||||
return catalogID >= 4050000 && catalogID <= 4059999;
|
||||
}
|
||||
|
||||
public bool IsMaceWeapon()
|
||||
{
|
||||
return catalogID >= 4060000 && catalogID <= 4069999;
|
||||
}
|
||||
|
||||
public bool IsBowWeapon()
|
||||
{
|
||||
return catalogID >= 4070000 && catalogID <= 4079999;
|
||||
}
|
||||
|
||||
public bool IsLanceWeapon()
|
||||
{
|
||||
return catalogID >= 4080000 && catalogID <= 4089999;
|
||||
}
|
||||
|
||||
public bool IsGunWeapon()
|
||||
{
|
||||
return catalogID >= 4090000 && catalogID <= 4099999;
|
||||
}
|
||||
|
||||
public bool IsLongRangeWeapon()
|
||||
{
|
||||
return catalogID >= 4050000 && catalogID <= 4059999;
|
||||
}
|
||||
|
||||
public bool IsShotWeapon()
|
||||
{
|
||||
return !IsBowWeapon() ? IsGunWeapon() : false;
|
||||
}
|
||||
|
||||
public bool IsAmmoWeapon()
|
||||
{
|
||||
return !IsThrowWeapon() && !IsArrowWeapon();
|
||||
}
|
||||
|
||||
public bool IsThrowWeapon()
|
||||
{
|
||||
return catalogID >= 3910000 && catalogID <= 3919999;
|
||||
}
|
||||
|
||||
public bool IsArrowWeapon()
|
||||
{
|
||||
return catalogID >= 3920000 && catalogID <= 3929999;
|
||||
}
|
||||
|
||||
public bool IsBulletWeapon()
|
||||
{
|
||||
return catalogID >= 3930000 && catalogID <= 3939999;
|
||||
}
|
||||
|
||||
public bool IsShieldWeapon()
|
||||
{
|
||||
return catalogID >= 4100000 && catalogID <= 4109999;
|
||||
}
|
||||
|
||||
public bool IsManualGuardShieldWeapon()
|
||||
{
|
||||
return IsShieldWeapon() && GetShieldGuardTime() != -1;
|
||||
}
|
||||
|
||||
public bool IsMagicWeapon()
|
||||
{
|
||||
return catalogID >= 5000000 && catalogID <= 5999999;
|
||||
}
|
||||
|
||||
public bool IsMysticWeapon()
|
||||
{
|
||||
return catalogID >= 5010000 && catalogID <= 5019999;
|
||||
}
|
||||
|
||||
public bool IsThaumaturgeWeapon()
|
||||
{
|
||||
return catalogID >= 5020000 && catalogID <= 5029999;
|
||||
}
|
||||
|
||||
public bool IsConjurerWeapon()
|
||||
{
|
||||
return catalogID >= 5030000 && catalogID <= 5039999;
|
||||
}
|
||||
|
||||
public bool IsArchanistWeapon()
|
||||
{
|
||||
return catalogID >= 5040000 && catalogID <= 5049999;
|
||||
}
|
||||
|
||||
public bool IsCraftWeapon()
|
||||
{
|
||||
return catalogID >= 6000000 && catalogID <= 6999999;
|
||||
}
|
||||
|
||||
public bool IsCarpenterWeapon()
|
||||
{
|
||||
return catalogID >= 6010000 && catalogID <= 6019999;
|
||||
}
|
||||
|
||||
public bool IsBlackSmithWeapon()
|
||||
{
|
||||
return catalogID >= 6020000 && catalogID <= 6029999;
|
||||
}
|
||||
|
||||
public bool IsArmorerWeapon()
|
||||
{
|
||||
return catalogID >= 6030000 && catalogID <= 6039999;
|
||||
}
|
||||
|
||||
public bool IsGoldSmithWeapon()
|
||||
{
|
||||
return catalogID >= 6040000 && catalogID <= 6049999;
|
||||
}
|
||||
|
||||
public bool IsTannerWeapon()
|
||||
{
|
||||
return catalogID >= 6050000 && catalogID <= 6059999;
|
||||
}
|
||||
|
||||
public bool IsWeaverWeapon()
|
||||
{
|
||||
return catalogID >= 6060000 && catalogID <= 6069999;
|
||||
}
|
||||
|
||||
public bool IsAlchemistWeapon()
|
||||
{
|
||||
return catalogID >= 6070000 && catalogID <= 6079999;
|
||||
}
|
||||
|
||||
public bool IsCulinarianWeapon()
|
||||
{
|
||||
return catalogID >= 6080000 && catalogID <= 6089999;
|
||||
}
|
||||
|
||||
public bool IsHarvestWeapon()
|
||||
{
|
||||
return catalogID >= 7000000 && catalogID <= 7999999;
|
||||
}
|
||||
|
||||
public bool IsMinerWeapon()
|
||||
{
|
||||
return catalogID >= 7010000 && catalogID <= 7019999;
|
||||
}
|
||||
|
||||
public bool IsBotanistWeapon()
|
||||
{
|
||||
return catalogID >= 7020000 && catalogID <= 7029999;
|
||||
}
|
||||
|
||||
public bool IsFishingWeapon()
|
||||
{
|
||||
return catalogID >= 7030000 && catalogID <= 7039999;
|
||||
}
|
||||
|
||||
public bool IsShepherdWeapon()
|
||||
{
|
||||
return catalogID >= 7040000 && catalogID <= 7049999;
|
||||
}
|
||||
|
||||
public bool IsFishingBaitWeapon()
|
||||
{
|
||||
return catalogID >= 3940000 && catalogID <= 3949999;
|
||||
}
|
||||
|
||||
public bool IsFishingLureWeapon()
|
||||
{
|
||||
return catalogID >= 3940100 && catalogID <= 3940199;
|
||||
}
|
||||
|
||||
public bool IsArmor()
|
||||
{
|
||||
return catalogID >= 8000000 && catalogID <= 8999999;
|
||||
}
|
||||
|
||||
public static bool IsArmor(uint catalogID)
|
||||
{
|
||||
return catalogID >= 8000000 && catalogID <= 8999999;
|
||||
}
|
||||
|
||||
public bool IsAccessory()
|
||||
{
|
||||
return catalogID >= 9000000 && catalogID <= 9079999;
|
||||
}
|
||||
|
||||
public static bool IsAccessory(uint catalogID)
|
||||
{
|
||||
return catalogID >= 9000000 && catalogID <= 9079999;
|
||||
}
|
||||
|
||||
public bool IsAmulet()
|
||||
{
|
||||
return catalogID >= 9080000 && catalogID <= 9089999;
|
||||
}
|
||||
|
||||
public bool IsEnchantMateria()
|
||||
{
|
||||
return catalogID >= 10100000 && catalogID <= 10199999;
|
||||
}
|
||||
|
||||
public bool IsMaterial()
|
||||
{
|
||||
return catalogID >= 10000000 && catalogID <= 10999999;
|
||||
}
|
||||
|
||||
public bool IsEventItem()
|
||||
{
|
||||
return catalogID >= 11000000 && catalogID <= 15000000;
|
||||
}
|
||||
|
||||
public bool IsUseForBattle()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool IsHostilityItem()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsUsable()
|
||||
{
|
||||
return isUseable != 0;
|
||||
}
|
||||
|
||||
public bool IsUseFree()
|
||||
{
|
||||
return isUseable == -1;
|
||||
}
|
||||
|
||||
public bool IsLostAfterUsed()
|
||||
{
|
||||
return !IsEquipment();
|
||||
}
|
||||
|
||||
public int GetShieldGuardTime()
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
public Double GetItemHQValue(float value1, float value2)
|
||||
{
|
||||
return Math.Max(value1 + 1, Math.Ceiling(value1 * value2));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
class EquipmentItem : ItemData
|
||||
{
|
||||
//graphics
|
||||
public readonly uint graphicsWeaponId;
|
||||
public readonly uint graphicsEquipmentId;
|
||||
public readonly uint graphicsVariantId;
|
||||
public readonly uint graphicsColorId;
|
||||
|
||||
//equipment sheet
|
||||
public readonly int equipPoint;
|
||||
public readonly short equipTribe;
|
||||
|
||||
public readonly int paramBonusType1;
|
||||
public readonly short paramBonusValue1;
|
||||
public readonly int paramBonusType2;
|
||||
public readonly short paramBonusValue2;
|
||||
public readonly int paramBonusType3;
|
||||
public readonly short paramBonusValue3;
|
||||
public readonly int paramBonusType4;
|
||||
public readonly short paramBonusValue4;
|
||||
public readonly int paramBonusType5;
|
||||
public readonly short paramBonusValue5;
|
||||
public readonly int paramBonusType6;
|
||||
public readonly short paramBonusValue6;
|
||||
public readonly int paramBonusType7;
|
||||
public readonly short paramBonusValue7;
|
||||
public readonly int paramBonusType8;
|
||||
public readonly short paramBonusValue8;
|
||||
public readonly int paramBonusType9;
|
||||
public readonly short paramBonusValue9;
|
||||
public readonly int paramBonusType10;
|
||||
public readonly short paramBonusValue10;
|
||||
|
||||
public readonly short AdditionalEffect;
|
||||
public readonly bool materialBindPermission;
|
||||
public readonly short materializeTable;
|
||||
|
||||
public EquipmentItem(MySqlDataReader reader)
|
||||
: base (reader)
|
||||
{
|
||||
if (!reader.IsDBNull(reader.GetOrdinal("weaponId")) && !reader.IsDBNull(reader.GetOrdinal("equipmentId")) && !reader.IsDBNull(reader.GetOrdinal("variantId")) && !reader.IsDBNull(reader.GetOrdinal("colorId")))
|
||||
{
|
||||
graphicsWeaponId = reader.GetUInt32("weaponId");
|
||||
graphicsEquipmentId = reader.GetUInt32("equipmentId");
|
||||
graphicsVariantId = reader.GetUInt32("variantId");
|
||||
graphicsColorId = reader.GetUInt32("colorId");
|
||||
}
|
||||
|
||||
equipPoint = reader.GetInt32("equipPoint");
|
||||
equipTribe = reader.GetInt16("equipTribe");
|
||||
|
||||
paramBonusType1 = reader.GetInt32("paramBonusType1");
|
||||
paramBonusValue1 = reader.GetInt16("paramBonusValue1");
|
||||
paramBonusType2 = reader.GetInt32("paramBonusType2");
|
||||
paramBonusValue2 = reader.GetInt16("paramBonusValue2");
|
||||
paramBonusType3 = reader.GetInt32("paramBonusType3");
|
||||
paramBonusValue3 = reader.GetInt16("paramBonusValue3");
|
||||
paramBonusType4 = reader.GetInt32("paramBonusType4");
|
||||
paramBonusValue4 = reader.GetInt16("paramBonusValue4");
|
||||
paramBonusType5 = reader.GetInt32("paramBonusType5");
|
||||
paramBonusValue5 = reader.GetInt16("paramBonusValue5");
|
||||
paramBonusType6 = reader.GetInt32("paramBonusType6");
|
||||
paramBonusValue6 = reader.GetInt16("paramBonusValue6");
|
||||
paramBonusType7 = reader.GetInt32("paramBonusType7");
|
||||
paramBonusValue7 = reader.GetInt16("paramBonusValue7");
|
||||
paramBonusType8 = reader.GetInt32("paramBonusType8");
|
||||
paramBonusValue8 = reader.GetInt16("paramBonusValue8");
|
||||
paramBonusType9 = reader.GetInt32("paramBonusType9");
|
||||
paramBonusValue9 = reader.GetInt16("paramBonusValue9");
|
||||
paramBonusType10 = reader.GetInt32("paramBonusType10");
|
||||
paramBonusValue10 = reader.GetInt16("paramBonusValue10");
|
||||
|
||||
AdditionalEffect = reader.GetInt16("additionalEffect");
|
||||
materialBindPermission = reader.GetBoolean("materiaBindPermission");
|
||||
materializeTable = reader.GetInt16("materializeTable");
|
||||
}
|
||||
}
|
||||
|
||||
class WeaponItem : EquipmentItem
|
||||
{
|
||||
//extra graphics
|
||||
public readonly uint graphicsOffhandWeaponId;
|
||||
public readonly uint graphicsOffhandEquipmentId;
|
||||
public readonly uint graphicsOffhandVariantId;
|
||||
|
||||
//weapon sheet
|
||||
public readonly short attack;
|
||||
public readonly short magicAttack;
|
||||
public readonly short craftProcessing;
|
||||
public readonly short craftMagicProcessing;
|
||||
public readonly short harvestPotency;
|
||||
public readonly short harvestLimit;
|
||||
public readonly byte frequency; // hit count, 2 for h2h weapons
|
||||
public readonly short rate;
|
||||
public readonly short magicRate;
|
||||
public readonly short craftProcessControl;
|
||||
public readonly short harvestRate;
|
||||
public readonly short critical;
|
||||
public readonly short magicCritical;
|
||||
public readonly short parry;
|
||||
|
||||
public readonly int damageAttributeType1; // 1 slashing, 2 piercing, 3 blunt, 4 projectile
|
||||
public readonly float damageAttributeValue1;
|
||||
public readonly int damageAttributeType2;
|
||||
public readonly float damageAttributeValue2;
|
||||
public readonly int damageAttributeType3;
|
||||
public readonly float damageAttributeValue3;
|
||||
|
||||
public readonly short damagePower;
|
||||
public readonly float damageInterval;
|
||||
public readonly short ammoVirtualDamagePower;
|
||||
public readonly float dps;
|
||||
|
||||
public WeaponItem(MySqlDataReader reader)
|
||||
: base(reader)
|
||||
{
|
||||
if (!reader.IsDBNull(reader.GetOrdinal("offHandWeaponId")) && !reader.IsDBNull(reader.GetOrdinal("offHandEquipmentId")) && !reader.IsDBNull(reader.GetOrdinal("offHandVarientId")))
|
||||
{
|
||||
graphicsOffhandWeaponId = reader.GetUInt32("offHandWeaponId");
|
||||
graphicsOffhandEquipmentId = reader.GetUInt32("offHandEquipmentId");
|
||||
graphicsOffhandVariantId = reader.GetUInt32("offHandVarientId");
|
||||
}
|
||||
|
||||
attack = reader.GetInt16("attack");
|
||||
magicAttack = reader.GetInt16("magicAttack");
|
||||
craftProcessing = reader.GetInt16("craftProcessing");
|
||||
craftMagicProcessing = reader.GetInt16("craftMagicProcessing");
|
||||
harvestPotency = reader.GetInt16("harvestPotency");
|
||||
harvestLimit = reader.GetInt16("harvestLimit");
|
||||
frequency = reader.GetByte("frequency");
|
||||
rate = reader.GetInt16("rate");
|
||||
magicRate = reader.GetInt16("magicRate");
|
||||
craftProcessControl = reader.GetInt16("craftProcessControl");
|
||||
harvestRate = reader.GetInt16("harvestRate");
|
||||
critical = reader.GetInt16("critical");
|
||||
magicCritical = reader.GetInt16("magicCritical");
|
||||
parry = reader.GetInt16("parry");
|
||||
|
||||
damageAttributeType1 = reader.GetInt32("damageAttributeType1");
|
||||
damageAttributeValue1 = reader.GetFloat("damageAttributeValue1");
|
||||
damageAttributeType2 = reader.GetInt32("damageAttributeType2");
|
||||
damageAttributeValue2 = reader.GetFloat("damageAttributeValue2");
|
||||
damageAttributeType3 = reader.GetInt32("damageAttributeType3");
|
||||
damageAttributeValue3 = reader.GetFloat("damageAttributeValue3");
|
||||
|
||||
damagePower = reader.GetInt16("damagePower");
|
||||
damageInterval = reader.GetFloat("damageInterval");
|
||||
ammoVirtualDamagePower = reader.GetInt16("ammoVirtualDamagePower");
|
||||
dps = (damagePower + ammoVirtualDamagePower) / damageInterval;
|
||||
}
|
||||
}
|
||||
|
||||
class ArmorItem : EquipmentItem
|
||||
{
|
||||
//armor sheet
|
||||
public readonly short defense;
|
||||
public readonly short magicDefense;
|
||||
public readonly short criticalDefense;
|
||||
public readonly short evasion;
|
||||
public readonly short magicResistance;
|
||||
|
||||
public readonly int damageDefenseType1;
|
||||
public readonly short damageDefenseValue1;
|
||||
public readonly int damageDefenseType2;
|
||||
public readonly short damageDefenseValue2;
|
||||
public readonly int damageDefenseType3;
|
||||
public readonly short damageDefenseValue3;
|
||||
public readonly int damageDefenseType4;
|
||||
public readonly short damageDefenseValue4;
|
||||
|
||||
public ArmorItem(MySqlDataReader reader)
|
||||
: base(reader)
|
||||
{
|
||||
defense = reader.GetInt16("defense");
|
||||
magicDefense = reader.GetInt16("magicDefense");
|
||||
criticalDefense = reader.GetInt16("criticalDefense");
|
||||
evasion = reader.GetInt16("evasion");
|
||||
magicResistance = reader.GetInt16("magicResistance");
|
||||
|
||||
damageDefenseType1 = reader.GetInt32("damageDefenseType1");
|
||||
damageDefenseValue1 = reader.GetInt16("damageDefenseValue1");
|
||||
damageDefenseType2 = reader.GetInt32("damageDefenseType2");
|
||||
damageDefenseValue2 = reader.GetInt16("damageDefenseValue2");
|
||||
damageDefenseType3 = reader.GetInt32("damageDefenseType3");
|
||||
damageDefenseValue3 = reader.GetInt16("damageDefenseValue3");
|
||||
damageDefenseType4 = reader.GetInt32("damageDefenseType4");
|
||||
damageDefenseValue4 = reader.GetInt16("damageDefenseValue4");
|
||||
}
|
||||
}
|
||||
|
||||
class AccessoryItem : EquipmentItem
|
||||
{
|
||||
//accessory sheet
|
||||
public readonly byte power;
|
||||
public readonly byte size;
|
||||
|
||||
public AccessoryItem(MySqlDataReader reader)
|
||||
: base(reader)
|
||||
{
|
||||
power = reader.GetByte("power");
|
||||
size = reader.GetByte("size");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects
|
||||
{
|
||||
class RecruitmentDetails
|
||||
{
|
||||
public string recruiterName;
|
||||
public string comment;
|
||||
|
||||
public uint purposeId;
|
||||
public uint locationId;
|
||||
public uint subTaskId;
|
||||
public uint timeSinceStart;
|
||||
|
||||
public uint[] discipleId = new uint[4];
|
||||
public uint[] classjobId = new uint[4];
|
||||
public byte[] minLvl = new byte[4];
|
||||
public byte[] maxLvl = new byte[4];
|
||||
public byte[] num = new byte[4];
|
||||
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects
|
||||
{
|
||||
class SeamlessBoundry
|
||||
{
|
||||
public readonly uint regionId;
|
||||
public readonly uint zoneId1, zoneId2;
|
||||
public readonly float zone1_x1, zone1_y1, zone1_x2, zone1_y2;
|
||||
public readonly float zone2_x1, zone2_y1, zone2_x2, zone2_y2;
|
||||
public readonly float merge_x1, merge_y1, merge_x2, merge_y2;
|
||||
|
||||
public SeamlessBoundry(uint regionId, uint zoneId1, uint zoneId2, float zone1_x1, float zone1_y1, float zone1_x2, float zone1_y2, float zone2_x1, float zone2_y1, float zone2_x2, float zone2_y2, float merge_x1, float merge_y1, float merge_x2, float merge_y2)
|
||||
{
|
||||
this.regionId = regionId;
|
||||
this.zoneId1 = zoneId1;
|
||||
this.zoneId2 = zoneId2;
|
||||
this.zone1_x1 = zone1_x1;
|
||||
this.zone1_y1= zone1_y1;
|
||||
this.zone1_x2 = zone1_x2;
|
||||
this.zone1_y2 = zone1_y2;
|
||||
this.zone2_x1 = zone2_x1;
|
||||
this.zone2_y1 = zone2_y1;
|
||||
this.zone2_x2 = zone2_x2;
|
||||
this.zone2_y2 = zone2_y2;
|
||||
this.merge_x1 = merge_x1;
|
||||
this.merge_y1 = merge_y1;
|
||||
this.merge_x2 = merge_x2;
|
||||
this.merge_y2 = merge_y2;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Meteor.Map.dataobjects
|
||||
{
|
||||
class SearchEntry
|
||||
{
|
||||
public ushort preferredClass;
|
||||
public ushort langauges;
|
||||
public ushort location;
|
||||
public ushort grandCompany;
|
||||
public ushort status;
|
||||
public ushort currentClass;
|
||||
public string name;
|
||||
public ushort[] classes = new ushort[2 * 20];
|
||||
public ushort[] jobs = new ushort[8];
|
||||
|
||||
public void WriteSearchEntry(BinaryWriter writer)
|
||||
{
|
||||
writer.Write((UInt16)preferredClass);
|
||||
writer.Write((UInt16)langauges);
|
||||
writer.Write((UInt16)location);
|
||||
writer.Write((UInt16)grandCompany);
|
||||
writer.Write((UInt16)status);
|
||||
writer.Write((UInt16)currentClass);
|
||||
|
||||
writer.Write(Encoding.ASCII.GetBytes(name), 0, Encoding.ASCII.GetByteCount(name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(name));
|
||||
|
||||
for (int i = 0; i < classes.Length; i++)
|
||||
writer.Write((UInt16)classes[i]);
|
||||
for (int i = 0; i < jobs.Length; i++)
|
||||
writer.Write((UInt16)jobs[i]);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,186 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.packets.send.actor;
|
||||
using System.Collections.Generic;
|
||||
using Meteor.Map.actors.chara.npc;
|
||||
|
||||
namespace Meteor.Map.dataobjects
|
||||
{
|
||||
class Session
|
||||
{
|
||||
public uint id = 0;
|
||||
Player playerActor;
|
||||
public List<Actor> actorInstanceList = new List<Actor>();
|
||||
public uint languageCode = 1;
|
||||
private uint lastPingPacket = Utils.UnixTimeStampUTC();
|
||||
|
||||
public bool isUpdatesLocked = true;
|
||||
|
||||
public string errorMessage = "";
|
||||
|
||||
public Session(uint sessionId)
|
||||
{
|
||||
this.id = sessionId;
|
||||
playerActor = new Player(this, sessionId);
|
||||
}
|
||||
|
||||
public void QueuePacket(List<SubPacket> packets)
|
||||
{
|
||||
foreach (SubPacket s in packets)
|
||||
QueuePacket(s);
|
||||
}
|
||||
|
||||
public void QueuePacket(SubPacket subPacket)
|
||||
{
|
||||
subPacket.SetTargetId(id);
|
||||
Server.GetWorldConnection().QueuePacket(subPacket);
|
||||
}
|
||||
|
||||
public Player GetActor()
|
||||
{
|
||||
return playerActor;
|
||||
}
|
||||
|
||||
public void Ping()
|
||||
{
|
||||
lastPingPacket = Utils.UnixTimeStampUTC();
|
||||
}
|
||||
|
||||
public bool CheckIfDCing()
|
||||
{
|
||||
uint currentTime = Utils.UnixTimeStampUTC();
|
||||
if (currentTime - lastPingPacket >= 5000) //Show D/C flag
|
||||
playerActor.SetDCFlag(true);
|
||||
else if (currentTime - lastPingPacket >= 30000) //DCed
|
||||
return true;
|
||||
else
|
||||
playerActor.SetDCFlag(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UpdatePlayerActorPosition(float x, float y, float z, float rot, ushort moveState)
|
||||
{
|
||||
if (isUpdatesLocked)
|
||||
return;
|
||||
|
||||
if (playerActor.positionX == x && playerActor.positionY == y && playerActor.positionZ == z && playerActor.rotation == rot)
|
||||
return;
|
||||
|
||||
/*
|
||||
playerActor.oldPositionX = playerActor.positionX;
|
||||
playerActor.oldPositionY = playerActor.positionY;
|
||||
playerActor.oldPositionZ = playerActor.positionZ;
|
||||
playerActor.oldRotation = playerActor.rotation;
|
||||
|
||||
playerActor.positionX = x;
|
||||
playerActor.positionY = y;
|
||||
playerActor.positionZ = z;
|
||||
*/
|
||||
playerActor.rotation = rot;
|
||||
playerActor.moveState = moveState;
|
||||
|
||||
//GetActor().GetZone().UpdateActorPosition(GetActor());
|
||||
playerActor.QueuePositionUpdate(new Vector3(x,y,z));
|
||||
}
|
||||
|
||||
public void UpdateInstance(List<Actor> list)
|
||||
{
|
||||
if (isUpdatesLocked)
|
||||
return;
|
||||
|
||||
List<BasePacket> basePackets = new List<BasePacket>();
|
||||
List<SubPacket> RemoveActorSubpackets = new List<SubPacket>();
|
||||
List<SubPacket> posUpdateSubpackets = new List<SubPacket>();
|
||||
|
||||
//Remove missing actors
|
||||
for (int i = 0; i < actorInstanceList.Count; i++)
|
||||
{
|
||||
//Retainer Instance
|
||||
if (actorInstanceList[i] is Retainer && playerActor.currentSpawnedRetainer == null)
|
||||
{
|
||||
QueuePacket(RemoveActorPacket.BuildPacket(actorInstanceList[i].actorId));
|
||||
actorInstanceList.RemoveAt(i);
|
||||
}
|
||||
else if (!list.Contains(actorInstanceList[i]) && !(actorInstanceList[i] is Retainer))
|
||||
{
|
||||
QueuePacket(RemoveActorPacket.BuildPacket(actorInstanceList[i].actorId));
|
||||
actorInstanceList.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
//Retainer Instance
|
||||
if (playerActor.currentSpawnedRetainer != null && !playerActor.sentRetainerSpawn)
|
||||
{
|
||||
Actor actor = playerActor.currentSpawnedRetainer;
|
||||
QueuePacket(actor.GetSpawnPackets(playerActor, 1));
|
||||
QueuePacket(actor.GetInitPackets());
|
||||
QueuePacket(actor.GetSetEventStatusPackets());
|
||||
actorInstanceList.Add(actor);
|
||||
((Npc)actor).DoOnActorSpawn(playerActor);
|
||||
playerActor.sentRetainerSpawn = true;
|
||||
}
|
||||
|
||||
//Add new actors or move
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
Actor actor = list[i];
|
||||
|
||||
if (actor.actorId == playerActor.actorId)
|
||||
continue;
|
||||
|
||||
if (actorInstanceList.Contains(actor))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
QueuePacket(actor.GetSpawnPackets(playerActor, 1));
|
||||
|
||||
QueuePacket(actor.GetInitPackets());
|
||||
QueuePacket(actor.GetSetEventStatusPackets());
|
||||
actorInstanceList.Add(actor);
|
||||
|
||||
if (actor is Npc)
|
||||
{
|
||||
((Npc)actor).DoOnActorSpawn(playerActor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void ClearInstance()
|
||||
{
|
||||
actorInstanceList.Clear();
|
||||
}
|
||||
|
||||
|
||||
public void LockUpdates(bool f)
|
||||
{
|
||||
isUpdatesLocked = f;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects
|
||||
{
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.Net.Sockets;
|
||||
|
||||
using Meteor.Common;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using Meteor.Map.packets.WorldPackets.Send;
|
||||
|
||||
namespace Meteor.Map.dataobjects
|
||||
{
|
||||
class ZoneConnection
|
||||
{
|
||||
//Connection stuff
|
||||
public Socket socket;
|
||||
public byte[] buffer;
|
||||
private BlockingCollection<SubPacket> SendPacketQueue = new BlockingCollection<SubPacket>(1000);
|
||||
public int lastPartialSize = 0;
|
||||
|
||||
public void QueuePacket(SubPacket subpacket)
|
||||
{
|
||||
if (SendPacketQueue.Count == SendPacketQueue.BoundedCapacity - 1)
|
||||
FlushQueuedSendPackets();
|
||||
|
||||
SendPacketQueue.Add(subpacket);
|
||||
}
|
||||
|
||||
public void FlushQueuedSendPackets()
|
||||
{
|
||||
if (socket == null || !socket.Connected)
|
||||
return;
|
||||
|
||||
while (SendPacketQueue.Count > 0)
|
||||
{
|
||||
SubPacket packet = SendPacketQueue.Take();
|
||||
|
||||
byte[] packetBytes = packet.GetBytes();
|
||||
|
||||
try
|
||||
{
|
||||
socket.Send(packetBytes);
|
||||
}
|
||||
catch (Exception e)
|
||||
{ Program.Log.Error(e, "Weird case, socket was d/ced: {0}"); }
|
||||
}
|
||||
}
|
||||
|
||||
public String GetAddress()
|
||||
{
|
||||
return String.Format("{0}:{1}", (socket.RemoteEndPoint as IPEndPoint).Address, (socket.RemoteEndPoint as IPEndPoint).Port);
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
{
|
||||
return (socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
if (socket.Connected)
|
||||
socket.Disconnect(false);
|
||||
}
|
||||
|
||||
public void RequestZoneChange(uint sessionId, uint destinationZoneId, byte spawnType, float spawnX, float spawnY, float spawnZ, float spawnRotation)
|
||||
{
|
||||
WorldRequestZoneChangePacket.BuildPacket(sessionId, destinationZoneId, spawnType, spawnX, spawnY, spawnZ, spawnRotation).DebugPrintSubPacket();
|
||||
QueuePacket(WorldRequestZoneChangePacket.BuildPacket(sessionId, destinationZoneId, spawnType, spawnX, spawnY, spawnZ, spawnRotation));
|
||||
}
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace FFXIVClassic_Lobby_Server.dataobjects
|
||||
{
|
||||
class DBWorld
|
||||
{
|
||||
public ushort id;
|
||||
public string address;
|
||||
public ushort port;
|
||||
public ushort listPosition;
|
||||
public ushort population;
|
||||
public string name;
|
||||
public bool isActive;
|
||||
public string motd;
|
||||
}
|
||||
}
|
@ -1,869 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.director;
|
||||
using Meteor.Map.Actors;
|
||||
using Meteor.Map.dataobjects;
|
||||
using Meteor.Map.packets.receive.events;
|
||||
using Meteor.Map.packets.send;
|
||||
using Meteor.Map.packets.send.events;
|
||||
using MoonSharp.Interpreter;
|
||||
using MoonSharp.Interpreter.Interop;
|
||||
using MoonSharp.Interpreter.Loaders;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Meteor.Common;
|
||||
using Meteor.Map.actors.area;
|
||||
using System.Threading;
|
||||
using Meteor.Map.actors.chara.ai;
|
||||
using Meteor.Map.actors.chara.ai.controllers;
|
||||
|
||||
namespace Meteor.Map.lua
|
||||
{
|
||||
class LuaEngine
|
||||
{
|
||||
public const string FILEPATH_PLAYER = "./scripts/player.lua";
|
||||
public const string FILEPATH_ZONE = "./scripts/unique/{0}/zone.lua";
|
||||
public const string FILEPATH_CONTENT = "./scripts/content/{0}.lua";
|
||||
public const string FILEPATH_COMMANDS = "./scripts/commands/{0}.lua";
|
||||
public const string FILEPATH_DIRECTORS = "./scripts/directors/{0}.lua";
|
||||
public const string FILEPATH_NPCS = "./scripts/unique/{0}/{1}/{2}.lua";
|
||||
public const string FILEPATH_QUEST = "./scripts/quests/{0}/{1}.lua";
|
||||
|
||||
private static LuaEngine mThisEngine;
|
||||
private Dictionary<Coroutine, ulong> mSleepingOnTime = new Dictionary<Coroutine, ulong>();
|
||||
private Dictionary<string, List<Coroutine>> mSleepingOnSignal = new Dictionary<string, List<Coroutine>>();
|
||||
private Dictionary<uint, Coroutine> mSleepingOnPlayerEvent = new Dictionary<uint, Coroutine>();
|
||||
|
||||
private Timer luaTimer;
|
||||
|
||||
|
||||
private LuaEngine()
|
||||
{
|
||||
UserData.RegistrationPolicy = InteropRegistrationPolicy.Automatic;
|
||||
|
||||
luaTimer = new Timer(new TimerCallback(PulseSleepingOnTime),
|
||||
null, TimeSpan.Zero, TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
public static LuaEngine GetInstance()
|
||||
{
|
||||
if (mThisEngine == null)
|
||||
mThisEngine = new LuaEngine();
|
||||
|
||||
return mThisEngine;
|
||||
}
|
||||
|
||||
public void AddWaitCoroutine(Coroutine coroutine, float seconds)
|
||||
{
|
||||
ulong time = Utils.MilisUnixTimeStampUTC() + (ulong)(seconds * 1000);
|
||||
mSleepingOnTime.Add(coroutine, time);
|
||||
}
|
||||
|
||||
public void AddWaitSignalCoroutine(Coroutine coroutine, string signal)
|
||||
{
|
||||
if (!mSleepingOnSignal.ContainsKey(signal))
|
||||
mSleepingOnSignal.Add(signal, new List<Coroutine>());
|
||||
mSleepingOnSignal[signal].Add(coroutine);
|
||||
}
|
||||
|
||||
public void AddWaitEventCoroutine(Player player, Coroutine coroutine)
|
||||
{
|
||||
if (!mSleepingOnPlayerEvent.ContainsKey(player.actorId))
|
||||
mSleepingOnPlayerEvent.Add(player.actorId, coroutine);
|
||||
}
|
||||
|
||||
public void PulseSleepingOnTime(object state)
|
||||
{
|
||||
ulong currentTime = Utils.MilisUnixTimeStampUTC();
|
||||
List<Coroutine> mToAwake = new List<Coroutine>();
|
||||
|
||||
foreach (KeyValuePair<Coroutine, ulong> entry in mSleepingOnTime)
|
||||
{
|
||||
if (entry.Value <= currentTime)
|
||||
mToAwake.Add(entry.Key);
|
||||
}
|
||||
|
||||
foreach (Coroutine key in mToAwake)
|
||||
{
|
||||
mSleepingOnTime.Remove(key);
|
||||
DynValue value = key.Resume();
|
||||
ResolveResume(null, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSignal(string signal, params object[] args)
|
||||
{
|
||||
List<Coroutine> mToAwake = new List<Coroutine>();
|
||||
|
||||
if (mSleepingOnSignal.ContainsKey(signal))
|
||||
{
|
||||
mToAwake.AddRange(mSleepingOnSignal[signal]);
|
||||
mSleepingOnSignal.Remove(signal);
|
||||
}
|
||||
|
||||
foreach (Coroutine key in mToAwake)
|
||||
{
|
||||
DynValue value = key.Resume(args);
|
||||
ResolveResume(null, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEventUpdate(Player player, List<LuaParam> args)
|
||||
{
|
||||
if (mSleepingOnPlayerEvent.ContainsKey(player.actorId))
|
||||
{
|
||||
try
|
||||
{
|
||||
Coroutine coroutine = mSleepingOnPlayerEvent[player.actorId];
|
||||
mSleepingOnPlayerEvent.Remove(player.actorId);
|
||||
DynValue value = coroutine.Resume(LuaUtils.CreateLuaParamObjectList(args));
|
||||
ResolveResume(player, coroutine, value);
|
||||
}
|
||||
catch (ScriptRuntimeException e)
|
||||
{
|
||||
LuaEngine.SendError(player, String.Format("OnEventUpdated: {0}", e.DecoratedMessage));
|
||||
player.EndEvent();
|
||||
}
|
||||
}
|
||||
else
|
||||
player.EndEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// // todo: this is dumb, should probably make a function for each action with different default return values
|
||||
/// or just make generic function and pass default value as first arg after functionName
|
||||
/// </summary>
|
||||
public static void CallLuaBattleFunction(Character actor, string functionName, params object[] args)
|
||||
{
|
||||
// todo: should use "scripts/zones/ZONE_NAME/battlenpcs/NAME.lua" instead of scripts/unique
|
||||
string path = "";
|
||||
|
||||
// todo: should we call this for players too?
|
||||
if (actor is Player)
|
||||
{
|
||||
// todo: check this is correct
|
||||
path = FILEPATH_PLAYER;
|
||||
}
|
||||
else if (actor is Npc)
|
||||
{
|
||||
// todo: this is probably unnecessary as im not sure there were pets for players
|
||||
if (!(actor.aiContainer.GetController<PetController>()?.GetPetMaster() is Player))
|
||||
path = String.Format("./scripts/unique/{0}/{1}/{2}.lua", actor.zone.zoneName, actor is BattleNpc ? "Monster" : "PopulaceStandard", ((Npc)actor).GetUniqueId());
|
||||
}
|
||||
// dont wanna throw an error if file doesnt exist
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var script = LoadGlobals();
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaBattleFunction [{functionName}] {e.Message}");
|
||||
}
|
||||
DynValue res = new DynValue();
|
||||
|
||||
if (!script.Globals.Get(functionName).IsNil())
|
||||
{
|
||||
res = script.Call(script.Globals.Get(functionName), args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int CallLuaStatusEffectFunction(Character actor, StatusEffect effect, string functionName, params object[] args)
|
||||
{
|
||||
// todo: this is stupid, load the actual effect name from db table
|
||||
string path = $"./scripts/effects/{effect.GetName()}.lua";
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaStatusEffectFunction [{functionName}] {e.Message}");
|
||||
}
|
||||
DynValue res = new DynValue();
|
||||
|
||||
if (!script.Globals.Get(functionName).IsNil())
|
||||
{
|
||||
res = script.Call(script.Globals.Get(functionName), args);
|
||||
if (res != null)
|
||||
return (int)res.Number;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaStatusEffectFunction [{effect.GetName()}] Unable to find script {path}");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int CallLuaBattleCommandFunction(Character actor, BattleCommand command, string folder, string functionName, params object[] args)
|
||||
{
|
||||
string path = $"./scripts/commands/{folder}/{command.name}.lua";
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{functionName}] {e.Message}");
|
||||
}
|
||||
DynValue res = new DynValue();
|
||||
|
||||
if (!script.Globals.Get(functionName).IsNil())
|
||||
{
|
||||
res = script.Call(script.Globals.Get(functionName), args);
|
||||
if (res != null)
|
||||
return (int)res.Number;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
path = $"./scripts/commands/{folder}/default.lua";
|
||||
//Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{command.name}] Unable to find script {path}");
|
||||
var script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{functionName}] {e.Message}");
|
||||
}
|
||||
DynValue res = new DynValue();
|
||||
// DynValue r = script.Globals.Get(functionName);
|
||||
|
||||
if (!script.Globals.Get(functionName).IsNil())
|
||||
{
|
||||
res = script.Call(script.Globals.Get(functionName), args);
|
||||
if (res != null)
|
||||
return (int)res.Number;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
public static void LoadBattleCommandScript(BattleCommand command, string folder)
|
||||
{
|
||||
string path = $"./scripts/commands/{folder}/{command.name}.lua";
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}");
|
||||
}
|
||||
command.script = script;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = $"./scripts/commands/{folder}/default.lua";
|
||||
//Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{command.name}] Unable to find script {path}");
|
||||
var script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}");
|
||||
}
|
||||
|
||||
command.script = script;
|
||||
}
|
||||
}
|
||||
|
||||
public static void LoadStatusEffectScript(StatusEffect effect)
|
||||
{
|
||||
string path = $"./scripts/effects/{effect.GetName()}.lua";
|
||||
|
||||
if (File.Exists(path))
|
||||
{
|
||||
var script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}");
|
||||
}
|
||||
effect.script = script;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = $"./scripts/effects/default.lua";
|
||||
//Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction [{command.name}] Unable to find script {path}");
|
||||
var script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error($"LuaEngine.CallLuaBattleCommandFunction {e.Message}");
|
||||
}
|
||||
|
||||
effect.script = script;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static string GetScriptPath(Actor target)
|
||||
{
|
||||
if (target is Player)
|
||||
{
|
||||
return String.Format(FILEPATH_PLAYER);
|
||||
}
|
||||
else if (target is Npc)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (target is Command)
|
||||
{
|
||||
return String.Format(FILEPATH_COMMANDS, target.GetName());
|
||||
}
|
||||
else if (target is Director)
|
||||
{
|
||||
return String.Format(FILEPATH_DIRECTORS, ((Director)target).GetScriptPath());
|
||||
}
|
||||
else if (target is PrivateAreaContent)
|
||||
{
|
||||
return String.Format(FILEPATH_CONTENT, ((PrivateAreaContent)target).GetPrivateAreaName());
|
||||
}
|
||||
else if (target is Area)
|
||||
{
|
||||
return String.Format(FILEPATH_ZONE, ((Area)target).zoneName);
|
||||
}
|
||||
else if (target is Quest)
|
||||
{
|
||||
string initial = ((Quest)target).actorName.Substring(0, 3);
|
||||
string questName = ((Quest)target).actorName;
|
||||
return String.Format(FILEPATH_QUEST, initial, questName);
|
||||
}
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
private List<LuaParam> CallLuaFunctionNpcForReturn(Player player, Npc target, string funcName, bool optional, params object[] args)
|
||||
{
|
||||
object[] args2 = new object[args.Length + (player == null ? 1 : 2)];
|
||||
Array.Copy(args, 0, args2, (player == null ? 1 : 2), args.Length);
|
||||
if (player != null)
|
||||
{
|
||||
args2[0] = player;
|
||||
args2[1] = target;
|
||||
}
|
||||
else
|
||||
args2[0] = target;
|
||||
|
||||
LuaScript parent = null, child = null;
|
||||
|
||||
if (File.Exists("./scripts/base/" + target.classPath + ".lua"))
|
||||
parent = LuaEngine.LoadScript("./scripts/base/" + target.classPath + ".lua");
|
||||
|
||||
Area area = target.zone;
|
||||
if (area is PrivateArea)
|
||||
{
|
||||
if (File.Exists(String.Format("./scripts/unique/{0}/privatearea/{1}_{2}/{3}/{4}.lua", area.zoneName, ((PrivateArea)area).GetPrivateAreaName(), ((PrivateArea)area).GetPrivateAreaType(), target.className, target.GetUniqueId())))
|
||||
child = LuaEngine.LoadScript(String.Format("./scripts/unique/{0}/privatearea/{1}_{2}/{3}/{4}.lua", area.zoneName, ((PrivateArea)area).GetPrivateAreaName(), ((PrivateArea)area).GetPrivateAreaType(), target.className, target.GetUniqueId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists(String.Format("./scripts/unique/{0}/{1}/{2}.lua", area.zoneName, target.className, target.GetUniqueId())))
|
||||
child = LuaEngine.LoadScript(String.Format("./scripts/unique/{0}/{1}/{2}.lua", area.zoneName, target.className, target.GetUniqueId()));
|
||||
}
|
||||
|
||||
if (parent == null && child == null)
|
||||
{
|
||||
LuaEngine.SendError(player, String.Format("ERROR: Could not find script for actor {0}.", target.GetName()));
|
||||
}
|
||||
|
||||
//Run Script
|
||||
DynValue result;
|
||||
|
||||
if (child != null && child.Globals[funcName] != null)
|
||||
result = child.Call(child.Globals[funcName], args2);
|
||||
else if (parent != null && parent.Globals[funcName] != null)
|
||||
result = parent.Call(parent.Globals[funcName], args2);
|
||||
else
|
||||
return null;
|
||||
|
||||
List<LuaParam> lparams = LuaUtils.CreateLuaParamList(result);
|
||||
return lparams;
|
||||
}
|
||||
|
||||
private void CallLuaFunctionNpc(Player player, Npc target, string funcName, bool optional, params object[] args)
|
||||
{
|
||||
object[] args2 = new object[args.Length + (player == null ? 1 : 2)];
|
||||
Array.Copy(args, 0, args2, (player == null ? 1 : 2), args.Length);
|
||||
if (player != null)
|
||||
{
|
||||
args2[0] = player;
|
||||
args2[1] = target;
|
||||
}
|
||||
else
|
||||
args2[0] = target;
|
||||
|
||||
LuaScript parent = null, child = null;
|
||||
|
||||
if (File.Exists("./scripts/base/" + target.classPath + ".lua"))
|
||||
parent = LuaEngine.LoadScript("./scripts/base/" + target.classPath + ".lua");
|
||||
|
||||
Area area = target.zone;
|
||||
if (area is PrivateArea)
|
||||
{
|
||||
if (File.Exists(String.Format("./scripts/unique/{0}/privatearea/{1}_{2}/{3}/{4}.lua", area.zoneName, ((PrivateArea)area).GetPrivateAreaName(), ((PrivateArea)area).GetPrivateAreaType(), target.className, target.GetUniqueId())))
|
||||
child = LuaEngine.LoadScript(String.Format("./scripts/unique/{0}/privatearea/{1}_{2}/{3}/{4}.lua", area.zoneName, ((PrivateArea)area).GetPrivateAreaName(), ((PrivateArea)area).GetPrivateAreaType(), target.className, target.GetUniqueId()));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (File.Exists(String.Format("./scripts/unique/{0}/{1}/{2}.lua", area.zoneName, target.className, target.GetUniqueId())))
|
||||
child = LuaEngine.LoadScript(String.Format("./scripts/unique/{0}/{1}/{2}.lua", area.zoneName, target.className, target.GetUniqueId()));
|
||||
}
|
||||
|
||||
if (parent == null && child == null)
|
||||
{
|
||||
LuaEngine.SendError(player, String.Format("Could not find script for actor {0}.", target.GetName()));
|
||||
return;
|
||||
}
|
||||
|
||||
//Run Script
|
||||
Coroutine coroutine = null;
|
||||
|
||||
if (child != null && !child.Globals.Get(funcName).IsNil())
|
||||
coroutine = child.CreateCoroutine(child.Globals[funcName]).Coroutine;
|
||||
else if (parent != null && parent.Globals.Get(funcName) != null && !parent.Globals.Get(funcName).IsNil())
|
||||
coroutine = parent.CreateCoroutine(parent.Globals[funcName]).Coroutine;
|
||||
|
||||
if (coroutine != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
DynValue value = coroutine.Resume(args2);
|
||||
ResolveResume(player, coroutine, value);
|
||||
}
|
||||
catch (ScriptRuntimeException e)
|
||||
{
|
||||
SendError(player, e.DecoratedMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<LuaParam> CallLuaFunctionForReturn(Player player, Actor target, string funcName, bool optional, params object[] args)
|
||||
{
|
||||
//Need a seperate case for NPCs cause that child/parent thing.
|
||||
if (target is Npc)
|
||||
return CallLuaFunctionNpcForReturn(player, (Npc)target, funcName, optional, args);
|
||||
|
||||
object[] args2 = new object[args.Length + (player == null ? 1 : 2)];
|
||||
Array.Copy(args, 0, args2, (player == null ? 1 : 2), args.Length);
|
||||
if (player != null)
|
||||
{
|
||||
args2[0] = player;
|
||||
args2[1] = target;
|
||||
}
|
||||
else
|
||||
args2[0] = target;
|
||||
|
||||
string luaPath = GetScriptPath(target);
|
||||
LuaScript script = LoadScript(luaPath);
|
||||
if (script != null)
|
||||
{
|
||||
if (!script.Globals.Get(funcName).IsNil())
|
||||
{
|
||||
//Run Script
|
||||
DynValue result = script.Call(script.Globals[funcName], args2);
|
||||
List<LuaParam> lparams = LuaUtils.CreateLuaParamList(result);
|
||||
return lparams;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!optional)
|
||||
SendError(player, String.Format("Could not find function '{0}' for actor {1}.", funcName, target.GetName()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!optional)
|
||||
SendError(player, String.Format("Could not find script for actor {0}.", target.GetName()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<LuaParam> CallLuaFunctionForReturn(string path, string funcName, bool optional, params object[] args)
|
||||
{
|
||||
string luaPath = path;
|
||||
LuaScript script = LoadScript(luaPath);
|
||||
if (script != null)
|
||||
{
|
||||
if (!script.Globals.Get(funcName).IsNil())
|
||||
{
|
||||
//Run Script
|
||||
DynValue result = script.Call(script.Globals[funcName], args);
|
||||
List<LuaParam> lparams = LuaUtils.CreateLuaParamList(result);
|
||||
return lparams;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void CallLuaFunction(Player player, Actor target, string funcName, bool optional, params object[] args)
|
||||
{
|
||||
//Need a seperate case for NPCs cause that child/parent thing.
|
||||
if (target is Npc)
|
||||
{
|
||||
CallLuaFunctionNpc(player, (Npc)target, funcName, optional, args);
|
||||
return;
|
||||
}
|
||||
|
||||
object[] args2 = new object[args.Length + 2];
|
||||
Array.Copy(args, 0, args2, 2, args.Length);
|
||||
args2[0] = player;
|
||||
args2[1] = target;
|
||||
|
||||
string luaPath = GetScriptPath(target);
|
||||
LuaScript script = LoadScript(luaPath);
|
||||
if (script != null)
|
||||
{
|
||||
if (!script.Globals.Get(funcName).IsNil())
|
||||
{
|
||||
try
|
||||
{
|
||||
Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine;
|
||||
DynValue value = coroutine.Resume(args2);
|
||||
ResolveResume(player, coroutine, value);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
player.SendMessage(0x20, "", e.Message);
|
||||
player.EndEvent();
|
||||
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!optional)
|
||||
SendError(player, String.Format("Could not find function '{0}' for actor {1}.", funcName, target.GetName()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(target is Area) && !optional)
|
||||
SendError(player, String.Format("Could not find script for actor {0}.", target.GetName()));
|
||||
}
|
||||
}
|
||||
|
||||
public void EventStarted(Player player, Actor target, EventStartPacket eventStart)
|
||||
{
|
||||
List<LuaParam> lparams = eventStart.luaParams;
|
||||
lparams.Insert(0, new LuaParam(2, eventStart.eventName));
|
||||
if (mSleepingOnPlayerEvent.ContainsKey(player.actorId))
|
||||
{
|
||||
Coroutine coroutine = mSleepingOnPlayerEvent[player.actorId];
|
||||
mSleepingOnPlayerEvent.Remove(player.actorId);
|
||||
|
||||
try
|
||||
{
|
||||
DynValue value = coroutine.Resume();
|
||||
ResolveResume(null, coroutine, value);
|
||||
}
|
||||
catch (ScriptRuntimeException e)
|
||||
{
|
||||
LuaEngine.SendError(player, String.Format("OnEventStarted: {0}", e.DecoratedMessage));
|
||||
player.EndEvent();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target is Director)
|
||||
((Director)target).OnEventStart(player, LuaUtils.CreateLuaParamObjectList(lparams));
|
||||
else
|
||||
CallLuaFunction(player, target, "onEventStarted", false, LuaUtils.CreateLuaParamObjectList(lparams));
|
||||
}
|
||||
}
|
||||
|
||||
public DynValue ResolveResume(Player player, Coroutine coroutine, DynValue value)
|
||||
{
|
||||
if (value == null || value.IsVoid())
|
||||
return value;
|
||||
|
||||
if (player != null && value.String != null && value.String.Equals("_WAIT_EVENT"))
|
||||
{
|
||||
GetInstance().AddWaitEventCoroutine(player, coroutine);
|
||||
}
|
||||
else if (value.Tuple != null && value.Tuple.Length >= 1 && value.Tuple[0].String != null)
|
||||
{
|
||||
switch (value.Tuple[0].String)
|
||||
{
|
||||
case "_WAIT_TIME":
|
||||
GetInstance().AddWaitCoroutine(coroutine, (float)value.Tuple[1].Number);
|
||||
break;
|
||||
case "_WAIT_SIGNAL":
|
||||
GetInstance().AddWaitSignalCoroutine(coroutine, (string)value.Tuple[1].String);
|
||||
break;
|
||||
case "_WAIT_EVENT":
|
||||
GetInstance().AddWaitEventCoroutine((Player)value.Tuple[1].UserData.Object, coroutine);
|
||||
break;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
#region RunGMCommand
|
||||
public static void RunGMCommand(Player player, String cmd, string[] param, bool help = false)
|
||||
{
|
||||
bool playerNull = player == null;
|
||||
|
||||
if (playerNull)
|
||||
{
|
||||
if (param.Length >= 2 && param[1].Contains("\""))
|
||||
player = Server.GetWorldManager().GetPCInWorld(param[1]);
|
||||
else if (param.Length > 2)
|
||||
player = Server.GetWorldManager().GetPCInWorld(param[1] + param[2]);
|
||||
}
|
||||
|
||||
if (playerNull && param.Length >= 3)
|
||||
player = Server.GetWorldManager().GetPCInWorld(param[1] + " " + param[2]);
|
||||
|
||||
// load from scripts/commands/gm/ directory
|
||||
var path = String.Format("./scripts/commands/gm/{0}.lua", cmd.ToLower());
|
||||
|
||||
// check if the file exists
|
||||
if (File.Exists(path))
|
||||
{
|
||||
// load global functions
|
||||
LuaScript script = LoadGlobals();
|
||||
|
||||
// see if this script has any syntax errors
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error("LuaEngine.RunGMCommand: {0}.", e.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// can we run this script
|
||||
if (!script.Globals.Get("onTrigger").IsNil())
|
||||
{
|
||||
// can i run this command
|
||||
var permissions = 0;
|
||||
|
||||
// parameter types (string, integer, double, float)
|
||||
var parameters = "";
|
||||
var description = "!" + cmd + ": ";
|
||||
|
||||
// get the properties table
|
||||
var res = script.Globals.Get("properties");
|
||||
|
||||
// make sure properties table exists
|
||||
if (!res.IsNil())
|
||||
{
|
||||
try
|
||||
{
|
||||
// returns table if one is found
|
||||
var table = res.Table;
|
||||
|
||||
// find each key/value pair
|
||||
foreach (var pair in table.Pairs)
|
||||
{
|
||||
if (pair.Key.String == "permissions")
|
||||
{
|
||||
permissions = (int)pair.Value.Number;
|
||||
}
|
||||
else if (pair.Key.String == "parameters")
|
||||
{
|
||||
parameters = pair.Value.String;
|
||||
}
|
||||
else if (pair.Key.String == "description")
|
||||
{
|
||||
description = pair.Value.String;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) { LuaScript.Log.Error("LuaEngine.RunGMCommand: " + e.Message); return; }
|
||||
}
|
||||
|
||||
// if this isnt a console command, make sure player exists
|
||||
if (player != null)
|
||||
{
|
||||
if (permissions > 0 && !player.isGM)
|
||||
{
|
||||
Program.Log.Info("LuaEngine.RunGMCommand: {0}'s GM level is too low to use command {1}.", player.actorName, cmd);
|
||||
return;
|
||||
}
|
||||
// i hate to do this, but cant think of a better way to keep !help
|
||||
else if (help)
|
||||
{
|
||||
player.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, String.Format("[Commands] [{0}]", cmd), description);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (help)
|
||||
{
|
||||
LuaScript.Log.Info("[Commands] [{0}]: {1}", cmd, description);
|
||||
return;
|
||||
}
|
||||
|
||||
// we'll push our lua params here
|
||||
List<object> LuaParam = new List<object>();
|
||||
|
||||
var i = playerNull ? 2 : 0;
|
||||
for (; i < parameters.Length; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
// convert chat parameters to command parameters
|
||||
switch (parameters[i])
|
||||
{
|
||||
case 'i':
|
||||
LuaParam.Add(Convert.ChangeType(param[i + 1], typeof(int)));
|
||||
continue;
|
||||
case 'd':
|
||||
LuaParam.Add(Convert.ChangeType(param[i + 1], typeof(double)));
|
||||
continue;
|
||||
case 'f':
|
||||
LuaParam.Add(Convert.ChangeType(param[i + 1], typeof(float)));
|
||||
continue;
|
||||
case 's':
|
||||
LuaParam.Add(param[i + 1]);
|
||||
continue;
|
||||
default:
|
||||
LuaScript.Log.Info("LuaEngine.RunGMCommand: {0} unknown parameter {1}.", path, parameters[i]);
|
||||
LuaParam.Add(param[i + 1]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is IndexOutOfRangeException) break;
|
||||
LuaParam.Add(param[i + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
// the script can double check the player exists, we'll push them anyways
|
||||
LuaParam.Insert(0, player);
|
||||
// push the arg count too
|
||||
LuaParam.Insert(1, i - (playerNull ? 2 : 0));
|
||||
|
||||
// run the script
|
||||
//script.Call(script.Globals["onTrigger"], LuaParam.ToArray());
|
||||
|
||||
// gm commands dont need to be coroutines?
|
||||
try
|
||||
{
|
||||
Coroutine coroutine = script.CreateCoroutine(script.Globals["onTrigger"]).Coroutine;
|
||||
DynValue value = coroutine.Resume(LuaParam.ToArray());
|
||||
GetInstance().ResolveResume(player, coroutine, value);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Program.Log.Error("LuaEngine.RunGMCommand: {0} - {1}", path, e.Message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
LuaScript.Log.Error("LuaEngine.RunGMCommand: Unable to find script {0}", path);
|
||||
return;
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static LuaScript LoadScript(string path)
|
||||
{
|
||||
if (!File.Exists(path))
|
||||
return null;
|
||||
|
||||
LuaScript script = LoadGlobals();
|
||||
|
||||
try
|
||||
{
|
||||
script.DoFile(path);
|
||||
}
|
||||
catch (SyntaxErrorException e)
|
||||
{
|
||||
Program.Log.Error("{0}.", e.DecoratedMessage);
|
||||
return null;
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
public static LuaScript LoadGlobals(LuaScript script = null)
|
||||
{
|
||||
script = script ?? new LuaScript();
|
||||
|
||||
// register and load all global functions here
|
||||
((FileSystemScriptLoader)script.Options.ScriptLoader).ModulePaths = FileSystemScriptLoader.UnpackStringPaths("./scripts/?;./scripts/?.lua");
|
||||
script.Globals["GetWorldManager"] = (Func<WorldManager>)Server.GetWorldManager;
|
||||
script.Globals["GetStaticActor"] = (Func<string, Actor>)Server.GetStaticActors;
|
||||
script.Globals["GetStaticActorById"] = (Func<uint, Actor>)Server.GetStaticActors;
|
||||
script.Globals["GetWorldMaster"] = (Func<Actor>)Server.GetWorldManager().GetActor;
|
||||
script.Globals["GetItemGamedata"] = (Func<uint, ItemData>)Server.GetItemGamedata;
|
||||
script.Globals["GetGuildleveGamedata"] = (Func<uint, GuildleveData>)Server.GetGuildleveGamedata;
|
||||
script.Globals["GetLuaInstance"] = (Func<LuaEngine>)LuaEngine.GetInstance;
|
||||
|
||||
script.Options.DebugPrint = s => { Program.Log.Debug(s); };
|
||||
return script;
|
||||
}
|
||||
|
||||
public static void SendError(Player player, string message)
|
||||
{
|
||||
message = "[LuaError] " + message;
|
||||
if (player == null)
|
||||
return;
|
||||
List<SubPacket> SendError = new List<SubPacket>();
|
||||
player.SendMessage(SendMessagePacket.MESSAGE_TYPE_SYSTEM_ERROR, "", message);
|
||||
player.QueuePacket(EndEventPacket.BuildPacket(player.actorId, player.currentEventOwner, player.currentEventName, 0));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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;
|
||||
|
||||
namespace Meteor.Map.lua
|
||||
{
|
||||
class LuaParam
|
||||
{
|
||||
public int typeID;
|
||||
public Object value;
|
||||
|
||||
public LuaParam(int type, Object value)
|
||||
{
|
||||
this.typeID = type;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,256 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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 MoonSharp.Interpreter;
|
||||
using NLog;
|
||||
using MoonSharp.Interpreter.Debugging;
|
||||
using System.IO;
|
||||
|
||||
namespace Meteor.Map.lua
|
||||
{
|
||||
class LuaScript : Script
|
||||
{
|
||||
public static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public LuaScript()
|
||||
{
|
||||
this.Options.DebugPrint = s => { Log.Debug(s); };
|
||||
}
|
||||
public new static DynValue RunFile(string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Script.RunFile(filename);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new void AttachDebugger(IDebugger debugger)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Script)this).AttachDebugger(debugger);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Debug(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue Call(DynValue function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).Call(function);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue Call(object function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).Call(function);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue Call(object function, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).Call(function, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue Call(DynValue function, params DynValue[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).Call(function, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue Call(DynValue function, params object[] args)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).Call(function, args);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue CreateCoroutine(DynValue function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).CreateCoroutine(function);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue CreateCoroutine(object function)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).CreateCoroutine(function);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue DoString(string code, Table globalContext = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).DoString(code, globalContext);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue DoFile(string filename, Table globalContext = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).DoFile(filename, globalContext);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new void Dump(DynValue function, Stream stream)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Script)this).Dump(function, stream);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
}
|
||||
}
|
||||
|
||||
public new DynValue RequireModule(string modname, Table globalContext = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
return ((Script)this).RequireModule(modname, globalContext);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public new void SetTypeMetatable(DataType type, Table metatable)
|
||||
{
|
||||
try
|
||||
{
|
||||
((Script)this).SetTypeMetatable(type, metatable);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
if (e is InterpreterException)
|
||||
Log.Debug(((InterpreterException)e).DecoratedMessage);
|
||||
else
|
||||
Log.Debug(e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,534 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.lua;
|
||||
using MoonSharp.Interpreter;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Meteor.Map
|
||||
{
|
||||
class LuaUtils
|
||||
{
|
||||
|
||||
public class ItemRefParam
|
||||
{
|
||||
public uint actorId;
|
||||
public byte unknown;
|
||||
public byte slot;
|
||||
public byte itemPackage;
|
||||
|
||||
public ItemRefParam(uint actorId, byte unknown, byte slot, byte itemPackage)
|
||||
{
|
||||
this.actorId = actorId;
|
||||
this.unknown = unknown;
|
||||
this.slot = slot;
|
||||
this.itemPackage = itemPackage;
|
||||
}
|
||||
}
|
||||
|
||||
public class ItemOfferParam
|
||||
{
|
||||
public uint actorId;
|
||||
public ushort offerSlot;
|
||||
public byte offerPackageId;
|
||||
public byte unknown1;
|
||||
public ushort seekSlot;
|
||||
public byte seekPackageId;
|
||||
public byte unknown2;
|
||||
|
||||
public ItemOfferParam(uint actorId, ushort offerSlot, byte offerPackageId, byte unknown1, ushort seekSlot, byte seekPackageId, byte unknown2)
|
||||
{
|
||||
this.actorId = actorId;
|
||||
this.offerSlot = offerSlot;
|
||||
this.offerPackageId = offerPackageId;
|
||||
this.unknown1 = unknown1;
|
||||
this.seekSlot = seekSlot;
|
||||
this.seekPackageId = seekPackageId;
|
||||
this.unknown2 = unknown2;
|
||||
}
|
||||
}
|
||||
|
||||
public class Type9Param
|
||||
{
|
||||
public ulong item1;
|
||||
public ulong item2;
|
||||
|
||||
public Type9Param(ulong item1, ulong item2)
|
||||
{
|
||||
this.item1 = item1;
|
||||
this.item2 = item2;
|
||||
}
|
||||
}
|
||||
|
||||
public static List<LuaParam> ReadLuaParams(BinaryReader reader)
|
||||
{
|
||||
List<LuaParam> luaParams = new List<LuaParam>();
|
||||
|
||||
bool isDone = false;
|
||||
while (true)
|
||||
{
|
||||
byte code = reader.ReadByte();
|
||||
object value = null;
|
||||
bool wasNil = false;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 0x0: //Int32
|
||||
value = Utils.SwapEndian(reader.ReadInt32());
|
||||
break;
|
||||
case 0x1: //Int32
|
||||
value = Utils.SwapEndian(reader.ReadUInt32());
|
||||
break;
|
||||
case 0x2: //Null Termed String
|
||||
List<byte> list = new List<byte>();
|
||||
while(true){
|
||||
byte readByte = reader.ReadByte();
|
||||
if (readByte == 0)
|
||||
break;
|
||||
list.Add(readByte);
|
||||
}
|
||||
value = Encoding.ASCII.GetString(list.ToArray());
|
||||
break;
|
||||
case 0x3: //Boolean True
|
||||
value = true;
|
||||
break;
|
||||
case 0x4: //Boolean False
|
||||
value = false;
|
||||
break;
|
||||
case 0x5: //Nil
|
||||
wasNil = true;
|
||||
break;
|
||||
case 0x6: //Actor (By Id)
|
||||
value = Utils.SwapEndian(reader.ReadUInt32());
|
||||
break;
|
||||
case 0x7: //Item Reference to Inventory Spot
|
||||
{
|
||||
uint type7ActorId = Utils.SwapEndian(reader.ReadUInt32());
|
||||
byte type7Unknown = reader.ReadByte();
|
||||
byte type7Slot = reader.ReadByte();
|
||||
byte type7InventoryType = reader.ReadByte();
|
||||
value = new ItemRefParam(type7ActorId, type7Unknown, type7Slot, type7InventoryType);
|
||||
}
|
||||
break;
|
||||
case 0x8: //Used for offering
|
||||
{
|
||||
uint actorId = Utils.SwapEndian(reader.ReadUInt32());
|
||||
ushort rewardSlot = Utils.SwapEndian(reader.ReadUInt16());
|
||||
byte rewardPackageId = reader.ReadByte();
|
||||
byte unk1 = reader.ReadByte(); //Always 0x2?
|
||||
ushort seekSlot = Utils.SwapEndian(reader.ReadUInt16());
|
||||
byte seekPackageId = reader.ReadByte();
|
||||
byte unk2 = reader.ReadByte(); //Always 0xD?
|
||||
value = new ItemOfferParam(actorId, rewardSlot, rewardPackageId, unk1, seekSlot, seekPackageId, unk2);
|
||||
}
|
||||
break;
|
||||
case 0x9: //Two Longs (only storing first one)
|
||||
value = new Type9Param(Utils.SwapEndian(reader.ReadUInt64()), Utils.SwapEndian(reader.ReadUInt64()));
|
||||
break;
|
||||
case 0xC: //Byte
|
||||
value = reader.ReadByte();
|
||||
break;
|
||||
case 0x1B: //Short?
|
||||
value = reader.ReadUInt16();
|
||||
break;
|
||||
case 0xF: //End
|
||||
isDone = true;
|
||||
continue;
|
||||
default:
|
||||
throw new ArgumentException("Unknown lua param...");
|
||||
}
|
||||
|
||||
if (isDone)
|
||||
break;
|
||||
|
||||
//Special case cause fuck Type8
|
||||
if (value != null && value is ItemOfferParam)
|
||||
{
|
||||
luaParams.Add(new LuaParam(code, value));
|
||||
luaParams.Add(new LuaParam(0x5, null)); //This is to clean up the seek script as it fucks with the args.
|
||||
}
|
||||
else if (value != null)
|
||||
luaParams.Add(new LuaParam(code, value));
|
||||
else if (wasNil)
|
||||
luaParams.Add(new LuaParam(code, value));
|
||||
}
|
||||
|
||||
return luaParams;
|
||||
}
|
||||
|
||||
public static List<LuaParam> ReadLuaParams(byte[] bytesIn)
|
||||
{
|
||||
List<LuaParam> luaParams = new List<LuaParam>();
|
||||
|
||||
using (MemoryStream memStream = new MemoryStream(bytesIn))
|
||||
{
|
||||
using (BinaryReader reader = new BinaryReader(memStream))
|
||||
{
|
||||
bool isDone = false;
|
||||
while (true)
|
||||
{
|
||||
byte code = reader.ReadByte();
|
||||
object value = null;
|
||||
bool wasNil = false;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 0x0: //Int32
|
||||
value = Utils.SwapEndian(reader.ReadInt32());
|
||||
break;
|
||||
case 0x1: //Int32
|
||||
value = Utils.SwapEndian(reader.ReadUInt32());
|
||||
break;
|
||||
case 0x2: //Null Termed String
|
||||
List<byte> list = new List<byte>();
|
||||
while (true)
|
||||
{
|
||||
byte readByte = reader.ReadByte();
|
||||
if (readByte == 0)
|
||||
break;
|
||||
list.Add(readByte);
|
||||
}
|
||||
value = Encoding.ASCII.GetString(list.ToArray());
|
||||
break;
|
||||
case 0x3: //Boolean True
|
||||
value = true;
|
||||
break;
|
||||
case 0x4: //Boolean False
|
||||
value = false;
|
||||
break;
|
||||
case 0x5: //Nil
|
||||
wasNil = true;
|
||||
break;
|
||||
case 0x6: //Actor (By Id)
|
||||
value = Utils.SwapEndian(reader.ReadUInt32());
|
||||
break;
|
||||
case 0x7: //Weird one used for inventory
|
||||
uint type7ActorId = Utils.SwapEndian(reader.ReadUInt32());
|
||||
byte type7Unknown = reader.ReadByte();
|
||||
byte type7Slot = reader.ReadByte();
|
||||
byte type7InventoryType = reader.ReadByte();
|
||||
value = new ItemRefParam(type7ActorId, type7Unknown, type7Slot, type7InventoryType);
|
||||
break;
|
||||
case 0x8:
|
||||
uint actorId = Utils.SwapEndian(reader.ReadUInt32());
|
||||
ushort rewardSlot = Utils.SwapEndian(reader.ReadUInt16());
|
||||
byte rewardPackageId = reader.ReadByte();
|
||||
byte unk1 = reader.ReadByte(); //Always 0x2?
|
||||
ushort seekSlot = Utils.SwapEndian(reader.ReadUInt16());
|
||||
byte seekPackageId = reader.ReadByte();
|
||||
byte unk2 = reader.ReadByte(); //Always 0xD?
|
||||
value = new ItemOfferParam(actorId, rewardSlot, rewardPackageId, unk1, seekSlot, seekPackageId, unk2);
|
||||
break;
|
||||
case 0x9: //Two Longs (only storing first one)
|
||||
value = new Type9Param(Utils.SwapEndian(reader.ReadUInt64()), Utils.SwapEndian(reader.ReadUInt64()));
|
||||
break;
|
||||
case 0xC: //Byte
|
||||
value = reader.ReadByte();
|
||||
break;
|
||||
case 0x1B: //Short?
|
||||
value = reader.ReadUInt16();
|
||||
break;
|
||||
case 0xF: //End
|
||||
isDone = true;
|
||||
continue;
|
||||
default:
|
||||
throw new ArgumentException("Unknown lua param...");
|
||||
}
|
||||
|
||||
if (isDone)
|
||||
break;
|
||||
|
||||
if (value != null && value is ItemOfferParam)
|
||||
{
|
||||
luaParams.Add(new LuaParam(code, value));
|
||||
luaParams.Add(new LuaParam(0x5, null)); //This is to clean up the seek script as it fucks with the args.
|
||||
}
|
||||
else if (value != null)
|
||||
luaParams.Add(new LuaParam(code, value));
|
||||
else if (wasNil)
|
||||
luaParams.Add(new LuaParam(code, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
return luaParams;
|
||||
}
|
||||
|
||||
public static void WriteLuaParams(BinaryWriter writer, List<LuaParam> luaParams)
|
||||
{
|
||||
if (luaParams == null)
|
||||
{
|
||||
Program.Log.Error("LuaUtils.WriteLuaParams LuaParams are null!");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (LuaParam l in luaParams)
|
||||
{
|
||||
if (l.typeID == 0x1)
|
||||
writer.Write((Byte)0);
|
||||
else
|
||||
writer.Write((Byte)l.typeID);
|
||||
|
||||
switch (l.typeID)
|
||||
{
|
||||
case 0x0: //Int32
|
||||
writer.Write((Int32)Utils.SwapEndian((Int32)l.value));
|
||||
break;
|
||||
case 0x1: //Int32
|
||||
writer.Write((UInt32)Utils.SwapEndian((UInt32)l.value));
|
||||
break;
|
||||
case 0x2: //Null Termed String
|
||||
string sv = (string)l.value;
|
||||
writer.Write(Encoding.ASCII.GetBytes(sv), 0, Encoding.ASCII.GetByteCount(sv));
|
||||
writer.Write((Byte)0);
|
||||
break;
|
||||
case 0x3: //Boolean True
|
||||
break;
|
||||
case 0x4: //Boolean False
|
||||
break;
|
||||
case 0x5: //Nil
|
||||
break;
|
||||
case 0x6: //Actor (By Id)
|
||||
writer.Write((UInt32)Utils.SwapEndian((UInt32)l.value));
|
||||
break;
|
||||
case 0x7: //Weird one used for inventory
|
||||
ItemRefParam type7 = (ItemRefParam)l.value;
|
||||
writer.Write((UInt32)Utils.SwapEndian((UInt32)type7.actorId));
|
||||
writer.Write((Byte)type7.unknown);
|
||||
writer.Write((Byte)type7.slot);
|
||||
writer.Write((Byte)type7.itemPackage);
|
||||
break;
|
||||
case 0x9: //Two Longs (only storing first one)
|
||||
writer.Write((UInt64)Utils.SwapEndian(((Type9Param)l.value).item1));
|
||||
writer.Write((UInt64)Utils.SwapEndian(((Type9Param)l.value).item2));
|
||||
break;
|
||||
case 0xC: //Byte
|
||||
writer.Write((Byte)l.value);
|
||||
break;
|
||||
case 0x1B: //Short?
|
||||
break;
|
||||
case 0xF: //End
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
writer.Write((Byte)0xF);
|
||||
}
|
||||
|
||||
public static List<LuaParam> CreateLuaParamList(DynValue fromScript)
|
||||
{
|
||||
List<LuaParam> luaParams = new List<LuaParam>();
|
||||
|
||||
if (fromScript == null)
|
||||
return luaParams;
|
||||
|
||||
if (fromScript.Type == DataType.Tuple)
|
||||
{
|
||||
foreach (DynValue d in fromScript.Tuple)
|
||||
{
|
||||
AddToList(d, luaParams);
|
||||
}
|
||||
}
|
||||
else
|
||||
AddToList(fromScript, luaParams);
|
||||
|
||||
return luaParams;
|
||||
}
|
||||
|
||||
public static List<LuaParam> CreateLuaParamList(params object[] list)
|
||||
{
|
||||
List<LuaParam> luaParams = new List<LuaParam>();
|
||||
|
||||
foreach (object o in list)
|
||||
{
|
||||
if (o != null && o.GetType().IsArray)
|
||||
{
|
||||
Array arrayO = (Array)o;
|
||||
foreach (object o2 in arrayO)
|
||||
AddToList(o2, luaParams);
|
||||
}
|
||||
else
|
||||
AddToList(o, luaParams);
|
||||
}
|
||||
|
||||
return luaParams;
|
||||
}
|
||||
|
||||
private static void AddToList(DynValue d, List<LuaParam> luaParams)
|
||||
{
|
||||
if (d.Type == DataType.Number)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x0, (int)d.Number));
|
||||
}
|
||||
else if (d.Type == DataType.Number)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x1, (uint)d.Number));
|
||||
}
|
||||
else if (d.Type == DataType.String)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x2, (string)d.String));
|
||||
}
|
||||
else if (d.Type == DataType.Boolean)
|
||||
{
|
||||
if (d.Boolean)
|
||||
luaParams.Add(new LuaParam(0x3, null));
|
||||
else
|
||||
luaParams.Add(new LuaParam(0x4, null));
|
||||
}
|
||||
else if (d.Type == DataType.Nil)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x5, null));
|
||||
}
|
||||
else if (d.Type == DataType.Table)
|
||||
{
|
||||
//luaParams.Add(new LuaParam(0x6, ((Actor)o).actorId));
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddToList(object o, List<LuaParam> luaParams)
|
||||
{
|
||||
if (o is int)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x0, (int)o));
|
||||
}
|
||||
else if (o is uint)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x1, (uint)o));
|
||||
}
|
||||
else if (o is Double)
|
||||
{
|
||||
if (((double)o) % 1 == 0)
|
||||
luaParams.Add(new LuaParam(0x0, (int)(double)o));
|
||||
}
|
||||
else if (o is string)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x2, (string)o));
|
||||
}
|
||||
else if (o is bool)
|
||||
{
|
||||
if (((bool)o))
|
||||
luaParams.Add(new LuaParam(0x3, null));
|
||||
else
|
||||
luaParams.Add(new LuaParam(0x4, null));
|
||||
}
|
||||
else if (o == null)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x5, null));
|
||||
}
|
||||
else if (o is Actor)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x6, ((Actor)o).actorId));
|
||||
}
|
||||
else if (o is ItemRefParam)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x7, (ItemRefParam)o));
|
||||
}
|
||||
else if (o is ItemOfferParam)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x8, (ItemOfferParam)o));
|
||||
}
|
||||
else if (o is Type9Param)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0x9, (Type9Param)o));
|
||||
}
|
||||
else if (o is byte)
|
||||
{
|
||||
luaParams.Add(new LuaParam(0xC, (byte)o));
|
||||
}
|
||||
}
|
||||
|
||||
public static object[] CreateLuaParamObjectList(List <LuaParam> luaParams)
|
||||
{
|
||||
object[] list = new object[luaParams.Count];
|
||||
|
||||
for (int i = 0; i < list.Length; i++)
|
||||
list[i] = luaParams[i].value;
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
|
||||
public static string DumpParams(List<LuaParam> lParams)
|
||||
{
|
||||
if (lParams == null)
|
||||
return "Param list was null?";
|
||||
|
||||
string dumpString = "";
|
||||
for (int i = 0; i < lParams.Count; i++)
|
||||
{
|
||||
switch (lParams[i].typeID)
|
||||
{
|
||||
case 0x0: //Int32
|
||||
dumpString += String.Format("0x{0:X}", (int)lParams[i].value);
|
||||
break;
|
||||
case 0x1: //Int32
|
||||
dumpString += String.Format("0x{0:X}", (uint)lParams[i].value);
|
||||
break;
|
||||
case 0x2: //Null Termed String
|
||||
dumpString += String.Format("\"{0}\"", (string)lParams[i].value);
|
||||
break;
|
||||
case 0x3: //Boolean True
|
||||
dumpString += "true";
|
||||
break;
|
||||
case 0x4: //Boolean False
|
||||
dumpString += "false";
|
||||
break;
|
||||
case 0x5: //NULL???
|
||||
dumpString += "nil";
|
||||
break;
|
||||
case 0x6: //Actor (By Id)
|
||||
dumpString += String.Format("0x{0:X}", (uint)lParams[i].value);
|
||||
break;
|
||||
case 0x7: //Weird one used for inventory
|
||||
ItemRefParam type7Param = ((ItemRefParam)lParams[i].value);
|
||||
dumpString += String.Format("Type7 Param: (0x{0:X}, 0x{1:X}, 0x{2:X}, 0x{3:X})", type7Param.actorId, type7Param.unknown, type7Param.slot, type7Param.itemPackage);
|
||||
break;
|
||||
case 0x8: //Weird one used for inventory
|
||||
ItemOfferParam itemOfferParam = ((ItemOfferParam)lParams[i].value);
|
||||
dumpString += String.Format("Type8 Param: (0x{0:X}, 0x{1:X}, 0x{2:X}, 0x{3:X}, 0x{4:X}, 0x{5:X}, 0x{6:X})", itemOfferParam.actorId, itemOfferParam.offerSlot, itemOfferParam.offerPackageId, itemOfferParam.unknown1, itemOfferParam.seekSlot, itemOfferParam.seekPackageId, itemOfferParam.unknown2);
|
||||
break;
|
||||
case 0x9: //Long (+ 8 bytes ignored)
|
||||
Type9Param type9Param = ((Type9Param)lParams[i].value);
|
||||
dumpString += String.Format("Type9 Param: (0x{0:X}, 0x{1:X})", type9Param.item1, type9Param.item2);
|
||||
break;
|
||||
case 0xC: //Byte
|
||||
dumpString += String.Format("0x{0:X}", (byte)lParams[i].value);
|
||||
break;
|
||||
case 0x1B: //Short?
|
||||
dumpString += String.Format("0x{0:X}", (ushort)lParams[i].value);
|
||||
break;
|
||||
case 0xF: //End
|
||||
break;
|
||||
}
|
||||
|
||||
if (i != lParams.Count - 1)
|
||||
dumpString += ", ";
|
||||
}
|
||||
|
||||
return dumpString;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.IO;
|
||||
|
||||
namespace Meteor.Map.packets.WorldPackets.Receive
|
||||
{
|
||||
class ErrorPacket
|
||||
{
|
||||
public uint errorCode;
|
||||
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public ErrorPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
errorCode = binReader.ReadUInt32();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.IO;
|
||||
|
||||
namespace Meteor.Map.packets.WorldPackets.Receive
|
||||
{
|
||||
class LinkshellResultPacket
|
||||
{
|
||||
public int resultCode;
|
||||
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public LinkshellResultPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
resultCode = binReader.ReadInt32();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.IO;
|
||||
|
||||
namespace Meteor.Map.packets.WorldPackets.Receive
|
||||
{
|
||||
class PartySyncPacket
|
||||
{
|
||||
public ulong partyGroupId;
|
||||
public uint owner;
|
||||
public uint[] memberActorIds;
|
||||
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public PartySyncPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
partyGroupId = binReader.ReadUInt64();
|
||||
owner = binReader.ReadUInt32();
|
||||
uint numMembers = binReader.ReadUInt32();
|
||||
memberActorIds = new uint[numMembers];
|
||||
|
||||
for (int i = 0; i < numMembers; i++)
|
||||
memberActorIds[i] = binReader.ReadUInt32();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.IO;
|
||||
|
||||
namespace Meteor.Map.packets.WorldPackets.Receive
|
||||
{
|
||||
class SessionBeginPacket
|
||||
{
|
||||
public bool isLogin;
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public SessionBeginPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
isLogin = binReader.ReadByte() != 0;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.IO;
|
||||
|
||||
namespace Meteor.Map.packets.WorldPackets.Receive
|
||||
{
|
||||
class SessionEndPacket
|
||||
{
|
||||
public uint destinationZoneId;
|
||||
public ushort destinationSpawnType;
|
||||
public float destinationX;
|
||||
public float destinationY;
|
||||
public float destinationZ;
|
||||
public float destinationRot;
|
||||
|
||||
public bool invalidPacket = false;
|
||||
|
||||
public SessionEndPacket(byte[] data)
|
||||
{
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryReader binReader = new BinaryReader(mem))
|
||||
{
|
||||
try
|
||||
{
|
||||
destinationZoneId = binReader.ReadUInt32();
|
||||
destinationSpawnType = binReader.ReadUInt16();
|
||||
destinationX = binReader.ReadSingle();
|
||||
destinationY = binReader.ReadSingle();
|
||||
destinationZ = binReader.ReadSingle();
|
||||
destinationRot = binReader.ReadSingle();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
invalidPacket = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
/*
|
||||
===========================================================================
|
||||
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.dataobjects;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace Meteor.Map.packets.WorldPackets.Send.Group
|
||||
{
|
||||
class CreateLinkshellPacket
|
||||
{
|
||||
public const ushort OPCODE = 0x1025;
|
||||
public const uint PACKET_SIZE = 0x48;
|
||||
|
||||
public static SubPacket BuildPacket(Session session, string name, ushort crest, uint master)
|
||||
{
|
||||
byte[] data = new byte[PACKET_SIZE - 0x20];
|
||||
using (MemoryStream mem = new MemoryStream(data))
|
||||
{
|
||||
using (BinaryWriter binWriter = new BinaryWriter(mem))
|
||||
{
|
||||
binWriter.Write(Encoding.ASCII.GetBytes(name), 0, Encoding.ASCII.GetByteCount(name) >= 0x20 ? 0x20 : Encoding.ASCII.GetByteCount(name));
|
||||
binWriter.BaseStream.Seek(0x20, SeekOrigin.Begin);
|
||||
binWriter.Write((UInt16)crest);
|
||||
binWriter.Write((UInt32)master);
|
||||
}
|
||||
}
|
||||
return new SubPacket(true, OPCODE, session.id, data);
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user