ModNPC documentation

ModNPC documentation

--- patches/tModLoader/Terraria.ModLoader/ModNPC.cs
+++ patches/tModLoader/Terraria.ModLoader/ModNPC.cs
@@ -6,16 +6,25 @@

 namespace Terraria.ModLoader
 {
+	/// <summary>
+	/// This class serves as a place for you to place all your properties and hooks for each NPC. Create instances of ModNPC (preferably overriding this class) to pass as parameters to Mod.AddNPC.
+	/// </summary>
 	public class ModNPC
 	{
 		//add modNPC property to Terraria.NPC (internal set)
 		//set modNPC to null at beginning of Terraria.NPC.SetDefaults
+		/// <summary>
+		/// The NPC object that this ModNPC controls.
+		/// </summary>
 		public NPC npc
 		{
 			get;
 			internal set;
 		}

+		/// <summary>
+		/// The mod that added this ModNPC.
+		/// </summary>
 		public Mod mod
 		{
 			get;
@@ -24,33 +33,69 @@

 		internal string texture;
 		internal string[] altTextures;
+		/// <summary>
+		/// Determines which type of vanilla NPC this ModNPC will copy the behavior (AI) of. Leave as 0 to not copy any behavior. Defaults to 0.
+		/// </summary>
 		public int aiType = 0;
+		/// <summary>
+		/// Determines which type of vanilla NPC this ModNPC will copy the animation (FindFrame) of. Leave as 0 to not copy any animation. Defaults to 0.
+		/// </summary>
 		public int animationType = 0;
+		/// <summary>
+		/// The item type of the boss bag that is dropped when DropBossBags is called for this NPC.
+		/// </summary>
 		public int bossBag = -1;
 		//make changes to Terraria.Main.UpdateMusic (see patch files)
+		/// <summary>
+		/// The ID of the music that plays when this NPC is on or near the screen. Defaults to -1, which means music plays normally.
+		/// </summary>
 		public int music = -1;
 		//in Terraria.Main.NPCAddHeight at end of else if chain add
 		//  else if(Main.npc[i].modNPC != null) { num = Main.npc[i].modNPC.drawOffsetY; }
+		/// <summary>
+		/// The vertical offset used for drawing this NPC. Defaults to 0.
+		/// </summary>
 		public float drawOffsetY = 0f;
 		//in Terraria.Item.NPCToBanner before returning 0 add
 		//  if(i >= NPCID.Count) { return NPCLoader.npcs[i].banner; }
 		//in Terraria.Item.BannerToNPC before returning 0 add
 		//  if(i >= NPCID.Count) { return i; }
+		/// <summary>
+		/// The type of NPC that this NPC will be considered as when determining banner drops and banner bonuses. By default this will be 0, which means this NPC is not associated with any banner. To give your NPC its own banner, set this field to the NPC's type.
+		/// </summary>
 		public int banner = 0;
 		//in Terraria.NPC.NPCLoot after if statements setting num6 add
 		//  if(num3 >= NPCID.Count) { num6 = NPCLoader.npcs[num3].bannerItem; }
+		/// <summary>
+		/// The type of the item this NPC drops for every 50 times it is defeated. For any ModNPC whose banner field is set to the type of this NPC, that ModNPC will drop this banner.
+		/// </summary>
 		public int bannerItem = 0;

+		/// <summary>
+		/// ModNPC constructor.
+		/// </summary>
 		public ModNPC()
 		{
 			npc = new NPC();
 		}

+		/// <summary>
+		/// Allows you to automatically load an NPC instead of using Mod.AddNPC. Return true to allow autoloading; by default returns the mod's autoload property. Name is initialized to the overriding class name, texture is initialized to the namespace and overriding class name with periods replaced with slashes, and altTextures is initialized to null. Use this method to either force or stop an autoload, to change the default display name and texture path, or to give the NPC alternate textures.
+		/// </summary>
+		/// <param name="name"></param>
+		/// <param name="texture"></param>
+		/// <param name="altTextures"></param>
+		/// <returns></returns>
 		public virtual bool Autoload(ref string name, ref string texture, ref string[] altTextures)
 		{
 			return mod.Properties.Autoload;
 		}

+		/// <summary>
+		/// Allows you to control the path to this NPC's head texture when it is autoloaded. The headTexture defaults to the default NPC texture plus "_Head", and the bossHeadTexture defaults to the default headTexture plus "_Head_Boss". If the textures don't exist, the NPC will not be given a head texture. The headTexture should be used for town NPCs, and the bossHeadTexture should be used for any non-town NPC that you want to display on the map.
+		/// </summary>
+		/// <param name="headTexture"></param>
+		/// <param name="bossHeadTexture"></param>
 		public virtual void AutoloadHead(ref string headTexture, ref string bossHeadTexture)
 		{
 		}
@@ -64,247 +109,536 @@
 			newNPC.SetDefaults();
 		}

+		/// <summary>
+		/// Whether instances of this ModNPC are created through a memberwise clone or its constructor. Defaults to false.
+		/// </summary>
 		public virtual bool CloneNewInstances => false;

+		/// <summary>
+		/// Allows you to set all your NPC's properties, such as width, damage, aiStyle, lifeMax, etc.
+		/// </summary>
 		public virtual void SetDefaults()
 		{
 		}

+		/// <summary>
+		/// Allows you to customize this NPC's stats in expert mode. This is useful because expert mode's doubling of damage and life might be too much sometimes (for example, with bosses). Also useful for scaling life with the number of players in the world.
+		/// </summary>
+		/// <param name="numPlayers"></param>
+		/// <param name="bossLifeScale"></param>
 		public virtual void ScaleExpertStats(int numPlayers, float bossLifeScale)
 		{
 		}

+		/// <summary>
+		/// This is where you reset any fields you add to your subclass to their default states. This is necessary in order to reset your fields if they are conditionally set by a tick update but the condition is no longer satisfied. (Note: This hook is only really useful for GlobalNPC, but is included in ModNPC for completion.)
+		/// </summary>
 		public virtual void ResetEffects()
 		{
 		}

+		/// <summary>
+		/// Allows you to determine how this NPC behaves. Return false to stop the vanilla AI and the AI hook from being run. Returns true by default.
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool PreAI()
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to determine how this NPC behaves. This will only be called if PreAI returns true.
+		/// </summary>
 		public virtual void AI()
 		{
 		}

+		//Allows you to determine how this NPC behaves. This will be called regardless of what PreAI returns.
 		public virtual void PostAI()
 		{
 		}

+		/// <summary>
+		/// If you are storing AI information outside of the npc.ai array, use this to send that AI information between clients and servers.
+		/// </summary>
+		/// <param name="writer"></param>
 		public virtual void SendExtraAI(BinaryWriter writer)
 		{
 		}

+		/// <summary>
+		/// Use this to receive information that was sent in SendExtraAI.
+		/// </summary>
+		/// <param name="reader"></param>
 		public virtual void ReceiveExtraAI(BinaryReader reader)
 		{
 		}

+		/// <summary>
+		/// Allows you to modify the frame from this NPC's texture that is drawn, which is necessary in order to animate NPCs.
+		/// </summary>
+		/// <param name="frameHeight"></param>
 		public virtual void FindFrame(int frameHeight)
 		{
 		}

+		/// <summary>
+		/// Allows you to make things happen whenever this NPC is hit, such as creating dust or gores.
+		/// </summary>
+		/// <param name="hitDirection"></param>
+		/// <param name="damage"></param>
 		public virtual void HitEffect(int hitDirection, double damage)
 		{
 		}

+		/// <summary>
+		/// Allows you to make the NPC either regenerate health or take damage over time by setting npc.lifeRegen. Regeneration or damage will occur at a rate of half of npc.lifeRegen per second. The damage parameter is the number that appears above the NPC's head if it takes damage over time.
+		/// </summary>
+		/// <param name="damage"></param>
 		public virtual void UpdateLifeRegen(ref int damage)
 		{
 		}

+		/// <summary>
+		/// Whether or not to run the code for checking whether this NPC will remain active. Return false to stop this NPC from being despawned and to stop this NPC from counting towards the limit for how many NPCs can exist near a player. Returns true by default.
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool CheckActive()
 		{
 			return true;
 		}

+		/// <summary>
+		/// Whether or not this NPC should be killed when it reaches 0 health. You may program extra effects in this hook (for example, how Golem's head lifts up for the second phase of its fight). Return false to stop this NPC from being killed. Returns true by default.
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool CheckDead()
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to determine whether or not this NPC will drop anything at all. Return false to stop the NPC from dropping anything. Returns true by default.
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool PreNPCLoot()
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to make things happen when this NPC dies (for example, dropping items).
+		/// </summary>
 		public virtual void NPCLoot()
 		{
 		}

+		/// <summary>
+		/// Allows you to customize what happens when this boss dies, such as which name is displayed in the defeat message and what type of potion it drops.
+		/// </summary>
+		/// <param name="name"></param>
+		/// <param name="potionType"></param>
 		public virtual void BossLoot(ref string name, ref int potionType)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine whether this NPC can hit the given player. Return false to block this NPC from hitting the target. Returns true by default. CooldownSlot determines which of the player's cooldown counters to use (-1, 0, or 1), and defaults to -1.
+		/// </summary>
+		/// <param name="target"></param>
+		/// <param name="cooldownSlot"></param>
+		/// <returns></returns>
 		public virtual bool CanHitPlayer(Player target, ref int cooldownSlot)
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to modify the damage, etc., that this NPC does to a player.
+		/// </summary>
+		/// <param name="target"></param>
+		/// <param name="damage"></param>
+		/// <param name="crit"></param>
 		public virtual void ModifyHitPlayer(Player target, ref int damage, ref bool crit)
 		{
 		}

+		/// <summary>
+		/// Allows you to create special effects when this NPC hits a player (for example, inflicting debuffs).
+		/// </summary>
+		/// <param name="target"></param>
+		/// <param name="damage"></param>
+		/// <param name="crit"></param>
 		public virtual void OnHitPlayer(Player target, int damage, bool crit)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine whether this NPC can hit the given friendly NPC. Return true to allow hitting the target, return false to block this NPC from hitting the target, and return null to use the vanilla code for whether the target can be hit. Returns null by default.
+		/// </summary>
+		/// <param name="target"></param>
+		/// <returns></returns>
 		public virtual bool? CanHitNPC(NPC target)
 		{
 			return null;
 		}

+		/// <summary>
+		/// Allows you to modify the damage, knockback, etc., that this NPC does to a friendly NPC.
+		/// </summary>
+		/// <param name="target"></param>
+		/// <param name="damage"></param>
+		/// <param name="knockback"></param>
+		/// <param name="crit"></param>
 		public virtual void ModifyHitNPC(NPC target, ref int damage, ref float knockback, ref bool crit)
 		{
 		}

+		/// <summary>
+		/// Allows you to create special effects when this NPC hits a friendly NPC.
+		/// </summary>
+		/// <param name="target"></param>
+		/// <param name="damage"></param>
+		/// <param name="knockback"></param>
+		/// <param name="crit"></param>
 		public virtual void OnHitNPC(NPC target, int damage, float knockback, bool crit)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine whether this NPC can be hit by the given melee weapon when swung. Return true to allow hitting the NPC, return false to block hitting the NPC, and return null to use the vanilla code for whether the NPC can be hit. Returns null by default.
+		/// </summary>
+		/// <param name="player"></param>
+		/// <param name="item"></param>
+		/// <returns></returns>
 		public virtual bool? CanBeHitByItem(Player player, Item item)
 		{
 			return null;
 		}

+		/// <summary>
+		/// Allows you to modify the damage, knockback, etc., that this NPC takes from a melee weapon.
+		/// </summary>
+		/// <param name="player"></param>
+		/// <param name="item"></param>
+		/// <param name="damage"></param>
+		/// <param name="knockback"></param>
+		/// <param name="crit"></param>
 		public virtual void ModifyHitByItem(Player player, Item item, ref int damage, ref float knockback, ref bool crit)
 		{
 		}

+		/// <summary>
+		/// Allows you to create special effects when this NPC is hit by a melee weapon.
+		/// </summary>
+		/// <param name="player"></param>
+		/// <param name="item"></param>
+		/// <param name="damage"></param>
+		/// <param name="knockback"></param>
+		/// <param name="crit"></param>
 		public virtual void OnHitByItem(Player player, Item item, int damage, float knockback, bool crit)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine whether this NPC can be hit by the given projectile. Return true to allow hitting the NPC, return false to block hitting the NPC, and return null to use the vanilla code for whether the NPC can be hit. Returns null by default.
+		/// </summary>
+		/// <param name="projectile"></param>
+		/// <returns></returns>
 		public virtual bool? CanBeHitByProjectile(Projectile projectile)
 		{
 			return null;
 		}

+		/// <summary>
+		/// Allows you to modify the damage, knockback, etc., that this NPC takes from a projectile.
+		/// </summary>
+		/// <param name="projectile"></param>
+		/// <param name="damage"></param>
+		/// <param name="knockback"></param>
+		/// <param name="crit"></param>
+		/// <param name="hitDirection"></param>
 		public virtual void ModifyHitByProjectile(Projectile projectile, ref int damage, ref float knockback, ref bool crit, ref int hitDirection)
 		{
 		}

+		/// <summary>
+		/// Allows you to create special effects when this NPC is hit by a projectile.
+		/// </summary>
+		/// <param name="projectile"></param>
+		/// <param name="damage"></param>
+		/// <param name="knockback"></param>
+		/// <param name="crit"></param>
 		public virtual void OnHitByProjectile(Projectile projectile, int damage, float knockback, bool crit)
 		{
 		}

+		/// <summary>
+		/// Allows you to use a custom damage formula for when this NPC takes damage from any source. For example, you can change the way defense works or use a different crit multiplier. Return false to stop the game from running the vanilla damage formula; returns true by default.
+		/// </summary>
+		/// <param name="damage"></param>
+		/// <param name="defense"></param>
+		/// <param name="knockback"></param>
+		/// <param name="hitDirection"></param>
+		/// <param name="crit"></param>
+		/// <returns></returns>
 		public virtual bool StrikeNPC(ref double damage, int defense, ref float knockback, int hitDirection, ref bool crit)
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to customize the boss head texture used by this NPC based on its state.
+		/// </summary>
+		/// <param name="index"></param>
 		public virtual void BossHeadSlot(ref int index)
 		{
 		}

+		/// <summary>
+		/// Allows you to customize the rotation of this NPC's boss head icon on the map.
+		/// </summary>
+		/// <param name="rotation"></param>
 		public virtual void BossHeadRotation(ref float rotation)
 		{
 		}

+		/// <summary>
+		/// Allows you to flip this NPC's boss head icon on the map.
+		/// </summary>
+		/// <param name="spriteEffects"></param>
 		public virtual void BossHeadSpriteEffects(ref SpriteEffects spriteEffects)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine the color and transparency in which this NPC is drawn. Return null to use the default color (normally light and buff color). Returns null by default.
+		/// </summary>
+		/// <param name="drawColor"></param>
+		/// <returns></returns>
 		public virtual Color? GetAlpha(Color drawColor)
 		{
 			return null;
 		}

+		/// <summary>
+		/// Allows you to add special visual effects to this NPC (such as creating dust), and modify the color in which the NPC is drawn.
+		/// </summary>
+		/// <param name="drawColor"></param>
 		public virtual void DrawEffects(ref Color drawColor)
 		{
 		}

+		/// <summary>
+		/// Allows you to draw things behind this NPC, or to modify the way this NPC is drawn. Return false to stop the game from drawing the NPC (useful if you're manually drawing the NPC). Returns true by default.
+		/// </summary>
+		/// <param name="spriteBatch"></param>
+		/// <param name="drawColor"></param>
+		/// <returns></returns>
 		public virtual bool PreDraw(SpriteBatch spriteBatch, Color drawColor)
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to draw things in front of this NPC. This method is called even if PreDraw returns false.
+		/// </summary>
+		/// <param name="spriteBatch"></param>
+		/// <param name="drawColor"></param>
 		public virtual void PostDraw(SpriteBatch spriteBatch, Color drawColor)
 		{
 		}

+		/// <summary>
+		/// Allows you to control how the health bar for this NPC is drawn. The hbPosition parameter is the same as Main.hbPosition; it determines whether the health bar gets drawn above or below the NPC by default. The scale parameter is the health bar's size. By default, it will be the normal 1f; most bosses set this to 1.5f. Return null to let the normal vanilla health-bar-drawing code to run. Return false to stop the health bar from being drawn. Return true to draw the health bar in the position specified by the position parameter (note that this is the world position, not screen position).
+		/// </summary>
+		/// <param name="hbPosition"></param>
+		/// <param name="scale"></param>
+		/// <param name="position"></param>
+		/// <returns></returns>
 		public virtual bool? DrawHealthBar(byte hbPosition, ref float scale, ref Vector2 position)
 		{
 			return null;
 		}

+		/// <summary>
+		/// Whether or not this NPC can spawn with the given spawning conditions. Return the weight for the chance of this NPC to spawn compared to vanilla mobs. All vanilla mobs combined have a total weight of 1. Returns 0 by default, which disables natural spawning.
+		/// </summary>
+		/// <param name="spawnInfo"></param>
+		/// <returns></returns>
 		public virtual float CanSpawn(NPCSpawnInfo spawnInfo)
 		{
 			return 0f;
 		}

+		/// <summary>
+		/// Allows you to customize how this NPC is created when it naturally spawns (for example, its position or ai array). Return the return value of NPC.NewNPC. By default this method spawns this NPC on top of the tile at the given coordinates.
+		/// </summary>
+		/// <param name="tileX"></param>
+		/// <param name="tileY"></param>
+		/// <returns></returns>
 		public virtual int SpawnNPC(int tileX, int tileY)
 		{
 			return NPC.NewNPC(tileX * 16 + 8, tileY * 16, npc.type);
 		}

+		/// <summary>
+		/// Whether or not the conditions have been met for this town NPC to be able to move into town. For example, the Demolitionist requires that any player has an explosive.
+		/// </summary>
+		/// <param name="numTownNPCs"></param>
+		/// <param name="money"></param>
+		/// <returns></returns>
 		public virtual bool CanTownNPCSpawn(int numTownNPCs, int money)
 		{
 			return false;
 		}

+		/// <summary>
+		/// Allows you to define special conditions required for this town NPC's house. For example, Truffle requires the house to be in an aboveground mushroom biome.
+		/// </summary>
+		/// <param name="left"></param>
+		/// <param name="right"></param>
+		/// <param name="top"></param>
+		/// <param name="bottom"></param>
+		/// <returns></returns>
 		public virtual bool CheckConditions(int left, int right, int top, int bottom)
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to give this town NPC any name when it spawns. By default returns something embarrassing.
+		/// </summary>
+		/// <returns></returns>
 		public virtual string TownNPCName()
 		{
 			return "No-Name";
 		}

+		/// <summary>
+		/// Allows you to determine whether this town NPC wears a party hat during a party. Returns true by default.
+		/// </summary>
+		/// <returns></returns>
 		public virtual bool UsesPartyHat()
 		{
 			return true;
 		}

+		/// <summary>
+		/// Allows you to give this town NPC a chat message when a player talks to it. By default returns something embarrassing.
+		/// </summary>
+		/// <returns></returns>
 		public virtual string GetChat()
 		{
 			return "My modder forgot to give me a chat message.";
 		}

+		/// <summary>
+		/// Allows you to set the text for the buttons that appear on this town NPC's chat window. A parameter left as an empty string will not be included as a button on the chat window.
+		/// </summary>
+		/// <param name="button"></param>
+		/// <param name="button2"></param>
 		public virtual void SetChatButtons(ref string button, ref string button2)
 		{
 		}

+		/// <summary>
+		/// Allows you to make something happen whenever a button is clicked on this town NPC's chat window. The firstButton parameter tells whether the first button or second button (button and button2 from SetChatButtons) was clicked. Set the shop parameter to true to open this NPC's shop.
+		/// </summary>
+		/// <param name="firstButton"></param>
+		/// <param name="shop"></param>
 		public virtual void OnChatButtonClicked(bool firstButton, ref bool shop)
 		{
 		}

+		/// <summary>
+		/// Allows you to add items to this town NPC's shop. Add an item by setting the defaults of shop.item[nextSlot] then incrementing nextSlot. In the end, nextSlot must have a value of 1 greater than the highest index in shop.item that contains an item.
+		/// </summary>
+		/// <param name="shop"></param>
+		/// <param name="nextSlot"></param>
 		public virtual void SetupShop(Chest shop, ref int nextSlot)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine the damage and knockback of this town NPC's attack before the damage is scaled. (More information on scaling in GlobalNPC.BuffTownNPCs.)
+		/// </summary>
+		/// <param name="damage"></param>
+		/// <param name="knockback"></param>
 		public virtual void TownNPCAttackStrength(ref int damage, ref float knockback)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine the cooldown between each of this town NPC's attack. The cooldown will be a number greater than or equal to the first parameter, and less then the sum of the two parameters.
+		/// </summary>
+		/// <param name="cooldown"></param>
+		/// <param name="randExtraCooldown"></param>
 		public virtual void TownNPCAttackCooldown(ref int cooldown, ref int randExtraCooldown)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine the projectile type of this town NPC's attack, and how long it takes for the projectile to actually appear. This hook is only used when the town NPC has an attack type of 0 (throwing), 1 (shooting), or 2 (magic).
+		/// </summary>
+		/// <param name="projType"></param>
+		/// <param name="attackDelay"></param>
 		public virtual void TownNPCAttackProj(ref int projType, ref int attackDelay)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine the speed at which this town NPC throws a projectile when it attacks. Multiplier is the speed of the projectile, gravityCorrection is how much extra the projectile gets thrown upwards, and randomOffset allows you to randomize the projectile's velocity in a square centered around the original velocity. This hook is only used when the town NPC has an attack type of 0 (throwing), 1 (shooting), or 2 (magic).
+		/// </summary>
+		/// <param name="multiplier"></param>
+		/// <param name="gravityCorrection"></param>
+		/// <param name="randomOffset"></param>
 		public virtual void TownNPCAttackProjSpeed(ref float multiplier, ref float gravityCorrection, ref float randomOffset)
 		{
 		}

+		/// <summary>
+		/// Allows you to tell the game that this town NPC has already created a projectile and will still create more projectiles as part of a single attack so that the game can animate the NPC's attack properly. Only used when the town NPC has an attack type of 1 (shooting).
+		/// </summary>
+		/// <param name="inBetweenShots"></param>
 		public virtual void TownNPCAttackShoot(ref bool inBetweenShots)
 		{
 		}

+		/// <summary>
+		/// Allows you to control the brightness of the light emitted by this town NPC's aura when it performs a magic attack. Only used when the town NPC has an attack type of 2 (magic)
+		/// </summary>
+		/// <param name="auraLightMultiplier"></param>
 		public virtual void TownNPCAttackMagic(ref float auraLightMultiplier)
 		{
 		}

+		/// <summary>
+		/// Allows you to determine the width and height of the item this town NPC swings when it attacks, which controls the range of this NPC's swung weapon. Only used when the town NPC has an attack type of 3 (swinging).
+		/// </summary>
+		/// <param name="itemWidth"></param>
+		/// <param name="itemHeight"></param>
 		public virtual void TownNPCAttackSwing(ref int itemWidth, ref int itemHeight)
 		{
 		}

+		/// <summary>
+		/// Allows you to customize how this town NPC's weapon is drawn when this NPC is shooting (this NPC must have an attack type of 1). Scale is a multiplier for the item's drawing size, item is the ID of the item to be drawn, and closeness is how close the item should be drawn to the NPC.
+		/// </summary>
+		/// <param name="scale"></param>
+		/// <param name="item"></param>
+		/// <param name="closeness"></param>
 		public virtual void DrawTownAttackGun(ref float scale, ref int item, ref int closeness)
 		{
 		}

+		/// <summary>
+		/// Allows you to customize how this town NPC's weapon is drawn when this NPC is swinging it (this NPC must have an attack type of 3). Item is the Texture2D instance of the item to be drawn (use Main.itemTexture[id of item]), itemSize is the width and height of the item's hitbox (the same values for TownNPCAttackSwing), scale is the multiplier for the item's drawing size, and offset is the offset from which to draw the item from its normal position.
+		/// </summary>
+		/// <param name="item"></param>
+		/// <param name="itemSize"></param>
+		/// <param name="scale"></param>
+		/// <param name="offset"></param>
 		public virtual void DrawTownAttackSwing(ref Texture2D item, ref int itemSize, ref float scale, ref Vector2 offset)
 		{
 		}


add Pagination to SearchControl.Reinitialize

--- Signum.Windows.Extensions/UserQueries/UserQueryClient.cs
+++ Signum.Windows.Extensions/UserQueries/UserQueryClient.cs
@@ -138,12 +138,12 @@
         internal static void ToSearchControl(UserQueryDN uq, SearchControl searchControl)
         {
             var filters = uq.WithoutFilters ? searchControl.FilterOptions.ToList() :
-                searchControl.FilterOptions.Where(f => f.Frozen).Concat(uq.Filters.Select(qf => new FilterOption
-            {
-                Path = qf.Token.Token.FullKey(),
-                Operation = qf.Operation,
-                Value = qf.Value
-            })).ToList();
+                 searchControl.FilterOptions.Where(f => f.Frozen).Concat(uq.Filters.Select(qf => new FilterOption
+             {
+                 Path = qf.Token.Token.FullKey(),
+                 Operation = qf.Operation,
+                 Value = qf.Value
+             })).ToList();

             var columns = uq.Columns.Select(qc => new ColumnOption
             {
@@ -157,9 +157,9 @@
                 OrderType = of.OrderType,
             }).ToList();

-            searchControl.Reinitialize(filters, columns, uq.ColumnsMode, orders);
-
-            searchControl.Pagination = uq.GetPagination() ?? Navigator.GetQuerySettings(searchControl.QueryName).Pagination ?? FindOptions.DefaultPagination;
+            var pagination = uq.GetPagination() ?? Navigator.GetQuerySettings(searchControl.QueryName).Pagination ?? FindOptions.DefaultPagination;
+
+            searchControl.Reinitialize(filters, columns, uq.ColumnsMode, orders, pagination);
         }

         internal static void ToCountSearchControl(UserQueryDN uq, CountSearchControl countSearchControl)


Fix columns for Solo.

Fix columns for Solo.

--- ConvertHelper/BemaniToSM.cs
+++ ConvertHelper/BemaniToSM.cs
@@ -119,8 +119,18 @@
                                         {
                                             foreach (Entry entry in chart.Entries)
                                             {
-                                                if (entry.Type == EntryType.Marker && entry.Column == 6)
-                                                    entry.Column = 5;
+                                                if (entry.Type == EntryType.Marker)
+												{
+													switch (entry.Column)
+													{
+														case 0: entry.Column = 1;
+														case 1: entry.Column = 2;
+														case 2: entry.Column = 3;
+														case 3: entry.Column = 4;
+														case 4: entry.Column = 0;
+														case 6: entry.Column = 5;
+													}
+												}
                                             }
                                         }


morning warm-up

morning warm-up

--- src/XInput.Wrapper/X.cs
+++ src/XInput.Wrapper/X.cs
@@ -1,6 +1,6 @@
 // XInput.Wrapper by Nikolai Voronin
 // http://github.com/nikvoronin/xinput.wrapper
-// Version 0.4 (June 30, 2017)
+// Version 0.4 (July 1, 2017)
 // Under the MIT License (MIT)
 //
 // Stick = Thumb
@@ -51,7 +51,7 @@

                 return isAvailable;
             }
-        }
+        } // IsAvailable

         public sealed class Gamepad
         {
@@ -76,7 +76,7 @@
                 GamepadBattery = new Battery(index, Battery.At.Gamepad);
                 HeadsetBattery = new Battery(index, Battery.At.Headset);

-                // UNDONE others buttons
+                // UNDONE other buttons
                 A = new Button(ButtonFlags.A);
                 // LATER Add supported buttons only. should based on capabilities
                 Buttons.Add(A);
@@ -84,7 +84,7 @@

             bool isConnected;
             public bool IsConnected { get { return isConnected; } }
-            public bool Enable { set { Native.XInputEnable(value); } }
+            public static bool Enable { set { Native.XInputEnable(value); } }

             /// <summary>
             /// Update gamepad data
@@ -183,7 +183,7 @@
                 EventHandler pceh = handler;

                 if (uiContext != null)
-                    uiContext.Post((o) => { pceh?.Invoke(this, EventArgs.Empty); }, null);
+                	uiContext.Post((o) => pceh?.Invoke(this, EventArgs.Empty), null);
                 else
                     pceh?.Invoke(this, EventArgs.Empty);
             }
