public async void Add(string s, PhysicalObject obj) { //if there's at least one object, the player must be able to bool add = false; if (obj == player || player.CanSee(obj)) { add = true; } else { add = true; } if (add && s.Length > 0) { if (s.Match(new System.Text.RegularExpressions.Regex("^[a-z]")) != null && s.Match(new System.Text.RegularExpressions.Regex("^[a-z]")).Length > 0) { s = s.Replace(new System.Text.RegularExpressions.Regex("^[a-z]"), (sr) => sr[0].ToString().ToUpper()); // c[0] = Char.ToUpper(s[0]); //s = new string(c); } int idx = str.Count - 1; str[idx] = str[idx] + s; //str.Add(str[idx]); //while (str[idx].Length > max_length) //{ // int extra_space_for_more = 7; // if (str.Count < 3) // { // extra_space_for_more = 1; // } // for (int i = max_length - extra_space_for_more; i >= 0; --i) // { // if (str[idx].Substring(i, 1) == " ") // { // if (str.Count == 3) // { // overflow = str[idx].Substring(i + 1); // } // else // { // //(str[idx].Substring(i + 1)); //todo - this breaks very long lines again. // } // str[idx] = str[idx].Substring(0, i + 1); // break; // } // } if (overflow != "") { Screen.ResetColors(); await Print(false); idx = 0; } } }
public Event(PhysicalObject target_,int delay_) { target=target_; delay=delay_; type=EventType.MOVE; value=0; msg=""; msg_objs = null; time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
public Event(PhysicalObject target_,int delay_,AttrType attr_,int value_) { target=target_; delay=delay_; type=EventType.REMOVE_ATTR; attr=attr_; value=value_; msg=""; msg_objs = null; time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
public Event(PhysicalObject target_,int delay_,AttrType attr_) { //todo: try removing some of these constructors. maybe FINALLY do Event.Create() and/or Event.RemoveAttr(...), Event.Move(...). that might work. target=target_; delay=delay_; type=EventType.REMOVE_ATTR; attr=attr_; value=1; msg=""; msg_objs = null; time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
/*public Event(int delay_,string msg_,params PhysicalObject[] objs){ target=null; delay=delay_; type=EventType.ANY_EVENT; attr=AttrType.NO_ATTR; value=0; msg=msg_; msg_objs = new List<PhysicalObject>(); foreach(PhysicalObject obj in objs){ msg_objs.Add(obj); } time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }*/ public Event(List<Tile> area_,int delay_,EventType type_) { target=null; /*area = new List<Tile>(); //todo: reverted this. hope it works. foreach(Tile t in area_){ area.Add(t); }*/ area=area_; delay=delay_; type=type_; attr=AttrType.NO_ATTR; value=0; msg=""; msg_objs = null; time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
public void Kill(PhysicalObject target_,AttrType attr_) { if(target == target_ && type == EventType.REMOVE_ATTR && attr == attr_){ target = null; if(msg_objs != null){ msg_objs.Clear(); msg_objs = null; } if(area != null){ area.Clear(); area = null; } dead = true; } }
public Tile FirstSolidTileInLine(PhysicalObject obj) { return FirstSolidTileInLine(obj,1); }
/*public Event(List<Tile> area_,int delay_,EventType type_,string msg_,params PhysicalObject[] objs){ target=null; area=area_; delay=delay_; type=type_; attr=AttrType.NO_ATTR; value=0; msg=msg_; msg_objs = new List<PhysicalObject>(); foreach(PhysicalObject obj in objs){ msg_objs.Add(obj); } time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }*/ public Event(PhysicalObject target_,List<Tile> area_,int delay_,EventType type_) { target=target_; area=area_; delay=delay_; type=type_; attr=AttrType.NO_ATTR; value=0; msg=""; msg_objs = null; time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
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 static void AnimateExplosion(PhysicalObject obj,int radius,colorchar ch,bool single_frame){ AnimateExplosion(obj,radius,ch,50,single_frame); }
public Event FindTargetedEvent(PhysicalObject target,EventType type) { for(LinkedListNode<Event> current = list.First;current!=null;current = current.Next){ if(!current.Value.dead && current.Value.target == target && current.Value.type == type){ return current.Value; } } return null; }
public List<Tile> GetBestLineOfEffect(PhysicalObject o) { return GetBestLineOfEffect(o.row,o.col); }
public static void RemoveMouseover() { if(mouselook_current_target != null){ if(mouselook_current_desc_area != Rectangle.Empty){ int h = mouselook_current_desc_area.Height; int w = mouselook_current_desc_area.Width; Screen.UpdateGLBuffer(Global.MAP_OFFSET_ROWS,Global.MAP_OFFSET_COLS + mouselook_current_desc_area.Left,Global.MAP_OFFSET_ROWS + h - 1,Global.MAP_OFFSET_COLS + mouselook_current_desc_area.Right - 1); mouselook_current_desc_area = Rectangle.Empty; } mouselook_current_target = null; Screen.CursorVisible = true; } if(mouse_path != null){ foreach(pos p in mouse_path){ int i = p.row; int j = p.col; colorchar cch = Screen.MapChar(i,j); //I tried doing this with a single call to UpdateVertexArray. It was slow. Game.gl.UpdateVertexArray(i+Global.MAP_OFFSET_ROWS,j+Global.MAP_OFFSET_COLS,GLGame.text_surface,0,(int)cch.c,cch.color.GetFloatValues(),cch.bgcolor.GetFloatValues()); } mouse_path = null; } }
public List<Tile> GetBestExtendedLine(PhysicalObject o) { return GetBestExtendedLine(o.row,o.col); }
public List<Tile> GetAlternateExtendedBresenhamLine(PhysicalObject o) { return GetAlternateExtendedBresenhamLine(o.row,o.col); }
public Tile FirstSolidTileInLine(PhysicalObject obj,int num) { if(obj == null){ return null; } int count = 0; List<Tile> line = GetBestLine(obj.row,obj.col); line.RemoveAt(0); foreach(Tile t in line){ if(!t.passable){ ++count; if(count == num){ return t; } } } return null; }
public static void AddBurningObject(PhysicalObject o) { if(fire_event == null){ Event player_move = Event.Q.FindTargetedEvent(Event.player,EventType.MOVE); int fire_time = player_move.TimeToExecute(); int remainder = fire_time % 100; if(remainder != 0){ fire_time = (fire_time - remainder) + 100; } fire_event = new Event(fire_time - Event.Q.turn,EventType.FIRE); fire_event.tiebreaker = 0; Event.Q.Add(fire_event); } burning_objects.AddUnique(o); }
public static void AnimateExplosion(PhysicalObject obj,int radius,colorchar ch,int duration){ AnimateExplosion(obj,radius,ch,duration,false); }
public Event FindAttrEvent(PhysicalObject target,AttrType attr) { for(LinkedListNode<Event> current = list.First;current!=null;current = current.Next){ if(!current.Value.dead && current.Value.target == target && current.Value.type == EventType.REMOVE_ATTR && current.Value.attr == attr){ return current.Value; } } return null; }
public static async void AnimateExplosion(PhysicalObject obj,int radius,colorchar ch,int duration,bool single_frame){ Game.Console.CursorVisible = false; colorchar[,] prev = new colorchar[radius*2+1,radius*2+1]; for(int i=0;i<=radius*2;++i){ for(int j=0;j<=radius*2;++j){ if(MapBoundsCheck(obj.row-radius+i,obj.col-radius+j)){ prev[i,j] = MapChar(obj.row-radius+i,obj.col-radius+j); } } } if(!single_frame){ for(int i=0;i<=radius;++i){ foreach(Tile t in obj.TilesAtDistance(i)){ WriteMapChar(t.row,t.col,ch); } await Task.Delay(duration); } } else{ foreach(Tile t in obj.TilesWithinDistance(radius)){ WriteMapChar(t.row,t.col,ch); } await Task.Delay(duration); } for(int i=0;i<=radius*2;++i){ for(int j=0;j<=radius*2;++j){ if(MapBoundsCheck(obj.row-radius+i,obj.col-radius+j)){ WriteMapChar(obj.row-radius+i,obj.col-radius+j,prev[i,j]); } } } Game.Console.CursorVisible = true; }
public void KillEvents(PhysicalObject target,AttrType attr) { for(LinkedListNode<Event> current = list.First;current!=null;current = current.Next){ current.Value.Kill(target,attr); } }
public Event(PhysicalObject target_,int delay_,AttrType attr_,int value_,string msg_,params PhysicalObject[] objs) { target=target_; delay=delay_; type=EventType.REMOVE_ATTR; attr=attr_; value=value_; msg=msg_; msg_objs = new List<PhysicalObject>(); foreach(PhysicalObject obj in objs){ msg_objs.Add(obj); } time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
public Event(PhysicalObject target_,List<Tile> area_,int delay_,EventType type_,AttrType attr_,FeatureType feature_,int value_,string msg_,params PhysicalObject[] objs) { target=target_; area=area_; delay=delay_; type=type_; attr=attr_; feature = feature_; value=value_; msg=msg_; msg_objs = new List<PhysicalObject>(); foreach(PhysicalObject obj in objs){ msg_objs.Add(obj); } time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
public void UpdateStatusBarWithFeatures() { foreach(FeatureType ft in features){ if(ft == FeatureType.BONES || ft == FeatureType.GRENADE || ft == FeatureType.STABLE_TELEPORTAL || ft == FeatureType.TELEPORTAL || ft == FeatureType.TROLL_BLOODWITCH_CORPSE || ft == FeatureType.TROLL_CORPSE){ PhysicalObject o = new PhysicalObject(proto_feature[ft].name,proto_feature[ft].symbol,proto_feature[ft].color); o.p = p; UI.sidebar_objects.Add(o); } } }
public Actor FirstActorInLine(PhysicalObject obj) { return FirstActorInLine(obj,1); }
public Actor FirstActorInExtendedLine(PhysicalObject obj,int num,int max_distance) { if(obj == null){ return null; } int count = 0; List<Tile> line = GetBestExtendedLine(obj.row,obj.col); line.RemoveAt(0); foreach(Tile t in line){ if(!t.passable){ return null; } if(max_distance != -1 && DistanceFrom(t) > max_distance){ return null; } if(M.actor[t.row,t.col] != null){ ++count; if(count == num){ return M.actor[t.row,t.col]; } } } return null; }
public Event(int delay_,EventType type_) { target=null; delay=delay_; type=type_; attr=AttrType.NO_ATTR; value=0; msg=""; msg_objs = null; time_created=Q.turn; dead=false; tiebreaker = Q.Tiebreaker; }
public Actor FirstActorInLine(PhysicalObject obj,int num) { if(obj == null){ return null; } int count = 0; List<Tile> line = GetBestLine(obj.row,obj.col); line.RemoveAt(0); foreach(Tile t in line){ if(!t.passable){ return null; } if(M.actor[t.row,t.col] != null){ ++count; if(count == num){ return M.actor[t.row,t.col]; } } } return null; }
private static void Define(FeatureType type_,string name_,char symbol_,Color color_) { proto_feature[type_] = new PhysicalObject(name_,symbol_,color_); switch(type_){ case FeatureType.BONES: proto_feature[type_].sprite_offset = new pos(0,31); break; case FeatureType.FIRE: proto_feature[type_].sprite_offset = new pos(0,27); break; case FeatureType.FOG: proto_feature[type_].sprite_offset = new pos(0,21); break; case FeatureType.FORASECT_EGG: proto_feature[type_].sprite_offset = new pos(1,18); break; case FeatureType.GRENADE: proto_feature[type_].sprite_offset = new pos(0,16); break; case FeatureType.INACTIVE_TELEPORTAL: proto_feature[type_].sprite_offset = new pos(0,24); break; case FeatureType.OIL: proto_feature[type_].sprite_offset = new pos(2,20); break; case FeatureType.PIXIE_DUST: proto_feature[type_].sprite_offset = new pos(1,17); break; case FeatureType.POISON_GAS: proto_feature[type_].sprite_offset = new pos(0,22); break; case FeatureType.SLIME: proto_feature[type_].sprite_offset = new pos(2,16); break; case FeatureType.SPORES: proto_feature[type_].sprite_offset = new pos(1,19); break; case FeatureType.STABLE_TELEPORTAL: proto_feature[type_].sprite_offset = new pos(0,25); break; case FeatureType.TELEPORTAL: proto_feature[type_].sprite_offset = new pos(0,23); break; case FeatureType.TROLL_BLOODWITCH_CORPSE: proto_feature[type_].sprite_offset = new pos(0,19); break; case FeatureType.TROLL_CORPSE: proto_feature[type_].sprite_offset = new pos(0,17); break; case FeatureType.WEB: proto_feature[type_].sprite_offset = new pos(1,16); break; //todo: no dust, confusion gas, any other new stuff yet. } }
public void Kill(PhysicalObject target_,EventType type_) { if(msg_objs != null && (type == type_ || type_ == EventType.ANY_EVENT)){ if(msg_objs.Contains(target_)){ msg_objs.Remove(target_); } } Tile t = target_ as Tile; if(t != null && area != null && area.Contains(t)){ /* target = null; if(msg_objs != null){ msg_objs.Clear(); msg_objs = null; } area.Clear(); area = null; dead = true;*/ area.Remove(t); } if(target_ == target && type == EventType.TOMBSTONE_GHOST && (type_ == type || type_ == EventType.ANY_EVENT)){ target = null; return; //don't destroy the event, just remove the reference to the ghost. } if(target == target_ && (type == type_ || type_ == EventType.ANY_EVENT)){ target = null; if(msg_objs != null){ msg_objs.Clear(); msg_objs = null; } if(area != null){ area.Clear(); area = null; } dead = true; } if(type_ == EventType.CHECK_FOR_HIDDEN && type == EventType.CHECK_FOR_HIDDEN){ dead = true; } if(target_ == null && type_ == EventType.REGENERATING_FROM_DEATH && type == EventType.REGENERATING_FROM_DEATH){ dead = true; } if(target_ == null && type_ == EventType.POLTERGEIST && type == EventType.POLTERGEIST){ dead = true; } if(target_ == null && type_ == EventType.RELATIVELY_SAFE && type == EventType.RELATIVELY_SAFE){ dead = true; } if(target_ == null && type_ == EventType.BLAST_FUNGUS && type == EventType.BLAST_FUNGUS){ dead = true; } }