/// <summary> /// Notifies the objective, that an event occured. The objective checks if that event matches the event it waits for /// </summary> public void Notify(Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType type, int value) { if (type != objective.ObjectiveType) return; switch (type) { case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterWorld: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterScene: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.InteractWithActor: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.KillMonster: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.CompleteQuest: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.HadConversation: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterLevelArea: if (value == objective.SNOName1.SNOId) { Counter++; questStep.UpdateCounter(this); } break; case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterTrigger: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EventReceived: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.GameFlagSet: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.KillGroup: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.PlayerFlagSet: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.PossessItem: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.TimedEventExpired: throw new NotImplementedException(); } }
public Inventory(Mooege.Core.GS.Player.Player owner) { this._owner = owner; this.Items = new Dictionary<uint, Item>(); this._equipment = new Equipment(owner); this._inventoryStash = new Stash(owner, 6, 10); }
/// <summary> /// Notifies the objective (if it is flagged as abonus objective), that an event occured. The objective checks if that event matches the event it waits for /// </summary> public void NotifyBonus(Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType type, int value) { Logger.Debug(" (NotifyBonus) objective details SNOName 1 : {0}, ID {1} \n SNOName 2 : {2},ID {3} ", objective.SNOName1.Name, objective.SNOName1.Id, objective.SNOName2.Name, objective.SNOName2.Id); Logger.Debug(" (NotifyBonus) in QuestObjective for type {0} and value {1} and objective.ObjectiveType is {2}", type, value, objective.ObjectiveType); //if (type != objective.ObjectiveType) return; switch (type) { case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.BonusStep: { Counter++; questStep.UpdateBonusCounter(this); } break; case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterWorld: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterScene: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.InteractWithActor: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.KillMonster: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.CompleteQuest: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.HadConversation: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterLevelArea: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EnterTrigger: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.EventReceived: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.GameFlagSet: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.KillGroup: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.PlayerFlagSet: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.PossessItem: case Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.TimedEventExpired: throw new NotImplementedException(); } }
public override bool Reveal(Mooege.Core.GS.Player.Player player) { if (TargetPos != null) // targetpos!=null in this case is used to detect if the portal has been completely initialized to have a target // if it doesn't have one, it won't be displayed - otherwise the client would crash from this. { //Logger.Info("Revealing portal {0}", PortalMessage.AsText()); if (!base.Reveal(player)) return false; player.InGameClient.SendMessage(new PortalSpecifierMessage() { ActorID = this.DynamicID, Destination = this.Destination }); // Probably unnecessary since the transform is ACD data and is set in the EnterKnown. /komiga /*player.InGameClient.SendMessage(new ACDTranslateFacingMessage(Opcodes.ACDTranslateFacingMessage1) { ActorID = this.DynamicID, Angle = 0f, Field2 = false, });*/ player.InGameClient.FlushOutgoingBuffer(); return true; } return false; }
public override void Reveal(Mooege.Core.GS.Player.Player player) { if (TargetPos != null) // targetpos!=null in this case is used to detect if the portal has been completely initialized to have a target // if it doesn't have one, it won't be displayed - otherwise the client would crash from this. { //Logger.Info("Revealing portal {0}", PortalMessage.AsText()); base.Reveal(player); // FIXME: Hardcoded crap player.InGameClient.SendMessage(new AffixMessage() { ActorID = this.DynamicID, Field1 = 1, aAffixGBIDs = new int[0] }); player.InGameClient.SendMessage(new AffixMessage() { ActorID = this.DynamicID, Field1 = 2, aAffixGBIDs = new int[0] }); player.InGameClient.SendMessage(new PortalSpecifierMessage() { ActorID = this.DynamicID, Destination = this.Destination }); player.InGameClient.SendMessage(new ACDCollFlagsMessage() { ActorID = this.DynamicID, CollFlags = 0x00000001, }); this.Attributes.SendMessage(player.InGameClient, this.DynamicID); player.InGameClient.SendMessage(new ACDGroupMessage() { ActorID = this.DynamicID, Field1 = -1, Field2 = -1, }); player.InGameClient.SendMessage(new ANNDataMessage(Opcodes.ANNDataMessage7) { ActorID = this.DynamicID, }); player.InGameClient.SendMessage(new ACDTranslateFacingMessage(Opcodes.ACDTranslateFacingMessage1) { ActorID = this.DynamicID, Angle = 0f, Field2 = false, }); } player.InGameClient.FlushOutgoingBuffer(); }
public SpellRune(World world, Mooege.Common.MPQ.FileFormats.ItemTable definition) : base(world, definition) { if (!definition.Name.Contains("X")) { // attuned rune, randomize power int classRnd = RandomHelper.Next(0, 5); int PowerSNOId = -1; switch (classRnd) { case 0: PowerSNOId = Skills.Skills.Barbarian.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.Barbarian.AllActiveSkillsList.Count)); break; case 1: PowerSNOId = Skills.Skills.DemonHunter.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.DemonHunter.AllActiveSkillsList.Count)); break; case 2: PowerSNOId = Skills.Skills.Monk.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.Monk.AllActiveSkillsList.Count)); break; case 3: PowerSNOId = Skills.Skills.WitchDoctor.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.WitchDoctor.AllActiveSkillsList.Count)); break; case 4: PowerSNOId = Skills.Skills.Wizard.AllActiveSkillsList.ElementAt(RandomHelper.Next(0, Mooege.Core.GS.Skills.Skills.Wizard.AllActiveSkillsList.Count)); break; } //this.Attributes[GameAttribute.Rune_Attuned_Power] = PowerSNOId; } }
public QuestObjective(Mooege.Common.MPQ.FileFormats.QuestStepObjective objective, QuestStep questStep, int id) { //Logger.Debug(" (QuestObjective ctor) creating an objective with ID {0}, QuestStepObjective {1} and QuestStep ID {2}", id, objective.Group1Name, questStep.QuestStepID); ID = id; this.objective = objective; this.questStep = questStep; }
public Inventory(Mooege.Core.GS.Player.Player owner) { this._owner = owner; this.Items = new Dictionary<uint, Item>(); this._equipment = new uint[16]; this._backpack = new uint[6, 10]; this._goldItem = null; }
public Book(World world, Mooege.Common.MPQ.FileFormats.ItemTable definition) : base(world, definition) { var actorData = ActorSNO.Target as Mooege.Common.MPQ.FileFormats.Actor; if (actorData.TagMap.ContainsKey(ActorKeys.Lore)) { LoreSNOId = actorData.TagMap[ActorKeys.Lore].Id; } }
public Book(World world, Mooege.Common.MPQ.FileFormats.ItemTable definition) : base(world, definition) { // Items are NOT constructed with tags var actorData = ActorSNO.Target as Mooege.Common.MPQ.FileFormats.Actor; if (actorData.TagMap.ContainsKey(TagKeys.Lore)) { LoreSNOId = actorData.TagMap[TagKeys.Lore].Id; } }
public Book(World world, Mooege.Common.MPQ.FileFormats.ItemTable definition) : base(world, definition) { // Items are NOT constructed with tags var actorData = (Mooege.Common.MPQ.FileFormats.Actor)Mooege.Common.MPQ.MPQStorage.Data.Assets[SNOGroup.Actor][this.ActorSNO.SNOId].Data; var loreTagEntry = actorData.TagMap.TagMapEntries.FirstOrDefault(x => x.TagID == (int)MarkerTagTypes.LoreSNOId); if (loreTagEntry != null) { LoreSNOId = loreTagEntry.Int2; } }
public Book(World world, Mooege.Common.MPQ.FileFormats.ItemTable definition) : base(world, definition) { var y = MPQStorage.Data.Assets[SNOGroup.Actor].FirstOrDefault(x => x.Value.SNOId == this.SNOId); var e = (y.Value.Data as Mooege.Common.MPQ.FileFormats.Actor).TagMap.TagMapEntries.FirstOrDefault(z => z.TagID == (int)MarkerTagTypes.LoreSNOId); if (e != null) { LoreSNOId = e.Int2; } else { LoreSNOId = -1; } }
private List<Player> GetPlayersInRange(Mooege.Core.GS.Map.World world) { // Not as clean and fancy as quadtreee, but the cost is like alot less. // Quadtree avg's 0.134ms vs 0.004ms for this. Probably could stick to the Quadtree by just only checking every X seconds. List<Player> playerList = new List<Player>(); foreach (var p in world.Players.Values) { if (MovementHelpers.GetDistance(this.Body.Position, p.Position) < 240f) { playerList.Add(p); } } return playerList; }
public World(Mooege.Core.GS.Game.Game game, int worldSNO) : base(game.NewWorldID) { this.Game = game; this.Game.StartTracking(this); this.Scenes = new Dictionary<uint, Scene>(); //this.Scenes = new List<Scene>(); this.Actors = new Dictionary<uint, Actor>(); this.Players = new Dictionary<uint, Mooege.Core.GS.Player.Player>(); // NOTE: WorldSNO must be valid before adding it to the game this.WorldSNO = worldSNO; this.StartPosition = new Vector3D(); this.Game.AddWorld(this); }
public override bool Reveal(Mooege.Core.GS.Player.Player player) { if (!base.Reveal(player)) return false; player.InGameClient.SendMessage(new PortalSpecifierMessage() { ActorID = this.DynamicID, Destination = this.Destination }); // Show a minimap icon Mooege.Common.MPQ.Asset asset; string markerName = ""; if (Mooege.Common.MPQ.MPQStorage.Data.Assets[Common.Types.SNO.SNOGroup.LevelArea].TryGetValue(this.Destination.DestLevelAreaSNO, out asset)) markerName = System.IO.Path.GetFileNameWithoutExtension(asset.FileName); //else Logger.Warn("No asset for LevelArea {0}", this.Destination.DestLevelAreaSNO); player.InGameClient.SendMessage(new MapMarkerInfoMessage() { Id = (int)Opcodes.MapMarkerInfoMessage, Field0 = (int)World.NewSceneID, // TODO What is the correct id space for mapmarkers? Field1 = new WorldPlace() { Position = this.Position, WorldID = this._world.DynamicID }, Field2 = 0x00018FB0, /* Marker_DungeonEntrance.tex */ // TODO Dont mark all portals as dungeon entrances... some may be exits too (although d3 does not necesarrily use the correct markers). Also i have found no hacky way to determine whether a portal is entrance or exit - farmy m_snoStringList = 0x0000CB2E, /* LevelAreaNames.stl */ // TODO Dont use hardcoded numbers Field4 = StringHashHelper.HashNormal(markerName), Field5 = 0, Field6 = 0, Field7 = 0, Field8 = 0, Field9 = true, Field10 = false, Field11 = true, Field12 = 0 }); return true; }
public QuestObjective(Game game, Mooege.Common.MPQ.FileFormats.QuestStepObjective objective, QuestStep questStep, int id) { ID = id; this.objective = objective; this.questStep = questStep; this.game = game; //TODO: Rewrite all this as quests should subscribe to events and not objects notify quests if (this.objective.ObjectiveType == Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.KillGroup) { logger.Debug("KillGroup objective"); foreach( var world in game._worlds.Values) { //subscribe to each actor in group destroy/kill event var spawnerGroupActors = world.GetActorsInGroup(this.objective.Group1Name); // foreach (var actor in spawnerGroupActors) { if (actor is Spawner) { (actor as Spawner).Spawn(); } } var groupActors = world.GetActorsInGroup(this.objective.Group1Name); foreach (var actor in groupActors) { actor.ActorKilled += new EventHandler(actor_ActorKilled); } } } if (this.objective.ObjectiveType == Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType.KillMonster) { } }
public override void OnTargeted(Mooege.Core.GS.Player.Player player, TargetMessage message) { World world = this.World.Game.GetWorld(this.Destination.WorldSNO); if (world == null) { Logger.Warn("Portal's destination world does not exist (WorldSNO = {0})", this.Destination.WorldSNO); return; } Actor startingPoint = world.GetActorByTag(this.Destination.StartingPointActorTag); if (startingPoint != null) player.TransferTo(world, startingPoint.Position); else Logger.Warn("Portal's tagged starting point does not exist (Tag = {0})", this.Destination.StartingPointActorTag); }
/// <summary> /// Loads all markersets of a scene and looks for the one with the subscene position /// </summary> private static Vector3D FindSubScenePosition(Mooege.Common.MPQ.FileFormats.SceneChunk sceneChunk) { var mpqScene = MPQStorage.Data.Assets[SNOGroup.Scene][sceneChunk.SNOHandle.Id].Data as Mooege.Common.MPQ.FileFormats.Scene; foreach (var markerSet in mpqScene.MarkerSets) { var mpqMarkerSet = MPQStorage.Data.Assets[SNOGroup.MarkerSet][markerSet].Data as Mooege.Common.MPQ.FileFormats.MarkerSet; foreach (var marker in mpqMarkerSet.Markers) if (marker.Type == Mooege.Common.MPQ.FileFormats.MarkerType.SubScenePosition) return marker.PRTransform.Vector3D; } return null; }
private static void AddTile(Mooege.Common.MPQ.FileFormats.World worldData, TileInfo tileInfo, Vector3D location) { var sceneChunk = new SceneChunk(); sceneChunk.SNOHandle = new SNOHandle(tileInfo.SNOScene); sceneChunk.PRTransform = new PRTransform(); sceneChunk.PRTransform.Quaternion = new Quaternion(); sceneChunk.PRTransform.Quaternion.W = 1.0f; sceneChunk.PRTransform.Quaternion.Vector3D = new Vector3D(0, 0, 0); sceneChunk.PRTransform.Vector3D = new Vector3D(); sceneChunk.PRTransform.Vector3D = location; var spec = new SceneSpecification(); //scene.Specification = spec; spec.Cell = new Vector2D() { X = 0, Y = 0 }; spec.CellZ = 0; spec.SNOLevelAreas = new int[] { -1, -1, -1, -1 }; spec.SNOMusic = -1; spec.SNONextLevelArea = -1; spec.SNONextWorld = -1; spec.SNOPresetWorld = -1; spec.SNOPrevLevelArea = -1; spec.SNOPrevWorld = -1; spec.SNOReverb = -1; spec.SNOWeather = 50542; spec.SNOCombatMusic = -1; spec.SNOAmbient = -1; spec.ClusterID = -1; spec.Unknown1 = 14; spec.Unknown3 = 5; spec.Unknown4 = -1; spec.Unknown5 = 0; spec.SceneCachedValues = new SceneCachedValues(); spec.SceneCachedValues.Unknown1 = 63; spec.SceneCachedValues.Unknown2 = 96; spec.SceneCachedValues.Unknown3 = 96; var sceneFile = MPQStorage.Data.Assets[SNOGroup.Scene][tileInfo.SNOScene]; var sceneData = (Mooege.Common.MPQ.FileFormats.Scene)sceneFile.Data; spec.SceneCachedValues.AABB1 = sceneData.AABBBounds; spec.SceneCachedValues.AABB2 = sceneData.AABBMarketSetBounds; spec.SceneCachedValues.Unknown4 = new int[4] { 0, 0, 0, 0 }; sceneChunk.SceneSpecification = spec; worldData.SceneParams.SceneChunks.Add(sceneChunk); worldData.SceneParams.ChunkCount++; }
/// <summary> /// Processes the commands for generating world /// </summary> /// <param name="drlgParams"></param> /// <param name="worldData"></param> /// <param name="levelIndex"></param> private static void ProcessCommands(DRLGParams drlgParams, Mooege.Common.MPQ.FileFormats.World worldData, int levelIndex) { //Process commands foreach (var command in drlgParams.Commands) { //Adds information about level if (command.CommandType == (int)CommandType.Group) { // command.TagMap //{Mooege.Core.GS.Common.Types.TagMap.TagMap} // _tagMapEntries: Count = 6 // TagMapEntries: Count = 6 // TagMapSize: 0 //command.TagMap.TagMapEntries //Count = 6 // [0]: {851986 = -1} // [1]: {1015841 = 1} // [2]: {851987 = -1} // [3]: {851993 = -1} // [4]: {1015822 = 0} // [5]: {851983 = 19780} //19780 LevelArea A1_trDun_Level01 //hardcode this now until proper tagmap implementation is done foreach (var chunk in worldData.SceneParams.SceneChunks) { if (command.TagMap.ContainsKey(DRLGCommandKeys.Group.Level)) chunk.SceneSpecification.SNOLevelAreas[levelIndex] = command.TagMap[DRLGCommandKeys.Group.Level].Id; } } if (command.CommandType == (int)CommandType.AddExit) { //drlgparam.Commands[6].TagMap.TagMapEntries //[0]: {852000 = -1} Type SNO (2) //[1]: {851984 = 60713} Type SNO (2) [20:16] (snobot) [1] 60713 Worlds trDun_Cain_Intro, //[2]: {1020032 = 1} (0) //[3]: {852050 = 0} //Starting location? ID (7) //[4]: {1015841 = 1} (0) //[5]: {852051 = 172} //Destination Actor Tag (7) //[6]: {1015814 = 0} (0) //[7]: {854612 = -1} Type SNO (2) //[8]: {1015813 = 300} (0) Tiletype (exit) //[9]: {1020416 = 1} (0) //[10]: {854613 = -1} Type SNO (2) //[11]: {1015821 = -1} (0) //find all tiles of TileType //foreach (var tile in worldTiles) //{ //} } } }
private static void GenerateRandomDungeon(int worldSNO, Mooege.Common.MPQ.FileFormats.World worldData) { Dictionary<int, TileInfo> tiles = new Dictionary<int, TileInfo>(); //Each DRLGParam is a level for (int paramIndex = 0; paramIndex < worldData.DRLGParams.Count; paramIndex++) { var drlgparam = worldData.DRLGParams[paramIndex]; foreach (var tile in drlgparam.Tiles) { Logger.Debug("RandomGeneration: TileType: {0}", (TileTypes)tile.TileType); tiles.Add(tile.SNOScene, tile); } TileInfo entrance = new TileInfo(); //HACK for Defiled Crypt as there is no tile yet with type 200. Maybe changing in DB would make more sense than putting this hack in // [11]: {[161961, Mooege.Common.MPQ.MPQAsset]}Worlds\\a1trDun_Cave_Old_Ruins_Random01.wrl if (worldSNO == 161961) { entrance = tiles[131902]; tiles.Remove(131902); } else entrance = GetTileInfo(tiles, TileTypes.Entrance); Vector3D initialStartTilePosition = new Vector3D(480, 480, 0); Dictionary<Vector3D, TileInfo> worldTiles = new Dictionary<Vector3D, TileInfo>(); worldTiles.Add(initialStartTilePosition, entrance); AddAdjacentTiles(worldTiles, entrance, tiles, 0, initialStartTilePosition); AddFillers(worldTiles, tiles); foreach (var tile in worldTiles) { AddTile(worldData, tile.Value, tile.Key); } //AddFiller ProcessCommands(drlgparam, worldData, paramIndex); } //Coordinates are added after selection of tiles and map //Leave it for Defiler Crypt debugging //AddTile(world, tiles[132218], new Vector3D(720, 480, 0)); //AddTile(world, tiles[132203], new Vector3D(480, 240, 0)); //AddTile(world, tiles[132263], new Vector3D(240, 480, 0)); //return world; }
public Dye(World world, Mooege.Common.MPQ.FileFormats.ItemTable definition) : base(world, definition) { }
public override bool Reveal(Mooege.Core.GS.Player.Player player) { if (!base.Reveal(player)) return false; player.InGameClient.SendMessage(new PlayerWarpedMessage() { Field0 = 9, Field1 = 0f, }); player.InGameClient.SendMessage(new PlayerEnterKnownMessage() { PlayerIndex = this.PlayerIndex, ActorId = this.DynamicID, }); if (this == player) // only send this to player itself. Warning: don't remove this check or you'll make the game start crashing! /raist. { player.InGameClient.SendMessage(new PlayerActorSetInitialMessage() { ActorId = this.DynamicID, PlayerIndex = this.PlayerIndex, }); } player.InGameClient.FlushOutgoingBuffer(); return true; }
public void Notify(Mooege.Common.MPQ.FileFormats.QuestStepObjectiveType type, int value) { if (CurrentStep != null) CurrentStep.Notify(type, value); }
public abstract bool Unreveal(Mooege.Core.GS.Player.Player player);
// Creates an item based on supplied definition. public static Item CreateItem(Mooege.Core.GS.Actors.Actor owner, ItemTable definition) { // Logger.Trace("Creating item: {0} [sno:{1}, gbid {2}]", definition.Name, definition.SNOActor, StringHashHelper.HashItemName(definition.Name)); Type type = GetItemClass(definition); var item = (Item)Activator.CreateInstance(type, new object[] { owner.World, definition }); return item; }
// generates a random item from given type category. // we can also set a difficulty mode parameter here, but it seems current db doesnt have nightmare or hell-mode items with valid snoId's /raist. public static Item GenerateRandom(Mooege.Core.GS.Actors.Actor player, ItemTypeTable type) { var itemDefinition = GetRandom(Items.Values .Where(def => ItemGroup .HierarchyToHashList(ItemGroup.FromHash(def.ItemType1)).Contains(type.Hash)).ToList()); return CreateItem(player, itemDefinition); }
public QuestObjective(Mooege.Common.MPQ.FileFormats.QuestStepObjective objective, QuestStep questStep, int id) { ID = id; this.objective = objective; this.questStep = questStep; }
public override bool Reveal(Mooege.Core.GS.Player.Player player) { if (!base.Reveal(player)) return false; if (this == player) // only send this when player's own actor being is revealed. /raist. { player.InGameClient.SendMessage(new PlayerWarpedMessage() { Field0 = 9, Field1 = 0f, }); } player.InGameClient.SendMessage(new PlayerEnterKnownMessage() { PlayerIndex = this.PlayerIndex, ActorId = this.DynamicID, }); this.Inventory.SendVisualInventory(player); if (this == player) // only send this to player itself. Warning: don't remove this check or you'll make the game start crashing! /raist. { player.InGameClient.SendMessage(new PlayerActorSetInitialMessage() { ActorId = this.DynamicID, PlayerIndex = this.PlayerIndex, }); } return true; }
// generates a random item. public static Item GenerateRandom(Mooege.Core.GS.Actors.Actor owner) { var itemDefinition = GetRandom(Items.Values.ToList()); return CreateItem(owner, itemDefinition); }