@@ -206,7 +206,7 @@
                 EventHandler<KeyEventArgs> pceh = handler;

                 if (uiContext != null)
-                    uiContext.Post((o) => { pceh?.Invoke(this, new KeyEventArgs(buttons)); }, null);
+                    uiContext.Post((o) => pceh?.Invoke(this, new KeyEventArgs(buttons)), null);
                 else
                     pceh?.Invoke(this, new KeyEventArgs(buttons)); ;
             }
@@ -224,7 +224,7 @@
                 public readonly ButtonFlags Mask;
                 public readonly string Name = string.Empty;
                 public bool Supported { get; internal set; } // LATER fill in the flag based on the capabilities
-                public bool SendKeyDownEveryTick { get; set; };
+                public bool SendKeyDownEveryTick { get; set; }

                 public bool Pressed;

@@ -392,7 +392,7 @@

             public class Battery
             {
-                uint uindex;
+                readonly uint uindex;
                 Native.XINPUT_BATTERY_INFORMATION state;

                 public readonly At Location;
@@ -441,7 +441,7 @@
             } // class Battery
         } // class Gamepad

-        public class Native
+        public static class Native
         {
             [DllImport("xinput1_4.dll")]
             public static extern uint XInputGetState
@@ -497,7 +497,6 @@
                 ref uint pCaptureCount
             );

