public void Execute() { if(!dead){ switch(type){ case EventType.MOVE: { Actor temp = target as Actor; temp.Act(); break; } case EventType.REMOVE_ATTR: { Actor temp = target as Actor; if(attr == AttrType.FLYING){ temp.attrs[AttrType.DESCENDING] = 2; if(temp == player){ B.Add("You start to descend as your flight wears off. "); B.Print(true); } break; } if(attr == AttrType.SHINING){ int old_rad = temp.LightRadius(); temp.attrs[attr] -= value; if(old_rad != temp.LightRadius() && !temp.HasAttr(AttrType.BURROWING)){ temp.UpdateRadius(old_rad,temp.LightRadius()); } break; } if(temp.type == ActorType.BERSERKER && attr == AttrType.COOLDOWN_2){ temp.attrs[attr] = 0; //this hack can probably be removed } else{ temp.attrs[attr] -= value; } if(attr == AttrType.BURNING && temp.LightRadius() == 0 && !temp.HasAttr(AttrType.BURROWING)){ temp.UpdateRadius(1,0); } if(attr == AttrType.TELEPORTING){ temp.attrs[attr] = 0; } if(attr == AttrType.CONVICTION){ if(temp.HasAttr(AttrType.IN_COMBAT)){ temp.attrs[AttrType.CONVICTION] += value; //whoops, undo that } else{ temp.attrs[AttrType.BONUS_SPIRIT] -= value; //otherwise, set things to normal temp.attrs[AttrType.BONUS_COMBAT] -= (value+1) / 2; if(temp.attrs[AttrType.KILLSTREAK] >= 2){ B.Add("You wipe off your weapon. "); } temp.attrs[AttrType.KILLSTREAK] = 0; } } if(attr == AttrType.COOLDOWN_1 && temp.type == ActorType.BERSERKER){ B.Add(temp.Your() + " rage diminishes. ",temp); B.Add(temp.the_name + " dies. ",temp); temp.Kill(); } break; } case EventType.REMOVE_GAS: { List<Tile> removed = new List<Tile>(); foreach(Tile t in area){ if(t.Is(feature)){ if(R.PercentChance(value)){ t.RemoveFeature(feature); removed.Add(t); } } else{ removed.Add(t); } } foreach(Tile t in removed){ area.Remove(t); } if(area.Count > 0){ Event.RemoveGas(area,100,feature,value); } break; } case EventType.CHECK_FOR_HIDDEN: { List<Tile> removed = new List<Tile>(); foreach(Tile t in area){ if(player.CanSee(t)){ int exponent = player.DistanceFrom(t) + 1; if(player.magic_trinkets.Contains(MagicTrinketType.RING_OF_KEEN_SIGHT)){ --exponent; } if(!t.IsLit()){ if(!player.HasAttr(AttrType.SHADOWSIGHT)){ ++exponent; } } if(exponent > 8){ exponent = 8; //because 1 in 256 is enough. } int difficulty = 1; for(int i=exponent;i>0;--i){ difficulty = difficulty * 2; } if(R.Roll(difficulty) == difficulty){ if(t.IsTrap() || t.Is(TileType.FIRE_GEYSER) || t.Is(TileType.FOG_VENT) || t.Is(TileType.POISON_GAS_VENT)){ t.name = Tile.Prototype(t.type).name; t.a_name = Tile.Prototype(t.type).a_name; t.the_name = Tile.Prototype(t.type).the_name; t.symbol = Tile.Prototype(t.type).symbol; t.color = Tile.Prototype(t.type).color; B.Add("You notice " + t.AName(true) + ". "); } else{ if(t.type == TileType.HIDDEN_DOOR){ t.Toggle(null); B.Add("You notice a hidden door. "); } } removed.Add(t); } } } foreach(Tile t in removed){ area.Remove(t); } if(area.Count > 0){ Q.Add(new Event(area,100,EventType.CHECK_FOR_HIDDEN)); } break; } case EventType.RELATIVELY_SAFE: { if(M.AllActors().Count == 1 && !Q.Contains(EventType.POLTERGEIST) && !Q.Contains(EventType.REGENERATING_FROM_DEATH) && !Q.Contains(EventType.MIMIC) && !Q.Contains(EventType.MARBLE_HORROR)){ //B.Add("The dungeon is still and silent. "); B.Add("The dungeon is utterly silent for a moment. "); B.PrintAll(); } else{ Q.Add(new Event((R.Roll(20)+30)*100,EventType.RELATIVELY_SAFE)); } break; } case EventType.POLTERGEIST: { if(target != null && target is Actor){ //target can either be a stolen item, or the currently manifested poltergeist. Q.Add(new Event(target,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,0,"")); break; //if it's manifested, the event does nothing for now. } if(area.Any(t => t.actor() == player)){ bool manifested = false; if(value == 0){ B.Add("You feel like you're being watched. "); } else{ if(target != null){ //if it has a stolen item Tile tile = null; tile = area.Where(t => t.actor() == null && t.DistanceFrom(player) >= 2 && t.HasLOE(player) && t.FirstActorInLine(player) == player).RandomOrDefault(); if(tile != null){ Actor temporary = new Actor(ActorType.POLTERGEIST,"something",'G',Color.DarkGreen,1,1,0,0); temporary.a_name = "something"; temporary.the_name = "something"; temporary.p = tile.p; temporary.inv = new List<Item>(); temporary.inv.Add(target as Item); Item item = temporary.inv[0]; if(item.NameOfItemType() == "orb"){ temporary.inv[0].Use(temporary,temporary.GetBestExtendedLineOfEffect(player)); } else{ B.Add("Something throws " + item.AName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstSolidTileOrActor(),new colorchar(item.color,item.symbol)); player.tile().GetItem(item); B.Add(item.TheName() + " hits you. "); player.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),temporary,"a flying " + item.Name()); } target = null; } else{ Q.Add(new Event(target,area,100,EventType.POLTERGEIST,AttrType.NO_ATTR,value,"")); return; //try again next turn } } else{ if(value >= 2 && area.Any(t => t.DistanceFrom(player) == 1 && t.passable && t.actor() == null)){ Tile tile = area.Where(t => t.DistanceFrom(player) == 1 && t.passable && t.actor() == null).Random(); B.DisplayNow(); for(int i=4;i>0;--i){ Screen.AnimateStorm(tile.p,i,2,1,'G',Color.DarkGreen); } Actor a = Actor.Create(ActorType.POLTERGEIST,tile.row,tile.col,TiebreakerAssignment.UseCurrent); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; B.Add("A poltergeist manifests in front of you! "); Q.Add(new Event(a,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,0,"")); manifested = true; } else{ if(player.tile().type == TileType.DOOR_O){ B.Add("The door slams closed on you! "); player.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),null,"a slamming door"); } else{ Tile tile = null; //check for items to throw... tile = area.Where(t => t.inv != null && t.actor() == null && t.DistanceFrom(player) >= 2 && t.HasLOE(player) && t.FirstActorInLine(player) == player).RandomOrDefault(); if(tile != null){ Actor temporary = new Actor(ActorType.POLTERGEIST,"something",'G',Color.DarkGreen,1,1,0,0); temporary.a_name = "something"; temporary.the_name = "something"; temporary.p = tile.p; temporary.inv = new List<Item>(); if(tile.inv.quantity <= 1){ temporary.inv.Add(tile.inv); tile.inv = null; } else{ temporary.inv.Add(new Item(tile.inv,-1,-1)); tile.inv.quantity--; } M.Draw(); Item item = temporary.inv[0]; if(item.NameOfItemType() == "orb"){ temporary.inv[0].Use(temporary,temporary.GetBestExtendedLineOfEffect(player)); } else{ B.Add("Something throws " + item.TheName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstSolidTileOrActor(),new colorchar(item.color,item.symbol)); player.tile().GetItem(item); B.Add(item.TheName() + " hits you. "); player.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),temporary,"a flying " + item.Name()); } } else{ if(area.Any(t => t.type == TileType.DOOR_O || t.type == TileType.DOOR_C)){ Tile door = area.Where(t=>t.type == TileType.DOOR_O || t.type == TileType.DOOR_C).Random(); if(door.type == TileType.DOOR_C){ if(player.CanSee(door)){ B.Add("The door flies open! ",door); } else{ if(door.seen || player.DistanceFrom(door) <= 12){ B.Add("You hear a door slamming. "); } } door.Toggle(null); } else{ if(door.actor() == null){ if(player.CanSee(door)){ B.Add("The door slams closed! ",door); } else{ if(door.seen || player.DistanceFrom(door) <= 12){ B.Add("You hear a door slamming. "); } } door.Toggle(null); } else{ if(player.CanSee(door)){ B.Add("The door slams closed on " + door.actor().TheName(true) + "! ",door); } else{ if(player.DistanceFrom(door) <= 12){ B.Add("You hear a door slamming and a grunt of pain. "); } } door.actor().TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(6),null,"a slamming door"); } } } else{ B.Add("You hear mocking laughter from nearby. "); } } } } } } if(!manifested){ Q.Add(new Event(target,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,value+1,"")); } } else{ Q.Add(new Event(target,area,(R.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,0,"")); } break; } case EventType.MIMIC: { Item item = target as Item; if(area[0].inv != item){ //it could have been picked up by the player or moved in another way foreach(Tile t in M.AllTiles()){ //if it was moved, make the correction to the event's area. if(t.inv == item){ area = new List<Tile>{t}; break; } } } if(area[0].inv == item){ bool attacked = false; if(player.DistanceFrom(area[0]) == 1 && area[0].actor() == null){ if(player.TotalSkill(SkillType.STEALTH) * 5 < R.Roll(1,100)){ B.Add(item.TheName(true) + " suddenly grows tentacles! "); attacked = true; area[0].inv = null; Actor a = Actor.Create(ActorType.MIMIC,area[0].row,area[0].col,TiebreakerAssignment.UseCurrent); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; a.symbol = item.symbol; a.color = item.color; } } if(!attacked){ Q.Add(new Event(target,area,100,EventType.MIMIC,AttrType.NO_ATTR,0,"")); } } else{ //if the item is missing, we assume that the player just picked it up List<Tile> open = new List<Tile>(); foreach(Tile t in player.TilesAtDistance(1)){ if(t.passable && t.actor() == null){ open.Add(t); } } if(open.Count > 0){ Tile t = open.Random(); B.Add(item.TheName() + " suddenly grows tentacles! "); Actor a = Actor.Create(ActorType.MIMIC,t.row,t.col,TiebreakerAssignment.UseCurrent); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; a.symbol = item.symbol; a.color = item.color; player.inv.Remove(item); } else{ B.Add("Your pack feels lighter. "); player.inv.Remove(item); } } break; } case EventType.GRENADE: { Tile t = target as Tile; if(t.Is(FeatureType.GRENADE)){ t.features.Remove(FeatureType.GRENADE); B.Add("The grenade explodes! ",t); if(t.seen){ Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); } B.DisplayNow(); t.ApplyExplosion(1,"an exploding grenade"); /*List<pos> cells = new List<pos>(); foreach(Tile tile in t.TilesWithinDistance(1)){ if(tile.passable && tile.seen){ //animation LOS check here cells.Add(tile.p); } } Screen.AnimateMapCells(cells,new colorchar('*',Color.RandomExplosion)); Actor a = t.actor(); if(a != null){ a.attrs[AttrType.TURN_INTO_CORPSE] = 1; } foreach(Actor a2 in t.ActorsWithinDistance(1)){ a2.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,R.Roll(3,6),null,"an exploding grenade"); } if(a != null){ int dir = Global.RandomDirection(); if(a.curhp > 0 || !a.HasAttr(AttrType.NO_CORPSE_KNOCKBACK)){ t.TileInDirection(dir).KnockObjectBack(a,1); } a.CorpseCleanup(); } t.MakeNoise(8);*/ } break; } case EventType.BLAST_FUNGUS: { Item i = target as Item; i.other_data--; if(i.other_data == 0){ Tile t = null; if(M.tile.BoundsCheck(i.row,i.col) && M.tile[i.p].inv == i){ t = M.tile[i.p]; t.inv = null; } else{ foreach(Actor a in M.AllActors()){ if(a.inv.Contains(i)){ a.inv.Remove(i); t = a.tile(); break; } } } if(t != null){ B.Add("The blast fungus explodes! ",t); if(t.seen){ Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); } B.DisplayNow(); t.ApplyExplosion(3,"an exploding blast fungus"); } } else{ Tile t = null; if(M.tile.BoundsCheck(i.row,i.col) && M.tile[i.p].inv == i){ t = M.tile[i.p]; } else{ foreach(Actor a in M.AllActors()){ if(a.inv.Contains(i)){ t = a.tile(); break; } } } if(t != null && t.seen){ Screen.AnimateMapCell(t.row,t.col,new colorchar(i.other_data.ToString()[0],Color.Red),100); } Q.Add(new Event(i,100,EventType.BLAST_FUNGUS)); } break; } case EventType.STALAGMITE: { if(value > 1){ int stalagmites = 0; //number removed int number_left = 0; List<Tile> crumbled = new List<Tile>(); foreach(Tile tile in area){ if(tile.type == TileType.STALAGMITE){ if(R.OneIn(value)){ crumbled.Add(tile); tile.Toggle(null); ++stalagmites; } else{ ++number_left; } } } if(stalagmites > 0){ if(stalagmites > 1){ B.Add("The stalagmites crumble. ",crumbled.ToArray()); } else{ B.Add("The stalagmite crumbles. ",crumbled.ToArray()); } } if(number_left > 0){ Q.Add(new Event(area,100,EventType.STALAGMITE,value)); } } else{ int stalagmites = 0; foreach(Tile tile in area){ if(tile.type == TileType.STALAGMITE){ stalagmites++; } } if(stalagmites > 0){ if(stalagmites > 1){ B.Add("The stalagmites crumble. ",area.ToArray()); } else{ B.Add("The stalagmite crumbles. ",area.ToArray()); } foreach(Tile tile in area){ if(tile.type == TileType.STALAGMITE){ tile.Toggle(null); } } } } break; } case EventType.FIRE_GEYSER: { int frequency = value / 10; //9-39 int variance = value % 10; //0-9 int variance_amount = (frequency * variance) / 10; int number_of_values = variance_amount*2 + 1; int minimum_value = frequency - variance_amount; if(minimum_value < 5){ int diff = 5 - minimum_value; number_of_values -= diff; minimum_value = 5; } int delay = ((minimum_value - 1) + R.Roll(number_of_values)) * 100; Q.Add(new Event(target,delay+200,EventType.FIRE_GEYSER,value)); Q.Add(new Event(target,delay,EventType.FIRE_GEYSER_ERUPTION,2)); break; } case EventType.FIRE_GEYSER_ERUPTION: { foreach(Tile t in target.TilesWithinDistance(2)){ t.RemoveFeature(FeatureType.FOG); } //int old_radius = target.light_radius; //target.UpdateRadius(old_radius,2,true); B.Add(target.the_name + " spouts flames! ",target); if(target.actor() != null){ target.actor().ApplyBurning(); } for(int i=0;i<4;++i){ Tile t = target.TilesWithinDistance(2).Where(x=>target.HasLOE(x)).RandomOrDefault(); if(t != null){ if(t.passable){ t.AddFeature(FeatureType.FIRE); } else{ t.ApplyEffect(DamageType.FIRE); } } } //target.UpdateRadius(2,old_radius,true); if(value > 0){ Q.Add(new Event(target,100,EventType.FIRE_GEYSER_ERUPTION,value - 1)); } break; } case EventType.FOG_VENT: { Tile current = target as Tile; if(!current.Is(FeatureType.FOG)){ current.AddFeature(FeatureType.FOG); List<Tile> new_area = new List<Tile>{current}; Q.RemoveTilesFromEventAreas(new_area,EventType.REMOVE_GAS); Event.RemoveGas(new_area,600,FeatureType.FOG,25); //Q.Add(new Event(new_area,600,EventType.FOG,25)); } else{ for(int tries=0;tries<50;++tries){ List<Tile> open = new List<Tile>(); foreach(Tile t in current.TilesAtDistance(1)){ //perhaps the rework could involve refreshing the duration of nearby tiles - if enough are refreshed, then no new tiles need to be added if(t.passable){ open.Add(t); if(!t.Is(FeatureType.FOG)){ open.Add(t); //3x as likely if it can expand there open.Add(t); } } } if(open.Count > 0){ Tile possible = open.Random(); if(!possible.Is(FeatureType.FOG)){ possible.AddFeature(FeatureType.FOG); List<Tile> new_area = new List<Tile>{possible}; Q.RemoveTilesFromEventAreas(new_area,EventType.REMOVE_GAS); Event.RemoveGas(new_area,600,FeatureType.FOG,25); break; } else{ current = possible; } } else{ break; } } } Q.Add(new Event(target,100,EventType.FOG_VENT)); break; } case EventType.POISON_GAS_VENT: { Tile current = target as Tile; if(R.OneIn(7)){ int num = R.Roll(5) + 2; List<Tile> new_area = new List<Tile>(); for(int i=0;i<num;++i){ if(!current.Is(FeatureType.POISON_GAS)){ current.AddFeature(FeatureType.POISON_GAS); new_area.Add(current); } else{ for(int tries=0;tries<50;++tries){ List<Tile> open = new List<Tile>(); foreach(Tile t in current.TilesAtDistance(1)){ if(t.passable){ open.Add(t); } } if(open.Count > 0){ Tile possible = open.Random(); if(!possible.Is(FeatureType.POISON_GAS)){ possible.AddFeature(FeatureType.POISON_GAS); new_area.Add(possible); break; } else{ current = possible; } } else{ break; } } } } if(new_area.Count > 0){ B.Add("Toxic vapors pour from " + target.the_name + "! ",target); Event.RemoveGas(new_area,200,FeatureType.POISON_GAS,18); } } Q.Add(new Event(target,100,EventType.POISON_GAS_VENT)); break; } case EventType.STONE_SLAB: { Tile t = target as Tile; if(t.type == TileType.STONE_SLAB && (t.IsLitFromAnywhere(true) || area.Any(x=>x.actor()!=null))){ bool vis = player.CanSee(t); t.Toggle(null); //t.Toggle(null,TileType.FLOOR); //t.symbol = '-'; //t.revealed_by_light = true; if(!vis && player.CanSee(t)){ vis = true; } if(vis){ B.Add("The stone slab rises with a grinding sound. "); } else{ if(player.DistanceFrom(t) <= 6){ B.Add("You hear a grinding sound. "); } } } else{ if(t.type == TileType.STONE_SLAB_OPEN && !t.IsLitFromAnywhere(true) && t.actor() == null && !area.Any(x=>x.actor()!=null)){ bool vis = player.CanSee(t); //t.Toggle(null,TileType.STONE_SLAB); t.Toggle(null); if(!vis && player.CanSee(t)){ vis = true; } if(vis){ B.Add("The stone slab descends with a grinding sound. "); } else{ if(player.DistanceFrom(t) <= 6){ B.Add("You hear a grinding sound. "); } } } } Q.Add(new Event(target,area,100,EventType.STONE_SLAB)); break; } case EventType.MARBLE_HORROR: { Tile t = target as Tile; if(t.type == TileType.STATUE){ if(value == 1 && player.CanSee(t) && !t.IsLit() && t.actor() == null){ //if target was visible last turn & this turn, and it's currently in darkness... t.TransformTo(TileType.FLOOR); Actor a = Actor.Create(ActorType.MARBLE_HORROR,t.row,t.col,TiebreakerAssignment.AtEnd); //todo: not sure - should this get a placeholder like poltergeist and mimic? foreach(Event e in Q.list){ if(e.target == a && e.type == EventType.MOVE){ e.dead = true; break; } } a.Q0(); switch(R.Roll(2)){ case 1: B.Add("You think that statue might have just moved... "); B.Print(true); break; case 2: B.Add("The statue turns its head to face you. "); B.Print(true); break; } } else{ if(player.CanSee(t)){ Q.Add(new Event(target,100,EventType.MARBLE_HORROR,1)); } else{ Q.Add(new Event(target,100,EventType.MARBLE_HORROR,0)); } } } break; } case EventType.REGENERATING_FROM_DEATH: { int health = value; int permanent_damage = secondary_value; if(target.tile().Is(FeatureType.TROLL_CORPSE)){ //otherwise, assume it was destroyed by fire int maxhp = Actor.Prototype(ActorType.TROLL).maxhp; int recovered = Actor.Prototype(ActorType.TROLL).attrs[AttrType.REGENERATING]; if(health + recovered > maxhp - permanent_damage){ recovered = (maxhp - permanent_damage) - health; } health += recovered; if(permanent_damage >= maxhp){ break; } if(health > 0 && target.actor() == null){ Actor a = Actor.Create(ActorType.TROLL,target.row,target.col,TiebreakerAssignment.UseCurrent); a.curhp = health; a.attrs[AttrType.PERMANENT_DAMAGE] = permanent_damage; a.attrs[AttrType.NO_ITEM]++; a.attrs[AttrType.DANGER_SENSED]++; B.Add("The troll stands up! ",target); a.player_visibility_duration = -1; if(target.tile().type == TileType.DOOR_C){ target.tile().Toggle(a); } target.tile().features.Remove(FeatureType.TROLL_CORPSE); a.attrs[AttrType.WANDERING]++; } else{ int roll = R.Roll(20); if(health == -1){ roll = 1; } if(health == 0){ roll = 3; } switch(roll){ case 1: case 2: B.Add("The troll's corpse twitches. ",target); break; case 3: case 4: B.Add("You hear sounds coming from the troll's corpse. ",target); break; case 5: B.Add("The troll on the floor regenerates. ",target); break; default: break; } Event e = new Event(target,100,EventType.REGENERATING_FROM_DEATH); e.value = health; e.secondary_value = permanent_damage; Q.Add(e); } } if(target.tile().Is(FeatureType.TROLL_BLOODWITCH_CORPSE)){ //otherwise, assume it was destroyed by fire int maxhp = Actor.Prototype(ActorType.TROLL_BLOODWITCH).maxhp; int recovered = Actor.Prototype(ActorType.TROLL_BLOODWITCH).attrs[AttrType.REGENERATING]; if(health + recovered > maxhp - permanent_damage){ recovered = (maxhp - permanent_damage) - health; } health += recovered; if(permanent_damage >= maxhp){ break; } if(recovered > 0){ List<pos> cells = new List<pos>(); List<colorchar> cch = new List<colorchar>(); foreach(pos p2 in target.PositionsWithinDistance(4)){ if(target.HasLOE(M.tile[p2]) && player.CanSee(M.tile[p2])){ cells.Add(p2); colorchar ch = M.VisibleColorChar(p2.row,p2.col); ch.color = Color.Red; cch.Add(ch); } } if(cells.Count > 0){ M.Draw(); Screen.AnimateMapCells(cells,cch,40); } foreach(Actor a in target.ActorsWithinDistance(4)){ if(target.HasLOE(a)){ if(a == player){ B.Add("Ow! "); } a.TakeDamage(DamageType.NORMAL,DamageClass.MAGICAL,recovered,null,"trollish blood magic"); } } } if(health > 0 && target.actor() == null){ Actor a = Actor.Create(ActorType.TROLL_BLOODWITCH,target.row,target.col,TiebreakerAssignment.UseCurrent); a.curhp = health; a.attrs[AttrType.PERMANENT_DAMAGE] = permanent_damage; a.attrs[AttrType.NO_ITEM]++; a.attrs[AttrType.DANGER_SENSED]++; B.Add("The troll bloodwitch rises! ",target); a.player_visibility_duration = -1; if(attr == AttrType.COOLDOWN_1){ a.attrs[AttrType.COOLDOWN_1]++; } if(target.tile().type == TileType.DOOR_C){ target.tile().Toggle(a); } target.tile().features.Remove(FeatureType.TROLL_BLOODWITCH_CORPSE); a.attrs[AttrType.WANDERING]++; } else{ int roll = R.Roll(20); if(health == -1){ roll = 1; } if(health == 0){ roll = 3; } switch(roll){ case 1: case 2: B.Add("The bloodwitch's corpse twitches. ",target); break; case 3: case 4: B.Add("You feel a pulse like a heartbeat coming from the bloodwitch. ",target); break; case 5: B.Add("The troll bloodwitch on the floor regenerates. ",target); break; default: break; } Event e = new Event(target,100,EventType.REGENERATING_FROM_DEATH); e.value = health; e.secondary_value = permanent_damage; Q.Add(e); } } break; } case EventType.REASSEMBLING: { Tile t = target as Tile; if(t.Is(FeatureType.BONES)){ if(t.actor() == null){ Actor a = Actor.Create(ActorType.SKELETON,target.row,target.col,TiebreakerAssignment.UseCurrent); B.Add("The skeleton reassembles itself. ",target); a.player_visibility_duration = -1; if(target.tile().type == TileType.DOOR_C){ target.tile().Toggle(a); } target.tile().features.Remove(FeatureType.BONES); if(R.OneIn(3)){ a.attrs[AttrType.WANDERING]++; } } else{ Q.Add(new Event(target,100,EventType.REASSEMBLING)); } } break; } case EventType.SHIELDING: { List<pos> cells = new List<pos>(); List<colorchar> symbols = new List<colorchar>(); int animation_delay = 75; foreach(Tile tile in area){ colorchar cch = tile.visual; if(tile.actor() != null){ if(!tile.actor().HasAttr(AttrType.SHIELDED)){ tile.actor().attrs[AttrType.SHIELDED] = 1; B.Add(tile.actor().YouAre() + " shielded. ",tile.actor()); } if(player.CanSee(tile.actor())){ animation_delay = 150; cch = tile.actor().visual; } } cch.bgcolor = Color.Blue; if(Global.LINUX && !Screen.GLMode){ cch.bgcolor = Color.DarkBlue; } if(cch.color == cch.bgcolor){ cch.color = Color.Black; } if(cch.c == '.'){ cch.c = '+'; } symbols.Add(cch); cells.Add(tile.p); } M.Draw(); Screen.AnimateMapCells(cells,symbols,animation_delay); --value; if(value > 0){ Q.Add(new Event(area,100,EventType.SHIELDING,value)); } break; } case EventType.FINAL_LEVEL_SPAWN_CULTISTS: { int num_cultists = M.AllActors().Where(x=>x.Is(ActorType.FINAL_LEVEL_CULTIST)).Count; if(num_cultists < 5){ Actor a = M.SpawnMob(ActorType.CULTIST); if(a != null){ List<Actor> group = null; if(a.group != null){ group = new List<Actor>(a.group); a.group.Clear(); } else{ group = new List<Actor>{a}; } List<int> valid_circles = new List<int>(); for(int i=0;i<5;++i){ if(M.FinalLevelSummoningCircle(i).PositionsWithinDistance(2,M.tile).Any(x=>M.tile[x].Is(TileType.DEMONIC_IDOL))){ valid_circles.Add(i); } } foreach(Actor a2 in group){ int i = valid_circles.RemoveLast(); pos circle = M.FinalLevelSummoningCircle(i); a2.FindPath(circle.row,circle.col); a2.attrs[AttrType.COOLDOWN_2] = i; a2.type = ActorType.FINAL_LEVEL_CULTIST; a2.group = null; if(!R.OneIn(20)){ a2.attrs[AttrType.NO_ITEM] = 1; } } } } Q.Add(new Event(R.Between(5,8)*100,EventType.FINAL_LEVEL_SPAWN_CULTISTS)); break; } /*case EventType.BOSS_SIGN: { string s = ""; switch(R.Roll(8)){ case 1: s = "You see scratch marks on the walls and floor. "; break; case 2: s = "There are deep gouges in the floor here. "; break; case 3: s = "The floor here is scorched and blackened. "; break; case 4: s = "You notice bones of an unknown sort on the floor. "; break; case 5: s = "You hear a distant roar. "; break; case 6: s = "You smell smoke. "; break; case 7: s = "You spot a large reddish scale on the floor. "; break; case 8: s = "A small tremor shakes the area. "; break; default: s = "Debug message. "; break; } if(!player.HasAttr(AttrType.RESTING)){ B.AddIfEmpty(s); } Q.Add(new Event((R.Roll(20)+35)*100,EventType.BOSS_SIGN)); break; } case EventType.BOSS_ARRIVE: { bool spawned = false; Actor a = null; if(M.AllActors().Count == 1 && !Q.Contains(EventType.POLTERGEIST)){ List<Tile> trolls = new List<Tile>(); for(LinkedListNode<Event> current = Q.list.First;current!=null;current = current.Next){ if(current.Value.type == EventType.REGENERATING_FROM_DEATH){ trolls.Add((current.Value.target) as Tile); } } foreach(Tile troll in trolls){ if(troll.Is(FeatureType.TROLL_CORPSE)){ B.Add("The troll corpse burns to ashes! ",troll); troll.features.Remove(FeatureType.TROLL_CORPSE); } else{ if(troll.Is(FeatureType.TROLL_BLOODWITCH_CORPSE)){ B.Add("The troll bloodwitch corpse burns to ashes! ",troll); troll.features.Remove(FeatureType.TROLL_BLOODWITCH_CORPSE); } } } Q.KillEvents(null,EventType.REGENERATING_FROM_DEATH); List<Tile> goodtiles = M.AllTiles(); List<Tile> removed = new List<Tile>(); foreach(Tile t in goodtiles){ if(!t.passable || t.Is(TileType.CHASM) || player.CanSee(t)){ removed.Add(t); } } foreach(Tile t in removed){ goodtiles.Remove(t); } if(goodtiles.Count > 0){ B.Add("You hear a loud crash and a nearby roar! "); Tile t = goodtiles[R.Roll(goodtiles.Count)-1]; a = Actor.Create(ActorType.FIRE_DRAKE,t.row,t.col,true,false); spawned = true; } else{ if(M.AllTiles().Any(t=>t.passable && !t.Is(TileType.CHASM) && t.actor() == null)){ B.Add("You hear a loud crash and a nearby roar! "); Tile tile = M.AllTiles().Where(t=>t.passable && !t.Is(TileType.CHASM) && t.actor() == null).Random(); a = Actor.Create(ActorType.FIRE_DRAKE,tile.row,tile.col,true,false); spawned = true; } } } if(!spawned){ Q.Add(new Event(null,null,(R.Roll(20)+10)*100,EventType.BOSS_ARRIVE,attr,value,"")); } else{ if(value > 0){ a.curhp = value; } else{ //if there's no good value, this means that this is the first appearance. B.Add("The ground shakes as dust and rocks fall from the cavern ceiling. "); B.Add("This place is falling apart! "); List<Tile> floors = M.AllTiles().Where(t=>t.passable && t.type != TileType.CHASM && player.tile() != t); Tile tile = null; if(floors.Count > 0){ tile = floors.Random(); (tile as Tile).Toggle(null,TileType.CHASM); } Q.Add(new Event(tile,100,EventType.FLOOR_COLLAPSE)); Q.Add(new Event((R.Roll(20)+20)*100,EventType.CEILING_COLLAPSE)); } } break; } case EventType.FLOOR_COLLAPSE: { Tile current = target as Tile; int tries = 0; if(current != null){ for(tries=0;tries<50;++tries){ List<Tile> open = new List<Tile>(); foreach(Tile t in current.TilesAtDistance(1)){ if(t.passable || t.Is(TileType.RUBBLE)){ open.Add(t); } } if(open.Count > 0){ Tile possible = open.Random(); if(!possible.Is(TileType.CHASM)){ possible.Toggle(null,TileType.CHASM); List<Tile> open_neighbors = possible.TilesAtDistance(1).Where(t=>t.passable && t.type != TileType.CHASM); int num_neighbors = open_neighbors.Count; while(open_neighbors.Count > num_neighbors/2){ Tile neighbor = open_neighbors.RemoveRandom(); neighbor.Toggle(null,TileType.CHASM); } break; } else{ current = possible; } } else{ break; } } } if(tries == 50 || current == null){ List<Tile> floors = M.AllTiles().Where(t=>t.passable && t.type != TileType.CHASM && player.tile() != t); if(floors.Count > 0){ target = floors.Random(); (target as Tile).Toggle(null,TileType.CHASM); } } Q.Add(new Event(target,100,EventType.FLOOR_COLLAPSE)); break; } case EventType.CEILING_COLLAPSE: { B.Add("The ground shakes and debris falls from the ceiling! "); for(int i=1;i<Global.ROWS-1;++i){ for(int j=1;j<Global.COLS-1;++j){ Tile t = M.tile[i,j]; if(t.Is(TileType.WALL)){ int num_walls = t.TilesAtDistance(1).Where(x=>x.Is(TileType.WALL)).Count; if(num_walls < 8 && R.OneIn(20)){ if(R.CoinFlip()){ t.Toggle(null,TileType.FLOOR); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } } else{ t.Toggle(null,TileType.RUBBLE); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; if(neighbor.type == TileType.FLOOR && R.OneIn(10)){ neighbor.Toggle(null,TileType.RUBBLE); } } } } } else{ int num_walls = t.TilesAtDistance(1).Where(x=>x.Is(TileType.WALL)).Count; if(num_walls == 0 && R.OneIn(100)){ if(R.OneIn(6)){ t.Toggle(null,TileType.RUBBLE); } foreach(Tile neighbor in t.TilesAtDistance(1)){ if(neighbor.type == TileType.FLOOR && R.OneIn(6)){ neighbor.Toggle(null,TileType.RUBBLE); } } } } } } Q.Add(new Event((R.Roll(20)+20)*100,EventType.CEILING_COLLAPSE)); break; }*/ case EventType.NORMAL_LIGHTING: { bool check_for_torch_dimming = false; if(M.wiz_lite){ B.Add("The supernatural brightness fades from the air. "); } if(M.wiz_dark){ B.Add("The supernatural darkness fades from the air. "); check_for_torch_dimming = true; } M.wiz_lite = false; M.wiz_dark = false; if(check_for_torch_dimming && player.HasAttr(AttrType.DIM_LIGHT)){ player.CalculateDimming(); } break; } case EventType.TELEPORTAL: { Tile t = target as Tile; if(t != null && t.Is(FeatureType.TELEPORTAL,FeatureType.STABLE_TELEPORTAL)){ if(t.Is(FeatureType.TELEPORTAL)){ value--; //unstable teleportals (from the item) degrade each turn } else{ if(value < 100){ value++; //stable ones repair themselves after use } } Actor a = t.actor(); Tile dest = null; if(a != null && !a.HasAttr(AttrType.JUST_TELEPORTED,AttrType.IMMOBILE)){ if(area != null){ dest = area.RandomOrDefault(); } else{ List<Tile> tiles = M.AllTiles().Where(x => x.passable && x.actor() == null && t.ApproximateEuclideanDistanceFromX10(x) >= 45); dest = tiles.RandomOrDefault(); } if(dest != null){ a.RefreshDuration(AttrType.JUST_TELEPORTED,101); value -= 25; bool visible = false; if(a == player){ B.Add("You disappear into the teleportal. "); } else{ if(player.CanSee(a)){ visible = true; B.Add(a.the_name + " disappears into the teleportal. ",t); } } a.Move(dest.row,dest.col); if(a != player && player.CanSee(a)){ if(visible){ B.Add(a.the_name + " reappears. ",a); } else{ B.Add(a.a_name + " suddenly appears! ",a); } } } } else{ if(a != null && a.HasAttr(AttrType.JUST_TELEPORTED)){ a.RefreshDuration(AttrType.JUST_TELEPORTED,101); } } if(t.inv != null && t.Is(FeatureType.TELEPORTAL)){ List<Tile> tiles = M.AllTiles().Where(x => x.passable && x.inv == null && t.ApproximateEuclideanDistanceFromX10(x) >= 45); dest = tiles.RandomOrDefault(); if(dest != null){ Item i = t.inv; bool visible = false; if(player.CanSee(t)){ visible = true; B.Add(i.TheName(true) + " disappears into the teleportal. ",t); } t.inv = null; dest.GetItem(i); if(player.CanSee(dest)){ if(visible){ B.Add(i.TheName(true) + " reappears. ",dest); } else{ B.Add(i.AName(true) + " suddenly appears! ",dest); } } } } if(value > 0){ Q.Add(new Event(target,area,100,EventType.TELEPORTAL,AttrType.NO_ATTR,value,"")); if(value < 25){ if(dest != null || R.OneIn(8)){ B.Add("The teleportal flickers. ",t,dest); } } } else{ if(t.Is(FeatureType.TELEPORTAL)){ t.RemoveFeature(FeatureType.TELEPORTAL); } if(t.Is(FeatureType.STABLE_TELEPORTAL)){ foreach(Tile t2 in area){ Event e2 = Q.FindTargetedEvent(t2,EventType.TELEPORTAL); if(e2 != null && t2.features.Contains(FeatureType.STABLE_TELEPORTAL)){ e2.area.Remove(t); if(e2.area.Count == 0){ t2.RemoveFeature(FeatureType.STABLE_TELEPORTAL); //t2.AddFeature(FeatureType.INACTIVE_TELEPORTAL); e2.dead = true; } } } t.RemoveFeature(FeatureType.STABLE_TELEPORTAL); } B.Add("The teleportal flickers and vanishes. ",t,dest); } } break; } case EventType.BREACH: { if(!R.OneIn(3)){ Tile t = area.WhereGreatest(x=>x.DistanceFrom(target)).RandomOrDefault(); if(t != null){ t.Toggle(null); if(t.actor() != null || t.inv != null){ foreach(Tile nearby in M.ReachableTilesByDistance(t.row,t.col,false)){ if(t.inv != null && nearby.inv == null){ nearby.GetItem(t.inv); t.inv = null; if(t.actor() == null){ break; } } if(t.actor() != null && nearby.actor() == null){ t.actor().Move(nearby.row,nearby.col); if(t.inv == null){ break; } } } if(t.actor() != null){ //if there wasn't an actual path to a passable tile, just move to the nearest for(int i=1;i<Math.Max(Global.ROWS,Global.COLS);++i){ List<Tile> tiles = t.TilesAtDistance(i).Where(x=>x.passable && x.actor() == null); bool done = false; while(tiles.Count > 0){ Tile dest = tiles.Random(); t.actor().Move(dest.row,dest.col); done = true; break; } if(done){ break; } } } if(t.inv != null){ for(int i=1;i<Math.Max(Global.ROWS,Global.COLS);++i){ List<Tile> tiles = t.TilesAtDistance(i).Where(x=>x.passable && x.inv == null); bool done = false; while(tiles.Count > 0){ Tile dest = tiles.Random(); dest.GetItem(t.inv); t.inv = null; done = true; break; } if(done){ break; } } } } if(t.features.Count > 0){ t.features.Clear(); } area.Remove(t); } } if(area.Count > 0){ Q.Add(new Event(target,area,100,EventType.BREACH)); } break; } case EventType.GRAVE_DIRT: { foreach(Tile t in area){ Actor a = t.actor(); if(a != null && a.type != ActorType.CORPSETOWER_BEHEMOTH && !a.HasAttr(AttrType.IMMOBILE,AttrType.JUST_GRABBED,AttrType.FROZEN,AttrType.FLYING) && R.OneIn(12)){ if(player.CanSee(a)){ B.Add("A dead hand reaches up and grabs " + a.the_name + "! ",t); } if(a == player){ B.Print(true); } if(a.HasAttr(AttrType.SLIMED,AttrType.OIL_COVERED,AttrType.BRUTISH_STRENGTH)){ if(player.CanSee(a)){ B.Add(a.You("slip") + " out of its grasp. ",t); } } else{ int duration = R.Roll(4) * 100; a.attrs[AttrType.IMMOBILE]++; Q.Add(new Event(a,duration,AttrType.IMMOBILE,"The dead hand releases " + a.TheName(true) + ". ",t)); //it'd be nice to check LOS here a.RefreshDuration(AttrType.JUST_GRABBED,duration + 100); } } } Q.Add(new Event(area,100,EventType.GRAVE_DIRT)); break; } case EventType.TOMBSTONE_GHOST: { if(area.Count > 0){ Tile t = area[0]; if(target == null && t.actor() == player){ foreach(Tile t2 in M.ReachableTilesByDistance(player.row,player.col,false)){ if(t2.passable && t2.actor() == null){ Actor ghost = Actor.Create(ActorType.GHOST,t2.row,t2.col); if(ghost != null){ target = ghost; ghost.player_visibility_duration = -1; ghost.target = player; t.color = Color.White; B.Add("A vengeful ghost rises! "); B.PrintAll(); break; } } } } Q.Add(new Event(target,area,100,EventType.TOMBSTONE_GHOST)); } break; } case EventType.POPPIES: { List<Tile> new_area = new List<Tile>(); bool recalculate_distance_map = false; foreach(Tile t in area){ if(t.type == TileType.POPPY_FIELD){ new_area.Add(t); Actor a = t.actor(); if(a == player){ Help.TutorialTip(TutorialTopic.Poppies); } if(a != null && !a.HasAttr(AttrType.NONLIVING,AttrType.PLANTLIKE)){ if(a.attrs[AttrType.POPPY_COUNTER] < 4){ a.GainAttrRefreshDuration(AttrType.POPPY_COUNTER,200); if(a == player && a.attrs[AttrType.POPPY_COUNTER] == 1){ B.Add("You breathe in the overwhelming scent of the poppies. "); //todo: this was set to "no interrupt" before. why? } } else{ a.RefreshDuration(AttrType.POPPY_COUNTER,200); } if(a.attrs[AttrType.POPPY_COUNTER] >= 4){ if(!a.HasAttr(AttrType.ASLEEP,AttrType.JUST_AWOKE)){ if(a.ResistedBySpirit()){ if(player.HasLOS(a)){ B.Add(a.You("resist") + " falling asleep. ",a); } } else{ if(player.HasLOS(a)){ B.Add(a.You("fall") + " asleep in the poppies. ",a); //B.Add("The poppies lull " + a.the_name + " to sleep. ",a); } a.attrs[AttrType.ASLEEP] = R.Between(4,6); } } /*a.ApplyStatus(AttrType.MAGICAL_DROWSINESS,(R.Roll(3)+4)*100); if(a == player && !a.HasAttr(AttrType.MAGICAL_DROWSINESS)){ //B.Add("The poppies make you drowsy. "); Help.TutorialTip(TutorialTopic.Drowsiness); } a.RefreshDuration(AttrType.MAGICAL_DROWSINESS,a.DurationOfMagicalEffect((R.Roll(3)+4)) * 100,a.YouFeel() + " less drowsy. ",a);*/ } } } else{ recalculate_distance_map = true; } } if(new_area.Count > 0){ Q.Add(new Event(new_area,100,EventType.POPPIES)); if(recalculate_distance_map){ M.CalculatePoppyDistanceMap(); } } break; } case EventType.BURROWING: { List<Tile> open = area.Where(x=>x.passable && x.actor() == null); Actor a = target as Actor; if(open.Count > 0){ Tile t = open.Random(); Event e = new Event(a,100,EventType.MOVE); e.tiebreaker = this.tiebreaker; Q.Add(e); a.attrs[AttrType.BURROWING] = 0; a.Move(t.row,t.col); if(player.CanSee(a)){ a.AnimateStorm(1,2,3,'*',Color.Gray); } B.Add(a.TheName(true) + " emerges from the ground. ",a,t); } else{ if(a.HasAttr(AttrType.REGENERATING)){ a.curhp += a.attrs[AttrType.REGENERATING]; if(a.curhp > a.maxhp){ a.curhp = a.maxhp; } } Q.Add(new Event(target,area,100,EventType.BURROWING)); } break; } case EventType.SPAWN_WANDERING_MONSTER: { int spawn_chance = 2; foreach(Actor a in Actor.tiebreakers){ if(a != player && a != null && !a.HasAttr(AttrType.IMMOBILE) && (a.group == null || a.group.Count == 0 || a.group[0] == a)){ spawn_chance *= 2; if(spawn_chance >= 65536){ break; } } } if(R.OneIn(spawn_chance)){ if(M.extra_danger < 8 && R.CoinFlip() && M.current_level != 1){ M.extra_danger++; B.Add("You sense danger. "); } Actor a = M.SpawnWanderingMob(); if(a != null){ a.attrs[AttrType.WANDERING] = 1; a.attrs[AttrType.NO_ITEM] = 1; if(player.CanSee(a)){ B.Add("You suddenly sense the presence of " + a.AName(true) + ". "); } } } Q.Add(new Event(R.Between(20,60)*100,EventType.SPAWN_WANDERING_MONSTER)); break; } /*case EventType.GAS_UPDATE: { int ROWS = Global.ROWS; int COLS = Global.COLS; float[,] g = null; for(int num=0;num<3;++num){ g = new float[ROWS,COLS]; for(int i=1;i<ROWS-1;++i){ for(int j=1;j<COLS-1;++j){ if(M.tile[i,j].passable){ float neighbors_total = 0.0f; int open = 0; foreach(int dir in U.EightDirections){ if(M.tile[i,j].TileInDirection(dir).passable){ pos p = new pos(i,j).PosInDir(dir); neighbors_total += M.gas[p.row,p.col]; ++open; } } if(open > 0){ float avg = neighbors_total / (float)open; float d = 0.03f * open; g[i,j] = M.gas[i,j] * (1-d) + avg * d; } } } } M.gas = g; } for(int i=0;i<ROWS;++i){ for(int j=0;j<COLS;++j){ if(g[i,j] > 0.0f){ if(g[i,j] <= 0.001f){ g[i,j] = 0.0f; M.tile[i,j].features.Remove(FeatureType.POISON_GAS); } else{ g[i,j] -= 0.001f;// * (float)R.r.NextDouble(); M.tile[i,j].features.AddUnique(FeatureType.POISON_GAS); } } else{ M.tile[i,j].features.Remove(FeatureType.POISON_GAS); } } } Q.Add(new Event(100,EventType.GAS_UPDATE)); break; }*/ case EventType.FIRE: { List<Tile> chance_to_burn = new List<Tile>(); //tiles that might be affected List<Tile> chance_to_die_out = new List<Tile>(); //fires that might die out List<PhysicalObject> no_fire = new List<PhysicalObject>(); foreach(PhysicalObject o in new List<PhysicalObject>(Fire.burning_objects)){ if(o.IsBurning()){ foreach(Tile neighbor in o.TilesWithinDistance(1)){ if(neighbor.actor() != null && neighbor.actor() != o){ if(neighbor.actor() == player){ if(!player.HasAttr(AttrType.JUST_SEARED,AttrType.FROZEN,AttrType.DAMAGE_RESISTANCE)){ B.Add("The heat sears you! "); } player.RefreshDuration(AttrType.JUST_SEARED,50); } neighbor.actor().TakeDamage(DamageType.FIRE,DamageClass.PHYSICAL,false,1,null,"searing heat"); } //every actor adjacent to a burning object takes proximity fire damage. (actors never get set on // fire directly this way, but an actor covered in oil will ignite if it takes any fire damage) //every tile adjacent to a burning object has a chance to be affected by fire. oil-covered objects are always affected. //if the roll is passed, fire is applied to the tile. chance_to_burn.AddUnique(neighbor); } if(o is Tile){ chance_to_die_out.AddUnique(o as Tile); } } else{ no_fire.AddUnique(o); } } foreach(Tile t in chance_to_burn){ if(R.OneIn(6) || t.Is(FeatureType.OIL,FeatureType.SPORES,FeatureType.CONFUSION_GAS) || t.Is(TileType.BARREL)){ t.ApplyEffect(DamageType.FIRE); } } foreach(Tile t in chance_to_die_out){ if(!t.Is(TileType.BARREL)){ bool more_flammable_terrain = false; bool more_fire = false; bool final_level_demonic_idol_present = false; //this will soon become a check for any terrain that prevents fires from dying foreach(Tile neighbor in t.TilesAtDistance(1)){ if(neighbor.IsCurrentlyFlammable()){ more_flammable_terrain = true; } if(neighbor.Is(TileType.DEMONIC_IDOL)){ final_level_demonic_idol_present = true; } if(neighbor.IsBurning()){ more_fire = true; } } if(final_level_demonic_idol_present){ continue; //this fire never goes out } int chance = 5; if(more_fire){ chance = 10; } if(more_flammable_terrain){ chance = 20; } if(R.OneIn(chance)){ t.RemoveFeature(FeatureType.FIRE); Fire.burning_objects.Remove(t); if(t.name == "floor" && t.type != TileType.BREACHED_WALL){ t.MakeFloorCharred(); } } } } foreach(PhysicalObject o in no_fire){ Fire.burning_objects.Remove(o); } if(Fire.burning_objects.Count > 0){ Event e = new Event(100,EventType.FIRE); Q.Add(e); Fire.fire_event = e; } else{ Fire.fire_event = null; } break; } } if(msg != ""){ if(msg_objs == null){ B.Add(msg); } else{ if(msg_objs.Count == 1 && msg_objs[0] is Actor && (msg_objs[0] as Actor).HasAttr(AttrType.BURROWING)){ //do nothing } else{ B.Add(msg,msg_objs.ToArray()); } } } } }
public async Task Execute(){ if(!dead){ switch(evtype){ case EventType.MOVE: { Actor temp = target as Actor; await temp.Input(); break; } case EventType.REMOVE_ATTR: { Actor temp = target as Actor; if(temp.atype == ActorType.BERSERKER && attr == AttrType.COOLDOWN_2){ temp.attrs[attr] = 0; } else{ temp.attrs[attr] -= value; } if(attr == AttrType.TELEPORTING || attr == AttrType.ARCANE_SHIELDED){ temp.attrs[attr] = 0; } if(attr==AttrType.ENHANCED_TORCH && temp.light_radius > 0){ temp.UpdateRadius(temp.LightRadius(),6 - temp.attrs[AttrType.DIM_LIGHT],true); //where 6 is the default radius if(temp.attrs[AttrType.ON_FIRE] > temp.light_radius){ temp.UpdateRadius(temp.light_radius,temp.attrs[AttrType.ON_FIRE]); } } if(attr==AttrType.SLOWED){ if(temp.atype != ActorType.PLAYER){ temp.speed = Actor.Prototype(temp.atype).speed; } else{ if(temp.HasAttr(AttrType.LONG_STRIDE)){ temp.speed = 80; } else{ temp.speed = 100; } } } if(attr==AttrType.AFRAID && target == player){ Global.FlushInput(); } if(attr==AttrType.BLOOD_BOILED){ temp.speed += (10 * value); } if(attr==AttrType.CONVICTION){ if(temp.HasAttr(AttrType.IN_COMBAT)){ temp.attrs[Forays.AttrType.CONVICTION] += value; //whoops, undo that } else{ temp.attrs[Forays.AttrType.BONUS_SPIRIT] -= value; //otherwise, set things to normal temp.attrs[Forays.AttrType.BONUS_COMBAT] -= value / 2; if(temp.attrs[Forays.AttrType.KILLSTREAK] >= 2){ B.Add("You wipe off your weapon. "); } temp.attrs[Forays.AttrType.KILLSTREAK] = 0; } } if(attr==AttrType.STUNNED && msg.Search(new System.Text.RegularExpressions.Regex("disoriented")) > 0){ if(!player.CanSee(target)){ msg = ""; } } if(attr==AttrType.POISONED && temp == player){ if(temp.HasAttr(AttrType.POISONED)){ B.Add("The poison begins to subside. "); } else{ B.Add("You are no longer poisoned. "); } } if(attr==AttrType.COOLDOWN_1 && temp.atype == ActorType.BERSERKER){ B.Add(temp.Your() + " rage diminishes. ",temp); B.Add(temp.the_name + " dies. ",temp); await temp.TakeDamage(DamageType.NORMAL, DamageClass.NO_TYPE, 8888, null); } 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.HasAttr(AttrType.KEEN_EYES)){ --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(Global.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.ttype).name; t.a_name = Tile.Prototype(t.ttype).a_name; t.the_name = Tile.Prototype(t.ttype).the_name; t.symbol = Tile.Prototype(t.ttype).symbol; t.color = Tile.Prototype(t.ttype).color; B.Add("You notice " + t.a_name + ". "); } else{ if(t.ttype == 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.BOSS_ARRIVE) && !Q.Contains(EventType.REGENERATING_FROM_DEATH) && !Q.Contains(EventType.MIMIC) && !Q.Contains(EventType.MARBLE_HORROR)){ B.Add("The dungeon is still and silent. "); await B.PrintAll(); } else{ Q.Add(new Event((Global.Roll(20)+40)*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,(Global.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).Random(); 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.symbol == "*"){ //orbs if(item.itype == ConsumableType.SUNLIGHT || item.itype == ConsumableType.DARKNESS){ B.Add(temporary.You("throw") + " " + item.AName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstObstruction(),new colorchar(item.color,item.symbol)); B.Add(item.TheName() + " shatters on you! "); } await temporary.inv[0].Use(temporary,temporary.GetBestExtendedLineOfEffect(player)); } else{ B.Add(temporary.You("throw") + " " + item.AName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstObstruction(),new colorchar(item.color,item.symbol)); player.tile().GetItem(item); B.Add(item.TheName() + " hits you. "); await player.TakeDamage(DamageType.NORMAL, DamageClass.PHYSICAL, Global.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 >= 3 && 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); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; foreach(Event e in Q.list){ if( e != null && e.target != null && e.target == a && e.evtype == EventType.MOVE){ e.tiebreaker = this.tiebreaker; break; } } Actor.tiebreakers[tiebreaker] = a; B.Add("A poltergeist manifests in front of you! "); Q.Add(new Event(a,area,(Global.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,0,"")); manifested = true; } else{ if(player.tile().ttype == TileType.DOOR_O){ B.Add("The door slams closed on you! "); await player.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,Global.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).Random(); 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.symbol == "*"){ //orbs if(item.itype == ConsumableType.SUNLIGHT || item.itype == ConsumableType.DARKNESS){ B.Add(temporary.You("throw") + " " + item.TheName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstObstruction(),new colorchar(item.color,item.symbol)); B.Add(item.TheName() + " shatters on you! "); } await temporary.inv[0].Use(temporary, temporary.GetBestExtendedLineOfEffect(player)); } else{ B.Add(temporary.You("throw") + " " + item.TheName() + ". ",temporary); B.DisplayNow(); Screen.AnimateProjectile(tile.GetBestExtendedLineOfEffect(player).ToFirstObstruction(),new colorchar(item.color,item.symbol)); player.tile().GetItem(item); B.Add(item.TheName() + " hits you. "); await player.TakeDamage(DamageType.NORMAL, DamageClass.PHYSICAL, Global.Roll(6), temporary, "a flying " + item.Name()); } } else{ if(area.Any(t => t.ttype == TileType.DOOR_O || t.ttype == TileType.DOOR_C)){ Tile door = area.Where(t=>t.ttype == TileType.DOOR_O || t.ttype == TileType.DOOR_C).Random(); if(door.ttype == 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().TheVisible() + "! ",door); } else{ if(player.DistanceFrom(door) <= 12){ B.Add("You hear a door slamming and a grunt of pain. "); } } await door.actor().TakeDamage(DamageType.NORMAL, DamageClass.PHYSICAL, Global.Roll(6), null, "a slamming door"); } } } else{ B.Add("You hear mocking laughter from nearby. "); } } } } } } if(!manifested){ Q.Add(new Event(target,area,(Global.Roll(8)+6)*100,EventType.POLTERGEIST,AttrType.NO_ATTR,value+1,"")); } } else{ Q.Add(new Event(target,area,(Global.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.Stealth() * 5 < Global.Roll(1,100)){ B.Add(item.TheName() + " suddenly grows tentacles! "); attacked = true; area[0].inv = null; Actor a = Actor.Create(ActorType.MIMIC,area[0].row,area[0].col); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; a.symbol = item.symbol; a.color = item.color; foreach(Event e in Q.list){ if(e.target == a && e.evtype == EventType.MOVE){ e.tiebreaker = this.tiebreaker; break; } } Actor.tiebreakers[tiebreaker] = a; } } 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); Q.KillEvents(a,EventType.MOVE); a.Q0(); a.player_visibility_duration = -1; a.symbol = item.symbol; a.color = item.color; foreach(Event e in Q.list){ if(e.target == a && e.evtype == EventType.MOVE){ e.tiebreaker = this.tiebreaker; break; } } Actor.tiebreakers[tiebreaker] = a; 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(); List<pos> cells = new List<pos>(); foreach(Tile tile in t.TilesWithinDistance(1)){ if(tile.passable && tile.seen){ cells.Add(tile.p); } } Screen.AnimateMapCells(cells,new colorchar('*',Color.DarkRed)); //Screen.AnimateExplosion(t,1,new colorchar('*',Color.DarkRed)); foreach(Actor a in t.ActorsWithinDistance(1)){ await a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,Global.Roll(3,6),null,"an exploding grenade"); } if(t.actor() != null){ int dir = Global.RandomDirection(); await t.actor().GetKnockedBack(t.TileInDirection(t.actor().RotateDirection(dir,true,4))); } if(player.DistanceFrom(t) <= 3){ player.MakeNoise(); //hacky - todo change } } break; } case EventType.BLAST_FUNGUS: { Tile t = target as Tile; if(t.Is(FeatureType.FUNGUS_PRIMED)){ t.features.Remove(FeatureType.FUNGUS_PRIMED); B.Add("The blast fungus explodes! ",t); if(t.seen){ Screen.WriteMapChar(t.row,t.col,M.VisibleColorChar(t.row,t.col)); } B.DisplayNow(); for(int i=1;i<=3;++i){ List<pos> cells = new List<pos>(); foreach(Tile tile in t.TilesWithinDistance(i)){ if(t.HasLOE(tile) && tile.passable && tile.seen){ cells.Add(tile.p); } } Screen.AnimateMapCells(cells,new colorchar('*',Color.DarkRed)); } foreach(Actor a in t.ActorsWithinDistance(3)){ if(t.HasLOE(a)){ await a.TakeDamage(DamageType.NORMAL,DamageClass.PHYSICAL,Global.Roll(5,6),null,"an exploding blast fungus"); } } if(t.actor() != null){ int dir = Global.RandomDirection(); await t.actor().GetKnockedBack(t.TileInDirection(t.actor().RotateDirection(dir,true,4))); } if(player.DistanceFrom(t) <= 3){ player.MakeNoise(); //hacky - todo change } } if(t.Is(FeatureType.FUNGUS_ACTIVE)){ t.features.Remove(FeatureType.FUNGUS_ACTIVE); t.features.Add(FeatureType.FUNGUS_PRIMED); Q.Add(new Event(t,100,EventType.BLAST_FUNGUS)); } break; } case EventType.STALAGMITE: { int stalagmites = 0; foreach(Tile tile in area){ if(tile.ttype == 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.ttype == TileType.STALAGMITE){ tile.Toggle(null); } } } break; } case EventType.FIRE_GEYSER: { int frequency = value / 10; //5-25 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) + Global.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: { if(target.name == "floor"){ Event hiddencheck = null; Tile t = target as Tile; foreach(Event e in Q.list){ if(!e.dead && e.evtype == EventType.CHECK_FOR_HIDDEN){ hiddencheck = e; break; } } if(player.HasLOS(t)){ //t.seen = true; if(hiddencheck != null){ hiddencheck.area.Remove(t); } t.name = Tile.Prototype(t.ttype).name; t.a_name = Tile.Prototype(t.ttype).a_name; t.the_name = Tile.Prototype(t.ttype).the_name; t.symbol = Tile.Prototype(t.ttype).symbol; t.color = Tile.Prototype(t.ttype).color; } } if(value >= 0){ //a value of -1 means 'reset light radius to 0' if(target.light_radius == 0){ target.UpdateRadius(0,8,true); } B.Add(target.the_name + " spouts flames! ",target); M.Draw(); for(int i=0;i<3;++i){ List<pos> cells = new List<pos>(); List<Tile> tiles = target.TilesWithinDistance(1); for(int j=0;j<5;++j){ Tile t = tiles.RemoveRandom(); if(player.CanSee(t)){ cells.Add(t.p); } } if(cells.Count > 0){ Screen.AnimateMapCells(cells,new colorchar('*',Color.Red),35); } } foreach(Tile t in target.TilesWithinDistance(1)){ Actor a = t.actor(); if(a != null){ if(await a.TakeDamage(DamageType.FIRE,DamageClass.PHYSICAL,Global.Roll(2,6),null,"a fiery eruption")){ if(!a.HasAttr(AttrType.RESIST_FIRE) && !a.HasAttr(AttrType.IMMUNE_FIRE) && !a.HasAttr(AttrType.ON_FIRE) && !a.HasAttr(AttrType.CATCHING_FIRE) && !a.HasAttr(AttrType.STARTED_CATCHING_FIRE_THIS_TURN)){ if(a.name == "you"){ B.Add("You start to catch fire! "); } else{ B.Add(a.the_name + " starts to catch fire. ",a); } a.attrs[AttrType.CATCHING_FIRE] = 1; } } } if(t.Is(FeatureType.TROLL_CORPSE)){ t.features.Remove(FeatureType.TROLL_CORPSE); B.Add("The troll corpse burns to ashes! ",t); } if(t.Is(FeatureType.TROLL_SEER_CORPSE)){ t.features.Remove(FeatureType.TROLL_SEER_CORPSE); B.Add("The troll seer corpse burns to ashes! ",t); } } Q.Add(new Event(target,100,EventType.FIRE_GEYSER_ERUPTION,value - 1)); } else{ target.UpdateRadius(8,0,true); } break; } case EventType.FOG_VENT: { if(target.name == "floor"){ Event hiddencheck = null; Tile t = target as Tile; foreach(Event e in Q.list){ if(!e.dead && e.evtype == EventType.CHECK_FOR_HIDDEN){ hiddencheck = e; break; } } if(player.CanSee(t)){ //t.seen = true; if(hiddencheck != null){ hiddencheck.area.Remove(t); } t.name = Tile.Prototype(t.ttype).name; t.a_name = Tile.Prototype(t.ttype).a_name; t.the_name = Tile.Prototype(t.ttype).the_name; t.symbol = Tile.Prototype(t.ttype).symbol; t.color = Tile.Prototype(t.ttype).color; } } Tile current = target as Tile; if(!current.Is(FeatureType.FOG)){ current.AddOpaqueFeature(FeatureType.FOG); Q.Add(new Event(new List<Tile>{current},400,EventType.FOG)); } 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.FOG)){ possible.AddOpaqueFeature(FeatureType.FOG); Q.Add(new Event(new List<Tile>{possible},400,EventType.FOG)); break; } else{ current = possible; } } else{ break; } } } Q.Add(new Event(target,100,EventType.FOG_VENT)); break; } case EventType.FOG: { List<Tile> removed = new List<Tile>(); foreach(Tile t in area){ if(t.Is(FeatureType.FOG) && Global.OneIn(4)){ t.RemoveOpaqueFeature(FeatureType.FOG); removed.Add(t); } } foreach(Tile t in removed){ area.Remove(t); } if(area.Count > 0){ Q.Add(new Event(area,100,EventType.FOG)); } break; } case EventType.POISON_GAS_VENT: { if(target.name == "floor"){ Event hiddencheck = null; Tile t = target as Tile; foreach(Event e in Q.list){ if(!e.dead && e.evtype == EventType.CHECK_FOR_HIDDEN){ hiddencheck = e; break; } } if(player.CanSee(t)){ //t.seen = true; if(hiddencheck != null){ hiddencheck.area.Remove(t); } t.name = Tile.Prototype(t.ttype).name; t.a_name = Tile.Prototype(t.ttype).a_name; t.the_name = Tile.Prototype(t.ttype).the_name; t.symbol = Tile.Prototype(t.ttype).symbol; t.color = Tile.Prototype(t.ttype).color; } } Tile current = target as Tile; if(Global.OneIn(7)){ int num = Global.Roll(5) + 2; List<Tile> new_area = new List<Tile>(); for(int i=0;i<num;++i){ if(!current.Is(FeatureType.POISON_GAS)){ current.features.Add(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.features.Add(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); Q.Add(new Event(new_area,200,EventType.POISON_GAS)); } } Q.Add(new Event(target,100,EventType.POISON_GAS_VENT)); break; } case EventType.POISON_GAS: { List<Tile> removed = new List<Tile>(); foreach(Tile t in area){ if(t.Is(FeatureType.POISON_GAS) && Global.OneIn(6)){ t.RemoveOpaqueFeature(FeatureType.POISON_GAS); removed.Add(t); } } foreach(Tile t in removed){ area.Remove(t); } if(area.Count > 0){ Q.Add(new Event(area,100,EventType.POISON_GAS)); } break; } case EventType.STONE_SLAB: { Tile t = target as Tile; if(t.ttype == TileType.STONE_SLAB && (t.IsLitFromAnywhere(true) || area.Any(x=>x.actor()!=null))){ bool vis = player.CanSee(t); t.Toggle(null,Forays.TileType.FLOOR); 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.ttype == TileType.FLOOR && !t.IsLitFromAnywhere(true) && t.actor() == null && !area.Any(x=>x.actor()!=null)){ bool vis = player.CanSee(t); t.Toggle(null,Forays.TileType.STONE_SLAB); 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.ttype == 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,true,true); foreach(Event e in Q.list){ if(e.target == a && e.evtype == EventType.MOVE){ e.dead = true; break; } } a.Q0(); switch(Global.Roll(2)){ case 1: B.Add("You think that statue might have just moved... "); break; case 2: B.Add("The statue turns its head to face you. "); 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: { if(target.tile().Is(FeatureType.TROLL_CORPSE)){ //otherwise, assume it was destroyed by fire value++; if(value > 0 && target.actor() == null){ Actor a = Actor.Create(ActorType.TROLL,target.row,target.col); foreach(Event e in Q.list){ if(e.target == M.actor[target.row,target.col] && e.evtype == EventType.MOVE){ e.tiebreaker = this.tiebreaker; break; } } Actor.tiebreakers[tiebreaker] = a; target.actor().curhp = value; target.actor().level = 0; target.actor().attrs[Forays.AttrType.NO_ITEM]++; B.Add("The troll stands up! ",target); target.actor().player_visibility_duration = -1; if(target.tile().ttype == TileType.DOOR_C){ target.tile().Toggle(target.actor()); } target.tile().features.Remove(FeatureType.TROLL_CORPSE); if(Global.OneIn(3)){ target.actor().attrs[Forays.AttrType.WANDERING]++; } } else{ int roll = Global.Roll(20); if(value == -1){ roll = 1; } if(value == 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; } Q.Add(new Event(target,null,100,EventType.REGENERATING_FROM_DEATH,attr,value,"")); } } if(target.tile().Is(FeatureType.TROLL_SEER_CORPSE)){ //otherwise, assume it was destroyed by fire value++; if(value > 0 && target.actor() == null){ Actor a = Actor.Create(ActorType.TROLL_SEER,target.row,target.col); foreach(Event e in Q.list){ if(e.target == M.actor[target.row,target.col] && e.evtype == EventType.MOVE){ e.tiebreaker = this.tiebreaker; break; } } Actor.tiebreakers[tiebreaker] = a; target.actor().curhp = value; target.actor().level = 0; target.actor().attrs[Forays.AttrType.NO_ITEM]++; B.Add("The troll seer stands up! ",target); target.actor().player_visibility_duration = -1; if(attr == AttrType.COOLDOWN_1){ target.actor().attrs[Forays.AttrType.COOLDOWN_1]++; } if(target.tile().ttype == TileType.DOOR_C){ target.tile().Toggle(target.actor()); } target.tile().features.Remove(FeatureType.TROLL_SEER_CORPSE); if(Global.OneIn(3)){ target.actor().attrs[Forays.AttrType.WANDERING]++; } } else{ int roll = Global.Roll(20); if(value == -1){ roll = 1; } if(value == 0){ roll = 3; } switch(roll){ case 1: case 2: B.Add("The troll seer's corpse twitches. ",target); break; case 3: case 4: B.Add("You hear sounds coming from the troll seer's corpse. ",target); break; case 5: B.Add("The troll seer on the floor regenerates. ",target); break; default: break; } Q.Add(new Event(target,null,100,EventType.REGENERATING_FROM_DEATH,attr,value,"")); } } break; } case EventType.QUICKFIRE: { List<Actor> actors = new List<Actor>(); if(value >= 0){ foreach(Tile t in area){ if(t.actor() != null){ actors.Add(t.actor()); } if(t.Is(FeatureType.TROLL_CORPSE)){ t.features.Remove(FeatureType.TROLL_CORPSE); B.Add("The troll corpse burns to ashes! ",t); } if(t.Is(FeatureType.TROLL_SEER_CORPSE)){ t.features.Remove(FeatureType.TROLL_SEER_CORPSE); B.Add("The troll seer corpse burns to ashes! ",t); } if(t.Is(FeatureType.FUNGUS)){ Q.Add(new Event(t,200,EventType.BLAST_FUNGUS)); Actor.B.Add("The blast fungus starts to smolder in the light. ",t); t.features.Remove(FeatureType.FUNGUS); t.features.Add(FeatureType.FUNGUS_ACTIVE); } } } if(value > 0){ int radius = 4 - value; List<Tile> added = new List<Tile>(); foreach(Tile t in target.TilesWithinDistance(radius)){ if(t.passable && !t.Is(FeatureType.QUICKFIRE) && t.IsAdjacentTo(FeatureType.QUICKFIRE) && !area.Contains(t)){ added.Add(t); } } foreach(Tile t in added){ area.Add(t); t.features.Add(FeatureType.QUICKFIRE); } } if(value < 0){ int radius = 4 + value; List<Tile> removed = new List<Tile>(); foreach(Tile t in area){ if(t.DistanceFrom(target) == radius){ removed.Add(t); } else{ if(t.actor() != null){ actors.Add(t.actor()); } if(t.Is(FeatureType.TROLL_CORPSE)){ t.features.Remove(FeatureType.TROLL_CORPSE); B.Add("The troll corpse burns to ashes! ",t); } if(t.Is(FeatureType.TROLL_SEER_CORPSE)){ t.features.Remove(FeatureType.TROLL_SEER_CORPSE); B.Add("The troll seer corpse burns to ashes! ",t); } if(t.Is(FeatureType.FUNGUS)){ Q.Add(new Event(t,200,EventType.BLAST_FUNGUS)); Actor.B.Add("The blast fungus starts to smolder in the light. ",t); t.features.Remove(FeatureType.FUNGUS); t.features.Add(FeatureType.FUNGUS_ACTIVE); } } } foreach(Tile t in removed){ area.Remove(t); t.features.Remove(FeatureType.QUICKFIRE); } } foreach(Actor a in actors){ if(!a.HasAttr(AttrType.IMMUNE_FIRE) && !a.HasAttr(AttrType.INVULNERABLE)){ if(player.CanSee(a.tile())){ B.Add("The quickfire burns " + a.the_name + ". ",a); } await a.TakeDamage(DamageType.FIRE, DamageClass.PHYSICAL, Global.Roll(6), null, "quickfire"); } } --value; if(value > -5){ Q.Add(new Event(target,area,100,EventType.QUICKFIRE,AttrType.NO_ATTR,value,"")); } break; } case EventType.BOSS_SIGN: { string s = ""; switch(Global.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((Global.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>(); foreach (Event current in Q.list){ if(current.evtype == EventType.REGENERATING_FROM_DEATH){ trolls.Add((current.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_SEER_CORPSE)){ B.Add("The troll seer corpse burns to ashes! ",troll); troll.features.Remove(FeatureType.TROLL_SEER_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[Global.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,(Global.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.ttype != 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((Global.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.ttype != 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.ttype != 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 && Global.OneIn(20)){ if(Global.CoinFlip()){ t.Toggle(null,Forays.TileType.FLOOR); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; } } else{ t.Toggle(null,Forays.TileType.RUBBLE); foreach(Tile neighbor in t.TilesAtDistance(1)){ neighbor.solid_rock = false; if(neighbor.ttype == TileType.FLOOR && Global.OneIn(10)){ neighbor.Toggle(null,Forays.TileType.RUBBLE); } } } } } else{ int num_walls = t.TilesAtDistance(1).Where(x=>x.Is(TileType.WALL)).Count; if(num_walls == 0 && Global.OneIn(100)){ if(Global.OneIn(6)){ t.Toggle(null,Forays.TileType.RUBBLE); } foreach(Tile neighbor in t.TilesAtDistance(1)){ if(neighbor.ttype == TileType.FLOOR && Global.OneIn(6)){ neighbor.Toggle(null,Forays.TileType.RUBBLE); } } } } } } Q.Add(new Event((Global.Roll(20)+20)*100,EventType.CEILING_COLLAPSE)); break; } } if(msg != ""){ if(msg_objs == null){ B.Add(msg); } else{ B.Add(msg,msg_objs.ToArray()); } } } }