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_;
 }
Example #3
0
 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;
 }
Example #4
0
 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;
 }
Example #5
0
 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;
 }
Example #7
0
 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;
 }
Example #8
0
 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;
 }
Example #10
0
 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;
 }
Example #11
0
 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;
     }
 }
Example #12
0
        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();
        }
Example #13
0
        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());
                        }
                    }
                }
            }
        }
Example #14
0
 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);
 }
Example #15
0
 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), "");
 }
Example #19
0
 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);
 }
Example #21
0
 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);
 }
Example #23
0
 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;
 }
Example #25
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;
 }
Example #26
0
 public bool Use(Actor user)
 {
     return Use(user,null);
 }
Example #27
0
 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('_',' ');
 }
Example #28
0
 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;
 }