-            // ex StatePacket
             [StructLayout(LayoutKind.Explicit)]
             public struct XINPUT_STATE
             {
@@ -508,7 +507,6 @@
                 public XINPUT_GAMEPAD Gamepad;
             }

-            // ex PadState
             [StructLayout(LayoutKind.Explicit)]
             public struct XINPUT_GAMEPAD
             {
@@ -541,7 +539,6 @@
                 public short sThumbRY;
             }

-            // ex VibrationSpeed
             [StructLayout(LayoutKind.Sequential)]
             public struct XINPUT_VIBRATION
             {
@@ -552,7 +549,6 @@
                 public ushort wRightMotorSpeed;
             }

-            // ex DeviceCapability
             [StructLayout(LayoutKind.Explicit)]
             public struct XINPUT_CAPABILITIES
             {
@@ -575,7 +571,6 @@
                 public XINPUT_VIBRATION Vibration;
             }

-            // ex Battery.State
             [StructLayout(LayoutKind.Explicit)]
             public struct XINPUT_BATTERY_INFORMATION
             {
@@ -588,7 +583,6 @@
                 public byte BatteryLevel;
             }

-            // ex Keystroke
             [StructLayout(LayoutKind.Explicit)]
             public struct XINPUT_KEYSTROKE
             {
@@ -611,7 +605,7 @@
                 [MarshalAs(UnmanagedType.I1)]
                 [FieldOffset(6)]
                 public byte HidCode;
-            } // struct Keystroke
+            }

         } // class Native


