public Damage(DamageType type_, DamageClass damclass_, Actor source_, int totaldamage) { dice = 0; num = totaldamage; type = type_; damclass = damclass_; source = source_; }
public Damage(int dice_, DamageType type_, DamageClass damclass_, Actor source_) { dice = dice_; num = null; type = type_; damclass = damclass_; source = source_; }
public Damage(DamageType type_,DamageClass damclass_,bool major,Actor source_,int totaldamage) { dice=0; num=totaldamage; type=type_; damclass=damclass_; major_damage = major; source=source_; weapon_used = WeaponType.NO_WEAPON; }
public Damage(int dice_,DamageType type_,DamageClass damclass_,bool major,Actor source_) { dice=dice_; num = null; type=type_; damclass=damclass_; major_damage = major; source=source_; weapon_used = WeaponType.NO_WEAPON; }
public Buffer(Game g) { max_length = Global.COLS; //because the message window runs along the top of the map str.Add(""); overflow = ""; log = new string[log_length]; for(int i=0;i<log_length;++i){ log[i] = ""; } position = 21; num_messages = position; M = g.M; player = g.player; }
public Buffer(Game g) { max_length = Global.COLS; //because the message window runs along the top of the map //str = ""; //str2 = ""; str.Add(""); overflow = ""; log = new string[20]; for (int i = 0; i < 20; ++i) { log[i] = ""; } position = 0; M = g.M; player = g.player; }
private ItemUseResult UseWand(bool targets_enemies,bool visible_line,Actor user,List<Tile> line,WandTargetingDelegate wand_effect) { ItemUseResult result = new ItemUseResult(true,true); if(charges == 0){ B.Add(TheName(true) + " is empty. Nothing happens. ",user); other_data = -1; result.IDed = false; return result; } TargetInfo info = null; if(user != player && line != null){ info = new TargetInfo(); info.extended_line = line; info.targeted = player.tile(); } if(!identified[type]){ targets_enemies = true; visible_line = false; } if(info == null){ info = user.GetTarget(false,12,0,!visible_line,false,targets_enemies,""); } if(info != null){ if(user != null && !user.HasLOE(info.targeted)){ foreach(Tile t in info.extended_line){ if(!t.passable){ info.targeted = t; break; } } } if(user == null || user.HasLOE(info.targeted)){ Tile LOE_tile = null; if(user != null){ LOE_tile = user.tile(); } else{ LOE_tile = info.extended_line[0]; } Tile prev = null; foreach(Tile t in info.extended_line){ if(!t.passable){ if(prev != null){ LOE_tile = prev; } break; } else{ if(t == info.targeted){ LOE_tile = t; break; } } prev = t; } wand_effect(LOE_tile,info,result); } } else{ result.used = false; } return result; }
private ItemUseResult UseOrb(int radius,bool never_target_enemies,Actor user,List<Tile> line,OrbTargetingDelegate orb_effect) { ItemUseResult result = new ItemUseResult(true,true); if(line == null){ if(!identified[type]){ radius = 0; } line = user.GetTargetTile(12,radius,false,!(never_target_enemies && identified[type])); } if(line != null){ Tile t = line.LastOrDefault(); Tile prev = line.LastBeforeSolidTile(); Actor first = null; bool trigger_trap = true; Screen.CursorVisible = false; if(user != null){ first = user.FirstActorInLine(line); B.Add(user.You("fling") + " the " + SingularName() + ". ",user); if(first != null && first != user){ trigger_trap = false; t = first.tile(); if(player.CanSee(user)){ B.Add("It shatters on " + first.the_name + "! ",first); } else{ B.Add("Something shatters on " + first.the_name + "! ",first); } first.player_visibility_duration = -1; first.attrs[AttrType.PLAYER_NOTICED]++; } else{ if(player.CanSee(user)){ B.Add("It shatters on " + t.the_name + "! ",t); } else{ B.Add("Something shatters on " + t.the_name + "! ",t); } } user.AnimateProjectile(line.ToFirstSolidTileOrActor(),'*',color); Screen.CursorVisible = false; } else{ trigger_trap = false; } Tile LOE_tile = t; if(!t.passable && prev != null){ LOE_tile = prev; } orb_effect(t,LOE_tile,result); t.MakeNoise(2); if(trigger_trap && t.IsTrap()){ t.TriggerTrap(); } if(!revealed_by_light){ result.IDed = false; } } else{ result.used = false; } return result; }
public async Task<bool> Attack(int attack_idx, Actor a) { //returns true if attack hit if (await StunnedThisTurn()) { return false; } //pos pos_of_target = new pos(a.row,a.col); AttackInfo info = AttackList.Attack(atype, attack_idx); if (weapons[0] != WeaponType.NO_WEAPON) { info.damage = Weapon.Damage(weapons[0]); } info.damage.source = this; int plus_to_hit = TotalSkill(SkillType.COMBAT); bool sneak_attack = false; if (this.IsHiddenFrom(a) || !a.CanSee(this) || (this == player && HasAttr(AttrType.SHADOW_CLOAK) && !tile().IsLit() && !a.HasAttr(AttrType.BLINDSIGHT))) { sneak_attack = true; } if (sneak_attack) { //sneak attacks get +25% accuracy. this usually totals 100% vs. unarmored targets. plus_to_hit += 25; } if (HasAttr(AttrType.BLESSED)) { plus_to_hit += 10; } plus_to_hit -= a.ArmorClass() * 2; bool hit = a.IsHit(plus_to_hit); if (HasFeat(FeatType.DRIVE_BACK)) { bool nowhere_to_run = true; int dir = DirectionOf(a); if (a.TileInDirection(dir).passable && a.ActorInDirection(dir) == null) { nowhere_to_run = false; } if (a.TileInDirection(RotateDirection(dir, true)).passable && a.ActorInDirection(RotateDirection(dir, true)) == null) { nowhere_to_run = false; } if (a.TileInDirection(RotateDirection(dir, false)).passable && a.ActorInDirection(RotateDirection(dir, false)) == null) { nowhere_to_run = false; } if (a.HasAttr(AttrType.FROZEN) || a.HasAttr(AttrType.NEVER_MOVES)) { nowhere_to_run = true; } if (nowhere_to_run) { hit = true; } } bool no_armor_message = false; //no_armor_message means "don't print 'your armor blocks the attack' for misses" if (a.HasAttr(AttrType.DEFENSIVE_STANCE) && Global.CoinFlip()) { hit = false; no_armor_message = true; } if ((this.tile().Is(FeatureType.FOG) || a.tile().Is(FeatureType.FOG)) && Global.CoinFlip()) { hit = false; no_armor_message = true; } if (a.IsHiddenFrom(this) || !CanSee(a) || (a == player && a.HasAttr(AttrType.SHADOW_CLOAK) && !a.tile().IsLit() && !HasAttr(AttrType.BLINDSIGHT))) { if (Global.CoinFlip()) { hit = false; no_armor_message = true; } } bool player_in_combat = false; if (this == player || a == player) { player_in_combat = true; } if (attack_idx == 2 && (atype == ActorType.FROSTLING || atype == ActorType.FIRE_DRAKE)) { hit = true; //hack! these are the 2 'area' attacks that always hit player_in_combat = false; } if (a == player && atype == ActorType.DREAM_CLONE) { player_in_combat = false; } if (player_in_combat) { player.attrs[Forays.AttrType.IN_COMBAT]++; } string s = info.desc + ". "; if (hit) { if (HasFeat(FeatType.NECK_SNAP) && a.HasAttr(AttrType.MEDIUM_HUMANOID) && IsHiddenFrom(a)) { if (!HasAttr(AttrType.RESIST_NECK_SNAP)) { B.Add(You("silently snap") + " " + a.Your() + " neck. "); await a.TakeDamage(DamageType.NORMAL, DamageClass.NO_TYPE, 9001, this); Q1(); return true; } else { B.Add(You("silently snap") + " " + a.Your() + " neck. "); B.Add("It doesn't seem to affect " + a.the_name + ". "); } } int dice = info.damage.dice; bool crit = false; int pos = s.IndexOf("&"); if (pos != -1) { s = s.Substring(0, pos) + TheVisible() + s.Substring(pos + 1); } pos = s.IndexOf("^"); if (pos != -1) { string sc = ""; int critical_target = 20; if (weapons[0] == WeaponType.DAGGER) { critical_target -= 2; } if (HasFeat(FeatType.LETHALITY)) { //10% crit plus 5% for each 20% health the target is missing critical_target -= 2; int fifth = a.maxhp / 5; //uses int because it assumes everything has a multiple of 5hp int totaldamage = a.maxhp - a.curhp; if (fifth > 0) { int missing_fifths = totaldamage / fifth; critical_target -= missing_fifths; } } if ((info.damage.type == DamageType.NORMAL || info.damage.type == DamageType.PIERCING || info.damage.type == DamageType.BASHING || info.damage.type == DamageType.SLASHING) && Global.Roll(1, 20) >= critical_target) { //maybe this should become a check for physical damage - todo? crit = true; sc = "critically "; } s = s.Substring(0, pos) + sc + s.Substring(pos + 1); } pos = s.IndexOf("*"); if (pos != -1) { s = s.Substring(0, pos) + a.TheVisible() + s.Substring(pos + 1); } if (sneak_attack && crit) { if (!a.HasAttr(AttrType.UNDEAD) && !a.HasAttr(AttrType.CONSTRUCT) && !a.HasAttr(AttrType.PLANTLIKE) && !a.HasAttr(AttrType.BOSS_MONSTER)) { if (a.atype != ActorType.PLAYER) { //being nice to the player here... switch (weapons[0]) { case WeaponType.SWORD: case WeaponType.FLAMEBRAND: B.Add("You run " + a.TheVisible() + " through! "); break; case WeaponType.MACE: case WeaponType.MACE_OF_FORCE: B.Add("You bash " + a.YourVisible() + " head in! "); break; case WeaponType.DAGGER: case WeaponType.VENOMOUS_DAGGER: B.Add("You pierce one of " + a.YourVisible() + " vital organs! "); break; case WeaponType.STAFF: case WeaponType.STAFF_OF_MAGIC: B.Add("You bring your staff down on " + a.YourVisible() + " head with a loud crack! "); break; case WeaponType.BOW: case WeaponType.HOLY_LONGBOW: B.Add("You choke " + a.TheVisible() + " with your bowstring! "); break; default: break; } MakeNoise(); await a.TakeDamage(DamageType.NORMAL, DamageClass.NO_TYPE, 1337, this); Q1(); return true; } else { //...but not too nice B.Add(AVisible() + " strikes from hiding! "); B.Add("The deadly attack leaves you stunned! "); int lotsofdamage = Math.Max(dice * 6, a.curhp / 2); a.attrs[AttrType.STUNNED]++; Q.Add(new Event(a, Global.Roll(2, 5) * 100, AttrType.STUNNED, "You are no longer stunned. ")); await a.TakeDamage(DamageType.NORMAL, DamageClass.PHYSICAL, lotsofdamage, this, a_name); } } } if (sneak_attack) { B.Add(YouVisible("strike") + " from hiding! "); if (atype != ActorType.PLAYER) { attrs[AttrType.TURNS_VISIBLE] = -1; attrs[Forays.AttrType.NOTICED]++; } else { a.player_visibility_duration = -1; a.attrs[Forays.AttrType.PLAYER_NOTICED]++; } } B.Add(s, this, a); int dmg; if (crit) { dmg = dice * 6; } else { dmg = Global.Roll(dice, 6); } dmg += TotalSkill(SkillType.COMBAT); int r = a.row; int c = a.col; bool troll = (a.atype == ActorType.TROLL || a.atype == ActorType.TROLL_SEER); bool mech_shield = a.HasAttr(AttrType.MECHANICAL_SHIELD); if (crit && mech_shield) { a.attrs[Forays.AttrType.MECHANICAL_SHIELD] = 0; } await a.TakeDamage(info.damage.type, info.damage.damclass, dmg, this, a_name); if (crit && mech_shield) { a.attrs[Forays.AttrType.MECHANICAL_SHIELD]++; } if (M.actor[r, c] != null) { if (HasAttr(AttrType.FIRE_HIT) || attrs[AttrType.ON_FIRE] >= 3) { //todo: a frostling's ranged attack shouldn't apply this if (!a.HasAttr(AttrType.INVULNERABLE)) { //to prevent the message int amount = Global.Roll(6); if (!a.HasAttr(AttrType.RESIST_FIRE) || amount / a.attrs[AttrType.RESIST_FIRE] > 0) { //todo i think resistance is wrong here B.Add(a.YouAre() + " burned. ", a); } await a.TakeDamage(DamageType.FIRE, DamageClass.PHYSICAL, amount, this, a_name); } } } if (troll && HasAttr(AttrType.FIRE_HIT) && M.tile[r, c].Is(FeatureType.TROLL_CORPSE)) { M.tile[r, c].features.Remove(FeatureType.TROLL_CORPSE); B.Add("The troll corpse burns to ashes! ", M.tile[r, c]); } if (troll && HasAttr(AttrType.FIRE_HIT) && M.tile[r, c].Is(FeatureType.TROLL_SEER_CORPSE)) { M.tile[r, c].features.Remove(FeatureType.TROLL_SEER_CORPSE); B.Add("The troll seer corpse burns to ashes! ", M.tile[r, c]); } if (HasAttr(AttrType.COLD_HIT) && attack_idx == 0 && M.actor[r, c] != null) { //hack: only applies to attack 0 if (!a.HasAttr(AttrType.INVULNERABLE)) { //to prevent the message B.Add(a.YouAre() + " chilled. ", a); await a.TakeDamage(DamageType.COLD, DamageClass.PHYSICAL, Global.Roll(1, 6), this, a_name); } } if (HasAttr(AttrType.POISON_HIT) && M.actor[r, c] != null) { if (!a.HasAttr(AttrType.UNDEAD) && !a.HasAttr(AttrType.CONSTRUCT) && !a.HasAttr(AttrType.POISON_HIT) && !a.HasAttr(AttrType.IMMUNE_TOXINS)) { if (a.HasAttr(AttrType.POISONED)) { B.Add(a.YouAre() + " more poisoned. ", a); } else { B.Add(a.YouAre() + " poisoned. ", a); } a.attrs[AttrType.POISONED]++; Q.Add(new Event(a, (Global.Roll(6) + 6) * 100, AttrType.POISONED)); } } if (HasAttr(AttrType.PARALYSIS_HIT) && attack_idx == 1 && atype == ActorType.CARRION_CRAWLER && M.actor[r, c] != null) { if (!a.HasAttr(AttrType.IMMUNE_TOXINS)) { //hack: carrion crawler only B.Add(a.YouAre() + " paralyzed. ", a); a.attrs[AttrType.PARALYZED] = Global.Roll(1, 3) + 3; } } if (HasAttr(AttrType.FORCE_HIT) && M.actor[r, c] != null) { if (Global.OneIn(3)) { if (Global.CoinFlip()) { await a.GetKnockedBack(this); } else { if (!a.HasAttr(AttrType.STUNNED)) { B.Add(a.YouAre() + " stunned. ", a); a.attrs[AttrType.STUNNED]++; int duration = (Global.Roll(4) + 3) * 100; if (crit) { duration += 250; crit = false; //note this - don't try to use crit again after this on-hit stuff. } Q.Add(new Event(a, duration, AttrType.STUNNED, a.YouAre() + " no longer stunned. ", new PhysicalObject[] { a })); } } } } if (HasAttr(AttrType.DIM_VISION_HIT) && M.actor[r, c] != null) { string str = ""; if (a.atype == ActorType.PLAYER) { B.Add("Your vision grows weak. "); str = "Your vision returns to normal. "; } //a.attrs[AttrType.DIM_VISION]++; //Q.Add(new Event(a,a.DurationOfMagicalEffect(Global.Roll(2,20)+20)*100,AttrType.DIM_VISION,str)); a.GainAttrRefreshDuration(AttrType.DIM_VISION, a.DurationOfMagicalEffect(Global.Roll(2, 20) + 20) * 100, str); } if (HasAttr(AttrType.STALAGMITE_HIT)) { List<Tile> tiles = new List<Tile>(); foreach (Tile t in M.tile[r, c].TilesWithinDistance(1)) { if (t.actor() == null && (t.ttype == TileType.FLOOR || t.ttype == TileType.STALAGMITE)) { if (Global.CoinFlip()) { //50% for each... tiles.Add(t); } } } foreach (Tile t in tiles) { if (t.ttype == TileType.STALAGMITE) { Q.KillEvents(t, EventType.STALAGMITE); } else { t.Toggle(this, TileType.STALAGMITE); } } Q.Add(new Event(tiles, 150, EventType.STALAGMITE)); } if (HasAttr(AttrType.GRAB_HIT) && M.actor[r, c] != null && !HasAttr(AttrType.GRABBING) && DistanceFrom(a) == 1) { a.attrs[Forays.AttrType.GRABBED]++; attrs[Forays.AttrType.GRABBING] = DirectionOf(a); B.Add(the_name + " grabs " + a.the_name + ". ", this, a); } if (HasAttr(AttrType.LIFE_DRAIN_HIT) && curhp < maxhp) { curhp += 10; if (curhp > maxhp) { curhp = maxhp; } B.Add(YouFeel() + " restored. ", this); } if (HasAttr(AttrType.STUN_HIT) && M.actor[r, c] != null) { B.Add(a.YouAre() + " stunned. ", a); int duration = 550; if (crit) { duration += 250; crit = false; } a.GainAttrRefreshDuration(AttrType.STUNNED, duration, a.YouAre() + " no longer stunned. ", a); } if (crit && M.actor[r, c] != null) { B.Add(a.YouAre() + " stunned. ", a); a.GainAttrRefreshDuration(AttrType.STUNNED, 250, a.YouAre() + " no longer stunned. ", a); } if (M.actor[r, c] != null && a.atype == ActorType.SWORDSMAN) { if (a.attrs[AttrType.BONUS_COMBAT] > 0) { B.Add(a.the_name + " returns to a defensive stance. ", a); a.attrs[AttrType.BONUS_COMBAT] = 0; } a.attrs[AttrType.COOLDOWN_1]++; Q.Add(new Event(a, 100, AttrType.COOLDOWN_1)); } } else { if (a.HasAttr(AttrType.DEFENSIVE_STANCE) || (a.HasFeat(FeatType.FULL_DEFENSE) && Global.CoinFlip())) { //make an attack against a random enemy next to a List<Actor> list = a.ActorsWithinDistance(1, true); list.Remove(this); //don't consider yourself or the original target if (list.Count > 0) { B.Add(a.You("deflect") + " the attack. ", this, a); return await Attack(attack_idx, list[Global.Roll(1, list.Count) - 1]); } //this would currently enter an infinite loop if two adjacent things used it at the same time } if (this == player || a == player || player.CanSee(this) || player.CanSee(a)) { //didn't change this yet if (s == "& lunges forward and ^hits *. ") { B.Add(the_name + " lunges forward and misses " + a.the_name + ". "); } else { if (s == "& hits * with a blast of cold. ") { B.Add(the_name + " nearly hits " + a.the_name + " with a blast of cold. "); } else { if (s.Length >= 20 && s.Substring(0, 20) == "& extends a tentacle") { B.Add(the_name + " misses " + a.the_name + " with a tentacle. "); } else { if (HasFeat(FeatType.DRIVE_BACK)) { B.Add(You("drive") + " " + a.TheVisible() + " back. "); } else { if (a.ArmorClass() > 0 && !no_armor_message) { if (a.atype != ActorType.PLAYER) { B.Add(a.YourVisible() + " armor blocks " + YourVisible() + " attack. "); } else { int miss_chance = 25 - plus_to_hit; if (Global.Roll(miss_chance) <= Armor.Protection(a.armors[0]) * 2) { B.Add(a.YourVisible() + " armor blocks " + YourVisible() + " attack. "); } else { B.Add(YouVisible("miss", true) + " " + a.TheVisible() + ". "); } } } else { B.Add(YouVisible("miss", true) + " " + a.TheVisible() + ". "); } } } } } } if (HasFeat(FeatType.DRIVE_BACK)) { if (!a.HasAttr(AttrType.FROZEN) && !HasAttr(AttrType.FROZEN)) { await a.AI_Step(this, true); await AI_Step(a); } } if (a.atype == ActorType.SWORDSMAN) { if (a.attrs[AttrType.BONUS_COMBAT] > 0) { B.Add(a.the_name + " returns to a defensive stance. ", a); a.attrs[AttrType.BONUS_COMBAT] = 0; } a.attrs[AttrType.COOLDOWN_1]++; Q.Add(new Event(a, 100, AttrType.COOLDOWN_1)); } } MakeNoise(); Q.Add(new Event(this, info.cost)); return hit; }
public static bool Telekinesis(bool cast,Actor user,Tile t) { bool wand = !cast; if(t == null){ return false; } if(t != null){ if(wand && user == player && !Item.identified[ConsumableType.TELEKINESIS]){ Item.identified[ConsumableType.TELEKINESIS] = true; B.Add("(It was a wand of telekinesis!) "); B.PrintAll(); } List<Tile> ai_line = null; if(user != player && t == player.tile()){ var fires = user.TilesWithinDistance(12).Where(x=>x.passable && x.actor() == null && x.Is(FeatureType.FIRE) && user.CanSee(x) && player.HasBresenhamLineWithCondition(x,false,y=>y.passable && y.actor() == null)); if(fires.Count > 0){ ai_line = player.GetBestExtendedLineOfEffect(fires.Random()); } else{ if(wand){ ai_line = player.GetBestExtendedLineOfEffect(user); } else{ ai_line = player.GetBestExtendedLineOfEffect(player.TileInDirection(Global.RandomDirection())); } } } Actor a = t.actor(); if(a == null && t.inv == null && !t.Is(FeatureType.GRENADE) && t.Is(FeatureType.TROLL_CORPSE,FeatureType.TROLL_BLOODWITCH_CORPSE)){ ActorType troll_type = t.Is(FeatureType.TROLL_CORPSE)? ActorType.TROLL : ActorType.TROLL_BLOODWITCH; FeatureType troll_corpse = t.Is(FeatureType.TROLL_CORPSE)? FeatureType.TROLL_CORPSE : FeatureType.TROLL_BLOODWITCH_CORPSE; Event troll_event = Q.FindTargetedEvent(t,EventType.REGENERATING_FROM_DEATH); troll_event.dead = true; Actor troll = Actor.Create(troll_type,t.row,t.col); foreach(Event e in Q.list){ if(e.target == troll && e.type == EventType.MOVE){ e.tiebreaker = troll_event.tiebreaker; e.dead = true; break; } } Actor.tiebreakers[troll_event.tiebreaker] = troll; troll.symbol = '%'; troll.attrs[AttrType.CORPSE] = 1; troll.SetName(troll.name + "'s corpse"); troll.curhp = troll_event.value; troll.attrs[AttrType.PERMANENT_DAMAGE] = troll_event.secondary_value; troll.attrs[AttrType.NO_ITEM]++; t.features.Remove(troll_corpse); a = troll; } if(a != null){ string msg = "Throw " + a.TheName(true) + " in which direction? "; if(a == player){ msg = "Throw yourself in which direction? "; } List<Tile> line = null; if(user == player){ TargetInfo info = a.GetTarget(false,12,0,false,true,false,msg); if(info != null){ line = info.extended_line; } } else{ line = ai_line; } if(line != null){ if(line.Count == 1 && line[0].actor() == user){ if(wand){ return true; } return false; } if(cast){ B.Add(user.You("cast") + " telekinesis. ",user); if(a.type == ActorType.ALASI_BATTLEMAGE && !a.HasSpell(SpellType.TELEKINESIS)){ a.curmp += Spell.Tier(SpellType.TELEKINESIS); if(a.curmp > a.maxmp){ a.curmp = a.maxmp; } a.GainSpell(SpellType.TELEKINESIS); B.Add("Runes on " + a.Your() + " armor align themselves with the spell. ",a); } } if(a == user && a == player){ B.Add("You throw yourself forward. "); } else{ if(line.Count == 1){ B.Add(user.YouVisible("throw") + " " + a.TheName(true) + " into the ceiling. ",user,a); } else{ B.Add(user.YouVisible("throw") + " " + a.TheName(true) + ". ",user,a); } } B.DisplayNow(); user.attrs[AttrType.SELF_TK_NO_DAMAGE] = 1; a.attrs[AttrType.TELEKINETICALLY_THROWN] = 1; a.attrs[AttrType.TURN_INTO_CORPSE]++; if(line.Count == 1){ a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(3,6),user,"colliding with the ceiling"); a.CollideWith(a.tile()); } else{ a.tile().KnockObjectBack(a,line,12,user); } a.attrs[AttrType.TELEKINETICALLY_THROWN] = 0; user.attrs[AttrType.SELF_TK_NO_DAMAGE] = 0; if(a.curhp <= 0 && a.HasAttr(AttrType.REGENERATES_FROM_DEATH)){ a.attrs[AttrType.TURN_INTO_CORPSE] = 0; a.attrs[AttrType.CORPSE] = 0; a.attrs[AttrType.FROZEN] = 0; a.attrs[AttrType.INVULNERABLE] = 0; a.attrs[AttrType.SHIELDED] = 0; a.attrs[AttrType.BLOCKING] = 0; a.curhp = 1; //this is all pretty hacky - perhaps I should relocate the regenerating corpse through other means. a.TakeDamage(DamageType.NORMAL,DamageClass.NO_TYPE,500,null); } else{ a.CorpseCleanup(); } } else{ if(wand){ return true; } return false; } } else{ bool blast_fungus = false; if(t.type == TileType.BLAST_FUNGUS && !t.Is(FeatureType.GRENADE,FeatureType.WEB,FeatureType.FORASECT_EGG,FeatureType.BONES)){ blast_fungus = true; } if(t.inv != null || blast_fungus){ Item i = t.inv; string itemname = ""; if(blast_fungus){ itemname = "the blast fungus"; } else{ itemname = i.TheName(true); if(i.quantity > 1){ itemname = "the " + i.SingularName(); } } string msg = "Throw " + itemname + " in which direction? "; List<Tile> line = null; if(user == player){ TargetInfo info = t.GetTarget(false,12,0,false,true,false,msg); if(info != null){ line = info.extended_line; } } else{ line = ai_line; } if(line != null){ if(line.Count > 13){ line = line.ToCount(13); //for range 12 } if(cast){ B.Add(user.You("cast") + " telekinesis. ",user); } if(blast_fungus){ B.Add("The blast fungus is pulled from the floor. ",t); B.Add("Its fuse ignites! ",t); t.Toggle(null); i = Item.Create(ConsumableType.BLAST_FUNGUS,t.row,t.col); if(i != null){ i.other_data = 3; i.revealed_by_light = true; Q.Add(new Event(i,100,EventType.BLAST_FUNGUS)); Screen.AnimateMapCell(t.row,t.col,new colorchar('3',Color.Red),100); } } if(line.Count == 1){ B.Add(user.YouVisible("throw") + " " + itemname + " into the ceiling. ",user,t); } else{ B.Add(user.YouVisible("throw") + " " + itemname + ". ",user,t); } B.DisplayNow(); if(i.quantity > 1){ i.quantity--; bool revealed = i.revealed_by_light; i = new Item(i,-1,-1); i.revealed_by_light = revealed; } else{ t.inv = null; Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); } bool trigger_traps = false; Tile t2 = line.LastBeforeSolidTile(); Actor first = user.FirstActorInLine(line); if(t2 == line.LastOrDefault() && first == null){ trigger_traps = true; } if(first != null){ t2 = first.tile(); } line = line.ToFirstSolidTileOrActor(); //if(line.Count > 0){ // line.RemoveAt(line.Count - 1); //} if(line.Count > 0){ line.RemoveAt(line.Count - 1); } { Tile first_unseen = null; foreach(Tile tile2 in line){ if(!tile2.seen){ first_unseen = tile2; break; } } if(first_unseen != null){ line = line.To(first_unseen); if(line.Count > 0){ line.RemoveAt(line.Count - 1); } } } if(line.Count > 0){ //or > 1 ? user.AnimateProjectile(line,i.symbol,i.color); } if(first == user){ B.Add(user.You("catch",true) + " it! ",user); if(user.inv.Count < Global.MAX_INVENTORY_SIZE){ user.GetItem(i); } else{ B.Add("Your pack is too full to fit anything else. "); i.ignored = true; user.tile().GetItem(i); } } else{ if(first != null){ B.Add("It hits " + first.the_name + ". ",first); } if(i.IsBreakable()){ if(i.quantity > 1){ B.Add(i.TheName(true) + " break! ",t2); } else{ B.Add(i.TheName(true) + " breaks! ",t2); } if(i.NameOfItemType() == "orb"){ i.Use(null,new List<Tile>{t2}); } else{ i.CheckForMimic(); } } else{ t2.GetItem(i); } t2.MakeNoise(2); } if(first != null && first != user && first != player){ first.player_visibility_duration = -1; first.attrs[AttrType.PLAYER_NOTICED]++; } else{ if(trigger_traps && t2.IsTrap()){ t2.TriggerTrap(); } } } else{ if(wand){ return true; } return false; } } else{ if(!t.Is(FeatureType.GRENADE) && (t.Is(TileType.DOOR_C,TileType.DOOR_O,TileType.POISON_BULB) || t.Is(FeatureType.WEB,FeatureType.FORASECT_EGG,FeatureType.BONES))){ if(cast){ B.Add(user.You("cast") + " telekinesis. ",user); } if(t.Is(TileType.DOOR_C)){ B.Add("The door slams open. ",t); t.Toggle(null); } else{ if(t.Is(TileType.DOOR_O)){ B.Add("The door slams open. ",t); t.Toggle(null); } } if(t.Is(TileType.POISON_BULB)){ t.Bump(0); } if(t.Is(FeatureType.WEB)){ B.Add("The web is destroyed. ",t); t.RemoveFeature(FeatureType.WEB); } if(t.Is(FeatureType.FORASECT_EGG)){ B.Add("The egg is destroyed. ",t); t.RemoveFeature(FeatureType.FORASECT_EGG); //todo: forasect pathing? } if(t.Is(FeatureType.BONES)){ B.Add("The bones are scattered. ",t); t.RemoveFeature(FeatureType.BONES); } } else{ bool grenade = t.Is(FeatureType.GRENADE); bool barrel = t.Is(TileType.BARREL); bool flaming_barrel = barrel && t.IsBurning(); bool torch = t.Is(TileType.STANDING_TORCH); string feature_name = ""; int impact_damage_dice = 3; colorchar vis = new colorchar(t.symbol,t.color); switch(t.type){ case TileType.CRACKED_WALL: feature_name = "cracked wall"; break; case TileType.RUBBLE: feature_name = "pile of rubble"; break; case TileType.STATUE: feature_name = "statue"; break; } if(grenade){ impact_damage_dice = 0; feature_name = "grenade"; vis.c = ','; vis.color = Color.Red; } if(flaming_barrel){ feature_name = "flaming barrel of oil"; } if(barrel){ feature_name = "barrel"; } if(torch){ feature_name = "torch"; } if(feature_name == ""){ if(wand){ if(user == player){ B.Add("The wand grabs at empty space. ",t); } return true; } return false; } string msg = "Throw the " + feature_name + " in which direction? "; bool passable_hack = !t.passable; if(passable_hack){ t.passable = true; } List<Tile> line = null; if(user == player){ TargetInfo info = t.GetTarget(false,12,0,false,true,false,msg); if(info != null){ line = info.extended_line; } } else{ line = ai_line; } if(passable_hack){ t.passable = false; } if(line != null){ if(cast){ B.Add(user.You("cast") + " telekinesis. ",user); } if(line.Count == 1){ B.Add(user.YouVisible("throw") + " the " + feature_name + " into the ceiling. ",user,t); } else{ B.Add(user.YouVisible("throw") + " the " + feature_name + ". ",user,t); } B.DisplayNow(); user.attrs[AttrType.SELF_TK_NO_DAMAGE] = 1; switch(t.type){ case TileType.CRACKED_WALL: case TileType.RUBBLE: case TileType.STATUE: case TileType.BARREL: case TileType.STANDING_TORCH: if(flaming_barrel){ t.RemoveFeature(FeatureType.FIRE); } t.Toggle(null,TileType.FLOOR); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } break; } if(grenade){ t.RemoveFeature(FeatureType.GRENADE); Event e = Q.FindTargetedEvent(t,EventType.GRENADE); if(e != null){ e.dead = true; } } Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); colorchar[,] mem = Screen.GetCurrentMap(); int current_row = t.row; int current_col = t.col; // int knockback_strength = 12; if(line.Count == 1){ knockback_strength = 0; } int i=0; while(true){ Tile t2 = line[i]; if(t2 == t){ break; } ++i; } line.RemoveRange(0,i+1); while(knockback_strength > 0){ //if the knockback strength is greater than 1, you're passing *over* at least one tile. Tile t2 = line[0]; line.RemoveAt(0); if(!t2.passable){ if(t2.Is(TileType.CRACKED_WALL,TileType.DOOR_C,TileType.HIDDEN_DOOR) && impact_damage_dice > 0){ string tilename = t2.TheName(true); if(t2.type == TileType.HIDDEN_DOOR){ tilename = "a hidden door"; t2.Toggle(null); } B.Add("The " + feature_name + " flies into " + tilename + ". ",t2); t2.Toggle(null); Screen.WriteMapChar(current_row,current_col,mem[current_row,current_col]); } else{ B.Add("The " + feature_name + " flies into " + t2.TheName(true) + ". ",t2); if(impact_damage_dice > 0){ t2.Bump(M.tile[current_row,current_col].DirectionOf(t2)); } Screen.WriteMapChar(current_row,current_col,mem[current_row,current_col]); } knockback_strength = 0; break; } else{ if(t2.actor() != null){ B.Add("The " + feature_name + " flies into " + t2.actor().TheName(true) + ". ",t2); if(t2.actor().type != ActorType.SPORE_POD && !t2.actor().HasAttr(AttrType.SELF_TK_NO_DAMAGE)){ t2.actor().TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(impact_damage_dice,6),user,"colliding with a " + feature_name); } knockback_strength = 0; Screen.WriteMapChar(t2.row,t2.col,vis); Screen.WriteMapChar(current_row,current_col,mem[current_row,current_col]); current_row = t2.row; current_col = t2.col; break; } else{ if(t2.Is(FeatureType.WEB)){ //unless perhaps grenades should get stuck and explode in the web? t2.RemoveFeature(FeatureType.WEB); } Screen.WriteMapChar(t2.row,t2.col,vis); Screen.WriteMapChar(current_row,current_col,mem[current_row,current_col]); current_row = t2.row; current_col = t2.col; Game.GLUpdate(); Thread.Sleep(20); } } //M.Draw(); knockback_strength--; } Tile current = M.tile[current_row,current_col]; if(grenade){ B.Add("The grenade explodes! ",current); current.ApplyExplosion(1,user,"an exploding grenade"); } if(barrel){ B.Add("The barrel smashes! ",current); List<Tile> cone = current.TilesWithinDistance(1).Where(x=>x.passable); List<Tile> added = new List<Tile>(); foreach(Tile t3 in cone){ foreach(int dir in U.FourDirections){ if(R.CoinFlip() && t3.TileInDirection(dir).passable){ added.AddUnique(t3.TileInDirection(dir)); } } } cone.AddRange(added); foreach(Tile t3 in cone){ t3.AddFeature(FeatureType.OIL); if(t3.actor() != null && !t3.actor().HasAttr(AttrType.OIL_COVERED,AttrType.SLIMED)){ if(t3.actor().IsBurning()){ t3.actor().ApplyBurning(); } else{ t3.actor().attrs[AttrType.OIL_COVERED] = 1; B.Add(t3.actor().YouAre() + " covered in oil. ",t3.actor()); if(t3.actor() == player){ Help.TutorialTip(TutorialTopic.Oiled); } } } } if(flaming_barrel){ current.ApplyEffect(DamageType.FIRE); } } if(torch){ current.AddFeature(FeatureType.FIRE); } user.attrs[AttrType.SELF_TK_NO_DAMAGE] = 0; } else{ if(wand){ return true; } return false; } } } } } else{ return false; } return true; }
public static bool StatusIsHidden(this AttrType attr,Actor a) { switch(attr){ case AttrType.FLYING: return a.HasAttr(AttrType.FLYING_LEAP,AttrType.PSEUDO_VAMPIRIC,AttrType.DESCENDING); case AttrType.IMMUNE_BURNING: return a.HasAttr(AttrType.STONEFORM); case AttrType.NONLIVING: return a.HasAttr(AttrType.STONEFORM); case AttrType.IMMOBILE: //return a.HasAttr(AttrType.ROOTS) || Actor.Prototype(a.type).HasAttr(AttrType.IMMOBILE); return a.HasAttr(AttrType.ROOTS); case AttrType.SILENCED: return a.HasAttr(AttrType.SILENCE_AURA); case AttrType.DETECTING_MONSTERS: return a.HasAttr(AttrType.MYSTIC_MIND); case AttrType.MENTAL_IMMUNITY: return a.HasAttr(AttrType.MYSTIC_MIND); default: return false; } }
public void Toggle(Actor toggler,TileType toggle_to) { bool lighting_update = false; List<PhysicalObject> light_sources = new List<PhysicalObject>(); TileType original_type = type; bool original_passable = passable; if(opaque != Prototype(toggle_to).opaque){ for(int i=row-1;i<=row+1;++i){ for(int j=col-1;j<=col+1;++j){ if(M.tile[i,j].IsLit(player.row,player.col,true)){ lighting_update = true; } } } } if(lighting_update){ for(int i=row-Global.MAX_LIGHT_RADIUS;i<=row+Global.MAX_LIGHT_RADIUS;++i){ for(int j=col-Global.MAX_LIGHT_RADIUS;j<=col+Global.MAX_LIGHT_RADIUS;++j){ if(i>0 && i<ROWS-1 && j>0 && j<COLS-1){ if(M.actor[i,j] != null && M.actor[i,j].LightRadius() > 0){ light_sources.Add(M.actor[i,j]); M.actor[i,j].UpdateRadius(M.actor[i,j].LightRadius(),0); } if(M.tile[i,j].inv != null && M.tile[i,j].inv.light_radius > 0){ light_sources.Add(M.tile[i,j].inv); M.tile[i,j].inv.UpdateRadius(M.tile[i,j].inv.light_radius,0); } if(M.tile[i,j].light_radius > 0){ light_sources.Add(M.tile[i,j]); M.tile[i,j].UpdateRadius(M.tile[i,j].light_radius,0); } else{ if(M.tile[i,j].Is(FeatureType.FIRE)){ light_sources.Add(M.tile[i,j]); M.tile[i,j].UpdateRadius(1,0); } } } } } } TransformTo(toggle_to); if(lighting_update){ foreach(PhysicalObject o in light_sources){ if(o is Actor){ Actor a = o as Actor; a.UpdateRadius(0,a.LightRadius()); } else{ if(o is Tile && o.light_radius == 0 && (o as Tile).Is(FeatureType.FIRE)){ o.UpdateRadius(0,1); } else{ o.UpdateRadius(0,o.light_radius); } } } } if(Prototype(type).revealed_by_light){ revealed_by_light = true; } if(toggler != null && toggler != player){ if(type == TileType.DOOR_C && original_type == TileType.DOOR_O){ if(player.CanSee(this)){ B.Add(toggler.TheName(true) + " closes the door. "); } } if(type == TileType.DOOR_O && original_type == TileType.DOOR_C){ if(player.CanSee(this)){ B.Add(toggler.TheName(true) + " opens the door. "); } } } if(toggler != null){ if(original_type == TileType.RUBBLE){ B.Add(toggler.YouVisible("scatter") + " the rubble. ",this); } } if(!passable && original_passable){ if(features.Contains(FeatureType.STABLE_TELEPORTAL)){ Event e = Q.FindTargetedEvent(this,EventType.TELEPORTAL); if(e != null){ foreach(Tile t in e.area){ Event e2 = Q.FindTargetedEvent(t,EventType.TELEPORTAL); if(e2 != null && t.features.Contains(FeatureType.STABLE_TELEPORTAL)){ e2.area.Remove(this); if(e2.area.Count == 0){ t.RemoveFeature(FeatureType.STABLE_TELEPORTAL); t.AddFeature(FeatureType.INACTIVE_TELEPORTAL); e2.dead = true; } } } } } foreach(FeatureType ft in new List<FeatureType>(features)){ RemoveFeature(ft); } } CheckForSpriteUpdate(); }
public void Execute() { if(!dead){ switch(type){ case EventType.MOVE: { Actor temp = target as Actor; temp.Act(); break; } case EventType.REMOVE_ATTR: { Actor temp = target as Actor; if(attr == AttrType.FLYING){ temp.attrs[AttrType.DESCENDING] = 2; if(temp == player){ B.Add("You start to descend as your flight wears off. "); B.Print(true); } break; } if(attr == AttrType.SHINING){ int old_rad = temp.LightRadius(); temp.attrs[attr] -= value; if(old_rad != temp.LightRadius() && !temp.HasAttr(AttrType.BURROWING)){ temp.UpdateRadius(old_rad,temp.LightRadius()); } break; } if(temp.type == ActorType.BERSERKER && attr == AttrType.COOLDOWN_2){ temp.attrs[attr] = 0; //this hack can probably be removed } else{ temp.attrs[attr] -= value; } if(attr == AttrType.BURNING && temp.LightRadius() == 0 && !temp.HasAttr(AttrType.BURROWING)){ temp.UpdateRadius(1,0); } if(attr == AttrType.TELEPORTING){ temp.attrs[attr] = 0; } if(attr == AttrType.CONVICTION){ if(temp.HasAttr(AttrType.IN_COMBAT)){ temp.attrs[AttrType.CONVICTION] += value; //whoops, undo that } else{ temp.attrs[AttrType.BONUS_SPIRIT] -= value; //otherwise, set things to normal temp.attrs[AttrType.BONUS_COMBAT] -= (value+1) / 2; if(temp.attrs[AttrType.KILLSTREAK] >= 2){ B.Add("You wipe off your weapon. "); } temp.attrs[AttrType.KILLSTREAK] = 0; } } if(attr == AttrType.COOLDOWN_1 && temp.type == ActorType.BERSERKER){ B.Add(temp.Your() + " rage diminishes. ",temp); B.Add(temp.the_name + " dies. ",temp); temp.Kill(); } break; } case EventType.REMOVE_GAS: { List<Tile> removed = new List<Tile>(); foreach(Tile t in area){ if(t.Is(feature)){ if(R.PercentChance(value)){ t.RemoveFeature(feature); removed.Add(t); } } else{ removed.Add(t); } } foreach(Tile t in removed){ area.Remove(t); } if(area.Count > 0){ Event.RemoveGas(area,100,feature,value); } break; } case EventType.CHECK_FOR_HIDDEN: { List<Tile> removed = new List<Tile>(); foreach(Tile t in area){ if(player.CanSee(t)){ int exponent = player.DistanceFrom(t) + 1; if(player.magic_trinkets.Contains(MagicTrinketType.RING_OF_KEEN_SIGHT)){ --exponent; } if(!t.IsLit()){ if(!player.HasAttr(AttrType.SHADOWSIGHT)){ ++exponent; } } if(exponent > 8){ exponent = 8; //because 1 in 256 is enough. } int difficulty = 1; for(int i=exponent;i>0;--i){ difficulty = difficulty * 2; } if(R.Roll(difficulty) == difficulty){ if(t.IsTrap() || t.Is(TileType.FIRE_GEYSER) || t.Is(TileType.FOG_VENT) || t.Is(TileType.POISON_GAS_VENT)){ t.name = Tile.Prototype(t.type).name; t.a_name = Tile.Prototype(t.type).a_name; t.the_name = Tile.Prototype(t.type).the_name; t.symbol = Tile.Prototype(t.type).symbol; t.color = Tile.Prototype(t.type).color; B.Add("You notice " + t.AName(true) + ". "); } else{ if(t.type == TileType.HIDDEN_DOOR){ t.Toggle(null); B.Add("You notice a hidden door. "); } } removed.Add(t); } } } foreach(Tile t in removed){ area.Remove(t); } if(area.Count > 0){ Q.Add(new Event(area,100,EventType.CHECK_FOR_HIDDEN)); } break; } case EventType.RELATIVELY_SAFE: { if(M.AllActors().Count == 1 && !Q.Contains(EventType.POLTERGEIST) && !Q.Contains(EventType.REGENERATING_FROM_DEATH) && !Q.Contains(EventType.MIMIC) && !Q.Contains(EventType.MARBLE_HORROR)){ //B.Add("The dungeon is still and silent. "); B.Add("The dungeon is utterly silent for a moment. "); B.PrintAll(); } else{ Q.Add(new Event((R.Roll(20)+30)*100,EventType.RELATIVELY_SAFE)); } break; } case EventType.POLTERGEIST: { if(target != null && target is Actor){ //target can either be a stolen item, or the currently manifested poltergeist. Q.Add(new Event(target,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,0,"")); break; //if it's manifested, the event does nothing for now. } if(area.Any(t => t.actor() == player)){ bool manifested = false; if(value == 0){ B.Add("You feel like you're being watched. "); } else{ if(target != null){ //if it has a stolen item Tile tile = null; tile = area.Where(t => t.actor() == null && t.DistanceFrom(player) >= 2 && t.HasLOE(player) && t.FirstActorInLine(player) == player).RandomOrDefault(); if(tile != null){ Actor temporary = new Actor(ActorType.POLTERGEIST,"something",'G',Color.DarkGreen,1,1,0,0); temporary.a_name = "something"; temporary.the_name = "something"; temporary.p = tile.p; temporary.inv = new List<Item>(); temporary.inv.Add(target as Item); Item item = temporary.inv[0]; if(item.NameOfItemType() == "orb"){ temporary.inv[0].Use(temporary,temporary.GetBestExtendedLineOfEffect(player)); } else{ B.Add("Something throws " + item.AName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstSolidTileOrActor(),new colorchar(item.color,item.symbol)); player.tile().GetItem(item); B.Add(item.TheName() + " hits you. "); player.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),temporary,"a flying " + item.Name()); } target = null; } else{ Q.Add(new Event(target,area,100,EventType.POLTERGEIST,AttrType.NO_ATTR,value,"")); return; //try again next turn } } else{ if(value >= 2 && area.Any(t => t.DistanceFrom(player) == 1 && t.passable && t.actor() == null)){ Tile tile = area.Where(t => t.DistanceFrom(player) == 1 && t.passable && t.actor() == null).Random(); B.DisplayNow(); for(int i=4;i>0;--i){ Screen.AnimateStorm(tile.p,i,2,1,'G',Color.DarkGreen); } Actor a = Actor.Create(ActorType.POLTERGEIST,tile.row,tile.col,TiebreakerAssignment.UseCurrent); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; B.Add("A poltergeist manifests in front of you! "); Q.Add(new Event(a,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,0,"")); manifested = true; } else{ if(player.tile().type == TileType.DOOR_O){ B.Add("The door slams closed on you! "); player.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),null,"a slamming door"); } else{ Tile tile = null; //check for items to throw... tile = area.Where(t => t.inv != null && t.actor() == null && t.DistanceFrom(player) >= 2 && t.HasLOE(player) && t.FirstActorInLine(player) == player).RandomOrDefault(); if(tile != null){ Actor temporary = new Actor(ActorType.POLTERGEIST,"something",'G',Color.DarkGreen,1,1,0,0); temporary.a_name = "something"; temporary.the_name = "something"; temporary.p = tile.p; temporary.inv = new List<Item>(); if(tile.inv.quantity <= 1){ temporary.inv.Add(tile.inv); tile.inv = null; } else{ temporary.inv.Add(new Item(tile.inv,-1,-1)); tile.inv.quantity--; } M.Draw(); Item item = temporary.inv[0]; if(item.NameOfItemType() == "orb"){ temporary.inv[0].Use(temporary,temporary.GetBestExtendedLineOfEffect(player)); } else{ B.Add("Something throws " + item.TheName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstSolidTileOrActor(),new colorchar(item.color,item.symbol)); player.tile().GetItem(item); B.Add(item.TheName() + " hits you. "); player.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),temporary,"a flying " + item.Name()); } } else{ if(area.Any(t => t.type == TileType.DOOR_O || t.type == TileType.DOOR_C)){ Tile door = area.Where(t=>t.type == TileType.DOOR_O || t.type == TileType.DOOR_C).Random(); if(door.type == TileType.DOOR_C){ if(player.CanSee(door)){ B.Add("The door flies open! ",door); } else{ if(door.seen || player.DistanceFrom(door) <= 12){ B.Add("You hear a door slamming. "); } } door.Toggle(null); } else{ if(door.actor() == null){ if(player.CanSee(door)){ B.Add("The door slams closed! ",door); } else{ if(door.seen || player.DistanceFrom(door) <= 12){ B.Add("You hear a door slamming. "); } } door.Toggle(null); } else{ if(player.CanSee(door)){ B.Add("The door slams closed on " + door.actor().TheName(true) + "! ",door); } else{ if(player.DistanceFrom(door) <= 12){ B.Add("You hear a door slamming and a grunt of pain. "); } } door.actor().TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),null,"a slamming door"); } } } else{ B.Add("You hear mocking laughter from nearby. "); } } } } } } if(!manifested){ Q.Add(new Event(target,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,value+1,"")); } } else{ Q.Add(new Event(target,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,0,"")); } break; } case EventType.MIMIC: { Item item = target as Item; if(area[0].inv != item){ //it could have been picked up by the player or moved in another way foreach(Tile t in M.AllTiles()){ //if it was moved, make the correction to the event's area. if(t.inv == item){ area = new List<Tile>{t}; break; } } } if(area[0].inv == item){ bool attacked = false; if(player.DistanceFrom(area[0]) == 1 && area[0].actor() == null){ if(player.TotalSkill(SkillType.STEALTH) * 5 < R.Roll(1,100)){ B.Add(item.TheName(true) + " suddenly grows tentacles! "); attacked = true; area[0].inv = null; Actor a = Actor.Create(ActorType.MIMIC,area[0].row,area[0].col,TiebreakerAssignment.UseCurrent); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; a.symbol = item.symbol; a.color = item.color; } } if(!attacked){ Q.Add(new Event(target,area,100,EventType.MIMIC,AttrType.NO_ATTR,0,"")); } } else{ //if the item is missing, we assume that the player just picked it up List<Tile> open = new List<Tile>(); foreach(Tile t in player.TilesAtDistance(1)){ if(t.passable && t.actor() == null){ open.Add(t); } } if(open.Count > 0){ Tile t = open.Random(); B.Add(item.TheName() + " suddenly grows tentacles! "); Actor a = Actor.Create(ActorType.MIMIC,t.row,t.col,TiebreakerAssignment.UseCurrent); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; a.symbol = item.symbol; a.color = item.color; player.inv.Remove(item); } else{ B.Add("Your pack feels lighter. "); player.inv.Remove(item); } } break; } case EventType.GRENADE: { Tile t = target as Tile; if(t.Is(FeatureType.GRENADE)){ t.features.Remove(FeatureType.GRENADE); B.Add("The grenade explodes! ",t); if(t.seen){ Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); } B.DisplayNow(); t.ApplyExplosion(1,"an exploding grenade"); /*List<pos> cells = new List<pos>(); foreach(Tile tile in t.TilesWithinDistance(1)){ if(tile.passable && tile.seen){ //animation LOS check here cells.Add(tile.p); } } Screen.AnimateMapCells(cells,new colorchar('*',Color.RandomExplosion)); Actor a = t.actor(); if(a != null){ a.attrs[AttrType.TURN_INTO_CORPSE] = 1; } foreach(Actor a2 in t.ActorsWithinDistance(1)){ a2.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(3,6),null,"an exploding grenade"); } if(a != null){ int dir = Global.RandomDirection(); if(a.curhp > 0 || !a.HasAttr(AttrType.NO_CORPSE_KNOCKBACK)){ t.TileInDirection(dir).KnockObjectBack(a,1); } a.CorpseCleanup(); } t.MakeNoise(8);*/ } break; } case EventType.BLAST_FUNGUS: { Item i = target as Item; i.other_data--; if(i.other_data == 0){ Tile t = null; if(M.tile.BoundsCheck(i.row,i.col) && M.tile[i.p].inv == i){ t = M.tile[i.p]; t.inv = null; } else{ foreach(Actor a in M.AllActors()){ if(a.inv.Contains(i)){ a.inv.Remove(i); t = a.tile(); break; } } } if(t != null){ B.Add("The blast fungus explodes! ",t); if(t.seen){ Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); } B.DisplayNow(); t.ApplyExplosion(3,"an exploding blast fungus"); } } else{ Tile t = null; if(M.tile.BoundsCheck(i.row,i.col) && M.tile[i.p].inv == i){ t = M.tile[i.p]; } else{ foreach(Actor a in M.AllActors()){ if(a.inv.Contains(i)){ t = a.tile(); break; } } } if(t != null && t.seen){ Screen.AnimateMapCell(t.row,t.col,new colorchar(i.other_data.ToString()[0],Color.Red),100); } Q.Add(new Event(i,100,EventType.BLAST_FUNGUS)); } break; } case EventType.STALAGMITE: { if(value > 1){ int stalagmites = 0; //number removed int number_left = 0; List<Tile> crumbled = new List<Tile>(); foreach(Tile tile in area){ if(tile.type == TileType.STALAGMITE){ if(R.OneIn(value)){ crumbled.Add(tile); tile.Toggle(null); ++stalagmites; } else{ ++number_left; } } } if(stalagmites > 0){ if(stalagmites > 1){ B.Add("The stalagmites crumble. ",crumbled.ToArray()); } else{ B.Add("The stalagmite crumbles. ",crumbled.ToArray()); } } if(number_left > 0){ Q.Add(new Event(area,100,EventType.STALAGMITE,value)); } } else{ int stalagmites = 0; foreach(Tile tile in area){ if(tile.type == TileType.STALAGMITE){ stalagmites++; } } if(stalagmites > 0){ if(stalagmites > 1){ B.Add("The stalagmites crumble. ",area.ToArray()); } else{ B.Add("The stalagmite crumbles. ",area.ToArray()); } foreach(Tile tile in area){ if(tile.type == TileType.STALAGMITE){ tile.Toggle(null); } } } } break; } case EventType.FIRE_GEYSER: { int frequency = value / 10; //9-39 int variance = value % 10; //0-9 int variance_amount = (frequency * variance) / 10; int number_of_values = variance_amount*2 + 1; int minimum_value = frequency - variance_amount; if(minimum_value < 5){ int diff = 5 - minimum_value; number_of_values -= diff; minimum_value = 5; } int delay = ((minimum_value - 1) + R.Roll(number_of_values)) * 100; Q.Add(new Event(target,delay+200,EventType.FIRE_GEYSER,value)); Q.Add(new Event(target,delay,EventType.FIRE_GEYSER_ERUPTION,2)); break; } case EventType.FIRE_GEYSER_ERUPTION: { foreach(Tile t in target.TilesWithinDistance(2)){ t.RemoveFeature(FeatureType.FOG); } //int old_radius = target.light_radius; //target.UpdateRadius(old_radius,2,true); B.Add(target.the_name + " spouts flames! ",target); if(target.actor() != null){ target.actor().ApplyBurning(); } for(int i=0;i<4;++i){ Tile t = target.TilesWithinDistance(2).Where(x=>target.HasLOE(x)).RandomOrDefault(); if(t != null){ if(t.passable){ t.AddFeature(FeatureType.FIRE); } else{ t.ApplyEffect(DamageType.FIRE); } } } //target.UpdateRadius(2,old_radius,true); if(value > 0){ Q.Add(new Event(target,100,EventType.FIRE_GEYSER_ERUPTION,value - 1)); } break; } case EventType.FOG_VENT: { Tile current = target as Tile; if(!current.Is(FeatureType.FOG)){ current.AddFeature(FeatureType.FOG); List<Tile> new_area = new List<Tile>{current}; Q.RemoveTilesFromEventAreas(new_area,EventType.REMOVE_GAS); Event.RemoveGas(new_area,600,FeatureType.FOG,25); //Q.Add(new Event(new_area,600,EventType.FOG,25)); } else{ for(int tries=0;tries<50;++tries){ List<Tile> open = new List<Tile>(); foreach(Tile t in current.TilesAtDistance(1)){ //perhaps the rework could involve refreshing the duration of nearby tiles - if enough are refreshed, then no new tiles need to be added if(t.passable){ open.Add(t); if(!t.Is(FeatureType.FOG)){ open.Add(t); //3x as likely if it can expand there open.Add(t); } } } if(open.Count > 0){ Tile possible = open.Random(); if(!possible.Is(FeatureType.FOG)){ possible.AddFeature(FeatureType.FOG); List<Tile> new_area = new List<Tile>{possible}; Q.RemoveTilesFromEventAreas(new_area,EventType.REMOVE_GAS); Event.RemoveGas(new_area,600,FeatureType.FOG,25); break; } else{ current = possible; } } else{ break; } } } Q.Add(new Event(target,100,EventType.FOG_VENT)); break; } case EventType.POISON_GAS_VENT: { Tile current = target as Tile; if(R.OneIn(7)){ int num = R.Roll(5) + 2; List<Tile> new_area = new List<Tile>(); for(int i=0;i<num;++i){ if(!current.Is(FeatureType.POISON_GAS)){ current.AddFeature(FeatureType.POISON_GAS); new_area.Add(current); } else{ for(int tries=0;tries<50;++tries){ List<Tile> open = new List<Tile>(); foreach(Tile t in current.TilesAtDistance(1)){ if(t.passable){ open.Add(t); } } if(open.Count > 0){ Tile possible = open.Random(); if(!possible.Is(FeatureType.POISON_GAS)){ possible.AddFeature(FeatureType.POISON_GAS); new_area.Add(possible); break; } else{ current = possible; } } else{ break; } } } } if(new_area.Count > 0){ B.Add("Toxic vapors pour from " + target.the_name + "! ",target); Event.RemoveGas(new_area,200,FeatureType.POISON_GAS,18); } } Q.Add(new Event(target,100,EventType.POISON_GAS_VENT)); break; } case EventType.STONE_SLAB: { Tile t = target as Tile; if(t.type == TileType.STONE_SLAB && (t.IsLitFromAnywhere(true) || area.Any(x=>x.actor()!=null))){ bool vis = player.CanSee(t); t.Toggle(null); //t.Toggle(null,TileType.FLOOR); //t.symbol = '-'; //t.revealed_by_light = true; if(!vis && player.CanSee(t)){ vis = true; } if(vis){ B.Add("The stone slab rises with a grinding sound. "); } else{ if(player.DistanceFrom(t) <= 6){ B.Add("You hear a grinding sound. "); } } } else{ if(t.type == TileType.STONE_SLAB_OPEN && !t.IsLitFromAnywhere(true) && t.actor() == null && !area.Any(x=>x.actor()!=null)){ bool vis = player.CanSee(t); //t.Toggle(null,TileType.STONE_SLAB); t.Toggle(null); if(!vis && player.CanSee(t)){ vis = true; } if(vis){ B.Add("The stone slab descends with a grinding sound. "); } else{ if(player.DistanceFrom(t) <= 6){ B.Add("You hear a grinding sound. "); } } } } Q.Add(new Event(target,area,100,EventType.STONE_SLAB)); break; } case EventType.MARBLE_HORROR: { Tile t = target as Tile; if(t.type == TileType.STATUE){ if(value == 1 && player.CanSee(t) && !t.IsLit() && t.actor() == null){ //if target was visible last turn & this turn, and it's currently in darkness... t.TransformTo(TileType.FLOOR); Actor a = Actor.Create(ActorType.MARBLE_HORROR,t.row,t.col,TiebreakerAssignment.AtEnd); //todo: not sure - should this get a placeholder like poltergeist and mimic? foreach(Event e in Q.list){ if(e.target == a && e.type == EventType.MOVE){ e.dead = true; break; } } a.Q0(); switch(R.Roll(2)){ case 1: B.Add("You think that statue might have just moved... "); B.Print(true); break; case 2: B.Add("The statue turns its head to face you. "); B.Print(true); break; } } else{ if(player.CanSee(t)){ Q.Add(new Event(target,100,EventType.MARBLE_HORROR,1)); } else{ Q.Add(new Event(target,100,EventType.MARBLE_HORROR,0)); } } } break; } case EventType.REGENERATING_FROM_DEATH: { int health = value; int permanent_damage = secondary_value; if(target.tile().Is(FeatureType.TROLL_CORPSE)){ //otherwise, assume it was destroyed by fire int maxhp = Actor.Prototype(ActorType.TROLL).maxhp; int recovered = Actor.Prototype(ActorType.TROLL).attrs[AttrType.REGENERATING]; if(health + recovered > maxhp - permanent_damage){ recovered = (maxhp - permanent_damage) - health; } health += recovered; if(permanent_damage >= maxhp){ break; } if(health > 0 && target.actor() == null){ Actor a = Actor.Create(ActorType.TROLL,target.row,target.col,TiebreakerAssignment.UseCurrent); a.curhp = health; a.attrs[AttrType.PERMANENT_DAMAGE] = permanent_damage; a.attrs[AttrType.NO_ITEM]++; a.attrs[AttrType.DANGER_SENSED]++; B.Add("The troll stands up! ",target); a.player_visibility_duration = -1; if(target.tile().type == TileType.DOOR_C){ target.tile().Toggle(a); } target.tile().features.Remove(FeatureType.TROLL_CORPSE); a.attrs[AttrType.WANDERING]++; } else{ int roll = R.Roll(20); if(health == -1){ roll = 1; } if(health == 0){ roll = 3; } switch(roll){ case 1: case 2: B.Add("The troll's corpse twitches. ",target); break; case 3: case 4: B.Add("You hear sounds coming from the troll's corpse. ",target); break; case 5: B.Add("The troll on the floor regenerates. ",target); break; default: break; } Event e = new Event(target,100,EventType.REGENERATING_FROM_DEATH); e.value = health; e.secondary_value = permanent_damage; Q.Add(e); } } if(target.tile().Is(FeatureType.TROLL_BLOODWITCH_CORPSE)){ //otherwise, assume it was destroyed by fire int maxhp = Actor.Prototype(ActorType.TROLL_BLOODWITCH).maxhp; int recovered = Actor.Prototype(ActorType.TROLL_BLOODWITCH).attrs[AttrType.REGENERATING]; if(health + recovered > maxhp - permanent_damage){ recovered = (maxhp - permanent_damage) - health; } health += recovered; if(permanent_damage >= maxhp){ break; } if(recovered > 0){ List<pos> cells = new List<pos>(); List<colorchar> cch = new List<colorchar>(); foreach(pos p2 in target.PositionsWithinDistance(4)){ if(target.HasLOE(M.tile[p2]) && player.CanSee(M.tile[p2])){ cells.Add(p2); colorchar ch = M.VisibleColorChar(p2.row,p2.col); ch.color = Color.Red; cch.Add(ch); } } if(cells.Count > 0){ M.Draw(); Screen.AnimateMapCells(cells,cch,40); } foreach(Actor a in target.ActorsWithinDistance(4)){ if(target.HasLOE(a)){ if(a == player){ B.Add("Ow! "); } a.TakeDamage(DamageType.NORMAL,DamageClass.MAGICAL,recovered,null,"trollish blood magic"); } } } if(health > 0 && target.actor() == null){ Actor a = Actor.Create(ActorType.TROLL_BLOODWITCH,target.row,target.col,TiebreakerAssignment.UseCurrent); a.curhp = health; a.attrs[AttrType.PERMANENT_DAMAGE] = permanent_damage; a.attrs[AttrType.NO_ITEM]++; a.attrs[AttrType.DANGER_SENSED]++; B.Add("The troll bloodwitch rises! ",target); a.player_visibility_duration = -1; if(attr == AttrType.COOLDOWN_1){ a.attrs[AttrType.COOLDOWN_1]++; } if(target.tile().type == TileType.DOOR_C){ target.tile().Toggle(a); } target.tile().features.Remove(FeatureType.TROLL_BLOODWITCH_CORPSE); a.attrs[AttrType.WANDERING]++; } else{ int roll = R.Roll(20); if(health == -1){ roll = 1; } if(health == 0){ roll = 3; } switch(roll){ case 1: case 2: B.Add("The bloodwitch's corpse twitches. ",target); break; case 3: case 4: B.Add("You feel a pulse like a heartbeat coming from the bloodwitch. ",target); break; case 5: B.Add("The troll bloodwitch on the floor regenerates. ",target); break; default: break; } Event e = new Event(target,100,EventType.REGENERATING_FROM_DEATH); e.value = health; e.secondary_value = permanent_damage; Q.Add(e); } } break; } case EventType.REASSEMBLING: { Tile t = target as Tile; if(t.Is(FeatureType.BONES)){ if(t.actor() == null){ Actor a = Actor.Create(ActorType.SKELETON,target.row,target.col,TiebreakerAssignment.UseCurrent); B.Add("The skeleton reassembles itself. ",target); a.player_visibility_duration = -1; if(target.tile().type == TileType.DOOR_C){ target.tile().Toggle(a); } target.tile().features.Remove(FeatureType.BONES); if(R.OneIn(3)){ a.attrs[AttrType.WANDERING]++; } } else{ Q.Add(new Event(target,100,EventType.REASSEMBLING)); } } break; } case EventType.SHIELDING: { List<pos> cells = new List<pos>(); List<colorchar> symbols = new List<colorchar>(); int animation_delay = 75; foreach(Tile tile in area){ colorchar cch = tile.visual; if(tile.actor() != null){ if(!tile.actor().HasAttr(AttrType.SHIELDED)){ tile.actor().attrs[AttrType.SHIELDED] = 1; B.Add(tile.actor().YouAre() + " shielded. ",tile.actor()); } if(player.CanSee(tile.actor())){ animation_delay = 150; cch = tile.actor().visual; } } cch.bgcolor = Color.Blue; if(Global.LINUX && !Screen.GLMode){ cch.bgcolor = Color.DarkBlue; } if(cch.color == cch.bgcolor){ cch.color = Color.Black; } if(cch.c == '.'){ cch.c = '+'; } symbols.Add(cch); cells.Add(tile.p); } M.Draw(); Screen.AnimateMapCells(cells,symbols,animation_delay); --value; if(value > 0){ Q.Add(new Event(area,100,EventType.SHIELDING,value)); } break; } case EventType.FINAL_LEVEL_SPAWN_CULTISTS: { int num_cultists = M.AllActors().Where(x=>x.Is(ActorType.FINAL_LEVEL_CULTIST)).Count; if(num_cultists < 5){ Actor a = M.SpawnMob(ActorType.CULTIST); if(a != null){ List<Actor> group = null; if(a.group != null){ group = new List<Actor>(a.group); a.group.Clear(); } else{ group = new List<Actor>{a}; } List<int> valid_circles = new List<int>(); for(int i=0;i<5;++i){ if(M.FinalLevelSummoningCircle(i).PositionsWithinDistance(2,M.tile).Any(x=>M.tile[x].Is(TileType.DEMONIC_IDOL))){ valid_circles.Add(i); } } foreach(Actor a2 in group){ int i = valid_circles.RemoveLast(); pos circle = M.FinalLevelSummoningCircle(i); a2.FindPath(circle.row,circle.col); a2.attrs[AttrType.COOLDOWN_2] = i; a2.type = ActorType.FINAL_LEVEL_CULTIST; a2.group = null; if(!R.OneIn(20)){ a2.attrs[AttrType.NO_ITEM] = 1; } } } } Q.Add(new Event(R.Between(5,8)*100,EventType.FINAL_LEVEL_SPAWN_CULTISTS)); break; } /*case EventType.BOSS_SIGN: { string s = ""; switch(R.Roll(8)){ case 1: s = "You see scratch marks on the walls and floor. "; break; case 2: s = "There are deep gouges in the floor here. "; break; case 3: s = "The floor here is scorched and blackened. "; break; case 4: s = "You notice bones of an unknown sort on the floor. "; break; case 5: s = "You hear a distant roar. "; break; case 6: s = "You smell smoke. "; break; case 7: s = "You spot a large reddish scale on the floor. "; break; case 8: s = "A small tremor shakes the area. "; break; default: s = "Debug message. "; break; } if(!player.HasAttr(AttrType.RESTING)){ B.AddIfEmpty(s); } Q.Add(new Event((R.Roll(20)+35)*100,EventType.BOSS_SIGN)); break; } case EventType.BOSS_ARRIVE: { bool spawned = false; Actor a = null; if(M.AllActors().Count == 1 && !Q.Contains(EventType.POLTERGEIST)){ List<Tile> trolls = new List<Tile>(); for(LinkedListNode<Event> current = Q.list.First;current!=null;current = current.Next){ if(current.Value.type == EventType.REGENERATING_FROM_DEATH){ trolls.Add((current.Value.target) as Tile); } } foreach(Tile troll in trolls){ if(troll.Is(FeatureType.TROLL_CORPSE)){ B.Add("The troll corpse burns to ashes! ",troll); troll.features.Remove(FeatureType.TROLL_CORPSE); } else{ if(troll.Is(FeatureType.TROLL_BLOODWITCH_CORPSE)){ B.Add("The troll bloodwitch corpse burns to ashes! ",troll); troll.features.Remove(FeatureType.TROLL_BLOODWITCH_CORPSE); } } } Q.KillEvents(null,EventType.REGENERATING_FROM_DEATH); List<Tile> goodtiles = M.AllTiles(); List<Tile> removed = new List<Tile>(); foreach(Tile t in goodtiles){ if(!t.passable || t.Is(TileType.CHASM) || player.CanSee(t)){ removed.Add(t); } } foreach(Tile t in removed){ goodtiles.Remove(t); } if(goodtiles.Count > 0){ B.Add("You hear a loud crash and a nearby roar! "); Tile t = goodtiles[R.Roll(goodtiles.Count)-1]; a = Actor.Create(ActorType.FIRE_DRAKE,t.row,t.col,true,false); spawned = true; } else{ if(M.AllTiles().Any(t=>t.passable && !t.Is(TileType.CHASM) && t.actor() == null)){ B.Add("You hear a loud crash and a nearby roar! "); Tile tile = M.AllTiles().Where(t=>t.passable && !t.Is(TileType.CHASM) && t.actor() == null).Random(); a = Actor.Create(ActorType.FIRE_DRAKE,tile.row,tile.col,true,false); spawned = true; } } } if(!spawned){ Q.Add(new Event(null,null,(R.Roll(20)+10)*100,EventType.BOSS_ARRIVE,attr,value,"")); } else{ if(value > 0){ a.curhp = value; } else{ //if there's no good value, this means that this is the first appearance. B.Add("The ground shakes as dust and rocks fall from the cavern ceiling. "); B.Add("This place is falling apart! "); List<Tile> floors = M.AllTiles().Where(t=>t.passable && t.type != TileType.CHASM && player.tile() != t); Tile tile = null; if(floors.Count > 0){ tile = floors.Random(); (tile as Tile).Toggle(null,TileType.CHASM); } Q.Add(new Event(tile,100,EventType.FLOOR_COLLAPSE)); Q.Add(new Event((R.Roll(20)+20)*100,EventType.CEILING_COLLAPSE)); } } break; } case EventType.FLOOR_COLLAPSE: { Tile current = target as Tile; int tries = 0; if(current != null){ for(tries=0;tries<50;++tries){ List<Tile> open = new List<Tile>(); foreach(Tile t in current.TilesAtDistance(1)){ if(t.passable || t.Is(TileType.RUBBLE)){ open.Add(t); } } if(open.Count > 0){ Tile possible = open.Random(); if(!possible.Is(TileType.CHASM)){ possible.Toggle(null,TileType.CHASM); List<Tile> open_neighbors = possible.TilesAtDistance(1).Where(t=>t.passable && t.type != TileType.CHASM); int num_neighbors = open_neighbors.Count; while(open_neighbors.Count > num_neighbors/2){ Tile neighbor = open_neighbors.RemoveRandom(); neighbor.Toggle(null,TileType.CHASM); } break; } else{ current = possible; } } else{ break; } } } if(tries == 50 || current == null){ List<Tile> floors = M.AllTiles().Where(t=>t.passable && t.type != TileType.CHASM && player.tile() != t); if(floors.Count > 0){ target = floors.Random(); (target as Tile).Toggle(null,TileType.CHASM); } } Q.Add(new Event(target,100,EventType.FLOOR_COLLAPSE)); break; } case EventType.CEILING_COLLAPSE: { B.Add("The ground shakes and debris falls from the ceiling! "); for(int i=1;i<Global.ROWS-1;++i){ for(int j=1;j<Global.COLS-1;++j){ Tile t = M.tile[i,j]; if(t.Is(TileType.WALL)){ int num_walls = t.TilesAtDistance(1).Where(x=>x.Is(TileType.WALL)).Count; if(num_walls < 8 && R.OneIn(20)){ if(R.CoinFlip()){ t.Toggle(null,TileType.FLOOR); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } } else{ t.Toggle(null,TileType.RUBBLE); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; if(neighbor.type == TileType.FLOOR && R.OneIn(10)){ neighbor.Toggle(null,TileType.RUBBLE); } } } } } else{ int num_walls = t.TilesAtDistance(1).Where(x=>x.Is(TileType.WALL)).Count; if(num_walls == 0 && R.OneIn(100)){ if(R.OneIn(6)){ t.Toggle(null,TileType.RUBBLE); } foreach(Tile neighbor in t.TilesAtDistance(1)){ if(neighbor.type == TileType.FLOOR && R.OneIn(6)){ neighbor.Toggle(null,TileType.RUBBLE); } } } } } } Q.Add(new Event((R.Roll(20)+20)*100,EventType.CEILING_COLLAPSE)); break; }*/ case EventType.NORMAL_LIGHTING: { bool check_for_torch_dimming = false; if(M.wiz_lite){ B.Add("The supernatural brightness fades from the air. "); } if(M.wiz_dark){ B.Add("The supernatural darkness fades from the air. "); check_for_torch_dimming = true; } M.wiz_lite = false; M.wiz_dark = false; if(check_for_torch_dimming && player.HasAttr(AttrType.DIM_LIGHT)){ player.CalculateDimming(); } break; } case EventType.TELEPORTAL: { Tile t = target as Tile; if(t != null && t.Is(FeatureType.TELEPORTAL,FeatureType.STABLE_TELEPORTAL)){ if(t.Is(FeatureType.TELEPORTAL)){ value--; //unstable teleportals (from the item) degrade each turn } else{ if(value < 100){ value++; //stable ones repair themselves after use } } Actor a = t.actor(); Tile dest = null; if(a != null && !a.HasAttr(AttrType.JUST_TELEPORTED,AttrType.IMMOBILE)){ if(area != null){ dest = area.RandomOrDefault(); } else{ List<Tile> tiles = M.AllTiles().Where(x => x.passable && x.actor() == null && t.ApproximateEuclideanDistanceFromX10(x) >= 45); dest = tiles.RandomOrDefault(); } if(dest != null){ a.RefreshDuration(AttrType.JUST_TELEPORTED,101); value -= 25; bool visible = false; if(a == player){ B.Add("You disappear into the teleportal. "); } else{ if(player.CanSee(a)){ visible = true; B.Add(a.the_name + " disappears into the teleportal. ",t); } } a.Move(dest.row,dest.col); if(a != player && player.CanSee(a)){ if(visible){ B.Add(a.the_name + " reappears. ",a); } else{ B.Add(a.a_name + " suddenly appears! ",a); } } } } else{ if(a != null && a.HasAttr(AttrType.JUST_TELEPORTED)){ a.RefreshDuration(AttrType.JUST_TELEPORTED,101); } } if(t.inv != null && t.Is(FeatureType.TELEPORTAL)){ List<Tile> tiles = M.AllTiles().Where(x => x.passable && x.inv == null && t.ApproximateEuclideanDistanceFromX10(x) >= 45); dest = tiles.RandomOrDefault(); if(dest != null){ Item i = t.inv; bool visible = false; if(player.CanSee(t)){ visible = true; B.Add(i.TheName(true) + " disappears into the teleportal. ",t); } t.inv = null; dest.GetItem(i); if(player.CanSee(dest)){ if(visible){ B.Add(i.TheName(true) + " reappears. ",dest); } else{ B.Add(i.AName(true) + " suddenly appears! ",dest); } } } } if(value > 0){ Q.Add(new Event(target,area,100,EventType.TELEPORTAL,AttrType.NO_ATTR,value,"")); if(value < 25){ if(dest != null || R.OneIn(8)){ B.Add("The teleportal flickers. ",t,dest); } } } else{ if(t.Is(FeatureType.TELEPORTAL)){ t.RemoveFeature(FeatureType.TELEPORTAL); } if(t.Is(FeatureType.STABLE_TELEPORTAL)){ foreach(Tile t2 in area){ Event e2 = Q.FindTargetedEvent(t2,EventType.TELEPORTAL); if(e2 != null && t2.features.Contains(FeatureType.STABLE_TELEPORTAL)){ e2.area.Remove(t); if(e2.area.Count == 0){ t2.RemoveFeature(FeatureType.STABLE_TELEPORTAL); //t2.AddFeature(FeatureType.INACTIVE_TELEPORTAL); e2.dead = true; } } } t.RemoveFeature(FeatureType.STABLE_TELEPORTAL); } B.Add("The teleportal flickers and vanishes. ",t,dest); } } break; } case EventType.BREACH: { if(!R.OneIn(3)){ Tile t = area.WhereGreatest(x=>x.DistanceFrom(target)).RandomOrDefault(); if(t != null){ t.Toggle(null); if(t.actor() != null || t.inv != null){ foreach(Tile nearby in M.ReachableTilesByDistance(t.row,t.col,false)){ if(t.inv != null && nearby.inv == null){ nearby.GetItem(t.inv); t.inv = null; if(t.actor() == null){ break; } } if(t.actor() != null && nearby.actor() == null){ t.actor().Move(nearby.row,nearby.col); if(t.inv == null){ break; } } } if(t.actor() != null){ //if there wasn't an actual path to a passable tile, just move to the nearest for(int i=1;i<Math.Max(Global.ROWS,Global.COLS);++i){ List<Tile> tiles = t.TilesAtDistance(i).Where(x=>x.passable && x.actor() == null); bool done = false; while(tiles.Count > 0){ Tile dest = tiles.Random(); t.actor().Move(dest.row,dest.col); done = true; break; } if(done){ break; } } } if(t.inv != null){ for(int i=1;i<Math.Max(Global.ROWS,Global.COLS);++i){ List<Tile> tiles = t.TilesAtDistance(i).Where(x=>x.passable && x.inv == null); bool done = false; while(tiles.Count > 0){ Tile dest = tiles.Random(); dest.GetItem(t.inv); t.inv = null; done = true; break; } if(done){ break; } } } } if(t.features.Count > 0){ t.features.Clear(); } area.Remove(t); } } if(area.Count > 0){ Q.Add(new Event(target,area,100,EventType.BREACH)); } break; } case EventType.GRAVE_DIRT: { foreach(Tile t in area){ Actor a = t.actor(); if(a != null && a.type != ActorType.CORPSETOWER_BEHEMOTH && !a.HasAttr(AttrType.IMMOBILE,AttrType.JUST_GRABBED,AttrType.FROZEN,AttrType.FLYING) && R.OneIn(12)){ if(player.CanSee(a)){ B.Add("A dead hand reaches up and grabs " + a.the_name + "! ",t); } if(a == player){ B.Print(true); } if(a.HasAttr(AttrType.SLIMED,AttrType.OIL_COVERED,AttrType.BRUTISH_STRENGTH)){ if(player.CanSee(a)){ B.Add(a.You("slip") + " out of its grasp. ",t); } } else{ int duration = R.Roll(4) * 100; a.attrs[AttrType.IMMOBILE]++; Q.Add(new Event(a,duration,AttrType.IMMOBILE,"The dead hand releases " + a.TheName(true) + ". ",t)); //it'd be nice to check LOS here a.RefreshDuration(AttrType.JUST_GRABBED,duration + 100); } } } Q.Add(new Event(area,100,EventType.GRAVE_DIRT)); break; } case EventType.TOMBSTONE_GHOST: { if(area.Count > 0){ Tile t = area[0]; if(target == null && t.actor() == player){ foreach(Tile t2 in M.ReachableTilesByDistance(player.row,player.col,false)){ if(t2.passable && t2.actor() == null){ Actor ghost = Actor.Create(ActorType.GHOST,t2.row,t2.col); if(ghost != null){ target = ghost; ghost.player_visibility_duration = -1; ghost.target = player; t.color = Color.White; B.Add("A vengeful ghost rises! "); B.PrintAll(); break; } } } } Q.Add(new Event(target,area,100,EventType.TOMBSTONE_GHOST)); } break; } case EventType.POPPIES: { List<Tile> new_area = new List<Tile>(); bool recalculate_distance_map = false; foreach(Tile t in area){ if(t.type == TileType.POPPY_FIELD){ new_area.Add(t); Actor a = t.actor(); if(a == player){ Help.TutorialTip(TutorialTopic.Poppies); } if(a != null && !a.HasAttr(AttrType.NONLIVING,AttrType.PLANTLIKE)){ if(a.attrs[AttrType.POPPY_COUNTER] < 4){ a.GainAttrRefreshDuration(AttrType.POPPY_COUNTER,200); if(a == player && a.attrs[AttrType.POPPY_COUNTER] == 1){ B.Add("You breathe in the overwhelming scent of the poppies. "); //todo: this was set to "no interrupt" before. why? } } else{ a.RefreshDuration(AttrType.POPPY_COUNTER,200); } if(a.attrs[AttrType.POPPY_COUNTER] >= 4){ if(!a.HasAttr(AttrType.ASLEEP,AttrType.JUST_AWOKE)){ if(a.ResistedBySpirit()){ if(player.HasLOS(a)){ B.Add(a.You("resist") + " falling asleep. ",a); } } else{ if(player.HasLOS(a)){ B.Add(a.You("fall") + " asleep in the poppies. ",a); //B.Add("The poppies lull " + a.the_name + " to sleep. ",a); } a.attrs[AttrType.ASLEEP] = R.Between(4,6); } } /*a.ApplyStatus(AttrType.MAGICAL_DROWSINESS,(R.Roll(3)+4)*100); if(a == player && !a.HasAttr(AttrType.MAGICAL_DROWSINESS)){ //B.Add("The poppies make you drowsy. "); Help.TutorialTip(TutorialTopic.Drowsiness); } a.RefreshDuration(AttrType.MAGICAL_DROWSINESS,a.DurationOfMagicalEffect((R.Roll(3)+4)) * 100,a.YouFeel() + " less drowsy. ",a);*/ } } } else{ recalculate_distance_map = true; } } if(new_area.Count > 0){ Q.Add(new Event(new_area,100,EventType.POPPIES)); if(recalculate_distance_map){ M.CalculatePoppyDistanceMap(); } } break; } case EventType.BURROWING: { List<Tile> open = area.Where(x=>x.passable && x.actor() == null); Actor a = target as Actor; if(open.Count > 0){ Tile t = open.Random(); Event e = new Event(a,100,EventType.MOVE); e.tiebreaker = this.tiebreaker; Q.Add(e); a.attrs[AttrType.BURROWING] = 0; a.Move(t.row,t.col); if(player.CanSee(a)){ a.AnimateStorm(1,2,3,'*',Color.Gray); } B.Add(a.TheName(true) + " emerges from the ground. ",a,t); } else{ if(a.HasAttr(AttrType.REGENERATING)){ a.curhp += a.attrs[AttrType.REGENERATING]; if(a.curhp > a.maxhp){ a.curhp = a.maxhp; } } Q.Add(new Event(target,area,100,EventType.BURROWING)); } break; } case EventType.SPAWN_WANDERING_MONSTER: { int spawn_chance = 2; foreach(Actor a in Actor.tiebreakers){ if(a != player && a != null && !a.HasAttr(AttrType.IMMOBILE) && (a.group == null || a.group.Count == 0 || a.group[0] == a)){ spawn_chance *= 2; if(spawn_chance >= 65536){ break; } } } if(R.OneIn(spawn_chance)){ if(M.extra_danger < 8 && R.CoinFlip() && M.current_level != 1){ M.extra_danger++; B.Add("You sense danger. "); } Actor a = M.SpawnWanderingMob(); if(a != null){ a.attrs[AttrType.WANDERING] = 1; a.attrs[AttrType.NO_ITEM] = 1; if(player.CanSee(a)){ B.Add("You suddenly sense the presence of " + a.AName(true) + ". "); } } } Q.Add(new Event(R.Between(20,60)*100,EventType.SPAWN_WANDERING_MONSTER)); break; } /*case EventType.GAS_UPDATE: { int ROWS = Global.ROWS; int COLS = Global.COLS; float[,] g = null; for(int num=0;num<3;++num){ g = new float[ROWS,COLS]; for(int i=1;i<ROWS-1;++i){ for(int j=1;j<COLS-1;++j){ if(M.tile[i,j].passable){ float neighbors_total = 0.0f; int open = 0; foreach(int dir in U.EightDirections){ if(M.tile[i,j].TileInDirection(dir).passable){ pos p = new pos(i,j).PosInDir(dir); neighbors_total += M.gas[p.row,p.col]; ++open; } } if(open > 0){ float avg = neighbors_total / (float)open; float d = 0.03f * open; g[i,j] = M.gas[i,j] * (1-d) + avg * d; } } } } M.gas = g; } for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(g[i,j] > 0.0f){ if(g[i,j] <= 0.001f){ g[i,j] = 0.0f; M.tile[i,j].features.Remove(FeatureType.POISON_GAS); } else{ g[i,j] -= 0.001f;// * (float)R.r.NextDouble(); M.tile[i,j].features.AddUnique(FeatureType.POISON_GAS); } } else{ M.tile[i,j].features.Remove(FeatureType.POISON_GAS); } } } Q.Add(new Event(100,EventType.GAS_UPDATE)); break; }*/ case EventType.FIRE: { List<Tile> chance_to_burn = new List<Tile>(); //tiles that might be affected List<Tile> chance_to_die_out = new List<Tile>(); //fires that might die out List<PhysicalObject> no_fire = new List<PhysicalObject>(); foreach(PhysicalObject o in new List<PhysicalObject>(Fire.burning_objects)){ if(o.IsBurning()){ foreach(Tile neighbor in o.TilesWithinDistance(1)){ if(neighbor.actor() != null && neighbor.actor() != o){ if(neighbor.actor() == player){ if(!player.HasAttr(AttrType.JUST_SEARED,AttrType.FROZEN,AttrType.DAMAGE_RESISTANCE)){ B.Add("The heat sears you! "); } player.RefreshDuration(AttrType.JUST_SEARED,50); } neighbor.actor().TakeDamage(DamageType.FIRE,DamageClass.PHYSICAL,false,1,null,"searing heat"); } //every actor adjacent to a burning object takes proximity fire damage. (actors never get set on // fire directly this way, but an actor covered in oil will ignite if it takes any fire damage) //every tile adjacent to a burning object has a chance to be affected by fire. oil-covered objects are always affected. //if the roll is passed, fire is applied to the tile. chance_to_burn.AddUnique(neighbor); } if(o is Tile){ chance_to_die_out.AddUnique(o as Tile); } } else{ no_fire.AddUnique(o); } } foreach(Tile t in chance_to_burn){ if(R.OneIn(6) || t.Is(FeatureType.OIL,FeatureType.SPORES,FeatureType.CONFUSION_GAS) || t.Is(TileType.BARREL)){ t.ApplyEffect(DamageType.FIRE); } } foreach(Tile t in chance_to_die_out){ if(!t.Is(TileType.BARREL)){ bool more_flammable_terrain = false; bool more_fire = false; bool final_level_demonic_idol_present = false; //this will soon become a check for any terrain that prevents fires from dying foreach(Tile neighbor in t.TilesAtDistance(1)){ if(neighbor.IsCurrentlyFlammable()){ more_flammable_terrain = true; } if(neighbor.Is(TileType.DEMONIC_IDOL)){ final_level_demonic_idol_present = true; } if(neighbor.IsBurning()){ more_fire = true; } } if(final_level_demonic_idol_present){ continue; //this fire never goes out } int chance = 5; if(more_fire){ chance = 10; } if(more_flammable_terrain){ chance = 20; } if(R.OneIn(chance)){ t.RemoveFeature(FeatureType.FIRE); Fire.burning_objects.Remove(t); if(t.name == "floor" && t.type != TileType.BREACHED_WALL){ t.MakeFloorCharred(); } } } } foreach(PhysicalObject o in no_fire){ Fire.burning_objects.Remove(o); } if(Fire.burning_objects.Count > 0){ Event e = new Event(100,EventType.FIRE); Q.Add(e); Fire.fire_event = e; } else{ Fire.fire_event = null; } break; } } if(msg != ""){ if(msg_objs == null){ B.Add(msg); } else{ if(msg_objs.Count == 1 && msg_objs[0] is Actor && (msg_objs[0] as Actor).HasAttr(AttrType.BURROWING)){ //do nothing } else{ B.Add(msg,msg_objs.ToArray()); } } } } }
public void RemoveTargets(Actor a) { //cleanup of references to dead monsters for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(actor[i,j]!=null){ actor[i,j].RemoveTarget(a); } } } Fire.burning_objects.Remove(a); }
public Map(Game g) { //tile = new Tile[ROWS,COLS]; //actor = new Actor[ROWS,COLS]; current_level = 0; Map.player = g.player; Map.Q = g.Q; Map.B = g.B; }
/*public static int Rarity(ActorType type){ int result = 1; if(((int)type)%3 == 2){ result = 2; } if(type == ActorType.PLAYER || type == ActorType.FIRE_DRAKE || type == ActorType.RAT || type == ActorType.DREAM_CLONE){ return 0; } return result; }*/ /*public void UpdateRadius(int from,int to){ UpdateRadius(from,to,false); } public void UpdateRadius(int from,int to,bool change){ if(from > 0){ for(int i=row-from;i<=row+from;++i){ for(int j=col-from;j<=col+from;++j){ if(i>0 && i<ROWS-1 && j>0 && j<COLS-1){ if(!M.tile[i,j].opaque && (HasBresenhamLine(i,j) || M.tile[i,j].HasBresenhamLine(row,col))){ M.tile[i,j].light_value--; } } } } } if(to > 0){ for(int i=row-to;i<=row+to;++i){ for(int j=col-to;j<=col+to;++j){ if(i>0 && i<ROWS-1 && j>0 && j<COLS-1){ if(!M.tile[i,j].opaque && (HasBresenhamLine(i,j) || M.tile[i,j].HasBresenhamLine(row,col))){ M.tile[i,j].light_value++; } } } } } if(change){ light_radius = to; } }*/ public void RemoveTarget(Actor a) { if (target == a) { target = null; } }
public async Task<bool> TakeDamage(DamageType dmgtype, DamageClass damclass, int dmg, Actor source, string cause_of_death) { return await TakeDamage(new Damage(dmgtype, damclass, source, dmg), cause_of_death); }
public async Task<bool> TakeDamage(DamageType dmgtype, DamageClass damclass, int dmg, Actor source) { return await TakeDamage(new Damage(dmgtype, damclass, source, dmg), ""); }
public static Item Create(ConsumableType type,Actor a) { Item i = null; if(a.InventoryCount() < Global.MAX_INVENTORY_SIZE){ i = new Item(proto[type],-1,-1); a.GetItem(i); /*foreach(Item held in a.inv){ if(held.type == type && !held.do_not_stack){ held.quantity++; return held; } } a.inv.Add(i);*/ } else{ i = Create(type,a.row,a.col); } return i; }
public bool KnockObjectBack(Actor a,int knockback_strength,Actor damage_source) { List<Tile> line = null; if(DistanceFrom(a) == 0){ line = GetBestExtendedLineOfEffect(TileInDirection(Global.RandomDirection())); } else{ line = GetBestExtendedLineOfEffect(a); } return KnockObjectBack(a,line,knockback_strength,damage_source); }
static void MainMenu() { ConsoleKeyInfo command; string recentname = "".PadRight(30); int recentdepth = -1; char recentwin = '-'; string recentcause = ""; bool on_highscore_list = false; MouseUI.PushButtonMap(); while(true){ Screen.Blank(); int row = 8; int col = (Global.SCREEN_W - 28) / 2; //centering "Forays into Norrendrin x.y.z", which is 28 chars. Screen.WriteString(row++,col,new cstr(Color.Yellow,"Forays into Norrendrin " + Global.VERSION)); Screen.WriteString(row++,col,new cstr(Color.Green,"".PadRight(28,'-'))); col += 4; //recenter for menu options row++; bool saved_game = File.Exists("forays.sav"); if(!saved_game){ Screen.WriteString(row++,col,"[a] Start a new game"); } else{ Screen.WriteString(row++,col,"[a] Resume saved game"); } Screen.WriteString(row++,col,"[b] How to play"); Screen.WriteString(row++,col,"[c] High scores"); Screen.WriteString(row++,col,"[d] Quit"); for(int i=0;i<4;++i){ Screen.WriteChar(i+row-4,col+1,new colorchar(Color.Cyan,(char)(i+'a'))); MouseUI.CreateButton((ConsoleKey)(i + ConsoleKey.A),false,i+row-4,0,1,Global.SCREEN_W); } Screen.ResetColors(); Screen.SetCursorPosition(Global.MAP_OFFSET_COLS,Global.MAP_OFFSET_ROWS+8); command = Input.ReadKey(); switch(command.KeyChar){ case 'a': { Global.GAME_OVER = false; Global.BOSS_KILLED = false; Global.SAVING = false; Global.LoadOptions(); Game game = new Game(); Actor.attack[ActorType.PLAYER] = new List<AttackInfo>{new AttackInfo(100,2,AttackEffect.NO_CRIT,"& hit *","& miss *","")}; if(!saved_game){ game.player = new Actor(ActorType.PLAYER,"you",'@',Color.White,100,100,0,0,AttrType.HUMANOID_INTELLIGENCE); game.player.inv = new List<Item>(); Actor.feats_in_order = new List<FeatType>(); Actor.spells_in_order = new List<SpellType>(); game.player.weapons.AddLast(new Weapon(WeaponType.SWORD)); game.player.weapons.AddLast(new Weapon(WeaponType.MACE)); game.player.weapons.AddLast(new Weapon(WeaponType.DAGGER)); game.player.weapons.AddLast(new Weapon(WeaponType.STAFF)); game.player.weapons.AddLast(new Weapon(WeaponType.BOW)); game.player.armors.AddLast(new Armor(ArmorType.LEATHER)); game.player.armors.AddLast(new Armor(ArmorType.CHAINMAIL)); game.player.armors.AddLast(new Armor(ArmorType.FULL_PLATE)); } game.M = new Map(game); game.B = new Buffer(game); game.Q = new Queue(game); Map.Q = game.Q; Map.B = game.B; PhysicalObject.M = game.M; PhysicalObject.B = game.B; PhysicalObject.Q = game.Q; PhysicalObject.player = game.player; Event.Q = game.Q; Event.B = game.B; Event.M = game.M; Event.player = game.player; Fire.fire_event = null; Fire.burning_objects = new List<PhysicalObject>(); if(!saved_game){ Actor.player_name = ""; if(File.Exists("name.txt")){ StreamReader file = new StreamReader("name.txt"); string base_name = file.ReadLine(); if(base_name == "%random%"){ Actor.player_name = Global.GenerateCharacterName(); } else{ Actor.player_name = base_name; } int num = 0; if(base_name != "%random%" && file.Peek() != -1){ num = Convert.ToInt32(file.ReadLine()); if(num > 1){ Actor.player_name = Actor.player_name + " " + Global.RomanNumeral(num); } } file.Close(); if(num > 0){ StreamWriter fileout = new StreamWriter("name.txt",false); fileout.WriteLine(base_name); fileout.WriteLine(num+1); fileout.Close(); } } if(Actor.player_name == ""){ MouseUI.PushButtonMap(MouseMode.NameEntry); Screen.Blank(); /*for(int i=4;i<=7;++i){ Screen.WriteMapString(i,0,"".PadToMapSize()); }*/ string s = ""; int name_option = 0; int c = 3; while(true){ Screen.WriteMapString(4,c,"Enter name: "); if(s == ""){ Screen.WriteMapString(6,c,"(Press [Enter] for a random name)".GetColorString()); } else{ Screen.WriteMapString(6,c,"(Press [Enter] when finished) ".GetColorString()); } List<string> name_options = new List<string>{"Default: Choose a new name for each character","Static: Use this name for every character","Legacy: Name all future characters after this one","Random: Name all future characters randomly"}; for(int i=0;i<4;++i){ Color option_color = Color.DarkGray; if(i == name_option){ option_color = Color.White; } Screen.WriteMapString(15+i,c,name_options[i],option_color); } Screen.WriteMapString(20,c,"(Press [Tab] to change naming preference)".GetColorString()); if(name_option != 0){ Screen.WriteMapString(22,c-5,"(To stop naming characters automatically, delete name.txt)",Color.Green); } else{ Screen.WriteMapString(22,c-5,"".PadToMapSize()); } Screen.WriteMapString(4,c+12,s.PadRight(26)); Screen.SetCursorPosition(c + Global.MAP_OFFSET_COLS + 12 + s.Length,Global.MAP_OFFSET_ROWS + 4); MouseUI.CreateButton(ConsoleKey.Enter,false,6+Global.MAP_OFFSET_ROWS,0,1,Global.SCREEN_W); MouseUI.CreateButton(ConsoleKey.Tab,false,20+Global.MAP_OFFSET_ROWS,0,1,Global.SCREEN_W); MouseUI.CreateButton(ConsoleKey.F21,false,15+Global.MAP_OFFSET_ROWS,0,1,Global.SCREEN_W); MouseUI.CreateButton(ConsoleKey.F22,false,16+Global.MAP_OFFSET_ROWS,0,1,Global.SCREEN_W); MouseUI.CreateButton(ConsoleKey.F23,false,17+Global.MAP_OFFSET_ROWS,0,1,Global.SCREEN_W); MouseUI.CreateButton(ConsoleKey.F24,false,18+Global.MAP_OFFSET_ROWS,0,1,Global.SCREEN_W); Screen.CursorVisible = true; command = Input.ReadKey(); if((command.KeyChar >= '!' && command.KeyChar <= '~') || command.KeyChar == ' '){ if(s.Length < 26){ s = s + command.KeyChar; } } else{ if(command.Key == ConsoleKey.Backspace && s.Length > 0){ s = s.Substring(0,s.Length-1); } else{ if(command.Key == ConsoleKey.Escape){ s = ""; } else{ if(command.Key == ConsoleKey.Tab){ name_option = (name_option + 1) % 4; } else{ if(command.Key == ConsoleKey.Enter){ if(s.Length == 0){ s = Global.GenerateCharacterName(); } else{ Actor.player_name = s; break; } } else{ switch(command.Key){ case ConsoleKey.F21: name_option = 0; break; case ConsoleKey.F22: name_option = 1; break; case ConsoleKey.F23: name_option = 2; break; case ConsoleKey.F24: name_option = 3; break; } } } } } } } MouseUI.PopButtonMap(); switch(name_option){ case 1: //static { StreamWriter fileout = new StreamWriter("name.txt",false); fileout.WriteLine(s); fileout.WriteLine(0); fileout.Close(); break; } case 2: //legacy { StreamWriter fileout = new StreamWriter("name.txt",false); fileout.WriteLine(s); fileout.WriteLine(2); fileout.Close(); break; } case 3: //random { StreamWriter fileout = new StreamWriter("name.txt",false); fileout.WriteLine("%random%"); fileout.WriteLine(0); fileout.Close(); break; } } } { Event e = new Event(game.player,0,EventType.MOVE); e.tiebreaker = 0; game.Q.Add(e); } Item.GenerateUnIDedNames(); game.M.GenerateLevelTypes(); game.M.GenerateLevel(); game.player.UpdateRadius(0,6,true); Item.Create(ConsumableType.BANDAGES,game.player).other_data = 5; Item.Create(ConsumableType.FLINT_AND_STEEL,game.player).other_data = 3; game.player.inv[0].revealed_by_light = true; game.player.inv[1].revealed_by_light = true; } else{ //loading FileStream file = new FileStream("forays.sav",FileMode.Open); BinaryReader b = new BinaryReader(file); Dictionary<int,PhysicalObject> id = new Dictionary<int, PhysicalObject>(); id.Add(0,null); Dict<PhysicalObject,int> missing_target_id = new Dict<PhysicalObject, int>(); List<Actor> need_targets = new List<Actor>(); Dict<PhysicalObject,int> missing_location_id = new Dict<PhysicalObject, int>(); List<Actor> need_location = new List<Actor>(); Actor.player_name = b.ReadString(); game.M.current_level = b.ReadInt32(); game.M.level_types = new List<LevelType>(); for(int i=0;i<20;++i){ game.M.level_types.Add((LevelType)b.ReadInt32()); } game.M.wiz_lite = b.ReadBoolean(); game.M.wiz_dark = b.ReadBoolean(); for(int i=0;i<Global.ROWS;++i){ for(int j=0;j<Global.COLS;++j){ game.M.last_seen[i,j].c = b.ReadChar(); game.M.last_seen[i,j].color = (Color)b.ReadInt32(); game.M.last_seen[i,j].bgcolor = (Color)b.ReadInt32(); } } if(game.M.current_level == 21){ game.M.final_level_cultist_count = new int[5]; for(int i=0;i<5;++i){ game.M.final_level_cultist_count[i] = b.ReadInt32(); } game.M.final_level_demon_count = b.ReadInt32(); game.M.final_level_clock = b.ReadInt32(); } Actor.feats_in_order = new List<FeatType>(); Actor.spells_in_order = new List<SpellType>(); int num_featlist = b.ReadInt32(); for(int i=0;i<num_featlist;++i){ Actor.feats_in_order.Add((FeatType)b.ReadInt32()); } int num_spelllist = b.ReadInt32(); for(int i=0;i<num_spelllist;++i){ Actor.spells_in_order.Add((SpellType)b.ReadInt32()); } int num_actor_tiebreakers = b.ReadInt32(); Actor.tiebreakers = new List<Actor>(num_actor_tiebreakers); for(int i=0;i<num_actor_tiebreakers;++i){ int ID = b.ReadInt32(); if(ID != 0){ Actor a = new Actor(); id.Add(ID,a); a.row = b.ReadInt32(); a.col = b.ReadInt32(); if(a.row >= 0 && a.row < Global.ROWS && a.col >= 0 && a.col < Global.COLS){ game.M.actor[a.row,a.col] = a; } Actor.tiebreakers.Add(a); a.name = b.ReadString(); a.the_name = b.ReadString(); a.a_name = b.ReadString(); a.symbol = b.ReadChar(); a.color = (Color)b.ReadInt32(); a.type = (ActorType)b.ReadInt32(); if(a.type == ActorType.PLAYER){ game.player = a; Actor.player = a; Buffer.player = a; Item.player = a; Map.player = a; Event.player = a; Tile.player = a; } a.maxhp = b.ReadInt32(); a.curhp = b.ReadInt32(); a.maxmp = b.ReadInt32(); a.curmp = b.ReadInt32(); a.speed = b.ReadInt32(); a.light_radius = b.ReadInt32(); int target_ID = b.ReadInt32(); if(id.ContainsKey(target_ID)){ a.target = (Actor)id[target_ID]; } else{ a.target = null; need_targets.Add(a); missing_target_id[a] = target_ID; } int num_items = b.ReadInt32(); for(int j=0;j<num_items;++j){ int item_id = b.ReadInt32(); if(item_id != 0){ Item item = new Item(); id.Add(item_id,item); item.name = b.ReadString(); item.the_name = b.ReadString(); item.a_name = b.ReadString(); item.symbol = b.ReadChar(); item.color = (Color)b.ReadInt32(); item.light_radius = b.ReadInt32(); item.type = (ConsumableType)b.ReadInt32(); item.quantity = b.ReadInt32(); item.charges = b.ReadInt32(); item.other_data = b.ReadInt32(); item.ignored = b.ReadBoolean(); item.do_not_stack = b.ReadBoolean(); item.revealed_by_light = b.ReadBoolean(); a.inv.Add(item); } } int num_attrs = b.ReadInt32(); for(int j=0;j<num_attrs;++j){ AttrType t = (AttrType)b.ReadInt32(); a.attrs[t] = b.ReadInt32(); } int num_skills = b.ReadInt32(); for(int j=0;j<num_skills;++j){ SkillType t = (SkillType)b.ReadInt32(); a.skills[t] = b.ReadInt32(); } int num_feats = b.ReadInt32(); for(int j=0;j<num_feats;++j){ FeatType t = (FeatType)b.ReadInt32(); a.feats[t] = b.ReadBoolean(); } int num_spells = b.ReadInt32(); for(int j=0;j<num_spells;++j){ SpellType t = (SpellType)b.ReadInt32(); a.spells[t] = b.ReadBoolean(); } a.exhaustion = b.ReadInt32(); a.time_of_last_action = b.ReadInt32(); a.recover_time = b.ReadInt32(); int path_count = b.ReadInt32(); for(int j=0;j<path_count;++j){ int path_row = b.ReadInt32(); int path_col = b.ReadInt32(); a.path.Add(new pos(path_row,path_col)); } int location_ID = b.ReadInt32(); if(id.ContainsKey(location_ID)){ a.target_location = (Tile)id[location_ID]; } else{ a.target_location = null; need_location.Add(a); missing_location_id[a] = location_ID; } a.player_visibility_duration = b.ReadInt32(); int num_weapons = b.ReadInt32(); for(int j=0;j<num_weapons;++j){ Weapon w = new Weapon(WeaponType.NO_WEAPON); w.type = (WeaponType)b.ReadInt32(); w.enchantment = (EnchantmentType)b.ReadInt32(); int num_statuses = b.ReadInt32(); for(int k=0;k<num_statuses;++k){ EquipmentStatus st = (EquipmentStatus)b.ReadInt32(); bool has_st = b.ReadBoolean(); w.status[st] = has_st; } a.weapons.AddLast(w); } int num_armors = b.ReadInt32(); for(int j=0;j<num_armors;++j){ Armor ar = new Armor(ArmorType.NO_ARMOR); ar.type = (ArmorType)b.ReadInt32(); ar.enchantment = (EnchantmentType)b.ReadInt32(); int num_statuses = b.ReadInt32(); for(int k=0;k<num_statuses;++k){ EquipmentStatus st = (EquipmentStatus)b.ReadInt32(); bool has_st = b.ReadBoolean(); ar.status[st] = has_st; } a.armors.AddLast(ar); } int num_magic_trinkets = b.ReadInt32(); for(int j=0;j<num_magic_trinkets;++j){ a.magic_trinkets.Add((MagicTrinketType)b.ReadInt32()); } } else{ Actor.tiebreakers.Add(null); } } int num_groups = b.ReadInt32(); for(int i=0;i<num_groups;++i){ List<Actor> group = new List<Actor>(); int group_size = b.ReadInt32(); for(int j=0;j<group_size;++j){ group.Add((Actor)id[b.ReadInt32()]); } foreach(Actor a in group){ a.group = group; } } int num_tiles = b.ReadInt32(); for(int i=0;i<num_tiles;++i){ Tile t = new Tile(); int ID = b.ReadInt32(); id.Add(ID,t); t.row = b.ReadInt32(); t.col = b.ReadInt32(); game.M.tile[t.row,t.col] = t; t.name = b.ReadString(); t.the_name = b.ReadString(); t.a_name = b.ReadString(); t.symbol = b.ReadChar(); t.color = (Color)b.ReadInt32(); t.light_radius = b.ReadInt32(); t.type = (TileType)b.ReadInt32(); t.passable = b.ReadBoolean(); t.SetInternalOpacity(b.ReadBoolean()); t.SetInternalSeen(b.ReadBoolean()); //t.seen = b.ReadBoolean(); t.revealed_by_light = b.ReadBoolean(); t.solid_rock = b.ReadBoolean(); t.light_value = b.ReadInt32(); t.direction_exited = b.ReadInt32(); if(b.ReadBoolean()){ //indicates a toggles_into value t.toggles_into = (TileType)b.ReadInt32(); } else{ t.toggles_into = null; } int item_id = b.ReadInt32(); if(item_id != 0){ t.inv = new Item(); id.Add(item_id,t.inv); t.inv.name = b.ReadString(); t.inv.the_name = b.ReadString(); t.inv.a_name = b.ReadString(); t.inv.symbol = b.ReadChar(); t.inv.color = (Color)b.ReadInt32(); t.inv.light_radius = b.ReadInt32(); t.inv.type = (ConsumableType)b.ReadInt32(); t.inv.quantity = b.ReadInt32(); t.inv.charges = b.ReadInt32(); t.inv.other_data = b.ReadInt32(); t.inv.ignored = b.ReadBoolean(); t.inv.do_not_stack = b.ReadBoolean(); t.inv.revealed_by_light = b.ReadBoolean(); } else{ t.inv = null; } int num_features = b.ReadInt32(); for(int j=0;j<num_features;++j){ t.features.Add((FeatureType)b.ReadInt32()); } } foreach(Actor a in need_targets){ if(id.ContainsKey(missing_target_id[a])){ a.target = (Actor)id[missing_target_id[a]]; } else{ throw new Exception("Error: some actors weren't loaded(1). "); } } foreach(Actor a in need_location){ if(id.ContainsKey(missing_location_id[a])){ a.target_location = (Tile)id[missing_location_id[a]]; } else{ throw new Exception("Error: some tiles weren't loaded(2). "); } } int game_turn = b.ReadInt32(); game.Q.turn = -1; //this keeps events from being added incorrectly to the front of the queue while loading. turn is set correctly after events are all loaded. int num_events = b.ReadInt32(); for(int i=0;i<num_events;++i){ Event e = new Event(); if(b.ReadBoolean()){ //if true, this is an item that doesn't exist elsewhere, so grab all its info. int item_id = b.ReadInt32(); if(item_id != 0){ Item item = new Item(); id.Add(item_id,item); item.name = b.ReadString(); item.the_name = b.ReadString(); item.a_name = b.ReadString(); item.symbol = b.ReadChar(); item.color = (Color)b.ReadInt32(); item.light_radius = b.ReadInt32(); item.type = (ConsumableType)b.ReadInt32(); item.quantity = b.ReadInt32(); item.charges = b.ReadInt32(); item.other_data = b.ReadInt32(); item.ignored = b.ReadBoolean(); item.do_not_stack = b.ReadBoolean(); item.revealed_by_light = b.ReadBoolean(); e.target = item; } } else{ int target_ID = b.ReadInt32(); if(id.ContainsKey(target_ID)){ e.target = id[target_ID]; } else{ throw new Exception("Error: some tiles/actors weren't loaded(4). "); } } int area_count = b.ReadInt32(); for(int j=0;j<area_count;++j){ if(e.area == null){ e.area = new List<Tile>(); } int tile_ID = b.ReadInt32(); if(id.ContainsKey(tile_ID)){ e.area.Add((Tile)id[tile_ID]); } else{ throw new Exception("Error: some tiles weren't loaded(5). "); } } e.delay = b.ReadInt32(); e.type = (EventType)b.ReadInt32(); e.attr = (AttrType)b.ReadInt32(); e.feature = (FeatureType)b.ReadInt32(); e.value = b.ReadInt32(); e.secondary_value = b.ReadInt32(); e.msg = b.ReadString(); int objs_count = b.ReadInt32(); for(int j=0;j<objs_count;++j){ if(e.msg_objs == null){ e.msg_objs = new List<PhysicalObject>(); } int obj_ID = b.ReadInt32(); if(id.ContainsKey(obj_ID)){ e.msg_objs.Add(id[obj_ID]); } else{ throw new Exception("Error: some actors/tiles weren't loaded(6). "); } } e.time_created = b.ReadInt32(); e.dead = b.ReadBoolean(); e.tiebreaker = b.ReadInt32(); game.Q.Add(e); if(e.type == EventType.FIRE && !e.dead){ Fire.fire_event = e; } } game.Q.turn = game_turn; foreach(Event e in game.Q.list){ if(e.type == EventType.MOVE && e.target == game.player){ game.Q.current_event = e; break; } } int num_footsteps = b.ReadInt32(); for(int i=0;i<num_footsteps;++i){ int step_row = b.ReadInt32(); int step_col = b.ReadInt32(); Actor.footsteps.Add(new pos(step_row,step_col)); } int num_prev_footsteps = b.ReadInt32(); for(int i=0;i<num_prev_footsteps;++i){ int step_row = b.ReadInt32(); int step_col = b.ReadInt32(); Actor.previous_footsteps.Add(new pos(step_row,step_col)); } Actor.interrupted_path.row = b.ReadInt32(); Actor.interrupted_path.col = b.ReadInt32(); UI.viewing_commands_idx = b.ReadInt32(); game.M.feat_gained_this_level = b.ReadBoolean(); game.M.extra_danger = b.ReadInt32(); int num_unIDed = b.ReadInt32(); for(int i=0;i<num_unIDed;++i){ ConsumableType ct = (ConsumableType)b.ReadInt32(); string s = b.ReadString(); Item.unIDed_name[ct] = s; } int num_IDed = b.ReadInt32(); for(int i=0;i<num_IDed;++i){ ConsumableType ct = (ConsumableType)b.ReadInt32(); bool IDed = b.ReadBoolean(); Item.identified[ct] = IDed; } int num_item_colors = b.ReadInt32(); for(int i=0;i<num_item_colors;++i){ ConsumableType ct = (ConsumableType)b.ReadInt32(); Item.proto[ct].color = (Color)b.ReadInt32(); } int num_burning = b.ReadInt32(); for(int i=0;i<num_burning;++i){ int obj_ID = b.ReadInt32(); if(id.ContainsKey(obj_ID)){ Fire.burning_objects.Add(id[obj_ID]); } else{ throw new Exception("Error: some actors/tiles weren't loaded(7). "); } } string[] messages = new string[Buffer.log_length]; int num_messages = b.ReadInt32(); for(int i=0;i<num_messages;++i){ messages[i] = b.ReadString(); } for(int i=num_messages;i<Buffer.log_length;++i){ messages[i] = ""; } int message_pos = b.ReadInt32(); game.B.LoadMessagesAndPosition(messages,message_pos,num_messages); b.Close(); file.Close(); File.Delete("forays.sav"); Tile.Feature(FeatureType.TELEPORTAL).color = Item.Prototype(ConsumableType.TELEPORTAL).color; game.M.CalculatePoppyDistanceMap(); game.M.UpdateDangerValues(); if(game.M.aesthetics == null) game.M.aesthetics = new PosArray<AestheticFeature>(Global.ROWS,Global.COLS); //todo! save these properly if(game.M.dungeon_description == null){ game.M.dungeon_description = new PosArray<string>(Global.ROWS,Global.COLS); for(int ii=0;ii<Global.ROWS;++ii){ for(int jj=0;jj<Global.COLS;++jj){ game.M.dungeon_description[ii,jj] = ""; } } } //todo fixme hack save properly } Game.NoClose = true; MouseUI.PushButtonMap(MouseMode.Map); MouseUI.CreateStatsButtons(); try{ while(!Global.GAME_OVER){ game.Q.Pop(); } } catch(Exception e){ StreamWriter fileout = new StreamWriter("error.txt",false); fileout.WriteLine(e.Message); fileout.WriteLine(e.StackTrace); fileout.Close(); MouseUI.IgnoreMouseMovement = true; MouseUI.IgnoreMouseClicks = true; Screen.CursorVisible = false; Screen.Blank(); Screen.WriteString(12,0," An error has occured. See error.txt for more details. Press any key to quit.".PadOuter(Global.SCREEN_W)); Input.ReadKey(); Global.Quit(); } MouseUI.PopButtonMap(); MouseUI.IgnoreMouseMovement = false; Game.NoClose = false; Screen.CursorVisible = false; Global.SaveOptions(); recentdepth = game.M.current_level; recentname = Actor.player_name; recentwin = Global.BOSS_KILLED? 'W' : '-'; recentcause = Global.KILLED_BY; on_highscore_list = false; if(!Global.SAVING){ List<string> newhighscores = new List<string>(); int num_scores = 0; bool added = false; if(File.Exists("highscore.txt")){ StreamReader file = new StreamReader("highscore.txt"); string s = ""; while(s.Length < 2 || s.Substring(0,2) != "--"){ s = file.ReadLine(); newhighscores.Add(s); } s = "!!"; while(s.Substring(0,2) != "--"){ s = file.ReadLine(); if(s.Substring(0,2) == "--"){ if(!added && num_scores < Global.HIGH_SCORES){ char symbol = Global.BOSS_KILLED? 'W' : '-'; newhighscores.Add(game.M.current_level.ToString() + " " + symbol + " " + Actor.player_name + " -- " + Global.KILLED_BY); on_highscore_list = true; } newhighscores.Add(s); break; } if(num_scores < Global.HIGH_SCORES){ string[] tokens = s.Split(' '); int dlev = Convert.ToInt32(tokens[0]); if(dlev < game.M.current_level || (dlev == game.M.current_level && Global.BOSS_KILLED)){ if(!added){ char symbol = Global.BOSS_KILLED? 'W' : '-'; newhighscores.Add(game.M.current_level.ToString() + " " + symbol + " " + Actor.player_name + " -- " + Global.KILLED_BY); ++num_scores; added = true; on_highscore_list = true; } if(num_scores < Global.HIGH_SCORES){ newhighscores.Add(s); ++num_scores; } } else{ newhighscores.Add(s); ++num_scores; } } } file.Close(); } else{ newhighscores.Add("High scores:"); newhighscores.Add("--"); char symbol = Global.BOSS_KILLED? 'W' : '-'; newhighscores.Add(game.M.current_level.ToString() + " " + symbol + " " + Actor.player_name + " -- " + Global.KILLED_BY); newhighscores.Add("--"); on_highscore_list = true; } StreamWriter fileout = new StreamWriter("highscore.txt",false); foreach(string str in newhighscores){ fileout.WriteLine(str); } fileout.Close(); } if(!Global.QUITTING && !Global.SAVING){ GameOverScreen(game); } break; } case 'b': { Help.DisplayHelp(); break; } case 'c': { MouseUI.PushButtonMap(); Screen.Blank(); List<string> scores = new List<string>(); { if(!File.Exists("highscore.txt")){ List<string> newhighscores = new List<string>(); newhighscores.Add("High scores:"); newhighscores.Add("--"); newhighscores.Add("--"); StreamWriter fileout = new StreamWriter("highscore.txt",false); foreach(string str in newhighscores){ fileout.WriteLine(str); } fileout.Close(); } StreamReader file = new StreamReader("highscore.txt"); string s = ""; while(s.Length < 2 || s.Substring(0,2) != "--"){ s = file.ReadLine(); } s = "!!"; while(s.Substring(0,2) != "--"){ s = file.ReadLine(); if(s.Substring(0,2) == "--" || scores.Count == Global.HIGH_SCORES){ break; } else{ scores.Add(s); } } file.Close(); } if(scores.Count == Global.HIGH_SCORES && !on_highscore_list && recentdepth != -1){ scores.RemoveLast(); scores.Add(recentdepth.ToString() + " " + recentwin + " " + recentname + " -- " + recentcause); } int longest_name = 0; int longest_cause = 0; foreach(string s in scores){ string[] tokens = s.Split(' '); string name_and_cause_of_death = s.Substring(tokens[0].Length + 3); int idx = name_and_cause_of_death.LastIndexOf(" -- "); string name = name_and_cause_of_death.Substring(0,idx); string cause_of_death = name_and_cause_of_death.Substring(idx+4); if(name.Length > longest_name){ longest_name = name.Length; } if(cause_of_death.Length > longest_cause){ longest_cause = cause_of_death.Length; } } int total_spaces = Global.SCREEN_W - (longest_name + 4 + longest_cause); //max name length is 26 and max cause length is 42. Depth is the '4'. int half_spaces = total_spaces / 2; int half_spaces_offset = (total_spaces+1) / 2; int spaces1 = half_spaces / 4; int spaces2 = half_spaces - (half_spaces / 4); int spaces3 = half_spaces_offset - (half_spaces_offset / 4); int name_middle = spaces1 + longest_name/2; int depth_middle = spaces1 + spaces2 + longest_name + 1; int cause_middle = spaces1 + spaces2 + spaces3 + longest_name + 4 + (longest_cause-1)/2; Color primary = Color.Green; Color recent = Color.Cyan; Screen.WriteString(0,(Global.SCREEN_W - 11) / 2,new cstr("HIGH SCORES",Color.Yellow)); //"HIGH SCORES" has width 11 Screen.WriteString(1,(Global.SCREEN_W - 11) / 2,new cstr("-----------",Color.Cyan)); Screen.WriteString(2,name_middle-4,new cstr("Character",primary)); Screen.WriteString(2,depth_middle-2,new cstr("Depth",primary)); Screen.WriteString(2,cause_middle-6,new cstr("Cause of death",primary)); bool written_recent = false; int line = 3; foreach(string s in scores){ if(line >= Global.SCREEN_H){ break; } string[] tokens = s.Split(' '); int dlev = Convert.ToInt32(tokens[0]); char winning = tokens[1][0]; string name_and_cause_of_death = s.Substring(tokens[0].Length + 3); int idx = name_and_cause_of_death.LastIndexOf(" -- "); string name = name_and_cause_of_death.Substring(0,idx); string cause_of_death = name_and_cause_of_death.Substring(idx+4); string cause_capitalized = cause_of_death.Substring(0,1).ToUpper() + cause_of_death.Substring(1); Color current_color = Color.White; if(!written_recent && name == recentname && dlev == recentdepth && winning == recentwin && cause_of_death == recentcause){ current_color = recent; written_recent = true; } else{ current_color = Color.White; } Screen.WriteString(line,spaces1,new cstr(name,current_color)); Screen.WriteString(line,spaces1 + spaces2 + longest_name,new cstr(dlev.ToString().PadLeft(2),current_color)); Screen.WriteString(line,spaces1 + spaces2 + spaces3 + longest_name + 4,new cstr(cause_capitalized,current_color)); if(winning == 'W'){ Screen.WriteString(line,spaces1 + spaces2 + longest_name + 3,new cstr("W",Color.Yellow)); } ++line; } Input.ReadKey(); MouseUI.PopButtonMap(); break; } case 'd': Global.Quit(); break; default: break; } if(Global.QUITTING){ Global.Quit(); } } }
public bool KnockObjectBack(Actor a,List<Tile> line,int knockback_strength,Actor damage_source) { if(knockback_strength == 0){ //note that TURN_INTO_CORPSE should be set for 'a' - therefore it won't be removed and we can do what we want with it. return a.CollideWith(a.tile()); } int i=0; while(true){ Tile t = line[i]; if(t.actor() == a){ break; } ++i; } line.RemoveRange(0,i+1); if(line.Count == 0){ return a.CollideWith(a.tile()); } bool immobile = a.MovementPrevented(line[0]); string knocked_back_message = ""; if(!a.HasAttr(AttrType.TELEKINETICALLY_THROWN,AttrType.SELF_TK_NO_DAMAGE) && !immobile && player.CanSee(a)){ //if the player can see it now, don't check CanSee later. knocked_back_message = a.YouAre() + " knocked back. "; //B.Add(a.YouAre() + " knocked back. ",a); } int dice = 1; int damage_dice_to_other = 1; if(a.HasAttr(AttrType.TELEKINETICALLY_THROWN)){ dice = 3; damage_dice_to_other = 3; } if(a.HasAttr(AttrType.SELF_TK_NO_DAMAGE)){ dice = 0; } if(a.type == ActorType.SPORE_POD){ dice = 0; damage_dice_to_other = 0; } while(knockback_strength > 1){ //if the knockback strength is greater than 1, you're passing *over* at least one tile. Tile t = line[0]; line.RemoveAt(0); immobile = a.MovementPrevented(t); if(immobile){ if(player.CanSee(a.tile())){ B.Add(a.YouVisibleAre() + " knocked about. ",a); } if(a.type == ActorType.SPORE_POD){ return true; } return a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"crashing into the floor"); } if(!t.passable){ string deathstringname = t.AName(false); if(t.Is(TileType.CRACKED_WALL,TileType.DOOR_C,TileType.HIDDEN_DOOR) && !a.HasAttr(AttrType.SMALL)){ string tilename = t.TheName(true); if(t.type == TileType.HIDDEN_DOOR){ tilename = "a hidden door"; t.Toggle(null); } if(player.CanSee(a.tile())){ B.Add(a.YouVisibleAre() + " knocked through " + tilename + ". ",a,t); } else{ B.Add(knocked_back_message); } knocked_back_message = ""; //knockback_strength -= 2; //removing the distance modification for now t.Toggle(null); a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"slamming into " + deathstringname); a.Move(t.row,t.col); if(a.HasAttr(AttrType.BLEEDING) && !a.HasAttr(AttrType.SHIELDED,AttrType.INVULNERABLE,AttrType.SELF_TK_NO_DAMAGE)){ if(a.type == ActorType.HOMUNCULUS){ if(R.CoinFlip()){ t.AddFeature(FeatureType.OIL); } } else{ if(t.symbol == '.' && t.color == Color.White && R.CoinFlip()){ t.color = a.BloodColor(); } } } } else{ if(player.CanSee(a.tile())){ B.Add(a.YouVisibleAre() + " knocked into " + t.TheName(true) + ". ",a,t); } else{ B.Add(knocked_back_message); } knocked_back_message = ""; if(a.type != ActorType.SPORE_POD){ Color blood = a.BloodColor(); if(blood != Color.Black && R.CoinFlip() && t.Is(TileType.WALL) && !a.HasAttr(AttrType.SHIELDED,AttrType.INVULNERABLE,AttrType.SELF_TK_NO_DAMAGE)){ t.color = blood; } a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"slamming into " + deathstringname); } if(!a.HasAttr(AttrType.SMALL)){ t.Bump(a.DirectionOf(t)); } a.CollideWith(a.tile()); return !a.HasAttr(AttrType.CORPSE); } } else{ if(t.actor() != null){ if(player.CanSee(a.tile()) || player.CanSee(t)){ B.Add(a.YouVisibleAre() + " knocked into " + t.actor().TheName(true) + ". ",a,t.actor()); } else{ B.Add(knocked_back_message); } knocked_back_message = ""; string actorname = t.actor().AName(false); string actorname2 = a.AName(false); if(t.actor().type != ActorType.SPORE_POD && !t.actor().HasAttr(AttrType.SELF_TK_NO_DAMAGE)){ t.actor().TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(damage_dice_to_other,6),damage_source,"colliding with " + actorname2); } if(a.type != ActorType.SPORE_POD){ a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"colliding with " + actorname); } a.CollideWith(a.tile()); return !a.HasAttr(AttrType.CORPSE); } else{ if(t.Is(FeatureType.WEB) && !a.HasAttr(AttrType.SMALL)){ t.RemoveFeature(FeatureType.WEB); } a.Move(t.row,t.col,false); if(t.Is(FeatureType.WEB) && a.HasAttr(AttrType.SMALL) && !a.HasAttr(AttrType.SLIMED,AttrType.OIL_COVERED,AttrType.BURNING)){ knockback_strength = 0; } if(a.HasAttr(AttrType.BLEEDING) && !a.HasAttr(AttrType.SHIELDED,AttrType.INVULNERABLE,AttrType.SELF_TK_NO_DAMAGE)){ if(a.type == ActorType.HOMUNCULUS){ if(R.CoinFlip()){ t.AddFeature(FeatureType.OIL); } } else{ if(t.symbol == '.' && t.color == Color.White && R.CoinFlip()){ t.color = a.BloodColor(); } } } } } M.Draw(); knockback_strength--; } if(knockback_strength < 1){ return !a.HasAttr(AttrType.CORPSE); } bool slip = false; int extra_slip_tiles = -1; bool slip_message_printed = false; do{ Tile t = line[0]; line.RemoveAt(0); immobile = a.MovementPrevented(t); if(immobile){ if(player.CanSee(a.tile())){ B.Add(a.YouVisibleAre() + " knocked about. ",a); } if(a.type == ActorType.SPORE_POD){ return true; } return a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"crashing into the floor"); } if(!t.passable){ string deathstringname = t.AName(false); if(t.Is(TileType.CRACKED_WALL,TileType.DOOR_C,TileType.HIDDEN_DOOR) && !a.HasAttr(AttrType.SMALL)){ string tilename = t.TheName(true); if(t.type == TileType.HIDDEN_DOOR){ tilename = "a hidden door"; t.Toggle(null); } if(player.CanSee(a.tile())){ B.Add(a.YouVisibleAre() + " knocked through " + tilename + ". ",a,t); } else{ B.Add(knocked_back_message); } knocked_back_message = ""; t.Toggle(null); a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"slamming into " + deathstringname); a.Move(t.row,t.col); if(a.HasAttr(AttrType.BLEEDING) && !a.HasAttr(AttrType.SHIELDED,AttrType.INVULNERABLE,AttrType.SELF_TK_NO_DAMAGE)){ if(a.type == ActorType.HOMUNCULUS){ if(R.CoinFlip()){ t.AddFeature(FeatureType.OIL); } } else{ if(t.symbol == '.' && t.color == Color.White && R.CoinFlip()){ t.color = a.BloodColor(); } } } return !a.HasAttr(AttrType.CORPSE); } else{ if(player.CanSee(a.tile())){ B.Add(a.YouVisibleAre() + " knocked into " + t.TheName(true) + ". ",a,t); } else{ B.Add(knocked_back_message); } knocked_back_message = ""; if(a.type != ActorType.SPORE_POD){ Color blood = a.BloodColor(); if(blood != Color.Black && R.CoinFlip() && t.Is(TileType.WALL) && !a.HasAttr(AttrType.SHIELDED,AttrType.INVULNERABLE,AttrType.SELF_TK_NO_DAMAGE)){ t.color = blood; } a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"slamming into " + deathstringname); } if(!a.HasAttr(AttrType.SMALL)){ t.Bump(a.DirectionOf(t)); } a.CollideWith(a.tile()); return !a.HasAttr(AttrType.CORPSE); } } else{ if(t.actor() != null){ if(player.CanSee(a.tile()) || player.CanSee(t)){ B.Add(a.YouVisibleAre() + " knocked into " + t.actor().TheName(true) + ". ",a,t.actor()); } else{ B.Add(knocked_back_message); } knocked_back_message = ""; string actorname = t.actor().AName(false); string actorname2 = a.AName(false); if(t.actor().type != ActorType.SPORE_POD && !t.actor().HasAttr(AttrType.SELF_TK_NO_DAMAGE)){ t.actor().TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(damage_dice_to_other,6),damage_source,"colliding with " + actorname2); } if(a.type != ActorType.SPORE_POD){ a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(dice,6),damage_source,"colliding with " + actorname); } a.CollideWith(a.tile()); return !a.HasAttr(AttrType.CORPSE); } else{ slip = false; if(t.IsSlippery()){ B.Add(knocked_back_message); knocked_back_message = ""; slip = true; if(!slip_message_printed){ slip_message_printed = true; B.Add(a.You("slide") + "! "); } } else{ if(extra_slip_tiles > 0){ extra_slip_tiles--; } if(extra_slip_tiles == -1 && a.HasAttr(AttrType.SLIMED,AttrType.OIL_COVERED) && !t.IsWater()){ B.Add(knocked_back_message); knocked_back_message = ""; extra_slip_tiles = 2; if(!slip_message_printed){ slip_message_printed = true; B.Add(a.You("slide") + "! "); } } } /*if(extra_slip_tiles > 0){ extra_slip_tiles--; } if(t.IsSlippery()){ B.Add(knocked_back_message); knocked_back_message = ""; slip = true; if(!slip_message_printed){ slip_message_printed = true; B.Add(a.You("slide") + "! "); } } else{ if(extra_slip_tiles == -1 && a.HasAttr(AttrType.SLIMED,AttrType.OIL_COVERED)){ B.Add(knocked_back_message); knocked_back_message = ""; extra_slip_tiles = 2; if(!slip_message_printed){ slip_message_printed = true; B.Add(a.You("slide") + "! "); } } }*/ bool interrupted = false; if(t.inv != null && t.inv.type == ConsumableType.DETONATION){ //this will cause a new knockback effect and end the current one B.Add(knocked_back_message); knocked_back_message = ""; interrupted = true; } if(t.IsTrap()){ if(t.type == TileType.FLING_TRAP){ //otherwise you'd teleport around, continuing to slide from your previous position. interrupted = true; } B.Add(knocked_back_message); knocked_back_message = ""; } if(t.Is(FeatureType.WEB) && !a.HasAttr(AttrType.SMALL)){ t.RemoveFeature(FeatureType.WEB); } a.Move(t.row,t.col); if(a.HasAttr(AttrType.BLEEDING) && !a.HasAttr(AttrType.SHIELDED,AttrType.INVULNERABLE,AttrType.SELF_TK_NO_DAMAGE)){ if(a.type == ActorType.HOMUNCULUS){ if(R.CoinFlip()){ t.AddFeature(FeatureType.OIL); } } else{ if(t.symbol == '.' && t.color == Color.White && R.CoinFlip()){ t.color = a.BloodColor(); } } } if(a.HasAttr(AttrType.FROZEN)){ interrupted = true; } if(a.HasAttr(AttrType.SMALL) && t.Is(FeatureType.WEB) && !a.HasAttr(AttrType.SLIMED,AttrType.OIL_COVERED,AttrType.BURNING)){ B.Add(knocked_back_message); interrupted = true; } else{ if(a.tile().IsWater()){ interrupted = true; } B.Add(knocked_back_message); a.CollideWith(a.tile()); } knocked_back_message = ""; if(interrupted){ return !a.HasAttr(AttrType.CORPSE); } } } M.Draw(); } while(slip || extra_slip_tiles > 0); if(knocked_back_message != ""){ B.Add(knocked_back_message); //this probably never happens } return !a.HasAttr(AttrType.CORPSE); }
public void Toggle(Actor toggler) { if(toggles_into != null){ Toggle(toggler,toggles_into.Value); } }
public Actor(Actor a, int r, int c) { atype = a.atype; name = a.name; the_name = a.the_name; a_name = a.a_name; symbol = a.symbol; color = a.color; maxhp = a.maxhp; curhp = maxhp; speed = a.speed; level = a.level; light_radius = a.light_radius; target = null; F = new SpellType[13]; for (int i = 0; i < 13; ++i) { F[i] = SpellType.NO_SPELL; } inv = new List<Item>(); row = r; col = c; target_location = null; time_of_last_action = 0; recover_time = 0; player_visibility_duration = 0; weapons = new List<WeaponType>(a.weapons); armors = new List<ArmorType>(a.armors); magic_items = new List<MagicItemType>(a.magic_items); attrs = new Dict<AttrType, int>(a.attrs); skills = new Dict<SkillType, int>(a.skills); feats = new Dict<FeatType, int>(a.feats); spells = new Dict<SpellType, int>(a.spells); magic_penalty = 0; }
public static bool StatusByStrength(this AttrType attr,Actor a,Event e,ref int value,ref int max) { switch(attr){ case AttrType.FROZEN: max = 35; break; case AttrType.POPPY_COUNTER: max = 4; break; case AttrType.RESTING: max = 10; break; case AttrType.BLIND: if(a.type == ActorType.DARKNESS_DWELLER && e != null && e.delay == 100){ value = 8 - a.attrs[AttrType.COOLDOWN_1]; max = 7; return true; } else{ return false; } case AttrType.LIFESPAN: max = Actor.Prototype(a.type).attrs[AttrType.LIFESPAN]; break; case AttrType.AMNESIA_STUN: max = 6; break; case AttrType.BLEEDING: max = 25; break; case AttrType.BANDAGED: max = 20; break; case AttrType.ASLEEP: max = 5; break; case AttrType.FLYING_LEAP: if(e != null){ value = e.delay + e.time_created + 50 - Q.turn; max = e.delay - 50; return true; } break; case AttrType.DETECTING_MONSTERS: if(a != null && a.type == ActorType.ROBED_ZEALOT){ max = 4; } else{ return false; } break; default: return false; } value = a.attrs[attr]; return true; }
public bool Use(Actor user) { return Use(user,null); }
public static string StatusName(this AttrType attr,Actor a) { switch(attr){ case AttrType.ROOTS: return "Rooted"; case AttrType.BLIND: return "Blinded"; case AttrType.SUSCEPTIBLE_TO_CRITS: return "Off-balance"; case AttrType.PSEUDO_VAMPIRIC: return "Vampiric"; case AttrType.BLOOD_BOILED: return "Boiling blood"; //"Blood-boiled", hmm. case AttrType.VIGOR: return "Hasted"; case AttrType.AMNESIA_STUN: return "Amnesiac"; case AttrType.DIM_VISION: return "Dimmed vision"; case AttrType.SHADOW_CLOAK: return "Shadow cloaked"; case AttrType.EMPOWERED_SPELLS: return "Empowered magic"; case AttrType.POPPY_COUNTER: return "Breathing poppies"; case AttrType.REGENERATING: if(a != null && a.attrs[attr] > 1){ return "Regenerating " + a.attrs[attr].ToString(); } break; case AttrType.CHILLED: if(a != null && a.attrs[attr] > 1){ return "Chilled " + a.attrs[attr].ToString(); } break; case AttrType.DETECTING_MONSTERS: if(a != null && a.type == ActorType.ROBED_ZEALOT){ return "Praying"; } break; //case AttrType.IMMOBILE: //return "Immobilized"; } return attr.ToString().ToLower().Capitalize().Replace('_',' '); }
public bool Use(Actor user,List<Tile> line) { bool used = true; bool IDed = true; switch(type){ case ConsumableType.HEALING: user.curhp = user.maxhp; B.Add(user.Your() + " wounds are healed completely. ",user); break; case ConsumableType.REGENERATION: { if(user == player){ B.Add("Your blood tingles. "); } else{ B.Add(user.the_name + " looks energized. ",user); } user.attrs[AttrType.REGENERATING]++; int duration = 100; Q.Add(new Event(user,duration*100,AttrType.REGENERATING)); break; } case ConsumableType.STONEFORM: { B.Add(user.You("transform") + " into a being of animated stone. ",user); int duration = R.Roll(2,20) + 20; List<AttrType> attributes = new List<AttrType>{AttrType.REGENERATING,AttrType.BRUTISH_STRENGTH,AttrType.VIGOR,AttrType.SILENCE_AURA,AttrType.SHADOW_CLOAK,AttrType.CAN_DODGE,AttrType.MENTAL_IMMUNITY,AttrType.DETECTING_MONSTERS,AttrType.MYSTIC_MIND}; foreach(AttrType at in attributes){ //in the rare case where a monster drinks this potion, it can lose these natural statuses permanently. this might eventually be fixed. if(user.HasAttr(at)){ user.attrs[at] = 0; Q.KillEvents(user,at); switch(at){ case AttrType.REGENERATING: B.Add(user.You("no longer regenerate") + ". ",user); break; case AttrType.BRUTISH_STRENGTH: B.Add(user.Your() + " brutish strength fades. ",user); break; case AttrType.VIGOR: B.Add(user.Your() + " extraordinary speed fades. ",user); break; case AttrType.SILENCED: B.Add(user.You("no longer radiate") + " an aura of silence. ",user); break; case AttrType.SHADOW_CLOAK: B.Add(user.YouAre() + " no longer cloaked. ",user); break; case AttrType.MYSTIC_MIND: B.Add(user.Your() + " consciousness returns to normal. ",user); break; } } } if(user.HasAttr(AttrType.PSEUDO_VAMPIRIC)){ user.attrs[AttrType.LIGHT_SENSITIVE] = 0; user.attrs[AttrType.FLYING] = 0; user.attrs[AttrType.PSEUDO_VAMPIRIC] = 0; Q.KillEvents(user,AttrType.LIGHT_SENSITIVE); Q.KillEvents(user,AttrType.FLYING); Q.KillEvents(user,AttrType.PSEUDO_VAMPIRIC); B.Add(user.YouAre() + " no longer vampiric. ",user); } if(user.HasAttr(AttrType.ROOTS)){ foreach(Event e in Q.list){ if(e.target == user && !e.dead){ if(e.attr == AttrType.IMMOBILE && e.msg.Contains("rooted to the ground")){ e.dead = true; user.attrs[AttrType.IMMOBILE]--; B.Add(user.YouAre() + " no longer rooted to the ground. ",user); } else{ if(e.attr == AttrType.BONUS_DEFENSE && e.value == 10){ e.dead = true; //this would break if there were other timed effects that gave the same amount of defense user.attrs[AttrType.BONUS_DEFENSE] -= 10; } else{ if(e.attr == AttrType.ROOTS){ e.dead = true; user.attrs[AttrType.ROOTS]--; } } } } } } if(user.HasAttr(AttrType.BURNING)){ user.RefreshDuration(AttrType.BURNING,0); } user.attrs[AttrType.IMMUNE_BURNING]++; Q.Add(new Event(user,duration*100,AttrType.IMMUNE_BURNING)); user.attrs[AttrType.DAMAGE_RESISTANCE]++; Q.Add(new Event(user,duration*100,AttrType.DAMAGE_RESISTANCE)); user.attrs[AttrType.NONLIVING]++; Q.Add(new Event(user,duration*100,AttrType.NONLIVING)); user.RefreshDuration(AttrType.STONEFORM,duration*100,user.Your() + " rocky form reverts to flesh. ",user); if(user == player){ Help.TutorialTip(TutorialTopic.Stoneform); } break; } case ConsumableType.VAMPIRISM: { B.Add(user.You("become") + " vampiric. ",user); B.Add(user.You("rise") + " into the air. ",user); int duration = R.Roll(2,20) + 20; user.RefreshDuration(AttrType.LIGHT_SENSITIVE,duration*100); user.RefreshDuration(AttrType.FLYING,duration*100); user.attrs[AttrType.DESCENDING] = 0; user.RefreshDuration(AttrType.PSEUDO_VAMPIRIC,duration*100,user.YouAre() + " no longer vampiric. ",user); if(user == player){ Help.TutorialTip(TutorialTopic.Vampirism); } break; } case ConsumableType.BRUTISH_STRENGTH: { if(user == player){ B.Add("You feel a surge of strength. "); } else{ B.Add(user.Your() + " muscles ripple. ",user); } user.RefreshDuration(AttrType.BRUTISH_STRENGTH,(R.Roll(3,6)+16)*100,user.Your() + " incredible strength wears off. ",user); if(user == player){ Help.TutorialTip(TutorialTopic.BrutishStrength); } break; } case ConsumableType.ROOTS: { if(user.HasAttr(AttrType.ROOTS)){ foreach(Event e in Q.list){ if(e.target == user && !e.dead){ if(e.attr == AttrType.IMMOBILE && e.msg.Contains("rooted to the ground")){ e.dead = true; user.attrs[AttrType.IMMOBILE]--; } else{ if(e.attr == AttrType.BONUS_DEFENSE && e.value == 10){ e.dead = true; //this would break if there were other timed effects that gave 5 defense user.attrs[AttrType.BONUS_DEFENSE] -= 10; } else{ if(e.attr == AttrType.ROOTS){ e.dead = true; user.attrs[AttrType.ROOTS]--; } } } } } B.Add(user.Your() + " roots extend deeper into the ground. ",user); } else{ B.Add(user.You("grow") + " roots and a hard shell of bark. ",user); } int duration = R.Roll(20) + 20; user.RefreshDuration(AttrType.ROOTS,duration*100); user.attrs[AttrType.BONUS_DEFENSE] += 10; Q.Add(new Event(user,duration*100,AttrType.BONUS_DEFENSE,10)); user.attrs[AttrType.IMMOBILE]++; Q.Add(new Event(user,duration*100,AttrType.IMMOBILE,user.YouAre() + " no longer rooted to the ground. ",user)); if(user == player){ Help.TutorialTip(TutorialTopic.Roots); } if(user.HasAttr(AttrType.FLYING) && user.tile().IsTrap()){ user.tile().TriggerTrap(); } break; } case ConsumableType.HASTE: { B.Add(user.You("start") + " moving with extraordinary speed. ",user); int duration = (R.Roll(2,10) + 10) * 100; user.RefreshDuration(AttrType.CAN_DODGE,duration); //todo: dodging tip goes here user.RefreshDuration(AttrType.VIGOR,duration,user.Your() + " extraordinary speed fades. ",user); if(user == player){ Help.TutorialTip(TutorialTopic.IncreasedSpeed); } break; } case ConsumableType.SILENCE: { B.Add("A hush falls around " + user.the_name + ". ",user); user.RefreshDuration(AttrType.SILENCE_AURA,(R.Roll(2,20)+20)*100,user.You("no longer radiate") + " an aura of silence. ",user); if(user == player){ Help.TutorialTip(TutorialTopic.Silenced); } break; } case ConsumableType.CLOAKING: if(user.tile().IsLit()){ if(user == player){ B.Add("You would feel at home in the shadows. "); } else{ B.Add("A shadow moves across " + user.the_name + ". ",user); } } else{ B.Add(user.You("fade") + " away in the darkness. ",user); } user.RefreshDuration(AttrType.SHADOW_CLOAK,(R.Roll(2,20)+30)*100,user.YouAre() + " no longer cloaked. ",user); break; case ConsumableType.MYSTIC_MIND: { B.Add(user.Your() + " mind expands. ",user); int duration = R.Roll(2,20)+60; user.attrs[AttrType.ASLEEP] = 0; //user.RefreshDuration(AttrType.MAGICAL_DROWSINESS,0); user.RefreshDuration(AttrType.CONFUSED,0); user.RefreshDuration(AttrType.STUNNED,0); user.RefreshDuration(AttrType.ENRAGED,0); user.RefreshDuration(AttrType.MENTAL_IMMUNITY,duration*100); user.RefreshDuration(AttrType.DETECTING_MONSTERS,duration*100); user.RefreshDuration(AttrType.MYSTIC_MIND,duration*100,user.Your() + " consciousness returns to normal. ",user); if(user == player){ Help.TutorialTip(TutorialTopic.MysticMind); } break; } case ConsumableType.BLINKING: { List<Tile> tiles = user.TilesWithinDistance(8).Where(x => x.passable && x.actor() == null && user.ApproximateEuclideanDistanceFromX10(x) >= 45); if(tiles.Count > 0 && !user.HasAttr(AttrType.IMMOBILE)){ Tile t = tiles.Random(); B.Add(user.You("step") + " through a rip in reality. ",M.tile[user.p],t); user.AnimateStorm(2,3,4,'*',Color.DarkMagenta); user.Move(t.row,t.col); M.Draw(); user.AnimateStorm(2,3,4,'*',Color.DarkMagenta); } else{ B.Add("Nothing happens. ",user); IDed = false; } break; } case ConsumableType.PASSAGE: { if(user.HasAttr(AttrType.IMMOBILE)){ B.Add("Nothing happens. ",user); IDed = false; break; } List<int> valid_dirs = new List<int>(); foreach(int dir in U.FourDirections){ Tile t = user.TileInDirection(dir); if(t != null && t.Is(TileType.WALL,TileType.CRACKED_WALL,TileType.WAX_WALL,TileType.DOOR_C,TileType.HIDDEN_DOOR,TileType.STONE_SLAB)){ while(!t.passable){ if(t.row == 0 || t.row == Global.ROWS-1 || t.col == 0 || t.col == Global.COLS-1){ break; } t = t.TileInDirection(dir); } if(t.passable){ valid_dirs.Add(dir); } } } if(valid_dirs.Count > 0){ int dir = valid_dirs.Random(); Tile t = user.TileInDirection(dir); colorchar ch = new colorchar(Color.Cyan,'!'); switch(user.DirectionOf(t)){ case 8: case 2: ch.c = '|'; break; case 4: case 6: ch.c = '-'; break; } List<Tile> tiles = new List<Tile>(); List<colorchar> memlist = new List<colorchar>(); Screen.CursorVisible = false; Tile last_wall = null; while(!t.passable){ tiles.Add(t); memlist.Add(Screen.MapChar(t.row,t.col)); Screen.WriteMapChar(t.row,t.col,ch); Game.GLUpdate(); Thread.Sleep(35); last_wall = t; t = t.TileInDirection(dir); } Input.FlushInput(); if(t.actor() == null){ int r = user.row; int c = user.col; user.Move(t.row,t.col); Screen.WriteMapChar(r,c,M.VisibleColorChar(r,c)); Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); int idx = 0; foreach(Tile tile in tiles){ Screen.WriteMapChar(tile.row,tile.col,memlist[idx++]); Game.GLUpdate(); Thread.Sleep(35); } Input.FlushInput(); B.Add(user.You("travel") + " through the passage. ",user,t); } else{ Tile destination = null; List<Tile> adjacent = t.TilesAtDistance(1).Where(x=>x.passable && x.actor() == null && x.DistanceFrom(last_wall) == 1); if(adjacent.Count > 0){ destination = adjacent.Random(); } else{ foreach(Tile tile in M.ReachableTilesByDistance(t.row,t.col,false)){ if(tile.actor() == null){ destination = tile; break; } } } if(destination != null){ int r = user.row; int c = user.col; user.Move(destination.row,destination.col); Screen.WriteMapChar(r,c,M.VisibleColorChar(r,c)); Screen.WriteMapChar(destination.row,destination.col,M.VisibleColorChar(destination.row,destination.col)); int idx = 0; foreach(Tile tile in tiles){ Screen.WriteMapChar(tile.row,tile.col,memlist[idx++]); Game.GLUpdate(); Thread.Sleep(35); } Input.FlushInput(); B.Add(user.You("travel") + " through the passage. ",user,destination); } else{ B.Add("Something blocks " + user.Your() + " movement through the passage. ",user); } } } else{ B.Add("Nothing happens. ",user); IDed = false; } break; } case ConsumableType.TIME: if(user == player){ B.Add("Time stops for a moment. ",user); } else{ B.Add("Time warps around " + user.the_name + "! ",user); B.PrintAll(); } if(Fire.fire_event == null){ //this prevents fire from updating while time is frozen Fire.fire_event = new Event(0,EventType.FIRE); Fire.fire_event.tiebreaker = 0; Q.Add(Fire.fire_event); } Q.turn -= 200; break; case ConsumableType.KNOWLEDGE: { if(user == player){ B.Add("Knowledge fills your mind. "); Event hiddencheck = null; foreach(Event e in Q.list){ if(!e.dead && e.type == EventType.CHECK_FOR_HIDDEN){ hiddencheck = e; break; } } int max_dist = 0; List<Tile> last_tiles = new List<Tile>(); foreach(Tile t in M.ReachableTilesByDistance(user.row,user.col,true,TileType.STONE_SLAB,TileType.DOOR_C,TileType.STALAGMITE,TileType.RUBBLE,TileType.HIDDEN_DOOR)){ if(t.type != TileType.FLOOR){ t.seen = true; if(t.type != TileType.WALL){ t.revealed_by_light = true; } if(t.IsTrap() || t.Is(TileType.HIDDEN_DOOR)){ if(hiddencheck != null){ hiddencheck.area.Remove(t); } } if(t.IsTrap()){ t.name = Tile.Prototype(t.type).name; t.a_name = Tile.Prototype(t.type).a_name; t.the_name = Tile.Prototype(t.type).the_name; t.symbol = Tile.Prototype(t.type).symbol; t.color = Tile.Prototype(t.type).color; } if(t.Is(TileType.HIDDEN_DOOR)){ t.Toggle(null); } colorchar ch2 = Screen.BlankChar(); if(t.inv != null){ t.inv.revealed_by_light = true; ch2.c = t.inv.symbol; ch2.color = t.inv.color; M.last_seen[t.row,t.col] = ch2; } else{ if(t.features.Count > 0){ ch2 = t.FeatureVisual(); M.last_seen[t.row,t.col] = ch2; } else{ ch2.c = t.symbol; ch2.color = t.color; if(ch2.c == '#' && ch2.color == Color.RandomGlowingFungus){ ch2.color = Color.Gray; } M.last_seen[t.row,t.col] = ch2; } } Screen.WriteMapChar(t.row,t.col,t.symbol,Color.RandomRainbow); //Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); if(user.DistanceFrom(t) > max_dist){ max_dist = user.DistanceFrom(t); Game.GLUpdate(); Thread.Sleep(10); while(last_tiles.Count > 0){ Tile t2 = last_tiles.RemoveRandom(); Screen.WriteMapChar(t2.row,t2.col,M.last_seen[t2.row,t2.col]); //Screen.WriteMapChar(t2.row,t2.col,M.VisibleColorChar(t2.row,t2.col)); } } last_tiles.Add(t); } } if(user.inv.Count > 0){ foreach(Item i in user.inv){ identified[i.type] = true; if(i.NameOfItemType() == "wand"){ i.other_data = -1; } } } } else{ B.Add(user.the_name + " looks more knowledgeable. ",user); } break; } case ConsumableType.SUNLIGHT: if(M.wiz_lite == false){ B.Add("The air itself seems to shine. "); M.wiz_lite = true; M.wiz_dark = false; Q.KillEvents(null,EventType.NORMAL_LIGHTING); Q.Add(new Event((R.Roll(2,20) + 120) * 100,EventType.NORMAL_LIGHTING)); } else{ B.Add("The air grows even brighter for a moment. "); Q.KillEvents(null,EventType.NORMAL_LIGHTING); Q.Add(new Event((R.Roll(2,20) + 120) * 100,EventType.NORMAL_LIGHTING)); } break; case ConsumableType.DARKNESS: if(M.wiz_dark == false){ B.Add("The air itself grows dark. "); if(player.light_radius > 0){ B.Add("Your light is extinguished! "); } M.wiz_dark = true; M.wiz_lite = false; Q.KillEvents(null,EventType.NORMAL_LIGHTING); Q.Add(new Event((R.Roll(2,20) + 120) * 100,EventType.NORMAL_LIGHTING)); } else{ B.Add("The air grows even darker for a moment. "); Q.KillEvents(null,EventType.NORMAL_LIGHTING); Q.Add(new Event((R.Roll(2,20) + 120) * 100,EventType.NORMAL_LIGHTING)); } break; case ConsumableType.RENEWAL: { B.Add("A glow envelops " + user.the_name + ". ",user); //B.Add("A glow envelops " + user.Your() + " equipment. ",user); bool repaired = false; foreach(EquipmentStatus eqstatus in Enum.GetValues(typeof(EquipmentStatus))){ foreach(Weapon w in user.weapons){ if(w.status[eqstatus]){ repaired = true; w.status[eqstatus] = false; } } foreach(Armor a in user.armors){ if(a.status[eqstatus]){ repaired = true; a.status[eqstatus] = false; } } } if(repaired){ B.Add(user.Your() + " equipment looks as good as new! ",user); } if(user.HasAttr(AttrType.SLIMED)){ B.Add(user.YouAre() + " no longer covered in slime. ",user); user.attrs[AttrType.SLIMED] = 0; } if(user.HasAttr(AttrType.OIL_COVERED)){ B.Add(user.YouAre() + " no longer covered in oil. ",user); user.attrs[AttrType.OIL_COVERED] = 0; } int recharged = 0; foreach(Item i in user.inv){ if(i.NameOfItemType() == "wand"){ i.charges++; recharged++; } } if(recharged > 0){ if(recharged == 1){ B.Add("The glow charges " + user.Your() + " wand. ",user); } else{ B.Add("The glow charges " + user.Your() + " wands. ",user); } } break; } case ConsumableType.CALLING: { bool found = false; if(user == player){ for(int dist = 1;dist < Math.Max(Global.ROWS,Global.COLS);++dist){ List<Tile> tiles = user.TilesAtDistance(dist).Where(x=>x.actor() != null && !x.actor().HasAttr(AttrType.IMMOBILE)); if(tiles.Count > 0){ Actor a = tiles.Random().actor(); Tile t2 = user.TileInDirection(user.DirectionOf(a)); if(t2.passable && t2.actor() == null){ B.Add("The scroll calls " + a.a_name + " to you. "); a.Move(t2.row,t2.col); found = true; break; } foreach(Tile t in M.ReachableTilesByDistance(user.row,user.col,false)){ if(t.actor() == null){ B.Add("The scroll calls " + a.a_name + " to you. "); a.Move(t.row,t.col); found = true; break; } } if(found){ break; } } } } else{ if(!player.HasAttr(AttrType.IMMOBILE) && user.DistanceFrom(player) > 1){ Tile t2 = user.TileInDirection(user.DirectionOf(player)); if(t2.passable && t2.actor() == null){ B.Add("The scroll calls you to " + user.TheName(true) + ". "); player.Move(t2.row,t2.col); found = true; } if(!found){ foreach(Tile t in M.ReachableTilesByDistance(user.row,user.col,false)){ if(t.actor() == null){ B.Add("The scroll calls you to " + user.TheName(true) + ". "); player.Move(t.row,t.col); found = true; break; } } } } } if(!found){ B.Add("Nothing happens. ",user); IDed = false; } break; } case ConsumableType.TRAP_CLEARING: { List<Tile> traps = new List<Tile>(); { List<Tile>[] traparray = new List<Tile>[5]; for(int i=0;i<5;++i){ traparray[i] = new List<Tile>(); } for(int i=0;i<=12;++i){ foreach(Tile t in user.TilesAtDistance(i)){ //all this ensures that the traps go off in the best order switch(t.type){ case TileType.ALARM_TRAP: case TileType.TELEPORT_TRAP: case TileType.ICE_TRAP: case TileType.BLINDING_TRAP: case TileType.SHOCK_TRAP: case TileType.FIRE_TRAP: case TileType.SCALDING_OIL_TRAP: traparray[0].Add(t); break; case TileType.POISON_GAS_TRAP: case TileType.GRENADE_TRAP: traparray[1].Add(t); break; case TileType.SLIDING_WALL_TRAP: case TileType.PHANTOM_TRAP: traparray[2].Add(t); break; case TileType.LIGHT_TRAP: case TileType.DARKNESS_TRAP: traparray[3].Add(t); break; case TileType.FLING_TRAP: case TileType.STONE_RAIN_TRAP: traparray[4].Add(t); break; } } } for(int i=0;i<5;++i){ foreach(Tile t in traparray[i]){ traps.Add(t); } } } if(traps.Count > 0){ B.Add("*CLICK*. "); foreach(Tile t in traps){ t.TriggerTrap(false); } } else{ B.Add("Nothing happens. ",user); IDed = false; } break; } case ConsumableType.ENCHANTMENT: { if(user == player){ EnchantmentType ench = (EnchantmentType)R.Between(0,4); while(ench == user.EquippedWeapon.enchantment){ ench = (EnchantmentType)R.Between(0,4); } B.Add("Your " + user.EquippedWeapon.NameWithEnchantment() + " glows brightly! "); user.EquippedWeapon.enchantment = ench; B.Add("Your " + user.EquippedWeapon.NameWithoutEnchantment() + " is now a " + user.EquippedWeapon.NameWithEnchantment() + "! "); } else{ B.Add("Nothing happens. ",user); IDed = false; } break; } case ConsumableType.THUNDERCLAP: { B.Add("Thunder crashes! ",user); var scr = Screen.GetCurrentMap(); List<Tile>[] printed = new List<Tile>[13]; Color leading_edge_color = Color.White; Color trail_color = Color.DarkCyan; if(Global.LINUX && !Screen.GLMode){ leading_edge_color = Color.Gray; } for(int dist=0;dist<=12;++dist){ printed[dist] = new List<Tile>(); foreach(Tile t in user.TilesAtDistance(dist)){ if(t.seen && user.HasLOE(t)){ printed[dist].Add(t); } } foreach(Tile t in printed[dist]){ colorchar cch = M.VisibleColorChar(t.row,t.col); cch.bgcolor = leading_edge_color; if(cch.color == leading_edge_color){ cch.color = Color.Black; } Screen.WriteMapChar(t.row,t.col,cch); } if(dist > 0){ foreach(Tile t in printed[dist-1]){ colorchar cch = M.VisibleColorChar(t.row,t.col); cch.bgcolor = trail_color; if(cch.color == trail_color){ cch.color = Color.Black; } Screen.WriteMapChar(t.row,t.col,cch); } if(dist > 4){ foreach(Tile t in printed[dist-5]){ Screen.WriteMapChar(t.row,t.col,scr[t.row,t.col]); } } } Game.GLUpdate(); Thread.Sleep(10); } List<Actor> actors = new List<Actor>(); for(int dist=0;dist<=12;++dist){ foreach(Tile t in user.TilesAtDistance(dist).Randomize()){ if(user.HasLOE(t)){ if(t.actor() != null && t.actor() != user){ actors.Add(t.actor()); } t.BreakFragileFeatures(); } } } foreach(Actor a in actors){ if(a.TakeDamage(DamageType.MAGIC,DamageClass.MAGICAL,R.Roll(4,6),user,"a scroll of thunderclap")){ a.ApplyStatus(AttrType.STUNNED,R.Between(5,10)*100); } } user.MakeNoise(12); break; } case ConsumableType.FIRE_RING: { List<pos> cells = new List<pos>(); List<Tile> valid = new List<Tile>(); foreach(Tile t in user.TilesWithinDistance(3)){ if(t.passable && user.DistanceFrom(t) > 1 && user.HasLOE(t) && user.ApproximateEuclideanDistanceFromX10(t) < 45){ valid.Add(t); cells.Add(t.p); } } if(valid.Count > 0){ if(player.CanSee(user)){ B.Add("A ring of fire surrounds " + user.the_name + ". "); } else{ B.Add("A ring of fire appears! ",user.tile()); } valid.Randomize(); foreach(Tile t in valid){ t.AddFeature(FeatureType.FIRE); } Screen.AnimateMapCells(cells,new colorchar('&',Color.RandomFire)); } else{ B.Add("Nothing happens. ",user); IDed = false; } break; } case ConsumableType.RAGE: { B.Add("A murderous red glow cascades outward. ",user); List<Tile>[] printed = new List<Tile>[13]; Color leading_edge_color = Color.Red; Color trail_color = Color.DarkRed; if(Global.LINUX && !Screen.GLMode){ leading_edge_color = Color.DarkRed; } for(int dist=0;dist<=12;++dist){ printed[dist] = new List<Tile>(); foreach(Tile t in user.TilesAtDistance(dist)){ if(t.seen && user.HasLOS(t)){ printed[dist].Add(t); } } foreach(Tile t in printed[dist]){ colorchar cch = M.VisibleColorChar(t.row,t.col); cch.bgcolor = leading_edge_color; if(cch.color == leading_edge_color){ cch.color = Color.Black; } Screen.WriteMapChar(t.row,t.col,cch); } if(dist > 0){ foreach(Tile t in printed[dist-1]){ colorchar cch = M.VisibleColorChar(t.row,t.col); cch.bgcolor = trail_color; if(cch.color == trail_color){ cch.color = Color.Black; } Screen.WriteMapChar(t.row,t.col,cch); } } Game.GLUpdate(); Thread.Sleep(5); } int actors_affected = 0; string name_is = ""; foreach(Actor a in M.AllActors()){ if(a != user && user.DistanceFrom(a) <= 12 && user.HasLOS(a)){ a.ApplyStatus(AttrType.ENRAGED,R.Between(10,17)*100,false,"",a.You("calm") + " down. ",a.You("resist") + "! "); actors_affected++; if(player.CanSee(a)){ name_is = a.YouAre(); } } } if(actors_affected > 0){ if(actors_affected == 1){ B.Add(name_is + " enraged! "); } else{ B.Add("Bloodlust fills the air. "); } } break; } case ConsumableType.FREEZING: { ItemUseResult orb_result = UseOrb(2,false,user,line,(t,LOE_tile,results)=>{ Screen.AnimateExplosion(t,2,new colorchar('*',Color.RandomIce)); List<Tile> targets = new List<Tile>(); foreach(Tile t2 in t.TilesWithinDistance(2)){ if(LOE_tile.HasLOE(t2)){ targets.Add(t2); } } while(targets.Count > 0){ Tile t2 = targets.RemoveRandom(); t2.ApplyEffect(DamageType.COLD); Actor ac = t2.actor(); if(ac != null){ ac.ApplyFreezing(); } } }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.FLAMES: { ItemUseResult orb_result = UseOrb(2,false,user,line,(t,LOE_tile,results)=>{ List<Tile> area = new List<Tile>(); List<pos> cells = new List<pos>(); foreach(Tile tile in t.TilesWithinDistance(2)){ if(LOE_tile.HasLOE(tile)){ if(tile.passable){ tile.AddFeature(FeatureType.FIRE); } else{ tile.ApplyEffect(DamageType.FIRE); } if(tile.Is(FeatureType.FIRE)){ area.Add(tile); } cells.Add(tile.p); } } Screen.AnimateMapCells(cells,new colorchar('&',Color.RandomFire)); }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.FOG: { ItemUseResult orb_result = UseOrb(3,false,user,line,(t,LOE_tile,results)=>{ List<Tile> area = new List<Tile>(); List<pos> cells = new List<pos>(); colorchar cch = new colorchar('*',Color.Gray); for(int i=0;i<=3;++i){ foreach(Tile tile in t.TilesAtDistance(i)){ if(tile.passable && LOE_tile.HasLOE(tile)){ tile.AddFeature(FeatureType.FOG); area.Add(tile); cells.Add(tile.p); if(tile.seen){ M.last_seen[tile.row,tile.col] = cch; } } } Screen.AnimateMapCells(cells,cch,40); } Q.RemoveTilesFromEventAreas(area,EventType.REMOVE_GAS); Event.RemoveGas(area,800,FeatureType.FOG,25); //Q.Add(new Event(area,600,EventType.FOG,25)); }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.DETONATION: { ItemUseResult orb_result = UseOrb(3,false,user,line,(t,LOE_tile,results)=>{ LOE_tile.ApplyExplosion(3,user,"an orb of detonation"); }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.BREACHING: { ItemUseResult orb_result = UseOrb(5,false,user,line,(t,LOE_tile,results)=>{ int max_dist = -1; foreach(Tile t2 in M.TilesByDistance(t.row,t.col,false,true)){ if(t.DistanceFrom(t2) > 5){ break; } if(t2.Is(TileType.WALL,TileType.WAX_WALL,TileType.STALAGMITE,TileType.CRACKED_WALL,TileType.DOOR_C)){ Screen.WriteMapChar(t2.row,t2.col,t2.symbol,Color.RandomBreached); if(t.DistanceFrom(t2) > max_dist){ max_dist = t.DistanceFrom(t2); Game.GLUpdate(); //todo: stalagmites - if I add them to caves, they should no longer always vanish. check for an event, maybe? Thread.Sleep(50); } } } List<Tile> area = new List<Tile>(); foreach(Tile tile in t.TilesWithinDistance(5)){ if(tile.Is(TileType.WALL,TileType.WAX_WALL,TileType.STALAGMITE,TileType.CRACKED_WALL,TileType.DOOR_C) && tile.p.BoundsCheck(M.tile,false)){ TileType prev_type = tile.type; if(tile.Is(TileType.STALAGMITE)){ tile.Toggle(null,TileType.FLOOR); } else{ tile.Toggle(null,TileType.BREACHED_WALL); tile.toggles_into = prev_type; area.Add(tile); } foreach(Tile neighbor in tile.TilesWithinDistance(1)){ neighbor.solid_rock = false; } } } if(area.Count > 0){ Q.Add(new Event(t,area,500,EventType.BREACH)); } }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.SHIELDING: { ItemUseResult orb_result = UseOrb(1,true,user,line,(t,LOE_tile,results)=>{ List<Tile> area = new List<Tile>(); List<pos> cells = new List<pos>(); List<colorchar> symbols = new List<colorchar>(); foreach(Tile tile in t.TilesWithinDistance(1)){ if(tile.passable && LOE_tile.HasLOE(tile)){ colorchar cch = tile.visual; if(tile.actor() != null){ if(!tile.actor().HasAttr(AttrType.SHIELDED)){ tile.actor().attrs[AttrType.SHIELDED] = 1; B.Add(tile.actor().YouAre() + " shielded. ",tile.actor()); } if(player.CanSee(tile.actor())){ cch = tile.actor().visual; } } cch.bgcolor = Color.Blue; if(Global.LINUX && !Screen.GLMode){ cch.bgcolor = Color.DarkBlue; } if(cch.color == cch.bgcolor){ cch.color = Color.Black; } if(cch.c == '.'){ cch.c = '+'; } symbols.Add(cch); cells.Add(tile.p); area.Add(tile); } } Screen.AnimateMapCells(cells,symbols,150); foreach(Tile tile in area){ if(player.CanSee(tile)){ B.Add("A zone of protection is created. "); break; } } Q.Add(new Event(area,100,EventType.SHIELDING,R.Roll(2,6)+6)); }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.TELEPORTAL: { ItemUseResult orb_result = UseOrb(0,false,user,line,(t,LOE_tile,results)=>{ LOE_tile.AddFeature(FeatureType.TELEPORTAL); if(LOE_tile.Is(FeatureType.TELEPORTAL)){ Q.Add(new Event(LOE_tile,0,EventType.TELEPORTAL,100)); } }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.PAIN: { ItemUseResult orb_result = UseOrb(5,false,user,line,(t,LOE_tile,results)=>{ List<pos> cells = new List<pos>(); List<colorchar> symbols = new List<colorchar>(); foreach(Tile tile in t.TilesWithinDistance(5)){ if(LOE_tile.HasLOE(tile)){ Actor a = tile.actor(); if(a != null){ if(a.TakeDamage(DamageType.MAGIC,DamageClass.MAGICAL,R.Roll(2,6),user,"an orb of pain")){ a.ApplyStatus(AttrType.VULNERABLE,(R.Roll(2,6)+6)*100); if(a == player){ Help.TutorialTip(TutorialTopic.Vulnerable); } } } symbols.Add(new colorchar('*',Color.RandomDoom)); /*if(tile.DistanceFrom(t) % 2 == 0){ symbols.Add(new colorchar('*',Color.DarkMagenta)); } else{ symbols.Add(new colorchar('*',Color.DarkRed)); }*/ cells.Add(tile.p); } } player.AnimateVisibleMapCells(cells,symbols,80); }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.CONFUSION: { ItemUseResult orb_result = UseOrb(2,false,user,line,(t,LOE_tile,results)=>{ List<Tile> area = new List<Tile>(); List<pos> cells = new List<pos>(); colorchar cch = new colorchar('*',Color.RandomConfusion); for(int i=0;i<=2;++i){ foreach(Tile tile in t.TilesAtDistance(i)){ if(tile.passable && LOE_tile.HasLOE(tile)){ tile.AddFeature(FeatureType.CONFUSION_GAS); area.Add(tile); cells.Add(tile.p); if(tile.seen){ M.last_seen[tile.row,tile.col] = cch; } } } Screen.AnimateMapCells(cells,cch,40); } Q.RemoveTilesFromEventAreas(area,EventType.REMOVE_GAS); Event.RemoveGas(area,R.Between(7,9)*100,FeatureType.CONFUSION_GAS,20); }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.BLADES: { ItemUseResult orb_result = UseOrb(1,false,user,line,(t,LOE_tile,results)=>{ List<Tile> targets = new List<Tile>(); foreach(Tile t2 in t.TilesWithinDistance(1)){ if(t2.passable && t2.actor() == null && LOE_tile.HasLOE(t2)){ targets.Add(t2); } } targets.Randomize(); foreach(Tile t2 in targets){ Actor a = Actor.Create(ActorType.BLADE,t2.row,t2.col); if(a != null){ a.speed = 50; } } }); used = orb_result.used; IDed = orb_result.IDed; break; } case ConsumableType.DUST_STORM: { ItemUseResult wand_result = UseWand(true,false,user,line,(LOE_tile,targeting,results)=>{ List<Tile> area = new List<Tile>(); List<pos> cells = new List<pos>(); foreach(Tile neighbor in LOE_tile.TilesWithinDistance(1)){ if(neighbor.passable){ area.Add(neighbor); } } List<Tile> added = new List<Tile>(); foreach(Tile n1 in area){ foreach(int dir in U.FourDirections){ if(R.CoinFlip() && n1.TileInDirection(dir).passable){ added.Add(n1.TileInDirection(dir)); } } } foreach(Tile n1 in added){ area.AddUnique(n1); } colorchar cch = new colorchar('*',Color.TerrainDarkGray); foreach(Tile t2 in area){ t2.AddFeature(FeatureType.THICK_DUST); cells.Add(t2.p); if(t2.seen){ M.last_seen[t2.row,t2.col] = cch; } Actor a = t2.actor(); if(a != null && t2.Is(FeatureType.THICK_DUST)){ if(!a.HasAttr(AttrType.NONLIVING,AttrType.PLANTLIKE,AttrType.BLINDSIGHT)){ if(a == player){ B.Add("Thick dust fills the air! "); } a.ApplyStatus(AttrType.BLIND,R.Between(1,3)*100); } } } Screen.AnimateMapCells(cells,cch,80); Q.RemoveTilesFromEventAreas(area,EventType.REMOVE_GAS); Event.RemoveGas(area,R.Between(20,25)*100,FeatureType.THICK_DUST,8); }); used = wand_result.used; IDed = wand_result.IDed; break; } case ConsumableType.FLESH_TO_FIRE: { ItemUseResult wand_result = UseWand(true,false,user,line,(LOE_tile,targeting,results)=>{ Actor a = targeting.targeted.actor(); if(a != null){ B.Add("Jets of flame erupt from " + a.TheName(true) + ". ",a,targeting.targeted); Screen.AnimateMapCell(a.row,a.col,new colorchar('&',Color.RandomFire)); int dmg = (a.curhp+1)/2; if(a.TakeDamage(DamageType.MAGIC,DamageClass.MAGICAL,dmg,user,"a wand of flesh to fire")){ a.ApplyBurning(); } } else{ if(targeting.targeted.Is(FeatureType.TROLL_CORPSE)){ B.Add("Jets of flame erupt from the troll corpse. ",a,targeting.targeted); targeting.targeted.ApplyEffect(DamageType.FIRE); if(targeting.targeted.Is(FeatureType.TROLL_CORPSE)){ //if it's still there because of thick gas, it still gets destroyed. targeting.targeted.RemoveFeature(FeatureType.TROLL_CORPSE); B.Add("The troll corpse burns to ashes! ",targeting.targeted); } } else{ if(targeting.targeted.Is(FeatureType.TROLL_BLOODWITCH_CORPSE)){ B.Add("Jets of flame erupt from the troll bloodwitch corpse. ",a,targeting.targeted); targeting.targeted.ApplyEffect(DamageType.FIRE); if(targeting.targeted.Is(FeatureType.TROLL_BLOODWITCH_CORPSE)){ //if it's still there because of thick gas, it still gets destroyed. targeting.targeted.RemoveFeature(FeatureType.TROLL_BLOODWITCH_CORPSE); B.Add("The troll bloodwitch corpse burns to ashes! ",targeting.targeted); } } else{ B.Add("Nothing happens. ",user); results.IDed = false; } } } }); used = wand_result.used; IDed = wand_result.IDed; break; } case ConsumableType.INVISIBILITY: { ItemUseResult wand_result = UseWand(false,false,user,line,(LOE_tile,targeting,results)=>{ Actor a = targeting.targeted.actor(); if(a != null){ B.Add(a.You("vanish",true) + " from view. ",a); if(a.light_radius > 0 && !M.wiz_dark && !M.wiz_lite){ B.Add(a.Your() + " light still reveals " + a.Your() + " location. ",a); } a.RefreshDuration(AttrType.INVISIBLE,(R.Between(2,20)+30)*100,a.YouAre() + " no longer invisible. ",a); } else{ B.Add("Nothing happens. ",user); results.IDed = false; } }); used = wand_result.used; IDed = wand_result.IDed; break; } case ConsumableType.REACH: { ItemUseResult wand_result = UseWand(true,false,user,line,(LOE_tile,targeting,results)=>{ Actor a = targeting.targeted.actor(); if(a != null && a != user){ user.Attack(0,a,true); } else{ B.Add("Nothing happens. ",user); results.IDed = false; } }); used = wand_result.used; IDed = wand_result.IDed; break; } case ConsumableType.SLUMBER: { ItemUseResult wand_result = UseWand(true,false,user,line,(LOE_tile,targeting,results)=>{ Actor a = targeting.targeted.actor(); if(a != null){ if(a.HasAttr(AttrType.MENTAL_IMMUNITY)){ if(a.HasAttr(AttrType.NONLIVING,AttrType.PLANTLIKE)){ B.Add(a.You("resist") + " becoming dormant. ",a); } else{ B.Add(a.You("resist") + " falling asleep. ",a); } } else{ if(a.ResistedBySpirit()){ if(player.HasLOS(a)){ if(a.HasAttr(AttrType.NONLIVING,AttrType.PLANTLIKE)){ B.Add(a.You("resist") + " becoming dormant. ",a); } else{ B.Add(a.You("almost fall") + " asleep. ",a); } } } else{ if(player.HasLOS(a)){ if(a.HasAttr(AttrType.NONLIVING,AttrType.PLANTLIKE)){ B.Add(a.You("become") + " dormant. ",a); } else{ B.Add(a.You("fall") + " asleep. ",a); } } a.attrs[AttrType.ASLEEP] = 6 + R.Roll(4,6); } } } else{ B.Add("Nothing happens. ",user); results.IDed = false; } }); used = wand_result.used; IDed = wand_result.IDed; break; } case ConsumableType.TELEKINESIS: { ItemUseResult wand_result = UseWand(true,false,user,line,(LOE_tile,targeting,results)=>{ if(!SharedEffect.Telekinesis(false,user,targeting.targeted)){ results.used = false; } }); used = wand_result.used; IDed = wand_result.IDed; break; } case ConsumableType.WEBS: { ItemUseResult wand_result = UseWand(true,true,user,line,(LOE_tile,targeting,results)=>{ if(targeting.targeted == user.tile()){ B.Add("Nothing happens. ",user); results.IDed = false; } else{ Screen.CursorVisible = false; foreach(Tile t in targeting.line_to_targeted){ if(t.passable && t != user.tile()){ t.AddFeature(FeatureType.WEB); if(t.seen){ Screen.WriteMapChar(t.row,t.col,';',Color.White); Game.GLUpdate(); Thread.Sleep(15); } } } M.Draw(); } }); used = wand_result.used; IDed = wand_result.IDed; break; } case ConsumableType.BLAST_FUNGUS: { if(line == null){ line = user.GetTargetTile(12,0,false,true); } if(line != null){ revealed_by_light = true; ignored = true; Tile t = line.LastBeforeSolidTile(); Actor first = user.FirstActorInLine(line); B.Add(user.You("fling") + " " + TheName() + ". "); if(first != null && first != user){ t = first.tile(); B.Add("It hits " + first.the_name + ". ",first); } line = line.ToFirstSolidTileOrActor(); if(line.Count > 0){ line.RemoveAt(line.Count - 1); } int idx = 0; foreach(Tile tile2 in line){ if(tile2.seen){ ++idx; } else{ line = line.To(tile2); if(line.Count > 0){ line.RemoveAt(line.Count - 1); } break; } } if(line.Count > 0){ user.AnimateProjectile(line,symbol,color); } t.GetItem(this); //inv.Remove(i); t.MakeNoise(2); if(first != null && first != user){ first.player_visibility_duration = -1; first.attrs[AttrType.PLAYER_NOTICED]++; } else{ if(t.IsTrap()){ t.TriggerTrap(); } } } else{ used = false; } break; } case ConsumableType.BANDAGES: if(!user.HasAttr(AttrType.BANDAGED)){ user.attrs[AttrType.BANDAGED] = 20; //user.recover_time = Q.turn + 100; B.Add(user.You("apply",false,true) + " a bandage. ",user); } else{ B.Add(user.the_name + " can't apply another bandage yet. ",user); used = false; } break; case ConsumableType.FLINT_AND_STEEL: { int dir = -1; if(user == player){ dir = user.GetDirection("Which direction? ",false,true); } else{ dir = user.DirectionOf(player); } if(dir != -1){ Tile t = user.TileInDirection(dir); B.Add(user.You("use") + " your flint & steel. ",user); if(t.actor() != null && t.actor().HasAttr(AttrType.OIL_COVERED) && !t.Is(FeatureType.POISON_GAS,FeatureType.THICK_DUST)){ t.actor().ApplyBurning(); } if(!t.Is(TileType.WAX_WALL)){ t.ApplyEffect(DamageType.FIRE); } } else{ used = false; } break; } default: used = false; break; } if(used){ if(IDed){ bool seen = true; //i'll try letting orbs always be IDed. keep an eye on this. /*bool seen = (user == player); if(user != player){ if(player.CanSee(line[0])){ //fix this line - or at least check for null/empty seen = true; } if(user != null && player.CanSee(user)){ //heck, I could even check to see whose turn it is, if I really wanted to be hacky. seen = true; } }*/ if(!identified[type] && seen){ identified[type] = true; B.Add("(It was " + SingularName(true) + "!) "); } } else{ if(!unIDed_name[type].Contains("{tried}")){ unIDed_name[type] = unIDed_name[type] + " {tried}"; } } if(quantity > 1){ --quantity; } else{ if(type == ConsumableType.BANDAGES){ --other_data; if(user != null && other_data == 0){ B.Add(user.You("use") + " your last bandage. ",user); user.inv.Remove(this); } } else{ if(type == ConsumableType.FLINT_AND_STEEL){ if(R.OneIn(3)){ --other_data; if(user != null){ if(other_data == 2){ B.Add("Your flint & steel shows signs of wear. ",user); } if(other_data == 1){ B.Add("Your flint & steel is almost depleted. ",user); } if(other_data == 0){ B.Add("Your flint & steel is used up. ",user); user.inv.Remove(this); } } } } else{ if(NameOfItemType() == "wand"){ if(charges > 0){ --charges; if(other_data >= 0){ ++other_data; } } else{ other_data = -1; } } else{ if(user != null){ user.inv.Remove(this); } } } } } CheckForMimic(); } return used; }
public void ApplyExplosion(int radius,Actor damage_source,string cause_of_death) { int damage_dice = ((radius+1) * (radius+2)) / 2; //1d6, 3d6, 6d6, 10d6, 15d6... List<pos> cells = new List<pos>(); foreach(Tile nearby in TilesWithinDistance(radius)){ if(nearby.seen && player.HasLOS(nearby) && HasLOE(nearby)){ cells.Add(nearby.p); } } if(cells.Count > 0){ Screen.AnimateMapCells(cells,new colorchar('*',Color.RandomExplosion)); } List<Tile> affected_walls = new List<Tile>(); for(int dist=radius;dist>=0;--dist){ foreach(Tile t in TilesAtDistance(dist)){ if(HasLOE(t)){ t.RemoveAllGases(); if(t.Is(FeatureType.BONES)){ t.RemoveFeature(FeatureType.BONES); } if(t.Is(FeatureType.WEB)){ t.RemoveFeature(FeatureType.WEB); } Actor a = t.actor(); if(a != null){ a.attrs[AttrType.TURN_INTO_CORPSE]++; a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(damage_dice,6),damage_source,cause_of_death); if(a.curhp > 0 || !a.HasAttr(AttrType.NO_CORPSE_KNOCKBACK)){ KnockObjectBack(a,1,damage_source); } a.CorpseCleanup(); } if(t.inv != null){ if(t.inv.quantity > 1){ if(t.inv.IsBreakable()){ B.Add(t.inv.TheName(true) + " break! ",t); } else{ B.Add(t.inv.TheName(true) + " are destroyed! ",t); } } else{ if(t.inv.IsBreakable()){ B.Add(t.inv.TheName(true) + " breaks! ",t); } else{ B.Add(t.inv.TheName(true) + " is destroyed! ",t); } } if(t.inv.NameOfItemType() != "orb"){ t.inv.CheckForMimic(); t.inv = null; } else{ Item i = t.inv; t.inv = null; i.Use(null,new List<Tile>{t}); } } if(t.Is(TileType.CRACKED_WALL,TileType.RUBBLE,TileType.STALAGMITE)){ affected_walls.Add(t); } if(t.Is(TileType.POISON_BULB,TileType.BARREL,TileType.STANDING_TORCH)){ t.Bump(DirectionOf(t)); } if(t.Is(TileType.DOOR_C,TileType.DOOR_O) && R.PercentChance(70)){ affected_walls.Add(t); } if(t.Is(TileType.WALL) && R.PercentChance(60)){ affected_walls.Add(t); } if(t.Is(TileType.WAX_WALL) && R.PercentChance(40)){ affected_walls.Add(t); } if(t.Is(TileType.STATUE,TileType.VINE) && R.PercentChance(20)){ affected_walls.Add(t); } } } } foreach(Tile t in affected_walls){ if(t.p.BoundsCheck(M.tile,false)){ if(t.Is(TileType.CRACKED_WALL,TileType.DOOR_C,TileType.DOOR_O,TileType.RUBBLE,TileType.WAX_WALL,TileType.STATUE,TileType.STALAGMITE,TileType.VINE)){ t.Toggle(null,TileType.FLOOR); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } } if(t.Is(TileType.WALL)){ t.Toggle(null,TileType.CRACKED_WALL); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } } } } MakeNoise(8); }
public static Actor Create(ActorType type, int r, int c) { return Create(type, r, c, false, false); } //not sure that false,false should be the default here public static Actor Create(ActorType type, int r, int c, bool add_to_tiebreaker_list, bool insert_after_current) { Actor a = null; if (M.actor[r, c] == null) { a = new Actor(proto[type], r, c); M.actor[r, c] = a; if (add_to_tiebreaker_list) { if (insert_after_current) { tiebreakers.Insert(Q.Tiebreaker + 1, a); Q.UpdateTiebreaker(Q.Tiebreaker + 1); Event e = new Event(a, a.speed, EventType.MOVE); e.tiebreaker = Q.Tiebreaker + 1; Q.Add(e); } else { tiebreakers.Add(a); Event e = new Event(a, a.speed, EventType.MOVE); e.tiebreaker = tiebreakers.Count - 1; //since it's the last one Q.Add(e); } } else { a.QS(); } if (a.light_radius > 0) { a.UpdateRadius(0, a.light_radius); } } return a; }