public override void Read(byte[] data, int offset, int length) { // The base class is constrained to only handle reads that are <= size of the cache // For larger reads we can take what is available in the cache and satisfy the // remainder from the stream if (length > (bytes.Length - align)) { // check if our inputstreambytesread < position in stream then throw out delta if (inputStreamBytesRead < positionInStream) { long amountToThrowOut = positionInStream - inputStreamBytesRead; while (amountToThrowOut > 0) { int read = inputStream.Read(bytes, 0, (int)Math.Min(amountToThrowOut, (long)bytes.Length)); inputStreamBytesRead += read; if (read == 0) { throw new Exception("Read past end of stream."); } amountToThrowOut -= read; } } int positionAlignmentOffset = position % align; int alignedLength = (length & ~(align - 1)) - positionAlignmentOffset; int cacheBytes = Math.Max(0, endPosition - position); Buffer.BlockCopy(bytes, position, data, offset, cacheBytes); int bytesRead = cacheBytes; while (bytesRead < alignedLength) { int count = inputStream.Read(data, offset + bytesRead, alignedLength - bytesRead); inputStreamBytesRead += count; if (count == 0) { throw new Exception("Read past end of stream."); } bytesRead += count; } // Update our internal state to match the read above Debug.Assert(bytesRead == alignedLength); Goto(Current.Add(alignedLength)); if (alignedLength == length) { return; } // fall through to read the unaligned portion length -= alignedLength; offset += bytesRead; } if (length > endPosition - position) { Fill(length); } Buffer.BlockCopy(bytes, position, data, offset, length); position += length; }
/// <summary> /// Adds an element with the provided key and value to the <see cref="IDictionary{TKey,TValue}" />. /// </summary> /// <param name="key">The object to use as the key of the element to add.</param> /// <param name="value">The object to use as the value of the element to add.</param> /// <exception cref="ArgumentNullException"> /// Thrown if <paramref name="key"/> is <see langword="null" />. /// </exception> /// <exception cref="ArgumentException"> /// Thrown if an element with the same key already exists in the <see cref="IDictionary{TKey,TValue}" />. /// </exception> public void Add(TKey key, TExternal value) { var storageItem = m_ExternalToStorage(value); Current.Add(key, storageItem); Changes.Add(new AddToDictionaryChange(key, storageItem)); }
/// <summary> /// Adds an item to the <see cref="IList{T}"/>. /// </summary> /// <param name="item">The object to add to the <see cref="IList{T}"/>.</param> public void Add(TExternal item) { var storageItem = m_ExternalToStorage(item); Current.Add(storageItem); Changes.Add(new InsertIntoListChange(Current.Count - 1, storageItem)); }
/// <summary> /// 添加缓存项 /// </summary> /// <param name="key">键值</param> /// <param name="data">缓存数据</param> public void Put(string key, object data) { Remove(key); CacheItemPolicy policy = new CacheItemPolicy(); Current.Add(key, data, policy, regionName); }
/// <summary> /// Begins the dictionary entry. /// </summary> /// <param name="name"> The name. </param> /// <remarks> /// </remarks> public void BeginDictionaryEntry(string name) { var e = new XElement("item", new XAttribute("key", name)); Current.Add(e); _stack.Push(e); }
/// <summary> /// 添加具有一个过期时间的缓存项 /// </summary> /// <param name="key">键值</param> /// <param name="data">缓存数据</param> /// <param name="absoluteExpiration">绝对的过期时间,到此时间后缓存过期</param> public void Add(string key, object data, DateTime absoluteExpiration) { CacheItemPolicy policy = new CacheItemPolicy(); policy.AbsoluteExpiration = absoluteExpiration; Current.Add(key, data, policy, regionName); }
public override void Read(byte[] data, int offset, int length) { // The base class is constrained to only handle reads that are <= size of the cache // For larger reads we can take what is available in the cache and satisfy the // remainder from the stream if (length > bytes.Length) { int cacheBytes = endPosition - position; Buffer.BlockCopy(bytes, position, data, offset, cacheBytes); int bytesRead = cacheBytes; while (bytesRead < length) { int count = inputStream.Read(data, offset + bytesRead, length - bytesRead); inputStreamBytesRead += (uint)count; if (count == 0) { throw new Exception("Read past end of stream."); } bytesRead += count; } // Update our internal state to match the read above Debug.Assert(bytesRead == length); Goto(Current.Add(length)); return; } if (length > endPosition - position) { Fill(length); } Buffer.BlockCopy(bytes, position, data, offset, length); position += length; }
public INI(StreamReader SR) { _Sections = new List <Section>(); Section Current = null; while (!SR.EndOfStream) { var Line = SR.ReadLine(); if (!string.IsNullOrEmpty(Line)) { if (Line.StartsWith("[") && Line.EndsWith("]")) { if (Current != null) { _Sections.Add(Current); } Current = new Section(Line.Substring(1, Line.Length - 2)); } else { Current.Add(new Pair(Line)); } } } }
private void AddToCurrent(Circle c, Circle n1, Circle n2) { Current.Add(c); c.Neighbours.Add(n1); c.Neighbours.Add(n2); n1.Neighbours.Add(c); n2.Neighbours.Add(c); }
public void AddTable(ModbusTable table = null, int index = -1) { if (Current != null) { Current.Add(table, index); Current = Current; IsModify = true; } }
/// <summary> /// 添加与某些文件关连的缓存项,当这些文件被修改时缓存过期 /// </summary> /// <param name="key">键值</param> /// <param name="data">缓存数据</param> /// <param name="files">缓存依赖的文件地址,当这些文件有变更时缓存项将自动失效</param> public void Add(string key, object data, params string[] files) { CacheItemPolicy policy = new CacheItemPolicy(); if (files != null) { policy.ChangeMonitors.Add(new HostFileChangeMonitor(files.ToList <string>())); } Current.Add(key, data, policy, regionName); }
public void Add <T>(T command) where T : IActionCommand { if (!Queued.Commands.ContainsKey(typeof(T))) { Current.Add(typeof(T), new ActionCommandList <T>()); Queued.Add(typeof(T), new ActionCommandList <T>()); } Queued.Add(command); }
/// <summary> /// Begins the array entry. /// </summary> /// <param name="idx"> The idx. </param> /// <param name="name"></param> /// <param name="noindex"></param> /// <remarks> /// </remarks> public void BeginArrayEntry(int idx, string name = "item", bool noindex = false) { name = name ?? "item"; var e = new XElement(name); if (!noindex) { e.Add(new XAttribute("__idx", idx)); } Current.Add(e); _stack.Push(e); }
/// <summary> /// 添加具有一个过期时间并且与某些文件关连的缓存项,当过期时间已到或这些文件被修改时缓存过期 /// </summary> /// <param name="key">键值</param> /// <param name="data">缓存数据</param> /// <param name="absoluteExpiration">绝对的过期时间,到此时间后缓存项自动过期</param> /// <param name="files">缓存依赖的文件地址,当这些文件有变更时缓存项将自动失效</param> public void Put(string key, object data, DateTime absoluteExpiration, params string[] files) { Remove(key); CacheItemPolicy policy = new CacheItemPolicy(); policy.AbsoluteExpiration = absoluteExpiration; if (files != null) { policy.ChangeMonitors.Add(new HostFileChangeMonitor(files.ToList <string>())); } Current.Add(key, data, policy, regionName); }
/// <summary> /// 添加具有某些依赖项的缓存项 /// </summary> /// <param name="key">键值</param> /// <param name="data">缓存数据</param> /// <param name="dependencies">依赖项</param> public void Add(string key, object data, ICollection <ChangeMonitor> changeMonitor) { CacheItemPolicy policy = new CacheItemPolicy(); if (changeMonitor != null) { foreach (var cm in changeMonitor) { policy.ChangeMonitors.Add(cm); } } Current.Add(key, data, policy, regionName); }
/// <summary> /// Begins the object item. /// </summary> /// <param name="name"> The name. </param> /// <param name="final"> if set to <c>true</c> [final]. </param> /// <remarks> /// </remarks> public void BeginObjectItem(string name, bool final) { _currentAttr = null; if (final) { _currentAttr = new XAttribute(name, ""); Current.Add(_currentAttr); } else { BeginObject(name); } }
public void Tick() { if (!_timer.Enabled) { return; } Current = Current.Add(_ticksize); if (Ticked == null) { return; } Ticked(_ticksize); }
/// <summary> /// Begins the object. /// </summary> /// <param name="name"> The name. </param> /// <remarks> /// </remarks> public void BeginObject(string name) { if (name.Contains("<")) { name = "anonymous"; } if (Regex.IsMatch(name, @"^\d")) { name = "item"; } var e = new XElement(name); Current.Add(e); _stack.Push(e); }
/// <summary> /// Renders the Window /// </summary> protected override void Render(Int32 id) { // Scroll BeginScrollView(200, Current.Curve.keys.Length * distance + distance * 2); // Add default frames if (Current.Curve.keys.Length == 0) { Current.Add(0, 0); Current.Add(1, 0); } // Edit the frames Keyframe[] fr = Current.Curve.keys; for (Int32 i = 0; i < Current.Curve.keys.Length; i++) { // Get the value Keyframe frame = fr[i]; // Edit TextField(frame.time, t => frame.time = t, new Rect(20, index * distance + 10, 60, 20)); index--; TextField(frame.value, t => frame.value = t, new Rect(90, index * distance + 10, 60, 20)); index--; TextField(frame.inTangent, t => frame.inTangent = t, new Rect(160, index * distance + 10, 60, 20)); index--; TextField(frame.outTangent, t => frame.outTangent = t, new Rect(230, index * distance + 10, 60, 20)); index--; fr[i] = frame; // Add Button("+", () => { List <Keyframe> frames = fr.ToList(); frames.Insert(i + 1, new Keyframe()); fr = frames.ToArray(); }, new Rect(300, index * distance + 10, 30, 20)); index--; // Remove Button("-", () => { List <Keyframe> frames = fr.ToList(); frames.RemoveAt(i); fr = frames.ToArray(); }, new Rect(340, index * distance + 10, 30, 20)); } Current.Curve.keys = fr; // Exit Callback?.Invoke(Current); Button(Localization.LOC_KITTOPIATECH_EXIT, () => UIController.Instance.DisableWindow(KittopiaWindows.Curve)); // End Scroll EndScrollView(); }
/// <summary> /// Initializes the region. /// </summary> /// <param name="model">The current page model.</param> public virtual void Init(object model) { using (var db = new DataContext()) { // Get the currently attached posts var posts = db.Posts.Include(p => p.Template).Include(p => p.Categories).Where(p => Posts.Contains(p.Id)).ToList(); foreach (var id in Posts) { var post = posts.Where(p => p.Id == id).SingleOrDefault(); if (posts != null) { Current.Add(post); } } // Get all of the available posts. Available = db.Posts.Include(p => p.Template).OrderBy(p => p.Template.Name).ThenBy(p => p.Title).ToList(); } }
public override T Animate(float timeStep) { T vector = Target.Subtract(Current); if (timeStep < 0) { timeStep = 0; } if (timeStep > TimeLeft) { timeStep = TimeLeft; } float relativeTimeElapsed = timeStep / TimeLeft; TimeLeft -= timeStep; vector = vector.Scale(relativeTimeElapsed); Current = Current.Add(vector); return(Current); }
public void AddLexemsToCurrentRPN(Model.ISymbol[] inputLexemList) { Operator typeAction = GetOperatorByKey(inputLexemList); if (typeAction == null) { return; } else if ((typeAction.Sign == "assign") && (inputLexemList[0] is Model.Link idnt)) { double calcultaionResult = Calculation(Current); idnt.Value = calcultaionResult; AllRPN.Add(new RPNSnap(CurrentRPNtoString(), calcultaionResult)); Current = new List <IRPNElement>(); } else if ((typeAction.Sign == "declare") && (inputLexemList[1] is Model.Link idn)) { double calcultaionResult = Calculation(Current); idn.Value = calcultaionResult; AllRPN.Add(new RPNSnap(CurrentRPNtoString(), Calculation(Current))); Current = new List <IRPNElement>(); } else if (typeAction.Sign == "end") { //AllRPN.Add(CurrentRPNtoString(), calcultaionResult); Current = new List <IRPNElement>(); } else if (typeAction.Sign == "const" || typeAction.Sign == "idn") { Current.Add((IRPNElement)inputLexemList[0]); } else { Current.Add(typeAction); } }
/// <summary> /// Place two largest circles in the circular bundle /// </summary> /// <returns></returns> private bool AddFirstTwo() { if (Circles.Count > 1) { Current.Add(Circles[0]); Circles[0].Centre = Centre; Current.Add(Circles[1]); Circles[1].Centre = new Vector(Circles[0].Centre[0] + Circles[0].Radius + Circles[1].Radius, Circles[0].Centre[1]); Circles[0].Neighbours.Add(Circles[1]); Circles[1].Neighbours.Add(Circles[0]); Radius = Circles[0].Radius * 1.2f; return(true); } if (Circles.Count == 1) { Radius = Circles[0].Radius * 1.2f; return(false); } Radius = 0; return(false); }
public void Update(GameTime gameTime) { var elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds; // Fade the screen in or out, and add/remove its controls accordingly. if (state == FadeState.In) { fadeAlpha = MathHelper.Clamp(fadeAlpha - (elapsed * FadeSpeed), 0, 1); fadeImage.Alpha = fadeAlpha; fadeImage.Color = Color.White * fadeImage.Alpha; if (fadeAlpha <= 0) { state = FadeState.Idle; } } else if (state == FadeState.Out) { fadeAlpha = MathHelper.Clamp(fadeAlpha + (elapsed * FadeSpeed), 0, 1); fadeImage.Alpha = fadeAlpha; fadeImage.Color = Color.White * fadeImage.Alpha; if (fadeAlpha >= 1) // Done fading to black, now set new screen and fade into it { Window.Client.Events.Game.ScreenChanged.Invoke(new EventManager.GameEvents.GameScreenEventArgs(fadeTo, Current)); // Destory objects from the first screen if (Current != fadeTo) { Current?.Remove(); Current = fadeTo; Current.Add(this); Window.Client.State = Current.State; } state = FadeState.In; fadeImage.BringToFront(); } } Current.Update(gameTime); }
/// <summary> /// Writes the final. /// </summary> /// <param name="value"> The value. </param> /// <remarks> /// </remarks> public void WriteFinal(object value) { if (_currentAttr != null) { try { _currentAttr.SetValue(value ?? ""); } catch (ArgumentException) { var val = value.ToString(); _currentAttr.SetValue(val ?? ""); } } else if (_stack.Count == 1) { Current.Add(new XElement("value", value)); } else { if (null != value) { Current.SetValue(value); } } }
internal static void AppendBlock(Block b) { int lastBlock = -1; if (GetLastBlock() != null) { if (b.BlockNumber <= GetLastBlock().BlockNumber) { return; } lastBlock = (int)GetLastBlock().BlockNumber; } CheckPointBlock checkPointBlock = new CheckPointBlock { AccountKey = b.AccountKey }; uint accNumber = (uint)(lastBlock + 1) * 5; if (accNumber == 0) { Log.Info("NULL"); } ulong accWork = WorkSum; for (int i = 0; i < 5; i++) { checkPointBlock.Accounts.Add(new Account { AccountNumber = accNumber, Balance = (i == 0 ? 1000000ul + (ulong)b.Fee : 0ul), BlockNumber = b.BlockNumber, UpdatedBlock = b.BlockNumber, NumberOfOperations = 0, AccountType = 0, Name = "", UpdatedByBlock = b.BlockNumber, AccountInfo = new AccountInfo { AccountKey = b.AccountKey, State = AccountState.Normal } }); accNumber++; } accWork += b.CompactTarget; WorkSum += b.CompactTarget; checkPointBlock.AccumulatedWork = accWork; checkPointBlock.AvailableProtocol = b.AvailableProtocol; checkPointBlock.BlockNumber = b.BlockNumber; checkPointBlock.BlockSignature = 2; checkPointBlock.CheckPointHash = b.CheckPointHash; checkPointBlock.CompactTarget = b.CompactTarget; checkPointBlock.Fee = b.Fee; checkPointBlock.Nonce = b.Nonce; checkPointBlock.Payload = b.Payload; checkPointBlock.ProofOfWork = b.ProofOfWork; checkPointBlock.ProtocolVersion = b.ProtocolVersion; checkPointBlock.Reward = b.Reward; checkPointBlock.Timestamp = b.Timestamp; checkPointBlock.TransactionHash = b.TransactionHash; foreach (var t in b.Transactions) { ApplyTransaction(t, b); } Current.Add(checkPointBlock); Accounts.AddRange(checkPointBlock.Accounts); if ((checkPointBlock.BlockNumber + 1) % 100 == 0) { SaveNext(); } OldCheckPointHash = CheckPointHash(Current); }
public HtmlTag Add(string tagName) { Last = Current.Add(tagName); return(Last); }
/// <summary> /// Build a list of memeber key, attributes, externkeys and identities /// </summary> /// <param name="pType"></param> internal void InitializeMembers(Type pType) { lock (RecordType) { RecordType = pType; if (Current.ContainsKey(pType)) { //ConnectionString = Current[pType].ConnectionString; RecordConnectionString = Current[pType].ConnectionString; RecordProviderFactory = Current[pType].ProviderFactory; if (String.IsNullOrEmpty(RecordConnectionString)) { RecordConnectionString = ConnectionString; } if (RecordProviderFactory == null) { RecordProviderFactory = ProviderFactory; // Record.SharedProviderFactory; } _Columns = new List <RecordColumn>(Current[pType].m_Columns); foreach (RecordColumn rc in _Columns) { rc.Record = this; } } else { MemberContext mc = new MemberContext(); Current.Add(pType, mc); Current[pType].Clear(); object[] customs = pType.GetCustomAttributes(true); foreach (object obj in customs) { if (obj is FieldCatalog) { Current[pType].CatalogName = String.Format("[{0}]", (obj as FieldCatalog).CatalogName); } if (obj is FieldScheme) { Current[pType].SchemeName = String.Format("[{0}]", (obj as FieldScheme).SchemeName); } if (obj is FieldTable) { Current[pType].TableName = String.Format("[{0}]", (obj as FieldTable).TableName); } if (obj is FieldConnectionString) { Current[pType].ConnectionString = (obj as FieldConnectionString).ConnectionString; } if (obj is FieldProviderName) { Current[pType].ProviderFactory = CreateProviderFactoty((obj as FieldProviderName).ProviderSupport); } } //ConnectionString = Current[pType].ConnectionString; RecordConnectionString = Current[pType].ConnectionString; RecordProviderFactory = Current[pType].ProviderFactory; //if (String.IsNullOrEmpty(SharedConnectionString)) SharedConnectionString = RecordConnectionString; //if (SharedProviderFactory == null) SharedProviderFactory = RecordProviderFactory; //if (String.IsNullOrEmpty(RecordConnectionString)) RecordConnectionString = ConnectionString; if (RecordProviderFactory == null) { RecordProviderFactory = ProviderFactory; // SharedProviderFactory; } // if (String.IsNullOrEmpty(RecordConnectionString)) RecordConnectionString = CreateConnectionString(pType); if (RecordProviderFactory == null) { RecordProviderFactory = ProviderFactory; // Record.SharedProviderFactory; } if (RecordProviderFactory != null) { RecordConnection = RecordProviderFactory.CreateConnection(); } //if (String.IsNullOrEmpty(RecordConnectionString)) throw new RecordConnectionStringNullException("ConnectionString is null value"); //if (ProviderFactory == null) throw new RecordProviderFactoryNullException("ProviderFactory is null value"); //if (RecordConnection == null) throw new RecordConnectionNullException("Connection is null value"); MemberInfo[] members = pType.GetProperties(RecordBindingFlags); foreach (PropertyInfo member in members) { if (member.MemberType == MemberTypes.Property) { object[] objs = member.GetCustomAttributes(typeof(Field), true); foreach (Object obj in objs) { if (obj is Field) { Field field = obj as Field; Current[pType].m_ColumnNames.Add(field.Name); Current[pType].m_Members.Add(member.Name); Current[pType].m_FieldNames.Add(field.Name); if (field.IsPrimaryKey) { Current[pType].m_FieldNameKeys.Add(field.Name); } if (field.IsIdentity) { Current[pType].m_FieldNameIdentites.Add(field.Name); Current[pType].PropertyIdentityName = member.Name; Current[pType].FieldIdentityName = field.Name; } if (field.Type == FieldType.ExternalKey) { Current[pType].m_FieldNameExternKeys.Add(field.Name); } Current[pType].m_ColumnNameToMemberName.Add(field.Name.TrimEnd(), member.Name.TrimEnd()); Current[pType].m_FieldNameToMemberName.Add(field.Name.TrimEnd(), member.Name.TrimEnd()); RecordColumn rc = new RecordColumn(field.Name.TrimEnd(), member.Name.TrimEnd(), field.ObjType, field, this); Current[pType].m_Columns.Add(rc); _Columns.Add(rc); } if (obj is FieldObject) { //FieldObject field = obj as FieldObject; //Current[pType].m_FieldNameObjects.Add(field.FieldName); //Current[pType].m_ColumnNameToMemberName.Add(field.FieldName, member.Name); //Current[pType].m_FieldNameToMemberName.Add(field.FieldName, member.Name); } } } } } } }
/// <summary> /// Resolves the battle. /// </summary> public void Resolve() { // update memories foreach (var sobj in StarSystem.SpaceObjects.Where(x => !x.IsMemory).ToArray()) { sobj.UpdateEmpireMemories(); } Current.Add(this); var reloads = new SafeDictionary <Component, double>(); var seekers = new Dictionary <Seeker, int>(); // let all combatants scan each other foreach (var c in Combatants) { c.UpdateEmpireMemories(); } for (int i = 0; i < Mod.Current.Settings.SpaceCombatTurns; i++) { LogRound(i + 1); // TODO - real 2D combat mechanics foreach (var seeker in seekers.Keys.ToArray()) { seekers[seeker]--; if (seekers[seeker] <= 0) { seekers.Remove(seeker); var minrng = seeker.LaunchingComponent.Template.WeaponMinRange; var maxrng = seeker.LaunchingComponent.Template.WeaponMinRange; var range = Dice.Next(maxrng - minrng) + minrng; // just pick a random valid range var shot = new Shot(seeker.LaunchingCombatant, seeker.LaunchingComponent, seeker.Target, range); Log.Add(seeker.CreateLogMessage(seeker + " detonates! " + seeker.Target + " takes " + shot.FullDamage + " damage.")); seeker.Target.TakeDamage(new Hit(shot, seeker.Target, seeker.Damage.Value)); } else { Log.Add(seeker.CreateLogMessage(seeker + " moves closer to " + seeker.Target + " (" + seekers[seeker] + " rounds to detonation)")); } } foreach (var launcher in Combatants.ToArray()) { // find launchable units var unitsToLaunch = new List <SpaceVehicle>(); if (launcher is Planet) { // planets can launch infinite units per turn var p = (Planet)launcher; if (p.Cargo != null && p.Cargo.Units != null) { unitsToLaunch.AddRange(p.Cargo.Units.OfType <SpaceVehicle>()); } } else if (launcher is ICargoTransferrer) { // ships, etc. can launch units based on abilities var ct = (ICargoTransferrer)launcher; foreach (var vt in Enum.GetValues(typeof(VehicleTypes)).Cast <VehicleTypes>().Distinct()) { var rate = ct.GetAbilityValue("Launch/Recover " + vt.ToSpacedString() + "s").ToInt(); unitsToLaunch.AddRange(ct.Cargo.Units.Where(u => u.Design.VehicleType == vt).OfType <SpaceVehicle>().Take(rate)); } } // launch them temporarily for combat foreach (var unit in unitsToLaunch) { Combatants.Add(unit); } } foreach (var attacker in Combatants.Shuffle(Dice).Where(sobj => sobj.Weapons.Any()).ToArray()) { if (!attacker.IsAlive) { continue; } var defenders = Combatants.Where(sobj => attacker.CanTarget(sobj) && sobj.IsAlive); if (!defenders.Any()) { continue; // no one to shoot at } var defender = defenders.PickRandom(Dice); int dmg = 0; foreach (var weapon in attacker.Weapons.Where(w => w.CanTarget(defender))) { while (reloads[weapon] <= 0) { // fire var winfo = weapon.Template.ComponentTemplate.WeaponInfo; if (winfo is SeekingWeaponInfo) { // launch a seeker var swinfo = (SeekingWeaponInfo)winfo; var seeker = new Seeker(this, attacker.Owner, attacker, weapon, defender); seekers.Add(seeker, 20 / swinfo.SeekerSpeed); LogLaunch(seeker); } else { // direct fire var minrng = weapon.Template.WeaponMinRange; var maxrng = weapon.Template.WeaponMinRange; var range = Dice.Next(maxrng - minrng) + minrng; // just pick a random valid range var shot = new Shot(attacker, weapon, defender, range); dmg += shot.FullDamage; defender.TakeDamage(new Hit(shot, defender, weapon.Template.GetWeaponDamage(range))); } // TODO - mounts that affect reload rate? reloads[weapon] += weapon.Template.ComponentTemplate.WeaponInfo.ReloadRate; } // reload reloads[weapon] -= 1; } LogSalvo(attacker, defender, dmg); } } // validate fleets since some ships might have died foreach (var fleet in Sector.SpaceObjects.OfType <Fleet>()) { fleet.Validate(); } // replenish combatants' shields foreach (var combatant in Sector.SpaceObjects.OfType <ICombatant>()) { combatant.ReplenishShields(); } // mark battle complete Current.Remove(this); Previous.Add(this); // update memories foreach (var sobj in Combatants.OfType <ISpaceObject>().Where(x => !x.IsMemory).ToArray()) { foreach (var emp in Empires) { emp.UpdateMemory(sobj);; } } }
/// <summary> /// Resolves the battle. /// </summary> public void Resolve() { // update memories foreach (var sobj in StarSystem.SpaceObjects.Where(x => !x.IsMemory).ToArray()) { sobj.UpdateEmpireMemories(); } Current.Add(this); var reloads = new SafeDictionary <Component, double>(); var locations = new SafeDictionary <ICombatant, IntVector2>(); PlaceCombatants(locations); Events = new List <IList <IBattleEvent> >(); UpdateBounds(0, locations.Values); // let all combatants scan each other foreach (var c in Combatants) { c.UpdateEmpireMemories(); } // make a query so we can check who's alive var alives = Combatants.Where(q => q.IsAlive); for (int i = 0; i < MaxRounds; i++) { var combatSpeeds = new SafeDictionary <ICombatant, double>(); var multiplex = new SafeDictionary <ICombatant, HashSet <ICombatant> >(true); foreach (var c in Combatants) { combatSpeeds[c] = c.CombatSpeed; } int GetCombatSpeedThisRound(ICombatant c) { return((int)(combatSpeeds[c] + CombatSpeedBuffer[c])); } Events.Add(new List <IBattleEvent>()); if (i == 0) { // first round, all combatants appear foreach (var c in Combatants) { Events.Last().Add(new CombatantAppearsEvent(this, c, locations[c])); } } var turnorder = alives.OrderBy(x => x is Seeker ? 1 : 0).ThenBy(x => combatSpeeds[x]).ThenShuffle(Dice).ToArray(); // phase 0: reload weapons foreach (var w in turnorder.SelectMany(q => q.Weapons)) { reloads[w]--; if (reloads[w] < 0) { reloads[w] = 0; } } // phase 1: combatants move starting with the slowest (so the faster ships get to react to their moves) - but seekers go last so they get a chance to hit foreach (var c in turnorder) { var oldpos = locations[c]; if (c is Seeker s) { if (locations[s] == null) { continue; // HACK - seeker is destroyed but still showing up in turn order } if (locations[s.Target] == null) { s.Hitpoints = 0; // seekers self destruct when their target is destroyed Events.Last().Add(new CombatantDestroyedEvent(this, s, locations[s])); continue; } s.DistanceTraveled += Math.Min(GetCombatSpeedThisRound(c), locations[s].DistanceToEightWay(locations[s.Target])); locations[s] = IntVector2.InterpolateEightWay(locations[s], locations[s.Target], GetCombatSpeedThisRound(c)); if (s.DistanceTraveled > s.WeaponInfo.MaxRange) { s.Hitpoints = 0; Events.Last().Add(new CombatantDestroyedEvent(this, s, locations[s])); } } else { // TODO - both pursue target and evade scary enemies at the same time using heatmap // find out how good each target is var targetiness = new SafeDictionary <ICombatant, double>(); foreach (var target in alives.Where(x => c.IsHostileTo(x.Owner) && (c.CanTarget(x) || (x is Planet && c is ICargoContainer cc && cc.Cargo.Units.OfType <Troop>().Any())))) { targetiness[target] = 1d / (locations[target] - locations[c]).LengthEightWay; } if (!targetiness.Any()) { // evade enemies var heatmap = new HeatMap(); foreach (var e in alives.Where(x => x.IsHostileTo(c.Owner) && x.CanTarget(c))) { int threat; if (e.Weapons.Any()) { threat = GetCombatSpeedThisRound(e) + e.Weapons.Where(w => w.CanTarget(c)).Max(w => w.Template.WeaponMaxRange); } else { threat = 0; } heatmap.AddLinearGradientEightWay(locations[e], threat, threat, -1); } if (c.FillsCombatTile) { // only one ship/base/planet per tile foreach (var tile in heatmap.ToArray()) { if (locations.Any(q => q.Key.FillsCombatTile && q.Value == tile.Key)) { heatmap.Remove(tile.Key); } } } if (heatmap.Any()) { locations[c] = heatmap.FindMin(locations[c], GetCombatSpeedThisRound(c)); } } else { // move to max range that we can inflict max damage on best target var goodTargets = targetiness.Where(x => !IgnoredTargets[c].Contains(x.Key)).WithMax(x => x.Value); ICombatant bestTarget = null; if (goodTargets.Any()) { bestTarget = goodTargets.First().Key; } if (bestTarget == null) { // try previously ignored targets IgnoredTargets[c].Clear(); goodTargets = targetiness.Where(x => !IgnoredTargets[c].Contains(x.Key)).WithMax(x => x.Value); if (goodTargets.Any()) { bestTarget = goodTargets.First().Key; } } if (bestTarget != null) { gotosAreEvil: var maxdmg = 0; var maxdmgrange = 0; if (c.Weapons.Any()) { for (var range = 0; range < c.Weapons.Max(w => w.Template.WeaponMaxRange); range++) { var dmg = c.Weapons.Where(w => w.CanTarget(bestTarget)).Sum(w => w.Template.GetWeaponDamage(range)); if (dmg >= maxdmg) { maxdmg = dmg; maxdmgrange = range; } } } if (c.Weapons.Any(w => w.Template.ComponentTemplate.WeaponInfo.IsSeeker) && locations[c].DistanceToEightWay(locations[bestTarget]) > DistancesToTargets[c]) { // adjust desired range due to seeker speed and target speed if retreating var roundsToClose = c.Weapons.Where(w => w.Template.ComponentTemplate.WeaponInfo.IsSeeker).Max(w => (int)Math.Ceiling((double)w.Template.WeaponMaxRange / (double)(w.Template.ComponentTemplate.WeaponInfo as SeekingWeaponInfo).SeekerSpeed)); var distanceAdjustment = (int)Ceiling(combatSpeeds[bestTarget] * roundsToClose); maxdmgrange -= distanceAdjustment; if (maxdmgrange < 0) { maxdmgrange = 0; } } var targetPos = locations[bestTarget]; var tiles = new HashSet <IntVector2>(); for (var x = targetPos.X - maxdmgrange; x <= targetPos.X + maxdmgrange; x++) { tiles.Add(new IntVector2(x, targetPos.Y - maxdmgrange)); tiles.Add(new IntVector2(x, targetPos.Y + maxdmgrange)); } for (var y = targetPos.Y - maxdmgrange; y <= targetPos.Y + maxdmgrange; y++) { tiles.Add(new IntVector2(targetPos.X - maxdmgrange, y)); tiles.Add(new IntVector2(targetPos.X + maxdmgrange, y)); } if (c.FillsCombatTile) { foreach (var tile in tiles.ToArray()) { if (locations.Any(q => q.Key.FillsCombatTile && q.Value == tile)) { tiles.Remove(tile); } } } if (tiles.Any()) { var closest = tiles.WithMin(t => t.DistanceToEightWay(locations[c])).First(); locations[c] = IntVector2.InterpolateEightWay(locations[c], closest, GetCombatSpeedThisRound(c), vec => locations.Values.Contains(vec)); var newdist = locations[c].DistanceToEightWay(locations[bestTarget]); if (DistancesToTargets.ContainsKey(c) && newdist >= DistancesToTargets[c] && combatSpeeds[c] <= combatSpeeds[bestTarget] && !c.Weapons.Any(w => w.Template.WeaponMaxRange >= newdist)) { DistancesToTargets.Remove(c); IgnoredTargets[c].Add(bestTarget); // can't catch it, might as well find a new target goodTargets = targetiness.Where(x => !IgnoredTargets[c].Contains(x.Key)).WithMax(x => x.Value); bestTarget = null; if (goodTargets.Any()) { bestTarget = goodTargets.First().Key; } if (bestTarget == null) { goto gotosAreVeryEvil; } goto gotosAreEvil; } else { DistancesToTargets[c] = newdist; } } } else { DistancesToTargets.Remove(c); } } } gotosAreVeryEvil: if (locations[c] != oldpos) { Events.Last().Add(new CombatantMovesEvent(this, c, oldpos, locations[c])); } } UpdateBounds(i, locations.Values); // phase 2: combatants launch units foreach (var c in turnorder) { // find launchable units var unitsToLaunch = new List <(ICombatant Launcher, SpaceVehicle Launchee)>(); if (c is Planet) { // planets can launch infinite units per turn var p = (Planet)c; if (p.Cargo != null && p.Cargo.Units != null) { foreach (var u in p.Cargo.Units.OfType <SpaceVehicle>()) { unitsToLaunch.Add((p, u)); } } } else if (c is ICargoTransferrer) { // ships, etc. can launch units based on abilities var ct = (ICargoTransferrer)c; foreach (var vt in Enum.GetValues(typeof(VehicleTypes)).Cast <VehicleTypes>().Distinct()) { var rate = ct.GetAbilityValue("Launch/Recover " + vt.ToSpacedString() + "s").ToInt(); foreach (var u in ct.Cargo.Units.Where(u => u.Design.VehicleType == vt).OfType <SpaceVehicle>().Take(rate)) { unitsToLaunch.Add((c, u)); } } } // launch them temporarily for combat foreach (var info in unitsToLaunch) { Launchers[info.Launchee] = info.Launcher; if (info.Launcher is ICargoTransferrer ct && info.Launchee is IUnit u) { ct.RemoveUnit(u); } Combatants.Add(info.Item2); StartCombatants[info.Item2.ID] = info.Item2.Copy(); for (var ix = 0; ix < info.Item2.Weapons.Count(); ix++) { var w = info.Item2.Weapons.ElementAt(ix); var wc = StartCombatants[info.Item2.ID].Weapons.ElementAt(ix); } locations[info.Launchee] = new IntVector2(locations[info.Launcher]); Events.Last().Add(new CombatantLaunchedEvent(this, info.Launcher, info.Launchee, locations[info.Launchee])); } } turnorder = alives.OrderBy(x => x.CombatSpeed).ThenShuffle(Dice).ToArray(); // phase 3: combatants fire point defense non-warhead weapons starting with the fastest (so the faster ships get to inflict damage first and possibly KO enemies preventing them from firing back) foreach (var c in turnorder.Reverse()) { foreach (var w in c.Weapons.Where(w => w.Template.ComponentTemplate.WeaponInfo.IsPointDefense && !w.Template.ComponentTemplate.WeaponInfo.IsWarhead)) { TryFireWeapon(c, w, reloads, locations, multiplex); } } turnorder = alives.OrderBy(x => x.CombatSpeed).ThenShuffle(Dice).ToArray(); // phase 4: point defense seekers detonate foreach (var s in turnorder.Reverse().OfType <Seeker>().Where(s => s.WeaponInfo.IsPointDefense)) { CheckSeekerDetonation(s, locations); } turnorder = alives.OrderBy(x => x.CombatSpeed).ThenShuffle(Dice).ToArray(); // phase 5: ships fire non-PD non-warhead weapons starting with the fastest (so the faster ships get to inflict damage first and possibly KO enemies preventing them from firing back) foreach (var c in turnorder.Reverse()) { foreach (var w in c.Weapons.Where(w => !w.Template.ComponentTemplate.WeaponInfo.IsPointDefense && !w.Template.ComponentTemplate.WeaponInfo.IsWarhead)) { TryFireWeapon(c, w, reloads, locations, multiplex); } } turnorder = alives.OrderBy(x => x.CombatSpeed).ThenShuffle(Dice).ToArray(); // phase 6: non-PD seekers detonate foreach (var s in turnorder.Reverse().OfType <Seeker>().Where(s => !s.WeaponInfo.IsPointDefense)) { CheckSeekerDetonation(s, locations); } turnorder = alives.OrderBy(x => x.CombatSpeed).ThenShuffle(Dice).ToArray(); // phase 7: ramming! only activates if ship has no other weapons foreach (var c in turnorder.Reverse()) { if (!c.Weapons.Any(w => !w.Template.ComponentTemplate.WeaponInfo.IsWarhead)) { // TODO - add damage from ship HP on both sides foreach (var w in c.Weapons.Where(w => w.Template.ComponentTemplate.WeaponInfo.IsWarhead)) { TryFireWeapon(c, w, reloads, locations, multiplex); } } } turnorder = alives.OrderBy(x => x.CombatSpeed).ThenShuffle(Dice).ToArray(); // TODO - boarding // phase 8: drop troops foreach (var c in turnorder.Reverse()) { if (c is ICargoTransferrer cc && cc.AllUnits.OfType <Troop>().Any()) { // find enemy planets in the same square var dropTargets = locations.Where(q => q.Key != c && q.Value == locations[c] && q.Key is Planet p && c.IsHostileTo(p.Owner)).Select(q => q.Key).Cast <Planet>(); var dropTarget = dropTargets.PickRandom(Dice); if (dropTarget != null) { var cd = new CargoDelta(); cd.UnitTypeTonnage.Add(VehicleTypes.Troop, null); cc.TransferCargo(cd, dropTarget, cc.Owner, true); var groundBattle = new GroundBattle(dropTarget); groundBattle.Resolve(); } } } // clear used combat speed buffer speed foreach (var x in Combatants) { CombatSpeedBuffer[x] += x.CombatSpeed - Floor(x.CombatSpeed); CombatSpeedBuffer[x] -= Floor(CombatSpeedBuffer[x]); } UpdateBounds(i, locations.Values); bool hostile = false; foreach (var a in alives) { foreach (var b in alives) { // TODO - check if ships want to ram even if they have no weapons if (a.IsHostileTo(b.Owner) && a.Weapons.Any()) { hostile = true; break; } } if (hostile) { break; } } if (!hostile) { break; } } // recover units var orphans = new List <IUnit>(); foreach (var u in Combatants.OfType <IUnit>()) { if (Launchers[u] is ICargoTransferrer cc && cc.CargoStorageFree() >= u.Design.Hull.Size && u.Owner == cc.Owner) { cc.Cargo.Units.Add(u); }