├── .gitattributes
├── .gitignore
├── LICENSE
├── Properties
├── AssemblyInfo.cs
├── Resources.Designer.cs
└── Resources.resx
├── Resources
└── TornadoScript.ini
├── ScriptCore
├── Game
│ ├── IScriptComponent.cs
│ ├── IScriptEntity.cs
│ ├── IScriptEventHandler.cs
│ ├── IScriptUpdatable.cs
│ ├── IScriptVar.cs
│ ├── ScriptComponent.cs
│ ├── ScriptEntity.cs
│ ├── ScriptEntityEventArgs.cs
│ ├── ScriptEventArgs.cs
│ ├── ScriptExtension.cs
│ ├── ScriptExtensionEventArgs.cs
│ ├── ScriptExtensionEventPool.cs
│ ├── ScriptExtensionPool.cs
│ ├── ScriptPed.cs
│ ├── ScriptPlane.cs
│ ├── ScriptProp.cs
│ ├── ScriptThread.cs
│ ├── ScriptVar.cs
│ └── ScriptVarCollection.cs
├── IO
│ ├── EncryptedFileStream.cs
│ ├── IXMLSimpleMetadata.cs
│ ├── XMLAttributesCollection.cs
│ ├── XMLSimpleMetadata.cs
│ └── XMLSimpleParser.cs
└── Logger.cs
├── ScriptMain
├── Commands
│ ├── CommandManager.cs
│ └── Commands.cs
├── Config
│ ├── IniFile.cs
│ └── IniHelper.cs
├── Frontend
│ ├── FrontendInput.cs
│ ├── FrontendManager.cs
│ └── FrontendOutput.cs
├── Memory
│ ├── MemoryAccess.cs
│ ├── NativeTypes.cs
│ └── Pattern.cs
├── Script
│ ├── TDebris.cs
│ ├── TEntity.cs
│ ├── TFactory.cs
│ ├── TParticle.cs
│ ├── TScript.cs
│ └── TVortex.cs
├── Utility
│ ├── Audio
│ │ ├── LoopStream.cs
│ │ ├── SoundManager.cs
│ │ └── WavePlayer.cs
│ ├── GameSound.cs
│ ├── Helpers.cs
│ ├── LoopedParticle.cs
│ ├── MathEx.cs
│ ├── Probability.cs
│ ├── ShapeTestEx.cs
│ ├── StrongRandom.cs
│ └── Win32Native.cs
└── WinHelper.cs
├── TornadoScript.csproj
└── packages.config
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Windows image file caches
2 | Thumbs.db
3 | ehthumbs.db
4 |
5 | # Folder config file
6 | Desktop.ini
7 |
8 | # Recycle Bin used on file shares
9 | $RECYCLE.BIN/
10 |
11 | # Windows Installer files
12 | *.cab
13 | *.msi
14 | *.msm
15 | *.msp
16 |
17 | # Windows shortcuts
18 | *.lnk
19 |
20 | # =========================
21 | # Operating System Files
22 | # =========================
23 |
24 | # OSX
25 | # =========================
26 |
27 | .DS_Store
28 | .AppleDouble
29 | .LSOverride
30 |
31 | # Thumbnails
32 | ._*
33 |
34 | # Files that might appear in the root of a volume
35 | .DocumentRevisions-V100
36 | .fseventsd
37 | .Spotlight-V100
38 | .TemporaryItems
39 | .Trashes
40 | .VolumeIcon.icns
41 |
42 | # Directories potentially created on remote AFP share
43 | .AppleDB
44 | .AppleDesktop
45 | Network Trash Folder
46 | Temporary Items
47 | .apdisk
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Cameron Berry
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("TornadoScript")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("TornadoScript")]
13 | [assembly: AssemblyCopyright("Copyright © 2016")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("61ed2e12-e7e2-42ed-8ccb-8d1dfccc5fd9")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | <<<<<<< HEAD
36 | [assembly: AssemblyVersion("1.3.1.0")]
37 | [assembly: AssemblyFileVersion("1.3.1.0")]
38 | =======
39 | [assembly: AssemblyVersion("1.2.2.0")]
40 | [assembly: AssemblyFileVersion("1.2.2.0")]
41 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
42 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace TornadoScript.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TornadoScript.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to [KeyBinds]
65 | ///; Binds for the keyboard -->
66 | ///
67 | ///KeybindsEnabled = true
68 | ///; Enable keybinds.
69 | ///
70 | ///ToggleScript = F6
71 | ///; Tornado activation key.
72 | ///
73 | ///;-- You can use any keys from the Windows Forms key enumeration found here:
74 | ///;https://msdn.microsoft.com/en-us/library/system.windows.forms.keys%28v=vs.110%29.aspx
75 | ///
76 | ///
77 | ///[Vortex]
78 | ///; Settings for the tornado vortex -->
79 | ///
80 | ///MovementEnabled = true
81 | ///; Enable/ disable tornado movement
82 | ///
83 | ///MoveSpeedScale = 1.0
84 | ///; Speed at which the tornado traverses the world.
85 | ///
86 | ///MaxEntitySpe [rest of string was truncated]";.
87 | ///
88 | internal static string TornadoScript {
89 | get {
90 | return ResourceManager.GetString("TornadoScript", resourceCulture);
91 | }
92 | }
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 |
122 | ..\Resources\TornadoScript.ini;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252
123 |
124 |
--------------------------------------------------------------------------------
/Resources/TornadoScript.ini:
--------------------------------------------------------------------------------
1 | [KeyBinds]
2 | ; Binds for the keyboard -->
3 |
4 | KeybindsEnabled = true
5 | ; Enable keybinds.
6 |
7 | ToggleScript = F6
8 | ; Tornado activation key.
9 |
10 | ;-- You can use any keys from the Windows Forms key enumeration found here:
11 | ;https://msdn.microsoft.com/en-us/library/system.windows.forms.keys%28v=vs.110%29.aspx
12 |
13 |
14 | [Vortex]
15 | ; Settings for the tornado vortex -->
16 |
17 | MovementEnabled = true
18 | ; Enable/ disable tornado movement
19 |
20 | MoveSpeedScale = 1.0
21 | ; Speed at which the tornado traverses the world.
22 |
23 | MaxEntitySpeed = 45.0
24 | ; Maximum speed for entities trapped in the vortex.
25 |
26 | MaxEntityDistance = 57.0
27 | ; Maximum distance entites must be from the vortex before we start using forces on them.
28 |
29 | HorizontalForceScale = 2.0
30 | ; Scale of horizontal forces to be applied to vehicles/ peds/ objects trapped in the tornado.
31 | ; If you change this value, also set the vertical force scale accordingly to avoid strange behaviour.
32 |
33 | VerticalForceScale = 1.6
34 | ; Scale of horizontal forces to be applied to vehicles/ peds/ objects trapped in the tornado.
35 | ; If you change this value, also set the horizontal force scale accordingly to avoid strange behaviour.
36 |
37 | VortexRadius = 9.4
38 | ; The initial radius (width) of the tornado for which particles will circulate around.
39 | ; This value gets extrapolated internally to add the characteristic 'cone-shape' of the tornado.
40 |
41 | RotationSpeed = 2.4
42 | ; The rotation speed for particles in the vortex.
43 |
44 | ReverseRotation = false
45 | ; Reverses vortex rotation to be clockwise
46 |
47 |
48 | [VortexAdvanced]
49 | ; Advanced settings for the tornado vortex -->
50 |
51 | MaxParticleLayers = 47
52 | ; Max vertical particle layers in the vortex (effectivley 'vortex height').
53 | ; Adjusting this (lower) may provide performance improvements on lower- end machines.
54 |
55 | ParticlesPerLayer = 9
56 | ; Max particles that will be instantiated around the circumference of the vortex.
57 | ; The default value is recommended, but this may also be adjusted in the interest of improving performance.
58 |
59 | LayerSeperationAmount = 22.0
60 | ; The distance between individual layers in the vortex.
61 | ; Basically adjusts the amount of 'clipping' that occurs between particles vertically in the vortex.
62 | ; Particles that clip into eachother seem to affect performance, so adjusting this (larger) could potentially help.
63 |
64 | MultiVortexEnabled = false
65 | ; Allows spawning multiple tornadoes.
66 |
67 | <<<<<<< HEAD
68 | CloudTopEnabled = true
69 | ; Simulates a cloud type effect around the tornado
70 |
71 | CloudTopDebrisEnabled = true
72 | ; Adds a debris effect to the top portion of the tornado
73 |
74 | =======
75 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
76 | ParticleMod = true
77 | ; Patches some stuff at runtime to change the particles to a more realistic color. Disable this if you experience
78 | ; compatibility issues with this mod and newer game updates.
79 |
80 | <<<<<<< HEAD
81 | SurfaceDetectionEnabled = true
82 | ; Enable dynamic detection of materials/ surfaces, blending the color of the tornado with the environment
83 |
84 | UseInternalPool = true
85 | ; Grabs entities from the games internal pool and slows down the "full" processing of entities to once every 6 sec v.s. the usual 2 sec
86 | ; Enabled by default to aid performance
87 |
88 | [Other]
89 | ; Other customizations -->
90 |
91 | Notifications = true
92 | ; Notify on spawning a tornado
93 |
94 | SpawnInStorm = true
95 | =======
96 | [Other]
97 | ; Other customizations -->
98 |
99 | EnableConsole = true
100 |
101 | SpawnInStorm = false
102 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
103 | ; Spawn in a tornado when a thunderstorm is occuring
104 |
105 | ; Enable an in-game console to change settings in real- time
106 | EnableConsole = true
107 |
108 | ; Enable SFX for more immersion. NOTE: There are issues in .NET framework that cause significant lag when registering handlers for "window focus" events.
109 | ; The mod must use these APIs to ensure sound does not play when the GTA V window loses focus, meaning you may experience lag when loading or reloading scripts with this option enabled.
110 | EnableSound = true
111 |
112 | ; Plays a siren to warn of an imminent tornado when SpawnInStorm = true
113 | EnableSiren = false
114 |
115 |
116 |
--------------------------------------------------------------------------------
/ScriptCore/Game/IScriptComponent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TornadoScript.ScriptCore.Game
4 | {
5 | ///
6 | /// Represents a component of a script.
7 | ///
8 | public interface IScriptComponent : IScriptUpdatable
9 | {
10 | ///
11 | /// Gets the name of this component.
12 | ///
13 | /// The name.
14 | string Name { get; }
15 |
16 | ///
17 | /// Unique Id.
18 | ///
19 | Guid Guid { get; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ScriptCore/Game/IScriptEntity.cs:
--------------------------------------------------------------------------------
1 | namespace TornadoScript.ScriptCore.Game
2 | {
3 | public interface IScriptEntity
4 | {
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/ScriptCore/Game/IScriptEventHandler.cs:
--------------------------------------------------------------------------------
1 | namespace TornadoScript.ScriptCore.Game
2 | {
3 | ///
4 | /// Represents a handler of script events.
5 | ///
6 | public interface IScriptEventHandler
7 | {
8 | ScriptExtensionEventPool Events { get; }
9 |
10 | void NotifyEvent(string name);
11 |
12 | void NotifyEvent(string name, ScriptEventArgs args);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ScriptCore/Game/IScriptUpdatable.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace TornadoScript.ScriptCore.Game
3 | {
4 | ///
5 | /// Represents an object that can be updated by a script.
6 | ///
7 | public interface IScriptUpdatable
8 | {
9 | ///
10 | /// Method to be fired each frame.
11 | ///
12 | void OnUpdate(int gameTime);
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/ScriptCore/Game/IScriptVar.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace TornadoScript.ScriptCore.Game
3 | {
4 | public interface IScriptVar
5 | {
6 | bool ReadOnly { get; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptComponent.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TornadoScript.ScriptCore.Game
4 | {
5 | ///
6 | /// Base class for all script components.
7 | ///
8 | public abstract class ScriptComponent : IScriptComponent, IScriptUpdatable
9 | {
10 | ///
11 | /// Name of the component.
12 | ///
13 | public string Name { get; protected set; }
14 |
15 | ///
16 | /// Unique id to identify this instance.
17 | ///
18 | public Guid Guid { get; private set; }
19 |
20 |
21 | public ScriptComponent() : this(string.Empty)
22 | { }
23 |
24 | public ScriptComponent(string name)
25 | {
26 | Name = name ?? GetType().Name;
27 | Guid = Guid.NewGuid();
28 | }
29 |
30 | public override bool Equals(object obj)
31 | {
32 | var component = obj as ScriptComponent;
33 |
34 | if (component == null)
35 | {
36 | return false;
37 | }
38 |
39 | return Guid.GetHashCode() == component.Guid.GetHashCode();
40 | }
41 |
42 | public override int GetHashCode()
43 | {
44 | return Guid.GetHashCode();
45 | }
46 |
47 | public virtual void OnUpdate(int gameTime)
48 | { }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using GTA;
3 |
4 | namespace TornadoScript.ScriptCore.Game
5 | {
6 | ///
7 | /// Represents a game entity.
8 | ///
9 | public abstract class ScriptEntity : ScriptExtension, IScriptEntity where T : Entity
10 | {
11 | ///
12 | /// Base game entity reference.
13 | ///
14 | public T Ref { get; }
15 |
16 | ///
17 | /// Total entity ticks.
18 | ///
19 | public int TotalTicks { get; private set; }
20 |
21 | ///
22 | /// Total time entity has been available to the script.
23 | ///
24 | public TimeSpan TotalTime { get; private set; }
25 |
26 | ///
27 | /// Time at which the entity was made avilable to the script.
28 | ///
29 | public int CreatedTime { get; }
30 |
31 | ///
32 | /// Initialize the class.
33 | ///
34 | ///
35 | protected ScriptEntity(T baseRef)
36 | {
37 | Ref = baseRef;
38 | CreatedTime = GTA.Game.GameTime;
39 | }
40 |
41 | ///
42 | /// Call this method each tick to update entity related information.
43 | ///
44 | public override void OnUpdate(int gameTime)
45 | {
46 | TotalTicks++;
47 |
48 | TotalTicks = TotalTicks % int.MaxValue;
49 |
50 | TotalTime = TimeSpan.FromMilliseconds(gameTime - CreatedTime);
51 | }
52 |
53 | public void Remove()
54 | {
55 | Ref.CurrentBlip?.Remove();
56 | Ref.Delete();
57 | }
58 |
59 | public override void Dispose()
60 | {
61 | Remove();
62 | base.Dispose();
63 | }
64 |
65 | public static implicit operator Entity(ScriptEntity e)
66 | {
67 | return e.Ref;
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptEntityEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TornadoScript.ScriptCore.Game
4 | {
5 | public delegate void ScriptEntityEventHandler(IScriptEntity sender, ScriptEntityEventArgs args);
6 |
7 | ///
8 | /// Event args for a script entity event.
9 | ///
10 | public sealed class ScriptEntityEventArgs : EventArgs
11 | {
12 | public ScriptEntityEventArgs(int gameTime)
13 | {
14 | GameTime = gameTime;
15 | }
16 |
17 | ///
18 | /// The entity that fired the event
19 | ///
20 | public int GameTime { get; private set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TornadoScript.ScriptCore.Game
4 | {
5 | public class ScriptEventArgs : EventArgs
6 | {
7 | public object Data { get; private set; }
8 |
9 | public ScriptEventArgs() : this(null)
10 | { }
11 |
12 | public ScriptEventArgs(object data)
13 | {
14 | Data = data;
15 | }
16 | }
17 |
18 | public class ScriptEventArgs : EventArgs
19 | {
20 | public T Data { get; private set; }
21 |
22 | public ScriptEventArgs(T data)
23 | {
24 | Data = data;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptExtension.cs:
--------------------------------------------------------------------------------
1 | namespace TornadoScript.ScriptCore.Game
2 | {
3 | public abstract class ScriptExtension : ScriptComponent, IScriptEventHandler
4 | {
5 | public ScriptExtensionEventPool Events { get; } =
6 | new ScriptExtensionEventPool();
7 |
8 | public ScriptExtension()
9 | {
10 | ScriptThread.Add(this);
11 | }
12 |
13 | ///
14 | /// Raise an event with the specified name.
15 | ///
16 | /// The name of the event.
17 | public void NotifyEvent(string name)
18 | {
19 | NotifyEvent(name, new ScriptEventArgs());
20 | }
21 |
22 | ///
23 | /// Raise an event with the specified name and arguments.
24 | ///
25 | /// The name of the event.
26 | /// Event specific arguments.
27 | public void NotifyEvent(string name, ScriptEventArgs args)
28 | {
29 | Events[name]?.Invoke(this, args);
30 | }
31 |
32 | ///
33 | /// Register a script event for the underlying extension.
34 | ///
35 | ///
36 | public void RegisterEvent(string name)
37 | {
38 | Events.Add(name, default(ScriptExtensionEventHandler));
39 | }
40 |
41 | internal virtual void OnThreadAttached()
42 | { }
43 |
44 | internal virtual void OnThreadDetached()
45 | { }
46 |
47 | public virtual void Dispose()
48 | {
49 | ScriptThread.Remove(this);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptExtensionEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TornadoScript.ScriptCore.Game
4 | {
5 | public delegate void ScriptComponentEventHandler(ScriptThread sender, ScriptComponentEventArgs args);
6 |
7 | ///
8 | /// Event args for a script extension event.
9 | ///
10 | public sealed class ScriptComponentEventArgs : EventArgs
11 | {
12 | public ScriptComponentEventArgs(ScriptComponent extension)
13 | {
14 | Extension = extension;
15 | }
16 |
17 | public ScriptComponent Extension { get; private set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptExtensionEventPool.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace TornadoScript.ScriptCore.Game
5 | {
6 | public delegate void ScriptExtensionEventHandler(ScriptExtension sender, ScriptEventArgs e);
7 |
8 | public class ScriptExtensionEventPool : Dictionary
9 | {
10 | public ScriptExtensionEventPool() : base(StringComparer.OrdinalIgnoreCase)
11 | { }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptExtensionPool.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | <<<<<<< HEAD
3 |
4 | =======
5 | using System;
6 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
7 | namespace TornadoScript.ScriptCore.Game
8 | {
9 | public class ScriptExtensionPool : List
10 | {
11 | ///
12 | /// Get an extension from the pool by its type.
13 | ///
14 | ///
15 | ///
16 | public T Get() where T : ScriptComponent
17 | {
18 | for (int i = 0; i < Count; i++)
19 | {
20 | if (this[i] is T item)
21 | {
22 | return item;
23 | }
24 | }
25 |
26 | return null;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptPed.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 |
3 | namespace TornadoScript.ScriptCore.Game
4 | {
5 | ///
6 | /// Represents a ped.
7 | ///
8 | public class ScriptPed : ScriptEntity
9 | {
10 | ///
11 | /// Fired when the ped has entered a vehicle.
12 | ///
13 | public event ScriptEntityEventHandler EnterVehicle;
14 |
15 | ///
16 | /// Fired when the ped has exited a vehicle.
17 | ///
18 | public event ScriptEntityEventHandler ExitVehicle;
19 |
20 |
21 | private int vehicleTicks = 0;
22 |
23 | ///
24 | /// If the ped is a local player/ human.
25 | ///
26 | public bool IsHuman
27 | {
28 | get { return Ref == GTA.Game.Player.Character; }
29 | }
30 |
31 | public ScriptPed(Ped baseRef) : base(baseRef)
32 | { }
33 |
34 | protected virtual void OnEnterVehicle(ScriptEntityEventArgs e)
35 | {
36 | EnterVehicle?.Invoke(this, e);
37 | }
38 |
39 | protected virtual void OnExitVehicle(ScriptEntityEventArgs e)
40 | {
41 | ExitVehicle?.Invoke(this, e);
42 | }
43 |
44 | public override void OnUpdate(int gameTime)
45 | {
46 | if (Ref.IsInVehicle())
47 | {
48 | if (vehicleTicks == 0)
49 | OnEnterVehicle(new ScriptEntityEventArgs(gameTime));
50 | vehicleTicks++;
51 | }
52 |
53 | else
54 | {
55 | if (vehicleTicks > 0)
56 | {
57 | OnExitVehicle(new ScriptEntityEventArgs(gameTime));
58 | vehicleTicks = 0;
59 | }
60 | }
61 |
62 | base.OnUpdate(gameTime);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptPlane.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Native;
3 |
4 | namespace TornadoScript.ScriptCore.Game
5 | {
6 | ///
7 | /// Represents a plane.
8 | ///
9 | public class ScriptPlane : ScriptEntity
10 | {
11 | ///
12 | /// Fired when the vehicle is no longer drivable.
13 | ///
14 | public event ScriptEntityEventHandler Undrivable;
15 |
16 | ///
17 | /// State of the vehicle landing gear.
18 | ///
19 | public LandingGearState LandingGearState
20 | {
21 | get { return (LandingGearState)Function.Call(Hash._GET_VEHICLE_LANDING_GEAR, Ref.Handle); }
22 | set { Function.Call(Hash._SET_VEHICLE_LANDING_GEAR, Ref.Handle, (int)value); }
23 | }
24 |
25 | private int undrivableTicks = 0;
26 |
27 | public ScriptPlane(Vehicle baseRef) : base(baseRef)
28 | { }
29 |
30 | protected virtual void OnUndrivable(ScriptEntityEventArgs e)
31 | {
32 | Undrivable?.Invoke(this, e);
33 | }
34 |
35 | public override void OnUpdate(int gameTime)
36 | {
37 | if (!Ref.IsDriveable)
38 | {
39 | if (undrivableTicks == 0)
40 | OnUndrivable(new ScriptEntityEventArgs(gameTime));
41 |
42 | undrivableTicks++;
43 | }
44 |
45 | else
46 | {
47 | undrivableTicks = 0;
48 | }
49 |
50 | base.OnUpdate(gameTime);
51 | }
52 | }
53 |
54 | public enum LandingGearState
55 | {
56 | Deployed,
57 | Closing,
58 | Opening,
59 | Retracted
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptProp.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 |
3 | namespace TornadoScript.ScriptCore.Game
4 | {
5 | ///
6 | /// Represents a prop.
7 | ///
8 | public class ScriptProp : ScriptEntity
9 | {
10 | public ScriptProp(Prop baseRef) : base(baseRef)
11 | { }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptThread.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 | using GTA;
3 |
4 | namespace TornadoScript.ScriptCore.Game
5 | {
6 | ///
7 | /// Base class for a script thread.
8 | ///
9 | public abstract class ScriptThread : Script
10 | {
11 | ///
12 | /// Script extension pool.
13 | ///
14 | private static ScriptExtensionPool _extensions;
15 |
16 | ///
17 | /// Script vars.
18 | ///
19 | public static ScriptVarCollection Vars { get; private set; }
20 |
21 | protected ScriptThread()
22 | {
23 | _extensions = new ScriptExtensionPool();
24 | Vars = new ScriptVarCollection();
25 | Tick += (s, e) => OnUpdate(GTA.Game.GameTime);
26 | KeyDown += KeyPressedInternal;
27 | }
28 |
29 | ///
30 | /// Get a script extension from the underlying pool by its type.
31 | ///
32 | ///
33 | ///
34 | public static T Get() where T : ScriptExtension
35 | {
36 | return _extensions.Get();
37 | }
38 |
39 | ///
40 | /// Adds a script extension to this thread.
41 | ///
42 | ///
43 | public static void Add(ScriptExtension extension)
44 | {
45 | if (_extensions.Contains(extension)) return;
46 |
47 | extension.RegisterEvent("keydown");
48 |
49 | _extensions.Add(extension);
50 |
51 | extension.OnThreadAttached();
52 | }
53 |
54 | ///
55 | /// Adds a script extension to this thread.
56 | ///
57 | public static void Create() where T : ScriptExtension, new()
58 | {
59 | var extension = Get();
60 |
61 | if (extension != null) return;
62 |
63 | extension = new T();
64 |
65 | Add(extension);
66 | }
67 |
68 | ///
69 | /// Get an extension, or create it if it doesn't exist.
70 | ///
71 | ///
72 | ///
73 | public static T GetOrCreate() where T : ScriptExtension, new()
74 | {
75 | var extension = Get();
76 |
77 | if (extension != null)
78 | return extension;
79 |
80 | extension = new T();
81 |
82 | Add(extension);
83 |
84 | return extension;
85 | }
86 |
87 | internal static void Remove(ScriptExtension extension)
88 | {
89 | extension.OnThreadDetached();
90 |
91 | _extensions.Remove(extension);
92 | }
93 |
94 | ///
95 | /// Register a new script variable and add it to the collection.
96 | ///
97 | ///
98 | /// The name of the var
99 | /// The default (reset) value
100 | ///
101 | public static void RegisterVar(string name, T defaultValue, bool readOnly = false)
102 | {
103 | Vars.Add(name, new ScriptVar(defaultValue, readOnly));
104 | }
105 |
106 | ///
107 | /// Get a script variable attached to this thread.
108 | ///
109 | ///
110 | ///
111 | ///
112 | public static ScriptVar GetVar(string name)
113 | {
114 | return Vars.Get(name);
115 | }
116 |
117 | ///
118 | /// Set the value of a script variable attached to this thread.
119 | ///
120 | ///
121 | ///
122 | ///
123 | ///
124 | public static bool SetVar(string name, T value)
125 | {
126 | var foundVar = GetVar(name);
127 |
128 | if (foundVar.ReadOnly)
129 | return false;
130 |
131 | foundVar.Value = value;
132 |
133 | return true;
134 | }
135 |
136 | internal virtual void KeyPressedInternal(object sender, KeyEventArgs e)
137 | {
138 | foreach (ScriptExtension s in _extensions)
139 | {
140 | s.NotifyEvent("keydown", new ScriptEventArgs(e));
141 | }
142 | }
143 |
144 | ///
145 | /// Updates the thread.
146 | ///
147 | public virtual void OnUpdate(int gameTime)
148 | {
149 | for (int i = 0; i < _extensions.Count; i++)
150 | {
151 | _extensions[i].OnUpdate(gameTime);
152 | }
153 | }
154 |
155 | ///
156 | /// Removes the thread and all extensions.
157 | ///
158 | ///
159 | protected override void Dispose(bool A_0)
160 | {
161 | for (int i = _extensions.Count - 1; i > -1; i--)
162 | {
163 | _extensions[i].Dispose();
164 | }
165 |
166 | base.Dispose(A_0);
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptVar.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace TornadoScript.ScriptCore.Game
3 | {
4 | ///
5 | /// Represents a script variable object.
6 | ///
7 | ///
8 | public class ScriptVar : IScriptVar
9 | {
10 | ///
11 | /// The current value of the script var.
12 | ///
13 | public T Value { get; set; }
14 |
15 | ///
16 | /// The default value of the script var.
17 | ///
18 | public T Default { get; }
19 |
20 | ///
21 | /// Whether the script var is read-only.
22 | ///
23 | public bool ReadOnly { get; }
24 |
25 | ///
26 | /// Initialize the class.
27 | ///
28 | /// The initial value of the variable.
29 | /// Whether the variable is readonly.
30 | public ScriptVar(T value, bool isReadonly)
31 | {
32 | Value = value;
33 | Default = value;
34 | ReadOnly = isReadonly;
35 | }
36 |
37 | ///
38 | /// Initialize the class.
39 | ///
40 | ///
41 | public ScriptVar(T value) : this(value, false)
42 | { }
43 |
44 | ///
45 | /// Implicit conversion from to nested value
46 | ///
47 | ///
48 | public static implicit operator T(ScriptVar var)
49 | {
50 | return var.Value;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ScriptCore/Game/ScriptVarCollection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace TornadoScript.ScriptCore.Game
5 | {
6 | public class ScriptVarCollection : Dictionary
7 | {
8 | public ScriptVarCollection() : base(StringComparer.OrdinalIgnoreCase)
9 | { }
10 |
11 | ///
12 | /// Get an extension from the pool by its type.
13 | ///
14 | ///
15 | ///
16 | public ScriptVar Get(string name)
17 | {
18 | <<<<<<< HEAD
19 | if (TryGetValue(name, out var result))
20 | =======
21 | if (TryGetValue(name, out IScriptVar result))
22 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
23 | {
24 | return result as ScriptVar;
25 | }
26 |
27 | return null;
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ScriptCore/IO/EncryptedFileStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace TornadoScript.ScriptCore.IO
8 | {
9 | public class EncryptedFileStream
10 | {
11 | private readonly FileStream stream;
12 | private readonly string DataHash = "dkfcn7tz";
13 | private readonly string Salt = "Delta0xa44";
14 | private readonly string VIKey = "@pQsQDF6vpfJA84A";
15 |
16 | public EncryptedFileStream(string filePath)
17 | {
18 | stream = new FileStream(filePath, FileMode.OpenOrCreate);
19 | }
20 |
21 | public async void WriteValueAsync(string key, int value)
22 | {
23 | string str = Encrypt(string.Format("{0}-{1}", key, value));
24 |
25 | try
26 | {
27 | int seekPos = 0;
28 |
29 | byte[] buffer = new byte[24];
30 |
31 | while (seekPos < stream.Length)
32 | {
33 | stream.Seek(seekPos, SeekOrigin.Begin);
34 |
35 | await stream.ReadAsync(buffer, 0, 24);
36 |
37 | var line = Decipher(Encoding.ASCII.GetString(buffer));
38 |
39 | var keyVal = line.Substring(0, line.IndexOf('-'));
40 |
41 | if (keyVal == key)
42 | {
43 | using (StreamWriter writer = new StreamWriter(stream))
44 | {
45 | writer.BaseStream.Seek(seekPos, SeekOrigin.Begin);
46 | writer.BaseStream.Write(Encoding.ASCII.GetBytes(str), 0, 24);
47 | }
48 |
49 | return;
50 | }
51 |
52 | seekPos += 24;
53 | }
54 |
55 |
56 | if (stream.CanWrite)
57 | stream.Write(Encoding.ASCII.GetBytes(str), (int)stream.Length, 24);
58 | }
59 |
60 | catch (IOException)
61 | {
62 | Logger.Log("Failed to write to data file.");
63 | }
64 | }
65 |
66 | public async Task ReadValueAsync(string key)
67 | {
68 | try
69 | {
70 | int seekPos = 0;
71 | byte[] buffer = new byte[24];
72 |
73 | while (seekPos < stream.Length)
74 | {
75 | stream.Seek(seekPos, SeekOrigin.Begin);
76 | await stream.ReadAsync(buffer, 0, 24);
77 | var line = Decipher(Encoding.ASCII.GetString(buffer));
78 | var keyVal = line.Substring(0, line.IndexOf('-'));
79 | var value = line.Substring(line.IndexOf('-') + 1);
80 | if (keyVal == key)
81 | return Convert.ToInt32(value);
82 | seekPos += 24;
83 | }
84 |
85 | return 0;
86 | }
87 |
88 | catch (IOException)
89 | {
90 | Logger.Log("Failed to read from stats file.");
91 | return 1;
92 | }
93 | }
94 |
95 | private string Encrypt(string plainText)
96 | {
97 | byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
98 |
99 | byte[] keyBytes = new Rfc2898DeriveBytes(DataHash, Encoding.ASCII.GetBytes(Salt)).GetBytes(256 / 8);
100 | var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.Zeros };
101 | var encryptor = symmetricKey.CreateEncryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
102 |
103 | byte[] cipherTextBytes;
104 |
105 | using (var memoryStream = new MemoryStream())
106 | {
107 | using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
108 | {
109 | cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
110 | cryptoStream.FlushFinalBlock();
111 | cipherTextBytes = memoryStream.ToArray();
112 | cryptoStream.Close();
113 | }
114 |
115 | memoryStream.Close();
116 | }
117 | return Convert.ToBase64String(cipherTextBytes);
118 | }
119 |
120 | private string Decipher(string encryptedText)
121 | {
122 | byte[] cipherTextBytes = Convert.FromBase64String(encryptedText);
123 | byte[] keyBytes = new Rfc2898DeriveBytes(DataHash, Encoding.ASCII.GetBytes(Salt)).GetBytes(256 / 8);
124 | var symmetricKey = new RijndaelManaged() { Mode = CipherMode.CBC, Padding = PaddingMode.None };
125 |
126 | var decryptor = symmetricKey.CreateDecryptor(keyBytes, Encoding.ASCII.GetBytes(VIKey));
127 | var memoryStream = new MemoryStream(cipherTextBytes);
128 | var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);
129 | byte[] plainTextBytes = new byte[cipherTextBytes.Length];
130 |
131 | int decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
132 |
133 | memoryStream.Close();
134 | cryptoStream.Close();
135 |
136 | return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount).TrimEnd("\0".ToCharArray());
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/ScriptCore/IO/IXMLSimpleMetadata.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace TornadoScript.ScriptCore.IO
3 | {
4 | ///
5 | /// Base interface for simple XML data.
6 | ///
7 | public interface IXMLSimpleMetadata
8 | {
9 | XMLSimpleMetadata ParseAttributes(XMLAttributesCollection col);
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ScriptCore/IO/XMLAttributesCollection.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace TornadoScript.ScriptCore.IO
5 | {
6 | ///
7 | /// Class to hold XML attributes.
8 | ///
9 | [Serializable]
10 | public class XMLAttributesCollection : Dictionary
11 | {
12 | public XMLAttributesCollection() : base(StringComparer.OrdinalIgnoreCase)
13 | { }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ScriptCore/IO/XMLSimpleMetadata.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace TornadoScript.ScriptCore.IO
3 | {
4 | ///
5 | /// Class to represent simple XML metadata.
6 | ///
7 | public abstract class XMLSimpleMetadata : IXMLSimpleMetadata
8 | {
9 | ///
10 | /// Method for parsing class members from a
11 | ///
12 | ///
13 | /// The parsed metadata.
14 | public virtual XMLSimpleMetadata ParseAttributes(XMLAttributesCollection c)
15 | {
16 | return this;
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ScriptCore/IO/XMLSimpleParser.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Xml;
3 |
4 | namespace TornadoScript.ScriptCore.IO
5 | {
6 | class XMLSimpleParser
7 | {
8 | ///
9 | /// Lightweight function for grabbing nested xml data in a file.
10 | ///
11 | /// The path to the file.
12 | /// The name of the xml element to be parsed.
13 | ///
14 | public static IEnumerable GetNestedAttributes(string fileName, string dataType)
15 | {
16 | using (XmlReader reader = XmlReader.Create(fileName))
17 | {
18 | while (reader.Read())
19 | {
20 | if (reader.NodeType == XmlNodeType.Element && reader.Name == dataType && reader.HasAttributes)
21 | {
22 | var childAttributes = new XMLAttributesCollection();
23 |
24 | while (reader.MoveToNextAttribute())
25 | {
26 | childAttributes.Add(reader.Name, reader.Value);
27 | }
28 |
29 | yield return childAttributes;
30 | }
31 | }
32 | }
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ScriptCore/Logger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Reflection;
4 | <<<<<<< HEAD
5 | using System.IO;
6 | =======
7 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
8 |
9 | namespace TornadoScript.ScriptCore
10 | {
11 | ///
12 | /// Static logger class that allows direct logging of anything to a text file
13 | ///
14 | public static class Logger
15 | {
16 | public static void Log(string format, params object[] args)
17 | {
18 | File.AppendAllText("TornadoScript.log", "[" + DateTime.Now + "] " + string.Format(format, args) + Environment.NewLine);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ScriptMain/Commands/CommandManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using TornadoScript.ScriptCore.Game;
6 | using TornadoScript.ScriptMain.Frontend;
7 |
8 | namespace TornadoScript.ScriptMain.Commands
9 | {
10 | public class CommandManager : ScriptExtension
11 | {
12 | private readonly Dictionary> _commands =
13 | new Dictionary>();
14 |
15 | private FrontendManager _frontendMgr;
16 |
17 | public CommandManager()
18 | {
19 | <<<<<<< HEAD
20 | AddCommand("spawn", Commands.SpawnVortex);
21 | AddCommand("summon", Commands.SummonVortex);
22 | AddCommand("set", Commands.SetVar);
23 | AddCommand("reset", Commands.ResetVar);
24 | AddCommand("ls", Commands.ListVars);
25 | AddCommand("list", Commands.ListVars);
26 | AddCommand("help", Commands.ShowHelp);
27 | AddCommand("?", Commands.ShowHelp);
28 | =======
29 | AddCommand("set", SetVar);
30 | AddCommand("reset", ResetVar);
31 | AddCommand("ls", ListVars);
32 | AddCommand("list", ListVars);
33 | AddCommand("help", ShowHelp);
34 | AddCommand("?", ShowHelp);
35 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
36 | }
37 |
38 | internal override void OnThreadAttached()
39 | {
40 | _frontendMgr = ScriptThread.GetOrCreate();
41 | _frontendMgr.Events["textadded"] += OnInputEvent;
42 | base.OnThreadAttached();
43 | }
44 |
45 | public void OnInputEvent(object sender, ScriptEventArgs e)
46 | {
47 | var cmd = (string)e.Data;
48 |
49 | if (cmd.Length <= 0) return;
50 |
51 | var stringArray = cmd.Split(' ');
52 |
53 | var command = stringArray[0].ToLower();
54 |
55 | if (!_commands.TryGetValue(command, out var func)) return;
56 |
57 | var args = stringArray.Skip(1).ToArray();
58 |
59 | var text = func?.Invoke(args);
60 |
61 | if (!string.IsNullOrEmpty(text))
62 | _frontendMgr.WriteLine(text);
63 | }
64 |
65 | <<<<<<< HEAD
66 | =======
67 | private static string SetVar(params string[] args)
68 | {
69 | if (args.Length < 2) return "SetVar: Invalid format.";
70 |
71 | var varName = args[0];
72 |
73 |
74 | if (int.TryParse(args[1], out var i))
75 | {
76 | var foundVar = ScriptThread.GetVar(varName);
77 |
78 | if (foundVar != null)
79 | {
80 | return !ScriptThread.SetVar(varName, i) ?
81 | "Failed to set the (integer) variable. Is it readonly?" : null;
82 | }
83 | }
84 |
85 | if (float.TryParse(args[1], out var f))
86 | {
87 | var foundVar = ScriptThread.GetVar(varName);
88 |
89 | if (foundVar != null)
90 | {
91 | return !ScriptThread.SetVar(varName, f) ?
92 | "Failed to set the (float) variable. Is it readonly?" : null;
93 | }
94 | }
95 |
96 | if (bool.TryParse(args[1], out var b))
97 | {
98 | var foundVar = ScriptThread.GetVar(varName);
99 |
100 | if (foundVar != null)
101 | {
102 | return !ScriptThread.SetVar(varName, b) ?
103 | "Failed to set the (bool) variable. Is it readonly?" : null;
104 | }
105 | }
106 |
107 | return "Variable '" + args[0] + "' not found.";
108 | }
109 |
110 | private static string ResetVar(params string[] args)
111 | {
112 | if (args.Length < 1) return "ResetVar: Invalid format.";
113 |
114 | var varName = args[0];
115 |
116 |
117 | if (int.TryParse(args[1], out var i))
118 | {
119 | var foundVar = ScriptThread.GetVar(varName);
120 |
121 | if (foundVar != null)
122 | {
123 | foundVar.Value = foundVar.Default;
124 |
125 | return null;
126 | }
127 | }
128 |
129 |
130 | if (float.TryParse(args[1], out var f))
131 | {
132 | var foundVar = ScriptThread.GetVar(varName);
133 |
134 | if (foundVar != null)
135 | {
136 | foundVar.Value = foundVar.Default;
137 |
138 | return null;
139 | }
140 | }
141 |
142 | if (bool.TryParse(args[1], out var b))
143 | {
144 | var foundVar = ScriptThread.GetVar(varName);
145 |
146 | if (foundVar == null) return "Variable '" + args[0] + "' not found.";
147 |
148 | foundVar.Value = foundVar.Default;
149 |
150 | return null;
151 | }
152 |
153 | return "Variable '" + args[0] + "' not found.";
154 | }
155 |
156 | private static string ListVars(params string[] args)
157 | {
158 | var foundCount = 0;
159 |
160 | var frontend = ScriptThread.Get();
161 |
162 | foreach (var var in ScriptThread.Vars)
163 | {
164 | frontend.WriteLine(var.Key + (var.Value.ReadOnly ? " (read-only) " : ""));
165 |
166 | foundCount++;
167 | }
168 |
169 | return "Found " + foundCount + " vars.";
170 | }
171 |
172 | private static string ShowHelp(params string[] args)
173 | {
174 | var frontend = ScriptThread.Get();
175 |
176 | frontend.WriteLine("~r~set~w~: Set a variable\t\t~r~reset~w~: Reset a variable\t\t~r~ls~w~: List all vars");
177 |
178 | return "Commands:";
179 | }
180 |
181 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
182 | public void AddCommand(string name, Func command)
183 | {
184 | _commands.Add(name, command);
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/ScriptMain/Commands/Commands.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Native;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using TornadoScript.ScriptCore;
9 | using TornadoScript.ScriptCore.Game;
10 | using TornadoScript.ScriptMain.Frontend;
11 | using TornadoScript.ScriptMain.Script;
12 |
13 | namespace TornadoScript.ScriptMain.Commands
14 | {
15 | public static class Commands
16 | {
17 | public static string SetVar(params string[] args)
18 | {
19 | if (args.Length < 2) return "SetVar: Invalid format.";
20 |
21 | var varName = args[0];
22 |
23 |
24 | if (int.TryParse(args[1], out var i))
25 | {
26 | var foundVar = ScriptThread.GetVar(varName);
27 |
28 | if (foundVar != null)
29 | {
30 | return !ScriptThread.SetVar(varName, i) ?
31 | "Failed to set the (integer) variable. Is it readonly?" : null;
32 | }
33 | }
34 |
35 | if (float.TryParse(args[1], out var f))
36 | {
37 | var foundVar = ScriptThread.GetVar(varName);
38 |
39 | if (foundVar != null)
40 | {
41 | return !ScriptThread.SetVar(varName, f) ?
42 | "Failed to set the (float) variable. Is it readonly?" : null;
43 | }
44 | }
45 |
46 | if (bool.TryParse(args[1], out var b))
47 | {
48 | var foundVar = ScriptThread.GetVar(varName);
49 |
50 | if (foundVar != null)
51 | {
52 | return !ScriptThread.SetVar(varName, b) ?
53 | "Failed to set the (bool) variable. Is it readonly?" : null;
54 | }
55 | }
56 |
57 | return "Variable '" + args[0] + "' not found.";
58 | }
59 |
60 | public static string ResetVar(params string[] args)
61 | {
62 | if (args.Length < 1) return "ResetVar: Invalid format.";
63 |
64 | var varName = args[0];
65 |
66 |
67 | if (int.TryParse(args[1], out var i))
68 | {
69 | var foundVar = ScriptThread.GetVar(varName);
70 |
71 | if (foundVar != null)
72 | {
73 | foundVar.Value = foundVar.Default;
74 |
75 | return null;
76 | }
77 | }
78 |
79 |
80 | if (float.TryParse(args[1], out var f))
81 | {
82 | var foundVar = ScriptThread.GetVar(varName);
83 |
84 | if (foundVar != null)
85 | {
86 | foundVar.Value = foundVar.Default;
87 |
88 | return null;
89 | }
90 | }
91 |
92 | if (bool.TryParse(args[1], out var b))
93 | {
94 | var foundVar = ScriptThread.GetVar(varName);
95 |
96 | if (foundVar == null) return "Variable '" + args[0] + "' not found.";
97 |
98 | foundVar.Value = foundVar.Default;
99 |
100 | return null;
101 | }
102 |
103 | return "Variable '" + args[0] + "' not found.";
104 | }
105 |
106 | public static string ListVars(params string[] args)
107 | {
108 | var foundCount = 0;
109 |
110 | var frontend = ScriptThread.Get();
111 |
112 | foreach (var var in ScriptThread.Vars)
113 | {
114 | frontend.WriteLine(var.Key + (var.Value.ReadOnly ? " (read-only) " : ""));
115 |
116 | foundCount++;
117 | }
118 |
119 | return "Found " + foundCount + " vars.";
120 | }
121 |
122 | public static string SummonVortex(params string[] args)
123 | {
124 | var vtxmgr = ScriptThread.Get();
125 |
126 | if (vtxmgr.ActiveVortexCount > 0)
127 | vtxmgr.ActiveVortexList[0].Position = Game.Player.Character.Position;
128 |
129 | return "Vortex summoned";
130 | }
131 |
132 | public static string SpawnVortex(params string[] args)
133 | {
134 | var vtxmgr = ScriptThread.Get();
135 |
136 | Function.Call(Hash.REMOVE_PARTICLE_FX_IN_RANGE, 0f, 0f, 0f, 1000000.0f);
137 |
138 | Function.Call(Hash.SET_WIND, 70.0f);
139 |
140 | var position = Game.Player.Character.Position + Game.Player.Character.ForwardVector * 180f;
141 |
142 | vtxmgr.CreateVortex(position);
143 |
144 | return "Vortex spawned (" + position + ")";
145 | }
146 |
147 | public static string ShowHelp(params string[] args)
148 | {
149 | var frontend = ScriptThread.Get();
150 |
151 | frontend.WriteLine("~r~set~w~: Set a variable\t\t~r~reset~w~: Reset a variable\t\t~r~ls~w~: List all vars~r~spawn~w~: Spawn a tornado vortex\t\t~r~summon~w~: Summon the vortex to your current position\t\t");
152 |
153 | return "Commands:";
154 | }
155 |
156 | }
157 | }
158 |
--------------------------------------------------------------------------------
/ScriptMain/Config/IniFile.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.InteropServices;
2 | using System.Text;
3 |
4 | namespace TornadoScript.ScriptMain.Config
5 | {
6 | ///
7 | /// Create a New INI file to store or load data
8 | ///
9 | public class IniFile
10 | {
11 | public string Path;
12 |
13 | [DllImport("kernel32")]
14 | private static extern long WritePrivateProfileString(string section,
15 | string key, string val, string filePath);
16 | [DllImport("kernel32")]
17 | private static extern int GetPrivateProfileString(string section,
18 | string key, string def, StringBuilder retVal,
19 | int size, string filePath);
20 |
21 | ///
22 | /// INIFile Constructor.
23 | ///
24 | ///
25 | public IniFile(string iniPath)
26 | {
27 | Path = iniPath;
28 | }
29 | ///
30 | /// Write Data to the INI File
31 | ///
32 | ///
33 | /// Section name
34 | ///
35 | /// Key Name
36 | ///
37 | /// Value Name
38 | public void IniWriteValue(string section, string key, string value)
39 | {
40 | WritePrivateProfileString(section, key, value, this.Path);
41 | }
42 |
43 | ///
44 | /// Read Data Value From the Ini File
45 | ///
46 | ///
47 | ///
48 | ///
49 | ///
50 | public string IniReadValue(string section, string key)
51 | {
52 | var temp = new StringBuilder(255);
53 | var i = GetPrivateProfileString(section, key, "", temp,
54 | 255, this.Path);
55 | return temp.ToString();
56 |
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ScriptMain/Config/IniHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.ComponentModel;
3 | using System.Globalization;
4 | using System.IO;
5 | using System.Reflection;
6 | using TornadoScript.ScriptMain.Utility;
7 |
8 | namespace TornadoScript.ScriptMain.Config
9 | {
10 | public static class IniHelper
11 | {
12 | public static readonly string IniPath;
13 | public static readonly IniFile IniFile;
14 |
15 | static IniHelper()
16 | {
17 | IniPath = string.Format("scripts\\{0}.ini",
18 | Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location));
19 | if (!File.Exists(IniPath)) Create();
20 | IniFile = new IniFile(IniPath);
21 | }
22 |
23 | ///
24 | /// Write a string value to the config file at the specified section and key
25 | ///
26 | /// The section in the config file
27 | /// The key of the config string
28 | /// The value of the config string
29 | public static void WriteValue(string section, string key, string value)
30 | {
31 | IniFile.IniWriteValue(section, key, value);
32 | }
33 |
34 | ///
35 | /// Gets a config setting
36 | ///
37 | /// The section of the config file
38 | /// The config setting
39 | ///
40 | ///
41 | public static T GetValue(string section, string key, T defaultValue = default(T))
42 | {
43 | Type type = typeof(T);
44 | if (!type.IsValueType && type != typeof(string))
45 | throw new ArgumentException("Not a known type.");
46 |
47 | var keyValue = IniFile.IniReadValue(section, key);
48 | var tConverter = TypeDescriptor.GetConverter(type);
49 |
50 | if (keyValue.Length > 0 && tConverter.CanConvertFrom(typeof(string)))
51 | {
52 | return (T)tConverter.ConvertFromString(null, CultureInfo.InvariantCulture, keyValue);
53 | }
54 |
55 | return defaultValue;
56 | }
57 |
58 | public static void Create()
59 | {
60 | try
61 | {
62 | if (File.Exists(IniPath)) File.Delete(IniPath);
63 | var list = Helpers.ReadEmbeddedResource(Properties.Resources.TornadoScript);
64 | Helpers.WriteListToFile(list, IniPath);
65 | }
66 |
67 | catch (AccessViolationException)
68 | {
69 | GTA.UI.Notify("TornadoScript failed to write a new INI file. Access to the path " + IniPath + " was denied");
70 | }
71 |
72 | catch (Exception e)
73 | {
74 | GTA.UI.Notify("TornadoScript failed to write a new INI file. " + e.Message);
75 | }
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/ScriptMain/Frontend/FrontendInput.cs:
--------------------------------------------------------------------------------
1 | using System.Drawing;
2 | using GTA;
3 | using GTA.Native;
4 |
5 | namespace TornadoScript.ScriptMain.Frontend
6 | {
7 | public class FrontendInput
8 | {
9 | private const int CursorPulseSpeed = 300;
10 |
11 | private bool _cursorState;
12 |
13 | private bool _active;
14 |
15 | private string _str = "";
16 |
17 | private int _lastCursorPulse, _currentTextWidth;
18 |
19 | private readonly UIText _text = new UIText("", new Point(14, 5), 0.3f);
20 |
21 | private readonly UIRectangle _cursorRect = new UIRectangle(new Point(14, 5), new Size(1, 15), Color.Empty);
22 |
23 | private readonly UIContainer _backgroundContainer = new UIContainer(new Point(20, 20), new Size(600, 30), Color.Empty);
24 |
25 | private readonly string WatermarkText = "Tornado Script Console - Press Backspace to exit";
26 |
27 | ///
28 | /// Initialize the class.
29 | ///
30 | public FrontendInput()
31 | {
32 | _backgroundContainer.Items.Add(_text);
33 | _backgroundContainer.Items.Add(_cursorRect);
34 | }
35 |
36 | ///
37 | /// Add a line of text to the input box.
38 | ///
39 | ///
40 | public void AddLine(string text)
41 | {
42 | Show();
43 | _str = text;
44 | _currentTextWidth = GetTextWidth();
45 | }
46 |
47 | ///
48 | /// Add a single character to the input box.
49 | ///
50 | public void AddChar(char c)
51 | {
52 | Show();
53 | _str += c;
54 | _currentTextWidth = GetTextWidth();
55 | }
56 |
57 | ///
58 | /// Gets the current text in the input box.
59 | ///
60 | ///
61 | public string GetText()
62 | {
63 | return _str;
64 | }
65 |
66 | ///
67 | /// Remove the last character from the input box.
68 | ///
69 | public void RemoveLastChar()
70 | {
71 | if (_str.Length > 0)
72 | {
73 | _str = _str.Substring(0, _str.Length - 1);
74 | _currentTextWidth = GetTextWidth();
75 | }
76 | }
77 |
78 | ///
79 | /// Show the input box.
80 | ///
81 | public void Show()
82 | {
83 | _backgroundContainer.Color = Color.FromArgb(140, 52, 144, 2);
84 |
85 | SetCursorColor(Color.White);
86 | SetTextColor(Color.White);
87 |
88 | _active = true;
89 | }
90 |
91 | ///
92 | /// Hide the input box.
93 | ///
94 | public void Hide()
95 | {
96 | _backgroundContainer.Color = Color.Empty;
97 |
98 | SetCursorColor(Color.Empty);
99 | SetTextColor(Color.Empty);
100 |
101 | _active = false;
102 | }
103 |
104 | ///
105 | /// Clear the active text.
106 | ///
107 | public void Clear()
108 | {
109 | _str = string.Empty;
110 | _currentTextWidth = GetTextWidth();
111 | }
112 |
113 | ///
114 | /// Set the text color.
115 | ///
116 | ///
117 | private void SetTextColor(Color color)
118 | {
119 | _text.Color = color;
120 | }
121 |
122 | ///
123 | /// Set the cursor color.
124 | ///
125 | ///
126 | private void SetCursorColor(Color color)
127 | {
128 | _cursorRect.Color = color;
129 | }
130 |
131 | private int GetTextWidth()
132 | {
133 | Function.Call((Hash)0x54CE8AC98E120CAB, "CELL_EMAIL_BCON");
134 |
135 | Function.Call(Hash._ADD_TEXT_COMPONENT_STRING, _str);
136 |
137 | Function.Call(Hash.SET_TEXT_FONT, (int)_text.Font);
138 |
139 | Function.Call(Hash.SET_TEXT_SCALE, _text.Scale, _text.Scale);
140 |
141 | return (int) (UI.WIDTH * Function.Call((Hash)0x85F061DA64ED2F67, (int)_text.Font));
142 | }
143 |
144 | ///
145 | /// Update and draw the this box.
146 | ///
147 | public void Update(int gameTime)
148 | {
149 | if (!_active) return;
150 |
151 | Game.DisableAllControlsThisFrame(0);
152 |
153 | _text.Caption = _str.Length > 0 ? _str : WatermarkText;
154 |
155 | _cursorRect.Position = new Point(14 + _currentTextWidth, 7);
156 |
157 | if (gameTime - _lastCursorPulse > CursorPulseSpeed && _text.Color.A > 0)
158 | {
159 | _cursorState = !_cursorState;
160 |
161 | _cursorRect.Color = _cursorState ? Color.FromArgb(255, _cursorRect.Color) : Color.FromArgb(0, _cursorRect.Color);
162 |
163 | _lastCursorPulse = gameTime;
164 | }
165 |
166 | _backgroundContainer.Draw();
167 | }
168 | }
169 | }
--------------------------------------------------------------------------------
/ScriptMain/Frontend/FrontendManager.cs:
--------------------------------------------------------------------------------
1 | using System.Windows.Forms;
2 | using System.Windows.Input;
3 | using GTA;
4 | using TornadoScript.ScriptCore.Game;
5 | using TornadoScript.ScriptMain.Utility;
6 |
7 | namespace TornadoScript.ScriptMain.Frontend
8 | {
9 | public class FrontendManager : ScriptExtension
10 | {
11 | private readonly FrontendInput _input = new FrontendInput();
12 |
13 | private readonly FrontendOutput _output = new FrontendOutput();
14 |
15 | private bool _showingConsole;
16 |
17 | private bool _capsLock;
18 |
19 | public FrontendManager()
20 | {
21 | RegisterEvent("textadded");
22 | }
23 |
24 | internal override void OnThreadAttached()
25 | {
26 | Events["keydown"] += OnKeyDown;
27 | base.OnThreadAttached();
28 | }
29 |
30 | private void OnKeyDown(object sender, ScriptEventArgs e)
31 | {
32 | <<<<<<< HEAD
33 | var keyArgs = e.Data as System.Windows.Forms.KeyEventArgs;
34 | =======
35 | var keyArgs = e.Data as KeyEventArgs;
36 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
37 |
38 | if (keyArgs == null) return;
39 |
40 | if (!ScriptThread.GetVar("enableconsole")) return;
41 |
42 | if (keyArgs.KeyCode == Keys.CapsLock)
43 | {
44 | _capsLock = !_capsLock;
45 | }
46 |
47 | if (!_showingConsole)
48 | {
49 | if (keyArgs.KeyCode == ScriptThread.GetVar("toggleconsole"))
50 | {
51 | ShowConsole();
52 | }
53 | }
54 |
55 | else
56 | GetConsoleInput(keyArgs);
57 | }
58 |
59 | public void ShowConsole()
60 | {
61 | if (_showingConsole) return;
62 | _input.Show();
63 | _output.Show();
64 | _output.DisableFadeOut();
65 | _showingConsole = true;
66 | }
67 |
68 | public void HideConsole()
69 | {
70 | if (!_showingConsole) return;
71 | _input.Clear();
72 | _input.Hide();
73 | _output.Hide();
74 | _output.EnableFadeOut();
75 | _showingConsole = false;
76 | }
77 |
78 | public void WriteLine(string format, params object[] args)
79 | {
80 | if (args == null)
81 | _output.WriteLine(format);
82 | else _output.WriteLine(format, args);
83 | }
84 |
85 | <<<<<<< HEAD
86 | private void GetConsoleInput(System.Windows.Forms.KeyEventArgs e)
87 | =======
88 | private void GetConsoleInput(KeyEventArgs e)
89 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
90 | {
91 | var key = KeyInterop.KeyFromVirtualKey((int)e.KeyCode);
92 |
93 | var keyChar = Win32Native.GetCharFromKey(key, (e.Modifiers & Keys.Shift) != 0);
94 |
95 | var capsLock = System.Windows.Forms.Control.IsKeyLocked(Keys.CapsLock);
96 |
97 | if (char.IsLetter(keyChar))
98 | {
99 | if (capsLock || e.Shift)
100 | keyChar = char.ToUpper(keyChar);
101 | else keyChar = char.ToLower(keyChar);
102 |
103 | }
104 |
105 | else
106 | {
107 | switch (e.KeyCode)
108 | {
109 | case Keys.Back:
110 | {
111 | var text = _input.GetText();
112 |
113 | if (text.Length < 1)
114 | {
115 | HideConsole();
116 | }
117 |
118 | _input.RemoveLastChar();
119 |
120 | return;
121 | }
122 |
123 | case Keys.Up:
124 | _output.ScrollUp();
125 | return;
126 |
127 | case Keys.Down:
128 | _output.ScrollDown();
129 | return;
130 |
131 | case Keys.Space:
132 | _input.AddChar(' ');
133 | return;
134 |
135 | case Keys.Enter:
136 | {
137 | var text = _input.GetText();
138 |
139 | NotifyEvent("textadded", new ScriptEventArgs(text));
140 |
141 | _output.WriteLine(text);
142 |
143 | _input.Clear();
144 |
145 | _output.ScrollToTop();
146 |
147 | // HideConsole();
148 |
149 | return;
150 | }
151 |
152 | case Keys.Escape:
153 | HideConsole();
154 | return;
155 | }
156 | }
157 |
158 | if (keyChar != ' ')
159 | {
160 | _input.AddChar(keyChar);
161 | }
162 | }
163 |
164 | public override void OnUpdate(int gameTime)
165 | {
166 | _input.Update(gameTime);
167 |
168 | _output.Update(gameTime);
169 |
170 | if (_showingConsole)
171 | {
172 | if (Game.IsControlJustPressed(0, (GTA.Control) 241) ||
173 | Game.IsControlJustPressed(0, (GTA.Control) 188))
174 | {
175 | _output.ScrollUp();
176 | }
177 |
178 | else if (Game.IsControlJustPressed(0, (GTA.Control) 242) ||
179 | Game.IsControlJustPressed(0, (GTA.Control) 187))
180 | {
181 | _output.ScrollDown();
182 | }
183 | }
184 |
185 | base.OnUpdate(gameTime);
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/ScriptMain/Frontend/FrontendOutput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using GTA;
4 |
5 | namespace TornadoScript.ScriptMain.Frontend
6 | {
7 | public class FrontendOutput
8 | {
9 | private const int TextActiveTime = 10000;
10 |
11 | private readonly UIContainer _backsplash;
12 |
13 | private readonly string[] _messageQueue = new string[20];
14 |
15 | private readonly UIText[] _text = new UIText[10];
16 |
17 | private bool _startFromTop = true;
18 |
19 | private bool _stayOnScreen;
20 |
21 | private int _scrollIndex;
22 |
23 | private int _shownTime;
24 |
25 | private int _linesCount;
26 |
27 | ///
28 | /// Write a new line to the message queue with the given format.
29 | ///
30 | ///
31 | ///
32 | public void WriteLine(string format, params object[] args)
33 | {
34 | WriteLine(string.Format(format, args));
35 | }
36 |
37 | ///
38 | /// Write a new line to the message queue.
39 | ///
40 | ///
41 | public void WriteLine(string text)
42 | {
43 | Show();
44 |
45 | for (var i = _messageQueue.Length - 1; i > 0; i--)
46 | {
47 | _messageQueue[i] = _messageQueue[i - 1];
48 | }
49 |
50 | _messageQueue[0] = $"~4~[{DateTime.Now.ToShortTimeString()}]: {text}";
51 |
52 | _linesCount = Math.Min(_linesCount + 1, _messageQueue.Length);
53 | }
54 |
55 | private void SetTextColor(Color color)
56 | {
57 | foreach (var text in _text)
58 | {
59 | text.Color = color;
60 | }
61 | }
62 |
63 | public FrontendOutput()
64 | {
65 | _backsplash = new UIContainer(new Point(20, 60), new Size(600, 200), Color.Empty);
66 | CreateText();
67 | }
68 |
69 | public void Show()
70 | {
71 | _backsplash.Color =
72 | Color.FromArgb(140, 52, 144, 2);
73 |
74 | SetTextColor(Color.White);
75 |
76 | _shownTime = Game.GameTime;
77 | }
78 |
79 | public void Hide()
80 | {
81 | _backsplash.Color = Color.Empty;
82 |
83 | SetTextColor(Color.Empty);
84 |
85 | _shownTime = 0;
86 | }
87 |
88 | public void ScrollUp()
89 | {
90 | if (_scrollIndex > 0)
91 | _scrollIndex--;
92 | }
93 |
94 | public void ScrollToTop()
95 | {
96 | _scrollIndex = 0;
97 | }
98 |
99 | public void ScrollDown()
100 | {
101 | if (_scrollIndex < _linesCount - 10)
102 | _scrollIndex++;
103 | }
104 |
105 | public void DisableFadeOut()
106 | {
107 | _stayOnScreen = true;
108 | }
109 |
110 | public void EnableFadeOut()
111 | {
112 | _stayOnScreen = false;
113 | }
114 |
115 | private void CreateText()
116 | {
117 | for (var i = 0; i < _text.Length; i++)
118 | {
119 | _text[i] = new UIText(string.Empty, new Point(14, 11 + 18 * i), 0.3f, Color.Empty);
120 | }
121 |
122 | _backsplash.Items.AddRange(_text);
123 | }
124 |
125 | public void Update(int gameTime)
126 | {
127 | if (gameTime > _shownTime + TextActiveTime && !_stayOnScreen)
128 | {
129 | if (_backsplash.Color.A > 0)
130 | _backsplash.Color = Color.FromArgb(Math.Max(0, _backsplash.Color.A - 2), _backsplash.Color);
131 |
132 | foreach (var text in _text)
133 | {
134 | if (text.Color.A > 0)
135 | {
136 | text.Color = Color.FromArgb(Math.Max(0, text.Color.A - 4), text.Color);
137 | }
138 | }
139 | }
140 |
141 | else
142 | {
143 | for (var i = _text.Length - 1; i > -1; i--)
144 | {
145 | _text[i].Caption = _messageQueue[
146 | _startFromTop
147 | ? i + _scrollIndex
148 | : _messageQueue.Length - 1 - i + _scrollIndex] ?? string.Empty;
149 | }
150 | }
151 |
152 | _backsplash.Draw();
153 | }
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/ScriptMain/Memory/MemoryAccess.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using GTA.Native;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Diagnostics;
7 | using System.Drawing;
8 | using System.Runtime.CompilerServices;
9 | using System.Runtime.InteropServices;
10 | using System.Text;
11 |
12 | namespace TornadoScript.ScriptMain.Memory
13 | {
14 | public static unsafe class MemoryAccess
15 | {
16 | private static bool bInitialized = false;
17 |
18 | [UnmanagedFunctionPointer(CallingConvention.ThisCall)]
19 | public delegate IntPtr FwGetAssetIndexFn(IntPtr assetStore, out int index, StringBuilder name);
20 |
21 | [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
22 | public delegate int AddEntityToPoolFn(ulong address);
23 |
24 | public delegate IntPtr GetPooledPtfxAddressFn(int handle);
25 |
26 | private static IntPtr PtfxAssetStorePtr;
27 |
28 | private static IntPtr ScriptEntityPoolAddr, VehiclePoolAddr, PedPoolAddr, ObjectPoolAddr;
29 |
30 | private static FwGetAssetIndexFn FwGetAssetIndex;
31 |
32 | public static AddEntityToPoolFn AddEntityToPool;
33 |
34 | private static readonly uint PtfxColourHash = (uint)Game.GenerateHash("ptxu_Colour");
35 |
36 | private static Dictionary ptfxRulePtrList = new Dictionary();
37 |
38 | /*
39 | struct fwPool
40 | {
41 | void *m_pData;
42 | unsigned __int8 *m_bitMap;
43 | int m_count;
44 | int m_itemSize;
45 | int unkItemIndex;
46 | int nextFreeSlotIndex;
47 | unsigned int m_flags;
48 | char pad1[4];
49 | };
50 | */
51 |
52 | public static void Initialize()
53 | {
54 | #region SetupPTFXAssetStore
55 |
56 | var pattern = new Pattern("\x0F\xBF\x04\x9F\xB9", "xxxxx");
57 |
58 | var result = pattern.Get(0x19);
59 |
60 | if (result != IntPtr.Zero)
61 | {
62 | var rip = result.ToInt64() + 7;
63 | var value = Marshal.ReadInt32(IntPtr.Add(result, 3));
64 | PtfxAssetStorePtr = new IntPtr(rip + value);
65 | }
66 |
67 | #endregion
68 |
69 | #region SetupfwGetAssetIndex
70 |
71 | pattern = new Pattern("\x41\x8B\xDE\x4C\x63\x00", "xxxxx?");
72 |
73 | result = pattern.Get();
74 |
75 | if (result != IntPtr.Zero)
76 | {
77 | var rip = result.ToInt64();
78 | var value = Marshal.ReadInt32(result - 4);
79 | FwGetAssetIndex = Marshal.GetDelegateForFunctionPointer(new IntPtr(rip + value));
80 | }
81 |
82 | // Entity Pool ->
83 |
84 | pattern = new Pattern("\x4C\x8B\x0D\x00\x00\x00\x00\x44\x8B\xC1\x49\x8B\x41\x08", "xxx????xxxxxxx");
85 |
86 | result = pattern.Get(7);
87 |
88 | if (result != IntPtr.Zero)
89 | {
90 | var rip = result.ToInt64();
91 | var value = Marshal.ReadInt32(result - 4);
92 | ScriptEntityPoolAddr = Marshal.ReadIntPtr(new IntPtr(rip + value));
93 |
94 | // UI.ShowSubtitle(ScriptEntityPoolAddr.ToString("X"));
95 | }
96 |
97 | // Vehicle Pool ->
98 |
99 | pattern = new Pattern("\x48\x8B\x05\x00\x00\x00\x00\xF3\x0F\x59\xF6\x48\x8B\x08", "xxx????xxxxxxx");
100 |
101 | result = pattern.Get(7);
102 |
103 | if (result != IntPtr.Zero)
104 | {
105 | var rip = result.ToInt64();
106 | var value = Marshal.ReadInt32(result - 4);
107 | VehiclePoolAddr = Marshal.ReadIntPtr(new IntPtr(rip + value));
108 |
109 | // UI.ShowSubtitle(VehiclePoolAddr.ToString("X"));
110 | }
111 |
112 | // Ped Pool ->
113 |
114 | pattern = new Pattern("\x48\x8B\x05\x00\x00\x00\x00\x41\x0F\xBF\xC8\x0F\xBF\x40\x10", "xxx????xxxxxxxx");
115 |
116 | result = pattern.Get(7);
117 |
118 | if (result != IntPtr.Zero)
119 | {
120 | var rip = result.ToInt64();
121 | var value = Marshal.ReadInt32(result - 4);
122 | PedPoolAddr = Marshal.ReadIntPtr(new IntPtr(rip + value));
123 |
124 | // UI.ShowSubtitle(PedPoolAddr.ToString("X"));
125 | }
126 |
127 | // Object Pool ->
128 |
129 | pattern = new Pattern("\x48\x8B\x05\x00\x00\x00\x00\x8B\x78\x10\x85\xFF", "xxx????xxxxx");
130 |
131 | result = pattern.Get(7);
132 |
133 | if (result != IntPtr.Zero)
134 | {
135 | var rip = result.ToInt64();
136 | var value = Marshal.ReadInt32(result - 4);
137 | ObjectPoolAddr = Marshal.ReadIntPtr(new IntPtr(rip + value));
138 |
139 | // UI.ShowSubtitle(ObjectPoolAddr.ToString("X"));
140 | }
141 |
142 | pattern = new Pattern("\x48\xF7\xF9\x49\x8B\x48\x08\x48\x63\xD0\xC1\xE0\x08\x0F\xB6\x1C\x11\x03\xD8", "xxxxxxxxxxxxxxxxxxx");
143 |
144 | result = pattern.Get();
145 |
146 | if (result != IntPtr.Zero)
147 | {
148 | AddEntityToPool = Marshal.GetDelegateForFunctionPointer(IntPtr.Subtract(result, 0x68));
149 |
150 | //UI.ShowSubtitle(result.ToString("X"));
151 | }
152 |
153 | // WinHelper.CopyTlsValues(WinHelper.GetProcessMainThreadId(), Win32Native.GetCurrentThreadId(), 0xC8, 0xC0, 0xB8);
154 |
155 | #endregion
156 |
157 | bInitialized = true;
158 | }
159 |
160 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
161 | public static Vector3 GetEntityPosition(IntPtr entity)
162 | {
163 | return (Vector3)Marshal.PtrToStructure(entity + 0x90, typeof(Vector3));
164 | }
165 |
166 | public static unsafe IList CollectEntitiesFull()
167 | {
168 | if (bInitialized == false)
169 | return null;
170 |
171 | FwPool * entityPool = (FwPool*)ScriptEntityPoolAddr;
172 |
173 | VehiclePool * vehiclePool = *(VehiclePool**)VehiclePoolAddr;
174 |
175 | GenericPool * pedPool = (GenericPool*)PedPoolAddr;
176 |
177 | GenericPool * objectPool = (GenericPool*)ObjectPoolAddr;
178 |
179 | List list = new List();
180 |
181 | for (uint i = 0; i < vehiclePool->size; i++)
182 | {
183 | if (entityPool->IsFull())
184 | {
185 | break;
186 | }
187 |
188 | if (vehiclePool->IsValid(i))
189 | {
190 | list.Add(new Vehicle(AddEntityToPool(vehiclePool->poolAddress[i])));
191 | }
192 | }
193 |
194 | for (uint i = 0; i < pedPool->size; i++)
195 | {
196 | if (entityPool->IsFull())
197 | {
198 | break;
199 | }
200 |
201 | if (pedPool->IsValid(i))
202 | {
203 | var address = pedPool->GetAddress(i);
204 |
205 | if (address != 0)
206 | {
207 | list.Add(new Ped(AddEntityToPool(address)));
208 | }
209 | }
210 | }
211 |
212 | for (uint i = 0; i < objectPool->size; i++)
213 | {
214 | if (entityPool->IsFull())
215 | {
216 | break;
217 | }
218 |
219 | if (objectPool->IsValid(i))
220 | {
221 | var address = objectPool->GetAddress(i);
222 |
223 | if (address != 0)
224 | {
225 | list.Add(new Ped(AddEntityToPool(address)));
226 | }
227 | }
228 | }
229 |
230 | return list;
231 | }
232 |
233 | public static IEnumerable GetAllEntitiesInternal()
234 | {
235 | if (bInitialized == false)
236 | yield return null;
237 |
238 | var poolItems = Marshal.ReadIntPtr(ScriptEntityPoolAddr);
239 |
240 | var bitMap = Marshal.ReadIntPtr(ScriptEntityPoolAddr + 0x8);
241 |
242 | var count = Marshal.ReadInt32(ScriptEntityPoolAddr + 0x10);
243 |
244 | for (int i = 0; i < count; i++)
245 | {
246 | var bitset = Marshal.ReadByte(bitMap + i);
247 | if ((bitset & 0x80) != 0)
248 | continue;
249 | var handle = (i << 8) + bitset;
250 |
251 | var type = Function.Call(Hash.GET_ENTITY_TYPE, handle);
252 |
253 | switch (type)
254 | {
255 | case 1:
256 | yield return new Ped(handle);
257 | break;
258 | case 2:
259 | yield return new Vehicle(handle);
260 | break;
261 | case 3:
262 | yield return new Prop(handle);
263 | break;
264 |
265 | }
266 | }
267 | }
268 |
269 | private static PgDictionary* GetPtfxRuleDictionary(string ptxAssetName)
270 | {
271 | if (bInitialized == false)
272 | return null;
273 |
274 | var assetStore = Marshal.PtrToStructure(PtfxAssetStorePtr);
275 |
276 | FwGetAssetIndex(PtfxAssetStorePtr, out var index, new StringBuilder(ptxAssetName));
277 |
278 | var ptxFxListPtr = Marshal.ReadIntPtr(assetStore.Items + assetStore.ItemSize * index);
279 |
280 | return (PgDictionary*)Marshal.ReadIntPtr(ptxFxListPtr + 0x48);
281 | }
282 |
283 | public static bool FindPtxEffectRule(PgDictionary* ptxRulesDict, string fxName, out IntPtr result)
284 | {
285 | if (bInitialized == false)
286 | {
287 | result = IntPtr.Zero;
288 | return false;
289 | }
290 |
291 | for (var i = 0; i < ptxRulesDict->ItemsCount; i++)
292 | {
293 | var itAddress = Marshal.ReadIntPtr(ptxRulesDict->Items + i * 8);
294 |
295 | if (itAddress == IntPtr.Zero) continue;
296 |
297 | var szName = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(itAddress + 0x20));
298 |
299 | if (szName != fxName) continue;
300 |
301 | result = itAddress;
302 |
303 | return true;
304 | }
305 |
306 | result = IntPtr.Zero;
307 |
308 | return false;
309 | }
310 |
311 | ///
312 | /// Get emitter by its name for the given asset rule
313 | ///
314 | /// Pointer to the PtfxAssetRule instance
315 | /// Name of the child emitter object
316 | ///
317 | private static PtxEventEmitter* GetPtfxEventEmitterByName(IntPtr ptxAssetRulePtr, string emitterName)
318 | {
319 | if (bInitialized == false)
320 | return null;
321 |
322 | PtxEventEmitter* foundEmitter = null;
323 |
324 | var ptxRule = Marshal.PtrToStructure(ptxAssetRulePtr);
325 |
326 | for (var i = 0; i < ptxRule.EmittersCount; i++)
327 | {
328 | var emitter = ptxRule.Emitters[i];
329 |
330 | var szName = Marshal.PtrToStringAnsi(emitter->SzEmitterName);
331 |
332 | if (szName == emitterName)
333 | {
334 | foundEmitter = emitter;
335 |
336 | break;
337 | }
338 | }
339 |
340 | return foundEmitter;
341 | }
342 |
343 | ///
344 | /// Lightweight function for when we know the emitters index
345 | ///
346 | ///
347 | ///
348 | ///
349 | private static PtxEventEmitter* GetPtfxEventEmitterByIndex(IntPtr ptxAssetRulePtr, int emitterIndex)
350 | {
351 | return (*(PtxEventEmitter***)IntPtr.Add(ptxAssetRulePtr, 0x38))[emitterIndex];
352 | }
353 |
354 | public static void SetPtfxLOD(string baseAsset, string particleName)
355 | {
356 | string key = baseAsset + ':' + particleName;
357 |
358 | if (!ptfxRulePtrList.TryGetValue(key, out var result) &&
359 | !FindPtxEffectRule(GetPtfxRuleDictionary(baseAsset), particleName, out result))
360 | {
361 | return;
362 | }
363 |
364 | ptfxRulePtrList[key] = result;
365 | }
366 |
367 | public static void SetPtfxColor(string baseAsset, string particleName, int emitterIndex, Color newColor)
368 | {
369 | if (bInitialized == false)
370 | return;
371 |
372 | string key = baseAsset + ':' + particleName;
373 |
374 | if (!ptfxRulePtrList.TryGetValue(key, out var result) &&
375 | !FindPtxEffectRule(GetPtfxRuleDictionary(baseAsset), particleName, out result)) {
376 | return;
377 | }
378 |
379 | ptfxRulePtrList[key] = result;
380 |
381 | PtxEventEmitter* emitter = GetPtfxEventEmitterByIndex(result, emitterIndex);
382 |
383 | Debug.Assert(emitter != null);
384 |
385 | SetEmitterColour(emitter, newColor);
386 | }
387 |
388 | private static void SetEmitterColour(PtxEventEmitter* emitter, Color colour)
389 | {
390 | SetEmitterColour(emitter, colour.R, colour.G, colour.B, colour.A);
391 | }
392 |
393 | private static void SetEmitterColour(PtxEventEmitter* emitter, byte red, byte green, byte blue, byte alpha)
394 | {
395 | var r = 1.0f / 255 * red;
396 | var g = 1.0f / 255 * green;
397 | var b = 1.0f / 255 * blue;
398 | var a = 1.0f / 255 * alpha;
399 |
400 | for (var i = 0; i < emitter->ParticleRule->BehavioursCount; i++)
401 | {
402 | Ptxu_Colour* behaviour = emitter->ParticleRule->Behaviours[i];
403 |
404 | if (behaviour->HashName != PtfxColourHash) continue;
405 |
406 | for (var x = 0; x < behaviour->NumFrames; x++)
407 | {
408 | PtxKeyframeProp* keyframe = behaviour->KeyframeProps[x];
409 |
410 | if (keyframe->Current.Items == IntPtr.Zero) continue;
411 |
412 | var items = (PtxVarVector*)keyframe->Current.Items;
413 |
414 | for (var y = 0; y < keyframe->Current.Count; y++)
415 | {
416 | if (items == null) continue;
417 |
418 | items[y].Min.R = r;
419 | items[y].Min.G = g;
420 | items[y].Min.B = b;
421 | items[y].Min.A = a;
422 |
423 | items[y].Max.R = r;
424 | items[y].Max.G = g;
425 | items[y].Max.B = b;
426 | items[y].Max.A = a;
427 | }
428 | }
429 |
430 | break;
431 | }
432 | }
433 | }
434 | }
435 |
--------------------------------------------------------------------------------
/ScriptMain/Memory/NativeTypes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace TornadoScript.ScriptMain.Memory
6 | {
7 | [StructLayout(LayoutKind.Explicit)]
8 | public struct PtfxAssetStore
9 | {
10 | [FieldOffset(0x10)]
11 | public int MaxItems; //0x10-0x14
12 | [FieldOffset(0x18)]
13 | [MarshalAs(UnmanagedType.LPStr)]
14 | public string StoreName;
15 | [FieldOffset(0x38)]
16 | public IntPtr Items; //0x38
17 | [FieldOffset(0x40)]
18 | public IntPtr BitMap; //0x40
19 | [FieldOffset(0x48)]
20 | public int ItemCount; //0x48-0x4C
21 | [FieldOffset(0x4C)]
22 | public int ItemSize;
23 | [FieldOffset(0x60)]
24 | [MarshalAs(UnmanagedType.LPStr)]
25 | public string AssetType; //0x60-0x68
26 | [FieldOffset(0x82)]
27 | public short NextAvailableSlot; //0x82 (hashMap + 0x12)
28 | };
29 |
30 | [StructLayout(LayoutKind.Sequential)]
31 | public struct PtxColour
32 | {
33 | public float R;
34 | public float G;
35 | public float B;
36 | public float A;
37 | }; //sizeof=0x8
38 |
39 | [StructLayout(LayoutKind.Sequential)]
40 | public struct PtxVarVector
41 | {
42 | public PtxColour Min; //0x0-0x8
43 | public PtxColour Max; //0x8-0x10
44 | }; //sizeof=0x10
45 |
46 | [StructLayout(LayoutKind.Sequential)]
47 | public unsafe struct PtxKeyframeVars
48 | {
49 | public PtxVarVector* Vectors;
50 | public short UnkCount;
51 | public short MaxVars;
52 | };
53 |
54 | [StructLayout(LayoutKind.Sequential)]
55 | public unsafe struct PtxKeyframeData
56 | {
57 | public PtxKeyframeVars* Vars;
58 | public short UnkCount;
59 | public short MaxVars;
60 | };
61 |
62 | [StructLayout(LayoutKind.Explicit)]
63 | public struct PtxVarVectorKfp
64 | {
65 | [FieldOffset(0x0)]
66 | public IntPtr SzArg1; //0x0-0x28
67 | [FieldOffset(0x4)]
68 | public IntPtr SzArg2;
69 | [FieldOffset(0x8)]
70 | public IntPtr SzArg3;
71 | [FieldOffset(0xC)]
72 | public IntPtr SzArg4;
73 | [FieldOffset(0x10)]
74 | public IntPtr SzArg5;
75 | [FieldOffset(0x40)]
76 | public PtxVarVector data;
77 | };
78 |
79 | [StructLayout(LayoutKind.Explicit)]
80 | public unsafe struct PtxKeyframeProp
81 | {
82 | [FieldOffset(0x18)]
83 | public PtxKeyframeData* F1;
84 | [FieldOffset(0x20)]
85 | public PtxKeyframeData* F2;
86 | [FieldOffset(0x28)]
87 | public PtxKeyframeData* F3;
88 | [FieldOffset(0x30)]
89 | public PtxKeyframeData* F4;
90 | [FieldOffset(0x70)]
91 | public PgCollection Current; // PtxVarVector* collection
92 | [FieldOffset(0x88)]
93 | public PtxVarVectorKfp Defaults;
94 | };
95 |
96 | [StructLayout(LayoutKind.Explicit)]
97 | public unsafe struct Ptxu_Colour
98 | {
99 | [FieldOffset(0x8)]
100 | public uint HashName; // inherited from ptxBehaviour
101 | [FieldOffset(0x10)]
102 | public PtxKeyframeProp** KeyframeProps; //0x10-0x18 // inherited from ptxBehaviour
103 | [FieldOffset(0x18)]
104 | public short NumFrames; //0x18-0x1A // inherited from ptxBehaviour
105 | [FieldOffset(0x1A)]
106 | public short MaxFrames; //0x1A-0x1C assumed // inherited from ptxBehaviour
107 | [FieldOffset(0xA0)]
108 | public PgCollection RGBA_Min; //0xA0-0xA8 PtxVarVector* collection
109 | [FieldOffset(0x130)]
110 | public PgCollection RGBA_Max; //0x130-0x138 PtxVarVector* collection
111 | [FieldOffset(0x1C0)]
112 | public PgCollection Emissive_Intensity; //0x130-0x138 PtxVarVector* collection
113 | };
114 |
115 | [StructLayout(LayoutKind.Explicit)]
116 | public unsafe struct PtxEffectRule
117 | {
118 | [FieldOffset(0x20)]
119 | [MarshalAs(UnmanagedType.LPStr)]
120 | public string EffectName; //0x20-0x28
121 | [FieldOffset(0x38)]
122 | public PtxEventEmitter** Emitters;
123 | [FieldOffset(0x40)]
124 | public short EmittersCount;
125 | [FieldOffset(0x42)]
126 | public short MaxEmitters;
127 | };
128 |
129 | [StructLayout(LayoutKind.Explicit)]
130 | public unsafe struct PtxParticleRule
131 | {
132 | [FieldOffset(0x20)]
133 | public IntPtr Spawner; //0x20 ptxEffectSpawner
134 | [FieldOffset(0x90)]
135 | public IntPtr Spawner1; //0x90-0x100 ptxEffectSpawner
136 | [FieldOffset(0x128)]
137 | public Ptxu_Colour** Behaviours; //0x128-0x130
138 | [FieldOffset(0x130)]
139 | public short BehavioursCount; //0x130-0x132
140 | [FieldOffset(0x132)]
141 | public short MaxBehaviours; // 0x132-0x134 assumed
142 | };
143 |
144 | [StructLayout(LayoutKind.Explicit)]
145 | public unsafe struct PtxEventEmitter
146 | {
147 | [FieldOffset(0x8)]
148 | public int Index; //0x8-0xC
149 | [FieldOffset(0x18)]
150 | public IntPtr EvolutionParams; //0x18-0x20
151 | [FieldOffset(0x30)]
152 | public IntPtr SzEmitterName; //0x30-0x38
153 | [FieldOffset(0x38)]
154 | public IntPtr SzParticleName; //0x38-0x40
155 | [FieldOffset(0x40)]
156 | public IntPtr EmitterRule; //0x40-0x48
157 | [FieldOffset(0x48)]
158 | public PtxParticleRule* ParticleRule; //0x48-0x50
159 | [FieldOffset(0x50)]
160 | public float MoveSpeedScale; //0x50-0x54
161 | [FieldOffset(0x54)]
162 | public float MoveSpeedScaleModifier; //0x54-0x58
163 | [FieldOffset(0x58)]
164 | public float ParticleScale; //0x58-0x5C
165 | [FieldOffset(0x5C)]
166 | public float ParticleScaleModifier; //0x5C-0x60
167 | };
168 |
169 | [StructLayout(LayoutKind.Explicit)]
170 | public struct PgCollection
171 | {
172 | [FieldOffset(0x0)]
173 | public IntPtr Items; //0x30-0x38
174 | [FieldOffset(0x08)]
175 | public short Count;//0x38-0x3A
176 | [FieldOffset(0xC)]
177 | public short Size;//0x38-0x3A
178 | };
179 |
180 | [StructLayout(LayoutKind.Explicit)]
181 | public struct PgDictionary
182 | {
183 | [FieldOffset(0x30)]
184 | public IntPtr Items; //0x30-0x38
185 | [FieldOffset(0x38)]
186 | public short ItemsCount;//0x38-0x3A
187 | };
188 |
189 | [StructLayout(LayoutKind.Sequential)]
190 | internal struct FwPool
191 | {
192 | public long Items; //0x0-0x8
193 | public IntPtr BitMap; //0x8-0x10
194 | public int Count; //0x10-0x14
195 | public int ItemSize; //0x14-0x18
196 | public int UnkIndex; //0x18-0x1C
197 | public int NextFreeSlot; //0x1C-0x20
198 | public uint Flags; //0x20-0x24
199 | public int Unk; //0x24-0x28
200 |
201 | public long GetMask(int index)
202 | {
203 | long num = Marshal.ReadByte(BitMap + index) & 0x80;
204 | return ~((num | -num) >> 0x3F);
205 | }
206 |
207 | public IntPtr GetAddress(int index)
208 | {
209 | return new IntPtr(GetMask(index) & (Items + ItemSize * index));
210 | }
211 |
212 | public bool IsFull()
213 | {
214 | return Count - (Flags & 0x3FFFFFFF) <= 256;
215 | }
216 | }
217 |
218 | [StructLayout(LayoutKind.Explicit)]
219 | internal unsafe struct GenericPool
220 | {
221 | [FieldOffset(0x00)]
222 | public ulong poolStartAddress;
223 | [FieldOffset(0x08)]
224 | public IntPtr byteArray;
225 | [FieldOffset(0x10)]
226 | public uint size;
227 | [FieldOffset(0x14)]
228 | public uint itemSize;
229 |
230 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
231 | public bool IsValid(uint index)
232 | {
233 | return Mask(index) != 0;
234 | }
235 |
236 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
237 | public ulong GetAddress(uint index)
238 | {
239 | return ((Mask(index) & (poolStartAddress + index * itemSize)));
240 | }
241 |
242 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
243 | private ulong Mask(uint index)
244 | {
245 | unsafe
246 | {
247 | byte* byteArrayPtr = (byte*)byteArray.ToPointer();
248 | long num1 = byteArrayPtr[index] & 0x80;
249 | return (ulong)(~((num1 | -num1) >> 63));
250 | }
251 | }
252 | }
253 |
254 |
255 | [StructLayout(LayoutKind.Explicit)]
256 | internal unsafe struct VehiclePool
257 | {
258 | [FieldOffset(0x00)]
259 | internal ulong* poolAddress;
260 | [FieldOffset(0x08)]
261 | internal uint size;
262 | [FieldOffset(0x30)]
263 | internal uint* bitArray;
264 | [FieldOffset(0x60)]
265 | internal uint itemCount;
266 |
267 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
268 | internal bool IsValid(uint i)
269 | {
270 | return (((bitArray[i >> 5] >> ((int)i & 0x1F)) & 1) != 0) && poolAddress[i] != 0;
271 | }
272 |
273 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
274 | internal ulong GetAddress(uint i)
275 | {
276 | return poolAddress[i];
277 | }
278 | }
279 | }
280 |
--------------------------------------------------------------------------------
/ScriptMain/Memory/Pattern.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using TornadoScript.ScriptMain.Utility;
3 |
4 | namespace TornadoScript.ScriptMain.Memory
5 | {
6 | public sealed unsafe class Pattern
7 | {
8 | private readonly string _bytes, _mask;
9 |
10 | public Pattern(string bytes, string mask)
11 | {
12 | _bytes = bytes;
13 | _mask = mask;
14 | }
15 |
16 | public IntPtr Get(int offset = 0)
17 | {
18 | MODULEINFO module;
19 |
20 | Win32Native.GetModuleInformation(
21 | Win32Native.GetCurrentProcess(),
22 | Win32Native.GetModuleHandle(null), out module, (uint)sizeof(MODULEINFO));
23 |
24 | for (var address = module.LpBaseOfDll.ToInt64();
25 | address < address + module.SizeOfImage; address++)
26 | {
27 | if (ByteCompare((byte*)address, _bytes.ToCharArray(), _mask.ToCharArray()))
28 | {
29 | return new IntPtr(address + offset);
30 | }
31 | }
32 |
33 | return IntPtr.Zero;
34 | }
35 |
36 | private static bool ByteCompare(byte* pData, char[] bMask, char[] szMask)
37 | {
38 | for (int i = 0; i < bMask.Length; i++)
39 | {
40 | if (szMask[i] != '?' && pData[i] != bMask[i])
41 | {
42 | break;
43 | }
44 | else if (i + 1 == bMask.Length)
45 | {
46 | return true;
47 | }
48 | }
49 |
50 | return false;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ScriptMain/Script/TDebris.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using System;
4 | using System.Collections.Generic;
5 | using TornadoScript.ScriptCore.Game;
6 | using TornadoScript.ScriptMain.Utility;
7 |
8 | namespace TornadoScript.ScriptMain.Script
9 | {
10 | ///
11 | /// Ttesting physical tornado debris
12 | ///
13 | class TDebris : ScriptProp
14 | {
15 | private float _radius;
16 |
17 | private int _spawnTime = 0;
18 |
19 | public TornadoVortex Parent { get; set; }
20 |
21 | enum MaterialGroup
22 | {
23 | urban,
24 | desert,
25 | countryside,
26 | dirt
27 | }
28 |
29 | private static readonly List debrisItems = new List
30 | {
31 | "prop_bush_med_02",
32 | "prop_fncwood_16d",
33 | "prop_fncwood_16e",
34 | "prop_railsleepers01"
35 | };
36 |
37 | /* readonly Dictionary> materialDebrisMap = new Dictionary>
38 | {
39 | { MaterialGroup.countryside, // countryside debris objects
40 | new List {
41 | "prop_bush_med_02",
42 | "prop_bush_med_05",
43 | "prop_fncwood_16d",
44 | "prop_fncwood_16e",
45 | "prop_fnclog_02b",
46 | "prop_railsleepers01", }
47 | },
48 |
49 | { MaterialGroup.desert, // desert debris objects
50 | new List {
51 | "prop_joshua_tree_01d",
52 | "prop_bush_med_02",
53 | }
54 | },
55 |
56 | { MaterialGroup.urban, // debris objects found in urban areas (downtown etc.)
57 | new List {
58 | "prop_wallbrick_01",
59 | "prop_fncwood_16d",
60 | "prop_fncwood_16e",
61 | "prop_railsleepers01",
62 | "ng_proc_food_burg02a",
63 | "ng_proc_sodacub_03a",
64 | "prop_fire_hydrant_1",
65 | "prop_fnclink_03c",
66 | "prop_dumpster_02a",
67 | "prop_dumpster_01a",
68 | "ng_proc_block_02a",
69 | "ng_proc_brick_01a",
70 | "prop_bin_01a",
71 | "prop_postbox_01a",
72 | "prop_cablespool_02",
73 | "prop_barrier_work06a",
74 | "prop_roadcone02a",
75 | "prop_sign_road_03g",
76 | "prop_lawnmower_01",
77 | "prop_table_04",
78 | "prop_chair_01a",
79 | "prop_chair_01b",
80 | "prop_table_03_chr",
81 | "prop_rub_binbag_04",
82 | "prop_rub_binbag_05",
83 | "prop_sacktruck_02a" }
84 | }
85 | };*/
86 |
87 | public TDebris(TornadoVortex vortex, Vector3 position, float radius)
88 | : base(Setup(position))
89 | {
90 | Parent = vortex;
91 | _radius = radius;
92 | _spawnTime = Game.GameTime;
93 | PostSetup();
94 | }
95 |
96 | private void PostSetup()
97 | {
98 | var centerPos = Parent.Position + new Vector3(0, 0, 5.0f);
99 |
100 | var angle = Probability.GetFloat(0.0f, (float)Math.PI * 2.0f);
101 |
102 | Ref.Position = centerPos + new Vector3(_radius * (float)Math.Cos(angle), _radius * (float)Math.Sin(angle), 0);
103 | }
104 |
105 | ///
106 | /// Setup the base entity.
107 | ///
108 | ///
109 | ///
110 | private static Prop Setup(Vector3 position)
111 | {
112 | var model = new Model(debrisItems[Probability.GetInteger(0, debrisItems.Count - 1)]);
113 |
114 | if (!model.IsLoaded) model.Request(1000);
115 |
116 | var prop = World.CreateProp(model, position, false, false);
117 |
118 | prop.LodDistance = 1000;
119 |
120 | return prop;
121 | }
122 |
123 | public override void OnUpdate(int gameTime)
124 | {
125 | base.OnUpdate(gameTime);
126 |
127 | if (gameTime - _spawnTime > 6400)
128 | {
129 | Dispose();
130 | }
131 | }
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/ScriptMain/Script/TEntity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using ScriptCore;
7 | using GTA.Math;
8 | using GTA.Native;
9 | using GTA;
10 |
11 | namespace TornadoScript.Script
12 | {
13 | public class TEntity : ScriptEntity
14 | {
15 | private TVortex parent;
16 |
17 | public TEntity(Entity baseRef, TVortex parent) : base(baseRef)
18 | {
19 | this.parent = parent;
20 | }
21 |
22 | public override void OnUpdate(int gameTime)
23 | {
24 | var parentPos = parent.Position;
25 |
26 | float dist = Vector2.Distance(Ref.Position.Vec2(), parentPos.Vec2());
27 |
28 | if (dist > parent.MaxEntityDist || Ref.HeightAboveGround > 300.0f)
29 | {
30 | Dispose();
31 | }
32 |
33 | else
34 | {
35 | var direction = Vector3.Normalize(new Vector3(parentPos.X, parentPos.Y, Ref.Position.Z) - Ref.Position);
36 |
37 | if (dist < parent.InternalForcesDist)
38 | {
39 | direction = -direction;
40 |
41 | Ref.ApplyForceToCenterOfMass(direction * 2.0f);
42 |
43 | var cross = Vector3.Cross(direction * 2.0f, Vector3.Cross(direction, Ref.ForwardVector));
44 |
45 | Ref.ApplyForceToCenterOfMass(Vector3.Normalize(cross));
46 |
47 | Dispose();
48 | }
49 |
50 | else
51 | {
52 | var upDir = Vector3.Normalize(new Vector3(parentPos.X, parentPos.Y, parentPos.Z + 1000.0f) - Ref.Position);
53 |
54 | Ref.ApplyForce(direction, new Vector3(Probability.NextFloat(),
55 | Probability.NextFloat(),
56 | Probability.NextFloat()));
57 |
58 | Ref.ApplyForceToCenterOfMass(direction * parent.ForceScale * (ScriptThread.GetVar("vortexHorzizontalPullForce") / dist));
59 |
60 | Ref.ApplyForceToCenterOfMass(upDir * ScriptThread.GetVar("vortexVerticalPullForce") * (2.0f / dist));
61 |
62 | var cross = Vector3.Cross(direction, Vector3.WorldUp);
63 |
64 | float forceBias = Probability.NextFloat();
65 |
66 | Ref.ApplyForceToCenterOfMass(Vector3.Normalize(cross) * parent.ForceScale * (((forceBias * 0.6f) + 0.16f) + (forceBias / dist))); // move them horizontally relative to the vortex.
67 |
68 | if (Probability.GetBoolean(0.23f))
69 | {
70 | Ref.ApplyForceToCenterOfMass(direction * parent.ForceScale);
71 |
72 | cross = Vector3.Cross(direction, Vector3.Cross(direction, Ref.ForwardVector));
73 |
74 | Ref.ApplyForceToCenterOfMass(Vector3.Normalize(cross) * parent.ForceScale);
75 | }
76 |
77 | if (Ref is Ped && Ref.Handle != Game.Player.Character.Handle && !(Ref as Ped).IsRagdoll)
78 | {
79 | Function.Call(Hash.SET_PED_TO_RAGDOLL, Ref.Handle, 800, 1500, 2, 1, 1, 0);
80 | }
81 | }
82 |
83 | Function.Call(Hash.SET_ENTITY_MAX_SPEED, Ref.Handle, 30.0f);
84 | }
85 |
86 | base.OnUpdate(gameTime);
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/ScriptMain/Script/TFactory.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using GTA.Native;
4 | using System;
5 | using System.IO;
6 | using TornadoScript.ScriptCore;
7 | using TornadoScript.ScriptCore.Game;
8 | using TornadoScript.ScriptMain.Utility;
9 |
10 | namespace TornadoScript.ScriptMain.Script
11 | {
12 | ///
13 | /// Extension to manage the spawning of tornadoes.
14 | ///
15 | public class TornadoFactory : ScriptExtension
16 | {
17 | private WavePlayer _tornadoWarningSiren;
18 |
19 | private WavePlayer _tornadoLowRumble;
20 |
21 | private const int VortexLimit = 30;
22 |
23 | private const int TornadoSpawnDelayBase = 20000;
24 |
25 | private int _spawnDelayAdditive = 0;
26 |
27 | private int _spawnDelayStartTime = 0;
28 |
29 | private int _lastSpawnAttempt;
30 |
31 | public int ActiveVortexCount { get; private set; }
32 |
33 | private bool soundEnabled = true, sirenEnabled = true;
34 |
35 | private readonly TornadoVortex[] _activeVortexList = new TornadoVortex[VortexLimit];
36 |
37 | public TornadoVortex[] ActiveVortexList => _activeVortexList;
38 |
39 | ///
40 | /// Whether we are in the process of spawning a tornado.
41 | /// If this is true, the script will prepare to spawn a tornado based on the
42 | /// set parameters.
43 | ///
44 | private bool spawnInProgress = false;
45 |
46 | private bool delaySpawn = false;
47 |
48 | public TornadoFactory()
49 | {
50 | InitSounds();
51 | }
52 |
53 | private void InitSounds()
54 | {
55 | soundEnabled = ScriptThread.GetVar("soundenabled");
56 |
57 | if (!soundEnabled) return;
58 |
59 | sirenEnabled = ScriptThread.GetVar("sirenenabled");
60 |
61 | SoundLoad("tornado-weather-alert.wav", ref _tornadoWarningSiren, true);
62 |
63 | SoundLoad("rumble-bass-2.wav", ref _tornadoLowRumble, true);
64 | }
65 |
66 | ///
67 | /// Load a sound by name from the working directory of the program
68 | ///
69 | ///
70 | private void SoundLoad(string soundName, ref WavePlayer loadedSound, bool looping = false)
71 | {
72 | string absolutePath =
73 | AppDomain.CurrentDomain.BaseDirectory + "\\TornadoScript\\sounds\\" + soundName;
74 |
75 | if (File.Exists(absolutePath))
76 | {
77 | loadedSound = new WavePlayer(absolutePath);
78 |
79 | loadedSound.SetLoopAudio(looping);
80 | }
81 |
82 | else
83 | Logger.Log("Could not load audio file '{0}'. Expected path: '{1}'", soundName, absolutePath);
84 | }
85 |
86 | ///
87 | /// Create a vortex at the given position.
88 | ///
89 | ///
90 | ///
91 | public TornadoVortex CreateVortex(Vector3 position)
92 | {
93 | if (spawnInProgress) // tornado already spawning in
94 | return null;
95 |
96 | for (var i = _activeVortexList.Length - 1; i > 0; i--)
97 | _activeVortexList[i] = _activeVortexList[i - 1];
98 |
99 | position.Z = World.GetGroundHeight(position) - 10.0f;
100 |
101 | var tVortex = new TornadoVortex(position, false);
102 |
103 | tVortex.Build();
104 |
105 | _activeVortexList[0] = tVortex;
106 |
107 | ActiveVortexCount = Math.Min(ActiveVortexCount + 1, _activeVortexList.Length);
108 |
109 | if (soundEnabled)
110 | {
111 | if (_tornadoLowRumble != null)
112 | {
113 | _tornadoLowRumble.SetVolume(0.0f);
114 |
115 | var volumeLevel = 1.0f - (1.0f / 300.0f * Vector3.Distance2D(position, GameplayCamera.Position));
116 |
117 | volumeLevel = volumeLevel < 0.0f ? 0.0f : volumeLevel > 1.0f ? 1.0f : volumeLevel;
118 |
119 | _tornadoLowRumble.DoFadeIn(5000, volumeLevel);
120 | }
121 | }
122 |
123 | if (ScriptThread.GetVar("notifications"))
124 | {
125 | UI.Notify("Tornado spawned nearby.");
126 | }
127 |
128 | spawnInProgress = true;
129 |
130 | return null;
131 | }
132 |
133 | public override void OnUpdate(int gameTime)
134 | {
135 | //UI.ShowSubtitle("vcount: " + ActiveVortexCount + " spawning: " + spawnInProgress + " delay spawn: " + delaySpawn);
136 |
137 | if (ActiveVortexCount < 1)
138 | {
139 | if (World.Weather == Weather.ThunderStorm && ScriptThread.GetVar("spawnInStorm"))
140 | {
141 | if (!spawnInProgress && Game.GameTime - _lastSpawnAttempt > 1000)
142 | {
143 | if (Probability.GetBoolean(0.05f))
144 | {
145 | _spawnDelayStartTime = Game.GameTime;
146 |
147 | _spawnDelayAdditive = Probability.GetInteger(0, 40);
148 |
149 | Function.Call(Hash.SET_WIND_SPEED, 70.0f); // add suspense :p
150 |
151 | if (soundEnabled && sirenEnabled && _tornadoWarningSiren != null)
152 | {
153 | _tornadoWarningSiren.SetVolume(0.6f);
154 |
155 | _tornadoWarningSiren.Play(true);
156 | }
157 |
158 | if (ScriptThread.GetVar("notifications"))
159 | {
160 | Helpers.NotifyWithIcon("Severe Weather Alert", "Tornado Warning issued for Los Santos and Blaine County", "char_milsite");
161 | }
162 |
163 | spawnInProgress = true;
164 | delaySpawn = true;
165 | }
166 |
167 | _lastSpawnAttempt = Game.GameTime;
168 | }
169 | }
170 |
171 | else
172 | {
173 | delaySpawn = false;
174 | }
175 |
176 | if (delaySpawn)
177 | {
178 | // UI.ShowSubtitle("current: " + (Game.GameTime - _spawnDelayStartTime) + " target: " + (TornadoSpawnDelayBase + _spawnDelayAdditive));
179 |
180 | if (Game.GameTime - _spawnDelayStartTime > (TornadoSpawnDelayBase + _spawnDelayAdditive))
181 | {
182 | spawnInProgress = false;
183 | delaySpawn = false;
184 |
185 | var position = Game.Player.Character.Position + Game.Player.Character.ForwardVector * 100f;
186 |
187 | CreateVortex(position.Around(150.0f).Around(175.0f));
188 | }
189 | }
190 | }
191 |
192 | else
193 | {
194 | if (_activeVortexList[0].DespawnRequested || Game.Player.IsDead && Function.Call(Hash.IS_SCREEN_FADED_OUT))
195 | {
196 | RemoveAll();
197 | }
198 |
199 | else if (soundEnabled)
200 | {
201 | if (_tornadoLowRumble != null)
202 | {
203 | var distance = Vector3.Distance2D(_activeVortexList[0].Position, GameplayCamera.Position); //attenuation factor
204 |
205 | var volumeLevel = 1.0f - 1.0f / 800.0f * distance;
206 |
207 | if (distance < 170.0f)
208 | volumeLevel += 0.087f * (2.219f * volumeLevel);
209 |
210 | volumeLevel = volumeLevel < 0.0f ? 0.0f : volumeLevel > 1.0f ? 1.0f : volumeLevel;
211 |
212 | _tornadoLowRumble.SetVolume(volumeLevel);
213 | }
214 | }
215 | }
216 |
217 | base.OnUpdate(gameTime);
218 | }
219 |
220 | public void RemoveAll()
221 | {
222 | spawnInProgress = false;
223 |
224 | if (_tornadoWarningSiren != null && _tornadoWarningSiren.IsPlaying())
225 | _tornadoWarningSiren.DoFadeOut(3000, 0.0f);
226 |
227 | if (_tornadoLowRumble != null && _tornadoLowRumble.IsPlaying())
228 | _tornadoLowRumble.DoFadeOut(3000, 0.0f);
229 |
230 | for (var i = 0; i < ActiveVortexCount; i++)
231 | {
232 | _activeVortexList[i].Dispose();
233 |
234 | _activeVortexList[i] = null;
235 | }
236 |
237 |
238 | ActiveVortexCount = 0;
239 | }
240 |
241 | public override void Dispose()
242 | {
243 | for (var i = 0; i < ActiveVortexCount; i++)
244 | {
245 | _activeVortexList[i].Dispose();
246 | }
247 |
248 | base.Dispose();
249 | }
250 | }
251 | }
252 |
--------------------------------------------------------------------------------
/ScriptMain/Script/TParticle.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using GTA.Native;
4 | using System;
5 | using TornadoScript.ScriptCore.Game;
6 | using TornadoScript.ScriptMain.Utility;
7 |
8 | namespace TornadoScript.ScriptMain.Script
9 | {
10 | ///
11 | /// Represents a particle in the tornado.
12 | ///
13 | public sealed class TornadoParticle : ScriptProp
14 | {
15 | public int LayerIndex { get; }
16 |
17 | public TornadoVortex Parent { get; set; }
18 |
19 | public bool IsCloud { get; }
20 |
21 | private Vector3 _centerPos;
22 |
23 | private readonly Vector3 _offset;
24 |
25 | private readonly Quaternion _rotation;
26 |
27 | private readonly LoopedParticle _ptfx;
28 |
29 | private readonly float _radius;
30 |
31 | private float _angle, _layerMask;
32 |
33 | ///
34 | /// Instantiate the class.
35 | ///
36 | ///
37 | ///
38 | ///
39 | ///
40 | ///
41 | ///
42 | ///
43 | public TornadoParticle(TornadoVortex vortex, Vector3 position, Vector3 angle, string fxAsset, string fxName, float radius, int layerIdx, bool isCloud = false)
44 | : base(Setup(position))
45 | {
46 | Parent = vortex;
47 | _centerPos = position;
48 | _rotation = MathEx.Euler(angle);
49 | _ptfx = new LoopedParticle(fxAsset, fxName);
50 | _radius = radius;
51 | _offset = new Vector3(0, 0, ScriptThread.GetVar("vortexLayerSeperationScale") * layerIdx);
52 | LayerIndex = layerIdx;
53 | IsCloud = isCloud;
54 | PostSetup();
55 | }
56 |
57 | private void PostSetup()
58 | {
59 | _layerMask = 1.0f - (float)LayerIndex / (ScriptThread.GetVar("vortexMaxParticleLayers") * 4);
60 |
61 | _layerMask *= 0.1f * LayerIndex;
62 |
63 | _layerMask = 1.0f - _layerMask;
64 |
65 | if (_layerMask <= 0.3f)
66 | _layerMask = 0.3f;
67 | }
68 |
69 | ///
70 | /// Setup the base entity.
71 | ///
72 | ///
73 | ///
74 | private static Prop Setup(Vector3 position)
75 | {
76 | var model = new Model("prop_beachball_02");
77 |
78 | if (!model.IsLoaded) model.Request(1000);
79 |
80 | var prop = World.CreateProp(model, position, false, false);
81 |
82 | Function.Call(Hash.SET_ENTITY_COLLISION, prop.Handle, 0, 0);
83 |
84 | prop.IsVisible = false;
85 |
86 | return prop;
87 | }
88 |
89 | ///
90 | /// Set the center position that the particle should rotate around.
91 | ///
92 | ///
93 | public void SetPosition(Vector3 center)
94 | {
95 | _centerPos = center;
96 | }
97 |
98 | ///
99 | /// Set the particle scale.
100 | ///
101 | ///
102 | public void SetScale(float scale)
103 | {
104 | _ptfx.Scale = scale;
105 | }
106 |
107 | public override void OnUpdate(int gameTime)
108 | {
109 | /* if (Parent == null)
110 | {
111 | Dispose();
112 | }
113 |
114 | else
115 | {*/
116 | _centerPos = Parent.Position + _offset;
117 |
118 | if (Math.Abs(_angle) > Math.PI * 2.0f)
119 | {
120 | _angle = 0.0f;
121 | }
122 |
123 | Ref.Position = _centerPos +
124 | MathEx.MultiplyVector(new Vector3(_radius * (float)Math.Cos(_angle), _radius * (float)Math.Sin(_angle), 0), _rotation);
125 |
126 | if (IsCloud)
127 | {
128 | _angle -= ScriptThread.GetVar("vortexRotationSpeed") * 0.16f * Game.LastFrameTime;
129 | }
130 | else
131 | {
132 | _angle -= ScriptThread.GetVar("vortexRotationSpeed") * _layerMask * Game.LastFrameTime;
133 | }
134 |
135 | // }
136 |
137 | base.OnUpdate(gameTime);
138 | }
139 |
140 | public void StartFx(float scale)
141 | {
142 | if (!_ptfx.IsLoaded)
143 | {
144 | _ptfx.Load();
145 | }
146 |
147 | _ptfx.Start(this, scale);
148 |
149 | // _ptfx.Alpha = 0.5f;
150 | }
151 |
152 | public void RemoveFx()
153 | {
154 | _ptfx.Remove();
155 | }
156 |
157 | public override void Dispose()
158 | {
159 | RemoveFx();
160 | base.Dispose();
161 | }
162 | }
163 | }
--------------------------------------------------------------------------------
/ScriptMain/Script/TScript.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Native;
3 | using System.Windows.Forms;
4 | using TornadoScript.ScriptCore.Game;
5 | using TornadoScript.ScriptMain.Commands;
6 | using TornadoScript.ScriptMain.Config;
7 | using TornadoScript.ScriptMain.Memory;
8 | using TornadoScript.ScriptMain.Utility;
9 |
10 | namespace TornadoScript.ScriptMain.Script
11 | {
12 | public class MainScript : ScriptThread
13 | {
14 | private readonly TornadoFactory _factory;
15 |
16 | public MainScript()
17 | {
18 | RegisterVars();
19 | SetupAssets();
20 | _factory = GetOrCreate();
21 | GetOrCreate();
22 | KeyDown += KeyPressed;
23 | }
24 |
25 | private static void SetupAssets()
26 | {
27 | MemoryAccess.Initialize();
28 |
29 | if (GetVar("vortexParticleMod"))
30 | {
31 | Function.Call(Hash.REQUEST_NAMED_PTFX_ASSET, "core"); // asset must be loaded before we can access its internal handle
32 | MemoryAccess.SetPtfxColor("core", "ent_amb_smoke_foundry", 1, System.Drawing.Color.Black);
33 | MemoryAccess.SetPtfxColor("core", "ent_amb_smoke_foundry", 2, System.Drawing.Color.Black);
34 | }
35 | }
36 |
37 | private static void RegisterVars()
38 | {
39 | RegisterVar("toggleconsole", Keys.T, true);
40 | RegisterVar("enableconsole", IniHelper.GetValue("Other", "EnableConsole", false));
41 | RegisterVar("notifications", IniHelper.GetValue("Other", "Notifications", true));
42 | RegisterVar("spawninstorm", IniHelper.GetValue("Other", "SpawnInStorm", true));
43 | RegisterVar("soundenabled", IniHelper.GetValue("Other", "SoundEnabled", true));
44 | RegisterVar("sirenenabled", IniHelper.GetValue("Other", "SirenEnabled", true));
45 | RegisterVar("togglescript", IniHelper.GetValue("KeyBinds", "ToggleScript", Keys.F6), true);
46 | RegisterVar("enablekeybinds", IniHelper.GetValue("KeyBinds", "KeybindsEnabled", true));
47 | RegisterVar("multiVortex", IniHelper.GetValue("VortexAdvanced", "MultiVortexEnabled", true));
48 | RegisterVar("vortexMovementEnabled", IniHelper.GetValue("Vortex", "MovementEnabled", true));
49 | RegisterVar("vortexMoveSpeedScale", IniHelper.GetValue("Vortex", "MoveSpeedScale", 1.0f));
50 | RegisterVar("vortexTopEntitySpeed", IniHelper.GetValue("Vortex", "MaxEntitySpeed", 40.0f));
51 | RegisterVar("vortexMaxEntityDist", IniHelper.GetValue("Vortex", "MaxEntityDistance", 57.0f));
52 | RegisterVar("vortexHorizontalPullForce", IniHelper.GetValue("Vortex", "HorizontalForceScale", 1.7f));
53 | RegisterVar("vortexVerticalPullForce", IniHelper.GetValue("Vortex", "VerticalForceScale", 2.29f));
54 | RegisterVar("vortexRotationSpeed", IniHelper.GetValue("Vortex", "RotationSpeed", 2.4f));
55 | RegisterVar("vortexRadius", IniHelper.GetValue("Vortex", "VortexRadius", 9.40f));
56 | RegisterVar("vortexReverseRotation", IniHelper.GetValue("Vortex", "ReverseRotation", false));
57 | RegisterVar("vortexMaxParticleLayers", IniHelper.GetValue("VortexAdvanced", "MaxParticleLayers", 48));
58 | RegisterVar("vortexParticleCount", IniHelper.GetValue("VortexAdvanced", "ParticlesPerLayer", 9));
59 | RegisterVar("vortexLayerSeperationScale", IniHelper.GetValue("VortexAdvanced", "LayerSeperationAmount", 22.0f));
60 | RegisterVar("vortexParticleName", IniHelper.GetValue("VortexAdvanced", "ParticleName", "ent_amb_smoke_foundry"));
61 | RegisterVar("vortexParticleAsset", IniHelper.GetValue("VortexAdvanced", "ParticleAsset", "core"));
62 | RegisterVar("vortexParticleMod", IniHelper.GetValue("VortexAdvanced", "ParticleMod", true));
63 | RegisterVar("vortexEnableCloudTopParticle", IniHelper.GetValue("VortexAdvanced", "CloudTopEnabled", true));
64 | RegisterVar("vortexEnableCloudTopParticleDebris", IniHelper.GetValue("VortexAdvanced", "CloudTopDebrisEnabled", true));
65 | RegisterVar("vortexEnableSurfaceDetection", IniHelper.GetValue("VortexAdvanced", "EnableSurfaceDetection", true));
66 | RegisterVar("vortexUseEntityPool", IniHelper.GetValue("VortexAdvanced", "UseInternalPool", true));
67 | }
68 |
69 | private void KeyPressed(object sender, KeyEventArgs e)
70 | {
71 | if (!GetVar("enablekeybinds")) return;
72 |
73 | if (e.KeyCode != GetVar("togglescript")) return;
74 |
75 | if (_factory.ActiveVortexCount > 0 && !GetVar("multiVortex"))
76 | {
77 | _factory.RemoveAll();
78 | }
79 |
80 | else
81 | {
82 | Function.Call(Hash.REMOVE_PARTICLE_FX_IN_RANGE, 0f, 0f, 0f, 1000000.0f);
83 |
84 | Function.Call(Hash.SET_WIND, 70.0f);
85 |
86 | var position = Game.Player.Character.Position + Game.Player.Character.ForwardVector * 180f;
87 |
88 | _factory.CreateVortex(position);
89 | }
90 | }
91 |
92 | private bool didInitTlsAlloc = false;
93 |
94 | public override void OnUpdate(int gameTime)
95 | {
96 | if (!didInitTlsAlloc)
97 | {
98 | WinHelper.CopyTlsValues(WinHelper.GetProcessMainThreadId(), Win32Native.GetCurrentThreadId(), 0xC8, 0xC0, 0xB8);
99 | didInitTlsAlloc = true;
100 | }
101 |
102 | base.OnUpdate(gameTime);
103 | }
104 |
105 | private static void ReleaseAssets()
106 | {
107 | //
108 | }
109 |
110 | protected override void Dispose(bool a0)
111 | {
112 | Function.Call(Hash.REMOVE_PARTICLE_FX_IN_RANGE, 0f, 0f, 0f, 1000000.0f);
113 |
114 | ReleaseAssets();
115 |
116 | base.Dispose(a0);
117 | }
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/ScriptMain/Script/TVortex.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using GTA.Native;
4 | using System;
5 | using System.Collections.Generic;
6 | using System.Drawing;
7 | using System.Runtime.CompilerServices;
8 | using TornadoScript.ScriptCore.Game;
9 | using TornadoScript.ScriptMain.Memory;
10 | using TornadoScript.ScriptMain.Utility;
11 |
12 | namespace TornadoScript.ScriptMain.Script
13 | {
14 | public class TornadoVortex : ScriptExtension
15 | {
16 | ///
17 | /// Scale of the vortex forces.
18 | ///
19 | public float ForceScale { get; } = 3.0f;
20 |
21 | ///
22 | /// Maximum distance entites must be from the vortex before we start using internal vortext forces on them.
23 | ///
24 | public float InternalForcesDist { get; } = 5.0f;
25 |
26 | readonly List _particles = new List();
27 |
28 | private int _aliveTime, _createdTime, _nextUpdateTime;
29 |
30 | private int _lastDebrisSpawnTime = 0;
31 |
32 | private int _lastFullUpdateTime;
33 |
34 | private int _lifeSpan;
35 |
36 | private struct ActiveEntity
37 | {
38 | public ActiveEntity(Entity entity, float xBias, float yBias)
39 | {
40 | Entity = entity;
41 | XBias = xBias;
42 | YBias = yBias;
43 | IsPlayer = entity == Helpers.GetLocalPed();
44 | }
45 |
46 | public Entity Entity { get; }
47 | public float XBias { get; }
48 | public float YBias { get; }
49 | public bool IsPlayer { get; }
50 | }
51 |
52 | public const int MaxEntityCount = 300;
53 |
54 | private readonly Dictionary _pulledEntities = new Dictionary();
55 |
56 | private readonly List pendingRemovalEntities = new List();
57 |
58 | private Vector3 _position, _destination;
59 |
60 | private bool _despawnRequested;
61 |
62 | public Vector3 Position
63 | {
64 | get { return _position; }
65 | set { _position = value; }
66 | }
67 |
68 | public bool DespawnRequested
69 | {
70 | get { return _despawnRequested; }
71 | set { _despawnRequested = value; }
72 | }
73 |
74 | private readonly Ped _player = Helpers.GetLocalPed();
75 |
76 | private int _lastPlayerShapeTestTime;
77 |
78 | bool _lastRaycastResultFailed;
79 |
80 | private materials lastMaterialTraversed;
81 |
82 | private int lastParticleShapeTestTime = 0;
83 |
84 | private Color particleColorPrev, particleColorGoal;
85 |
86 | private Color particleColor = Color.Black;
87 |
88 | private float particleLerpTime = 0.0f;
89 |
90 | private const float ColorLerpDuration = 200.0f;
91 |
92 | private bool _useInternalEntityArray = false;
93 |
94 | // todo: Add crosswinds at vortex base w/ raycast
95 | public TornadoVortex(Vector3 initialPosition, bool neverDespawn)
96 | {
97 | _position = initialPosition;
98 | _createdTime = Game.GameTime;
99 | _lifeSpan = neverDespawn ? -1 : Probability.GetInteger(160000, 600000);
100 | _useInternalEntityArray = ScriptThread.GetVar("vortexUseEntityPool");
101 | }
102 |
103 | public void ChangeDestination(bool trackToPlayer )
104 | {
105 | for (int i = 0; i < 50; i++)
106 | {
107 | _destination = trackToPlayer ? _player.Position.Around(130.0f) : Helpers.GetRandomPositionFromCoords(_destination, 100.0f);
108 |
109 | _destination.Z = World.GetGroundHeight(_destination) - 10.0f;
110 |
111 | var nearestRoadPos = World.GetNextPositionOnStreet(_destination);
112 |
113 | if (_destination.DistanceTo(nearestRoadPos) < 40.0f && Math.Abs(nearestRoadPos.Z - _destination.Z) < 10.0f)
114 | {
115 | break;
116 | }
117 | }
118 | }
119 |
120 | public void Build()
121 | {
122 | float radius = ScriptThread.GetVar("vortexRadius");
123 |
124 | int particleCount = ScriptThread.GetVar("vortexParticleCount");
125 |
126 | int maxLayers = ScriptThread.GetVar("vortexMaxParticleLayers");
127 |
128 | string particleAsset = ScriptThread.GetVar("vortexParticleAsset");
129 |
130 | string particleName = ScriptThread.GetVar("vortexParticleName");
131 |
132 | bool enableClouds = ScriptThread.GetVar("vortexEnableCloudTopParticle");
133 |
134 | bool enableDebris = ScriptThread.GetVar("vortexEnableCloudTopParticleDebris");
135 |
136 | var multiplier = 360 / particleCount;
137 |
138 | var particleSize = 3.0685f;
139 |
140 | maxLayers = enableClouds ? 12 : maxLayers; // cannot spawn top particles with more than 12 layers!!
141 |
142 | for (var layerIdx = 0; layerIdx < maxLayers; layerIdx++)
143 | {
144 | //var lyrParticleNum = (i > maxLayers - 4 ? particleCount + 5 : particleCount);
145 |
146 | //multiplier = 360 / lyrParticleNum;
147 | for (var angle = 0; angle < (layerIdx > maxLayers - 4 ? particleCount + 5 : particleCount); angle++)
148 | {
149 | // increment the Z axis as we build up.
150 | var position = _position;
151 |
152 | position.Z += ScriptThread.GetVar("vortexLayerSeperationScale") * layerIdx;
153 |
154 | // place the particles at 360 / 10 on the X axis.
155 | var rotation = new Vector3(angle * multiplier, 0, 0);
156 |
157 | TornadoParticle particle;
158 |
159 | bool bIsTopParticle = false;
160 |
161 | if (layerIdx < 2) //debris layer
162 | {
163 | particle = new TornadoParticle(this, position, rotation, "scr_agencyheistb", "scr_env_agency3b_smoke", radius, layerIdx);
164 |
165 | particle.StartFx(4.7f);
166 |
167 | _particles.Add(particle);
168 |
169 | Function.Call(Hash.ADD_SHOCKING_EVENT_FOR_ENTITY, 86, particle.Ref.Handle, 0.0f); // shocking event at outer vorticies
170 | }
171 |
172 | if (enableClouds && layerIdx > maxLayers - 3)
173 | {
174 | if (enableDebris)
175 | {
176 | particle = new TornadoParticle(this, position, rotation, "scr_agencyheistb", "scr_env_agency3b_smoke", radius * 2.2f, layerIdx);
177 |
178 | particle.StartFx(12.7f);
179 |
180 | _particles.Add(particle);
181 | }
182 |
183 | position.Z += 12f;
184 | particleSize += 6.0f;
185 |
186 | radius += 7f;
187 |
188 | bIsTopParticle = true;
189 | }
190 |
191 | particle = new TornadoParticle(this, position, rotation, particleAsset, particleName, radius, layerIdx, bIsTopParticle);
192 |
193 | particle.StartFx(particleSize);
194 |
195 | radius += 0.0799999982118607f * (0.720000028610229f * layerIdx);
196 |
197 | particleSize += 0.00999999977648258f * (0.119999997317791f * layerIdx);
198 |
199 | _particles.Add(particle);
200 |
201 | }
202 | }
203 | }
204 |
205 | private void ReleaseEntity(int entityIdx)
206 | {
207 | pendingRemovalEntities.Add(entityIdx);
208 | }
209 |
210 | ///
211 | /// Adds a to the queue to be processed next frame
212 | ///
213 | ///
214 | private void AddEntity(ActiveEntity entity)
215 | {
216 | _pulledEntities[entity.Entity.Handle] = entity;
217 | }
218 |
219 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
220 | private void CollectNearbyEntities(int gameTime, float maxDistanceDelta)
221 | {
222 | if (gameTime < _nextUpdateTime)
223 | return;
224 |
225 | foreach (var ent in MemoryAccess.CollectEntitiesFull())
226 | {
227 | if (_pulledEntities.Count >= MaxEntityCount) break;
228 |
229 | if (_pulledEntities.ContainsKey(ent.Handle) ||
230 | ent.Position.DistanceTo2D(_position) > maxDistanceDelta + 4.0f || ent.HeightAboveGround > 300.0f) continue;
231 |
232 | if (ent is Ped && /*entities[p].Handle != _player.Handle &&*/ !(ent as Ped).IsRagdoll)
233 | {
234 | Function.Call(Hash.SET_PED_TO_RAGDOLL, ent.Handle, 800, 1500, 2, 1, 1, 0);
235 | }
236 |
237 | AddEntity(new ActiveEntity(ent, 3.0f * Probability.GetScalar(), 3.0f * Probability.GetScalar()));
238 | }
239 |
240 | _nextUpdateTime = gameTime + 600;
241 | }
242 |
243 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
244 | private void CollectNearbyEntitiesInternal(int gameTime, float maxDistanceDelta)
245 | {
246 | if (gameTime - _lastFullUpdateTime > 5000)
247 | {
248 | MemoryAccess.CollectEntitiesFull();
249 |
250 | _lastFullUpdateTime = gameTime;
251 | }
252 |
253 | if (gameTime > _nextUpdateTime )
254 | {
255 | foreach (var ent in MemoryAccess.GetAllEntitiesInternal())
256 | {
257 | if (_pulledEntities.Count >= MaxEntityCount) break;
258 |
259 | if (_pulledEntities.ContainsKey(ent.Handle) ||
260 | ent.Position.DistanceTo2D(_position) > maxDistanceDelta ||
261 | ent.HeightAboveGround > 300.0f) continue;
262 |
263 | if (ent is Ped && !(ent as Ped).IsRagdoll && ent.HeightAboveGround > 2.0f)
264 | {
265 | Function.Call(Hash.SET_PED_TO_RAGDOLL, ent.Handle, 800, 1500, 2, 1, 1, 0);
266 | }
267 |
268 | AddEntity(new ActiveEntity(ent, 3.0f * Probability.GetScalar(), 3.0f * Probability.GetScalar()));
269 | }
270 |
271 | _nextUpdateTime = gameTime + 200;
272 | }
273 | }
274 |
275 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
276 | private void UpdatePulledEntities(int gameTime, float maxDistanceDelta)
277 | {
278 | float verticalForce = ScriptThread.GetVar("vortexVerticalPullForce");
279 |
280 | float horizontalForce = ScriptThread.GetVar("vortexHorizontalPullForce");
281 |
282 | float topSpeed = ScriptThread.GetVar("vortexTopEntitySpeed");
283 |
284 | pendingRemovalEntities.Clear();
285 |
286 | foreach (var e in _pulledEntities)
287 | {
288 | var entity = e.Value.Entity;
289 |
290 | var dist = Vector2.Distance(entity.Position.Vec2(), _position.Vec2());
291 |
292 | if (dist > maxDistanceDelta - 13f || entity.HeightAboveGround > 300.0f)
293 | {
294 | ReleaseEntity(e.Key);
295 | continue;
296 | }
297 |
298 | var targetPos = new Vector3(_position.X + e.Value.XBias, _position.Y + e.Value.YBias, entity.Position.Z);
299 |
300 | var direction = Vector3.Normalize(targetPos - entity.Position);
301 |
302 | var forceBias = Probability.NextFloat();
303 |
304 | var force = ForceScale * (forceBias + forceBias / dist);
305 |
306 | if (e.Value.IsPlayer)
307 | {
308 | verticalForce *= 1.62f;
309 |
310 | horizontalForce *= 1.2f;
311 |
312 | // horizontalForce *= 1.5f;
313 |
314 | if (gameTime - _lastPlayerShapeTestTime > 1000)
315 | {
316 | var raycast = World.Raycast(entity.Position, targetPos, IntersectOptions.Map);
317 |
318 | _lastRaycastResultFailed = raycast.DitHitAnything;
319 |
320 | _lastPlayerShapeTestTime = gameTime;
321 | }
322 |
323 | if (_lastRaycastResultFailed)
324 | continue;
325 | }
326 |
327 | if (entity.Model.IsPlane)
328 | {
329 | force *= 6.0f;
330 | verticalForce *= 6.0f;
331 | }
332 |
333 | // apply a directional force pulling them into the tornado...
334 | entity.ApplyForce(direction * horizontalForce,
335 | new Vector3(Probability.NextFloat(), 0, Probability.GetScalar()));
336 |
337 | var upDir = Vector3.Normalize(new Vector3(_position.X, _position.Y, _position.Z + 1000.0f) -
338 | entity.Position);
339 | // apply vertical forces
340 | entity.ApplyForceToCenterOfMass(upDir * verticalForce);
341 |
342 | var cross = Vector3.Cross(direction, Vector3.WorldUp);
343 |
344 | // move them along side the vortex.
345 | entity.ApplyForceToCenterOfMass(Vector3.Normalize(cross) * force *
346 | horizontalForce);
347 |
348 | Function.Call(Hash.SET_ENTITY_MAX_SPEED, entity.Handle, topSpeed);
349 | }
350 |
351 | foreach (var e in pendingRemovalEntities)
352 | {
353 | _pulledEntities.Remove(e);
354 | }
355 | }
356 |
357 | private static void ApplyDirectionalForce(Entity entity, Vector3 origin, Vector3 direction, float scale)
358 | {
359 | if (Function.Call(Hash.GET_VEHICLE_CLASS, entity) == 16 || entity.HeightAboveGround > 15.0f) return;
360 |
361 | float entityDist = Vector3.Distance(entity.Position, origin);
362 |
363 | float zForce, scaleModifier;
364 |
365 | Vector3 rotationalForce;
366 |
367 | if (entity is Vehicle)
368 | {
369 | zForce = Probability.GetBoolean(0.50f) ? 0.0332f : 0.0318f;
370 | scaleModifier = 22.0f;
371 | rotationalForce = new Vector3(0.0f, 0.1f, 0.40f);
372 | }
373 |
374 | else if (entity is Ped)
375 | {
376 | if (((Ped)entity).IsRagdoll == false)
377 | Function.Call(Hash.SET_PED_TO_RAGDOLL, entity.Handle, 800, 1500, 2, 1, 1, 0);
378 | zForce = 0.0034f;
379 | scaleModifier = 30.0f;
380 | rotationalForce = new Vector3(0.0f, 0.0f, 0.12f);
381 | }
382 |
383 | else
384 | {
385 | zForce = 0.000f;
386 | scaleModifier = 30.0f;
387 | rotationalForce = new Vector3(0.0f, 0.338f, 0.0f);
388 | }
389 |
390 | var force = (direction + new Vector3(0, 0, zForce)) * Math.Min(1.0f, scaleModifier / entityDist) * scale;
391 |
392 | entity.ApplyForce(force, rotationalForce, ForceType.MaxForceRot);
393 | }
394 |
395 | private bool DoEntityCapsuleTest(Vector3 start, Vector3 target, float radius, Entity ignore, out Entity hitEntity)
396 | {
397 | var raycastResult = World.RaycastCapsule(start, target, radius, IntersectOptions.Everything, ignore);
398 |
399 | hitEntity = raycastResult.HitEntity;
400 |
401 | return raycastResult.DitHitEntity;
402 | }
403 |
404 | private void UpdateCrosswinds(int gameTime)
405 | {
406 | var forwardLeft = _position + Vector3.WorldNorth * 100.0f;
407 |
408 | var rearLeft = _position - Vector3.WorldNorth * 100.0f;
409 |
410 | var direction = Vector3.Normalize(rearLeft - forwardLeft);
411 |
412 | Entity target;
413 |
414 | if (DoEntityCapsuleTest(forwardLeft, rearLeft, 22.0f, null, out target))
415 | ApplyDirectionalForce(target, forwardLeft, direction, 4.0f);
416 | }
417 |
418 | private void UpdateSurfaceDetection(int gameTime)
419 | {
420 | if (gameTime - lastParticleShapeTestTime > 1200)
421 | {
422 | var str = ShapeTestEx.RunShapeTest(_position + Vector3.WorldUp * 10.0f,
423 | _position + Vector3.WorldDown * 10.0f, null, IntersectOptions.Everything);
424 |
425 | if (str.HitMaterial != lastMaterialTraversed)
426 | {
427 | switch (lastMaterialTraversed)
428 | {
429 | case materials.sand_track:
430 | case materials.sand_compact:
431 | case materials.sand_dry_deep:
432 | case materials.sand_loose:
433 | case materials.sand_wet:
434 | case materials.sand_wet_deep:
435 | {
436 | particleColorPrev = particleColor;
437 | particleColorGoal = Color.NavajoWhite;
438 | particleLerpTime = 0.0f;
439 | }
440 |
441 | break;
442 | default:
443 | particleColorPrev = particleColor;
444 | particleColorGoal = Color.Black;
445 | particleLerpTime = 0.0f;
446 | break;
447 | }
448 |
449 | lastMaterialTraversed = str.HitMaterial;
450 | }
451 |
452 | lastParticleShapeTestTime = gameTime;
453 | }
454 |
455 | if (particleLerpTime < 1.0f)
456 | {
457 | particleLerpTime += Game.LastFrameTime / ColorLerpDuration;
458 | particleColor = particleColor.Lerp(particleColorGoal, particleLerpTime);
459 | }
460 |
461 | MemoryAccess.SetPtfxColor("core", "ent_amb_smoke_foundry", 0, particleColor);
462 | MemoryAccess.SetPtfxColor("core", "ent_amb_smoke_foundry", 1, particleColor);
463 | MemoryAccess.SetPtfxColor("core", "ent_amb_smoke_foundry", 2, particleColor);
464 | }
465 |
466 | private void UpdateDebrisLayer(materials material)
467 | {
468 | if (Game.GameTime - _lastDebrisSpawnTime > 3000 + Probability.GetInteger(0, 5000))
469 | {
470 | // UI.ShowSubtitle("spawn debris");
471 |
472 | new TDebris(this, _position, ScriptThread.GetVar("vortexRadius"));
473 | }
474 | }
475 |
476 | public override void OnUpdate(int gameTime)
477 | {
478 | if (gameTime - _createdTime > _lifeSpan)
479 | _despawnRequested = true;
480 |
481 | if (ScriptThread.GetVar("vortexEnableSurfaceDetection"))
482 | UpdateSurfaceDetection(gameTime);
483 |
484 | if (ScriptThread.GetVar("vortexMovementEnabled"))
485 | {
486 | if (_destination == Vector3.Zero || _position.DistanceTo(_destination) < 15.0f)
487 | {
488 | ChangeDestination(false);
489 | }
490 |
491 | if (_position.DistanceTo(_player.Position) > 200.0f)
492 | {
493 | ChangeDestination(true);
494 | }
495 |
496 | var vTarget = MathEx.MoveTowards(_position, _destination, ScriptThread.GetVar("vortexMoveSpeedScale") * 0.287f);
497 |
498 | _position = Vector3.Lerp(_position, vTarget, Game.LastFrameTime * 20.0f);
499 | }
500 |
501 | float maxEntityDist = ScriptThread.GetVar("vortexMaxEntityDist");
502 |
503 | CollectNearbyEntities(gameTime, maxEntityDist);
504 |
505 | UpdatePulledEntities(gameTime, maxEntityDist);
506 |
507 | UpdateDebrisLayer(lastMaterialTraversed);
508 | // UpdateCrosswinds(gameTime);
509 | }
510 |
511 | public override void Dispose()
512 | {
513 | _particles.ForEach(x => x.Dispose());
514 |
515 | base.Dispose();
516 | }
517 | }
518 | }
519 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/Audio/LoopStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using NAudio.Wave;
7 |
8 | namespace TornadoScript.ScriptMain.Utility
9 | {
10 | ///
11 | /// Stream for looping playback
12 | ///
13 | public class LoopStream : WaveStream
14 | {
15 | WaveStream sourceStream;
16 |
17 | ///
18 | /// Creates a new Loop stream
19 | ///
20 | /// The stream to read from. Note: the Read method of this stream should return 0 when it reaches the end
21 | /// or else we will not loop to the start again.
22 | public LoopStream(WaveStream sourceStream)
23 | {
24 | this.sourceStream = sourceStream;
25 | this.EnableLooping = true;
26 | }
27 |
28 | ///
29 | /// Use this to turn looping on or off
30 | ///
31 | public bool EnableLooping { get; set; }
32 |
33 | ///
34 | /// Return source stream's wave format
35 | ///
36 | public override WaveFormat WaveFormat
37 | {
38 | get { return sourceStream.WaveFormat; }
39 | }
40 |
41 | ///
42 | /// LoopStream simply returns
43 | ///
44 | public override long Length
45 | {
46 | get { return sourceStream.Length; }
47 | }
48 |
49 | ///
50 | /// LoopStream simply passes on positioning to source stream
51 | ///
52 | public override long Position
53 | {
54 | get { return sourceStream.Position; }
55 | set { sourceStream.Position = value; }
56 | }
57 |
58 | public override int Read(byte[] buffer, int offset, int count)
59 | {
60 | int totalBytesRead = 0;
61 |
62 | while (totalBytesRead < count)
63 | {
64 | int bytesRead = sourceStream.Read(buffer, offset + totalBytesRead, count - totalBytesRead);
65 | if (bytesRead == 0)
66 | {
67 | if (sourceStream.Position == 0 || !EnableLooping)
68 | {
69 | // something wrong with the source stream
70 | break;
71 | }
72 | // loop
73 | sourceStream.Position = 0;
74 | }
75 | totalBytesRead += bytesRead;
76 | }
77 | return totalBytesRead;
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/Audio/SoundManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Windows.Automation;
4 | using TornadoScript.ScriptCore.Game;
5 |
6 | namespace TornadoScript.ScriptMain.Utility
7 | {
8 | public class SoundManager : ScriptExtension
9 | {
10 | private List sounds = new List();
11 |
12 | public SoundManager()
13 | {
14 | SetupWindowHandling();
15 | }
16 |
17 | private void SetupWindowHandling()
18 | {
19 | #if !DEBUG
20 | AutomationFocusChangedEventHandler handler = SoundManager_OnWindowFocusChange;
21 |
22 | Automation.AddAutomationFocusChangedEventHandler(handler);
23 | #endif
24 | }
25 |
26 | public void Add(WavePlayer sound)
27 | {
28 | sounds.Add(sound);
29 | }
30 |
31 | private void SoundManager_OnWindowFocusChange(object source, AutomationFocusChangedEventArgs e)
32 | {
33 | var focusedHandle = new IntPtr(AutomationElement.FocusedElement.Current.NativeWindowHandle);
34 | var mainWindowHandle = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle;
35 |
36 | if (focusedHandle == mainWindowHandle)
37 | {
38 | foreach (var sound in sounds)
39 | {
40 | if (sound.IsPaused())
41 | sound.Play();
42 | }
43 | }
44 |
45 | else
46 | {
47 | foreach (var sound in sounds)
48 | {
49 | if (sound.IsPlaying())
50 | sound.Pause();
51 | }
52 | }
53 | }
54 |
55 | public override void OnUpdate(int gameTime)
56 | {
57 | foreach (var sound in sounds)
58 | {
59 | sound.Update();
60 | }
61 |
62 | base.OnUpdate(gameTime);
63 | }
64 |
65 | public override void Dispose()
66 | {
67 | #if !DEBUG
68 | Automation.RemoveAutomationFocusChangedEventHandler(SoundManager_OnWindowFocusChange);
69 | #endif
70 |
71 | base.Dispose();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/Audio/WavePlayer.cs:
--------------------------------------------------------------------------------
1 | using NAudio.Wave;
2 | using NAudio.Wave.SampleProviders;
3 | using TornadoScript.ScriptCore.Game;
4 |
5 | namespace TornadoScript.ScriptMain.Utility
6 | {
7 | public class WavePlayer
8 | {
9 | private bool soundWasPlaying = false;
10 |
11 | private float fadeStartVolume = 0.0f, fadeTarget = 0.0f;
12 |
13 | private float currentVolume = 0.0f;
14 |
15 | private int fadeTime = 0;
16 |
17 | private bool soundFadingIn = false, soundFadingOut = false;
18 |
19 | private LoopStream _waveStream;
20 |
21 | private SampleChannel _waveChannel;
22 |
23 | private WaveOutEvent _waveOut;
24 |
25 | public WavePlayer(string audioFilename)
26 | {
27 | _waveStream = new LoopStream(new WaveFileReader(audioFilename));
28 |
29 | _waveChannel = new SampleChannel(_waveStream);
30 |
31 | _waveOut = new WaveOutEvent();
32 |
33 | _waveOut.Init(_waveChannel);
34 |
35 | var soundManager = ScriptThread.GetOrCreate();
36 |
37 | soundManager.Add(this);
38 | }
39 |
40 | public void SetVolume(float volumeLevel)
41 | {
42 | // GTA.UI.ShowSubtitle(_waveOut.Volume.ToString());
43 | if (soundFadingIn || soundFadingOut)
44 | return;
45 | _waveChannel.Volume = volumeLevel;
46 | }
47 |
48 | public bool IsPlaying()
49 | {
50 | return _waveOut.PlaybackState == PlaybackState.Playing;
51 | }
52 |
53 | public bool IsPaused()
54 | {
55 | return _waveOut.PlaybackState == PlaybackState.Paused;
56 | }
57 |
58 | public void SetLoopAudio(bool shouldLoopAudio)
59 | {
60 | _waveStream.EnableLooping = shouldLoopAudio;
61 | }
62 |
63 | public void DoFadeIn(int fadeTime, float fadeTarget)
64 | {
65 | this.fadeTime = fadeTime;
66 | this.fadeTarget = fadeTarget;
67 | soundFadingOut = false;
68 | soundFadingIn = true;
69 | currentVolume = _waveChannel.Volume;
70 | _waveOut.Play();
71 | }
72 |
73 |
74 | public void DoFadeOut(int fadeTime, float fadeTarget)
75 | {
76 | this.fadeTime = fadeTime;
77 | this.fadeTarget = fadeTarget;
78 | soundFadingOut = true;
79 | soundFadingIn = false;
80 | currentVolume = _waveChannel.Volume;
81 | _waveOut.Play();
82 | }
83 |
84 | public void Pause()
85 | {
86 | _waveOut.Pause();
87 | }
88 |
89 | public void Stop()
90 | {
91 | _waveOut.Stop();
92 | }
93 |
94 | public void Play(bool fromStart = false)
95 | {
96 | if (fromStart)
97 | _waveStream.CurrentTime = System.TimeSpan.Zero;
98 |
99 | _waveOut.Play();
100 |
101 | soundWasPlaying = true;
102 | }
103 |
104 | public void Update()
105 | {
106 | if (soundFadingIn)
107 | {
108 | if (currentVolume < fadeTarget)
109 | {
110 | currentVolume += GTA.Game.LastFrameTime * (1000.0f / fadeTime);
111 |
112 | currentVolume = currentVolume < 0.0f ? 0.0f : currentVolume > 1.0f ? 1.0f : currentVolume;
113 |
114 | _waveChannel.Volume = currentVolume;
115 | }
116 |
117 | else
118 | soundFadingIn = false;
119 | }
120 |
121 | else if (soundFadingOut)
122 | {
123 | if (currentVolume > fadeTarget)
124 | {
125 | currentVolume -= GTA.Game.LastFrameTime * (1000.0f / fadeTime);
126 |
127 | currentVolume = currentVolume < 0.0f ? 0.0f : currentVolume > 1.0f ? 1.0f : currentVolume;
128 |
129 | _waveChannel.Volume = currentVolume;
130 | }
131 |
132 | else
133 | {
134 | _waveOut.Stop();
135 |
136 | soundFadingOut = false;
137 | }
138 | }
139 | }
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/GameSound.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using GTA.Native;
4 |
5 | namespace TornadoScript.ScriptMain.Utility
6 | {
7 | public class GameSound
8 | {
9 | private int _soundId;
10 | private readonly string _soundSet, _sound;
11 |
12 | public bool Active { get; private set; }
13 |
14 | public GameSound(string sound, string soundSet)
15 | {
16 | Active = false;
17 | _sound = sound;
18 | _soundSet = soundSet;
19 | _soundId = -1;
20 | }
21 |
22 | public static void Load(string audioBank)
23 | {
24 | Function.Call(Hash.REQUEST_SCRIPT_AUDIO_BANK, audioBank, false);
25 | }
26 |
27 | public static void Release(string audioBank)
28 | {
29 | Function.Call(Hash.RELEASE_NAMED_SCRIPT_AUDIO_BANK, audioBank);
30 | }
31 |
32 | public static void Load(GameSound sound)
33 | {
34 | Function.Call(Hash.REQUEST_SCRIPT_AUDIO_BANK, sound._soundSet, false);
35 | }
36 |
37 | public void Play(Entity ent)
38 | {
39 | _soundId = Function.Call(Hash.GET_SOUND_ID);
40 | Function.Call(Hash.PLAY_SOUND_FROM_ENTITY, _soundId, _sound, ent.Handle, 0, 0, 0);
41 | Active = true;
42 | }
43 |
44 | public void Play(Vector3 position, int range)
45 | {
46 | _soundId = Function.Call(Hash.GET_SOUND_ID);
47 | Function.Call(Hash.PLAY_SOUND_FROM_COORD, _soundId, _sound, position.X, position.Y, position.Z, 0, 1, range, 0);
48 | Active = true;
49 | }
50 |
51 | public void Destroy()
52 | {
53 | if (_soundId == -1) return;
54 | Function.Call(Hash.STOP_SOUND, _soundId);
55 | Function.Call(Hash.RELEASE_SOUND_ID, _soundId);
56 | _soundId = -1;
57 | Active = false;
58 | }
59 | }
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Drawing;
4 | using System.IO;
5 | using System.Runtime.CompilerServices;
6 | using GTA;
7 | using GTA.Math;
8 | using GTA.Native;
9 |
10 | namespace TornadoScript.ScriptMain.Utility
11 | {
12 | public static class Helpers
13 | {
14 | public static Ped GetLocalPed()
15 | {
16 | return Game.Player.Character;
17 | }
18 |
19 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
20 | public static Vector2 Vec2(this Vector3 v)
21 | {
22 | return new Vector2(v.X, v.Y);
23 | }
24 |
25 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
26 | public static void ApplyForceToCenterOfMass(this Entity entity, Vector3 force)
27 | {
28 | Function.Call(Hash.APPLY_FORCE_TO_ENTITY_CENTER_OF_MASS, entity.Handle, 1, force.X, force.Y, force.Z, 0, 0, 1, 1);
29 | }
30 |
31 | public static Vector3 GetRandomPositionFromCoords(Vector3 position, float multiplier)
32 | {
33 | float randX, randY;
34 |
35 | int v1 = Function.Call(Hash.GET_RANDOM_INT_IN_RANGE, 0, 3999) / 1000;
36 |
37 | if (v1 == 0)
38 | {
39 | randX = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, 50.0f, 200.0f) * multiplier;
40 | randY = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, -50.0f, 50.0f) * multiplier;
41 | }
42 | else if (v1 == 1)
43 | {
44 | randX = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, 50.0f, 200.0f) * multiplier;
45 | randY = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, -50.0f, 50.0f) * multiplier;
46 | }
47 | else if (v1 == 2)
48 | {
49 | randX = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, -50.0f, -200.0f) * multiplier;
50 | randY = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, 50.0f, 50.0f) * multiplier;
51 | }
52 | else
53 | {
54 | randX = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, 50.0f, -200.0f) * multiplier;
55 | randY = Function.Call(Hash.GET_RANDOM_FLOAT_IN_RANGE, -50.0f, 50.0f) * multiplier;
56 | }
57 | return new Vector3(randX + position.X, randY + position.Y, position.Z);
58 |
59 | }
60 |
61 | public static string[] GetLines(this string s)
62 | {
63 | return s.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
64 | }
65 |
66 | ///
67 | /// Populates a list of strings from an embedded string resource.
68 | ///
69 | /// The string resource (Properties.Resources.ProjectName...)
70 | ///
71 | public static IList ReadEmbeddedResource(string resource)
72 | {
73 | string[] text = resource.GetLines();
74 | return new List(text);
75 | }
76 |
77 | public static float Lerp(this float a, float b, float f)
78 | {
79 | return a * (1.0f - f) + b * f;
80 | }
81 |
82 | public static Color Lerp(this Color source, Color target, double percent)
83 | {
84 | var r = (byte)(source.R + (target.R - source.R) * percent);
85 | var g = (byte)(source.G + (target.G - source.G) * percent);
86 | var b = (byte)(source.B + (target.B - source.B) * percent);
87 |
88 | return Color.FromArgb(source.A, r, g, b);
89 | }
90 |
91 | ///
92 | /// Writes a list of strings to a file at the specified path.
93 | ///
94 | /// The list to write
95 | /// The specified path
96 | public static void WriteListToFile(IList list, string filepath)
97 | {
98 | if (File.Exists(filepath)) File.Delete(filepath);
99 | using (StreamWriter stream = new StreamWriter(filepath))
100 | {
101 | foreach (string line in list)
102 | {
103 | stream.WriteLine(line);
104 | }
105 | }
106 | }
107 |
108 | public static void NotifyWithIcon(string title, string text, string icon)
109 | {
110 | Function.Call(Hash._SET_NOTIFICATION_TEXT_ENTRY, "STRING");
111 | Function.Call(Hash._ADD_TEXT_COMPONENT_STRING, text);
112 | Function.Call(Hash._SET_NOTIFICATION_MESSAGE, icon, icon, false, 4, title, "");
113 | Function.Call(Hash._DRAW_NOTIFICATION, false, true);
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/LoopedParticle.cs:
--------------------------------------------------------------------------------
1 | using GTA;
2 | using GTA.Math;
3 | using GTA.Native;
4 | using Color = System.Drawing.Color;
5 |
6 | namespace TornadoScript.ScriptMain.Utility
7 | {
8 | public class LoopedParticle
9 | {
10 | private float _scale;
11 |
12 | private float _alpha;
13 |
14 | public string AssetName { get; }
15 |
16 | public string FxName { get; }
17 |
18 | public int Handle { get; private set; }
19 |
20 | ///
21 | /// If the particle FX is spawned.
22 | ///
23 | public bool Exists => Handle != -1 && Function.Call(Hash.DOES_PARTICLE_FX_LOOPED_EXIST, Handle);
24 |
25 | ///
26 | /// If the particle FX asset is loaded.
27 | ///
28 | public bool IsLoaded => Function.Call(Hash.HAS_NAMED_PTFX_ASSET_LOADED, AssetName);
29 |
30 | ///
31 | /// Set the particle FX scale.
32 | ///
33 | public float Alpha { get { return _alpha; } set { Function.Call(Hash.SET_PARTICLE_FX_LOOPED_ALPHA, Handle, _alpha = value); } }
34 |
35 | ///
36 | /// Set the particle FX scale.
37 | ///
38 | public float Scale { get { return _scale; } set { Function.Call(Hash.SET_PARTICLE_FX_LOOPED_SCALE, Handle, _scale = value); } }
39 |
40 |
41 | ///
42 | /// Set the particle FX looped colour.
43 | ///
44 | public Color Colour { set { Function.Call(Hash.SET_PARTICLE_FX_LOOPED_COLOUR, Handle, value.R, value.G, value.B, 0); } }
45 |
46 | public LoopedParticle(string assetName, string fxName)
47 | {
48 | Handle = -1;
49 | AssetName = assetName;
50 | FxName = fxName;
51 | }
52 |
53 | ///
54 | /// Load the particle FX asset.
55 | ///
56 | public void Load()
57 | {
58 | Function.Call(Hash.REQUEST_NAMED_PTFX_ASSET, AssetName);
59 | }
60 |
61 | ///
62 | /// Start particle FX on the specified entity.
63 | ///
64 | /// Entity to attach to.
65 | /// Scale of the fx.
66 | /// Optional offset.
67 | /// Optional rotation.
68 | /// Entity bone.
69 | public void Start(Entity entity, float scale, Vector3 offset, Vector3 rotation, Bone? bone)
70 | {
71 | if (Handle != -1) return;
72 |
73 | _scale = scale;
74 |
75 | Function.Call(Hash._SET_PTFX_ASSET_NEXT_CALL, AssetName);
76 |
77 | Handle = bone == null ?
78 | Function.Call(Hash.START_PARTICLE_FX_LOOPED_ON_ENTITY, FxName,
79 | entity, offset.X, offset.Y, offset.Z, rotation.X, rotation.Y, rotation.Z, scale, 0, 0, 1) :
80 | Function.Call(Hash._START_PARTICLE_FX_LOOPED_ON_ENTITY_BONE, FxName,
81 | entity, offset.X, offset.Y, offset.Z, rotation.X, rotation.Y, rotation.Z, (int)bone, scale, 0, 0, 0);
82 | }
83 |
84 | ///
85 | /// Start particle FX on the specified entity.
86 | ///
87 | /// Entity to attach to.
88 | /// Scale of the fx.
89 | public void Start(Entity entity, float scale)
90 | {
91 | Start(entity, scale, Vector3.Zero, Vector3.Zero, null);
92 | }
93 |
94 | ///
95 | /// Start particle FX at the specified position.
96 | ///
97 | /// Position in world space.
98 | /// Scale of the fx.
99 | /// Optional rotation.
100 | public void Start(Vector3 position, float scale, Vector3 rotation)
101 | {
102 | if (Handle != -1) return;
103 |
104 | _scale = scale;
105 |
106 | Function.Call(Hash._SET_PTFX_ASSET_NEXT_CALL, AssetName);
107 |
108 | Handle = Function.Call(Hash.START_PARTICLE_FX_LOOPED_AT_COORD, FxName,
109 | position.X, position.Y, position.Z, rotation.X, rotation.Y, rotation.Z, scale, 0, 0, 0, 0);
110 | }
111 |
112 | ///
113 | /// Start particle FX at the specified position.
114 | ///
115 | /// Position in world space.
116 | /// Scale of the fx.
117 | public void Start(Vector3 position, float scale)
118 | {
119 | Start(position, scale, Vector3.Zero);
120 | }
121 |
122 | ///
123 | /// Set position offsets.
124 | ///
125 | ///
126 | ///
127 | public void SetOffsets(Vector3 offset, Vector3 rotOffset)
128 | {
129 | Function.Call(Hash.SET_PARTICLE_FX_LOOPED_OFFSETS, Handle, offset.X, offset.Y, offset.Z, rotOffset.X, rotOffset.Y, rotOffset.Z);
130 | }
131 |
132 | ///
133 | /// Set custom PTFX evolution variables.
134 | ///
135 | ///
136 | ///
137 | public void SetEvolution(string variableName, float value)
138 | {
139 | Function.Call(Hash.SET_PARTICLE_FX_LOOPED_EVOLUTION, Handle, variableName, value, 0);
140 | }
141 |
142 | ///
143 | /// Remove the particle FX.
144 | ///
145 | public void Remove()
146 | {
147 | if (Handle == -1) return;
148 |
149 | Function.Call(Hash.STOP_PARTICLE_FX_LOOPED, Handle, 0);
150 |
151 | Function.Call(Hash.REMOVE_PARTICLE_FX, Handle, 0);
152 | Handle = -1;
153 | }
154 |
155 | ///
156 | /// Remove the particle FX in range.
157 | ///
158 | public void Remove(Vector3 position, float radius)
159 | {
160 | if (Handle == -1) return;
161 |
162 | Function.Call(Hash.REMOVE_PARTICLE_FX_IN_RANGE, position.X, position.Y, position.Z, radius);
163 | Handle = -1;
164 | }
165 |
166 | ///
167 | /// Unload the loaded particle FX asset.
168 | ///
169 | public void Unload()
170 | {
171 | if (IsLoaded)
172 | Function.Call((Hash)0x5F61EBBE1A00F96D, AssetName);
173 | }
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/MathEx.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using GTA.Math;
4 |
5 | namespace TornadoScript.ScriptMain.Utility
6 | {
7 | public static class MathEx
8 | {
9 | private static Dictionary _cosTable = new Dictionary();
10 |
11 | private static float[] _cos = new float[720];
12 |
13 | private static float[] _sin = new float[720];
14 |
15 | public const double RadToDeg = 180 / Math.PI;
16 |
17 | public const double DegToRad = Math.PI / 180;
18 |
19 | static MathEx()
20 | {
21 | for (int i = 0; i < 360; i++)
22 | {
23 | _cos[i] = (float) Math.Cos(ToRadians(360 - i));
24 | _sin[i] = (float) Math.Sin(ToRadians(360 - i));
25 | _cos[i + 360] = (float)Math.Cos(ToRadians(i));
26 | _sin[i + 360] = (float)Math.Sin(ToRadians(i));
27 | }
28 | }
29 |
30 | public static float Cos(double value)
31 | {
32 | int deg = (int)value.ToDegrees();
33 | return value < 0 ? _cos[-deg] : _cos[deg + 360];
34 | }
35 |
36 | public static float Sin(double value)
37 | {
38 | int deg = (int)value.ToDegrees();
39 | return value < 0 ? _sin[-deg] : _sin[deg + 360];
40 | }
41 |
42 | public static Vector3 MoveTowards(Vector3 current, Vector3 target, float maxDistanceDelta)
43 | {
44 | Vector3 a = target - current;
45 | float magnitude = a.Length();
46 | if (magnitude <= maxDistanceDelta || magnitude == 0f)
47 | {
48 | return target;
49 | }
50 |
51 | return current + a / magnitude * maxDistanceDelta;
52 | }
53 |
54 | public static Vector3 AnglesToForward(Vector3 position, Vector3 angles, int length)
55 | {
56 | float num = (float)Math.Sin(angles.X * Math.PI / 180) * length;
57 | float num1 = (float)Math.Sqrt(length * length - num * num);
58 | float num2 = (float)Math.Sin(angles.Y * Math.PI / 180) * num1;
59 | float num3 = (float)Math.Cos(angles.Y * Math.PI / 180) * num1;
60 | return new Vector3(position.X + num3, position.Y + num2, position.Z - num);
61 | }
62 |
63 | private static Quaternion AngleAxis(float degress, ref Vector3 axis)
64 | {
65 | if (axis.Length() == 0.0f)
66 | return Quaternion.Identity;
67 |
68 | Quaternion result = Quaternion.Identity;
69 | var radians = degress * (float)(Math.PI / 180.0);
70 | radians *= 0.5f;
71 | axis.Normalize();
72 | axis = axis * (float)Math.Sin(radians);
73 | result.X = axis.X;
74 | result.Y = axis.Y;
75 | result.Z = axis.Z;
76 | result.W = (float)Math.Cos(radians);
77 |
78 | result.Normalize();
79 |
80 | return result;
81 | }
82 |
83 | ///
84 | /// Convert degrees to radians.
85 | ///
86 | /// The value in degrees.
87 | ///
88 | public static double ToRadians(this double val)
89 | {
90 | return DegToRad * val;
91 | }
92 |
93 |
94 | ///
95 | /// Convert degrees to radians.
96 | ///
97 | /// The value in degrees.
98 | ///
99 | public static double ToDegrees(this double val)
100 | {
101 | return RadToDeg * val;
102 | }
103 |
104 | public static Quaternion Euler(Vector3 eulerAngles)
105 | {
106 | float halfPhi = 0.5f * eulerAngles.X; // Half the roll.
107 | float halfTheta = 0.5f * eulerAngles.Y; // Half the pitch.
108 | float halfPsi = 0.5f * eulerAngles.Z; // Half the yaw.
109 |
110 | float cosHalfPhi = (float)Math.Cos(halfPhi);
111 | float sinHalfPhi = (float)Math.Sin(halfPhi);
112 | float cosHalfTheta = (float)Math.Cos(halfTheta);
113 | float sinHalfTheta = (float)Math.Sin(halfTheta);
114 | float cosHalfPsi = (float)Math.Cos(halfPsi);
115 | float sinHalfPsi = (float)Math.Sin(halfPsi);
116 |
117 | return new Quaternion(
118 | cosHalfPhi * cosHalfTheta * cosHalfPsi - sinHalfPhi * sinHalfTheta * sinHalfPsi,
119 | sinHalfPhi * cosHalfTheta * cosHalfPsi + cosHalfPhi * sinHalfTheta * sinHalfPsi,
120 | cosHalfPhi * sinHalfTheta * cosHalfPsi - sinHalfPhi * cosHalfTheta * sinHalfPsi,
121 | cosHalfPhi * cosHalfTheta * sinHalfPsi + sinHalfPhi * sinHalfTheta * cosHalfPsi
122 | );
123 | }
124 |
125 | public static Vector3 MultiplyVector(Vector3 vec, Quaternion quat)
126 | {
127 | float num = quat.X * 2f;
128 | float num2 = quat.Y * 2f;
129 | float num3 = quat.Z * 2f;
130 | float num4 = quat.X * num;
131 | float num5 = quat.Y * num2;
132 | float num6 = quat.Z * num3;
133 | float num7 = quat.X * num2;
134 | float num8 = quat.X * num3;
135 | float num9 = quat.Y * num3;
136 | float num10 = quat.W * num;
137 | float num11 = quat.W * num2;
138 | float num12 = quat.W * num3;
139 | Vector3 result;
140 | result.X = (1f - (num5 + num6)) * vec.X + (num7 - num12) * vec.Y + (num8 + num11) * vec.Z;
141 | result.Y = (num7 + num12) * vec.X + (1f - (num4 + num6)) * vec.Y + (num9 - num10) * vec.Z;
142 | result.Z = (num8 - num11) * vec.X + (num9 + num10) * vec.Y + (1f - (num4 + num5)) * vec.Z;
143 | return result;
144 | }
145 |
146 | public static Quaternion Euler(float x, float y, float z)
147 | {
148 | return Euler(new Vector3(x, y, z));
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/Probability.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace TornadoScript.ScriptMain.Utility
4 | {
5 | public static class Probability
6 | {
7 | private static int _lastCheckedTime;
8 |
9 | private static readonly Random Rand = new Random();
10 |
11 | ///
12 | /// Gets a random float value
13 | ///
14 | ///
15 | public static float GetFloat()
16 | {
17 | return GetScalar() * float.MaxValue;
18 | }
19 |
20 |
21 | ///
22 | /// Gets a random float value in range
23 | ///
24 | ///
25 | public static float GetFloat(float min, float max)
26 | {
27 | return NextFloat() * (max - min) + min;
28 | }
29 |
30 | ///
31 | /// Gets a random float value from 0.0 to 1.0
32 | ///
33 | ///
34 | public static float NextFloat()
35 | {
36 | return (float)Rand.NextDouble();
37 | }
38 |
39 | ///
40 | /// Gets a random float value from -1.0 to 1.0
41 | ///
42 | ///
43 | public static float GetScalar()
44 | {
45 | var val = Rand.NextDouble();
46 | val -= 0.5;
47 | val *= 2;
48 | return (float) val;
49 | }
50 |
51 | ///
52 | /// Gets a random integer value in range
53 | ///
54 | ///
55 | public static int GetInteger(int min, int max)
56 | {
57 | return GetInteger(min, max, false);
58 | }
59 |
60 | ///
61 | /// Gets a random integer value
62 | ///
63 | ///
64 | public static int GetInteger()
65 | {
66 | return GetInteger(0, int.MaxValue, false);
67 | }
68 |
69 | ///
70 | /// Gets a random integer value in range
71 | ///
72 | /// Return the absolute value.
73 | ///
74 | public static int GetInteger(int min, int max, bool abs)
75 | {
76 | var result = StrongRandom.Next(min, max);
77 | return abs ? Math.Abs(result) : result;
78 | }
79 |
80 | ///
81 | /// Checks for a conditon given a % of chance and interval
82 | ///
83 | /// % chance of success
84 | /// rand
85 | public static bool GetBoolean()
86 | {
87 | return GetBoolean(0.5f);
88 | }
89 |
90 | ///
91 | /// Checks for a conditon given a % of chance and interval
92 | ///
93 | /// % chance of success
94 | /// rand
95 | public static bool GetBoolean(float chance)
96 | {
97 | return GetBoolean(chance, 0);
98 | }
99 |
100 | public static bool GetBoolean(float chance, int checkInterval)
101 | {
102 | if (checkInterval <= 0)
103 | return StrongRandom.Next(0, 1000) < (int) (chance * 1000.0f);
104 |
105 | if (Environment.TickCount - _lastCheckedTime < checkInterval)
106 | return false;
107 |
108 | _lastCheckedTime = Environment.TickCount;
109 |
110 | return StrongRandom.Next(0, 1000) < (int)(chance * 1000.0f);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/ShapeTestEx.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using GTA;
7 | using GTA.Native;
8 | using GTA.Math;
9 |
10 | namespace TornadoScript.ScriptMain.Utility
11 | {
12 | public class ShapeTestResult
13 | {
14 | public bool DidHit { get; private set; }
15 | public int HitEntity { get; private set; }
16 | public Vector3 HitPosition { get; private set; }
17 | public Vector3 HitNormal { get; private set; }
18 | public materials HitMaterial { get; private set; }
19 |
20 | public ShapeTestResult(bool didHit, int hitEntity, Vector3 hitPosition, Vector3 hitNormal, materials hitMaterial)
21 | {
22 | DidHit = didHit;
23 | HitEntity = hitEntity;
24 | HitPosition = hitPosition;
25 | HitNormal = hitNormal;
26 | HitMaterial = hitMaterial;
27 | }
28 | }
29 |
30 | public static class ShapeTestEx
31 | {
32 | public unsafe static ShapeTestResult RunShapeTest(Vector3 start, Vector3 end, Entity ignoreEntity, IntersectOptions options)
33 | {
34 | var shapeTest = Function.Call(Hash._CAST_RAY_POINT_TO_POINT,
35 | start.X, start.Y, start.Z, end.X, end.Y, end.Z, (int)options, ignoreEntity, 7);
36 |
37 | bool didHit;
38 |
39 | int result, handle;
40 |
41 | float[] hitPosition = new float[6], hitNormal = new float[6];
42 |
43 | int material;
44 |
45 | fixed (float* position = hitPosition)
46 | fixed (float* normal = hitNormal)
47 | {
48 | result = Function.Call((Hash)0x65287525D951F6BE, shapeTest, &didHit, position, normal, &material, &handle);
49 | }
50 |
51 | return new ShapeTestResult(didHit, handle, new Vector3(hitPosition[0], hitPosition[2], hitPosition[4]),
52 | new Vector3(hitNormal[0], hitNormal[2], hitNormal[4]), (materials)material);
53 | }
54 | }
55 |
56 | public enum materials
57 | {
58 | none = -1,
59 | unk = -1775485061,
60 | concrete = 1187676648,
61 | concrete_pothole = 359120722,
62 | concrete_dusty = -1084640111,
63 | tarmac = 282940568,
64 | tarmac_painted = -1301352528,
65 | tarmac_pothole = 1886546517,
66 | rumble_strip = -250168275,
67 | breeze_block = -954112554,
68 | rock = -840216541,
69 | rock_mossy = -124769592,
70 | stone = 765206029,
71 | cobblestone = 576169331,
72 | brick = 1639053622,
73 | marble = 1945073303,
74 | paving_slab = 1907048430,
75 | sandstone_solid = 592446772,
76 | sandstone_brittle = 1913209870,
77 | sand_loose = -1595148316,
78 | sand_compact = 510490462,
79 | sand_wet = 909950165,
80 | sand_track = -1907520769,
81 | sand_underwater = -1136057692,
82 | sand_dry_deep = 509508168,
83 | sand_wet_deep = 1288448767,
84 | ice = -786060715,
85 | ice_tarmac = -1931024423,
86 | snow_loose = -1937569590,
87 | snow_compact = -878560889,
88 | snow_deep = 1619704960,
89 | snow_tarmac = 1550304810,
90 | gravel_small = 951832588,
91 | gravel_large = 2128369009,
92 | gravel_deep = -356706482,
93 | gravel_train_track = 1925605558,
94 | dirt_track = -1885547121,
95 | mud_hard = -1942898710,
96 | mud_pothole = 312396330,
97 | mud_soft = 1635937914,
98 | mud_underwater = -273490167,
99 | mud_deep = 1109728704,
100 | marsh = 223086562,
101 | marsh_deep = 1584636462,
102 | soil = -700658213,
103 | clay_hard = 1144315879,
104 | clay_soft = 560985072,
105 | grass_long = -461750719,
106 | grass = 1333033863,
107 | grass_short = -1286696947,
108 | hay = -1833527165,
109 | bushes = 581794674,
110 | twigs = -913351839,
111 | leaves = -2041329971,
112 | woodchips = -309121453,
113 | tree_bark = -1915425863,
114 | metal_solid_small = -1447280105,
115 | metal_solid_medium = -365631240,
116 | metal_solid_large = 752131025,
117 | metal_hollow_small = 15972667,
118 | metal_hollow_medium = 1849540536,
119 | metal_hollow_large = -583213831,
120 | metal_chainlink_small = 762193613,
121 | metal_chainlink_large = 125958708,
122 | metal_corrugated_iron = 834144982,
123 | metal_grille = -426118011,
124 | metal_railing = 2100727187,
125 | metal_duct = 1761524221,
126 | metal_garage_door = -231260695,
127 | metal_manhole = -754997699,
128 | wood_solid_small = -399872228,
129 | wood_solid_medium = 555004797,
130 | wood_solid_large = 815762359,
131 | wood_solid_polished = 126470059,
132 | wood_floor_dusty = -749452322,
133 | wood_hollow_small = 1993976879,
134 | wood_hollow_medium = -365476163,
135 | wood_hollow_large = -925419289,
136 | wood_chipboard = 1176309403,
137 | wood_old_creaky = 722686013,
138 | wood_high_density = -1742843392,
139 | wood_lattice = 2011204130,
140 | ceramic = -1186320715,
141 | roof_tile = 1755188853,
142 | roof_felt = -1417164731,
143 | fibreglass = 1354180827,
144 | tarpaulin = -642658848,
145 | plastic = -2073312001,
146 | plastic_hollow = 627123000,
147 | plastic_high_density = -1625995479,
148 | plastic_clear = -1859721013,
149 | plastic_hollow_clear = 772722531,
150 | plastic_high_density_clear = -1338473170,
151 | fibreglass_hollow = -766055098,
152 | rubber = -145735917,
153 | rubber_hollow = -783934672,
154 | linoleum = 289630530,
155 | laminate = 1845676458,
156 | carpet_solid = 669292054,
157 | carpet_solid_dusty = 158576196,
158 | carpet_floorboard = -1396484943,
159 | cloth = 122789469,
160 | plaster_solid = -574122433,
161 | plaster_brittle = -251888898,
162 | cardboard_sheet = 236511221,
163 | cardboard_box = -1409054440,
164 | paper = 474149820,
165 | foam = 808719444,
166 | feather_pillow = 1341866303,
167 | polystyrene = -1756927331,
168 | leather = -570470900,
169 | tvscreen = 1429989756,
170 | slatted_blinds = 673696729,
171 | glass_shoot_through = 937503243,
172 | glass_bulletproof = 244521486,
173 | glass_opaque = 1500272081,
174 | perspex = -1619794068,
175 | car_metal = -93061983,
176 | car_plastic = 2137197282,
177 | car_softtop = -979647862,
178 | car_softtop_clear = 2130571536,
179 | car_glass_weak = 1247281098,
180 | car_glass_medium = 602884284,
181 | car_glass_strong = 1070994698,
182 | car_glass_bulletproof = -1721915930,
183 | car_glass_opaque = 513061559,
184 | water = 435688960,
185 | blood = 5236042,
186 | oil = -634481305,
187 | petrol = -1634184340,
188 | fresh_meat = 868733839,
189 | dried_meat = -1445160429,
190 | emissive_glass = 1501078253,
191 | emissive_plastic = 1059629996,
192 | vfx_metal_electrified = -309134265,
193 | vfx_metal_water_tower = 611561919,
194 | vfx_metal_steam = -691277294,
195 | vfx_metal_flame = 332778253,
196 | phys_no_friction = 1666473731,
197 | phys_golf_ball = -1693813558,
198 | phys_tennis_ball = -256704763,
199 | phys_caster = -235302683,
200 | phys_caster_rusty = 2016463089,
201 | phys_car_void = 1345867677,
202 | phys_ped_capsule = -291631035,
203 | phys_electric_fence = -1170043733,
204 | phys_electric_metal = -2013761145,
205 | phys_barbed_wire = -1543323456,
206 | phys_pooltable_surface = 605776921,
207 | phys_pooltable_cushion = 972939963,
208 | phys_pooltable_ball = -748341562,
209 | buttocks = 483400232,
210 | thigh_left = -460535871,
211 | shin_left = 652772852,
212 | foot_left = 1926285543,
213 | thigh_right = -236981255,
214 | shin_right = -446036155,
215 | foot_right = -1369136684,
216 | spine0 = -1922286884,
217 | spine1 = -1140112869,
218 | spine2 = 1457572381,
219 | spine3 = 32752644,
220 | clavicle_left = -1469616465,
221 | upper_arm_left = -510342358,
222 | lower_arm_left = 1045062756,
223 | hand_left = 113101985,
224 | clavicle_right = -1557288998,
225 | upper_arm_right = 1501153539,
226 | lower_arm_right = 1777921590,
227 | hand_right = 2000961972,
228 | neck = 1718294164,
229 | head = -735392753,
230 | animal_default = 286224918,
231 | car_engine = -1916939624,
232 | puddle = 999829011,
233 | concrete_pavement = 2015599386,
234 | brick_pavement = -1147361576,
235 | phys_dynamic_cover_bound = -2047468855,
236 | vfx_wood_beer_barrel = 998201806,
237 | wood_high_friction = -2140087047,
238 | rock_noinst = 127813971,
239 | bushes_noinst = 1441114862,
240 | metal_solid_road_surface = -729112334,
241 | stunt_ramp_surface = -2088174996,
242 | temp_01 = 746881105,
243 | temp_02 = -1977970111,
244 | temp_03 = 1911121241,
245 | temp_04 = 1923995104,
246 | temp_05 = -1393662448,
247 | temp_06 = 1061250033,
248 | temp_07 = -1765523682,
249 | temp_08 = 1343679702,
250 | temp_09 = 1026054937,
251 | temp_10 = 63305994,
252 | temp_11 = 47470226,
253 | temp_12 = 702596674,
254 | temp_13 = -1637485913,
255 | temp_14 = -645955574,
256 | temp_15 = -1583997931,
257 | temp_16 = -1512735273,
258 | temp_17 = 1011960114,
259 | temp_18 = 1354993138,
260 | temp_19 = -801804446,
261 | temp_20 = -2052880405,
262 | temp_21 = -1037756060,
263 | temp_22 = -620388353,
264 | temp_23 = 465002639,
265 | temp_24 = 1963820161,
266 | temp_25 = 1952288305,
267 | temp_26 = -1116253098,
268 | temp_27 = 889255498,
269 | temp_28 = -1179674098,
270 | temp_29 = 1078418101,
271 | temp_30 = 13626292
272 | }
273 | }
274 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/StrongRandom.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Cryptography;
3 |
4 | namespace TornadoScript.ScriptMain.Utility
5 | {
6 | ///
7 | /// http://stackoverflow.com/questions/1399039/best-way-to-seed-random-in-singleton
8 | ///
9 | public static class StrongRandom
10 | {
11 | [ThreadStatic]
12 | private static Random _random;
13 |
14 | public static int Next(int inclusiveLowerBound, int inclusiveUpperBound)
15 | {
16 | if (_random == null)
17 | {
18 | var cryptoResult = new byte[4];
19 | new RNGCryptoServiceProvider().GetBytes(cryptoResult);
20 |
21 | int seed = BitConverter.ToInt32(cryptoResult, 0);
22 |
23 | _random = new Random(seed);
24 | }
25 |
26 | // upper bound of Random.Next is exclusive
27 | int exclusiveUpperBound = inclusiveUpperBound + 1;
28 | return _random.Next(inclusiveLowerBound, exclusiveUpperBound);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/ScriptMain/Utility/Win32Native.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | <<<<<<< HEAD
3 | using System.Runtime.ConstrainedExecution;
4 | using System.Runtime.InteropServices;
5 | using System.Security;
6 | =======
7 | using System.Runtime.InteropServices;
8 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
9 | using System.Text;
10 | using System.Windows.Input;
11 |
12 | namespace TornadoScript.ScriptMain.Utility
13 | {
14 | <<<<<<< HEAD
15 | [Flags]
16 | public enum ThreadAccess : int
17 | {
18 | TERMINATE = (0x0001),
19 | SUSPEND_RESUME = (0x0002),
20 | GET_CONTEXT = (0x0008),
21 | SET_CONTEXT = (0x0010),
22 | SET_INFORMATION = (0x0020),
23 | QUERY_INFORMATION = (0x0040),
24 | SET_THREAD_TOKEN = (0x0080),
25 | IMPERSONATE = (0x0100),
26 | DIRECT_IMPERSONATION = (0x0200)
27 | }
28 |
29 | [StructLayout(LayoutKind.Sequential)]
30 | public struct CLIENT_ID
31 | {
32 | public uint UniqueProcess; // original: PVOID
33 | public uint UniqueThread; // original: PVOID
34 | }
35 |
36 | [StructLayout(LayoutKind.Explicit, Size = 0x30)]
37 | public struct THREAD_BASIC_INFORMATION
38 | {
39 | [FieldOffset(0x0000)] public int ExitStatus;
40 | [FieldOffset(0x0008)] public IntPtr TebBaseAddress;
41 | }
42 |
43 | // http://msdn.moonsols.com/win7rtm_x64/TEB.html
44 | [StructLayout(LayoutKind.Explicit, Size = 0x1818)]
45 | public struct TEB
46 | {
47 | [FieldOffset(0x0058)] public IntPtr ThreadLocalStoragePointer;
48 | }
49 |
50 | public enum PlaySoundFlags : uint
51 | {
52 | SND_SYNC = 0x0, // play synchronously (default)
53 | SND_ASYNC = 0x1, // play asynchronously
54 | SND_NODEFAULT = 0x2, // silence (!default) if sound not found
55 | SND_MEMORY = 0x4, // pszSound points to a memory file
56 | SND_LOOP = 0x8, // loop the sound until next sndPlaySound
57 | SND_NOSTOP = 0x10, // don't stop any currently playing sound
58 | SND_NOWAIT = 0x2000, // don't wait if the driver is busy
59 | SND_ALIAS = 0x10000, // name is a registry alias
60 | SND_ALIAS_ID = 0x110000, // alias is a predefined ID
61 | SND_FILENAME = 0x20000, // name is file name
62 | SND_RESOURCE = 0x40004, // name is resource name or atom
63 | };
64 |
65 | public struct MODULEINFO
66 | {
67 | public IntPtr LpBaseOfDll;
68 | public uint SizeOfImage;
69 | public IntPtr EntryPoint;
70 | }
71 |
72 | public enum ThreadInfoClass : int
73 | {
74 | ThreadQuerySetWin32StartAddress = 9
75 | }
76 |
77 | public sealed unsafe class Win32Native
78 | {
79 | public delegate int NtQueryInformationThreadDelegate(IntPtr threadHandle, uint threadInformationClass, THREAD_BASIC_INFORMATION* outThreadInformation, ulong threadInformationLength, ulong* returnLength);
80 |
81 | public static NtQueryInformationThreadDelegate NtQueryInformationThread { get; }
82 |
83 | static Win32Native()
84 | {
85 | IntPtr ntdllHandle = GetModuleHandle("ntdll.dll");
86 | NtQueryInformationThread = Marshal.GetDelegateForFunctionPointer(GetProcAddress(ntdllHandle, "NtQueryInformationThread"));
87 | }
88 |
89 | [DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
90 | public static extern IntPtr GetProcAddress(IntPtr moduleHandle, string procName);
91 |
92 | [DllImport("kernel32.dll", SetLastError = true)]
93 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
94 | [SuppressUnmanagedCodeSecurity]
95 | [return: MarshalAs(UnmanagedType.Bool)]
96 | public static extern bool CloseHandle(IntPtr hObject);
97 |
98 | [DllImport("kernel32.dll", SetLastError = true)]
99 | public static extern IntPtr OpenThread(ThreadAccess desiredAccess, bool inheritHandle, int threadId);
100 |
101 | [DllImport("kernel32.dll")]
102 | public static extern int GetCurrentThreadId();
103 |
104 | [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
105 | public static extern IntPtr GetForegroundWindow();
106 |
107 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
108 | public static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
109 |
110 | =======
111 | public sealed class Win32Native
112 | {
113 | public struct MODULEINFO
114 | {
115 | public IntPtr LpBaseOfDll;
116 | public uint SizeOfImage;
117 | public IntPtr EntryPoint;
118 | }
119 |
120 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
121 | [DllImport("kernel32.dll")]
122 | public static extern IntPtr GetCurrentProcess();
123 |
124 | [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
125 | public static extern IntPtr GetModuleHandle(string lpModuleName);
126 |
127 | [DllImport("psapi.dll", SetLastError = true)]
128 | public static extern bool GetModuleInformation(IntPtr hProcess, IntPtr hModule, out MODULEINFO lpmodinfo, uint cb);
129 |
130 | [DllImport("user32.dll", EntryPoint = "BlockInput")]
131 | [return: MarshalAs(UnmanagedType.Bool)]
132 | public static extern bool BlockInput([MarshalAs(UnmanagedType.Bool)] bool fBlockIt);
133 |
134 | public enum MapType : uint
135 | {
136 | MapvkVkToVsc = 0x0,
137 | MapvkVscToVk = 0x1,
138 | MapvkVkToChar = 0x2,
139 | MapvkVscToVkEx = 0x3,
140 | }
141 |
142 | [DllImport("user32.dll")]
143 | public static extern int ToUnicode(
144 | uint wVirtKey,
145 | uint wScanCode,
146 | byte[] lpKeyState,
147 | [Out, MarshalAs(UnmanagedType.LPWStr, SizeParamIndex = 4)]
148 | StringBuilder pwszBuff,
149 | int cchBuff,
150 | uint wFlags);
151 |
152 | [DllImport("user32.dll")]
153 | public static extern bool GetKeyboardState(byte[] lpKeyState);
154 |
155 | [DllImport("user32.dll")]
156 | public static extern uint MapVirtualKey(uint uCode, MapType uMapType);
157 |
158 | public static char GetCharFromKey(Key key, bool shift)
159 | {
160 | char ch = ' ';
161 |
162 | int virtualKey = KeyInterop.VirtualKeyFromKey(key);
163 | byte[] keyboardState = new byte[256];
164 |
165 | if (shift)
166 | keyboardState[0x10] = 0x80;
167 | GetKeyboardState(keyboardState);
168 |
169 | uint scanCode = MapVirtualKey((uint)virtualKey, MapType.MapvkVkToVsc);
170 | StringBuilder stringBuilder = new StringBuilder(2);
171 |
172 | int result = ToUnicode((uint)virtualKey, scanCode, keyboardState, stringBuilder, stringBuilder.Capacity, 0);
173 | switch (result)
174 | {
175 | case -1:
176 | break;
177 | case 0:
178 | break;
179 | case 1:
180 | {
181 | ch = stringBuilder[0];
182 | break;
183 | }
184 | default:
185 | {
186 | ch = stringBuilder[0];
187 | break;
188 | }
189 | }
190 | return ch;
191 | }
192 | <<<<<<< HEAD
193 |
194 | [DllImport("winmm.dll", SetLastError = true)]
195 | public static extern int PlaySound(
196 | string szSound,
197 | IntPtr hModule,
198 | int flags);
199 | =======
200 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
201 | }
202 | }
203 |
204 |
205 |
--------------------------------------------------------------------------------
/ScriptMain/WinHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Diagnostics;
7 | using TornadoScript.ScriptMain.Utility;
8 | using System.Runtime.InteropServices;
9 | namespace TornadoScript.ScriptMain
10 | {
11 | ///
12 | /// thanks alex
13 | /// https://github.com/alexguirre/Spotlight/blob/master/Source/Core/Memory/WinFunctions.cs
14 | ///
15 | public static unsafe class WinHelper
16 | {
17 | private static int mainThreadId = -1;
18 |
19 | private static Dictionary threadHandleDictionary = new Dictionary();
20 |
21 | private static Dictionary threadInformationDictionary = new Dictionary();
22 |
23 | public static int GetProcessMainThreadId()
24 | {
25 | if (mainThreadId == -1)
26 | {
27 | long lowestStartTime = long.MaxValue;
28 | ProcessThread lowestStartTimeThread = null;
29 | foreach (ProcessThread thread in Process.GetCurrentProcess().Threads)
30 | {
31 | long startTime = thread.StartTime.Ticks;
32 | if (startTime < lowestStartTime)
33 | {
34 | lowestStartTime = startTime;
35 | lowestStartTimeThread = thread;
36 | }
37 | }
38 |
39 | mainThreadId = lowestStartTimeThread == null ? -1 : lowestStartTimeThread.Id;
40 | }
41 |
42 | return mainThreadId;
43 | }
44 |
45 | public static void CopyTlsValues(IntPtr sourceThreadHandle, IntPtr targetThreadHandle, params int[] valuesOffsets)
46 | {
47 | THREAD_BASIC_INFORMATION sourceThreadInfo, targetThreadInfo;
48 |
49 | if (!threadInformationDictionary.TryGetValue(sourceThreadHandle, out sourceThreadInfo))
50 | {
51 | sourceThreadInfo = new THREAD_BASIC_INFORMATION();
52 | int sourceStatus = Win32Native.NtQueryInformationThread(sourceThreadHandle, 0, &sourceThreadInfo, (ulong)sizeof(THREAD_BASIC_INFORMATION), null);
53 | if (sourceStatus != 0)
54 | {
55 | ScriptCore.Logger.Log($"Source Thread Invalid Query Status: {sourceStatus}");
56 | return;
57 | }
58 |
59 | threadInformationDictionary[sourceThreadHandle] = sourceThreadInfo;
60 | }
61 |
62 | if (!threadInformationDictionary.TryGetValue(targetThreadHandle, out targetThreadInfo))
63 | {
64 | targetThreadInfo = new THREAD_BASIC_INFORMATION();
65 | int sourceStatus = Win32Native.NtQueryInformationThread(targetThreadHandle, 0, &targetThreadInfo, (ulong)sizeof(THREAD_BASIC_INFORMATION), null);
66 | if (sourceStatus != 0)
67 | {
68 | ScriptCore.Logger.Log($"Source Thread Invalid Query Status: {sourceStatus}");
69 | return;
70 | }
71 |
72 | threadInformationDictionary[targetThreadHandle] = targetThreadInfo;
73 | }
74 |
75 | TEB* sourceTeb = (TEB*)sourceThreadInfo.TebBaseAddress;
76 | TEB* targetTeb = (TEB*)targetThreadInfo.TebBaseAddress;
77 |
78 | foreach (int offset in valuesOffsets)
79 | {
80 | *(long*)(*(byte**)(targetTeb->ThreadLocalStoragePointer) + offset) = *(long*)(*(byte**)(sourceTeb->ThreadLocalStoragePointer) + offset);
81 | }
82 | }
83 |
84 | public static void CopyTlsValues(int sourceThreadId, int targetThreadId, params int[] valuesOffsets)
85 | {
86 | IntPtr sourceThreadHandle = IntPtr.Zero, targetThreadHandle = IntPtr.Zero;
87 |
88 | if (!threadHandleDictionary.TryGetValue(sourceThreadId, out sourceThreadHandle))
89 | {
90 | try
91 | {
92 | sourceThreadHandle = Win32Native.OpenThread(ThreadAccess.QUERY_INFORMATION, false, sourceThreadId);
93 |
94 | threadHandleDictionary[sourceThreadId] = sourceThreadHandle;
95 | }
96 | catch { }
97 | }
98 |
99 | if (!threadHandleDictionary.TryGetValue(targetThreadId, out targetThreadHandle))
100 | {
101 | try
102 | {
103 | targetThreadHandle = Win32Native.OpenThread(ThreadAccess.QUERY_INFORMATION, false, targetThreadId);
104 |
105 | threadHandleDictionary[targetThreadId] = targetThreadHandle;
106 | }
107 | catch { }
108 | }
109 |
110 | CopyTlsValues(sourceThreadHandle, targetThreadHandle, valuesOffsets);
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/TornadoScript.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {61ED2E12-E7E2-42ED-8CCB-8D1DFCCC5FD9}
8 | Library
9 | Properties
10 | TornadoScript
11 | TornadoScript
12 | <<<<<<< HEAD
13 | v4.6.1
14 | =======
15 | v4.7.1
16 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
17 | 512
18 |
19 |
20 |
21 | true
22 | full
23 | false
24 | bin\Debug\
25 | DEBUG;TRACE
26 | prompt
27 | 4
28 |
29 |
30 | pdbonly
31 | true
32 | bin\Release\
33 | TRACE
34 | prompt
35 | 4
36 | true
37 |
38 |
39 | true
40 | bin\x64\Debug\
41 | DEBUG;TRACE
42 | full
43 | x64
44 | prompt
45 | MinimumRecommendedRules.ruleset
46 | true
47 | 7.1
48 | false
49 |
50 |
51 | bin\x64\Release\
52 | TRACE
53 | true
54 | pdbonly
55 | x64
56 | prompt
57 | MinimumRecommendedRules.ruleset
58 | true
59 | 7.2
60 |
61 |
62 | <<<<<<< HEAD
63 |
64 | False
65 | E:\Program Files\Rockstar Games\Grand Theft Auto V\scripts\NAudio.dll
66 |
67 |
68 | False
69 | ..\..\..\..\Desktop\Projects\ScriptHookVDotNet2.dll
70 | =======
71 |
72 | ..\..\..\..\..\Desktop\ScriptHookVDotNet.dll
73 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 | True
87 | True
88 | Resources.resx
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | <<<<<<< HEAD
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 | =======
127 |
128 |
129 |
130 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
131 |
132 |
133 |
134 |
135 |
136 |
137 | <<<<<<< HEAD
138 |
139 |
140 | =======
141 |
142 |
143 |
144 |
145 | True
146 | True
147 | Resources.resx
148 |
149 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | <<<<<<< HEAD
159 |
160 | =======
161 | >>>>>>> 46660d5b9e2a5942c1c3eb32c40357e5d9abfc48
162 |
163 |
164 |
165 | ResXFileCodeGenerator
166 | Resources.Designer.cs
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | copy "$(TargetPath)" "E:\Program Files\Rockstar Games\Grand Theft Auto V\scripts\$(ProjectName).dll"
176 |
177 |
184 |
--------------------------------------------------------------------------------
/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------