polyfill module support for not

polyfill module support for not

--- IPTables.Net/Iptables/Modules/Polyfill/PolyfillModule.cs
+++ IPTables.Net/Iptables/Modules/Polyfill/PolyfillModule.cs
@@ -31,6 +31,10 @@
         {
             String current = parser.GetCurrentArg();
             _data.Add(current, new List<string>());
+            if (not)
+            {
+                _data[current].Add("!");
+            }
             for (int i = 1; i < parser.GetRemainingArgs(); i++)
             {
                 string arg = parser.GetNextArg(i);


Missed a test

Missed a test

--- src/UnitTests/NullBehavior.cs
+++ src/UnitTests/NullBehavior.cs
@@ -341,7 +341,6 @@
             cfg.CreateProfile("Foo", p =>
             {
                 p.AllowNullDestinationValues = true;
-                p.CreateMap<ModelObject, ModelDto>();
                 p.CreateMap<ModelSubObject, ModelSubDto>();
                 p.CreateMap<ModelObject, ModelDto>()
                     .ForMember(d => d.SubExpressionName, opt => opt.MapFrom(src =>


WxOpen，v2.0.11.2 修正 TemplateApi.Add() 方法返回类型，感谢 @davidmaster  #1288

--- src/Senparc.Weixin.WxOpen/Senparc.Weixin.WxOpen/AdvancedAPIs/Template/TemplateApi.cs
+++ src/Senparc.Weixin.WxOpen/Senparc.Weixin.WxOpen/AdvancedAPIs/Template/TemplateApi.cs
@@ -33,6 +33,9 @@
     修改标识：Senparc - 20170707
     修改描述：v1.7.4 完善模板消息发送参数

+    修改标识：Senparc - 20180712
+    修改描述：v2.0.11.2 修正 TemplateApi.Add() 方法返回类型
+
 ----------------------------------------------------------------*/

 /*
@@ -40,9 +43,9 @@
  */

 using System.Threading.Tasks;
+using Senparc.Weixin.CommonAPIs;
 using Senparc.Weixin.Entities;
 using Senparc.Weixin.MP;
-using Senparc.Weixin.MP.CommonAPIs;

 namespace Senparc.Weixin.WxOpen.AdvancedAPIs.Template
 {
@@ -164,7 +167,7 @@
         /// <param name="keywordIdList">开发者自行组合好的模板关键词列表，关键词顺序可以自由搭配（例如[3,5,4]或[4,5,3]），最多支持10个关键词组合</param>
         /// <param name="timeOut">请求超时时间</param>
         /// <returns></returns>
-        public static LibraryGetJsonResult Add(string accessToken, string id, int[] keywordIdList, int timeOut = Config.TIME_OUT)
+        public static AddJsonResult Add(string accessToken, string id, int[] keywordIdList, int timeOut = Config.TIME_OUT)
         {
             string urlFormat = Config.ApiMpHost + "/cgi-bin/wxopen/template/add?access_token={0}";
             var data = new
@@ -172,7 +175,7 @@
                 id = id,
                 keyword_id_list = keywordIdList
             };
-            return CommonJsonSend.Send<LibraryGetJsonResult>(accessToken, urlFormat, data, timeOut: timeOut);
+            return CommonJsonSend.Send<AddJsonResult>(accessToken, urlFormat, data, timeOut: timeOut);
         }

         #endregion
@@ -306,7 +309,7 @@
         /// <param name="keywordIdList">开发者自行组合好的模板关键词列表，关键词顺序可以自由搭配（例如[3,5,4]或[4,5,3]），最多支持10个关键词组合</param>
         /// <param name="timeOut">请求超时时间</param>
         /// <returns></returns>
-        public static async Task<LibraryGetJsonResult> AddAsync(string accessToken, string id, int[] keywordIdList, int timeOut = Config.TIME_OUT)
+        public static async Task<AddJsonResult> AddAsync(string accessToken, string id, int[] keywordIdList, int timeOut = Config.TIME_OUT)
         {
             string urlFormat = Config.ApiMpHost + "/cgi-bin/wxopen/template/add?access_token={0}";
             var data = new
@@ -314,7 +317,7 @@
                 id = id,
                 keyword_id_list = keywordIdList
             };
-            return await CommonJsonSend.SendAsync<LibraryGetJsonResult>(accessToken, urlFormat, data, timeOut: timeOut);
+            return await CommonJsonSend.SendAsync<AddJsonResult>(accessToken, urlFormat, data, timeOut: timeOut);
         }

         #endregion


Revert "Revert "Merge pull request #59 from hAhmadz/AE-1868""

This reverts commit 53e72ef2f71ee9978d860b4d5f3b2c17136b742f.

--- src/Venue/Bookings/Package.cs
+++ src/Venue/Bookings/Package.cs
@@ -43,5 +43,17 @@
         {
             get; set;
         }
+
+        [JsonProperty("packageId")]
+        public int PackageId
+        {
+            get; set;
+        }
+
+        [JsonProperty("packageName")]
+        public string PackageName
+        {
+            get; set;
+        }
     }
 }


beo numbers are strings, not integers

beo numbers are strings, not integers

--- src/Venue/Bookings/Session.cs
+++ src/Venue/Bookings/Session.cs
@@ -35,7 +35,7 @@
         }

         [JsonProperty("beoNumbers")]
-        public int[] BeoNumbers
+        public string[] BeoNumbers
         {
             get; set;
         }


Fix additional nullable warning

Fix additional nullable warning

--- src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs
+++ src/VisualStudio/Core/Def/Implementation/CodeLens/RemoteCodeLensReferencesService.cs
@@ -169,6 +169,11 @@
                 }

                 var excerpter = document.Services.GetService<IDocumentExcerptService>();
+                if (excerpter == null)
+                {
+                    continue;
+                }
+
                 var referenceExcerpt = await excerpter.TryExcerptAsync(document, span, ExcerptMode.SingleLine, cancellationToken).ConfigureAwait(false);
                 var tooltipExcerpt = await excerpter.TryExcerptAsync(document, span, ExcerptMode.Tooltip, cancellationToken).ConfigureAwait(false);


Support for parsing inlines with linebreaks

--- src/XText.Tests/ParseTests.cs
+++ src/XText.Tests/ParseTests.cs
@@ -26,6 +26,13 @@
         }

         [Fact]
