public CharacterSheetComponent(int x, int y, Actor actor) { this.locationX = x; this.locationY = y; this.actor = actor; PerformDrag(0, 0); }
/// <summary> /// Creates a new HealthDisplayComponent for a particular actor at a particular position /// </summary> /// <param name="locationX"></param> /// <param name="locationY"></param> /// <param name="actor"></param> public HealthDisplayComponent(int locationX, int locationY, Actor actor) { this.locationX = locationX; this.locationY = locationY; this.actor = actor; this.visible = true; PerformDrag(0, 0); }
public ThrowItemComponent(int x, int y, MapCoordinate targetCoordinate, Actor actor) { this.locationX = x; this.locationY = y; this.targetCoordinate = targetCoordinate; this.Actor = actor; PerformDrag(0, 0); }
//Drawing stuff public InventoryDisplayComponent(int locationX, int locationY, Actor currentActor) { this.locationX = locationX; this.locationY = locationY; this.CurrentActor = currentActor; ChosenCategory = 0; PerformDrag(locationX, locationY); }
public static ActionFeedback[] HuntDownMission(HuntDownMission mission, Actor actor) { if (actor.UsesRanged) { //Are we within range? if (actor.MapCharacter.Coordinate - mission.Target.MapCharacter.Coordinate <= actor.LineOfSight) { //Attack! actor.CurrentMission = new AttackMission(mission.Target); return new ActionFeedback[] { }; } } else { //Are we near the mission point? if (Math.Abs(actor.MapCharacter.Coordinate - mission.Target.MapCharacter.Coordinate) <= 1) { //MIssion is done. Attack! actor.CurrentMission = new AttackMission(mission.Target); } } if (Math.Abs(mission.Target.MapCharacter.Coordinate - actor.MapCharacter.Coordinate) > actor.LineOfSight) { //Ran away - cancel the mission actor.CurrentMission = null; } //Otherwise, check whether the target is still where he was before, and whether we have anywhere to go to if (mission.Coordinates == null || mission.Target.MapCharacter.Coordinate != mission.TargetCoordinate) { //Regenerate the path mission.TargetCoordinate = mission.Target.MapCharacter.Coordinate; mission.Coordinates = PathfinderInterface.GetPath(actor.MapCharacter.Coordinate, mission.TargetCoordinate); } //Okay, now...advance if (mission.Coordinates == null || mission.Coordinates.Count == 0) { //Where did he go? Take a turn to figure it out mission.Coordinates = null; return new ActionFeedback[] { }; } MapCoordinate nextStep = mission.Coordinates.Pop(); //Do it if (GameState.LocalMap.GetBlockAtCoordinate(nextStep).MayContainItems) { GameState.LocalMap.GetBlockAtCoordinate(nextStep).PutItemOnBlock(actor.MapCharacter); actor.MapCharacter.Coordinate = nextStep; } //And that's done return new ActionFeedback[] { }; }
//Drawing stuff public TradeDisplayComponent(int locationX, int locationY, Actor currentActor, Actor vendorActor) { this.locationX = locationX; this.locationY = locationY; this.VendorActor = vendorActor; this.PlayerActor = currentActor; ChosenCategory = 0; PerformDrag(locationX, locationY); Buy = true; }
public static ActionFeedback[] AttackMission(AttackMission mission, Actor actor) { List<ActionFeedback> feedback = new List<ActionFeedback>(); if (Math.Abs(mission.AttackTarget.MapCharacter.Coordinate - actor.MapCharacter.Coordinate) > actor.LineOfSight) { //Ran away - cancel the mission actor.CurrentMission = null; } else { if (actor.UsesRanged) { //Attack! return CombatManager.Attack(actor, mission.AttackTarget, CombatManager.GetRandomAttackLocation(actor, mission.AttackTarget).Value); } else { if (Math.Abs(mission.AttackTarget.MapCharacter.Coordinate - actor.MapCharacter.Coordinate) > 1) { //Hunt him down! actor.CurrentMission = new HuntDownMission() { Target = mission.AttackTarget, TargetCoordinate = mission.AttackTarget.MapCharacter.Coordinate }; } else { if (actor.IsAggressive && CombatManager.GetRandomAttackLocation(actor, mission.AttackTarget).HasValue) //we can hit them { //Attack! return CombatManager.Attack(actor, mission.AttackTarget, CombatManager.GetRandomAttackLocation(actor, mission.AttackTarget).Value); } else { //TODO: Run away! } } } } return new ActionFeedback[] { }; }
/// <summary> /// Performs the effect on this actor. This includes assigning it to the effects list of the GameState /// </summary> /// <param name="actor"></param> /// <param name="effect"></param> public static void PerformEffect(Actor actor,Effect effect) { switch(effect.Name) { case EffectName.AGIL: actor.Attributes.TempAgil = actor.Attributes.TempAgil ?? 0 + effect.EffectAmount; break; case EffectName.BRAWN: actor.Attributes.TempBrawn = actor.Attributes.TempBrawn ?? 0 + effect.EffectAmount; break; case EffectName.CHAR: actor.Attributes.TempChar = actor.Attributes.TempChar ?? 0 + effect.EffectAmount; break; case EffectName.INTEL: actor.Attributes.TempIntel = actor.Attributes.TempIntel ?? 0 + effect.EffectAmount; break; case EffectName.PERC: actor.Attributes.TempPerc = actor.Attributes.TempPerc ?? 0 + effect.EffectAmount; break; case EffectName.BLIND: actor.Attributes.TempPerc = (actor.Attributes.TempPerc ?? 0) - effect.EffectAmount; break; case EffectName.HEAL: HealthCheckManager.HealCharacter(actor, 5); break; } effect.Actor = actor; GameState.LocalMap.ActiveEffects.Add(effect); }
/// <summary> /// Kills the current character /// </summary> /// <param name="actor"></param> public static void KillCharacter(Actor actor) { actor.IsAlive = false; //Drop any stuff they have foreach (InventoryItem item in actor.Inventory.Inventory.GetAllObjects()) { //Always drop loot & supplies, 10% chance of dropping something else if (item.Category == InventoryCategory.LOOT || item.Category == InventoryCategory.SUPPLY || GameState.Random.Next(10) == 5) { //Drop them item.InInventory = false; item.IsEquipped = false; GameState.LocalMap.GetBlockAtCoordinate(actor.MapCharacter.Coordinate).PutItemUnderneathOnBlock(item); } } }
/// <summary> /// Calculates the chance of hitting a particular body part. /// </summary> /// <param name="attacker"></param> /// <param name="defender"></param> /// <param name="location"></param> /// <returns></returns> public static int CalculateHitPercentage(Actor attacker, Actor defender, AttackLocation location) { //If the bodypart is destroyed, give a percentage of -1 so we can filter it out later switch (location) { case AttackLocation.CHEST: if (defender.Anatomy.Chest <= -5) { return -1; } break; case AttackLocation.HEAD: if (defender.Anatomy.Head <= -5) { return -1; } break; case AttackLocation.LEFT_ARM: if (defender.Anatomy.LeftArm <= -5) { return -1; } break; case AttackLocation.LEGS: if (defender.Anatomy.Legs <= -5) { return -1; } break; case AttackLocation.RIGHT_ARM: if (defender.Anatomy.RightArm <= -5) { return -1; } break; } int atk = 0; int def = 0; GetStanceEffect(out atk, out def, attacker.CombatStance); //Chance to hit - // Attacker Skill + Brawn - 5 + location penalty + stance effect VS Defender Skill + Agil + stance effect int hitChance = attacker.Attributes.HandToHand + attacker.TotalBrawn - 5 + penaltyDict[location] + atk; GetStanceEffect(out atk, out def, defender.CombatStance); //Does the defender have a shield? int shieldBonus = 0; if (defender.Inventory.EquippedItems.ContainsKey(EquipmentLocation.SHIELD) && defender.Anatomy.LeftArm != 0) { shieldBonus = defender.Inventory.EquippedItems[EquipmentLocation.SHIELD].ArmourRating; } //Don't forget the shield if it's there int defendChance = defender.Attributes.Dodge + def + shieldBonus; //See what the difference is int difference = 20 + (hitChance - defendChance); return difference > 20 ? 100 : difference < 0 ? 0 : difference / 2 * 10; }
/// <summary> /// Gets a random attack location to attack. Will prefer attack locations which have more chance of hitting, and will never return any locations which have a 0 chance to hit. /// If this is not possible, return null /// </summary> /// <param name="attacker"></param> /// <param name="defender"></param> /// <returns></returns> public static AttackLocation? GetRandomAttackLocation(Actor attacker, Actor defender) { List<AttackLocation> attackLocations = new List<AttackLocation>(); foreach (AttackLocation loc in Enum.GetValues(typeof(AttackLocation)).Cast<AttackLocation>()) { //Avoid hitting parts which have been disabled already switch (loc) { case AttackLocation.CHEST: if (defender.Anatomy.Chest < 0) { continue; //No use } break; case AttackLocation.HEAD: if (defender.Anatomy.Head < 0) { continue; } break; case AttackLocation.LEFT_ARM: if (defender.Anatomy.LeftArm < 0) { continue; } break; case AttackLocation.LEGS: if (defender.Anatomy.Legs < 0) { continue; } break; case AttackLocation.RIGHT_ARM: if (defender.Anatomy.RightArm < 0) { continue; } break; } for (int i = 0; i < CalculateHitPercentage(attacker, defender, loc); i++) { attackLocations.Add(loc); } } //Now pick one at random return attackLocations.OrderBy(o => random.Next(100)).FirstOrDefault(); }
/// <summary> /// Generates a map with a particular biome /// </summary> /// <param name="herdAmount">The total amount of herds to generate</param> /// <param name="BanditAmount">The total amount of bandits to generate.</param> /// <param name="actors"></param> /// <returns></returns> public static MapBlock[,] GenerateMap(GlobalBiome biome, int herdAmount, int banditAmount, out Actor[] actors, out MapCoordinate startPoint) { MapBlock[,] map = new MapBlock[MAP_EDGE, MAP_EDGE]; Random random = new Random(); ItemFactory.ItemFactory factory = new ItemFactory.ItemFactory(); int tileID = 0; factory.CreateItem(Archetype.TILES, details[biome].BaseTileTag, out tileID); //Create a new map which is edge X edge in dimensions and made of the base tile for (int x = 0; x < MAP_EDGE; x++) { for (int y = 0; y < MAP_EDGE; y++) { MapBlock block = new MapBlock(); map[x, y] = block; block.Tile = factory.CreateItem("tile", tileID); block.Tile.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); } } #region Leave Town Item //Now select all the border tiles and put in a "Exit here" border for (int x = 0; x < map.GetLength(0); x++) { MapCoordinate coo = new MapCoordinate(x, 0, 0, MapType.LOCAL); LeaveTownItem lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[x, 0].ForcePutItemOnBlock(lti); coo = new MapCoordinate(x, map.GetLength(1) - 1, 0, MapType.LOCAL); lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[x, map.GetLength(1) - 1].ForcePutItemOnBlock(lti); } for (int y = 0; y < map.GetLength(1); y++) { MapCoordinate coo = new MapCoordinate(0, y, 0, MapType.LOCAL); LeaveTownItem lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[0, y].ForcePutItemOnBlock(lti); coo = new MapCoordinate(map.GetLength(0) - 1, y, 0, MapType.LOCAL); lti = new LeaveTownItem(); lti.Coordinate = coo; lti.Description = "continue on your journey"; lti.Name = "Leave Area"; lti.Coordinate = coo; map[map.GetLength(0) - 1, y].ForcePutItemOnBlock(lti); } #endregion #region Desert Oasis if (biome == GlobalBiome.ARID_DESERT) { //Let's create a pool of water towards one of the corners. int randomNumber = random.Next(2); int rXCoord = 0; if (randomNumber == 1) { //Lower rXCoord = random.Next(0, MAP_EDGE / 3); } else { rXCoord = random.Next(2 * MAP_EDGE / 3, MAP_EDGE); } randomNumber = random.Next(2); int rYCoord = 0; if (randomNumber == 1) { //Lower rYCoord = random.Next(0, MAP_EDGE / 3); } else { rYCoord = random.Next(2 * MAP_EDGE / 3, MAP_EDGE); } //The pool will have a radius of 3 MapCoordinate coo = new MapCoordinate(rXCoord, rYCoord, 0, MapType.LOCAL); //Go through the blocks with a radius of 3 var oasisBlocks = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - coo) <= 3).ToArray(); int waterTile = -1; factory.CreateItem(Archetype.TILES, "water", out waterTile); foreach (var block in oasisBlocks) { var coord = block.Tile.Coordinate; block.Tile = factory.CreateItem("tile", waterTile); block.Tile.Coordinate = coord; } var aroundOasis = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - coo) <= 4 && Math.Abs(b.Tile.Coordinate - coo) > 3).ToArray(); int dummy; int grassTile = 0; factory.CreateItem(Archetype.TILES, "grass", out grassTile); foreach (var block in aroundOasis) { var coord = block.Tile.Coordinate; block.Tile = factory.CreateItem("tile", grassTile); block.Tile.Coordinate = coord; } //Put in some trees around the pool for (int i = 0; i < 3; i++) { MapItem tree = factory.CreateItem(Archetype.MUNDANEITEMS, "jungle tree", out dummy); var block = aroundOasis[random.Next(aroundOasis.Length)]; if (block.MayContainItems) { //Drop it block.ForcePutItemOnBlock(tree); } } } #endregion #region Wetland Splotches if (biome == GlobalBiome.WETLAND) { int waterTile = -1; factory.CreateItem(Archetype.TILES, "water", out waterTile); for (int i=0; i < 7; i++) { MapBlock rBlock = map[random.Next(map.GetLength(0)), random.Next(map.GetLength(1))]; Rectangle safeRect = new Rectangle( MAP_EDGE/2 - 5,MAP_EDGE/2 -5,10,10); if (safeRect.Contains(rBlock.Tile.Coordinate.X,rBlock.Tile.Coordinate.Y)) { continue; //Not here! } int size = random.Next(1, 3); //Get all the tiles around the block for a particular size var pool = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - rBlock.Tile.Coordinate) <= size).ToArray(); foreach(var block in pool) { MapCoordinate coo = block.Tile.Coordinate; block.Tile = factory.CreateItem("tiles", waterTile); block.Tile.Coordinate = coo; } } } #endregion for (int i = 0; i < details[biome].TreeCount; i++) { int treeID = 0; MapItem item = null; item = factory.CreateItem(Archetype.MUNDANEITEMS, details[biome].TreeTag, out treeID); //try 50 times to put it somewhere int tries = 0; while (tries < 50) { MapBlock randomBlock = map[random.Next(map.GetLength(0)), random.Next(map.GetLength(1))]; if (randomBlock.MayContainItems) { randomBlock.ForcePutItemOnBlock(item); break; } tries++; } } List<Actor> actorList = new List<Actor>(); //There, now that's done, lets generate some animals if (herdAmount + banditAmount > 0) { var herds = ActorGeneration.CreateAnimalHerds(biome, false, null, herdAmount); var bandits = CampGenerator.CreateBandits(banditAmount); var actorGroups = herds.Union(bandits); //Each actor group will be placed in a random 3 radius circle foreach (var actorGroup in actorGroups) { MapBlock randomBlock = map[random.Next(map.GetLength(0)), random.Next(map.GetLength(1))]; Rectangle wanderRect = new Rectangle(randomBlock.Tile.Coordinate.X - 2, randomBlock.Tile.Coordinate.Y - 2, 4, 4); //Put the actor groups somewhere around that block var blocks = map.Cast<MapBlock>().ToArray().Where(b => Math.Abs(b.Tile.Coordinate - randomBlock.Tile.Coordinate) < 4).ToArray(); //Pick a number of random blocks foreach (var newActor in actorGroup) { int tries = 0; while (tries < 50) { var actorBlock = blocks[random.Next(blocks.Length)]; if (actorBlock.MayContainItems) { //Put it there newActor.MapCharacter.Coordinate = actorBlock.Tile.Coordinate; actorBlock.ForcePutItemOnBlock(newActor.MapCharacter); //Make them wander newActor.MissionStack.Push(new WanderMission() { LoiterPercentage = 50, WanderPoint = actorBlock.Tile.Coordinate, WanderRectangle = wanderRect }); actorList.Add(newActor); break; } tries++; } } } } //Drop the player in the thick of it MapBlock center = map[map.GetLength(0) / 2, map.GetLength(1) / 2]; center.RemoveTopItem(); //Remove it if someone else wanted it startPoint = center.Tile.Coordinate; actors = actorList.ToArray(); return map; }
/// <summary> /// Performs an attack on a character. Will also give an amount of feedback. Also reduces the health of the defender if the attack hits /// </summary> /// <param name="attacker"></param> /// <param name="defender"></param> /// <param name="location"></param> /// <param name="special">Special attack modifications to make. THIS DOES NOT CONSIDER TARGETS OR ATTACKS </param> /// <returns></returns> public static ActionFeedback[] Attack(Actor attacker, Actor defender, AttackLocation location, SpecialAttack special = null) { List<ActionFeedback> feedback = new List<ActionFeedback>(); if (defender.MapCharacter == null) { return new ActionFeedback[] { }; //What on earth are you doing? } int distance = attacker.MapCharacter.Coordinate - defender.MapCharacter.Coordinate; //This will later be used for ranged attacks if (distance >= 2) { //If the defender is not the character, get thhe defender to walk to the attacked location if (!defender.IsPlayerCharacter) { defender.MissionStack.Push(defender.CurrentMission); //It's not retainable, so if it's preempted by another mission he won't walk back to the attacked location for nothing defender.CurrentMission = new WalkToMission() { isRetainable = false, TargetCoordinate = attacker.MapCharacter.Coordinate }; } } //Do we succeed in the attack? int atk = 0; int def = 0; //The type of dice we roll 3 of to determine weapon damage int weaponDiceRolls = attacker.UnarmedDamageDice; //Default damage int weaponWoundPotential = 0; int weaponStunAmount = 0; if (distance < 2) { if (attacker.Inventory.EquippedItems.ContainsKey(EquipmentLocation.WEAPON) && attacker.Anatomy.RightArm > 0) { //Do we have a weapon and an arm to use it? weaponDiceRolls = attacker.Inventory.EquippedItems[EquipmentLocation.WEAPON].DamageDice; weaponWoundPotential = attacker.Inventory.EquippedItems[EquipmentLocation.WEAPON].WoundPotential; weaponStunAmount = attacker.Inventory.EquippedItems[EquipmentLocation.WEAPON].StunAmount; } } else { //Grab the weapon from the BOW slot if (attacker.Inventory.EquippedItems.ContainsKey(EquipmentLocation.BOW) && attacker.Anatomy.RightArm > 0 && attacker.Anatomy.LeftArm > 0) { weaponDiceRolls = attacker.Inventory.EquippedItems[EquipmentLocation.BOW].DamageDice; weaponWoundPotential = attacker.Inventory.EquippedItems[EquipmentLocation.BOW].WoundPotential; weaponStunAmount = attacker.Inventory.EquippedItems[EquipmentLocation.BOW].StunAmount; } else { //Firing with a destroyed upper body? For shame feedback.Add(new LogFeedback(null, Color.Red, "You are unable to use a ranged weapon under these conditions")); } } DamageType damageType = DamageType.SLASH; if (distance >= 2) { //Ranged damageType = DamageType.PIERCE; } GetStanceEffect(out atk, out def, attacker.CombatStance); //Does the defender have a shield? int shieldBonus = 0; if (defender.Inventory.EquippedItems.ContainsKey(EquipmentLocation.SHIELD) && defender.Anatomy.LeftArm != 0) { shieldBonus = defender.Inventory.EquippedItems[EquipmentLocation.SHIELD].ArmourRating; } int hitChance = 0; if (distance < 2) { //Chance to hit for hand to hand- // Attacker Skill + Brawn - 5 + location penalty + stance effect VS Defender Skill + Agil + stance effect + shield bonus hitChance = attacker.Attributes.HandToHand + attacker.TotalBrawn - 5 + penaltyDict[location] + atk; } else { //Chance to hit for ranged- // Attacker Skill + perc - 5 + location penalty + stance effect - distance*2 VS Defender Skill + Agil + stance effect + shield bonus hitChance = attacker.Attributes.Ranged + attacker.TotalPerc - 5 - distance * 2 + penaltyDict[location] + atk; } if (special != null) { hitChance += special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.ACCURACY) == null ? 0 : special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.ACCURACY).EffectValue; } GetStanceEffect(out atk, out def, defender.CombatStance); int defendChance = defender.Attributes.Dodge + def + shieldBonus; //See what the difference is int difference = hitChance - defendChance; //Now roll a d20 and see whether we hit int diceRoll = random.Next(20) + 1; if (difference + diceRoll > 0) { //We have a hit //The defender will learn something defender.Attributes.IncreaseSkill(SkillName.DODGER); //Are they wearing armour ? if (defender.Inventory.EquippedItems.ContainsKey(EquipmentLocation.BODY)) { defender.Attributes.IncreaseSkill(SkillName.ARMOUR_USER); } //Are they using a shield? if (defender.Inventory.EquippedItems.ContainsKey(EquipmentLocation.SHIELD)) { defender.Attributes.IncreaseSkill(SkillName.BLOCKER); } feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.HIT, diceRoll)); //Calculate the amount of damage we're going to do. Roll 3 dice of a particular kind int weaponDamage = random.Next(weaponDiceRolls + 1) + random.Next(weaponDiceRolls + 1) + random.Next(weaponDiceRolls + 1); Console.WriteLine("Damage Roll : " + weaponDamage); int damage = weaponDamage; if (special != null) { damage += special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.DAMAGE) == null ? 0 : special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.DAMAGE).EffectValue; } if (damage > 1) { //Do we have any defences? if (defender.IsPlayerCharacter && defender.CurrentDefences > 0) { //Break one instead defender.CurrentDefences--; feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DEFENDED, diceRoll)); //And mark it GameState.LocalMap.TemporaryGraphics.Add(new TemporaryGraphic() { Coord = new MapCoordinate(defender.MapCharacter.Coordinate), Graphic = SpriteManager.GetSprite(distance < 2 ? InterfaceSpriteName.SWORD_BLOCKED : InterfaceSpriteName.BOW_BLOCKED), LifeTime = attacker.IsPlayerCharacter ? 2 : 2 }); return feedback.ToArray(); } } //Apply the damage //Is the user wearing any armour? if (location == AttackLocation.CHEST || location == AttackLocation.LEFT_ARM || location == AttackLocation.RIGHT_ARM) { //Determine how much damage we can get through due to pierce int piercing = 0; if (special != null) { piercing = special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.PIERCING) == null ? 0 : special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.PIERCING).EffectValue; } //Use the chest armour if (defender.Inventory.EquippedItems.ContainsKey(EquipmentLocation.BODY)) { int block = defender.Inventory.EquippedItems[EquipmentLocation.BODY].ArmourRating - piercing; damage -= (block > 0 ? block : 0); //Do we damage the armour? int sunder = 0; if (special != null) { sunder = special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.SUNDER) == null ? 0 : special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.SUNDER).EffectValue; } if (sunder > 0 && defender.Inventory.EquippedItems[EquipmentLocation.BODY].ArmourRating > 0) { defender.Inventory.EquippedItems[EquipmentLocation.BODY].ArmourRating -= sunder; if (defender.Inventory.EquippedItems[EquipmentLocation.BODY].ArmourRating < 0) //no negative block { defender.Inventory.EquippedItems[EquipmentLocation.BODY].ArmourRating = 0; } } } } //Other location ? if (location == AttackLocation.HEAD) { //Use the head armour if (defender.Inventory.EquippedItems.ContainsKey(EquipmentLocation.HEAD)) { damage -= defender.Inventory.EquippedItems[EquipmentLocation.HEAD].ArmourRating; } } if (location == AttackLocation.LEGS) { //Use the head armour if (defender.Inventory.EquippedItems.ContainsKey(EquipmentLocation.LEGS)) { damage -= defender.Inventory.EquippedItems[EquipmentLocation.LEGS].ArmourRating; } } if (damage <= 0) { //Bounced off the armour feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.BOUNCE, diceRoll)); GameState.LocalMap.TemporaryGraphics.Add(new TemporaryGraphic() { Coord = new MapCoordinate(defender.MapCharacter.Coordinate), Graphic = SpriteManager.GetSprite(distance < 2 ? InterfaceSpriteName.SWORD_ARMOUR : InterfaceSpriteName.BOW_ARMOUR), LifeTime = attacker.IsPlayerCharacter ? 2 : 2 }); return feedback.ToArray(); } //Do we wound the character? diceRoll = random.Next(10) + 1; int woundRoll = diceRoll + (defender.Attributes.WoundResist - weaponWoundPotential) - 5; int woundBonus = 0; if (special != null) { woundBonus = special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.BLEED) == null ? 0 : special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.BLEED).EffectValue; } woundRoll += woundBonus; if (woundRoll <= 0) { //Yes - open a wound defender.Anatomy.BloodLoss++; defender.Anatomy.BloodLoss += woundBonus; //Yeah, bleed makes you bleed more AND increases chance of bleed. Not entirely sure this won't be overpowered Console.WriteLine("Wounded " + woundRoll); //Log it feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.BLEED, woundRoll)); } //Roll again diceRoll = random.Next(10) + 1; int stunRoll = diceRoll + (defender.Attributes.WoundResist - weaponStunAmount) - 5; int stunBonus = 0; if (special != null) { stunBonus = special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.STUN) == null ? 0 : special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.STUN).EffectValue; } stunRoll += stunBonus; if (stunRoll <= 0) { //Stun him defender.Anatomy.StunAmount++; defender.Anatomy.StunAmount += stunRoll; //Yeah stun makes you more likely to get stunned and makes you stun longer. Not entirely sure this won't be as overpowered as ell Console.WriteLine("Stunned " + stunRoll); } //Any damage bonus ? if (special != null) { damage += special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.DAMAGE) == null ? 0 : special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.DAMAGE).EffectValue; } #region Push if (special != null) { //Do we have a push? int pushAmount = special.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.PUSH) == null ? 0 : special.Effects.First(e => e.EffectType == SpecialAttackType.PUSH).EffectValue; if (distance >= 2) { pushAmount = 0; //No push! } if (pushAmount > 0) { //Shove the target back by the total amount of push. Or try to at least //Let's determine the direction int pushX = 0; int pushY = 0; pushX = attacker.MapCharacter.Coordinate.X - defender.MapCharacter.Coordinate.X ; pushY = attacker.MapCharacter.Coordinate.Y - defender.MapCharacter.Coordinate.Y; for (int i = 0; i < pushAmount; i++) { //PUSH! //Is the tile free? if (GameState.LocalMap.GetBlockAtCoordinate(new MapCoordinate(defender.MapCharacter.Coordinate.X, defender.MapCharacter.Coordinate.Y, 0, MapType.LOCAL)).MayContainItems) { //Yeah push em GameState.LocalMap.GetBlockAtCoordinate(new MapCoordinate(defender.MapCharacter.Coordinate.X + pushX, defender.MapCharacter.Coordinate.Y + pushY, 0, MapType.LOCAL)).ForcePutItemOnBlock(defender.MapCharacter); } else { //Is there an obstacle in the way? if (GameState.LocalMap.GetBlockAtCoordinate(new MapCoordinate(defender.MapCharacter.Coordinate.X, defender.MapCharacter.Coordinate.Y, 0, MapType.LOCAL)).IsSeeThrough) { //No, stop there break; } else { //Ouch! Stop there anyway //Do damage equal to push left damage += pushAmount - i; feedback.Add(new LogFeedback(null, Color.Red, defender.Name + " hits an obstacle")); break; } } } } } #endregion //Apply the damage switch (location) { case AttackLocation.CHEST: defender.Anatomy.Chest -= damage; break; case AttackLocation.HEAD: defender.Anatomy.Head -= damage; break; case AttackLocation.LEFT_ARM: defender.Anatomy.LeftArm -= damage; break; case AttackLocation.LEGS: defender.Anatomy.Legs -= damage; break; case AttackLocation.RIGHT_ARM: defender.Anatomy.RightArm -= damage; break; default: throw new NotImplementedException(location + " has no code prepared for damage"); } GameState.LocalMap.TemporaryGraphics.Add(new TemporaryGraphic() { Coord = new MapCoordinate(defender.MapCharacter.Coordinate), Graphic = SpriteManager.GetSprite(distance < 2 ? InterfaceSpriteName.SWORD_HIT : InterfaceSpriteName.BOW_HIT), LifeTime = attacker.IsPlayerCharacter ? 2 : 2 }); //Damage assessment - Do this properly later switch (location) { case AttackLocation.HEAD: if (defender.Anatomy.Head < 0) { if (defender.Anatomy.Head <= -5) { //Destroyed feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DESTROY, diceRoll)); } else { //Disabled feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DISABLE, diceRoll)); } //Dead feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.KILL, diceRoll)); //Close the interface feedback.Add(new InterfaceToggleFeedback(InternalActionEnum.OPEN_ATTACK, false, defender)); KillCharacter(defender); } break; case AttackLocation.CHEST: if (defender.Anatomy.Chest < 0) { if (defender.Anatomy.Chest <= -5) { //Destroyed feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DESTROY, diceRoll)); } else { //Disabled feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DISABLE, diceRoll)); } //Dead feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.KILL, diceRoll)); feedback.Add(new InterfaceToggleFeedback(InternalActionEnum.OPEN_ATTACK, false, defender)); KillCharacter(defender); } break; case AttackLocation.LEFT_ARM: if (defender.Anatomy.LeftArm < 0) { if (defender.Anatomy.LeftArm <= -5) { //Destroyed feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DESTROY, diceRoll)); } else { //Disabled feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DISABLE, diceRoll)); } } break; case AttackLocation.LEGS: if (defender.Anatomy.Legs < 0) { if (defender.Anatomy.Legs <= -5) { //Destroyed feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DESTROY, diceRoll)); } else { //Disabled feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DISABLE, diceRoll)); } } break; case AttackLocation.RIGHT_ARM: if (defender.Anatomy.RightArm < 0) { if (defender.Anatomy.RightArm <= -5) { //Destroyed feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DESTROY, diceRoll)); } else { //Disabled feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.DISABLE, diceRoll)); } } break; default: throw new NotImplementedException("You've injured an unknown body part " + location); } } else { //We have a miss //The attacker learned something attacker.Attributes.IncreaseSkill(SkillName.FIGHTER); if (distance > 2) { attacker.Attributes.IncreaseSkill(SkillName.ARCHER); } else //Are they armed? if (attacker.Inventory.EquippedItems.ContainsKey(EquipmentLocation.WEAPON)) { switch (attacker.Inventory.EquippedItems[EquipmentLocation.WEAPON].WeaponType.ToUpper()) { case "SWORD": attacker.Attributes.IncreaseSkill(SkillName.SWORDFIGHTER); break; case "AXE": attacker.Attributes.IncreaseSkill(SkillName.AXEFIGHTER); break; case "BOW": attacker.Attributes.IncreaseSkill(SkillName.ARCHER); break; default: throw new NotImplementedException("No skill pertains to the weapon type " + attacker.Inventory.EquippedItems[EquipmentLocation.WEAPON].WeaponType); } } else { //Unarmed then attacker.Attributes.IncreaseSkill(SkillName.BRAWLER); } feedback.Add(LogAction(attacker, defender, location, damageType, LogMessageStatus.MISS, diceRoll)); GameState.LocalMap.TemporaryGraphics.Add(new TemporaryGraphic() { Coord = new MapCoordinate(defender.MapCharacter.Coordinate), Graphic = SpriteManager.GetSprite(distance < 2 ? InterfaceSpriteName.SWORD_BLOCKED : InterfaceSpriteName.BOW_BLOCKED), LifeTime = (attacker.IsPlayerCharacter ? 2 : 2) }); } //We're done return feedback.ToArray(); }
public AttackActorComponent(int locationX, int locationY, Actor attacker, Actor target) { this.locationX = locationX; this.locationY = locationY; this.attacker = attacker; this.TargetActor = target; this.currentAttackLocation = AttackLocation.CHEST; //Force a move this.PerformDrag(locationX, locationY); }
/// <summary> /// Creates an Enemy of a paticular type (optional), having a particular tag (optional) and having a particular intelligence (optional). /// Will return the enemy ID so that you can get the actual item from the crea /// </summary> /// <param name="enemyType"></param> /// <param name="enemyTag"></param> /// <param name="intelligent"></param> /// <param name="level">The skill level of this enemy</param> /// <param name="gearCost">The total cost of this unit's equipped items</param> /// <param name="?"></param> /// <returns></returns> public static Actor CreateActor(string enemyType, string enemyTag, bool? intelligent, int level, int gearCost, Gender? gender, out int enemyID, ActorProfession? profession = null) { //Get all the data from the database and we'll make our own filtering var dictionary = DatabaseHandling.GetDatabase(Archetype.ENEMIES); var possibleMatches = dictionary.Values.AsQueryable(); if (!String.IsNullOrEmpty(enemyType)) { possibleMatches = possibleMatches.Where(v => v[4].Equals(enemyType)); } if (!String.IsNullOrEmpty(enemyTag)) { possibleMatches = possibleMatches.Where(v => v[5].Equals(enemyTag)); } if (intelligent.HasValue) { possibleMatches = possibleMatches.Where(v => Boolean.Parse(v[7]).Equals(intelligent.Value)); } if (gender.HasValue) { possibleMatches = possibleMatches.Where(v => (v[12]).Equals(gender.Value.ToString())); } //Put the possible matches and pick one at random if (possibleMatches.Count() == 0) { enemyID = -1; return null; //No match } //Pick one at random var selected = possibleMatches.ToArray()[random.Next(possibleMatches.Count())]; int aggressivity = Int32.Parse(selected[10]); //Create the details enemyID = Int32.Parse(selected[0]); EnemyData data = GetEnemyData(enemyID); DRObjects.Actor actor = new DRObjects.Actor(); actor.EnemyData = data; actor.IsPlayerCharacter = false; // actor.LineOfSight = data.EnemyLineOfSight; actor.UniqueId = Guid.NewGuid(); actor.IsAggressive = aggressivity > 0; actor.Gender = (Gender)Enum.Parse(typeof(Gender), selected[12]); actor.Name = ActorNameGenerator.GenerateName(enemyType, actor.Gender); //Give him attributes actor.Attributes = GenerateAttributes(data.EnemyType, data.Profession, level, actor); //Set his anatomy too actor.Anatomy = GenerateAnatomy(data.EnemyType); //link one to another actor.Attributes.Health = actor.Anatomy; //Create the equipped item inventory actor.Inventory.EquippedItems = GenerateEquippedItems(gearCost); //And create the inventory foreach (var item in actor.Inventory.EquippedItems.Values) { actor.Inventory.Inventory.Add(item.Category, item); } return actor; }
/// <summary> /// Updates the Vendor's stock to brand new stock /// </summary> /// <param name="actor"></param> public void UpdateVendorStock(Actor actor) { //First see how much money they have to buy stuff with. //Money to buy stuff = SUM (Base Values of old stock Inventory Items) + Money on Hand - 1000 int totalMoney = actor.VendorDetails.Stock.GetAllObjects().Sum(s => s.BaseValue) + actor.VendorDetails.Money - 1000; //Generate the stuff InventoryItemManager iim = new InventoryItemManager(); actor.VendorDetails.Stock = new GroupedList<InventoryItem>(); actor.VendorDetails.GenerationTime = new DivineRightDateTime(GameState.UniverseTime); switch (actor.VendorDetails.VendorType) { case VendorType.GENERAL: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.ARMOUR.ToString(), totalMoney / 4)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.LOOT.ToString(), totalMoney / 4)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.WEAPON.ToString(), totalMoney / 4)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.SUPPLY.ToString(), totalMoney / 4)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } break; case VendorType.SMITH: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.ARMOUR.ToString(), totalMoney / 2)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.WEAPON.ToString(), totalMoney / 2)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } break; case VendorType.TRADER: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.LOOT.ToString(), totalMoney)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } break; case VendorType.TAVERN: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.SUPPLY.ToString(), totalMoney)) { inv.InInventory = true; actor.VendorDetails.Stock.Add(inv.Category, inv); } break; } actor.VendorDetails.Money = 1000; }
/// <summary> /// Generates a vendor, adds the vendor details to the actor being created /// </summary> /// <param name="newActor"></param> /// <param name="actor"></param> public void GenerateVendor(Actor newActor, MapletActor actor) { newActor.VendorDetails = new VendorDetails(); newActor.VendorDetails.VendorType = actor.VendorType.Value; newActor.VendorDetails.VendorLevel = actor.VendorLevel ?? 1; newActor.VendorDetails.GenerationTime = new DivineRightDateTime(GameState.UniverseTime); //Generate the stuff InventoryItemManager iim = new InventoryItemManager(); int maxCategorySize = 1000 * newActor.VendorDetails.VendorLevel; newActor.VendorDetails.Stock = new GroupedList<InventoryItem>(); switch (newActor.VendorDetails.VendorType) { case VendorType.GENERAL: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.SUPPLY.ToString(), (int)(maxCategorySize * 0.75))) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.LOOT.ToString(), (int)(maxCategorySize * 0.75))) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.ARMOUR.ToString(), (int)(maxCategorySize * 0.75))) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.WEAPON.ToString(), (int)(maxCategorySize * 0.75))) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } break; case VendorType.SMITH: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.ARMOUR.ToString(), (int)(maxCategorySize * 1.5))) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.WEAPON.ToString(), (int)(maxCategorySize * 1.5))) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } break; case VendorType.TRADER: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.LOOT.ToString(), maxCategorySize * 3)) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } break; case VendorType.TAVERN: foreach (InventoryItem inv in iim.GetItemsWithAMaxValue(InventoryCategory.SUPPLY.ToString(), maxCategorySize)) { inv.InInventory = true; newActor.VendorDetails.Stock.Add(inv.Category, inv); } break; } newActor.VendorDetails.Money = 1000; }
/// <summary> /// Regenerates the stats of an orc such that it retains any missions it has, however will be of a particular level with a particular gear cost /// Will also pick the correct orc icon depending on level /// </summary> /// <param name="level"></param> /// <param name="gearCost"></param> /// <param name="enemyID"></param> /// <returns></returns> public static void RegenerateOrc(Actor actor, int level, int gearCost) { Random random = new Random(); //This should give us a random from 0.75 to 1.25 double multiplier = (random.NextDouble() / 2) - 0.25 + 1; //Start by regenerating the actor's stats and gear actor.Attributes = GenerateAttributes("orc", level == 5 ? ActorProfession.WORKER : ActorProfession.WARRIOR, (int)(level * multiplier), actor); multiplier = (random.NextDouble() / 2) - 0.25 + 1; //And the gear actor.Inventory.EquippedItems = GenerateEquippedItems((int)(gearCost * multiplier)); foreach (var item in actor.Inventory.EquippedItems.Values) { actor.Inventory.Inventory.Add(item.Category, item); } //And finally, we need to pick the right graphic string graphic = String.Empty; if (level <= 5) { actor.MapCharacter.Graphic = SpriteManager.GetSprite(LocalSpriteName.ENEMY_ORC_CIV); } else if (level <= 10) { actor.MapCharacter.Graphic = SpriteManager.GetSprite(LocalSpriteName.ENEMY_ORC_LIGHT); } else { actor.MapCharacter.Graphic = SpriteManager.GetSprite(LocalSpriteName.ENEMY_ORC_HEAVY); } //Done }
/// <summary> /// Gets the actions which can be performed on this Block /// </summary> /// <param name="actor"></param> /// <returns></returns> public ActionType[] GetActions(Actor actor) { MapItem item = GetTopItem(); List<ActionType> actions = item.GetPossibleActions(actor).ToList(); if (this.Tile.GetType().Equals(typeof(GlobalTile))) { //We can also throw something - if we have line of sight - we'll worry about this later actions.Add(ActionType.THROW); } if (this.MayContainItems) { actions.Add(ActionType.MOVE); } return actions.ToArray(); }
public static SkillsAndAttributes GenerateAttributes(string race, ActorProfession profession, int level, Actor actor) { //Roll 6 3d3s and put the results in an array int[] results = new int[6]; //We'll throw away the smallest one for (int i = 0; i < results.Length; i++) { results[i] = random.Next(3) + 1 + random.Next(3) + 1 + random.Next(3) + 1; } //sort the array by size so top one goes first results = results.OrderByDescending(r => r).ToArray(); //Create a new ActorAttribute SkillsAndAttributes att = new SkillsAndAttributes(); if (profession == ActorProfession.WARRIOR) { //Prefer Brawn, Agil, Perc, Intel then char att.Brawn = results[0]; att.Agil = results[1]; att.Perc = results[2]; att.Intel = results[3]; att.Char = results[4]; //Combat skills - give him evasion and attack in an amount equal to level att.Evasion = level; att.HandToHand = level; } else if (profession == ActorProfession.BRUTE) { //Prefer Brawn, Agil, perc, Intel then char. att.Brawn = results[0]; att.Agil = results[1]; att.Perc = results[2]; att.Intel = results[3]; att.Char = results[4]; //Combat skills - give him evasion and attack in an amount equal to level. More hand to hand, less evasion att.Evasion = level - 2; att.HandToHand = level + 2; } else if (profession == ActorProfession.RANGED) { //Prefer Perc, Agil, Brawn, Intel then char att.Perc = results[0]; att.Agil = results[1]; att.Brawn = results[2]; att.Intel = results[3]; att.Char = results[4]; //Combat skills - give him evasion and attack in an amount equal to level. att.Evasion = level; att.HandToHand = level; actor.UsesRanged = true; } else if (profession == ActorProfession.DEFENDER) { //Prefer agil, brawn, perc, intel than char att.Agil = results[0]; att.Brawn = results[1]; att.Perc = results[2]; att.Intel = results[3]; att.Char = results[4]; //Combat skills - give him evasion and attack in an amount equal to level. +2 to evasion -2 to H2H att.Evasion = level + 2; att.HandToHand = level - 2; } else if (profession == ActorProfession.WORKER || profession == ActorProfession.MERCHANT || profession == ActorProfession.RICH) { //Prefer things randomly results = results.OrderByDescending(r => random.Next(10)).ToArray(); att.Brawn = results[0]; att.Agil = results[1]; att.Perc = results[2]; att.Intel = results[3]; att.Char = results[4]; att.HandToHand = level; att.Evasion = level; } else { throw new NotImplementedException("No code for profession " + profession); } //Add the racial bonuses RaceData data = ReadRaceData(race); att.Agil = att.BaseAgil + data.AgilModifier; att.Brawn = att.BaseBrawn + data.BrawnModifier; att.Char = att.BaseChar + data.DexModifier; att.Intel = att.BaseIntel + data.IntelModifier; att.Perc = att.BasePerc + data.PercModifier; //Make sure all attributes are larger than 0 att.Agil = att.BaseAgil > 0 ? att.BaseAgil : 0; att.Brawn = att.BaseBrawn > 0 ? att.BaseBrawn : 0; att.Char = att.BaseChar > 0 ? att.BaseChar : 0; att.Intel = att.BaseIntel > 0 ? att.BaseIntel : 0; att.Perc = att.BasePerc > 0 ? att.BasePerc : 0; att.Actor = actor; att.Health = actor.Anatomy; return att; }
/// <summary> /// Creates an actor owned by a particular faction having a level +- 25% of the level passed. /// The actual actor will be chosen according to the actual level /// The worth of the equipment the actor is as described in the database for the chosen actor /// Actors chosen will be of the warrior profession /// </summary> /// <param name="owner"></param> /// <param name="level"></param> /// <returns></returns> public static Actor CreateActor(OwningFactions owner, int skillLevel) { int levelToCreate = GameState.Random.Next((int)(skillLevel * 0.75) > 0 ? (int)(skillLevel * 0.75) : 0, (int)(skillLevel * 1.25)); //Get all the data from the database and we'll make our own filtering var dictionary = DatabaseHandling.GetDatabase(Archetype.ACTORS); var possibleMatches = dictionary.Values.AsQueryable(); //Correct owner possibleMatches = possibleMatches.Where(v => v[4].ToUpper().Equals(owner.ToString())); //The closest one w.r.t skill level possibleMatches = possibleMatches.Where(v => Int32.Parse(v[11]) < levelToCreate).OrderBy(v => Int32.Parse(v[11])); //Have we got at least one? if (possibleMatches.Count() == 0) { //Nope sorry return null; } else { //Copy pasted from CreateActors - rule of 3s var chosen = possibleMatches.First(); //Now we can generate the actor itself Actor actor = new Actor(); actor.Anatomy = GenerateAnatomy(chosen[5]); actor.EnemyData = new EnemyData() { EnemyID = Int32.Parse(chosen[0]), EnemyLineOfSight = Int32.Parse(chosen[9]), EnemyName = chosen[1], EnemyType = chosen[5], Intelligent = true, Profession = ActorProfession.WARRIOR }; //Pick a profession from warrior, rogue, brute or ranged int professionRandom = GameState.Random.Next(4); switch (professionRandom) { case 0: actor.EnemyData.Profession = ActorProfession.BRUTE; break; case 1: actor.EnemyData.Profession = ActorProfession.RANGED; break; case 2: actor.EnemyData.Profession = ActorProfession.WARRIOR; break; case 3: actor.EnemyData.Profession = ActorProfession.RANGED; break; } actor.Attributes = GenerateAttributes(chosen[5], actor.EnemyData.Profession, levelToCreate, actor); actor.Attributes.Actor = actor; actor.FeedingLevel = FeedingLevel.FULL; actor.Gender = (Gender)Enum.Parse(typeof(Gender), chosen[13]); actor.Inventory = new ActorInventory(); actor.Inventory.EquippedItems = GenerateEquippedItems(Int32.Parse(chosen[12]),actor.EnemyData.Profession); //Add all of those into the inventory foreach (var item in actor.Inventory.EquippedItems.Values) { actor.Inventory.Inventory.Add(item.Category, item); } actor.IsActive = true; actor.IsAggressive = chosen[8] == "1"; actor.IsAlive = true; actor.IsAnimal = false; actor.IsDomesticatedAnimal = false; actor.IsPlayerCharacter = false; actor.IsStunned = false; //For fixing LoS actor.Attributes.Perc = Int32.Parse(chosen[9]) - 2; actor.Name = ActorNameGenerator.CanGenerateName(chosen[5]) ? ActorNameGenerator.GenerateName(chosen[5], actor.Gender) : chosen[1]; actor.Owners = owner; actor.UniqueId = Guid.NewGuid(); actor.MapCharacter = new LocalCharacter(); LocalCharacter mc = actor.MapCharacter as LocalCharacter; mc.Actor = actor; mc.Coordinate = new MapCoordinate(); mc.Description = chosen[10]; mc.EnemyThought = EnemyThought.WAIT; string chosenGraphic = string.Empty; if (!String.IsNullOrWhiteSpace(chosen[3])) { string setChoice = String.Empty; //Does graphicset contain multiple choices? if (chosen[3].Contains(",")) { //Yes, lets split it var possibleSets = chosen[3].Split(','); setChoice = possibleSets[GameState.Random.Next(possibleSets.Length)]; } else { setChoice = chosen[3]; } //Instead of a single graphic, use a graphical set mc.Graphics = GraphicSetManager.GetSprites((GraphicSetName)Enum.Parse(typeof(GraphicSetName), setChoice.ToUpper())); } else { //Does graphic contain multiple choices? if (chosen[2].Contains(",")) { //yes, lets split it var graphics = chosen[2].Split(','); //use random to determine which one we want chosenGraphic = graphics[GameState.Random.Next(graphics.Length)]; mc.Graphic = SpriteManager.GetSprite((LocalSpriteName)Enum.Parse(typeof(LocalSpriteName), chosenGraphic)); } else { //nope mc.Graphic = SpriteManager.GetSprite((LocalSpriteName)Enum.Parse(typeof(LocalSpriteName), chosen[2])); } } mc.InternalName = chosen[2]; mc.IsActive = true; mc.IsStunned = false; mc.MayContainItems = false; mc.Name = actor.Name; mc.OwnedBy = owner; return actor; } }
/// <summary> /// Creates a number of actors for a particular owner and profession. It will also balance the level and difficulty of acctors, and will create a hierarchy of warriors. /// Do not use this for animals /// If there are no actors of that particular type for that profession, will not create anything /// </summary> public static Actor[] CreateActors(OwningFactions owner, ActorProfession profession, int total) { List<Actor> actors = new List<Actor>(); //Get all the data from the database and we'll make our own filtering var dictionary = DatabaseHandling.GetDatabase(Archetype.ACTORS); var possibleMatches = dictionary.Values.AsQueryable(); //Correct owner possibleMatches = possibleMatches.Where(v => v[4].ToUpper().Equals(owner.ToString())); //Correct profession possibleMatches = possibleMatches.Where(v => v[6].ToUpper().Equals(profession.ToString())); //Have we got at least one ? if (possibleMatches.Count() == 0) { return new Actor[] { }; //Nope - return nothing } //For use with warriors int level1 = 0; int level2 = 0; for (int i = 0; i < total; i++) { var possibles = possibleMatches; //Are we generating warriors? if (profession == ActorProfession.WARRIOR) { int warriorGenerationLevel = 1; if (level2 == 3) { //hard warriorGenerationLevel = 3; level2 = 0; } else if (level1 == 3) { //Generate a medium warriorGenerationLevel = 2; level1 = 0; level2++; } else { //Generate an easy warriorGenerationLevel = 1; level1++; } possibles = possibles.Where(p => p[7].ToString().Equals(warriorGenerationLevel.ToString())); } //Pick a random one from the possibilities - this'll crash if we have no possibles, but that's going to be a problem anyway var chosen = possibles.ToArray()[(GameState.Random.Next(possibles.Count()))]; //Now we can generate the actors themselves Actor actor = new Actor(); actor.Anatomy = GenerateAnatomy(chosen[5]); actor.Attributes = GenerateAttributes(chosen[5], (ActorProfession)Enum.Parse(typeof(ActorProfession), chosen[6], true), Int32.Parse(chosen[11]), actor); actor.Attributes.Actor = actor; actor.EnemyData = new EnemyData() { EnemyID = Int32.Parse(chosen[0]), EnemyLineOfSight = Int32.Parse(chosen[9]), EnemyName = chosen[1], EnemyType = chosen[5], Intelligent = true, Profession = profession }; actor.FeedingLevel = FeedingLevel.FULL; actor.Gender = (Gender)Enum.Parse(typeof(Gender), chosen[13]); actor.Inventory = new ActorInventory(); if (profession == ActorProfession.WARRIOR) { actor.Inventory.EquippedItems = GenerateEquippedItems(Int32.Parse(chosen[12])); //Add all of those into the inventory foreach (var item in actor.Inventory.EquippedItems.Values) { actor.Inventory.Inventory.Add(item.Category, item); } } actor.IsActive = true; actor.IsAggressive = chosen[8] == "1"; actor.IsAlive = true; actor.IsAnimal = false; actor.IsDomesticatedAnimal = false; actor.IsPlayerCharacter = false; actor.IsStunned = false; actor.Attributes.Perc = Int32.Parse(chosen[9]) - 2; actor.Name = ActorNameGenerator.CanGenerateName(chosen[5]) ? ActorNameGenerator.GenerateName(chosen[5], actor.Gender) : chosen[1]; actor.Owners = owner; actor.UniqueId = Guid.NewGuid(); actor.MapCharacter = new LocalCharacter(); LocalCharacter mc = actor.MapCharacter as LocalCharacter; mc.Actor = actor; mc.Coordinate = new MapCoordinate(); mc.Description = chosen[10]; mc.EnemyThought = EnemyThought.WAIT; string chosenGraphic = string.Empty; if (!String.IsNullOrWhiteSpace(chosen[3])) { string setChoice = String.Empty; //Does graphicset contain multiple choices? if (chosen[3].Contains(",")) { //Yes, lets split it var possibleSets = chosen[3].Split(','); setChoice = possibleSets[GameState.Random.Next(possibleSets.Length)]; } else { setChoice = chosen[3]; } //Instead of a single graphic, use a graphical set mc.Graphics = GraphicSetManager.GetSprites((GraphicSetName)Enum.Parse(typeof(GraphicSetName), setChoice.ToUpper())); } else { //Does graphic contain multiple choices? if (chosen[2].Contains(",")) { //yes, lets split it var graphics = chosen[2].Split(','); //use random to determine which one we want chosenGraphic = graphics[GameState.Random.Next(graphics.Length)]; mc.Graphic = SpriteManager.GetSprite((LocalSpriteName)Enum.Parse(typeof(LocalSpriteName), chosenGraphic)); } else { //nope mc.Graphic = SpriteManager.GetSprite((LocalSpriteName)Enum.Parse(typeof(LocalSpriteName), chosen[2])); } } mc.InternalName = chosen[2]; mc.IsActive = true; mc.IsStunned = false; //mc.LineOfSightRange = actor.LineOfSight.Value; mc.MayContainItems = false; mc.Name = actor.Name; mc.OwnedBy = owner; actors.Add(actor); } return actors.ToArray(); }
/// <summary> /// Attack a particular target using this special attack /// </summary> /// <param name="attacker"></param> /// <param name="target"></param> /// <param name="attack"></param> /// <returns></returns> public static ActionFeedback[] PerformSpecialAttack(Actor attacker, Actor target, SpecialAttack attack) { List<ActionFeedback> feedback = new List<ActionFeedback>(); feedback.Add(new LogFeedback(InterfaceSpriteName.SA1, Color.Blue, "You strike using " + attack.AttackName)); if (target.MapCharacter == null) { return new ActionFeedback[] { }; //Huh ? } //So, first we need to determine how many targets we're going to attack, and how many times List<Actor> targets = new List<Actor>(); targets.Add(target); for (int i = 1; i < attack.Effects.Where(e => e.EffectType == SpecialAttackType.TARGETS).Select(e => e.EffectValue).FirstOrDefault(); i++) { //Go around the attacker and add another target - later we'll need to check if they're hostile foreach (var block in GameState.LocalMap.GetBlocksAroundPoint(attacker.MapCharacter.Coordinate, 1)) { foreach (var character in block.GetItems().Where(gi => gi.IsActive && gi.GetType() == typeof(LocalCharacter))) { Actor newTarget = (character as LocalCharacter).Actor; if (!targets.Contains(newTarget)) { //Add it! targets.Add(newTarget); continue; } } } break; //No more actors } int attacksToMake = attack.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.ATTACKS) == null ? 1 : attack.Effects.FirstOrDefault(e => e.EffectType == SpecialAttackType.ATTACKS).EffectValue; for (int i = 0; i < attacksToMake; i++) { foreach (var defender in targets) { //Are we alive? if (!defender.IsAlive) { continue; //yeah he's dead jim } else { //Attack! feedback.AddRange(Attack(attacker, defender, AttackLocation.CHEST, attack)); } } } //Phew, now let's set the timeout attack.TimeOutLeft = attack.TimeOut; //Done return feedback.ToArray(); }
/// <summary> /// Regenerates a site's actors based on the sitedata. If it is not marked as needing regeneration , will do nothing /// </summary> /// <param name="sitedata"></param> /// <param name="currentMap"></param> /// <param name="actors"></param> /// <returns></returns> public static MapBlock[,] RegenerateSite(SiteData sitedata, MapBlock[,] currentMap, Actor[] currentActors, out Actor[] newActors) { if (!sitedata.MapRegenerationRequired) { //Do nothing newActors = currentActors; return currentMap; } //So, first go through the actors and remove any of them which shouldn't be there. That is to say all of them which are site members (I realise it's a big ugly but it's a clean check) List<Actor> actors = currentActors.ToList(); var actorsToRemove = currentActors.Where(ca => ca.SiteMember); //Remove the map items for these actors foreach(var a in actorsToRemove) { a.IsAlive = false; } //We'll handle actors soon, first we need to go through all the items and set the ones with the wrong owner to inactive foreach (var mapBlock in currentMap) { foreach (var item in mapBlock.GetItems()) { if (!item.OwnedBy.HasFlag(sitedata.Owners)) { item.IsActive = false; } else { item.IsActive = true; } } } //Now go through the site's actors and create the new ones as required foreach (ActorProfession profession in Enum.GetValues(typeof(ActorProfession))) { //So do we have any wander areas for them ? var possibleAreas = sitedata.WanderAreas.Where(wa => wa.Factions.HasFlag(sitedata.Owners) && wa.Profession.Equals(profession)); var possibleRoutes = sitedata.PatrolRoutes.Where(pr => pr.Owners.HasFlag(sitedata.Owners) && pr.Profession.Equals(profession)); //Any actors? if (sitedata.ActorCounts.ContainsKey(profession)) { //Yes, how many int total = sitedata.ActorCounts[profession]; var a = ActorGeneration.CreateActors(sitedata.Owners, profession, total); foreach (var ac in a) { ac.SiteMember = true; } actors.AddRange(a); foreach (var actor in a) { //So, where we going to drop them off ? Randomly int tries = 0; for (; ; ) { int randomX = GameState.Random.Next(currentMap.GetLength(0)); int randomY = GameState.Random.Next(currentMap.GetLength(1)); if (currentMap[randomX, randomY].MayContainItems) { //Plop it on there actor.MapCharacter.Coordinate = new MapCoordinate(randomX, randomY, 0, MapType.LOCAL); currentMap[randomX, randomY].ForcePutItemOnBlock(actor.MapCharacter); tries = 0; break; } else { tries++; } if (tries >= 150) { //give up break; } } //Go through each actor, and either tell them to wander in the whole map, or within any possible area which matches //Any possible area avaialble? List<object> possibleMissions = new List<object>(); //I know :( But Using an interface or trying to mangle together an inheritance was worse possibleMissions.AddRange(possibleAreas.Where(pa => pa.MaxAmount > pa.CurrentAmount)); possibleMissions.AddRange(possibleRoutes); var chosenArea = possibleMissions.OrderBy(pa => GameState.Random.Next(100)).FirstOrDefault(); if (chosenArea == null) { //Wander around the whole map actor.CurrentMission = new WanderMission() { LoiterPercentage = 25, WanderPoint = new MapCoordinate(currentMap.GetLength(0) / 2, currentMap.GetLength(1) / 2, 0, MapType.LOCAL), WanderRectangle = new Rectangle(0, 0, currentMap.GetLength(0), currentMap.GetLength(1)) }; } else { //Is this a patrol or a wander ? if (chosenArea.GetType().Equals(typeof(PatrolRoute))) { var patrolDetails = chosenArea as PatrolRoute; PatrolRouteMission pm = new PatrolRouteMission(); pm.PatrolRoute.AddRange(patrolDetails.Route); actor.CurrentMission = pm; } else if (chosenArea.GetType().Equals(typeof(MapletActorWanderArea))) { var wanderDetails = chosenArea as MapletActorWanderArea; //Wander around in that area actor.CurrentMission = new WanderMission() { LoiterPercentage = 25, WanderPoint = new MapCoordinate(wanderDetails.WanderPoint), WanderRectangle = wanderDetails.WanderRect }; wanderDetails.CurrentAmount++; } } } } } newActors = actors.ToArray(); sitedata.MapRegenerationRequired = false; //We're done return currentMap; }
/// <summary> /// Returns a particular logged action. /// </summary> /// <returns></returns> private static ActionFeedback LogAction(Actor attacker, Actor defender, AttackLocation loc, DamageType type, LogMessageStatus status, int diceroll) { //Later we'll expand this and put in some randomisation and stuff ActionFeedback log = null; if (status == LogMessageStatus.BOUNCE) { //Bounces off the armour if (!attacker.IsPlayerCharacter) { if (type == DamageType.CRUSH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "The hit bounces off your armour"); } else if (type == DamageType.SLASH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "The hit scratches the armour, but doesn't cut through"); } else if (type == DamageType.STAB) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "The hit fails to punch through your armour"); } else if (type == DamageType.THRUST) { log = log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "The hit fails to punch through your armour"); } else if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "The missile fails to punch through your armour"); } } else { if (type == DamageType.CRUSH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "The hit bounces off your target's armour"); } else if (type == DamageType.SLASH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "The hit scratches the armour, but doesn't cut through"); } else if (type == DamageType.STAB) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "The hit fails to punch through your target's armour"); } else if (type == DamageType.THRUST) { log = log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "The hit fails to punch through your target's armour"); } else if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "The missile fails to punch through your target's armour"); } } return log; } if (status == LogMessageStatus.DESTROY) { if (!attacker.IsPlayerCharacter) { if (type == DamageType.CRUSH) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " smashes into pulp under the force of the blow"); } else if (type == DamageType.SLASH) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " flies off in an arc"); } else if (type == DamageType.STAB) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " dangles off by its skin"); } else if (type == DamageType.THRUST) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " dangles off by its skin"); } else if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your perforated " + loc.ToString().ToLower().Replace("_", " ") + " hangs limply off your side"); } } else { if (type == DamageType.CRUSH) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " smashes into pulp under the force of the blow"); } else if (type == DamageType.SLASH) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " flies off in an arc"); } else if (type == DamageType.STAB) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " dangles off by its skin"); } else if (type == DamageType.THRUST) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " dangles off by its skin"); } else if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "Your opponent's perforated " + loc.ToString().ToLower().Replace("_", " ") + " hangs limply off their side"); } } return log; } if (status == LogMessageStatus.DISABLE) { if (!attacker.IsPlayerCharacter) { if (type == DamageType.CRUSH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " cracks loudly as the bones smash under the blow"); } else if (type == DamageType.SLASH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " is cut open with the attack"); } else if (type == DamageType.STAB) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " bleeds heavily as the attack goes through"); } else if (type == DamageType.THRUST) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " bleeds heavily as the attack goes through"); } else if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkRed, "Your " + loc.ToString().ToLower().Replace("_", " ") + " bleeds heavily as the missile pierces through"); } } else { if (type == DamageType.CRUSH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " cracks loudly as the bones smash under the blow"); } else if (type == DamageType.SLASH) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " is cut open with the attack"); } else if (type == DamageType.STAB) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " bleeds heavily as the attack goes through"); } else if (type == DamageType.THRUST) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " bleeds heavily as the attack goes through"); } else if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkGreen, "Your opponent's " + loc.ToString().ToLower().Replace("_", " ") + " bleeds heavily as the missile pierces through"); } } return log; } if (status == LogMessageStatus.HIT) { if (attacker.IsPlayerCharacter) { if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.BOW, Color.DarkGreen, "You loose (" + diceroll + ") at " + defender.EnemyData.EnemyName + " and hit him in the " + loc.ToString().ToLower().Replace("_", " ")); } else { log = new LogFeedback(InterfaceSpriteName.SWORD, Color.DarkGreen, "You swing (" + diceroll + ") at " + defender.EnemyData.EnemyName + " and hit him in the " + loc.ToString().ToLower().Replace("_", " ")); } } else { if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.SWORD, Color.DarkRed, attacker.EnemyData.EnemyName + " looses (" + diceroll + ") a projectile at you and hits you in the " + loc.ToString().ToLower().Replace("_", " ")); } else { log = new LogFeedback(InterfaceSpriteName.SWORD, Color.DarkRed, attacker.EnemyData.EnemyName + " swings (" + diceroll + ") at you and hits you in the " + loc.ToString().ToLower().Replace("_", " ")); } } return log; } if (status == LogMessageStatus.KILL) { if (attacker.IsPlayerCharacter) { log = new LogFeedback(InterfaceSpriteName.HEAD, Color.DarkGreen, "You strike your opponent down"); } else { //log = new CurrentLogFeedback(InterfaceSpriteName.HEAD, Color.DarkRed, "You die"); log = new CreateEventFeedback("Death"); } return log; } if (status == LogMessageStatus.MISS) { //We have a miss if (attacker.IsPlayerCharacter) { if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.SWORD, Color.DarkRed, "You loose (" + diceroll + ") your projectile at " + defender.EnemyData.EnemyName + "'s " + loc.ToString().ToLower().Replace("_", " ") + " but miss"); } else { log = new LogFeedback(InterfaceSpriteName.SWORD, Color.DarkRed, "You swing (" + diceroll + ") at " + defender.EnemyData.EnemyName + "'s " + loc.ToString().ToLower().Replace("_", " ") + " but miss"); } } else { //Player is the defender if (type == DamageType.PIERCE) { log = new LogFeedback(InterfaceSpriteName.SWORD, Color.DarkGreen, "The " + attacker.EnemyData.EnemyName + " looses (" + diceroll + ") their projectile at your " + loc.ToString().ToLower().Replace("_", " ") + ", but misses"); } else { log = new LogFeedback(InterfaceSpriteName.SWORD, Color.DarkGreen, "The " + attacker.EnemyData.EnemyName + " swings (" + diceroll + ") at your " + loc.ToString().ToLower().Replace("_", " ") + ", but misses"); } } return log; } if (status == LogMessageStatus.NO_WOUND) { //No damage! if (attacker.IsPlayerCharacter) { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkRed, "You however fail to wound your opponent"); } else { log = new LogFeedback(InterfaceSpriteName.BLOOD, Color.DarkGreen, "It fails to properly wound you, however"); } return log; } if (status == LogMessageStatus.BLEED) { if (defender.IsPlayerCharacter) { if (defender.Anatomy.BloodLoss == 1) { //Wound opened log = new LogFeedback(InterfaceSpriteName.BLEEDING, Color.DarkRed, "The attack opens a wound"); } else { //Wound worsended log = new LogFeedback(InterfaceSpriteName.BLEEDING, Color.DarkRed, "The attack worsens your wound"); } } return log; } if (status == LogMessageStatus.DEFENDED) { log = new LogFeedback(InterfaceSpriteName.DEFENSE, Color.DarkBlue, "You dodge the blow at the last moment"); return log; } throw new NotImplementedException("No idea what to do here"); }
/// <summary> /// Generates a site /// </summary> /// <param name="siteType"></param> /// <param name="biomeType"></param> /// <param name="owner"></param> /// <param name="startPoint"></param> /// <param name="actors"></param> /// <returns></returns> public static MapBlock[,] GenerateSite(SiteData siteData, out Actor[] actors) { MapCoordinate startPoint = null; //First we generate some empty wilderness of the right type MapBlock[,] map = WildernessGenerator.GenerateMap(siteData.Biome, 0, 0, out actors, out startPoint); //Now, clear the tiles from between 5,5 till 25,25 for (int x = 5; x < 26; x++) { for (int y = 5; y < 26; y++) { MapBlock block = map[x, y]; block.RemoveAllItems(); } } ItemFactory.ItemFactory itemFactory = new ItemFactory.ItemFactory(); int waterID = 0; MapItem waterTile = itemFactory.CreateItem(Archetype.TILES, "water", out waterID); //If it's a fishing village, put some water in if (siteData.SiteTypeData.SiteType == SiteType.FISHING_VILLAGE) { for (int x = 0; x < map.GetLength(0); x++) { for (int y = map.GetLength(1) - 10; y < map.GetLength(1); y++) { MapBlock block = map[x, y]; block.RemoveAllItems(); //Set the tile to water block.Tile = itemFactory.CreateItem("tiles", waterID); block.Tile.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); } } } LocalMapGenerator lmg = new LocalMapGenerator(); LocalMapXMLParser parser = new LocalMapXMLParser(); Maplet maplet = parser.ParseMapletFromTag(siteData.SiteTypeData.SiteType.ToString().Replace("_", " ").ToLower(),siteData.Biome); var tileID = DatabaseHandling.GetItemIdFromTag(Archetype.TILES, WildernessGenerator.details[siteData.Biome].BaseTileTag); MapletActorWanderArea[] wanderAreas = null; MapletPatrolPoint[] patrolPoints = null; MapletFootpathNode[] footPath = null; //Now generate the actual map MapBlock[,] siteMap = lmg.GenerateMap(tileID, null, maplet, false, "", siteData.Owners, out actors, out wanderAreas, out patrolPoints, out footPath); //Now lets fuse the maps map = lmg.JoinMaps(map, siteMap, 5, 5); foreach (var actor in actors) { if (actor.CurrentMission == null) { actor.CurrentMission = actor.MissionStack.Count > 0 ? actor.MissionStack.Pop() : null; } if (actor.CurrentMission != null && actor.CurrentMission.GetType() == typeof(WanderMission)) { WanderMission wMiss = actor.CurrentMission as WanderMission; wMiss.WanderPoint.X += 5; wMiss.WanderPoint.Y += 5; wMiss.WanderRectangle = new Rectangle(wMiss.WanderRectangle.X + 5, wMiss.WanderRectangle.Y + 5, wMiss.WanderRectangle.Width, wMiss.WanderRectangle.Height); } } //Fix the patrol points foreach (var point in patrolPoints) { point.Point.X += 5; point.Point.Y += 5; } //Let's fix the patrol points, we need to merge them into PatrolRoutes var patrolRoutes = PatrolRoute.GetPatrolRoutes(patrolPoints); foreach (var area in wanderAreas) { area.WanderRect = new Rectangle(area.WanderRect.X + 5, area.WanderRect.Y + 5, area.WanderRect.Width, area.WanderRect.Height); } //And fix the path nodes foreach (var pn in footPath) { pn.Point.X += 5; pn.Point.Y += 5; } //If the map already has any actors in it, make the characters prone foreach (var actor in actors) { actor.IsProne = true; } //Now generate the pathfinding map PathfinderInterface.Nodes = GeneratePathfindingMap(map); int pathTileID = -1; var dummy = itemFactory.CreateItem(Archetype.TILES, "stone", out pathTileID); //Go through each footpath node. Attempt to connect the node with the other primary nodes foreach (var fp in footPath) { foreach (var primary in footPath.Where(p => p.IsPrimary)) { //Join them up var path = PathfinderInterface.GetPath(fp.Point, primary.Point); if (path != null) { foreach (var coord in path) { MapBlock block = map[coord.X, coord.Y]; //Only do this if the tile isn't wood, or stone if (!block.Tile.InternalName.ToUpper().Contains("STONE") && !block.Tile.InternalName.ToUpper().Contains("WOOD")) { block.Tile = itemFactory.CreateItem("tiles", pathTileID); block.Tile.Coordinate = new MapCoordinate(coord); } } } } } List<Actor> actorList = new List<Actor>(); actorList.AddRange(actors); //And add the actors //Let's generate a number of actors then foreach (ActorProfession profession in Enum.GetValues(typeof(ActorProfession))) { //So do we have any wander areas for them ? var possibleAreas = wanderAreas.Where(wa => wa.Factions.HasFlag(siteData.Owners) && wa.Profession.Equals(profession)); var possibleRoutes = patrolRoutes.Where(pr => pr.Owners.HasFlag(siteData.Owners) && pr.Profession.Equals(profession)); //Any actors? if (siteData.ActorCounts.ContainsKey(profession)) { //Yes, how many int total = siteData.ActorCounts[profession]; var a = ActorGeneration.CreateActors(siteData.Owners, profession, total); foreach(var ac in a) { ac.SiteMember = true; } actorList.AddRange(a); foreach (var actor in a) { //So, where we going to drop them off ? Randomly int tries = 0; for (; ; ) { int randomX = GameState.Random.Next(map.GetLength(0)); int randomY = GameState.Random.Next(map.GetLength(1)); if (map[randomX, randomY].MayContainItems) { //Plop it on there actor.MapCharacter.Coordinate = new MapCoordinate(randomX, randomY, 0, MapType.LOCAL); map[randomX, randomY].ForcePutItemOnBlock(actor.MapCharacter); tries = 0; break; } else { tries++; } if (tries >= 150) { //give up break; } } //Go through each actor, and either tell them to wander in the whole map, or within any possible area which matches //Any possible area avaialble? List<object> possibleMissions = new List<object>(); //I know :( But Using an interface or trying to mangle together an inheritance was worse possibleMissions.AddRange(possibleAreas.Where(pa => pa.MaxAmount > pa.CurrentAmount)); possibleMissions.AddRange(possibleRoutes); var chosenArea = possibleMissions.OrderBy(pa => GameState.Random.Next(100)).FirstOrDefault(); if (chosenArea == null) { //Wander around the whole map actor.CurrentMission = new WanderMission() { LoiterPercentage = 25, WanderPoint = new MapCoordinate(map.GetLength(0) / 2, map.GetLength(1) / 2, 0, MapType.LOCAL), WanderRectangle = new Rectangle(0, 0, map.GetLength(0), map.GetLength(1)) }; } else { //Is this a patrol or a wander ? if (chosenArea.GetType().Equals(typeof(PatrolRoute))) { var patrolDetails = chosenArea as PatrolRoute; PatrolRouteMission pm = new PatrolRouteMission(); pm.PatrolRoute.AddRange(patrolDetails.Route); actor.CurrentMission = pm; } else if (chosenArea.GetType().Equals(typeof(MapletActorWanderArea))) { var wanderDetails = chosenArea as MapletActorWanderArea; //Wander around in that area actor.CurrentMission = new WanderMission() { LoiterPercentage = 25, WanderPoint = new MapCoordinate(wanderDetails.WanderPoint), WanderRectangle = wanderDetails.WanderRect }; wanderDetails.CurrentAmount++; } } } } } actors = actorList.ToArray(); siteData.PatrolRoutes = patrolRoutes.ToList(); siteData.WanderAreas = wanderAreas.ToList(); return map; }
/// <summary> /// Checks for health and decides whether the character should be dead /// Also bleeds the character /// Also checks whether the character is stunned or not /// </summary> /// <param name="actor"></param> /// <returns></returns> public static ActionFeedback[] CheckHealth(Actor actor) { if (!actor.IsAlive || !actor.MapCharacter.IsActive) { //Nothing we can do here return new ActionFeedback[] { }; } //Check for body part damage if (actor.Anatomy.Head < 0) { //Character is dead actor.IsAlive = false; if (actor.IsPlayerCharacter) { //Inform the character return new ActionFeedback[] { new CreateEventFeedback("DEATH") }; } } if (actor.Anatomy.Chest < 0) { //Character is dead actor.IsAlive = false; if (actor.IsPlayerCharacter) { //Inform the character return new ActionFeedback[] { new CreateEventFeedback("DEATH") }; } } //Bleed a bit actor.Anatomy.BloodTotal -= actor.Anatomy.BloodLoss; if (actor.Anatomy.BloodLoss <= 0 && actor.Anatomy.BloodTotal < HumanoidAnatomy.BLOODTOTAL) { //Increase the blood amount actor.Anatomy.BloodTotal++; } else if (actor.Anatomy.BloodLoss > 0) { //Is it time to reduce the blood level? if (actor.Anatomy.BodyTimer++ >= HumanoidAnatomy.BODY_TIMER_FLIP) { //Decrease bleeding amount actor.Anatomy.BloodLoss--; actor.Anatomy.BodyTimer = 0; } else { //tick actor.Anatomy.BodyTimer++; } } //Are we low on blood? if (actor.Anatomy.BloodTotal < HumanoidAnatomy.BLOOD_STUN_AMOUNT) { //Increase the stun level actor.Anatomy.StunAmount++; } if (actor.Anatomy.BloodTotal < 0) { //Death CombatManager.KillCharacter(actor); //Drop the stuff if (actor.IsPlayerCharacter) { return new ActionFeedback[] { new LogFeedback(InterfaceSpriteName.BLEEDING, Color.Red, "You bleed to death"), new CreateEventFeedback("DEATH") }; } else { return new ActionFeedback[] { new LogFeedback(InterfaceSpriteName.BLEEDING, Color.Red, actor.Name + " has bled to death") }; } } if (actor.IsStunned) //unstun { actor.IsStunned = false; if (actor.MapCharacter as LocalCharacter != null) { (actor.MapCharacter as LocalCharacter).IsStunned = false; } } if (actor.Anatomy.StunAmount > 0) { if (actor.Anatomy.StunAmount > 10) { //Reduce it to the max amount. 10 actor.Anatomy.StunAmount = 10; } //Check whether the actor is stunned or not if (actor.Anatomy.StunAmount > 0) { //Stunned actor.IsStunned = true; if (actor.MapCharacter as LocalCharacter != null) { (actor.MapCharacter as LocalCharacter).IsStunned = true; } //Feel better actor.Anatomy.StunAmount--; if (actor.IsPlayerCharacter) { return new ActionFeedback[] { new LogFeedback(InterfaceSpriteName.SPIRAL, Color.Red, "You black out") }; } } } return new ActionFeedback[] { }; }
/// <summary> /// Generates a map based on the maplet assigned /// </summary> /// <param name="maplet">The maplet to generate</param> /// <param name="parentWallID">The wall that the parent has</param> /// <param name="parentTileID">The ID of the tiles used in the parent maplet item</param> /// <param name="enemyType">The type of actor which is dominant in this map</param> /// <param name="owner">The owner of the map. Any maplet items which don't belong will be hidden</param> /// <param name="actors">The actors which we have generated</param> /// <param name="actorType">The type of actors to generate</param> /// <returns></returns> public MapBlock[,] GenerateMap(int parentTileID, int? parentWallID, Maplet maplet, bool preferSides, string actorType, OwningFactions owner, out Actor[] actors, out MapletActorWanderArea[] wAreas, out MapletPatrolPoint[] patrolRoutes, out MapletFootpathNode[] footpathNodes) { List<Actor> actorList = new List<Actor>(); List<MapletActorWanderArea> wanderAreas = new List<MapletActorWanderArea>(); List<MapletPatrolPoint> patrolRouteList = new List<MapletPatrolPoint>(); List<MapletFootpathNode> footpathNodeList = new List<MapletFootpathNode>(); PlanningMapItemType[,] planningMap = new PlanningMapItemType[maplet.SizeX, maplet.SizeY]; //Step 1: Plan how the map will look //Step 1a: Set all tiles to available, and set the frame to walls if there's a wall planningMap = this.CreateBlueprint(maplet); //Step 1b: Put in the tiles in the actual map, and the walls if they are present MapBlock[,] generatedMap = new MapBlock[maplet.SizeX, maplet.SizeY]; ItemFactory.ItemFactory factory = new ItemFactory.ItemFactory(); int tileID = 0; if (maplet.Tiled) { if (maplet.TileID.HasValue) { tileID = maplet.TileID.Value; } else { //Load the tileID from the factory factory.CreateItem(Archetype.TILES, maplet.TileTag, out tileID); } } else { tileID = parentTileID; } //That's the tiles done for (int x = 0; x < generatedMap.GetLength(0); x++) { for (int y = 0; y < generatedMap.GetLength(1); y++) { MapItem tile = factory.CreateItem("tile", tileID); tile.Coordinate = new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL); MapBlock block = new MapBlock(); block.Tile = tile; generatedMap[x, y] = block; } } //Do the walls now if they are required int? wallID = null; int tempWallID = -1; if (parentWallID.HasValue) { MapItem wall = factory.CreateItem("MUNDANEITEMS", parentWallID.Value); wallID = parentWallID; } else { MapItem wall = factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "wall", out tempWallID); wallID = tempWallID; } if (maplet.Walled && wallID.HasValue) { //wall the edge tiles for (int x = 0; x < maplet.SizeX; x++) { generatedMap[x, 0].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); generatedMap[x, maplet.SizeY - 1].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); if (maplet.WindowProbability.HasValue && maplet.WindowProbability.Value > 0) { if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[x, 0].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } else if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[x, maplet.SizeY - 1].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } } } for (int y = 0; y < maplet.SizeY; y++) { generatedMap[0, y].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); generatedMap[maplet.SizeX - 1, y].PutItemOnBlock(factory.CreateItem("mundaneitems", wallID.Value)); if (maplet.WindowProbability.HasValue && maplet.WindowProbability.Value > 0) { if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[0, y].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } else if (random.Next(100) < maplet.WindowProbability.Value) { int itemID; //Put a window :) generatedMap[maplet.SizeX - 1, y].ForcePutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "window", out itemID)); } } } } //Step 1c: Determine where we'll put the maplets foreach (MapletContentsMaplet childMaplet in maplet.MapletContents.Where(mc => (mc is MapletContentsMaplet)).OrderByDescending(mc => mc.ProbabilityPercentage).ThenBy(mc => random.Next())) { //Calculate the probability of putting the item in, and how many items we're putting for (int i = 0; i < childMaplet.MaxAmount; i++) { if (random.NextDouble() * 100 <= childMaplet.ProbabilityPercentage) { //Does it fit? int x = -1; int y = -1; PlanningMapItemType[,] newMap; //Convert the child maplet into a planning map PlanningMapItemType[,] childMapletBlueprint = this.CreateBlueprint(childMaplet.Maplet); //mark the areas covered by the blueprint as being held by that blueprint if (childMaplet.Position == PositionAffinity.FIXED) { if (Fits(planningMap, childMapletBlueprint, childMaplet.x.Value, childMaplet.y.Value, out newMap)) { //it fits, generate it - <3 Recursion Actor[] childActors = null; MapletActorWanderArea[] wanderA = null; MapletPatrolPoint[] patrolPoints = null; MapletFootpathNode[] fpNodes = null; MapBlock[,] childMap = this.GenerateMap(tileID, wallID.Value, childMaplet.Maplet, childMaplet.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES, actorType, owner, out childActors, out wanderA, out patrolPoints, out fpNodes); //Add the child actors actorList.AddRange(childActors); //Update any actors's locations should they have any foreach (var actor in childActors) { if (actor.MissionStack.Count() > 0) { var wander = actor.MissionStack.Peek() as WanderMission; if (wander != null) { wander.WanderPoint.X += childMaplet.x.Value; wander.WanderPoint.Y += childMaplet.y.Value; wander.WanderRectangle = new Rectangle(wander.WanderRectangle.X + childMaplet.x.Value, wander.WanderRectangle.Y + childMaplet.y.Value, wander.WanderRectangle.Width, wander.WanderRectangle.Height); } } } //Update any wander areas too foreach (var area in wanderA) { area.WanderRect = new Rectangle(area.WanderRect.X + childMaplet.x.Value, area.WanderRect.Y + childMaplet.y.Value, area.WanderRect.Width, area.WanderRect.Height); area.WanderPoint.X += childMaplet.x.Value; area.WanderPoint.Y += childMaplet.y.Value; } //and patrol points foreach (var point in patrolPoints) { point.Point.X += childMaplet.x.Value; point.Point.Y += childMaplet.y.Value; } foreach (var n in fpNodes) { n.Point.X += childMaplet.x.Value; n.Point.Y += childMaplet.y.Value; } //And add them wanderAreas.AddRange(wanderA); patrolRouteList.AddRange(patrolPoints); footpathNodeList.AddRange(fpNodes); //Join the two maps together generatedMap = this.JoinMaps(generatedMap, childMap, childMaplet.x.Value, childMaplet.y.Value); } } else { if (Fits(planningMap, childMapletBlueprint, childMaplet.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES, childMaplet.FirstFit, childMaplet.Padding, out x, out y, out newMap)) { //it fits, generate it - <3 Recursion Actor[] childActors = null; MapletActorWanderArea[] wanderA = null; MapletPatrolPoint[] patrolPoints = null; MapletFootpathNode[] fpNodes = null; MapBlock[,] childMap = this.GenerateMap(tileID, wallID.Value, childMaplet.Maplet, childMaplet.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES, actorType, owner, out childActors, out wanderA, out patrolPoints, out fpNodes); //Add the child actors actorList.AddRange(childActors); //Update any actors's locations should they have any foreach (var actor in childActors) { if (actor.MissionStack.Count() > 0) { var wander = actor.MissionStack.Peek() as WanderMission; if (wander != null) { wander.WanderPoint.X += x; wander.WanderPoint.Y += y; wander.WanderRectangle = new Rectangle(wander.WanderRectangle.X + x, wander.WanderRectangle.Y + y, wander.WanderRectangle.Width, wander.WanderRectangle.Height); } } } //Update any wander areas too foreach (var area in wanderA) { area.WanderRect = new Rectangle(area.WanderRect.X + x, area.WanderRect.Y + y, area.WanderRect.Width, area.WanderRect.Height); area.WanderPoint.X += x; area.WanderPoint.Y += y; } //and patrol routes foreach (var point in patrolPoints) { point.Point.X += x; point.Point.Y += y; } foreach (var n in fpNodes) { n.Point.X += x; n.Point.Y += y; } //And add them wanderAreas.AddRange(wanderA); patrolRouteList.AddRange(patrolPoints); footpathNodeList.AddRange(fpNodes); //Join the two maps together generatedMap = this.JoinMaps(generatedMap, childMap, x, y); } } } } } //Step 2: Put the items into the map //Lets list places we can put it in List<MapBlock> candidateBlocks = new List<MapBlock>(); for (int x = 0; x < planningMap.GetLength(0); x++) { for (int y = 0; y < planningMap.GetLength(1); y++) { if (planningMap[x, y] == PlanningMapItemType.FREE) { candidateBlocks.Add(generatedMap[x, y]); } } } List<MapBlock> edgeBlocks = new List<MapBlock>(); //Lets also get the edge mapblocks - for those who prefer being on the edge for (int x = 0; x < planningMap.GetLength(0); x++) { if (!maplet.Walled) { if (planningMap[x, 0] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, 0]); } if (planningMap[x, planningMap.GetLength(1) - 1] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, planningMap.GetLength(1) - 1]); } } else { if (planningMap[x, 1] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, 1]); } if (planningMap[x, planningMap.GetLength(1) - 2] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[x, planningMap.GetLength(1) - 2]); } } } //Doing the y parts for (int y = 0; y < planningMap.GetLength(1); y++) { if (!maplet.Walled) { if (planningMap[0, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[0, y]); } if (planningMap[planningMap.GetLength(0) - 1, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[planningMap.GetLength(0) - 1, y]); } } else { if (planningMap[1, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[1, y]); } if (planningMap[planningMap.GetLength(0) - 2, y] == PlanningMapItemType.FREE) { edgeBlocks.Add(generatedMap[planningMap.GetLength(0) - 2, y]); } } } //go through the maplet contents //Get the smallest x and y coordinate in the candidate blocks so we can use it for fixed things int smallestX = -1; int smallestY = -1; try { smallestX = candidateBlocks.Select(b => b.Tile.Coordinate.X).Min(); smallestY = candidateBlocks.Select(b => b.Tile.Coordinate.Y).Min(); } catch { //No space :( } foreach (MapletContents contents in maplet.MapletContents.Where(mc => mc is MapletContentsItem || mc is MapletContentsItemTag || mc is MapletContentsItemSpecial).OrderByDescending(mc => mc.ProbabilityPercentage)) { //We'll see if we even put this at all MapItem itemPlaced = null; for (int i = 0; i < contents.MaxAmount; i++) { //lets see what the probability of putting it in is if ((random.NextDouble() * 100) <= contents.ProbabilityPercentage) { //Put it in if (contents is MapletContentsItem) { MapletContentsItem mapletContent = (MapletContentsItem)contents; itemPlaced = factory.CreateItem(mapletContent.ItemCategory, mapletContent.ItemID); } else if (contents is MapletContentsItemTag) { MapletContentsItemTag mapletContent = (MapletContentsItemTag)contents; int tempInt; itemPlaced = factory.CreateItem(mapletContent.Category, mapletContent.Tag, out tempInt); //I CHANGED THIS itemPlaced.OwnedBy = mapletContent.Factions; } else if (contents is MapletContentsItemSpecial) { //what type is it switch ((contents as MapletContentsItemSpecial).Type) { case "StairsUp": itemPlaced = new DungeonStairs(true); break; case "StairsDown": itemPlaced = new DungeonStairs(false); break; case "SummoningCircle": itemPlaced = new SummoningCircle(); break; case "TreasureChest": itemPlaced = new TreasureChest(); break; case "Altar": itemPlaced = new Altar(); break; case "WishingWell": itemPlaced = new WishingWell(); break; case "Potion": var potionType = (PotionType[]) Enum.GetValues(typeof(PotionType)); var potion = potionType.GetRandom(); itemPlaced = new Potion(potion); break; default: throw new NotImplementedException("No code for " + (contents as MapletContentsItemSpecial).Type + " can be found"); } } if (candidateBlocks.Count != 0) { //Lets decide where to put it if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.SIDES && edgeBlocks.Count != 0) { //pick a place at random and add it to the maplet int position = random.Next(edgeBlocks.Count); edgeBlocks[position].PutItemOnBlock(itemPlaced); if (!contents.AllowItemsOnTop) { //remove it from both candidateBlocks.Remove(edgeBlocks[position]); edgeBlocks.RemoveAt(position); } } if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.MIDDLE && candidateBlocks.Except(edgeBlocks).Count() != 0) { //pick a place at random and add it to the maplet int position = random.Next(candidateBlocks.Except(edgeBlocks).Count()); MapBlock block = candidateBlocks.Except(edgeBlocks).ToArray()[position]; block.PutItemOnBlock(itemPlaced); if (!contents.AllowItemsOnTop) { //remove it from both candidateBlocks.Remove(block); edgeBlocks.Remove(block); } } if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.ANYWHERE) { //pick a place at random and add it to the maplet int position = random.Next(candidateBlocks.Count); candidateBlocks[position].PutItemOnBlock(itemPlaced); if (!contents.AllowItemsOnTop) { //remove it from both edgeBlocks.Remove(candidateBlocks[position]); candidateBlocks.RemoveAt(position); } } if (contents.Position == DRObjects.LocalMapGeneratorObjects.Enums.PositionAffinity.FIXED) { //Fix it in a particular position. MapCoordinate coordinate = new MapCoordinate(smallestX + contents.x.Value, smallestY + contents.y.Value, 0, DRObjects.Enums.MapType.LOCAL); var selectedBlock = candidateBlocks.Where(cb => cb.Tile.Coordinate.Equals(coordinate)).FirstOrDefault(); if (selectedBlock != null) { //maybe someone put something there already selectedBlock.PutItemOnBlock(itemPlaced); } if (!contents.AllowItemsOnTop) { //and remoev it from both candidateBlocks.Remove(selectedBlock); edgeBlocks.Remove(selectedBlock); } } } } } } //Step 3: Stripe through the map except for the current maplet's walls - work out where the walls are, and for each wall segment, put a door in #region Wall Segments List<Line> wallSegments = new List<Line>(); for (int x = 1; x < planningMap.GetLength(0) - 1; x++) { //lets see if we find a wall Segment Line wallSegment = null; for (int y = 1; y < planningMap.GetLength(1) - 1; y++) { if (planningMap[x, y] == PlanningMapItemType.WALL) { //Three possibilities exist. Either this is the start of a wall segment //Or this is a continuation of a wall segment //Or this is the end of a wall segment // -> Because there is an intersection // -> Because there was an active wall segment and there is no wall in this one if (wallSegment == null) { //Its a start wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //Continuation or end //Check if there's an interesection //Go up one and down one. If there is the maplet's walls there won't be a door - but then there'll be a double wall anyway which makes no sense if (planningMap[x + 1, y] == PlanningMapItemType.WALL || planningMap[x - 1, y] == PlanningMapItemType.WALL) { //terminate the wall - and start a new one wallSegment.End = new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //do nothing, its a continuation } } } else { //Mayhaps a line has stopped? if (wallSegment != null) { //It has - lets terminate it wallSegment.End = new MapCoordinate(x, y - 1, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } } //Check if there's an active line - maybe it reaches till the end of the maplet if (wallSegment != null) { wallSegment.End = new MapCoordinate(x, (planningMap.GetLength(1) - 1), 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } //Now stripe in the other direction for (int y = 1; y < planningMap.GetLength(1) - 1; y++) { //lets see if we find a wall Segment Line wallSegment = null; for (int x = 1; x < planningMap.GetLength(0) - 1; x++) { if (planningMap[x, y] == PlanningMapItemType.WALL) { //Three possibilities exist. Either this is the start of a wall segment //Or this is a continuation of a wall segment //Or this is the end of a wall segment // -> Because there is an intersection // -> Because there was an active wall segment and there is no wall in this one if (wallSegment == null) { //Its a start wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //Continuation or end //Check if there's an interesection //Go up one and down one. If there is the maplet's walls there won't be a door - but then there'll be a double wall anyway which makes no sense if (planningMap[x, y + 1] == PlanningMapItemType.WALL || planningMap[x, y - 1] == PlanningMapItemType.WALL) { //terminate the wall - and start a new one wallSegment.End = new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = new Line(new MapCoordinate(x, y, 0, DRObjects.Enums.MapType.LOCAL), null); } else { //do nothing, its a continuation } } } else { //Mayhaps a line has stopped? if (wallSegment != null) { //It has - lets terminate it wallSegment.End = new MapCoordinate(x - 1, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } } //Check if there's an active line - maybe it reaches till the end of the maplet if (wallSegment != null) { wallSegment.End = new MapCoordinate(planningMap.GetLength(0) - 1, y, 0, DRObjects.Enums.MapType.LOCAL); wallSegments.Add(wallSegment); wallSegment = null; } } #endregion Wall Segments #region Doors //Get all wall segments larger than 0, and we can put a door there foreach (Line segment in wallSegments.Where(ws => ws.Length() > 1)) { //Put a door somewhere, as long as its not the start or end //Oh and remove the wall MapBlock block = null; if (segment.Start.X == segment.End.X) { //Get the entirety of the segment List<int> possibleYs = new List<int>(); int smallerY = Math.Min(segment.Start.Y, segment.End.Y); int largerY = Math.Max(segment.Start.Y, segment.End.Y); //Check in the real map whether the tile next to it is free for walking in for (int y = smallerY + 1; y <= largerY; y++) { if (generatedMap[segment.Start.X - 1, y].MayContainItems && generatedMap[segment.Start.X + 1, y].MayContainItems) { possibleYs.Add(y); } } //Now check whether there's a possible y, and pick a random one from it if (possibleYs.Count != 0) { block = generatedMap[segment.Start.X, possibleYs[random.Next(possibleYs.Count - 1)]]; } else { //nothing to do - take smallest block = generatedMap[segment.Start.X, segment.Start.Y + 1]; } } else { List<int> possibleXs = new List<int>(); int smallerX = Math.Min(segment.Start.X, segment.End.X); int largerX = Math.Max(segment.Start.X, segment.End.X); //Check in the real map whether the tile next to it is free for walking in for (int x = smallerX + 1; x <= largerX; x++) { if (generatedMap[x, segment.Start.Y - 1].MayContainItems && generatedMap[x, segment.Start.Y + 1].MayContainItems) { possibleXs.Add(x); } } //Now check whether there's a possible x, and pick a random one from it if (possibleXs.Count != 0) { block = generatedMap[possibleXs[random.Next(possibleXs.Count - 1)], segment.Start.Y]; } else { //nothing to do - take the smallest one block = generatedMap[segment.Start.X + 1, segment.Start.Y]; } } try { if (block != null) { block.RemoveTopItem(); int doorID = -1; block.PutItemOnBlock(factory.CreateItem(DRObjects.Enums.Archetype.MUNDANEITEMS, "door", out doorID)); } } catch { } } #endregion #region Enemies //Now lets create enemies :) foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletActor))).OrderByDescending(o => o.ProbabilityPercentage)) { var actor = mc as MapletActor; for (int i = 0; i < actor.MaxAmount; i++) { //Check the random if (random.Next(100) < actor.ProbabilityPercentage) { int actorID = 0; string enemyType = actor.UseLocalType ? actorType : actor.EnemyType; //For now set gear cost to 0 Actor newActor = ActorGeneration.CreateActor(enemyType, actor.EnemyTag, null, 10, 0, null, out actorID); if (actor.VendorType.HasValue) { GenerateVendor(newActor, actor); } //Generate the map character var mapCharacter = factory.CreateItem("enemies", actorID); newActor.MapCharacter = mapCharacter; if (!actor.Factions.HasFlag(owner)) { //inactive character newActor.MapCharacter.IsActive = false; } (mapCharacter as LocalCharacter).Actor = newActor; //Lets position them randomly for (int attempt = 0; attempt < 150; attempt++) { //Try 150 times int x = random.Next(maplet.SizeX); int y = random.Next(maplet.SizeY); if (generatedMap[x, y].MayContainItems) { //Put it there mapCharacter.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); generatedMap[x, y].ForcePutItemOnBlock(mapCharacter); actorList.Add(newActor); //What mission does he have? if (actor.EnemyMission == ActorMissionType.WANDER) { newActor.MissionStack.Push(new WanderMission() { LoiterPercentage = 80, WanderPoint = new MapCoordinate(mapCharacter.Coordinate), WanderRectangle = new Rectangle(0, 0, generatedMap.GetLength(0), generatedMap.GetLength(1)) }); } break; } } } } } #endregion #region Wander Areas foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletActorWanderArea)))) { var wander = mc as MapletActorWanderArea; //The area of this is going to be the entire maplet (so if we're in a submaplet, they'll wander in there - Awesome no ?) wander.WanderRect = new Rectangle(0, 0, generatedMap.GetLength(0), generatedMap.GetLength(1)); //Pick the wander point to be the middle of the rectangle. If the point isn't valid we might have a problem wander.WanderPoint = new MapCoordinate(generatedMap.GetLength(0) / 2, generatedMap.GetLength(1) / 2, 0, MapType.LOCAL); MapletActorWanderArea clone = new MapletActorWanderArea(); clone.WanderPoint = new MapCoordinate(wander.WanderPoint); clone.WanderRect = new Rectangle(wander.WanderRect.X, wander.WanderRect.Y, wander.WanderRect.Width, wander.WanderRect.Height); clone.Profession = wander.Profession; clone.OwnerFactions = wander.OwnerFactions; wanderAreas.Add(clone); } wAreas = wanderAreas.ToArray(); #endregion #region Patrol Points & Paths foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletPatrolPoint)))) { var point = mc as MapletPatrolPoint; //The point is going to be in the middle of the entire maplet point.Point = new MapCoordinate(generatedMap.GetLength(0) / 2, generatedMap.GetLength(1) / 2, 0, MapType.LOCAL); MapletPatrolPoint clone = new MapletPatrolPoint(); clone.Point = new MapCoordinate(point.Point); clone.OwnerFactions = point.OwnerFactions; clone.Profession = point.Profession; clone.PointRadius = point.PointRadius; patrolRouteList.Add(clone); } patrolRoutes = patrolRouteList.ToArray(); foreach (var n in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletFootpathNode)))) { var node = n as MapletFootpathNode; //Point is going to be in the middle of the entire maplet node.Point = new MapCoordinate(generatedMap.GetLength(0) / 2, generatedMap.GetLength(1) / 2, 0, MapType.LOCAL); //Better create a new one MapletFootpathNode clone = new MapletFootpathNode(); clone.Point = new MapCoordinate(node.Point); clone.IsPrimary = node.IsPrimary; footpathNodeList.Add(clone); } footpathNodes = footpathNodeList.ToArray(); #endregion #region Aniamls //Now lets create enemies :) foreach (var mc in maplet.MapletContents.Where(mc => mc.GetType().Equals(typeof(MapletHerd))).OrderByDescending(o => o.ProbabilityPercentage)) { var herd = mc as MapletHerd; for (int i = 0; i < herd.MaxAmount; i++) { //Check the random if (random.Next(100) < herd.ProbabilityPercentage) { var herds = ActorGeneration.CreateAnimalHerds(herd.Biome, herd.Domesticated, herd.HerdTag, 1); foreach (var animalHerd in herds) { foreach (var animal in animalHerd) { //Position them on the map for (int attempt = 0; attempt < 150; attempt++) { //Try 150 times int x = random.Next(maplet.SizeX); int y = random.Next(maplet.SizeY); if (generatedMap[x, y].MayContainItems) { //Put it there animal.MapCharacter.Coordinate = new MapCoordinate(x, y, 0, MapType.LOCAL); generatedMap[x, y].ForcePutItemOnBlock(animal.MapCharacter); actorList.Add(animal); //Wander around does he have? animal.MissionStack.Push(new WanderMission() { LoiterPercentage = 80, WanderPoint = new MapCoordinate(animal.MapCharacter.Coordinate), WanderRectangle = new Rectangle(0, 0, generatedMap.GetLength(0), generatedMap.GetLength(1)) }); break; } } } } } } } #endregion actors = actorList.ToArray(); #region Ownership //Go through all map items - If they're not valid for this particular owner, make them inactive. foreach (var mapBlock in generatedMap) { foreach (var item in mapBlock.GetItems()) { if (!item.OwnedBy.HasFlag(owner)) { item.IsActive = false; } } } #endregion //we're done return generatedMap; }
public ActionFeedback[] PerformAction(ActionType actionType, Actor actor, object[] args) { if (actionType == ActionType.MOVE) { //the block must handle this //TODO: Future - the block can transmit 'moved on' action on the top item or the tile for things like traps if (this.MayContainItems) { //it is possible to move there //you can only move to a particular block if its only 1 square away int distance = this.Tile.Coordinate - actor.MapCharacter.Coordinate; if (distance > 1) { return new ActionFeedback[] { new TextFeedback("Can't move there") }; } actor.MapCharacter.Coordinate = this.Tile.Coordinate; this.mapItems.Add(actor.MapCharacter); //Are we moving the player character on the world map? if (actor.IsPlayerCharacter && this.Tile.GetType() == typeof(GlobalTile)) { //So, how about a random encounter? Random random = new Random(); int randomValue = random.Next(100); List<ActionFeedback> feedback = new List<ActionFeedback>(); //1% if (randomValue > 99) { //Are we next to a bandit camp ? feedback.Add(new LocationChangeFeedback() { RandomEncounter = (this.Tile as GlobalTile).Biome }); } //Change the location of the character actor.GlobalCoordinates = new MapCoordinate(this.Tile.Coordinate); actor.GlobalCoordinates.MapType = MapType.GLOBAL; //Make some time pass feedback.Add(new TimePassFeedback() { TimePassInMinutes = (this.Tile as GlobalTile).TraverseTimeInMinutes(actor) }); return feedback.ToArray(); } if (actor.IsPlayerCharacter) { //Mark all tiles around him as having been visited return new ActionFeedback[1] { new VisitedBlockFeedback { Coordinate = this.Tile.Coordinate } }; } return new ActionFeedback[0]; } else { //Check whether it's the player character trying to move on a tile upon which there is an enemy if (actor.IsPlayerCharacter) { if (this.GetTopItem().GetType() == typeof(LocalCharacter)) { //Attack him instead - randomly LocalCharacter lc = (LocalCharacter)this.GetTopItem(); return new ActionFeedback[] { new AttackFeedback() { Attacker = actor, Defender = lc.Actor } }; } } //not possible return new ActionFeedback[] { new TextFeedback("Not possible to move there") }; } } else if (actionType == ActionType.THROW) { //Create and open the 'throw item' feedback return new ActionFeedback[] { new OpenInterfaceFeedback(new ThrowItemInterface() { Coordinate = this.Tile.Coordinate }) }; } MapItem item = GetTopItem(); if (item == null) { return new ActionFeedback[0] { }; } return item.PerformAction(actionType, actor, args); }
/// <summary> /// Heal this character if they require it /// You heal one point each time, +1 for every 5 levels in Healing /// Healing happens top down /// </summary> /// <param name="actor"></param> /// <param name="rounds">The amount of rounds we're going to do this for.</param> public static void HealCharacter(Actor actor, int rounds) { //Do we need healing? if (actor.Anatomy.Head > -5 && actor.Anatomy.Head == actor.Anatomy.HeadMax && actor.Anatomy.Chest > -5 && actor.Anatomy.Chest == actor.Anatomy.ChestMax && actor.Anatomy.Legs > -5 && actor.Anatomy.Legs == actor.Anatomy.LegsMax && actor.Anatomy.LeftArm > -5 && actor.Anatomy.LeftArm == actor.Anatomy.LeftArmMax && actor.Anatomy.RightArm > -5 && actor.Anatomy.RightArm == actor.Anatomy.RightArmMax) { //Nope, we're fine return; } //Let's determine how much healing we're able to do int healing = 1*rounds; int skill = 0; if (actor.Attributes.Skills.ContainsKey(SkillName.HEALER)) { skill = (int) actor.Attributes.Skills[SkillName.HEALER].SkillLevel; } healing = (1 + (skill / 5)) * rounds; //Go through each body part and incremement as needed int headHealing = actor.Anatomy.Head > -5 ? actor.Anatomy.HeadMax - actor.Anatomy.Head : 0; int chestHealing = actor.Anatomy.Chest > -5 ? actor.Anatomy.ChestMax - actor.Anatomy.Chest : 0; int leftHealing = actor.Anatomy.LeftArm > -5 ? actor.Anatomy.LeftArmMax - actor.Anatomy.LeftArm : 0; int rightHealing = actor.Anatomy.RightArm > -5 ? actor.Anatomy.RightArmMax - actor.Anatomy.RightArm : 0; int legHealing = actor.Anatomy.Legs > -5 ? actor.Anatomy.LegsMax - actor.Anatomy.Legs : 0; if (headHealing > 0) { //Heal the head if (headHealing <= healing) { actor.Anatomy.Head += headHealing; healing -= headHealing; } else { actor.Anatomy.Head += healing; healing = 0; } } if (chestHealing > 0 && healing > 0) { if (chestHealing <= healing) { actor.Anatomy.Chest += chestHealing; healing -= chestHealing; } else { actor.Anatomy.Chest += healing; healing = 0; } } if (leftHealing > 0 && healing > 0) { if (leftHealing<= healing) { actor.Anatomy.LeftArm+= leftHealing; healing -= leftHealing; } else { actor.Anatomy.LeftArm += healing; healing = 0; } } if (rightHealing > 0 && healing > 0) { if (rightHealing <= healing) { actor.Anatomy.RightArm+= rightHealing; healing -= rightHealing; } else { actor.Anatomy.RightArm += healing; healing = 0; } } if (legHealing > 0 && healing > 0) { if (legHealing <= healing) { actor.Anatomy.Legs+= legHealing; healing -= legHealing; } else { actor.Anatomy.Legs += healing; healing = 0; } } //And increase some skill! for (int i = 0; i < rounds; i++) { actor.Attributes.IncreaseSkill(SkillName.HEALER); } }