diff --git a/.gitignore b/.gitignore index 0528469bb..269991578 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,6 @@ /*.sln /*.csproj /UserSettings/ -*.DS_Store \ No newline at end of file +*.DS_Store +/ClassicUO.Resources/.vs/ClassicUO.Resources +/ClassicUO.Resources/obj/Debug diff --git a/.gitmodules b/.gitmodules index 16d8fecd2..6c80a893f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,7 @@ path = external/ClassicUO url = https://github.com/ClassicUO/ClassicUO.git # commit = 303c2ae699ca02caa6b09fbe5fbcb0c92f8bbd68 + +[submodule "external/MP3Sharp"] + path = external/MP3Sharp + url = https://github.com/andreakarasho/MP3Sharp.git diff --git a/Assets/ClassicUO.Resources.dll b/Assets/ClassicUO.Resources.dll new file mode 100644 index 000000000..285ad67b9 Binary files /dev/null and b/Assets/ClassicUO.Resources.dll differ diff --git a/Assets/ClassicUO.Resources.dll.meta b/Assets/ClassicUO.Resources.dll.meta new file mode 100644 index 000000000..330ab6467 --- /dev/null +++ b/Assets/ClassicUO.Resources.dll.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 7401572494f7899499d98586118bf3c4 \ No newline at end of file diff --git a/Assets/ClassicUO.Resources.pdb b/Assets/ClassicUO.Resources.pdb new file mode 100644 index 000000000..701664210 Binary files /dev/null and b/Assets/ClassicUO.Resources.pdb differ diff --git a/Assets/ClassicUO.Resources.pdb.meta b/Assets/ClassicUO.Resources.pdb.meta new file mode 100644 index 000000000..19ff3a75c --- /dev/null +++ b/Assets/ClassicUO.Resources.pdb.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 4a77c56e83a9d1848b0e60f4e405b8e8 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Debug.meta b/Assets/Debug.meta new file mode 100644 index 000000000..3223e727e --- /dev/null +++ b/Assets/Debug.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 35f3795808c0dc4499b9eb5a0ff94650 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/NuGet.config b/Assets/NuGet.config new file mode 100644 index 000000000..0c083882b --- /dev/null +++ b/Assets/NuGet.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Assets/NuGet.config.meta b/Assets/NuGet.config.meta new file mode 100644 index 000000000..7427431c5 --- /dev/null +++ b/Assets/NuGet.config.meta @@ -0,0 +1,28 @@ +fileFormatVersion: 2 +guid: 91efe62c1e5ae514292cbd1108fa1a17 +labels: +- NuGetForUnity +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 0 + settings: {} + Editor: + enabled: 0 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages.meta b/Assets/Packages.meta new file mode 100644 index 000000000..6179d9611 --- /dev/null +++ b/Assets/Packages.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4e4994ecd2fc4d4da66f32ab39c7449 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3.meta new file mode 100644 index 000000000..f092c9fa9 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f212def5c56fa674195ea03a45853c7d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/.signature.p7s b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/.signature.p7s new file mode 100644 index 000000000..0b25909d7 Binary files /dev/null and b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/.signature.p7s differ diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/LICENSE.TXT b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/LICENSE.TXT new file mode 100644 index 000000000..984713a49 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/LICENSE.TXT @@ -0,0 +1,23 @@ +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/LICENSE.TXT.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/LICENSE.TXT.meta new file mode 100644 index 000000000..6c3ccf982 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/LICENSE.TXT.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 82654112136a72d478fb2dfed9930268 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/System.Runtime.CompilerServices.Unsafe.nuspec b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/System.Runtime.CompilerServices.Unsafe.nuspec new file mode 100644 index 000000000..1f3197458 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/System.Runtime.CompilerServices.Unsafe.nuspec @@ -0,0 +1,40 @@ + + + + System.Runtime.CompilerServices.Unsafe + 4.5.3 + System.Runtime.CompilerServices.Unsafe + Microsoft + microsoft,dotnetframework + false + https://github.com/dotnet/corefx/blob/master/LICENSE.TXT + https://dot.net/ + http://go.microsoft.com/fwlink/?LinkID=288859 + Provides the System.Runtime.CompilerServices.Unsafe class, which provides generic, low-level functionality for manipulating pointers. + +Commonly Used Types: +System.Runtime.CompilerServices.Unsafe + +7601f4f6225089ffb291dc7d58293c7bbf5c5d4f +When using NuGet 3.x this package requires at least version 3.4. + https://go.microsoft.com/fwlink/?LinkID=799421 + © Microsoft Corporation. All rights reserved. + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/System.Runtime.CompilerServices.Unsafe.nuspec.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/System.Runtime.CompilerServices.Unsafe.nuspec.meta new file mode 100644 index 000000000..3cc991062 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/System.Runtime.CompilerServices.Unsafe.nuspec.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 937b42d1200a331479415385dca8f7cf +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/THIRD-PARTY-NOTICES.TXT b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/THIRD-PARTY-NOTICES.TXT new file mode 100644 index 000000000..db542ca24 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/THIRD-PARTY-NOTICES.TXT @@ -0,0 +1,309 @@ +.NET Core uses third-party libraries or other resources that may be +distributed under licenses different than the .NET Core software. + +In the event that we accidentally failed to list a required notice, please +bring it to our attention. Post an issue or email us: + + dotnet@microsoft.com + +The attached notices are provided for information only. + +License notice for Slicing-by-8 +------------------------------- + +http://sourceforge.net/projects/slicing-by-8/ + +Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved + + +This software program is licensed subject to the BSD License, available at +http://www.opensource.org/licenses/bsd-license.html. + + +License notice for Unicode data +------------------------------- + +http://www.unicode.org/copyright.html#License + +Copyright © 1991-2017 Unicode, Inc. All rights reserved. +Distributed under the Terms of Use in http://www.unicode.org/copyright.html. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Unicode data files and any associated documentation +(the "Data Files") or Unicode software and any associated documentation +(the "Software") to deal in the Data Files or Software +without restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, and/or sell copies of +the Data Files or Software, and to permit persons to whom the Data Files +or Software are furnished to do so, provided that either +(a) this copyright and permission notice appear with all copies +of the Data Files or Software, or +(b) this copyright and permission notice appear in associated +Documentation. + +THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT OF THIRD PARTY RIGHTS. +IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS +NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL +DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THE DATA FILES OR SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, +use or other dealings in these Data Files or Software without prior +written authorization of the copyright holder. + +License notice for Zlib +----------------------- + +https://github.com/madler/zlib +http://zlib.net/zlib_license.html + +/* zlib.h -- interface of the 'zlib' general purpose compression library + version 1.2.11, January 15th, 2017 + + Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + +*/ + +License notice for Mono +------------------------------- + +http://www.mono-project.com/docs/about-mono/ + +Copyright (c) .NET Foundation Contributors + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +License notice for International Organization for Standardization +----------------------------------------------------------------- + +Portions (C) International Organization for Standardization 1986: + Permission to copy in any form is granted for use with + conforming SGML systems and applications as defined in + ISO 8879, provided this notice is included in all copies. + +License notice for Intel +------------------------ + +"Copyright (c) 2004-2006 Intel Corporation - All Rights Reserved + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License notice for Xamarin and Novell +------------------------------------- + +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Copyright (c) 2011 Novell, Inc (http://www.novell.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Third party notice for W3C +-------------------------- + +"W3C SOFTWARE AND DOCUMENT NOTICE AND LICENSE +Status: This license takes effect 13 May, 2015. +This work is being provided by the copyright holders under the following license. +License +By obtaining and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions. +Permission to copy, modify, and distribute this work, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the work or portions thereof, including modifications: +The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. +Any pre-existing intellectual property disclaimers, notices, or terms and conditions. If none exist, the W3C Software and Document Short Notice should be included. +Notice of any changes or modifications, through a copyright statement on the new code or document such as "This software or document includes material copied from or derived from [title and URI of the W3C document]. Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." +Disclaimers +THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. +The name and trademarks of copyright holders may NOT be used in advertising or publicity pertaining to the work without specific, written prior permission. Title to copyright in this work will at all times remain with copyright holders." + +License notice for Bit Twiddling Hacks +-------------------------------------- + +Bit Twiddling Hacks + +By Sean Eron Anderson +seander@cs.stanford.edu + +Individually, the code snippets here are in the public domain (unless otherwise +noted) — feel free to use them however you please. The aggregate collection and +descriptions are © 1997-2005 Sean Eron Anderson. The code and descriptions are +distributed in the hope that they will be useful, but WITHOUT ANY WARRANTY and +without even the implied warranty of merchantability or fitness for a particular +purpose. + +License notice for Brotli +-------------------------------------- + +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +compress_fragment.c: +Copyright (c) 2011, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +decode_fuzzer.c: +Copyright (c) 2015 The Chromium Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/THIRD-PARTY-NOTICES.TXT.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/THIRD-PARTY-NOTICES.TXT.meta new file mode 100644 index 000000000..fc6b465e1 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/THIRD-PARTY-NOTICES.TXT.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 70a5408f448e27a4197eac225be06084 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib.meta new file mode 100644 index 000000000..3edab93b4 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c88c9f82dac741c49949d11f671a0d17 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0.meta new file mode 100644 index 000000000..dc9db8ce4 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 04bfeb7a85300ed40a9947c83bb98b42 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll new file mode 100644 index 000000000..b17135bc9 Binary files /dev/null and b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll differ diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll.meta new file mode 100644 index 000000000..6b9c52500 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll.meta @@ -0,0 +1,29 @@ +fileFormatVersion: 2 +guid: 4b97fc1b1f1573d4a856461fb388b119 +labels: +- NuGetForUnity +PluginImporter: + externalObjects: {} + serializedVersion: 3 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 0 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + Any: + enabled: 1 + settings: {} + Editor: + enabled: 0 + settings: + DefaultValueInitialized: true + WindowsStoreApps: + enabled: 0 + settings: + CPU: AnyCPU + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml new file mode 100644 index 000000000..6a7cfcffe --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml @@ -0,0 +1,200 @@ + + + System.Runtime.CompilerServices.Unsafe + + + + Contains generic, low-level functionality for manipulating pointers. + + + Adds an element offset to the given reference. + The reference to add the offset to. + The offset to add. + The type of reference. + A new reference that reflects the addition of offset to pointer. + + + Adds an element offset to the given reference. + The reference to add the offset to. + The offset to add. + The type of reference. + A new reference that reflects the addition of offset to pointer. + + + Adds a byte offset to the given reference. + The reference to add the offset to. + The offset to add. + The type of reference. + A new reference that reflects the addition of byte offset to pointer. + + + Determines whether the specified references point to the same location. + The first reference to compare. + The second reference to compare. + The type of reference. + true if left and right point to the same location; otherwise, false. + + + Casts the given object to the specified type. + The object to cast. + The type which the object will be cast to. + The original object, casted to the given type. + + + Reinterprets the given reference as a reference to a value of type TTo. + The reference to reinterpret. + The type of reference to reinterpret.. + The desired type of the reference. + A reference to a value of type TTo. + + + Returns a pointer to the given by-ref parameter. + The object whose pointer is obtained. + The type of object. + A pointer to the given value. + + + Reinterprets the given location as a reference to a value of type T. + The location of the value to reference. + The type of the interpreted location. + A reference to a value of type T. + + + Determines the byte offset from origin to target from the given references. + The reference to origin. + The reference to target. + The type of reference. + Byte offset from origin to target i.e. target - origin. + + + Copies a value of type T to the given location. + The location to copy to. + A reference to the value to copy. + The type of value to copy. + + + Copies a value of type T to the given location. + The location to copy to. + A pointer to the value to copy. + The type of value to copy. + + + Copies bytes from the source address to the destination address. + The destination address to copy to. + The source address to copy from. + The number of bytes to copy. + + + Copies bytes from the source address to the destination address. + The destination address to copy to. + The source address to copy from. + The number of bytes to copy. + + + Copies bytes from the source address to the destination address +without assuming architecture dependent alignment of the addresses. + The destination address to copy to. + The source address to copy from. + The number of bytes to copy. + + + Copies bytes from the source address to the destination address +without assuming architecture dependent alignment of the addresses. + The destination address to copy to. + The source address to copy from. + The number of bytes to copy. + + + Initializes a block of memory at the given location with a given initial value. + The address of the start of the memory block to initialize. + The value to initialize the block to. + The number of bytes to initialize. + + + Initializes a block of memory at the given location with a given initial value. + The address of the start of the memory block to initialize. + The value to initialize the block to. + The number of bytes to initialize. + + + Initializes a block of memory at the given location with a given initial value +without assuming architecture dependent alignment of the address. + The address of the start of the memory block to initialize. + The value to initialize the block to. + The number of bytes to initialize. + + + Initializes a block of memory at the given location with a given initial value +without assuming architecture dependent alignment of the address. + The address of the start of the memory block to initialize. + The value to initialize the block to. + The number of bytes to initialize. + + + Reads a value of type T from the given location. + The location to read from. + The type to read. + An object of type T read from the given location. + + + Reads a value of type T from the given location +without assuming architecture dependent alignment of the addresses. + The location to read from. + The type to read. + An object of type T read from the given location. + + + Reads a value of type T from the given location +without assuming architecture dependent alignment of the addresses. + The location to read from. + The type to read. + An object of type T read from the given location. + + + Returns the size of an object of the given type parameter. + The type of object whose size is retrieved. + The size of an object of type T. + + + Subtracts an element offset from the given reference. + The reference to subtract the offset from. + The offset to subtract. + The type of reference. + A new reference that reflects the subraction of offset from pointer. + + + Subtracts an element offset from the given reference. + The reference to subtract the offset from. + The offset to subtract. + The type of reference. + A new reference that reflects the subraction of offset from pointer. + + + Subtracts a byte offset from the given reference. + The reference to subtract the offset from. + + The type of reference. + A new reference that reflects the subraction of byte offset from pointer. + + + Writes a value of type T to the given location. + The location to write to. + The value to write. + The type of value to write. + + + Writes a value of type T to the given location +without assuming architecture dependent alignment of the addresses. + The location to write to. + The value to write. + The type of value to write. + + + Writes a value of type T to the given location +without assuming architecture dependent alignment of the addresses. + The location to write to. + The value to write. + The type of value to write. + + + \ No newline at end of file diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml.meta new file mode 100644 index 000000000..4d06f0d05 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3425bd589343d8741a2bd9b8aa11b0a0 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/useSharedDesignerContext.txt b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/useSharedDesignerContext.txt new file mode 100644 index 000000000..e69de29bb diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/useSharedDesignerContext.txt.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/useSharedDesignerContext.txt.meta new file mode 100644 index 000000000..2ded82e56 --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/useSharedDesignerContext.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 02d3ee895e35ea84489450e278cc1656 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/version.txt b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/version.txt new file mode 100644 index 000000000..8d6cdd69c --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/version.txt @@ -0,0 +1 @@ +7601f4f6225089ffb291dc7d58293c7bbf5c5d4f diff --git a/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/version.txt.meta b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/version.txt.meta new file mode 100644 index 000000000..9d4c46d7c --- /dev/null +++ b/Assets/Packages/System.Runtime.CompilerServices.Unsafe.4.5.3/version.txt.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 1cbed3d8bcb82f44d9d9c11bf2c0e780 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Resources/HueShader.shader b/Assets/Resources/HueShader.shader index 60692a189..54396a802 100644 --- a/Assets/Resources/HueShader.shader +++ b/Assets/Resources/HueShader.shader @@ -5,6 +5,7 @@ _MainTex ("Main Texture", 2D) = "white" {} [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("SrcBlend", Float) = 1.0 [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("DstBlend", Float) = 10 + _Brightlight ("Brightness", Float) = 0.0 } SubShader { @@ -22,28 +23,29 @@ #include "UnityCG.cginc" - static const int NOCOLOR = 0; - static const int COLOR = 1; - static const int PARTIAL_COLOR = 2; + static const int NONE = 0; + static const int HUED = 1; + static const int PARTIAL_HUED = 2; static const int HUE_TEXT_NO_BLACK = 3; static const int HUE_TEXT = 4; - static const int LAND = 6; - static const int LAND_COLOR = 7; - static const int SPECTRAL = 10; - static const int SHADOW = 12; - static const int LIGHTS = 13; + static const int LAND = 5; + static const int LAND_COLOR = 6; + static const int SPECTRAL = 7; + static const int SHADOW = 8; + static const int LIGHTS = 9; + static const int EFFECT_HUED = 10; static const int GUMP = 20; static const float HuesPerTexture = 2048; - static const float3 LIGHT_DIRECTION = float3(-1.0f, -1.0f, .5f); - static const float3 VEC3_ZERO = float3(0, 0, 0); + static const float3 LIGHT_DIRECTION = float3(0.0f, 1.0f, 1.0f); struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; + float3 Hue : TEXCOORD1; }; struct v2f @@ -51,6 +53,7 @@ float2 uv : TEXCOORD0; float3 Normal : NORMAL; float4 pos : SV_POSITION; + float3 Hue : TEXCOORD2; }; sampler2D _MainTex; @@ -60,29 +63,42 @@ float _Debug; float _Scissor; float4 _ScissorRect; + float _Brightlight; sampler2D _HueTex1; sampler2D _HueTex2; + sampler2D _HueTex3; - float3 get_rgb(float red, float hue) + float3 get_rgb(float gray, float hue) { if (hue <= HuesPerTexture) { - float2 texcoord = float2(red % 32, hue / HuesPerTexture); + float2 texcoord = float2(gray % 32, hue / HuesPerTexture); return tex2D(_HueTex1, texcoord).rgb; } else { - float2 texcoord = float2(red % 32, (hue - HuesPerTexture) / HuesPerTexture); + float2 texcoord = float2(gray % 32, (hue - HuesPerTexture) / HuesPerTexture); return tex2D(_HueTex2, texcoord).rgb; } } - float3 get_light(float3 norm) + float get_light(float3 norm) { float3 light = normalize(LIGHT_DIRECTION); float3 normal = normalize(norm); - return max((dot(normal, light) + 0.5f), 0.0f); + float base = (max(dot(normal, light), 0.0f) / 2.0f) + 0.5f; + + // At 45 degrees (the angle the flat tiles are lit at) it must come out + // to (cos(45) / 2) + 0.5 or 0.85355339... + return base + ((_Brightlight * (base - 0.85355339f)) - (base - 0.85355339f)); + } + + float3 get_colored_light(float shader, float gray) + { + float2 texcoord = float2(gray, (shader - 0.5) / 63); + + return tex2D(_HueTex3, texcoord).rgb; } v2f vert (appdata v) @@ -91,6 +107,7 @@ o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.Normal = v.normal; + o.Hue = v.Hue; return o; } @@ -119,76 +136,81 @@ int mode = int(_Hue.y); float alpha = 1 - _Hue.z; - float red = color.r; - if (mode > NOCOLOR) + if (mode == NONE) { - float hue = _Hue.x; - if (mode >= GUMP) - { - mode -= GUMP; - if (color.r < 0.02f) - { - hue = 0; - } - } + return color * alpha; + } - if (mode == COLOR || (mode == PARTIAL_COLOR && color.r == color.g && color.r == color.b)) - { - color.rgb = get_rgb(red, hue); - } - else if (mode > 5) - { - if (mode > 9) - { - if (mode > 10) - { - if (mode > 11) - { - if (mode > 12) - { - if (_Hue.x != 0.0f) - { - color.rgb *= get_rgb(color.r, hue); - } - return color * alpha; - } - - red = 0.6f; - } - else - { - red *= 0.5f; - } - } - else - { - red *= 1.5f; - } - - alpha = 1 - red; - color.rgb = VEC3_ZERO; - } - else - { - float3 norm = get_light(IN.Normal); - - if (mode > 6) - { - color.rgb = get_rgb(red, hue) * norm; - } - else - { - color.rgb *= norm; - } - } - } - else if (mode == 4 || (mode == 3 && (color.r > 0.04f || color.g > 0.04f || color.b > 0.04f)) /*|| (mode == 5 && color.r >= 0.08f)*/) + float hue = _Hue.x; + if (mode >= GUMP) + { + mode -= GUMP; + + if (color.r < 0.02f) { - color.rgb = get_rgb(31, hue); + hue = 0; } } + if (mode == HUED) + { + color.rgb = get_rgb(color.r, hue); + } + else if (mode == PARTIAL_HUED) + { + if (color.r == color.g && color.r == color.b) + { + // Gray pixels are hued + color.rgb = get_rgb(color.r, hue); + } + } + else if (mode == HUE_TEXT_NO_BLACK) + { + if (color.r > 0.04f || color.g > 0.04f || color.b > 0.04f) + { + color.rgb = get_rgb(31, hue); + } + } + else if (mode == HUE_TEXT) + { + // 31 is max red, so this is just selecting the color of the darkest pixel in the hue + color.rgb = get_rgb(31, hue); + } + else if (mode == LAND) + { + color.rgb *= get_light(IN.Normal); + } + else if (mode == LAND_COLOR) + { + color.rgb = get_rgb(color.r, hue) * get_light(IN.Normal); + } + else if (mode == SPECTRAL) + { + alpha = 1 - (color.r * 1.5f); + color.r = 0; + color.g = 0; + color.b = 0; + } + else if (mode == SHADOW) + { + alpha = 0.4f; + color.r = 0; + color.g = 0; + color.b = 0; + } + else if (mode == LIGHTS) + { + if (IN.Hue.x > 1.0f) + { + color.rgb = get_colored_light(IN.Hue.x - 1, color.r); + } + } + else if (mode == EFFECT_HUED) + { + color.rgb = get_rgb(color.g, hue); + } + return color * alpha; } ENDCG diff --git a/Assets/Scripts/Assistant/Assistant.cs b/Assets/Scripts/Assistant/Assistant.cs index 1aabd5e31..9d1078e04 100644 --- a/Assets/Scripts/Assistant/Assistant.cs +++ b/Assets/Scripts/Assistant/Assistant.cs @@ -132,8 +132,9 @@ internal ObjectInspectorGump(UOEntity inspected) : base(0, 0) Add(new SelectableReadOnlyBox(FONT, -1, 0, true, FontStyle.None, ScriptTextBox.GREEN_HUE) { X = x + mw, Y = y, Width = w - (40 + mw), Text = $"{inspected.Hue}" }); if (inspected.Hue > 0 && inspected.Hue <= HuesLoader.Instance.HuesCount) { + // MobileUO: TODO: ColorBox dropped a parameter in CUO 0.1.9.0, this may need to be revisited for(ushort i = 0; i < 32; ++i) - Add(new ColorBox(3, 14, inspected.Hue, HuesLoader.Instance.GetPolygoneColor(i, inspected.Hue)) { X = 190 + (3 * i), Y = y + 3 }); + Add(new ColorBox(3, 14, inspected.Hue/*, HuesLoader.Instance.GetPolygoneColor(i, inspected.Hue)*/) { X = 190 + (3 * i), Y = y + 3 }); } y += mh + 2; Add(new Label("Position (X Y Z):", true, ScriptTextBox.GRAY_HUE, mw) { X = x, Y = y }); @@ -233,7 +234,7 @@ internal ChangeAssistantGump(OptionsGump gump, Checkbox control) : base(0, 0) Add(new Label("Click on OKAY to close ClassicUO!", true, ScriptTextBox.GRAY_HUE, 280, FONT, FontStyle.None, TEXT_ALIGN_TYPE.TS_CENTER) { X = 20, Y = 50 }); Add(new NiceButton(40, 80, 80, 30, ButtonAction.Activate, "OKAY") { ButtonParameter = 123, IsSelectable = false }); Add(new NiceButton(180, 80, 80, 30, ButtonAction.Activate, "CANCEL") { ButtonParameter = 321, IsSelectable = false }); - ControlInfo.IsModal = true; + IsModal = true; } else Dispose(); @@ -503,7 +504,7 @@ internal OverWriteHKGump(uint num, HotKeyOpts hkopts, AssistHotkeyBox box, ref s Add(new Label("Click on OKAY to overwrite it!", true, ScriptTextBox.GRAY_HUE, 280, FONT, FontStyle.None, TEXT_ALIGN_TYPE.TS_CENTER) { X = 20, Y = 50 }); Add(new NiceButton(40, 80, 80, 30, ButtonAction.Activate, "OKAY") { ButtonParameter = 123, IsSelectable = false }); Add(new NiceButton(180, 80, 80, 30, ButtonAction.Activate, "CANCEL") { ButtonParameter = 321, IsSelectable = false }); - ControlInfo.IsModal = true; + IsModal = true; } else Dispose(); @@ -1009,7 +1010,7 @@ private void FilterChanged(object sender, EventArgs e) private void BuildGeneral(int page) { int starty = (_buttonHeight * 2) - (_buttonHeight >> 2), startx = (WIDTH >> 5) * 13, diffx = (_buttonWidth - (_buttonWidth >> 3)) >> 2, diffy = ((_buttonHeight - (_buttonHeight >> 3)) >> 2); - ScrollArea leftArea = new ScrollArea(8, _buttonHeight * 2, (_buttonWidth >> 2) * 30, diffy * 45, true); + AssistScrollArea leftArea = new AssistScrollArea(8, _buttonHeight * 2, (_buttonWidth >> 2) * 30, diffy * 45, true); Line.CreateRectangleArea(this, 3, starty, startx, leftArea.Height + (_buttonHeight >> 1), page, Color.Gray.PackedValue, 1, "Filters"); FiltersCB = new AssistCheckbox[Filter.List.Count]; for(int i = 0; i < Filter.List.Count; i++) @@ -1165,7 +1166,7 @@ private enum SmartTargetFor private Combobox _openDoorsOptions, _openCorpsesOptions, //_commandPrefix,//Generic _spellShareTargetOn, _smartLastTarget, _shareEnemyTargetOn,//Combat _friendHealSelection;//Friends - private ScrollArea _friendListArea;//Friends + private AssistScrollArea _friendListArea;//Friends private uint _friendSelected;//Friends private void BuildOptions() @@ -1345,7 +1346,7 @@ private void BuildOptions() Line.CreateRectangleArea(this, x - (_buttonWidth >> 3), y, (WIDTH >> 1) - (_buttonWidth >> 2), HEIGHT - (_buttonHeight * 6 + (diffy >> 3)), page, Color.Gray.PackedValue, 1, "Friends List"); y += (_buttonHeight >> 3) + (diffy >> 3); //The FRIENDLIST is created here, but only for dimensional and positioning handling, the list is populated later on - _friendListArea = new ScrollArea(x, y, (WIDTH >> 1) - (_buttonHeight >> 1), HEIGHT - ((_buttonHeight * 6) + (diffy >> 1)), true); + _friendListArea = new AssistScrollArea(x, y, (WIDTH >> 1) - (_buttonHeight >> 1), HEIGHT - ((_buttonHeight * 6) + (diffy >> 1)), true); Add(_friendListArea, page); y += _friendListArea.Height + (_buttonHeight >> 3); Add(new NiceButton(x, y, (_friendListArea.Width >> 1) - buttondiffx, diffy, ButtonAction.Activate, "Remove Friend") { IsSelectable = false, ButtonParameter = (int)ButtonType.RemoveFriend }, page); @@ -1462,7 +1463,7 @@ private void BuildHotkeys(int page) Line.CreateRectangleArea(this, x, y, WIDTH - (y + _buttonWidth * 6), HEIGHT - (y + (_buttonHeight >> 3)), page, Color.Gray.PackedValue, 1, "Controllable Elements", ScriptTextBox.GRAY_HUE, FONT); x += _buttonWidth >> 3; y += _buttonHeight >> 2; - ScrollArea leftArea = new ScrollArea(x, y, WIDTH - (y + _buttonWidth * 6), HEIGHT - (y + (_buttonHeight >> 2)), true); + AssistScrollArea leftArea = new AssistScrollArea(x, y, WIDTH - (y + _buttonWidth * 6), HEIGHT - (y + (_buttonHeight >> 2)), true); Add(leftArea, page); _mainHK = CreateMultiSelection(leftArea, "Main", new string[] { "Ping", "Resyncronize", "Toggle Hotkeys", "Snapshot" }, 2, (int)ButtonType.HotKeyList, 0x93A, 0x939); _mainHK.OnOptionSelected += HotKey_OnOptionSelected; @@ -1691,7 +1692,7 @@ private void MacroHotKey_OnOptionSelected(object sender, Control c) #endregion #region Macros - private ScrollArea _macroListArea; + private AssistScrollArea _macroListArea; private NiceButton _playMacro, _recordMacro, _newMacro, _delMacro, _saveMacro; internal NiceButton PlayMacro => _playMacro; internal NiceButton RecordMacro => _recordMacro; @@ -1717,7 +1718,7 @@ private void BuildMacros(int page) Line.CreateRectangleArea(this, x, y, (WIDTH >> 2) + _buttonWidth + (_buttonWidth >> 1), HEIGHT - (y + _buttonHeight * 2) + (_buttonHeight >> 3), page, Color.Gray.PackedValue, 1, "Macro Names", ScriptTextBox.GRAY_HUE, FONT); x += _buttonWidth >> 4; y += _buttonHeight >> 2; - _macroListArea = new ScrollArea(x, y, (WIDTH >> 2) + _buttonWidth + (_buttonWidth >> 2) + (_buttonWidth >> 3), HEIGHT - (y + _buttonHeight * 2), true); + _macroListArea = new AssistScrollArea(x, y, (WIDTH >> 2) + _buttonWidth + (_buttonWidth >> 2) + (_buttonWidth >> 3), HEIGHT - (y + _buttonHeight * 2), true); Add(_macroListArea, page); y += _macroListArea.Height + (_buttonHeight >> 3); x = _buttonWidth >> 3; @@ -1812,7 +1813,7 @@ private void OnHotKeyChanged(ref string macroselected, AssistHotkeyBox box, uint HotKeys.AddHotkey(vkey, new HotKeyOpts(box.PassToCUO, macroselected), box, ref macroselected, this); } - private class AreaContainer : ScrollArea + private class AreaContainer : AssistScrollArea { internal StbTextBox _textBox; internal AreaContainer(int x, int y, int w, int h, StbTextBox box) : base(x, y, w, h, true) @@ -1957,11 +1958,11 @@ private void BuildSkills(int page) _newBuySellList, _removeBuySellList, _removeBuySellItem, _insertBuySellItem; private Combobox _BuySellCombo; internal NiceButton PlayOrganizer { get { return _playOrganizer; } } - private ScrollArea _autolootArea, //autoloot + private AssistScrollArea _autolootArea, //autoloot _dressItemsArea, _dressListsArea,//dress _organizerListArea,//organizer _vendorsListArea;//vendors - private ScrollArea _organizerItems,//organizer + private AssistScrollArea _organizerItems,//organizer _scavengerItems,//scavenger _vendorsItemsArea;//vendors private int[] _organizerItemsWidth, _scavengerItemsWidth, _BuySellItemsWidth; @@ -2030,7 +2031,7 @@ private void BuildAgents() Line[] l = Line.CreateRectangleArea(this, x, y, _autolootContainer.Width + _autolootContainer.X + (buttondiffx >> 2), HEIGHT - (_disableInGuardZone.Y + (_buttonHeight * 3)) , page, Color.Gray.PackedValue, 1, "Loot Items", ScriptTextBox.GRAY_HUE, FONT); y += (buttondiffy >> 1); x += 2; - _autolootArea = new ScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); + _autolootArea = new AssistScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); Add(_autolootArea, page); y = l[2].Y + (_buttonHeight >> 2); x -= 2; @@ -2066,7 +2067,7 @@ private void BuildAgents() Line[] l = Line.CreateRectangleArea(this, x, y, _buttonWidth * 7, HEIGHT - (_buttonHeight * 5), page, Color.Gray.PackedValue, 1, "Dress Lists", ScriptTextBox.GRAY_HUE, FONT); y += (buttondiffy >> 1); x += 2; - _dressListsArea = new ScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); + _dressListsArea = new AssistScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); Add(_dressListsArea, page); x -= 2; y += l[0].Height; @@ -2079,7 +2080,7 @@ private void BuildAgents() l = Line.CreateRectangleArea(this, x, y, WIDTH - (x + _buttonWidth * 4), HEIGHT - (_buttonHeight * 4), page, Color.Gray.PackedValue, 1, "Dress Items", ScriptTextBox.GRAY_HUE, FONT); y += (buttondiffy >> 1); x += 2; - _dressItemsArea = new ScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); + _dressItemsArea = new AssistScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); Add(_dressItemsArea, page); y = l[2].Y + (_buttonHeight >> 2); x -= 2; @@ -2134,7 +2135,7 @@ private void BuildAgents() Line[] l = Line.CreateRectangleArea(this, x, y, _buttonWidth * 5 + (_buttonWidth >> 1), HEIGHT - (_buttonHeight * 6), page, Color.Gray.PackedValue, 1, "Organizer Lists", ScriptTextBox.GRAY_HUE, FONT); y += (buttondiffy >> 1); x += 2; - _organizerListArea = new ScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); + _organizerListArea = new AssistScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true); Add(_organizerListArea, page); x -= 2; y += l[0].Height; @@ -2165,7 +2166,7 @@ private void BuildAgents() _insertOrganizerItem.TextLabel.Hue = ScriptTextBox.RED_HUE; x += l[2].Width - 1; l = Line.CreateRectangleArea(this, x, l[0].Y, 16, l[0].Height, page, Color.Gray.PackedValue, 1, "", ScriptTextBox.GRAY_HUE, FONT); - Add(_organizerItems = new ScrollArea(tmpx, y + (buttondiffy >> 1), (_organizerItemsWidth.Sum() + (2 * _organizerItemsWidth.Length) + 12), l[0].Height - ((buttondiffy >> 2) * 3), true), page); + Add(_organizerItems = new AssistScrollArea(tmpx, y + (buttondiffy >> 1), (_organizerItemsWidth.Sum() + (2 * _organizerItemsWidth.Length) + 12), l[0].Height - ((buttondiffy >> 2) * 3), true), page); break; } #endregion @@ -2200,7 +2201,7 @@ private void BuildAgents() _scavengerItemsWidth[2] = l[2].Width - 2; x += l[2].Width - 1; l = Line.CreateRectangleArea(this, x, y, 16, l[0].Height, page, Color.Gray.PackedValue, 1, "", ScriptTextBox.GRAY_HUE, FONT); - Add(_scavengerItems = new ScrollArea(temp, y + (buttondiffy >> 1), (_scavengerItemsWidth.Sum() + (2 * _scavengerItemsWidth.Length) + 12), l[0].Height - ((buttondiffy >> 2) * 3), true), page); + Add(_scavengerItems = new AssistScrollArea(temp, y + (buttondiffy >> 1), (_scavengerItemsWidth.Sum() + (2 * _scavengerItemsWidth.Length) + 12), l[0].Height - ((buttondiffy >> 2) * 3), true), page); break; } #endregion @@ -2219,7 +2220,7 @@ private void BuildAgents() Line[] l = Line.CreateRectangleArea(this, x, y, _buttonWidth * 5 + (_buttonWidth >> 1), HEIGHT - (_buttonHeight * 5), page, Color.Gray.PackedValue, 1, "Lists", ScriptTextBox.GRAY_HUE, FONT); y += (buttondiffy >> 1); x += 2; - Add(_vendorsListArea = new ScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true), page); + Add(_vendorsListArea = new AssistScrollArea(x, y, l[2].Width - 6, l[0].Height - ((buttondiffy >> 2) * 3), true), page); x -= 2; y += l[0].Height; Add(_removeBuySellList = new NiceButton(x + 4, y - 4, (l[2].Width >> 2) + (_buttonWidth >> 1), _buttonHeight, ButtonAction.Activate, "Remove", (int)ButtonType.RemoveBuySellList, TEXT_ALIGN_TYPE.TS_CENTER) { IsSelectable = false, ButtonParameter = (int)ButtonType.RemoveBuySellList }, page); @@ -2250,7 +2251,7 @@ private void BuildAgents() _insertBuySellItem.TextLabel.Hue = ScriptTextBox.RED_HUE; x += l[2].Width - 1; l = Line.CreateRectangleArea(this, x, l[0].Y, 16, l[0].Height, page, Color.Gray.PackedValue, 1, "", ScriptTextBox.GRAY_HUE, FONT); - Add(_vendorsItemsArea = new ScrollArea(tmpx, y + (buttondiffy >> 1), (_BuySellItemsWidth.Sum() + (2 * _BuySellItemsWidth.Length) + 12), l[0].Height - ((buttondiffy >> 2) * 3), true), page); + Add(_vendorsItemsArea = new AssistScrollArea(tmpx, y + (buttondiffy >> 1), (_BuySellItemsWidth.Sum() + (2 * _BuySellItemsWidth.Length) + 12), l[0].Height - ((buttondiffy >> 2) * 3), true), page); break; } #endregion @@ -3709,7 +3710,7 @@ public override void Dispose() base.Dispose(); } - private AssistCheckbox CreateCheckBox(ScrollArea area, string text, bool ischecked, int x, int y, ushort inactiveimg = 0x00D2, ushort activeimg = 0x00D3) + private AssistCheckbox CreateCheckBox(AssistScrollArea area, string text, bool ischecked, int x, int y, ushort inactiveimg = 0x00D2, ushort activeimg = 0x00D3) { AssistCheckbox box = new AssistCheckbox(inactiveimg, activeimg, text, FONT, ScriptTextBox.GRAY_HUE, true) { @@ -3741,13 +3742,14 @@ private ClickableColorBox CreateClickableColorBox(int x, int y, ushort hue, stri if (hue != 0xFFFF) color = HuesLoader.Instance.GetPolygoneColor(12, hue); - ClickableColorBox box = new ClickableColorBox(x, y, 13, 14, hue, color); + // MobileUO: TODO: ClickableColorBox dropped a parameter in CUO 0.1.9.0, this may need to be revisited + ClickableColorBox box = new ClickableColorBox(x, y, 13, 14, hue/*, color*/); Add(box, page); Add(new Label(text, true, ScriptTextBox.GRAY_HUE) { X = x + box.Width * 2, Y = y }, page); return box; } - private NiceButtonStbText CreateTextSelection(ScrollArea area, int y, int group, int index, object tag, TEXT_ALIGN_TYPE align, int[] width, byte labelentry = 0, bool hascheckbox = false, params string[] text) + private NiceButtonStbText CreateTextSelection(AssistScrollArea area, int y, int group, int index, object tag, TEXT_ALIGN_TYPE align, int[] width, byte labelentry = 0, bool hascheckbox = false, params string[] text) { if (width.Length != text.Length) new Exception($"zero text parameters or width Length ({width.Length}) is not equal to text Length ({text.Length}) - parameters must be equal in length or arrays"); @@ -3757,14 +3759,14 @@ private NiceButtonStbText CreateTextSelection(ScrollArea area, int y, int group, return but; } - private NiceButton CreateSelection(ScrollArea area, string text, int y, int group, int index, object tag) + private NiceButton CreateSelection(AssistScrollArea area, string text, int y, int group, int index, object tag) { NiceButton but = new NiceButton(0, y, area.Width - (_buttonHeight >> 1), _buttonHeight - (_buttonHeight >> 2), ButtonAction.Activate, text, group) { ButtonParameter = index, Tag = tag }; area.Add(but); return but; } - private AssistMultiSelectionShrinkbox CreateMultiSelection(ScrollArea area, string text, string[] items, int y, int group, ushort buttonimg, ushort pressbuttonimg) + private AssistMultiSelectionShrinkbox CreateMultiSelection(AssistScrollArea area, string text, string[] items, int y, int group, ushort buttonimg, ushort pressbuttonimg) { AssistMultiSelectionShrinkbox msb = new AssistMultiSelectionShrinkbox(0, y, area.Width - (_buttonWidth >> 1), text, items, ScriptTextBox.GRAY_HUE, true, FONT, group, buttonimg, pressbuttonimg); area.Add(msb); @@ -3901,8 +3903,9 @@ internal bool HighlightCurrentTarget internal ushort HLTargetHue => _highlightCurrentTarget.IsChecked ? _highlightCurrentTargetHue.Hue : (ushort)0; internal ushort HighlightCurrentTargetHue { + // MobileUO: TODO: ClickableColorBox dropped SetColor in CUO 0.1.9.0, this may need to be revisited get => _highlightCurrentTargetHue.Hue; - set => _highlightCurrentTargetHue.SetColor(value, HuesLoader.Instance.GetPolygoneColor(12, value)); + set => _highlightCurrentTargetHue.Hue = value; //SetColor(value, HuesLoader.Instance.GetPolygoneColor(12, value)); } internal bool BlockInvalidHeal diff --git a/Assets/Scripts/Assistant/InternalUI/AssistCheckbox.cs b/Assets/Scripts/Assistant/InternalUI/AssistCheckbox.cs index bedf522a2..dd3d40c77 100644 --- a/Assets/Scripts/Assistant/InternalUI/AssistCheckbox.cs +++ b/Assets/Scripts/Assistant/InternalUI/AssistCheckbox.cs @@ -34,7 +34,7 @@ internal class AssistCheckbox : Control private const int INACTIVE = 0; private const int ACTIVE = 1; private readonly RenderedText _text; - private readonly UOTexture32[] _textures = new UOTexture32[2]; + private readonly UOTexture[] _textures = new UOTexture[2]; private bool _isChecked; public AssistCheckbox(ushort inactive, ushort active, string text = "", byte font = 0, ushort color = 0, bool isunicode = true, int maxWidth = 0) @@ -49,7 +49,7 @@ public AssistCheckbox(ushort inactive, ushort active, string text = "", byte fon return; } - UOTexture32 t = _textures[INACTIVE]; + UOTexture t = _textures[INACTIVE]; Width = t.Width; _text = RenderedText.Create(text, color, font, isunicode, maxWidth: maxWidth); @@ -105,7 +105,7 @@ public override void Update(double totalMS, double frameMS) { for (int i = 0; i < _textures.Length; i++) { - UOTexture32 t = _textures[i]; + UOTexture t = _textures[i]; if (t != null) t.Ticks = (long) totalMS; @@ -122,7 +122,7 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) ResetHueVector(); bool ok = base.Draw(batcher, x, y); - batcher.Draw2D(IsChecked ? _textures[ACTIVE] : _textures[INACTIVE], x, y, ref _hueVector); + batcher.Draw2D(IsChecked ? _textures[ACTIVE] : _textures[INACTIVE], x, y, ref HueVector); _text.Draw(batcher, x + _textures[ACTIVE].Width + 2, y); return ok; diff --git a/Assets/Scripts/Assistant/InternalUI/AssistMultiSelectionShrinkBox.cs b/Assets/Scripts/Assistant/InternalUI/AssistMultiSelectionShrinkBox.cs index 53d04ba7c..f3af6c84d 100644 --- a/Assets/Scripts/Assistant/InternalUI/AssistMultiSelectionShrinkBox.cs +++ b/Assets/Scripts/Assistant/InternalUI/AssistMultiSelectionShrinkBox.cs @@ -129,7 +129,7 @@ internal bool NestBox(AssistMultiSelectionShrinkbox box) while (c != null) { - if (c is ScrollArea area) + if (c is AssistScrollArea area) { _arrow.IsVisible = true; _nestedBoxes.Add(box); @@ -255,7 +255,7 @@ private void Selection_MouseClick(object sender, MouseEventArgs e) private void OnGroupSelection() { - if (Parent != null && Parent.Parent is ScrollArea area) + if (Parent != null && Parent.Parent is AssistScrollArea area) { foreach (Control sai in area.Children) { diff --git a/Assets/Scripts/Assistant/InternalUI/AssistScrollArea.cs b/Assets/Scripts/Assistant/InternalUI/AssistScrollArea.cs new file mode 100644 index 000000000..2f6bc3af2 --- /dev/null +++ b/Assets/Scripts/Assistant/InternalUI/AssistScrollArea.cs @@ -0,0 +1,277 @@ +#region license + +// Copyright (C) 2020 ClassicUO Development Community on Github +// +// This project is an alternative client for the game Ultima Online. +// The goal of this is to develop a lightweight client considering +// new technologies. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +#endregion + +using System.Linq; +using ClassicUO.Input; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; + +// MobileUO: using the old version of this class so that Assistant UI isn't broken +// https://raw.githubusercontent.com/ClassicUO/ClassicUO/b2ca71d4a09c839b279841c5a58cbf2d20ccb242/src/Game/UI/Controls/ScrollArea.cs +// TODO: re-write assistant UI to use newer method/behavior of scroll area +namespace ClassicUO.Game.UI.Controls +{ + //internal enum ScrollbarBehaviour + //{ + // ShowWhenDataExceedFromView, + // ShowAlways + //} + + internal class AssistScrollArea : Control + { + private bool _isNormalScroll; + private readonly ScrollBarBase _scrollBar; + + public AssistScrollArea(int x, int y, int w, int h, bool normalScrollbar, int scroll_max_height = -1) + { + X = x; + Y = y; + Width = w; + Height = h; + _isNormalScroll = normalScrollbar; + + if (normalScrollbar) + { + _scrollBar = new ScrollBar(Width - 14, 0, Height); + } + else + { + _scrollBar = new ScrollFlag + { + X = Width - 19, Height = h + }; + + Width += 15; + } + + ScrollMaxHeight = scroll_max_height; + + _scrollBar.MinValue = 0; + _scrollBar.MaxValue = scroll_max_height >= 0 ? scroll_max_height : Height; + //Add((Control)_scrollBar); + + _scrollBar.Parent = this; + + AcceptMouseInput = true; + WantUpdateSize = false; + CanMove = true; + ScrollbarBehaviour = ScrollbarBehaviour.ShowWhenDataExceedFromView; + } + + public int ScrollMaxHeight { get; set; } = -1; + public Rectangle ScissorRectangle; + + + public ScrollbarBehaviour ScrollbarBehaviour; + + public override void Update(double totalMS, double frameMS) + { + base.Update(totalMS, frameMS); + + CalculateScrollBarMaxValue(); + + if (ScrollbarBehaviour == ScrollbarBehaviour.ShowAlways) + { + _scrollBar.IsVisible = true; + } + else if (ScrollbarBehaviour == ScrollbarBehaviour.ShowWhenDataExceedFromView) + { + _scrollBar.IsVisible = _scrollBar.MaxValue > _scrollBar.MinValue; + } + } + + public void Scroll(bool isup) + { + if (isup) + { + _scrollBar.Value -= _scrollBar.ScrollStep; + } + else + { + _scrollBar.Value += _scrollBar.ScrollStep; + } + } + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + Control scrollbar = Children[0]; + scrollbar.Draw(batcher, x + scrollbar.X, y + scrollbar.Y); + + Rectangle scissor = ScissorStack.CalculateScissors(Matrix.Identity, x + ScissorRectangle.X, y + ScissorRectangle.Y, Width - 14 + ScissorRectangle.Width, Height + ScissorRectangle.Height); + + if (ScissorStack.PushScissors(batcher.GraphicsDevice, scissor)) + { + batcher.EnableScissorTest(true); + int height = ScissorRectangle.Y; + + for (int i = 1; i < Children.Count; i++) + { + Control child = Children[i]; + + if (!child.IsVisible) + { + continue; + } + + child.Y = height - _scrollBar.Value; + + if (height + child.Height <= _scrollBar.Value) + { + // do nothing + } + else + { + child.Draw(batcher, x + child.X, y + child.Y); + } + + height += child.Height; + } + + batcher.EnableScissorTest(false); + ScissorStack.PopScissors(batcher.GraphicsDevice); + } + + return true; + } + + protected override void OnMouseWheel(MouseEventType delta) + { + switch (delta) + { + case MouseEventType.WheelScrollUp: + _scrollBar.Value -= _scrollBar.ScrollStep; + + break; + + case MouseEventType.WheelScrollDown: + _scrollBar.Value += _scrollBar.ScrollStep; + + break; + } + } + + public override void Remove(Control c) + { + if (c is ScrollAreaItem) + { + base.Remove(c); + } + else + { + // Try to find the wrapped control + ScrollAreaItem wrapper = Children.OfType() + .FirstOrDefault(o => o.Children.Contains(c)); + + base.Remove(wrapper); + } + } + + public override void Add(Control c, int page = 0) + { + ScrollAreaItem item = new ScrollAreaItem + { + CanMove = true + }; + + item.Add(c); + base.Add(item, page); + } + + public void Add(ScrollAreaItem c, int page = 0) + { + c.CanMove = true; + base.Add(c, page); + } + + public override void Clear() + { + for (int i = 1; i < Children.Count; i++) + { + Children[i] + .Dispose(); + } + } + + private void CalculateScrollBarMaxValue() + { + _scrollBar.Height = ScrollMaxHeight >= 0 ? ScrollMaxHeight : Height; + bool maxValue = _scrollBar.Value == _scrollBar.MaxValue && _scrollBar.MaxValue != 0; + int height = ScissorRectangle.Y; + + for (int i = 1; i < Children.Count; i++) + { + if (Children[i] + .IsVisible) + { + height += Children[i] + .Height; + } + } + + height -= _scrollBar.Height; + + height -= ScissorRectangle.Y + ScissorRectangle.Height; + + //if (_isNormalScroll) + // height += 40; + + if (height > 0) + { + _scrollBar.MaxValue = height; + + if (maxValue) + { + _scrollBar.Value = _scrollBar.MaxValue; + } + } + else + { + _scrollBar.MaxValue = 0; + _scrollBar.Value = 0; + } + } + } + + internal class ScrollAreaItem : Control + { + public override void Update(double totalMS, double frameMS) + { + base.Update(totalMS, frameMS); + + if (Children.Count == 0) + { + Dispose(); + } + + WantUpdateSize = true; + } + + public override void OnPageChanged() + { + int maxheight = Children.Count > 0 ? Children.Sum(o => o.IsVisible ? o.Y < 0 ? o.Height + o.Y : o.Height : 0) : 0; + IsVisible = maxheight > 0; + Height = maxheight; + Parent?.OnPageChanged(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/Assistant/InternalUI/AssistScrollArea.cs.meta b/Assets/Scripts/Assistant/InternalUI/AssistScrollArea.cs.meta new file mode 100644 index 000000000..2904ad8be --- /dev/null +++ b/Assets/Scripts/Assistant/InternalUI/AssistScrollArea.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 29899d9d7e786cb4c8d4f5ab2ac96f3f \ No newline at end of file diff --git a/Assets/Scripts/Assistant/InternalUI/NiceButtonStbText.cs b/Assets/Scripts/Assistant/InternalUI/NiceButtonStbText.cs index f33cb7fe2..c137c1e96 100644 --- a/Assets/Scripts/Assistant/InternalUI/NiceButtonStbText.cs +++ b/Assets/Scripts/Assistant/InternalUI/NiceButtonStbText.cs @@ -137,7 +137,7 @@ public bool IsSelected internal static NiceButtonStbText GetSelected(Control p, int group) { - IEnumerable list = p is ScrollArea ? p.FindControls().SelectMany(s => s.Children.OfType()) : p.FindControls(); + IEnumerable list = p is AssistScrollArea ? p.FindControls().SelectMany(s => s.Children.OfType()) : p.FindControls(); foreach (var b in list) if (b._groupnumber == group && b.IsSelected) @@ -175,11 +175,11 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) if (IsSelected) { ResetHueVector(); - ShaderHuesTraslator.GetHueVector(ref _hueVector, 0, false, Alpha); + ShaderHueTranslator.GetHueVector(ref HueVector, 0, false, Alpha); if (_SelectedArea > 0) - batcher.Draw2D(_texture, x + TextBoxes[_SelectedArea - 1].X, y, 0, 0, TextBoxes[_SelectedArea - 1].Width, Height, ref _hueVector); + batcher.Draw2D(_texture, x + TextBoxes[_SelectedArea - 1].X, y, 0, 0, TextBoxes[_SelectedArea - 1].Width, Height, ref HueVector); else - batcher.Draw2D(_texture, x + TextLabel.X, y, 0, 0, TextLabel.Width, Height, ref _hueVector); + batcher.Draw2D(_texture, x + TextLabel.X, y, 0, 0, TextLabel.Width, Height, ref HueVector); } return base.Draw(batcher, x, y); diff --git a/Assets/Scripts/Assistant/Player.cs b/Assets/Scripts/Assistant/Player.cs index 7ffc47017..d6246355f 100644 --- a/Assets/Scripts/Assistant/Player.cs +++ b/Assets/Scripts/Assistant/Player.cs @@ -738,7 +738,7 @@ internal void Say(int hue, string msg, MessageType msgtype = MessageType.Regular internal void Say(string msg) { - Say(ProfileManager.Current.SpeechHue, msg); + Say(ProfileManager.CurrentProfile.SpeechHue, msg); } internal class GumpData diff --git a/Assets/Scripts/Assistant/Scripts/Commands.cs b/Assets/Scripts/Assistant/Scripts/Commands.cs index 63daf2f33..b8d68b73d 100644 --- a/Assets/Scripts/Assistant/Scripts/Commands.cs +++ b/Assets/Scripts/Assistant/Scripts/Commands.cs @@ -1953,26 +1953,34 @@ private static bool ClickScreen(string command, Argument[] args, bool quiet, boo { if (args.Length > 3 && args[3].AsString() == "right") { - UIManager.OnRightMouseDoubleClick(); - UIManager.OnRightMouseButtonUp(); + //UIManager.OnRightMouseDoubleClick(); + //UIManager.OnRightMouseButtonUp(); + UIManager.OnMouseDoubleClick(MouseButtonType.Right); + UIManager.OnMouseButtonUp(MouseButtonType.Right); } else { - UIManager.OnLeftMouseDoubleClick(); - UIManager.OnLeftMouseButtonUp(); + //UIManager.OnLeftMouseDoubleClick(); + //UIManager.OnLeftMouseButtonUp(); + UIManager.OnMouseDoubleClick(MouseButtonType.Left); + UIManager.OnMouseButtonUp(MouseButtonType.Left); } } else { if (args.Length > 3 && args[3].AsString() == "right") { - UIManager.OnRightMouseButtonDown(); - UIManager.OnRightMouseButtonUp(); + //UIManager.OnRightMouseButtonDown(); + //UIManager.OnRightMouseButtonUp(); + UIManager.OnMouseButtonDown(MouseButtonType.Right); + UIManager.OnMouseButtonUp(MouseButtonType.Right); } else { - UIManager.OnLeftMouseButtonDown(); - UIManager.OnLeftMouseButtonUp(); + //UIManager.OnLeftMouseButtonDown(); + //UIManager.OnLeftMouseButtonUp(); + UIManager.OnMouseButtonDown(MouseButtonType.Left); + UIManager.OnMouseButtonUp(MouseButtonType.Left); } } } diff --git a/Assets/Scripts/Assistant/XmlFileParser.cs b/Assets/Scripts/Assistant/XmlFileParser.cs index d731997d4..914c38935 100644 --- a/Assets/Scripts/Assistant/XmlFileParser.cs +++ b/Assets/Scripts/Assistant/XmlFileParser.cs @@ -228,7 +228,7 @@ internal static void LoadConfig(FileInfo info, AssistantGump gump) } FileInfo profileinfo = new FileInfo(Path.Combine(Profile.ProfilePath, profile)); - if (profileinfo.Exists && SerialHelper.IsMobile(serial) && World.Player.Serial == serial && ProfileManager.Current.Username == account) + if (profileinfo.Exists && SerialHelper.IsMobile(serial) && World.Player.Serial == serial && ProfileManager.CurrentProfile.Username == account) { gump.LastProfile = profileinfo.Name; break; diff --git a/Assets/Scripts/AssistantHotkeyButtonGump.cs b/Assets/Scripts/AssistantHotkeyButtonGump.cs index 685764d8a..b8192ae18 100644 --- a/Assets/Scripts/AssistantHotkeyButtonGump.cs +++ b/Assets/Scripts/AssistantHotkeyButtonGump.cs @@ -40,7 +40,7 @@ public AssistantHotkeyButtonGump() : base(0, 0) AnchorType = ANCHOR_TYPE.SPELL; } - public override GUMP_TYPE GumpType => GUMP_TYPE.GT_ASSISTANTHOTKEYBUTTON; + public override GumpType GumpType => GumpType.AssistantHotkeyButton; private void BuildGump() { @@ -55,20 +55,20 @@ private void BuildGump() label.Y = (Height >> 1) - (label.Height >> 1); Add(label); - backgroundTexture = Texture2DCache.GetTexture(new Color(30, 30, 30)); + backgroundTexture = SolidColorTextureCache.GetTexture(new Color(30, 30, 30)); } protected override void OnMouseEnter(int x, int y) { label.Hue = 53; - backgroundTexture = Texture2DCache.GetTexture(Color.DimGray); + backgroundTexture = SolidColorTextureCache.GetTexture(Color.DimGray); base.OnMouseEnter(x, y); } protected override void OnMouseExit(int x, int y) { label.Hue = 1001; - backgroundTexture = Texture2DCache.GetTexture(new Color(30, 30, 30)); + backgroundTexture = SolidColorTextureCache.GetTexture(new Color(30, 30, 30)); base.OnMouseExit(x, y); } @@ -76,9 +76,9 @@ protected override void OnMouseUp(int x, int y, MouseButtonType button) { base.OnMouseUp(x, y, MouseButtonType.Left); - Point offset = Mouse.LDroppedOffset; + Point offset = Mouse.LDragOffset; - if (ProfileManager.Current.CastSpellsByOneClick && button == MouseButtonType.Left && !Keyboard.Alt && Math.Abs(offset.X) < 5 && Math.Abs(offset.Y) < 5) + if (ProfileManager.CurrentProfile.CastSpellsByOneClick && button == MouseButtonType.Left && !Keyboard.Alt && Math.Abs(offset.X) < 5 && Math.Abs(offset.Y) < 5) { RunHotkey(); } @@ -86,7 +86,7 @@ protected override void OnMouseUp(int x, int y, MouseButtonType button) protected override bool OnMouseDoubleClick(int x, int y, MouseButtonType button) { - if (ProfileManager.Current.CastSpellsByOneClick || button != MouseButtonType.Left) + if (ProfileManager.CurrentProfile.CastSpellsByOneClick || button != MouseButtonType.Left) return false; RunHotkey(); @@ -104,12 +104,12 @@ private void RunHotkey() public override bool Draw(UltimaBatcher2D batcher, int x, int y) { ResetHueVector(); - _hueVector.Z = 0.1f; + HueVector.Z = 0.1f; - batcher.Draw2D(backgroundTexture, x, y, Width, Height, ref _hueVector); + batcher.Draw2D(backgroundTexture, x, y, Width, Height, ref HueVector); - _hueVector.Z = 0; - batcher.DrawRectangle(Texture2DCache.GetTexture(Color.Gray), x, y, Width, Height, ref _hueVector); + HueVector.Z = 0; + batcher.DrawRectangle(SolidColorTextureCache.GetTexture(Color.Gray), x, y, Width, Height, ref HueVector); base.Draw(batcher, x, y); return true; diff --git a/Assets/Scripts/AssistantMacroButtonGump.cs b/Assets/Scripts/AssistantMacroButtonGump.cs index cc281b5ad..e5bd2a25c 100644 --- a/Assets/Scripts/AssistantMacroButtonGump.cs +++ b/Assets/Scripts/AssistantMacroButtonGump.cs @@ -40,7 +40,7 @@ public AssistantMacroButtonGump() : base(0, 0) AnchorType = ANCHOR_TYPE.SPELL; } - public override GUMP_TYPE GumpType => GUMP_TYPE.GT_ASSISTANTMACROBUTTON; + public override GumpType GumpType => GumpType.AssistantMacroButton; private void BuildGump() { @@ -55,20 +55,20 @@ private void BuildGump() label.Y = (Height >> 1) - (label.Height >> 1); Add(label); - backgroundTexture = Texture2DCache.GetTexture(new Color(30, 30, 30)); + backgroundTexture = SolidColorTextureCache.GetTexture(new Color(30, 30, 30)); } protected override void OnMouseEnter(int x, int y) { label.Hue = 53; - backgroundTexture = Texture2DCache.GetTexture(Color.DimGray); + backgroundTexture = SolidColorTextureCache.GetTexture(Color.DimGray); base.OnMouseEnter(x, y); } protected override void OnMouseExit(int x, int y) { label.Hue = 1001; - backgroundTexture = Texture2DCache.GetTexture(new Color(30, 30, 30)); + backgroundTexture = SolidColorTextureCache.GetTexture(new Color(30, 30, 30)); base.OnMouseExit(x, y); } @@ -76,9 +76,9 @@ protected override void OnMouseUp(int x, int y, MouseButtonType button) { base.OnMouseUp(x, y, MouseButtonType.Left); - Point offset = Mouse.LDroppedOffset; + Point offset = Mouse.LDragOffset; - if (ProfileManager.Current.CastSpellsByOneClick && button == MouseButtonType.Left && !Keyboard.Alt && Math.Abs(offset.X) < 5 && Math.Abs(offset.Y) < 5) + if (ProfileManager.CurrentProfile.CastSpellsByOneClick && button == MouseButtonType.Left && !Keyboard.Alt && Math.Abs(offset.X) < 5 && Math.Abs(offset.Y) < 5) { RunMacro(); } @@ -86,7 +86,7 @@ protected override void OnMouseUp(int x, int y, MouseButtonType button) protected override bool OnMouseDoubleClick(int x, int y, MouseButtonType button) { - if (ProfileManager.Current.CastSpellsByOneClick || button != MouseButtonType.Left) + if (ProfileManager.CurrentProfile.CastSpellsByOneClick || button != MouseButtonType.Left) return false; RunMacro(); @@ -104,12 +104,12 @@ private void RunMacro() public override bool Draw(UltimaBatcher2D batcher, int x, int y) { ResetHueVector(); - _hueVector.Z = 0.1f; + HueVector.Z = 0.1f; - batcher.Draw2D(backgroundTexture, x, y, Width, Height, ref _hueVector); + batcher.Draw2D(backgroundTexture, x, y, Width, Height, ref HueVector); - _hueVector.Z = 0; - batcher.DrawRectangle(Texture2DCache.GetTexture(Color.Gray), x, y, Width, Height, ref _hueVector); + HueVector.Z = 0; + batcher.DrawRectangle(SolidColorTextureCache.GetTexture(Color.Gray), x, y, Width, Height, ref HueVector); base.Draw(batcher, x, y); return true; diff --git a/Assets/Scripts/ClassicUO/src/CUOEnviroment.cs b/Assets/Scripts/ClassicUO/src/CUOEnviroment.cs index 44da46212..1ce01c17e 100644 --- a/Assets/Scripts/ClassicUO/src/CUOEnviroment.cs +++ b/Assets/Scripts/ClassicUO/src/CUOEnviroment.cs @@ -1,22 +1,33 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; @@ -26,7 +37,7 @@ namespace ClassicUO { - static class CUOEnviroment + internal static class CUOEnviroment { public static Thread GameThread; public static float DPIScaleFactor = 1.0f; @@ -39,13 +50,19 @@ static class CUOEnviroment public static uint CurrentRefreshRate; public static bool SkipLoginScreen; public static bool IsOutlands; + public static bool PacketLog; + public static bool NoServerPing; - public static readonly bool IsUnix = Environment.OSVersion.Platform != PlatformID.Win32NT && - Environment.OSVersion.Platform != PlatformID.Win32Windows && - Environment.OSVersion.Platform != PlatformID.Win32S && - Environment.OSVersion.Platform != PlatformID.WinCE; + public static readonly bool IsUnix = Environment.OSVersion.Platform != PlatformID.Win32NT && Environment.OSVersion.Platform != PlatformID.Win32Windows && Environment.OSVersion.Platform != PlatformID.Win32S && Environment.OSVersion.Platform != PlatformID.WinCE; - public static readonly Version Version = Assembly.GetExecutingAssembly().GetName().Version; - public static string ExecutablePath = Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); + // MobileUO: hard coded CUO version MobileUO is based on + public static readonly Version Version = new Version(0, 1, 10, 0);//Assembly.GetExecutingAssembly().GetName().Version; + // MobileUO: removed readonly + public static string ExecutablePath = +#if NETFRAMEWORK + Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location); +#else + Environment.CurrentDirectory; +#endif } -} +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Client.cs b/Assets/Scripts/ClassicUO/src/Client.cs index 094a76418..7cbccd0af 100644 --- a/Assets/Scripts/ClassicUO/src/Client.cs +++ b/Assets/Scripts/ClassicUO/src/Client.cs @@ -1,55 +1,65 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion +using System; +using System.Diagnostics; +using System.IO; using ClassicUO.Configuration; using ClassicUO.Data; using ClassicUO.Game.Data; using ClassicUO.IO; -using ClassicUO.Utility.Logging; -using System; -using System.Diagnostics; -using System.IO; using ClassicUO.Network; +using ClassicUO.Network.Encryption; +using ClassicUO.Resources; +using ClassicUO.Utility.Logging; using ClassicUO.Utility.Platforms; - using SDL2; -using ClassicUO.Renderer; -using ClassicUO.IO.Resources; -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; -using ClassicUO.Game; namespace ClassicUO { - static class Client + internal static class Client { - public static ClientVersion Version { get; private set; } + public static ClientVersion Version { get; private set; } public static ClientFlags Protocol { get; set; } public static string ClientPath { get; private set; } public static bool IsUOPInstallation { get; private set; } public static bool UseUOPGumps { get; set; } + // MobileUO: removed private setter public static GameController Game { get; set; } + // MobileUO: added variable public static event Action SceneChanged; + // MobileUO: added method public static void InvokeSceneChanged() { SceneChanged?.Invoke(); @@ -59,14 +69,35 @@ public static void Run() { Debug.Assert(Game == null); + Load(); + Log.Trace("Running game..."); + using (Game = new GameController()) - //Game = new GameController(); { // https://github.com/FNA-XNA/FNA/wiki/7:-FNA-Environment-Variables#fna_graphics_enable_highdpi CUOEnviroment.IsHighDPI = Environment.GetEnvironmentVariable("FNA_GRAPHICS_ENABLE_HIGHDPI") == "1"; - Game.Run(); + + if (CUOEnviroment.IsHighDPI) + { + Log.Trace("HIGH DPI - ENABLED"); + } + + Log.Trace("Loading plugins..."); + + foreach (string p in Settings.GlobalSettings.Plugins) + { + Plugin.Create(p); + } + + Log.Trace("Done!"); + + // MobileUO: commented out + //UoAssist.Start(); + + Game.Run(); } + Log.Trace("Exiting game..."); } @@ -76,10 +107,8 @@ public static void ShowErrorMessage(string msg) } - public static void Load() + private static void Load() { - Log.Trace(">>>>>>>>>>>>> Loading >>>>>>>>>>>>>"); - string clientPath = Settings.GlobalSettings.UltimaOnlineDirectory; Log.Trace($"Ultima Online installation folder: {clientPath}"); @@ -97,7 +126,8 @@ public static void Load() if (!Directory.Exists(clientPath)) { Log.Error("Invalid client directory: " + clientPath); - ShowErrorMessage($"'{clientPath}' is not a valid UO directory"); + ShowErrorMessage(string.Format(ResErrorMessages.ClientPathIsNotAValidUODirectory, clientPath)); + throw new InvalidClientDirectory($"'{clientPath}' is not a valid directory"); } @@ -107,11 +137,11 @@ public static void Load() Log.Warn($"Client version [{clientVersionText}] is invalid, let's try to read the client.exe"); // mmm something bad happened, try to load from client.exe - if (!ClientVersionHelper.TryParseFromFile(Path.Combine(clientPath, "client.exe"), out clientVersionText) || - !ClientVersionHelper.IsClientVersionValid(clientVersionText, out clientVersion)) + if (!ClientVersionHelper.TryParseFromFile(Path.Combine(clientPath, "client.exe"), out clientVersionText) || !ClientVersionHelper.IsClientVersionValid(clientVersionText, out clientVersion)) { Log.Error("Invalid client version: " + clientVersionText); - ShowErrorMessage($"Impossible to define the client version.\nClient version: '{clientVersionText}'"); + ShowErrorMessage(string.Format(ResGumps.ImpossibleToDefineTheClientVersion0, clientVersionText)); + throw new InvalidClientVersion($"Invalid client version: '{clientVersionText}'"); } @@ -123,21 +153,40 @@ public static void Load() Version = clientVersion; ClientPath = clientPath; + IsUOPInstallation = Version >= ClientVersion.CV_7000 && File.Exists(UOFileManager.GetUOFilePath("MainMisc.uop")); + Protocol = ClientFlags.CF_T2A; if (Version >= ClientVersion.CV_200) + { Protocol |= ClientFlags.CF_RE; + } + if (Version >= ClientVersion.CV_300) + { Protocol |= ClientFlags.CF_TD; + } + if (Version >= ClientVersion.CV_308) + { Protocol |= ClientFlags.CF_LBR; + } + if (Version >= ClientVersion.CV_308Z) + { Protocol |= ClientFlags.CF_AOS; + } + if (Version >= ClientVersion.CV_405A) + { Protocol |= ClientFlags.CF_SE; + } + if (Version >= ClientVersion.CV_60144) + { Protocol |= ClientFlags.CF_SA; + } Log.Trace($"Client path: '{clientPath}'"); Log.Trace($"Client version: {clientVersion}"); @@ -148,9 +197,13 @@ public static void Load() UOFileManager.Load(); StaticFilters.Load(); + BuffTable.Load(); + ChairTable.Load(); + Log.Trace("Network calibration..."); PacketHandlers.Load(); //ATTENTION: you will need to enable ALSO ultimalive server-side, or this code will have absolutely no effect! + // MobileUO: commented out //UltimaLive.Enable(); PacketsTable.AdjustPacketSizeByVersion(Version); @@ -166,18 +219,6 @@ public static void Load() Settings.GlobalSettings.Encryption = (byte) EncryptionHelper.Type; } } - - Log.Trace("Done!"); - - Log.Trace("Loading plugins..."); - - foreach (var p in Settings.GlobalSettings.Plugins) - Plugin.Create(p); - Log.Trace("Done!"); - - //UoAssist.Start(); - - Log.Trace(">>>>>>>>>>>>> DONE >>>>>>>>>>>>>"); } } -} +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Configuration/Profile.cs b/Assets/Scripts/ClassicUO/src/Configuration/Profile.cs index 8b011ccc1..c98bfc7d4 100644 --- a/Assets/Scripts/ClassicUO/src/Configuration/Profile.cs +++ b/Assets/Scripts/ClassicUO/src/Configuration/Profile.cs @@ -1,39 +1,47 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.Collections.Generic; using System.IO; -using System.Runtime.Serialization; using System.Text; using System.Xml; - using ClassicUO.Game; +using ClassicUO.Game.GameObjects; using ClassicUO.Game.Managers; using ClassicUO.Game.UI.Gumps; using ClassicUO.Utility; using ClassicUO.Utility.Logging; - using Microsoft.Xna.Framework; - using TinyJson; namespace ClassicUO.Configuration @@ -41,11 +49,6 @@ namespace ClassicUO.Configuration [MatchSnakeCase] internal sealed class Profile { - public Profile() - { - - } - [JsonIgnore] public string Username { get; set; } [JsonIgnore] public string ServerName { get; set; } [JsonIgnore] public string CharacterName { get; set; } @@ -63,8 +66,10 @@ public Profile() public byte ChatFont { get; set; } = 1; public int SpeechDelay { get; set; } = 100; public bool ScaleSpeechDelay { get; set; } = true; - public bool SaveJournalToFile { get; set; } + public bool SaveJournalToFile { get; set; } = true; public bool ForceUnicodeJournal { get; set; } + public bool IgnoreAllianceMessages { get; set; } + public bool IgnoreGuildMessages { get; set; } // hues public ushort SpeechHue { get; set; } = 0x02B2; @@ -79,7 +84,7 @@ public Profile() public ushort PartyAuraHue { get; set; } = 0x0044; public ushort FriendHue { get; set; } = 0x0044; public ushort CriminalHue { get; set; } = 0x03B2; - public ushort AnimalHue { get; set; } = 0x03B2; + public ushort CanAttackHue { get; set; } = 0x03B2; public ushort EnemyHue { get; set; } = 0x0031; public ushort MurdererHue { get; set; } = 0x0023; public ushort BeneficHue { get; set; } = 0x0059; @@ -100,9 +105,11 @@ public Profile() public bool UseOldStatusGump { get; set; } public int BackpackStyle { get; set; } public bool HighlightGameObjects { get; set; } - public bool HighlightMobilesByFlags { get; set; } = true; + public bool HighlightMobilesByParalize { get; set; } = true; + public bool HighlightMobilesByPoisoned { get; set; } = true; + public bool HighlightMobilesByInvul { get; set; } = true; public bool ShowMobilesHP { get; set; } - public int MobileHPType { get; set; } // 0 = %, 1 = line, 2 = both + public int MobileHPType { get; set; } // 0 = %, 1 = line, 2 = both public int MobileHPShowWhen { get; set; } // 0 = Always, 1 - <100% public bool DrawRoofs { get; set; } = true; public bool TreeToStumps { get; set; } @@ -113,7 +120,7 @@ public Profile() public bool UseCircleOfTransparency { get; set; } public int CircleOfTransparencyRadius { get; set; } = Constants.MAX_CIRCLE_OF_TRANSPARENCY_RADIUS / 2; public int CircleOfTransparencyType { get; set; } // 0 = normal, 1 = like original client - public int VendorGumpHeight { get; set; } = 60; //original vendor gump size + public int VendorGumpHeight { get; set; } = 60; //original vendor gump size public float DefaultScale { get; set; } = 1.0f; public bool EnableMousewheelScaleZoom { get; set; } public bool SaveScaleAfterClose { get; set; } @@ -165,40 +172,44 @@ public Profile() public bool CloseAllAnchoredGumpsInGroupWithRightClick { get; set; } = false; public bool HoldAltToMoveGumps { get; set; } + public bool HideScreenshotStoredInMessage { get; set; } + // Experimental - public bool CastSpellsByOneClick { get; set; } - public bool BuffBarTime { get; set; } - public bool AutoOpenDoors { get; set; } - public bool SmoothDoors { get; set; } - public bool AutoOpenCorpses { get; set; } - public int AutoOpenCorpseRange { get; set; } = 2; - public int CorpseOpenOptions { get; set; } = 3; - public bool SkipEmptyCorpse { get; set; } - public bool DisableDefaultHotkeys { get; set; } - public bool DisableArrowBtn { get; set; } - public bool DisableTabBtn { get; set; } - public bool DisableCtrlQWBtn { get; set; } - public bool DisableAutoMove { get; set; } - public bool EnableDragSelect { get; set; } - public int DragSelectModifierKey { get; set; } // 0 = none, 1 = control, 2 = shift - public bool OverrideContainerLocation { get; set; } - public int OverrideContainerLocationSetting { get; set; } // 0 = container position, 1 = top right of screen, 2 = last dragged position, 3 = remember every container - public Point OverrideContainerLocationPosition { get; set; } = new Point(200, 200); - public bool DragSelectHumanoidsOnly { get; set; } - public NameOverheadTypeAllowed NameOverheadTypeAllowed { get; set; } = NameOverheadTypeAllowed.All; - public bool NameOverheadToggled { get; set; } = false; - public bool ShowTargetRangeIndicator { get; set; } - public bool PartyInviteGump { get; set; } - public bool CustomBarsToggled { get; set; } - public bool CBBlackBGToggled { get; set; } - - public bool ShowInfoBar { get; set; } - public int InfoBarHighlightType { get; set; } // 0 = text colour changes, 1 = underline - - - - public InfoBarItem[] InfoBarItems { get; set; }// [FILE_FIX] TODO: REMOVE IT - public Macro[] Macros { get; set; } // [FILE_FIX] TODO: REMOVE IT + public bool CastSpellsByOneClick { get; set; } + public bool BuffBarTime { get; set; } + public bool FastSpellsAssign { get; set; } + public bool AutoOpenDoors { get; set; } + public bool SmoothDoors { get; set; } + public bool AutoOpenCorpses { get; set; } + public int AutoOpenCorpseRange { get; set; } = 2; + public int CorpseOpenOptions { get; set; } = 3; + public bool SkipEmptyCorpse { get; set; } + public bool DisableDefaultHotkeys { get; set; } + public bool DisableArrowBtn { get; set; } + public bool DisableTabBtn { get; set; } + public bool DisableCtrlQWBtn { get; set; } + public bool DisableAutoMove { get; set; } + public bool EnableDragSelect { get; set; } + public int DragSelectModifierKey { get; set; } // 0 = none, 1 = control, 2 = shift + public bool OverrideContainerLocation { get; set; } + + public int OverrideContainerLocationSetting { get; set; } // 0 = container position, 1 = top right of screen, 2 = last dragged position, 3 = remember every container + + public Point OverrideContainerLocationPosition { get; set; } = new Point(200, 200); + public bool DragSelectHumanoidsOnly { get; set; } + public NameOverheadTypeAllowed NameOverheadTypeAllowed { get; set; } = NameOverheadTypeAllowed.All; + public bool NameOverheadToggled { get; set; } = false; + public bool ShowTargetRangeIndicator { get; set; } + public bool PartyInviteGump { get; set; } + public bool CustomBarsToggled { get; set; } + public bool CBBlackBGToggled { get; set; } + + public bool ShowInfoBar { get; set; } + public int InfoBarHighlightType { get; set; } // 0 = text colour changes, 1 = underline + + + public InfoBarItem[] InfoBarItems { get; set; } // [FILE_FIX] TODO: REMOVE IT + public Macro[] Macros { get; set; } // [FILE_FIX] TODO: REMOVE IT public bool CounterBarEnabled { get; set; } @@ -211,13 +222,21 @@ public Profile() public int CounterBarRows { get; set; } = 1; public int CounterBarColumns { get; set; } = 1; + public bool ShowSkillsChangedMessage { get; set; } = true; + public int ShowSkillsChangedDeltaValue { get; set; } = 1; + public bool ShowStatsChangedMessage { get; set; } = true; + + public int FilterType { get; set; } = 0; public bool ShadowsEnabled { get; set; } = true; + public bool ShadowsStatics { get; set; } = true; + public int TerrainShadowsLevel { get; set; } = 15; public int AuraUnderFeetType { get; set; } // 0 = NO, 1 = in warmode, 2 = ctrl+shift, 3 = always public bool AuraOnMouse { get; set; } = true; public bool PartyAura { get; set; } + // MobileUO: set to false public bool UseXBR { get; set; } = false; public bool HideChatGradient { get; set; } = false; @@ -238,8 +257,6 @@ public Profile() public bool SallosEasyGrab { get; set; } - public float Brighlight { get; set; } - public bool JournalDarkMode { get; set; } public byte ContainersScale { get; set; } = 100; @@ -252,6 +269,8 @@ public Profile() public bool RelativeDragAndDropItems { get; set; } + public bool HighlightContainerWhenSelected { get; set; } + public bool ShowHouseContent { get; set; } public bool SaveHealthbars { get; set; } public bool TextFading { get; set; } = true; @@ -284,41 +303,31 @@ public Profile() public bool WorldMapShowMultis { get; set; } = true; public string WorldMapHiddenMarkerFiles { get; set; } = string.Empty; - + // MobileUO: this was moved to ProfileManager, but think we need to keep them for now internal static string ProfilePath { get; } = Path.Combine(CUOEnviroment.ExecutablePath, "Data", "Profiles"); internal static string DataPath { get; } = Path.Combine(CUOEnviroment.ExecutablePath, "Data"); + public static uint GumpsVersion { get; private set; } - public void Save(List gumps = null) + public void Save(string path) { - if (string.IsNullOrEmpty(ServerName)) - throw new InvalidDataException(); - - if (string.IsNullOrEmpty(Username)) - throw new InvalidDataException(); - - if (string.IsNullOrEmpty(CharacterName)) - throw new InvalidDataException(); - - string path = FileSystemHelper.CreateFolderIfNotExists(ProfilePath, Username.Trim(), ServerName.Trim(), CharacterName.Trim()); - - Log.Trace( $"Saving path:\t\t{path}"); + Log.Trace($"Saving path:\t\t{path}"); // Save profile settings ConfigurationResolver.Save(this, Path.Combine(path, "profile.json")); // Save opened gumps - SaveGumps(path, gumps); + SaveGumps(path); - Log.Trace( "Saving done!"); + Log.Trace("Saving done!"); } - private void SaveGumps(string path, List gumps) + private void SaveGumps(string path) { string gumpsXmlPath = Path.Combine(path, "gumps.xml"); using (XmlTextWriter xml = new XmlTextWriter(gumpsXmlPath, Encoding.UTF8) { - Formatting = System.Xml.Formatting.Indented, + Formatting = Formatting.Indented, IndentChar = '\t', Indentation = 1 }) @@ -328,158 +337,118 @@ private void SaveGumps(string path, List gumps) UIManager.AnchorManager.Save(xml); - for (int i = 0; i < gumps.Count; i++) - { - var gump = gumps[i]; - - if (gump.IsDisposed) - continue; + LinkedList gumps = new LinkedList(); - if (gump is AnchorableGump anchored && UIManager.AnchorManager[anchored] != null) - { - // do nothing - } - else + foreach (Gump gump in UIManager.Gumps) + { + if (!gump.IsDisposed && gump.CanBeSaved && !(gump is AnchorableGump anchored && UIManager.AnchorManager[anchored] != null)) { - xml.WriteStartElement("gump"); - gump.Save(xml); - xml.WriteEndElement(); + gumps.AddLast(gump); } } - xml.WriteEndElement(); - xml.WriteEndDocument(); - } - - - - SkillsGroupManager.Save(); - } - - public static uint GumpsVersion { get; private set; } - - public List ReadGumps() - { - string path = FileSystemHelper.CreateFolderIfNotExists(ProfilePath, Username.Trim(), ServerName.Trim(), CharacterName.Trim()); - List gumps = new List(); - - - // ######################################################### - // [FILE_FIX] - // TODO: this code is a workaround to port old macros to the new xml system. - string skillsGroupsPath = Path.Combine(path, "skillsgroups.bin"); + LinkedListNode first = gumps.First; - if (File.Exists(skillsGroupsPath)) - { - try + while (first != null) { - using (BinaryReader reader = new BinaryReader(File.OpenRead(skillsGroupsPath))) - { - int version = reader.ReadInt32(); + Gump gump = first.Value; - int groupCount = reader.ReadInt32(); + if (gump.LocalSerial != 0) + { + Item item = World.Items.Get(gump.LocalSerial); - for (int i = 0; i < groupCount; i++) + if (item != null && !item.IsDestroyed && item.Opened) { - int entriesCount = reader.ReadInt32(); - string groupName = reader.ReadUTF8String(reader.ReadInt32()); + while (SerialHelper.IsItem(item.Container)) + { + item = World.Items.Get(item.Container); + } - SkillsGroup g = new SkillsGroup(); - g.Name = groupName; + SaveItemsGumpRecursive(item, xml, gumps); - for (int j = 0; j < entriesCount; j++) + if (first.List != null) { - byte idx = (byte) reader.ReadInt32(); - g.Add(idx); + gumps.Remove(first); } - g.Sort(); + first = gumps.First; - SkillsGroupManager.Add(g); + continue; } } - } - catch (Exception e) - { - SkillsGroupManager.MakeDefault(); - Log.Error( e.StackTrace); - } + xml.WriteStartElement("gump"); + gump.Save(xml); + xml.WriteEndElement(); - - SkillsGroupManager.Save(); + if (first.List != null) + { + gumps.Remove(first); + } - try - { - File.Delete(skillsGroupsPath); + first = gumps.First; } - catch { } + + xml.WriteEndElement(); + xml.WriteEndDocument(); } - string binpath = Path.Combine(path, "gumps.bin"); - if (File.Exists(binpath)) + SkillsGroupManager.Save(); + } + + private static void SaveItemsGumpRecursive(Item parent, XmlTextWriter xml, LinkedList list) + { + if (parent != null && !parent.IsDestroyed && parent.Opened) { - using (BinaryReader reader = new BinaryReader(File.OpenRead(binpath))) - { - if (reader.BaseStream.Position + 12 < reader.BaseStream.Length) - { - GumpsVersion = reader.ReadUInt32(); - uint empty = reader.ReadUInt32(); + SaveItemsGump(parent, xml, list); - int count = reader.ReadInt32(); + Item first = (Item) parent.Items; - for (int i = 0; i < count; i++) - { - try - { - int typeLen = reader.ReadUInt16(); - string typeName = reader.ReadUTF8String(typeLen); - int x = reader.ReadInt32(); - int y = reader.ReadInt32(); + while (first != null) + { + Item next = (Item) first.Next; - Type type = Type.GetType(typeName, true); - Gump gump = (Gump) Activator.CreateInstance(type); - gump.Restore(reader); - gump.X = x; - gump.Y = y; + SaveItemsGumpRecursive(first, xml, list); - //gump.SetInScreen(); + first = next; + } + } + } - if (gump.LocalSerial != 0) - UIManager.SavePosition(gump.LocalSerial, new Point(x, y)); + private static void SaveItemsGump(Item item, XmlTextWriter xml, LinkedList list) + { + if (item != null && !item.IsDestroyed && item.Opened) + { + LinkedListNode first = list.First; - if (!gump.IsDisposed) - gumps.Add(gump); - } - catch (Exception e) - { - Log.Error(e.StackTrace); - } - } - } - } + while (first != null) + { + LinkedListNode next = first.Next; - SaveGumps(path, gumps); + if (first.Value.LocalSerial == item.Serial && !first.Value.IsDisposed) + { + xml.WriteStartElement("gump"); + first.Value.Save(xml); + xml.WriteEndElement(); - gumps.Clear(); + list.Remove(first); - try - { - File.Delete(binpath); - } - catch - { + break; + } + first = next; } } - // ######################################################### + } + public List ReadGumps(string path) + { + List gumps = new List(); // load skillsgroup - //SkillsGroupManager.Load(); SkillsGroupManager.Load(); // load gumps @@ -488,6 +457,7 @@ public List ReadGumps() if (File.Exists(gumpsXmlPath)) { XmlDocument doc = new XmlDocument(); + try { doc.Load(gumpsXmlPath); @@ -503,96 +473,147 @@ public List ReadGumps() if (root != null) { - foreach (XmlElement xml in root.ChildNodes/*.GetElementsByTagName("gump")*/) + foreach (XmlElement xml in root.ChildNodes /*.GetElementsByTagName("gump")*/) { if (xml.Name != "gump") + { continue; + } + try { - GUMP_TYPE type = (GUMP_TYPE) int.Parse(xml.GetAttribute("type")); - int x = int.Parse(xml.GetAttribute("x")); - int y = int.Parse(xml.GetAttribute("y")); - uint serial = uint.Parse(xml.GetAttribute("serial")); + GumpType type = (GumpType) int.Parse(xml.GetAttribute(nameof(type))); + int x = int.Parse(xml.GetAttribute(nameof(x))); + int y = int.Parse(xml.GetAttribute(nameof(y))); + uint serial = uint.Parse(xml.GetAttribute(nameof(serial))); Gump gump = null; + switch (type) { - case GUMP_TYPE.GT_BUFF: + case GumpType.Buff: gump = new BuffGump(); + break; - case GUMP_TYPE.GT_CONTAINER: + + case GumpType.Container: gump = new ContainerGump(); + break; - case GUMP_TYPE.GT_COUNTERBAR: + + case GumpType.CounterBar: gump = new CounterBarGump(); + break; - case GUMP_TYPE.GT_HEALTHBAR: + + case GumpType.HealthBar: if (CustomBarsToggled) + { gump = new HealthBarGumpCustom(); + } else + { gump = new HealthBarGump(); + } + break; - case GUMP_TYPE.GT_INFOBAR: + + case GumpType.InfoBar: gump = new InfoBarGump(); + break; - case GUMP_TYPE.GT_JOURNAL: + + case GumpType.Journal: gump = new JournalGump(); + break; - case GUMP_TYPE.GT_MACROBUTTON: + + case GumpType.MacroButton: gump = new MacroButtonGump(); break; - case GUMP_TYPE.GT_ASSISTANTMACROBUTTON: + // MobileUO: added assistant gumps + case GumpType.AssistantMacroButton: gump = new AssistantMacroButtonGump(); break; - case GUMP_TYPE.GT_ASSISTANTHOTKEYBUTTON: + case GumpType.AssistantHotkeyButton: gump = new AssistantHotkeyButtonGump(); break; - case GUMP_TYPE.GT_MINIMAP: + + case GumpType.MiniMap: gump = new MiniMapGump(); + break; - case GUMP_TYPE.GT_PAPERDOLL: + + case GumpType.PaperDoll: gump = new PaperDollGump(); + break; - case GUMP_TYPE.GT_SKILLMENU: + + case GumpType.SkillMenu: if (StandardSkillsGump) + { gump = new StandardSkillsGump(); + } else + { gump = new SkillGumpAdvanced(); + } + break; - case GUMP_TYPE.GT_SPELLBOOK: + + case GumpType.SpellBook: gump = new SpellbookGump(); + break; - case GUMP_TYPE.GT_STATUSGUMP: + + case GumpType.StatusGump: gump = StatusGumpBase.AddStatusGump(0, 0); + break; - //case GUMP_TYPE.GT_TIPNOTICE: + + //case GumpType.TipNotice: // gump = new TipNoticeGump(); // break; - case GUMP_TYPE.GT_ABILITYBUTTON: + case GumpType.AbilityButton: gump = new UseAbilityButtonGump(); + break; - case GUMP_TYPE.GT_SPELLBUTTON: + + case GumpType.SpellButton: gump = new UseSpellButtonGump(); + break; - case GUMP_TYPE.GT_SKILLBUTTON: + + case GumpType.SkillButton: gump = new SkillButtonGump(); + break; - case GUMP_TYPE.GT_RACIALBUTTON: + + case GumpType.RacialButton: gump = new RacialAbilityButton(); + break; - case GUMP_TYPE.GT_WORLDMAP: + + case GumpType.WorldMap: gump = new WorldMapGump(); + break; - case GUMP_TYPE.GT_DEBUG: + + case GumpType.Debug: gump = new DebugGump(100, 100); + break; - case GUMP_TYPE.GT_NETSTATS: + + case GumpType.NetStats: gump = new NetworkStatsGump(100, 100); + break; } if (gump == null) + { continue; + } gump.LocalSerial = serial; gump.Restore(xml); @@ -608,7 +629,6 @@ public List ReadGumps() { gumps.Add(gump); } - } catch (Exception ex) { @@ -628,7 +648,7 @@ public List ReadGumps() { try { - GUMP_TYPE type = (GUMP_TYPE) int.Parse(xml.GetAttribute("type")); + GumpType type = (GumpType) int.Parse(xml.GetAttribute("type")); int x = int.Parse(xml.GetAttribute("x")); int y = int.Parse(xml.GetAttribute("y")); uint serial = uint.Parse(xml.GetAttribute("serial")); @@ -640,28 +660,41 @@ public List ReadGumps() switch (type) { - case GUMP_TYPE.GT_SPELLBUTTON: + case GumpType.SpellButton: gump = new UseSpellButtonGump(); + break; - case GUMP_TYPE.GT_SKILLBUTTON: + + case GumpType.SkillButton: gump = new SkillButtonGump(); + break; - case GUMP_TYPE.GT_HEALTHBAR: + + case GumpType.HealthBar: if (CustomBarsToggled) + { gump = new HealthBarGumpCustom(); + } else + { gump = new HealthBarGump(); + } + break; - case GUMP_TYPE.GT_ABILITYBUTTON: + + case GumpType.AbilityButton: gump = new UseAbilityButtonGump(); + break; - case GUMP_TYPE.GT_MACROBUTTON: + + case GumpType.MacroButton: gump = new MacroButtonGump(); break; - case GUMP_TYPE.GT_ASSISTANTMACROBUTTON: + // MobileUO: added assistant gumps + case GumpType.AssistantMacroButton: gump = new AssistantMacroButtonGump(); break; - case GUMP_TYPE.GT_ASSISTANTHOTKEYBUTTON: + case GumpType.AssistantHotkeyButton: gump = new AssistantHotkeyButtonGump(); break; } @@ -681,8 +714,10 @@ public List ReadGumps() UIManager.AnchorManager[gump] = ancoGroup; ancoGroup.AddControlToMatrix(matrix_x, matrix_y, gump); } - else + else + { gump.Dispose(); + } } } } @@ -698,4 +733,4 @@ public List ReadGumps() return gumps; } } -} +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Configuration/ProfileManager.cs b/Assets/Scripts/ClassicUO/src/Configuration/ProfileManager.cs index e68fff3ec..afd73dfa5 100644 --- a/Assets/Scripts/ClassicUO/src/Configuration/ProfileManager.cs +++ b/Assets/Scripts/ClassicUO/src/Configuration/ProfileManager.cs @@ -1,52 +1,74 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System.IO; - using ClassicUO.Utility; - using Microsoft.Xna.Framework; -using TinyJson; - namespace ClassicUO.Configuration { internal static class ProfileManager { - public static Profile Current { get; private set; } + public static Profile CurrentProfile { get; private set; } + public static string ProfilePath { get; private set; } + // MobileUO: added variable public static System.Action ProfileLoaded; public static void Load(string servername, string username, string charactername) { - string path = FileSystemHelper.CreateFolderIfNotExists(CUOEnviroment.ExecutablePath, "Data", "Profiles", username, servername, charactername); + string rootpath; + + if (string.IsNullOrWhiteSpace(Settings.GlobalSettings.ProfilesPath)) + { + rootpath = Path.Combine(CUOEnviroment.ExecutablePath, "Data", "Profiles"); + } + else + { + rootpath = Settings.GlobalSettings.ProfilesPath; + } + + string path = FileSystemHelper.CreateFolderIfNotExists(rootpath, username, servername, charactername); string fileToLoad = Path.Combine(path, "profile.json"); - Current = ConfigurationResolver.Load(fileToLoad) ?? new Profile(); + ProfilePath = path; + CurrentProfile = ConfigurationResolver.Load(fileToLoad) ?? new Profile(); - Current.Username = username; - Current.ServerName = servername; - Current.CharacterName = charactername; + CurrentProfile.Username = username; + CurrentProfile.ServerName = servername; + CurrentProfile.CharacterName = charactername; - ValidateFields(Current); - + ValidateFields(CurrentProfile); + + // MobileUO: added invoke ProfileLoaded?.Invoke(); } @@ -54,18 +76,39 @@ public static void Load(string servername, string username, string charactername private static void ValidateFields(Profile profile) { if (profile == null) + { return; + } + + if (string.IsNullOrEmpty(profile.ServerName)) + { + throw new InvalidDataException(); + } + + if (string.IsNullOrEmpty(profile.Username)) + { + throw new InvalidDataException(); + } + + if (string.IsNullOrEmpty(profile.CharacterName)) + { + throw new InvalidDataException(); + } if (profile.WindowClientBounds.X < 600) + { profile.WindowClientBounds = new Point(600, profile.WindowClientBounds.Y); + } + if (profile.WindowClientBounds.Y < 480) + { profile.WindowClientBounds = new Point(profile.WindowClientBounds.X, 480); - + } } public static void UnLoadProfile() { - Current = null; + CurrentProfile = null; } } } \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Configuration/Settings.cs b/Assets/Scripts/ClassicUO/src/Configuration/Settings.cs index 99e74ce1d..ac32c20aa 100644 --- a/Assets/Scripts/ClassicUO/src/Configuration/Settings.cs +++ b/Assets/Scripts/ClassicUO/src/Configuration/Settings.cs @@ -1,46 +1,51 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System.IO; - using Microsoft.Xna.Framework; - using TinyJson; namespace ClassicUO.Configuration { internal sealed class Settings { + public const string SETTINGS_FILENAME = "settings.json"; public static Settings GlobalSettings = new Settings(); - - public Settings() - { - } + public static string CustomSettingsFilepath = null; - [JsonProperty("username")] - public string Username { get; set; } = string.Empty; + [JsonProperty("username")] public string Username { get; set; } = string.Empty; - [JsonProperty("password")] - public string Password { get; set; } = string.Empty; + [JsonProperty("password")] public string Password { get; set; } = string.Empty; [JsonProperty("ip")] public string IP { get; set; } = "127.0.0.1"; @@ -49,91 +54,78 @@ public Settings() [JsonProperty("ultimaonlinedirectory")] public string UltimaOnlineDirectory { get; set; } = ""; - [JsonProperty("clientversion")] - public string ClientVersion { get; set; } = string.Empty; + [JsonProperty("profilespath")] public string ProfilesPath { get; set; } = string.Empty; + + [JsonProperty("clientversion")] public string ClientVersion { get; set; } = string.Empty; + + [JsonProperty("lang")] public string Language { get; set; } = ""; - [JsonProperty("lastcharactername")] - public string LastCharacterName { get; set; } = string.Empty; + [JsonProperty("lastservernum")] public ushort LastServerNum { get; set; } = 1; - [JsonProperty("cliloc")] - public string ClilocFile { get; set; } = "Cliloc.enu"; + [JsonProperty("last_server_name")] public string LastServerName { get; set; } = string.Empty; - [JsonProperty("lastservernum")] - public ushort LastServerNum { get; set; } = 1; + [JsonProperty("fps")] public int FPS { get; set; } = 60; - [JsonProperty("fps")] - public int FPS { get; set; } = 60; [JsonProperty("window_position")] public Point? WindowPosition { get; set; } [JsonProperty("window_size")] public Point? WindowSize { get; set; } - [JsonProperty("is_win_maximized")] - public bool IsWindowMaximized { get; set; } = true; + [JsonProperty("is_win_maximized")] public bool IsWindowMaximized { get; set; } = true; - [JsonProperty("saveaccount")] - public bool SaveAccount { get; set; } + [JsonProperty("saveaccount")] public bool SaveAccount { get; set; } - [JsonProperty("autologin")] - public bool AutoLogin { get; set; } + [JsonProperty("autologin")] public bool AutoLogin { get; set; } - [JsonProperty("reconnect")] - public bool Reconnect { get; set; } + [JsonProperty("reconnect")] public bool Reconnect { get; set; } - [JsonProperty("reconnect_time")] - public int ReconnectTime { get; set; } + [JsonProperty("reconnect_time")] public int ReconnectTime { get; set; } = 1; - [JsonProperty("login_music")] - public bool LoginMusic { get; set; } = true; + [JsonProperty("login_music")] public bool LoginMusic { get; set; } = true; - [JsonProperty("login_music_volume")] - public int LoginMusicVolume { get; set; } = 70; + [JsonProperty("login_music_volume")] public int LoginMusicVolume { get; set; } = 70; - [JsonProperty("shard_type")] - public int ShardType { get; set; } // 0 = normal (no customization), 1 = old, 2 = outlands?? + [JsonProperty("shard_type")] public int ShardType { get; set; } // 0 = normal (no customization), 1 = old, 2 = outlands?? - [JsonProperty("fixed_time_step")] - public bool FixedTimeStep { get; set; } = true; + [JsonProperty("fixed_time_step")] public bool FixedTimeStep { get; set; } = true; [JsonProperty("run_mouse_in_separate_thread")] public bool RunMouseInASeparateThread { get; set; } = true; - [JsonProperty("force_driver")] - public byte ForceDriver { get; set; } + [JsonProperty("force_driver")] public byte ForceDriver { get; set; } + + [JsonProperty("use_verdata")] public bool UseVerdata { get; set; } - [JsonProperty("use_verdata")] - public bool UseVerdata { get; set; } + [JsonProperty("maps_layouts")] public string MapsLayouts { get; set; } - [JsonProperty("encryption")] - public byte Encryption { get; set; } + [JsonProperty("encryption")] public byte Encryption { get; set; } - [JsonProperty("plugins")] - public string[] Plugins { get; set; } = { @"./Assistant/Razor.dll" }; + [JsonProperty("plugins")] public string[] Plugins { get; set; } = { @"./Assistant/Razor.dll" }; + // MobileUO: added variable [JsonProperty("internal_assistant")] public bool EnableInternalAssistant { get; set; } = true; - public const string SETTINGS_FILENAME = "settings.json"; - public static string CustomSettingsFilepath = null; - public static string GetSettingsFilepath() { if (CustomSettingsFilepath != null) { if (Path.IsPathRooted(CustomSettingsFilepath)) + { return CustomSettingsFilepath; - else - return Path.Combine(CUOEnviroment.ExecutablePath, CustomSettingsFilepath); + } + + return Path.Combine(CUOEnviroment.ExecutablePath, CustomSettingsFilepath); } return Path.Combine(CUOEnviroment.ExecutablePath, SETTINGS_FILENAME); } - public void Save() { // Make a copy of the settings object that we will use in the saving process string json = this.Encode(true); - Settings settingsToSave = json.Decode(); // JsonConvert.DeserializeObject(JsonConvert.SerializeObject(this)); + + Settings settingsToSave = json.Decode(); // JsonConvert.DeserializeObject(JsonConvert.SerializeObject(this)); // Make sure we don't save username and password if `saveaccount` flag is not set // NOTE: Even if we pass username and password via command-line arguments they won't be saved @@ -143,6 +135,8 @@ public void Save() settingsToSave.Password = string.Empty; } + settingsToSave.ProfilesPath = string.Empty; + // NOTE: We can do any other settings clean-ups here before we save them ConfigurationResolver.Save(settingsToSave, GetSettingsFilepath()); diff --git a/Assets/Scripts/ClassicUO/src/Game/CircleOfTransparency.cs b/Assets/Scripts/ClassicUO/src/Game/CircleOfTransparency.cs index 3847cbcd6..21d7c5929 100644 --- a/Assets/Scripts/ClassicUO/src/Game/CircleOfTransparency.cs +++ b/Assets/Scripts/ClassicUO/src/Game/CircleOfTransparency.cs @@ -1,58 +1,72 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; - using ClassicUO.Renderer; using ClassicUO.Utility; - using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace ClassicUO.Game { - static class CircleOfTransparency + internal static class CircleOfTransparency { - private static readonly Lazy _stencil = new Lazy(() => - { - DepthStencilState state = new DepthStencilState + private static readonly Lazy _stencil = new Lazy + ( + () => { - StencilEnable = true, - StencilFunction = CompareFunction.Always, - StencilPass = StencilOperation.Replace, - ReferenceStencil = 1, - //DepthBufferEnable = true, - //DepthBufferWriteEnable = true, - }; + DepthStencilState state = new DepthStencilState + { + StencilEnable = true, + StencilFunction = CompareFunction.Always, + StencilPass = StencilOperation.Replace, + ReferenceStencil = 1 + //DepthBufferEnable = true, + //DepthBufferWriteEnable = true, + }; - return state; - }); + return state; + } + ); private static Texture2D _texture; private static short _width, _height; private static int _radius; - + private static Vector3 _hueVector; + + public static uint[] CreateCircleTexture(int radius, ref short width, ref short height) { int fixRadius = radius + 1; @@ -83,32 +97,45 @@ public static uint[] CreateCircleTexture(int radius, ref short width, ref short return pixels; } - private static Vector3 _hueVector; - - public static void Draw(UltimaBatcher2D batcher, int x, int y) + public static void Draw(UltimaBatcher2D batcher, int x, int y, ushort hue = 0) { if (_texture != null) { - x -= (_width >> 1); - y -= (_height >> 1); + x -= _width >> 1; + y -= _height >> 1; - //batcher.Begin(); + if (hue == 0) + { + _hueVector.X = 0; + _hueVector.Y = 0f; + } + else + { + _hueVector.X = hue; + _hueVector.Y = 1f; + } + batcher.SetStencil(_stencil.Value); batcher.Draw2D(_texture, x, y, ref _hueVector); batcher.SetStencil(null); - //batcher.End(); } } public static void Create(int radius) { if (radius < Constants.MIN_CIRCLE_OF_TRANSPARENCY_RADIUS) + { radius = Constants.MIN_CIRCLE_OF_TRANSPARENCY_RADIUS; + } else if (radius > Constants.MAX_CIRCLE_OF_TRANSPARENCY_RADIUS) + { radius = Constants.MAX_CIRCLE_OF_TRANSPARENCY_RADIUS; + } if (_radius == radius && _texture != null && !_texture.IsDisposed) + { return; + } _radius = radius; _texture?.Dispose(); @@ -136,6 +163,7 @@ public static void Create(int radius) _texture.SetData(pixels); } + // MobileUO: added Dispose public static void Dispose() { _texture?.Dispose(); diff --git a/Assets/Scripts/ClassicUO/src/Game/Constants.cs b/Assets/Scripts/ClassicUO/src/Game/Constants.cs deleted file mode 120000 index 3ea93ffea..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/Constants.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../external/ClassicUO/src/Game/Constants.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Constants.cs b/Assets/Scripts/ClassicUO/src/Game/Constants.cs new file mode 100644 index 000000000..eed1fd8fa --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Constants.cs @@ -0,0 +1,160 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +namespace ClassicUO.Game +{ + internal static class Constants + { + public const string WIKI_LINK = "https://github.com/andreakarasho/ClassicUO/wiki"; + + public const int MIN_FPS = 12; + public const int MAX_FPS = 250; + public const int LOGIN_SCREEN_FPS = 60; + + public const int CHARACTER_ANIMATION_DELAY = 80; + public const int ITEM_EFFECT_ANIMATION_DELAY = 50; + + public const int MAX_STEP_COUNT = 5; + public const int TURN_DELAY = 100; // original client 12.5 fps = 80ms delay. FIXME: this patch causes a packet throttle. Reverted back to 100ms + public const int TURN_DELAY_FAST = 45; + public const int WALKING_DELAY = 150; // 750 + public const int PLAYER_WALKING_DELAY = 150; + public const int DEFAULT_CHARACTER_HEIGHT = 16; + public const int DEFAULT_BLOCK_HEIGHT = 16; + + public const float TIME_FADEOUT_TEXT = 1000; + public const uint TIME_DISPLAY_SYSTEM_MESSAGE_TEXT = 10000; + + public const int MIN_TERRAIN_SHADOWS_LEVEL = 5; + public const int MAX_TERRAIN_SHADOWS_LEVEL = 25; + + public const int USED_LAYER_COUNT = 23; + + public const int CLEAR_TEXTURES_DELAY = 3000; + public const int MAX_ANIMATIONS_OBJECT_REMOVED_BY_GARBAGE_COLLECTOR = 20; + public const int MAX_ART_OBJECT_REMOVED_BY_GARBAGE_COLLECTOR = 20; + public const int MAX_GUMP_OBJECT_REMOVED_BY_GARBAGE_COLLECTOR = 20; + public const int MAX_SOUND_OBJECT_REMOVED_BY_GARBAGE_COLLECTOR = 20; + public const int MAX_MAP_OBJECT_REMOVED_BY_GARBAGE_COLLECTOR = 50; + + public const int MAX_FAST_WALK_STACK_SIZE = 5; + + public const byte FOLIAGE_ALPHA = 76; + public const byte ALPHA_TIME = 20; + + public const int MAX_OBJECT_HANDLES = 200; + public const int OBJECT_HANDLES_GUMP_WIDTH = 100; + public const int OBJECT_HANDLES_GUMP_HEIGHT = 18; + + public const int SPELLBOOK_1_SPELLS_COUNT = 64; + public const int SPELLBOOK_2_SPELLS_COUNT = 17; + public const int SPELLBOOK_3_SPELLS_COUNT = 10; + public const int SPELLBOOK_4_SPELLS_COUNT = 6; + public const int SPELLBOOK_5_SPELLS_COUNT = 8; + public const int SPELLBOOK_6_SPELLS_COUNT = 16; + public const int SPELLBOOK_7_SPELLS_COUNT = 30; + public const int SPELLBOOK_8_SPELLS_COUNT = 45; + + public const int WAIT_FOR_TARGET_DELAY = 5000; + + public const int CONTAINER_RECT_STEP = 20; + public const int CONTAINER_RECT_DEFAULT_POSITION = 40; + public const int CONTAINER_RECT_LINESTEP = 800; + public const int ITEM_GUMP_TEXTURE_OFFSET = 11369; + + public const int MAX_LAND_DATA_INDEX_COUNT = 0x4000; + public const int MAX_STATIC_DATA_INDEX_COUNT = 0x10000; + public const int MAX_LAND_TEXTURES_DATA_INDEX_COUNT = 0x4000; + public const int MAX_GUMP_DATA_INDEX_COUNT = 0x10000; + public const int MAX_SOUND_DATA_INDEX_COUNT = 0xFFFF; + public const int MAX_MULTI_DATA_INDEX_COUNT = 0x2200; + public const int MAX_MUSIC_DATA_INDEX_COUNT = 150; + public const int MAX_ANIMATIONS_DATA_INDEX_COUNT = 2048; + public const int MAX_LIGHTS_DATA_INDEX_COUNT = 100; + + public const ushort FIELD_REPLACE_GRAPHIC = 0x1826; + public const ushort TREE_REPLACE_GRAPHIC = 0x0E59; + + public const int MIN_CIRCLE_OF_TRANSPARENCY_RADIUS = 50; + public const int MAX_CIRCLE_OF_TRANSPARENCY_RADIUS = 200; + + public const int MAX_ABILITIES_COUNT = 32; + + public const int DRAG_ITEMS_DISTANCE = 3; + public const int MIN_GUMP_DRAG_DISTANCE = 0; + public const int MIN_PICKUP_DRAG_DISTANCE_PIXELS = 5; + + public const int MIN_VIEW_RANGE = 5; + public const int MAX_VIEW_RANGE = 24; + public const int MAX_CONTAINER_OPENED_ON_GROUND_RANGE = 3; + + public const int OUT_RANGE_COLOR = 0x038B; + public const int DEAD_RANGE_COLOR = 0x038E; + public const int DEATH_SCREEN_TIMER = 1500; + public const float SOUND_DELTA = 250; + + public const ushort HIGHLIGHT_CURRENT_OBJECT_HUE = 0x014; + + public const int MAX_JOURNAL_HISTORY_COUNT = 100; + + // MobileUO: keep for Assistant + public const uint JOURNAL_LOCALSERIAL = 0xFFFFFFE1; + public const uint SKILLSTD_LOCALSERIAL = 0xFFFFFFE2; + public const uint PROFILE_LOCALSERIAL = 0xFFFFFFE3; + + public const byte MIN_CONTAINER_SIZE_PERC = 50; + public const byte MAX_CONTAINER_SIZE_PERC = 200; + + public const int MALE_GUMP_OFFSET = 50000; + public const int FEMALE_GUMP_OFFSET = 60000; + + public const int WEATHER_TIMER = 6 * 60 * 1000; + + public const int PREDICTABLE_CHUNKS = 300; + public const int PREDICTABLE_TILE_COUNT = 64 * PREDICTABLE_CHUNKS; + public const int PREDICTABLE_STATICS = PREDICTABLE_TILE_COUNT * 2; + public const int PREDICTABLE_MULTIS = PREDICTABLE_TILE_COUNT * 4; + + // cannot be a const, due to UOLive implementation + public static int MAPS_COUNT = 6; + + public static readonly bool[] BAD_CONTAINER_LAYERS = + { + false, // invalid [body] + true, true, true, true, true, true, true, true, + true, true, false, true, true, true, false, false, + true, true, true, true, + false, // backpack + true, true, true, false, false, false, false, false + }; + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Data/ChairTable.cs b/Assets/Scripts/ClassicUO/src/Game/Data/ChairTable.cs new file mode 120000 index 000000000..a40a58cae --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Data/ChairTable.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Data/ChairTable.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameCursor.cs b/Assets/Scripts/ClassicUO/src/Game/GameCursor.cs index a63f23801..00c5792bc 100644 --- a/Assets/Scripts/ClassicUO/src/Game/GameCursor.cs +++ b/Assets/Scripts/ClassicUO/src/Game/GameCursor.cs @@ -1,43 +1,49 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.Collections.Generic; - using ClassicUO.Configuration; using ClassicUO.Game.Data; using ClassicUO.Game.GameObjects; using ClassicUO.Game.Managers; using ClassicUO.Game.Scenes; using ClassicUO.Game.UI; -using ClassicUO.Game.UI.Controls; using ClassicUO.Input; using ClassicUO.IO.Resources; using ClassicUO.Renderer; using ClassicUO.Utility; -using ClassicUO.Utility.Collections; - using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; - using SDL2; namespace ClassicUO.Game @@ -47,54 +53,32 @@ internal sealed class GameCursor private static readonly ushort[,] _cursorData = new ushort[3, 16] { { - 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F, 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079 + 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F, 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, + 0x2077, 0x2078, 0x2079 }, { - 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F, 0x2060, 0x2061, 0x2062 + 0x2053, 0x2054, 0x2055, 0x2056, 0x2057, 0x2058, 0x2059, 0x205A, 0x205B, 0x205C, 0x205D, 0x205E, 0x205F, + 0x2060, 0x2061, 0x2062 }, { - 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F, 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079 + 0x206A, 0x206B, 0x206C, 0x206D, 0x206E, 0x206F, 0x2070, 0x2071, 0x2072, 0x2073, 0x2074, 0x2075, 0x2076, + 0x2077, 0x2078, 0x2079 } }; - private readonly Texture2D _aura; + private readonly Aura _aura = new Aura(30); + private readonly CustomBuildObject[] _componentsList = new CustomBuildObject[10]; private readonly int[,] _cursorOffset = new int[2, 16]; private readonly IntPtr[,] _cursors_ptr = new IntPtr[3, 16]; - private readonly Tooltip _tooltip; - private Vector3 _auraVector = new Vector3(0, 13, 0); - private readonly RenderedText _targetDistanceText = RenderedText.Create(String.Empty, 0x0481, style: FontStyle.BlackBorder); - private ArtTexture _draggedItemTexture; + private UOTexture _draggedItemTexture; private ushort _graphic = 0x2073; private bool _needGraphicUpdate = true; private Point _offset; - private static Vector3 _vec = Vector3.Zero; private readonly List _temp = new List(); - + private readonly Tooltip _tooltip; public GameCursor() { - short ww = 0; - short hh = 0; - uint[] data = CircleOfTransparency.CreateCircleTexture(25, ref ww, ref hh); - - for (int i = 0; i < data.Length; i++) - { - ref uint pixel = ref data[i]; - - if (pixel != 0) - { - ushort value = (ushort) (pixel << 3); - - if (value > 0xFF) - value = 0xFF; - - pixel = (uint) ((value << 24) | (value << 16) | (value << 8) | value); - } - } - - _aura = new Texture2D(Client.Game.GraphicsDevice, ww, hh); - _aura.SetData(data); - _tooltip = new Tooltip(); for (int i = 0; i < 3; i++) @@ -103,171 +87,18 @@ public GameCursor() { ushort id = _cursorData[i, j]; - uint[] pixels = ArtLoader.Instance.ReadStaticArt(id, out short w, out short h, out _); + IntPtr surface = ArtLoader.Instance.CreateCursorSurfacePtr(id, (ushort)(i == 2 ? 0x0033 : 0), out int hotX, out int hotY); - if (i == 0) + if (surface != IntPtr.Zero) { - if (pixels != null && pixels.Length > 0) - { - float offX = 0; - float offY = 0; - float dw = w; - float dh = h; - - if (id == 0x206A) - offX = -4f; - else if (id == 0x206B) - offX = -dw + 3f; - else if (id == 0x206C) - { - offX = -dw + 3f; - offY = -(dh / 2f); - } - else if (id == 0x206D) - { - offX = -dw; - offY = -dh; - } - else if (id == 0x206E) - { - offX = -(dw * 0.66f); - offY = -dh; - } - else if (id == 0x206F) - offY = -dh + 4f; - else if (id == 0x2070) - offY = -dh + 4f; - else if (id == 0x2075) - offY = -4f; - else if (id == 0x2076) - { - offX = -12f; - offY = -14f; - } - else if (id == 0x2077) - { - offX = -(dw / 2f); - offY = -(dh / 2f); - } - else if (id == 0x2078) - offY = -(dh * 0.66f); - else if (id == 0x2079) offY = -(dh / 2f); - - switch (id) - { - case 0x206B: - offX = -29; - offY = -1; - - break; - - case 0x206C: - offX = -41; - offY = -9; - - break; - - case 0x206D: - offX = -36; - offY = -25; - - break; - - case 0x206E: - offX = -14; - offY = -33; - - break; - - case 0x206F: - offX = -2; - offY = -26; - - break; - - case 0x2070: - offX = -3; - offY = -8; - - break; - - case 0x2071: - offX = -1; - offY = -1; - - break; - - case 0x206A: - offX = -4; - offY = -2; - - break; - - case 0x2075: - offX = -2; - offY = -10; - - break; - } - - //if (offX == 0 && offY == 0) - //{ - // offX = -1; - // offY = -1; - //} - - - _cursorOffset[0, j] = (int) offX; - _cursorOffset[1, j] = (int) offY; - } - else + if (hotX != 0 || hotY != 0) { - _cursorOffset[0, j] = 0; - _cursorOffset[1, j] = 0; + _cursorOffset[0, j] = hotX; + _cursorOffset[1, j] = hotY; } + + _cursors_ptr[i, j] = SDL.SDL_CreateColorCursor(surface, hotX, hotY); } - // if (pixels != null && pixels.Length != 0) - // { - // unsafe - // { - // fixed (uint* ptr = pixels) - // { - // SDL.SDL_Surface* surface = (SDL.SDL_Surface*) SDL.SDL_CreateRGBSurfaceWithFormatFrom((IntPtr) ptr, w, h, 32, 4 * w, SDL.SDL_PIXELFORMAT_ABGR8888); - // - // if (i == 2) - // { - // int stride = surface->pitch >> 2; - // uint* pixels_ptr = (uint*) surface->pixels; - // uint* p_line_end = pixels_ptr + w; - // uint* p_img_end = pixels_ptr + (stride * h); - // int delta = stride - w; - // Color c = default; - // - // while (pixels_ptr < p_img_end) - // { - // while (pixels_ptr < p_line_end) - // { - // if (*pixels_ptr != 0 && *pixels_ptr != 0xFF_00_00_00) - // { - // c.PackedValue = *pixels_ptr; - // * pixels_ptr = HuesHelper.Color16To32(HuesLoader.Instance.GetColor16(HuesHelper.ColorToHue(c), 0x0033)) | 0xFF_00_00_00; - // } - // - // ++pixels_ptr; - // } - // - // pixels_ptr += delta; - // p_line_end += stride; - // } - // } - // - // int hotX = -_cursorOffset[0, j]; - // int hotY = -_cursorOffset[1, j]; - // - // _cursors_ptr[i, j] = SDL.SDL_CreateColorCursor((IntPtr) surface, hotX, hotY); - // } - // } - // } } } } @@ -292,13 +123,19 @@ public ushort Graphic public void SetDraggedItem(Point? offset) { - _draggedItemTexture = ArtLoader.Instance.GetTexture(ItemHold.DisplayedGraphic); + _draggedItemTexture = ItemHold.IsGumpTexture ? GumpsLoader.Instance.GetTexture((ushort) (ItemHold.DisplayedGraphic - Constants.ITEM_GUMP_TEXTURE_OFFSET)) : ArtLoader.Instance.GetTexture(ItemHold.DisplayedGraphic); + if (_draggedItemTexture == null) + { return; + } float scale = 1; - if (ProfileManager.Current != null && ProfileManager.Current.ScaleItemsInsideContainers) + + if (ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.ScaleItemsInsideContainers) + { scale = UIManager.ContainerScale; + } _offset.X = (int) ((_draggedItemTexture.Width >> 1) * scale); _offset.Y = (int) ((_draggedItemTexture.Height >> 1) * scale); @@ -309,7 +146,7 @@ public void SetDraggedItem(Point? offset) } } - public unsafe void Update(double totalMS, double frameMS) + public void Update(double totalTime, double frameTime) { Graphic = AssignGraphicByState(); @@ -322,9 +159,14 @@ public unsafe void Update(double totalMS, double frameMS) ushort id = Graphic; if (id < 0x206A) + { id -= 0x2053; + } else + { id -= 0x206A; + } + int war = World.InGame && World.Player.InWarMode ? 1 : World.InGame && World.MapIndex != 0 ? 2 : 0; ref IntPtr ptrCursor = ref _cursors_ptr[war, id]; @@ -338,15 +180,14 @@ public unsafe void Update(double totalMS, double frameMS) if (ItemHold.Enabled) { - _draggedItemTexture.Ticks = (long) totalMS; + _draggedItemTexture.Ticks = (long) totalTime; if (ItemHold.IsFixedPosition && !UIManager.IsDragging) { int x = ItemHold.FixedX - _offset.X; int y = ItemHold.FixedY - _offset.Y; - if (Mouse.Position.X >= x && Mouse.Position.X < x + _draggedItemTexture.Width && - Mouse.Position.Y >= y && Mouse.Position.Y < y + _draggedItemTexture.Height) + if (Mouse.Position.X >= x && Mouse.Position.X < x + _draggedItemTexture.Width && Mouse.Position.Y >= y && Mouse.Position.Y < y + _draggedItemTexture.Height) { if (!ItemHold.IgnoreFixedPosition) { @@ -363,21 +204,19 @@ public unsafe void Update(double totalMS, double frameMS) } } - private readonly CustomBuildObject[] _componentsList = new CustomBuildObject[10]; - public void Draw(UltimaBatcher2D sb) { - if (World.InGame && TargetManager.IsTargeting && ProfileManager.Current != null) + if (World.InGame && TargetManager.IsTargeting && ProfileManager.CurrentProfile != null) { if (TargetManager.TargetingState == CursorTarget.MultiPlacement) { if (World.CustomHouseManager != null && World.CustomHouseManager.SelectedGraphic != 0) { ushort hue = 0; - + Array.Clear(_componentsList, 0, 10); - if (!World.CustomHouseManager.CanBuildHere(_componentsList, out var type)) + if (!World.CustomHouseManager.CanBuildHere(_componentsList, out CUSTOM_HOUSE_BUILD_TYPE type)) { hue = 0x0021; } @@ -390,9 +229,12 @@ public void Draw(UltimaBatcher2D sb) for (int i = 0; i < _componentsList.Length; i++) { if (_componentsList[i].Graphic == 0) + { break; + } Multi m = Multi.Create(_componentsList[i].Graphic); + m.AlphaHue = 0xFF; m.Hue = hue; m.State = CUSTOM_HOUSE_MULTI_OBJECT_FLAGS.CHMOF_PREVIEW; @@ -408,13 +250,14 @@ public void Draw(UltimaBatcher2D sb) if (selectedObj.Z < World.CustomHouseManager.MinHouseZ) { - if (selectedObj.X >= World.CustomHouseManager.StartPos.X && selectedObj.X <= World.CustomHouseManager.EndPos.X - 1 && - selectedObj.Y >= World.CustomHouseManager.StartPos.Y && selectedObj.Y <= World.CustomHouseManager.EndPos.Y - 1) + if (selectedObj.X >= World.CustomHouseManager.StartPos.X && selectedObj.X <= World.CustomHouseManager.EndPos.X - 1 && selectedObj.Y >= World.CustomHouseManager.StartPos.Y && selectedObj.Y <= World.CustomHouseManager.EndPos.Y - 1) { if (type != CUSTOM_HOUSE_BUILD_TYPE.CHBT_STAIR) + { z += 7; + } } - } + } GameScene gs = Client.Game.GetScene(); @@ -423,16 +266,23 @@ public void Draw(UltimaBatcher2D sb) ref readonly CustomBuildObject item = ref _componentsList[i]; if (item.Graphic == 0) + { break; + } _temp[i].X = (ushort) (selectedObj.X + item.X); + _temp[i].Y = (ushort) (selectedObj.Y + item.Y); + _temp[i].Z = (sbyte) (selectedObj.Z + z + item.Z); + _temp[i].UpdateRealScreenPosition(gs.ScreenOffset.X, gs.ScreenOffset.Y); + _temp[i].UpdateScreenPosition(); + _temp[i].AddToTile(); } - } + } } } else if (_temp.Count != 0) @@ -447,50 +297,55 @@ public void Draw(UltimaBatcher2D sb) _temp.Clear(); } - if (ProfileManager.Current.AuraOnMouse) + if (ProfileManager.CurrentProfile.AuraOnMouse) { ushort id = Graphic; if (id < 0x206A) + { id -= 0x2053; + } else + { id -= 0x206A; + } - int hotX = _cursorOffset[0, id]; - int hotY = _cursorOffset[1, id]; + ushort hue = 0; switch (TargetManager.TargetingType) { case TargetType.Neutral: - _auraVector.X = 0x03b2; + hue = 0x03b2; break; case TargetType.Harmful: - _auraVector.X = 0x0023; + hue = 0x0023; break; case TargetType.Beneficial: - _auraVector.X = 0x005A; + hue = 0x005A; break; } - sb.Draw2D(_aura, Mouse.Position.X + hotX - (25 >> 1), Mouse.Position.Y + hotY - (25 >> 1), ref _auraVector); + _aura.Draw(sb, Mouse.Position.X, Mouse.Position.Y, hue); } - if (ProfileManager.Current.ShowTargetRangeIndicator) + if (ProfileManager.CurrentProfile.ShowTargetRangeIndicator) { - GameScene gs = Client.Game.GetScene(); - - if (gs != null && gs.IsMouseOverViewport) + if (UIManager.IsMouseOverWorld) { if (SelectedObject.Object is GameObject obj) { - _targetDistanceText.Text = obj.Distance.ToString(); + string dist = obj.Distance.ToString(); - _targetDistanceText.Draw(sb, Mouse.Position.X - 25, Mouse.Position.Y - 20, 0); + Vector3 hue = new Vector3(0, 1, 0); + sb.DrawString(Fonts.Bold, dist, Mouse.Position.X - 26, Mouse.Position.Y - 21, ref hue); + + hue.Y = 0; + sb.DrawString(Fonts.Bold, dist, Mouse.Position.X - 25, Mouse.Position.Y - 20, ref hue); } } } @@ -505,22 +360,42 @@ public void Draw(UltimaBatcher2D sb) { float scale = 1; - if (ProfileManager.Current != null && ProfileManager.Current.ScaleItemsInsideContainers) + if (ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.ScaleItemsInsideContainers) + { scale = UIManager.ContainerScale; + } int x = (ItemHold.IsFixedPosition ? ItemHold.FixedX : Mouse.Position.X) - _offset.X; int y = (ItemHold.IsFixedPosition ? ItemHold.FixedY : Mouse.Position.Y) - _offset.Y; Vector3 hue = Vector3.Zero; - ShaderHuesTraslator.GetHueVector(ref hue, ItemHold.Hue, ItemHold.IsPartialHue, ItemHold.HasAlpha ? .5f : 0); - sb.Draw2D(_draggedItemTexture, x, y, _draggedItemTexture.Width * scale, _draggedItemTexture.Height * scale, ref hue); + ShaderHueTranslator.GetHueVector(ref hue, ItemHold.Hue, ItemHold.IsPartialHue, ItemHold.HasAlpha ? .5f : 0); + + sb.Draw2D + ( + _draggedItemTexture, + x, + y, + _draggedItemTexture.Width * scale, + _draggedItemTexture.Height * scale, + ref hue + ); if (ItemHold.Amount > 1 && ItemHold.DisplayedGraphic == ItemHold.Graphic && ItemHold.IsStackable) { x += 5; y += 5; - sb.Draw2D(_draggedItemTexture, x, y, _draggedItemTexture.Width * scale, _draggedItemTexture.Height * scale, ref hue); + + sb.Draw2D + ( + _draggedItemTexture, + x, + y, + _draggedItemTexture.Width * scale, + _draggedItemTexture.Height * scale, + ref hue + ); } } @@ -528,63 +403,75 @@ public void Draw(UltimaBatcher2D sb) if (!Settings.GlobalSettings.RunMouseInASeparateThread) { + Graphic = AssignGraphicByState(); + ushort graphic = Graphic; if (graphic < 0x206A) + { graphic -= 0x2053; + } else + { graphic -= 0x206A; + } int offX = _cursorOffset[0, graphic]; int offY = _cursorOffset[1, graphic]; + Vector3 hueVec = Vector3.Zero; + if (World.InGame && World.MapIndex != 0 && !World.Player.InWarMode) { - _vec.X = 0x0034; - _vec.Y = 1; - _vec.Z = 0; - } - else - { - _vec = Vector3.Zero; + ShaderHueTranslator.GetHueVector(ref hueVec, 0x0033); } - sb.Draw2D(ArtLoader.Instance.GetTexture(Graphic), Mouse.Position.X + offX, Mouse.Position.Y + offY, ref _vec); + sb.Draw2D(ArtLoader.Instance.GetTexture(Graphic), Mouse.Position.X - offX, Mouse.Position.Y - offY, ref hueVec); } - } private void DrawToolTip(UltimaBatcher2D batcher, Point position) { if (Client.Game.Scene is GameScene gs) { - if (!World.ClientFeatures.TooltipsEnabled || + if (!World.ClientFeatures.TooltipsEnabled || (SelectedObject.Object is Item selectedItem && - selectedItem.IsLocked && - selectedItem.ItemData.Weight == 255 - && !selectedItem.ItemData.IsContainer) || + selectedItem.IsLocked && + selectedItem.ItemData.Weight == 255 && + !selectedItem.ItemData.IsContainer && + // We need to check if OPL contains data. + // If not we can ignore tooltip. + !World.OPL.Contains(selectedItem)) || (ItemHold.Enabled && !ItemHold.IsFixedPosition)) { - if (!_tooltip.IsEmpty && (!UIManager.IsMouseOverAControl || UIManager.IsMouseOverWorld)) + if (!_tooltip.IsEmpty && (UIManager.MouseOverControl == null || UIManager.IsMouseOverWorld)) + { _tooltip.Clear(); + } } else { - if (gs.IsMouseOverViewport && SelectedObject.Object is Entity item && World.OPL.Contains(item)) + if (UIManager.IsMouseOverWorld && SelectedObject.Object is Entity item && World.OPL.Contains(item)) { if (_tooltip.IsEmpty || item != _tooltip.Serial) + { _tooltip.SetGameObject(item); + } + _tooltip.Draw(batcher, position.X, position.Y + 24); return; } - if (UIManager.IsMouseOverAControl && UIManager.MouseOverControl.Tooltip is uint serial) + if (UIManager.MouseOverControl != null && UIManager.MouseOverControl.Tooltip is uint serial) { if (SerialHelper.IsValid(serial) && World.OPL.Contains(serial)) { if (_tooltip.IsEmpty || serial != _tooltip.Serial) + { _tooltip.SetGameObject(serial); + } + _tooltip.Draw(batcher, position.X, position.Y + 24); return; @@ -593,18 +480,22 @@ private void DrawToolTip(UltimaBatcher2D batcher, Point position) } } - if (UIManager.IsMouseOverAControl && UIManager.MouseOverControl != null && UIManager.MouseOverControl.HasTooltip && !Mouse.IsDragging) + if (UIManager.MouseOverControl != null && UIManager.MouseOverControl.HasTooltip && !Mouse.IsDragging) { if (UIManager.MouseOverControl.Tooltip is string text) { if (_tooltip.IsEmpty || _tooltip.Text != text) + { _tooltip.SetText(text, UIManager.MouseOverControl.TooltipMaxLength); + } _tooltip.Draw(batcher, position.X, position.Y + 24); } } - else if (!_tooltip.IsEmpty) + else if (!_tooltip.IsEmpty) + { _tooltip.Clear(); + } } private ushort AssignGraphicByState() @@ -612,31 +503,50 @@ private ushort AssignGraphicByState() int war = World.InGame && World.Player.InWarMode ? 1 : 0; if (TargetManager.IsTargeting) - { + { return _cursorData[war, 12]; } if (UIManager.IsDragging || IsDraggingCursorForced) + { return _cursorData[war, 8]; + } if (IsLoading) + { return _cursorData[war, 13]; + } if (UIManager.MouseOverControl != null && UIManager.MouseOverControl.AcceptKeyboardInput && UIManager.MouseOverControl.IsEditable) + { return _cursorData[war, 14]; + } ushort result = _cursorData[war, 9]; if (!UIManager.IsMouseOverWorld) + { return result; + } - if (ProfileManager.Current == null) + if (ProfileManager.CurrentProfile == null) + { return result; + } + + int windowCenterX = ProfileManager.CurrentProfile.GameWindowPosition.X + (ProfileManager.CurrentProfile.GameWindowSize.X >> 1); - int windowCenterX = ProfileManager.Current.GameWindowPosition.X + (ProfileManager.Current.GameWindowSize.X >> 1); - int windowCenterY = ProfileManager.Current.GameWindowPosition.Y + (ProfileManager.Current.GameWindowSize.Y >> 1); + int windowCenterY = ProfileManager.CurrentProfile.GameWindowPosition.Y + (ProfileManager.CurrentProfile.GameWindowSize.Y >> 1); - return _cursorData[war, GetMouseDirection(windowCenterX, windowCenterY, Mouse.Position.X, Mouse.Position.Y, 1)]; + return _cursorData[war, + GetMouseDirection + ( + windowCenterX, + windowCenterY, + Mouse.Position.X, + Mouse.Position.Y, + 1 + )]; } public static int GetMouseDirection(int x1, int y1, int to_x, int to_y, int current_facing) @@ -651,83 +561,59 @@ public static int GetMouseDirection(int x1, int y1, int to_x, int to_y, int curr shiftY = Math.Abs(shiftY); if (shiftY * 5 <= shiftX * 2) + { hashf = hashf + 1; + } else if (shiftY * 2 >= shiftX * 5) + { hashf = hashf + 3; + } else + { hashf = hashf + 2; + } } else if (shiftX == 0) { if (shiftY == 0) + { return current_facing; + } } switch (hashf) { - case 111: - - return (int) Direction.West; // W - - case 112: - - return (int) Direction.Up; // NW - - case 113: - - return (int) Direction.North; // N - - case 120: - - return (int) Direction.West; // W - - case 131: - - return (int) Direction.West; // W - - case 132: - - return (int) Direction.Left; // SW - - case 133: - - return (int) Direction.South; // S - - case 210: - - return (int) Direction.North; // N - - case 230: + case 111: return (int) Direction.West; // W - return (int) Direction.South; // S + case 112: return (int) Direction.Up; // NW - case 311: + case 113: return (int) Direction.North; // N - return (int) Direction.East; // E + case 120: return (int) Direction.West; // W - case 312: + case 131: return (int) Direction.West; // W - return (int) Direction.Right; // NE + case 132: return (int) Direction.Left; // SW - case 313: + case 133: return (int) Direction.South; // S - return (int) Direction.North; // N + case 210: return (int) Direction.North; // N - case 320: + case 230: return (int) Direction.South; // S - return (int) Direction.East; // E + case 311: return (int) Direction.East; // E - case 331: + case 312: return (int) Direction.Right; // NE - return (int) Direction.East; // E + case 313: return (int) Direction.North; // N - case 332: + case 320: return (int) Direction.East; // E - return (int) Direction.Down; // SE + case 331: return (int) Direction.East; // E - case 333: + case 332: return (int) Direction.Down; // SE - return (int) Direction.South; // S + case 333: return (int) Direction.South; // S } return current_facing; @@ -754,6 +640,7 @@ public CursorInfo(IntPtr ptr, int w, int h) public readonly IntPtr CursorPtr; } + // MobileUO: dispose public void Dispose() { _aura?.Dispose(); diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/AnimatedItemEffect.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/AnimatedItemEffect.cs deleted file mode 120000 index 50fc298d5..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/GameObjects/AnimatedItemEffect.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../external/ClassicUO/src/Game/GameObjects/AnimatedItemEffect.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/FixedEffect.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/FixedEffect.cs new file mode 120000 index 000000000..3ad4f2551 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/GameObjects/FixedEffect.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/GameObjects/FixedEffect.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/GameObject.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/GameObject.cs deleted file mode 120000 index 081a40d68..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/GameObjects/GameObject.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../external/ClassicUO/src/Game/GameObjects/GameObject.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/GameObject.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/GameObject.cs new file mode 100644 index 000000000..e74c1c849 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/GameObjects/GameObject.cs @@ -0,0 +1,441 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using ClassicUO.Configuration; +using ClassicUO.Data; +using ClassicUO.Game.Data; +using ClassicUO.Game.Managers; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; +using IUpdateable = ClassicUO.Interfaces.IUpdateable; + +namespace ClassicUO.Game.GameObjects +{ + internal abstract class BaseGameObject : LinkedObject + { + public Point RealScreenPosition; + } + + internal abstract partial class GameObject : BaseGameObject, IUpdateable + { + private Point _screenPosition; + + public bool IsDestroyed { get; protected set; } + public bool IsPositionChanged { get; protected set; } + public TextContainer TextContainer { get; private set; } + + public int Distance + { + get + { + if (World.Player == null /*|| IsDestroyed*/) + { + return ushort.MaxValue; + } + + if (ReferenceEquals(this, World.Player)) + { + return 0; + } + + int x = X, y = Y; + + if (this is Mobile mobile && mobile.Steps.Count != 0) + { + ref Mobile.Step step = ref mobile.Steps.Back(); + x = step.X; + y = step.Y; + } + + int fx = World.RangeSize.X; + int fy = World.RangeSize.Y; + + return Math.Max(Math.Abs(x - fx), Math.Abs(y - fy)); + } + } + + public virtual void Update(double totalTime, double frameTime) + { + } + + public int CurrentRenderIndex; + // FIXME: remove it + public sbyte FoliageIndex = -1; + public ushort Graphic; + public ushort Hue; + public Vector3 Offset; + public short PriorityZ; + public GameObject TNext; + public GameObject TPrevious; + public byte UseInRender; + public ushort X, Y; + public sbyte Z; + +#if RENDER_LIST_LINKED_LIST + public GameObject RenderListNext; +#endif + + + public void AddToTile(int x, int y) + { + if (World.Map != null) + { + RemoveFromTile(); + + if (!IsDestroyed) + { + World.Map.GetChunk(x, y)?.AddGameObject(this, x % 8, y % 8); + } + } + } + + public void AddToTile() + { + AddToTile(X, Y); + } + + public void RemoveFromTile() + { + if (TPrevious != null) + { + TPrevious.TNext = TNext; + } + + if (TNext != null) + { + TNext.TPrevious = TPrevious; + } + + TNext = null; + TPrevious = null; + } + + public virtual void UpdateGraphicBySeason() + { + } + + public void UpdateScreenPosition() + { + _screenPosition.X = (X - Y) * 22; + _screenPosition.Y = (X + Y) * 22 - (Z << 2); + IsPositionChanged = true; + OnPositionChanged(); + } + + public void UpdateRealScreenPosition(int offsetX, int offsetY) + { + RealScreenPosition.X = _screenPosition.X - offsetX - 22; + RealScreenPosition.Y = _screenPosition.Y - offsetY - 22; + IsPositionChanged = false; + + UpdateTextCoordsV(); + } + + + public void AddMessage(MessageType type, string message, TextType text_type) + { + AddMessage + ( + type, + message, + ProfileManager.CurrentProfile.ChatFont, + ProfileManager.CurrentProfile.SpeechHue, + true, + text_type + ); + } + + public virtual void UpdateTextCoordsV() + { + if (TextContainer == null) + { + return; + } + + TextObject last = (TextObject) TextContainer.Items; + + while (last?.Next != null) + { + last = (TextObject) last.Next; + } + + if (last == null) + { + return; + } + + int offY = 0; + + Point p = RealScreenPosition; + + ArtTexture texture = ArtLoader.Instance.GetTexture(Graphic); + + if (texture != null) + { + p.Y -= texture.ImageRectangle.Height >> 1; + } + + p.X += (int) Offset.X + 22; + p.Y += (int) (Offset.Y - Offset.Z) + 44; + + p = Client.Game.Scene.Camera.WorldToScreen(p); + + for (; last != null; last = (TextObject) last.Previous) + { + if (last.RenderedText != null && !last.RenderedText.IsDestroyed) + { + if (offY == 0 && last.Time < Time.Ticks) + { + continue; + } + + last.OffsetY = offY; + offY += last.RenderedText.Height; + + last.RealScreenPosition.X = p.X - (last.RenderedText.Width >> 1); + last.RealScreenPosition.Y = p.Y - offY; + } + } + + FixTextCoordinatesInScreen(); + } + + protected void FixTextCoordinatesInScreen() + { + if (this is Item it && SerialHelper.IsValid(it.Container)) + { + return; + } + + int offsetY = 0; + + // MobileUO: fixed viewport boundary condition check + int posX = ProfileManager.CurrentProfile.GameWindowPosition.X + 5; + int posY = ProfileManager.CurrentProfile.GameWindowPosition.Y + 5; + + int minX = posX + 6; + int maxX = posX + minX + ProfileManager.CurrentProfile.GameWindowSize.X - 6; + int minY = posY + 0; + int maxY = minY + ProfileManager.CurrentProfile.GameWindowSize.Y - 6; + + for (TextObject item = (TextObject) TextContainer.Items; item != null; item = (TextObject) item.Next) + { + if (item.RenderedText == null || item.RenderedText.IsDestroyed || item.RenderedText.Texture == null || item.Time < Time.Ticks) + { + continue; + } + + int startX = item.RealScreenPosition.X; + int endX = startX + item.RenderedText.Width; + + if (startX < minX) + { + item.RealScreenPosition.X += minX - startX; + } + + if (endX > maxX) + { + item.RealScreenPosition.X -= endX - maxX; + } + + int startY = item.RealScreenPosition.Y; + // MobileUO: fixed viewport boundary condition check + int endY = startY + item.RenderedText.Height; + + if (startY < minY && offsetY == 0) + { + offsetY = minY - startY; + } + + // MobileUO: fixed viewport boundary condition check + if (endY > maxY) + { + offsetY = -(endY - maxY); + } + + //int endY = startY + item.RenderedText.Height; + + //if (endY > maxY) + // UseInRender = 0xFF; + // //item.RealScreenPosition.Y -= endY - maxY; + + if (offsetY != 0) + { + item.RealScreenPosition.Y += offsetY; + } + } + } + + public void AddMessage + ( + MessageType type, + string text, + byte font, + ushort hue, + bool isunicode, + TextType text_type + ) + { + if (string.IsNullOrEmpty(text)) + { + return; + } + + TextObject msg = MessageManager.CreateMessage + ( + text, + hue, + font, + isunicode, + type, + text_type + ); + + AddMessage(msg); + } + + public void AddMessage(TextObject msg) + { + if (TextContainer == null) + { + TextContainer = new TextContainer(); + } + + msg.Owner = this; + TextContainer.Add(msg); + + if (this is Item it && SerialHelper.IsValid(it.Container)) + { + UpdateTextCoordsV(); + } + else + { + IsPositionChanged = true; + World.WorldTextManager.AddMessage(msg); + } + } + + + protected virtual void OnPositionChanged() + { + } + + protected virtual void OnDirectionChanged() + { + } + + public virtual void Destroy() + { + if (IsDestroyed) + { + return; + } + + Next = null; + Previous = null; + +#if RENDER_LIST_LINKED_LIST + RenderListNext = null; +#endif + + Clear(); + RemoveFromTile(); + TextContainer?.Clear(); + + IsDestroyed = true; + PriorityZ = 0; + IsPositionChanged = false; + Hue = 0; + Offset = Vector3.Zero; + CurrentRenderIndex = 0; + UseInRender = 0; + RealScreenPosition = Point.Zero; + _screenPosition = Point.Zero; + IsFlipped = false; + Graphic = 0; + ObjectHandlesStatus = ObjectHandlesStatus.NONE; + FrameInfo = Rectangle.Empty; + } + + + public static bool CanBeDrawn(ushort g) + { + switch (g) + { + case 0x0001: + case 0x21BC: + //case 0x5690: + return false; + + case 0x9E4C: + case 0x9E64: + case 0x9E65: + case 0x9E7D: + ref StaticTiles data = ref TileDataLoader.Instance.StaticData[g]; + + return !data.IsBackground && !data.IsSurface; + } + + if (g != 0x63D3) + { + if (g >= 0x2198 && g <= 0x21A4) + { + return false; + } + + // Easel fix. + // In older clients the tiledata flag for this + // item contains NoDiagonal for some reason. + // So the next check will make the item invisible. + if (g == 0x0F65 && Client.Version < ClientVersion.CV_60144) + { + return true; + } + + if (g < TileDataLoader.Instance?.StaticData?.Length) + { + ref StaticTiles data = ref TileDataLoader.Instance.StaticData[g]; + + if (!data.IsNoDiagonal || data.IsAnimated && World.Player != null && World.Player.Race == RaceType.GARGOYLE) + { + return true; + } + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Land.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Land.cs index cb60fc512..d651989a4 100644 --- a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Land.cs +++ b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Land.cs @@ -1,48 +1,70 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; -using System.Collections.Generic; using System.Runtime.CompilerServices; using ClassicUO.Game.Managers; using ClassicUO.IO.Resources; +using ClassicUO.Renderer; using ClassicUO.Utility; -using ClassicUO.Utility.Logging; - using Microsoft.Xna.Framework; namespace ClassicUO.Game.GameObjects { internal sealed partial class Land : GameObject { - private static Vector3[,,] _vectCache = new Vector3[3, 3, 4]; - private static readonly QueuedPool _pool = new QueuedPool(Constants.PREDICTABLE_TILE_COUNT, l => - { - l.IsDestroyed = false; - l.AlphaHue = 255; - l.Normal0 = l.Normal1 = l.Normal2 = l.Normal3 = Vector3.Zero; - l.Rectangle = Rectangle.Empty; - l.MinZ = l.AverageZ = 0; - }); + private static readonly QueuedPool _pool = new QueuedPool + ( + Constants.PREDICTABLE_TILE_COUNT, + l => + { + l.IsDestroyed = false; + l.AlphaHue = 255; + l.NormalTop = l.NormalRight = l.NormalLeft = l.NormalBottom = Vector3.Zero; + l.YOffsets.Top = l.YOffsets.Right = l.YOffsets.Left = l.YOffsets.Bottom = 0; + l.MinZ = l.AverageZ = 0; + } + ); + public ref LandTiles TileData => ref TileDataLoader.Instance.LandData[Graphic]; + public sbyte AverageZ; + public bool IsStretched; + + public sbyte MinZ; + + + public Vector3 NormalTop, NormalRight, NormalLeft, NormalBottom; + public ushort OriginalGraphic; + public UltimaBatcher2D.YOffsets YOffsets; public static Land Create(ushort graphic) { @@ -51,26 +73,17 @@ public static Land Create(ushort graphic) land.OriginalGraphic = graphic; land.IsStretched = land.TileData.TexID == 0 && land.TileData.IsWet; land.AllowedToDraw = graphic > 2; + land.UpdateGraphicBySeason(); return land; } - - - public Vector3 Normal0, Normal1, Normal2, Normal3; - public Rectangle Rectangle; - public ushort OriginalGraphic; - - public ref LandTiles TileData => ref TileDataLoader.Instance.LandData[Graphic]; - - public sbyte MinZ; - public sbyte AverageZ; - public bool IsStretched; - public override void Destroy() { if (IsDestroyed) + { return; + } base.Destroy(); _pool.ReturnOne(this); @@ -82,234 +95,180 @@ public override void UpdateGraphicBySeason() AllowedToDraw = Graphic > 2; } - public void UpdateZ(int zTop, int zRight, int zBottom, sbyte currentZ) - { - if (IsStretched) - { - int x = (currentZ << 2) + 1; - int y = (zTop << 2); - int w = (zRight << 2) - x; - int h = (zBottom << 2) + 1 - y; - - Rectangle.X = x; - Rectangle.Y = y; - Rectangle.Width = w; - Rectangle.Height = h; - - if (Math.Abs(currentZ - zRight) <= Math.Abs(zBottom - zTop)) - AverageZ = (sbyte) ((currentZ + zRight) >> 1); - else - AverageZ = (sbyte) ((zBottom + zTop) >> 1); - - MinZ = currentZ; - - if (zTop < MinZ) - MinZ = (sbyte) zTop; - - if (zRight < MinZ) - MinZ = (sbyte) zRight; - - if (zBottom < MinZ) - MinZ = (sbyte) zBottom; - } - } - public int CalculateCurrentAverageZ(int direction) { int result = GetDirectionZ(((byte) (direction >> 1) + 1) & 3); if ((direction & 1) != 0) + { return result; + } return (result + GetDirectionZ(direction >> 1)) >> 1; } - [MethodImpl(256)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] private int GetDirectionZ(int direction) { switch (direction) { - case 1: return Rectangle.Bottom >> 2; - case 2: return Rectangle.Right >> 2; - case 3: return Rectangle.Top >> 2; + case 1: return YOffsets.Right >> 2; + case 2: return YOffsets.Bottom >> 2; + case 3: return YOffsets.Left >> 2; default: return Z; } } - - public void ApplyStrech(int x, int y, sbyte z) + public void ApplyStretch(Map.Map map, int x, int y, sbyte z) { - Map.Map map = World.Map; - - if (IsStretched || TexmapsLoader.Instance.GetTexture(TileData.TexID) == null || !TestStretched(x, y, z, true)) + if (IsStretched || TexmapsLoader.Instance.GetValidRefEntry(TileData.TexID).Length <= 0) { IsStretched = false; + AverageZ = z; MinZ = z; + + return; + } + + /* _____ _____ + * | top | rig | + * |_____|_____| + * | lef | bot | + * |_____|_____| + */ + sbyte zTop = z; + sbyte zRight = map.GetTileZ(x + 1, y); + sbyte zLeft = map.GetTileZ(x, y + 1); + sbyte zBottom = map.GetTileZ(x + 1, y + 1); + + YOffsets.Top = zTop * 4; + YOffsets.Right = zRight * 4; + YOffsets.Left = zLeft * 4; + YOffsets.Bottom = zBottom * 4; + + if (Math.Abs(zTop - zBottom) <= Math.Abs(zLeft - zRight)) + { + AverageZ = (sbyte) ((zTop + zBottom) >> 1); } else { - IsStretched = true; - UpdateZ( - map.GetTileZ(x, y + 1), - map.GetTileZ(x + 1, y + 1), - map.GetTileZ(x + 1, y), - z); - - //Vector3[,,] vec = new Vector3[3, 3, 4]; - - int i; - int j; - - for (i = -1; i < 2; i++) - { - int curX = x + i; - int curI = i + 1; - - for (j = -1; j < 2; j++) - { - int curY = y + j; - int curJ = j + 1; - sbyte currentZ = map.GetTileZ(curX, curY); - sbyte leftZ = map.GetTileZ(curX, curY + 1); - sbyte rightZ = map.GetTileZ(curX + 1, curY); - sbyte bottomZ = map.GetTileZ(curX + 1, curY + 1); - - if (currentZ == leftZ && currentZ == rightZ && currentZ == bottomZ) - { - for (int k = 0; k < 4; k++) - { - ref var v = ref _vectCache[curI, curJ, k]; - v.X = 0; - v.Y = 0; - v.Z = 1; - } - } - else - { - int half_0 = (currentZ - rightZ) << 2; - int half_1 = (leftZ - currentZ) << 2; - int half_2 = (rightZ - bottomZ) << 2; - int half_3 = (bottomZ - leftZ) << 2; - - ref var v0 = ref _vectCache[curI, curJ, 0]; - v0.X = -22; - v0.Y = 22; - v0.Z = half_0; - MergeAndNormalize(ref v0, -22.0f, -22.0f, half_1); - - - ref var v1 = ref _vectCache[curI, curJ, 1]; - v1.X = 22; - v1.Y = 22; - v1.Z = half_2; - MergeAndNormalize(ref v1, -22.0f, 22.0f, half_0); - - ref var v2 = ref _vectCache[curI, curJ, 2]; - v2.X = 22; - v2.Y = -22; - v2.Z = half_3; - MergeAndNormalize(ref v2, 22.0f, 22.0f, half_2); - - ref var v3 = ref _vectCache[curI, curJ, 3]; - v3.X = -22; - v3.Y = -22; - v3.Z = half_1; - MergeAndNormalize(ref v3, 22.0f, -22.0f, half_3); - } - } - } - - i = 1; - j = 1; - - // 0 - SumAndNormalize( - ref _vectCache, - i - 1, j - 1, 2, - i - 1, j, 1, - i, j - 1, 3, - i, j, 0, - out Normal0); - - // 1 - SumAndNormalize( - ref _vectCache, - i, j - 1, 2, - i, j, 1, - i + 1, j - 1, 3, - i + 1, j, 0, - out Normal1); - - // 2 - SumAndNormalize( - ref _vectCache, - i, j, 2, - i, j + 1, 1, - i + 1, j, 3, - i + 1, j + 1, 0, - out Normal2); - - // 3 - SumAndNormalize( - ref _vectCache, - i - 1, j, 2, - i - 1, j + 1, 1, - i, j, 3, - i, j + 1, 0, - out Normal3); + AverageZ = (sbyte) ((zLeft + zRight) >> 1); } - } - - [MethodImpl(256)] - private static void SumAndNormalize( - ref Vector3[,,] vec, - int index0_x, int index0_y, int index0_z, - int index1_x, int index1_y, int index1_z, - int index2_x, int index2_y, int index2_z, - int index3_x, int index3_y, int index3_z, - out Vector3 result) - { - Vector3.Add(ref vec[index0_x, index0_y, index0_z], ref vec[index1_x, index1_y, index1_z], out var v0Result); - Vector3.Add(ref vec[index2_x, index2_y, index2_z], ref vec[index3_x, index3_y, index3_z], out var v1Result); - Vector3.Add(ref v0Result, ref v1Result, out result); - Vector3.Normalize(ref result, out result); + MinZ = Math.Min(zTop, Math.Min(zRight, Math.Min(zLeft, zBottom))); + + + /* _____ _____ _____ _____ + * | | t10 | t20 | | + * |_____|_____|_____|_____| + * | t01 | z | t21 | t31 | + * |_____|_____|_____|_____| + * | t02 | t12 | t22 | t32 | + * |_____|_____|_____|_____| + * | | t13 | t23 | | + * |_____|_____|_____|_____| + */ + sbyte t10 = map.GetTileZ(x, y - 1); + sbyte t20 = map.GetTileZ(x + 1, y - 1); + sbyte t01 = map.GetTileZ(x - 1, y); + sbyte t21 = zRight; + sbyte t31 = map.GetTileZ(x + 2, y); + sbyte t02 = map.GetTileZ(x - 1, y + 1); + sbyte t12 = zLeft; + sbyte t22 = zBottom; + sbyte t32 = map.GetTileZ(x + 2, y + 1); + sbyte t13 = map.GetTileZ(x, y + 2); + sbyte t23 = map.GetTileZ(x + 1, y + 2); + + + IsStretched |= CalculateNormal(z, t10, t21, t12, t01, out NormalTop); + IsStretched |= CalculateNormal(t21, t20, t31, t22, z, out NormalRight); + IsStretched |= CalculateNormal(t22, t21, t32, t23, t12, out NormalBottom); + IsStretched |= CalculateNormal(t12, z, t22, t13, t02, out NormalLeft); } - private static bool TestStretched(int x, int y, sbyte z, bool recurse) + private static bool CalculateNormal(sbyte tile, sbyte top, sbyte right, sbyte bottom, sbyte left, out Vector3 normal) { - bool result = false; - - for (int i = -1; i < 2 && !result; i++) + if (tile == top && tile == right && tile == bottom && tile == left) { - for (int j = -1; j < 2 && !result; j++) - { - if (recurse) - result = TestStretched(x + i, y + j, z, false); - else - { - sbyte testZ = World.Map.GetTileZ(x + i, y + j); - result = testZ != z && testZ != -125; - } - } + normal.X = 0; + normal.Y = 0; + normal.Z = 1f; + + return false; } - return result; - } + Vector3 u = new Vector3(); + Vector3 v = new Vector3(); + Vector3 ret = new Vector3(); - [MethodImpl(256)] - private static void MergeAndNormalize(ref Vector3 v, float x, float y, float z) - { - float newX = v.Y * z - v.Z * y; - float newY = v.Z * x - v.X * z; - float newZ = v.X * y - v.Y * x; - v.X = newX; - v.Y = newY; - v.Z = newZ; - - Vector3.Normalize(ref v, out v); + + // ========================== + u.X = -22; + u.Y = -22; + u.Z = (left - tile) * 4; + + v.X = -22; + v.Y = 22; + v.Z = (bottom - tile) * 4; + + Vector3.Cross(ref v, ref u, out ret); + + //Vector3.Cross(ref v, ref u, out normal); + //Vector3.Normalize(ref normal, out ret); + // ========================== + + + // ========================== + u.X = -22; + u.Y = 22; + u.Z = (bottom - tile) * 4; + + v.X = 22; + v.Y = 22; + v.Z = (right - tile) * 4; + + Vector3.Cross(ref v, ref u, out normal); + //Vector3.Normalize(ref normal, out normal); + Vector3.Add(ref ret, ref normal, out ret); + // ========================== + + + // ========================== + u.X = 22; + u.Y = 22; + u.Z = (right - tile) * 4; + + v.X = 22; + v.Y = -22; + v.Z = (top - tile) * 4; + + Vector3.Cross(ref v, ref u, out normal); + //Vector3.Normalize(ref normal, out normal); + Vector3.Add(ref ret, ref normal, out ret); + // ========================== + + + // ========================== + u.X = 22; + u.Y = -22; + u.Z = (top - tile) * 4; + + v.X = -22; + v.Y = -22; + v.Z = (left - tile) * 4; + + Vector3.Cross(ref v, ref u, out normal); + //Vector3.Normalize(ref normal, out normal); + Vector3.Add(ref ret, ref normal, out ret); + // ========================== + + Vector3.Normalize(ref ret, out normal); + + return true; } + // MobileUO: added Dispose public static void Dispose() { foreach (var land in _pool._pool) @@ -320,4 +279,4 @@ public static void Dispose() } } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/AnimatedEffectView.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/AnimatedEffectView.cs deleted file mode 120000 index 88ece14cb..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/AnimatedEffectView.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/GameObjects/Views/AnimatedEffectView.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/GameEffectView.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/GameEffectView.cs new file mode 120000 index 000000000..242c3fe54 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/GameEffectView.cs @@ -0,0 +1 @@ +../../../../../../../external/ClassicUO/src/Game/GameObjects/Views/GameEffectView.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/ItemView.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/ItemView.cs deleted file mode 120000 index 05ee2a1b2..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/ItemView.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/GameObjects/Views/ItemView.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/ItemView.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/ItemView.cs new file mode 100644 index 000000000..793eea8cb --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/ItemView.cs @@ -0,0 +1,466 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System.Collections.Generic; +using ClassicUO.Configuration; +using ClassicUO.Game.Data; +using ClassicUO.Game.Managers; +using ClassicUO.Game.Scenes; +using ClassicUO.IO; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; +using MathHelper = ClassicUO.Utility.MathHelper; + +namespace ClassicUO.Game.GameObjects +{ + internal partial class Item + { + private static EquipConvData? _equipConvData; + + public override bool Draw(UltimaBatcher2D batcher, int posX, int posY, ref Vector3 hueVec) + { + if (!AllowedToDraw || IsDestroyed) + { + return false; + } + + //Engine.DebugInfo.ItemsRendered++; + + hueVec = Vector3.Zero; + + DrawTransparent = false; + + posX += (int) Offset.X; + posY += (int) (Offset.Y + Offset.Z); + + if (ItemData.IsTranslucent) + { + hueVec.Z = 0.5f; + } + + if (AlphaHue != 255) + { + hueVec.Z = 1f - AlphaHue / 255f; + } + + if (IsCorpse) + { + return DrawCorpse(batcher, posX, posY - 3, ref hueVec); + } + + + ushort hue = Hue; + ushort graphic = DisplayedGraphic; + bool partial = ItemData.IsPartialHue; + + if (OnGround) + { + if (ItemData.IsAnimated) + { + if (ProfileManager.CurrentProfile.FieldsType == 2) + { + if (StaticFilters.IsFireField(Graphic)) + { + graphic = Constants.FIELD_REPLACE_GRAPHIC; + hue = 0x0020; + } + else if (StaticFilters.IsParalyzeField(Graphic)) + { + graphic = Constants.FIELD_REPLACE_GRAPHIC; + hue = 0x0058; + } + else if (StaticFilters.IsEnergyField(Graphic)) + { + graphic = Constants.FIELD_REPLACE_GRAPHIC; + hue = 0x0070; + } + else if (StaticFilters.IsPoisonField(Graphic)) + { + graphic = Constants.FIELD_REPLACE_GRAPHIC; + hue = 0x0044; + } + else if (StaticFilters.IsWallOfStone(Graphic)) + { + graphic = Constants.FIELD_REPLACE_GRAPHIC; + hue = 0x038A; + } + } + } + + if (ItemData.IsContainer && SelectedObject.SelectedContainer == this) + { + hue = 0x0035; + partial = false; + } + } + + if (ProfileManager.CurrentProfile.HighlightGameObjects && ReferenceEquals(SelectedObject.LastObject, this)) + { + hue = Constants.HIGHLIGHT_CURRENT_OBJECT_HUE; + partial = false; + } + else if (ProfileManager.CurrentProfile.NoColorObjectsOutOfRange && Distance > World.ClientViewRange) + { + hue = Constants.OUT_RANGE_COLOR; + } + else if (World.Player.IsDead && ProfileManager.CurrentProfile.EnableBlackWhiteEffect) + { + hue = Constants.DEAD_RANGE_COLOR; + } + else + { + if (!IsLocked && !IsMulti && ReferenceEquals(SelectedObject.LastObject, this)) + { + // TODO: check why i put this. + //isPartial = ItemData.Weight == 0xFF; + hue = 0x0035; + } + else if (IsHidden) + { + hue = 0x038E; + } + } + + ShaderHueTranslator.GetHueVector(ref hueVec, hue, partial, hueVec.Z); + + if (!IsMulti && !IsCoin && Amount > 1 && ItemData.IsStackable) + { + DrawStaticAnimated + ( + batcher, + graphic, + posX - 5, + posY - 5, + ref hueVec, + ref DrawTransparent, + false + ); + } + + if (ItemData.IsLight || graphic >= 0x3E02 && graphic <= 0x3E0B || graphic >= 0x3914 && graphic <= 0x3929) + { + Client.Game.GetScene().AddLight(this, this, posX + 22, posY + 22); + } + + if (!SerialHelper.IsValid(Serial) && IsMulti && TargetManager.TargetingState == CursorTarget.MultiPlacement) + { + hueVec.Z = 0.5f; + } + + DrawStaticAnimated + ( + batcher, + graphic, + posX, + posY, + ref hueVec, + ref DrawTransparent, + false + ); + + if (ReferenceEquals(SelectedObject.Object, this) || TargetManager.TargetingState == CursorTarget.MultiPlacement) + { + return false; + } + + ArtTexture texture = ArtLoader.Instance.GetTexture(graphic); + + if (texture != null) + { + ref UOFileIndex index = ref ArtLoader.Instance.GetValidRefEntry(graphic + 0x4000); + + posX -= index.Width; + posY -= index.Height; + + if (ArtLoader.Instance.PixelCheck + ( + graphic, + SelectedObject.TranslatedMousePositionByViewport.X - posX, + SelectedObject.TranslatedMousePositionByViewport.Y - posY + )) + { + SelectedObject.Object = this; + } + else if (!IsMulti && !IsCoin && Amount > 1 && ItemData.IsStackable) + { + if (ArtLoader.Instance.PixelCheck + ( + graphic, + SelectedObject.TranslatedMousePositionByViewport.X - posX + 5, + SelectedObject.TranslatedMousePositionByViewport.Y - posY + 5 + )) + { + SelectedObject.Object = this; + } + } + } + + return true; + } + + private bool DrawCorpse(UltimaBatcher2D batcher, int posX, int posY, ref Vector3 hueVec) + { + if (IsDestroyed || World.CorpseManager.Exists(Serial, 0)) + { + return false; + } + + posX += 22; + posY += 22; + + byte direction = (byte) ((byte) Layer & 0x7F & 7); + AnimationsLoader.Instance.GetAnimDirection(ref direction, ref IsFlipped); + + byte animIndex = (byte) AnimIndex; + ushort graphic = GetGraphicForAnimation(); + AnimationsLoader.Instance.ConvertBodyIfNeeded(ref graphic); + byte group = AnimationsLoader.Instance.GetDieGroupIndex(graphic, UsedLayer); + + bool ishuman = MathHelper.InRange(Amount, 0x0190, 0x0193) || MathHelper.InRange(Amount, 0x00B7, 0x00BA) || MathHelper.InRange(Amount, 0x025D, 0x0260) || MathHelper.InRange(Amount, 0x029A, 0x029B) || MathHelper.InRange(Amount, 0x02B6, 0x02B7) || Amount == 0x03DB || Amount == 0x03DF || Amount == 0x03E2 || Amount == 0x02E8 || Amount == 0x02E9; + + DrawLayer + ( + batcher, + posX, + posY, + this, + Layer.Invalid, + animIndex, + ishuman, + Hue, + IsFlipped, + hueVec.Z, + group, + direction, + ref hueVec + ); + + for (int i = 0; i < Constants.USED_LAYER_COUNT; i++) + { + Layer layer = LayerOrder.UsedLayers[direction, i]; + + DrawLayer + ( + batcher, + posX, + posY, + this, + layer, + animIndex, + ishuman, + 0, + IsFlipped, + hueVec.Z, + group, + direction, + ref hueVec + ); + } + + return true; + } + + private static void DrawLayer + ( + UltimaBatcher2D batcher, + int posX, + int posY, + Item owner, + Layer layer, + byte animIndex, + bool ishuman, + ushort color, + bool flipped, + float alpha, + byte animGroup, + byte dir, + ref Vector3 hueVec + ) + { + _equipConvData = null; + bool ispartialhue = false; + + ushort graphic; + + if (layer == Layer.Invalid) + { + graphic = owner.GetGraphicForAnimation(); + } + else if (ishuman) + { + Item itemEquip = owner.FindItemByLayer(layer); + + if (itemEquip == null) + { + return; + } + + graphic = itemEquip.ItemData.AnimID; + ispartialhue = itemEquip.ItemData.IsPartialHue; + + if (AnimationsLoader.Instance.EquipConversions.TryGetValue(graphic, out Dictionary map)) + { + if (map.TryGetValue(graphic, out EquipConvData data)) + { + _equipConvData = data; + graphic = data.Graphic; + } + } + + color = itemEquip.Hue; + } + else + { + return; + } + + ushort newHue = 0; + + AnimationGroup gr = layer == Layer.Invalid ? AnimationsLoader.Instance.GetCorpseAnimationGroup(ref graphic, ref animGroup, ref newHue) : AnimationsLoader.Instance.GetBodyAnimationGroup(ref graphic, ref animGroup, ref newHue); + + if (color == 0) + { + color = newHue; + } + + AnimationDirection direction = gr.Direction[dir]; + + if (direction == null) + { + return; + } + + if ((direction.FrameCount == 0 || direction.Frames == null) && !AnimationsLoader.Instance.LoadAnimationFrames(graphic, animGroup, dir, ref direction)) + { + return; + } + + direction.LastAccessTime = Time.Ticks; + int fc = direction.FrameCount; + + if (fc > 0 && animIndex >= fc) + { + animIndex = (byte) (fc - 1); + } + + if (animIndex < direction.FrameCount) + { + AnimationFrameTexture frame = direction.Frames[animIndex]; + + if (frame == null || frame.IsDisposed) + { + return; + } + + frame.Ticks = Time.Ticks; + + if (flipped) + { + posX -= frame.Width - frame.CenterX; + } + else + { + posX -= frame.CenterX; + } + + posY -= frame.Height + frame.CenterY; + + + if (color == 0) + { + if ((color & 0x8000) != 0) + { + ispartialhue = true; + color &= 0x7FFF; + } + + if (color == 0 && _equipConvData.HasValue) + { + color = _equipConvData.Value.Color; + ispartialhue = false; + } + } + + hueVec = Vector3.Zero; + + if (ProfileManager.CurrentProfile.NoColorObjectsOutOfRange && owner.Distance > World.ClientViewRange) + { + hueVec.X = Constants.OUT_RANGE_COLOR; + hueVec.Y = 1; + } + else if (World.Player.IsDead && ProfileManager.CurrentProfile.EnableBlackWhiteEffect) + { + hueVec.X = Constants.DEAD_RANGE_COLOR; + hueVec.Y = 1; + } + else + { + if (ProfileManager.CurrentProfile.GridLootType > 0 && SelectedObject.CorpseObject == owner) + { + color = 0x0034; + } + else if (ProfileManager.CurrentProfile.HighlightGameObjects && ReferenceEquals(SelectedObject.LastObject, owner)) + { + color = Constants.HIGHLIGHT_CURRENT_OBJECT_HUE; + } + + ShaderHueTranslator.GetHueVector(ref hueVec, color, ispartialhue, alpha); + } + + batcher.DrawSprite + ( + frame, + posX, + posY, + flipped, + ref hueVec + ); + + if (!SerialHelper.IsValid(owner)) + { + return; + } + + if (ReferenceEquals(SelectedObject.Object, owner)) + { + return; + } + + if (AnimationsLoader.Instance.PixelCheck(graphic, animGroup, dir, direction.IsUOP, animIndex, flipped ? posX + frame.Width - SelectedObject.TranslatedMousePositionByViewport.X : SelectedObject.TranslatedMousePositionByViewport.X - posX, SelectedObject.TranslatedMousePositionByViewport.Y - posY)) + { + SelectedObject.Object = owner; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/MovingEffectView.cs b/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/MovingEffectView.cs deleted file mode 120000 index 7e208a338..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/GameObjects/Views/MovingEffectView.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/GameObjects/Views/MovingEffectView.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/AnimatedStaticsManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/AnimatedStaticsManager.cs deleted file mode 100644 index 3f0edcaec..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/Managers/AnimatedStaticsManager.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -using ClassicUO.Configuration; -using ClassicUO.Game.Data; -using ClassicUO.IO.Resources; -using ClassicUO.Utility.Collections; - -namespace ClassicUO.Game.Managers -{ - static class AnimatedStaticsManager - { - private static RawList _static_infos; - - public static uint ProcessTime; - - - - - public static unsafe void Initialize() - { - if (_static_infos != null) - return; - - _static_infos = new RawList(); - var file = AnimDataLoader.Instance.AnimDataFile; - - if (file == null) - return; - - var startAddr = file.StartAddress.ToInt64(); - uint lastaddr = (uint) (startAddr + file.Length - sizeof(AnimDataFrame2)); - - for (int i = 0; i < TileDataLoader.Instance.StaticData.Length; i++) - { - if (TileDataLoader.Instance.StaticData[i].IsAnimated) - { - var addr = (uint) ((i * 68) + 4 * ((i / 8) + 1)); - uint offset = (uint) (startAddr + addr); - - if (offset <= lastaddr) - { - _static_infos.Add(new static_animation_info() - { - index = (ushort) i, - is_field = StaticFilters.IsField((ushort) i) - }); - } - } - } - } - - public static unsafe void Process() - { - if (_static_infos == null || _static_infos.Count == 0 || ProcessTime >= Time.Ticks) - { - return; - } - - var file = AnimDataLoader.Instance.AnimDataFile; - - if (file == null) - return; - - // fix static animations time to reflect the standard client - uint delay = Constants.ITEM_EFFECT_ANIMATION_DELAY * 2; - uint next_time = Time.Ticks + 250; - bool no_animated_field = ProfileManager.Current != null && ProfileManager.Current.FieldsType != 0; - var startAddr = file.StartAddress.ToInt64(); - var static_data = ArtLoader.Instance.Entries; - - for (int i = 0; i < _static_infos.Count; i++) - { - ref var o = ref _static_infos[i]; - - if (no_animated_field && o.is_field) - { - o.anim_index = 0; - continue; - } - - if (o.time < Time.Ticks) - { - var addr = (uint) ((o.index * 68) + 4 * ((o.index / 8) + 1)); - AnimDataFrame2* info = (AnimDataFrame2*) (startAddr + addr); - - byte offset = o.anim_index; - - if (info->FrameInterval > 0) - { - o.time = Time.Ticks + (info->FrameInterval * delay) + 1; - } - else - { - o.time = Time.Ticks + delay; - } - - if (offset < info->FrameCount && o.index + 0x4000 < static_data.Length) - { - static_data[o.index + 0x4000].AnimOffset = info->FrameData[offset++]; - } - - if (offset >= info->FrameCount) - { - offset = 0; - } - - o.anim_index = offset; - } - - if (o.time < next_time) - { - next_time = o.time; - } - } - - ProcessTime = next_time; - } - - - - private struct static_animation_info - { - public uint time; - public ushort index; - public byte anim_index; - public bool is_field; - } - } -} diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/AnimatedStaticsManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/AnimatedStaticsManager.cs new file mode 120000 index 000000000..3b55a0f51 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/AnimatedStaticsManager.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/AnimatedStaticsManager.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/AudioManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/AudioManager.cs index f025a24ea..ddbe5ac08 100644 --- a/Assets/Scripts/ClassicUO/src/Game/Managers/AudioManager.cs +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/AudioManager.cs @@ -1,29 +1,39 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.Collections.Generic; - +using ClassicUO.Data; using ClassicUO.Configuration; -using ClassicUO.Game.Scenes; using ClassicUO.IO.Audio; using ClassicUO.IO.Resources; using ClassicUO.Utility.Logging; @@ -33,11 +43,12 @@ namespace ClassicUO.Game.Managers { internal class AudioManager { - private UOMusic[] _currentMusic = { null, null }; - private LinkedList _current_sounds = new LinkedList(); private bool _canReproduceAudio = true; - private int[] _currentMusicIndices = { 0, 0 }; - + private readonly LinkedList _currentSounds = new LinkedList(); + private readonly UOMusic[] _currentMusic = { null, null }; + private readonly int[] _currentMusicIndices = { 0, 0 }; + public int LoginMusicIndex { get; private set; } + public int DeathMusicIndex { get; } = 42; public void Initialize() { @@ -47,50 +58,70 @@ public void Initialize() } catch (NoAudioHardwareException ex) { - Log.Warn( ex.ToString()); + Log.Warn(ex.ToString()); _canReproduceAudio = false; } + LoginMusicIndex = Client.Version >= ClientVersion.CV_7000 ? 78 : Client.Version > ClientVersion.CV_308Z ? 0 : 8; + Client.Game.Activated += OnWindowActivated; Client.Game.Deactivated += OnWindowDeactivated; } - private void OnWindowDeactivated(object sender, System.EventArgs e) + private void OnWindowDeactivated(object sender, EventArgs e) { - if (ProfileManager.Current == null || ProfileManager.Current.ReproduceSoundsInBackground) return; + if (!_canReproduceAudio || ProfileManager.CurrentProfile == null || ProfileManager.CurrentProfile.ReproduceSoundsInBackground) + { + return; + } SoundEffect.MasterVolume = 0; } - private void OnWindowActivated(object sender, System.EventArgs e) + private void OnWindowActivated(object sender, EventArgs e) { - if (ProfileManager.Current == null || ProfileManager.Current.ReproduceSoundsInBackground) return; + if (!_canReproduceAudio || ProfileManager.CurrentProfile == null || ProfileManager.CurrentProfile.ReproduceSoundsInBackground) + { + return; + } SoundEffect.MasterVolume = 1; } public void PlaySound(int index) { - if (!_canReproduceAudio || ProfileManager.Current == null) + Profile currentProfile = ProfileManager.CurrentProfile; + + if (!_canReproduceAudio || currentProfile == null) + { return; + } - float volume = ProfileManager.Current.SoundVolume / Constants.SOUND_DELTA; + float volume = currentProfile.SoundVolume / Constants.SOUND_DELTA; if (Client.Game.IsActive) { - if (!ProfileManager.Current.ReproduceSoundsInBackground) - volume = ProfileManager.Current.SoundVolume / Constants.SOUND_DELTA; + if (!currentProfile.ReproduceSoundsInBackground) + { + volume = currentProfile.SoundVolume / Constants.SOUND_DELTA; + } } - else if (!ProfileManager.Current.ReproduceSoundsInBackground) + else if (!currentProfile.ReproduceSoundsInBackground) + { volume = 0; + } if (volume < -1 || volume > 1f) + { return; + } - if (!ProfileManager.Current.EnableSound || !Client.Game.IsActive && !ProfileManager.Current.ReproduceSoundsInBackground) + if (!currentProfile.EnableSound || !Client.Game.IsActive && !currentProfile.ReproduceSoundsInBackground) + { volume = 0; + } - UOSound sound = (UOSound)SoundsLoader.Instance.GetSound(index); + UOSound sound = (UOSound) SoundsLoader.Instance.GetSound(index); if (sound != null && sound.Play(volume)) { @@ -98,20 +129,23 @@ public void PlaySound(int index) sound.Y = -1; sound.CalculateByDistance = false; - _current_sounds.AddLast(sound); + _currentSounds.AddLast(sound); } } public void PlaySoundWithDistance(int index, int x, int y) { if (!_canReproduceAudio || !World.InGame) + { return; + } int distX = Math.Abs(x - World.Player.X); int distY = Math.Abs(y - World.Player.Y); int distance = Math.Max(distX, distY); - float volume = ProfileManager.Current.SoundVolume / Constants.SOUND_DELTA; + Profile currentProfile = ProfileManager.CurrentProfile; + float volume = currentProfile.SoundVolume / Constants.SOUND_DELTA; float distanceFactor = 0.0f; if (distance >= 1) @@ -126,10 +160,14 @@ public void PlaySoundWithDistance(int index, int x, int y) } if (volume < -1 || volume > 1f) + { return; + } - if (ProfileManager.Current == null || !ProfileManager.Current.EnableSound || !Client.Game.IsActive && !ProfileManager.Current.ReproduceSoundsInBackground) + if (currentProfile == null || !currentProfile.EnableSound || !Client.Game.IsActive && !currentProfile.ReproduceSoundsInBackground) + { volume = 0; + } UOSound sound = (UOSound) SoundsLoader.Instance.GetSound(index); @@ -139,17 +177,21 @@ public void PlaySoundWithDistance(int index, int x, int y) sound.Y = y; sound.CalculateByDistance = true; - _current_sounds.AddLast(sound); + _currentSounds.AddLast(sound); } } public void PlayMusic(int music, bool iswarmode = false, bool is_login = false) { if (!_canReproduceAudio) + { return; + } if (music >= Constants.MAX_MUSIC_DATA_INDEX_COUNT) + { return; + } float volume; @@ -159,16 +201,18 @@ public void PlayMusic(int music, bool iswarmode = false, bool is_login = false) } else { - if (ProfileManager.Current == null || !ProfileManager.Current.EnableMusic) + Profile currentProfile = ProfileManager.CurrentProfile; + + if (currentProfile == null || !currentProfile.EnableMusic) { volume = 0; } else { - volume = ProfileManager.Current.MusicVolume / Constants.SOUND_DELTA; + volume = currentProfile.MusicVolume / Constants.SOUND_DELTA; } - if (ProfileManager.Current != null && !ProfileManager.Current.EnableCombatMusic && iswarmode) + if (currentProfile != null && !currentProfile.EnableCombatMusic && iswarmode) { return; } @@ -176,7 +220,9 @@ public void PlayMusic(int music, bool iswarmode = false, bool is_login = false) if (volume < -1 || volume > 1f) + { return; + } Sound m = SoundsLoader.Instance.GetMusic(music); @@ -191,14 +237,17 @@ public void PlayMusic(int music, bool iswarmode = false, bool is_login = false) int idx = iswarmode ? 1 : 0; _currentMusicIndices[idx] = music; _currentMusic[idx] = (UOMusic) m; + _currentMusic[idx].Play(volume); } } - public void UpdateCurrentMusicVolume(bool is_login = false) + public void UpdateCurrentMusicVolume(bool isLogin = false) { if (!_canReproduceAudio) + { return; + } for (int i = 0; i < 2; i++) { @@ -206,39 +255,47 @@ public void UpdateCurrentMusicVolume(bool is_login = false) { float volume; - if (is_login) + if (isLogin) { volume = Settings.GlobalSettings.LoginMusic ? Settings.GlobalSettings.LoginMusicVolume / Constants.SOUND_DELTA : 0; } else { - volume = ProfileManager.Current == null || !ProfileManager.Current.EnableMusic ? - 0 : - ProfileManager.Current.MusicVolume / Constants.SOUND_DELTA; + Profile currentProfile = ProfileManager.CurrentProfile; + + volume = currentProfile == null || !currentProfile.EnableMusic ? 0 : currentProfile.MusicVolume / Constants.SOUND_DELTA; } - + if (volume < -1 || volume > 1f) + { return; + } _currentMusic[i].Volume = i == 0 && _currentMusic[1] != null ? 0 : volume; } - } + } } public void UpdateCurrentSoundsVolume() { if (!_canReproduceAudio) + { return; + } - float volume = ProfileManager.Current == null || !ProfileManager.Current.EnableSound ? 0 : ProfileManager.Current.SoundVolume / Constants.SOUND_DELTA; + Profile currentProfile = ProfileManager.CurrentProfile; + + float volume = currentProfile == null || !currentProfile.EnableSound ? 0 : currentProfile.SoundVolume / Constants.SOUND_DELTA; if (volume < -1 || volume > 1f) + { return; + } - for (var s = _current_sounds.First; s != null; s = s.Next) + for (LinkedListNode soundNode = _currentSounds.First; soundNode != null; soundNode = soundNode.Next) { - s.Value.Volume = volume; + soundNode.Value.Volume = volume; } } @@ -254,8 +311,8 @@ public void StopMusic() } } - //NOTE: Also discard sounds - foreach (var sound in _current_sounds) + //MobileUO: NOTE: Also discard sounds + foreach (UOSound sound in _currentSounds) { if (sound != null) { @@ -263,7 +320,7 @@ public void StopMusic() sound.Dispose(); } } - _current_sounds.Clear(); + _currentSounds.Clear(); DynamicSoundEffectInstance.DisposePool(); } @@ -275,15 +332,15 @@ public void StopWarMusic() public void StopSounds() { - var first = _current_sounds.First; + LinkedListNode first = _currentSounds.First; while (first != null) { - var next = first.Next; + LinkedListNode next = first.Next; first.Value.Stop(); - _current_sounds.Remove(first); + _currentSounds.Remove(first); first = next; } @@ -292,41 +349,60 @@ public void StopSounds() public void Update() { if (!_canReproduceAudio) + { return; + } bool runninWarMusic = _currentMusic[1] != null; + Profile currentProfile = ProfileManager.CurrentProfile; for (int i = 0; i < 2; i++) { - if (_currentMusic[i] != null && ProfileManager.Current != null) + if (_currentMusic[i] != null && currentProfile != null) { if (Client.Game.IsActive) { - if (!ProfileManager.Current.ReproduceSoundsInBackground) - _currentMusic[i].Volume = (i == 0 && runninWarMusic) || !ProfileManager.Current.EnableMusic ? 0 : ProfileManager.Current.MusicVolume / Constants.SOUND_DELTA; + if (!currentProfile.ReproduceSoundsInBackground) + { + _currentMusic[i].Volume = i == 0 && runninWarMusic || !currentProfile.EnableMusic ? 0 : currentProfile.MusicVolume / Constants.SOUND_DELTA; + } } - else if (!ProfileManager.Current.ReproduceSoundsInBackground && _currentMusic[i].Volume != 0.0f) + else if (!currentProfile.ReproduceSoundsInBackground && _currentMusic[i].Volume != 0.0f) + { _currentMusic[i].Volume = 0; + } } _currentMusic[i]?.Update(); } - - var first = _current_sounds.First; + + LinkedListNode first = _currentSounds.First; while (first != null) { - var next = first.Next; + LinkedListNode next = first.Next; if (!first.Value.IsPlaying) { first.Value.Stop(); - _current_sounds.Remove(first); + _currentSounds.Remove(first); } first = next; } } + + public UOMusic GetCurrentMusic() + { + for (int i = 0; i < 2; i++) + { + if (_currentMusic[i] != null && _currentMusic[i].IsPlaying) + { + return _currentMusic[i]; + } + } + return null; + } } } \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/AuraManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/AuraManager.cs index cfd9011ec..79bff0542 100644 --- a/Assets/Scripts/ClassicUO/src/Game/Managers/AuraManager.cs +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/AuraManager.cs @@ -1,41 +1,112 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion +using System; using ClassicUO.Configuration; using ClassicUO.Input; using ClassicUO.Renderer; - using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace ClassicUO.Game.Managers { - internal static class AuraManager + class Aura : IDisposable { - /*private readonly BlendState _blend = new BlendState + private static readonly Lazy _blend = new Lazy + ( + () => new BlendState + { + ColorSourceBlend = Blend.SourceAlpha, + ColorDestinationBlend = Blend.InverseSourceAlpha + } + ); + + private readonly Texture2D _texture; + + + public Aura(int radius) + { + short w = 0; + short h = 0; + uint[] data = CircleOfTransparency.CreateCircleTexture(radius, ref w, ref h); + + for (int i = 0; i < data.Length; i++) + { + ref uint pixel = ref data[i]; + + if (pixel != 0) + { + ushort value = (ushort)(pixel << 3); + + if (value > 0xFF) + { + value = 0xFF; + } + + pixel = (uint)((value << 24) | (value << 16) | (value << 8) | value); + } + } + + _texture = new Texture2D(Client.Game.GraphicsDevice, w, h); + _texture.SetData(data); + } + + + + public void Draw(UltimaBatcher2D batcher, int x, int y, ushort hue) + { + x -= (_texture.Width >> 1); + y -= (_texture.Height >> 1); + + Vector3 hueVec = new Vector3(hue, 1, 0); + + batcher.SetBlendState(_blend.Value); + batcher.Draw2D(_texture, x, y, ref hueVec); + batcher.SetBlendState(null); + } + + + public void Dispose() { - ColorSourceBlend = Blend.SourceAlpha, - ColorDestinationBlend = Blend.InverseSourceAlpha - };*/ - private static Vector3 _auraHueVector = new Vector3(0, 13, 0); + if (_texture != null && !_texture.IsDisposed) + { + _texture.Dispose(); + } + } + } + + internal static class AuraManager + { + private static readonly Aura _aura = new Aura(30); private static int _saveAuraUnderFeetType; @@ -43,13 +114,16 @@ public static bool IsEnabled { get { - if (ProfileManager.Current == null) + if (ProfileManager.CurrentProfile == null) + { return false; + } - switch (ProfileManager.Current.AuraUnderFeetType) + switch (ProfileManager.CurrentProfile.AuraUnderFeetType) { default: case 0: return false; + case 1 when World.Player != null && World.Player.InWarMode: return true; case 2 when Keyboard.Ctrl && Keyboard.Shift: return true; case 3: return true; @@ -57,62 +131,30 @@ public static bool IsEnabled } } - public static Texture2D AuraTexture { get; private set; } - public static void ToggleVisibility() { + Profile currentProfile = ProfileManager.CurrentProfile; + if (!IsEnabled) { - _saveAuraUnderFeetType = ProfileManager.Current.AuraUnderFeetType; - ProfileManager.Current.AuraUnderFeetType = 3; + _saveAuraUnderFeetType = currentProfile.AuraUnderFeetType; + currentProfile.AuraUnderFeetType = 3; } else - ProfileManager.Current.AuraUnderFeetType = _saveAuraUnderFeetType; - } - - public static void CreateAuraTexture(int radius = 30) - { - AuraTexture?.Dispose(); - - short w = 0; - short h = 0; - uint[] data = CircleOfTransparency.CreateCircleTexture(radius, ref w, ref h); - - for (int i = 0; i < data.Length; i++) { - ref uint pixel = ref data[i]; - - if (pixel != 0) - { - ushort value = (ushort) (pixel << 3); - - if (value > 0xFF) - value = 0xFF; - - pixel = (uint) ((value << 24) | (value << 16) | (value << 8) | value); - } + currentProfile.AuraUnderFeetType = _saveAuraUnderFeetType; } - - AuraTexture = new Texture2D(Client.Game.GraphicsDevice, w, h); - AuraTexture.SetData(data); } public static void Draw(UltimaBatcher2D batcher, int x, int y, ushort hue) { - x -= AuraTexture.Width >> 1; - y -= AuraTexture.Height >> 1; - - _auraHueVector.X = hue; - - //batcher.SetBlendState(_blend); - batcher.Draw2D(AuraTexture, x, y, ref _auraHueVector); - //batcher.SetBlendState(null); + _aura.Draw(batcher, x, y, hue); } + // MobileUO: added Dispose public static void Dispose() { - AuraTexture?.Dispose(); - AuraTexture = null; + _aura?.Dispose(); } } } \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/ChatChannel.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/ChatChannel.cs new file mode 120000 index 000000000..a6660c706 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/ChatChannel.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/ChatChannel.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/ChatManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/ChatManager.cs new file mode 120000 index 000000000..dae46a226 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/ChatManager.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/ChatManager.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/ChatStatus.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/ChatStatus.cs new file mode 120000 index 000000000..dc84fe9df --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/ChatStatus.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/ChatStatus.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/HealthLinesManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/HealthLinesManager.cs deleted file mode 120000 index 502d74337..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/Managers/HealthLinesManager.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../external/ClassicUO/src/Game/Managers/HealthLinesManager.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/HealthLinesManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/HealthLinesManager.cs new file mode 100644 index 000000000..4b9fdeada --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/HealthLinesManager.cs @@ -0,0 +1,358 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using ClassicUO.Configuration; +using ClassicUO.Game.Data; +using ClassicUO.Game.GameObjects; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; + +namespace ClassicUO.Game.Managers +{ + internal class HealthLinesManager + { + private const int BAR_WIDTH = 34; //28; + private const int BAR_HEIGHT = 8; + private const int BAR_WIDTH_HALF = BAR_WIDTH >> 1; + private const int BAR_HEIGHT_HALF = BAR_HEIGHT >> 1; + + + private readonly UOTexture _background_texture, _hp_texture; + private Vector3 _vectorHue = Vector3.Zero; + + public HealthLinesManager() + { + _background_texture = GumpsLoader.Instance.GetTexture(0x1068); + _hp_texture = GumpsLoader.Instance.GetTexture(0x1069); + } + + public bool IsEnabled => ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.ShowMobilesHP; + + + public void Update() + { + if (_background_texture != null) + { + _background_texture.Ticks = Time.Ticks; + } + + if (_hp_texture != null) + { + _hp_texture.Ticks = Time.Ticks; + } + } + + public void Draw(UltimaBatcher2D batcher) + { + int screenW = ProfileManager.CurrentProfile.GameWindowSize.X; + int screenH = ProfileManager.CurrentProfile.GameWindowSize.Y; + // MobileUO: fixed viewport boundary condition check + int posX = ProfileManager.CurrentProfile.GameWindowPosition.X + 5; + int posY = ProfileManager.CurrentProfile.GameWindowPosition.Y + 5; + + if (SerialHelper.IsMobile(TargetManager.LastTargetInfo.Serial)) + { + DrawHealthLineWithMath(batcher, TargetManager.LastTargetInfo.Serial, screenW, screenH); + } + + if (SerialHelper.IsMobile(TargetManager.SelectedTarget)) + { + DrawHealthLineWithMath(batcher, TargetManager.SelectedTarget, screenW, screenH); + } + + if (SerialHelper.IsMobile(TargetManager.LastAttack)) + { + DrawHealthLineWithMath(batcher, TargetManager.LastAttack, screenW, screenH); + } + + if (!IsEnabled) + { + return; + } + + int mode = ProfileManager.CurrentProfile.MobileHPType; + + if (mode < 0) + { + return; + } + + int showWhen = ProfileManager.CurrentProfile.MobileHPShowWhen; + + foreach (Mobile mobile in World.Mobiles.Values) + { + if (mobile.IsDestroyed) + { + continue; + } + + int current = mobile.Hits; + int max = mobile.HitsMax; + + if (max == 0) + { + continue; + } + + if (showWhen == 1 && current == max) + { + continue; + } + + Point p = mobile.RealScreenPosition; + p.X += (int) mobile.Offset.X + 22 + 5; + p.Y += (int) (mobile.Offset.Y - mobile.Offset.Z) + 22 + 5; + + + if (mode != 1 && !mobile.IsDead) + { + if (showWhen == 2 && current != max || showWhen <= 1) + { + if (mobile.HitsPercentage != 0) + { + AnimationsLoader.Instance.GetAnimationDimensions + ( + mobile.AnimIndex, + mobile.GetGraphicForAnimation(), + /*(byte) m.GetDirectionForAnimation()*/ + 0, + /*Mobile.GetGroupForAnimation(m, isParent:true)*/ + 0, + mobile.IsMounted, + /*(byte) m.AnimIndex*/ + 0, + out int centerX, + out int centerY, + out int width, + out int height + ); + + Point p1 = p; + p1.Y -= height + centerY + 8 + 22; + + if (mobile.IsGargoyle && mobile.IsFlying) + { + p1.Y -= 22; + } + else if (!mobile.IsMounted) + { + p1.Y += 22; + } + + p1 = Client.Game.Scene.Camera.WorldToScreen(p1); + p1.X -= (mobile.HitsTexture.Width >> 1) + 5; + p1.Y -= mobile.HitsTexture.Height; + + if (mobile.ObjectHandlesStatus == ObjectHandlesStatus.DISPLAYING) + { + p1.Y -= Constants.OBJECT_HANDLES_GUMP_HEIGHT + 5; + } + + // MobileUO: fixed viewport boundary condition check + if (!(p1.X < posX || p1.X > posX + screenW - mobile.HitsTexture.Width || p1.Y < posY || p1.Y > posY + screenH)) + { + mobile.HitsTexture.Draw(batcher, p1.X, p1.Y); + } + } + } + } + + if (mobile.Serial == TargetManager.LastTargetInfo.Serial || mobile.Serial == TargetManager.SelectedTarget || mobile.Serial == TargetManager.LastAttack) + { + continue; + } + + p.X -= 5; + p = Client.Game.Scene.Camera.WorldToScreen(p); + p.X -= BAR_WIDTH_HALF; + p.Y -= BAR_HEIGHT_HALF; + + // MobileUO: fixed viewport boundary condition check + if (p.X < posX || p.X > posX + screenW - BAR_WIDTH) + { + continue; + } + + if (p.Y < posY || p.Y > posY + screenH - BAR_HEIGHT) + { + continue; + } + + if (mode >= 1) + { + DrawHealthLine + ( + batcher, + mobile, + p.X, + p.Y, + mobile.Serial != World.Player.Serial + ); + } + } + } + + private void DrawHealthLineWithMath(UltimaBatcher2D batcher, uint serial, int screenW, int screenH) + { + Entity entity = World.Get(serial); + + if (entity == null) + { + return; + } + + Point p = entity.RealScreenPosition; + p.X += (int) entity.Offset.X + 22; + p.Y += (int) (entity.Offset.Y - entity.Offset.Z) + 22 + 5; + + p = Client.Game.Scene.Camera.WorldToScreen(p); + p.X -= BAR_WIDTH_HALF; + p.Y -= BAR_HEIGHT_HALF; + + // MobileUO: fixed viewport boundary condition check + int posX = ProfileManager.CurrentProfile.GameWindowPosition.X + 5; + int posY = ProfileManager.CurrentProfile.GameWindowPosition.Y + 5; + + if (p.X < posX || p.X > posX + screenW - BAR_WIDTH) + { + return; + } + + if (p.Y < posY || p.Y > posY + screenH - BAR_HEIGHT) + { + return; + } + + DrawHealthLine + ( + batcher, + entity, + p.X, + p.Y, + false + ); + } + + private void DrawHealthLine(UltimaBatcher2D batcher, Entity entity, int x, int y, bool passive) + { + if (entity == null) + { + return; + } + + int per = BAR_WIDTH * entity.HitsPercentage / 100; + + Mobile mobile = entity as Mobile; + + + float alpha = passive ? 0.5f : 0.0f; + + _vectorHue.X = mobile != null ? Notoriety.GetHue(mobile.NotorietyFlag) : Notoriety.GetHue(NotorietyFlag.Gray); + + _vectorHue.Y = 1; + _vectorHue.Z = alpha; + + if (mobile == null) + { + y += 22; + } + + + const int MULTIPLER = 1; + + batcher.Draw2D + ( + _background_texture, + x, + y, + _background_texture.Width * MULTIPLER, + _background_texture.Height * MULTIPLER, + ref _vectorHue + ); + + + _vectorHue.X = 0x21; + + + if (entity.Hits != entity.HitsMax || entity.HitsMax == 0) + { + int offset = 2; + + if (per >> 2 == 0) + { + offset = per; + } + + batcher.Draw2DTiled + ( + _hp_texture, + x + per * MULTIPLER - offset, + y, + (BAR_WIDTH - per) * MULTIPLER - offset / 2, + _hp_texture.Height * MULTIPLER, + ref _vectorHue + ); + } + + ushort hue = 90; + + if (per > 0) + { + if (mobile != null) + { + if (mobile.IsPoisoned) + { + hue = 63; + } + else if (mobile.IsYellowHits) + { + hue = 53; + } + } + + _vectorHue.X = hue; + + + batcher.Draw2DTiled + ( + _hp_texture, + x, + y, + per * MULTIPLER, + _hp_texture.Height * MULTIPLER, + ref _vectorHue + ); + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/LastCharacterManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/LastCharacterManager.cs new file mode 120000 index 000000000..5b2f4cf03 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/LastCharacterManager.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/LastCharacterManager.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/MessageEventArgs.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/MessageEventArgs.cs new file mode 120000 index 000000000..0aeaf226d --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/MessageEventArgs.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/MessageEventArgs.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/Season.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/Season.cs new file mode 120000 index 000000000..85a941ceb --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/Season.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/Season.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/SeasonManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/SeasonManager.cs new file mode 120000 index 000000000..2bc60bfc4 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/SeasonManager.cs @@ -0,0 +1 @@ +../../../../../../external/ClassicUO/src/Game/Managers/SeasonManager.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/Seasons.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/Seasons.cs deleted file mode 120000 index 468016e25..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/Managers/Seasons.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../external/ClassicUO/src/Game/Managers/Seasons.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/TextRenderer.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/TextRenderer.cs new file mode 100644 index 000000000..349415f41 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/TextRenderer.cs @@ -0,0 +1,364 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System.Collections.Generic; +using ClassicUO.Configuration; +using ClassicUO.Game.GameObjects; +using ClassicUO.Input; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; + +namespace ClassicUO.Game.Managers +{ + internal class TextRenderer : TextObject + { + private readonly List _bounds = new List(); + + public TextRenderer() + { + FirstNode = this; + } + + protected TextObject FirstNode; + protected TextObject DrawPointer; + + public override void Destroy() + { + //Clear(); + } + + public virtual void Update(double totalTime, double frameTime) + { + ProcessWorldText(false); + } + + public void Select(int startX, int startY, int renderIndex, bool isGump = false) + { + int mouseX = Mouse.Position.X; + int mouseY = Mouse.Position.Y; + + for (TextObject item = DrawPointer; item != null; item = item.DLeft) + { + if (item.RenderedText == null || item.RenderedText.IsDestroyed || item.RenderedText.Texture == null) + { + continue; + } + + if (item.Time >= ClassicUO.Time.Ticks) + { + if (item.Owner == null || item.Owner.UseInRender != renderIndex) + { + continue; + } + } + + if (item.RenderedText.PixelCheck(mouseX - startX - item.RealScreenPosition.X, mouseY - startY - item.RealScreenPosition.Y)) + { + SelectedObject.LastObject = item; + } + } + + if (SelectedObject.LastObject is TextObject t) + { + if (isGump) + { + if (t.IsTextGump) + { + t.ToTopD(); + } + } + else + { + MoveToTop(t); + } + } + } + + public virtual void Draw(UltimaBatcher2D batcher, int startX, int startY, int renderIndex, bool isGump = false) + { + ProcessWorldText(false); + + int mouseX = Mouse.Position.X; + int mouseY = Mouse.Position.Y; + + BaseGameObject last = SelectedObject.LastObject; + + for (TextObject o = DrawPointer; o != null; o = o.DLeft) + { + if (o.IsDestroyed || o.RenderedText == null || o.RenderedText.IsDestroyed || o.RenderedText.Texture == null || o.Time < ClassicUO.Time.Ticks || o.Owner.UseInRender != renderIndex && !isGump) + { + continue; + } + + ushort hue = 0; + + float alpha = 1f - o.Alpha / 255f; + + if (o.IsTransparent) + { + if (o.Alpha == 0xFF) + { + alpha = 1f - 0x7F / 255f; + } + } + + int x = o.RealScreenPosition.X; + int y = o.RealScreenPosition.Y; + + if (o.RenderedText.PixelCheck(mouseX - x - startX, mouseY - y - startY)) + { + if (isGump) + { + SelectedObject.LastObject = o; + } + else + { + SelectedObject.Object = o; + } + } + + if (!isGump) + { + if (o.Owner is Entity && last == o) + { + hue = 0x0035; + } + } + else + { + x += startX; + y += startY; + } + + o.RenderedText.Draw + ( + batcher, + x, + y, + alpha, + hue + ); + } + } + + public void MoveToTop(TextObject obj) + { + if (obj == null) + { + return; + } + + obj.UnlinkD(); + + TextObject next = FirstNode.DRight; + FirstNode.DRight = obj; + obj.DLeft = FirstNode; + obj.DRight = next; + + if (next != null) + { + next.DLeft = obj; + } + } + + public void ProcessWorldText(bool doit) + { + if (doit) + { + if (_bounds.Count != 0) + { + _bounds.Clear(); + } + } + + for (DrawPointer = FirstNode; DrawPointer != null; DrawPointer = DrawPointer.DRight) + { + if (doit) + { + TextObject t = DrawPointer; + + if (t.Time >= ClassicUO.Time.Ticks && t.RenderedText != null && !t.RenderedText.IsDestroyed) + { + if (t.Owner != null) + { + t.IsTransparent = Collides(t); + CalculateAlpha(t); + } + } + } + + if (DrawPointer.DRight == null) + { + break; + } + } + } + + private void CalculateAlpha(TextObject msg) + { + if (ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.TextFading) + { + int delta = (int) (msg.Time - ClassicUO.Time.Ticks); + + if (delta >= 0 && delta <= 1000) + { + delta /= 10; + + if (delta > 100) + { + delta = 100; + } + else if (delta < 1) + { + delta = 0; + } + + delta = 255 * delta / 100; + + if (!msg.IsTransparent || delta <= 0x7F) + { + msg.Alpha = (byte) delta; + } + + msg.IsTransparent = true; + } + } + } + + private bool Collides(TextObject msg) + { + bool result = false; + + Rectangle rect = new Rectangle + { + X = msg.RealScreenPosition.X, + Y = msg.RealScreenPosition.Y, + Width = msg.RenderedText.Width, + Height = msg.RenderedText.Height + }; + + for (int i = 0; i < _bounds.Count; i++) + { + if (_bounds[i].Intersects(rect)) + { + result = true; + + break; + } + } + + _bounds.Add(rect); + + return result; + } + + public void AddMessage(TextObject obj) + { + if (obj == null) + { + return; + } + + obj.UnlinkD(); + + TextObject item = FirstNode; + + if (item != null) + { + if (item.DRight != null) + { + TextObject next = item.DRight; + + item.DRight = obj; + obj.DLeft = item; + obj.DRight = next; + next.DLeft = obj; + } + else + { + item.DRight = obj; + obj.DLeft = item; + obj.DRight = null; + } + } + } + + + public new virtual void Clear() + { + if (FirstNode != null) + { + TextObject first = FirstNode; + + while (first?.DLeft != null) + { + first = first.DLeft; + } + + while (first != null) + { + TextObject next = first.DRight; + + first.Destroy(); + first.Clear(); + + first = next; + } + } + + if (DrawPointer != null) + { + TextObject first = DrawPointer; + + while (first?.DLeft != null) + { + first = first.DLeft; + } + + while (first != null) + { + TextObject next = first.DRight; + + first.Destroy(); + first.Clear(); + + first = next; + } + } + + FirstNode = this; + FirstNode.DLeft = null; + FirstNode.DRight = null; + DrawPointer = null; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/UIManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/UIManager.cs index 3d1dd2d05..ac06672ac 100644 --- a/Assets/Scripts/ClassicUO/src/Game/Managers/UIManager.cs +++ b/Assets/Scripts/ClassicUO/src/Game/Managers/UIManager.cs @@ -1,41 +1,42 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.Collections.Generic; -using System.Linq; -using System.Text; - using ClassicUO.Configuration; -using ClassicUO.Game.GameObjects; using ClassicUO.Game.UI.Controls; using ClassicUO.Game.UI.Gumps; using ClassicUO.Input; -using ClassicUO.IO.Resources; -using ClassicUO.Network; using ClassicUO.Renderer; -using ClassicUO.Utility; -using ClassicUO.Utility.Collections; -using ClassicUO.Utility.Logging; - using Microsoft.Xna.Framework; namespace ClassicUO.Game.Managers @@ -43,28 +44,36 @@ namespace ClassicUO.Game.Managers internal static class UIManager { private static readonly Dictionary _gumpPositionCache = new Dictionary(); - private static readonly Control[] _mouseDownControls = new Control[5]; + private static readonly Control[] _mouseDownControls = new Control[0xFF]; //private static readonly Dictionary _targetLineGumps = new Dictionary(); - private static int _dragOriginX, _dragOriginY; + private static Point _dragOrigin; private static bool _isDraggingControl; - private static Control _keyboardFocusControl, _validForDClick, _lastFocus; + private static Control _keyboardFocusControl, _lastFocus; private static bool _needSort; - public static float ContainerScale { get; set; } = 1f; public static AnchorManager AnchorManager { get; } = new AnchorManager(); - public static LinkedList Gumps { get; } = new LinkedList(); + public static LinkedList Gumps { get; } = new LinkedList(); public static Control MouseOverControl { get; private set; } - public static bool IsMouseOverAControl => MouseOverControl != null; + public static bool IsModalOpen { get; private set; } - public static bool IsMouseOverWorld => MouseOverControl is WorldViewport; + public static bool IsMouseOverWorld + { + get + { + Point mouse = Mouse.Position; + Profile profile = ProfileManager.CurrentProfile; + + return profile != null && GameCursor.AllowDrawSDLCursor && DraggingControl == null && MouseOverControl == null && !IsModalOpen && mouse.X >= profile.GameWindowPosition.X + 5 && mouse.X < profile.GameWindowPosition.X + 5 + profile.GameWindowSize.X && mouse.Y >= profile.GameWindowPosition.Y + 5 && mouse.Y < profile.GameWindowPosition.Y + 5 + profile.GameWindowSize.Y; + } + } public static Control DraggingControl { get; private set; } @@ -74,23 +83,9 @@ internal static class UIManager public static PopupMenuGump PopupMenu { get; private set; } - public static void ShowGamePopup(PopupMenuGump popup) - { - PopupMenu?.Dispose(); - PopupMenu = popup; - - if (popup == null || popup.IsDisposed) - return; - - Add(PopupMenu); - } - public static Control KeyboardFocusControl { - get - { - return _keyboardFocusControl; - } + get => _keyboardFocusControl; set { if (_keyboardFocusControl != value) @@ -101,53 +96,68 @@ public static Control KeyboardFocusControl if (value != null && value.AcceptKeyboardInput) { if (!value.IsFocused) + { value.OnFocusEnter(); + } } } } } + public static bool IsDragging => _isDraggingControl && DraggingControl != null; - public static bool IsModalControlOpen() + public static ContextMenuShowMenu ContextMenu { get; private set; } + + public static void ShowGamePopup(PopupMenuGump popup) { - foreach (Control control in Gumps) + PopupMenu?.Dispose(); + PopupMenu = popup; + + if (popup == null || popup.IsDisposed) { - if (control.ControlInfo.IsModal) - return true; + return; } - return false; + Add(PopupMenu); } - public static bool IsDragging => _isDraggingControl && DraggingControl != null; - public static bool ValidForDClick() => !(_validForDClick is WorldViewport); + public static bool IsModalControlOpen() + { + foreach (Gump control in Gumps) + { + if (control.IsModal) + { + return true; + } + } + return false; + } public static void OnMouseDragging() { HandleMouseInput(); - //if (_mouseDownControls[0] == MouseOverControl && MouseOverControl != null) - if (_mouseDownControls[0] != null) + if (_mouseDownControls[(int) MouseButtonType.Left] != null) { - if (ProfileManager.Current == null || !ProfileManager.Current.HoldAltToMoveGumps || Keyboard.Alt) + if (ProfileManager.CurrentProfile == null || !ProfileManager.CurrentProfile.HoldAltToMoveGumps || Keyboard.Alt) { - AttemptDragControl(_mouseDownControls[0], Mouse.Position, true); + AttemptDragControl(_mouseDownControls[(int) MouseButtonType.Left], true); } } if (_isDraggingControl) { - DoDragControl(Mouse.Position); + DoDragControl(); } } - public static void OnLeftMouseButtonDown() + public static void OnMouseButtonDown(MouseButtonType button) { HandleMouseInput(); - _validForDClick = null; + if (MouseOverControl != null) { if (MouseOverControl.IsEnabled && MouseOverControl.IsVisible) @@ -161,18 +171,20 @@ public static void OnLeftMouseButtonDown() } MakeTopMostGump(MouseOverControl); - MouseOverControl.InvokeMouseDown(Mouse.Position, MouseButtonType.Left); + MouseOverControl.InvokeMouseDown(Mouse.Position, button); if (MouseOverControl.AcceptKeyboardInput) + { _keyboardFocusControl = MouseOverControl; - - _mouseDownControls[(int) MouseButtonType.Left] = MouseOverControl; + } + + _mouseDownControls[(int) button] = MouseOverControl; } else { - foreach (Control s in Gumps) + foreach (Gump s in Gumps) { - if (s.ControlInfo.IsModal && s.ControlInfo.ModalClickOutsideAreaClosesThisControl) + if (s.IsModal && s.ModalClickOutsideAreaClosesThisControl) { s.Dispose(); Mouse.CancelDoubleClick = true; @@ -181,247 +193,66 @@ public static void OnLeftMouseButtonDown() } if (PopupMenu != null && !PopupMenu.Bounds.Contains(Mouse.Position.X, Mouse.Position.Y)) - ShowGamePopup(null); - } - - public static void OnLeftMouseButtonUp() - { - HandleMouseInput(); - - const int btn = (int) MouseButtonType.Left; - EndDragControl(Mouse.Position); - - if (MouseOverControl != null) { - if ((_mouseDownControls[btn] != null && MouseOverControl == _mouseDownControls[btn]) || ItemHold.Enabled) - MouseOverControl.InvokeMouseUp(Mouse.Position, MouseButtonType.Left); - else if (_mouseDownControls[btn] != null && MouseOverControl != _mouseDownControls[btn]) - _mouseDownControls[btn].InvokeMouseUp(Mouse.Position, MouseButtonType.Left); - } - else - _mouseDownControls[btn]?.InvokeMouseUp(Mouse.Position, MouseButtonType.Left); - - _mouseDownControls[btn] = null; - _validForDClick = MouseOverControl; - } - - public static bool OnLeftMouseDoubleClick() - { - HandleMouseInput(); - - if (MouseOverControl != null && IsMouseOverAControl) - { - if (MouseOverControl.InvokeMouseDoubleClick(Mouse.Position, MouseButtonType.Left)) - { - DelayedObjectClickManager.Clear(); - return true; - } - } - - return false; - } - - public static void OnRightMouseButtonDown() - { - HandleMouseInput(); - - if (MouseOverControl != null) - { - MakeTopMostGump(MouseOverControl); - MouseOverControl.InvokeMouseDown(Mouse.Position, MouseButtonType.Right); - - if (MouseOverControl.AcceptKeyboardInput) - _keyboardFocusControl = MouseOverControl; - _mouseDownControls[(int) MouseButtonType.Right] = MouseOverControl; - } - else - { - foreach (Control s in Gumps) - { - if (s.ControlInfo.IsModal && s.ControlInfo.ModalClickOutsideAreaClosesThisControl) - { - s.Dispose(); - Mouse.CancelDoubleClick = true; - } - } + ShowGamePopup(null); } - - ShowGamePopup(null); } - public static void OnRightMouseButtonUp() + public static void OnMouseButtonUp(MouseButtonType button) { + EndDragControl(Mouse.Position); HandleMouseInput(); - const int btn = (int) MouseButtonType.Right; - EndDragControl(Mouse.Position); + int index = (int) button; if (MouseOverControl != null) { - if (_mouseDownControls[btn] != null && MouseOverControl == _mouseDownControls[btn]) - { - MouseOverControl.InvokeMouseCloseGumpWithRClick(); - } - - MouseOverControl.InvokeMouseUp(Mouse.Position, MouseButtonType.Right); - - if (_mouseDownControls[btn] != null && MouseOverControl != _mouseDownControls[btn]) + if (_mouseDownControls[index] != null && MouseOverControl == _mouseDownControls[index] || ItemHold.Enabled) { - _mouseDownControls[btn].InvokeMouseUp(Mouse.Position, MouseButtonType.Right); - _mouseDownControls[btn].InvokeMouseCloseGumpWithRClick(); + MouseOverControl.InvokeMouseUp(Mouse.Position, button); } - } - else if (_mouseDownControls[btn] != null) - { - _mouseDownControls[btn].InvokeMouseUp(Mouse.Position, MouseButtonType.Right); - _mouseDownControls[btn].InvokeMouseCloseGumpWithRClick(); - } - - _mouseDownControls[btn] = null; - } - - public static bool OnRightMouseDoubleClick() - { - if (MouseOverControl != null && IsMouseOverAControl) - return MouseOverControl.InvokeMouseDoubleClick(Mouse.Position, MouseButtonType.Right); - - return false; - } - - public static void OnMiddleMouseButtonDown() - { - HandleMouseInput(); - - const int btn = (int) MouseButtonType.Middle; - - if (MouseOverControl != null) - { - MakeTopMostGump(MouseOverControl); - MouseOverControl.InvokeMouseDown(Mouse.Position, MouseButtonType.Middle); - - if (MouseOverControl.IsEnabled && MouseOverControl.IsVisible) + else if (_mouseDownControls[index] != null && MouseOverControl != _mouseDownControls[index]) { - if (_lastFocus != MouseOverControl) - { - _lastFocus?.OnFocusLost(); - MouseOverControl.OnFocusEnter(); - _lastFocus = MouseOverControl; - } + _mouseDownControls[index].InvokeMouseUp(Mouse.Position, button); } - - _mouseDownControls[btn] = MouseOverControl; } else { - foreach (Control s in Gumps) - { - if (s.ControlInfo.IsModal && s.ControlInfo.ModalClickOutsideAreaClosesThisControl) - { - s.Dispose(); - Mouse.CancelDoubleClick = true; - } - } + _mouseDownControls[index]?.InvokeMouseUp(Mouse.Position, button); } - ShowGamePopup(null); - } - - public static void OnMiddleMouseButtonUp() - { - HandleMouseInput(); - - const int btn = (int) MouseButtonType.Middle; - EndDragControl(Mouse.Position); - - if (MouseOverControl != null) + if (button == MouseButtonType.Right) { - if (_mouseDownControls[btn] != null && MouseOverControl == _mouseDownControls[btn]) - MouseOverControl.InvokeMouseUp(Mouse.Position, MouseButtonType.Middle); - - if (_mouseDownControls[btn] != null && MouseOverControl != _mouseDownControls[btn]) - _mouseDownControls[btn].InvokeMouseUp(Mouse.Position, MouseButtonType.Middle); + _mouseDownControls[index]?.InvokeMouseCloseGumpWithRClick(); } - else - _mouseDownControls[btn]?.InvokeMouseUp(Mouse.Position, MouseButtonType.Middle); - - _mouseDownControls[btn] = null; - _validForDClick = MouseOverControl; - } - - public static bool OnMiddleMouseDoubleClick() - { - if (MouseOverControl != null && IsMouseOverAControl) - return MouseOverControl.InvokeMouseDoubleClick(Mouse.Position, MouseButtonType.Middle); - return false; + _mouseDownControls[index] = null; } - public static void OnExtraMouseButtonDown(int btn) + public static bool OnMouseDoubleClick(MouseButtonType button) { HandleMouseInput(); + if (MouseOverControl != null) { - MakeTopMostGump(MouseOverControl); - MouseOverControl.InvokeMouseDown(Mouse.Position, (MouseButtonType)btn); - - if (MouseOverControl.IsEnabled && MouseOverControl.IsVisible) + if (MouseOverControl.InvokeMouseDoubleClick(Mouse.Position, button)) { - if (_lastFocus != MouseOverControl) - { - _lastFocus?.OnFocusLost(); - MouseOverControl.OnFocusEnter(); - _lastFocus = MouseOverControl; - } - } + if (button == MouseButtonType.Left) + DelayedObjectClickManager.Clear(); - _mouseDownControls[btn] = MouseOverControl; - } - else - { - foreach (Control s in Gumps) - { - if (s.ControlInfo.IsModal && s.ControlInfo.ModalClickOutsideAreaClosesThisControl) - { - s.Dispose(); - Mouse.CancelDoubleClick = true; - } + return true; } } - ShowGamePopup(null); - } - - public static void OnExtraMouseButtonUp(int btn) - { - HandleMouseInput(); - EndDragControl(Mouse.Position); - - if (MouseOverControl != null) - { - if (_mouseDownControls[btn] != null && MouseOverControl == _mouseDownControls[btn]) - MouseOverControl.InvokeMouseUp(Mouse.Position, (MouseButtonType)btn); - - if (_mouseDownControls[btn] != null && MouseOverControl != _mouseDownControls[btn]) - _mouseDownControls[btn].InvokeMouseUp(Mouse.Position, (MouseButtonType)btn); - } - else - _mouseDownControls[btn]?.InvokeMouseUp(Mouse.Position, (MouseButtonType)btn); - - _mouseDownControls[btn] = null; - _validForDClick = MouseOverControl; + return false; } public static void OnMouseWheel(bool isup) { if (MouseOverControl != null && MouseOverControl.AcceptMouseInput) + { MouseOverControl.InvokeMouseWheel(isup ? MouseEventType.WheelScrollUp : MouseEventType.WheelScrollDown); - } - - - public static bool HadMouseDownOnGump(MouseButtonType button) - { - var c = LastControlMouseDown(button); - return c != null && !c.IsDisposed && !(c is WorldViewport) && !ItemHold.Enabled; + } } public static Control LastControlMouseDown(MouseButtonType button) @@ -434,7 +265,7 @@ public static void InitializeGameCursor() { GameCursor = new GameCursor(); } - + public static void SavePosition(uint serverSerial, Point point) { _gumpPositionCache[serverSerial] = point; @@ -450,8 +281,6 @@ public static bool GetGumpCachePosition(uint id, out Point pos) return _gumpPositionCache.TryGetValue(id, out pos); } - public static ContextMenuShowMenu ContextMenu { get; private set; } - public static void ShowContextMenu(ContextMenuShowMenu menu) { ContextMenu?.Dispose(); @@ -459,7 +288,9 @@ public static void ShowContextMenu(ContextMenuShowMenu menu) ContextMenu = menu; if (ContextMenu == null || menu.IsDisposed) + { return; + } Add(ContextMenu); } @@ -468,35 +299,42 @@ public static T GetGump(uint? serial = null) where T : Control { if (serial.HasValue) { - for (var last = Gumps.Last; last != null; last = last.Previous) + for (LinkedListNode last = Gumps.Last; last != null; last = last.Previous) { - var c = last.Value; + Control c = last.Value; if (!c.IsDisposed && c.LocalSerial == serial.Value && c is T t) + { return t; + } } } else { - for (var first = Gumps.First; first != null; first = first.Next) + for (LinkedListNode first = Gumps.First; first != null; first = first.Next) { - var c = first.Value; + Control c = first.Value; if (!c.IsDisposed && c is T t) + { return t; + } } } + return null; } public static Gump GetGump(uint serial) { - for (var last = Gumps.Last; last != null; last = last.Previous) + for (LinkedListNode last = Gumps.Last; last != null; last = last.Previous) { - var c = last.Value; + Control c = last.Value; if (!c.IsDisposed && c.LocalSerial == serial) + { return c as Gump; + } } return null; @@ -504,7 +342,7 @@ public static Gump GetGump(uint serial) public static TradingGump GetTradingGump(uint serial) { - for (var g = Gumps.Last; g != null; g = g.Previous) + for (LinkedListNode g = Gumps.Last; g != null; g = g.Previous) { if (g.Value != null && !g.Value.IsDisposed && g.Value is TradingGump trading && (trading.ID1 == serial || trading.ID2 == serial || trading.LocalSerial == serial)) { @@ -515,27 +353,29 @@ public static TradingGump GetTradingGump(uint serial) return null; } - public static void Update(double totalMS, double frameMS) + public static void Update(double totalTime, double frameTime) { SortControlsByInfo(); - var first = Gumps.First; + LinkedListNode first = Gumps.First; while (first != null) { - var next = first.Next; + LinkedListNode next = first.Next; Control g = first.Value; - g.Update(totalMS, frameMS); + g.Update(totalTime, frameTime); if (g.IsDisposed) + { Gumps.Remove(first); + } first = next; } - GameCursor?.Update(totalMS, frameMS); + GameCursor?.Update(totalTime, frameTime); HandleKeyboardInput(); HandleMouseInput(); } @@ -544,13 +384,11 @@ public static void Draw(UltimaBatcher2D batcher) { SortControlsByInfo(); - batcher.GraphicsDevice.Clear(Color.Black); - batcher.Begin(); - for (var last = Gumps.Last; last != null; last = last.Previous) + for (LinkedListNode last = Gumps.Last; last != null; last = last.Previous) { - var g = last.Value; + Control g = last.Value; g.Draw(batcher, g.X, g.Y); } @@ -559,22 +397,31 @@ public static void Draw(UltimaBatcher2D batcher) batcher.End(); } - public static void Add(Control gump) + public static void Add(Gump gump, bool front = true) { if (!gump.IsDisposed) { - Gumps.AddFirst(gump); + if (front) + { + Gumps.AddFirst(gump); + } + else + { + Gumps.AddLast(gump); + } + _needSort = Gumps.Count > 1; } } public static void Clear() { - foreach (Control s in Gumps) + foreach (Gump s in Gumps) { s.Dispose(); } + // MobileUO: clear logic Gumps.Clear(); for (int i = 0; i < _mouseDownControls.Length; i++) @@ -583,13 +430,13 @@ public static void Clear() } } + // MobileUO: added Dispose method public static void Dispose() { GameCursor?.Dispose(); GameCursor = null; _keyboardFocusControl = null; - _validForDClick = null; _lastFocus = null; MouseOverControl = null; @@ -603,7 +450,9 @@ public static void Dispose() private static void HandleKeyboardInput() { if (_keyboardFocusControl != null && _keyboardFocusControl.IsDisposed) + { _keyboardFocusControl = null; + } if (_keyboardFocusControl == null) { @@ -614,9 +463,9 @@ private static void HandleKeyboardInput() } else { - for (var first = Gumps.First; first != null; first = first.Next) + for (LinkedListNode first = Gumps.First; first != null; first = first.Next) { - var c = first.Value; + Control c = first.Value; if (!c.IsDisposed && c.IsVisible && c.IsEnabled) { @@ -625,6 +474,7 @@ private static void HandleKeyboardInput() if (_keyboardFocusControl != null) { _keyboardFocusControl.OnFocusEnter(); + break; } } @@ -644,7 +494,9 @@ private static void HandleMouseInput() if (MouseOverControl.RootParent != null) { if (gump == null || gump.RootParent != MouseOverControl.RootParent) + { MouseOverControl.RootParent.InvokeMouseExit(Mouse.Position); + } } } @@ -657,7 +509,9 @@ private static void HandleMouseInput() if (gump.RootParent != null) { if (MouseOverControl == null || gump.RootParent != MouseOverControl.RootParent) + { gump.RootParent.InvokeMouseEnter(Mouse.Position); + } } } @@ -666,27 +520,32 @@ private static void HandleMouseInput() MouseOverControl = gump; - for (int i = 0; i < 5; i++) + for (int i = 0; i < (int) MouseButtonType.Size; i++) { if (_mouseDownControls[i] != null && _mouseDownControls[i] != gump) + { _mouseDownControls[i].InvokeMouseOver(Mouse.Position); + } } } + // MobileUO: made method public public static Control GetMouseOverControl(Point position) { if (_isDraggingControl) + { return DraggingControl; + } Control control = null; - bool ismodal = IsModalControlOpen(); + IsModalOpen = IsModalControlOpen(); - for (var first = Gumps.First; first != null; first = first.Next) + for (LinkedListNode first = Gumps.First; first != null; first = first.Next) { - var c = first.Value; + Control c = first.Value; - if ((ismodal && !c.ControlInfo.IsModal) || !c.IsVisible || !c.IsEnabled) + if (IsModalOpen && !c.IsModal || !c.IsVisible || !c.IsEnabled) { continue; } @@ -704,21 +563,37 @@ public static Control GetMouseOverControl(Point position) public static void MakeTopMostGump(Control control) { - Control c = control; - - while (c.Parent != null) - c = c.Parent; - - var first = Gumps.First?.Next; // skip game window + Gump gump = control as Gump; + if (gump == null && control?.RootParent is Gump) + { + gump = control.RootParent as Gump; + } - for (; first != null; first = first.Next) + if (gump != null) { - if (first.Value == c) + for (LinkedListNode start = Gumps.First; start != null; start = start.Next) { - Gumps.Remove(first); - Gumps.AddFirst(first); - _needSort = Gumps.Count > 1; + if (start.Value == gump) + { + if (gump.LayerOrder == UILayer.Under) + { + if (start != Gumps.Last) + { + Gumps.Remove(gump); + Gumps.AddBefore(Gumps.Last, start); + } + } + else + { + Gumps.Remove(gump); + Gumps.AddFirst(start); + } + + break; + } } + + _needSort = Gumps.Count > 1; } } @@ -726,30 +601,32 @@ private static void SortControlsByInfo() { if (_needSort) { - for (var el = Gumps.First; el != null; el = el.Next) + for (LinkedListNode el = Gumps.First; el != null; el = el.Next) { - var c = el.Value; + Gump c = el.Value; - if (c.ControlInfo.Layer == UILayer.Default) + if (c.LayerOrder == UILayer.Default) + { continue; + } - if (c.ControlInfo.Layer == UILayer.Under) + if (c.LayerOrder == UILayer.Under) { - for (var first = Gumps.First; first != null; first = first.Next) + for (LinkedListNode first = Gumps.First; first != null; first = first.Next) { if (first.Value == c) { - if (Gumps.Last != null) + if (c != Gumps.Last.Value) { Gumps.Remove(first); - Gumps.AddAfter(Gumps.Last, c); + Gumps.AddBefore(Gumps.Last, first); } } } } - else if (c.ControlInfo.Layer == UILayer.Over) + else if (c.LayerOrder == UILayer.Over) { - for (var first = Gumps.First; first != null; first = first.Next) + for (LinkedListNode first = Gumps.First; first != null; first = first.Next) { if (first.Value == c) { @@ -764,63 +641,71 @@ private static void SortControlsByInfo() } } - public static void AttemptDragControl(Control control, Point mousePosition, bool attemptAlwaysSuccessful = false) + public static void AttemptDragControl(Control control, bool attemptAlwaysSuccessful = false) { - if (_isDraggingControl || (ItemHold.Enabled && !ItemHold.IsFixedPosition)) + if (_isDraggingControl || ItemHold.Enabled && !ItemHold.IsFixedPosition) + { return; + } Control dragTarget = control; if (!dragTarget.CanMove) + { return; + } while (dragTarget.Parent != null) + { dragTarget = dragTarget.Parent; + } if (dragTarget.CanMove) { if (attemptAlwaysSuccessful || !_isDraggingControl) { DraggingControl = dragTarget; - _dragOriginX = Mouse.LDropPosition.X; - _dragOriginY = Mouse.LDropPosition.Y; + // MobileUO: don't change this or dragging gumps goes wrong + _dragOrigin = Mouse.Position; - for (int i = 0; i < 5; i++) + for (int i = 0; i < (int) MouseButtonType.Size; i++) + { _mouseDownControls[i] = null; + } } - int deltaX = mousePosition.X - _dragOriginX; - int deltaY = mousePosition.Y - _dragOriginY; + Point delta = Mouse.Position - _dragOrigin; - int delta = Math.Abs(deltaX) + Math.Abs(deltaY); - - if (attemptAlwaysSuccessful || delta > Constants.MIN_GUMP_DRAG_DISTANCE) + if (attemptAlwaysSuccessful || delta != Point.Zero) { _isDraggingControl = true; - dragTarget.InvokeDragBegin(new Point(deltaX, deltaY)); + dragTarget.InvokeDragBegin(delta); } } } - private static void DoDragControl(Point mousePosition) + private static void DoDragControl() { if (DraggingControl == null) + { return; + } - int deltaX = mousePosition.X - _dragOriginX; - int deltaY = mousePosition.Y - _dragOriginY; + Point delta = Mouse.Position - _dragOrigin; - DraggingControl.X = DraggingControl.X + deltaX; - DraggingControl.Y = DraggingControl.Y + deltaY; - DraggingControl.InvokeMove(deltaX, deltaY); - _dragOriginX = mousePosition.X; - _dragOriginY = mousePosition.Y; + DraggingControl.X += delta.X; + DraggingControl.Y += delta.Y; + DraggingControl.InvokeMove(delta.X, delta.Y); + _dragOrigin = Mouse.Position; } private static void EndDragControl(Point mousePosition) { if (_isDraggingControl) - DoDragControl(mousePosition); + { + DoDragControl(); + } + DraggingControl?.InvokeDragEnd(mousePosition); DraggingControl = null; _isDraggingControl = false; diff --git a/Assets/Scripts/ClassicUO/src/Game/Managers/UOChatManager.cs b/Assets/Scripts/ClassicUO/src/Game/Managers/UOChatManager.cs deleted file mode 120000 index 4271c975a..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/Managers/UOChatManager.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../external/ClassicUO/src/Game/Managers/UOChatManager.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/ScanModeObject.cs b/Assets/Scripts/ClassicUO/src/Game/ScanModeObject.cs new file mode 120000 index 000000000..5f18d75ab --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/ScanModeObject.cs @@ -0,0 +1 @@ +../../../../../external/ClassicUO/src/Game/ScanModeObject.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/ScanTypeObject.cs b/Assets/Scripts/ClassicUO/src/Game/ScanTypeObject.cs new file mode 120000 index 000000000..0c099f8ee --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/ScanTypeObject.cs @@ -0,0 +1 @@ +../../../../../external/ClassicUO/src/Game/ScanTypeObject.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameScene.cs b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameScene.cs index 4fd04afd1..cbe44dca3 100644 --- a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameScene.cs +++ b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameScene.cs @@ -1,107 +1,122 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.Collections.Generic; using System.Linq; using System.Net.Sockets; - using ClassicUO.Configuration; using ClassicUO.Game.Data; using ClassicUO.Game.GameObjects; using ClassicUO.Game.Managers; -using ClassicUO.Game.Map; using ClassicUO.Game.UI.Controls; using ClassicUO.Game.UI.Gumps; using ClassicUO.Input; using ClassicUO.IO.Resources; using ClassicUO.Network; using ClassicUO.Renderer; +using ClassicUO.Resources; using ClassicUO.Utility; using ClassicUO.Utility.Logging; - using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; +using SDL2; namespace ClassicUO.Game.Scenes { internal partial class GameScene : Scene { - //NOTE: Added these to allow the game viewport to be smaller than what CUO was allowing + // MobileUO: NOTE: Added these to allow the game viewport to be smaller than what CUO was allowing public static int MinimumViewportWidth = 200; public static int MinimumViewportHeight = 300; - private readonly LightData[] _lights = new LightData[Constants.MAX_LIGHTS_DATA_INDEX_COUNT]; - private readonly float[] _scaleArray = Enumerable.Range(5, 21).Select(i => i / 10.0f).ToArray(); // 0.5 => 2.5 + private static readonly Lazy _darknessBlend = new Lazy + ( + () => + { + BlendState state = new BlendState(); + state.ColorSourceBlend = Blend.Zero; + state.ColorDestinationBlend = Blend.SourceColor; + state.ColorBlendFunction = BlendFunction.Add; + + return state; + } + ); + + private static readonly Lazy _altLightsBlend = new Lazy + ( + () => + { + BlendState state = new BlendState(); + state.ColorSourceBlend = Blend.DestinationColor; + state.ColorDestinationBlend = Blend.One; + state.ColorBlendFunction = BlendFunction.Add; + + return state; + } + ); + + + private static XBREffect _xbr; private bool _alphaChanged; private long _alphaTimer; - private bool _deathScreenActive; - private Label _deathScreenLabel; private bool _forceStopScene; private HealthLinesManager _healthLinesManager; + + private bool _isListReady; + private Point _lastSelectedMultiPositionInHouseCustomization; private int _lightCount; + private readonly LightData[] _lights = new LightData[Constants.MAX_LIGHTS_DATA_INDEX_COUNT]; + private Item _multi; private Rectangle _rectangleObj = Rectangle.Empty, _rectanglePlayer; - private RenderTarget2D _viewportRenderTarget, _lightRenderTarget; - private int _scale = 5; // 1.0 - private bool _useObjectHandles; + private Vector3 _selectionLines = Vector3.Zero; + private long _timePing; private uint _timeToPlaceMultiInHouseCustomization; - private Point _lastSelectedMultiPositionInHouseCustomization; - private long _timePing; + private readonly bool _use_render_target = true; // MobileUO: rendering looks completely wrong if this is set to false. need to re-visit later ~mandlar private UseItemQueue _useItemQueue = new UseItemQueue(); - private Vector4 _vectorClear = new Vector4(Vector3.Zero, 1); - private Weather _weather; - + private bool _useObjectHandles; + private RenderTarget2D _world_render_target, _lightRenderTarget; + + // MobileUO: joystick variables public Vector2 JoystickInput; public float JoystickRunThreshold; - public GameScene() : base((int) SceneType.Game, - true, - true, - true) - { - } - - public bool UpdateDrawPosition { get; set; } - - public int ScalePos + public GameScene() : base((int) SceneType.Game, true, true, false) { - get => _scale; - set - { - if (value < 0) - value = 0; - else if (value >= _scaleArray.Length - 1) - value = _scaleArray.Length - 1; - - _scale = value; - } } - public float Scale - { - get => _scaleArray[_scale]; - set => ScalePos = (int) (value * 10) - 5; - } + public bool UpdateDrawPosition { get; set; } public HotkeysManager Hotkeys { get; private set; } @@ -109,14 +124,13 @@ public float Scale public InfoBarManager InfoBars { get; private set; } - public Texture2D ViewportTexture => _viewportRenderTarget; + public Weather Weather { get; private set; } - public Texture2D LightRenderTarget => _lightRenderTarget; + public bool DisconnectionRequested { get; set; } - public Weather Weather => _weather; + public bool UseLights => ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.UseCustomLightLevel ? World.Light.Personal < World.Light.Overall : World.Light.RealPersonal < World.Light.RealOverall; - public bool UseLights => ProfileManager.Current != null && ProfileManager.Current.UseCustomLightLevel ? World.Light.Personal < World.Light.Overall : World.Light.RealPersonal < World.Light.RealOverall; - public bool UseAltLights => ProfileManager.Current != null && ProfileManager.Current.UseAlternativeLights; + public bool UseAltLights => ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.UseAlternativeLights; public void DoubleClickDelayed(uint serial) { @@ -134,14 +148,16 @@ public override void Load() // ######################################################### // [FILE_FIX] // TODO: this code is a workaround to port old macros to the new xml system. - if (ProfileManager.Current.Macros != null) + if (ProfileManager.CurrentProfile.Macros != null) { - for (int i = 0; i < ProfileManager.Current.Macros.Length; i++) - Macros.AppendMacro(ProfileManager.Current.Macros[i]); + for (int i = 0; i < ProfileManager.CurrentProfile.Macros.Length; i++) + { + Macros.PushToBack(ProfileManager.CurrentProfile.Macros[i]); + } Macros.Save(); - ProfileManager.Current.Macros = null; + ProfileManager.CurrentProfile.Macros = null; } // ######################################################### @@ -150,14 +166,15 @@ public override void Load() InfoBars = new InfoBarManager(); InfoBars.Load(); _healthLinesManager = new HealthLinesManager(); - _weather = new Weather(); + Weather = new Weather(); WorldViewportGump viewport = new WorldViewportGump(this); + UIManager.Add(viewport, false); - UIManager.Add(viewport); - - if (!ProfileManager.Current.TopbarGumpIsDisabled) + if (!ProfileManager.CurrentProfile.TopbarGumpIsDisabled) + { TopBarGump.Create(); + } CommandManager.Initialize(); @@ -165,11 +182,12 @@ public override void Load() MessageManager.MessageReceived += ChatOnMessageReceived; - Scale = ProfileManager.Current.DefaultScale; + UIManager.ContainerScale = ProfileManager.CurrentProfile.ContainersScale / 100f; - UIManager.ContainerScale = ProfileManager.Current.ContainersScale / 100f; + // MobileUO: MinimumViewportWidth and MinimumViewportHeight + SDL.SDL_SetWindowMinimumSize(Client.Game.Window.Handle, MinimumViewportWidth, MinimumViewportHeight); - if (ProfileManager.Current.WindowBorderless) + if (ProfileManager.CurrentProfile.WindowBorderless) { Client.Game.SetWindowBorderless(true); } @@ -182,23 +200,36 @@ public override void Load() int w = Settings.GlobalSettings.WindowSize.Value.X; int h = Settings.GlobalSettings.WindowSize.Value.Y; + // MobileUO: MinimumViewportWidth and MinimumViewportHeight w = Math.Max(MinimumViewportWidth, w); h = Math.Max(MinimumViewportHeight, h); Client.Game.SetWindowSize(w, h); } - - CircleOfTransparency.Create(ProfileManager.Current.CircleOfTransparencyRadius); + + + CircleOfTransparency.Create(ProfileManager.CurrentProfile.CircleOfTransparencyRadius); Plugin.OnConnected(); - // UIManager.Add(new Hues_gump()); + Camera.SetZoomValues + ( + new[] + { + .5f, .6f, .7f, .8f, 0.9f, 1f, 1.1f, 1.2f, 1.3f, 1.5f, 1.6f, 1.7f, 1.8f, 1.9f, 2.0f, 2.1f, 2.2f, + 2.3f, 2.4f, 2.5f + } + ); + + Camera.Zoom = ProfileManager.CurrentProfile.DefaultScale; } - private void ChatOnMessageReceived(object sender, UOMessageEventArgs e) + private void ChatOnMessageReceived(object sender, MessageEventArgs e) { if (e.Type == MessageType.Command) + { return; + } string name; string text; @@ -211,16 +242,21 @@ private void ChatOnMessageReceived(object sender, UOMessageEventArgs e) case MessageType.Limit3Spell: if (e.Parent == null || !SerialHelper.IsValid(e.Parent.Serial)) - name = "System"; + { + name = ResGeneral.System; + } else + { name = e.Name; + } text = e.Text; break; case MessageType.System: - name = string.IsNullOrEmpty(e.Name) || e.Name.ToLowerInvariant() == "system" ? "System" : e.Name; + name = string.IsNullOrEmpty(e.Name) || string.Equals(e.Name, "system", StringComparison.InvariantCultureIgnoreCase) ? ResGeneral.System : e.Name; + text = e.Text; break; @@ -230,12 +266,27 @@ private void ChatOnMessageReceived(object sender, UOMessageEventArgs e) text = $"{e.Text}"; if (e.Hue == 0) - hue = ProfileManager.Current.EmoteHue; + { + hue = ProfileManager.CurrentProfile.EmoteHue; + } break; case MessageType.Label: - name = "You see"; + + if (e.Parent == null || !SerialHelper.IsValid(e.Parent.Serial)) + { + name = string.Empty; + } + else if (string.IsNullOrEmpty(e.Name)) + { + name = ResGeneral.YouSee; + } + else + { + name = e.Name; + } + text = e.Text; break; @@ -248,22 +299,22 @@ private void ChatOnMessageReceived(object sender, UOMessageEventArgs e) case MessageType.Party: text = e.Text; - name = $"[Party][{e.Name}]"; - hue = ProfileManager.Current.PartyMessageHue; + name = string.Format(ResGeneral.Party0, e.Name); + hue = ProfileManager.CurrentProfile.PartyMessageHue; break; case MessageType.Alliance: text = e.Text; - name = $"[Alliance][{e.Name}]"; - hue = ProfileManager.Current.AllyMessageHue; + name = string.Format(ResGeneral.Alliance0, e.Name); + hue = ProfileManager.CurrentProfile.AllyMessageHue; break; case MessageType.Guild: text = e.Text; - name = $"[Guild][{e.Name}]"; - hue = ProfileManager.Current.GuildMessageHue; + name = string.Format(ResGeneral.Guild0, e.Name); + hue = ProfileManager.CurrentProfile.GuildMessageHue; break; @@ -276,12 +327,24 @@ private void ChatOnMessageReceived(object sender, UOMessageEventArgs e) break; } + if (!string.IsNullOrEmpty(text)) - World.Journal.Add(text, hue, name, e.TextType, e.IsUnicode); + { + World.Journal.Add + ( + text, + hue, + name, + e.TextType, + e.IsUnicode + ); + } } public override void Unload() { + Client.Game.SetWindowTitle(string.Empty); + ItemHold.Clear(); try @@ -297,7 +360,8 @@ public override void Unload() // special case for wmap. this allow us to save settings UIManager.GetGump()?.SaveSettings(); - ProfileManager.Current?.Save(UIManager.Gumps.OfType().Where(s => s.CanBeSaved).Reverse().ToList()); + ProfileManager.CurrentProfile?.Save(ProfileManager.ProfilePath); + Macros.Save(); InfoBars.Save(); ProfileManager.UnLoadProfile(); @@ -307,14 +371,14 @@ public override void Unload() NetClient.Socket.Disconnected -= SocketOnDisconnected; NetClient.Socket.Disconnect(); - _viewportRenderTarget?.Dispose(); _lightRenderTarget?.Dispose(); + _world_render_target?.Dispose(); CommandManager.UnRegisterAll(); - _weather.Reset(); + Weather.Reset(); UIManager.Clear(); World.Clear(); - UOChatManager.Clear(); + ChatManager.Clear(); DelayedObjectClickManager.Clear(); _useItemQueue?.Clear(); @@ -325,9 +389,11 @@ public override void Unload() Settings.GlobalSettings.WindowSize = new Point(Client.Game.Window.ClientBounds.Width, Client.Game.Window.ClientBounds.Height); + Settings.GlobalSettings.IsWindowMaximized = Client.Game.IsWindowMaximized(); Client.Game.SetWindowBorderless(false); + // MobileUO: dispose CircleOfTransparency.Dispose(); base.Unload(); @@ -336,40 +402,70 @@ public override void Unload() private void SocketOnDisconnected(object sender, SocketError e) { if (Settings.GlobalSettings.Reconnect) + { _forceStopScene = true; + } else { - UIManager.Add(new MessageBoxGump(200, 200, $"Connection lost:\n{StringHelper.AddSpaceBeforeCapital(e.ToString())}", s => - { - if (s) - Client.Game.SetScene(new LoginScene()); - })); + UIManager.Add + ( + new MessageBoxGump + ( + 200, + 200, + string.Format(ResGeneral.ConnectionLost0, StringHelper.AddSpaceBeforeCapital(e.ToString())), + s => + { + if (s) + { + Client.Game.SetScene(new LoginScene()); + } + } + ) + ); } } public void RequestQuitGame() { - UIManager.Add(new QuestionGump("Quit\nUltima Online?", s => - { - if (s) - { - NetClient.Socket.Disconnect(); - Client.Game.SetScene(new LoginScene()); - } - })); + UIManager.Add + ( + new QuestionGump + ( + ResGeneral.QuitPrompt, + s => + { + if (s) + { + if ((World.ClientFeatures.Flags & CharacterListFlags.CLF_OWERWRITE_CONFIGURATION_BUTTON) != 0) + { + DisconnectionRequested = true; + NetClient.Socket.Send_LogoutNotification(); + } + else + { + NetClient.Socket.Disconnect(); + Client.Game.SetScene(new LoginScene()); + } + } + } + ) + ); } public void AddLight(GameObject obj, GameObject lightObject, int x, int y) { - if (_lightCount >= Constants.MAX_LIGHTS_DATA_INDEX_COUNT || (!UseLights && !UseAltLights) || obj == null) + if (_lightCount >= Constants.MAX_LIGHTS_DATA_INDEX_COUNT || !UseLights && !UseAltLights || obj == null) + { return; + } bool canBeAdded = true; int testX = obj.X + 1; int testY = obj.Y + 1; - var tile = World.Map.GetTile(testX, testY); + GameObject tile = World.Map.GetTile(testX, testY); if (tile != null) { @@ -377,9 +473,10 @@ public void AddLight(GameObject obj, GameObject lightObject, int x, int y) for (GameObject o = tile; o != null; o = o.TNext) { - if ((!(o is Static s) || s.ItemData.IsTransparent) && - (!(o is Multi m) || m.ItemData.IsTransparent) || !o.AllowedToDraw) + if ((!(o is Static s) || s.ItemData.IsTransparent) && (!(o is Multi m) || m.ItemData.IsTransparent) || !o.AllowedToDraw) + { continue; + } if (o.Z < _maxZ && o.Z >= z5) { @@ -393,36 +490,46 @@ public void AddLight(GameObject obj, GameObject lightObject, int x, int y) if (canBeAdded) { - ref var light = ref _lights[_lightCount]; + ref LightData light = ref _lights[_lightCount]; ushort graphic = lightObject.Graphic; - if ((graphic >= 0x3E02 && graphic <= 0x3E0B) || - (graphic >= 0x3914 && graphic <= 0x3929) || - graphic == 0x0B1D) + if (graphic >= 0x3E02 && graphic <= 0x3E0B || graphic >= 0x3914 && graphic <= 0x3929 || graphic == 0x0B1D) + { light.ID = 2; + } else { if (obj == lightObject && obj is Item item) + { light.ID = item.LightID; + } else if (lightObject is Item it) + { light.ID = (byte) it.ItemData.LightIndex; + } else if (obj is Mobile _) + { light.ID = 1; + } else { - ref var data = ref TileDataLoader.Instance.StaticData[obj.Graphic]; + ref StaticTiles data = ref TileDataLoader.Instance.StaticData[obj.Graphic]; light.ID = data.Layer; } } if (light.ID >= Constants.MAX_LIGHTS_DATA_INDEX_COUNT) + { return; + } - light.Color = (ushort) (ProfileManager.Current.UseColoredLights ? LightColors.GetHue(graphic) : (ushort) 0); + light.Color = ProfileManager.CurrentProfile.UseColoredLights ? LightColors.GetHue(graphic) : (ushort) 0; if (light.Color != 0) + { light.Color++; + } light.DrawX = x; light.DrawY = y; @@ -432,29 +539,51 @@ public void AddLight(GameObject obj, GameObject lightObject, int x, int y) private void FillGameObjectList() { + +#if RENDER_LIST_LINKED_LIST + _first = null; + _renderList = null; + + _firstLand = null; + _renderListLand = null; + _renderListLandCount = 0; +#endif + + _renderListCount = 0; + _foliageCount = 0; + + if (!World.InGame) + { + return; + } + + _isListReady = false; _alphaChanged = _alphaTimer < Time.Ticks; if (_alphaChanged) + { _alphaTimer = Time.Ticks + Constants.ALPHA_TIME; + } - _foliageIndex++; - if (_foliageIndex >= 100) + FoliageIndex++; + + if (FoliageIndex >= 100) { - _foliageIndex = 1; + FoliageIndex = 1; } - _foliageCount = 0; GetViewPort(); - _renderListCount = 0; - _objectHandlesCount = 0; - - _useObjectHandles = NameOverHeadManager.IsToggled || (Keyboard.Ctrl && Keyboard.Shift); + _useObjectHandles = NameOverHeadManager.IsToggled || Keyboard.Ctrl && Keyboard.Shift; if (_useObjectHandles) + { NameOverHeadManager.Open(); + } else + { NameOverHeadManager.Close(); + } _rectanglePlayer.X = (int) (World.Player.RealScreenPosition.X - World.Player.FrameInfo.X + 22 + World.Player.Offset.X); _rectanglePlayer.Y = (int) (World.Player.RealScreenPosition.Y - World.Player.FrameInfo.Y + 22 + (World.Player.Offset.Y - World.Player.Offset.Z)); @@ -466,8 +595,8 @@ private void FillGameObjectList() int minY = _minTile.Y; int maxX = _maxTile.X; int maxY = _maxTile.Y; - var map = World.Map; - var use_handles = _useObjectHandles; + Map.Map map = World.Map; + bool use_handles = _useObjectHandles; for (int i = 0; i < 2; ++i) { @@ -493,7 +622,14 @@ private void FillGameObjectList() while (x >= minX && x <= maxX && y >= minY && y <= maxY) { - AddTileToRenderList(map.GetTile(x, y), x, y, use_handles, 150/*, null*/); + AddTileToRenderList + ( + map.GetTile(x, y), + x, + y, + use_handles, + 150 /*, null*/ + ); ++x; --y; @@ -505,28 +641,32 @@ private void FillGameObjectList() { for (int i = 0; i < _foliageCount; i++) { - var f = _foliages[i]; + GameObject f = _foliages[i]; - if (f.FoliageIndex == _foliageIndex) + if (f.FoliageIndex == FoliageIndex) { - f.ProcessAlpha(Constants.FOLIAGE_ALPHA); + CalculateAlpha(ref f.AlphaHue, Constants.FOLIAGE_ALPHA); } else { - f.ProcessAlpha(0xFF); + CalculateAlpha(ref f.AlphaHue, 0xFF); } } } - UpdateTextServerEntities(World.Mobiles, true); - UpdateTextServerEntities(World.Items, false); + UpdateTextServerEntities(World.Mobiles.Values, true); + UpdateTextServerEntities(World.Items.Values, false); _renderIndex++; if (_renderIndex >= 100) + { _renderIndex = 1; + } + UpdateDrawPosition = false; + _isListReady = true; } private void UpdateTextServerEntities(IEnumerable entities, bool force) where T : Entity @@ -541,15 +681,20 @@ private void UpdateTextServerEntities(IEnumerable entities, bool force) wh } } - public override void Update(double totalMS, double frameMS) + public override void Update(double totalTime, double frameTime) { - base.Update(totalMS, frameMS); + Profile currentProfile = ProfileManager.CurrentProfile; + Camera.SetGameWindowBounds(currentProfile.GameWindowPosition.X + 5, currentProfile.GameWindowPosition.Y + 5, currentProfile.GameWindowSize.X, currentProfile.GameWindowSize.Y); + + SelectedObject.TranslatedMousePositionByViewport = Camera.MouseToWorldPosition(); + + base.Update(totalTime, frameTime); PacketHandlers.SendMegaClilocRequests(); if (_forceStopScene) { - var loginScene = new LoginScene(); + LoginScene loginScene = new LoginScene(); Client.Game.SetScene(loginScene); loginScene.Reconnect = true; @@ -557,37 +702,35 @@ public override void Update(double totalMS, double frameMS) } if (!World.InGame) + { return; + } _healthLinesManager.Update(); - World.Update(totalMS, frameMS); + World.Update(totalTime, frameTime); AnimatedStaticsManager.Process(); BoatMovingManager.Update(); Pathfinder.ProcessAutoWalk(); DelayedObjectClickManager.Update(); - //Don't allow MoveCharacterByMouseInput on mobile platforms unless UseMouseOnMobile is enabled + // MobileUO: Don't allow MoveCharacterByMouseInput on mobile platforms unless UseMouseOnMobile is enabled // if (UnityEngine.Application.isMobilePlatform == false || UserPreferences.UseMouseOnMobile.CurrentValue == 1) + if (!MoveCharacterByMouseInput() && !currentProfile.DisableArrowBtn) { - if (!MoveCharacterByMouseInput() && !ProfileManager.Current.DisableArrowBtn) - { - Direction dir = DirectionHelper.DirectionFromKeyboardArrows(_flags[0], - _flags[2], - _flags[1], - _flags[3]); + Direction dir = DirectionHelper.DirectionFromKeyboardArrows(_flags[0], _flags[2], _flags[1], _flags[3]); - if (World.InGame && !Pathfinder.AutoWalking && dir != Direction.NONE) - { - World.Player.Walk(dir, ProfileManager.Current.AlwaysRun); - } + if (World.InGame && !Pathfinder.AutoWalking && dir != Direction.NONE) + { + World.Player.Walk(dir, currentProfile.AlwaysRun); } } + // MobileUO: Joystick input if (JoystickInput != Vector2.Zero && UserPreferences.JoystickCancelsFollow.CurrentValue == (int) PreferenceEnums.JoystickCancelsFollow.On) { _continueRunning = false; StopFollowing(); - World.Player.Walk(DirectionHelper.DirectionFromVectors(Vector2.Zero, JoystickInput), ProfileManager.Current.AlwaysRun || JoystickInput.Length() > JoystickRunThreshold); + World.Player.Walk(DirectionHelper.DirectionFromVectors(Vector2.Zero, JoystickInput), ProfileManager.CurrentProfile.AlwaysRun || JoystickInput.Length() > JoystickRunThreshold); } if (_followingMode && SerialHelper.IsMobile(_followingTarget) && !Pathfinder.AutoWalking) @@ -599,46 +742,51 @@ public override void Update(double totalMS, double frameMS) int distance = follow.Distance; if (distance > World.ClientViewRange) + { StopFollowing(); + } else if (distance > 3) + { Pathfinder.WalkTo(follow.X, follow.Y, follow.Z, 1); + } } else + { StopFollowing(); + } } - if (totalMS > _timePing) + if (totalTime > _timePing) { NetClient.Socket.Statistics.SendPing(); - _timePing = (long) totalMS + 1000; + _timePing = (long) totalTime + 1000; } Macros.Update(); - if (((ProfileManager.Current.CorpseOpenOptions == 1 || ProfileManager.Current.CorpseOpenOptions == 3) && TargetManager.IsTargeting) || - ((ProfileManager.Current.CorpseOpenOptions == 2 || ProfileManager.Current.CorpseOpenOptions == 3) && World.Player.IsHidden)) - _useItemQueue.ClearCorpses(); + if ((currentProfile.CorpseOpenOptions == 1 || currentProfile.CorpseOpenOptions == 3) && TargetManager.IsTargeting || (currentProfile.CorpseOpenOptions == 2 || currentProfile.CorpseOpenOptions == 3) && World.Player.IsHidden) + { + _useItemQueue.ClearCorpses(); + } - _useItemQueue.Update(totalMS, frameMS); + _useItemQueue.Update(totalTime, frameTime); - if (!IsMouseOverViewport) - SelectedObject.Object = SelectedObject.LastObject = null; - else + if (!UIManager.IsMouseOverWorld) { - SelectedObject.TranslatedMousePositionByViewport.X = (int) ((Mouse.Position.X - (ProfileManager.Current.GameWindowPosition.X + 5)) * Scale); - SelectedObject.TranslatedMousePositionByViewport.Y = (int) ((Mouse.Position.Y - (ProfileManager.Current.GameWindowPosition.Y + 5)) * Scale); + SelectedObject.Object = SelectedObject.LastObject = null; } + if (TargetManager.IsTargeting && TargetManager.TargetingState == CursorTarget.MultiPlacement && World.CustomHouseManager == null && TargetManager.MultiTargetInfo != null) { if (_multi == null) - _multi = new Item() - { - Graphic = TargetManager.MultiTargetInfo.Model, - Hue = TargetManager.MultiTargetInfo.Hue, - IsMulti = true, - }; + { + _multi = Item.Create(0); + _multi.Graphic = TargetManager.MultiTargetInfo.Model; + _multi.Hue = TargetManager.MultiTargetInfo.Hue; + _multi.IsMulti = true; + } if (SelectedObject.Object is GameObject gobj) { @@ -648,7 +796,8 @@ public override void Update(double totalMS, double frameMS) int cellX = gobj.X % 8; int cellY = gobj.Y % 8; - var o = World.Map.GetChunk(gobj.X, gobj.Y)?.Tiles[cellX, cellY]; + GameObject o = World.Map.GetChunk(gobj.X, gobj.Y)?.Tiles[cellX, cellY]; + if (o != null) { x = o.X; @@ -665,7 +814,9 @@ public override void Update(double totalMS, double frameMS) World.Map.GetMapZ(x, y, out sbyte groundZ, out sbyte _); if (gobj is Static st && st.ItemData.IsWet) + { groundZ = gobj.Z; + } x = (ushort) (x - TargetManager.MultiTargetInfo.XOff); y = (ushort) (y - TargetManager.MultiTargetInfo.YOff); @@ -677,11 +828,11 @@ public override void Update(double totalMS, double frameMS) _multi.UpdateScreenPosition(); _multi.CheckGraphicChange(); _multi.AddToTile(); - World.HouseManager.TryGetHouse(_multi.Serial, out var house); + World.HouseManager.TryGetHouse(_multi.Serial, out House house); foreach (Multi s in house.Components) { - s.IsFromTarget = true; + s.IsHousePreview = true; s.X = (ushort) (_multi.X + s.MultiOffsetX); s.Y = (ushort) (_multi.Y + s.MultiOffsetY); s.Z = (sbyte) (_multi.Z + s.MultiOffsetZ); @@ -700,15 +851,9 @@ public override void Update(double totalMS, double frameMS) if (_isMouseLeftDown && !ItemHold.Enabled) { - if (World.CustomHouseManager != null && - World.CustomHouseManager.SelectedGraphic != 0 && - !World.CustomHouseManager.SeekTile && - !World.CustomHouseManager.Erasing && - Time.Ticks > _timeToPlaceMultiInHouseCustomization) + if (World.CustomHouseManager != null && World.CustomHouseManager.SelectedGraphic != 0 && !World.CustomHouseManager.SeekTile && !World.CustomHouseManager.Erasing && Time.Ticks > _timeToPlaceMultiInHouseCustomization) { - if (SelectedObject.LastObject is GameObject obj && - (obj.X != _lastSelectedMultiPositionInHouseCustomization.X || - obj.Y != _lastSelectedMultiPositionInHouseCustomization.Y)) + if (SelectedObject.LastObject is GameObject obj && (obj.X != _lastSelectedMultiPositionInHouseCustomization.X || obj.Y != _lastSelectedMultiPositionInHouseCustomization.Y)) { World.CustomHouseManager.OnTargetWorld(obj); _timeToPlaceMultiInHouseCustomization = Time.Ticks + 50; @@ -727,67 +872,211 @@ public override void Update(double totalMS, double frameMS) } } - public override void FixedUpdate(double totalMS, double frameMS) + public override void FixedUpdate(double totalTime, double frameTime) { FillGameObjectList(); } - public override bool Draw(UltimaBatcher2D batcher) { - //Revert scaling during game scene drawing + // MobileUO: Revert scaling during game scene drawing var originalBatcherScale = batcher.scale; batcher.scale = 1f; - if (!World.InGame) + + if (!World.InGame || !_isListReady) + { return false; + } + + + int posX = ProfileManager.CurrentProfile.GameWindowPosition.X + 5; + int posY = ProfileManager.CurrentProfile.GameWindowPosition.Y + 5; + int width = ProfileManager.CurrentProfile.GameWindowSize.X; + int height = ProfileManager.CurrentProfile.GameWindowSize.Y; + + if (CheckDeathScreen + ( + batcher, + posX, + posY, + width, + height + )) + { + return true; + } + + + Viewport r_viewport = batcher.GraphicsDevice.Viewport; + Viewport camera_viewport = Camera.GetViewport(); + + Matrix matrix = _use_render_target ? Matrix.Identity : Camera.ViewTransformMatrix; + + + bool can_draw_lights = false; + + if (!_use_render_target) + { + can_draw_lights = PrepareLightsRendering(batcher, ref matrix); + batcher.GraphicsDevice.Viewport = camera_viewport; + } + + DrawWorld(batcher, ref matrix, _use_render_target); + + // MobileUO: Return to original scaling + batcher.scale = originalBatcherScale; + + if (_use_render_target) + { + // MobileUO: commented out + //can_draw_lights = PrepareLightsRendering(batcher, ref matrix); + batcher.GraphicsDevice.Viewport = camera_viewport; + } + + // draw world rt + Vector3 hue = Vector3.Zero; + - if (ProfileManager.Current.EnableDeathScreen) + if (_use_render_target) { - if (_deathScreenLabel == null || _deathScreenLabel.IsDisposed) + //switch (ProfileManager.CurrentProfile.FilterType) + //{ + // default: + // case 0: + // batcher.SetSampler(SamplerState.PointClamp); + // break; + // case 1: + // batcher.SetSampler(SamplerState.AnisotropicClamp); + // break; + // case 2: + // batcher.SetSampler(SamplerState.LinearClamp); + // break; + //} + + if (_xbr == null) { - if (World.Player.IsDead && World.Player.DeathScreenTimer > Time.Ticks) - { - UIManager.Add(_deathScreenLabel = new Label("You are dead.", false, 999, 200, 3) - { - X = (Client.Game.Window.ClientBounds.Width >> 1) - 50, - Y = (Client.Game.Window.ClientBounds.Height >> 1) - 50 - }); - _deathScreenActive = true; - } + _xbr = new XBREffect(batcher.GraphicsDevice); + } + + _xbr.SetSize(width, height); + + + //Point p = Point.Zero; + + //p = Camera.ScreenToWorld(p); + //int minPixelsX = p.X; + //int minPixelsY = p.Y; + + //p.X = Camera.Bounds.Width; + //p.Y = Camera.Bounds.Height; + //p = Camera.ScreenToWorld(p); + //int maxPixelsX = p.X; + //int maxPixelsY = p.Y; + + batcher.Begin(null, Camera.ViewTransformMatrix); + + // MobileUO: fix game window being deattached from view port + batcher.Draw2D + ( + _world_render_target, + posX, + posY, + width, + height, + ref hue + ); + + batcher.End(); + + //batcher.SetSampler(null); + } + + // draw lights + if (can_draw_lights) + { + batcher.Begin(); + + if (UseAltLights) + { + hue.Z = .5f; + batcher.SetBlendState(_altLightsBlend.Value); } - else if (World.Player.DeathScreenTimer < Time.Ticks) + else { - _deathScreenActive = false; - _deathScreenLabel.Dispose(); + batcher.SetBlendState(_darknessBlend.Value); } + + // MobileUO: fix game window being deattached from view port + batcher.Draw2D + ( + _lightRenderTarget, + posX, + posY, + width, + height, + ref hue + ); + + batcher.SetBlendState(null); + batcher.End(); + + hue.Z = 0f; } - DrawWorld(batcher); - batcher.scale = originalBatcherScale; + batcher.Begin(); + DrawOverheads(batcher, posX, posY); + DrawSelection(batcher); + batcher.End(); - return base.Draw(batcher); - } + batcher.GraphicsDevice.Viewport = r_viewport; + return base.Draw(batcher); + } - private void DrawWorld(UltimaBatcher2D batcher) + private void DrawWorld(UltimaBatcher2D batcher, ref Matrix matrix, bool use_render_target) { SelectedObject.Object = null; - bool usecircle = ProfileManager.Current.UseCircleOfTransparency; + if (use_render_target) + { + batcher.GraphicsDevice.SetRenderTarget(_world_render_target); + // MobileUO: clear + batcher.GraphicsDevice.Clear(Color.Black); - batcher.GraphicsDevice.Clear(Color.Black); - batcher.GraphicsDevice.SetRenderTarget(_viewportRenderTarget); - - //NOTE: This extra Clear is important, otherwise hall-of-mirrors effects can happen in areas which are not drawn, such as black tiles surrounding caves - batcher.GraphicsDevice.Clear(ClearOptions.Stencil | ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 0, 0); + // MobileUO: NOTE: This extra Clear is important, otherwise hall-of-mirrors effects can happen in areas which are not drawn, such as black tiles surrounding caves + batcher.GraphicsDevice.Clear(ClearOptions.Stencil | ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 0, 0); + } + else + { + switch (ProfileManager.CurrentProfile.FilterType) + { + default: + case 0: + batcher.SetSampler(SamplerState.PointClamp); - batcher.SetBrightlight(ProfileManager.Current.Brighlight); + break; - batcher.Begin(); + case 1: + batcher.SetSampler(SamplerState.AnisotropicClamp); + + break; + + case 2: + batcher.SetSampler(SamplerState.LinearClamp); + + break; + } + } + + + batcher.Begin(null, matrix); + batcher.SetBrightlight(ProfileManager.CurrentProfile.TerrainShadowsLevel * 0.1f); + + bool usecircle = ProfileManager.CurrentProfile.UseCircleOfTransparency; if (usecircle) { @@ -795,104 +1084,174 @@ private void DrawWorld(UltimaBatcher2D batcher) int fy = (int) (World.Player.RealScreenPosition.Y + (World.Player.Offset.Y - World.Player.Offset.Z)); fx += 22; - //fy -= 22; + fy += 22; CircleOfTransparency.Draw(batcher, fx, fy); } - if (!_deathScreenActive) - { - RenderedObjectsCount = 0; + RenderedObjectsCount = 0; + + int z = World.Player.Z + 5; + + + Vector3 hueVec = Vector3.Zero; + + GameObject.DrawTransparent = usecircle; - int z = World.Player.Z + 5; +#if RENDER_LIST_LINKED_LIST - for (int i = 0; i < _renderListCount; ++i) + GameObject obj = _firstLand; + for (int i = 0; i < _renderListLandCount; obj = obj.RenderListNext, ++i) + { + if (obj.Z <= _maxGroundZ) { - GameObject obj = _renderList[i]; + if (obj.Draw(batcher, obj.RealScreenPosition.X, obj.RealScreenPosition.Y, ref hueVec)) + { + ++RenderedObjectsCount; + } + } + } - if (obj.Z <= _maxGroundZ) + obj = _first; + for (int i = 0; i < _renderListCount; obj = obj.RenderListNext, ++i) + { + if (obj.Z <= _maxGroundZ) + { + if (usecircle) { - GameObject.DrawTransparent = usecircle && obj.TransparentTest(z); + GameObject.DrawTransparent = obj.TransparentTest(z); + } - if (obj.Draw(batcher, obj.RealScreenPosition.X, obj.RealScreenPosition.Y)) - ++RenderedObjectsCount; + if (obj.Draw(batcher, obj.RealScreenPosition.X, obj.RealScreenPosition.Y, ref hueVec)) + { + ++RenderedObjectsCount; } } + } +#else + ushort hue = 0; + for (int i = 0; i < _renderListCount; ++i) + { + ref var info = ref _renderList[i]; + + var obj = info.Object; + + if (obj.Z <= _maxGroundZ) + { + if (usecircle) + { + GameObject.DrawTransparent = obj.TransparentTest(z); + } + + hue = obj.Hue; + obj.Hue = info.Hue; + + if (obj.Draw(batcher, obj.RealScreenPosition.X, obj.RealScreenPosition.Y, ref hueVec)) + { + ++RenderedObjectsCount; + } - if (_multi != null && TargetManager.IsTargeting && TargetManager.TargetingState == CursorTarget.MultiPlacement) - _multi.Draw(batcher, _multi.RealScreenPosition.X, _multi.RealScreenPosition.Y); + obj.Hue = hue; + } } +#endif + + if (_multi != null && TargetManager.IsTargeting && TargetManager.TargetingState == CursorTarget.MultiPlacement) + { + hueVec = Vector3.Zero; + _multi.Draw(batcher, _multi.RealScreenPosition.X, _multi.RealScreenPosition.Y, ref hueVec); + } // draw weather - _weather.Draw(batcher, 0, 0 /*ProfileManager.Current.GameWindowPosition.X, ProfileManager.Current.GameWindowPosition.Y*/); + Weather.Draw(batcher, 0, 0); batcher.End(); + batcher.SetSampler(null); - // DrawLights(batcher); - - batcher.GraphicsDevice.SetRenderTarget(null); + if (use_render_target) + { + batcher.GraphicsDevice.SetRenderTarget(null); + } } - private Item _multi; - - private void DrawLights(UltimaBatcher2D batcher) + private bool PrepareLightsRendering(UltimaBatcher2D batcher, ref Matrix matrix) { - if (_deathScreenActive || (!UseLights && !UseAltLights) || (World.Player.IsDead && ProfileManager.Current.EnableBlackWhiteEffect)) - return; + if (!UseLights && !UseAltLights || World.Player.IsDead && ProfileManager.CurrentProfile.EnableBlackWhiteEffect || _lightRenderTarget == null) + { + return false; + } batcher.GraphicsDevice.SetRenderTarget(_lightRenderTarget); + // MobileUO: clear batcher.GraphicsDevice.Clear(ClearOptions.Target, Color.Black, 0, 0); - if (!UseAltLights) { - var lightColor = World.Light.IsometricLevel; + float lightColor = World.Light.IsometricLevel; - if (ProfileManager.Current.UseDarkNights) + if (ProfileManager.CurrentProfile.UseDarkNights) + { lightColor -= 0.04f; - - _vectorClear.X = _vectorClear.Y = _vectorClear.Z = lightColor; - - batcher.GraphicsDevice.Clear(ClearOptions.Target, _vectorClear, 0, 0); + } + + batcher.GraphicsDevice.Clear(ClearOptions.Target, new Vector4(lightColor, lightColor, lightColor, 1), 0, 0); } - batcher.Begin(); + batcher.Begin(null, matrix); batcher.SetBlendState(BlendState.Additive); Vector3 hue = Vector3.Zero; - hue.Y = ShaderHuesTraslator.SHADER_LIGHTS; + hue.Y = ShaderHueTranslator.SHADER_LIGHTS; hue.Z = 0; for (int i = 0; i < _lightCount; i++) { - ref var l = ref _lights[i]; + ref LightData l = ref _lights[i]; + + UOTexture texture = LightsLoader.Instance.GetTexture(l.ID); - UOTexture32 texture = LightsLoader.Instance.GetTexture(l.ID); if (texture == null) + { continue; + } hue.X = l.Color; - - batcher.DrawSprite(texture, l.DrawX - (texture.Width >> 1), l.DrawY - (texture.Height >> 1), false, ref hue); + + batcher.DrawSprite + ( + texture, + l.DrawX - (texture.Width >> 1), + l.DrawY - (texture.Height >> 1), + false, + ref hue + ); } _lightCount = 0; batcher.SetBlendState(null); batcher.End(); + + batcher.GraphicsDevice.SetRenderTarget(null); + + return true; } public void DrawOverheads(UltimaBatcher2D batcher, int x, int y) { - _healthLinesManager.Draw(batcher, Scale); + _healthLinesManager.Draw(batcher); int renderIndex = _renderIndex - 1; if (renderIndex < 1) + { renderIndex = 99; + } - if (!IsMouseOverViewport) + if (!UIManager.IsMouseOverWorld) + { SelectedObject.Object = null; + } World.WorldTextManager.ProcessWorldText(true); World.WorldTextManager.Draw(batcher, x, y, renderIndex); @@ -900,19 +1259,68 @@ public void DrawOverheads(UltimaBatcher2D batcher, int x, int y) SelectedObject.LastObject = SelectedObject.Object; } - private Vector3 _selectionLines = Vector3.Zero; - - public void DrawSelection(UltimaBatcher2D batcher, int x, int y) + public void DrawSelection(UltimaBatcher2D batcher) { if (_isSelectionActive) { _selectionLines.Z = 0.3F; - batcher.Draw2D(Texture2DCache.GetTexture(Color.Black), _selectionStart.Item1, _selectionStart.Item2, Mouse.Position.X - _selectionStart.Item1, Mouse.Position.Y - _selectionStart.Item2, ref _selectionLines); + + batcher.Draw2D + ( + // MobileUO: remove camera bounds to fix mouse selection + SolidColorTextureCache.GetTexture(Color.Black), + _selectionStart.X, + _selectionStart.Y, + Mouse.Position.X - _selectionStart.X, + Mouse.Position.Y - _selectionStart.Y, + ref _selectionLines + ); + _selectionLines.Z = 0.7f; - batcher.DrawRectangle(Texture2DCache.GetTexture(Color.DeepSkyBlue), _selectionStart.Item1, _selectionStart.Item2, Mouse.Position.X - _selectionStart.Item1, Mouse.Position.Y - _selectionStart.Item2, ref _selectionLines); + + batcher.DrawRectangle + ( + // MobileUO: remove camera bounds to fix mouse selection + SolidColorTextureCache.GetTexture(Color.DeepSkyBlue), + _selectionStart.X, + _selectionStart.Y, + Mouse.Position.X - _selectionStart.X, + Mouse.Position.Y - _selectionStart.Y, + ref _selectionLines + ); } } + private static readonly RenderedText _youAreDeadText = RenderedText.Create + ( + ResGeneral.YouAreDead, + 0xFFFF, + 3, + false, + FontStyle.BlackBorder, + TEXT_ALIGN_TYPE.TS_LEFT + ); + + private bool CheckDeathScreen(UltimaBatcher2D batcher, int x, int y, int width, int height) + { + if (ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.EnableDeathScreen) + { + if (World.InGame) + { + if (World.Player.IsDead && World.Player.DeathScreenTimer > Time.Ticks) + { + batcher.Begin(); + _youAreDeadText.Draw(batcher, x + (width / 2 - _youAreDeadText.Width / 2), y + height / 2); + batcher.End(); + + return true; + } + } + } + + return false; + } + private void StopFollowing() { if (_followingMode) @@ -921,20 +1329,19 @@ private void StopFollowing() _followingTarget = 0; Pathfinder.StopAutoWalk(); - MessageManager.HandleMessage(World.Player, "Stopped following.", String.Empty, 0, MessageType.Regular, 3, TEXT_TYPE.CLIENT, false); + MessageManager.HandleMessage + ( + World.Player, + ResGeneral.StoppedFollowing, + string.Empty, + 0, + MessageType.Regular, + 3, + TextType.CLIENT + ); } } - public void ZoomIn() - { - ScalePos--; - } - - public void ZoomOut() - { - ScalePos++; - } - private struct LightData { public byte ID; @@ -942,4 +1349,4 @@ private struct LightData public int DrawX, DrawY; } } -} \ No newline at end of file +} diff --git a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneDrawingSorting.cs b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneDrawingSorting.cs deleted file mode 120000 index 8ddcfe803..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneDrawingSorting.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../external/ClassicUO/src/Game/Scenes/GameSceneDrawingSorting.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneDrawingSorting.cs b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneDrawingSorting.cs new file mode 100644 index 000000000..aece5c64c --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneDrawingSorting.cs @@ -0,0 +1,1304 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using ClassicUO.Configuration; +using ClassicUO.Game.Data; +using ClassicUO.Game.GameObjects; +using ClassicUO.Game.Managers; +using ClassicUO.Game.Map; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using ClassicUO.Utility; +using ClassicUO.Utility.Logging; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace ClassicUO.Game.Scenes +{ + internal partial class GameScene + { + private static GameObject[] _foliages = new GameObject[100]; + private static readonly TreeUnion[] _treeInfos = + { + new TreeUnion(0x0D45, 0x0D4C), + new TreeUnion(0x0D5C, 0x0D62), + new TreeUnion(0x0D73, 0x0D79), + new TreeUnion(0x0D87, 0x0D8B), + new TreeUnion(0x12BE, 0x12C7), + new TreeUnion(0x0D4D, 0x0D53), + new TreeUnion(0x0D63, 0x0D69), + new TreeUnion(0x0D7A, 0x0D7F), + new TreeUnion(0x0D8C, 0x0D90) + }; + + private sbyte _maxGroundZ; + private int _maxZ; + private Vector2 _minPixel, _maxPixel; + private bool _noDrawRoofs; + private Point _offset, _maxTile, _minTile, _last_scaled_offset; + private int _oldPlayerX, _oldPlayerY, _oldPlayerZ; + private int _renderIndex = 1; + + +#if RENDER_LIST_LINKED_LIST + private int _renderListCount, _foliageCount, _renderListLandCount; + private GameObject _first, _renderList; + private GameObject _firstLand, _renderListLand; + private bool _useLandList = false; +#else + struct DrawingInfo + { + public GameObject Object; + public ushort Hue; + } + + private static DrawingInfo[] _renderList = new DrawingInfo[10000]; + private int _renderListCount, _foliageCount; +#endif + + + public Point ScreenOffset => _offset; + public sbyte FoliageIndex { get; private set; } + + + public void UpdateMaxDrawZ(bool force = false) + { + int playerX = World.Player.X; + int playerY = World.Player.Y; + int playerZ = World.Player.Z; + + if (playerX == _oldPlayerX && playerY == _oldPlayerY && playerZ == _oldPlayerZ && !force) + { + return; + } + + _oldPlayerX = playerX; + _oldPlayerY = playerY; + _oldPlayerZ = playerZ; + + sbyte maxGroundZ = 127; + _maxGroundZ = 127; + _maxZ = 127; + _noDrawRoofs = !ProfileManager.CurrentProfile.DrawRoofs; + int bx = playerX; + int by = playerY; + Chunk chunk = World.Map.GetChunk(bx, by, false); + + if (chunk != null) + { + int x = playerX % 8; + int y = playerY % 8; + + int pz14 = playerZ + 14; + int pz16 = playerZ + 16; + + for (GameObject obj = chunk.GetHeadObject(x, y); obj != null; obj = obj.TNext) + { + sbyte tileZ = obj.Z; + + if (obj is Land l) + { + if (l.IsStretched) + { + tileZ = l.AverageZ; + } + + if (pz16 <= tileZ) + { + maxGroundZ = (sbyte) pz16; + _maxGroundZ = (sbyte) pz16; + _maxZ = _maxGroundZ; + + break; + } + + continue; + } + + if (obj is Mobile) + { + continue; + } + + + //if (obj is Item it && !it.ItemData.IsRoof || !(obj is Static) && !(obj is Multi)) + // continue; + + if (tileZ > pz14 && _maxZ > tileZ) + { + ref StaticTiles itemdata = ref TileDataLoader.Instance.StaticData[obj.Graphic]; + + //if (GameObjectHelper.TryGetStaticData(obj, out var itemdata) && ((ulong) itemdata.Flags & 0x20004) == 0 && (!itemdata.IsRoof || itemdata.IsSurface)) + if (((ulong) itemdata.Flags & 0x20004) == 0 && (!itemdata.IsRoof || itemdata.IsSurface)) + { + _maxZ = tileZ; + _noDrawRoofs = true; + } + } + } + + int tempZ = _maxZ; + _maxGroundZ = (sbyte) _maxZ; + playerX++; + playerY++; + bx = playerX; + by = playerY; + chunk = World.Map.GetChunk(bx, by, false); + + if (chunk != null) + { + x = playerX % 8; + y = playerY % 8; + + for (GameObject obj2 = chunk.GetHeadObject(x, y); obj2 != null; obj2 = obj2.TNext) + { + //if (obj is Item it && !it.ItemData.IsRoof || !(obj is Static) && !(obj is Multi)) + // continue; + + if (obj2 is Mobile) + { + continue; + } + + sbyte tileZ = obj2.Z; + + if (tileZ > pz14 && _maxZ > tileZ) + { + if (!(obj2 is Land)) + { + ref StaticTiles itemdata = ref TileDataLoader.Instance.StaticData[obj2.Graphic]; + + if (((ulong) itemdata.Flags & 0x204) == 0 && itemdata.IsRoof) + { + _maxZ = tileZ; + World.Map.ClearBockAccess(); + _maxGroundZ = World.Map.CalculateNearZ(tileZ, playerX, playerY, tileZ); + _noDrawRoofs = true; + } + } + + //if (GameObjectHelper.TryGetStaticData(obj2, out var itemdata) && ((ulong) itemdata.Flags & 0x204) == 0 && itemdata.IsRoof) + //{ + // _maxZ = tileZ; + // World.Map.ClearBockAccess(); + // _maxGroundZ = World.Map.CalculateNearZ(tileZ, playerX, playerY, tileZ); + // _noDrawRoofs = true; + //} + } + } + + tempZ = _maxGroundZ; + } + + _maxZ = _maxGroundZ; + + if (tempZ < pz16) + { + _maxZ = pz16; + _maxGroundZ = (sbyte) pz16; + } + + _maxGroundZ = maxGroundZ; + } + } + + private void IsFoliageUnion(ushort graphic, int x, int y, int z) + { + for (int i = 0; i < _treeInfos.Length; i++) + { + ref TreeUnion info = ref _treeInfos[i]; + + if (info.Start <= graphic && graphic <= info.End) + { + while (graphic > info.Start) + { + graphic--; + x--; + y++; + } + + for (graphic = info.Start; graphic <= info.End; graphic++, x++, y--) + { + ApplyFoliageTransparency(graphic, x, y, z); + } + + break; + } + } + } + + private void ApplyFoliageTransparency(ushort graphic, int x, int y, int z) + { + GameObject tile = World.Map.GetTile(x, y); + + if (tile != null) + { + for (GameObject obj = tile; obj != null; obj = obj.TNext) + { + ushort testGraphic = obj.Graphic; + + if (testGraphic == graphic && obj.Z == z) + { + obj.FoliageIndex = FoliageIndex; + } + } + } + } + + private void UpdateObjectHandles(Entity obj, bool useObjectHandles) + { + if (useObjectHandles && NameOverHeadManager.IsAllowed(obj)) + { + if (obj.ObjectHandlesStatus != ObjectHandlesStatus.CLOSED) + { + if (obj.ObjectHandlesStatus == ObjectHandlesStatus.NONE) + { + obj.ObjectHandlesStatus = ObjectHandlesStatus.OPEN; + } + + obj.UpdateTextCoordsV(); + } + } + else if (obj.ObjectHandlesStatus != ObjectHandlesStatus.NONE) + { + obj.ObjectHandlesStatus = ObjectHandlesStatus.NONE; + obj.UpdateTextCoordsV(); + } + } + + private void CheckIfBehindATree(GameObject obj, int worldX, int worldY, ref StaticTiles itemData) + { + if (itemData.IsFoliage) + { + if (obj.FoliageIndex != FoliageIndex) + { + sbyte index = 0; + + bool check = World.Player.X <= worldX && World.Player.Y <= worldY; + + if (!check) + { + check = World.Player.Y <= worldY && World.Player.X <= worldX + 1; + + if (!check) + { + check = World.Player.X <= worldX && World.Player.Y <= worldY + 1; + } + } + + if (check) + { + ArtTexture texture = ArtLoader.Instance.GetTexture(obj.Graphic); + + if (texture != null) + { + Rectangle rect = texture.ImageRectangle; + + rect.X = obj.RealScreenPosition.X - (rect.Width >> 1) + rect.X; + rect.Y = obj.RealScreenPosition.Y - rect.Height + rect.Y; + + check = Exstentions.InRect(ref rect, ref _rectanglePlayer); + + if (check) + { + index = FoliageIndex; + IsFoliageUnion(obj.Graphic, obj.X, obj.Y, obj.Z); + } + } + } + + obj.FoliageIndex = index; + } + + if (_foliageCount >= _foliages.Length) + { + int newsize = _foliages.Length + 50; + Array.Resize(ref _foliages, newsize); + } + + _foliages[_foliageCount++] = obj; + } + } + + private bool ProcessAlpha(GameObject obj, ref StaticTiles itemData) + { + if (obj.Z >= _maxZ) + { + bool changed; + + if (_alphaChanged) + { + changed = CalculateAlpha(ref obj.AlphaHue, 0); + } + else + { + changed = obj.AlphaHue != 0; + } + + if (!changed) + { + obj.UseInRender = (byte)_renderIndex; + + return false; + } + } + else if (_noDrawRoofs && itemData.IsRoof) + { + if (_alphaChanged) + { + if (!CalculateAlpha(ref obj.AlphaHue, 0)) + { + return false; + } + } + + return obj.AlphaHue != 0; + } + else if (itemData.IsTranslucent) + { + if (_alphaChanged) + { + CalculateAlpha(ref obj.AlphaHue, 178); + } + } + else if (!itemData.IsFoliage && obj.AlphaHue != 0xFF) + { + if (_alphaChanged) + { + CalculateAlpha(ref obj.AlphaHue, 0xFF); + } + } + + return true; + } + + private static bool CalculateAlpha(ref byte alphaHue, int maxAlpha) + { + if (ProfileManager.CurrentProfile != null && !ProfileManager.CurrentProfile.UseObjectsFading) + { + alphaHue = (byte)maxAlpha; + + return maxAlpha != 0; + } + + bool result = false; + + int alpha = alphaHue; + + if (alpha > maxAlpha) + { + alpha -= 25; + + if (alpha < maxAlpha) + { + alpha = maxAlpha; + } + + result = true; + } + else if (alpha < maxAlpha) + { + alpha += 25; + + if (alpha > maxAlpha) + { + alpha = maxAlpha; + } + + result = true; + } + + alphaHue = (byte)alpha; + + return result; + } + + private static byte CalculateObjectHeight(ref int maxObjectZ, ref StaticTiles itemData) + { + if (itemData.Height != 0xFF /*&& itemData.Flags != 0*/) + { + byte height = itemData.Height; + + if (itemData.Height == 0) + { + if (!itemData.IsBackground && !itemData.IsSurface) + { + height = 10; + } + } + + if ((itemData.Flags & TileFlag.Bridge) != 0) + { + height /= 2; + } + + maxObjectZ += height; + + return height; + } + + return 0xFF; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool IsFoliageVisibleAtSeason(ref StaticTiles itemData, Season season) + { + return !(itemData.IsFoliage && !itemData.IsMultiMovable && season >= Season.Winter); + } + + private void PushToRenderList(GameObject obj, GameObject parent, bool island = false) + { +#if RENDER_LIST_LINKED_LIST + if (!_useLandList && island) + { + island = false; + } + + ref GameObject first = ref (island ? ref _firstLand : ref _first); + ref GameObject renderList = ref (island ? ref _renderListLand : ref _renderList); + + if (first == null) + { + first = renderList = obj; + } + else + { + renderList.RenderListNext = obj; + renderList = obj; + } + + obj.RenderListNext = null; + + if (island) + { + ++_renderListLandCount; + } + else + { + ++_renderListCount; + } +#else + if (_renderListCount >= _renderList.Length) + { + Array.Resize(ref _renderList, _renderList.Length + 1000); + } + + ref var r = ref _renderList[_renderListCount++]; + + r.Object = obj; + r.Hue = CUOEnviroment.Debug ? (ushort)(parent != null ? 0x0044 : 300 + obj.PriorityZ) : obj.Hue; +#endif + + obj.UseInRender = (byte)_renderIndex; + } + + private unsafe bool AddTileToRenderList(GameObject obj, int worldX, int worldY, bool useObjectHandles, int maxZ, GameObject parent = null) + { + for (; obj != null; obj = obj.TNext) + { + if (obj.CurrentRenderIndex == _renderIndex) + { + continue; + } + + if (UpdateDrawPosition && obj.CurrentRenderIndex != _renderIndex || obj.IsPositionChanged) + { + obj.UpdateRealScreenPosition(_offset.X, _offset.Y); + } + + obj.UseInRender = 0xFF; + + int screenX = obj.RealScreenPosition.X; + + if (screenX < _minPixel.X || screenX > _maxPixel.X) + { + break; + } + + int screenY = obj.RealScreenPosition.Y; + int maxObjectZ = obj.PriorityZ; + +#if !OK + if (obj is Land land) + { + if (maxObjectZ > maxZ) + { + return false; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY > _maxPixel.Y) + { + continue; + } + + if (land.IsStretched) + { + screenY += (land.Z << 2); + screenY -= (land.MinZ << 2); + } + + if (screenY < _minPixel.Y) + { + continue; + } + + PushToRenderList(obj, parent, true); + } + else if (obj is Static staticc) + { + ref var itemData = ref staticc.ItemData; + + if (itemData.IsInternal) + { + continue; + } + + if (!IsFoliageVisibleAtSeason(ref itemData, World.Season)) + { + continue; + } + + if (!ProcessAlpha(obj, ref itemData)) + { + continue; + } + + //we avoid to hide impassable foliage or bushes, if present... + if (itemData.IsFoliage && ProfileManager.CurrentProfile.TreeToStumps) + { + continue; + } + + if (!itemData.IsMultiMovable && staticc.IsVegetation && ProfileManager.CurrentProfile.HideVegetation) + { + continue; + } + + byte height = 0; + + if (obj.AllowedToDraw) + { + height = CalculateObjectHeight(ref maxObjectZ, ref itemData); + } + + if (maxObjectZ > maxZ) + { + return itemData.Height != 0 && maxObjectZ - maxZ < height; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + CheckIfBehindATree(obj, worldX, worldY, ref itemData); + + PushToRenderList(obj, parent); + } + else if (obj is Multi multi) + { + ref StaticTiles itemData = ref multi.ItemData; + + if (itemData.IsInternal) + { + continue; + } + + if (!ProcessAlpha(obj, ref itemData)) + { + continue; + } + + //we avoid to hide impassable foliage or bushes, if present... + + if (!itemData.IsMultiMovable) + { + if (itemData.IsFoliage && ProfileManager.CurrentProfile.TreeToStumps) + { + continue; + } + + if (multi.IsVegetation && ProfileManager.CurrentProfile.HideVegetation) + { + continue; + } + } + + byte height = 0; + + if (obj.AllowedToDraw) + { + height = CalculateObjectHeight(ref maxObjectZ, ref itemData); + } + + if (maxObjectZ > maxZ) + { + return itemData.Height != 0 && maxObjectZ - maxZ < height; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + if (multi.IsMovable) + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, true); + } + + CheckIfBehindATree(obj, worldX, worldY, ref itemData); + + PushToRenderList(obj, parent); + } + else if (obj is Mobile mobile) + { + UpdateObjectHandles(mobile, useObjectHandles); + + maxObjectZ += Constants.DEFAULT_CHARACTER_HEIGHT; + + if (maxObjectZ > maxZ) + { + return false; + } + + StaticTiles empty = default; + + if (!ProcessAlpha(obj, ref empty)) + { + continue; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, false); + + PushToRenderList(obj, parent); + } + else if (obj is Item item) + { + ref StaticTiles itemData = ref (item.IsMulti ? ref TileDataLoader.Instance.StaticData[item.MultiGraphic] : ref item.ItemData); + + if (!item.IsCorpse && itemData.IsInternal) + { + continue; + } + + if (item.IsCorpse || (!item.IsMulti && (!item.IsLocked || item.IsLocked && itemData.IsContainer))) + { + UpdateObjectHandles(item, useObjectHandles); + } + + if (!IsFoliageVisibleAtSeason(ref itemData, World.Season)) + { + continue; + } + + if (!ProcessAlpha(obj, ref itemData)) + { + continue; + } + + if (!itemData.IsMultiMovable && itemData.IsFoliage && ProfileManager.CurrentProfile.TreeToStumps) + { + continue; + } + + byte height = 0; + + if (obj.AllowedToDraw) + { + height = CalculateObjectHeight(ref maxObjectZ, ref itemData); + } + + if (maxObjectZ > maxZ) + { + return itemData.Height != 0 && maxObjectZ - maxZ < height; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + if (item.IsCorpse) + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, false); + } + else if (itemData.IsMultiMovable) + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, true); + } + + if (!item.IsCorpse) + { + CheckIfBehindATree(obj, worldX, worldY, ref itemData); + } + + PushToRenderList(obj, parent); + } + else if (obj is GameEffect effect) + { + if (!ProcessAlpha(obj, ref TileDataLoader.Instance.StaticData[effect.Graphic])) + { + continue; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + if (effect.IsMoving) // TODO: check for typeof(MovingEffect) ? + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, true); + } + + PushToRenderList(obj, parent); + } +#else + + switch (obj) + { + case Land land: + + if (maxObjectZ > maxZ) + { + return false; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY > _maxPixel.Y) + { + continue; + } + + if (land.IsStretched) + { + screenY += (land.Z << 2); + screenY -= (land.MinZ << 2); + } + + if (screenY < _minPixel.Y) + { + continue; + } + + PushToRenderList(obj, parent, true); + + break; + + case Static staticc: + + ref StaticTiles itemData = ref staticc.ItemData; + + if (itemData.IsInternal) + { + continue; + } + + if (!IsFoliageVisibleAtSeason(ref itemData, World.Season)) + { + continue; + } + + if (!ProcessAlpha(obj, ref itemData)) + { + continue; + } + + //we avoid to hide impassable foliage or bushes, if present... + if (itemData.IsFoliage && ProfileManager.CurrentProfile.TreeToStumps) + { + continue; + } + + if (!itemData.IsMultiMovable && staticc.IsVegetation && ProfileManager.CurrentProfile.HideVegetation) + { + continue; + } + + byte height = 0; + if (obj.AllowedToDraw) + { + height = CalculateObjectHeight(ref maxObjectZ, ref itemData); + } + + if (maxObjectZ > maxZ) + { + return itemData.Height != 0 && maxObjectZ - maxZ < height; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + CheckIfBehindATree(obj, worldX, worldY, ref itemData); + + PushToRenderList(obj, parent); + + break; + + case Multi multi: + + itemData = ref multi.ItemData; + + if (itemData.IsInternal) + { + continue; + } + + if (!ProcessAlpha(obj, ref itemData)) + { + continue; + } + + //we avoid to hide impassable foliage or bushes, if present... + if (itemData.IsFoliage && ProfileManager.CurrentProfile.TreeToStumps) + { + continue; + } + + if (!itemData.IsMultiMovable && multi.IsVegetation && ProfileManager.CurrentProfile.HideVegetation) + { + continue; + } + + if (obj.AllowedToDraw) + { + height = CalculateObjectHeight(ref maxObjectZ, ref itemData); + } + else + { + height = 0; + } + + if (maxObjectZ > maxZ) + { + return itemData.Height != 0 && maxObjectZ - maxZ < height; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + if (multi.IsMovable) + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, true); + } + + CheckIfBehindATree(obj, worldX, worldY, ref itemData); + + PushToRenderList(obj, parent); + + break; + + case Mobile mobile: + + UpdateObjectHandles(mobile, useObjectHandles); + + maxObjectZ += Constants.DEFAULT_CHARACTER_HEIGHT; + + if (maxObjectZ > maxZ) + { + return false; + } + + StaticTiles empty = default; + + if (!ProcessAlpha(obj, ref empty)) + { + continue; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, false); + + PushToRenderList(obj, parent); + + break; + + case Item item: + + itemData = ref (item.IsMulti ? ref TileDataLoader.Instance.StaticData[item.MultiGraphic] : ref item.ItemData); + + if (!item.IsCorpse && itemData.IsInternal) + { + continue; + } + + if (item.IsCorpse || (!item.IsMulti && (!item.IsLocked || item.IsLocked && itemData.IsContainer))) + { + UpdateObjectHandles(item, useObjectHandles); + } + + if (!IsFoliageVisibleAtSeason(ref itemData, World.Season)) + { + continue; + } + + if (!ProcessAlpha(obj, ref itemData)) + { + continue; + } + + if (itemData.IsFoliage && ProfileManager.CurrentProfile.TreeToStumps) + { + continue; + } + + if (obj.AllowedToDraw) + { + height = CalculateObjectHeight(ref maxObjectZ, ref itemData); + } + else + { + height = 0; + } + + if (maxObjectZ > maxZ) + { + return itemData.Height != 0 && maxObjectZ - maxZ < height; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + if (item.IsCorpse) + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, false); + } + else if (itemData.IsMultiMovable) + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, true); + } + + if (!item.IsCorpse) + { + CheckIfBehindATree(obj, worldX, worldY, ref itemData); + } + + PushToRenderList(obj, parent); + + break; + + case GameEffect effect: + + if (!ProcessAlpha(obj, ref TileDataLoader.Instance.StaticData[effect.Graphic])) + { + continue; + } + + obj.CurrentRenderIndex = _renderIndex; + + if (screenY < _minPixel.Y || screenY > _maxPixel.Y) + { + continue; + } + + if (effect.IsMoving) // TODO: check for typeof(MovingEffect) ? + { + AddOffsetCharacterTileToRenderList(obj, useObjectHandles, true); + } + + PushToRenderList(obj, parent); + + break; + + default: + + Log.Error("OBJECT NOT RENDERED --> " + obj.GetType()); + + break; + } + +#endif + } + + return false; + } + + private static readonly sbyte[,] _offets = new sbyte[8, 2] + { + { 1, -1 }, + { 1, -2 }, + { 0, 1 }, + { -1, 2 }, + { 1, 0 }, + { 1, 1 }, + { 2, -2 }, + { 2, -1 }, + }; + + + private void AddOffsetCharacterTileToRenderList(GameObject entity, bool useObjectHandles, bool ignoreDefaultHeightOffset) + { + short maxZ = entity.PriorityZ; + + /* Rotation 45° side: ---> + * + * [ ][ ][ ][ ][ ][ ][ ] + * [ ][ ][ ][ ][1][6][ ] + * [ ][ ][ ][ ][0][7][ ] + * [ ][ ][ ][+][4][ ][ ] + * [ ][ ][ ][2][5][ ][ ] + * [ ][ ][3][ ][ ][ ][ ] + * [ ][ ][ ][ ][ ][ ][ ] + * + */ + + for (int i = 0; i < 8; ++i) + { + int charX = entity.X + _offets[i, 0]; + int charY = entity.Y + _offets[i, 1]; + + if (charX < _minTile.X || charX > _maxTile.X || + charY < _minTile.Y || charY > _maxTile.Y) + { + continue; + } + + int currentMaxZ = maxZ; + + if (!ignoreDefaultHeightOffset && i <= 1) + { + currentMaxZ += 20; + } + + GameObject tile = World.Map.GetTile(charX, charY); + + if (tile != null) + { + if (AddTileToRenderList + ( + tile, + charX, + charY, + useObjectHandles, + currentMaxZ, + entity + ) && i >= 4) + { + break; + } + } + } + } + + // MobileUO: Using old method to correctly render the world + // Using older version from here: https://raw.githubusercontent.com/ClassicUO/ClassicUO/beb925392f5cd1ffcd7f7460c76f9068926157d0/src/Game/Scenes/GameSceneDrawingSorting.cs + private void GetViewPort() + { + var zoom = Camera.Zoom; + + int oldDrawOffsetX = _offset.X; + int oldDrawOffsetY = _offset.Y; + int winGamePosX = 0; + int winGamePosY = 0; + int winGameWidth = ProfileManager.CurrentProfile.GameWindowSize.X; + int winGameHeight = ProfileManager.CurrentProfile.GameWindowSize.Y; + int winGameCenterX = winGamePosX + (winGameWidth >> 1); + int winGameCenterY = winGamePosY + (winGameHeight >> 1) + (World.Player.Z << 2); + winGameCenterX -= (int) World.Player.Offset.X; + winGameCenterY -= (int) (World.Player.Offset.Y - World.Player.Offset.Z); + int winDrawOffsetX = (World.Player.X - World.Player.Y) * 22 - winGameCenterX; + int winDrawOffsetY = (World.Player.X + World.Player.Y) * 22 - winGameCenterY; + + int winGameScaledOffsetX; + int winGameScaledOffsetY; + int winGameScaledWidth; + int winGameScaledHeight; + + if (ProfileManager.CurrentProfile != null /*&& ProfileManager.Current.EnableScaleZoom*/) + { + float left = winGamePosX; + float right = winGameWidth + left; + float top = winGamePosY; + float bottom = winGameHeight + top; + float newRight = right * zoom; + float newBottom = bottom * zoom; + + winGameScaledOffsetX = (int)(left * zoom - (newRight - right)); + winGameScaledOffsetY = (int)(top * zoom - (newBottom - bottom)); + winGameScaledWidth = (int)(newRight - winGameScaledOffsetX); + winGameScaledHeight = (int)(newBottom - winGameScaledOffsetY); + } + else + { + winGameScaledOffsetX = 0; + winGameScaledOffsetY = 0; + winGameScaledWidth = 0; + winGameScaledHeight = 0; + } + + + int width = (int) ((winGameWidth / 44 + 1) * zoom); + int height = (int) ((winGameHeight / 44 + 1) * zoom); + + winDrawOffsetX += winGameScaledOffsetX >> 1; + winDrawOffsetY += winGameScaledOffsetY >> 1; + + const int MAX = 70; + + if (width > MAX) + width = MAX; + + if (height > MAX) + height = MAX; + + int size = Math.Max(width, height); + + if (size < World.ClientViewRange) + size = World.ClientViewRange; + + int realMinRangeX = World.Player.X - size; + + if (realMinRangeX < 0) + realMinRangeX = 0; + int realMaxRangeX = World.Player.X + size; + + //if (realMaxRangeX >= FileManager.Map.MapsDefaultSize[World.Map.Index][0]) + // realMaxRangeX = FileManager.Map.MapsDefaultSize[World.Map.Index][0]; + int realMinRangeY = World.Player.Y - size; + + if (realMinRangeY < 0) + realMinRangeY = 0; + int realMaxRangeY = World.Player.Y + size; + + //if (realMaxRangeY >= FileManager.Map.MapsDefaultSize[World.Map.Index][1]) + // realMaxRangeY = FileManager.Map.MapsDefaultSize[World.Map.Index][1]; + int minBlockX = (realMinRangeX >> 3) - 1; + int minBlockY = (realMinRangeY >> 3) - 1; + int maxBlockX = (realMaxRangeX >> 3) + 1; + int maxBlockY = (realMaxRangeY >> 3) + 1; + + if (minBlockX < 0) + minBlockX = 0; + + if (minBlockY < 0) + minBlockY = 0; + + if (maxBlockX >= MapLoader.Instance.MapsDefaultSize[World.Map.Index, 0]) + maxBlockX = MapLoader.Instance.MapsDefaultSize[World.Map.Index, 0] - 1; + + if (maxBlockY >= MapLoader.Instance.MapsDefaultSize[World.Map.Index, 1]) + maxBlockY = MapLoader.Instance.MapsDefaultSize[World.Map.Index, 1] - 1; + + int drawOffset = (int) (zoom * 40.0); + float maxX = winGamePosX + winGameWidth ; + float maxY = winGamePosY + winGameHeight; + float newMaxX = maxX * zoom + drawOffset; + float newMaxY = maxY * zoom + drawOffset; + + int minPixelsX = (int) ((winGamePosX) * zoom /*- (newMaxX - maxX)*/ ) - drawOffset * 2; + int maxPixelsX = (int) newMaxX; + int minPixelsY = (int) ((winGamePosY) * zoom /*- (newMaxY - maxY)*/) - drawOffset * 2; + int maxPixlesY = (int) newMaxY; + + if (UpdateDrawPosition || oldDrawOffsetX != winDrawOffsetX || oldDrawOffsetY != winDrawOffsetY) + { + UpdateDrawPosition = true; + + if (_world_render_target == null || _world_render_target.Width != (int)(winGameWidth * zoom) || _world_render_target.Height != (int)(winGameHeight * zoom)) + { + _world_render_target?.Dispose(); + _lightRenderTarget?.Dispose(); + + _world_render_target = new RenderTarget2D(Client.Game.GraphicsDevice, (int)(winGameWidth * zoom), (int)(winGameHeight * zoom), false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8, 0, RenderTargetUsage.DiscardContents); + _lightRenderTarget = new RenderTarget2D(Client.Game.GraphicsDevice, (int)(winGameWidth * zoom), (int)(winGameHeight * zoom), false, SurfaceFormat.Color, DepthFormat.Depth24Stencil8, 0, RenderTargetUsage.DiscardContents); + } + } + + _minTile.X = realMinRangeX; + _minTile.Y = realMinRangeY; + _maxTile.X = realMaxRangeX; + _maxTile.Y = realMaxRangeY; + + _minPixel.X = minPixelsX; + _minPixel.Y = minPixelsY; + _maxPixel.X = maxPixelsX; + _maxPixel.Y = maxPixlesY; + + _offset.X = winDrawOffsetX; + _offset.Y = winDrawOffsetY; + + + UpdateMaxDrawZ(); + } + + private struct TreeUnion + { + public TreeUnion(ushort start, ushort end) + { + Start = start; + End = end; + } + + public readonly ushort Start; + public readonly ushort End; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneInputHandler.cs b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneInputHandler.cs deleted file mode 120000 index 5f84a0fab..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneInputHandler.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../external/ClassicUO/src/Game/Scenes/GameSceneInputHandler.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneInputHandler.cs b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneInputHandler.cs new file mode 100644 index 000000000..7f550b7e4 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/Scenes/GameSceneInputHandler.cs @@ -0,0 +1,1336 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Linq; +using ClassicUO.Configuration; +using ClassicUO.Game.Data; +using ClassicUO.Game.GameObjects; +using ClassicUO.Game.Managers; +using ClassicUO.Game.UI.Gumps; +using ClassicUO.Input; +using ClassicUO.IO.Resources; +using ClassicUO.Network; +using ClassicUO.Resources; +using ClassicUO.Utility; +using Microsoft.Xna.Framework; +using SDL2; +using MathHelper = ClassicUO.Utility.MathHelper; + +namespace ClassicUO.Game.Scenes +{ + internal partial class GameScene + { + private bool _boatRun, _boatIsMoving; + private readonly bool[] _flags = new bool[5]; + private bool _followingMode; + private uint _followingTarget; + private uint _holdMouse2secOverItemTime; + private bool _isMouseLeftDown; + private bool _isSelectionActive; + private Direction _lastBoatDirection; + private bool _requestedWarMode; + private bool _rightMousePressed, _continueRunning; + private Point _selectionStart, _selectionEnd; + + + private bool MoveCharacterByMouseInput() + { + if ((_rightMousePressed || _continueRunning) && World.InGame) // && !Pathfinder.AutoWalking) + { + if (Pathfinder.AutoWalking) + { + Pathfinder.StopAutoWalk(); + } + + int x = ProfileManager.CurrentProfile.GameWindowPosition.X + (ProfileManager.CurrentProfile.GameWindowSize.X >> 1); + int y = ProfileManager.CurrentProfile.GameWindowPosition.Y + (ProfileManager.CurrentProfile.GameWindowSize.Y >> 1); + + Direction direction = (Direction) GameCursor.GetMouseDirection + ( + x, + y, + Mouse.Position.X, + Mouse.Position.Y, + 1 + ); + + double mouseRange = MathHelper.Hypotenuse(x - Mouse.Position.X, y - Mouse.Position.Y); + + Direction facing = direction; + + if (facing == Direction.North) + { + facing = (Direction) 8; + } + + bool run = mouseRange >= 190; + + if (World.Player.IsDrivingBoat) + { + if (!_boatIsMoving || _boatRun != run || _lastBoatDirection != facing - 1) + { + _boatRun = run; + _lastBoatDirection = facing - 1; + _boatIsMoving = true; + + BoatMovingManager.MoveRequest(facing - 1, (byte) (run ? 2 : 1)); + } + } + else + { + World.Player.Walk(facing - 1, run); + } + + return true; + } + + return false; + } + + private bool CanDragSelectOnObject(GameObject obj) + { + return obj is null || obj is Static || obj is Land || obj is Multi || obj is Item tmpitem && tmpitem.IsLocked; + } + + + private bool DragSelectModifierActive() + { + // src: https://github.com/andreakarasho/ClassicUO/issues/621 + // drag-select should be disabled when using nameplates + if (Keyboard.Ctrl && Keyboard.Shift) + { + return false; + } + + if (ProfileManager.CurrentProfile.DragSelectModifierKey == 0) + { + return true; + } + + if (ProfileManager.CurrentProfile.DragSelectModifierKey == 1 && Keyboard.Ctrl) + { + return true; + } + + if (ProfileManager.CurrentProfile.DragSelectModifierKey == 2 && Keyboard.Shift) + { + return true; + } + + return false; + } + + + private void DoDragSelect() + { + if (_selectionStart.X > Mouse.Position.X) + { + _selectionEnd.X = _selectionStart.X; + _selectionStart.X = Mouse.Position.X; + } + else + { + _selectionEnd.X = Mouse.Position.X; + } + + if (_selectionStart.Y > Mouse.Position.Y) + { + _selectionEnd.Y = _selectionStart.Y; + _selectionStart.Y = Mouse.Position.Y; + } + else + { + _selectionEnd.Y = Mouse.Position.Y; + } + + // MobileUO: remove camera bounds to fix mouse selection + _rectangleObj.X = _selectionStart.X; + _rectangleObj.Y = _selectionStart.Y; + _rectangleObj.Width = _selectionEnd.X - _rectangleObj.X; + _rectangleObj.Height = _selectionEnd.Y - _rectangleObj.Y; + + int finalX = 100; + int finalY = 100; + + bool useCHB = ProfileManager.CurrentProfile.CustomBarsToggled; + + Rectangle rect = useCHB ? new Rectangle(0, 0, HealthBarGumpCustom.HPB_BAR_WIDTH, HealthBarGumpCustom.HPB_HEIGHT_MULTILINE) : GumpsLoader.Instance.GetTexture(0x0804).Bounds; + + foreach (Mobile mobile in World.Mobiles.Values) + { + if (ProfileManager.CurrentProfile.DragSelectHumanoidsOnly && !mobile.IsHuman) + { + continue; + } + + Point p = mobile.RealScreenPosition; + + p.X += (int) mobile.Offset.X + 22 + 5; + p.Y += (int) (mobile.Offset.Y - mobile.Offset.Z) + 22 + 5; + p.X -= mobile.FrameInfo.X; + p.Y -= mobile.FrameInfo.Y; + + Point size = new Point(p.X + mobile.FrameInfo.Width, p.Y + mobile.FrameInfo.Height); + + p = Camera.WorldToScreen(p); + _rectanglePlayer.X = p.X; + _rectanglePlayer.Y = p.Y; + + + size = Camera.WorldToScreen(size); + _rectanglePlayer.Width = size.X - p.X; + _rectanglePlayer.Height = size.Y - p.Y; + + if (_rectangleObj.Intersects(_rectanglePlayer)) + { + if (mobile != World.Player) + { + if (UIManager.GetGump(mobile) != null) + { + continue; + } + + BaseHealthBarGump hbgc; + + if (useCHB) + { + hbgc = new HealthBarGumpCustom(mobile); + } + else + { + hbgc = new HealthBarGump(mobile); + } + + if (finalY >= ProfileManager.CurrentProfile.GameWindowPosition.Y + ProfileManager.CurrentProfile.GameWindowSize.Y - 100) + { + finalY = 100; + finalX += rect.Width + 2; + } + + if (finalX >= ProfileManager.CurrentProfile.GameWindowPosition.X + ProfileManager.CurrentProfile.GameWindowSize.X - 100) + { + finalX = 100; + } + + hbgc.X = finalX; + hbgc.Y = finalY; + + + foreach (BaseHealthBarGump bar in UIManager.Gumps.OfType() + //.OrderBy(s => mobile.NotorietyFlag) + //.OrderBy(s => s.ScreenCoordinateX) ///testing placement SYRUPZ SYRUPZ SYRUPZ + .OrderBy(s => s.ScreenCoordinateX) + .ThenBy(s => s.ScreenCoordinateY)) + { + if (bar.Bounds.Intersects(hbgc.Bounds)) + { + finalY = bar.Bounds.Bottom + 2; + + if (finalY >= ProfileManager.CurrentProfile.GameWindowPosition.Y + ProfileManager.CurrentProfile.GameWindowSize.Y - 100) + { + finalY = 100; + finalX = bar.Bounds.Right + 2; + } + + if (finalX >= ProfileManager.CurrentProfile.GameWindowPosition.X + ProfileManager.CurrentProfile.GameWindowSize.X - 100) + { + finalX = 100; + } + + hbgc.X = finalX; + hbgc.Y = finalY; + } + } + + + finalY += rect.Height + 2; + + + UIManager.Add(hbgc); + + hbgc.SetInScreen(); + } + } + } + + _isSelectionActive = false; + } + + internal override bool OnMouseDown(MouseButtonType button) + { + switch (button) + { + case MouseButtonType.Left: return OnLeftMouseDown(); + case MouseButtonType.Right: return OnRightMouseDown(); + } + + return false; + } + + internal override bool OnMouseUp(MouseButtonType button) + { + switch (button) + { + case MouseButtonType.Left: return OnLeftMouseUp(); + case MouseButtonType.Right: return OnRightMouseUp(); + } + + return false; + } + + internal override bool OnMouseDoubleClick(MouseButtonType button) + { + switch (button) + { + case MouseButtonType.Left: return OnLeftMouseDoubleClick(); + case MouseButtonType.Right: return OnRightMouseDoubleClick(); + } + + return false; + } + + private bool OnLeftMouseDown() + { + if (UIManager.PopupMenu != null && !UIManager.PopupMenu.Bounds.Contains(Mouse.Position.X, Mouse.Position.Y)) + { + UIManager.ShowGamePopup(null); + } + + if (!UIManager.IsMouseOverWorld) + { + return false; + } + + if (World.CustomHouseManager != null) + { + _isMouseLeftDown = true; + + if (TargetManager.IsTargeting && TargetManager.TargetingState == CursorTarget.MultiPlacement && (World.CustomHouseManager.SelectedGraphic != 0 || World.CustomHouseManager.Erasing || World.CustomHouseManager.SeekTile) && SelectedObject.LastObject is GameObject obj) + { + World.CustomHouseManager.OnTargetWorld(obj); + _lastSelectedMultiPositionInHouseCustomization.X = obj.X; + _lastSelectedMultiPositionInHouseCustomization.Y = obj.Y; + } + } + else + { + SelectedObject.LastLeftDownObject = SelectedObject.Object; + + if (ProfileManager.CurrentProfile.EnableDragSelect && DragSelectModifierActive()) + { + if (CanDragSelectOnObject(SelectedObject.Object as GameObject)) + { + _selectionStart = Mouse.Position; + _isSelectionActive = true; + } + } + else + { + _isMouseLeftDown = true; + _holdMouse2secOverItemTime = Time.Ticks; + } + } + + return true; + } + + private bool OnLeftMouseUp() + { + if (UIManager.PopupMenu != null && !UIManager.PopupMenu.Bounds.Contains(Mouse.Position.X, Mouse.Position.Y)) + { + UIManager.ShowGamePopup(null); + } + + if (_isMouseLeftDown) + { + _isMouseLeftDown = false; + _holdMouse2secOverItemTime = 0; + } + + // drag-select code comes first to allow selection finish on mouseup outside of viewport + if (_selectionStart.X == Mouse.Position.X && _selectionStart.Y == Mouse.Position.Y) + { + _isSelectionActive = false; + } + + if (_isSelectionActive) + { + DoDragSelect(); + + return true; + } + + if (!UIManager.IsMouseOverWorld) + { + return false; + } + + if (UIManager.SystemChat != null && !UIManager.SystemChat.IsFocused) + { + UIManager.KeyboardFocusControl = null; + UIManager.SystemChat.SetFocus(); + } + + if (!ProfileManager.CurrentProfile.DisableAutoMove && _rightMousePressed) + { + _continueRunning = true; + } + + BaseGameObject lastObj = SelectedObject.LastObject;// SelectedObject.LastLeftDownObject; <-- this makes the target cursor less responsive if you move the mouse fast + SelectedObject.LastLeftDownObject = null; + + if (UIManager.IsDragging) + { + return false; + } + + if (ItemHold.Enabled && !ItemHold.IsFixedPosition) + { + uint drop_container = 0xFFFF_FFFF; + bool can_drop = false; + ushort dropX = 0; + ushort dropY = 0; + sbyte dropZ = 0; + + GameObject gobj = SelectedObject.LastObject as GameObject; + + if (gobj is Entity obj) + { + can_drop = obj.Distance <= Constants.DRAG_ITEMS_DISTANCE; + + if (can_drop) + { + if (obj is Item it && it.ItemData.IsContainer || obj is Mobile) + { + dropX = 0xFFFF; + dropY = 0xFFFF; + dropZ = 0; + drop_container = obj.Serial; + } + else if (obj is Item it2 && (it2.ItemData.IsSurface || it2.ItemData.IsStackable && it2.Graphic == ItemHold.Graphic)) + { + dropX = obj.X; + dropY = obj.Y; + dropZ = obj.Z; + + if (it2.ItemData.IsSurface) + { + dropZ += (sbyte) (it2.ItemData.Height == 0xFF ? 0 : it2.ItemData.Height); + } + else + { + drop_container = obj.Serial; + } + } + } + else + { + Client.Game.Scene.Audio.PlaySound(0x0051); + } + } + else if (gobj is Land || gobj is Static || gobj is Multi) + { + can_drop = gobj.Distance <= Constants.DRAG_ITEMS_DISTANCE; + + if (can_drop) + { + dropX = gobj.X; + dropY = gobj.Y; + dropZ = gobj.Z; + + if (gobj is Land land) + { + + } + else + { + ref StaticTiles itemData = ref TileDataLoader.Instance.StaticData[gobj.Graphic]; + + if (itemData.IsSurface) + { + dropZ += (sbyte)(itemData.Height == 0xFF ? 0 : itemData.Height); + } + } + } + else + { + Client.Game.Scene.Audio.PlaySound(0x0051); + } + } + + + if (can_drop) + { + if (drop_container == 0xFFFF_FFFF && dropX == 0 && dropY == 0) + { + can_drop = false; + } + + if (can_drop) + { + GameActions.DropItem + ( + ItemHold.Serial, + dropX, + dropY, + dropZ, + drop_container + ); + } + } + } + else if (TargetManager.IsTargeting) + { + switch (TargetManager.TargetingState) + { + case CursorTarget.Grab: + case CursorTarget.SetGrabBag: + case CursorTarget.Position: + case CursorTarget.Object: + case CursorTarget.MultiPlacement when World.CustomHouseManager == null: + { + BaseGameObject obj = lastObj; + + if (obj is TextObject ov) + { + obj = ov.Owner; + } + + switch (obj) + { + case Entity ent: + TargetManager.Target(ent.Serial); + + break; + + case Land land: + TargetManager.Target + ( + 0, + land.X, + land.Y, + land.Z, + land.TileData.IsWet + ); + + break; + + case GameObject o: + TargetManager.Target(o.Graphic, o.X, o.Y, o.Z); + + break; + } + } + + Mouse.LastLeftButtonClickTime = 0; + + break; + + case CursorTarget.SetTargetClientSide: + { + BaseGameObject obj = lastObj; + + if (obj is TextObject ov) + { + obj = ov.Owner; + } + else if (obj is GameEffect eff && eff.Source != null) + { + obj = eff.Source; + } + + switch (obj) + { + case Entity ent: + TargetManager.Target(ent.Serial); + UIManager.Add(new InspectorGump(ent)); + + break; + + case Land land: + TargetManager.Target(0, land.X, land.Y, land.Z); + UIManager.Add(new InspectorGump(land)); + + break; + + case GameObject o: + TargetManager.Target(o.Graphic, o.X, o.Y, o.Z); + UIManager.Add(new InspectorGump(o)); + + break; + } + + Mouse.LastLeftButtonClickTime = 0; + } + + break; + + case CursorTarget.HueCommandTarget: + + if (SelectedObject.Object is Entity selectedEntity) + { + CommandManager.OnHueTarget(selectedEntity); + } + + break; + } + } + else + { + GameObject obj = lastObj as GameObject; + + switch (obj) + { + case Static st: + string name = StringHelper.GetPluralAdjustedString(st.Name, st.ItemData.Count > 1); + + if (string.IsNullOrEmpty(name)) + { + name = ClilocLoader.Instance.GetString(1020000 + st.Graphic, st.ItemData.Name); + } + + MessageManager.HandleMessage + ( + null, + name, + string.Empty, + 0x03b2, + MessageType.Label, + 3, + TextType.CLIENT + ); + + obj.AddMessage + ( + MessageType.Label, + name, + 3, + 0x03b2, + false, + TextType.CLIENT + ); + + + if (obj.TextContainer != null && obj.TextContainer.MaxSize != 1) + { + obj.TextContainer.MaxSize = 1; + } + + break; + + case Multi multi: + name = multi.Name; + + if (string.IsNullOrEmpty(name)) + { + name = ClilocLoader.Instance.GetString(1020000 + multi.Graphic, multi.ItemData.Name); + } + + MessageManager.HandleMessage + ( + null, + name, + string.Empty, + 0x03b2, + MessageType.Label, + 3, + TextType.CLIENT + ); + + obj.AddMessage + ( + MessageType.Label, + name, + 3, + 0x03b2, + false, + TextType.CLIENT + ); + + if (obj.TextContainer != null && obj.TextContainer.MaxSize == 5) + { + obj.TextContainer.MaxSize = 1; + } + + break; + + case Entity ent: + + if (Keyboard.Alt && ent is Mobile) + { + MessageManager.HandleMessage + ( + World.Player, + ResGeneral.NowFollowing, + string.Empty, + 0, + MessageType.Regular, + 3, + TextType.CLIENT + ); + + _followingMode = true; + _followingTarget = ent; + } + else if (!DelayedObjectClickManager.IsEnabled) + { + DelayedObjectClickManager.Set(ent.Serial, Mouse.Position.X, Mouse.Position.Y, Time.Ticks + Mouse.MOUSE_DELAY_DOUBLE_CLICK); + } + + break; + } + } + + return true; + } + + private bool OnLeftMouseDoubleClick() + { + bool result = false; + + if (!UIManager.IsMouseOverWorld) + { + result = DelayedObjectClickManager.IsEnabled; + + if (result) + { + DelayedObjectClickManager.Clear(); + + return false; + } + + return false; + } + + BaseGameObject obj = SelectedObject.LastObject; + + switch (obj) + { + case Item item: + result = true; + + if (!GameActions.OpenCorpse(item)) + { + GameActions.DoubleClick(item); + } + + break; + + case Mobile mob: + result = true; + + if (World.Player.InWarMode && World.Player != mob) + { + GameActions.Attack(mob); + } + else + { + GameActions.DoubleClick(mob); + } + + break; + + case TextObject msg when msg.Owner is Entity entity: + result = true; + GameActions.DoubleClick(entity); + + break; + + default: + World.LastObject = 0; + + break; + } + + if (result) + { + DelayedObjectClickManager.Clear(); + } + + return result; + } + + + private bool OnRightMouseDown() + { + if (UIManager.PopupMenu != null && !UIManager.PopupMenu.Bounds.Contains(Mouse.Position.X, Mouse.Position.Y)) + { + UIManager.ShowGamePopup(null); + } + + if (!UIManager.IsMouseOverWorld) + { + return false; + } + + _rightMousePressed = true; + _continueRunning = false; + StopFollowing(); + + return true; + } + + + private bool OnRightMouseUp() + { + if (UIManager.PopupMenu != null && !UIManager.PopupMenu.Bounds.Contains(Mouse.Position.X, Mouse.Position.Y)) + { + UIManager.ShowGamePopup(null); + } + + _rightMousePressed = false; + + if (_boatIsMoving) + { + _boatIsMoving = false; + BoatMovingManager.MoveRequest(World.Player.Direction, 0); + } + + return UIManager.IsMouseOverWorld; + } + + + private bool OnRightMouseDoubleClick() + { + if (!UIManager.IsMouseOverWorld) + { + return false; + } + + if (ProfileManager.CurrentProfile.EnablePathfind && !Pathfinder.AutoWalking) + { + if (ProfileManager.CurrentProfile.UseShiftToPathfind && !Keyboard.Shift) + { + return false; + } + + if (SelectedObject.Object is GameObject obj) + { + if (obj is Static || obj is Multi || obj is Item) + { + ref StaticTiles itemdata = ref TileDataLoader.Instance.StaticData[obj.Graphic]; + + if (itemdata.IsSurface && Pathfinder.WalkTo(obj.X, obj.Y, obj.Z, 0)) + { + World.Player.AddMessage + ( + MessageType.Label, + ResGeneral.Pathfinding, + 3, + 0, + false, + TextType.CLIENT + ); + + return true; + } + } + else if (obj is Land && Pathfinder.WalkTo(obj.X, obj.Y, obj.Z, 0)) + { + World.Player.AddMessage + ( + MessageType.Label, + ResGeneral.Pathfinding, + 3, + 0, + false, + TextType.CLIENT + ); + + return true; + } + } + + //if (SelectedObject.Object is Land || GameObjectHelper.TryGetStaticData(SelectedObject.Object as GameObject, out var itemdata) && itemdata.IsSurface) + //{ + // if (SelectedObject.Object is GameObject obj && Pathfinder.WalkTo(obj.X, obj.Y, obj.Z, 0)) + // { + + // } + //} + } + + return false; + } + + + internal override bool OnMouseWheel(bool up) + { + if (Keyboard.Ctrl && ItemHold.Enabled) + { + if (!up && !ItemHold.IsFixedPosition) + { + ItemHold.IsFixedPosition = true; + ItemHold.IgnoreFixedPosition = true; + ItemHold.FixedX = Mouse.Position.X; + ItemHold.FixedY = Mouse.Position.Y; + } + + if (ItemHold.IgnoreFixedPosition) + { + return true; + } + } + + if (!UIManager.IsMouseOverWorld) + { + return false; + } + + if (Keyboard.Ctrl && ProfileManager.CurrentProfile.EnableMousewheelScaleZoom) + { + Camera.ZoomIndex += up ? -1 : 1; + + return true; + } + + return false; + } + + + internal override bool OnMouseDragging() + { + if (!UIManager.IsMouseOverWorld) + { + return false; + } + + bool ok = true; + + if (Mouse.LButtonPressed && !ItemHold.Enabled) + { + Point offset = Mouse.LDragOffset; + + if (!UIManager.GameCursor.IsDraggingCursorForced && // don't trigger "sallos ez grab" when dragging wmap or skill + !_isSelectionActive && // and ofc when selection is enabled + (Math.Abs(offset.X) > Constants.MIN_PICKUP_DRAG_DISTANCE_PIXELS || Math.Abs(offset.Y) > Constants.MIN_PICKUP_DRAG_DISTANCE_PIXELS)) + { + Entity obj; + + if (ProfileManager.CurrentProfile.SallosEasyGrab && SelectedObject.LastObject is Entity ent && SelectedObject.LastLeftDownObject == null) + { + obj = ent; + } + else + { + obj = SelectedObject.LastLeftDownObject as Entity; + } + + if (obj != null) + { + if (SerialHelper.IsMobile(obj.Serial) || obj is Item it && it.IsDamageable) + { + BaseHealthBarGump customgump = UIManager.GetGump(obj); + customgump?.Dispose(); + + if (obj == World.Player) + { + StatusGumpBase.GetStatusGump()?.Dispose(); + } + + if (ProfileManager.CurrentProfile.CustomBarsToggled) + { + Rectangle rect = new Rectangle(0, 0, HealthBarGumpCustom.HPB_WIDTH, HealthBarGumpCustom.HPB_HEIGHT_SINGLELINE); + + UIManager.Add + ( + customgump = new HealthBarGumpCustom(obj) + { + X = Mouse.Position.X - (rect.Width >> 1), + Y = Mouse.Position.Y - (rect.Height >> 1) + } + ); + } + else + { + Rectangle rect = GumpsLoader.Instance.GetTexture(0x0804).Bounds; + + UIManager.Add + ( + customgump = new HealthBarGump(obj) + { + X = Mouse.LClickPosition.X - (rect.Width >> 1), + Y = Mouse.LClickPosition.Y - (rect.Height >> 1) + } + ); + } + + UIManager.AttemptDragControl(customgump, true); + ok = false; + } + else if (obj is Item item) + { + GameActions.PickUp(item, Mouse.Position.X, Mouse.Position.Y); + } + } + + SelectedObject.LastLeftDownObject = null; + } + } + + return ok; + } + + internal override void OnKeyDown(SDL.SDL_KeyboardEvent e) + { + if (e.keysym.sym == SDL.SDL_Keycode.SDLK_TAB && e.repeat != 0) + { + return; + } + + if (e.keysym.sym == SDL.SDL_Keycode.SDLK_ESCAPE && TargetManager.IsTargeting) + { + TargetManager.CancelTarget(); + } + + if (UIManager.KeyboardFocusControl != UIManager.SystemChat.TextBoxControl) + { + return; + } + + switch (e.keysym.sym) + { + case SDL.SDL_Keycode.SDLK_ESCAPE: + + if (Pathfinder.AutoWalking && Pathfinder.PathindingCanBeCancelled) + { + Pathfinder.StopAutoWalk(); + } + + break; + + case SDL.SDL_Keycode.SDLK_TAB when !ProfileManager.CurrentProfile.DisableTabBtn: + + if (ProfileManager.CurrentProfile.HoldDownKeyTab) + { + if (!_requestedWarMode) + { + _requestedWarMode = true; + + if (!World.Player.InWarMode) + { + NetClient.Socket.Send_ChangeWarMode(true); + } + } + } + + break; + + + // chat system activation + + case SDL.SDL_Keycode.SDLK_1 when Keyboard.Shift: // ! + case SDL.SDL_Keycode.SDLK_BACKSLASH when Keyboard.Shift: // \ + + if (ProfileManager.CurrentProfile.ActivateChatAfterEnter && ProfileManager.CurrentProfile.ActivateChatAdditionalButtons && !UIManager.SystemChat.IsActive) + { + UIManager.SystemChat.IsActive = true; + } + + break; + + case SDL.SDL_Keycode.SDLK_EXCLAIM: // ! + case SDL.SDL_Keycode.SDLK_SEMICOLON: // ; + case SDL.SDL_Keycode.SDLK_COLON: // : + case SDL.SDL_Keycode.SDLK_SLASH: // / + case SDL.SDL_Keycode.SDLK_BACKSLASH: // \ + case SDL.SDL_Keycode.SDLK_PERIOD: // . + case SDL.SDL_Keycode.SDLK_KP_PERIOD: // . + case SDL.SDL_Keycode.SDLK_COMMA: // , + case SDL.SDL_Keycode.SDLK_LEFTBRACKET: // [ + case SDL.SDL_Keycode.SDLK_MINUS: // - + case SDL.SDL_Keycode.SDLK_KP_MINUS: // - + if (ProfileManager.CurrentProfile.ActivateChatAfterEnter && ProfileManager.CurrentProfile.ActivateChatAdditionalButtons && !UIManager.SystemChat.IsActive) + { + if (!Keyboard.Shift && !Keyboard.Alt && !Keyboard.Ctrl) + { + UIManager.SystemChat.IsActive = true; + } + else if (Keyboard.Shift && e.keysym.sym == SDL.SDL_Keycode.SDLK_SEMICOLON) + { + UIManager.SystemChat.IsActive = true; + } + } + + break; + + case SDL.SDL_Keycode.SDLK_RETURN: + case SDL.SDL_Keycode.SDLK_KP_ENTER: + + if (UIManager.KeyboardFocusControl == UIManager.SystemChat.TextBoxControl) + { + if (ProfileManager.CurrentProfile.ActivateChatAfterEnter) + { + UIManager.SystemChat.Mode = ChatMode.Default; + + if (!(Keyboard.Shift && ProfileManager.CurrentProfile.ActivateChatShiftEnterSupport)) + { + UIManager.SystemChat.ToggleChatVisibility(); + } + } + + return; + } + + break; + } + + if (UIManager.KeyboardFocusControl == UIManager.SystemChat.TextBoxControl && UIManager.SystemChat.IsActive && ProfileManager.CurrentProfile.ActivateChatAfterEnter) + { + return; + } + + bool canExecuteMacro = UIManager.KeyboardFocusControl == UIManager.SystemChat.TextBoxControl && UIManager.SystemChat.Mode >= ChatMode.Default; + + if (canExecuteMacro) + { + Macro macro = Macros.FindMacro(e.keysym.sym, Keyboard.Alt, Keyboard.Ctrl, Keyboard.Shift); + + if (macro != null && e.keysym.sym != SDL.SDL_Keycode.SDLK_UNKNOWN) + { + if (macro.Items != null && macro.Items is MacroObject mac) + { + if (mac.Code == MacroType.Walk) + { + _flags[4] = true; + + switch (mac.SubCode) + { + case MacroSubType.NW: + _flags[0] = true; + + break; + + case MacroSubType.SW: + _flags[1] = true; + + break; + + case MacroSubType.SE: + _flags[2] = true; + + break; + + case MacroSubType.NE: + _flags[3] = true; + + break; + + case MacroSubType.N: + _flags[0] = true; + _flags[3] = true; + + break; + + case MacroSubType.S: + _flags[1] = true; + _flags[2] = true; + + break; + + case MacroSubType.E: + _flags[3] = true; + _flags[2] = true; + + break; + + case MacroSubType.W: + _flags[0] = true; + _flags[1] = true; + + break; + } + } + else + { + Macros.SetMacroToExecute(mac); + Macros.WaitingBandageTarget = false; + Macros.WaitForTargetTimer = 0; + Macros.Update(); + } + } + } + else + { + if (string.IsNullOrEmpty(UIManager.SystemChat.TextBoxControl.Text)) + { + switch (e.keysym.sym) + { + case SDL.SDL_Keycode.SDLK_UP: + _flags[0] = true; + + break; + + case SDL.SDL_Keycode.SDLK_LEFT: + _flags[1] = true; + + break; + + case SDL.SDL_Keycode.SDLK_DOWN: + _flags[2] = true; + + break; + + case SDL.SDL_Keycode.SDLK_RIGHT: + _flags[3] = true; + + break; + } + } + } + } + } + + + internal override void OnKeyUp(SDL.SDL_KeyboardEvent e) + { + if (ProfileManager.CurrentProfile.EnableMousewheelScaleZoom && ProfileManager.CurrentProfile.RestoreScaleAfterUnpressCtrl && !Keyboard.Ctrl) + { + Camera.Zoom = ProfileManager.CurrentProfile.DefaultScale; + } + + if (_flags[4]) + { + Macro macro = Macros.FindMacro(e.keysym.sym, Keyboard.Alt, Keyboard.Ctrl, Keyboard.Shift); + + if (macro != null && e.keysym.sym != SDL.SDL_Keycode.SDLK_UNKNOWN) + { + if (macro.Items != null && macro.Items is MacroObject mac && mac.Code == MacroType.Walk) + { + _flags[4] = false; + + switch (mac.SubCode) + { + case MacroSubType.NW: + _flags[0] = false; + + break; + + case MacroSubType.SW: + _flags[1] = false; + + break; + + case MacroSubType.SE: + _flags[2] = false; + + break; + + case MacroSubType.NE: + _flags[3] = false; + + break; + + case MacroSubType.N: + _flags[0] = false; + _flags[3] = false; + + break; + + case MacroSubType.S: + _flags[1] = false; + _flags[2] = false; + + break; + + case MacroSubType.E: + _flags[3] = false; + _flags[2] = false; + + break; + + case MacroSubType.W: + _flags[0] = false; + _flags[1] = false; + + break; + } + + Macros.SetMacroToExecute(mac); + Macros.WaitForTargetTimer = 0; + Macros.Update(); + + for (int i = 0; i < 4; i++) + { + if (_flags[i]) + { + _flags[4] = true; + + break; + } + } + } + } + } + + switch (e.keysym.sym) + { + case SDL.SDL_Keycode.SDLK_UP: + _flags[0] = false; + + break; + + case SDL.SDL_Keycode.SDLK_LEFT: + _flags[1] = false; + + break; + + case SDL.SDL_Keycode.SDLK_DOWN: + _flags[2] = false; + + break; + + case SDL.SDL_Keycode.SDLK_RIGHT: + _flags[3] = false; + + break; + } + + if (e.keysym.sym == SDL.SDL_Keycode.SDLK_TAB && !ProfileManager.CurrentProfile.DisableTabBtn) + { + if (ProfileManager.CurrentProfile.HoldDownKeyTab) + { + if (_requestedWarMode) + { + NetClient.Socket.Send_ChangeWarMode(false); + _requestedWarMode = false; + } + } + else + { + GameActions.ToggleWarMode(); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/Scenes/LoginScene.cs b/Assets/Scripts/ClassicUO/src/Game/Scenes/LoginScene.cs index eea336288..f0f29014f 100644 --- a/Assets/Scripts/ClassicUO/src/Game/Scenes/LoginScene.cs +++ b/Assets/Scripts/ClassicUO/src/Game/Scenes/LoginScene.cs @@ -1,31 +1,42 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.IO; -using System.Linq; using System.Net; +using System.Net.NetworkInformation; using System.Net.Sockets; using System.Text; - +using System.Threading.Tasks; using ClassicUO.Configuration; using ClassicUO.Data; using ClassicUO.Game.Data; @@ -35,15 +46,17 @@ using ClassicUO.Game.UI.Gumps.CharCreation; using ClassicUO.Game.UI.Gumps.Login; using ClassicUO.IO; +using ClassicUO.IO.Resources; using ClassicUO.Network; +using ClassicUO.Network.Encryption; +using ClassicUO.Resources; using ClassicUO.Utility; using ClassicUO.Utility.Logging; using Microsoft.Xna.Framework; -using ClassicUO.IO.Resources; namespace ClassicUO.Game.Scenes { - enum LoginSteps + internal enum LoginSteps { Main, Connecting, @@ -61,17 +74,13 @@ internal sealed class LoginScene : Scene { private Gump _currentGump; private LoginSteps _lastLoginStep; - private long? _reconnectTime; - private int _reconnectTryCounter = 1; private uint _pingTime; + private long _reconnectTime; + private int _reconnectTryCounter = 1; + private bool _autoLogin; - - public LoginScene() : base((int) SceneType.Login, - false, - false, - true) + public LoginScene() : base((int) SceneType.Login, false, false, true) { - } @@ -93,26 +102,27 @@ public LoginScene() : base((int) SceneType.Login, public string Password { get; private set; } + public bool CanAutologin => _autoLogin || Reconnect; + + public override void Load() { base.Load(); - //Engine.FpsLimit = Settings.GlobalSettings.MaxLoginFPS; + _autoLogin = Settings.GlobalSettings.AutoLogin; UIManager.Add(new LoginBackground()); UIManager.Add(_currentGump = new LoginGump(this)); // Registering Packet Events - NetClient.PacketReceived += NetClient_PacketReceived; + //NetClient.PacketReceived += NetClient_PacketReceived; NetClient.Socket.Disconnected += NetClient_Disconnected; NetClient.LoginSocket.Connected += NetClient_Connected; NetClient.LoginSocket.Disconnected += Login_NetClient_Disconnected; - int music = Client.Version >= ClientVersion.CV_7000 ? 78 : Client.Version > ClientVersion.CV_308Z ? 0 : 8; - - Audio.PlayMusic(music, false, true); + Audio.PlayMusic(Audio.LoginMusicIndex, false, true); - if (((Settings.GlobalSettings.AutoLogin || Reconnect) && (CurrentLoginStep != LoginSteps.Main)) || CUOEnviroment.SkipLoginScreen) + if (CanAutologin && CurrentLoginStep != LoginSteps.Main || CUOEnviroment.SkipLoginScreen) { if (!string.IsNullOrEmpty(Settings.GlobalSettings.Username)) { @@ -123,16 +133,18 @@ public override void Load() } if (Client.Game.IsWindowMaximized()) + { Client.Game.RestoreWindow(); + } + Client.Game.SetWindowSize(640, 480); } public override void Unload() { - Audio.StopMusic(); - UIManager.GetGump()?.Dispose(); + _currentGump?.Dispose(); // UnRegistering Packet Events @@ -140,9 +152,9 @@ public override void Unload() NetClient.Socket.Disconnected -= NetClient_Disconnected; NetClient.LoginSocket.Connected -= NetClient_Connected; NetClient.LoginSocket.Disconnected -= Login_NetClient_Disconnected; - NetClient.PacketReceived -= NetClient_PacketReceived; + //NetClient.PacketReceived -= NetClient_PacketReceived; - //NOTE: We force the login socket to disconnect in case it hasn't already been disposed + // MobileUO: NOTE: We force the login socket to disconnect in case it hasn't already been disposed //This is good practice since the Client can be quit while the socket is still active if (NetClient.LoginSocket.IsDisposed == false) { @@ -150,54 +162,61 @@ public override void Unload() } UIManager.GameCursor.IsLoading = false; + // MobileUO: added Clear UIManager.Clear(); base.Unload(); } - public override void Update(double totalMS, double frameMS) + public override void Update(double totalTime, double frameTime) { - base.Update(totalMS, frameMS); + base.Update(totalTime, frameTime); if (_lastLoginStep != CurrentLoginStep) { UIManager.GameCursor.IsLoading = false; // this trick avoid the flickering - var g = _currentGump; + Gump g = _currentGump; UIManager.Add(_currentGump = GetGumpForStep()); g.Dispose(); _lastLoginStep = CurrentLoginStep; } - if (Reconnect && (CurrentLoginStep == LoginSteps.PopUpMessage || CurrentLoginStep == LoginSteps.Main)) + if (Reconnect && (CurrentLoginStep == LoginSteps.PopUpMessage || CurrentLoginStep == LoginSteps.Main) && !NetClient.Socket.IsConnected && !NetClient.LoginSocket.IsConnected) { - long rt = (long) totalMS + Settings.GlobalSettings.ReconnectTime * 1000; - - if (_reconnectTime == null) - _reconnectTime = rt; - - if (_reconnectTime < totalMS) + if (_reconnectTime < totalTime) { if (!string.IsNullOrEmpty(Account)) + { Connect(Account, Crypter.Decrypt(Settings.GlobalSettings.Password)); + } else if (!string.IsNullOrEmpty(Settings.GlobalSettings.Username)) + { Connect(Settings.GlobalSettings.Username, Crypter.Decrypt(Settings.GlobalSettings.Password)); + } - _reconnectTime = rt; + int timeT = Settings.GlobalSettings.ReconnectTime * 1000; + + if (timeT < 1000) + { + timeT = 1000; + } + + _reconnectTime = (long) totalTime + timeT; _reconnectTryCounter++; } } - if ((CurrentLoginStep == LoginSteps.CharacterCreation) && Time.Ticks > _pingTime) + if (CUOEnviroment.NoServerPing == false && CurrentLoginStep == LoginSteps.CharacterCreation && Time.Ticks > _pingTime) { if (NetClient.Socket != null && NetClient.Socket.IsConnected) { - NetClient.Socket.Send(new PPing()); + NetClient.Socket.Statistics.SendPing(); } else if (NetClient.LoginSocket != null && NetClient.LoginSocket.IsConnected) { - NetClient.LoginSocket.Send(new PPing()); + NetClient.LoginSocket.Statistics.SendPing(); } _pingTime = Time.Ticks + 60000; @@ -206,11 +225,15 @@ public override void Update(double totalMS, double frameMS) private Gump GetGumpForStep() { - foreach (var item in World.Items) + foreach (Item item in World.Items.Values) + { World.RemoveItem(item); + } - foreach (Mobile mobile in World.Mobiles) + foreach (Mobile mobile in World.Mobiles.Values) + { World.RemoveMobile(mobile); + } World.Mobiles.Clear(); World.Items.Clear(); @@ -232,16 +255,16 @@ private Gump GetGumpForStep() return GetLoadingScreen(); - case LoginSteps.CharacterSelection: - - return new CharacterSelectionGump(); + case LoginSteps.CharacterSelection: return new CharacterSelectionGump(); case LoginSteps.ServerSelection: _pingTime = Time.Ticks + 60000; // reset ping timer + return new ServerSelectionGump(); case LoginSteps.CharacterCreation: _pingTime = Time.Ticks + 60000; // reset ping timer + return new CharCreationGump(this); } @@ -250,8 +273,8 @@ private Gump GetGumpForStep() private LoadingGump GetLoadingScreen() { - var labelText = "No Text"; - var showButtons = LoginButtons.None; + string labelText = "No Text"; + LoginButtons showButtons = LoginButtons.None; if (!string.IsNullOrEmpty(PopupMessage)) { @@ -264,26 +287,30 @@ private LoadingGump GetLoadingScreen() switch (CurrentLoginStep) { case LoginSteps.Connecting: - labelText = ClilocLoader.Instance.GetString(3000002, "Connecting..."); // "Connecting..." + labelText = ClilocLoader.Instance.GetString(3000002, ResGeneral.Connecting); // "Connecting..." break; case LoginSteps.VerifyingAccount: - labelText = ClilocLoader.Instance.GetString(3000003, "Verifying Account..."); // "Verifying Account..." + labelText = ClilocLoader.Instance.GetString(3000003, ResGeneral.VerifyingAccount); // "Verifying Account..." + showButtons = LoginButtons.Cancel; + break; case LoginSteps.LoginInToServer: - labelText = ClilocLoader.Instance.GetString(3000053, "Logging into Shard"); // logging into shard + labelText = ClilocLoader.Instance.GetString(3000053, ResGeneral.LoggingIntoShard); // logging into shard break; case LoginSteps.EnteringBritania: - labelText = ClilocLoader.Instance.GetString(3000001, "Entering Britannia..."); // Entering Britania... + labelText = ClilocLoader.Instance.GetString(3000001, ResGeneral.EnteringBritannia); // Entering Britania... break; + case LoginSteps.CharacterCreationDone: - labelText = "Creating character..."; + labelText = ResGeneral.CreatingCharacter; + break; } } @@ -296,13 +323,17 @@ private void OnLoadingGumpButtonClick(int buttonId) LoginButtons butt = (LoginButtons) buttonId; if (butt == LoginButtons.OK || butt == LoginButtons.Cancel) + { StepBack(); + } } - public void Connect(string account, string password) + public async void Connect(string account, string password) { if (CurrentLoginStep == LoginSteps.Connecting) + { return; + } Account = account; Password = password; @@ -315,18 +346,54 @@ public void Connect(string account, string password) Settings.GlobalSettings.Save(); } - Log.Trace( $"Start login to: {Settings.GlobalSettings.IP},{Settings.GlobalSettings.Port}"); + Log.Trace($"Start login to: {Settings.GlobalSettings.IP},{Settings.GlobalSettings.Port}"); - EncryptionHelper.Initialize(true, NetClient.ClientAddress, (ENCRYPTION_TYPE) Settings.GlobalSettings.Encryption); + if (!Reconnect) + { + CurrentLoginStep = LoginSteps.Connecting; + } + + if (!await NetClient.LoginSocket.Connect(Settings.GlobalSettings.IP, Settings.GlobalSettings.Port)) + { + PopupMessage = ResGeneral.CheckYourConnectionAndTryAgain; + CurrentLoginStep = LoginSteps.PopUpMessage; + Log.Error("No Internet Access"); + } + } - if (!NetClient.LoginSocket.Connect(Settings.GlobalSettings.IP, Settings.GlobalSettings.Port)) + public int GetServerIndexByName(string name) + { + if (!string.IsNullOrWhiteSpace(name)) { - PopupMessage = "Check your internet connection and try again"; - Log.Error( "No Internet Access"); + for (int i = 0; i < Servers.Length; i++) + { + if (Servers[i].Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)) + { + return i; + } + } } - if(!Reconnect) - CurrentLoginStep = LoginSteps.Connecting; + + return -1; + } + + public int GetServerIndexFromSettings() + { + string name = Settings.GlobalSettings.LastServerName; + int index = GetServerIndexByName(name); + + if (index == -1) + { + index = Settings.GlobalSettings.LastServerNum; + } + + if (index < 0 || index >= Servers.Length) + { + index = 0; + } + + return index; } public void SelectServer(byte index) @@ -344,11 +411,14 @@ public void SelectServer(byte index) } Settings.GlobalSettings.LastServerNum = (ushort) (1 + ServerIndex); + Settings.GlobalSettings.LastServerName = Servers[ServerIndex].Name; Settings.GlobalSettings.Save(); CurrentLoginStep = LoginSteps.LoginInToServer; + World.ServerName = Servers[ServerIndex].Name; - NetClient.LoginSocket.Send(new PSelectServer(index)); + + NetClient.LoginSocket.Send_SelectServer(index); } } @@ -356,17 +426,19 @@ public void SelectCharacter(uint index) { if (CurrentLoginStep == LoginSteps.CharacterSelection) { - Settings.GlobalSettings.LastCharacterName = Characters[index]; - Settings.GlobalSettings.Save(); + LastCharacterManager.Save(Account, World.ServerName, Characters[index]); + CurrentLoginStep = LoginSteps.EnteringBritania; - NetClient.Socket.Send(new PSelectCharacter(index, Characters[index], NetClient.ClientAddress)); + NetClient.Socket.Send_SelectCharacter(index, Characters[index], NetClient.Socket.LocalIP); } } public void StartCharCreation() { if (CurrentLoginStep == LoginSteps.CharacterSelection) + { CurrentLoginStep = LoginSteps.CharacterCreation; + } } public void CreateCharacter(PlayerMobile character, int cityIndex, byte profession) @@ -376,17 +448,29 @@ public void CreateCharacter(PlayerMobile character, int cityIndex, byte professi for (; i < Characters.Length; i++) { if (string.IsNullOrEmpty(Characters[i])) + { break; + } } - Settings.GlobalSettings.LastCharacterName = character.Name; - NetClient.Socket.Send(new PCreateCharacter(character, cityIndex, NetClient.ClientAddress, ServerIndex, (uint) i, profession)); + LastCharacterManager.Save(Account, World.ServerName, character.Name); + + NetClient.Socket.Send_CreateCharacter(character, + cityIndex, + NetClient.Socket.LocalIP, + ServerIndex, + (uint)i, + profession); + CurrentLoginStep = LoginSteps.CharacterCreationDone; } public void DeleteCharacter(uint index) { - if (CurrentLoginStep == LoginSteps.CharacterSelection) NetClient.Socket.Send(new PDeleteCharacter((byte) index, NetClient.ClientAddress)); + if (CurrentLoginStep == LoginSteps.CharacterSelection) + { + NetClient.Socket.Send_DeleteCharacter((byte)index, NetClient.Socket.LocalIP); + } } public void StepBack() @@ -403,7 +487,7 @@ public void StepBack() case LoginSteps.Connecting: case LoginSteps.VerifyingAccount: case LoginSteps.ServerSelection: - Servers = null; + DisposeAllServerEntries(); CurrentLoginStep = LoginSteps.Main; NetClient.LoginSocket.Disconnect(); @@ -412,7 +496,7 @@ public void StepBack() case LoginSteps.LoginInToServer: NetClient.Socket.Disconnect(); Characters = null; - Servers = null; + DisposeAllServerEntries(); Connect(Account, Password); break; @@ -427,7 +511,7 @@ public void StepBack() NetClient.LoginSocket.Disconnect(); NetClient.Socket.Disconnect(); Characters = null; - Servers = null; + DisposeAllServerEntries(); CurrentLoginStep = LoginSteps.Main; break; @@ -437,7 +521,9 @@ public void StepBack() public CityInfo GetCity(int index) { if (index < Cities.Length) + { return Cities[index]; + } return null; } @@ -447,6 +533,10 @@ private void NetClient_Connected(object sender, EventArgs e) Log.Info("Connected!"); CurrentLoginStep = LoginSteps.VerifyingAccount; + uint address = NetClient.LoginSocket.LocalIP; + + EncryptionHelper.Initialize(true, address, (ENCRYPTION_TYPE)Settings.GlobalSettings.Encryption); + if (Client.Version >= ClientVersion.CV_6040) { uint clientVersion = (uint) Client.Version; @@ -456,223 +546,218 @@ private void NetClient_Connected(object sender, EventArgs e) byte build = (byte) (clientVersion >> 8); byte extra = (byte) clientVersion; - PSeed packet = new PSeed(NetClient.ClientAddress, major, minor, build, extra); - NetClient.LoginSocket.Send(packet.ToArray(), packet.Length, true, true); + NetClient.LoginSocket.Send_Seed(address, major, minor, build, extra); } else { - uint address = NetClient.ClientAddress; - - byte[] packet = new byte[4]; - packet[0] = (byte)(address >> 24); - packet[1] = (byte) (address >> 16); - packet[2] = (byte) (address >> 8); - packet[3] = (byte)address; - - NetClient.LoginSocket.Send(packet, packet.Length, true, true); + NetClient.LoginSocket.Send_Seed_Old(address); } - NetClient.LoginSocket.Send(new PFirstLogin(Account, Password)); + NetClient.LoginSocket.Send_FirstLogin(Account, Password); } private void NetClient_Disconnected(object sender, SocketError e) { - Log.Warn( "Disconnected (game socket)!"); + Log.Warn("Disconnected (game socket)!"); if (CurrentLoginStep == LoginSteps.CharacterCreation) + { return; + } Characters = null; - Servers = null; - PopupMessage = $"Connection lost:\n{StringHelper.AddSpaceBeforeCapital(e.ToString())}"; + DisposeAllServerEntries(); + PopupMessage = string.Format(ResGeneral.ConnectionLost0, StringHelper.AddSpaceBeforeCapital(e.ToString())); CurrentLoginStep = LoginSteps.PopUpMessage; } private void Login_NetClient_Disconnected(object sender, SocketError e) { - Log.Warn( "Disconnected (login socket)!"); + Log.Warn("Disconnected (login socket)!"); - if (e > 0) + if (e != 0) { Characters = null; - Servers = null; + DisposeAllServerEntries(); if (Settings.GlobalSettings.Reconnect) { Reconnect = true; - PopupMessage = $"Reconnect, please wait...`{_reconnectTryCounter}`\n`{StringHelper.AddSpaceBeforeCapital(e.ToString())}`"; + + PopupMessage = string.Format(ResGeneral.ReconnectPleaseWait01, _reconnectTryCounter, StringHelper.AddSpaceBeforeCapital(e.ToString())); + UIManager.GetGump()?.SetText(PopupMessage); } else - PopupMessage = $"Connection lost:\n`{StringHelper.AddSpaceBeforeCapital(e.ToString())}`"; + { + PopupMessage = string.Format(ResGeneral.ConnectionLost0, StringHelper.AddSpaceBeforeCapital(e.ToString())); + } CurrentLoginStep = LoginSteps.PopUpMessage; } } - private void NetClient_PacketReceived(object sender, Packet e) + public void ServerListReceived(ref StackDataReader p) { - e.MoveToData(); + byte flags = p.ReadUInt8(); + ushort count = p.ReadUInt16BE(); + DisposeAllServerEntries(); + Servers = new ServerListEntry[count]; - switch (e.ID) + for (ushort i = 0; i < count; i++) { - case 0xA8: // ServerListReceived - ParseServerList(e); - - CurrentLoginStep = LoginSteps.ServerSelection; + Servers[i] = ServerListEntry.Create(ref p); + } - if (Settings.GlobalSettings.AutoLogin || Reconnect) - { - if (Servers.Length != 0) - { - int index = Settings.GlobalSettings.LastServerNum; + CurrentLoginStep = LoginSteps.ServerSelection; - if (index <= 0 || index > Servers.Length) - { - Log.Warn( $"Wrong server index: {index}"); - index = 1; - } + if (CanAutologin) + { + if (Servers.Length != 0) + { + int index = GetServerIndexFromSettings(); - SelectServer((byte) Servers[index - 1].Index); - } - } + SelectServer((byte)Servers[index].Index); + } + } + } - break; + public void UpdateCharacterList(ref StackDataReader p) + { + ParseCharacterList(ref p); - case 0x8C: // ReceiveServerRelay - // On OSI, upon receiving this packet, the client would disconnect and - // log in to the specified server. Since emulated servers use the same - // server for both shard selection and world, we don't need to disconnect. - HandleRelayServerPacket(e); + if (CurrentLoginStep != LoginSteps.PopUpMessage) + { + PopupMessage = null; + } + CurrentLoginStep = LoginSteps.CharacterSelection; + UIManager.GetGump()?.Dispose(); - break; + _currentGump?.Dispose(); - case 0x86: // UpdateCharacterList - ParseCharacterList(e); + UIManager.Add(_currentGump = new CharacterSelectionGump()); + if (!string.IsNullOrWhiteSpace(PopupMessage)) + { + Gump g = null; + g = new LoadingGump(PopupMessage, LoginButtons.OK, (but) => g.Dispose()) { IsModal = true }; + UIManager.Add(g); + PopupMessage = null; + } + } - UIManager.GetGump()?.Dispose(); + public void ReceiveCharacterList(ref StackDataReader p) + { + ParseCharacterList(ref p); + ParseCities(ref p); - _currentGump?.Dispose(); + World.ClientFeatures.SetFlags((CharacterListFlags) p.ReadUInt32BE()); + CurrentLoginStep = LoginSteps.CharacterSelection; - UIManager.Add(_currentGump = new CharacterSelectionGump()); + uint charToSelect = 0; - break; + bool haveAnyCharacter = false; + bool canLogin = CanAutologin; - case 0xA9: // ReceiveCharacterList - ParseCharacterList(e); - ParseCities(e); - ParseFlags(e); - CurrentLoginStep = LoginSteps.CharacterSelection; + if (_autoLogin) + { + _autoLogin = false; + } - uint charToSelect = 0; + string lastCharName = LastCharacterManager.GetLastCharacter(Account, World.ServerName); - bool haveAnyCharacter = false; - bool tryAutologin = Settings.GlobalSettings.AutoLogin || Reconnect; + for (byte i = 0; i < Characters.Length; i++) + { + if (Characters[i].Length > 0) + { + haveAnyCharacter = true; - for (byte i = 0; i < Characters.Length; i++) + if (Characters[i] == lastCharName) { - if (Characters[i].Length > 0) - { - haveAnyCharacter = true; - - if (Characters[i] == Settings.GlobalSettings.LastCharacterName) - { - charToSelect = i; + charToSelect = i; - break; - } - } + break; } + } + } - if (tryAutologin && haveAnyCharacter) - SelectCharacter(charToSelect); - else if (!haveAnyCharacter) - StartCharCreation(); - - break; - - case 0xBD: // ReceiveVersionRequest - NetClient.Socket.Send(new PClientVersion(Settings.GlobalSettings.ClientVersion)); - - break; - - case 0x82: // ReceiveLoginRejection - case 0x85: // character list notification - case 0x53: // Error Code - byte code = e.ReadByte(); - - PopupMessage = ServerErrorMessages.GetError(e.ID, code); - CurrentLoginStep = LoginSteps.PopUpMessage; - - break; - case 0xB9: - uint flags = 0; - - if (Client.Version >= ClientVersion.CV_60142) - flags = e.ReadUInt(); - else - flags = e.ReadUShort(); - World.ClientLockedFeatures.SetFlags((LockedFeatureFlags) flags); - break; - default: - break; + if (canLogin && haveAnyCharacter) + { + SelectCharacter(charToSelect); + } + else if (!haveAnyCharacter) + { + StartCharCreation(); } } - private void HandleRelayServerPacket(Packet p) + public void HandleErrorCode(ref StackDataReader p) { - p.Seek(0); - p.MoveToData(); + byte code = p.ReadUInt8(); + + PopupMessage = ServerErrorMessages.GetError(p[0], code); + CurrentLoginStep = LoginSteps.PopUpMessage; + } + + public void HandleRelayServerPacket(ref StackDataReader p) + { + long ip = p.ReadUInt32LE(); // use LittleEndian here + ushort port = p.ReadUInt16BE(); + uint seed = p.ReadUInt32BE(); - byte[] ip = - { - p.ReadByte(), p.ReadByte(), p.ReadByte(), p.ReadByte() - }; - ushort port = p.ReadUShort(); - uint seed = p.ReadUInt(); NetClient.LoginSocket.Disconnect(); EncryptionHelper.Initialize(false, seed, (ENCRYPTION_TYPE) Settings.GlobalSettings.Encryption); - NetClient.Socket.Connect(new IPAddress(ip), port); - NetClient.Socket.EnableCompression(); - byte[] ss = new byte[4] {(byte) (seed >> 24), (byte) (seed >> 16), (byte) (seed >> 8), (byte) seed}; - NetClient.Socket.Send(ss, 4, true, true); - NetClient.Socket.Send(new PSecondLogin(Account, Password, seed)); + NetClient.Socket.Connect(new IPAddress(ip), port) + .ContinueWith + ( + t => + { + if (!t.IsFaulted) + { + NetClient.Socket.EnableCompression(); + + unsafe + { + Span b = stackalloc byte[4] { (byte)(seed >> 24), (byte)(seed >> 16), (byte)(seed >> 8), (byte)seed }; + StackDataWriter writer = new StackDataWriter(b); + NetClient.Socket.Send(writer.AllocatedBuffer, writer.BytesWritten, true, true); + writer.Dispose(); + } + + NetClient.Socket.Send_SecondLogin(Account, Password, seed); + } + }, + TaskContinuationOptions.ExecuteSynchronously + ); } - private void ParseServerList(Packet reader) - { - byte flags = reader.ReadByte(); - ushort count = reader.ReadUShort(); - Servers = new ServerListEntry[count]; - - for (ushort i = 0; i < count; i++) - Servers[i] = new ServerListEntry(reader); - } - private void ParseCharacterList(Packet p) + private void ParseCharacterList(ref StackDataReader p) { - int count = p.ReadByte(); + int count = p.ReadUInt8(); Characters = new string[count]; for (ushort i = 0; i < count; i++) { Characters[i] = p.ReadASCII(30).TrimEnd('\0'); + p.Skip(30); } } - private void ParseCities(Packet p) + private void ParseCities(ref StackDataReader p) { - var count = p.ReadByte(); + byte count = p.ReadUInt8(); Cities = new CityInfo[count]; bool isNew = Client.Version >= ClientVersion.CV_70130; string[] descriptions = null; if (!isNew) + { descriptions = ReadCityTextFile(count); + } Point[] oldtowns = { @@ -689,25 +774,47 @@ private void ParseCities(Packet p) if (isNew) { - byte cityIndex = p.ReadByte(); + byte cityIndex = p.ReadUInt8(); string cityName = p.ReadASCII(32); string cityBuilding = p.ReadASCII(32); - ushort cityX = (ushort) p.ReadUInt(); - ushort cityY = (ushort) p.ReadUInt(); - sbyte cityZ = (sbyte) p.ReadUInt(); - uint cityMapIndex = p.ReadUInt(); - uint cityDescription = p.ReadUInt(); + ushort cityX = (ushort) p.ReadUInt32BE(); + ushort cityY = (ushort) p.ReadUInt32BE(); + sbyte cityZ = (sbyte) p.ReadUInt32BE(); + uint cityMapIndex = p.ReadUInt32BE(); + uint cityDescription = p.ReadUInt32BE(); p.Skip(4); - cityInfo = new CityInfo(cityIndex, cityName, cityBuilding, ClilocLoader.Instance.GetString((int) cityDescription), cityX, cityY, cityZ, cityMapIndex, isNew); + cityInfo = new CityInfo + ( + cityIndex, + cityName, + cityBuilding, + ClilocLoader.Instance.GetString((int) cityDescription), + cityX, + cityY, + cityZ, + cityMapIndex, + isNew + ); } else { - byte cityIndex = p.ReadByte(); + byte cityIndex = p.ReadUInt8(); string cityName = p.ReadASCII(31); string cityBuilding = p.ReadASCII(31); - cityInfo = new CityInfo(cityIndex, cityName, cityBuilding, descriptions != null ? descriptions[i] : string.Empty, (ushort) oldtowns[i].X, (ushort) oldtowns[i].Y, 0, 0, isNew); + cityInfo = new CityInfo + ( + cityIndex, + cityName, + cityBuilding, + descriptions != null ? descriptions[i] : string.Empty, + (ushort) oldtowns[i % oldtowns.Length].X, + (ushort) oldtowns[i % oldtowns.Length].Y, + 0, + 0, + isNew + ); } Cities[i] = cityInfo; @@ -719,10 +826,13 @@ private string[] ReadCityTextFile(int count) string path = UOFileManager.GetUOFilePath("citytext.enu"); if (!File.Exists(path)) + { return null; + } string[] descr = new string[count]; + // TODO: stackalloc ? byte[] data = new byte[4]; StringBuilder name = new StringBuilder(); @@ -737,7 +847,9 @@ private string[] ReadCityTextFile(int count) int r = stream.Read(data, 0, 4); if (r == -1) + { break; + } string dataText = Encoding.UTF8.GetString(data, 0, 4); @@ -765,7 +877,10 @@ private string[] ReadCityTextFile(int count) { char b; - while ((b = (char) stream.ReadByte()) != '\0') text.Append(b); + while ((b = (char) stream.ReadByte()) != '\0') + { + text.Append(b); + } if (text.Length != 0) { @@ -780,68 +895,219 @@ private string[] ReadCityTextFile(int count) stream.Position = pos; if (end == 0x2E) + { break; + } int r1 = stream.Read(data, 0, 4); stream.Position = pos; if (r1 == -1) + { break; + } string dataText1 = Encoding.UTF8.GetString(data, 0, 4); if (dataText1 == "END\0") + { break; + } } - if (descr.Length <= cityIndex) break; + if (descr.Length <= cityIndex) + { + break; + } descr[cityIndex++] = text.ToString(); } else + { stream.Position -= 3; + } } } return descr; } - private void ParseFlags(Packet p) + private void DisposeAllServerEntries() { - World.ClientFeatures.SetFlags((CharacterListFlags) p.ReadUInt()); + if (Servers != null) + { + for (int i = 0; i < Servers.Length; i++) + { + if (Servers[i] != null) + { + Servers[i].Dispose(); + Servers[i] = null; + } + } + + Servers = null; + } } } internal class ServerListEntry { - public readonly uint Address; - public readonly ushort Index; - public readonly string Name; - public readonly byte PercentFull; - public readonly byte Timezone; + private IPAddress _ipAddress; + private Ping _pinger = new Ping(); + private bool _sending; + private readonly bool[] _last10Results = new bool[10]; + private int _resultIndex; - public ServerListEntry(Packet reader) + private ServerListEntry() { - Index = reader.ReadUShort(); - Name = reader.ReadASCII(32).MakeSafe(); - PercentFull = reader.ReadByte(); - Timezone = reader.ReadByte(); - Address = reader.ReadUInt(); + } + + public static ServerListEntry Create(ref StackDataReader p) + { + ServerListEntry entry = new ServerListEntry() + { + Index = p.ReadUInt16BE(), + Name = p.ReadASCII(32, true), + PercentFull = p.ReadUInt8(), + Timezone = p.ReadUInt8(), + Address = p.ReadUInt32BE() + }; + + // some server sends invalid ip. + try + { + entry._ipAddress = new IPAddress + ( + new byte[] + { + (byte) ((entry.Address >> 24) & 0xFF), + (byte) ((entry.Address >> 16) & 0xFF), + (byte) ((entry.Address >> 8) & 0xFF), + (byte) (entry.Address & 0xFF) + } + ); + } + catch (Exception e) + { + Log.Error(e.ToString()); + } + + entry._pinger.PingCompleted += entry.PingerOnPingCompleted; + + return entry; + } + + + public uint Address; + public ushort Index; + public string Name; + public byte PercentFull; + public byte Timezone; + public int Ping = -1; + public int PacketLoss; + public IPStatus PingStatus; + + private static byte[] _buffData = new byte[32]; + private static PingOptions _pingOptions = new PingOptions(64, true); + + public void DoPing() + { + if (_ipAddress != null && !_sending && _pinger != null) + { + if (_resultIndex >= _last10Results.Length) + { + _resultIndex = 0; + } + + try + { + _pinger.SendAsync + ( + _ipAddress, + 1000, + _buffData, + _pingOptions, + _resultIndex++ + ); + + _sending = true; + } + catch + { + _ipAddress = null; + Dispose(); + } + } + } + + private void PingerOnPingCompleted(object sender, PingCompletedEventArgs e) + { + int index = (int) e.UserState; + + if (e.Reply != null) + { + Ping = (int) e.Reply.RoundtripTime; + PingStatus = e.Reply.Status; + + _last10Results[index] = e.Reply.Status == IPStatus.Success; + } + + //if (index >= _last10Results.Length - 1) + { + PacketLoss = 0; + + for (int i = 0; i < _resultIndex; i++) + { + if (!_last10Results[i]) + { + ++PacketLoss; + } + } + + PacketLoss = (Math.Max(1, PacketLoss) / Math.Max(1, _resultIndex)) * 100; + + //_resultIndex = 0; + } + + _sending = false; + } + + public void Dispose() + { + if (_pinger != null) + { + _pinger.PingCompleted -= PingerOnPingCompleted; + + if (_sending) + { + try + { + _pinger.SendAsyncCancel(); + } + catch { } + + } + + _pinger.Dispose(); + _pinger = null; + } } } internal class CityInfo { - public readonly string Building; - public readonly string City; - public readonly string Description; - public readonly int Index; - public readonly bool IsNewCity; - public readonly uint Map; - public readonly ushort X, Y; - public readonly sbyte Z; - - public CityInfo(int index, string city, string building, string description, ushort x, ushort y, sbyte z, uint map, bool isNew) + public CityInfo + ( + int index, + string city, + string building, + string description, + ushort x, + ushort y, + sbyte z, + uint map, + bool isNew + ) { Index = index; City = city; @@ -853,5 +1119,14 @@ public CityInfo(int index, string city, string building, string description, ush Map = map; IsNewCity = isNew; } + + public readonly string Building; + public readonly string City; + public readonly string Description; + public readonly int Index; + public readonly bool IsNewCity; + public readonly uint Map; + public readonly ushort X, Y; + public readonly sbyte Z; } -} \ No newline at end of file +} diff --git a/Assets/Scripts/ClassicUO/src/Game/SelectedObject.cs b/Assets/Scripts/ClassicUO/src/Game/SelectedObject.cs deleted file mode 120000 index c339de73a..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/SelectedObject.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../external/ClassicUO/src/Game/SelectedObject.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/SelectedObject.cs b/Assets/Scripts/ClassicUO/src/Game/SelectedObject.cs new file mode 100644 index 000000000..621501d90 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/SelectedObject.cs @@ -0,0 +1,95 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System.Runtime.CompilerServices; +using ClassicUO.Game.GameObjects; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; + +namespace ClassicUO.Game +{ + internal static class SelectedObject + { + public static Point TranslatedMousePositionByViewport; + public static BaseGameObject Object; + public static BaseGameObject LastObject; + public static BaseGameObject LastLeftDownObject; + public static Entity HealthbarObject; + public static Item SelectedContainer; + public static Item CorpseObject; + + private static readonly bool[,] _InternalArea = new bool[44, 44]; + + static SelectedObject() + { + for (int y = 21, i = 0; y >= 0; --y, i++) + { + for (int x = 0; x < 22; x++) + { + if (x < i) + { + continue; + } + + _InternalArea[x, y] = _InternalArea[43 - x, 43 - y] = _InternalArea[43 - x, y] = _InternalArea[x, 43 - y] = true; + } + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsPointInLand(int x, int y) + { + x = TranslatedMousePositionByViewport.X - x; + y = TranslatedMousePositionByViewport.Y - y; + + return x >= 0 && x < 44 && y >= 0 && y < 44 && _InternalArea[x, y]; + } + + public static bool IsPointInStretchedLand(ref UltimaBatcher2D.YOffsets yOffsets, int x, int y) + { + //y -= 22; + x += 22; + + int testX = TranslatedMousePositionByViewport.X - x; + int testY = TranslatedMousePositionByViewport.Y; + + int y0 = -yOffsets.Top; + int y1 = 22 - yOffsets.Left; + int y2 = 44 - yOffsets.Bottom; + int y3 = 22 - yOffsets.Right; + + + return testY >= testX * (y1 - y0) / -22 + y + y0 && testY >= testX * (y3 - y0) / 22 + y + y0 && testY <= testX * (y3 - y2) / 22 + y + y2 && testY <= testX * (y1 - y2) / -22 + y + y2; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Button.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Button.cs index a060439c3..2ae2d6e41 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Button.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Button.cs @@ -1,26 +1,36 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System.Collections.Generic; - using ClassicUO.Game.Scenes; using ClassicUO.Input; using ClassicUO.IO.Resources; @@ -29,7 +39,7 @@ namespace ClassicUO.Game.UI.Controls { - enum ButtonAction + internal enum ButtonAction { Default = 0, SwitchPage = 0, @@ -42,16 +52,17 @@ internal class Button : Control private const int PRESSED = 1; private const int OVER = 2; private readonly string _caption; - private readonly RenderedText[] _fontTexture; - private readonly ushort[] _gumpGraphics = new ushort[3]; - private readonly UOTexture32[] _textures = new UOTexture32[3]; private bool _entered; + private readonly RenderedText[] _fontTexture; + private readonly ushort[] _gumpGraphics = new ushort[3]; + private readonly UOTexture[] _textures = new UOTexture[3]; - //NOTE: Added for enlarging small buttons in MobileUO + // MobileUO: NOTE: Added for enlarging small buttons in MobileUO private bool enlarged; private const int smallButtonThreshold = 12; + // MobileUO: added method public void ToggleSize(bool enlarge) { if (enlarged == false && Width > smallButtonThreshold && Height > smallButtonThreshold) @@ -77,7 +88,18 @@ public void ToggleSize(bool enlarge) } } - public Button(int buttonID, ushort normal, ushort pressed, ushort over = 0, string caption = "", byte font = 0, bool isunicode = true, ushort normalHue = ushort.MaxValue, ushort hoverHue = ushort.MaxValue) + public Button + ( + int buttonID, + ushort normal, + ushort pressed, + ushort over = 0, + string caption = "", + byte font = 0, + bool isunicode = true, + ushort normalHue = ushort.MaxValue, + ushort hoverHue = ushort.MaxValue + ) { ButtonID = buttonID; _gumpGraphics[NORMAL] = normal; @@ -85,8 +107,13 @@ public Button(int buttonID, ushort normal, ushort pressed, ushort over = 0, stri _gumpGraphics[OVER] = over; _textures[NORMAL] = GumpsLoader.Instance.GetTexture(normal); _textures[PRESSED] = GumpsLoader.Instance.GetTexture(pressed); - if (over > 0) _textures[OVER] = GumpsLoader.Instance.GetTexture(over); - UOTexture32 t = _textures[NORMAL]; + + if (over > 0) + { + _textures[OVER] = GumpsLoader.Instance.GetTexture(over); + } + + UOTexture t = _textures[NORMAL]; if (t == null) { @@ -106,7 +133,7 @@ public Button(int buttonID, ushort normal, ushort pressed, ushort over = 0, stri _caption = caption; - _fontTexture[0] = RenderedText.Create(caption,FontHue, font, isunicode); + _fontTexture[0] = RenderedText.Create(caption, FontHue, font, isunicode); if (hoverHue != ushort.MaxValue) { @@ -155,8 +182,11 @@ public ushort ButtonGraphicNormal { _textures[NORMAL] = GumpsLoader.Instance.GetTexture(value); _gumpGraphics[NORMAL] = value; + // MobileUO: added if if (_textures[NORMAL] == null) return; + Width = _textures[NORMAL].Width; + Height = _textures[NORMAL].Height; } } @@ -168,8 +198,11 @@ public ushort ButtonGraphicPressed { _textures[PRESSED] = GumpsLoader.Instance.GetTexture(value); _gumpGraphics[PRESSED] = value; + // MobileUO: added if if (_textures[PRESSED] == null) return; + Width = _textures[PRESSED].Width; + Height = _textures[PRESSED].Height; } } @@ -181,12 +214,15 @@ public ushort ButtonGraphicOver { _textures[OVER] = GumpsLoader.Instance.GetTexture(value); _gumpGraphics[OVER] = value; + // MobileUO: added if if (_textures[OVER] == null) return; Width = _textures[OVER].Width; + Height = _textures[OVER].Height; } } + public int Hue { get; set; } public ushort FontHue { get; } public ushort HueHover { get; } @@ -195,22 +231,26 @@ public ushort ButtonGraphicOver public bool ContainsByBounds { get; set; } - public override void Update(double totalMS, double frameMS) + public override void Update(double totalTime, double frameTime) { - base.Update(totalMS, frameMS); + base.Update(totalTime, frameTime); if (IsDisposed) + { return; + } for (int i = 0; i < _textures.Length; i++) { - UOTexture32 t = _textures[i]; + UOTexture t = _textures[i]; if (t != null) + { t.Ticks = Time.Ticks; + } } - //NOTE: Added for enlarging small buttons in MobileUO + // MobileUO: NOTE: Added for enlarging small buttons in MobileUO ToggleSize(UserPreferences.EnlargeSmallButtons.CurrentValue == (int) PreferenceEnums.EnlargeSmallButtons.On); } @@ -226,13 +266,28 @@ protected override void OnMouseExit(int x, int y) public override bool Draw(UltimaBatcher2D batcher, int x, int y) { - UOTexture32 texture = GetTextureByState(); - - ResetHueVector(); - - _hueVector.Z = Alpha; - - batcher.Draw2D(texture, x, y, Width, Height, ref _hueVector); + UOTexture texture = GetTextureByState(); + + ShaderHueTranslator.GetHueVector + ( + ref HueVector, + Hue, + false, + Alpha, + true + ); + + HueVector.Z = Alpha; + + batcher.Draw2D + ( + texture, + x, + y, + Width, + Height, + ref HueVector + ); if (!string.IsNullOrEmpty(_caption)) { @@ -241,10 +296,13 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) if (FontCenter) { int yoffset = IsClicked ? 1 : 0; + textTexture.Draw(batcher, x + ((Width - textTexture.Width) >> 1), y + yoffset + ((Height - textTexture.Height) >> 1)); } else + { textTexture.Draw(batcher, x, y); + } } return base.Draw(batcher, x, y); @@ -253,7 +311,9 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) protected override void OnMouseDown(int x, int y, MouseButtonType button) { if (button == MouseButtonType.Left) + { IsClicked = true; + } } protected override void OnMouseUp(int x, int y, MouseButtonType button) @@ -263,7 +323,9 @@ protected override void OnMouseUp(int x, int y, MouseButtonType button) IsClicked = false; if (!MouseIsOver) + { return; + } if (_entered || Client.Game.Scene is GameScene) { @@ -286,49 +348,43 @@ protected override void OnMouseUp(int x, int y, MouseButtonType button) } } - private UOTexture32 GetTextureByState() + private UOTexture GetTextureByState() { if (_entered || IsClicked) { if (IsClicked && _textures[PRESSED] != null) + { return _textures[PRESSED]; + } if (_textures[OVER] != null) + { return _textures[OVER]; + } } return _textures[NORMAL]; } - - private ushort GetGraphicByState() - { - if (_entered) - { - if (IsClicked && _textures[PRESSED] != null) - return _gumpGraphics[PRESSED]; - - if (_textures[OVER] != null) - return _gumpGraphics[OVER]; - } - - return _gumpGraphics[NORMAL]; - } - - - + public override bool Contains(int x, int y) { if (IsDisposed) + { return false; + } - return ContainsByBounds ? base.Contains(x, y) : _textures[NORMAL].Contains(x, y); + return ContainsByBounds ? base.Contains(x, y) : GumpsLoader.Instance.PixelCheck(_gumpGraphics[NORMAL], x - Offset.X, y - Offset.Y); } public sealed override void Dispose() { if (_fontTexture != null) + { foreach (RenderedText t in _fontTexture) + { t?.Destroy(); + } + } base.Dispose(); } diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Checkbox.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Checkbox.cs index 6617d83ef..37c138534 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Checkbox.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Checkbox.cs @@ -1,27 +1,37 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.Collections.Generic; - using ClassicUO.Input; using ClassicUO.IO.Resources; using ClassicUO.Renderer; @@ -33,11 +43,11 @@ internal class Checkbox : Control { private const int INACTIVE = 0; private const int ACTIVE = 1; - private readonly RenderedText _text; - private readonly UOTexture32[] _textures = new UOTexture32[2]; private bool _isChecked; - - //NOTE: Added for Assistant + private readonly RenderedText _text; + private readonly UOTexture[] _textures = new UOTexture[2]; + + // MobileUO: NOTE: Added for Assistant public ushort Hue { get => _text.Hue; @@ -51,7 +61,16 @@ public ushort Hue } } - public Checkbox(ushort inactive, ushort active, string text = "", byte font = 0, ushort color = 0, bool isunicode = true, int maxWidth = 0) + public Checkbox + ( + ushort inactive, + ushort active, + string text = "", + byte font = 0, + ushort color = 0, + bool isunicode = true, + int maxWidth = 0 + ) { _textures[INACTIVE] = GumpsLoader.Instance.GetTexture(inactive); _textures[ACTIVE] = GumpsLoader.Instance.GetTexture(active); @@ -63,10 +82,18 @@ public Checkbox(ushort inactive, ushort active, string text = "", byte font = 0, return; } - UOTexture32 t = _textures[INACTIVE]; + UOTexture t = _textures[INACTIVE]; Width = t.Width; - _text = RenderedText.Create(text, color, font, isunicode, maxWidth: maxWidth); + _text = RenderedText.Create + ( + text, + color, + font, + isunicode, + maxWidth: maxWidth + ); + Width += _text.Width; Height = Math.Max(t.Width, _text.Height); @@ -96,32 +123,39 @@ public bool IsChecked } } + public override ClickPriority Priority => ClickPriority.High; + public string Text => _text.Text; public event EventHandler ValueChanged; - public override void Update(double totalMS, double frameMS) + public override void Update(double totalTime, double frameTime) { for (int i = 0; i < _textures.Length; i++) { - UOTexture32 t = _textures[i]; + UOTexture t = _textures[i]; if (t != null) - t.Ticks = (long) totalMS; + { + t.Ticks = (long) totalTime; + } } - base.Update(totalMS, frameMS); + base.Update(totalTime, frameTime); } public override bool Draw(UltimaBatcher2D batcher, int x, int y) { if (IsDisposed) + { return false; + } ResetHueVector(); bool ok = base.Draw(batcher, x, y); - batcher.Draw2D(IsChecked ? _textures[ACTIVE] : _textures[INACTIVE], x, y, ref _hueVector); + batcher.Draw2D(IsChecked ? _textures[ACTIVE] : _textures[INACTIVE], x, y, ref HueVector); + _text.Draw(batcher, x + _textures[ACTIVE].Width + 2, y); return ok; @@ -135,7 +169,9 @@ protected virtual void OnCheckedChanged() protected override void OnMouseUp(int x, int y, MouseButtonType button) { if (button == MouseButtonType.Left && MouseIsOver) + { IsChecked = !IsChecked; + } } public override void Dispose() diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ClickPriority.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ClickPriority.cs new file mode 120000 index 000000000..9425a9cda --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ClickPriority.cs @@ -0,0 +1 @@ +../../../../../../../external/ClassicUO/src/Game/UI/Controls/ClickPriority.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Combobox.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Combobox.cs deleted file mode 120000 index a999d94e3..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Combobox.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/Combobox.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Combobox.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Combobox.cs new file mode 100644 index 000000000..187ca986b --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Combobox.cs @@ -0,0 +1,312 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Linq; +using ClassicUO.Configuration; +using ClassicUO.Game.Managers; +using ClassicUO.Game.UI.Gumps; +using ClassicUO.Input; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; + +namespace ClassicUO.Game.UI.Controls +{ + internal class Combobox : Control + { + private readonly byte _font; + // MobileUO: removed readonly - for assistant + private string[] _items; + private readonly Label _label; + private readonly int _maxHeight; + private int _selectedIndex; + + public Combobox + ( + int x, + int y, + int width, + string[] items, + int selected = -1, + int maxHeight = 200, + bool showArrow = true, + string emptyString = "", + byte font = 9 + ) + { + X = x; + Y = y; + Width = width; + Height = 25; + SelectedIndex = selected; + _font = font; + _items = items; + _maxHeight = maxHeight; + + Add + ( + new ResizePic(0x0BB8) + { + Width = width, Height = Height + } + ); + + string initialText = selected > -1 ? items[selected] : emptyString; + + bool isAsianLang = string.Compare(Settings.GlobalSettings.Language, "CHT", StringComparison.InvariantCultureIgnoreCase) == 0 || + string.Compare(Settings.GlobalSettings.Language, "KOR", StringComparison.InvariantCultureIgnoreCase) == 0 || + string.Compare(Settings.GlobalSettings.Language, "JPN", StringComparison.InvariantCultureIgnoreCase) == 0; + + bool unicode = isAsianLang; + byte font1 = (byte)(isAsianLang ? 1 : _font); + + Add + ( + _label = new Label(initialText, unicode, 0x0453, font: font1) + { + X = 2, Y = 5 + } + ); + + if (showArrow) + { + Add(new GumpPic(width - 18, 2, 0x00FC, 0)); + } + } + + + public int SelectedIndex + { + get => _selectedIndex; + set + { + _selectedIndex = value; + + if (_items != null) + { + _label.Text = _items[value]; + + OnOptionSelected?.Invoke(this, value); + } + } + } + + // MobileUO: keep for assistant + internal string GetSelectedItem => _label.Text; + + // MobileUO: for assistant + internal uint GetItemsLength => (uint) _items.Length; + + // MobileUO: for assistant + internal void SetItemsValue(string[] items) + { + _items = items; + } + + // MobileUO: for assistant + public event EventHandler OnBeforeContextMenu; + + public event EventHandler OnOptionSelected; + + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + if (batcher.ClipBegin(x, y, Width, Height)) + { + base.Draw(batcher, x, y); + batcher.ClipEnd(); + } + + return true; + } + + + protected override void OnMouseUp(int x, int y, MouseButtonType button) + { + if (button != MouseButtonType.Left) + { + return; + } + + // MobileUO: for assistant + OnBeforeContextMenu?.Invoke(this, null); + + int comboY = ScreenCoordinateY + Offset.Y; + + if (comboY < 0) + { + comboY = 0; + } + else if (comboY + _maxHeight > Client.Game.Window.ClientBounds.Height) + { + comboY = Client.Game.Window.ClientBounds.Height - _maxHeight; + } + + UIManager.Add + ( + new ComboboxGump + ( + ScreenCoordinateX, + comboY, + Width, + _maxHeight, + _items, + _font, + this + ) + ); + + base.OnMouseUp(x, y, button); + } + + private class ComboboxGump : Gump + { + private const int ELEMENT_HEIGHT = 15; + + + private readonly Combobox _combobox; + + public ComboboxGump + ( + int x, + int y, + int width, + int maxHeight, + string[] items, + byte font, + Combobox combobox + ) : base(0, 0) + { + CanMove = false; + AcceptMouseInput = true; + X = x; + Y = y; + + IsModal = true; + LayerOrder = UILayer.Over; + ModalClickOutsideAreaClosesThisControl = true; + + _combobox = combobox; + + ResizePic background; + Add(background = new ResizePic(0x0BB8)); + background.AcceptMouseInput = false; + + HoveredLabel[] labels = new HoveredLabel[items.Length]; + + bool isAsianLang = string.Compare(Settings.GlobalSettings.Language, "CHT", StringComparison.InvariantCultureIgnoreCase) == 0 || + string.Compare(Settings.GlobalSettings.Language, "KOR", StringComparison.InvariantCultureIgnoreCase) == 0 || + string.Compare(Settings.GlobalSettings.Language, "JPN", StringComparison.InvariantCultureIgnoreCase) == 0; + + bool unicode = isAsianLang; + byte font1 = (byte)(isAsianLang ? 1 : font); + + for (int i = 0; i < items.Length; i++) + { + string item = items[i]; + + if (item == null) + { + item = string.Empty; + } + + HoveredLabel label = new HoveredLabel + ( + item, + unicode, + 0x0453, + 0x0453, + 0x0453, + font: font1 + ) + { + X = 2, + Y = i * ELEMENT_HEIGHT, + DrawBackgroundCurrentIndex = true, + IsVisible = item.Length != 0, + Tag = i + }; + + label.MouseUp += LabelOnMouseUp; + + labels[i] = label; + } + + int totalHeight = Math.Min(maxHeight, labels.Max(o => o.Y + o.Height)); + int maxWidth = Math.Max(width, labels.Max(o => o.X + o.Width)); + + ScrollArea area = new ScrollArea + ( + 0, + 0, + maxWidth + 15, + totalHeight, + true + ); + + foreach (HoveredLabel label in labels) + { + label.Width = maxWidth; + area.Add(label); + } + + Add(area); + + background.Width = maxWidth; + background.Height = totalHeight; + } + + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + if (batcher.ClipBegin(x, y, Width, Height)) + { + base.Draw(batcher, x, y); + + batcher.ClipEnd(); + } + + return true; + } + + private void LabelOnMouseUp(object sender, MouseEventArgs e) + { + if (e.Button == MouseButtonType.Left) + { + _combobox.SelectedIndex = (int) ((Label) sender).Tag; + + Dispose(); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Control.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Control.cs index 84ddef515..9daaeb4fe 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Control.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Control.cs @@ -1,22 +1,33 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; @@ -24,53 +35,34 @@ using System.Linq; using ClassicUO.Game.Managers; using ClassicUO.Input; -using ClassicUO.Interfaces; using ClassicUO.Renderer; using ClassicUO.Utility; - using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Input; - using SDL2; - -using IUpdateable = ClassicUO.Interfaces.IUpdateable; using Keyboard = ClassicUO.Input.Keyboard; using Mouse = ClassicUO.Input.Mouse; namespace ClassicUO.Game.UI.Controls { - enum ClickPriority - { - High, - Default, - Low - } - internal abstract class Control { internal static int _StepsDone = 1; internal static int _StepChanger = 1; + + protected static Vector3 HueVector = Vector3.Zero; private bool _acceptKeyboardInput, _acceptMouseInput, _mouseIsDown; private int _activePage; private bool _attempToDrag; private Rectangle _bounds; - private GumpControlInfo _controlInfo; private bool _handlesKeyboardFocus; + private Point _offset; private Control _parent; - protected static Vector3 _hueVector = Vector3.Zero; - + // MobileUO: added variables protected const int MOBILE_CLOSE_BUTTON_ID = -9999; public Control ControlToForwardMouseEventsTo; - - protected static void ResetHueVector() - { - _hueVector.X = 0; - _hueVector.Y = 0; - _hueVector.Z = 0; - } - protected Control(Control parent = null) { Parent = parent; @@ -105,7 +97,9 @@ public Point Location } public ref Rectangle Bounds => ref _bounds; - + + public Point Offset => _offset; + public bool IsDisposed { get; private set; } public bool IsVisible { get; set; } = true; @@ -148,28 +142,14 @@ public virtual bool AcceptMouseInput set => _acceptMouseInput = value; } - public int Width - { - get => _bounds.Width; - set - { - if (_bounds.Width != value) _bounds.Width = value; - } - } - - public int Height - { - get => _bounds.Height; - set - { - if (_bounds.Height != value) _bounds.Height = value; - } - } - public ref int X => ref _bounds.X; public ref int Y => ref _bounds.Y; + public ref int Width => ref _bounds.Width; + + public ref int Height => ref _bounds.Height; + public int ParentX => Parent != null ? Parent.X + Parent.ParentX : 0; public int ParentY => Parent != null ? Parent.Y + Parent.ParentY : 0; @@ -186,12 +166,15 @@ public Control Parent internal set { if (value == null) + { _parent?.Children.Remove(this); + } else { _parent?.Children.Remove(this); value.Children.Add(this); } + _parent = value; } } @@ -201,36 +184,51 @@ public Control RootParent get { if (Parent == null) + { return null; + } Control p = Parent; while (p.Parent != null) + { p = p.Parent; + } return p; } } - public GumpControlInfo ControlInfo => _controlInfo ?? (_controlInfo = new GumpControlInfo(this)); + public UILayer LayerOrder { get; set; } = UILayer.Default; + public bool IsModal { get; set; } + public bool ModalClickOutsideAreaClosesThisControl { get; set; } + public virtual bool HandlesKeyboardFocus { get { if (!IsEnabled || IsDisposed || !IsVisible) + { return false; + } if (_handlesKeyboardFocus) + { return true; + } if (Children == null) + { return false; + } foreach (Control c in Children) { if (c.HandlesKeyboardFocus) + { return true; + } } return false; @@ -255,10 +253,36 @@ public int ActivePage public int TooltipMaxLength { get; private set; } + public void UpdateOffset(int x, int y) + { + if (_offset.X != x || _offset.Y != y) + { + _offset.X = x; + _offset.Y = y; + + foreach (Control c in Children) + { + c.UpdateOffset(x, y); + } + } + } + + protected static void ResetHueVector() + { + HueVector.X = 0; + HueVector.Y = 0; + HueVector.Z = 0; + } + + + + public virtual bool Draw(UltimaBatcher2D batcher, int x, int y) { - if (IsDisposed) + if (IsDisposed) + { return false; + } foreach (Control c in Children) { @@ -276,9 +300,12 @@ public virtual bool Draw(UltimaBatcher2D batcher, int x, int y) return true; } - public virtual void Update(double totalMS, double frameMS) + public virtual void Update(double totalTime, double frameTime) { - if (IsDisposed) return; + if (IsDisposed) + { + return; + } if (Children.Count != 0) { @@ -297,17 +324,21 @@ public virtual void Update(double totalMS, double frameMS) continue; } - c.Update(totalMS, frameMS); + c.Update(totalTime, frameTime); if (WantUpdateSize) { if ((c.Page == 0 || c.Page == ActivePage) && c.IsVisible) { if (w < c.Bounds.Right) + { w = c.Bounds.Right; + } if (h < c.Bounds.Bottom) + { h = c.Bounds.Bottom; + } } } } @@ -315,10 +346,15 @@ public virtual void Update(double totalMS, double frameMS) if (WantUpdateSize && IsVisible) { if (w != Width) + { Width = w; + } if (h != Height) + { Height = h; + } + WantUpdateSize = false; } } @@ -328,7 +364,9 @@ public virtual void OnPageChanged() { //Update size as pages may vary in size. if (ServerSerial != 0) + { WantUpdateSize = true; + } } private void DrawDebug(UltimaBatcher2D batcher, int x, int y) @@ -336,7 +374,16 @@ private void DrawDebug(UltimaBatcher2D batcher, int x, int y) if (IsVisible && CUOEnviroment.Debug) { ResetHueVector(); - batcher.DrawRectangle(Texture2DCache.GetTexture(Color.Green), x, y, Width, Height, ref _hueVector); + + batcher.DrawRectangle + ( + SolidColorTextureCache.GetTexture(Color.Green), + x, + y, + Width, + Height, + ref HueVector + ); } } @@ -388,27 +435,31 @@ public void SetKeyboardFocus() public void HitTest(int x, int y, ref Control res) { - if (!IsVisible || !IsEnabled) + if (!IsVisible || !IsEnabled || IsDisposed) + { return; + } int parentX = ParentX; int parentY = ParentY; - if (Bounds.Contains(x - parentX, y - parentY)) + if (Bounds.Contains(x - parentX - _offset.X, y - parentY - _offset.Y)) { if (Contains(x - X - parentX, y - Y - parentY)) { if (AcceptMouseInput) { - if (res == null || res.Priority >= this.Priority) + if (res == null || res.Priority >= Priority) { res = this; OnHitTestSuccess(x, y, ref res); } } - foreach (Control c in Children) + for (int i = 0; i < Children.Count; ++i) { + Control c = Children[i]; + if (c.Page == 0 || c.Page == ActivePage) { c.HitTest(x, y, ref res); @@ -426,21 +477,27 @@ public void HitTest(Point position, ref Control res) public virtual void OnHitTestSuccess(int x, int y, ref Control res) { } - + public Control GetFirstControlAcceptKeyboardInput() { if (_acceptKeyboardInput) + { return this; + } if (Children == null || Children.Count == 0) + { return null; + } foreach (Control c in Children) { Control a = c.GetFirstControlAcceptKeyboardInput(); if (a != null) + { return a; + } } return null; @@ -462,14 +519,16 @@ public void Insert(int index, Control c, int page = 0) c._parent = this; Children.Insert(index, c); - + OnChildAdded(); } public virtual void Remove(Control c) { if (c == null) + { return; + } c.Parent = null; Children.Remove(c); @@ -480,7 +539,7 @@ public virtual void Clear() { foreach (Control c in Children) { - //NOTE: Prevent mobile close buttons from being disposed + // MobileUO: NOTE: Prevent mobile close buttons from being disposed if (IsMobileCloseButton()) { continue; @@ -506,6 +565,7 @@ public void InvokeMouseDown(Point position, MouseButtonType button) int y = position.Y - Y - ParentY; OnMouseDown(x, y, button); MouseDown.Raise(new MouseEventArgs(x, y, button, ButtonState.Pressed), this); + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseDown(position, button); } @@ -515,14 +575,18 @@ public void InvokeMouseUp(Point position, MouseButtonType button) int y = position.Y - Y - ParentY; OnMouseUp(x, y, button); MouseUp.Raise(new MouseEventArgs(x, y, button), this); + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseUp(position, button); } public void InvokeMouseCloseGumpWithRClick() { if (CanCloseWithRightClick) + { CloseWithRightClick(); - + } + + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseCloseGumpWithRClick(); } @@ -532,6 +596,7 @@ public void InvokeMouseOver(Point position) int y = position.Y - Y - ParentY; OnMouseOver(x, y); MouseOver.Raise(new MouseEventArgs(x, y), this); + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseOver(position); } @@ -541,6 +606,7 @@ public void InvokeMouseEnter(Point position) int y = position.Y - Y - ParentY; OnMouseEnter(x, y); MouseEnter.Raise(new MouseEventArgs(x, y), this); + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseEnter(position); } @@ -550,6 +616,7 @@ public void InvokeMouseExit(Point position) int y = position.Y - Y - ParentY; OnMouseExit(x, y); MouseExit.Raise(new MouseEventArgs(x, y), this); + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseExit(position); } @@ -559,10 +626,10 @@ public bool InvokeMouseDoubleClick(Point position, MouseButtonType button) int y = position.Y - Y - ParentY; bool result = OnMouseDoubleClick(x, y, button); - var arg = new MouseDoubleClickEventArgs(x, y, button); + MouseDoubleClickEventArgs arg = new MouseDoubleClickEventArgs(x, y, button); MouseDoubleClick.Raise(arg, this); result |= arg.Result; - + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseDoubleClick(position, button); return result; @@ -591,6 +658,7 @@ public void InvokeMouseWheel(MouseEventType delta) { OnMouseWheel(delta); MouseWheel.Raise(new MouseWheelEventArgs(delta), this); + // MobileUO: added invoke ControlToForwardMouseEventsTo?.InvokeMouseWheel(delta); } @@ -650,18 +718,19 @@ protected virtual void OnMouseOver(int x, int y) { if (_mouseIsDown && !_attempToDrag) { - Point offset = Mouse.LButtonPressed ? Mouse.LDroppedOffset : Mouse.MButtonPressed ? Mouse.MDroppedOffset : Point.Zero; + Point offset = Mouse.LButtonPressed ? Mouse.LDragOffset : Mouse.MButtonPressed ? Mouse.MDragOffset : Point.Zero; - if (Math.Abs(offset.X) > Constants.MIN_GUMP_DRAG_DISTANCE - || Math.Abs(offset.Y) > Constants.MIN_GUMP_DRAG_DISTANCE) + if (Math.Abs(offset.X) > Constants.MIN_GUMP_DRAG_DISTANCE || Math.Abs(offset.Y) > Constants.MIN_GUMP_DRAG_DISTANCE) { InvokeDragBegin(new Point(x, y)); _attempToDrag = true; } } - else + else + { Parent?.OnMouseOver(X + x, Y + y); + } } protected virtual void OnMouseEnter(int x, int y) @@ -709,7 +778,7 @@ public virtual bool Contains(int x, int y) protected virtual void OnMove(int x, int y) { } - + internal virtual void OnFocusEnter() { if (!IsFocused) @@ -741,22 +810,30 @@ protected virtual void OnChildRemoved() protected virtual void CloseWithRightClick() { if (!CanCloseWithRightClick) + { return; + } Control parent = Parent; while (parent != null) { if (!parent.CanCloseWithRightClick) + { return; + } parent = parent.Parent; } if (Parent == null) + { Dispose(); + } else + { Parent.CloseWithRightClick(); + } } public void KeyboardTabToNextFocus(Control c) @@ -786,7 +863,7 @@ public void KeyboardTabToNextFocus(Control c) public virtual void OnButtonClick(int buttonID) { - //NOTE: Mobile close button functionality + // MobileUO: NOTE: Mobile close button functionality if (buttonID == MOBILE_CLOSE_BUTTON_ID) { Parent?.CloseWithRightClick(); @@ -807,7 +884,9 @@ public virtual void ChangePage(int pageIndex) public virtual void Dispose() { if (IsDisposed) + { return; + } foreach (Control c in Children) { @@ -819,6 +898,7 @@ public virtual void Dispose() IsDisposed = true; } + // MobileUO: added function public bool IsMobileCloseButton() { return this is Button button && button.ButtonID == MOBILE_CLOSE_BUTTON_ID; diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/GumpPic.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/GumpPic.cs deleted file mode 120000 index 783a987c9..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/GumpPic.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/GumpPic.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/GumpPic.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/GumpPic.cs new file mode 100644 index 000000000..21036dfa7 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/GumpPic.cs @@ -0,0 +1,192 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System.Collections.Generic; +using ClassicUO.Input; +using ClassicUO.IO.Resources; +using ClassicUO.Network; +using ClassicUO.Renderer; +using ClassicUO.Utility; + +namespace ClassicUO.Game.UI.Controls +{ + internal abstract class GumpPicBase : Control + { + private ushort _graphic; + + protected GumpPicBase() + { + CanMove = true; + AcceptMouseInput = true; + } + + public ushort Graphic + { + get => _graphic; + set + { + //if (_graphic != value) + { + _graphic = value; + + UOTexture texture = GumpsLoader.Instance.GetTexture(_graphic); + + if (texture == null) + { + Dispose(); + + return; + } + + Width = texture.Width; + Height = texture.Height; + } + } + } + + public ushort Hue { get; set; } + + + public override bool Contains(int x, int y) + { + UOTexture texture = GumpsLoader.Instance.GetTexture(Graphic); + + if (texture == null) + { + return false; + } + + if (GumpsLoader.Instance.PixelCheck(Graphic, x - Offset.X, y - Offset.Y)) + { + return true; + } + + for (int i = 0; i < Children.Count; i++) + { + Control c = Children[i]; + + // might be wrong x, y. They should be calculated by position + if (c.Contains(x, y)) + { + return true; + } + } + + return false; + } + } + + internal class GumpPic : GumpPicBase + { + public GumpPic(int x, int y, ushort graphic, ushort hue) + { + X = x; + Y = y; + Graphic = graphic; + Hue = hue; + IsFromServer = true; + } + + public GumpPic(List parts) : this(int.Parse(parts[1]), int.Parse(parts[2]), UInt16Converter.Parse(parts[3]), (ushort) (parts.Count > 4 ? TransformHue((ushort) (UInt16Converter.Parse(parts[4].Substring(parts[4].IndexOf('=') + 1)) + 1)) : 0)) + { + } + + public bool IsPartialHue { get; set; } + public bool ContainsByBounds { get; set; } + public bool IsVirtue { get; set; } + + protected override bool OnMouseDoubleClick(int x, int y, MouseButtonType button) + { + if (IsVirtue && button == MouseButtonType.Left) + { + NetClient.Socket.Send_VirtueGumpResponse(World.Player, Graphic); + + return true; + } + + return base.OnMouseDoubleClick(x, y, button); + } + + public override bool Contains(int x, int y) + { + return ContainsByBounds || base.Contains(x, y); + } + + private static ushort TransformHue(ushort hue) + { + if (hue <= 2) + { + hue = 0; + } + + //if (hue < 2) + // hue = 1; + return hue; + } + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + if (IsDisposed) + { + return false; + } + + ResetHueVector(); + + ShaderHueTranslator.GetHueVector + ( + ref HueVector, + Hue, + IsPartialHue, + Alpha, + true + ); + + UOTexture texture = GumpsLoader.Instance.GetTexture(Graphic); + + if (texture != null) + { + batcher.Draw2D + ( + texture, + x, + y, + Width, + Height, + ref HueVector + ); + } + + return base.Draw(batcher, x, y); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/HtmlControl.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/HtmlControl.cs deleted file mode 100644 index 9337a5161..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/HtmlControl.cs +++ /dev/null @@ -1,294 +0,0 @@ -#region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -#endregion - -using System; -using System.Collections.Generic; -using System.Diagnostics; - -using ClassicUO.Input; -using ClassicUO.IO.Resources; -using ClassicUO.Renderer; -using ClassicUO.Utility; -using ClassicUO.Utility.Logging; - -using Microsoft.Xna.Framework; - -namespace ClassicUO.Game.UI.Controls -{ - internal class HtmlControl : Control - { - private RenderedText _gameText; - private ScrollBarBase _scrollBar; - - public HtmlControl(List parts, string[] lines) : this() - { - X = int.Parse(parts[1]); - Y = int.Parse(parts[2]); - Width = int.Parse(parts[3]); - Height = int.Parse(parts[4]); - int textIndex = int.Parse(parts[5]); - HasBackground = parts[6] == "1"; - HasScrollbar = parts[7] != "0"; - UseFlagScrollbar = HasScrollbar && parts[7] == "2"; - _gameText.IsHTML = true; - _gameText.MaxWidth = Width - (HasScrollbar ? 16 : 0) - (HasBackground ? 8 : 0); - IsFromServer = true; - - if (textIndex >= 0 && textIndex < lines.Length) - InternalBuild(lines[textIndex], 0); - } - - public HtmlControl(int x, int y, int w, int h, bool hasbackground, bool hasscrollbar, bool useflagscrollbar = false, string text = "", int hue = 0, bool ishtml = false, byte font = 1, bool isunicode = true, FontStyle style = FontStyle.None, TEXT_ALIGN_TYPE align = TEXT_ALIGN_TYPE.TS_LEFT) : this() - { - X = x; - Y = y; - Width = w; - Height = h; - HasBackground = hasbackground; - HasScrollbar = hasscrollbar; - UseFlagScrollbar = useflagscrollbar; //hasscrollbar != 0 && hasscrollbar == 2; - - if (!string.IsNullOrEmpty(text)) - { - _gameText.IsHTML = ishtml; - _gameText.FontStyle = style; - _gameText.Align = align; - _gameText.Font = font; - _gameText.IsUnicode = isunicode; - _gameText.MaxWidth = w - (HasScrollbar ? 16 : 0) - (HasBackground ? 8 : 0); - } - - InternalBuild(text, hue); - } - - public HtmlControl() - { - _gameText = RenderedText.Create(string.Empty, isunicode: true, font: 1); - - CanMove = true; - } - - public bool HasScrollbar { get; } - - public bool HasBackground { get; } - - public bool UseFlagScrollbar { get; } - - public int ScrollX { get; set; } - - public int ScrollY { get; set; } - - public string Text - { - get => _gameText.Text; - set => _gameText.Text = value; - } - - private void InternalBuild(string text, int hue) - { - if (!string.IsNullOrEmpty(text)) - { - if (_gameText.IsHTML) - { - uint htmlColor = 0xFFFFFFFF; - ushort color = 0; - - if (hue > 0) - { - if (hue == 0x00FFFFFF || hue == 0xFFFF || hue == 0xFF) - htmlColor = 0xFFFFFFFE; - else - htmlColor = (HuesHelper.Color16To32((ushort) hue) << 8) | 0xFF; - } - else if (!HasBackground) - { - color = 0xFFFF; - - if (!HasScrollbar) - htmlColor = 0x010101FF; - } - else - { - _gameText.MaxWidth -= 9; - htmlColor = 0x010101FF; - } - - _gameText.HTMLColor = htmlColor; - _gameText.Hue = color; - } - else - { - _gameText.Hue = (ushort) hue; - } - - _gameText.HasBackgroundColor = !HasBackground; - _gameText.Text = text; - } - - if (HasBackground) - { - Add(new ResizePic(0x2486) - { - Width = Width - (HasScrollbar ? 16 : 0), Height = Height, AcceptMouseInput = false - }); - } - - if (HasScrollbar) - { - if (UseFlagScrollbar) - { - _scrollBar = new ScrollFlag - { - Location = new Point(Width - 14, 0) - }; - } - else - _scrollBar = new ScrollBar(Width - 14, 0, Height); - - _scrollBar.Height = Height; - _scrollBar.MinValue = 0; - _scrollBar.MaxValue = /* _gameText.Height*/ /* Children.Sum(s => s.Height) - Height +*/ _gameText.Height - Height + (HasBackground ? 8 : 0); - ScrollY = _scrollBar.Value; - - Add(_scrollBar); - } - - //if (Width != _gameText.Width) - // Width = _gameText.Width; - } - - protected override void OnMouseWheel(MouseEventType delta) - { - if (!HasScrollbar) - return; - - switch (delta) - { - case MouseEventType.WheelScrollUp: - _scrollBar.Value -= _scrollBar.ScrollStep; - - break; - - case MouseEventType.WheelScrollDown: - _scrollBar.Value += _scrollBar.ScrollStep; - - break; - } - } - - public override void Update(double totalMS, double frameMS) - { - if (HasScrollbar) - { - if (WantUpdateSize) - { - _scrollBar.Height = Height; - _scrollBar.MinValue = 0; - _scrollBar.MaxValue = /* _gameText.Height*/ /*Children.Sum(s => s.Height) - Height */_gameText.Height - Height + (HasBackground ? 8 : 0); - //_scrollBar.IsVisible = _scrollBar.MaxValue > _scrollBar.MinValue; - WantUpdateSize = false; - } - - ScrollY = _scrollBar.Value; - } - - base.Update(totalMS, frameMS); - } - - public override bool Draw(UltimaBatcher2D batcher, int x, int y) - { - if (IsDisposed) - return false; - - ResetHueVector(); - - Rectangle scissor = ScissorStack.CalculateScissors(Matrix.Identity, x, y, Width, Height); - - if (ScissorStack.PushScissors(batcher.GraphicsDevice, scissor)) - { - batcher.EnableScissorTest(true); - base.Draw(batcher, x, y); - - _gameText.Draw(batcher, - Width + ScrollX, Height + ScrollY, - x + (HasBackground ? 4 : 0), - y + (HasBackground ? 4 : 0), - Width - (HasBackground ? 8 : 0), - Height - (HasBackground ? 8 : 0), - ScrollX, - ScrollY); - - batcher.EnableScissorTest(false); - ScissorStack.PopScissors(batcher.GraphicsDevice); - } - - return true; - } - - protected override void OnMouseUp(int x, int y, MouseButtonType button) - { - if (button == MouseButtonType.Left) - { - if (_gameText != null) - { - for (int i = 0; i < _gameText.Links.Count; i++) - { - ref var link = ref _gameText.Links[i]; - - bool inbounds = link.Bounds.Contains(x, (_scrollBar == null ? 0 : _scrollBar.Value) + y); - - if (inbounds && FontsLoader.Instance.GetWebLink(link.LinkID, out WebLink result)) - { - Log.Info("LINK CLICKED: " + result.Link); - - if (UnityEngine.Application.isMobilePlatform) - { - UnityEngine.Application.OpenURL(result.Link); - } - else - { - try - { - Process.Start(result.Link); - } - catch (Exception ex) - { - Log.Error(ex.ToString()); - } - } - - break; - } - } - } - } - - base.OnMouseUp(x, y, button); - } - - public override void Dispose() - { - _gameText?.Destroy(); - _gameText = null; - base.Dispose(); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/HtmlControl.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/HtmlControl.cs new file mode 120000 index 000000000..0c3f6839c --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/HtmlControl.cs @@ -0,0 +1 @@ +../../../../../../../external/ClassicUO/src/Game/UI/Controls/HtmlControl.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ItemGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ItemGump.cs index 85e88f664..1e396c44f 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ItemGump.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ItemGump.cs @@ -1,44 +1,65 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; - using ClassicUO.Configuration; +using ClassicUO.Game.Data; using ClassicUO.Game.GameObjects; using ClassicUO.Game.Managers; -using ClassicUO.Game.Scenes; using ClassicUO.Game.UI.Gumps; using ClassicUO.Input; +using ClassicUO.IO.Resources; using ClassicUO.Renderer; -using ClassicUO.Game.Data; - using Microsoft.Xna.Framework; -using ClassicUO.IO.Resources; namespace ClassicUO.Game.UI.Controls { - internal class ItemGump : StaticPic + internal class ItemGump : Control { - public ItemGump(uint serial, ushort graphic, ushort hue, int x, int y) : base(graphic, hue) + private ushort _graphic; + private readonly bool _is_gump; + + public ItemGump + ( + uint serial, + ushort graphic, + ushort hue, + int x, + int y, + bool is_gump = false + ) { + _is_gump = is_gump; + AcceptMouseInput = true; X = (short) x; Y = (short) y; @@ -48,30 +69,59 @@ public ItemGump(uint serial, ushort graphic, ushort hue, int x, int y) : base(gr WantUpdateSize = false; CanMove = false; + + Graphic = graphic; + Hue = hue; + SetTooltip(serial); } + public ushort Graphic + { + get => _graphic; + set + { + _graphic = value; + + UOTexture texture = _is_gump ? GumpsLoader.Instance.GetTexture(value) : ArtLoader.Instance.GetTexture(value); + + if (texture == null) + { + Dispose(); + + return; + } + + Width = texture.Width; + Height = texture.Height; + + IsPartialHue = !_is_gump && TileDataLoader.Instance.StaticData[value].IsPartialHue; + } + } + + public ushort Hue { get; set; } + public bool IsPartialHue { get; set; } public bool HighlightOnMouseOver { get; set; } public bool CanPickUp { get; set; } + // MobileUO: added variable public static bool PixelCheck = false; - public override void Update(double totalMS, double frameMS) + public override void Update(double totalTime, double frameTime) { if (IsDisposed) + { return; + } - base.Update(totalMS, frameMS); + base.Update(totalTime, frameTime); if (World.InGame) { - if (CanPickUp && !ItemHold.Enabled && Mouse.LButtonPressed && - UIManager.LastControlMouseDown(MouseButtonType.Left) == this && - ((Mouse.LastLeftButtonClickTime != 0xFFFF_FFFF && Mouse.LastLeftButtonClickTime != 0 && Mouse.LastLeftButtonClickTime + Mouse.MOUSE_DELAY_DOUBLE_CLICK < Time.Ticks) || - CanPickup())) + if (CanPickUp && !ItemHold.Enabled && Mouse.LButtonPressed && UIManager.LastControlMouseDown(MouseButtonType.Left) == this && (Mouse.LastLeftButtonClickTime != 0xFFFF_FFFF && Mouse.LastLeftButtonClickTime != 0 && Mouse.LastLeftButtonClickTime + Mouse.MOUSE_DELAY_DOUBLE_CLICK < Time.Ticks || CanPickup())) { - AttempPickUp(); + AttemptPickUp(); } else if (MouseIsOver) { @@ -83,24 +133,52 @@ public override void Update(double totalMS, double frameMS) public override bool Draw(UltimaBatcher2D batcher, int x, int y) { if (IsDisposed) + { return false; + } base.Draw(batcher, x, y); ResetHueVector(); - ShaderHuesTraslator.GetHueVector(ref _hueVector, HighlightOnMouseOver && MouseIsOver ? 0x0035 : Hue, IsPartialHue, 0, false); - - var texture = ArtLoader.Instance.GetTexture(Graphic); + + bool partialHue = IsPartialHue; + ushort hue = Hue; + + if (HighlightOnMouseOver && MouseIsOver) + { + hue = 0x0035; + partialHue = false; + } + + ShaderHueTranslator.GetHueVector(ref HueVector, hue, partialHue, 0); + + UOTexture texture = _is_gump ? GumpsLoader.Instance.GetTexture(Graphic) : ArtLoader.Instance.GetTexture(Graphic); if (texture != null) { - batcher.Draw2D(texture, x, y, Width, Height, ref _hueVector); + batcher.Draw2D + ( + texture, + x, + y, + Width, + Height, + ref HueVector + ); Item item = World.Items.Get(LocalSerial); if (item != null && !item.IsMulti && !item.IsCoin && item.Amount > 1 && item.ItemData.IsStackable) { - batcher.Draw2D(texture, x + 5, y + 5, Width, Height, ref _hueVector); + batcher.Draw2D + ( + texture, + x + 5, + y + 5, + Width, + Height, + ref HueVector + ); } } @@ -109,34 +187,57 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) public override bool Contains(int x, int y) { - var texture = ArtLoader.Instance.GetTexture(Graphic); + UOTexture texture = _is_gump ? GumpsLoader.Instance.GetTexture(Graphic) : ArtLoader.Instance.GetTexture(Graphic); if (texture == null) { return false; } - if (ProfileManager.Current != null && ProfileManager.Current.ScaleItemsInsideContainers) + x -= Offset.X; + y -= Offset.Y; + + if (ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.ScaleItemsInsideContainers) { float scale = UIManager.ContainerScale; - x = (int)(x / scale); - y = (int)(y / scale); + x = (int) (x / scale); + y = (int) (y / scale); } - if (texture.Contains(x, y, PixelCheck)) + if (_is_gump) { - return true; - } + if (GumpsLoader.Instance.PixelCheck(Graphic, x, y)) + { + return true; + } - Item item = World.Items.Get(LocalSerial); + Item item = World.Items.Get(LocalSerial); - if (item != null && !item.IsCoin && item.Amount > 1 && item.ItemData.IsStackable) + if (item != null && !item.IsCoin && item.Amount > 1 && item.ItemData.IsStackable) + { + if (GumpsLoader.Instance.PixelCheck(Graphic, x - 5, y - 5)) + { + return true; + } + } + } + else { - if (texture.Contains(x - 5, y - 5, PixelCheck)) + if (ArtLoader.Instance.PixelCheck(Graphic, x, y)) { return true; } + + Item item = World.Items.Get(LocalSerial); + + if (item != null && !item.IsCoin && item.Amount > 1 && item.ItemData.IsStackable) + { + if (ArtLoader.Instance.PixelCheck(Graphic, x - 5, y - 5)) + { + return true; + } + } } return false; @@ -155,18 +256,23 @@ protected override void OnMouseOver(int x, int y) private bool CanPickup() { - Point offset = Mouse.LDroppedOffset; - if (Math.Abs(offset.X) < Constants.MIN_PICKUP_DRAG_DISTANCE_PIXELS && - Math.Abs(offset.Y) < Constants.MIN_PICKUP_DRAG_DISTANCE_PIXELS) + Point offset = Mouse.LDragOffset; + + if (Math.Abs(offset.X) < Constants.MIN_PICKUP_DRAG_DISTANCE_PIXELS && Math.Abs(offset.Y) < Constants.MIN_PICKUP_DRAG_DISTANCE_PIXELS) + { return false; + } + + SplitMenuGump split = UIManager.GetGump(LocalSerial); - var split = UIManager.GetGump(LocalSerial); if (split == null) + { return true; + } - split.X = Mouse.Position.X - 80; - split.Y = Mouse.Position.Y - 40; - UIManager.AttemptDragControl(split, Mouse.Position, true); + split.X = Mouse.LClickPosition.X - 80; + split.Y = Mouse.LClickPosition.Y - 40; + UIManager.AttemptDragControl(split, true); split.BringOnTop(); return false; @@ -176,18 +282,14 @@ private bool CanPickup() protected override bool OnMouseDoubleClick(int x, int y, MouseButtonType button) { if (button != MouseButtonType.Left || TargetManager.IsTargeting) + { return false; + } Item item = World.Items.Get(LocalSerial); Item container; - if ( !Input.Keyboard.Ctrl && - ProfileManager.Current.DoubleClickToLootInsideContainers && - item != null && !item.IsDestroyed && - !item.ItemData.IsContainer && item.IsEmpty && - (container = World.Items.Get(item.RootContainer)) != null && - container != World.Player.FindItemByLayer(Layer.Backpack) - ) + if (!Keyboard.Ctrl && ProfileManager.CurrentProfile.DoubleClickToLootInsideContainers && item != null && !item.IsDestroyed && !item.ItemData.IsContainer && item.IsEmpty && (container = World.Items.Get(item.RootContainer)) != null && container != World.Player.FindItemByLayer(Layer.Backpack)) { GameActions.GrabItem(LocalSerial, item.Amount); } @@ -195,34 +297,44 @@ protected override bool OnMouseDoubleClick(int x, int y, MouseButtonType button) { GameActions.DoubleClick(LocalSerial); } - + return true; } - private void AttempPickUp() + private void AttemptPickUp() { if (CanPickUp) { - Rectangle bounds = ArtLoader.Instance.GetTexture(Graphic).Bounds; + UOTexture texture = _is_gump ? GumpsLoader.Instance.GetTexture(Graphic) : ArtLoader.Instance.GetTexture(Graphic); + + Rectangle bounds = texture.Bounds; int centerX = bounds.Width >> 1; int centerY = bounds.Height >> 1; - if (ProfileManager.Current != null && ProfileManager.Current.ScaleItemsInsideContainers) + if (ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.ScaleItemsInsideContainers) { float scale = UIManager.ContainerScale; centerX = (int) (centerX * scale); centerY = (int) (centerY * scale); } - if (ProfileManager.Current != null && ProfileManager.Current.RelativeDragAndDropItems) + if (ProfileManager.CurrentProfile != null && ProfileManager.CurrentProfile.RelativeDragAndDropItems) { Point p = new Point(centerX - (Mouse.Position.X - ScreenCoordinateX), centerY - (Mouse.Position.Y - ScreenCoordinateY)); - GameActions.PickUp(LocalSerial, centerX, centerY, offset: p); + + GameActions.PickUp + ( + LocalSerial, + centerX, + centerY, + offset: p, + is_gump: _is_gump + ); } else { - GameActions.PickUp(LocalSerial, centerX, centerY); + GameActions.PickUp(LocalSerial, centerX, centerY, is_gump: _is_gump); } } } diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Line.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Line.cs deleted file mode 120000 index d57cd2a7f..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Line.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/Line.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Line.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Line.cs new file mode 100644 index 000000000..4e41f14cc --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/Line.cs @@ -0,0 +1,102 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using ClassicUO.Game.UI.Gumps; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using System; + +namespace ClassicUO.Game.UI.Controls +{ + internal class Line : Control + { + private readonly Texture2D _texture; + + public Line(int x, int y, int w, int h, uint color) + { + X = x; + Y = y; + Width = w; + Height = h; + + _texture = SolidColorTextureCache.GetTexture(new Color { PackedValue = color }); + } + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + ResetHueVector(); + ShaderHueTranslator.GetHueVector(ref HueVector, 0, false, Alpha); + + return batcher.Draw2D + ( + _texture, + x, + y, + Width, + Height, + ref HueVector + ); + } + + // MobileUO: need to keep for assistant + internal static Line[] CreateRectangleArea(Gump g, int startx, int starty, int width, int height, int topage = 0, uint linecolor = 0xAFAFAF, int linewidth = 1, string toplabel = null, ushort textcolor = 999, byte textfont = 0xFF) + { + Line[] lines = new Line[3]; + + if (!string.IsNullOrEmpty(toplabel)) + { + Label l = new Label(toplabel, true, textcolor, font: textfont); + int rwidth = (width - l.Width) >> 1; + l.X = startx + rwidth + 2; + l.Y = Math.Max(0, starty - ((l.Height + 1) >> 1)); + g.Add(l, topage); + + if (rwidth > 0) + { + g.Add(new Line(startx, starty, rwidth, linewidth, linecolor), topage); + g.Add(new Line(startx + width - rwidth, starty, rwidth, linewidth, linecolor), topage); + } + } + else + { + g.Add(new Line(startx, starty, width, linewidth, linecolor), topage); + } + + g.Add(lines[0] = new Line(startx, starty, linewidth, height, linecolor), topage); + g.Add(lines[1] = new Line(startx + width - 1, starty, linewidth, height, linecolor), topage); + g.Add(lines[2] = new Line(startx, starty + height - 1, width, linewidth, linecolor), topage); + + return lines; + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/NiceButton.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/NiceButton.cs deleted file mode 120000 index 625424d68..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/NiceButton.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/NiceButton.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/NiceButton.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/NiceButton.cs new file mode 100644 index 000000000..93098be0f --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/NiceButton.cs @@ -0,0 +1,195 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System.Collections.Generic; +// MobileUO: added import +using System.Linq; +using ClassicUO.Input; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; + +namespace ClassicUO.Game.UI.Controls +{ + internal class NiceButton : HitBox + { + private readonly ButtonAction _action; + private readonly int _groupnumber; + private bool _isSelected; + + public NiceButton + ( + int x, + int y, + int w, + int h, + ButtonAction action, + string text, + int groupnumber = 0, + TEXT_ALIGN_TYPE align = TEXT_ALIGN_TYPE.TS_CENTER, + ushort hue = 0xFFFF, + bool unicode = true, + byte font = 0xFF + ) : base(x, y, w, h) + { + _action = action; + + Add + ( + TextLabel = new Label + ( + text, + unicode, + hue, + w, + font, + FontStyle.BlackBorder | FontStyle.Cropped, + align + ) + ); + + TextLabel.Y = (h - TextLabel.Height) >> 1; + _groupnumber = groupnumber; + } + + internal Label TextLabel { get; } + + public int ButtonParameter { get; set; } + + public bool IsSelectable { get; set; } = true; + + public bool IsSelected + { + get => _isSelected && IsSelectable; + set + { + if (!IsSelectable) + { + return; + } + + _isSelected = value; + + if (value) + { + Control p = Parent; + + if (p == null) + { + return; + } + + // MobileUO: for assistant + IEnumerable list; + + if (p is ScrollAreaItem) + { + p = p.Parent; + + list = p.FindControls() + .SelectMany(s => s.Children.OfType()); + } + else + { + list = p.FindControls(); + } + + foreach (NiceButton b in list) + { + if (b != this && b._groupnumber == _groupnumber) + { + b.IsSelected = false; + } + } + } + } + } + + internal static NiceButton GetSelected(Control p, int group) + { + // MobileUO: for assistant + IEnumerable list = p is ScrollArea + ? p.FindControls() + .SelectMany(s => s.Children.OfType()) + : p.FindControls(); + + foreach (NiceButton b in list) + { + if (b._groupnumber == group && b.IsSelected) + { + return b; + } + } + + return null; + } + + protected override void OnMouseUp(int x, int y, MouseButtonType button) + { + if (button == MouseButtonType.Left) + { + IsSelected = true; + + if (_action == ButtonAction.SwitchPage) + { + ChangePage(ButtonParameter); + } + else + { + OnButtonClick(ButtonParameter); + } + } + } + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + if (IsSelected) + { + ResetHueVector(); + ShaderHueTranslator.GetHueVector(ref HueVector, 0, false, Alpha); + + batcher.Draw2D + ( + _texture, + x, + y, + 0, + 0, + Width, + Height, + ref HueVector + ); + } + + return base.Draw(batcher, x, y); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ResizePic.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ResizePic.cs deleted file mode 120000 index 15ab332f7..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ResizePic.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/ResizePic.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ResizePic.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ResizePic.cs new file mode 100644 index 000000000..dd699a943 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ResizePic.cs @@ -0,0 +1,594 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using ClassicUO.Utility; +using Microsoft.Xna.Framework; + +namespace ClassicUO.Game.UI.Controls +{ + internal class ResizePic : Control + { + private readonly UOTexture[] _gumpTexture = new UOTexture[9]; + + public ResizePic(ushort graphic) + { + CanMove = true; + CanCloseWithRightClick = true; + + for (int i = 0; i < _gumpTexture.Length; i++) + { + UOTexture t = GumpsLoader.Instance.GetTexture((ushort) (graphic + i)); + + if (t == null) + { + return; + } + + if (i == 4) + { + _gumpTexture[8] = t; + } + else if (i > 4) + { + _gumpTexture[i - 1] = t; + } + else + { + _gumpTexture[i] = t; + } + } + + Graphic = graphic; + } + + public ResizePic(List parts) : this(UInt16Converter.Parse(parts[3])) + { + X = int.Parse(parts[1]); + Y = int.Parse(parts[2]); + Width = int.Parse(parts[4]); + Height = int.Parse(parts[5]); + IsFromServer = true; + } + + public ushort Graphic { get; } + + public override void Update(double totalTime, double frameTime) + { + for (int i = 0; i < _gumpTexture.Length; i++) + { + if (_gumpTexture[i] != null) + { + _gumpTexture[i].Ticks = (long) totalTime; + } + } + + base.Update(totalTime, frameTime); + } + + public override bool Contains(int x, int y) + { + x -= Offset.X; + y -= Offset.Y; + + int th_0_width = _gumpTexture[0]?.Width ?? 0; + + int th_0_height = _gumpTexture[0]?.Height ?? 0; + + int th_1_width = _gumpTexture[1]?.Width ?? 0; + + int th_1_height = _gumpTexture[1]?.Height ?? 0; + + int th_2_width = _gumpTexture[2]?.Width ?? 0; + + int th_2_height = _gumpTexture[2]?.Height ?? 0; + + int th_3_width = _gumpTexture[3]?.Width ?? 0; + + int th_3_height = _gumpTexture[3]?.Height ?? 0; + + int th_4_width = _gumpTexture[4]?.Width ?? 0; + + int th_4_height = _gumpTexture[4]?.Height ?? 0; + + int th_5_width = _gumpTexture[5]?.Width ?? 0; + + int th_5_height = _gumpTexture[5]?.Height ?? 0; + + int th_6_width = _gumpTexture[6]?.Width ?? 0; + + int th_6_height = _gumpTexture[6]?.Height ?? 0; + + int th_7_width = _gumpTexture[7]?.Width ?? 0; + + int th_7_height = _gumpTexture[7]?.Height ?? 0; + + int th_8_width = _gumpTexture[8]?.Width ?? 0; + + int th_8_height = _gumpTexture[8]?.Height ?? 0; + + + int offsetTop = Math.Max(th_0_height, th_2_height) - th_1_height; + int offsetBottom = Math.Max(th_5_height, th_7_height) - th_6_height; + int offsetLeft = Math.Abs(Math.Max(th_0_width, th_5_width) - th_2_width); + int offsetRight = Math.Max(th_2_width, th_7_width) - th_4_width; + + + for (int i = 0; i < 9; i++) + { + if (_gumpTexture[i] == null) + { + continue; + } + + switch (i) + { + case 0: + if (PixelsInXY(GumpsLoader.Instance.GetTexture(Graphic), Graphic, x, y)) + { + return true; + } + + break; + + case 1: + int DW = Width - th_0_width - th_2_width; + + if (DW < 1) + { + break; + } + + if (PixelsInXY(GumpsLoader.Instance.GetTexture((ushort) (Graphic + 1)), (ushort)(Graphic + 1), x - th_0_width, y, DW)) + { + return true; + } + + break; + + case 2: + + if (PixelsInXY(GumpsLoader.Instance.GetTexture((ushort) (Graphic + 2)), (ushort)(Graphic + 2), x - (Width - th_2_width), y - offsetTop)) + { + return true; + } + + break; + + case 3: + + int DH = Height - th_0_height - th_5_height; + + if (DH < 1) + { + break; + } + + if (PixelsInXY + ( + GumpsLoader.Instance.GetTexture((ushort) (Graphic + 3)), (ushort)(Graphic + 3), + x /*- offsetLeft*/, + y - th_0_height, + 0, + DH + )) + { + return true; + } + + + break; + + case 4: + + DH = Height - th_2_height - th_7_height; + + if (DH < 1) + { + break; + } + + if (PixelsInXY + ( + GumpsLoader.Instance.GetTexture((ushort) (Graphic + 5)), (ushort)(Graphic + 5), + x - (Width - th_4_width /*- offsetRight*/), + y - th_2_height, + 0, + DH + )) + { + return true; + } + + break; + + case 5: + + if (PixelsInXY(GumpsLoader.Instance.GetTexture((ushort) (Graphic + 6)), (ushort)(Graphic + 6), x, y - (Height - th_5_height))) + { + return true; + } + + break; + + case 6: + + DW = Width - th_5_width - th_2_width; + + if (DW < 1) + { + break; + } + + if (PixelsInXY(GumpsLoader.Instance.GetTexture((ushort) (Graphic + 7)), (ushort)(Graphic + 7), x - th_5_width, y - (Height - th_6_height - offsetBottom), DW)) + { + return true; + } + + + break; + + case 7: + + if (PixelsInXY(GumpsLoader.Instance.GetTexture((ushort) (Graphic + 8)), (ushort)(Graphic + 8), x - (Width - th_7_width), y - (Height - th_7_height))) + { + return true; + } + + break; + + case 8: + + DW = Width - th_0_width - th_2_width; + + DW += offsetLeft + offsetRight; + + if (DW < 1) + { + break; + } + + DH = Height - th_2_height - th_7_height; + + if (DH < 1) + { + break; + } + + if (PixelsInXY + ( + GumpsLoader.Instance.GetTexture((ushort) (Graphic + 4)), + (ushort)(Graphic + 4), + x - th_0_width, + y - th_0_height, + DW, + DH + )) + { + return true; + } + + + break; + } + } + + return false; + } + + + private static bool PixelsInXY(UOTexture texture, ushort graphic, int x, int y, int width = 0, int height = 0) + { + if (x < 0 || y < 0 || width > 0 && x >= width || height > 0 && y >= height) + { + return false; + } + + int textureWidth = texture.Width; + int textureHeight = texture.Height; + + if (width == 0) + { + width = textureWidth; + } + + if (height == 0) + { + height = textureHeight; + } + + + while (x >= textureWidth && width >= textureWidth) + { + x -= textureWidth; + width -= textureWidth; + } + + if (x < 0 || x > width) + { + return false; + } + + while (y >= textureHeight && height >= textureHeight) + { + y -= textureHeight; + height -= textureHeight; + } + + if (y < 0 || y > height) + { + return false; + } + + return GumpsLoader.Instance.PixelCheck(graphic, x, y); + } + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + ResetHueVector(); + + if (batcher.ClipBegin(x, y, Width, Height)) + { + ShaderHueTranslator.GetHueVector + ( + ref HueVector, + 0, + false, + Alpha, + true + ); + + DrawInternal(batcher, x, y, ref HueVector); + base.Draw(batcher, x, y); + + batcher.ClipEnd(); + } + + return true; + } + + private void DrawInternal(UltimaBatcher2D batcher, int x, int y, ref Vector3 color) + { + int th_0_width = _gumpTexture[0]?.Width ?? 0; + + int th_0_height = _gumpTexture[0]?.Height ?? 0; + + int th_1_width = _gumpTexture[1]?.Width ?? 0; + + int th_1_height = _gumpTexture[1]?.Height ?? 0; + + int th_2_width = _gumpTexture[2]?.Width ?? 0; + + int th_2_height = _gumpTexture[2]?.Height ?? 0; + + int th_3_width = _gumpTexture[3]?.Width ?? 0; + + int th_3_height = _gumpTexture[3]?.Height ?? 0; + + int th_4_width = _gumpTexture[4]?.Width ?? 0; + + int th_4_height = _gumpTexture[4]?.Height ?? 0; + + int th_5_width = _gumpTexture[5]?.Width ?? 0; + + int th_5_height = _gumpTexture[5]?.Height ?? 0; + + int th_6_width = _gumpTexture[6]?.Width ?? 0; + + int th_6_height = _gumpTexture[6]?.Height ?? 0; + + int th_7_width = _gumpTexture[7]?.Width ?? 0; + + int th_7_height = _gumpTexture[7]?.Height ?? 0; + + int th_8_width = _gumpTexture[8]?.Width ?? 0; + + int th_8_height = _gumpTexture[8]?.Height ?? 0; + + + int offsetTop = Math.Max(th_0_height, th_2_height) - th_1_height; + int offsetBottom = Math.Max(th_5_height, th_7_height) - th_6_height; + int offsetLeft = Math.Abs(Math.Max(th_0_width, th_5_width) - th_2_width); + int offsetRight = Math.Max(th_2_width, th_7_width) - th_4_width; + + + for (int i = 0; i < 9; i++) + { + UOTexture t = _gumpTexture[i]; + + if (t == null) + { + continue; + } + + int drawWidth = t.Width; + int drawHeight = t.Height; + int drawX = x; + int drawY = y; + + switch (i) + { + case 0: + + batcher.Draw2D + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 1: + drawX += th_0_width; + drawWidth = Width - th_0_width - th_2_width; + + batcher.Draw2DTiled + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 2: + drawX += Width - drawWidth; + drawY += offsetTop; + + batcher.Draw2D + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 3: + //drawX += offsetLeft; + drawY += th_0_height; + drawHeight = Height - th_0_height - th_5_height; + + batcher.Draw2DTiled + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 4: + drawX += Width - drawWidth /*- offsetRight*/; + drawY += th_2_height; + drawHeight = Height - th_2_height - th_7_height; + + batcher.Draw2DTiled + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 5: + drawY += Height - drawHeight; + + batcher.Draw2D + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 6: + drawX += th_5_width; + drawY += Height - drawHeight - offsetBottom; + drawWidth = Width - th_5_width - th_7_width; + + batcher.Draw2DTiled + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 7: + drawX += Width - drawWidth; + drawY += Height - drawHeight; + + batcher.Draw2D + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + + case 8: + drawX += th_0_width; + drawY += th_0_height; + drawWidth = Width - th_0_width - th_2_width; + drawHeight = Height - th_2_height - th_7_height; + + drawWidth += offsetLeft + offsetRight; + + batcher.Draw2DTiled + ( + t, + drawX, + drawY, + drawWidth, + drawHeight, + ref color + ); + + break; + } + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollArea.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollArea.cs deleted file mode 120000 index c4df2c741..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollArea.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/ScrollArea.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollArea.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollArea.cs new file mode 100644 index 000000000..035c811e8 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollArea.cs @@ -0,0 +1,245 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using ClassicUO.Input; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; + +namespace ClassicUO.Game.UI.Controls +{ + internal enum ScrollbarBehaviour + { + ShowWhenDataExceedFromView, + ShowAlways + } + + internal class ScrollArea : Control + { + private bool _isNormalScroll; + private readonly ScrollBarBase _scrollBar; + + public ScrollArea + ( + int x, + int y, + int w, + int h, + bool normalScrollbar, + int scroll_max_height = -1 + ) + { + X = x; + Y = y; + Width = w; + Height = h; + _isNormalScroll = normalScrollbar; + + if (normalScrollbar) + { + _scrollBar = new ScrollBar(Width - 14, 0, Height); + } + else + { + _scrollBar = new ScrollFlag + { + X = Width - 19, Height = h + }; + + Width += 15; + } + + ScrollMaxHeight = scroll_max_height; + + _scrollBar.MinValue = 0; + _scrollBar.MaxValue = scroll_max_height >= 0 ? scroll_max_height : Height; + _scrollBar.Parent = this; + + AcceptMouseInput = true; + WantUpdateSize = false; + CanMove = true; + ScrollbarBehaviour = ScrollbarBehaviour.ShowWhenDataExceedFromView; + } + + + public int ScrollMaxHeight { get; set; } = -1; + public ScrollbarBehaviour ScrollbarBehaviour { get; set; } + public int ScrollValue => _scrollBar.Value; + public int ScrollMinValue => _scrollBar.MinValue; + public int ScrollMaxValue => _scrollBar.MaxValue; + + + public Rectangle ScissorRectangle; + + + public override void Update(double totalTime, double frameTime) + { + base.Update(totalTime, frameTime); + + CalculateScrollBarMaxValue(); + + if (ScrollbarBehaviour == ScrollbarBehaviour.ShowAlways) + { + _scrollBar.IsVisible = true; + } + else if (ScrollbarBehaviour == ScrollbarBehaviour.ShowWhenDataExceedFromView) + { + _scrollBar.IsVisible = _scrollBar.MaxValue > _scrollBar.MinValue; + } + } + + public void Scroll(bool isup) + { + if (isup) + { + _scrollBar.Value -= _scrollBar.ScrollStep; + } + else + { + _scrollBar.Value += _scrollBar.ScrollStep; + } + } + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + ScrollBarBase scrollbar = (ScrollBarBase) Children[0]; + scrollbar.Draw(batcher, x + scrollbar.X, y + scrollbar.Y); + + if (batcher.ClipBegin(x + ScissorRectangle.X, y + ScissorRectangle.Y, Width - 14 + ScissorRectangle.Width, Height + ScissorRectangle.Height)) + { + for (int i = 1; i < Children.Count; i++) + { + Control child = Children[i]; + + if (!child.IsVisible) + { + continue; + } + + int finalY = y + child.Y - scrollbar.Value + ScissorRectangle.Y; + + child.Draw(batcher, x + child.X, finalY); + } + + batcher.ClipEnd(); + } + + return true; + } + + + protected override void OnMouseWheel(MouseEventType delta) + { + switch (delta) + { + case MouseEventType.WheelScrollUp: + _scrollBar.Value -= _scrollBar.ScrollStep; + + break; + + case MouseEventType.WheelScrollDown: + _scrollBar.Value += _scrollBar.ScrollStep; + + break; + } + } + + public override void Clear() + { + for (int i = 1; i < Children.Count; i++) + { + Children[i].Dispose(); + } + } + + private void CalculateScrollBarMaxValue() + { + _scrollBar.Height = ScrollMaxHeight >= 0 ? ScrollMaxHeight : Height; + bool maxValue = _scrollBar.Value == _scrollBar.MaxValue && _scrollBar.MaxValue != 0; + + int startX = 0, startY = 0, endX = 0, endY = 0; + + for (int i = 1; i < Children.Count; i++) + { + Control c = Children[i]; + + if (c.IsVisible && !c.IsDisposed) + { + if (c.X < startX) + { + startX = c.X; + } + + if (c.Y < startY) + { + startY = c.Y; + } + + if (c.Bounds.Right > endX) + { + endX = c.Bounds.Right; + } + + if (c.Bounds.Bottom > endY) + { + endY = c.Bounds.Bottom; + } + } + } + + int width = Math.Abs(startX) + Math.Abs(endX); + int height = Math.Abs(startY) + Math.Abs(endY) - _scrollBar.Height; + height = Math.Max(0, height - (-ScissorRectangle.Y + ScissorRectangle.Height)); + + if (height > 0) + { + _scrollBar.MaxValue = height; + + if (maxValue) + { + _scrollBar.Value = _scrollBar.MaxValue; + } + } + else + { + _scrollBar.Value = _scrollBar.MaxValue = 0; + } + + _scrollBar.UpdateOffset(0, Offset.Y); + + for (int i = 1; i < Children.Count; i++) + { + Children[i].UpdateOffset(0, -_scrollBar.Value + ScissorRectangle.Y); + } + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollFlag.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollFlag.cs deleted file mode 120000 index d494c0f14..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollFlag.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/ScrollFlag.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollFlag.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollFlag.cs new file mode 100644 index 000000000..41349c895 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/ScrollFlag.cs @@ -0,0 +1,176 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using ClassicUO.Input; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using Microsoft.Xna.Framework; + +namespace ClassicUO.Game.UI.Controls +{ + internal class ScrollFlag : ScrollBarBase + { + private readonly bool _showButtons; + + + public ScrollFlag(int x, int y, int height, bool showbuttons) : this() + { + X = x; + Y = y; + Height = height; + + //TODO: + _showButtons = false; // showbuttons; + } + + public ScrollFlag() + { + AcceptMouseInput = true; + + UOTexture texture_flag = GumpsLoader.Instance.GetTexture(0x0828); + + if (texture_flag == null) + { + Dispose(); + + return; + } + + Width = texture_flag.Width; + Height = texture_flag.Height; + + UOTexture texture_button_up = GumpsLoader.Instance.GetTexture(0x0824); + UOTexture texture_button_down = GumpsLoader.Instance.GetTexture(0x0825); + + _rectUpButton = new Rectangle(0, 0, texture_button_up.Width, texture_button_up.Height); + _rectDownButton = new Rectangle(0, Height, texture_button_down.Width, texture_button_down.Height); + + WantUpdateSize = false; + } + + public override ClickPriority Priority { get; set; } = ClickPriority.High; + + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + ResetHueVector(); + + UOTexture texture_flag = GumpsLoader.Instance.GetTexture(0x0828); + UOTexture texture_button_up = GumpsLoader.Instance.GetTexture(0x0824); + UOTexture texture_button_down = GumpsLoader.Instance.GetTexture(0x0825); + + + if (MaxValue != MinValue && texture_flag != null) + { + batcher.Draw2D(texture_flag, x, (int) (y + _sliderPosition), ref HueVector); + } + + if (_showButtons) + { + if (texture_button_up != null) + { + batcher.Draw2D(texture_button_up, x, y, ref HueVector); + } + + if (texture_button_down != null) + { + batcher.Draw2D(texture_button_down, x, y + Height, ref HueVector); + } + } + + return base.Draw(batcher, x, y); + } + + protected override int GetScrollableArea() + { + UOTexture texture = GumpsLoader.Instance.GetTexture(0x0828); + + return Height - texture?.Height ?? 0; + } + + + protected override void CalculateByPosition(int x, int y) + { + if (y != _clickPosition.Y) + { + UOTexture texture = GumpsLoader.Instance.GetTexture(0x0828); + int height = texture?.Height ?? 0; + + y -= (height >> 1); + + + if (y < 0) + { + y = 0; + } + + int scrollableArea = GetScrollableArea(); + + if (y > scrollableArea) + { + y = scrollableArea; + } + + _sliderPosition = y; + _clickPosition.X = x; + _clickPosition.Y = y; + + if (y == 0 && _clickPosition.Y < height >> 1) + { + _clickPosition.Y = height >> 1; + } + else if (y == scrollableArea && _clickPosition.Y > Height - (height >> 1)) + { + _clickPosition.Y = Height - (height >> 1); + } + + _value = (int) Math.Round(y / (float) scrollableArea * (MaxValue - MinValue) + MinValue); + } + } + + + public override bool Contains(int x, int y) + { + UOTexture texture_flag = GumpsLoader.Instance.GetTexture(0x0828); + + if (texture_flag == null) + { + return false; + } + + y -= _sliderPosition; + + return GumpsLoader.Instance.PixelCheck(0x0828, x, y); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StaticPic.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StaticPic.cs deleted file mode 120000 index bbb718d24..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StaticPic.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Controls/StaticPic.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StaticPic.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StaticPic.cs new file mode 100644 index 000000000..370ebaa9d --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StaticPic.cs @@ -0,0 +1,115 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System.Collections.Generic; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using ClassicUO.Utility; + +namespace ClassicUO.Game.UI.Controls +{ + internal class StaticPic : Control + { + private ushort _graphic; + + public StaticPic(ushort graphic, ushort hue) + { + Hue = hue; + Graphic = graphic; + CanMove = true; + WantUpdateSize = false; + } + + public StaticPic(List parts) : this(UInt16Converter.Parse(parts[3]), parts.Count > 4 ? UInt16Converter.Parse(parts[4]) : (ushort) 0) + { + X = int.Parse(parts[1]); + Y = int.Parse(parts[2]); + IsFromServer = true; + } + + + public ushort Hue { get; set; } + public bool IsPartialHue { get; set; } + + public ushort Graphic + { + get => _graphic; + set + { + _graphic = value; + + ArtTexture texture = ArtLoader.Instance.GetTexture(value); + + if (texture == null) + { + Dispose(); + + return; + } + + Width = texture.Width; + Height = texture.Height; + + IsPartialHue = TileDataLoader.Instance.StaticData[value].IsPartialHue; + } + } + + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + ResetHueVector(); + ShaderHueTranslator.GetHueVector(ref HueVector, Hue, IsPartialHue, 0); + + ArtTexture texture = ArtLoader.Instance.GetTexture(Graphic); + + if (texture != null) + { + batcher.Draw2D + ( + texture, + x, + y, + Width, + Height, + ref HueVector + ); + } + + return base.Draw(batcher, x, y); + } + + public override bool Contains(int x, int y) + { + return ArtLoader.Instance.PixelCheck(Graphic, x - Offset.X, y - Offset.Y); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StbTextBox.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StbTextBox.cs index e8bb36098..f84f6f0cd 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StbTextBox.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/StbTextBox.cs @@ -1,35 +1,70 @@ -using ClassicUO.Input; +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using ClassicUO.Game.Managers; +using ClassicUO.Input; using ClassicUO.IO.Resources; using ClassicUO.Renderer; using ClassicUO.Utility; +using ClassicUO.Utility.Logging; using Microsoft.Xna.Framework; using SDL2; using StbTextEditSharp; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +// MobileUO: added import using ClassicUO.Game.UI.Gumps.Login; -using ClassicUO.Utility.Logging; namespace ClassicUO.Game.UI.Controls { - class StbTextBox : Control, ITextEditHandler + internal class StbTextBox : Control, ITextEditHandler { - private readonly TextEdit _stb; - protected TextEdit Stb => _stb; - protected RenderedText _rendererText, _rendererCaret; - - private int _maxCharCount = -1; - protected Point _caretScreenPosition; - protected bool _leftWasDown, _fromServer; - private FontStyle _fontStyle; - protected bool _is_writing = false; - - - public StbTextBox(byte font, int max_char_count = -1, int maxWidth = 0, bool isunicode = true, FontStyle style = FontStyle.None, ushort hue = 0, TEXT_ALIGN_TYPE align = 0) + protected static readonly Color SELECTION_COLOR = new Color() { PackedValue = 0x80a06020 }; + private readonly FontStyle _fontStyle; + + private readonly int _maxCharCount = -1; + + + public StbTextBox + ( + byte font, + int max_char_count = -1, + int maxWidth = 0, + bool isunicode = true, + FontStyle style = FontStyle.None, + ushort hue = 0, + TEXT_ALIGN_TYPE align = 0 + ) { AcceptKeyboardInput = true; AcceptMouseInput = true; @@ -38,11 +73,13 @@ public StbTextBox(byte font, int max_char_count = -1, int maxWidth = 0, bool isu _maxCharCount = max_char_count; - _stb = new TextEdit(this); - _stb.SingleLine = true; + Stb = new TextEdit(this); + Stb.SingleLine = true; if (maxWidth > 0) + { style |= FontStyle.CropTexture; + } _fontStyle = style; @@ -52,22 +89,44 @@ public StbTextBox(byte font, int max_char_count = -1, int maxWidth = 0, bool isu } // stb_textedit will handle part of these tag - style &= ~(/*FontStyle.Fixed | */FontStyle.Cropped | FontStyle.CropTexture); - - _rendererText = RenderedText.Create(string.Empty, hue, font, isunicode, style, align, maxWidth, 30, false, false, false); - _rendererCaret = RenderedText.Create("_", hue, font, isunicode, (style & FontStyle.BlackBorder) != 0 ? FontStyle.BlackBorder : FontStyle.None, align: align); + style &= ~( /*FontStyle.Fixed | */FontStyle.Cropped | FontStyle.CropTexture); + + _rendererText = RenderedText.Create + ( + string.Empty, + hue, + font, + isunicode, + style, + align, + maxWidth + ); + + _rendererCaret = RenderedText.Create + ( + "_", + hue, + font, + isunicode, + (style & FontStyle.BlackBorder) != 0 ? FontStyle.BlackBorder : FontStyle.None, + align + ); Height = _rendererCaret.Height; - - if (Height < 50) - Height = 50; } - public StbTextBox(List parts, string[] lines) : this(1, parts[0] == "textentrylimited" ? int.Parse(parts[8]) : byte.MaxValue, int.Parse(parts[3]), style: FontStyle.BlackBorder | FontStyle.CropTexture, hue: (ushort) (UInt16Converter.Parse(parts[5]) + 1)) + public StbTextBox(List parts, string[] lines) : this + ( + 1, + parts[0] == "textentrylimited" ? int.Parse(parts[8]) : byte.MaxValue, + int.Parse(parts[3]), + style: FontStyle.BlackBorder | FontStyle.CropTexture, + hue: (ushort) (UInt16Converter.Parse(parts[5]) + 1) + ) { X = int.Parse(parts[1]); Y = int.Parse(parts[2]); - Width = _rendererText.MaxWidth;//int.Parse(parts[3]); + Width = _rendererText.MaxWidth; //int.Parse(parts[3]); Height = _rendererText.MaxHeight = int.Parse(parts[4]); Multiline = false; _fromServer = true; @@ -82,69 +141,70 @@ public StbTextBox(List parts, string[] lines) : this(1, parts[0] == "tex } } + protected TextEdit Stb { get; } + public override bool AcceptKeyboardInput => base.AcceptKeyboardInput && IsEditable; - public string Text + public byte Font { - get => _rendererText.Text; - + get => _rendererText.Font; set { - if (_maxCharCount >= 0 && value != null && value.Length > _maxCharCount) - value = value.Substring(0, _maxCharCount); - - //Sanitize(ref value); - - _rendererText.Text = value; - - if (!_is_writing) + if (_rendererText.Font != value) { - OnTextChanged(); + _rendererText.Font = value; + _rendererText.CreateTexture(); + _rendererCaret.Font = value; + _rendererCaret.CreateTexture(); + + UpdateCaretScreenPosition(); } } } - public int Length => Text?.Length ?? 0; - public bool AllowTAB { get; set; } public bool NoSelection { get; set; } public int CaretIndex { - get => _stb.CursorIndex; + get => Stb.CursorIndex; set { - _stb.CursorIndex = value; + Stb.CursorIndex = value; UpdateCaretScreenPosition(); } } public bool Multiline { - get => !_stb.SingleLine; - set => _stb.SingleLine = !value; + get => !Stb.SingleLine; + set => Stb.SingleLine = !value; } public bool NumbersOnly { get; set; } public int SelectionStart { - get => _stb.SelectStart; + get => Stb.SelectStart; set { if (AllowSelection) - _stb.SelectStart = value; - } + { + Stb.SelectStart = value; + } + } } public int SelectionEnd { - get => _stb.SelectEnd; + get => Stb.SelectEnd; set { if (AllowSelection) - _stb.SelectEnd = value; + { + Stb.SelectEnd = value; + } } } @@ -168,15 +228,54 @@ public ushort Hue } } - public event EventHandler TextChanged; + internal int TotalHeight + { + get + { + int h = 20; + MultilinesFontInfo info = GetInfo(); - public MultilinesFontInfo CalculateFontInfo(string text, bool countret = true) + while (info != null) + { + h += info.MaxHeight; + info = info.Next; + } + + return h; + } + } + + public string Text { - if (IsUnicode) - return FontsLoader.Instance.GetInfoUnicode(_rendererText.Font, text, text.Length, _rendererText.Align, (ushort) _rendererText.FontStyle, _rendererText.MaxWidth, countret); - return FontsLoader.Instance.GetInfoASCII(_rendererText.Font, text, text.Length, _rendererText.Align, (ushort) _rendererText.FontStyle, _rendererText.MaxWidth, countret); + get => _rendererText.Text; + + set + { + if (_maxCharCount > 0) + { + if (NumbersOnly) + { + + } + if (value != null && value.Length > _maxCharCount) + { + value = value.Substring(0, _maxCharCount); + } + } + + //Sanitize(ref value); + + _rendererText.Text = value; + + if (!_is_writing) + { + OnTextChanged(); + } + } } + public int Length => Text?.Length ?? 0; + public float GetWidth(int index) { return _rendererText.GetCharWidthAtIndex(index); @@ -197,18 +296,53 @@ public TextEditRow LayoutRow(int startIndex) return r; } + protected Point _caretScreenPosition; + protected bool _is_writing; + protected bool _leftWasDown, _fromServer; + protected RenderedText _rendererText, _rendererCaret; + + public event EventHandler TextChanged; + + public MultilinesFontInfo CalculateFontInfo(string text, bool countret = true) + { + if (IsUnicode) + { + return FontsLoader.Instance.GetInfoUnicode + ( + _rendererText.Font, + text, + text.Length, + _rendererText.Align, + (ushort) _rendererText.FontStyle, + _rendererText.MaxWidth, + countret + ); + } + + return FontsLoader.Instance.GetInfoASCII + ( + _rendererText.Font, + text, + text.Length, + _rendererText.Align, + (ushort) _rendererText.FontStyle, + _rendererText.MaxWidth, + countret + ); + } + public void SelectAll() { if (AllowSelection) { - _stb.SelectStart = 0; - _stb.SelectEnd = Length; + Stb.SelectStart = 0; + Stb.SelectEnd = Length; } } protected void UpdateCaretScreenPosition() { - _caretScreenPosition = _rendererText.GetCaretPosition(_stb.CursorIndex); + _caretScreenPosition = _rendererText.GetCaretPosition(Stb.CursorIndex); } private ControlKeys ApplyShiftIfNecessary(ControlKeys k) @@ -222,7 +356,9 @@ private ControlKeys ApplyShiftIfNecessary(ControlKeys k) } private bool IsMaxCharReached(int count) - => _maxCharCount >= 0 && Length + count >= _maxCharCount; + { + return _maxCharCount >= 0 && Length + count >= _maxCharCount; + } private void Sanitize(ref string text) { @@ -231,24 +367,24 @@ private void Sanitize(ref string text) if (_rendererText.MaxWidth == 0) { Log.Warn("maxwidth must be setted."); + return; } if (string.IsNullOrEmpty(text)) + { return; + } - - int realWidth = _rendererText.IsUnicode - ? FontsLoader.Instance.GetWidthUnicode(_rendererText.Font, text) - : FontsLoader.Instance.GetWidthASCII(_rendererText.Font, text); + int realWidth = _rendererText.IsUnicode ? FontsLoader.Instance.GetWidthUnicode(_rendererText.Font, text) : FontsLoader.Instance.GetWidthASCII(_rendererText.Font, text); if (realWidth > _rendererText.MaxWidth) { if ((_fontStyle & FontStyle.Fixed) != 0) { text = Text; - _stb.CursorIndex = Math.Max(0, text.Length - 1); + Stb.CursorIndex = Math.Max(0, text.Length - 1); return; } @@ -319,7 +455,6 @@ private void Sanitize(ref string text) if ((_fontStyle & FontStyle.Cropped) != 0) { - } } } @@ -333,7 +468,10 @@ protected virtual void OnTextChanged() UpdateCaretScreenPosition(); } - protected MultilinesFontInfo GetInfo() => _rendererText.GetInfo(); + protected MultilinesFontInfo GetInfo() + { + return _rendererText.GetInfo(); + } internal override void OnFocusEnter() { @@ -343,8 +481,10 @@ internal override void OnFocusEnter() internal override void OnFocusLost() { - if (_stb != null) - _stb.SelectStart = _stb.SelectEnd = 0; + if (Stb != null) + { + Stb.SelectStart = Stb.SelectEnd = 0; + } base.OnFocusLost(); } @@ -366,59 +506,81 @@ protected override void OnKeyDown(SDL.SDL_Keycode key, SDL.SDL_Keymod mod) { Parent?.KeyboardTabToNextFocus(this); } + break; + case SDL.SDL_Keycode.SDLK_a when Keyboard.Ctrl && !NoSelection: SelectAll(); + break; + case SDL.SDL_Keycode.SDLK_ESCAPE: SelectionStart = 0; SelectionEnd = 0; + break; case SDL.SDL_Keycode.SDLK_INSERT when IsEditable: stb_key = ControlKeys.InsertMode; + break; + case SDL.SDL_Keycode.SDLK_c when Keyboard.Ctrl && !NoSelection: - int selectStart = Math.Min(_stb.SelectStart, _stb.SelectEnd); - int selectEnd = Math.Max(_stb.SelectStart, _stb.SelectEnd); + int selectStart = Math.Min(Stb.SelectStart, Stb.SelectEnd); + int selectEnd = Math.Max(Stb.SelectStart, Stb.SelectEnd); - if (selectStart < selectEnd && selectStart >= 0 && selectEnd - selectStart < Text.Length) + if (selectStart < selectEnd && selectStart >= 0 && selectEnd - selectStart <= Text.Length) { SDL.SDL_SetClipboardText(Text.Substring(selectStart, selectEnd - selectStart)); } break; + case SDL.SDL_Keycode.SDLK_x when Keyboard.Ctrl && !NoSelection: - selectStart = Math.Min(_stb.SelectStart, _stb.SelectEnd); - selectEnd = Math.Max(_stb.SelectStart, _stb.SelectEnd); + selectStart = Math.Min(Stb.SelectStart, Stb.SelectEnd); + selectEnd = Math.Max(Stb.SelectStart, Stb.SelectEnd); - if (selectStart < selectEnd && selectStart >= 0 && selectEnd - selectStart < Text.Length) + if (selectStart < selectEnd && selectStart >= 0 && selectEnd - selectStart <= Text.Length) { SDL.SDL_SetClipboardText(Text.Substring(selectStart, selectEnd - selectStart)); + if (IsEditable) - _stb.Cut(); + { + Stb.Cut(); + } } break; + case SDL.SDL_Keycode.SDLK_v when Keyboard.Ctrl && IsEditable: OnTextInput(StringHelper.GetClipboardText(Multiline)); + break; + case SDL.SDL_Keycode.SDLK_z when Keyboard.Ctrl && IsEditable: stb_key = ControlKeys.Undo; + break; + case SDL.SDL_Keycode.SDLK_y when Keyboard.Ctrl && IsEditable: stb_key = ControlKeys.Redo; + break; + case SDL.SDL_Keycode.SDLK_LEFT: if (Keyboard.Ctrl && Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.WordLeft; + } } else if (Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.Left; + } } else if (Keyboard.Ctrl) { @@ -430,17 +592,23 @@ protected override void OnKeyDown(SDL.SDL_Keycode key, SDL.SDL_Keymod mod) } update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_RIGHT: if (Keyboard.Ctrl && Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.WordRight; + } } else if (Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.Right; + } } else if (Keyboard.Ctrl) { @@ -450,34 +618,49 @@ protected override void OnKeyDown(SDL.SDL_Keycode key, SDL.SDL_Keymod mod) { stb_key = ControlKeys.Right; } + update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_UP: stb_key = ApplyShiftIfNecessary(ControlKeys.Up); update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_DOWN: stb_key = ApplyShiftIfNecessary(ControlKeys.Down); update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_BACKSPACE when IsEditable: stb_key = ApplyShiftIfNecessary(ControlKeys.BackSpace); update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_DELETE when IsEditable: stb_key = ApplyShiftIfNecessary(ControlKeys.Delete); update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_HOME: if (Keyboard.Ctrl && Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.TextStart; + } } else if (Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.LineStart; + } } else if (Keyboard.Ctrl) { @@ -487,18 +670,25 @@ protected override void OnKeyDown(SDL.SDL_Keycode key, SDL.SDL_Keymod mod) { stb_key = ControlKeys.LineStart; } + update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_END: if (Keyboard.Ctrl && Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.TextEnd; + } } else if (Keyboard.Shift) { if (!NoSelection) + { stb_key = ControlKeys.Shift | ControlKeys.LineEnd; + } } else if (Keyboard.Ctrl) { @@ -508,8 +698,11 @@ protected override void OnKeyDown(SDL.SDL_Keycode key, SDL.SDL_Keymod mod) { stb_key = ControlKeys.LineEnd; } + update_caret = true; + break; + case SDL.SDL_Keycode.SDLK_KP_ENTER: case SDL.SDL_Keycode.SDLK_RETURN: if (IsEditable) @@ -524,14 +717,28 @@ protected override void OnKeyDown(SDL.SDL_Keycode key, SDL.SDL_Keymod mod) else { Parent?.OnKeyboardReturn(0, Text); + + if (UIManager.SystemChat != null && UIManager.SystemChat.TextBoxControl != null && IsFocused) + { + if (!IsFromServer || !UIManager.SystemChat.TextBoxControl.IsVisible) + { + OnFocusLost(); + OnFocusEnter(); + } + else if (UIManager.KeyboardFocusControl == null || UIManager.KeyboardFocusControl != UIManager.SystemChat.TextBoxControl) + { + UIManager.SystemChat.TextBoxControl.SetKeyboardFocus(); + } + } } } + break; } if (stb_key != null) { - _stb.Key(stb_key.Value); + Stb.Key(stb_key.Value); } if (update_caret) @@ -550,13 +757,22 @@ public void SetText(string text) } else { - if (_maxCharCount >= 0 && text.Length > _maxCharCount) - text = text.Substring(0, _maxCharCount); + if (_maxCharCount > 0) + { + if (NumbersOnly) + { + // TODO ? + } + else if (text.Length > _maxCharCount) + { + text = text.Substring(0, _maxCharCount); + } + } - _stb.ClearState(!Multiline); + Stb.ClearState(!Multiline); Text = text; - _stb.CursorIndex = Length; + Stb.CursorIndex = Length; if (!_is_writing) { @@ -571,7 +787,7 @@ public void ClearText() { SelectionStart = 0; SelectionEnd = 0; - _stb.Delete(0, Length); + Stb.Delete(0, Length); if (!_is_writing) { @@ -582,31 +798,34 @@ public void ClearText() public void AppendText(string text) { - _stb.Paste(text); + Stb.Paste(text); } protected override void OnTextInput(string c) { if (c == null || !IsEditable) + { return; + } _is_writing = true; if (SelectionStart != SelectionEnd) { - _stb.DeleteSelection(); + Stb.DeleteSelection(); } int count; - if (_maxCharCount >= 0) + if (_maxCharCount > 0) { int remains = _maxCharCount - Length; if (remains <= 0) { _is_writing = false; + return; } @@ -631,6 +850,18 @@ protected override void OnTextInput(string c) if (!char.IsNumber(c[i])) { _is_writing = false; + + return; + } + } + + if (_maxCharCount > 0 && int.TryParse(Stb.text + c, out int val)) + { + if (val > _maxCharCount) + { + _is_writing = false; + SetText(_maxCharCount.ToString()); + return; } } @@ -639,14 +870,14 @@ protected override void OnTextInput(string c) if (count > 1) { - _stb.Paste(c); + Stb.Paste(c); OnTextChanged(); } else if (_rendererText.GetCharWidth(c[0]) > 0 || c[0] == '\n') { - _stb.InputChar(c[0]); + Stb.InputChar(c[0]); OnTextChanged(); - } + } } _is_writing = false; @@ -654,40 +885,32 @@ protected override void OnTextInput(string c) public override bool Draw(UltimaBatcher2D batcher, int x, int y) { - Rectangle scissor = ScissorStack.CalculateScissors(Matrix.Identity, x, y, Width, Height); - - if (ScissorStack.PushScissors(batcher.GraphicsDevice, scissor)) + if (batcher.ClipBegin(x, y, Width, Height)) { - batcher.EnableScissorTest(true); - base.Draw(batcher, x, y); - DrawSelection(batcher, x, y); - _rendererText.Draw(batcher, x, y); - DrawCaret(batcher, x, y); - batcher.EnableScissorTest(false); - ScissorStack.PopScissors(batcher.GraphicsDevice); + batcher.ClipEnd(); } - + return true; } - protected static readonly Color SELECTION_COLOR = new Color(0, 148, 216); - private protected void DrawSelection(UltimaBatcher2D batcher, int x, int y) { if (!AllowSelection) + { return; + } ResetHueVector(); - int selectStart = Math.Min(_stb.SelectStart, _stb.SelectEnd); - int selectEnd = Math.Max(_stb.SelectStart, _stb.SelectEnd); + int selectStart = Math.Min(Stb.SelectStart, Stb.SelectEnd); + int selectEnd = Math.Max(Stb.SelectStart, Stb.SelectEnd); - _hueVector.Z = 0.5f; + HueVector.Z = 0.5f; if (selectStart < selectEnd) { @@ -695,6 +918,7 @@ private protected void DrawSelection(UltimaBatcher2D batcher, int x, int y) int drawY = 1; int start = 0; + int diffX = _rendererText.Align != TEXT_ALIGN_TYPE.TS_LEFT ? _rendererText.GetCaretPosition(0).X - 1 : 0; while (info != null && selectStart < selectEnd) { @@ -705,6 +929,7 @@ private protected void DrawSelection(UltimaBatcher2D batcher, int x, int y) // calculate offset x int drawX = 0; + for (int i = 0; i < startSelectionIndex; i++) { drawX += _rendererText.GetCharWidth(info.Data[i].Item); @@ -723,26 +948,30 @@ private protected void DrawSelection(UltimaBatcher2D batcher, int x, int y) endX += _rendererText.GetCharWidth(info.Data[startSelectionIndex + k].Item); } - batcher.Draw2D( - Texture2DCache.GetTexture(SELECTION_COLOR), - x + drawX, - y + drawY, - endX, - info.MaxHeight + 1, - ref _hueVector); + batcher.Draw2D + ( + SolidColorTextureCache.GetTexture(SELECTION_COLOR), + x + drawX + diffX, + y + drawY, + endX, + info.MaxHeight + 1, + ref HueVector + ); break; } // do the whole line - batcher.Draw2D( - Texture2DCache.GetTexture(SELECTION_COLOR), - x + drawX, - y + drawY, - info.Width - drawX, - info.MaxHeight + 1, - ref _hueVector); + batcher.Draw2D + ( + SolidColorTextureCache.GetTexture(SELECTION_COLOR), + x + drawX + diffX, + y + drawY, + info.Width - drawX, + info.MaxHeight + 1, + ref HueVector + ); // first selection is gone. M selectStart = start + info.CharCount; @@ -771,16 +1000,19 @@ protected override void OnMouseDown(int x, int y, MouseButtonType button) if (button == MouseButtonType.Left && IsEditable) { if (!NoSelection) + { _leftWasDown = true; - _stb.Click(Mouse.Position.X, Mouse.Position.Y); + } + + Stb.Click(Mouse.Position.X, Mouse.Position.Y); UpdateCaretScreenPosition(); } if (UnityEngine.Application.isMobilePlatform && IsEditable && UserPreferences.DisableTouchscreenKeyboardOnMobile.CurrentValue == (int) PreferenceEnums.DisableTouchscreenKeyboardOnMobile.Off) { - //NOTE: Show touchscreen keyboard when abstract text box is selected - GameController.TouchScreenKeyboard = UnityEngine.TouchScreenKeyboard.Open(_stb.text, + // MobileUO: NOTE: Show touchscreen keyboard when abstract text box is selected + GameController.TouchScreenKeyboard = UnityEngine.TouchScreenKeyboard.Open(Stb.text, UnityEngine.TouchScreenKeyboardType.Default, false, Multiline, this is LoginGump.PasswordStbTextBox); } @@ -802,9 +1034,11 @@ protected override void OnMouseOver(int x, int y) base.OnMouseOver(x, y); if (!_leftWasDown) + { return; + } - _stb.Drag(Mouse.Position.X, Mouse.Position.Y); + Stb.Drag(Mouse.Position.X, Mouse.Position.Y); } public override void Dispose() @@ -815,19 +1049,29 @@ public override void Dispose() base.Dispose(); } - internal int TotalHeight + protected override bool OnMouseDoubleClick(int x, int y, MouseButtonType button) { - get + if (!NoSelection && CaretIndex < Text.Length && CaretIndex >= 0 && !char.IsWhiteSpace(Text[CaretIndex])) { - int h = 20; - var info = GetInfo(); - while (info != null) + int idx = CaretIndex; + + if (idx - 1 >= 0 && char.IsWhiteSpace(Text[idx - 1])) { - h += info.MaxHeight; - info = info.Next; + ++idx; } - return h; + + SelectionStart = Stb.MoveToPreviousWord(idx); + SelectionEnd = Stb.MoveToNextWord(idx); + + if (SelectionEnd < Text.Length) + { + --SelectionEnd; + } + + return true; } + + return base.OnMouseDoubleClick(x, y, button); } } } \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/WorldViewport.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Controls/WorldViewport.cs deleted file mode 100644 index 3b46a61ca..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Controls/WorldViewport.cs +++ /dev/null @@ -1,159 +0,0 @@ -#region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -#endregion - -using ClassicUO.Configuration; -using ClassicUO.Game.Managers; -using ClassicUO.Game.Scenes; -using ClassicUO.Input; -using ClassicUO.Renderer; - -using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; - -namespace ClassicUO.Game.UI.Controls -{ - internal class WorldViewport : Control - { - private readonly BlendState _darknessBlend = new BlendState - { - ColorSourceBlend = Blend.Zero, - ColorDestinationBlend = Blend.SourceColor, - - ColorBlendFunction = BlendFunction.Add - }; - - private readonly BlendState _altLightsBlend = new BlendState - { - ColorSourceBlend = Blend.DestinationColor, - ColorDestinationBlend = Blend.One, - - ColorBlendFunction = BlendFunction.Add - }; - - private readonly GameScene _scene; - - private XBREffect _xBR; - - public WorldViewport(GameScene scene, int x, int y, int width, int height) - { - X = x; - Y = y; - Width = width; - Height = height; - _scene = scene; - AcceptMouseInput = true; - } - - public override bool Draw(UltimaBatcher2D batcher, int x, int y) - { - if (_scene.ViewportTexture == null) - return false; - - Rectangle rectangle = ScissorStack.CalculateScissors(Matrix.Identity, x, y, Width, Height); - - if (ScissorStack.PushScissors(batcher.GraphicsDevice, rectangle)) - { - batcher.EnableScissorTest(true); - - ResetHueVector(); - - if (ProfileManager.Current != null && ProfileManager.Current.UseXBR) - { - // draw regular world - if (_xBR == null) - { - _xBR = new XBREffect(batcher.GraphicsDevice); - } - - _xBR.SetSize(_scene.ViewportTexture.Width, _scene.ViewportTexture.Height); - - batcher.End(); - - batcher.Begin(_xBR); - batcher.Draw2D(_scene.ViewportTexture, x, y, Width, Height, ref _hueVector); - batcher.End(); - - batcher.Begin(); - } - else - batcher.Draw2D(_scene.ViewportTexture, x, y, Width, Height, ref _hueVector); - - - // draw lights - // if (_scene.UseAltLights) - // { - // batcher.SetBlendState(_altLightsBlend); - // _hueVector.Z = 0.5f; - // batcher.Draw2D(_scene.LightRenderTarget, x, y, Width, Height, ref _hueVector); - // _hueVector.Z = 0; - // batcher.SetBlendState(null); - // } - // else if (_scene.UseLights) - // { - // batcher.SetBlendState(_darknessBlend); - // batcher.Draw2D(_scene.LightRenderTarget, x, y, Width, Height, ref _hueVector); - // batcher.SetBlendState(null); - // } - - // draw overheads - _scene.DrawSelection(batcher, x, y); - _scene.DrawOverheads(batcher, x, y); - - base.Draw(batcher, x, y); - - batcher.EnableScissorTest(false); - ScissorStack.PopScissors(batcher.GraphicsDevice); - } - - return true; - } - - - public override void Dispose() - { - _xBR?.Dispose(); - _darknessBlend?.Dispose(); - _altLightsBlend?.Dispose(); - base.Dispose(); - } - - protected override void OnMouseUp(int x, int y, MouseButtonType button) - { - if (!UIManager.IsMouseOverWorld && UIManager.MouseOverControl != null) - { - var p = UIManager.MouseOverControl.GetFirstControlAcceptKeyboardInput(); - p?.SetKeyboardFocus(); - } - else - { - if (UIManager.KeyboardFocusControl != UIManager.SystemChat.TextBoxControl) - { - UIManager.KeyboardFocusControl = UIManager.SystemChat.TextBoxControl; - } - - //if (!(UIManager.KeyboardFocusControl is TextBox tb && tb.Parent is WorldViewportGump)) - // Parent.GetFirstControlAcceptKeyboardInput()?.SetKeyboardFocus(); - } - - base.OnMouseUp(x, y, button); - } - } -} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ChatGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ChatGump.cs new file mode 120000 index 000000000..64cde6074 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ChatGump.cs @@ -0,0 +1 @@ +../../../../../../../external/ClassicUO/src/Game/UI/Gumps/ChatGump.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ChatGumpChooseName.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ChatGumpChooseName.cs new file mode 120000 index 000000000..f9751ad10 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ChatGumpChooseName.cs @@ -0,0 +1 @@ +../../../../../../../external/ClassicUO/src/Game/UI/Gumps/ChatGumpChooseName.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/CreditsGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/CreditsGump.cs new file mode 120000 index 000000000..2f2218bf6 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/CreditsGump.cs @@ -0,0 +1 @@ +../../../../../../../external/ClassicUO/src/Game/UI/Gumps/CreditsGump.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Gump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Gump.cs index 9747590aa..cd24f89e6 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Gump.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Gump.cs @@ -1,70 +1,51 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using System; using System.Collections.Generic; using System.IO; using System.Xml; - using ClassicUO.Game.GameObjects; using ClassicUO.Game.Managers; using ClassicUO.Game.UI.Controls; using ClassicUO.Renderer; using ClassicUO.Utility; - using Microsoft.Xna.Framework; namespace ClassicUO.Game.UI.Gumps { - enum GUMP_TYPE - { - NONE, - - GT_BUFF, - GT_CONTAINER, - GT_COUNTERBAR, - GT_HEALTHBAR, - GT_INFOBAR, - GT_JOURNAL, - GT_MACROBUTTON, - GT_MINIMAP, - GT_PAPERDOLL, - GT_SKILLMENU, - GT_SPELLBOOK, - GT_STATUSGUMP, - GT_TIPNOTICE, - GT_ABILITYBUTTON, - GT_SPELLBUTTON, - GT_SKILLBUTTON, - GT_RACIALBUTTON, - GT_WORLDMAP, - - GT_DEBUG, - GT_NETSTATS, - GT_ASSISTANTMACROBUTTON, - GT_ASSISTANTHOTKEYBUTTON - } - internal class Gump : Control { + // MobileUO: added variables private Button closeButton; public static bool CloseButtonsEnabled; @@ -76,12 +57,14 @@ public Gump(uint local, uint server) AcceptKeyboardInput = false; } - //NOTE: Make sure close button is always on top + // MobileUO: NOTE: Make sure close button is always on top + // MobileUO: added function protected override void OnChildAdded() { UpdateCloseButton(); } + // MobileUO: added function private void InitCloseButton() { if ((closeButton == null || closeButton.IsDisposed) && CloseButtonsEnabled && (CanCloseWithRightClick || CanCloseWithEsc)) @@ -94,6 +77,7 @@ private void InitCloseButton() } } + // MobileUO: added function public void UpdateCloseButton() { InitCloseButton(); @@ -111,9 +95,9 @@ public void UpdateCloseButton() public bool BlockMovement { get; set; } - public bool CanBeSaved => GumpType != GUMP_TYPE.NONE; + public bool CanBeSaved => GumpType != Gumps.GumpType.None; - public virtual GUMP_TYPE GumpType { get; } + public virtual GumpType GumpType { get; } public bool InvalidateContents { get; set; } @@ -124,7 +108,10 @@ public override bool CanMove set => base.CanMove = value; } - public override void Update(double totalMS, double frameMS) + public uint MasterGumpSerial { get; set; } + + + public override void Update(double totalTime, double frameTime) { if (InvalidateContents) { @@ -133,9 +120,11 @@ public override void Update(double totalMS, double frameMS) } if (ActivePage == 0) + { ActivePage = 1; + } - base.Update(totalMS, frameMS); + base.Update(totalTime, frameTime); } public override void Dispose() @@ -143,10 +132,13 @@ public override void Dispose() Item it = World.Items.Get(LocalSerial); if (it != null && it.Opened) + { it.Opened = false; + } base.Dispose(); + // MobileUO: added dispose if (closeButton != null && closeButton.IsDisposed == false) { closeButton.Dispose(); @@ -154,20 +146,10 @@ public override void Dispose() } } - public virtual void Save(BinaryWriter writer) - { - // the header - Type type = GetType(); - ushort typeLen = (ushort) type.FullName.Length; - writer.Write(typeLen); - writer.WriteUTF8String(type.FullName); - writer.Write(X); - writer.Write(Y); - } public virtual void Save(XmlTextWriter writer) { - writer.WriteAttributeString("type", ((int)GumpType).ToString()); + writer.WriteAttributeString("type", ((int) GumpType).ToString()); writer.WriteAttributeString("x", X.ToString()); writer.WriteAttributeString("y", Y.ToString()); writer.WriteAttributeString("serial", LocalSerial.ToString()); @@ -175,8 +157,12 @@ public virtual void Save(XmlTextWriter writer) public void SetInScreen() { - if (Bounds.Width >= 0 && Bounds.X <= Client.Game.Window.ClientBounds.Width && - Bounds.Height >= 0 && Bounds.Y <= Client.Game.Window.ClientBounds.Height) + Rectangle windowBounds = Client.Game.Window.ClientBounds; + Rectangle bounds = Bounds; + bounds.X += windowBounds.X; + bounds.Y += windowBounds.Y; + + if (windowBounds.Intersects(bounds)) { return; } @@ -185,21 +171,17 @@ public void SetInScreen() Y = 0; } - public virtual void Restore(BinaryReader reader) - { - } - public virtual void Restore(XmlElement xml) { - } public void RequestUpdateContents() - => InvalidateContents = true; + { + InvalidateContents = true; + } protected virtual void UpdateContents() { - } protected override void OnDragEnd(int x, int y) @@ -209,16 +191,25 @@ protected override void OnDragEnd(int x, int y) int halfHeight = Height - (Height >> 2); if (X < -halfWidth) + { position.X = -halfWidth; + } if (Y < -halfHeight) + { position.Y = -halfHeight; + } if (X > Client.Game.Window.ClientBounds.Width - (Width - halfWidth)) + { position.X = Client.Game.Window.ClientBounds.Width - (Width - halfWidth); + } if (Y > Client.Game.Window.ClientBounds.Height - (Height - halfHeight)) + { position.Y = Client.Game.Window.ClientBounds.Height - (Height - halfHeight); + } + Location = position; } @@ -229,7 +220,7 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) public override void OnButtonClick(int buttonID) { - if (!IsDisposed && LocalSerial != 0 && !SerialHelper.IsValidLocalGumpSerial(LocalSerial)) + if (!IsDisposed && LocalSerial != 0) { List switches = new List(); List> entries = new List>(); @@ -250,12 +241,24 @@ public override void OnButtonClick(int buttonID) } } - GameActions.ReplyGump(LocalSerial, ServerSerial, buttonID, switches.ToArray(), entries.ToArray()); + GameActions.ReplyGump + ( + LocalSerial, + // Seems like MasterGump serial does not work as expected. + /*MasterGumpSerial != 0 ? MasterGumpSerial :*/ ServerSerial, + buttonID, + switches.ToArray(), + entries.ToArray() + ); if (CanMove) + { UIManager.SavePosition(ServerSerial, Location); + } else + { UIManager.RemovePosition(ServerSerial); + } Dispose(); } @@ -264,10 +267,14 @@ public override void OnButtonClick(int buttonID) protected override void CloseWithRightClick() { if (!CanCloseWithRightClick) + { return; + } if (ServerSerial != 0) + { OnButtonClick(0); + } base.CloseWithRightClick(); } diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/GumpType.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/GumpType.cs new file mode 100644 index 000000000..05245cfb9 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/GumpType.cs @@ -0,0 +1,66 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +namespace ClassicUO.Game.UI.Gumps +{ + internal enum GumpType + { + None, + + Buff, + Container, + CounterBar, + HealthBar, + InfoBar, + Journal, + MacroButton, + MiniMap, + PaperDoll, + SkillMenu, + SpellBook, + StatusGump, + TipNotice, + AbilityButton, + SpellButton, + SkillButton, + RacialButton, + WorldMap, + + Debug, + NetStats, + + // MobileUO: Assisstant buttons + AssistantMacroButton, + AssistantHotkeyButton + + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Login/LoginGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Login/LoginGump.cs index 90b342324..483c5a472 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Login/LoginGump.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/Login/LoginGump.cs @@ -1,35 +1,45 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion -using System; -using System.IO; using ClassicUO.Configuration; using ClassicUO.Data; using ClassicUO.Game.Managers; using ClassicUO.Game.Scenes; using ClassicUO.Game.UI.Controls; +using ClassicUO.Input; using ClassicUO.IO.Resources; using ClassicUO.Renderer; +using ClassicUO.Resources; using ClassicUO.Utility; -using ClassicUO.Input; using Microsoft.Xna.Framework; using SDL2; @@ -42,8 +52,8 @@ internal class LoginGump : Gump private readonly Checkbox _checkboxAutologin; private readonly Checkbox _checkboxSaveAccount; private readonly Button _nextArrow0; - private readonly StbTextBox _textboxAccount; private readonly PasswordStbTextBox _passwordFake; + private readonly StbTextBox _textboxAccount; private float _time; @@ -64,85 +74,147 @@ public LoginGump(LoginScene scene) : base(0, 0) const ushort HUE = 0x0386; if (Client.Version >= ClientVersion.CV_500A) + { Add(new GumpPic(0, 0, 0x2329, 0)); + } //UO Flag Add(new GumpPic(0, 4, 0x15A0, 0) { AcceptKeyboardInput = false }); + // Quit Button - Add(new Button((int)Buttons.Quit, 0x1589, 0x158B, 0x158A) - { - X = 555, - Y = 4, - ButtonAction = ButtonAction.Activate - }); + Add + ( + new Button((int) Buttons.Quit, 0x1589, 0x158B, 0x158A) + { + X = 555, + Y = 4, + ButtonAction = ButtonAction.Activate + } + ); //Login Panel - Add(new ResizePic(0x13BE) - { - X = 128, - Y = 288, - Width = 451, - Height = 157 - }); + Add + ( + new ResizePic(0x13BE) + { + X = 128, + Y = 288, + Width = 451, + Height = 157 + } + ); if (Client.Version < ClientVersion.CV_500A) + { Add(new GumpPic(286, 45, 0x058A, 0)); + } - Add(new Label("Log in to Ultima Online", false, HUE, font: 2) - { - X = 253, - Y = 305 - }); + // Credits + Add + ( + new Button((int)Buttons.Credits, 0x1583, 0x1585, 0x1584) + { + X = 60, + Y = 385, + ButtonAction = ButtonAction.Activate + } + ); - Add(new Label("Account Name", false, HUE, font: 2) - { - X = 183, - Y = 345 - }); + Add + ( + new Label(ResGumps.LoginToUO, false, HUE, font: 2) + { + X = 253, + Y = 305 + } + ); - Add(new Label("Password", false, HUE, font: 2) - { - X = 183, - Y = 385 - }); + Add + ( + new Label(ResGumps.Account, false, HUE, font: 2) + { + X = 183, + Y = 345 + } + ); + + Add + ( + new Label(ResGumps.Password, false, HUE, font: 2) + { + X = 183, + Y = 385 + } + ); // Arrow Button - Add(_nextArrow0 = new Button((int) Buttons.NextArrow, 0x15A4, 0x15A6, 0x15A5) - { - X = 610, - Y = 445, - ButtonAction = ButtonAction.Activate - }); + Add + ( + _nextArrow0 = new Button((int) Buttons.NextArrow, 0x15A4, 0x15A6, 0x15A5) + { + X = 610, + Y = 445, + ButtonAction = ButtonAction.Activate + } + ); offsetX = 328; offsetY = 343; offtextY = 40; - Add(new Label($"UO Version {Settings.GlobalSettings.ClientVersion}.", false, 0x034E, font: 9) - { - X = 286, - Y = 453 - }); - - Add(new Label($"ClassicUO Version {CUOEnviroment.Version}", false, 0x034E, font: 9) - { - X = 286, - Y = 465 - }); - - - Add(_checkboxAutologin = new Checkbox(0x00D2, 0x00D3, "Autologin", 1, 0x0386, false) - { - X = 150, - Y = 417 - }); + Add + ( + new Label($"UO Version {Settings.GlobalSettings.ClientVersion}.", false, 0x034E, font: 9) + { + X = 286, + Y = 453 + } + ); - Add(_checkboxSaveAccount = new Checkbox(0x00D2, 0x00D3, "Save Account", 1, 0x0386, false) - { - X = _checkboxAutologin.X + _checkboxAutologin.Width + 10, - Y = 417 - }); + Add + ( + new Label(string.Format(ResGumps.CUOVersion0, CUOEnviroment.Version), false, 0x034E, font: 9) + { + X = 286, + Y = 465 + } + ); + + + Add + ( + _checkboxAutologin = new Checkbox + ( + 0x00D2, + 0x00D3, + ResGumps.Autologin, + 1, + 0x0386, + false + ) + { + X = 150, + Y = 417 + } + ); + + Add + ( + _checkboxSaveAccount = new Checkbox + ( + 0x00D2, + 0x00D3, + ResGumps.SaveAccount, + 1, + 0x0386, + false + ) + { + X = _checkboxAutologin.X + _checkboxAutologin.Width + 10, + Y = 417 + } + ); font = 1; hue = 0x0386; @@ -155,96 +227,167 @@ public LoginGump(LoginScene scene) : base(0, 0) Add(new GumpPic(0, 0, 0x014E, 0)); //// Quit Button - Add(new Button((int) Buttons.Quit, 0x05CA, 0x05C9, 0x05C8) - { - X = 25, - Y = 240, - ButtonAction = ButtonAction.Activate - }); + Add + ( + new Button((int) Buttons.Quit, 0x05CA, 0x05C9, 0x05C8) + { + X = 25, + Y = 240, + ButtonAction = ButtonAction.Activate + } + ); + + //// Credit Button + Add + ( + new Button((int)Buttons.Credits, 0x05D0, 0x05CF, 0x5CE) + { + X = 530, + Y = 125, + ButtonAction = ButtonAction.Activate + } + ); // Arrow Button - Add(_nextArrow0 = new Button((int) Buttons.NextArrow, 0x5CD, 0x5CC, 0x5CB) - { - X = 280, - Y = 365, - ButtonAction = ButtonAction.Activate - }); + Add + ( + _nextArrow0 = new Button((int) Buttons.NextArrow, 0x5CD, 0x5CC, 0x5CB) + { + X = 280, + Y = 365, + ButtonAction = ButtonAction.Activate + } + ); offsetX = 218; offsetY = 283; offtextY = 50; - Add(new Label($"UO Version {Settings.GlobalSettings.ClientVersion}.", false, 0x0481, font: 9) - { - X = 286, - Y = 453 - }); - - Add(new Label($"ClassicUO Version {CUOEnviroment.Version}", false, 0x0481, font: 9) - { - X = 286, - Y = 465 - }); - - - Add(_checkboxAutologin = new Checkbox(0x00D2, 0x00D3, "Autologin", 9, 0x0481, false) - { - X = 150, - Y = 417 - }); + Add + ( + new Label($"UO Version {Settings.GlobalSettings.ClientVersion}.", false, 0x0481, font: 9) + { + X = 286, + Y = 453 + } + ); - Add(_checkboxSaveAccount = new Checkbox(0x00D2, 0x00D3, "Save Account", 9, 0x0481, false) - { - X = _checkboxAutologin.X + _checkboxAutologin.Width + 10, - Y = 417 - }); + Add + ( + new Label(string.Format(ResGumps.CUOVersion0, CUOEnviroment.Version), false, 0x0481, font: 9) + { + X = 286, + Y = 465 + } + ); + + + Add + ( + _checkboxAutologin = new Checkbox + ( + 0x00D2, + 0x00D3, + ResGumps.Autologin, + 9, + 0x0481, + false + ) + { + X = 150, + Y = 417 + } + ); + + Add + ( + _checkboxSaveAccount = new Checkbox + ( + 0x00D2, + 0x00D3, + ResGumps.SaveAccount, + 9, + 0x0481, + false + ) + { + X = _checkboxAutologin.X + _checkboxAutologin.Width + 10, + Y = 417 + } + ); font = 9; hue = 0x0481; } - //Upscale arrow button on mobile + // MobileUO: Upscale arrow button on mobile UpscaleNextArrow(); // Account Text Input Background - Add(new ResizePic(0x0BB8) - { - X = offsetX, - Y = offsetY, - Width = 210, - Height = 30 - }); + Add + ( + new ResizePic(0x0BB8) + { + X = offsetX, + Y = offsetY, + Width = 210, + Height = 30 + } + ); // Password Text Input Background - Add(new ResizePic(0x0BB8) - { - X = offsetX, - Y = offsetY + offtextY, - Width = 210, - Height = 30 - }); + Add + ( + new ResizePic(0x0BB8) + { + X = offsetX, + Y = offsetY + offtextY, + Width = 210, + Height = 30 + } + ); offsetX += 7; // Text Inputs - Add(_textboxAccount = new StbTextBox(5, 16, 190, false, hue: 0x034F) - { - X = offsetX, - Y = offsetY, - Width = 190, - Height = 25, - }); + Add + ( + _textboxAccount = new StbTextBox + ( + 5, + 16, + 190, + false, + hue: 0x034F + ) + { + X = offsetX, + Y = offsetY, + Width = 190, + Height = 25 + } + ); _textboxAccount.SetText(Settings.GlobalSettings.Username); - Add(_passwordFake = new PasswordStbTextBox(5, 16, 190, false, hue: 0x034F) - { - X = offsetX, - Y = offsetY + offtextY + 2, - Width = 190, - Height = 25, - }); + Add + ( + _passwordFake = new PasswordStbTextBox + ( + 5, + 16, + 190, + false, + hue: 0x034F + ) + { + X = offsetX, + Y = offsetY + offtextY + 2, + Width = 190, + Height = 25 + } + ); _passwordFake.RealText = Crypter.Decrypt(Settings.GlobalSettings.Password); @@ -257,41 +400,103 @@ public LoginGump(LoginScene scene) : base(0, 0) //Add(new NiceButton(){ }); - // Add(new HtmlControl(htmlX, htmlY, 150, 15, - // false, false, - // false, - // text: "Click to donate PayPal", - // 0x32, true, isunicode: true, style: FontStyle.BlackBorder)); - // Add(new HtmlControl(htmlX, htmlY + 20, 150, 15, - // false, false, - // false, - // text: "Become a Patreon!", - // 0x32, true, isunicode: true, style: FontStyle.BlackBorder)); - - - Add(new HtmlControl(505, htmlY, 100, 15, - false, false, - false, - text: "Website", - 0x32, true, isunicode: true, style: FontStyle.BlackBorder)); - - Add(new HtmlControl(505, htmlY + 19, 100, 15, - false, false, - false, - text: "Join Discord", - 0x32, true, isunicode: true, style: FontStyle.BlackBorder)); - - - - var loginmusic_checkbox = new Checkbox(0x00D2, 0x00D3, "Music", font, hue, false) + // MobileUO: commented out + //Add + //( + // new HtmlControl + // ( + // htmlX, htmlY, 150, 15, + // false, false, + // false, + // "Click to donate PayPal", + // 0x32, true, isunicode: true, style: FontStyle.BlackBorder + // ) + //); + + //Add + //( + // new HtmlControl + // ( + // htmlX, htmlY + 20, 150, 15, + // false, false, + // false, + // "Become a Patreon!", + // 0x32, true, isunicode: true, style: FontStyle.BlackBorder + // ) + //); + + + Add + ( + new HtmlControl + ( + 505, + htmlY, + 100, + 15, + false, + false, + false, + "Website", + 0x32, + true, + isunicode: true, + style: FontStyle.BlackBorder + ) + ); + + Add + ( + new HtmlControl + ( + 505, + htmlY + 19, + 100, + 15, + false, + false, + false, + "Join Discord", + 0x32, + true, + isunicode: true, + style: FontStyle.BlackBorder + ) + ); + + + Checkbox loginmusic_checkbox = new Checkbox + ( + 0x00D2, + 0x00D3, + "Music", + font, + hue, + false + ) { X = _checkboxSaveAccount.X + _checkboxSaveAccount.Width + 10, Y = 417, - IsChecked = Settings.GlobalSettings.LoginMusic, + IsChecked = Settings.GlobalSettings.LoginMusic }; + Add(loginmusic_checkbox); - var login_music = new HSliderBar(loginmusic_checkbox.X + loginmusic_checkbox.Width + 10, loginmusic_checkbox.Y + 4, 80, 0, 100, Settings.GlobalSettings.LoginMusicVolume, HSliderBarStyle.MetalWidgetRecessedBar, true, font, hue, unicode: false); + HSliderBar login_music = new HSliderBar + ( + loginmusic_checkbox.X + loginmusic_checkbox.Width + 10, + loginmusic_checkbox.Y + 4, + 80, + 0, + 100, + Settings.GlobalSettings.LoginMusicVolume, + HSliderBarStyle.MetalWidgetRecessedBar, + true, + font, + hue, + false + ); + Add(login_music); login_music.IsVisible = Settings.GlobalSettings.LoginMusic; @@ -302,6 +507,7 @@ public LoginGump(LoginScene scene) : base(0, 0) login_music.IsVisible = Settings.GlobalSettings.LoginMusic; }; + login_music.ValueChanged += (sender, e) => { Settings.GlobalSettings.LoginMusicVolume = login_music.Value; @@ -310,11 +516,16 @@ public LoginGump(LoginScene scene) : base(0, 0) if (!string.IsNullOrEmpty(_textboxAccount.Text)) + { _passwordFake.SetKeyboardFocus(); + } else + { _textboxAccount.SetKeyboardFocus(); + } } + // MobileUO: added method private void UpscaleNextArrow() { //We use a size threshold because for some servers or client versions, the next arrow is actually a Login @@ -329,14 +540,15 @@ private void UpscaleNextArrow() } } - public override void OnKeyboardReturn(int textID, string text) { SaveCheckboxStatus(); LoginScene ls = Client.Game.GetScene(); if (ls.CurrentLoginStep == LoginSteps.Main) + { ls.Connect(_textboxAccount.Text, _passwordFake.RealText); + } } private void SaveCheckboxStatus() @@ -345,37 +557,48 @@ private void SaveCheckboxStatus() Settings.GlobalSettings.AutoLogin = _checkboxAutologin.IsChecked; } - public override void Update(double totalMS, double frameMS) + public override void Update(double totalTime, double frameTime) { if (IsDisposed) + { return; + } - base.Update(totalMS, frameMS); + base.Update(totalTime, frameTime); - if (_time < totalMS) + if (_time < totalTime) { - _time = (float) totalMS + 1000; + _time = (float) totalTime + 1000; + _nextArrow0.ButtonGraphicNormal = _nextArrow0.ButtonGraphicNormal == _buttonNormal ? _buttonOver : _buttonNormal; - - //Setting ButtonGraphicNormal resets the button's Width and Height so we need to apply the upscaling again + + // MobileUO: Setting ButtonGraphicNormal resets the button's Width and Height so we need to apply the upscaling again UpscaleNextArrow(); } if (_passwordFake.HasKeyboardFocus) { if (_passwordFake.Hue != 0x0021) + { _passwordFake.Hue = 0x0021; + } } else if (_passwordFake.Hue != 0) + { _passwordFake.Hue = 0; + } if (_textboxAccount.HasKeyboardFocus) { if (_textboxAccount.Hue != 0x0021) + { _textboxAccount.Hue = 0x0021; + } } else if (_textboxAccount.Hue != 0) + { _textboxAccount.Hue = 0; + } } public override void OnButtonClick(int buttonID) @@ -384,36 +607,97 @@ public override void OnButtonClick(int buttonID) { case Buttons.NextArrow: SaveCheckboxStatus(); + if (!_textboxAccount.IsDisposed) + { Client.Game.GetScene().Connect(_textboxAccount.Text, _passwordFake.RealText); + } break; case Buttons.Quit: Client.Game.Exit(); + break; + + case Buttons.Credits: + UIManager.Add(new CreditsGump()); + break; } } + // MobileUO: made public public class PasswordStbTextBox : StbTextBox { - public PasswordStbTextBox(byte font, int max_char_count = -1, int maxWidth = 0, bool isunicode = true, FontStyle style = FontStyle.None, ushort hue = 0, TEXT_ALIGN_TYPE align = TEXT_ALIGN_TYPE.TS_LEFT) : base(font, max_char_count, maxWidth, isunicode, style, hue, align) + private Point _caretScreenPosition; + private readonly RenderedText _rendererCaret; + + private readonly RenderedText _rendererText; + + public PasswordStbTextBox + ( + byte font, + int max_char_count = -1, + int maxWidth = 0, + bool isunicode = true, + FontStyle style = FontStyle.None, + ushort hue = 0, + TEXT_ALIGN_TYPE align = TEXT_ALIGN_TYPE.TS_LEFT + ) : base + ( + font, + max_char_count, + maxWidth, + isunicode, + style, + hue, + align + ) { - _rendererText = RenderedText.Create(string.Empty, hue, font, isunicode, style, align, maxWidth, 30, false, false, false); - _rendererCaret = RenderedText.Create("_", hue, font, isunicode, (style & FontStyle.BlackBorder) != 0 ? FontStyle.BlackBorder : FontStyle.None, align: align); + _rendererText = RenderedText.Create + ( + string.Empty, + hue, + font, + isunicode, + style, + align, + maxWidth + ); + + _rendererCaret = RenderedText.Create + ( + "_", + hue, + font, + isunicode, + (style & FontStyle.BlackBorder) != 0 ? FontStyle.BlackBorder : FontStyle.None, + align + ); + NoSelection = true; } internal string RealText { - get - { - return Text; - } + get => Text; + set => SetText(value); + } + + public ushort Hue + { + get => _rendererText.Hue; set { - SetText(value); + if (_rendererText.Hue != value) + { + _rendererText.Hue = value; + _rendererCaret.Hue = value; + + _rendererText.CreateTexture(); + _rendererCaret.CreateTexture(); + } } } @@ -425,10 +709,10 @@ protected override void DrawCaret(UltimaBatcher2D batcher, int x, int y) } } - private Point _caretScreenPosition; protected override void OnMouseDown(int x, int y, MouseButtonType button) { base.OnMouseDown(x, y, button); + if (button == MouseButtonType.Left) { UpdateCaretScreenPosition(); @@ -449,33 +733,22 @@ public override void Dispose() base.Dispose(); } - public ushort Hue - { - get => _rendererText.Hue; - set - { - if (_rendererText.Hue != value) - { - _rendererText.Hue = value; - _rendererCaret.Hue = value; - - _rendererText.CreateTexture(); - _rendererCaret.CreateTexture(); - } - } - } - protected override void OnTextInput(string c) { - base.OnTextInput(c); + base.OnTextInput(c); } protected override void OnTextChanged() { if (Text.Length > 0) + { _rendererText.Text = new string('*', Text.Length); + } else + { _rendererText.Text = string.Empty; + } + base.OnTextChanged(); UpdateCaretScreenPosition(); } @@ -492,10 +765,16 @@ private void UpdateCaretScreenPosition() _caretScreenPosition = _rendererText.GetCaretPosition(Stb.CursorIndex); } - private RenderedText _rendererText, _rendererCaret; public override bool Draw(UltimaBatcher2D batcher, int x, int y) { - Rectangle scissor = ScissorStack.CalculateScissors(Matrix.Identity, x, y, Width, Height); + Rectangle scissor = ScissorStack.CalculateScissors + ( + Matrix.Identity, + x, + y, + Width, + Height + ); if (ScissorStack.PushScissors(batcher.GraphicsDevice, scissor)) { @@ -514,11 +793,12 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) } } - + // MobileUO: made public public enum Buttons { NextArrow, - Quit + Quit, + Credits } } } \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/MacroGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/MacroGump.cs new file mode 120000 index 000000000..9d42d1c0b --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/MacroGump.cs @@ -0,0 +1 @@ +../../../../../../../external/ClassicUO/src/Game/UI/Gumps/MacroGump.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ModernBookGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ModernBookGump.cs index 32dbf1470..bf49a5bc3 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ModernBookGump.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ModernBookGump.cs @@ -1,10 +1,38 @@ -using System; +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Diagnostics.Tracing; -using System.Linq; using System.Text; -using System.Text.RegularExpressions; using ClassicUO.Data; using ClassicUO.Game.Managers; using ClassicUO.Game.UI.Controls; @@ -12,11 +40,9 @@ using ClassicUO.IO.Resources; using ClassicUO.Network; using ClassicUO.Renderer; -using ClassicUO.Utility.Logging; +using ClassicUO.Resources; using Microsoft.Xna.Framework; -using SDL2; - namespace ClassicUO.Game.UI.Gumps { internal class ModernBookGump : Gump @@ -27,14 +53,20 @@ internal class ModernBookGump : Gump private const int RIGHT_X = 223; private const int UPPER_MARGIN = 34; private const int PAGE_HEIGHT = 166; - internal string[] BookLines => _bookPage._pageLines; - internal bool[] _pagesChanged => _bookPage._pagesChanged; + private StbPageTextBox _bookPage; private GumpPic _forwardGumpPic, _backwardGumpPic; private StbTextBox _titleTextBox, _authorTextBox; - private StbPageTextBox _bookPage; - public ModernBookGump(uint serial, ushort page_count, string title, string author, bool is_editable, bool old_packet) : base(serial, 0) + public ModernBookGump + ( + uint serial, + ushort page_count, + string title, + string author, + bool is_editable, + bool old_packet + ) : base(serial, 0) { CanMove = true; AcceptMouseInput = true; @@ -46,28 +78,53 @@ public ModernBookGump(uint serial, ushort page_count, string title, string autho BuildGump(title, author); } + internal string[] BookLines => _bookPage._pageLines; + internal bool[] _pagesChanged => _bookPage._pagesChanged; + public ushort BookPageCount { get; internal set; } + public HashSet KnownPages { get; internal set; } = new HashSet(); public static bool IsNewBook => Client.Version > ClientVersion.CV_200; public bool UseNewHeader { get; set; } = true; - public static byte DefaultFont => (byte)(IsNewBook ? 1 : 4); + public static byte DefaultFont => (byte) (IsNewBook ? 1 : 4); + + public bool IntroChanges => _pagesChanged[0]; + internal int MaxPage => (BookPageCount >> 1) + 1; internal void ServerSetBookText() { if (BookLines == null || BookLines.Length <= 0) + { return; + } + StringBuilder sb = new StringBuilder(); int sw = _bookPage.renderedText.GetCharWidth(' '); + for (int i = 0, l = BookLines.Length; i < l; i++) { - int w = (IsNewBook ? FontsLoader.Instance.GetWidthUnicode(_bookPage.renderedText.Font, BookLines[i]) : FontsLoader.Instance.GetWidthASCII(_bookPage.renderedText.Font, BookLines[i])); + if (BookLines[i] != null && BookLines[i].Contains("\n")) + { + BookLines[i] = BookLines[i].Replace("\n", ""); + } + } + + for (int i = 0, l = BookLines.Length; i < l; i++) + { + int w = IsNewBook ? FontsLoader.Instance.GetWidthUnicode(_bookPage.renderedText.Font, BookLines[i]) : FontsLoader.Instance.GetWidthASCII(_bookPage.renderedText.Font, BookLines[i]); + sb.Append(BookLines[i]); - if (i + 1 < l && (string.IsNullOrWhiteSpace(BookLines[i]) || w + sw < _bookPage.renderedText.MaxWidth)) + + if (BookLines[i] == null) + continue; + + if (i + 1 < l && (string.IsNullOrWhiteSpace(BookLines[i]) && !BookLines[i].Contains("\n") || w + sw < _bookPage.renderedText.MaxWidth)) { sb.Append('\n'); BookLines[i] += '\n'; } } + _bookPage._ServerUpdate = true; _bookPage.SetText(sb.ToString()); _bookPage.CaretIndex = 0; @@ -75,17 +132,18 @@ internal void ServerSetBookText() _bookPage._ServerUpdate = false; } - public bool IntroChanges => _pagesChanged[0]; - internal int MaxPage => (BookPageCount >> 1) + 1; - private void BuildGump(string title, string author) { CanCloseWithRightClick = true; - Add(new GumpPic(0, 0, 0x1FE, 0) - { - CanMove = true - }); + + Add + ( + new GumpPic(0, 0, 0x1FE, 0) + { + CanMove = true + } + ); Add(_backwardGumpPic = new GumpPic(0, 0, 0x1FF, 0)); @@ -94,28 +152,46 @@ private void BuildGump(string title, string author) _forwardGumpPic.MouseUp += (sender, e) => { if (e.Button == MouseButtonType.Left && sender is Control ctrl) + { SetActivePage(ActivePage + 1); + } }; _forwardGumpPic.MouseDoubleClick += (sender, e) => { if (e.Button == MouseButtonType.Left && sender is Control ctrl) + { SetActivePage(MaxPage); + } }; _backwardGumpPic.MouseUp += (sender, e) => { if (e.Button == MouseButtonType.Left && sender is Control ctrl) + { SetActivePage(ActivePage - 1); + } }; _backwardGumpPic.MouseDoubleClick += (sender, e) => { if (e.Button == MouseButtonType.Left && sender is Control ctrl) + { SetActivePage(1); + } }; - _bookPage = new StbPageTextBox(DefaultFont, BookPageCount, this, MAX_BOOK_CHARS_PER_LINE * MAX_BOOK_LINES * BookPageCount, 156, IsNewBook, FontStyle.ExtraHeight, 2) + _bookPage = new StbPageTextBox + ( + DefaultFont, + BookPageCount, + this, + MAX_BOOK_CHARS_PER_LINE * MAX_BOOK_LINES * BookPageCount, + 156, + IsNewBook, + FontStyle.ExtraHeight, + 2 + ) { X = 0, Y = 0, @@ -124,37 +200,57 @@ private void BuildGump(string title, string author) IsEditable = IsEditable, Multiline = true }; - Add(_titleTextBox = new StbTextBox(DefaultFont, 47, 150, IsNewBook, FontStyle.None, 0) - { - X = 40, - Y = 60, - Height = 25, - Width = 155, - IsEditable = IsEditable, - }, 1); + + Add + ( + _titleTextBox = new StbTextBox(DefaultFont, 47, 150, IsNewBook) + { + X = 40, + Y = 60, + Height = 25, + Width = 155, + IsEditable = IsEditable + }, + 1 + ); + _titleTextBox.SetText(title); _titleTextBox.TextChanged += PageZero_TextChanged; - Add(new Label("by", true, 1) { X = 40, Y = 130 }, 1); - Add(_authorTextBox = new StbTextBox(DefaultFont, 29, 150, IsNewBook, FontStyle.None, 0) - { - X = 40, - Y = 160, - Height = 25, - Width = 155, - IsEditable = IsEditable, - }, 1); + Add(new Label(ResGumps.By, true, 1) { X = 40, Y = 130 }, 1); + + Add + ( + _authorTextBox = new StbTextBox(DefaultFont, 29, 150, IsNewBook) + { + X = 40, + Y = 160, + Height = 25, + Width = 155, + IsEditable = IsEditable + }, + 1 + ); + _authorTextBox.SetText(author); _authorTextBox.TextChanged += PageZero_TextChanged; for (int k = 1, x = 38; k <= BookPageCount; k++) { if (k % 2 == 1) - x = 223;//right hand page + { + x = 223; //right hand page + } else + { x = 38; + } + int page = k + 1; + if (page % 2 == 1) + { page += 1; + } page >>= 1; Add(new Label(k.ToString(), true, 1) { X = x + 80, Y = 200 }, page); @@ -205,23 +301,27 @@ public void SetAuthor(string author, bool editable) private void SetActivePage(int page) { page = Math.Min(Math.Max(page, 1), MaxPage); //clamp the value between 1..MaxPage + if (page != ActivePage) { Client.Game.Scene.Audio.PlaySound(0x0055); } - //Non-editable books only have page data sent for currently viewed pages + //Non-editable books may only have data for the currently displayed pages, + //but some servers send their entire contents in one go so we need to keep track of which pages we know if (!IsEditable) { int leftPage = (page - 1) << 1; int rightPage = leftPage + 1; - if (leftPage > 0) + + if (leftPage > 0 && !KnownPages.Contains(leftPage)) { - NetClient.Socket.Send(new PBookPageDataRequest(LocalSerial, (ushort)leftPage)); + NetClient.Socket.Send_BookPageDataRequest(LocalSerial, (ushort)leftPage); } - if (leftPage + 1 < MaxPage * 2) + + if (rightPage < MaxPage * 2 && !KnownPages.Contains(rightPage)) { - NetClient.Socket.Send(new PBookPageDataRequest(LocalSerial, (ushort)rightPage)); + NetClient.Socket.Send_BookPageDataRequest(LocalSerial, (ushort)rightPage); } } else @@ -231,21 +331,28 @@ private void SetActivePage(int page) if (_pagesChanged[i]) { _pagesChanged[i] = false; + if (i < 1) { if (UseNewHeader) - NetClient.Socket.Send(new PBookHeaderChanged(LocalSerial, _titleTextBox.Text, _authorTextBox.Text)); + { + NetClient.Socket.Send_BookHeaderChanged(LocalSerial, _titleTextBox.Text, _authorTextBox.Text); + } else - NetClient.Socket.Send(new PBookHeaderChangedOld(LocalSerial, _titleTextBox.Text, _authorTextBox.Text)); + { + NetClient.Socket.Send_BookHeaderChanged_Old(LocalSerial, _titleTextBox.Text, _authorTextBox.Text); + } } else { string[] text = new string[MAX_BOOK_LINES]; - for (int x = (i - 1) * MAX_BOOK_LINES, l = 0; x < ((i - 1) * MAX_BOOK_LINES) + 8; x++, l++) + + for (int x = (i - 1) * MAX_BOOK_LINES, l = 0; x < (i - 1) * MAX_BOOK_LINES + 8; x++, l++) { text[l] = BookLines[x]; } - NetClient.Socket.Send(new PBookPageData(LocalSerial, text, i)); + + NetClient.Socket.Send_BookPageData(LocalSerial, text, i); } } } @@ -253,7 +360,8 @@ private void SetActivePage(int page) ActivePage = page; UpdatePageButtonVisibility(); - if (UIManager.KeyboardFocusControl == null || (UIManager.KeyboardFocusControl != UIManager.SystemChat.TextBoxControl && UIManager.KeyboardFocusControl != _bookPage && page != (_bookPage._focusPage / 2 + 1))) + + if (UIManager.KeyboardFocusControl == null || UIManager.KeyboardFocusControl != UIManager.SystemChat.TextBoxControl && UIManager.KeyboardFocusControl != _bookPage && page != _bookPage._focusPage / 2 + 1) { UIManager.SystemChat.TextBoxControl.SetKeyboardFocus(); } @@ -261,7 +369,6 @@ private void SetActivePage(int page) public override void OnButtonClick(int buttonID) { - } protected override void CloseWithRightClick() @@ -274,45 +381,98 @@ protected override void CloseWithRightClick() public override bool Draw(UltimaBatcher2D batcher, int x, int y) { base.Draw(batcher, x, y); - Rectangle scissor = ScissorStack.CalculateScissors(Matrix.Identity, x, y, Width, Height); - if (ScissorStack.PushScissors(batcher.GraphicsDevice, scissor)) + + if (batcher.ClipBegin(x, y, Width, Height)) { - batcher.EnableScissorTest(true); - var t = _bookPage.renderedText; + RenderedText t = _bookPage.renderedText; int startpage = (ActivePage - 1) * 2; + if (startpage < BookPageCount) { int poy = _bookPage._pageCoords[startpage, 0], phy = _bookPage._pageCoords[startpage, 1]; - _bookPage.DrawSelection(batcher, x + RIGHT_X, y + UPPER_MARGIN, poy, poy + phy); - t.Draw(batcher, x + RIGHT_X, y + UPPER_MARGIN, 0, poy, t.Width, phy); + + _bookPage.DrawSelection + ( + batcher, + x + RIGHT_X, + y + UPPER_MARGIN, + poy, + poy + phy + ); + + t.Draw + ( + batcher, + x + RIGHT_X, + y + UPPER_MARGIN, + 0, + poy, + t.Width, + phy + ); + if (startpage == _bookPage._caretPage) { if (_bookPage._caretPos.Y < poy + phy) { if (_bookPage._caretPos.Y >= poy) { - if(_bookPage.HasKeyboardFocus) - _bookPage.renderedCaret.Draw(batcher, _bookPage._caretPos.X + x + RIGHT_X, (_bookPage._caretPos.Y + y + UPPER_MARGIN) - poy, 0, 0, _bookPage.renderedCaret.Width, _bookPage.renderedCaret.Height); + if (_bookPage.HasKeyboardFocus) + { + _bookPage.renderedCaret.Draw + ( + batcher, + _bookPage._caretPos.X + x + RIGHT_X, + _bookPage._caretPos.Y + y + UPPER_MARGIN - poy, + 0, + 0, + _bookPage.renderedCaret.Width, + _bookPage.renderedCaret.Height + ); + } } else + { _bookPage._caretPage = _bookPage.GetCaretPage(); + } } else if (_bookPage._caretPos.Y <= _bookPage.Height) { if (_bookPage._caretPage + 2 < _bookPage._pagesChanged.Length) { _bookPage._focusPage = _bookPage._caretPage++; - SetActivePage((_bookPage._caretPage / 2) + 2); + SetActivePage(_bookPage._caretPage / 2 + 2); } } } } + startpage--; + if (startpage > 0) { int poy = _bookPage._pageCoords[startpage, 0], phy = _bookPage._pageCoords[startpage, 1]; - _bookPage.DrawSelection(batcher, x + LEFT_X, y + UPPER_MARGIN, poy, poy + phy); - t.Draw(batcher, x + LEFT_X, y + UPPER_MARGIN, 0, poy, t.Width, phy); + + _bookPage.DrawSelection + ( + batcher, + x + LEFT_X, + y + UPPER_MARGIN, + poy, + poy + phy + ); + + t.Draw + ( + batcher, + x + LEFT_X, + y + UPPER_MARGIN, + 0, + poy, + t.Width, + phy + ); + if (startpage == _bookPage._caretPage) { if (_bookPage._caretPos.Y < poy + phy) @@ -320,24 +480,38 @@ public override bool Draw(UltimaBatcher2D batcher, int x, int y) if (_bookPage._caretPos.Y >= poy) { if (_bookPage.HasKeyboardFocus) - _bookPage.renderedCaret.Draw(batcher, _bookPage._caretPos.X + x + LEFT_X, (_bookPage._caretPos.Y + y + UPPER_MARGIN) - poy, 0, 0, _bookPage.renderedCaret.Width, _bookPage.renderedCaret.Height); + { + _bookPage.renderedCaret.Draw + ( + batcher, + _bookPage._caretPos.X + x + LEFT_X, + _bookPage._caretPos.Y + y + UPPER_MARGIN - poy, + 0, + 0, + _bookPage.renderedCaret.Width, + _bookPage.renderedCaret.Height + ); + } } else if (_bookPage._caretPage > 0) { _bookPage._focusPage = _bookPage._caretPage--; - SetActivePage((_bookPage._caretPage / 2) + 1); + SetActivePage(_bookPage._caretPage / 2 + 1); } } else if (_bookPage._caretPos.Y <= _bookPage.Height) { if (_bookPage._caretPage + 2 < _bookPage._pagesChanged.Length) + { _bookPage._caretPage++; + } } } } - batcher.EnableScissorTest(false); - ScissorStack.PopScissors(batcher.GraphicsDevice); + + batcher.ClipEnd(); } + return true; } @@ -352,14 +526,16 @@ public override void OnHitTestSuccess(int x, int y, ref Control res) if (!IsDisposed) { int page = -1; + if (ActivePage > 1 && x >= LEFT_X + X && x <= LEFT_X + X + _bookPage.Width) { page = (ActivePage - 1) * 2 - 1; } - else if (ActivePage - 1 < (BookPageCount >> 1) && x >= RIGHT_X + X && x <= RIGHT_X + _bookPage.Width + X) + else if (ActivePage - 1 < BookPageCount >> 1 && x >= RIGHT_X + X && x <= RIGHT_X + _bookPage.Width + X) { page = (ActivePage - 1) * 2; } + if (page >= 0 && page < BookPageCount && y >= UPPER_MARGIN + Y && y <= UPPER_MARGIN + PAGE_HEIGHT + Y) { _bookPage._focusPage = page; @@ -370,20 +546,60 @@ public override void OnHitTestSuccess(int x, int y, ref Control res) private class StbPageTextBox : StbTextBox { - internal readonly int[,] _pageCoords; - internal readonly string[] _pageLines; - internal bool[] _pagesChanged; + private static readonly StringBuilder _sb = new StringBuilder(); + private static string[] _handler; + private readonly ModernBookGump _gump; + + public StbPageTextBox + ( + byte font, + int bookpages, + ModernBookGump gump, + int max_char_count = -1, + int maxWidth = 0, + bool isunicode = true, + FontStyle style = FontStyle.None, + ushort hue = 0 + ) : base + ( + font, + max_char_count, + maxWidth, + isunicode, + style, + hue + ) + { + _pageCoords = new int[bookpages, 2]; + _pageLines = new string[bookpages * MAX_BOOK_LINES]; + _pagesChanged = new bool[bookpages + 1]; + Priority = ClickPriority.High; + _gump = gump; + } + internal Point _caretPos => _caretScreenPosition; + + internal RenderedText renderedText => _rendererText; + internal RenderedText renderedCaret => _rendererCaret; internal int _caretPage, _focusPage; - private ModernBookGump _gump; + internal readonly int[,] _pageCoords; + internal readonly string[] _pageLines; + internal readonly bool[] _pagesChanged; + + internal bool _ServerUpdate; + internal int GetCaretPage() { Point p = _rendererText.GetCaretPosition(CaretIndex); + for (int i = 0, l = _pageCoords.GetLength(0); i < l; i++) { if (p.Y >= _pageCoords[i, 0] && p.Y < _pageCoords[i, 0] + _pageCoords[i, 1]) + { return i; + } } + return 0; } @@ -392,24 +608,36 @@ protected override void OnMouseDown(int x, int y, MouseButtonType button) if (button == MouseButtonType.Left) { if (IsEditable) + { SetKeyboardFocus(); + } + if (!NoSelection) + { _leftWasDown = true; + } + if (_focusPage >= 0 && _focusPage < _pageCoords.GetLength(0)) { if (_focusPage % 2 == 0) + { x -= RIGHT_X + _gump.X; + } else + { x -= LEFT_X + _gump.X; + } + y += _pageCoords[_focusPage, 0] - (UPPER_MARGIN + _gump.Y); } + Stb.Click(x, y); UpdateCaretScreenPosition(); _caretPage = GetCaretPage(); if (UnityEngine.Application.isMobilePlatform && IsEditable && UserPreferences.DisableTouchscreenKeyboardOnMobile.CurrentValue == (int) PreferenceEnums.DisableTouchscreenKeyboardOnMobile.Off) { - //NOTE: Show touchscreen keyboard when abstract text box is selected + // MobileUO: NOTE: Show touchscreen keyboard when abstract text box is selected GameController.TouchScreenKeyboard = UnityEngine.TouchScreenKeyboard.Open(Stb.text, UnityEngine.TouchScreenKeyboardType.Default, false, Multiline, false); } @@ -423,11 +651,17 @@ protected override void OnMouseOver(int x, int y) if (_focusPage >= 0 && _focusPage < _pageCoords.GetLength(0)) { if (_focusPage % 2 == 0) + { x -= RIGHT_X + _gump.X; + } else + { x -= LEFT_X + _gump.X; + } + y += _pageCoords[_focusPage, 0] - (UPPER_MARGIN + _gump.Y); } + Stb.Drag(x, y); } } @@ -437,50 +671,48 @@ protected override void OnMouseUp(int x, int y, MouseButtonType button) if (_focusPage >= 0 && _focusPage < _pageCoords.GetLength(0)) { if (_focusPage % 2 == 0) + { x -= RIGHT_X + _gump.X; + } else + { x -= LEFT_X + _gump.X; + } + y += _pageCoords[_focusPage, 0] - (UPPER_MARGIN + _gump.Y); } - base.OnMouseUp(x, y, button); - } - - internal RenderedText renderedText => _rendererText; - internal RenderedText renderedCaret => _rendererCaret; - public StbPageTextBox(byte font, int bookpages, ModernBookGump gump, int max_char_count = -1, int maxWidth = 0, bool isunicode = true, FontStyle style = FontStyle.None, ushort hue = 0) : base(font, max_char_count, maxWidth, isunicode, style, hue, TEXT_ALIGN_TYPE.TS_LEFT) - { - _pageCoords = new int[bookpages, 2]; - _pageLines = new string[bookpages * MAX_BOOK_LINES]; - _pagesChanged = new bool[bookpages + 1]; - Priority = ClickPriority.High; - _gump = gump; + base.OnMouseUp(x, y, button); } internal void UpdatePageCoords() { MultilinesFontInfo info = _rendererText.GetInfo(); + for (int page = 0, y = 0; page < _pageCoords.GetLength(0); page++) { _pageCoords[page, 0] = y; _pageCoords[page, 1] = 0; + for (int i = 0; i < MAX_BOOK_LINES; i++) { if (info == null) + { break; + } + _pageCoords[page, 1] += info.MaxHeight; info = info.Next; } + y += _pageCoords[page, 1]; } } - private static readonly StringBuilder _sb = new StringBuilder(); - internal void DrawSelection(UltimaBatcher2D batcher, int x, int y, int starty, int endy) { ResetHueVector(); - _hueVector.Z = 0.5f; + HueVector.Z = 0.5f; int selectStart = Math.Min(Stb.SelectStart, Stb.SelectEnd); int selectEnd = Math.Max(Stb.SelectStart, Stb.SelectEnd); @@ -501,6 +733,7 @@ internal void DrawSelection(UltimaBatcher2D batcher, int x, int y, int starty, i // calculate offset x int drawX = 0; + for (int i = 0; i < startSelectionIndex; i++) { drawX += _rendererText.GetCharWidth(info.Data[i].Item); @@ -520,13 +753,17 @@ internal void DrawSelection(UltimaBatcher2D batcher, int x, int y, int starty, i } if (drawY >= starty && drawY <= endy) - batcher.Draw2D( - Texture2DCache.GetTexture(SELECTION_COLOR), - x + drawX, - y + drawY - starty, - endX, - info.MaxHeight + 1, - ref _hueVector); + { + batcher.Draw2D + ( + SolidColorTextureCache.GetTexture(SELECTION_COLOR), + x + drawX, + y + drawY - starty, + endX, + info.MaxHeight + 1, + ref HueVector + ); + } break; } @@ -534,13 +771,17 @@ internal void DrawSelection(UltimaBatcher2D batcher, int x, int y, int starty, i // do the whole line if (drawY >= starty && drawY <= endy) - batcher.Draw2D( - Texture2DCache.GetTexture(SELECTION_COLOR), - x + drawX, - y + drawY - starty, - info.Width - drawX, - info.MaxHeight + 1, - ref _hueVector); + { + batcher.Draw2D + ( + SolidColorTextureCache.GetTexture(SELECTION_COLOR), + x + drawX, + y + drawY - starty, + info.Width - drawX, + info.MaxHeight + 1, + ref HueVector + ); + } // first selection is gone. M selectStart = start + info.CharCount; @@ -553,21 +794,24 @@ internal void DrawSelection(UltimaBatcher2D batcher, int x, int y, int starty, i } } - internal bool _ServerUpdate = false; - private static string[] _handler; protected override void OnTextChanged() { _is_writing = true; + if (!_ServerUpdate) { if (_handler == null || _handler.Length < _pageLines.Length) + { _handler = new string[_pageLines.Length]; + } + string[] split = Text.Split('\n'); + for (int i = 0, l = 0; i < split.Length && l < _pageLines.Length; i++) { if (split[i].Length > 0) { - for (int p = 0, w = 0, pw = _rendererText.GetCharWidth(split[i][p]); ; pw = _rendererText.GetCharWidth(split[i][p])) + for (int p = 0, w = 0, pw = _rendererText.GetCharWidth(split[i][p]);; pw = _rendererText.GetCharWidth(split[i][p])) { if (w + pw > _rendererText.MaxWidth) { @@ -576,18 +820,24 @@ protected override void OnTextChanged() l++; //CaretIndex++; w = 0; + if (l >= _pageLines.Length) + { break; + } } + w += pw; _sb.Append(split[i][p]); p++; + if (p >= split[i].Length) { _sb.Append('\n'); _handler[l] = _sb.ToString(); _sb.Clear(); l++; + break; } } @@ -599,11 +849,16 @@ protected override void OnTextChanged() //_sb.Append('\n'); } } + _sb.Clear(); + for (int i = 0; i < _pageLines.Length; i++) { if (!_pagesChanged[(i >> 3) + 1] && _handler[i] != _pageLines[i]) + { _pagesChanged[(i >> 3) + 1] = true; + } + _sb.Append(_pageLines[i] = _handler[i]); } @@ -611,6 +866,7 @@ protected override void OnTextChanged() _sb.Clear(); UpdatePageCoords(); } + base.OnTextChanged(); _is_writing = false; } @@ -618,10 +874,14 @@ protected override void OnTextChanged() protected override void CloseWithRightClick() { if (_gump != null && !_gump.IsDisposed) + { _gump.CloseWithRightClick(); + } else + { base.CloseWithRightClick(); + } } } } -} +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/NameOverheadGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/NameOverheadGump.cs deleted file mode 120000 index d48d72d51..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/NameOverheadGump.cs +++ /dev/null @@ -1 +0,0 @@ -../../../../../../../external/ClassicUO/src/Game/UI/Gumps/NameOverheadGump.cs \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/NameOverheadGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/NameOverheadGump.cs new file mode 100644 index 000000000..6f17e5842 --- /dev/null +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/NameOverheadGump.cs @@ -0,0 +1,644 @@ +#region license + +// Copyright (c) 2021, andreakarasho +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using ClassicUO.Configuration; +using ClassicUO.Game.Data; +using ClassicUO.Game.GameObjects; +using ClassicUO.Game.Managers; +using ClassicUO.Game.UI.Controls; +using ClassicUO.Input; +using ClassicUO.IO.Resources; +using ClassicUO.Renderer; +using ClassicUO.Utility; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; + +namespace ClassicUO.Game.UI.Gumps +{ + internal class NameOverheadGump : Gump + { + private AlphaBlendControl _background; + private Point _lockedPosition; + private bool _positionLocked; + private readonly RenderedText _renderedText; + private Texture2D _borderColor = SolidColorTextureCache.GetTexture(Color.Black); + + public NameOverheadGump(uint serial) : base(serial, 0) + { + CanMove = false; + AcceptMouseInput = true; + CanCloseWithRightClick = true; + + Entity entity = World.Get(serial); + + if (entity == null) + { + Dispose(); + + return; + } + + _renderedText = RenderedText.Create + ( + string.Empty, + entity is Mobile m ? Notoriety.GetHue(m.NotorietyFlag) : (ushort) 0x0481, + 0xFF, + true, + FontStyle.BlackBorder, + TEXT_ALIGN_TYPE.TS_CENTER, + 100, + 30, + true + ); + + SetTooltip(entity); + + BuildGump(); + } + + public bool SetName() + { + Entity entity = World.Get(LocalSerial); + + if (entity == null) + { + return false; + } + + if (entity is Item item) + { + if (!World.OPL.TryGetNameAndData(item, out string t, out _)) + { + t = StringHelper.CapitalizeAllWords(item.ItemData.Name); + + if (string.IsNullOrEmpty(t)) + { + t = ClilocLoader.Instance.GetString(1020000 + item.Graphic, true, t); + } + } + + if (string.IsNullOrEmpty(t)) + { + return false; + } + + if (!item.IsCorpse && item.Amount > 1) + { + t += ": " + item.Amount; + } + + FontsLoader.Instance.SetUseHTML(true); + FontsLoader.Instance.RecalculateWidthByInfo = true; + + + int width = FontsLoader.Instance.GetWidthUnicode(_renderedText.Font, t); + + if (width > Constants.OBJECT_HANDLES_GUMP_WIDTH) + { + t = FontsLoader.Instance.GetTextByWidthUnicode + ( + _renderedText.Font, + t.AsSpan(), + Constants.OBJECT_HANDLES_GUMP_WIDTH, + true, + TEXT_ALIGN_TYPE.TS_CENTER, + (ushort) FontStyle.BlackBorder + ); + + width = Constants.OBJECT_HANDLES_GUMP_WIDTH; + } + + _renderedText.MaxWidth = width; + _renderedText.Text = t; + + FontsLoader.Instance.RecalculateWidthByInfo = false; + FontsLoader.Instance.SetUseHTML(false); + + Width = _background.Width = Math.Max(60, _renderedText.Width) + 4; + Height = _background.Height = Constants.OBJECT_HANDLES_GUMP_HEIGHT + 4; + + WantUpdateSize = false; + + return true; + } + + if (!string.IsNullOrEmpty(entity.Name)) + { + string t = entity.Name; + + int width = FontsLoader.Instance.GetWidthUnicode(_renderedText.Font, t); + + if (width > Constants.OBJECT_HANDLES_GUMP_WIDTH) + { + t = FontsLoader.Instance.GetTextByWidthUnicode + ( + _renderedText.Font, + t.AsSpan(), + Constants.OBJECT_HANDLES_GUMP_WIDTH, + true, + TEXT_ALIGN_TYPE.TS_CENTER, + (ushort) FontStyle.BlackBorder + ); + + width = Constants.OBJECT_HANDLES_GUMP_WIDTH; + } + + _renderedText.MaxWidth = width; + + _renderedText.Text = t; + + Width = _background.Width = Math.Max(60, _renderedText.Width) + 4; + Height = _background.Height = Constants.OBJECT_HANDLES_GUMP_HEIGHT + 4; + + WantUpdateSize = false; + + return true; + } + + return false; + } + + private void BuildGump() + { + Entity entity = World.Get(LocalSerial); + + if (entity == null) + { + Dispose(); + + return; + } + + Add + ( + _background = new AlphaBlendControl(.3f) + { + WantUpdateSize = false, + Hue = entity is Mobile m ? Notoriety.GetHue(m.NotorietyFlag) : (ushort) 0x0481 + } + ); + } + + protected override void CloseWithRightClick() + { + Entity entity = World.Get(LocalSerial); + + if (entity != null) + { + entity.ObjectHandlesStatus = ObjectHandlesStatus.CLOSED; + } + + base.CloseWithRightClick(); + } + + protected override void OnDragBegin(int x, int y) + { + _positionLocked = false; + + Entity entity = World.Get(LocalSerial); + + if (entity is Mobile || entity is Item it && it.IsDamageable) + { + if (UIManager.IsDragging) + { + return; + } + + BaseHealthBarGump gump = UIManager.GetGump(LocalSerial); + gump?.Dispose(); + + if (entity == World.Player) + { + StatusGumpBase.GetStatusGump()?.Dispose(); + } + + if (ProfileManager.CurrentProfile.CustomBarsToggled) + { + Rectangle rect = new Rectangle(0, 0, HealthBarGumpCustom.HPB_WIDTH, HealthBarGumpCustom.HPB_HEIGHT_SINGLELINE); + + UIManager.Add + ( + gump = new HealthBarGumpCustom(entity) + { + X = Mouse.Position.X - (rect.Width >> 1), + Y = Mouse.Position.Y - (rect.Height >> 1) + } + ); + } + else + { + Rectangle rect = GumpsLoader.Instance.GetTexture(0x0804).Bounds; + + UIManager.Add + ( + gump = new HealthBarGump(entity) + { + X = Mouse.LClickPosition.X - (rect.Width >> 1), + Y = Mouse.LClickPosition.Y - (rect.Height >> 1) + } + ); + } + + UIManager.AttemptDragControl(gump, true); + } + else if (entity != null) + { + GameActions.PickUp(LocalSerial, 0, 0); + + //if (entity.Texture != null) + // GameActions.PickUp(LocalSerial, entity.Texture.Width >> 1, entity.Texture.Height >> 1); + //else + // GameActions.PickUp(LocalSerial, 0, 0); + } + } + + protected override bool OnMouseDoubleClick(int x, int y, MouseButtonType button) + { + if (button == MouseButtonType.Left) + { + if (SerialHelper.IsMobile(LocalSerial)) + { + if (World.Player.InWarMode) + { + GameActions.Attack(LocalSerial); + } + else + { + GameActions.DoubleClick(LocalSerial); + } + } + else + { + if (!GameActions.OpenCorpse(LocalSerial)) + { + GameActions.DoubleClick(LocalSerial); + } + } + + return true; + } + + return false; + } + + protected override void OnMouseUp(int x, int y, MouseButtonType button) + { + if (button == MouseButtonType.Left) + { + if (!ItemHold.Enabled) + { + if (UIManager.IsDragging || Math.Max(Math.Abs(Mouse.LDragOffset.X), Math.Abs(Mouse.LDragOffset.Y)) >= 1) + { + _positionLocked = false; + + return; + } + } + + if (TargetManager.IsTargeting) + { + switch (TargetManager.TargetingState) + { + case CursorTarget.Position: + case CursorTarget.Object: + case CursorTarget.Grab: + case CursorTarget.SetGrabBag: + TargetManager.Target(LocalSerial); + Mouse.LastLeftButtonClickTime = 0; + + break; + + case CursorTarget.SetTargetClientSide: + TargetManager.Target(LocalSerial); + Mouse.LastLeftButtonClickTime = 0; + UIManager.Add(new InspectorGump(World.Get(LocalSerial))); + + break; + + case CursorTarget.HueCommandTarget: + CommandManager.OnHueTarget(World.Get(LocalSerial)); + + break; + } + } + else + { + if (ItemHold.Enabled && !ItemHold.IsFixedPosition) + { + uint drop_container = 0xFFFF_FFFF; + bool can_drop = false; + ushort dropX = 0; + ushort dropY = 0; + sbyte dropZ = 0; + + Entity obj = World.Get(LocalSerial); + + if (obj != null) + { + can_drop = obj.Distance <= Constants.DRAG_ITEMS_DISTANCE; + + if (can_drop) + { + if (obj is Item it && it.ItemData.IsContainer || obj is Mobile) + { + dropX = 0xFFFF; + dropY = 0xFFFF; + dropZ = 0; + drop_container = obj.Serial; + } + else if (obj is Item it2 && (it2.ItemData.IsSurface || it2.ItemData.IsStackable && it2.DisplayedGraphic == ItemHold.DisplayedGraphic)) + { + dropX = obj.X; + dropY = obj.Y; + dropZ = obj.Z; + + if (it2.ItemData.IsSurface) + { + dropZ += (sbyte) (it2.ItemData.Height == 0xFF ? 0 : it2.ItemData.Height); + } + else + { + drop_container = obj.Serial; + } + } + } + else + { + Client.Game.Scene.Audio.PlaySound(0x0051); + } + + if (can_drop) + { + if (drop_container == 0xFFFF_FFFF && dropX == 0 && dropY == 0) + { + can_drop = false; + } + + if (can_drop) + { + GameActions.DropItem + ( + ItemHold.Serial, + dropX, + dropY, + dropZ, + drop_container + ); + } + } + } + } + else if (!DelayedObjectClickManager.IsEnabled) + { + DelayedObjectClickManager.Set(LocalSerial, Mouse.Position.X, Mouse.Position.Y, Time.Ticks + Mouse.MOUSE_DELAY_DOUBLE_CLICK); + } + } + } + + base.OnMouseUp(x, y, button); + } + + protected override void OnMouseOver(int x, int y) + { + if (_positionLocked) + { + return; + } + + if (SerialHelper.IsMobile(LocalSerial)) + { + Mobile m = World.Mobiles.Get(LocalSerial); + + if (m == null) + { + Dispose(); + + return; + } + + _positionLocked = true; + + AnimationsLoader.Instance.GetAnimationDimensions + ( + m.AnimIndex, + m.GetGraphicForAnimation(), + /*(byte) m.GetDirectionForAnimation()*/ + 0, + /*Mobile.GetGroupForAnimation(m, isParent:true)*/ + 0, + m.IsMounted, + /*(byte) m.AnimIndex*/ + 0, + out int centerX, + out int centerY, + out int width, + out int height + ); + + _lockedPosition.X = (int) (m.RealScreenPosition.X + m.Offset.X + 22 + 5); + + _lockedPosition.Y = (int) (m.RealScreenPosition.Y + (m.Offset.Y - m.Offset.Z) - (height + centerY + 8) + (m.IsGargoyle && m.IsFlying ? -22 : !m.IsMounted ? 22 : 0)); + } + + base.OnMouseOver(x, y); + } + + protected override void OnMouseExit(int x, int y) + { + _positionLocked = false; + base.OnMouseExit(x, y); + } + + public override void Update(double totalTime, double frameTime) + { + base.Update(totalTime, frameTime); + + Entity entity = World.Get(LocalSerial); + + if (entity == null || entity.IsDestroyed || entity.ObjectHandlesStatus == ObjectHandlesStatus.NONE || entity.ObjectHandlesStatus == ObjectHandlesStatus.CLOSED) + { + Dispose(); + } + else + { + if (entity == TargetManager.LastTargetInfo.Serial) + { + _borderColor = SolidColorTextureCache.GetTexture(Color.Red); + _background.Hue = _renderedText.Hue = entity is Mobile m ? Notoriety.GetHue(m.NotorietyFlag) : (ushort) 0x0481; + } + else + { + _borderColor = SolidColorTextureCache.GetTexture(Color.Black); + _background.Hue = _renderedText.Hue = entity is Mobile m ? Notoriety.GetHue(m.NotorietyFlag) : (ushort) 0x0481; + } + } + } + + public override bool Draw(UltimaBatcher2D batcher, int x, int y) + { + if (IsDisposed || !SetName()) + { + return false; + } + + // MobileUO: fix position of CTRL+SHIFT name plates over mobiles/items/etc. + int gx = 0;// ProfileManager.CurrentProfile.GameWindowPosition.X; + int gy = 0;// ProfileManager.CurrentProfile.GameWindowPosition.Y; + int w = ProfileManager.CurrentProfile.GameWindowSize.X; + int h = ProfileManager.CurrentProfile.GameWindowSize.Y; + + if (SerialHelper.IsMobile(LocalSerial)) + { + Mobile m = World.Mobiles.Get(LocalSerial); + + if (m == null) + { + Dispose(); + + return false; + } + + if (_positionLocked) + { + x = _lockedPosition.X; + y = _lockedPosition.Y; + } + else + { + AnimationsLoader.Instance.GetAnimationDimensions + ( + m.AnimIndex, + m.GetGraphicForAnimation(), + /*(byte) m.GetDirectionForAnimation()*/ + 0, + /*Mobile.GetGroupForAnimation(m, isParent:true)*/ + 0, + m.IsMounted, + /*(byte) m.AnimIndex*/ + 0, + out int centerX, + out int centerY, + out int width, + out int height + ); + + x = (int) (m.RealScreenPosition.X + m.Offset.X + 22 + 5); + + y = (int) (m.RealScreenPosition.Y + (m.Offset.Y - m.Offset.Z) - (height + centerY + 8) + (m.IsGargoyle && m.IsFlying ? -22 : !m.IsMounted ? 22 : 0)); + } + } + else if (SerialHelper.IsItem(LocalSerial)) + { + Item item = World.Items.Get(LocalSerial); + + if (item == null) + { + Dispose(); + + return false; + } + + ArtTexture texture = ArtLoader.Instance.GetTexture(item.Graphic); + + if (texture != null) + { + x = item.RealScreenPosition.X + (int) item.Offset.X + 22 + 5; + + y = item.RealScreenPosition.Y + (int) (item.Offset.Y - item.Offset.Z) + (texture.ImageRectangle.Height >> 1); + } + else + { + x = item.RealScreenPosition.X + (int) item.Offset.X + 22 + 5; + y = item.RealScreenPosition.Y + (int) (item.Offset.Y - item.Offset.Z) + 22; + } + } + + + ResetHueVector(); + + Point p = Client.Game.Scene.Camera.WorldToScreen(new Point(x, y)); + x = p.X - (Width >> 1); + y = p.Y - (Height >> 1); + + x += gx; + y += gy; + + if (x < gx || x + Width > gx + w) + { + return false; + } + + if (y < gy || y + Height > gy + h) + { + return false; + } + + X = x; + Y = y; + + batcher.DrawRectangle + ( + _borderColor, + x - 1, + y - 1, + Width + 1, + Height + 1, + ref HueVector + ); + + base.Draw(batcher, x, y); + + int renderedTextOffset = Math.Max(0, Width - _renderedText.Width - 4) >> 1; + + return _renderedText.Draw + ( + batcher, + Width, + Height, + x + 2 + renderedTextOffset, + y + 2, + Width, + Height, + 0, + 0 + ); + } + + + public override void Dispose() + { + _renderedText?.Destroy(); + base.Dispose(); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ResizableGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ResizableGump.cs index fcfa307ed..89dd88d17 100644 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ResizableGump.cs +++ b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ResizableGump.cs @@ -1,60 +1,88 @@ #region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. + +// Copyright (c) 2021, andreakarasho +// All rights reserved. // -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. All advertising materials mentioning features or use of this software +// must display the following acknowledgement: +// This product includes software developed by andreakarasho - https://github.com/andreakarasho +// 4. Neither the name of the copyright holder nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. // -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY +// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + #endregion using ClassicUO.Game.UI.Controls; using ClassicUO.Input; - using Microsoft.Xna.Framework; namespace ClassicUO.Game.UI.Gumps { - abstract class ResizableGump : Gump + internal abstract class ResizableGump : Gump { - private readonly Button _button; private readonly BorderControl _borderControl; - private Point _lastSize, _savedSize; + private readonly Button _button; private bool _clicked; - private int _minW, _minH; - - - - protected ResizableGump(int width, int height, int minW, int minH, uint local, uint server, ushort borderHue = 0) : base(local, server) + private Point _lastSize, _savedSize; + private readonly int _minH; + private readonly int _minW; + + + protected ResizableGump + ( + int width, + int height, + int minW, + int minH, + uint local, + uint server, + ushort borderHue = 0 + ) : base(local, server) { - _borderControl = new BorderControl(0, 0, Width, Height, 4) + _borderControl = new BorderControl + ( + 0, + 0, + Width, + Height, + 4 + ) { Hue = borderHue }; + Add(_borderControl); _button = new Button(0, 0x837, 0x838, 0x838); Add(_button); - //Upscale resize button on mobile + // MobileUO: Upscale resize button on mobile if (UnityEngine.Application.isMobilePlatform) { _button.Width *= 2; _button.Height *= 2; _button.ContainsByBounds = true; } - - _button.MouseDown += (sender, e) => { _clicked = true;}; + + _button.MouseDown += (sender, e) => { _clicked = true; }; + _button.MouseUp += (sender, e) => { ResizeWindow(_lastSize); @@ -80,16 +108,17 @@ public bool ShowBorder } - - - public Point ResizeWindow(Point newSize) { if (newSize.X < _minW) + { newSize.X = _minW; + } if (newSize.Y < _minH) + { newSize.Y = _minH; + } //Resize(); _savedSize = newSize; @@ -98,13 +127,14 @@ public Point ResizeWindow(Point newSize) } - - public override void Update(double totalMS, double frameMS) + public override void Update(double totalTime, double frameTime) { if (IsDisposed) + { return; + } - Point offset = Mouse.LDroppedOffset; + Point offset = Mouse.LDragOffset; _lastSize = _savedSize; @@ -114,10 +144,14 @@ public override void Update(double totalMS, double frameMS) int h = _lastSize.Y + offset.Y; if (w < _minW) + { w = _minW; + } if (h < _minH) + { h = _minH; + } _lastSize.X = w; _lastSize.Y = h; @@ -130,7 +164,7 @@ public override void Update(double totalMS, double frameMS) OnResize(); } - base.Update(totalMS, frameMS); + base.Update(totalTime, frameTime); } @@ -141,6 +175,5 @@ public virtual void OnResize() _button.X = Width - (_button.Width >> 0) + 2; _button.Y = Height - (_button.Height >> 0) + 2; } - } -} +} \ No newline at end of file diff --git a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ShopGump.cs b/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ShopGump.cs deleted file mode 100644 index 139412f01..000000000 --- a/Assets/Scripts/ClassicUO/src/Game/UI/Gumps/ShopGump.cs +++ /dev/null @@ -1,939 +0,0 @@ -#region license -// Copyright (C) 2020 ClassicUO Development Community on Github -// -// This project is an alternative client for the game Ultima Online. -// The goal of this is to develop a lightweight client considering -// new technologies. -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; - -using ClassicUO.Configuration; -using ClassicUO.Game.GameObjects; -using ClassicUO.Game.UI.Controls; -using ClassicUO.Input; -using ClassicUO.IO.Resources; -using ClassicUO.Network; -using ClassicUO.Renderer; -using ClassicUO.Utility; - -using Microsoft.Xna.Framework.Graphics; - -namespace ClassicUO.Game.UI.Gumps -{ - internal class ShopGump : Gump - { - private static UOTexture32[] _shopGumpParts; - private readonly GumpPicTexture _middleGumpLeft, _middleGumpRight; - private readonly Dictionary _shopItems; - private readonly ScrollArea _shopScrollArea, _transactionScrollArea; - private readonly Label _totalLabel, _playerGoldLabel; - private readonly Dictionary _transactionItems; - private Button _button_expander; - - private bool _isUpDOWN, _isDownDOWN; - private bool _isUpDOWN_T, _isDownDOWN_T; - - private bool _shiftPressed; - private bool _updateTotal; - - public ShopGump(uint serial, bool isBuyGump, int x, int y) : base(serial, 0) //60 is the base height, original size - { - int height = ProfileManager.Current.VendorGumpHeight; - if (_shopGumpParts == null) - GenerateVirtualTextures(); - - X = x; - Y = y; - AcceptMouseInput = false; - AcceptKeyboardInput = true; - CanMove = true; - CanCloseWithRightClick = true; - IsBuyGump = isBuyGump; - - _transactionItems = new Dictionary(); - _shopItems = new Dictionary(); - _updateTotal = false; - - int add = isBuyGump ? 0 : 6; - - GumpPicTexture pic = new GumpPicTexture(0, 0, _shopGumpParts[0 + add]); - Add(pic); - - pic = new GumpPicTexture(250, 144, _shopGumpParts[3 + add]); - Add(pic); - - - _middleGumpLeft = new GumpPicTexture(0, 64, _shopGumpParts[1 + add], true) - { - Width = pic.Width, - Height = height - }; - Add(_middleGumpLeft); - - var left_down = new GumpPicTexture(0, _middleGumpLeft.Height + _middleGumpLeft.Y, _shopGumpParts[2 + add]); - Add(left_down); - - _shopScrollArea = new ScrollArea(30, 60, 225, _middleGumpLeft.Height + _middleGumpLeft.Y + 50, false, _middleGumpLeft.Height + _middleGumpLeft.Y); - Add(_shopScrollArea); - - - _middleGumpRight = new GumpPicTexture(250, 144 + 64, _shopGumpParts[4 + add], true) - { - Width = pic.Width, - Height = _middleGumpLeft.Height >> 1 - }; - Add(_middleGumpRight); - - - var right_down = new GumpPicTexture(250, _middleGumpRight.Height + _middleGumpRight.Y, _shopGumpParts[5 + add]); - Add(right_down); - - - HitBox boxAccept = new HitBox(280, 306 + _middleGumpRight.Height, 34, 30) - { - Alpha = 1 - }; - - HitBox boxClear = new HitBox(452, 310 + _middleGumpRight.Height, 24, 24) - { - Alpha = 1 - }; - - boxAccept.MouseUp += (sender, e) => { OnButtonClick((int) Buttons.Accept); }; - boxClear.MouseUp += (sender, e) => { OnButtonClick((int) Buttons.Clear); }; - Add(boxAccept); - Add(boxClear); - - - if (isBuyGump) - { - Add(_totalLabel = new Label("0", true, 0x0386, 0, 1) - { - X = 318, - Y = 281 + _middleGumpRight.Height - }); - - Add(_playerGoldLabel = new Label(World.Player.Gold.ToString(), true, 0x0386, 0, 1) - { - X = 436, - Y = 281 + _middleGumpRight.Height - }); - } - else - { - Add(_totalLabel = new Label("0", true, 0x0386, 0, 1) - { - X = 436, - Y = 281 + _middleGumpRight.Height - }); - } - - Label name = new Label(World.Player.Name, false, 0x0386, font: 5) - { - X = 322, - Y = 308 + _middleGumpRight.Height - }; - Add(name); - - Add(_transactionScrollArea = new ScrollArea(260, 215, 245, 53 + _middleGumpRight.Height, false)); - - - HitBox upButton = new HitBox(233, 50, 18, 16) - { - Alpha = 1 - }; - upButton.MouseDown += (sender, e) => { _isUpDOWN = true; }; - upButton.MouseUp += (sender, e) => { _isUpDOWN = false; }; - - Add(upButton); - - HitBox downButton = new HitBox(233, 130 + _middleGumpLeft.Height, 18, 16) - { - Alpha = 1 - }; - downButton.MouseDown += (sender, e) => { _isDownDOWN = true; }; - downButton.MouseUp += (sender, e) => { _isDownDOWN = false; }; - Add(downButton); - - - HitBox upButtonT = new HitBox(483, 195, 18, 16) - { - Alpha = 1 - }; - upButtonT.MouseDown += (sender, e) => { _isUpDOWN_T = true; }; - upButtonT.MouseUp += (sender, e) => { _isUpDOWN_T = false; }; - - Add(upButtonT); - - HitBox downButtonT = new HitBox(483, 270 + _middleGumpRight.Height, 18, 16) - { - Alpha = 1 - }; - downButtonT.MouseDown += (sender, e) => { _isDownDOWN_T = true; }; - downButtonT.MouseUp += (sender, e) => { _isDownDOWN_T = false; }; - Add(downButtonT); - - - Add(_button_expander = new Button(2, 0x082E, 0x82F) - { - ButtonAction = ButtonAction.Activate, - X = _shopGumpParts[0 + add].Width / 2 - 10, - Y = left_down.Y + left_down.Height - }); - - bool is_pressing = false; - int initial_Y = 0; - int initial_height = 0; - _button_expander.MouseDown += (sender, args) => - { - is_pressing = true; - initial_Y = Mouse.Position.Y; - initial_height = _middleGumpLeft.Height; - }; - _button_expander.MouseUp += (sender, args) => { is_pressing = false; }; - - _button_expander.MouseOver += (sender, args) => - { - if (is_pressing && Mouse.Position.Y != initial_Y) - { - _middleGumpLeft.Height = initial_height + (Mouse.Position.Y - initial_Y); - - if (_middleGumpLeft.Height < 60) - { - _middleGumpLeft.Height = 60; - } - else if (_middleGumpLeft.Height > 450) - { - _middleGumpLeft.Height = 450; - } - - ProfileManager.Current.VendorGumpHeight = _middleGumpLeft.Height; - - _middleGumpRight.Height = _middleGumpLeft.Height >> 1; - - left_down.Y = _middleGumpLeft.Y + _middleGumpLeft.Height; - right_down.Y = _middleGumpRight.Y + _middleGumpRight.Height; - boxAccept.Y = 306 + _middleGumpRight.Height; - boxClear.Y = 310 + _middleGumpRight.Height; - _transactionScrollArea.Height = _middleGumpRight.Height + 53; - _shopScrollArea.Height = left_down.Y + 50; - _shopScrollArea.ScrollMaxHeight = left_down.Y; - _button_expander.Y = left_down.Y + left_down.Height; - downButton.Y = 130 + _middleGumpLeft.Height; - downButtonT.Y = 70 + _middleGumpRight.Height; - name.Y = 308 + _middleGumpRight.Height; - _totalLabel.Y = 281 + _middleGumpRight.Height; - - if (_playerGoldLabel != null) - _playerGoldLabel.Y = 281 + _middleGumpRight.Height; - - WantUpdateSize = true; - } - - - }; - } - - public bool IsBuyGump { get; } - - private void GenerateVirtualTextures() - { - _shopGumpParts = new UOTexture32[12]; - var t = GumpsLoader.Instance.GetTexture(0x0870, true); - UOTexture32[][] splits = new UOTexture32[4][]; - - splits[0] = Utility.GraphicHelper.SplitTexture16(t, - new int[3, 4] - { - {0, 0, t.Width, 64}, - {0, 64, t.Width, 124}, - {0, 124, t.Width, t.Height - 124} - }); - t = GumpsLoader.Instance.GetTexture(0x0871, true); - - splits[1] = Utility.GraphicHelper.SplitTexture16(t, - new int[3, 4] - { - {0, 0, t.Width, 64}, - {0, 64, t.Width, 94}, - {0, 94, t.Width, t.Height - 94} - }); - t = GumpsLoader.Instance.GetTexture(0x0872, true); - - splits[2] = Utility.GraphicHelper.SplitTexture16(t, - new int[3, 4] - { - {0, 0, t.Width, 64}, - {0, 64, t.Width, 124}, - {0, 124, t.Width, t.Height - 124} - }); - t = GumpsLoader.Instance.GetTexture(0x0873, true); - - splits[3] = Utility.GraphicHelper.SplitTexture16(t, - new int[3, 4] - { - {0, 0, t.Width, 64}, - {0, 64, t.Width, 94}, - {0, 94, t.Width, t.Height - 94} - }); - - for (int i = 0, idx = 0; i < splits.Length; i++) - { - for (int ii = 0; ii < splits[i].Length; ii++) _shopGumpParts[idx++] = splits[i][ii]; - } - } - - - - //public void SetIfNameIsFromCliloc(Item it, bool fromcliloc) - //{ - // if (_shopItems.TryGetValue(it, out var shopItem)) - // { - // shopItem.NameFromCliloc = fromcliloc; - - // if (fromcliloc) - // { - // shopItem.SetName(ClilocLoader.Instance.Translate(it.Name, $"\t{it.Amount}\t{it.ItemData.Name}", true)); - // } - // } - //} - - public void AddItem(uint serial, ushort graphic, ushort hue, ushort amount, uint price, string name, bool fromcliloc) - { - ShopItem shopItem; - - _shopScrollArea.Add(shopItem = new ShopItem(serial, graphic, hue, amount, price, name) - { - X = 5, - Y = 5, - NameFromCliloc = fromcliloc - }); - - _shopScrollArea.Add(new ResizePicLine(0x39) - { - X = 10, - Width = 190 - }); - shopItem.MouseUp += ShopItem_MouseClick; - shopItem.MouseDoubleClick += ShopItem_MouseDoubleClick; - _shopItems.Add(serial, shopItem); - - //var it = World.Items.Get(serial); - //Console.WriteLine("ITEM: name: {0} - tiledata name: {1} - price: {2} - X,Y= {3},{4}", name, TileDataLoader.Instance.StaticData[graphic].Name, price, it.X, it.Y); - } - - public void SetNameTo(Item item, string name) - { - if (!string.IsNullOrEmpty(name) && _shopItems.TryGetValue(item, out ShopItem shopItem)) - shopItem.SetName(name, false); - } - - - public override void Update(double totalMS, double frameMS) - { - if (!World.InGame || IsDisposed) - return; - - if (_shopItems.Count == 0) - { - Dispose(); - } - - _shiftPressed = Keyboard.Shift; - - if (_isUpDOWN || _isDownDOWN || _isDownDOWN_T || _isUpDOWN_T) - { - if (_isDownDOWN) - _shopScrollArea.Scroll(false); - else if (_isUpDOWN) - _shopScrollArea.Scroll(true); - else if (_isDownDOWN_T) - _transactionScrollArea.Scroll(false); - else - _transactionScrollArea.Scroll(true); - } - - if (_updateTotal) - { - int sum = 0; - - foreach (var t in _transactionItems.Values) - { - sum += t.Amount * t.Price; - } - _totalLabel.Text = sum.ToString(); - _updateTotal = false; - } - - if (_playerGoldLabel != null) - _playerGoldLabel.Text = World.Player.Gold.ToString(); - - base.Update(totalMS, frameMS); - } - - public override bool Draw(UltimaBatcher2D batcher, int x, int y) - { - return base.Draw(batcher, x, y); - } - - private void ShopItem_MouseDoubleClick(object sender, MouseDoubleClickEventArgs e) - { - var shopItem = (ShopItem) sender; - - if (shopItem.Amount <= 0) - return; - - - int total = _shiftPressed ? shopItem.Amount : 1; - - if (_transactionItems.TryGetValue(shopItem.LocalSerial, out TransactionItem transactionItem)) - transactionItem.Amount += total; - else - { - transactionItem = new TransactionItem(shopItem.LocalSerial, shopItem.Graphic, shopItem.Hue, total, (ushort) shopItem.Price, shopItem.ShopItemName); - transactionItem.OnIncreaseButtomClicked += TransactionItem_OnIncreaseButtomClicked; - transactionItem.OnDecreaseButtomClicked += TransactionItem_OnDecreaseButtomClicked; - _transactionScrollArea.Add(transactionItem); - _transactionItems.Add(shopItem.LocalSerial, transactionItem); - } - - shopItem.Amount -= total; - _updateTotal = true; - } - - private void TransactionItem_OnDecreaseButtomClicked(object sender, EventArgs e) - { - var transactionItem = (TransactionItem) sender; - - int total = _shiftPressed ? transactionItem.Amount : 1; - - if (transactionItem.Amount > 0) - { - _shopItems[transactionItem.LocalSerial].Amount += total; - transactionItem.Amount -= total; - } - - if (transactionItem.Amount <= 0) - RemoveTransactionItem(transactionItem); - _updateTotal = true; - } - - private void RemoveTransactionItem(TransactionItem transactionItem) - { - _shopItems[transactionItem.LocalSerial].Amount += transactionItem.Amount; - transactionItem.OnIncreaseButtomClicked -= TransactionItem_OnIncreaseButtomClicked; - transactionItem.OnDecreaseButtomClicked -= TransactionItem_OnDecreaseButtomClicked; - _transactionItems.Remove(transactionItem.LocalSerial); - _transactionScrollArea.Remove(transactionItem); - _updateTotal = true; - } - - private void TransactionItem_OnIncreaseButtomClicked(object sender, EventArgs e) - { - var transactionItem = (TransactionItem) sender; - - if (_shopItems[transactionItem.LocalSerial].Amount > 0) - { - _shopItems[transactionItem.LocalSerial].Amount--; - transactionItem.Amount++; - } - - _updateTotal = true; - } - - private void ShopItem_MouseClick(object sender, MouseEventArgs e) - { - foreach (var shopItem in _shopScrollArea.Children.SelectMany(o => o.Children).OfType()) - shopItem.IsSelected = shopItem == sender; - } - - public override void OnButtonClick(int buttonID) - { - switch ((Buttons) buttonID) - { - case Buttons.Accept: - var items = _transactionItems.Select(t => new Tuple(t.Key, (ushort) t.Value.Amount)).ToArray(); - - if (IsBuyGump) - NetClient.Socket.Send(new PBuyRequest(LocalSerial, items)); - else - NetClient.Socket.Send(new PSellRequest(LocalSerial, items)); - - Dispose(); - - break; - - case Buttons.Clear: - - foreach (var t in _transactionItems.Values.ToList()) - RemoveTransactionItem(t); - - break; - } - } - - private enum Buttons - { - Accept, - Clear - } - - private class ShopItem : Control - { - private readonly Label _amountLabel, _name; - - private static byte GetAnimGroup(ushort graphic) - { - switch (AnimationsLoader.Instance.GetGroupIndex(graphic)) - { - case ANIMATION_GROUPS.AG_LOW: - return (byte) LOW_ANIMATION_GROUP.LAG_STAND; - - case ANIMATION_GROUPS.AG_HIGHT: - return (byte) HIGHT_ANIMATION_GROUP.HAG_STAND; - - case ANIMATION_GROUPS.AG_PEOPLE: - return (byte) PEOPLE_ANIMATION_GROUP.PAG_STAND; - } - - return 0; - } - - private static AnimationDirection GetMobileAnimationDirection(ushort graphic, ref ushort hue, byte dirIndex) - { - if (graphic >= Constants.MAX_ANIMATIONS_DATA_INDEX_COUNT) - { - return null; - } - - byte group = GetAnimGroup(graphic); - var index = AnimationsLoader.Instance.DataIndex[graphic]; - - AnimationDirection direction = index.Groups[group].Direction[dirIndex]; - AnimationsLoader.Instance.AnimID = graphic; - AnimationsLoader.Instance.AnimGroup = group; - AnimationsLoader.Instance.Direction = dirIndex; - - for (int i = 0; i < 2 && direction.FrameCount == 0; i++) - { - if (!AnimationsLoader.Instance.LoadDirectionGroup(ref direction)) - { - //direction = AnimationsLoader.Instance.GetCorpseAnimationGroup(ref graphic, ref group, ref hue2).Direction[dirIndex]; - //graphic = item.ItemData.AnimID; - //group = GetAnimGroup(graphic); - //index = AnimationsLoader.Instance.DataIndex[graphic]; - //direction = AnimationsLoader.Instance.GetBodyAnimationGroup(ref graphic, ref group, ref hue2, true).Direction[dirIndex]; - ////direction = index.Groups[group].Direction[1]; - //AnimationsLoader.Instance.AnimID = graphic; - //AnimationsLoader.Instance.AnimGroup = group; - //AnimationsLoader.Instance.Direction = dirIndex; - } - } - - return direction; - } - - public ShopItem(uint serial, ushort graphic, ushort hue, int count, uint price, string name) - { - LocalSerial = serial; - Graphic = graphic; - Hue = hue; - Price = price; - Name = name; - - string itemName = StringHelper.CapitalizeAllWords(Name); - - TextureControl control; - - if (SerialHelper.IsMobile(LocalSerial)) - { - ushort hue2 = Hue; - AnimationDirection direction = GetMobileAnimationDirection(Graphic, ref hue2, 1); - - Add(control = new TextureControl - { - Texture = direction != null ? direction.FrameCount != 0 ? direction.Frames[0] : null : null, - X = 5, - Y = 5, - AcceptMouseInput = false, - Hue = Hue == 0 ? hue2 : Hue, - IsPartial = TileDataLoader.Instance.StaticData[Graphic].IsPartialHue - }); - - if (control.Texture != null) - { - control.Width = control.Texture.Width; - control.Height = control.Texture.Height; - } - else - { - control.Width = 35; - control.Height = 35; - } - - if (control.Width > 35) - control.Width = 35; - - if (control.Height > 35) - control.Height = 35; - } - else if (SerialHelper.IsItem(LocalSerial)) - { - var texture = ArtLoader.Instance.GetTexture(Graphic); - - Add(control = new TextureControl - { - Texture = texture, - X = 10 - texture.ImageRectangle.X, - Y = 10 + texture.ImageRectangle.Y, - Width = texture.ImageRectangle.Width, - Height = texture.ImageRectangle.Height, - AcceptMouseInput = false, - ScaleTexture = false, - Hue = Hue, - IsPartial = TileDataLoader.Instance.StaticData[Graphic].IsPartialHue - }); - } - else - return; - - string subname = $"{itemName} at {Price}gp"; - - Add(_name = new Label(subname, true, 0x219, 110, 1, FontStyle.None, TEXT_ALIGN_TYPE.TS_LEFT, true) - { - Y = 0, - X = 55 - }); - - int height = Math.Max(_name.Height, control.Height) + 10; - - Add(_amountLabel = new Label(count.ToString(), true, 0x0219, 35, 1, FontStyle.None, TEXT_ALIGN_TYPE.TS_RIGHT) - { - X = 168, - Y = height >> 2 - }); - - Width = 220; - - - Height = height; - WantUpdateSize = false; - - if (World.ClientFeatures.TooltipsEnabled) - SetTooltip(LocalSerial); - - Amount = count; - } - - internal string ShopItemName => _name.Text; - - - public int Amount - { - get => int.Parse(_amountLabel.Text); - set => _amountLabel.Text = value.ToString(); - } - - public bool IsSelected - { - set - { - foreach (var label in Children.OfType