+        public void NewLine()
+        {
+            Verify(new XSpan("Multiline", new XLineBreak(), "Text"));
+            Verify(new XSpan(new XLineBreak(), "Start with newline"));
+        }
+
+        [Fact]
         public void SimpleSpans()
         {
             Verify(new XSpan(new XBold("Bold"), "First"));
@@ -48,7 +55,14 @@
         public static XTextElement Parse(string str)
         {
             var inlines = new List<XInline>();
+            str = str.Replace("\r\n", "\n");

+            return ParseInlineString(str);
+        }
+
+        private static XInline ParseInlineString(string str)
+        {
+            var inlines = new List<XInline>();
             bool formattedInlineMatch;
             do
             {
@@ -57,17 +71,34 @@
                 var italicDelimiter = "*";
                 var boldStart = str.IndexOf(boldDelimiter, StringComparison.Ordinal);
                 var italicStart = str.IndexOf(italicDelimiter, StringComparison.Ordinal);
-
-                if (boldStart != -1 && boldStart <= italicStart)
+                var newLineStart = str.IndexOf("\n", StringComparison.Ordinal);
+
+                if (newLineStart != -1 && (newLineStart < boldStart || boldStart == -1) &&
+                    (newLineStart < italicStart || italicStart == -1))
+                {
+                    if (newLineStart == 0)
+                    {
+                        inlines.Add(new XLineBreak());
+                        str = str.Substring(1);
+                    }
+                    else
+                    {
+                        inlines.Add(new XRun(str.Substring(0, newLineStart)));
+                        str = str.Substring(newLineStart + 1);
+                        inlines.Add(new XLineBreak());
+                    }
+                }
+                else if (boldStart != -1 && boldStart <= italicStart)
                 {
                     str = ParseNextInline(str, boldDelimiter, boldStart, inlines, s => new XBold(s));
                     formattedInlineMatch = true;
-                } else if (italicStart != -1)
+                }
+                else if (italicStart != -1)
                 {
                     str = ParseNextInline(str, italicDelimiter, italicStart, inlines, s => new XItalic(s));
                     formattedInlineMatch = true;
                 }
-            } while (formattedInlineMatch);
+            } while (formattedInlineMatch);

             if (!string.IsNullOrEmpty(str))
                 inlines.Add(new XRun(str));


Fix for multiple enumeration

Fix for multiple enumeration

--- src/Potato.Core/Events/PushEventsController.cs
+++ src/Potato.Core/Events/PushEventsController.cs
@@ -260,7 +260,7 @@

                 ICommandResult uris = this.Shared.Variables.Tunnel(CommandBuilder.VariablesGet(CommonVariableNames.EventsPushConfigGroups).SetOrigin(CommandOrigin.Local));

