/// <summary> /// Do the player log out work.<para /> /// If you want to force a player to logout, use Session.LogOffPlayer(). /// </summary> public void Logout(bool clientSessionTerminatedAbruptly = false) { if (!IsOnline) { return; } InWorld = false; IsOnline = false; SendFriendStatusUpdates(); // remove the player from landblock management LandblockManager.RemoveObject(this); if (!clientSessionTerminatedAbruptly) { Session.Network.EnqueueSend(new GameMessageMotion(this, Session, MotionCommand.Logout1)); SetPhysicsState(PhysicsState.ReportCollision | PhysicsState.Gravity | PhysicsState.EdgeSlide); // Thie retail server sends a ChatRoomTracker 0x0295 first, then the status message, 0x028B. It does them one at a time for each individual channel. // The ChatRoomTracker message doesn't seem to change at all. // For the purpose of ACE, we simplify this process. var general = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "General"); var trade = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "Trade"); var lfg = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "LFG"); var roleplay = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "Roleplay"); Session.Network.EnqueueSend(general, trade, lfg, roleplay); } }
/// <summary> /// Put in place to clean corpses, until more global cleanup management can be implemented /// </summary> public override void HeartBeat() { --rotTimer; if (rotTimer <= 0) { ActionChain selfDestructChain = new ActionChain(); selfDestructChain.AddAction(this, () => LandblockManager.RemoveObject(this)); selfDestructChain.EnqueueChain(); return; } QueueNextHeartBeat(); }
public void Die() { ActionChain dieChain = new ActionChain(); dieChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcastMotion(this, dead); }); dieChain.AddDelaySeconds(DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.Dead)); dieChain.AddAction(this, () => { LandblockManager.RemoveObject(this); CreateCorpse(); }); dieChain.EnqueueChain(); }
public void Kill() { ActionChain killChain = new ActionChain(); killChain.AddAction(this, () => { HandleActionMotion(MotionDeath); }); killChain.AddDelaySeconds(5); killChain.AddAction(this, () => { ApplyVisualEffects(global::ACE.Entity.Enum.PlayScript.Destroy); }); killChain.AddDelaySeconds(1); killChain.AddAction(this, () => { LandblockManager.RemoveObject(this); }); killChain.EnqueueChain(); }
public void Kill() { ActionChain killChain = new ActionChain(); killChain.AddAction(this, () => { EnqueueBroadcastMotion(new UniversalMotion(MotionStance.NonCombat, new MotionItem(MotionCommand.Dead))); }); killChain.AddDelaySeconds(5); killChain.AddAction(this, () => { ApplyVisualEffects(global::ACE.Entity.Enum.PlayScript.Destroy); }); killChain.AddDelaySeconds(1); killChain.AddAction(this, () => { LandblockManager.RemoveObject(this); }); killChain.EnqueueChain(); }
// Inventory Management Functions public virtual void AddToInventory(WorldObject inventoryItem) { lock (inventoryMutex) { if (!inventory.ContainsKey(inventoryItem.Guid)) { inventory.Add(inventoryItem.Guid, inventoryItem); } GameData.Burden += inventoryItem.GameData.Burden; inventoryItem.GameData.ContainerId = Guid.Full; if (inventoryItem.PhysicsData.Position != null) { LandblockManager.RemoveObject(inventoryItem); } inventoryItem.PhysicsData.PhysicsDescriptionFlag &= ~PhysicsDescriptionFlag.Position; inventoryItem.PositionFlag = UpdatePositionFlag.None; inventoryItem.PhysicsData.Position = null; } }
/// <summary> /// Do the player log out work.<para /> /// If you want to force a player to logout, use Session.LogOffPlayer(). /// </summary> public void Logout(bool clientSessionTerminatedAbruptly = false) { if (!IsOnline) { return; } InWorld = false; IsOnline = false; SendFriendStatusUpdates(); // remove the player from landblock management LandblockManager.RemoveObject(this); // NOTE: Adding this here for now because some chracter options do not trigger the GameActionSetCharacterOptions packet to fire when apply is clicked (which is where we are currently saving to the db). // Once we get a CharacterSave method, we might consider removing this and putting it in that method instead. DatabaseManager.Character.SaveCharacterOptions(character); DatabaseManager.Character.UpdateCharacter(character); if (!clientSessionTerminatedAbruptly) { // TODO: Evt_Movement__MovementEvent_ID = F74C SetPhysicsState(PhysicsState.ReportCollision | PhysicsState.Gravity | PhysicsState.EdgeSlide); // Thie retail server sends a ChatRoomTracker 0x0295 first, then the status message, 0x028B. It does them one at a time for each individual channel. // The ChatRoomTracker message doesn't seem to change at all. // For the purpose of ACE, we simplify this process. var general = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "General"); var trade = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "Trade"); var lfg = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "LFG"); var roleplay = new GameEventDisplayParameterizedStatusMessage(Session, StatusMessageType2.YouHaveLeftThe_Channel, "Roleplay"); Session.Network.EnqueueSend(general, trade, lfg, roleplay); } }
public void ProjectileImpact() { //Console.WriteLine($"{Name}.ProjectileImpact()"); ActionChain selfDestructChain = new ActionChain(); selfDestructChain.AddAction(this, () => { ReportCollisions = false; Ethereal = true; IgnoreCollisions = true; NoDraw = true; Cloaked = true; LightsStatus = false; PhysicsObj.set_active(false); EnqueueBroadcastPhysicsState(); EnqueueBroadcast(new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Explode, GetProjectileScriptIntensity(SpellType))); }); selfDestructChain.AddDelaySeconds(5.0); selfDestructChain.AddAction(this, () => LandblockManager.RemoveObject(this)); selfDestructChain.EnqueueChain(); }
private void HandleGameAction(QueuedGameAction action, Player player) { switch (action.ActionType) { case GameActionType.TalkDirect: { // TODO: remove this hack (using TalkDirect) ASAP var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } DeathMessageArgs d = new DeathMessageArgs(action.ActionBroadcastMessage, new ObjectGuid(action.ObjectId), new ObjectGuid(action.SecondaryObjectId)); HandleDeathMessage(obj, d); break; } case GameActionType.TeleToHouse: case GameActionType.TeleToLifestone: case GameActionType.TeleToMansion: case GameActionType.TeleToMarketPlace: case GameActionType.TeleToPkArena: case GameActionType.TeleToPklArena: { player.Teleport(action.ActionLocation); break; } case GameActionType.ApplyVisualEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var particleEffect = (PlayScript)action.SecondaryObjectId; HandleParticleEffectEvent(obj, particleEffect); break; } case GameActionType.ApplySoundEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var soundEffect = (Sound)action.SecondaryObjectId; HandleSoundEvent(obj, soundEffect); break; } case GameActionType.PutItemInContainer: { var playerId = new ObjectGuid(action.ObjectId); var inventoryId = new ObjectGuid(action.SecondaryObjectId); if (playerId.IsPlayer()) { Player aPlayer = null; WorldObject inventoryItem = null; if (worldObjects.ContainsKey(playerId) && worldObjects.ContainsKey(inventoryId)) { aPlayer = (Player)worldObjects[playerId]; inventoryItem = worldObjects[inventoryId]; } if ((aPlayer != null) && (inventoryItem != null)) { var motion = new GeneralMotion(MotionStance.Standing); motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup; aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(aPlayer), new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageSound(aPlayer.Guid, Sound.PickUpItem, (float)1.0)); // Add to the inventory list. aPlayer.AddToInventory(inventoryItem); LandblockManager.RemoveObject(inventoryItem); motion = new GeneralMotion(MotionStance.Standing); aPlayer.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(aPlayer.Session, PropertyInt.EncumbVal, aPlayer.GameData.Burden), new GameMessagePutObjectInContainer(aPlayer.Session, aPlayer, inventoryId), new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageUpdateInstanceId(inventoryId, playerId), new GameMessagePickupEvent(aPlayer.Session, inventoryItem)); aPlayer.TrackObject(inventoryItem); } } break; } case GameActionType.DropItem: { var g = new ObjectGuid(action.ObjectId); // ReSharper disable once InconsistentlySynchronizedField if (worldObjects.ContainsKey(g)) { var playerId = new ObjectGuid(action.ObjectId); var inventoryId = new ObjectGuid(action.SecondaryObjectId); if (playerId.IsPlayer()) { Player aPlayer = null; WorldObject inventoryItem = null; if (worldObjects.ContainsKey(playerId)) { aPlayer = (Player)worldObjects[playerId]; inventoryItem = aPlayer.GetInventoryItem(inventoryId); aPlayer.RemoveFromInventory(inventoryId); } if ((aPlayer != null) && (inventoryItem != null)) { var targetContainer = new ObjectGuid(0); aPlayer.Session.Network.EnqueueSend( new GameMessagePrivateUpdatePropertyInt( aPlayer.Session, PropertyInt.EncumbVal, (uint)aPlayer.Session.Player.GameData.Burden)); var motion = new GeneralMotion(MotionStance.Standing); motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup; aPlayer.Session.Network.EnqueueSend( new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageUpdateInstanceId(inventoryId, targetContainer)); motion = new GeneralMotion(MotionStance.Standing); aPlayer.Session.Network.EnqueueSend( new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessagePutObjectIn3d(aPlayer.Session, aPlayer, inventoryId), new GameMessageSound(aPlayer.Guid, Sound.DropItem, (float)1.0), new GameMessageUpdateInstanceId(inventoryId, targetContainer)); // This is the sequence magic - adds back into 3d space seem to be treated like teleport. inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectTeleport); inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectVector); LandblockManager.AddObject(inventoryItem); aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(inventoryItem)); } } } break; } case GameActionType.MovementEvent: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var motion = action.Motion; HandleMovementEvent(obj, motion); break; } case GameActionType.ObjectCreate: { this.AddWorldObject(action.WorldObject); break; } case GameActionType.ObjectDelete: { this.RemoveWorldObject(action.WorldObject.Guid, false); break; } case GameActionType.QueryHealth: { if (action.ObjectId == 0) { // Deselect the formerly selected Target player.SelectedTarget = 0; break; } object target = null; var targetId = new ObjectGuid(action.ObjectId); // Remember the selected Target player.SelectedTarget = action.ObjectId; // TODO: once items are implemented check if there are items that can trigger // the QueryHealth event. So far I believe it only gets triggered for players and creatures if (targetId.IsPlayer() || targetId.IsCreature()) { if (this.worldObjects.ContainsKey(targetId)) { target = this.worldObjects[targetId]; } if (target == null) { // check adjacent landblocks for the targetId foreach (var block in adjacencies) { if (block.Value != null) { if (block.Value.worldObjects.ContainsKey(targetId)) { target = block.Value.worldObjects[targetId]; } } } } if (target != null) { float healthPercentage = 0; if (targetId.IsPlayer()) { Player tmpTarget = (Player)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } if (targetId.IsCreature()) { Creature tmpTarget = (Creature)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } var updateHealth = new GameEventUpdateHealth(player.Session, targetId.Full, healthPercentage); player.Session.Network.EnqueueSend(updateHealth); } } break; } case GameActionType.Use: { var g = new ObjectGuid(action.ObjectId); if (worldObjects.ContainsKey(g)) { WorldObject obj = worldObjects[g]; switch (obj.Type) { case Enum.ObjectType.Portal: { // validate within use range :: set to a fixed value as static Portals are normally OnCollide usage float rangeCheck = 5.0f; if (player.Location.SquaredDistanceTo(obj.Location) < rangeCheck) { PortalDestination portalDestination = DatabaseManager.World.GetPortalDestination(obj.WeenieClassid); if (portalDestination != null) { player.Session.Player.Teleport(portalDestination.Position); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } else { string serverMessage = "Portal destination for portal ID " + obj.WeenieClassid + " not yet implemented!"; var usePortalMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.System); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(usePortalMessage, sendUseDoneEvent); } } else { // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(sendUseDoneEvent); } break; } case Enum.ObjectType.LifeStone: { string serverMessage = null; // validate within use range float radiusSquared = obj.GameData.UseRadius * obj.GameData.UseRadius; var motionSanctuary = new GeneralMotion(MotionStance.Standing, new MotionItem(MotionCommand.Sanctuary)); var animationEvent = new GameMessageUpdateMotion(player, player.Session, motionSanctuary); // This event was present for a pcap in the training dungeon.. Why? The sound comes with animationEvent... var soundEvent = new GameMessageSound(obj.Guid, Sound.LifestoneOn, 1); if (player.Location.SquaredDistanceTo(obj.Location) >= radiusSquared) { serverMessage = "You wandered too far to attune with the Lifestone!"; } else { player.SetCharacterPosition(PositionType.Sanctuary, player.Location); // create the outbound server message serverMessage = "You have attuned your spirit to this Lifestone. You will resurrect here after you die."; player.EnqueueMovementEvent(motionSanctuary, player.Guid); player.Session.Network.EnqueueSend(soundEvent); } var lifestoneBindMessage = new GameMessageSystemChat(serverMessage, ChatMessageType.Magic); // always send useDone event var sendUseDoneEvent = new GameEventUseDone(player.Session); player.Session.Network.EnqueueSend(lifestoneBindMessage, sendUseDoneEvent); break; } } } break; } } }
private void HandleGameAction(QueuedGameAction action, Player player) { switch (action.ActionType) { case GameActionType.TalkDirect: { // TODO: remove this hack (using TalkDirect) ASAP var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } DeathMessageArgs d = new DeathMessageArgs(action.ActionBroadcastMessage, new ObjectGuid(action.ObjectId), new ObjectGuid(action.SecondaryObjectId)); HandleDeathMessage(obj, d); break; } case GameActionType.TeleToHouse: case GameActionType.TeleToLifestone: case GameActionType.TeleToMansion: case GameActionType.TeleToMarketPlace: case GameActionType.TeleToPkArena: case GameActionType.TeleToPklArena: { player.Teleport(action.ActionLocation); break; } case GameActionType.ApplyVisualEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var particleEffect = (PlayScript)action.SecondaryObjectId; HandleParticleEffectEvent(obj, particleEffect); break; } case GameActionType.ApplySoundEffect: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var soundEffect = (Sound)action.SecondaryObjectId; HandleSoundEvent(obj, soundEffect); break; } case GameActionType.IdentifyObject: { // TODO: Throttle this request. The live servers did this, likely for a very good reason, so we should, too. var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var identifyResponse = new GameEventIdentifyObjectResponse(player.Session, action.ObjectId, obj); player.Session.Network.EnqueueSend(identifyResponse); break; } case GameActionType.PutItemInContainer: { var playerId = new ObjectGuid(action.ObjectId); var inventoryId = new ObjectGuid(action.SecondaryObjectId); if (playerId.IsPlayer()) { Player aPlayer = null; WorldObject inventoryItem = null; if (worldObjects.ContainsKey(playerId) && worldObjects.ContainsKey(inventoryId)) { aPlayer = (Player)worldObjects[playerId]; inventoryItem = worldObjects[inventoryId]; } if ((aPlayer != null) && (inventoryItem != null)) { if (aPlayer.PhysicsData.Position.SquaredDistanceTo(inventoryItem.PhysicsData.Position) > Math.Pow(inventoryItem.GameData.UseRadius, 2)) { // This is where I need to hook in the move to object code. // TODO: Og II work on this soon. } var motion = new UniversalMotion(MotionStance.Standing); motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup; aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(aPlayer), new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageSound(aPlayer.Guid, Sound.PickUpItem, (float)1.0)); // Add to the inventory list. aPlayer.AddToInventory(inventoryItem); LandblockManager.RemoveObject(inventoryItem); motion = new UniversalMotion(MotionStance.Standing); aPlayer.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(aPlayer.Session, PropertyInt.EncumbVal, aPlayer.GameData.Burden), new GameMessagePutObjectInContainer(aPlayer.Session, aPlayer, inventoryId), new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageUpdateInstanceId(inventoryId, playerId), new GameMessagePickupEvent(aPlayer.Session, inventoryItem)); aPlayer.TrackObject(inventoryItem); // This may not be needed when we fix landblock update object - // TODO: Og II - check this later to see if it is still required. aPlayer.Session.Network.EnqueueSend(new GameMessageUpdateObject(inventoryItem)); } } break; } case GameActionType.DropItem: { var g = new ObjectGuid(action.ObjectId); // ReSharper disable once InconsistentlySynchronizedField if (worldObjects.ContainsKey(g)) { var playerId = new ObjectGuid(action.ObjectId); var inventoryId = new ObjectGuid(action.SecondaryObjectId); if (playerId.IsPlayer()) { Player aPlayer = null; WorldObject inventoryItem = null; if (worldObjects.ContainsKey(playerId)) { aPlayer = (Player)worldObjects[playerId]; inventoryItem = aPlayer.GetInventoryItem(inventoryId); aPlayer.RemoveFromInventory(inventoryId); } if ((aPlayer != null) && (inventoryItem != null)) { var targetContainer = new ObjectGuid(0); aPlayer.Session.Network.EnqueueSend( new GameMessagePrivateUpdatePropertyInt( aPlayer.Session, PropertyInt.EncumbVal, (uint)aPlayer.Session.Player.GameData.Burden)); var motion = new UniversalMotion(MotionStance.Standing); motion.MovementData.ForwardCommand = (ushort)MotionCommand.Pickup; aPlayer.Session.Network.EnqueueSend( new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessageUpdateInstanceId(inventoryId, targetContainer)); motion = new UniversalMotion(MotionStance.Standing); aPlayer.Session.Network.EnqueueSend( new GameMessageUpdateMotion(aPlayer, aPlayer.Session, motion), new GameMessagePutObjectIn3d(aPlayer.Session, aPlayer, inventoryId), new GameMessageSound(aPlayer.Guid, Sound.DropItem, (float)1.0), new GameMessageUpdateInstanceId(inventoryId, targetContainer)); // This is the sequence magic - adds back into 3d space seem to be treated like teleport. inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectTeleport); inventoryItem.Sequences.GetNextSequence(SequenceType.ObjectVector); LandblockManager.AddObject(inventoryItem); // This may not be needed when we fix landblock update object - // TODO: Og II - check this later to see if it is still required. aPlayer.Session.Network.EnqueueSend(new GameMessageUpdateObject(inventoryItem)); aPlayer.Session.Network.EnqueueSend(new GameMessageUpdatePosition(inventoryItem)); } } } break; } case GameActionType.MovementEvent: { var g = new ObjectGuid(action.ObjectId); WorldObject obj = (WorldObject)player; if (worldObjects.ContainsKey(g)) { obj = worldObjects[g]; } var motion = action.Motion; HandleMovementEvent(obj, motion); break; } case GameActionType.ObjectCreate: { this.AddWorldObject(action.WorldObject); break; } case GameActionType.ObjectDelete: { this.RemoveWorldObject(action.WorldObject.Guid, false); break; } case GameActionType.QueryHealth: { if (action.ObjectId == 0) { // Deselect the formerly selected Target player.SelectedTarget = 0; break; } object target = null; var targetId = new ObjectGuid(action.ObjectId); // Remember the selected Target player.SelectedTarget = action.ObjectId; // TODO: once items are implemented check if there are items that can trigger // the QueryHealth event. So far I believe it only gets triggered for players and creatures if (targetId.IsPlayer() || targetId.IsCreature()) { if (this.worldObjects.ContainsKey(targetId)) { target = this.worldObjects[targetId]; } if (target == null) { // check adjacent landblocks for the targetId foreach (var block in adjacencies) { if (block.Value != null) { if (block.Value.worldObjects.ContainsKey(targetId)) { target = block.Value.worldObjects[targetId]; } } } } if (target != null) { float healthPercentage = 0; if (targetId.IsPlayer()) { Player tmpTarget = (Player)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } if (targetId.IsCreature()) { Creature tmpTarget = (Creature)target; healthPercentage = (float)tmpTarget.Health.Current / (float)tmpTarget.Health.MaxValue; } var updateHealth = new GameEventUpdateHealth(player.Session, targetId.Full, healthPercentage); player.Session.Network.EnqueueSend(updateHealth); } } break; } case GameActionType.Use: { var g = new ObjectGuid(action.ObjectId); if (worldObjects.ContainsKey(g)) { WorldObject obj = worldObjects[g]; if ((obj.DescriptionFlags & ObjectDescriptionFlag.LifeStone) != 0) { (obj as Lifestone).OnUse(player); } else if ((obj.DescriptionFlags & ObjectDescriptionFlag.Portal) != 0) { // TODO: When Physics collisions are implemented, this logic should be switched there, as normal portals are not onUse. (obj as Portal).OnCollide(player); } else if ((obj.DescriptionFlags & ObjectDescriptionFlag.Door) != 0) { (obj as Door).OnUse(player); } // switch (obj.Type) // { // case Enum.ObjectType.Portal: // { // // TODO: When Physics collisions are implemented, this logic should be switched there, as normal portals are not onUse. // // (obj as Portal).OnCollide(player); // // break; // } // case Enum.ObjectType.LifeStone: // { // (obj as Lifestone).OnUse(player); // break; // } // } } break; } } }