using CommonAI.Data; using CommonAI.RTS; using CommonAI.Zone.EventTrigger; using CommonAI.Zone.Formula; using CommonAI.Zone.Helper; using CommonAI.ZoneClient; using CommonAI.ZoneEditor; using CommonAI.ZoneServer.JSGModule; using CommonLang; using CommonLang.Concurrent; using CommonLang.IO; using CommonLang.Log; using CommonLang.Property; using CommonLang.Vector; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using static CommonAI.Zone.SpellTemplate; using static CommonAI.Zone.UnitInfo; namespace CommonAI.Zone.Instance { /// /// 服务端场景 /// public partial class InstanceZone : GameEntity { private static AtomicInteger s_alloc_zone_count = new AtomicInteger(0); private static AtomicInteger s_active_zone_count = new AtomicInteger(0); /// /// 分配实例数量 /// public static int AllocZoneCount { get { return s_alloc_zone_count.Value; } } /// /// 未释放实例数量 /// public static int ActiveZoneCount { get { return s_active_zone_count.Value; } } //------------------------------------------------------------------------ // 基础数据 private InstanceZoneListener mListener; private TemplateManager mTemplates; private IFormula mFormula = TemplateManager.Factory.Formula; private IQuestAdapter mQuestAdapter; private SyncMessageQueue mSyncActionQueue = new SyncMessageQueue(); private ConcurrentQueue mSendingEvents = new ConcurrentQueue(); private InstanceZoneObjectMap mObjects; public TemplateManager Templates { get { return mTemplates; } } public IQuestAdapter QuestAdapter { get { return mQuestAdapter; } } public int UpdateIntervalMS { get { return mLastInterval; } } private readonly int mMaxUnitCount; private int mLastInterval = 0; private ulong mTimer = 0; private long mCurPassTimeMS = 0; private long mQueryPassTimeMS = 0; private int mQueryPassTimeSEC = 0; private bool mHalfSync = false; readonly public long mCreateTime; readonly private Random random; readonly private Logger log; public string UUID { get; set; } //记录触发器总览,避免日志过多 public long mTotalTimeUse; public long mTotalTrigers; //------------------------------------------------------------------------ readonly private ZoneInfo m_TerrainSrc; readonly private SpaceDivision mSpaceDiv; public ZoneInfo TerrainSrc { get { return m_TerrainSrc; } } public ZoneInfo Terrain { get { return path_terrain_data.Data; } } public int SpaceDivSize { get; private set; } [Desc("单位当前帧位移的最小距离")] public float MinStep { get; private set; } [Desc("最大单位数量")] public int MaxUnitCount { get { return mMaxUnitCount; } } [Desc("场景中是否存在Area")] public bool HasArea { get { return mHasArea; } } //山大王id protected int mKingID; //绑定服务器 protected string mBindGameSrvId; //------------------------------------------------------------------------ /// /// /// /// /// 消息接收者 /// 场景数据 /// 空间分割参数 /// 最大单位数 /// 随机种子 internal InstanceZone(TemplateManager templates, InstanceZoneListener listener, ZoneEditor.SceneData data, GSCreateAreaData gsData, int spaceDivSize, int maxUnitCount, int randomSeed) { mCreateTime = TimeUtil.GetTimestampMS(); InstanceZone.s_alloc_zone_count++; InstanceZone.s_active_zone_count++; var info = data.ZoneData; this.log = LoggerFactory.GetLogger(string.Format("InstanceZone({0})", info.TemplateID)); this.random = TemplateManager.Factory.CreateRandom(randomSeed);// new Random(randomSeed); this.mTemplates = templates; this.IsSyncZ = false; this.MinStep = MoveHelper.GetDistance(1000 / templates.CFG.SYSTEM_FPS, templates.CFG.OBJECT_MOVE_TO_MIN_STEP_SEC); if (spaceDivSize < 1) { throw new Exception("SpaceDivSize must large than map 1 !"); } this.mMaxUnitCount = maxUnitCount; this.mListener = listener; this.m_TerrainSrc = info; this.SpaceDivSize = spaceDivSize; this.mSpaceDiv = new SpaceDivision( m_TerrainSrc.TotalWidth, m_TerrainSrc.TotalHeight, spaceDivSize * m_TerrainSrc.GridCellW, spaceDivSize * m_TerrainSrc.GridCellH); this.mObjects = new InstanceZoneObjectMap("zoneId:" + this.GetSceneID()); // this.path_terrain_data = new InstanceZoneManhattanMap(templates, info.Clone() as ZoneInfo); // this.path_finder = new AstarManhattan(path_terrain_data, true, spaceDivSize); // this.path_terrain_area_gen = new ManhattanMapAreaGenerator(path_terrain_data.Data); this.InitTerrain(data, spaceDivSize, out this.path_terrain_data, out this.path_finder, out this.path_terrain_area_gen); this.mQuestAdapter = TemplateManager.Factory.CreateQuestAdapter(this); this.baseInit(data, gsData); } protected virtual void baseInit(ZoneEditor.SceneData data, GSCreateAreaData gsData) { } // ----------------------------------------------------------------------------------- ~InstanceZone() { InstanceZone.s_alloc_zone_count--; } protected override void Disposing() { base.Disposing(); this.ClearEvents(); foreach (var obj in mObjects.Objects) { obj.Dispose(); } this.mObjects.Dispose(); this.mObjects = null; foreach (var flg in mFlags.Values) { flg.Dispose(); } this.mFlags.Clear(); this.DisposeTerrain(); this.mSpaceDiv.Dispose(); this.mQuestAdapter.Dispose(); this.mQuestAdapter = null; this.mListener = null; this.mTemplates = null; this.mFormula = null; this.mSyncActionQueue = null; this.mSendingEvents = null; this.mTasks.Clear(); this.mTasks = null; this.mTimeTasks.Dispose(); this.mTimeTasks = null; this.EnvironmentVarMap.Clear(); this.mFlags.Clear(); this.m_UnitsPos.Clear(); this.m_UnitsStates.Clear(); InstanceZone.s_active_zone_count--; } // ----------------------------------------------------------------------------------- protected Logger Log { get { return log; } } public int TotalWidth { get { return m_TerrainSrc.TotalWidth; } } public int TotalHeight { get { return m_TerrainSrc.TotalHeight; } } public int GridCellW { get { return m_TerrainSrc.GridCellW; } } public int GridCellH { get { return m_TerrainSrc.GridCellH; } } public int XCount { get { return m_TerrainSrc.XCount; } } public int YCount { get { return m_TerrainSrc.YCount; } } public ulong Tick { get { return mTimer; } } /// /// 是否发 SyncPosEvent 包 /// public bool SyncPos { get { return sync_pos_list.Enable; } set { sync_pos_list.Enable = value; } } /// /// 是否为低速率网络同步(将优化为半字通信) /// public bool IsHalfSync { get { return mHalfSync; } set { if (value && TotalWidth <= 255 && TotalHeight <= 255) { mHalfSync = value; } else { mHalfSync = false; } } } /// /// 同步包是否包含Z /// public bool IsSyncZ { get; set; } public long PassTimeMS { get { return mQueryPassTimeMS; } } public int PassTimeSEC { get { return mQueryPassTimeSEC; } } public Random RandomN { get { return random; } } // public void Trace(string text) // { // log.Info(text); // } // ----------------------------------------------------------------------------------- //------------------------------------------------------------------------------------ #region TIMING_AND_TASK private SyncMessageQueue> mTasks = new SyncMessageQueue>(); private TimeTaskQueue mTimeTasks = new TimeTaskQueue(); private void doTask(Action task) { task(this); } /// /// 【线程安全】向主线程排一个任务 /// /// public void queueTask(Action task) { mTasks.Enqueue(task); } public string GetInfo() { if (this.mObjects == null) { return "对象:-1"; } return "对象:" + AllObjectsCount + ", 单位:" + AllUnitsCount + ", 法术:" + AllSpellsCount + ", 道具:" + AllItemsCount + ", 玩家:" + AllPlayersCount; } /// /// 【线程安全】增加时间任务 /// /// /// /// /// public TimeTaskMS AddTimeTask(int intervalMS, int delayMS, int repeat, TickHandler handler) { return mTimeTasks.AddTimeTask(intervalMS, delayMS, repeat, handler); } /// /// 【线程安全】增加延时回调方法 /// /// /// public TimeTaskMS AddTimeDelayMS(int delayMS, TickHandler handler) { return mTimeTasks.AddTimeDelayMS(delayMS, handler); } /// /// 【线程安全】增加定时回调方法 /// /// /// public TimeTaskMS AddTimePeriodicMS(int intervalMS, TickHandler handler) { if (intervalMS <= 0) { log.Error("增加定时任务异常:" + intervalMS); return null; } return mTimeTasks.AddTimePeriodicMS(intervalMS, handler); } #endregion //----------------------------------------------------------------------------------- /// /// 【线程安全】向当前场景输入指令 /// /// public void pushAction(Action act) { mSyncActionQueue.Enqueue(act); } protected internal void queueEventInternal(Event evt) { mSendingEvents.Enqueue(evt); } /// /// 【内部调用】输出消息 /// public void queueEvent(ZoneEvent evt, GameEntity sender = null) { if (sender != null) { evt.sender = sender; } this.queueEventInternal(evt); } /// /// 【内部调用】输出消息 /// /// /// public void queueObjectEvent(InstanceZoneObject obj, ObjectEvent evt, bool force = false) { if (mSendingEvents == null) { if (mObjects == null) { log.Warn("queueObjectEvent exception1: " + this.GetSceneID() + ", mObjects null, " + this.UUID + ", " + evt.GetType() + ", " + new StackTrace().ToString()); } else { log.Warn("queueObjectEvent exception2: " + this.GetSceneID() + ", Players: " + this.AllPlayersCount + ", Items: " + this.AllItemsCount + ", Units: " + this.AllUnitsCount + ", " + this.UUID + ", " + evt.GetType() + ", " + new StackTrace().ToString()); } return; } if (obj.ID == 0) return; if (!force && !obj.IsInZone) return; obj.onSendingEvent(ref evt); if (evt != null) { evt.object_id = obj.ID; evt.sender = obj; mSendingEvents.Enqueue(evt); } } /// /// 立即发送指令 /// /// /// /// public void sendActorResponse(InstanceZoneObject obj, ActorRequest req, ActorResponse rsp) { if (obj.ID == 0) return; if (!obj.IsInZone) return; rsp.MessageID = req.MessageID; rsp.object_id = obj.ID; rsp.sender = obj; mSendingEvents.Enqueue(rsp); } public void sendMessageBox(string msg) { queueEventInternal(new TestMessageBox(msg)); } public virtual int GetSceneID() { return 0; } //----------------------------------------------------------------------------------- public void RecvMessageFromGameServer(ZoneServer.SendMessageR2B msg) { LastRecvMessageR2B = msg; if (mOnRecvFromGS != null) { mOnRecvFromGS.Invoke(this, msg); } } public void SendMessageToGameServer(string msg) { ZoneServer.SendMessageB2R b2r = new ZoneServer.SendMessageB2R(); b2r.Message = msg; LastSentMessageB2R = b2r; if (mOnSendToGS != null) { mOnSendToGS.Invoke(this, b2r); } } public void SendEventToGameServer(IExternalizable evt) { ZoneServer.SendEventB2R b2r = new ZoneServer.SendEventB2R(); b2r.evt = evt; if (mOnSendMessageB2REvent != null) { mOnSendMessageB2REvent.Invoke(this, b2r); } } public bool CheckAutoDropItem() { return this.mListener.CheckDropItem(); } //----------------------------------------------------------------------------------- public virtual void Update(int intervalMS, bool slowRefresh) { beginEventsRecord(); this.mLastInterval = intervalMS; this.MinStep = MoveHelper.GetDistance(intervalMS, Templates.CFG.OBJECT_MOVE_TO_MIN_STEP_SEC); if (mTimer == 0) { mCurPassTimeMS = 0; mQueryPassTimeMS = 0; mQueryPassTimeSEC = 0; foreach (InstanceFlag f in mFlags.Values) { f.OnStart(); } if (mOnInit != null) mOnInit.Invoke(this); } mTasks.ProcessMessages(doTask); mSyncActionQueue.ProcessMessages(updateAction); mObjects.Refresh(); var objetes = mObjects.Objects; { // clean space div state foreach (InstanceZoneObject u in objetes) { u.onUpdate(this, slowRefresh); } mSpaceDiv.ClearSpaceNearChanges(); bool dirty = false; foreach (InstanceZoneObject u in objetes) { if (u.Enable) { dirty = u.updatePos(this); u.mCurCellNode.MarkPosDirty(dirty); if (dirty && u.ClientVisible && u.SyncPos) { if (mOnObjectPosChanged != null) { mOnObjectPosChanged.Invoke(this, u); } sync_pos_list.Add(u); } } } } foreach (InstanceFlag f in mFlags.Values) { f.update(); } if (mOnUpdate != null) { mOnUpdate.Invoke(this); } updateEvents(); mTimeTasks.Update(intervalMS); mCurPassTimeMS += intervalMS; mQueryPassTimeMS = mCurPassTimeMS; mQueryPassTimeSEC = (int)(mCurPassTimeMS / 1000); mTimer++; } private void updateAction(Action act) { if (act is SystemMessage) { processSystemMessage(act as SystemMessage); } else if (act is ObjectAction) { ObjectAction oa = (ObjectAction)act; InstanceUnit unit = act.sender as InstanceUnit; if (unit == null) { unit = mObjects.GetObject(oa.object_id); } if (unit != null) { unit.doAction(oa); if (mOnHandleObjectAction != null) { mOnHandleObjectAction.Invoke(unit, oa); } mFormula.OnUnitHandleNetMessage(unit, oa); } else { log.Info("Drop message : " + oa); } } else { if (mOnHandleAction != null) { mOnHandleAction.Invoke(this, act); } mFormula.OnZoneHandleNetMessage(this, act); } } private void updateEvents() { if (sync_pos_list.Enable) { queueEvent(sync_pos_list.AsEvent(this)); } sync_pos_list.Clear(); while (mSendingEvents.TryDequeue(out Event evt)) { //log.Info("---->onEvent:" + evt.ToString()); mListener.onEventHandler(evt); if (mOnPostEvent != null) { mOnPostEvent.Invoke(this, evt); } if (evt is GameOverEvent && mOnGameOver != null) { mOnGameOver.Invoke(this, evt as GameOverEvent); } } } virtual protected void processSystemMessage(SystemMessage msg) { if (msg is Ping) { queueEventInternal(new Pong(msg as Ping)); } } //------------------------------------------------------------------------------------------- #region OBJECTS private uint mObjectIDIndexer = 0; internal uint genObjectID() { return ++mObjectIDIndexer; } // 获取一个单位 public T getObject(uint obj_id) where T : InstanceZoneObject { if (obj_id == 0) return null; T go = mObjects.GetObject(obj_id); if (go != null) { return go; } return null; } public InstanceUnit getUnit(uint obj_id) { if (obj_id == 0) return null; return mObjects.GetObject(obj_id); } public InstanceUnit getUnitByTemplateID(int templateId) { if (templateId == 0) return null; foreach (InstanceUnit u in mObjects.Units) { if (u.Info.TemplateID == templateId) { return u; } } return null; } public InstanceUnit getTheWeightOneAtForce(int force) { float weightest = -999; InstanceUnit theone = null; foreach (InstanceUnit u in mObjects.Units) { if (u.Force == force && u.Weight > weightest) { theone = u; weightest = u.Weight; } } return theone; } public InstanceUnit getUnitByName(string name) { if (string.IsNullOrEmpty(name)) { return null; } foreach (InstanceUnit u in mObjects.Units) { if (name.Equals(u.Name)) { return u; } } return null; } public InstancePlayer getPlayerByUUID(string playerUUID) { if (string.IsNullOrEmpty(playerUUID)) { return null; } return mObjects.GetPlayer(playerUUID); } public InstanceItem getItemByName(string name) { if (string.IsNullOrEmpty(name)) { return null; } foreach (InstanceItem u in mObjects.Items) { if (name.Equals(u.Name)) { return u; } } return null; } public InstanceUnit getUnitByID(int UnitID) { foreach (InstanceUnit obj in mObjects.Units) { if (obj.ID == UnitID) { return obj; } } return null; } public int getDeadUnit(int count, int force, ref List list) { int n = 0; foreach(var obj in mObjects.Units) { if(obj.is_dead() && obj.Force == force && !list.Exists(u => u.ID == obj.ID)) { list.Add(obj); if (++n >= count) break; } } return n; } public bool IsObjectMapNull() { return this.mObjects == null; } public IEnumerable AllPlayers { get { return mObjects.Players; } } public int AllPlayersCount { get { return mObjects.PlayersCount; } } public IEnumerable AllUnits { get { return mObjects.Units; } } public int AllUnitsCount { get { return mObjects.UnitsCount; } } public IEnumerable AllSpells { get { return mObjects.Spells; } } public int AllSpellsCount { get { return mObjects.SpellsCount; } } public IEnumerable AllItems { get { return mObjects.Items; } } public int AllItemsCount { get { return mObjects.ItemsCount; } } public IEnumerable AllObjects { get { return mObjects.Objects; } } public int AllObjectsCount { get { return mObjects.ObjectsCount; } } [Obsolete("use {@link #AllUnits} {@link #AllSpells} {@link #AllItems} for best performance!")] public IEnumerable allObjs() { return mObjects.Objects; } [Obsolete("use {@link #AllUnitsCount} {@link #AllSpellsCount} {@link #AllItemsCount} for best performance!")] public int allObjsCount() { return mObjects.ObjectsCount; } [Obsolete] public List selectUnits(Predicate select) where T : InstanceUnit { List ret = new List(mObjects.UnitsCount); foreach (InstanceUnit obj in mObjects.Units) { if (select(obj as T)) { ret.Add(obj as T); } } return ret; } public void selectUnits(Predicate select, List ret) where T : InstanceUnit { foreach (InstanceUnit obj in mObjects.Units) { if (select(obj as T)) { ret.Add(obj as T); } } } public T selectRandomUnit(Predicate select) where T : InstanceUnit { T ret = null; using (var list = ListObjectPool.AllocAutoRelease()) { foreach (InstanceUnit obj in mObjects.Units) { if (obj is T && select(obj as T)) { list.Add(obj as T); } } if (list.Count > 0) { ret = CUtils.GetRandomInArray(list, random) as T; } } return ret; } public T selectUnit(Predicate select) where T : InstanceUnit { foreach (InstanceUnit obj in mObjects.Units) { if (select(obj as T)) { return obj as T; } } return null; } //------------------------------------------------------------------------------------------- /// /// 创建一个单位实体 /// virtual public InstanceZoneObject CreateUnit(UnitInfo info, string name, int force, int alliesForce, int level) { InstanceUnit unit = TemplateManager.Factory.CreateUnit(this, info, name, force, alliesForce, level); if (unit != null) { return unit; } switch (info.UType) { case UnitInfo.UnitType.TYPE_PLAYER: return new InstancePlayer(this, info, name, force, level, alliesForce); case UnitInfo.UnitType.TYPE_MANUAL: return new InstanceManual(this, info, name, force, level); case UnitInfo.UnitType.TYPE_PET: return new InstancePet(this, info, name, force, level); case UnitInfo.UnitType.TYPE_SUMMON: return new InstanceSummon(this, info, name, force, level); case UnitInfo.UnitType.TYPE_BUILDING: return new InstanceBuilding(this, info, name, force, level); case UnitInfo.UnitType.TYPE_MONSTER: return new InstanceGuard(this, info, name, force, level); case UnitInfo.UnitType.TYPE_NPC: return new InstanceGuard(this, info, name, force, level); default: return new InstanceGuard(this, info, name, force, level); } } //------------------------------------------------------------------------------------------- public InstanceUnit AddUnit(int unitTemplateID, string name, int force, int level, float x, float y, float direction, bool pointLv = false, uint CustomId = 0, string alias = null) { UnitInfo info = Templates.getUnit(unitTemplateID); if (info != null) { return AddUnit(info, name, force, level, x, y, direction, null, "", 0, pointLv, CustomId, alias); } return null; } /// /// 添加一个单位 /// /// 单位模板 /// 场景中名字(Key) /// 单位阵营 /// 单位等级 /// 坐标 /// 坐标 /// 方向 /// 召唤者 /// public InstanceUnit AddUnit(UnitInfo info, string name, int force, int level, float x, float y, float direction, InstanceUnit summoner = null, String clientShowName = "", int gsFlag = 0, bool pointLv = false, uint CustomId = 0, string alias = null) { AddUnitEvent add; return AddUnit(info, name, force, level, x, y, direction, out add, summoner, clientShowName, gsFlag, 0, pointLv, CustomId, alias); } /// /// 添加一个单位 /// /// 单位模板 /// 场景中名字(Key) /// 单位阵营 /// 单位等级 /// 坐标 /// 坐标 /// 方向 /// 输出事件 /// 召唤者 /// /// private long mAddUnitPrintTime = 0; public InstanceUnit AddUnit(UnitInfo info, string name, int force, int level, float x, float y, float direction, out AddUnitEvent add, InstanceUnit summoner = null, String clientShowName = "", int gsFlag = 0, int alliesForce = 0, bool pointLv = false, uint CustomId = 0, string alias = null) { add = null; if (mObjects.UnitsCount >= mMaxUnitCount) { if (mAddUnitPrintTime < CommonLang.CUtils.localTimeMS) { mAddUnitPrintTime = CommonLang.CUtils.localTimeMS + 3000; log.Warn(string.Format("--Zone AddUnit to max: {0}, UUID: {1}, ID: {2} 单位数量:{3}", this.GetSceneID(), this.UUID, info.TemplateID, GetZoneUnitInfo())); } return null; } if (mTryAddUnit != null && !mTryAddUnit.Invoke(info)) { //log.Info(string.Format("Zone Unit TryAdd max: {0}, {1}, 单位数量:{2}", this.TerrainSrc.ID, info.TemplateID, mObjects.UnitsCount)); return null; } // 创建实体单位 InstanceZoneObject ret = CreateUnit(info, name, force, alliesForce, level); // 存入单位列表 if (ret is InstanceUnit) { InstanceUnit unit = ret as InstanceUnit; unit.Alias = alias; unit.gameServerFlag = gsFlag; if(CustomId > 0 && mObjects.ContainsObjectByKey(CustomId)) { log.Warn(string.Format("--Zone AddUnit failed, ID Exist: id:{0} template:{1}", CustomId, info.TemplateID)); return null; } // 存入单位列表 if (unit.tryAdd(x, y, direction, CustomId)) { mObjects.AddObject(unit); this.LastAddedUnit = unit; if (unit is ISummonedUnit) { ((ISummonedUnit)unit).SummonerUnit = summoner; } try { unit.onAdded(this, pointLv); if (mOnUnitAdded != null) mOnUnitAdded.Invoke(this, unit); add = new AddUnitEvent(); add.sender = unit; if (unit.ClientVisible) { if (clientShowName != null && !unit.IsPlayer && clientShowName.Length > 0) { unit.SetDiaplayerName(clientShowName); } //// 阵营判断 //if(info.UType == UnitType.TYPE_MONSTER && force > 1) //{ // add.flag = 1; //} queueEvent(add); } } catch (Exception err) { log.Warn("AddUnit catch :" + name + ", e:" + err); add.ErrorMessage = err.Message; return null; } finally { add.Sync = unit.GenSyncUnitInfo(true); } return unit; } } return null; } /** 获取场景单位异常信息 */ private string GetZoneUnitInfo(int nMinNums = 50) { HashMap mInfo = new HashMap(); foreach (InstanceUnit obj in mObjects.Units) { mInfo.Put(obj.Info.ID, mInfo.Get(obj.Info.ID) + 1); } string finalStr = ""; foreach (int unitID in mInfo.Keys) { int count = mInfo.Get(unitID); if (count < nMinNums) { continue; } finalStr = finalStr + (unitID) + "-" + count + ", "; } return finalStr; } public InstanceItem AddItem(ItemTemplate template, string name, float x, float y, float direction, int force, string disPlayName, out AddItemEvent add, InstanceUnit creater, int from) { add = null; if (mTryAddItem != null && !mTryAddItem.Invoke(template)) { //log.Info(string.Format("Zone item TryAdd max: {0}, {1}", this.mSceneType, template.TemplateID)); return null; } InstanceItem ret = TemplateManager.Factory.CreateItem(this, template, name, force, creater, disPlayName, from); ret.setPos(x, y); if (ret.tryAdd(x, y, direction)) { mObjects.AddObject(ret); this.LastCreatedInstanceItem = ret; add = new AddItemEvent(); add.sender = ret; if (ret.ClientVisible) { queueEvent(add); } try { ret.onAdded(this); if (mItemAdded != null) mItemAdded.Invoke(this, ret, creater); } catch (Exception err) { log.Warn("AddItem: " + template.ID + ", catch: " + err); add.ErrorMessage = err.Message; return null; } finally { add.Sync = ret.GenSyncItemInfo(true, null); } return ret; } return null; } public InstanceItem AddItem(ItemTemplate template, string name, float x, float y, float direction, int force, string disPlayName, InstanceUnit creater, int from = 0) { AddItemEvent add; return AddItem(template, name, x, y, direction, force, disPlayName, out add, creater, from); } /// /// 添加一个法术或飞行道具 /// /// /// /// /// 此法术的最初发起者 /// /// /// /// /// /// /// public InstanceSpell AddSpell( XmdsSkillType fromSkillType, SpellTemplate template, LaunchSpell launch, InstanceZoneObject sender, InstanceUnit launcher, uint target_obj_id, Vector2 targetPos, float startX, float startY, float direction, SpellChainLevelInfo chain = null, int actionIndex = -1, int maxAffectUnit = 0, Dictionary damageList = null, int spellIndex = 0, JSGCreateSpellData createData = null) { if (template != null && sender != null) { InstanceUnit target = getUnit(target_obj_id); switch (template.MType) { // case SpellTemplate.MotionType.Missile: // if (target == null) // { // return null; // } // break; case SpellTemplate.MotionType.BindingTarget: if (target == null) { return null; } startX = target.X; startY = target.Y; break; case SpellTemplate.MotionType.SelectTarget: if (targetPos != null) { startX = targetPos.X; startY = targetPos.Y; } break; case SpellTemplate.MotionType.SeekerMissile: case SpellTemplate.MotionType.SeekerSelectTarget: { target = null; target_obj_id = 0; } break; case SpellTemplate.MotionType.Chain: if (target == null) { return null; } break; case SpellTemplate.MotionType.Cannon: if (target == null && targetPos == null) { targetPos = new Vector2(startX, startY); } break; } // 创建实体单位 //InstanceSpell ret = new InstanceSpell(this, template, launch, launcher, sender, chainLevel); InstanceSpell ret = TemplateManager.Factory.CreateSpell(this, template, launch, launcher, sender, fromSkillType, damageList, spellIndex, createData); //ret.Direction = direction; ret.BindActionIndex = actionIndex; ret.MaxAffectUnit = (maxAffectUnit == 0) ? ret.Info.MaxAffectUnit : maxAffectUnit; ret.faceTo(direction); ret.setChainInfo(chain); ret.setTargetPos(targetPos); ret.setTarget(target); // 存入单位列表 float xChanage, yChange; launch.GetXModify(launcher, out xChanage, out yChange); if (ret.tryAdd(startX + xChanage, startY + yChange, direction)) { //System.Console.WriteLine("--AddSpell: " + ret.Info.ID + ", " + ret.ID); launcher.Virtual.DispatchSendSpellOverEvent(launch, template, ret.X, ret.Y); mObjects.AddObject(ret); this.LastLaunchSpell = ret.Info; ret.onAdded(this); if (ret.ClientVisible) { // 产生事件 AddSpellEvent evt = new AddSpellEvent(ret); evt.sender = ret; queueEvent(evt); } return ret; } } return null; } // 移除一个单位 public bool RemoveObject(InstanceZoneObject obj) { //Console.WriteLine("RemoveObject : " + obj.ID); if (mObjects.RemoveObject(obj)) { obj.onRemoved(this); if (obj is InstanceUnit) { InstanceUnit unit = obj as InstanceUnit; mFormula.OnUnitRemoved(unit); if (mOnUnitRemoved != null) mOnUnitRemoved.Invoke(this, unit); } else if (obj is InstanceItem) { if (this.mOnItemRemoved != null) this.mOnItemRemoved.Invoke(this, (InstanceItem)obj); } if (obj.ClientVisible) { RemoveObjectEvent remove = new RemoveObjectEvent(obj.ID); remove.sender = obj; queueEvent(remove); } obj.Dispose(); return true; } return false; } public InstanceZoneObject RemoveObjectByID(uint oid) { var obj = mObjects.GetObject(oid); if (obj != null && RemoveObject(obj)) { return obj; } return null; } public InstanceZoneObject RemoveUnitByID(uint unitTemplateId) { InstanceUnit obj = mObjects.GetUnit(unitTemplateId); if (obj != null && RemoveObject(obj)) { return obj; } return null; } public InstanceZoneObject RemoveSpellByLaunchIDAndSpellID(int launchId, int spellId) { InstanceSpell spell = mObjects.GetSpllByLanuchAndSpellId(launchId, spellId); if (spell != null && RemoveObject(spell)) { return spell; } return null; } public InstanceZoneObject RemoveItemByID(uint unitTemplateId) { InstanceItem obj = mObjects.GetItem(unitTemplateId); if (obj != null && RemoveObject(obj)) { Console.WriteLine("成功移除单位:" + unitTemplateId); return obj; } return null; } public int GetZoneKingID() { return this.mKingID; } public string GetBindGameSrvID() { return this.mBindGameSrvId; } //---------------------------------------------------------------------------------------------------------------------------------------- private class InstanceZoneObjectMap { private class DirtyList where T : InstanceZoneObject { private static Logger log = LoggerFactory.GetLogger("DirtyList"); private bool dirty = true; private List mObjectsCollection = new List(); private HashMap mObjects = new HashMap(); public int Count { get { return mObjects.Count; } } public void Add(K key, T obj) { dirty = true; mObjects.Add(key, obj); } public void Put(K key, T obj) { dirty = true; mObjects.Put(key, obj); } public bool Remove(K key) { if (mObjects.Remove(key)) { dirty = true; return true; } return false; } public void Dispose() { mObjectsCollection.Clear(); mObjects.Clear(); } public T Get(K key) { return mObjects.Get(key); } public bool ContainsKey(K key) { return mObjects.ContainsKey(key); } public IEnumerable GetCollection(string logFlag) { try { if (dirty) { dirty = false; mObjectsCollection.Clear(); mObjectsCollection.AddRange(mObjects.Values); } return mObjectsCollection; } catch (Exception err) { log.Warn("GetCollectioncatch:" + logFlag + ", CollectCnt: " + mObjectsCollection.Count + ", mObjectsCnt: " + mObjects.Count + ", e:" + err); } return mObjects.Values; } } private DirtyList mObjects = new DirtyList(); private DirtyList mObjects_MirrorUnits = new DirtyList(); private DirtyList mObjects_MirrorSpells = new DirtyList(); private DirtyList mObjects_MirrorItems = new DirtyList(); private DirtyList mObjects_MirrorPlayers = new DirtyList(); private string logFlag = ""; public InstanceZoneObjectMap(string logFlag) { this.logFlag = logFlag; } internal void Refresh() { //mObjects.Refresh(); //mObjects_MirrorUnits.Refresh(); //mObjects_MirrorSpells.Refresh(); //mObjects_MirrorItems.Refresh(); //mObjects_MirrorPlayers.Refresh(); } public void AddObject(InstanceZoneObject obj) { mObjects.Add(obj.ID, obj); if (obj is InstanceUnit) { mObjects_MirrorUnits.Add(obj.ID, obj as InstanceUnit); } else if (obj is InstanceSpell) { mObjects_MirrorSpells.Add(obj.ID, obj as InstanceSpell); } else if (obj is InstanceItem) { mObjects_MirrorItems.Add(obj.ID, obj as InstanceItem); } if (obj is InstancePlayer) { var p = obj as InstancePlayer; mObjects_MirrorPlayers.Put(p.PlayerUUID, p); } } public bool RemoveObject(InstanceZoneObject obj) { if (mObjects.Remove(obj.ID)) { if (obj is InstanceUnit) { mObjects_MirrorUnits.Remove(obj.ID); } else if (obj is InstanceSpell) { mObjects_MirrorSpells.Remove(obj.ID); } else if (obj is InstanceItem) { mObjects_MirrorItems.Remove(obj.ID); } if (obj is InstancePlayer) { var p = obj as InstancePlayer; mObjects_MirrorPlayers.Remove(p.PlayerUUID); } return true; } return false; } private InstanceZoneObject GetObject(uint id) { return mObjects.Get(id); } public bool ContainsObject(InstanceZoneObject obj) { return mObjects.ContainsKey(obj.ID); } public bool ContainsObjectByKey(uint id) { return mObjects.ContainsKey(id); } public void Dispose() { mObjects.Dispose(); mObjects_MirrorUnits.Dispose(); mObjects_MirrorSpells.Dispose(); mObjects_MirrorItems.Dispose(); mObjects_MirrorPlayers.Dispose(); } public T GetObject(uint id) where T : InstanceZoneObject { Type type = typeof(T); if (type.IsSubclassOf(typeof(InstanceUnit))) { return mObjects_MirrorUnits.Get(id) as T; } else if (type.IsSubclassOf(typeof(InstanceSpell))) { return mObjects_MirrorSpells.Get(id) as T; } else if (type.IsSubclassOf(typeof(InstanceItem))) { return mObjects_MirrorItems.Get(id) as T; } else { return mObjects.Get(id) as T; } } public InstanceUnit GetUnit(uint unitTemplateId) { foreach (InstanceUnit obj in mObjects_MirrorUnits.GetCollection(logFlag)) { if (obj.Info.ID == unitTemplateId) { return obj; } } return null; } public InstanceSpell GetSpllByLanuchAndSpellId(int launchId, int spellId) { foreach (InstanceSpell obj in mObjects_MirrorSpells.GetCollection(logFlag)) { if (obj.Info.ID == spellId && obj.LauncherID == launchId) { return obj; } } return null; } public InstanceItem GetItem(uint itemId) { return mObjects_MirrorItems.Get(itemId); } public InstancePlayer GetPlayer(string uuid) { return mObjects_MirrorPlayers.Get(uuid); } public int ObjectsCount { get { return mObjects.Count; } } public IEnumerable Objects { get { return mObjects.GetCollection(logFlag); } } public int UnitsCount { get { return mObjects_MirrorUnits.Count; } } public IEnumerable Units { get { return mObjects_MirrorUnits.GetCollection(logFlag); } } public int SpellsCount { get { return mObjects_MirrorSpells.Count; } } public IEnumerable Spells { get { return mObjects_MirrorSpells.GetCollection(logFlag); } } public int ItemsCount { get { return mObjects_MirrorItems.Count; } } public IEnumerable Items { get { return mObjects_MirrorItems.GetCollection(logFlag); } } public int PlayersCount { get { return mObjects_MirrorPlayers.Count; } } public IEnumerable Players { get { return mObjects_MirrorPlayers.GetCollection(logFlag); } } } #endregion //------------------------------------------------------------------------------------ #region CALL_BACK internal void cb_unitDamageCallBack(InstanceUnit target, InstanceUnit attacker, int reduceHP, AttackSource source) { LastHittedUnit = target; LastAttackUnit = attacker; if (attacker != null) attacker.callback_onAttack(this, target, reduceHP, source); if (target != null) target.callback_onDamage(this, attacker, reduceHP, source); if (mOnUnitDamage != null) mOnUnitDamage.Invoke(this, target, attacker, reduceHP, source); } internal void cb_unitDeadCallBack(InstanceUnit obj, InstanceUnit attacker) { try { if (obj.mProcessDeadCallbackTime > CommonLang.CUtils.localTimeMS) { long processTime = CommonLang.TimeUtil.GetTimestampMS() - (CommonLang.CUtils.localTimeMS - (obj.mProcessDeadCallbackTime - 60000)); log.Warn("cb_unitDeadCallBack 跳过:" + processTime + ", " + obj.Parent.GetSceneID() + ", ID:" + obj.Info.ID + ", UID:" + obj.PlayerUUID); return; } if (obj.IsMonster && obj.Virtual.GetMaType() >= 4 && obj.Level >= 80) { log.Info("boss死亡:" + obj.Parent.GetSceneID() + ", 单位id=" + obj.Info.ID + ", 处理死亡时间:" + obj.mProcessDeadCallbackTime + ", SceneUID: " + obj.mZone.UUID + ", " + (attacker == null ? "null" : attacker.PlayerUUID)); if (attacker != null && attacker.IsPlayerUnit) { JSGHackerModule.OnPlayerKillMonster(attacker, obj); } } if (obj.mProcessDeadCallbackTime > 0) { log.Warn("单位多次回调死亡逻辑:" + this.GetSceneID() + ", " + this.UUID + ", " + obj.Info.ID + ", attackID:" + (attacker == null ? -1 : attacker.Info.ID) + ", UUID" + (attacker == null ? "null" : attacker.PlayerUUID) + ", " + obj.mProcessDeadCallbackTime); } obj.mProcessDeadCallbackTime = CommonLang.CUtils.localTimeMS + 60000; obj.mDeadTime = this.PassTimeMS; statisticForceDead(obj); LastHittedUnit = obj; LastKilledUnit = obj; //if (attacker == null) // attacker = obj; LastAttackUnit = attacker; obj.doDead(attacker); if (attacker != null) { obj.deadDropItems(attacker.Force); attacker.CurrentMoney += obj.Info.DropMoney; } mFormula.OnUnitDead(obj, attacker); obj.callback_onDead(this, attacker); if (mOnUnitDead != null) { mOnUnitDead.Invoke(this, obj, attacker); } } catch (Exception e) { log.Error("cb_unitDeadCallBackcatch: " + this.GetSceneID() + ", " + (obj == null ? -1 : obj.Info.ID) + ", " + (attacker == null ? -1 : attacker.Info.ID) + ",UUID: " + (obj == null ? "null" : obj.PlayerUUID) + ", " + (attacker == null ? "null" : attacker.PlayerUUID) + ", e: " + e); } } internal void cb_unitActivatedCallBack(InstanceUnit obj) { nearChange(obj); LastActivatedUnit = obj; obj.callback_onActivated(this); if (mOnUnitActivated != null) mOnUnitActivated.Invoke(this, obj); } internal void cb_unitRebirthCallBack(InstanceUnit obj) { LastRebirthUnit = obj; obj.callback_onRebirth(this); if (mOnUnitRebirth != null) mOnUnitRebirth.Invoke(this, obj); } internal void cb_unitGotInventoryItemCallBack(InstanceUnit obj, ItemTemplate item, int count) { LastUnitGotInventoryItem = item; obj.callback_onGotInventoryItem(this, item); if (mOnUnitGotInventoryItem != null) mOnUnitGotInventoryItem.Invoke(this, obj, item, count); } internal void cb_unitLostInventoryItemCallBack(InstanceUnit obj, ItemTemplate item, int count) { LastUnitLostInventoryItem = item; obj.callback_onLostInventoryItem(this, item); if (mOnUnitLostInventoryItem != null) mOnUnitLostInventoryItem.Invoke(this, obj, item, count); } internal int cb_unitGotInstanceItemCallBack(InstanceUnit obj, InstanceItem item) { LastUnitGotInstanceItem = item; obj.callback_onGotInstanceItem(this, item); short times = 0; if (mOnUnitGotInstanceItem != null) { InstancePlayer player = (obj as InstancePlayer); if (player == null) { return 0; } if (item.unitPickInfo != null) { if (item.Info.maxPickTimes > 0) { times = item.unitPickInfo.Get(player.PlayerUUID); if (times >= item.Info.maxPickTimes) { player.Virtual.SendMsgToClient(XmdsConstConfig.TIPS_PICK_MAX); return item.Info.maxPickTimes; } } item.unitPickInfo.Put(player.PlayerUUID, ++times); item.TotalPickTimes++; } mOnUnitGotInstanceItem.Invoke(this, obj, item); } return times; } internal void cb_unitUseItemCallBack(InstanceUnit obj, ItemTemplate item, InstanceUnit item_creater) { LastUnitUseItem = item; obj.callback_onUseItem(this, item, item_creater); if (mOnUnitUseItem != null) mOnUnitUseItem.Invoke(this, obj, item, item_creater); } internal void cb_unitGotBuffCallBack(InstanceUnit obj, InstanceUnit.BuffState buff) { LastUnitGotBuff = buff.Data; obj.callback_onGotBuff(this, buff); if (mOnUnitGotBuff != null) mOnUnitGotBuff.Invoke(this, obj, buff); } internal void cb_unitLostBuffCallBack(InstanceUnit obj, InstanceUnit.BuffState buff) { obj.callback_onLostBuff(this, buff); if (mOnUnitLostBuff != null) mOnUnitLostBuff.Invoke(this, obj, buff); } internal void cb_unitGotMoneyCallBack(InstanceUnit obj, int add_money) { if (mOnUnitGotMoney != null) { mOnUnitGotMoney.Invoke(obj, add_money); } } internal void cb_unitPickUnitCallBack(InstanceUnit src, InstanceUnit pickable) { LastPickableUnit = pickable; if (mOnUnitPickUnit != null) { mOnUnitPickUnit.Invoke(this, src, pickable); } } public void cb_unitOutBattleCallBack(InstanceUnit obj) { if (mOnUnitOutBattle != null) { mOnUnitOutBattle.Invoke(this, obj); } } internal bool cb_unitTryPickItem(InstanceUnit unit, InstanceItem item) { LastPickingItem = item; LastPickingItemUnit = unit; bool ret = true; if (mTryPickItem != null) { foreach (TryPickItemHandler trypick in mTryPickItem.GetInvocationList()) { if (!trypick.Invoke(this, unit, item)) { ret = false; } } } return ret; } internal void cb_unitFinishPickItem(InstanceUnit unit, InstanceItem item) { if (mFinishPickItem != null) { mFinishPickItem.Invoke(this, unit, item); } } internal void cb_unitLaunchSkill(InstanceUnit unit, CommonAI.Zone.Instance.InstanceUnit.SkillState ss) { LastLaunchSkill = ss.Data; LastLaunchSkillUnit = unit; if (mOnUnitLaunchSkill != null) { mOnUnitLaunchSkill.Invoke(this, unit, ss); } } internal void cb_playerReady(InstancePlayer player) { if (mPlayerReady != null) { mPlayerReady.Invoke(player); } } #endregion //------------------------------------------------------------------------------------------- #region UNIT_ATTACK /// /// 获得两个对象是否是队友 /// /// /// /// public virtual bool IsTeammates(InstanceUnit src, InstanceUnit dst) { return false; } /// /// 测试是否可见,单位间可交互(AOI) /// /// /// /// public virtual bool IsVisibleAOI(InstanceZoneObject src, InstanceZoneObject dst) { if (src.AoiStatus == dst.AoiStatus) { if (dst is InstanceUnit) { return (dst as InstanceUnit).IsVisible; } return true; } else if (src.AoiStatus != null) { return src.AoiStatus.CanSeeOther; } else if (dst.AoiStatus != null) { return dst.AoiStatus.CanSeeMe; } return false; } /// /// 测试是否可攻击 /// /// /// /// /// /// /// public virtual bool IsAttackable(InstanceUnit src, InstanceUnit target, SkillTemplate.CastTarget expectTarget, AttackReason reason, ITemplateData weapon) { if (src == null || target == null) { return false; } if (!IsVisibleAOI(src, target)) { return false; } if (target.CanWhiplashDeadBody) { if (!target.IsAttackable) return false; if (!target.IsVisible) return false; if (target.IsInvincible) return false; } else { if (!target.IsActive) return false; if (!target.IsVisible) return false; if (target.IsInvincible) return false; } switch (expectTarget) { case SkillTemplate.CastTarget.Enemy: return src.Force != target.Force; case SkillTemplate.CastTarget.PetForMaster: if (src is InstancePet) { return (src as InstancePet).Master == target; } return false; case SkillTemplate.CastTarget.Alias: return (src != target) && (src.Force == target.Force); case SkillTemplate.CastTarget.AlliesIncludeSelf: return (src.Force == target.Force); case SkillTemplate.CastTarget.AlliesExcludeSelf: return (src != target) && (src.Force == target.Force); case SkillTemplate.CastTarget.EveryOne: return true; case SkillTemplate.CastTarget.EveryOneExcludeSelf: return (src != target); case SkillTemplate.CastTarget.Self: return src == target; case SkillTemplate.CastTarget.EnemyAndSelf: return src.Force != target.Force || src == target; case SkillTemplate.CastTarget.NA: default: return false; } } /// /// 扫描所有可攻击对象 /// /// /// /// /// /// public void getAttackableUnits(InstanceUnit src, List list, SkillTemplate.CastTarget expectTarget, AttackReason reason, ITemplateData weapon) { for (int i = list.Count - 1; i >= 0; --i) { InstanceUnit o = list[i]; if (!IsAttackable(src, o, expectTarget, reason, weapon)) { list.RemoveAt(i); } } } /// /// 对单个单位攻击。 /// /// /// /// /// 判断IsAttackable /// public bool unitAttackSingle( InstanceUnit src, AttackSource attack, InstanceUnit target, SkillTemplate.CastTarget expectTarget) { if (IsAttackable(src, target, expectTarget, AttackReason.Attack, attack.Weapon)) { target.doHitAttack(src, attack); return true; } return false; } /// /// 某个单位对指定列表里的 IsAttackable 单位发起攻击。 /// 此操作会修改List /// /// /// /// 调用完成后,列表中未命中单位会自动移除。 /// 判断IsAttackable /// public int unitAttack( InstanceUnit src, AttackSource attack, List list, SkillTemplate.CastTarget expectTarget) { int count = 0; for (int i = list.Count - 1; i >= 0; --i) { InstanceUnit o = list[i]; if (IsAttackable(src, o, expectTarget, AttackReason.Attack, attack.Weapon)) { o.doHitAttack(src, attack); count++; } else { list.RemoveAt(i); } } return count; } /// /// 直接对列表中的单位攻击,不做任何判断。 /// /// /// /// public int unitAttackDirect( InstanceUnit src, AttackSource attack, List list) { int count = 0; for (int i = list.Count - 1; i >= 0; --i) { InstanceUnit o = list[i]; o.doHitAttack(src, attack); count++; } return count; } /// /// 某个单位发起周身攻击 /// /// /// /// /// /// public int unitAttackRound( InstanceUnit src, AttackSource attack, float range, SkillTemplate.CastTarget expectTarget) { using (var list = ListObjectPool.AllocAutoRelease()) { getObjectsRoundRange( Collider.Object_HitBody_TouchRound, src.X, src.Y, range, list, src.AoiStatus); return unitAttack(src, attack, list, expectTarget); } } /// /// 某个单位发起扇形范围攻击 /// /// /// /// /// /// /// /// public int unitAttackFan( InstanceUnit src, AttackSource attack, float direction, float range, float angle, SkillTemplate.CastTarget expectTarget) { float dr = angle / 2; using (var list = ListObjectPool.AllocAutoRelease()) { getObjectsFanRange( Collider.Object_HitBody_TouchFan, src.X, src.Y, range, direction - dr, direction + dr, list, src.AoiStatus); return unitAttack(src, attack, list, expectTarget); } } //------------------------------------------------------------------------------------------------------- // 单位释放法术 //------------------------------------------------------------------------------------------------------- public void unitLaunchSpell( XmdsSkillType fromSkillType, InstanceUnit launcher, LaunchSpell launch, float startX, float startY, uint targetUnitID = 0, Vector2 targetPos = null, int actionIndex = -1, int maxAffectUnit = 0, float pointDir = 0) { if (launch == null) { log.Error("单位释放技能异常: " + launcher.Info.ID + ", " + new StackTrace().ToString()); return; } if (CUtils.RandomPercent(RandomN, launch.LaunchPercent)) { float direction = pointDir == 0 ? launcher.Direction : pointDir; if (targetPos != null) { //direction = MathVector.getDegree(startX, startY, targetPos.x, targetPos.y); targetPos = (Vector2)targetPos.Clone(); } SpellTemplate spell = Templates.getSpell(launch.SpellID); if (spell == null) { log.Error("unitLaunchSpell找不到法术模板:" + launcher.PlayerUUID + ", " + launch.SpellID + ", SN: " + launch.SerialNumber); return; } JSGCreateSpellData createData; if (spell != null && launch.Count > 0 && mFormula.TryLaunchSpell(launcher, launch, ref spell, out createData, ref startX, ref startY)) { SpellChainLevelInfo chain = null; if (launch.ChainLevel > 0) { chain = new SpellChainLevelInfo(launch); } switch (launch.PType) { case LaunchSpell.PosType.POS_TYPE_FAN: { float startAngle = direction - launch.Angle / 2f;// + launch.StartAngle; float interAngle = launch.Count > 1 ? launch.Angle / (launch.Count - 1) : 0; for (int i = 0; i < launch.Count; i++) { AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos, startX, startY, startAngle + interAngle * i, chain, actionIndex, maxAffectUnit); } } break; case LaunchSpell.PosType.POS_TYPE_TOSAMETARGET: { InstanceUnit target = SeekSpellAttackable(launcher, spell, launcher.AoiStatus, startX, startY, launch.SeekingTargetRange, spell.ExpectTarget, SeekingExpect.Nearest, null); if (target != null) { launcher.faceTo(target.X, target.Y); launcher.SendForceSync(); targetPos = new Vector2(target.X, target.Y); //Console.WriteLine("-------------targetPos 1 - " + target.X + ", " + target.Y); //Console.WriteLine("-------------targetPos 2 - " + targetPos); int[] index = { 0, -1, 1 }; float testAngle = (float)Math.Atan2(targetPos.Y - launcher.Y, targetPos.X - launcher.X); float xBase = 3.0f; float xAdd = (float)(xBase * Math.Sin(testAngle)); //角Dir的对边 float yAdd = (float)(xBase * Math.Cos(testAngle)); //Dir的邻边 for (int i = 0; i < launch.Count; i++) { float tempX = startX - xAdd * index[i]; float tempY = startY + yAdd * index[i]; float angle = (float)Math.Atan2(targetPos.Y - tempY, targetPos.X - tempX); AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos, tempX, tempY, angle, chain, actionIndex, maxAffectUnit); } } else { int[] index = { 0, -1, 1 }; float xBase = 3.0f; float xAdd = (float)(xBase * Math.Sin(direction)); //角Dir的对边 float yAdd = (float)(xBase * Math.Cos(direction)); //Dir的邻边 float[] startAngleTemp = { 0, launch.Angle, launch.Angle * 1.2f, launch.Angle * 1.4f, launch.Angle * 1.5f }; for (int i = 0; i < launch.Count; i++) { float tempX = startX - xAdd * index[i]; float tempY = startY + yAdd * index[i]; AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos, tempX, tempY, direction - startAngleTemp[i] * index[i], chain, actionIndex, maxAffectUnit); } } } break; case LaunchSpell.PosType.POS_TYPE_CYCLE: { float startAngle = direction + launch.StartAngle; float interAngle = CMath.PI_MUL_2 / launch.Count; InstanceSpell[] spellBrothers = null; if (launch.Count > 1 && spell.MType == SpellTemplate.MotionType.Foxfire) { spellBrothers = new InstanceSpell[launch.Count]; } for (int i = 0; i < launch.Count; i++) { var sb = AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos, startX, startY, startAngle + interAngle * i, chain, actionIndex, maxAffectUnit); if (spellBrothers != null) { spellBrothers[i] = sb; sb.brothers = spellBrothers; } } } break; case LaunchSpell.PosType.POS_TYPE_X: { float startAngle = direction + launch.StartAngle; float[] interAngle = { launch.Angle, CMath.PI_F - launch.Angle, CMath.PI_F + launch.Angle, CMath.PI_F * 2 - launch.Angle }; Dictionary damageList = (spell.HitIntervalMS <= 0 ? new Dictionary() : null); for (int i = 0; i < 4; i++) { AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos, startX, startY, startAngle + interAngle[i], chain, actionIndex, maxAffectUnit, damageList); } } break; case LaunchSpell.PosType.POS_TYPE_RANDOM_DIRECTION: { for (int i = 0; i < launch.Count; i++) { float d = (float)(random.NextDouble() * CMath.PI_MUL_2); AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos, startX, startY, d, chain, actionIndex, maxAffectUnit); } } break; case LaunchSpell.PosType.POS_TYPE_AREA: using (var enemy_list = ListObjectPool.AllocAutoRelease()) { SeekSpellAttackable(enemy_list, startX, startY, launcher, spell, launch, chain); for (int i = 0; i < launch.Count && i < enemy_list.Count; i++) { AddSpell(fromSkillType, spell, launch, launcher, launcher, enemy_list[i].ID, null, startX, startY, direction, chain, actionIndex, maxAffectUnit); } } break; case LaunchSpell.PosType.POS_TYPE_RANDOM_FOR_SPELL: launch_randomTypeSpell(fromSkillType, spell, launcher, launch, chain, launcher, actionIndex); break; case LaunchSpell.PosType.POS_TYPE_RANDOM_POS: launch_randomPosSpell(fromSkillType, spell, launcher, launch, chain, launcher, actionIndex); break; case LaunchSpell.PosType.POS_TYPE_MANY_CANNON: launch_ManyCannon(fromSkillType, spell, launcher, launch, chain, launcher, targetUnitID, actionIndex, createData); break; case LaunchSpell.PosType.POS_TYPE_DEFAULT_SINGLE: default: { AddSpell(fromSkillType, spell, launch, launcher, launcher, targetUnitID, targetPos, startX, startY, direction + launch.StartAngle, chain, actionIndex, maxAffectUnit); } break; } } } } // 单位释放法术 public void spellLaunchSpell( XmdsSkillType fromSkillType, InstanceSpell sender, LaunchSpell launch, float startX, float startY, uint targetUnitID = 0, Vector2 targetPos = null, int actionIndex = -1, int maxAffectUnit = 0) { switch (launch.SenderUnit) { case LaunchSpell.LaunchSpllSenderUnit.Launcher: this.unitLaunchSpell(fromSkillType, sender.Launcher, launch, startX, startY, targetUnitID, targetPos); return; } if (CUtils.RandomPercent(RandomN, launch.LaunchPercent)) { SpellChainLevelInfo chain = sender.ChainInfo; if (chain != null) { if (!chain.TryLaunch(launch.SpellID)) { // chain is end // return; } } SpellTemplate spell = Templates.getSpell(launch.SpellID); JSGCreateSpellData createData; if (spell != null && launch.Count > 0 && mFormula.TryLaunchSpell(sender.Launcher, launch, ref spell, out createData, ref startX, ref startY)) { float direction = sender.Direction; if (targetPos != null) { direction = MathVector.getDegree(startX, startY, targetPos.X, targetPos.Y); targetPos = (Vector2)targetPos.Clone(); } switch (launch.PType) { case LaunchSpell.PosType.POS_TYPE_FAN: { float startAngle = direction - launch.Angle / 2f + launch.StartAngle; float interAngle = launch.Count > 1 ? launch.Angle / (launch.Count - 1) : 0; for (int i = 0; i < launch.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, sender.Launcher, targetUnitID, targetPos, startX, startY, startAngle + interAngle * i, chain, actionIndex, maxAffectUnit); } } break; case LaunchSpell.PosType.POS_TYPE_CYCLE: { float startAngle = direction + launch.StartAngle; float interAngle = CMath.PI_MUL_2 / launch.Count; for (int i = 0; i < launch.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, sender.Launcher, targetUnitID, targetPos, startX, startY, startAngle + interAngle * i, chain, actionIndex, maxAffectUnit); } } break; case LaunchSpell.PosType.POS_TYPE_X: { float startAngle = direction + launch.StartAngle; float[] interAngle = { launch.Angle, CMath.PI_F - launch.Angle, CMath.PI_F + launch.Angle, CMath.PI_F * 2 - launch.Angle }; Dictionary damageList = (spell.HitIntervalMS <= 0 ? new Dictionary() : null); for (int i = 0; i < launch.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, sender.Launcher, targetUnitID, targetPos, startX, startY, startAngle + interAngle[i], chain, actionIndex, maxAffectUnit, damageList); } } break; case LaunchSpell.PosType.POS_TYPE_RANDOM_FOR_SPELL: launch_randomTypeSpell(fromSkillType, spell, sender, launch, chain, sender.Launcher, actionIndex); break; case LaunchSpell.PosType.POS_TYPE_MANY_CANNON: launch_ManyCannon(fromSkillType, spell, sender, launch, chain, sender.Launcher, targetUnitID, actionIndex, createData); break; case LaunchSpell.PosType.POS_TYPE_RANDOM_POS: launch_randomPosSpell(fromSkillType, spell, sender, launch, chain, sender.Launcher, actionIndex); break; case LaunchSpell.PosType.POS_TYPE_RANDOM_DIRECTION: { for (int i = 0; i < launch.Count; i++) { float d = (float)(random.NextDouble() * CMath.PI_MUL_2); AddSpell(fromSkillType, spell, launch, sender, sender.Launcher, targetUnitID, targetPos, startX, startY, d, chain, actionIndex, maxAffectUnit); } } break; case LaunchSpell.PosType.POS_TYPE_AREA: using (var enemy_list = ListObjectPool.AllocAutoRelease()) { SeekSpellAttackable(enemy_list, startX, startY, sender.Launcher, spell, launch, chain); for (int i = 0; i < launch.Count && i < enemy_list.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, sender.Launcher, enemy_list[i].ID, targetPos, startX, startY, direction, chain, actionIndex, maxAffectUnit); } } break; case LaunchSpell.PosType.POS_TYPE_DEFAULT_SINGLE: default: { AddSpell(fromSkillType, spell, launch, sender, sender.Launcher, targetUnitID, targetPos, startX, startY, direction, chain, actionIndex, maxAffectUnit); } break; } } } } public void attackLaunchSpell( XmdsSkillType fromSkillType, InstanceUnit attacker, InstanceUnit damage, AttackSource source, int serverExt = 0) { LaunchSpell launch = source.Attack.Spell; if (launch == null) return; InstanceZoneObject sender; if (source.FromSpellUnit != null) { sender = source.FromSpellUnit; } else { sender = attacker; } switch (launch.SenderUnit) { case LaunchSpell.LaunchSpllSenderUnit.Launcher: sender = attacker; break; case LaunchSpell.LaunchSpllSenderUnit.DamagedUnit: sender = damage; break; } if (CUtils.RandomPercent(RandomN, launch.LaunchPercent)) { float startX = damage.X; float startY = damage.Y; SpellTemplate spell = Templates.getSpell(launch.SpellID); JSGCreateSpellData createData; if (spell != null && launch.Count > 0 && mFormula.TryLaunchSpell(attacker, launch, ref spell, out createData, ref startX, ref startY)) { SpellChainLevelInfo chain = null; if (source.FromSpellUnit != null && source.FromSpellUnit.ChainInfo != null) { chain = source.FromSpellUnit.ChainInfo; if (!chain.TryLaunch(launch.SpellID)) { // chain is end // return; } } switch (launch.PType) { case LaunchSpell.PosType.POS_TYPE_FAN: { float startAngle = sender.Direction - launch.Angle / 2f + launch.StartAngle; float interAngle = launch.Count > 1 ? launch.Angle / (launch.Count - 1) : 0; for (int i = 0; i < launch.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null, startX, startY, startAngle + interAngle * i, chain, serverExt); } } break; case LaunchSpell.PosType.POS_TYPE_CYCLE: { float startAngle = sender.Direction + launch.StartAngle; float interAngle = CMath.PI_MUL_2 / launch.Count; for (int i = 0; i < launch.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null, startX, startY, startAngle + interAngle * i, chain, serverExt); } } break; case LaunchSpell.PosType.POS_TYPE_X: { float startAngle = sender.Direction + launch.StartAngle; float[] interAngle = { launch.Angle, CMath.PI_F - launch.Angle, CMath.PI_F + launch.Angle, CMath.PI_F * 2 - launch.Angle }; Dictionary damageList = (spell.HitIntervalMS <= 0 ? new Dictionary() : null); for (int i = 0; i < launch.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null, startX, startY, startAngle + interAngle[i], chain, serverExt, 0, damageList); } } break; case LaunchSpell.PosType.POS_TYPE_RANDOM_FOR_SPELL: launch_randomTypeSpell(fromSkillType, spell, sender, launch, chain, attacker, serverExt); break; case LaunchSpell.PosType.POS_TYPE_MANY_CANNON: launch_ManyCannon(fromSkillType, spell, sender, launch, chain, attacker, damage.ID, serverExt, createData); break; case LaunchSpell.PosType.POS_TYPE_RANDOM_POS: launch_randomPosSpell(fromSkillType, spell, sender, launch, chain, attacker, serverExt); break; case LaunchSpell.PosType.POS_TYPE_RANDOM_DIRECTION: { for (int i = 0; i < launch.Count; i++) { float d = (float)(random.NextDouble() * CMath.PI_MUL_2); AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null, startX, startY, d, chain, serverExt); } } break; case LaunchSpell.PosType.POS_TYPE_AREA: using (var enemy_list = ListObjectPool.AllocAutoRelease()) { SeekSpellAttackable(enemy_list, startX, startY, attacker, spell, launch, chain); for (int i = 0; i < launch.Count && i < enemy_list.Count; i++) { AddSpell(fromSkillType, spell, launch, sender, attacker, enemy_list[i].ID, null, startX, startY, 0, chain, serverExt); } } break; case LaunchSpell.PosType.POS_TYPE_DEFAULT_SINGLE: default: { AddSpell(fromSkillType, spell, launch, sender, attacker, damage.ID, null, startX, startY, sender.Direction, chain, serverExt); } break; } } } } private void launch_randomTypeSpell(XmdsSkillType fromSkillType, SpellTemplate spell, InstanceZoneObject sender, LaunchSpell launch, SpellChainLevelInfo chain, InstanceUnit attacker, int serverExt = 0) { using (var list = ListObjectPool.AllocAutoRelease()) { attacker.Parent.getObjectsRoundRange( (obj, dx, dy, dr) => { var u = obj as InstanceUnit; //己方单位. if ((attacker.IsPlayer && !u.IsPlayer) || (!attacker.IsPlayer && u.IsPlayer)) { return CMath.includeRoundPoint(dx, dy, dr, u.X, u.Y); } return false; }, attacker.X, attacker.Y, launch.SeekingTargetRange, list, attacker.AoiStatus); int maxCount = launch.Count == 0 ? list.Count : Math.Min(launch.Count, list.Count); for (int i = 0; i < maxCount; i++) { float r = (float)(random.NextDouble() * spell.BodySize); float a = (float)(random.NextDouble() * CMath.PI_MUL_2); float x = (float)(list[i].X + Math.Cos(a) * r); float y = (float)(list[i].Y + Math.Sin(a) * r); float d = (float)(random.NextDouble() * CMath.PI_MUL_2); AddSpell(fromSkillType, spell, launch, sender, attacker, 0, null, x, y, d, chain, serverExt); } } } private void launch_randomPosSpell(XmdsSkillType fromSkillType, SpellTemplate spell, InstanceZoneObject sender, LaunchSpell launch, SpellChainLevelInfo chain, InstanceUnit attacker, int serverExt = 0) { for (int i = 0; i < launch.Count; i++) { float x = (float)(random.NextDouble() * launch.SpellRange / 2); float y = (float)(random.NextDouble() * launch.SpellRange / 2); float d = (float)(random.NextDouble() * CMath.PI_MUL_2); int randX = random.Next() % 2 == 0 ? 1 : -1; int randY = random.Next() % 2 == 0 ? 1 : -1; AddSpell(fromSkillType, spell, launch, sender, attacker, 0, null, sender.X + x * randX, sender.Y + y * randY, d, chain, serverExt); } } public InstanceSpell launch_ManyCannon(XmdsSkillType fromSkillType, SpellTemplate spell, InstanceZoneObject sender, LaunchSpell launch, SpellChainLevelInfo chain, InstanceUnit attacker, uint targetUnitID, int serverExt, JSGCreateSpellData createData) { //float angleOffset = targetUnitID == 0 ? launch.StartAngle / 3 : launch.StartAngle; float angleOffset = JSGModule.GetCurveStartAngleOffect(attacker, getUnit(targetUnitID), launch.StartAngle, spell.SeekingRange); float startAngle = CMath.OpitimizeRadians(attacker.Direction) + angleOffset; float launchX = attacker.X - (float)(1.0f * Math.Cos(attacker.Direction)); float launchY = attacker.Y - (float)(1.0f * Math.Sin(attacker.Direction)); Vector2 targetPos = JSGModule.GetCurveDefaultTargetPos(attacker, spell.SeekingRange); //System.Console.WriteLine("targetPos: " + targetPos.X + ", " + targetPos.Y); int totalCount = createData == null ? launch.Count : createData.mMaxSpellCount; return AddSpell(fromSkillType, spell, launch, sender, attacker, targetUnitID, targetPos, launchX, launchY, startAngle, chain, serverExt, 0, null, totalCount - 1, createData); } public void spellSummonUnit(InstanceSpell summoner, SummonUnit summon) { float x = summoner.X; float y = summoner.Y; float radius = summoner.BodyBlockSize; UnitInfo info = Templates.getUnit(summon.UnitTemplateID); if (info.UType != UnitType.TYPE_SUMMON || info.LifeTimeMS <= 0) { log.Warn("召唤非召唤类单位1:" + summoner.ID + ", 召唤id: " + summon.UnitTemplateID); return; } //UnitInfo un = (UnitInfo)info.Clone(); //un.UType = UnitInfo.UnitType.TYPE_SUMMON; string name = null; if (TemplateManager.Formula.TrySummonUnit(summoner.Launcher, summon, ref info, ref name)) { for (int i = 0; i < summon.Count; i++) { float dx, dy, dr; //float angle = (float)RandomN.NextDouble() * CMath.PI_MUL_2; //float lengt = (float)RandomN.NextDouble() * radius; //dx = x + (float)Math.Cos(angle) * lengt; //dy = y + (float)Math.Sin(angle) * lengt; //dr = (float)RandomN.NextDouble() * CMath.PI_MUL_2; //召唤单位时判断当前位置能否行走,防止被召唤单位卡在地图里.Editor by Alex. var pos = PathFinder.FindNearRandomMoveableNode(RandomN, x, y, radius, true); if (pos == null) { dx = summoner.Launcher.X; dy = summoner.Launcher.Y; } else { dx = pos.PosX; dy = pos.PosY; } dr = summoner.Direction; InstanceUnit unit = null; if (!summoner.Launcher.IsActive) { unit = AddUnit(info, name, summoner.Launcher.Force, summon.UnitLevel, dx, dy, dr, null); } else { unit = AddUnit(info, name, summoner.Launcher.Force, summon.UnitLevel, dx, dy, dr, summoner.Launcher); } if (unit != null) { if (info.LifeTimeMS > 0) { AddTimeDelayMS(info.LifeTimeMS, (task) => { unit.kill(); }); } if (summon.Effect != null) { queueEvent(new AddEffectEvent(unit.ID, dx, dy, dr, summon.Effect)); } } } } } public void unitSummonUnit(InstanceUnit summoner, SummonUnit summon) { float x = summoner.X; float y = summoner.Y; MathVector.movePolar(ref x, ref y, summoner.Direction, summoner.BodyBlockSize * 4); float radius = summoner.BodyBlockSize * 2; UnitInfo info = Templates.getUnit(summon.UnitTemplateID); if (info.UType != UnitType.TYPE_SUMMON || info.LifeTimeMS <= 0) { log.Warn("召唤非召唤类单位2:" + summoner.ID + ", 召唤id: " + summon.UnitTemplateID); return; } //UnitInfo un = (UnitInfo)info.Clone(); //un.UType = UnitInfo.UnitType.TYPE_SUMMON; string name = null; if (TemplateManager.Formula.TrySummonUnit(summoner, summon, ref info, ref name)) { for (int i = 0; i < summon.Count; i++) { float angle = (float)RandomN.NextDouble() * CMath.PI_MUL_2; float lengt = (float)RandomN.NextDouble() * radius; float dx = x + (float)Math.Cos(angle) * lengt; float dy = y + (float)Math.Sin(angle) * lengt; float dr = (float)RandomN.NextDouble() * CMath.PI_MUL_2; InstanceUnit unit = AddUnit(info, name, summoner.Force, summon.UnitLevel, dx, dy, dr, summoner); if (unit != null) { if (info.LifeTimeMS > 0) { AddTimeDelayMS(info.LifeTimeMS, (task) => { unit.kill(); }); } if (summon.Effect != null) { queueEvent(new AddEffectEvent(unit.ID, dx, dy, dr, summon.Effect)); } } } } } public InstanceUnit summonUnit(InstanceUnit summoner, UnitInfo info, string name, int level, float x, float y, float direction) { if (info.UType != UnitType.TYPE_SUMMON || info.LifeTimeMS <= 0) { log.Warn("召唤非召唤类单位3:" + summoner.ID + ", 召唤id: " + info.ID); return null; } InstanceUnit unit = AddUnit(info, name, summoner.Force, level, x, y, direction, summoner); if (unit != null) { if (info.LifeTimeMS > 0) { AddTimeDelayMS(info.LifeTimeMS, (task) => { unit.kill(); }); } } return unit; } /// /// 锁定范围内指定目标 /// /// /// /// /// /// /// /// /// public virtual InstanceUnit SeekSpellAttackable( InstanceUnit launcher, SpellTemplate spellData, ObjectAoiStatus aoiStatus, float X, float Y, float range, SkillTemplate.CastTarget expectTarget, SpellTemplate.SeekingExpect expectSeeking, SpellChainLevelInfo chain) { using (var list = ListObjectPool.AllocAutoRelease()) { getObjectsRoundRange((InstanceZoneObject o, float x, float y, float r) => { InstanceUnit u = o as InstanceUnit; if (chain != null) { switch (expectSeeking) { case SpellTemplate.SeekingExpect.RandomIgnoreInChain: case SpellTemplate.SeekingExpect.NearestIgnoreInChain: case SpellTemplate.SeekingExpect.FarthestIgnoreInChain: if (chain.ContainsTarget(u)) { return false; } break; default: if (chain.LastTarget == u) { return false; } break; } } if (IsAttackable(launcher, u, expectTarget, AttackReason.Look, spellData)) { return CMath.intersectRound(x, y, r, o.X, o.Y, o.BodyHitSize); } return false; }, X, Y, range, list, aoiStatus); switch (expectSeeking) { case SpellTemplate.SeekingExpect.Random: case SpellTemplate.SeekingExpect.RandomIgnoreInChain: CUtils.RandomList(RandomN, list); break; case SpellTemplate.SeekingExpect.Nearest: case SpellTemplate.SeekingExpect.NearestIgnoreInChain: list.Sort(new ObjectSorterNearest(X, Y)); break; case SpellTemplate.SeekingExpect.Farthest: case SpellTemplate.SeekingExpect.FarthestIgnoreInChain: list.Sort(new ObjectSorterFarthest(X, Y)); break; case SpellTemplate.SeekingExpect.TheOne: list.Sort((a, b) => { return (int)(b.Weight - a.Weight); }); break; } if (list.Count > 0) { return list[0]; } } return null; } public virtual void SeekSpellAttackable(List ret, float X, float Y, InstanceUnit Launcher, SpellTemplate spell, LaunchSpell launch, SpellChainLevelInfo chain) { getObjectsRoundRange((InstanceZoneObject o, float x, float y, float r) => { InstanceUnit u = o as InstanceUnit; if (chain != null) { switch (launch.SeekingTargetExpect) { case SpellTemplate.SeekingExpect.RandomIgnoreInChain: case SpellTemplate.SeekingExpect.NearestIgnoreInChain: case SpellTemplate.SeekingExpect.FarthestIgnoreInChain: if (chain.ContainsTarget(u)) { return false; } break; default: if (chain.LastTarget == u) { return false; } break; } } if (IsAttackable(Launcher, u, spell.ExpectTarget, AttackReason.Look, spell)) { return CMath.intersectRound(x, y, r, o.X, o.Y, o.BodyHitSize); } return false; }, X, Y, launch.SeekingTargetRange, ret, Launcher.AoiStatus); switch (launch.SeekingTargetExpect) { case SpellTemplate.SeekingExpect.Random: case SpellTemplate.SeekingExpect.RandomIgnoreInChain: CUtils.RandomList(RandomN, ret); break; case SpellTemplate.SeekingExpect.Nearest: case SpellTemplate.SeekingExpect.NearestIgnoreInChain: ret.Sort(new ObjectSorterNearest(X, Y)); break; case SpellTemplate.SeekingExpect.Farthest: case SpellTemplate.SeekingExpect.FarthestIgnoreInChain: ret.Sort(new ObjectSorterFarthest(X, Y)); break; } } /**游戏服场景标识通知*/ public virtual void GSZoneFlagNotifyMsg(int value1) { } #endregion //-------------------------------------------------------------------------------------------------------// #region EDITOR_AND_SCRIPT readonly private HashMap mFlags = new HashMap(); private bool mHasArea = false; protected bool addFlag(InstanceFlag flag) { if (!mFlags.ContainsKey(flag.Name)) { mFlags.Add(flag.Name, flag); flag.onAdded(); if (flag is ZoneArea) { mHasArea = true; } return true; } return false; } public InstanceFlag getFlag(string name) { if (string.IsNullOrEmpty(name)) { return null; } return mFlags.Get(name); } public T getFlagAs(string name) where T : InstanceFlag { return mFlags.Get(name) as T; } public bool setFlag(string name, bool enable) { InstanceFlag flag = getFlag(name); if (flag != null) { flag.Enable = enable; return true; } return false; } #endregion //-------------------------------------------------------------------------------------------------------// #region Environment private HashMap EnvironmentVarMap = new HashMap(); public void AddEnvironmentVar(string key, object value, bool syncToClient) { if (!string.IsNullOrEmpty(key)) { EnvironmentVar var = EnvironmentVarMap.Get(key); if (var != null) { if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || (var.SyncToClient && var.Value != value)) { queueEvent(new SyncEnvironmentVarEvent(key, value)); } var.Value = value; } else { var = new EnvironmentVar(key, syncToClient, value); EnvironmentVarMap.Add(key, var); if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || var.SyncToClient) { queueEvent(new SyncEnvironmentVarEvent(key, value)); } } } } public void SetEnvironmentVar(string key, object value) { if (!string.IsNullOrEmpty(key)) { EnvironmentVar var = EnvironmentVarMap.Get(key); if (var != null) { if (EnvironmentVar.ALWAYS_SYNC_ENVIRONMENT_VAR || (var.SyncToClient && var.Value != value)) { queueEvent(new SyncEnvironmentVarEvent(key, value)); } var.Value = value; } } } public T GetEnvironmentVarAs(string key) { if (!string.IsNullOrEmpty(key)) { EnvironmentVar var = EnvironmentVarMap.Get(key); if (var != null) { try { return (T)var.Value; } catch (Exception err) { log.Warn("GetEnvironmentVarAs : " + key + ", catch: " + err); } } } return default(T); } public int ListEnvironmentVars(List list) { list.AddRange(EnvironmentVarMap.Values); return EnvironmentVarMap.Count; } public ClientStruct.ZoneEnvironmentVar[] GetCurrentZoneVars() { ClientStruct.ZoneEnvironmentVar[] ret = new ClientStruct.ZoneEnvironmentVar[EnvironmentVarMap.Count]; int i = 0; foreach (EnvironmentVar var in EnvironmentVarMap.Values) { ret[i] = new ClientStruct.ZoneEnvironmentVar(); ret[i].Key = var.Key; ret[i].Value = var.Value; ret[i].SyncToClient = var.SyncToClient; i++; } return ret; } protected void BindZoneVar(ZoneVar var, BindValuesAdapter api) { if (string.IsNullOrEmpty(var.Key)) { // Error return; } if (var.Value is IEditorValue) { IEditorValue evalue = (var.Value as IEditorValue); AddEnvironmentVar(var.Key, evalue.GetEnvValue(api), var.SyncToClient); } else if (var.Value is Array) { Array array = var.Value as Array; for (int i = 0; i < array.Length; i++) { object e = array.GetValue(i); if (e is IEditorValue) { string key = string.Format(var.Key + "[{0}]", i); IEditorValue evalue = (e as IEditorValue); AddEnvironmentVar(key, evalue.GetEnvValue(api), var.SyncToClient); } } } } #endregion //-------------------------------------------------------------------------------------------------------// #region QUEST /// /// 【第三方系统通知】任务已接受 /// /// /// internal void gs_OnQuestAccepted(string playerUUID, string quest) { queueTask((InstanceZone zone) => { InstancePlayer p = getPlayerByUUID(playerUUID); if (p != null) { p.doQuestAccepted(quest); if (mOnQuestAccepted != null) mOnQuestAccepted.Invoke(p, quest); } }); } /// /// 【第三方系统通知】任务已完成 /// /// /// internal void gs_OnQuestCommitted(string playerUUID, string quest) { queueTask((InstanceZone zone) => { InstancePlayer p = getPlayerByUUID(playerUUID); if (p != null) { if (mOnQuestCommitted != null) mOnQuestCommitted.Invoke(p, quest); p.doQuestCommitted(quest); } }); } /// /// 【第三方系统通知】任务已放弃 /// /// /// internal void gs_OnQuestDropped(string playerUUID, string quest) { queueTask((InstanceZone zone) => { InstancePlayer p = getPlayerByUUID(playerUUID); if (p != null) { if (mOnQuestDropped != null) mOnQuestDropped.Invoke(p, quest); p.doQuestDropped(quest); } }); } /// /// 【第三方系统通知】任务状态已改变 /// /// /// /// /// internal void gs_OnQuestStatusChanged(string playerUUID, string quest, string key, string value) { queueTask((InstanceZone zone) => { InstancePlayer p = getPlayerByUUID(playerUUID); if (p != null) { p.doQuestStatusChanged(quest, key, value); if (mOnQuestStatusChanged != null) mOnQuestStatusChanged.Invoke(p, quest, key, value); } }); } //给单个玩家发送消息 internal void doSendMsgToPlayer(InstancePlayer player, CommonLang.Protocol.IMessage msg) { mQuestAdapter.DoSendMsgToPlayer(player.PlayerUUID, msg); } /// /// 本地通知游戏服 /// /// /// /// internal void doAcceptQuest(InstancePlayer player, string quest, string args) { mQuestAdapter.DoAcceptQuest(player.PlayerUUID, quest, args); } /// /// 本地通知游戏服 /// /// /// /// internal void doCommitQuest(InstancePlayer player, string quest, string args) { mQuestAdapter.DoCommitQuest(player.PlayerUUID, quest, args); } /// /// 本地通知游戏服 /// /// /// /// internal void doDropQuest(InstancePlayer player, string quest, string args) { mQuestAdapter.DoDropQuest(player.PlayerUUID, quest, args); } /// /// 本地通知游戏服 /// /// /// /// /// internal void doUpdateQuestStatus(InstancePlayer player, string quest, string key, string value) { mQuestAdapter.DoUpdateQuestStatus(player.PlayerUUID, quest, key, value); } #endregion //-------------------------------------------------------------------------------------------------------// } }