-                var content = uris.Now.Variables != null ? uris.Now.Variables.SelectMany(variable => variable.ToList<String>()) : new List<String>();
+                var content = uris.Now.Variables != null ? uris.Now.Variables.SelectMany(variable => variable.ToList<String>()).ToList() : new List<String>();

                 // If the name has not been registered already..
                 if (uris.Success == true && content.Contains(name) == false) {


fix last edit

fix last edit

--- LCIAToolAPI/LCIAToolAPI.Tests/ResourceControllerTests.cs
+++ LCIAToolAPI/LCIAToolAPI.Tests/ResourceControllerTests.cs
@@ -13,7 +13,6 @@
 using System.Threading.Tasks;
 using System.Web.Http;
 using System.Web.Http.Results;
-using System.Net.Http;

 namespace LCIAToolAPI.Tests
 {


Changed Quality from string to int

Changed Quality from string to int

--- SteamTrade/Inventory.cs
+++ SteamTrade/Inventory.cs
@@ -95,7 +95,7 @@
             public byte Level { get; set; }

             [JsonProperty("quality")]
-            public string Quality { get; set; }
+            public int Quality { get; set; }

             [JsonProperty("quantity")]
             public int RemainingUses { get; set; }


Refactor: Reformat

Refactor: Reformat

--- src/ReniLanguagePackage/ReniLanguagePackage.cs
+++ src/ReniLanguagePackage/ReniLanguagePackage.cs
@@ -1,7 +1,5 @@
 using System;
-using System.Collections.Generic;
 using System.ComponentModel.Design;
-using System.Linq;
 using System.Runtime.InteropServices;
 using System.Windows.Forms;
 using JetBrains.Annotations;
@@ -41,8 +39,7 @@
             {
                 MaxLineLength = pd.MaxLineLength,
                 EmptyLineLimit = pd.EmptyLineLimit
-            }.
-            Create();
+            }.Create();
         }
     }
 }


Fix IsNewSession variable

Fix IsNewSession variable

--- src/SFA.DAS.EmployerFinance.Web/Global.asax.cs
+++ src/SFA.DAS.EmployerFinance.Web/Global.asax.cs
@@ -121,5 +121,12 @@
             Logger.Error(exception, message, properties);
             telemetryClient.TrackException(exception);
         }
+
+        protected void Session_Start(Object sender, EventArgs e)
+        {
+            // placeholder handler is require to ensure HttpContext.Session.IsNewSession will
+            // return false correctly when a new session has already been started but nothing
+            // has been written to the session yet
+        }
     }
 }


[io] fix error spam with sounds

[io] fix error spam with sounds

--- DataTool/SaveLogic/Combo.cs
+++ DataTool/SaveLogic/Combo.cs
@@ -832,10 +832,14 @@
         {
             string outputFile = Path.Combine(directory, $"{name ?? soundFileInfo.GetName()}.ogg");
             CreateDirectoryFromFile(outputFile);
-            using (Stream outputStream = File.OpenWrite(outputFile))
-            {
-                outputStream.SetLength(0);
-                ConvertSoundFile(stream, outputStream);
+            try {
+                using (Stream outputStream = File.OpenWrite(outputFile)) {
+                    outputStream.SetLength(0);
+                    ConvertSoundFile(stream, outputStream);
+                }
+            } catch (IOException e) {
+                if (File.Exists(outputFile)) return;
+                throw;
             }
         }


Renamed string length constant in NextStringShouldReturnStringWithTheSameNumberOfCharactersAsMethodParameterWhenAsciiEncodingIsUsed test method to better represent its value.

--- MissingFeatures.Tests/RandomGenerator/NextStringTests.cs
+++ MissingFeatures.Tests/RandomGenerator/NextStringTests.cs
@@ -21,10 +21,10 @@
         [TestMethod]
         public void NextStringShouldReturnStringWithTheSameNumberOfCharactersAsMethodParameterWhenAsciiEncodingIsUsed()
         {
-            const int MaxNumberOfCharacters = 7;
+            const int NumberOfCharacters = 7;
             var randomGenerator = new RandomGenerator();
-            var result = randomGenerator.NextString(MaxNumberOfCharacters, Encoding.ASCII);
-            Assert.AreEqual(MaxNumberOfCharacters, result.Length);
+            var result = randomGenerator.NextString(NumberOfCharacters, Encoding.ASCII);
+            Assert.AreEqual(NumberOfCharacters, result.Length);
         }
     }
 }


デモプロジェクトを更新

デモプロジェクトを更新

--- Demo/DemoNotify.Designer.cs
+++ Demo/DemoNotify.Designer.cs
@@ -68,7 +68,7 @@
             this.TitleTextBox.Location = new System.Drawing.Point(80, 66);
             this.TitleTextBox.Name = "TitleTextBox";
             this.TitleTextBox.Size = new System.Drawing.Size(358, 23);
-            this.TitleTextBox.TabIndex = 0;
+            this.TitleTextBox.TabIndex = 1;
             this.TitleTextBox.Text = "NotifyForm Demo";
             //
             // ImageTextBox
@@ -76,7 +76,7 @@
             this.ImageTextBox.Location = new System.Drawing.Point(80, 95);
             this.ImageTextBox.Name = "ImageTextBox";
             this.ImageTextBox.Size = new System.Drawing.Size(302, 23);
-            this.ImageTextBox.TabIndex = 1;
+            this.ImageTextBox.TabIndex = 2;
             //
             // ImageBrowseButton
             //
@@ -108,7 +108,7 @@
             this.ImageBrowseButton.Surface.BorderSize = 1;
             this.ImageBrowseButton.Surface.Image = null;
             this.ImageBrowseButton.Surface.TextColor = System.Drawing.Color.Black;
-            this.ImageBrowseButton.TabIndex = 2;
+            this.ImageBrowseButton.TabIndex = 3;
             this.ImageBrowseButton.Text = "...";
             this.ImageBrowseButton.UseVisualStyleBackColor = false;
             this.ImageBrowseButton.Click += new System.EventHandler(this.ImageBrowseButton_Click);
@@ -132,7 +132,7 @@
             0});
             this.DisplayMilliseconds.Name = "DisplayMilliseconds";
             this.DisplayMilliseconds.Size = new System.Drawing.Size(358, 23);
-            this.DisplayMilliseconds.TabIndex = 3;
+            this.DisplayMilliseconds.TabIndex = 4;
             this.DisplayMilliseconds.ThousandsSeparator = true;
             this.DisplayMilliseconds.Value = new decimal(new int[] {
             10000,
@@ -150,7 +150,7 @@
             0});
             this.DelayMilliseconds.Name = "DelayMilliseconds";
             this.DelayMilliseconds.Size = new System.Drawing.Size(358, 23);
-            this.DelayMilliseconds.TabIndex = 4;
+            this.DelayMilliseconds.TabIndex = 5;
             this.DelayMilliseconds.ThousandsSeparator = true;
             this.DelayMilliseconds.Value = new decimal(new int[] {
             1000,
@@ -219,7 +219,7 @@
             this.LevelLabel.Location = new System.Drawing.Point(12, 40);
             this.LevelLabel.Name = "LevelLabel";
             this.LevelLabel.Size = new System.Drawing.Size(43, 15);
-            this.LevelLabel.TabIndex = 1000;
+            this.LevelLabel.TabIndex = 999;
             this.LevelLabel.Text = "重要度";
             //
             // LevelComboBox
@@ -229,7 +229,7 @@
             this.LevelComboBox.Location = new System.Drawing.Point(80, 37);
             this.LevelComboBox.Name = "LevelComboBox";
             this.LevelComboBox.Size = new System.Drawing.Size(358, 23);
-            this.LevelComboBox.TabIndex = 1001;
+            this.LevelComboBox.TabIndex = 0;
             //
             // DemoNotify
             //


hide/show functions (where should these go?? should we send event??)

--- util/SceneUtil.cs
+++ util/SceneUtil.cs
@@ -242,6 +242,18 @@

+        // not sure where these should go...
+        public static void Show(SceneObject so)
+        {
+            so.RootGameObject.Show();
+        }
+        public static void Hide(SceneObject so)
+        {
+            so.RootGameObject.Hide();
+        }
+
+
+
         public static void DestroySO(SceneObject so)
         {
             so.RootGameObject.transform.parent = null;


properly save and load tumbler parameters

--- NTumbleBit/ClassicTumbler/Client/TumblerClientRuntime.cs
+++ NTumbleBit/ClassicTumbler/Client/TumblerClientRuntime.cs
@@ -100,7 +100,7 @@
 			else
 				throw new ConfigException("Missing configuration for outputwallet");

-			TumblerParameters = dbreeze.Get<ClassicTumbler.ClassicTumblerParameters>("Configuration", configuration.ToString());
+			TumblerParameters = dbreeze.Get<ClassicTumbler.ClassicTumblerParameters>("Configuration", TumblerServer.ToString());

 			if(TumblerParameters != null && TumblerParameters.GetHash() != configuration.TumblerServer.ConfigurationHash)
 				TumblerParameters = null;


Windows: Added missing namespace

Windows: Added missing namespace

--- source/FFImageLoading.Windows/TaskParameterExtensions.cs
+++ source/FFImageLoading.Windows/TaskParameterExtensions.cs
@@ -6,6 +6,7 @@
 using Windows.UI.Xaml.Media.Imaging;
 using Windows.UI.Xaml.Media.Animation;
 using Windows.UI.Xaml;
+using FFImageLoading.Cache;

 namespace FFImageLoading
 {


Improve some logging in LandConnector

Improve some logging in LandConnector

This also eliminate the last 'OGS1' logging message (in this case, an erroneous one)

--- OpenSim/Services/Connectors/Land/LandServiceConnector.cs
+++ OpenSim/Services/Connectors/Land/LandServiceConnector.cs
@@ -88,7 +88,7 @@
                     XmlRpcResponse response = request.Send(uri, 10000);
                     if (response.IsFault)
                     {
-                        m_log.ErrorFormat("[LAND CONNECTOR] remote call returned an error: {0}", response.FaultString);
+                        m_log.ErrorFormat("[LAND CONNECTOR]: remote call returned an error: {0}", response.FaultString);
                     }
                     else
                     {
@@ -110,22 +110,26 @@
                             landData.UserLocation = Vector3.Parse((string)hash["UserLocation"]);
                             if (hash["RegionAccess"] != null)
                                 regionAccess = (byte)Convert.ToInt32((string)hash["RegionAccess"]);
-                            m_log.DebugFormat("[OGS1 GRID SERVICES] Got land data for parcel {0}", landData.Name);
+                            m_log.DebugFormat("[LAND CONNECTOR]: Got land data for parcel {0}", landData.Name);
                         }
                         catch (Exception e)
                         {
-                            m_log.Error("[LAND CONNECTOR] Got exception while parsing land-data:", e);
+                            m_log.ErrorFormat(
+                                "[LAND CONNECTOR]: Got exception while parsing land-data: {0} {1}",
+                                e.Message, e.StackTrace);
                         }
                     }
                 }
-                else m_log.WarnFormat("[LAND CONNECTOR] Couldn't find region with handle {0}", regionHandle);
+                else
+                    m_log.WarnFormat("[LAND CONNECTOR]: Couldn't find region with handle {0}", regionHandle);
             }
             catch (Exception e)
             {
-                m_log.ErrorFormat("[LAND CONNECTOR] Couldn't contact region {0}: {1}", regionHandle, e);
+                m_log.ErrorFormat(
+                    "[LAND CONNECTOR]: Couldn't contact region {0}: {1} {2}", regionHandle, e.Message, e.StackTrace);
             }

             return landData;
         }
     }
