From 45178e745b9fa69d057362274434e6efbfb1b2c5 Mon Sep 17 00:00:00 2001 From: Regalis Date: Tue, 29 Sep 2015 18:03:38 +0300 Subject: [PATCH] misc optimization & refactoring --- Backup/ChainingPropertyDescriptor.cs | 108 +++++++++ Backup/HyperPropertyDescriptor.csproj | 50 ++++ Backup/HyperPropertyDescriptor.csproj.user | 5 + Backup/HyperTypeDescriptionProvider.cs | 74 ++++++ Backup/HyperTypeDescriptor.cs | 218 ++++++++++++++++++ Backup/Properties/AssemblyInfo.cs | 38 +++ .../BackgroundSprite/BackgroundSprite.cs | 20 +- .../BackgroundSpriteManager.cs | 24 +- Subsurface/Source/GUI/GUI.cs | 14 +- Subsurface/Source/Game1.cs | 2 + .../Source/Items/Components/Container.cs | 2 +- .../Source/Items/Components/ItemComponent.cs | 11 +- .../Source/Items/Components/Projectile.cs | 2 +- Subsurface/Source/Items/Inventory.cs | 2 +- Subsurface/Source/Items/Item.cs | 77 +++---- Subsurface/Source/Items/ItemPrefab.cs | 2 +- Subsurface/Source/Map/Gap.cs | 2 +- Subsurface/Source/Map/Hull.cs | 3 +- Subsurface/Source/Map/Lights/ConvexHull.cs | 217 ++++++++--------- Subsurface/Source/Map/MapEntity.cs | 38 ++- Subsurface/Source/Map/Structure.cs | 13 +- Subsurface/Source/Map/Submarine.cs | 6 +- Subsurface/Source/Map/WayPoint.cs | 2 +- Subsurface/Source/Networking/GameServer.cs | 7 +- Subsurface/Source/Physics/PhysicsBody.cs | 9 +- Subsurface/Source/Screens/GameScreen.cs | 70 ++++-- Subsurface/Source/Sprite.cs | 6 +- Subsurface_Solution.sln | 8 + Subsurface_Solution.v12.suo | Bin 715776 -> 746496 bytes UpgradeLog.htm | Bin 0 -> 36916 bytes 30 files changed, 820 insertions(+), 210 deletions(-) create mode 100644 Backup/ChainingPropertyDescriptor.cs create mode 100644 Backup/HyperPropertyDescriptor.csproj create mode 100644 Backup/HyperPropertyDescriptor.csproj.user create mode 100644 Backup/HyperTypeDescriptionProvider.cs create mode 100644 Backup/HyperTypeDescriptor.cs create mode 100644 Backup/Properties/AssemblyInfo.cs create mode 100644 UpgradeLog.htm diff --git a/Backup/ChainingPropertyDescriptor.cs b/Backup/ChainingPropertyDescriptor.cs new file mode 100644 index 000000000..997cfb27c --- /dev/null +++ b/Backup/ChainingPropertyDescriptor.cs @@ -0,0 +1,108 @@ +using System; +using System.ComponentModel; + +namespace Hyper.ComponentModel { + public abstract class ChainingPropertyDescriptor : PropertyDescriptor { + private readonly PropertyDescriptor _root; + protected PropertyDescriptor Root { get { return _root; } } + protected ChainingPropertyDescriptor(PropertyDescriptor root) + : base(root) { + _root = root; + } + public override void AddValueChanged(object component, EventHandler handler) { + Root.AddValueChanged(component, handler); + } + public override AttributeCollection Attributes { + get { + return Root.Attributes; + } + } + public override bool CanResetValue(object component) { + return Root.CanResetValue(component); + } + public override string Category { + get { + return Root.Category; + } + } + public override Type ComponentType { + get { return Root.ComponentType; } + } + public override TypeConverter Converter { + get { + return Root.Converter; + } + } + public override string Description { + get { + return Root.Description; + } + } + public override bool DesignTimeOnly { + get { + return Root.DesignTimeOnly; + } + } + public override string DisplayName { + get { + return Root.DisplayName; + } + } + public override bool Equals(object obj) { + return Root.Equals(obj); + } + public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) { + return Root.GetChildProperties(instance, filter); + } + public override object GetEditor(Type editorBaseType) { + return Root.GetEditor(editorBaseType); + } + public override int GetHashCode() { + return Root.GetHashCode(); + } + public override object GetValue(object component) { + return Root.GetValue(component); + } + public override bool IsBrowsable { + get { + return Root.IsBrowsable; + } + } + public override bool IsLocalizable { + get { + return Root.IsLocalizable; + } + } + public override bool IsReadOnly { + get { return Root.IsReadOnly; } + } + public override string Name { + get { + return Root.Name; + } + } + public override Type PropertyType { + get { return Root.PropertyType; } + } + public override void RemoveValueChanged(object component, EventHandler handler) { + Root.RemoveValueChanged(component, handler); + } + public override void ResetValue(object component) { + Root.ResetValue(component); + } + public override void SetValue(object component, object value) { + Root.SetValue(component, value); + } + public override bool ShouldSerializeValue(object component) { + return Root.ShouldSerializeValue(component); + } + public override bool SupportsChangeEvents { + get { + return Root.SupportsChangeEvents; + } + } + public override string ToString() { + return Root.ToString(); + } + } +} diff --git a/Backup/HyperPropertyDescriptor.csproj b/Backup/HyperPropertyDescriptor.csproj new file mode 100644 index 000000000..cf60a1d62 --- /dev/null +++ b/Backup/HyperPropertyDescriptor.csproj @@ -0,0 +1,50 @@ + + + Debug + AnyCPU + 8.0.50727 + 2.0 + {954502B1-7282-417D-9310-3332962A1CA1} + Library + Properties + HyperPropertyDescriptor + HyperPropertyDescriptor + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Backup/HyperPropertyDescriptor.csproj.user b/Backup/HyperPropertyDescriptor.csproj.user new file mode 100644 index 000000000..6a34e7dcd --- /dev/null +++ b/Backup/HyperPropertyDescriptor.csproj.user @@ -0,0 +1,5 @@ + + + ShowAllFiles + + \ No newline at end of file diff --git a/Backup/HyperTypeDescriptionProvider.cs b/Backup/HyperTypeDescriptionProvider.cs new file mode 100644 index 000000000..f21c47d85 --- /dev/null +++ b/Backup/HyperTypeDescriptionProvider.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.ComponentModel; +using System.Security.Permissions; + +/* Change history: + * 20 Apr 2007 Marc Gravell Rollback dictionary on error; + * Assert ReflectionPermission for main creation + * (thanks/credit to Josh Smith for feedback/hints) + */ + +namespace Hyper.ComponentModel { + public sealed class HyperTypeDescriptionProvider : TypeDescriptionProvider { + public static void Add(Type type) { + TypeDescriptionProvider parent = TypeDescriptor.GetProvider(type); + TypeDescriptor.AddProvider(new HyperTypeDescriptionProvider(parent), type); + } + public HyperTypeDescriptionProvider() : this(typeof(object)) { } + public HyperTypeDescriptionProvider(Type type) : this(TypeDescriptor.GetProvider(type)) { } + public HyperTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) { } + public static void Clear(Type type) { + lock (descriptors) { + descriptors.Remove(type); + } + } + public static void Clear() { + lock (descriptors) { + descriptors.Clear(); + } + } + private static readonly Dictionary descriptors = new Dictionary(); + public sealed override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { + ICustomTypeDescriptor descriptor; + lock (descriptors) { + if (!descriptors.TryGetValue(objectType, out descriptor)) { + try + { + descriptor = BuildDescriptor(objectType); + } + catch + { + return base.GetTypeDescriptor(objectType, instance); + } + } + return descriptor; + } + } + [ReflectionPermission( SecurityAction.Assert, Flags = ReflectionPermissionFlag.AllFlags)] + private ICustomTypeDescriptor BuildDescriptor(Type objectType) + { + // NOTE: "descriptors" already locked here + + // get the parent descriptor and add to the dictionary so that + // building the new descriptor will use the base rather than recursing + ICustomTypeDescriptor descriptor = base.GetTypeDescriptor(objectType, null); + descriptors.Add(objectType, descriptor); + try + { + // build a new descriptor from this, and replace the lookup + descriptor = new HyperTypeDescriptor(descriptor); + descriptors[objectType] = descriptor; + return descriptor; + } + catch + { // rollback and throw + // (perhaps because the specific caller lacked permissions; + // another caller may be successful) + descriptors.Remove(objectType); + throw; + } + } + } +} diff --git a/Backup/HyperTypeDescriptor.cs b/Backup/HyperTypeDescriptor.cs new file mode 100644 index 000000000..d47858bca --- /dev/null +++ b/Backup/HyperTypeDescriptor.cs @@ -0,0 +1,218 @@ +using System; +using System.ComponentModel; +using System.Reflection.Emit; +using System.Reflection; +using System.Threading; +using System.Collections.Generic; +using System.Diagnostics; + +/* Change history: + * 20 Apr 2007 Marc Gravell Renamed + */ + +namespace Hyper.ComponentModel { + sealed class HyperTypeDescriptor : CustomTypeDescriptor { + private readonly PropertyDescriptorCollection propertyCollections; + static readonly Dictionary properties = new Dictionary(); + internal HyperTypeDescriptor(ICustomTypeDescriptor parent) + : base(parent) { + propertyCollections = WrapProperties(parent.GetProperties()); + } + public sealed override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { + return propertyCollections; + } + public sealed override PropertyDescriptorCollection GetProperties() { + return propertyCollections; + } + private static PropertyDescriptorCollection WrapProperties(PropertyDescriptorCollection oldProps) { + PropertyDescriptor[] newProps = new PropertyDescriptor[oldProps.Count]; + int index = 0; + bool changed = false; + // HACK: how to identify reflection, given that the class is internal... + Type wrapMe = Assembly.GetAssembly(typeof(PropertyDescriptor)).GetType("System.ComponentModel.ReflectPropertyDescriptor"); + foreach (PropertyDescriptor oldProp in oldProps) { + PropertyDescriptor pd = oldProp; + // if it looks like reflection, try to create a bespoke descriptor + if (ReferenceEquals(wrapMe, pd.GetType()) && TryCreatePropertyDescriptor(ref pd)) { + changed = true; + } + newProps[index++] = pd; + } + + return changed ? new PropertyDescriptorCollection(newProps, true) : oldProps; + } + + static readonly ModuleBuilder moduleBuilder; + static int counter; + static HyperTypeDescriptor() { + AssemblyName an = new AssemblyName("Hyper.ComponentModel.dynamic"); + AssemblyBuilder ab = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run); + moduleBuilder = ab.DefineDynamicModule("Hyper.ComponentModel.dynamic.dll"); + + } + + private static bool TryCreatePropertyDescriptor(ref PropertyDescriptor descriptor) { + try { + PropertyInfo property = descriptor.ComponentType.GetProperty(descriptor.Name); + if (property == null) return false; + + lock (properties) { + PropertyDescriptor foundBuiltAlready; + if (properties.TryGetValue(property, out foundBuiltAlready)) { + descriptor = foundBuiltAlready; + return true; + } + + string name = "_c" + Interlocked.Increment(ref counter).ToString(); + TypeBuilder tb = moduleBuilder.DefineType(name, TypeAttributes.Sealed | TypeAttributes.NotPublic | TypeAttributes.Class | TypeAttributes.BeforeFieldInit | TypeAttributes.AutoClass | TypeAttributes.Public, typeof(ChainingPropertyDescriptor)); + + // ctor calls base + ConstructorBuilder cb = tb.DefineConstructor(MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(PropertyDescriptor) }); + ILGenerator il = cb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_0); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Call, typeof(ChainingPropertyDescriptor).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(PropertyDescriptor) }, null)); + il.Emit(OpCodes.Ret); + + MethodBuilder mb; + MethodInfo baseMethod; + if (property.CanRead) { + // obtain the implementation that we want to override + baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("GetValue"); + // create a new method that accepts an object and returns an object (as per the base) + mb = tb.DefineMethod(baseMethod.Name, + MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, + baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object) }); + // start writing IL into the method + il = mb.GetILGenerator(); + if (property.DeclaringType.IsValueType) { + // upbox the object argument into our known (instance) struct type + LocalBuilder lb = il.DeclareLocal(property.DeclaringType); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Unbox_Any, property.DeclaringType); + il.Emit(OpCodes.Stloc_0); + il.Emit(OpCodes.Ldloca_S, lb); + } else { + // cast the object argument into our known class type + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Castclass, property.DeclaringType); + } + // call the "get" method + il.Emit(OpCodes.Callvirt, property.GetGetMethod()); + + if (property.PropertyType.IsValueType) { + // box it from the known (value) struct type + il.Emit(OpCodes.Box, property.PropertyType); + } + // return the value + il.Emit(OpCodes.Ret); + // signal that this method should override the base + tb.DefineMethodOverride(mb, baseMethod); + } + + bool supportsChangeEvents = descriptor.SupportsChangeEvents, isReadOnly = descriptor.IsReadOnly; + + // override SupportsChangeEvents + baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("SupportsChangeEvents").GetGetMethod(); + mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes); + il = mb.GetILGenerator(); + if (supportsChangeEvents) { + il.Emit(OpCodes.Ldc_I4_1); + } else { + il.Emit(OpCodes.Ldc_I4_0); + } + il.Emit(OpCodes.Ret); + tb.DefineMethodOverride(mb, baseMethod); + + // override IsReadOnly + baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("IsReadOnly").GetGetMethod(); + mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes); + il = mb.GetILGenerator(); + if (isReadOnly) { + il.Emit(OpCodes.Ldc_I4_1); + } else { + il.Emit(OpCodes.Ldc_I4_0); + } + il.Emit(OpCodes.Ret); + tb.DefineMethodOverride(mb, baseMethod); + + /* REMOVED: PropertyType, ComponentType; actually *adds* time overriding these + // override PropertyType + baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("PropertyType").GetGetMethod(); + mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes); + il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldtoken, descriptor.PropertyType); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); + il.Emit(OpCodes.Ret); + tb.DefineMethodOverride(mb, baseMethod); + + // override ComponentType + baseMethod = typeof(ChainingPropertyDescriptor).GetProperty("ComponentType").GetGetMethod(); + mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, Type.EmptyTypes); + il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldtoken, descriptor.ComponentType); + il.Emit(OpCodes.Call, typeof(Type).GetMethod("GetTypeFromHandle")); + il.Emit(OpCodes.Ret); + tb.DefineMethodOverride(mb, baseMethod); + */ + + // for classes, implement write (would be lost in unbox for structs) + if (!property.DeclaringType.IsValueType) { + if (!isReadOnly && property.CanWrite) { + // override set method + baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("SetValue"); + mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final, baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object), typeof(object) }); + il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Castclass, property.DeclaringType); + il.Emit(OpCodes.Ldarg_2); + if (property.PropertyType.IsValueType) { + il.Emit(OpCodes.Unbox_Any, property.PropertyType); + } else { + il.Emit(OpCodes.Castclass, property.PropertyType); + } + il.Emit(OpCodes.Callvirt, property.GetSetMethod()); + il.Emit(OpCodes.Ret); + tb.DefineMethodOverride(mb, baseMethod); + } + + if (supportsChangeEvents) { + EventInfo ei = property.DeclaringType.GetEvent(property.Name + "Changed"); + if (ei != null) { + baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("AddValueChanged"); + mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object), typeof(EventHandler) }); + il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Castclass, property.DeclaringType); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Callvirt, ei.GetAddMethod()); + il.Emit(OpCodes.Ret); + tb.DefineMethodOverride(mb, baseMethod); + + baseMethod = typeof(ChainingPropertyDescriptor).GetMethod("RemoveValueChanged"); + mb = tb.DefineMethod(baseMethod.Name, MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.SpecialName, baseMethod.CallingConvention, baseMethod.ReturnType, new Type[] { typeof(object), typeof(EventHandler) }); + il = mb.GetILGenerator(); + il.Emit(OpCodes.Ldarg_1); + il.Emit(OpCodes.Castclass, property.DeclaringType); + il.Emit(OpCodes.Ldarg_2); + il.Emit(OpCodes.Callvirt, ei.GetRemoveMethod()); + il.Emit(OpCodes.Ret); + tb.DefineMethodOverride(mb, baseMethod); + } + } + + } + PropertyDescriptor newDesc = tb.CreateType().GetConstructor(new Type[] { typeof(PropertyDescriptor) }).Invoke(new object[] { descriptor }) as PropertyDescriptor; + if (newDesc == null) { + return false; + } + descriptor = newDesc; + properties.Add(property, descriptor); + return true; + } + } catch { + return false; + } + } + } +} diff --git a/Backup/Properties/AssemblyInfo.cs b/Backup/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..89228e1b0 --- /dev/null +++ b/Backup/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +[assembly: AllowPartiallyTrustedCallers] + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("HyperTypeDescriptor")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("HyperTypeDescriptor")] +[assembly: AssemblyCopyright("Copyright © Marc Gravell 2007")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f22bc56b-501e-438a-967e-8f0b48827869")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSprite.cs b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSprite.cs index 57eb967ad..a00c2f2c0 100644 --- a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSprite.cs +++ b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSprite.cs @@ -15,6 +15,8 @@ namespace Subsurface const float CheckWallsInterval = 5.0f; + public bool Enabled; + private BackgroundSpritePrefab prefab; private Vector2 position; @@ -29,6 +31,12 @@ namespace Subsurface public Swarm Swarm; + Vector2 drawPosition; + public Vector2 TransformedPosition + { + get { return drawPosition; } + } + public Vector2 Position { get { return position; } @@ -51,6 +59,8 @@ namespace Subsurface this.position = position; + drawPosition = position + Level.Loaded.Position; + steeringManager = new SteeringManager(this); velocity = new Vector3( @@ -141,18 +151,16 @@ namespace Subsurface if (velocity.X < 0.0f) rotation -= MathHelper.Pi; } - Vector2 drawPos = position; - - if (Level.Loaded != null) drawPos += Level.Loaded.Position; + if (Level.Loaded != null) drawPosition = position + Level.Loaded.Position; if (depth > 0.0f) { - Vector2 camOffset = drawPos - GameMain.GameScreen.Cam.WorldViewCenter; + Vector2 camOffset = drawPosition - GameMain.GameScreen.Cam.WorldViewCenter; - drawPos = drawPos - camOffset * (depth / MaxDepth) * 0.05f; + drawPosition = drawPosition - camOffset * (depth / MaxDepth) * 0.05f; } - prefab.Sprite.Draw(spriteBatch, new Vector2(drawPos.X, -drawPos.Y), Color.Lerp(Color.White, Color.DarkBlue, (depth/MaxDepth)*0.3f), + prefab.Sprite.Draw(spriteBatch, new Vector2(drawPosition.X, -drawPosition.Y), Color.Lerp(Color.White, Color.DarkBlue, (depth/MaxDepth)*0.3f), rotation, 1.0f - (depth / MaxDepth) * 0.2f, velocity.X > 0.0f ? SpriteEffects.None : SpriteEffects.FlipHorizontally, (depth / MaxDepth)); } } diff --git a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs index e6fcaac09..7435d6459 100644 --- a/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs +++ b/Subsurface/Source/Characters/BackgroundSprite/BackgroundSpriteManager.cs @@ -12,6 +12,10 @@ namespace Subsurface { const int MaxSprites = 100; + const float checkActiveInterval = 1.0f; + + float checkActiveTimer; + private List prefabs; private List activeSprites; @@ -31,7 +35,6 @@ namespace Subsurface public void SpawnSprites(int count) { - activeSprites.Clear(); if (prefabs.Count == 0) return; @@ -77,10 +80,26 @@ namespace Subsurface activeSprites.Clear(); } - public void Update(float deltaTime) + public void Update(Camera cam, float deltaTime) { + if (checkActiveTimer<0.0f) + { + foreach (BackgroundSprite sprite in activeSprites) + { + sprite.Enabled = (Math.Abs(sprite.TransformedPosition.X - cam.WorldViewCenter.X) < 4000.0f && + Math.Abs(sprite.TransformedPosition.Y - cam.WorldViewCenter.Y) < 4000.0f); + } + + checkActiveTimer = checkActiveInterval; + } + else + { + checkActiveTimer -= deltaTime; + } + foreach (BackgroundSprite sprite in activeSprites) { + if (!sprite.Enabled) continue; sprite.Update(deltaTime); } } @@ -89,6 +108,7 @@ namespace Subsurface { foreach (BackgroundSprite sprite in activeSprites) { + if (!sprite.Enabled) continue; sprite.Draw(spriteBatch); } } diff --git a/Subsurface/Source/GUI/GUI.cs b/Subsurface/Source/GUI/GUI.cs index 274826e1c..602304e6d 100644 --- a/Subsurface/Source/GUI/GUI.cs +++ b/Subsurface/Source/GUI/GUI.cs @@ -307,16 +307,22 @@ namespace Subsurface if (GameMain.DebugDraw) { spriteBatch.DrawString(Font, - "Physics: " + GameMain.World.UpdateTime - + " - bodies: " + GameMain.World.BodyList.Count - + " Camera pos: " + GameMain.GameScreen.Cam.Position, + "Physics: " + GameMain.World.UpdateTime, new Vector2(10, 30), Color.White); + spriteBatch.DrawString(Font, + "Bodies: " + GameMain.World.BodyList.Count + " (" + GameMain.World.BodyList.FindAll(b => b.Awake && b.Enabled).Count + " awake)", + new Vector2(10, 50), Color.White); + + spriteBatch.DrawString(Font, + "Camera pos: " + GameMain.GameScreen.Cam.Position, + new Vector2(10, 70), Color.White); + if (Submarine.Loaded!=null) { spriteBatch.DrawString(Font, "Sub pos: " + Submarine.Loaded.Position, - new Vector2(10, 50), Color.White); + new Vector2(10, 90), Color.White); } } diff --git a/Subsurface/Source/Game1.cs b/Subsurface/Source/Game1.cs index 6c62db18f..3a188df65 100644 --- a/Subsurface/Source/Game1.cs +++ b/Subsurface/Source/Game1.cs @@ -120,6 +120,8 @@ namespace Subsurface //TargetElapsedTime = new TimeSpan(0, 0, 0, 0, 55); World = new World(new Vector2(0, -9.82f)); + FarseerPhysics.Settings.AllowSleep = true; + FarseerPhysics.Settings.ContinuousPhysics = false; FarseerPhysics.Settings.VelocityIterations = 2; FarseerPhysics.Settings.PositionIterations = 1; } diff --git a/Subsurface/Source/Items/Components/Container.cs b/Subsurface/Source/Items/Components/Container.cs index e2e66e542..e252f75ae 100644 --- a/Subsurface/Source/Items/Components/Container.cs +++ b/Subsurface/Source/Items/Components/Container.cs @@ -179,7 +179,7 @@ namespace Subsurface.Items.Components { if (containedItem == null) continue; - containedItem.sprite.Draw( + containedItem.Sprite.Draw( spriteBatch, new Vector2(transformedItemPos.X, -transformedItemPos.Y), -currentRotation, diff --git a/Subsurface/Source/Items/Components/ItemComponent.cs b/Subsurface/Source/Items/Components/ItemComponent.cs index 2a1119e5b..0dbb5c1f1 100644 --- a/Subsurface/Source/Items/Components/ItemComponent.cs +++ b/Subsurface/Source/Items/Components/ItemComponent.cs @@ -82,7 +82,16 @@ namespace Subsurface.Items.Components public virtual bool IsActive { get { return isActive; } - set { isActive = value; } + set + { + if (!value && isActive) + { + StopSounds(ActionType.OnActive); + StopSounds(ActionType.OnUse); + } + + isActive = value; + } } [HasDefaultValue(false, false)] diff --git a/Subsurface/Source/Items/Components/Projectile.cs b/Subsurface/Source/Items/Components/Projectile.cs index 95a92416f..efae83239 100644 --- a/Subsurface/Source/Items/Components/Projectile.cs +++ b/Subsurface/Source/Items/Components/Projectile.cs @@ -205,7 +205,7 @@ namespace Subsurface.Items.Components stickJoint.MaxMotorForce = 30.0f; stickJoint.LimitEnabled = true; - stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.sprite.size.X*0.7f); + stickJoint.UpperLimit = ConvertUnits.ToSimUnits(item.Sprite.size.X*0.7f); item.body.FarseerBody.IgnoreCollisionWith(targetBody); stickTarget = targetBody; diff --git a/Subsurface/Source/Items/Inventory.cs b/Subsurface/Source/Items/Inventory.cs index 9657701a7..708b5a40b 100644 --- a/Subsurface/Source/Items/Inventory.cs +++ b/Subsurface/Source/Items/Inventory.cs @@ -251,7 +251,7 @@ namespace Subsurface if (item == null) return; - item.sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), item.Color); + item.Sprite.Draw(spriteBatch, new Vector2(rect.X + rect.Width / 2, rect.Y + rect.Height / 2), item.Color); if (isHighLighted) { diff --git a/Subsurface/Source/Items/Item.cs b/Subsurface/Source/Items/Item.cs index 07bcbdde3..79966335d 100644 --- a/Subsurface/Source/Items/Item.cs +++ b/Subsurface/Source/Items/Item.cs @@ -68,7 +68,7 @@ namespace Subsurface get { return prefab.Name; } } - public override Sprite sprite + public override Sprite Sprite { get { return prefab.sprite; } } @@ -299,9 +299,8 @@ namespace Subsurface } } - + InsertToList(); itemList.Add(this); - mapEntityList.Add(this); } public T GetComponent() @@ -475,23 +474,8 @@ namespace Subsurface { if (ic.Parent != null) ic.IsActive = ic.Parent.IsActive; - //if (!ic.WasUsed) - //{ - // if (ic.Name == "RepairTool" && ic.IsActive) - // { - // System.Diagnostics.Debug.WriteLine("stop sounds"); - // } - // ic.StopSounds(ActionType.OnUse); - //} - //ic.WasUsed = false; - + if (!ic.IsActive) continue; - if (!ic.IsActive) - { - ic.StopSounds(ActionType.OnActive); - ic.StopSounds(ActionType.OnUse); - continue; - } if (condition > 0.0f) { ic.Update(deltaTime, cam); @@ -503,17 +487,19 @@ namespace Subsurface { ic.UpdateBroken(deltaTime, cam); } + } + + if (body == null || !body.Enabled) return; + + if (body.LinearVelocity.Length() > 0.001f) + { + FindHull(); + + Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); + + rect.X = (int)(displayPos.X - rect.Width / 2.0f); + rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); } - - - if (body == null) return; - - if (body.LinearVelocity.Length()>0.001f) FindHull(); - - Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition); - - rect.X = (int)(displayPos.X - rect.Width / 2.0f); - rect.Y = (int)(displayPos.Y + rect.Height / 2.0f); body.SetToTargetPosition(); @@ -527,7 +513,7 @@ namespace Subsurface { Vector2 impulse = -body.LinearVelocity * (body.Mass / body.Density); body.ApplyLinearImpulse(impulse); - int n = (int)((displayPos.X - CurrentHull.Rect.X) / Hull.WaveWidth); + int n = (int)((ConvertUnits.ToDisplayUnits(body.SimPosition.X) - CurrentHull.Rect.X) / Hull.WaveWidth); CurrentHull.WaveVel[n] = impulse.Y * 10.0f; } } @@ -537,28 +523,21 @@ namespace Subsurface Vector2 buoyancy = new Vector2(0, volume * 20.0f); //apply buoyancy and drag - try - { - //if ((buoyancy - body.LinearVelocity * volume) == Vector2.Zero) DebugConsole.ThrowError("v.zero "); - if (body.LinearVelocity != Vector2.Zero && body.LinearVelocity.Length() > 1000.0f) - { - body.ResetDynamics(); - if (body.SimPosition.Length() > 1000.0f) - { - Remove(); - return; - } - } - body.ApplyForce(buoyancy - body.LinearVelocity * volume); - - //apply simple angular drag - body.ApplyTorque(body.AngularVelocity * volume * -0.05f); - } - catch + //if ((buoyancy - body.LinearVelocity * volume) == Vector2.Zero) DebugConsole.ThrowError("v.zero "); + if (body.LinearVelocity != Vector2.Zero && body.LinearVelocity.Length() > 1000.0f) { - DebugConsole.ThrowError("something bad happened with the physics"); + body.ResetDynamics(); + if (body.SimPosition.Length() > 1000.0f) + { + Remove(); + return; + } } + body.ApplyForce(buoyancy - body.LinearVelocity * volume); + + //apply simple angular drag + body.ApplyTorque(body.AngularVelocity * volume * -0.05f); } public override void Draw(SpriteBatch spriteBatch, bool editing) diff --git a/Subsurface/Source/Items/ItemPrefab.cs b/Subsurface/Source/Items/ItemPrefab.cs index def5e6eb4..b7021d576 100644 --- a/Subsurface/Source/Items/ItemPrefab.cs +++ b/Subsurface/Source/Items/ItemPrefab.cs @@ -145,7 +145,7 @@ namespace Subsurface name = ToolBox.GetAttributeString(element, "name", ""); if (name == "") DebugConsole.ThrowError("Unnamed item in "+filePath+"!"); - + pickDistance = ConvertUnits.ToSimUnits(ToolBox.GetAttributeFloat(element, "pickdistance", 0.0f)); isLinkable = ToolBox.GetAttributeBool(element, "linkable", false); diff --git a/Subsurface/Source/Map/Gap.cs b/Subsurface/Source/Map/Gap.cs index 42c7cc6bf..7f929f0a8 100644 --- a/Subsurface/Source/Map/Gap.cs +++ b/Subsurface/Source/Map/Gap.cs @@ -73,7 +73,7 @@ namespace Subsurface FindHulls(); GapList.Add(this); - mapEntityList.Add(this); + InsertToList(); } public static void UpdateHulls() diff --git a/Subsurface/Source/Map/Hull.cs b/Subsurface/Source/Map/Hull.cs index 6bf01cea4..4c41965d6 100644 --- a/Subsurface/Source/Map/Hull.cs +++ b/Subsurface/Source/Map/Hull.cs @@ -146,8 +146,7 @@ namespace Subsurface Volume = 0.0f; - //add to list of entities as well - mapEntityList.Add(this); + InsertToList(); } public override bool Contains(Vector2 position) diff --git a/Subsurface/Source/Map/Lights/ConvexHull.cs b/Subsurface/Source/Map/Lights/ConvexHull.cs index e2f115ea8..d069705a6 100644 --- a/Subsurface/Source/Map/Lights/ConvexHull.cs +++ b/Subsurface/Source/Map/Lights/ConvexHull.cs @@ -10,18 +10,17 @@ namespace Subsurface.Lights public static List list = new List(); static BasicEffect shadowEffect; static BasicEffect penumbraEffect; - - private static VertexPositionTexture[] penumbraVertices; - - private VertexPositionColor[] vertices; - private short[] indices; - int primitiveCount; + + private Vector2[] vertices; + private int primitiveCount; - bool[] backFacing; - VertexPositionColor[] shadowVertices; + private bool[] backFacing; + + private VertexPositionColor[] shadowVertices; + private VertexPositionTexture[] penumbraVertices; private Rectangle boundingBox; - + public bool Enabled { get; @@ -48,86 +47,77 @@ namespace Subsurface.Lights penumbraEffect.LightingEnabled = false; penumbraEffect.Texture = TextureLoader.FromFile("Content/Lights/penumbra.png"); } + + vertices = points; + primitiveCount = vertices.Length; - if (penumbraVertices==null) - { - penumbraVertices = new VertexPositionTexture[6]; - } + CalculateDimensions(); + //indices = new short[primitiveCount * 3]; - int vertexCount = points.Length; - vertices = new VertexPositionColor[vertexCount + 1]; - Vector2 center = Vector2.Zero; - - float? minX = null, minY = null, maxX = null, maxY = null; - - for (int i = 0; i < vertexCount; i++) - { - vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), color); - center += points[i]; - - if (minX == null || points[i].X < minX) minX = points[i].X; - if (minY == null || points[i].Y < minY) minY = points[i].Y; - - if (maxX == null || points[i].X > maxX) maxX = points[i].X; - if (maxY == null || points[i].Y > minY) maxY = points[i].Y; - } - center /= points.Length; - vertices[vertexCount] = new VertexPositionColor(new Vector3(center, 0), color); - - boundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX-minX), (int)(maxY-minY)); - - primitiveCount = points.Length; - indices = new short[primitiveCount * 3]; - - for (int i = 0; i < primitiveCount; i++) - { - indices[3 * i] = (short)i; - indices[3 * i + 1] = (short)((i + 1) % vertexCount); - indices[3 * i + 2] = (short)vertexCount; - } - backFacing = new bool[vertexCount]; + //for (int i = 0; i < primitiveCount; i++) + //{ + // indices[3 * i] = (short)i; + // indices[3 * i + 1] = (short)((i + 1) % vertexCount); + // indices[3 * i + 2] = (short)vertexCount; + //} + backFacing = new bool[primitiveCount]; Enabled = true; list.Add(this); } + private void CalculateDimensions() + { + Vector2 center = Vector2.Zero; + + float? minX = null, minY = null, maxX = null, maxY = null; + + for (int i = 0; i < vertices.Length; i++) + { + center += vertices[i]; + + if (minX == null || vertices[i].X < minX) minX = vertices[i].X; + if (minY == null || vertices[i].Y < minY) minY = vertices[i].Y; + + if (maxX == null || vertices[i].X > maxX) maxX = vertices[i].X; + if (maxY == null || vertices[i].Y > minY) maxY = vertices[i].Y; + } + center /= vertices.Length; + + boundingBox = new Rectangle((int)minX, (int)minY, (int)(maxX - minX), (int)(maxY - minY)); + } + public void Move(Vector2 amount) { for (int i = 0; i < vertices.Count(); i++) { - vertices[i].Position = new Vector3(vertices[i].Position.X + amount.X, vertices[i].Position.Y + amount.Y, vertices[i].Position.Z); + vertices[i] += amount; } + + CalculateDimensions(); } public void SetVertices(Vector2[] points) { - int vertexCount = points.Length; - vertices = new VertexPositionColor[vertexCount + 1]; - - for (int i = 0; i < vertexCount; i++) - { - vertices[i] = new VertexPositionColor(new Vector3(points[i], 0), Color.Black); - } + vertices = points; } - public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Vector2 lightSourcePos, Matrix transform, bool los = true) + private void CalculateShadowVertices(Vector2 lightSourcePos, bool los = true) { - if (!Enabled) return; - //compute facing of each edge, using N*L for (int i = 0; i < primitiveCount; i++) { - Vector2 firstVertex = new Vector2(vertices[i].Position.X, vertices[i].Position.Y); + Vector2 firstVertex = new Vector2(vertices[i].X, vertices[i].Y); int secondIndex = (i + 1) % primitiveCount; - Vector2 secondVertex = new Vector2(vertices[secondIndex].Position.X, vertices[secondIndex].Position.Y); + Vector2 secondVertex = new Vector2(vertices[secondIndex].X, vertices[secondIndex].Y); Vector2 middle = (firstVertex + secondVertex) / 2; Vector2 L = lightSourcePos - middle; - Vector2 N = new Vector2(); - N.X = -(secondVertex.Y - firstVertex.Y); - N.Y = secondVertex.X - firstVertex.X; + Vector2 N = new Vector2( + -(secondVertex.Y - firstVertex.Y), + secondVertex.X - firstVertex.X); backFacing[i] = (Vector2.Dot(N, L) < 0); } @@ -148,49 +138,6 @@ namespace Subsurface.Lights startingIndex = nextEdge; } - if (los) - { - for (int n = 0; n < 4; n+=3) - { - Vector3 penumbraStart = (n == 0) ? vertices[startingIndex].Position : vertices[endingIndex].Position; - - penumbraVertices[n] = new VertexPositionTexture(); - penumbraVertices[n].Position = penumbraStart; - penumbraVertices[n].TextureCoordinate = new Vector2(0.0f, 1.0f); - //penumbraVertices[0].te = fow ? Color.Black : Color.Transparent; - - for (int i = 0; i < 2; i++ ) - { - penumbraVertices[n + i + 1] = new VertexPositionTexture(); - Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0); - vertexDir.Normalize(); - - Vector3 normal = (i == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f)*0.05f; - if (n > 0) normal = -normal; - vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f); - vertexDir.Normalize(); - penumbraVertices[n + i + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000; - - if (los) - { - penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f); - } - else - { - penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(1.0f, 0.0f) : Vector2.Zero; - } - } - - if (n > 0) - { - var temp = penumbraVertices[4]; - penumbraVertices[4] = penumbraVertices[5]; - penumbraVertices[5] = temp; - } - } - } - - int shadowVertexCount; //nr of vertices that are in the shadow @@ -206,7 +153,7 @@ namespace Subsurface.Lights int svCount = 0; while (svCount != shadowVertexCount * 2) { - Vector3 vertexPos = vertices[currentIndex].Position; + Vector3 vertexPos = new Vector3(vertices[currentIndex], 0.0f); //one vertex on the hull shadowVertices[svCount] = new VertexPositionColor(); @@ -224,10 +171,68 @@ namespace Subsurface.Lights currentIndex = (currentIndex + 1) % primitiveCount; } + if (los) + { + CalculatePenumbraVertices(startingIndex, endingIndex, lightSourcePos, los); + } + } + + private void CalculatePenumbraVertices(int startingIndex, int endingIndex, Vector2 lightSourcePos, bool los) + { + penumbraVertices = new VertexPositionTexture[6]; + + for (int n = 0; n < 4; n += 3) + { + Vector3 penumbraStart = new Vector3((n == 0) ? vertices[startingIndex] : vertices[endingIndex], 0.0f); + + penumbraVertices[n] = new VertexPositionTexture(); + penumbraVertices[n].Position = penumbraStart; + penumbraVertices[n].TextureCoordinate = new Vector2(0.0f, 1.0f); + //penumbraVertices[0].te = fow ? Color.Black : Color.Transparent; + + for (int i = 0; i < 2; i++) + { + penumbraVertices[n + i + 1] = new VertexPositionTexture(); + Vector3 vertexDir = penumbraStart - new Vector3(lightSourcePos, 0); + vertexDir.Normalize(); + + Vector3 normal = (i == 0) ? new Vector3(-vertexDir.Y, vertexDir.X, 0.0f) : new Vector3(vertexDir.Y, -vertexDir.X, 0.0f) * 0.05f; + if (n > 0) normal = -normal; + + vertexDir = penumbraStart - (new Vector3(lightSourcePos, 0) - normal * 20.0f); + vertexDir.Normalize(); + penumbraVertices[n + i + 1].Position = new Vector3(lightSourcePos, 0) + vertexDir * 9000; + + if (los) + { + penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(0.05f, 0.0f) : new Vector2(1.0f, 0.0f); + } + else + { + penumbraVertices[n + i + 1].TextureCoordinate = (i == 0) ? new Vector2(1.0f, 0.0f) : Vector2.Zero; + } + } + + if (n > 0) + { + var temp = penumbraVertices[4]; + penumbraVertices[4] = penumbraVertices[5]; + penumbraVertices[5] = temp; + } + } + } + + public void DrawShadows(GraphicsDevice graphicsDevice, Camera cam, Vector2 lightSourcePos, Matrix transform, bool los = true) + { + if (!Enabled) return; + + CalculateShadowVertices(lightSourcePos, los); + + shadowEffect.World = transform; shadowEffect.CurrentTechnique.Passes[0].Apply(); - graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertexCount * 2 - 2); + graphicsDevice.DrawUserPrimitives(PrimitiveType.TriangleStrip, shadowVertices, 0, shadowVertices.Length - 2); if (los) { diff --git a/Subsurface/Source/Map/MapEntity.cs b/Subsurface/Source/Map/MapEntity.cs index 92c8f8263..c7ec8e9e4 100644 --- a/Subsurface/Source/Map/MapEntity.cs +++ b/Subsurface/Source/Map/MapEntity.cs @@ -72,7 +72,7 @@ namespace Subsurface set { rect = value; } } - public virtual Sprite sprite + public virtual Sprite Sprite { get { return null; } } @@ -150,6 +150,28 @@ namespace Subsurface return (Submarine.RectContains(rect, position)); } + protected void InsertToList() + { + int i = 0; + + if (Sprite==null) + { + mapEntityList.Add(this); + return; + } + + while (i - public static void LinkAll() + public static void OnMapLoaded() { foreach (MapEntity e in mapEntityList) { @@ -465,7 +487,15 @@ namespace Subsurface e.linkedTo.Add(linked); } } + + //mapEntityList.Sort((x, y) => + //{ + // return x.Name.CompareTo(y.Name); + //}); + } + + public void RemoveLinked(MapEntity e) { diff --git a/Subsurface/Source/Map/Structure.cs b/Subsurface/Source/Map/Structure.cs index 9843ca16c..886fea72d 100644 --- a/Subsurface/Source/Map/Structure.cs +++ b/Subsurface/Source/Map/Structure.cs @@ -57,7 +57,7 @@ namespace Subsurface bool isHorizontal; - public override Sprite sprite + public override Sprite Sprite { get { return prefab.sprite; } } @@ -265,7 +265,7 @@ namespace Subsurface convexHull = new ConvexHull(corners, Color.Black); } - mapEntityList.Add(this); + InsertToList(); } public override void Remove() @@ -290,19 +290,22 @@ namespace Subsurface Color color = (isHighlighted) ? Color.Green : Color.White; if (isSelected && editing) color = Color.Red; + + prefab.sprite.DrawTiled(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), Vector2.Zero, color); - prefab.sprite.DrawTiled(spriteBatch, new Vector2(rect.X, -rect.Y), new Vector2(rect.Width, rect.Height), Vector2.Zero, color); - foreach (WallSection s in sections) { if (s.isHighLighted) + { GUI.DrawRectangle(spriteBatch, new Rectangle((int)s.rect.X, (int)-s.rect.Y, (int)s.rect.Width, (int)s.rect.Height), new Color((s.damage / prefab.MaxHealth), 1.0f - (s.damage / prefab.MaxHealth), 0.0f, 1.0f), true); + } + s.isHighLighted = false; - if (s.damage == 0.0f) continue; + if (s.damage < 0.01f) continue; GUI.DrawRectangle(spriteBatch, new Rectangle((int)s.rect.X, (int)-s.rect.Y, (int)s.rect.Width, (int)s.rect.Height), diff --git a/Subsurface/Source/Map/Submarine.cs b/Subsurface/Source/Map/Submarine.cs index 822026e1d..8d18ce961 100644 --- a/Subsurface/Source/Map/Submarine.cs +++ b/Subsurface/Source/Map/Submarine.cs @@ -159,7 +159,7 @@ namespace Subsurface { for (int i = 0; i < MapEntity.mapEntityList.Count(); i++) { - if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth < 0.5f) + if (MapEntity.mapEntityList[i].Sprite == null || MapEntity.mapEntityList[i].Sprite.Depth < 0.5f) MapEntity.mapEntityList[i].Draw(spriteBatch, editing); } @@ -181,7 +181,7 @@ namespace Subsurface { for (int i = 0; i < MapEntity.mapEntityList.Count(); i++) { - if (MapEntity.mapEntityList[i].sprite == null || MapEntity.mapEntityList[i].sprite.Depth >= 0.5f) + if (MapEntity.mapEntityList[i].Sprite == null || MapEntity.mapEntityList[i].Sprite.Depth >= 0.5f) MapEntity.mapEntityList[i].Draw(spriteBatch, editing); } } @@ -603,7 +603,7 @@ namespace Subsurface subBody = new SubmarineBody(this); - MapEntity.LinkAll(); + MapEntity.OnMapLoaded(); foreach (Item item in Item.itemList) { diff --git a/Subsurface/Source/Map/WayPoint.cs b/Subsurface/Source/Map/WayPoint.cs index 2261bea12..bcfca1136 100644 --- a/Subsurface/Source/Map/WayPoint.cs +++ b/Subsurface/Source/Map/WayPoint.cs @@ -48,7 +48,7 @@ namespace Subsurface linkedTo = new ObservableCollection(); idCardTags = new string[0]; - mapEntityList.Add(this); + InsertToList(); WayPointList.Add(this); } diff --git a/Subsurface/Source/Networking/GameServer.cs b/Subsurface/Source/Networking/GameServer.cs index 986e32237..81fd3e84b 100644 --- a/Subsurface/Source/Networking/GameServer.cs +++ b/Subsurface/Source/Networking/GameServer.cs @@ -436,9 +436,6 @@ namespace Subsurface.Networking userID = inc.ReadInt32(); userPassword = inc.ReadString(); version = inc.ReadString(); -#if DEBUG - version = GameMain.Version.ToString(); -#endif packageName = inc.ReadString(); packageHash = inc.ReadString(); name = inc.ReadString(); @@ -450,6 +447,8 @@ namespace Subsurface.Networking return; } +#if !DEBUG + if (userPassword != password) { inc.SenderConnection.Deny("Wrong password!"); @@ -481,6 +480,8 @@ namespace Subsurface.Networking return; } +#endif + //existing user re-joining if (userID > 0) { diff --git a/Subsurface/Source/Physics/PhysicsBody.cs b/Subsurface/Source/Physics/PhysicsBody.cs index 828f3b699..acb4e5834 100644 --- a/Subsurface/Source/Physics/PhysicsBody.cs +++ b/Subsurface/Source/Physics/PhysicsBody.cs @@ -226,6 +226,8 @@ namespace Subsurface body.CollidesWith = Physics.CollisionWall; body.Friction = ToolBox.GetAttributeFloat(element, "friction", 0.3f); + body.Restitution = 0.05f; + body.BodyType = BodyType.Dynamic; //body.AngularDamping = Limb.LimbAngularDamping; @@ -307,9 +309,14 @@ namespace Subsurface UpdateDrawPosition(); SpriteEffects spriteEffect = (dir == 1.0f) ? SpriteEffects.None : SpriteEffects.FlipHorizontally; + + if (GameMain.DebugDraw && !body.Awake) + { + color = Color.Blue; + } sprite.Draw(spriteBatch, new Vector2(drawPosition.X, -drawPosition.Y), color, -drawRotation, 1.0f, spriteEffect, depth); - + //prevPosition = body.Position; //prevRotation = body.Rotation; } diff --git a/Subsurface/Source/Screens/GameScreen.cs b/Subsurface/Source/Screens/GameScreen.cs index 83e81e44b..9d88a2f84 100644 --- a/Subsurface/Source/Screens/GameScreen.cs +++ b/Subsurface/Source/Screens/GameScreen.cs @@ -3,6 +3,7 @@ using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Input; using Subsurface.Lights; +using System.Diagnostics; namespace Subsurface { @@ -57,8 +58,15 @@ namespace Subsurface //http://gafferongames.com/game-physics/fix-your-timestep/ Physics.accumulator += deltaTime; + Stopwatch sw = new Stopwatch(); + sw.Start(); + AmbientSoundManager.Update(); + sw.Stop(); + Debug.WriteLine("************** abupdate: "+sw.ElapsedTicks); + sw.Restart(); + //if (Game1.GameSession != null && Game1.GameSession.Level != null) //{ // Vector2 targetMovement = Vector2.Zero; @@ -73,17 +81,38 @@ namespace Subsurface if (GameMain.GameSession!=null) GameMain.GameSession.Update((float)deltaTime); //EventManager.Update(gameTime); + sw.Stop(); + Debug.WriteLine("gamesession update: " + sw.ElapsedTicks); + sw.Restart(); + Character.UpdateAll(cam, (float)deltaTime); - BackgroundSpriteManager.Update((float)deltaTime); + sw.Stop(); + Debug.WriteLine("characterupdate: " + sw.ElapsedTicks); + sw.Restart(); + + BackgroundSpriteManager.Update(cam, (float)deltaTime); + + sw.Stop(); + Debug.WriteLine("bgsprite: " + sw.ElapsedTicks); + sw.Restart(); GameMain.ParticleManager.Update((float)deltaTime); + sw.Stop(); + Debug.WriteLine("particlemanager: " + sw.ElapsedTicks); + sw.Restart(); + StatusEffect.UpdateAll((float)deltaTime); + + sw.Stop(); + Debug.WriteLine("statuseff: " + sw.ElapsedTicks); + Physics.accumulator = Math.Min(Physics.accumulator, Physics.step * 4); while (Physics.accumulator >= Physics.step) { + sw.Restart(); cam.MoveCamera((float)Physics.step); foreach (PhysicsBody pb in PhysicsBody.list) @@ -93,8 +122,16 @@ namespace Subsurface MapEntity.UpdateAll(cam, (float)Physics.step); + sw.Stop(); + Debug.WriteLine(" mapentity: " + sw.ElapsedTicks); + sw.Restart(); + Character.UpdateAnimAll((float)Physics.step); + sw.Stop(); + Debug.WriteLine(" char: " + sw.ElapsedTicks); + sw.Restart(); + Ragdoll.UpdateAll((float)Physics.step); if (GameMain.GameSession != null && GameMain.GameSession.Level != null) @@ -102,8 +139,16 @@ namespace Subsurface GameMain.GameSession.Submarine.Update((float)Physics.step); } + sw.Stop(); + Debug.WriteLine(" sub: " + sw.ElapsedTicks); + sw.Restart(); + GameMain.World.Step((float)Physics.step); + sw.Stop(); + Debug.WriteLine(" worldstep: " + sw.ElapsedTicks); + sw.Restart(); + Level.AfterWorldStep(); Physics.accumulator -= Physics.step; @@ -146,7 +191,7 @@ namespace Subsurface public void DrawMap(GraphicsDevice graphics, SpriteBatch spriteBatch) { GameMain.LightManager.DrawLightmap(graphics, spriteBatch, cam); - + //---------------------------------------------------------------------------------------- //1. draw the background, characters and the parts of the submarine that are behind them //---------------------------------------------------------------------------------------- @@ -200,7 +245,7 @@ namespace Subsurface Submarine.DrawBack(spriteBatch); foreach (Character c in Character.CharacterList) c.Draw(spriteBatch); - + spriteBatch.End(); //---------------------------------------------------------------------------------------- @@ -213,10 +258,6 @@ namespace Subsurface spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), new Color(0.75f, 0.8f, 0.9f, 1.0f)); spriteBatch.End(); - BlendState blend = new BlendState(); - blend.AlphaSourceBlend = Blend.One; - blend.AlphaDestinationBlend = Blend.InverseSourceAlpha; - spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, null, DepthStencilState.DepthRead, null, null, @@ -258,7 +299,7 @@ namespace Subsurface } Hull.renderer.Render(graphics, cam, renderTargetAir, Cam.ShaderTransform); - + if (GameMain.GameSession != null && GameMain.GameSession.Level != null) { GameMain.GameSession.Level.Render(graphics, cam); @@ -285,17 +326,16 @@ namespace Subsurface Submarine.DrawFront(spriteBatch); foreach (Character c in Character.CharacterList) c.DrawFront(spriteBatch); - - if (GameMain.GameSession != null && GameMain.GameSession.Level != null) - { - GameMain.GameSession.Level.Draw(spriteBatch); - //Game1.GameSession.Level.SetObserverPosition(cam.WorldViewCenter); - } + + //if (GameMain.GameSession != null && GameMain.GameSession.Level != null) + //{ + // GameMain.GameSession.Level.Draw(spriteBatch); + // //Game1.GameSession.Level.SetObserverPosition(cam.WorldViewCenter); + //} spriteBatch.End(); GameMain.LightManager.DrawLOS(graphics, cam, LightManager.ViewPos); - } } } diff --git a/Subsurface/Source/Sprite.cs b/Subsurface/Source/Sprite.cs index 7cf40d378..9ab714479 100644 --- a/Subsurface/Source/Sprite.cs +++ b/Subsurface/Source/Sprite.cs @@ -80,9 +80,9 @@ namespace Subsurface if (!path.EndsWith("/")) path += "/"; } - file = path + file; - - texture = LoadTexture(file); + this.file = path + file; + + texture = LoadTexture(this.file); if (texture == null) return; diff --git a/Subsurface_Solution.sln b/Subsurface_Solution.sln index 895b06759..441d727e0 100644 --- a/Subsurface_Solution.sln +++ b/Subsurface_Solution.sln @@ -19,6 +19,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Launcher2", "Launcher2\Laun EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrashReporter", "CrashReporter\CrashReporter.csproj", "{6BE950CD-9A34-49C9-939A-786AC89C287E}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D32A29D8-AC7B-4189-B734-8ED9EB4120D0}" + ProjectSection(SolutionItems) = preProject + Performance1.psess = Performance1.psess + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Android|Any CPU = Android|Any CPU @@ -396,4 +401,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/Subsurface_Solution.v12.suo b/Subsurface_Solution.v12.suo index 4ea7d0b8e3cf504adfac8bfcc8ba5e5664143299..a32fa03b6a0d2699278e7bc1697982d0ba693fb4 100644 GIT binary patch delta 17993 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 delta 16533 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 Z@Xo(j<)T-Y>Y4hcb)znfd&cx>{|AqtW?TRO diff --git a/UpgradeLog.htm b/UpgradeLog.htm new file mode 100644 index 0000000000000000000000000000000000000000..14f56873d2359c4c0a30b83406a37746cf4b617d GIT binary patch literal 36916 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 um&Kis`ak*53r literal 0 HcmV?d00001