-}
+}


Added tests for E&M ToSpouses()

Added tests for E&M ToSpouses()

--- PART/Source/CprBroker/E_M.Tests/CitizenRelationsTests.cs
+++ PART/Source/CprBroker/E_M.Tests/CitizenRelationsTests.cs
@@ -185,30 +185,6 @@
         DateTime[] TestDates = new DateTime[] { new DateTime(2010, 5, 5), new DateTime(2011, 7, 7) };
         DateTime[] TestDatesWithNull = new DateTime[] { new DateTime(), new DateTime(2010, 5, 5), new DateTime(2011, 7, 7) };

-        [Test]
-        [Combinatorial]
-        public void TestToSpouses(
-            [ValueSource("RandomCprNumbers1")] decimal cprNumber,
-            [Values('G', 'F', 'E')]char maritalStatus,
-            [ValueSource("TestDates")] DateTime maritalStatusDate,
-            [Values(' ')]char maritalStatusDateUncertainty,
-            [ValueSource("TestDates")]DateTime maritalStatusEndDate,
-            [Values(' ')]char maritalStatusEndDateUncertainty)
-        {
-            var citizen = new Citizen()
-            {
-                SpousePNR = cprNumber,
-                MaritalStatus = maritalStatus,
-                MaritalStatusTimestamp = maritalStatusDate,
-                MaritalStatusTimestampUncertainty = maritalStatusDateUncertainty,
-                MaritalStatusTerminationTimestamp = maritalStatusEndDate,
-                MaritalStatusTerminationTimestampUncertainty = maritalStatusEndDateUncertainty
-            };
-
-            var result = Citizen.ToSpouses(citizen, ToUuid);
-            AssertCprNumbers(new decimal[] { cprNumber }, result);
-        }
-
         #region ToMaritalStatusDate
         [Test]
         [ExpectedException(typeof(ArgumentNullException))]
@@ -293,6 +269,170 @@
         }
         #endregion

+        #region ToSpouses
+
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void ToSpouses_Null_ThrowsException()
+        {
+            Citizen.ToSpouses(null, ToUuid);
+        }
+
+        [Test]
+        [ExpectedException(typeof(ArgumentNullException))]
+        public void ToSpouses_NullConverter_ThrowsException()
+        {
+            var citizen = new Citizen() { SpousePNR = Utilities.RandomCprNumber() };
+            Citizen.ToSpouses(citizen, null);
+        }
+
+        [Test]
+        [ExpectedException(typeof(ArgumentException))]
+        public void ToToSpouses_InvalidMaritalStatus_ThrowsException(
+            [Values(' ', 'W')] char maritalStatus)
+        {
+            var citizen = new Citizen() { SpousePNR = Utilities.RandomCprNumber(), MaritalStatus = maritalStatus };
+            Citizen.ToSpouses(citizen, ToUuid);
+        }
+
+        [Test]
+        public void ToSpouses_IrrelevantMaritalStatus_ZeroElements(
+            [Values('U', 'P', 'O', 'L')] char maritalStatus)
+        {
+            var citizen = new Citizen() { SpousePNR = Utilities.RandomCprNumber(), MaritalStatus = maritalStatus };
+            var result = Citizen.ToSpouses(citizen, ToUuid);
+            Assert.AreEqual(0, result.Length);
+        }
+
+        [Test]
+        [ExpectedException(typeof(ArgumentException))]
+        public void ToSpouses_InconsistentDates_ThrowsException(
+            [Values('E', 'F')]char maritalStatus)
+        {
+            var citizen = new Citizen()
+            {
+                SpousePNR = Utilities.RandomCprNumber(),
+                MaritalStatus = maritalStatus,
+                MaritalStatusTimestamp = DateTime.Today,
+                MaritalStatusTimestampUncertainty = DateCertaintyTrue,
+                MaritalStatusTerminationTimestamp = DateTime.Today.AddDays(-2),
+                MaritalStatusTerminationTimestampUncertainty = DateCertaintyTrue
+            };
+            Citizen.ToSpouses(citizen, ToUuid);
+        }
+
+        [Test]
+        [Ignore]
+        [ExpectedException(typeof(ArgumentException))]
+        public void ToSpouses_TerminationDateForUnterminatedPartnership_ThrowsException()
+        {
+            var citizen = new Citizen()
+            {
+                SpousePNR = Utilities.RandomCprNumber(),
+                MaritalStatus = 'G',
+                MaritalStatusTerminationTimestamp = DateTime.Today.AddDays(-2),
+                MaritalStatusTerminationTimestampUncertainty = DateCertaintyTrue
+            };
+            Citizen.ToSpouses(citizen, ToUuid);
+        }
+
+        [Test]
+        [ExpectedException(typeof(ArgumentException))]
+        public void ToSpouses_InvalidSpousePnr_Exception(
+            [Values('G', 'F', 'E')]char maritalStatus,
+            [ValueSource("InvalidCprNumbers")]decimal spousePnr)
+        {
+            var citizen = new Citizen() { MaritalStatus = maritalStatus, SpousePNR = spousePnr };
+            Citizen.ToSpouses(citizen, ToUuid);
+        }
+
+        [Test]
+        [ExpectedException(typeof(ArgumentException))]
+        public void ToSpouses_ZeroSpousePnr_Exception(
+            [Values('G', 'F', 'E')]char maritalStatus)
+        {
+            var citizen = new Citizen() { MaritalStatus = maritalStatus, SpousePNR = 0 };
+            Citizen.ToSpouses(citizen, ToUuid);
+        }
+
+        [Test]
+        public void ToSpouses_Valid_Has1Element(
+            [ValueSource("TestDates")] DateTime maritalStatusDate,
+            [Values('G', 'F', 'E')] char maritalStatus)
+        {
+            var citizen = new Citizen()
+            {
+                SpousePNR = Utilities.RandomCprNumber(),
+                MaritalStatus = maritalStatus,
+                MaritalStatusTimestamp = maritalStatusDate,
+                MaritalStatusTimestampUncertainty = DateCertaintyTrue
+            };
+
+            var result = Citizen.ToSpouses(citizen, ToUuid);
+            Assert.AreEqual(1, result.Length);
+        }
+
+        [Test]
+        public void ToSpouses_Valid_CorrectUuidReturned(
+            [ValueSource("TestDates")] DateTime maritalStatusDate,
+            [Values('G', 'F', 'E')] char maritalStatus)
+        {
+            var citizen = new Citizen()
+            {
+                SpousePNR = Utilities.RandomCprNumber(),
+                MaritalStatus = maritalStatus,
+                MaritalStatusTimestamp = maritalStatusDate,
+                MaritalStatusTimestampUncertainty = DateCertaintyTrue
+            };
+
+            var result = Citizen.ToSpouses(citizen, ToUuid);
+            var stringCprNumber = Converters.ToCprNumber(citizen.SpousePNR);
+            Assert.AreEqual(uuids[stringCprNumber].ToString(), result[0].ReferenceID.Item);
+        }
+
+        [Test]
+        [Combinatorial]
+        public void ToSpouses_Unterminated_NullEndDate(
+            [ValueSource("TestDates")] DateTime maritalStatusDate,
+            [Values('G')] char maritalStatus)
+        {
+            var citizen = new Citizen()
+            {
+                SpousePNR = Utilities.RandomCprNumber(),
+                MaritalStatus = maritalStatus,
+                MaritalStatusTimestamp = maritalStatusDate,
+                MaritalStatusTimestampUncertainty = DateCertaintyTrue,
+                MaritalStatusTerminationTimestamp = maritalStatusDate.AddDays(1),
+                MaritalStatusTerminationTimestampUncertainty = DateCertaintyTrue
+            };
+
+            var result = Citizen.ToSpouses(citizen, ToUuid);
+            var stringCprNumber = Converters.ToCprNumber(citizen.SpousePNR);
+            Assert.Null(result[0].Virkning.TilTidspunkt.ToDateTime());
+        }
+
+        [Test]
+        [Combinatorial]
+        public void ToSpouses_Terminated_HasEndDate(
+            [ValueSource("TestDates")] DateTime maritalStatusDate,
+            [Values('F', 'E')] char maritalStatus)
+        {
+            var citizen = new Citizen()
+            {
+                SpousePNR = Utilities.RandomCprNumber(),
+                MaritalStatus = maritalStatus,
+                MaritalStatusTimestamp = maritalStatusDate,
+                MaritalStatusTimestampUncertainty = DateCertaintyTrue,
+                MaritalStatusTerminationTimestamp = maritalStatusDate.AddDays(1),
+                MaritalStatusTerminationTimestampUncertainty = DateCertaintyTrue
+            };
+
+            var result = Citizen.ToSpouses(citizen, ToUuid);
+            var stringCprNumber = Converters.ToCprNumber(citizen.SpousePNR);
+            Assert.NotNull(result[0].Virkning.TilTidspunkt.ToDateTime());
+        }
+        #endregion
+
         #region ToRegisteredPartners

         [Test]
@@ -317,15 +457,6 @@
         {
             var citizen = new Citizen() { SpousePNR = Utilities.RandomCprNumber(), MaritalStatus = maritalStatus };
             Citizen.ToRegisteredPartners(citizen, ToUuid);
-        }
-
-        [Test]
-        public void ToRegisteredPartners_IrrelevantMaritalStatus_NotNull(
-            [Values('E', 'F', 'G', 'U')] char maritalStatus)
-        {
-            var citizen = new Citizen() { SpousePNR = Utilities.RandomCprNumber(), MaritalStatus = maritalStatus };
-            var result = Citizen.ToRegisteredPartners(citizen, ToUuid);
-            Assert.NotNull(result);
         }

         [Test]


Added editor support for point tool

Added editor support for point tool

--- Test2d/Editor/Editor.cs
+++ Test2d/Editor/Editor.cs
@@ -316,6 +316,10 @@
                     {
                         yield return point;
                     }
+                    foreach (var point in group.Connectors)
+                    {
+                        yield return point;
+                    }
                 }
             }
         }
@@ -572,6 +576,11 @@
                         SelectionLeftDown(x, y);
                     }
                     break;
+                case Tool.Point:
+                    {
+                        PointLeftDown(sx, sy);
+                    }
+                    break;
                 case Tool.Line:
                     {
                         LineLeftDown(sx, sy);
@@ -622,6 +631,8 @@
                     {
                         SelectionLeftUp(x, y);
                     }
+                    break;
+                case Tool.Point:
                     break;
                 case Tool.Line:
                     break;
@@ -656,6 +667,11 @@
                     break;
                 case Tool.Selection:
                     break;
+                case Tool.Point:
+                    {
+                        PointRightDown(sx, sy);
+                    }
+                    break;
                 case Tool.Line:
                     {
                         LineRightDown(sx, sy);
@@ -704,6 +720,8 @@
                     break;
                 case Tool.Selection:
                     break;
+                case Tool.Point:
+                    break;
                 case Tool.Line:
                     break;
                 case Tool.Rectangle:
@@ -732,6 +750,11 @@
                 case Tool.Selection:
                     {
                         SelectionMove(x, y);
+                    }
+                    break;
+                case Tool.Point:
+                    {
+                        PointMove(sx, sy);
                     }
                     break;
                 case Tool.Line:
@@ -852,6 +875,23 @@
             }
         }

+        private void PointLeftDown(double sx, double sy)
+        {
+            switch (CurrentState)
+            {
+                case State.None:
+                    {
+                        //var ellipse = XEllipse.Create(-3, -3, 3, 3, _container.CurrentStyle, null, true, "");
+                        //var point = XPoint.Create(sx, sy, ellipse);
+                        //_container.CurrentLayer.Shapes.Add(point);
+                        _shape = XPoint.Create(sx, sy, _container.PointShape);
+                        _container.CurrentLayer.Shapes.Add(_shape);
+                        _container.Invalidate();
+                    }
+                    break;
+            }
+        }
+
         private void LineLeftDown(double sx, double sy)
         {
             switch (CurrentState)
@@ -1176,7 +1216,16 @@
                     break;
             }
         }
-
+
+        private void PointRightDown(double sx, double sy)
+        {
+            switch (CurrentState)
+            {
+                case State.None:
+                    break;
+            }
+        }
+
         private void LineRightDown(double sx, double sy)
         {
             switch (CurrentState)
@@ -1312,6 +1361,15 @@
                         rectangle.BottomRight.Y = sy;
                         _container.WorkingLayer.Invalidate();
                     }
+                    break;
+            }
+        }
+
+        private void PointMove(double sx, double sy)
+        {
+            switch (CurrentState)
+            {
+                case State.None:
                     break;
             }
         }
@@ -1506,36 +1564,43 @@
             }
         }

-        public void GroupSelected()
-        {
-            if (_renderer.SelectedShapes != null)
-            {
-                var group = XGroup.Create("g");
-                var layer = Container.CurrentLayer;
-
-                foreach (var shape in _renderer.SelectedShapes)
+        public void Group(IEnumerable<BaseShape> shapes, Layer layer, string name)
+        {
+            var group = XGroup.Create(name);
+
+            foreach (var shape in shapes)
+            {
+                if (shape is XPoint)
+                {
+                    group.Connectors.Add(shape as XPoint);
+                }
+                else
                 {
                     group.Shapes.Add(shape);
-                    layer.Shapes.Remove(shape);
-                }
-
-                layer.Shapes.Add(group);
-                layer.Invalidate();
-            }
-        }
-
-        public void GroupCurrentLayer()
-        {
-            var group = XGroup.Create("g");
-            var layer = Container.CurrentLayer;
-            foreach (var shape in layer.Shapes.ToList())
-            {
-                group.Shapes.Add(shape);
+                }
+
                 layer.Shapes.Remove(shape);
             }

             layer.Shapes.Add(group);
             layer.Invalidate();
+        }
+
+        public void GroupSelected()
+        {
+            if (_renderer.SelectedShapes != null)
+            {
+                Group(_renderer.SelectedShapes, Container.CurrentLayer, "g");
+            }
+        }
+
+        public void GroupCurrentLayer()
+        {
+            var layer = Container.CurrentLayer;
+            if (layer.Shapes.Count > 0)
+            {
+                Group(layer.Shapes.ToList(), layer, "g");
+            }
         }

         public void MoveSelection(double sx, double sy)