├── .gitignore ├── clipper.cpp ├── clipper.h ├── cpplinq.h ├── plugin_sdk.cpp └── plugin_sdk.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /clipper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /******************************************************************************* 3 | * * 4 | * Author : Angus Johnson * 5 | * Version : 6.4.2 * 6 | * Date : 27 February 2017 * 7 | * Website : http://www.angusj.com * 8 | * Copyright : Angus Johnson 2010-2017 * 9 | * * 10 | * License: * 11 | * Use, modification & distribution is subject to Boost Software License Ver 1. * 12 | * http://www.boost.org/LICENSE_1_0.txt * 13 | * * 14 | * Attributions: * 15 | * The code in this library is an extension of Bala Vatti's clipping algorithm: * 16 | * "A generic solution to polygon clipping" * 17 | * Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. * 18 | * http://portal.acm.org/citation.cfm?id=129906 * 19 | * * 20 | * Computer graphics and geometric modeling: implementation and algorithms * 21 | * By Max K. Agoston * 22 | * Springer; 1 edition (January 4, 2005) * 23 | * http://books.google.com/books?q=vatti+clipping+agoston * 24 | * * 25 | * See also: * 26 | * "Polygon Offsetting by Computing Winding Numbers" * 27 | * Paper no. DETC2005-85513 pp. 565-575 * 28 | * ASME 2005 International Design Engineering Technical Conferences * 29 | * and Computers and Information in Engineering Conference (IDETC/CIE2005) * 30 | * September 24-28, 2005 , Long Beach, California, USA * 31 | * http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf * 32 | * * 33 | *******************************************************************************/ 34 | 35 | #ifndef clipper_hpp 36 | #define clipper_hpp 37 | 38 | #define CLIPPER_VERSION "6.4.2" 39 | 40 | //use_int32: When enabled 32bit ints are used instead of 64bit ints. This 41 | //improve performance but coordinate values are limited to the range +/- 46340 42 | //#define use_int32 43 | 44 | //use_xyz: adds a Z member to IntPoint. Adds a minor cost to perfomance. 45 | //#define use_xyz 46 | 47 | //use_lines: Enables line clipping. Adds a very minor cost to performance. 48 | #define use_lines 49 | 50 | //use_deprecated: Enables temporary support for the obsolete functions 51 | //#define use_deprecated 52 | 53 | #include 54 | #include 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | namespace ClipperLib 64 | { 65 | enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor }; 66 | enum PolyType { ptSubject, ptClip }; 67 | //By far the most widely used winding rules for polygon filling are 68 | //EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32) 69 | //Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL) 70 | //see http://glprogramming.com/red/chapter11.html 71 | enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative }; 72 | 73 | #ifdef use_int32 74 | typedef int cInt; 75 | static cInt const loRange = 0x7FFF; 76 | static cInt const hiRange = 0x7FFF; 77 | #else 78 | typedef signed long long cInt; 79 | static cInt const loRange = 0x3FFFFFFF; 80 | static cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL; 81 | typedef signed long long long64; //used by Int128 class 82 | typedef unsigned long long ulong64; 83 | 84 | #endif 85 | 86 | struct IntPoint 87 | { 88 | cInt X; 89 | cInt Y; 90 | #ifdef use_xyz 91 | cInt Z; 92 | IntPoint(cInt x = 0, cInt y = 0, cInt z = 0) : X(x), Y(y), Z(z) {}; 93 | #else 94 | IntPoint(cInt x = 0, cInt y = 0) : X(x), Y(y) {}; 95 | #endif 96 | 97 | friend inline bool operator== (const IntPoint& a, const IntPoint& b) 98 | { 99 | return a.X == b.X && a.Y == b.Y; 100 | } 101 | friend inline bool operator!= (const IntPoint& a, const IntPoint& b) 102 | { 103 | return a.X != b.X || a.Y != b.Y; 104 | } 105 | }; 106 | //------------------------------------------------------------------------------ 107 | 108 | typedef std::vector< IntPoint > Path; 109 | typedef std::vector< Path > Paths; 110 | 111 | inline Path& operator <<(Path& poly, const IntPoint& p) { poly.push_back(p); return poly; } 112 | inline Paths& operator <<(Paths& polys, const Path& p) { polys.push_back(p); return polys; } 113 | 114 | std::ostream& operator <<(std::ostream& s, const IntPoint& p); 115 | std::ostream& operator <<(std::ostream& s, const Path& p); 116 | std::ostream& operator <<(std::ostream& s, const Paths& p); 117 | 118 | struct DoublePoint 119 | { 120 | double X; 121 | double Y; 122 | DoublePoint(double x = 0, double y = 0) : X(x), Y(y) {} 123 | DoublePoint(IntPoint ip) : X((double)ip.X), Y((double)ip.Y) {} 124 | }; 125 | //------------------------------------------------------------------------------ 126 | 127 | #ifdef use_xyz 128 | typedef void(*ZFillCallback)(IntPoint& e1bot, IntPoint& e1top, IntPoint& e2bot, IntPoint& e2top, IntPoint& pt); 129 | #endif 130 | 131 | enum InitOptions { ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4 }; 132 | enum JoinType { jtSquare, jtRound, jtMiter }; 133 | enum EndType { etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound }; 134 | 135 | class PolyNode; 136 | typedef std::vector< PolyNode* > PolyNodes; 137 | 138 | class PolyNode 139 | { 140 | public: 141 | PolyNode(); 142 | virtual ~PolyNode() {}; 143 | Path Contour; 144 | PolyNodes Childs; 145 | PolyNode* Parent; 146 | PolyNode* GetNext() const; 147 | bool IsHole() const; 148 | bool IsOpen() const; 149 | int ChildCount() const; 150 | private: 151 | //PolyNode& operator =(PolyNode& other); 152 | unsigned Index; //node index in Parent.Childs 153 | bool m_IsOpen; 154 | JoinType m_jointype; 155 | EndType m_endtype; 156 | PolyNode* GetNextSiblingUp() const; 157 | void AddChild(PolyNode& child); 158 | friend class Clipper; //to access Index 159 | friend class ClipperOffset; 160 | }; 161 | 162 | class PolyTree : public PolyNode 163 | { 164 | public: 165 | ~PolyTree() { Clear(); }; 166 | PolyNode* GetFirst() const; 167 | void Clear(); 168 | int Total() const; 169 | private: 170 | //PolyTree& operator =(PolyTree& other); 171 | PolyNodes AllNodes; 172 | friend class Clipper; //to access AllNodes 173 | }; 174 | 175 | bool Orientation(const Path& poly); 176 | double Area(const Path& poly); 177 | int PointInPolygon(const IntPoint& pt, const Path& path); 178 | 179 | void SimplifyPolygon(const Path& in_poly, Paths& out_polys, PolyFillType fillType, bool* exception = nullptr ); 180 | void SimplifyPolygons(const Paths& in_polys, Paths& out_polys, PolyFillType fillType, bool* exception = nullptr ); 181 | void SimplifyPolygons(Paths& polys, PolyFillType fillType, bool* exception = nullptr ); 182 | 183 | void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415); 184 | void CleanPolygon(Path& poly, double distance = 1.415); 185 | void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415); 186 | void CleanPolygons(Paths& polys, double distance = 1.415); 187 | 188 | void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed, bool* exception = nullptr ); 189 | void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed, bool* exception = nullptr ); 190 | void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution, bool* exception = nullptr ); 191 | 192 | void PolyTreeToPaths(const PolyTree& polytree, Paths& paths); 193 | void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths); 194 | void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths); 195 | 196 | void ReversePath(Path& p); 197 | void ReversePaths(Paths& p); 198 | 199 | struct IntRect { cInt left; cInt top; cInt right; cInt bottom; }; 200 | 201 | //enums that are used internally ... 202 | enum EdgeSide { esLeft = 1, esRight = 2 }; 203 | 204 | //forward declarations (for stuff used internally) ... 205 | struct TEdge; 206 | struct IntersectNode; 207 | struct LocalMinimum; 208 | struct OutPt; 209 | struct OutRec; 210 | struct Join; 211 | 212 | typedef std::vector < OutRec* > PolyOutList; 213 | typedef std::vector < TEdge* > EdgeList; 214 | typedef std::vector < Join* > JoinList; 215 | typedef std::vector < IntersectNode* > IntersectList; 216 | 217 | //------------------------------------------------------------------------------ 218 | 219 | //ClipperBase is the ancestor to the Clipper class. It should not be 220 | //instantiated directly. This class simply abstracts the conversion of sets of 221 | //polygon coordinates into edge objects that are stored in a LocalMinima list. 222 | class ClipperBase 223 | { 224 | public: 225 | ClipperBase(); 226 | virtual ~ClipperBase(); 227 | virtual bool AddPath(const Path& pg, PolyType PolyTyp, bool Closed, bool* exception = nullptr); 228 | bool AddPaths(const Paths& ppg, PolyType PolyTyp, bool Closed, bool* exception = nullptr ); 229 | virtual void Clear(); 230 | IntRect GetBounds(); 231 | bool PreserveCollinear() { return m_PreserveCollinear; }; 232 | void PreserveCollinear(bool value) { m_PreserveCollinear = value; }; 233 | protected: 234 | void DisposeLocalMinimaList(); 235 | TEdge* AddBoundsToLML(TEdge* e, bool IsClosed); 236 | virtual void Reset(); 237 | TEdge* ProcessBound(TEdge* E, bool IsClockwise); 238 | void InsertScanbeam(const cInt Y); 239 | bool PopScanbeam(cInt& Y); 240 | bool LocalMinimaPending(); 241 | bool PopLocalMinima(cInt Y, const LocalMinimum*& locMin); 242 | OutRec* CreateOutRec(); 243 | void DisposeAllOutRecs(); 244 | void DisposeOutRec(PolyOutList::size_type index); 245 | void SwapPositionsInAEL(TEdge* edge1, TEdge* edge2); 246 | void DeleteFromAEL(TEdge* e); 247 | void UpdateEdgeIntoAEL(TEdge*& e, bool* exception = nullptr ); 248 | 249 | typedef std::vector MinimaList; 250 | MinimaList::iterator m_CurrentLM; 251 | MinimaList m_MinimaList; 252 | 253 | bool m_UseFullRange; 254 | EdgeList m_edges; 255 | bool m_PreserveCollinear; 256 | bool m_HasOpenPaths; 257 | PolyOutList m_PolyOuts; 258 | TEdge* m_ActiveEdges; 259 | 260 | typedef std::priority_queue ScanbeamList; 261 | ScanbeamList m_Scanbeam; 262 | }; 263 | //------------------------------------------------------------------------------ 264 | 265 | class Clipper : public virtual ClipperBase 266 | { 267 | public: 268 | Clipper(int initOptions = 0); 269 | bool Execute(ClipType clipType, 270 | Paths& solution, 271 | PolyFillType fillType, 272 | bool* exception = nullptr ); 273 | bool Execute(ClipType clipType, 274 | Paths& solution, 275 | PolyFillType subjFillType, 276 | PolyFillType clipFillType, 277 | bool* exception = nullptr ); 278 | bool Execute(ClipType clipType, 279 | PolyTree& polytree, 280 | PolyFillType fillType = pftEvenOdd); 281 | bool Execute(ClipType clipType, 282 | PolyTree& polytree, 283 | PolyFillType subjFillType, 284 | PolyFillType clipFillType); 285 | bool ReverseSolution() { return m_ReverseOutput; }; 286 | void ReverseSolution(bool value) { m_ReverseOutput = value; }; 287 | bool StrictlySimple() { return m_StrictSimple; }; 288 | void StrictlySimple(bool value) { m_StrictSimple = value; }; 289 | //set the callback function for z value filling on intersections (otherwise Z is 0) 290 | #ifdef use_xyz 291 | void ZFillFunction(ZFillCallback zFillFunc); 292 | #endif 293 | protected: 294 | virtual bool ExecuteInternal(); 295 | private: 296 | JoinList m_Joins; 297 | JoinList m_GhostJoins; 298 | IntersectList m_IntersectList; 299 | ClipType m_ClipType; 300 | typedef std::list MaximaList; 301 | MaximaList m_Maxima; 302 | TEdge* m_SortedEdges; 303 | bool m_ExecuteLocked; 304 | PolyFillType m_ClipFillType; 305 | PolyFillType m_SubjFillType; 306 | bool m_ReverseOutput; 307 | bool m_UsingPolyTree; 308 | bool m_StrictSimple; 309 | #ifdef use_xyz 310 | ZFillCallback m_ZFill; //custom callback 311 | #endif 312 | void SetWindingCount(TEdge& edge); 313 | bool IsEvenOddFillType(const TEdge& edge) const; 314 | bool IsEvenOddAltFillType(const TEdge& edge) const; 315 | void InsertLocalMinimaIntoAEL(const cInt botY); 316 | void InsertEdgeIntoAEL(TEdge* edge, TEdge* startEdge); 317 | void AddEdgeToSEL(TEdge* edge); 318 | bool PopEdgeFromSEL(TEdge*& edge); 319 | void CopyAELToSEL(); 320 | void DeleteFromSEL(TEdge* e); 321 | void SwapPositionsInSEL(TEdge* edge1, TEdge* edge2); 322 | bool IsContributing(const TEdge& edge) const; 323 | bool IsTopHorz(const cInt XPos); 324 | void DoMaxima(TEdge* e, bool* exception = nullptr ); 325 | void ProcessHorizontals( bool* exception = nullptr ); 326 | void ProcessHorizontal(TEdge* horzEdge, bool* exception = nullptr ); 327 | void AddLocalMaxPoly(TEdge* e1, TEdge* e2, const IntPoint& pt); 328 | OutPt* AddLocalMinPoly(TEdge* e1, TEdge* e2, const IntPoint& pt); 329 | OutRec* GetOutRec(int idx); 330 | void AppendPolygon(TEdge* e1, TEdge* e2); 331 | void IntersectEdges(TEdge* e1, TEdge* e2, IntPoint& pt); 332 | OutPt* AddOutPt(TEdge* e, const IntPoint& pt); 333 | OutPt* GetLastOutPt(TEdge* e); 334 | bool ProcessIntersections(const cInt topY, bool* exception = nullptr ); 335 | void BuildIntersectList(const cInt topY); 336 | void ProcessIntersectList(); 337 | void ProcessEdgesAtTopOfScanbeam(const cInt topY, bool* exception = nullptr ); 338 | void BuildResult(Paths& polys); 339 | void BuildResult2(PolyTree& polytree); 340 | void SetHoleState(TEdge* e, OutRec* outrec); 341 | void DisposeIntersectNodes(); 342 | bool FixupIntersectionOrder(); 343 | void FixupOutPolygon(OutRec& outrec); 344 | void FixupOutPolyline(OutRec& outrec); 345 | bool IsHole(TEdge* e); 346 | bool FindOwnerFromSplitRecs(OutRec& outRec, OutRec*& currOrfl); 347 | void FixHoleLinkage(OutRec& outrec); 348 | void AddJoin(OutPt* op1, OutPt* op2, const IntPoint offPt); 349 | void ClearJoins(); 350 | void ClearGhostJoins(); 351 | void AddGhostJoin(OutPt* op, const IntPoint offPt); 352 | bool JoinPoints(Join* j, OutRec* outRec1, OutRec* outRec2); 353 | void JoinCommonEdges(); 354 | void DoSimplePolygons(); 355 | void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec); 356 | void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec); 357 | void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec); 358 | #ifdef use_xyz 359 | void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2); 360 | #endif 361 | }; 362 | //------------------------------------------------------------------------------ 363 | 364 | class ClipperOffset 365 | { 366 | public: 367 | ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25); 368 | ~ClipperOffset(); 369 | void AddPath(const Path& path, JoinType joinType, EndType endType); 370 | void AddPaths(const Paths& paths, JoinType joinType, EndType endType); 371 | void Execute(Paths& solution, double delta, bool* exception = nullptr ); 372 | void Execute(PolyTree& solution, double delta, bool* exception = nullptr ); 373 | void Clear(); 374 | double MiterLimit; 375 | double ArcTolerance; 376 | private: 377 | Paths m_destPolys; 378 | Path m_srcPoly; 379 | Path m_destPoly; 380 | std::vector m_normals; 381 | double m_delta, m_sinA, m_sin, m_cos; 382 | double m_miterLim, m_StepsPerRad; 383 | IntPoint m_lowest; 384 | PolyNode m_polyNodes; 385 | 386 | void FixOrientations(); 387 | void DoOffset(double delta); 388 | void OffsetPoint(int j, int& k, JoinType jointype); 389 | void DoSquare(int j, int k); 390 | void DoMiter(int j, int k, double r); 391 | void DoRound(int j, int k); 392 | }; 393 | //------------------------------------------------------------------------------ 394 | } 395 | 396 | #endif //clipper_hpp -------------------------------------------------------------------------------- /plugin_sdk.cpp: -------------------------------------------------------------------------------- 1 | #include "plugin_sdk.hpp" 2 | 3 | plugin_sdk_core* plugin_sdk = nullptr; 4 | 5 | game_state* state = nullptr; 6 | r3d_renderer* renderer = nullptr; 7 | hud_manager* hud = nullptr; 8 | menu_gui* gui = nullptr; 9 | game_time* gametime = nullptr; 10 | game_ping* ping = nullptr; 11 | input* game_input = nullptr; 12 | game_event_manager* event_manager = nullptr; 13 | nav_mesh* navmesh = nullptr; 14 | game_keyboard_state* keyboard_state = nullptr; 15 | locale_manager* locale = nullptr; 16 | mission_info* missioninfo = nullptr; 17 | game_object_script myhero = nullptr; 18 | target_selector_manager* target_selector = nullptr; 19 | prediction_manager* prediction = nullptr; 20 | entity_list* entitylist = nullptr; 21 | tree_menu* menu = nullptr; 22 | health_prediction_manager* health_prediction = nullptr; 23 | orbwalker_manager* orbwalker = nullptr; 24 | damagelib_manager* damagelib = nullptr; 25 | drawning_manager* draw_manager = nullptr; 26 | scheduler_manager* scheduler = nullptr; 27 | console_manager* console = nullptr; 28 | glow_manager* glow = nullptr; 29 | sound_manager* sound = nullptr; 30 | evade_manager* evade = nullptr; 31 | neutral_camp_manager* camp_manager = nullptr; 32 | translation_manager* translation = nullptr; 33 | game_database* database = nullptr; 34 | 35 | std::uint16_t locked_target_selector::_last_target_id = 0; 36 | std::uint32_t locked_target_selector::_last_target_network_id = 0; 37 | damage_type locked_target_selector::_last_damage_type = damage_type::physical; 38 | 39 | 40 | int __stdcall DllMain( void*, unsigned long, void* ) { return 1; } 41 | 42 | PLUGIN_API int get_sdk_version( ) 43 | { 44 | return PLUGIN_SDK_VERSION; 45 | } 46 | 47 | PLUGIN_API void on_plugin_reconnect( ) 48 | { 49 | myhero = plugin_sdk->get_myhero( ); 50 | } 51 | 52 | std::vector> script_spells; 53 | 54 | script_spell* plugin_sdk_core::register_spell( spellslot slot, float range ) 55 | { 56 | if ( slot == spellslot::invalid ) return nullptr; 57 | 58 | script_spells.push_back( std::make_unique( slot, range ) ); 59 | 60 | return script_spells.back( ).get( ); 61 | } 62 | 63 | bool plugin_sdk_core::remove_spell( script_spell* spell ) 64 | { 65 | auto const& it = std::find_if( script_spells.begin( ), script_spells.end( ), [ & ]( std::unique_ptr& x ) { return x.get( ) == spell; } ); 66 | 67 | if ( it == script_spells.end( ) ) 68 | return false; 69 | 70 | script_spells.erase( it ); 71 | return true; 72 | } 73 | 74 | void drawning_manager::draw_circle_on_minimap( vector const& center, float radius, unsigned int color, float thickness, int quality ) 75 | { 76 | geometry::polygon result; 77 | 78 | result.add( vector( 0, 0, 0 ) ); 79 | result.add( vector( 0, 14800, 0 ) ); 80 | result.add( vector( 14800, 14800, 0 ) ); 81 | result.add( vector( 14800, 0, 0 ) ); 82 | 83 | vector cc = center; 84 | 85 | auto points = geometry::geometry::circle_points( cc, radius, quality ); 86 | 87 | for ( size_t i = 0; i < points.size( ); ++i ) 88 | { 89 | auto start = points[ i ]; 90 | auto end = points[ points.size( ) - 1 == i ? 0 : i + 1 ]; 91 | 92 | if ( !result.is_inside( start ) && !result.is_inside( end ) ) 93 | { 94 | continue; 95 | } 96 | 97 | gui->get_tactical_map( )->to_map_coord( start, start ); 98 | gui->get_tactical_map( )->to_map_coord( end, end ); 99 | 100 | draw_manager->add_line_on_screen( start, end, color, thickness ); 101 | } 102 | } 103 | 104 | game_object_script target_selector_manager::get_target_min_hitchance( script_spell* spell, hit_chance min_hitchance ) 105 | { 106 | return this->get_target_min_hitchance( spell, min_hitchance, spell->get_damage_type( ) ); 107 | } 108 | 109 | game_object_script target_selector_manager::get_target_min_hitchance( script_spell* spell, hit_chance min_hitchance, damage_type damage_type ) 110 | { 111 | if ( spell != nullptr ) 112 | { 113 | std::vector < game_object_script > enemies; 114 | 115 | for ( auto&& enemy : entitylist->get_enemy_heroes( ) ) 116 | if ( spell->can_cast( enemy ) && spell->get_prediction( enemy ).hitchance >= min_hitchance ) 117 | enemies.push_back( enemy ); 118 | 119 | return target_selector->get_target( enemies, damage_type ); 120 | } 121 | 122 | return nullptr; 123 | } 124 | 125 | game_object_script target_selector_manager::get_target( script_spell* spell, damage_type damage_type ) 126 | { 127 | if ( spell != nullptr ) 128 | { 129 | auto is_missile = spell->speed > 0 && spell->speed != FLT_MAX; 130 | auto range = spell->range( ); 131 | 132 | /*if ( spell->type == skillshot_type::skillshot_circle ) 133 | range += spell->radius;*/ 134 | 135 | return target_selector->get_target( range, damage_type, false, is_missile ); 136 | } 137 | return nullptr; 138 | } 139 | 140 | bool target_selector_manager::is_invulnerable( game_object_script target ) 141 | { 142 | if ( target == nullptr || !target->is_valid( ) ) 143 | { 144 | return false; 145 | } 146 | 147 | for ( auto&& buff : target->get_bufflist( ) ) 148 | { 149 | if ( buff == nullptr || !buff->is_valid( ) || !buff->is_alive( ) ) 150 | { 151 | continue; 152 | } 153 | 154 | if ( buff->get_type( ) == buff_type::Invulnerability ) 155 | return true; 156 | 157 | switch ( buff->get_hash_name( ) ) 158 | { 159 | case buff_hash_real( "KindredRNoDeathBuff" ): 160 | case buff_hash_real( "UndyingRage" ): 161 | case buff_hash_real( "ChronoRevive" ): 162 | case buff_hash_real( "ChronoShift" ): 163 | if ( target->get_health_percent( ) <= 10 ) 164 | { 165 | return true; 166 | } 167 | break; 168 | case buff_hash_real( "KayleR" ): 169 | case buff_hash_real( "VladimirSanguinePool" ): 170 | case buff_hash_real( "lissandrarself" ): 171 | case buff_hash_real( "fioraw" ): 172 | { 173 | return true; 174 | } 175 | } 176 | } 177 | 178 | return false; 179 | } 180 | 181 | bool target_selector_manager::has_spellshield( game_object_script target ) 182 | { 183 | if ( target == nullptr || !target->is_valid( ) ) 184 | { 185 | return false; 186 | } 187 | 188 | return target->get_buff_by_type( { buff_type::SpellShield, buff_type::SpellImmunity, buff_type::Invulnerability } ) != nullptr; 189 | } 190 | 191 | bool game_object::is_valid( bool force ) { return reinterpret_cast< bool( __thiscall* )( game_object*, bool ) >( plugin_sdk->get_is_valid_function( ) )( this, force ); } 192 | 193 | float game_object::get_buff_time_left( uint32_t hash ) 194 | { 195 | auto buff = this->get_buff( hash ); 196 | 197 | return buff ? buff->get_remaining_time( ) : 0.f; 198 | } 199 | 200 | float game_object::get_exp_percent( ) 201 | { 202 | if ( !this->is_ai_hero( ) ) 203 | return 0; 204 | auto exp = this->get_exp( ); 205 | auto level = 1; 206 | auto required_exp = 180 + level * 100; 207 | while ( level++ != this->get_level( ) ) 208 | required_exp += 180 + level * 100; 209 | auto missing_exp = required_exp - exp; 210 | exp = 180 + this->get_level( ) * 100 - missing_exp; 211 | auto percentage = fmaxf( 0, fminf( 100, exp / ( 180 + this->get_level( ) * 100 ) ) ); 212 | 213 | if ( this->get_level( ) == 18 ) 214 | percentage = 1; 215 | 216 | return percentage; 217 | } 218 | 219 | float game_object::get_real_health( bool physical_shield, bool magical_shield ) 220 | { 221 | if ( this->is_ai_base( ) ) 222 | { 223 | auto result = get_health( ); 224 | result += this->get_all_shield( ); 225 | result += this->get_hp_regen_rate( ); 226 | if ( physical_shield ) 227 | result += this->get_physical_shield( ); 228 | 229 | if ( magical_shield ) 230 | result += this->get_magical_shield( ); 231 | 232 | if ( this->is_ai_hero( ) ) 233 | { 234 | if ( this->get_health_percent( ) > 30 ) 235 | { 236 | bool has_hexdrinker = false, has_maw = false; 237 | 238 | for ( spellslot slot = spellslot::item_1; slot <= spellslot::item_6; slot = static_cast< spellslot >( static_cast< int >( slot ) + 1 ) ) 239 | { 240 | auto item = get_item( slot ); 241 | 242 | if ( item ) 243 | { 244 | ItemId item_id = static_cast< ItemId >( item->get_item_id( ) ); 245 | 246 | if ( item_id == ItemId::Maw_of_Malmortius ) 247 | { 248 | has_hexdrinker = false; 249 | has_maw = true; 250 | } 251 | 252 | if ( item_id == ItemId::Hexdrinker ) 253 | has_hexdrinker = !has_maw; 254 | 255 | if ( get_spell_state( slot ) != spell_state::Ready ) 256 | continue; 257 | 258 | if ( item_id == ItemId::Steraks_Gage ) 259 | { 260 | auto bonus_health = get_max_health( ) - ( get_base_hp( ) + get_stat_for_level( per_level_stat_type::health, get_level( ) ) ); 261 | result += 0.75f * bonus_health; 262 | } 263 | 264 | if ( item_id == ItemId::Immortal_Shieldbow ) 265 | result += 275 + ( get_level( ) >= 10 ? ( ( get_level( ) - 9 ) * 47.22f ) : 0 ); 266 | 267 | if ( item_id == ItemId::Maw_of_Malmortius && magical_shield ) 268 | result += 150 + ( this->is_melee( ) ? 50 : 0 ) + this->get_additional_attack_damage( ) * ( this->is_melee( ) ? 2.25f : 1.6875f ); 269 | } 270 | } 271 | 272 | if ( magical_shield && has_hexdrinker ) 273 | result += ( this->is_melee( ) ? ( 100.f + 10.f * this->get_level( ) ) : ( 75.f + 7.5f * this->get_level( ) ) ); 274 | } 275 | 276 | switch ( this->get_champion( ) ) 277 | { 278 | case champion_id::Kled: 279 | { 280 | result += this->get_hp_bar_stacks( ); 281 | break; 282 | } 283 | case champion_id::Blitzcrank: 284 | { 285 | if ( this->has_buff( { buff_hash( "manabarriercooldown" ), buff_hash( "manabarrier" ) } ) == false ) 286 | result += this->get_max_mana( ) * 0.3f; 287 | 288 | break; 289 | } 290 | case champion_id::Yasuo: 291 | { 292 | if ( this->get_mana( ) == 100 ) 293 | { 294 | constexpr double scalar = 475.0 / 17.0; 295 | int lvl = std::min( 17, this->get_level( ) - 1 ); 296 | double passive = 125.0 + ( scalar * lvl * ( 0.7025 + 0.0175 * lvl ) ); 297 | 298 | result += ( float ) passive; 299 | } 300 | break; 301 | } 302 | default: 303 | break; 304 | } 305 | } 306 | return result; 307 | } 308 | return 0; 309 | } 310 | 311 | float game_object::get_distance( game_object_script to ) 312 | { 313 | auto vFrom = this->get_position( ); 314 | auto vTo = to->get_position( ); 315 | 316 | if ( this->is_ai_hero( ) ) 317 | { 318 | if ( auto path_controller = this->get_path_controller( ) ) 319 | { 320 | vFrom = path_controller->get_position_on_path( ); 321 | } 322 | } 323 | 324 | if ( to->is_ai_hero( ) ) 325 | { 326 | if ( auto path_controller = to->get_path_controller( ) ) 327 | { 328 | vTo = path_controller->get_position_on_path( ); 329 | } 330 | } 331 | 332 | return vFrom.distance( vTo ); 333 | } 334 | 335 | float game_object::get_distance( const vector& to ) 336 | { 337 | auto vFrom = this->get_position( ); 338 | 339 | if ( this->is_ai_hero( ) ) 340 | { 341 | if ( auto path_controller = this->get_path_controller( ) ) 342 | { 343 | vFrom = path_controller->get_position_on_path( ); 344 | } 345 | } 346 | 347 | return vFrom.distance( to ); 348 | } 349 | 350 | int32_t game_object::count_allies_in_range( float range ) 351 | { 352 | auto count = 0; 353 | 354 | for ( auto hero : ( this->is_ally( ) ? entitylist->get_ally_heroes( ) : entitylist->get_enemy_heroes( ) ) ) 355 | { 356 | if ( this->get_handle( ) != hero->get_handle( ) && !hero->is_dead( ) && hero->is_visible( ) && hero->is_targetable( ) && this->get_distance( hero ) < range ) 357 | count++; 358 | } 359 | 360 | return count; 361 | } 362 | 363 | int32_t game_object::count_enemies_in_range( float range ) 364 | { 365 | auto count = 0; 366 | 367 | for ( auto hero : ( this->is_ally( ) ? entitylist->get_enemy_heroes( ) : entitylist->get_ally_heroes( ) ) ) 368 | { 369 | if ( hero->is_visible( ) && !hero->is_dead( ) && hero->is_targetable( ) && this->get_distance( hero ) < range ) 370 | count++; 371 | } 372 | 373 | return count; 374 | } 375 | 376 | float game_object::get_immovibility_time( ) 377 | { 378 | float t_max = 0; 379 | 380 | for ( auto&& buff : this->get_bufflist( ) ) 381 | { 382 | if ( !buff->is_valid( ) || !buff->is_alive( ) ) 383 | continue; 384 | 385 | switch ( buff->get_type( ) ) 386 | { 387 | case buff_type::Charm: 388 | case buff_type::Stun: 389 | case buff_type::Flee: 390 | case buff_type::Fear: 391 | case buff_type::Snare: 392 | case buff_type::Knockup: 393 | case buff_type::Suppression: 394 | case buff_type::Taunt: 395 | case buff_type::Asleep: 396 | t_max = fmaxf( t_max, buff->get_remaining_time( ) ); 397 | break; 398 | default: 399 | break; 400 | } 401 | } 402 | 403 | return t_max; 404 | } 405 | 406 | bool game_object::is_moving( ) 407 | { 408 | auto pc = this->get_path_controller( ); 409 | 410 | if ( pc ) 411 | return pc->is_moving( ); 412 | 413 | return false; 414 | } 415 | 416 | bool game_object::is_dashing( ) 417 | { 418 | auto pc = this->get_path_controller( ); 419 | 420 | if ( pc ) 421 | return pc->is_dashing( ); 422 | 423 | return false; 424 | } 425 | 426 | bool game_object::has_buff_type( buff_type type ) 427 | { 428 | auto buff = this->get_buff_by_type( type ); 429 | 430 | if ( buff && buff->is_valid( ) && buff->is_alive( ) ) 431 | { 432 | return true; 433 | } 434 | 435 | return false; 436 | } 437 | 438 | bool game_object::has_buff_type( const std::vector& type ) 439 | { 440 | auto buff = this->get_buff_by_type( type ); 441 | 442 | if ( buff && buff->is_valid( ) && buff->is_alive( ) ) 443 | { 444 | return true; 445 | } 446 | 447 | return false; 448 | } 449 | 450 | bool game_object::is_item_ready( int32_t itemid ) 451 | { 452 | auto slot = this->has_item( itemid ); 453 | 454 | if ( slot != spellslot::invalid ) 455 | { 456 | return this->get_spell_state( slot ) == spell_state::Ready; 457 | } 458 | 459 | return false; 460 | } 461 | 462 | bool game_object::is_item_ready( ItemId itemid ) 463 | { 464 | return this->is_item_ready( ( int32_t ) itemid ); 465 | } 466 | 467 | spellslot game_object::has_item( ItemId itemid ) 468 | { 469 | return this->has_item( ( int32_t ) itemid ); 470 | } 471 | 472 | std::vector game_object::get_real_path( ) 473 | { 474 | std::vector result = 475 | { 476 | this->get_position( ) 477 | }; 478 | 479 | if ( this->is_ai_base( ) && !this->is_ai_turret( ) && !this->is_nexus( ) && !this->is_inhibitor( ) ) 480 | { 481 | auto pctr = this->get_path_controller( ); 482 | 483 | if ( pctr ) 484 | { 485 | size_t current_path_node = pctr->get_current_path_node( ); 486 | 487 | if ( current_path_node == 0 ) 488 | current_path_node = 1; 489 | 490 | auto const& path = pctr->get_path( ); 491 | 492 | for ( size_t i = current_path_node; i < path.size( ); ++i ) 493 | result.push_back( path[ i ] ); 494 | } 495 | } 496 | 497 | return result; 498 | } 499 | 500 | bool game_object::is_in_auto_attack_range( game_object_script to, float additional ) 501 | { 502 | #if INTERNAL_CORE 503 | return is_in_auto_attack_range_native( to.get( ), additional ); 504 | #else 505 | return is_in_auto_attack_range_native( to, additional ); 506 | #endif 507 | } 508 | 509 | bool game_object::is_in_auto_attack_range_native( game_object* to, float additional ) 510 | { 511 | if ( to == nullptr ) 512 | return false; 513 | 514 | if ( this->get_champion( ) == champion_id::Azir ) 515 | { 516 | if ( to->is_ai_hero( ) || to->is_ai_minion( ) ) 517 | { 518 | bool is_allowed_object = true; 519 | if ( to->is_ai_minion( ) ) 520 | { 521 | if ( !to->is_ward( ) ) 522 | { 523 | auto hash = buff_hash_real( to->get_base_skin_name( ).c_str( ) ); 524 | 525 | if ( hash == buff_hash( "TeemoMushroom" ) 526 | || hash == buff_hash( "JhinTrap" ) 527 | || hash == buff_hash( "NidaleeSpear" ) ) 528 | is_allowed_object = false; 529 | } 530 | else 531 | is_allowed_object = false; 532 | } 533 | 534 | if ( is_allowed_object ) 535 | { 536 | for ( auto& it : entitylist->get_other_minion_objects( ) ) 537 | { 538 | if ( it->is_dead( ) ) 539 | continue; 540 | 541 | if ( it->get_object_owner( ) != this->get_base( ) ) 542 | continue; 543 | 544 | if ( it->get_name( ) != "AzirSoldier" ) 545 | continue; 546 | 547 | if ( it->get_position( ).distance( this->get_position( ) ) > 730.f ) 548 | continue; 549 | 550 | if ( it->get_attack_range( ) == 0.f ) 551 | continue; 552 | 553 | if ( it->is_in_auto_attack_range_native( to, it->get_bounding_radius( ) * -2.f - 15.f ) ) 554 | return true; 555 | } 556 | } 557 | } 558 | } 559 | 560 | auto attack_range = this->get_bounding_radius( ) + to->get_bounding_radius( ) + additional - 5; 561 | 562 | if ( this->is_ai_turret( ) ) 563 | attack_range += 775.f; 564 | else if ( this->is_ai_hero( ) && this->get_champion( ) == champion_id::Zeri ) 565 | attack_range += 500.f; 566 | else 567 | attack_range += this->get_attack_range( ); 568 | 569 | vector to_position = to->get_position( ); 570 | vector from_position = this->get_position( ); 571 | 572 | if ( to->is_ai_hero( ) ) 573 | to_position = to->get_path_controller( )->get_position_on_path( ); 574 | 575 | if ( this->is_ai_hero( ) ) 576 | from_position = this->get_path_controller( )->get_position_on_path( ); 577 | 578 | if ( this->is_ai_hero( ) && to->is_ai_hero( ) ) 579 | { 580 | if ( this->get_path_controller( )->is_moving( ) ) 581 | attack_range -= 20; 582 | 583 | if ( to->get_path_controller( )->is_moving( ) && to->get_pathing_direction( ).angle_between( from_position - to_position ) > 89 ) 584 | attack_range -= 12; 585 | } 586 | 587 | if ( this->get_champion( ) == champion_id::Caitlyn && to->has_buff( buff_hash( "caitlynyordletrapinternal" ) ) ) 588 | attack_range = 1300.f; 589 | 590 | if ( this->get_champion( ) == champion_id::Aphelios && to->has_buff( buff_hash( "aphelioscalibrumbonusrangedebuff" ) ) ) 591 | attack_range = 1800.f; 592 | 593 | if ( this->get_champion( ) == champion_id::Samira 594 | && !to->has_buff( buff_hash( "samirapcooldown" ) ) 595 | && to->has_buff_type( { buff_type::Stun, buff_type::Snare, buff_type::Knockup, buff_type::Charm, buff_type::Flee, buff_type::Taunt, buff_type::Asleep, buff_type::Suppression } ) ) 596 | { 597 | attack_range += 300.f; 598 | } 599 | 600 | return from_position.distance_squared( to_position ) < attack_range * attack_range; 601 | } 602 | 603 | bool game_object::is_under_ally_turret( ) 604 | { 605 | for ( auto x : ( this->is_ally( ) ? entitylist->get_ally_turrets( ) : entitylist->get_enemy_turrets( ) ) ) 606 | { 607 | if ( x->is_in_auto_attack_range_native( this ) ) 608 | return true; 609 | } 610 | 611 | return false; 612 | } 613 | 614 | bool game_object::is_under_enemy_turret( ) 615 | { 616 | for ( auto x : ( this->is_ally( ) ? entitylist->get_enemy_turrets( ) : entitylist->get_ally_turrets( ) ) ) 617 | { 618 | if ( x->is_in_auto_attack_range_native( this ) ) 619 | return true; 620 | } 621 | 622 | return false; 623 | } 624 | 625 | bool game_object::is_recalling( ) 626 | { 627 | auto active = this->get_active_spell( ); 628 | 629 | if ( !active ) 630 | return false; 631 | 632 | auto name = active->get_spell_data( )->get_name_hash( ); 633 | 634 | return name == spell_hash( "recall" ) || name == spell_hash( "SuperRecall" ); 635 | } 636 | 637 | bool game_object::is_valid_target( float range, vector const& from, bool ignore_invulnerability ) 638 | { 639 | if ( !is_valid( ) ) 640 | return false; 641 | 642 | if ( from.length( ) == 0 && ( this->get_position( ).distance( myhero->get_position( ) ) > range ) ) 643 | return false; 644 | else if ( from.length( ) != 0 && this->get_position( ).distance( from ) > range ) 645 | return false; 646 | 647 | if ( !is_attack_allowed_on_target( ignore_invulnerability ) ) 648 | return false; 649 | 650 | if ( !is_visible( ) ) 651 | return false; 652 | 653 | return true; 654 | } 655 | 656 | int32_t game_object::is_casting_interruptible_spell( ) 657 | { 658 | if ( !is_ai_hero( ) ) 659 | return 0; 660 | 661 | auto champion = this->get_champion( ); 662 | 663 | if ( champion == champion_id::Nunu && this->has_buff( buff_hash( "NunuW" ) ) ) 664 | return 2; 665 | 666 | auto active = this->get_active_spell( ); 667 | 668 | if ( !active ) 669 | return 0; 670 | 671 | //High priority 672 | if ( champion == champion_id::Rammus && active->get_spellslot( ) == spellslot::q ) 673 | return 2; 674 | if ( champion == champion_id::Warwick && active->get_spellslot( ) == ( spellslot ) 48 ) 675 | return 2; 676 | if ( champion == champion_id::Velkoz && active->get_spellslot( ) == spellslot::r ) 677 | return 2; 678 | if ( champion == champion_id::Akshan && active->get_spellslot( ) == spellslot::r ) 679 | return 2; 680 | if ( champion == champion_id::Xerath && active->get_spellslot( ) == spellslot::r ) 681 | return 2; 682 | if ( champion == champion_id::Caitlyn && active->get_spellslot( ) == spellslot::r ) 683 | return 2; 684 | if ( champion == champion_id::FiddleSticks && active->get_spellslot( ) == spellslot::r ) 685 | return 2; 686 | if ( champion == champion_id::Karthus && active->get_spellslot( ) == spellslot::r ) 687 | return 2; 688 | if ( champion == champion_id::Katarina && active->get_spellslot( ) == spellslot::r ) 689 | return 2; 690 | if ( champion == champion_id::Lucian && active->get_spellslot( ) == spellslot::r ) 691 | return 2; 692 | if ( champion == champion_id::Malzahar && active->get_spellslot( ) == spellslot::r ) 693 | return 2; 694 | if ( champion == champion_id::MissFortune && active->get_spellslot( ) == spellslot::r ) 695 | return 2; 696 | if ( champion == champion_id::Nunu && active->get_spellslot( ) == spellslot::r ) 697 | return 2; 698 | if ( champion == champion_id::Nunu && active->get_spellslot( ) == spellslot::w ) 699 | return 2; 700 | if ( champion == champion_id::Jhin && active->get_spellslot( ) == spellslot::r ) 701 | return 2; 702 | if ( champion == champion_id::TwistedFate && active->get_spellslot( ) == spellslot::r ) 703 | return 2; 704 | if ( champion == champion_id::Janna && active->get_spellslot( ) == spellslot::r ) 705 | return 2; 706 | if ( champion == champion_id::Shen && active->get_spellslot( ) == spellslot::r ) 707 | return 2; 708 | if ( champion == champion_id::Yuumi && active->get_spellslot( ) == spellslot::r ) 709 | return 2; 710 | if ( champion == champion_id::Galio && active->get_spellslot( ) == spellslot::r ) 711 | return 2; 712 | if ( champion == champion_id::Pantheon && active->get_spellslot( ) == spellslot::r ) 713 | return 2; 714 | if ( champion == champion_id::TahmKench && active->get_spellslot( ) == spellslot::w ) 715 | return 2; 716 | if ( champion == champion_id::Poppy && active->get_spellslot( ) == spellslot::r ) 717 | return 2; 718 | if ( champion == champion_id::Pyke && active->get_spellslot( ) == spellslot::q ) 719 | return 2; 720 | if ( champion == champion_id::Viego && active->get_spellslot( ) == spellslot::w ) 721 | return 2; 722 | 723 | //Low Priority 724 | if ( champion == champion_id::Sion && active->get_spellslot( ) == spellslot::q ) 725 | return 1; 726 | if ( champion == champion_id::Galio && active->get_spellslot( ) == spellslot::w ) 727 | return 1; 728 | if ( champion == champion_id::Varus && active->get_spellslot( ) == spellslot::q ) 729 | return 1; 730 | if ( champion == champion_id::Xerath && active->get_spellslot( ) == spellslot::q ) 731 | return 1; 732 | if ( champion == champion_id::Zac && active->get_spellslot( ) == spellslot::e ) 733 | return 1; 734 | if ( champion == champion_id::Vi && active->get_spellslot( ) == spellslot::q ) 735 | return 1; 736 | if ( champion == champion_id::MasterYi && active->get_spellslot( ) == spellslot::w ) 737 | return 1; 738 | if ( champion == champion_id::FiddleSticks && active->get_spellslot( ) == spellslot::w ) 739 | return 1; 740 | if ( champion == champion_id::Quinn && active->get_spellslot( ) == spellslot::r ) 741 | return 1; 742 | if ( champion == champion_id::Vladimir && active->get_spellslot( ) == spellslot::e ) 743 | return 1; 744 | if ( champion == champion_id::Pantheon && active->get_spellslot( ) == spellslot::q ) 745 | return 1; 746 | 747 | return 0; 748 | } 749 | 750 | vector vector::zero = vector( 0, 0, 0 ); 751 | 752 | point2::point2( int32_t x, int32_t y ) 753 | { 754 | this->x = x; 755 | this->y = y; 756 | } 757 | 758 | point2::point2( ) 759 | { 760 | this->x = 0; 761 | this->y = 0; 762 | } 763 | 764 | bool point2::operator==( const point2& vOther ) const 765 | { 766 | return this->x == vOther.x && this->y == vOther.y; 767 | } 768 | 769 | bool point2::operator!=( const point2& vOther ) const 770 | { 771 | return this->x != vOther.x || this->y != vOther.y; 772 | } 773 | 774 | point2 point2::operator+( const point2& v ) const 775 | { 776 | return point2( this->x + v.x, this->y + v.y ); 777 | } 778 | 779 | point2 point2::operator-( const point2& v ) const 780 | { 781 | return point2( this->x - v.x, this->y - v.y ); 782 | } 783 | 784 | point2 point2::operator*( const point2& v ) const 785 | { 786 | return point2( this->x * v.x, this->y * v.y ); 787 | } 788 | 789 | point2 point2::operator/( const point2& v ) const 790 | { 791 | return point2( this->x / v.x, this->y / v.y ); 792 | } 793 | 794 | vector::vector( ) 795 | { 796 | x = 0; 797 | y = 0; 798 | z = 0; 799 | } 800 | 801 | vector::vector( float x, float y ) 802 | { 803 | this->x = x; 804 | this->y = y; 805 | this->z = 0; 806 | } 807 | 808 | vector::vector( float x, float y, float z ) 809 | { 810 | this->x = x; 811 | this->y = y; 812 | this->z = z; 813 | } 814 | 815 | vector::vector( point2 p2 ) 816 | { 817 | this->x = ( float ) p2.x; 818 | this->y = ( float ) p2.y; 819 | this->z = 0; 820 | } 821 | 822 | bool vector::is_valid( ) const 823 | { 824 | if ( this->x != this->x || this->y != this->y || this->z != this->z ) 825 | return false; 826 | 827 | return this->x != 0 && this->y != 0; 828 | } 829 | 830 | bool vector::operator==( const vector& v_other ) const 831 | { 832 | return math::NearEqual( x, v_other.x ) 833 | && math::NearEqual( y, v_other.y ) 834 | && math::NearEqual( z, v_other.z ); 835 | } 836 | 837 | bool vector::operator!=( const vector& v_other ) const 838 | { 839 | return !math::NearEqual( x, v_other.x ) 840 | || !math::NearEqual( y, v_other.y ) 841 | || !math::NearEqual( z, v_other.z ); 842 | } 843 | 844 | float vector::length( ) const 845 | { 846 | return sqrtf( this->x * this->x + this->y * this->y ); 847 | } 848 | 849 | float vector::length_sqr( void ) const 850 | { 851 | return this->x * this->x + this->y * this->y; 852 | } 853 | 854 | float vector::distance( const vector& vOther ) const 855 | { 856 | vector delta; 857 | 858 | delta.x = this->x - vOther.x; 859 | delta.y = this->y - vOther.y; 860 | 861 | return delta.length( ); 862 | } 863 | 864 | float vector::distance( game_object_script unit ) const 865 | { 866 | if ( unit && unit->is_valid( ) ) 867 | { 868 | vector vPosition = unit->get_position( ); 869 | 870 | if ( unit->is_ai_hero( ) ) 871 | { 872 | vPosition = unit->get_path_controller( )->get_position_on_path( ); 873 | } 874 | 875 | vector delta; 876 | 877 | delta.x = this->x - vPosition.x; 878 | delta.y = this->y - vPosition.y; 879 | 880 | return delta.length( ); 881 | } 882 | 883 | return 0.0f; 884 | } 885 | 886 | float vector::distance( const vector& segment_start, const vector& segment_end, bool only_if_on_segment, bool squared ) const 887 | { 888 | auto const projection_info = this->project_on( segment_start, segment_end ); 889 | 890 | if ( projection_info.is_on_segment || !only_if_on_segment ) 891 | { 892 | return squared 893 | ? this->distance_squared( projection_info.segment_point ) 894 | : this->distance( projection_info.segment_point ); 895 | } 896 | return FLT_MAX; 897 | } 898 | 899 | float vector::distance_squared( const vector& vOther ) const 900 | { 901 | vector delta; 902 | 903 | delta.x = this->x - vOther.x; 904 | delta.y = this->y - vOther.y; 905 | 906 | return delta.length_sqr( ); 907 | } 908 | 909 | float vector::dot_product( const vector& other ) const 910 | { 911 | return this->x * other.x + this->y * other.y; 912 | } 913 | 914 | float vector::cross_product( const vector& other ) const 915 | { 916 | return other.y * this->x - other.x * this->y; 917 | } 918 | 919 | float vector::polar( ) const 920 | { 921 | if ( abs( x - 0 ) <= 1e-9 ) 922 | { 923 | if ( y > 0.f ) 924 | { 925 | return 90.f; 926 | } 927 | return y < 0.f ? 270.f : 0.f; 928 | } 929 | 930 | auto theta = atan( y / x ) * ( 180.0 / M_PI ); 931 | 932 | if ( x < 0.f ) 933 | { 934 | theta = theta + 180.; 935 | } 936 | if ( theta < 0. ) 937 | { 938 | theta = theta + 360.; 939 | } 940 | return ( float ) theta; 941 | } 942 | 943 | float vector::angle_between( const vector& other ) const 944 | { 945 | auto theta = polar( ) - other.polar( ); 946 | if ( theta < 0.f ) 947 | { 948 | theta = theta + 360.f; 949 | } 950 | if ( theta > 180.f ) 951 | { 952 | theta = 360.f - theta; 953 | } 954 | return theta; 955 | } 956 | 957 | vector vector::extend( const vector& to, float range ) const 958 | { 959 | const auto from = *this; 960 | return from + ( ( to - from ).normalized( ) * range ); 961 | } 962 | 963 | vector vector::normalized( ) const 964 | { 965 | float len = length( ); 966 | 967 | if ( !math::IsZero( len ) ) 968 | { 969 | return *this / len; 970 | } 971 | 972 | return *this; 973 | } 974 | 975 | vector vector::rotated( float angle ) const 976 | { 977 | float c = cos( angle ); 978 | float s = sin( angle ); 979 | 980 | return vector( x * c - y * s, y * c + x * s ); 981 | } 982 | 983 | vector vector::set_z( float value ) const 984 | { 985 | if ( value == -1.f ) 986 | return vector( x, y, plugin_sdk->get_nav_mesh( )->get_height_for_position( x, y ) ); 987 | else 988 | return vector( x, y, value ); 989 | } 990 | 991 | vector vector::perpendicular( ) const 992 | { 993 | return vector( -y, x ); 994 | } 995 | 996 | projection_info vector::project_on( const vector& segment_start, const vector& segment_end ) const 997 | { 998 | float rs; 999 | auto const cx = x; 1000 | auto const cy = y; 1001 | auto const ax = segment_start.x; 1002 | auto const ay = segment_start.y; 1003 | auto const bx = segment_end.x; 1004 | auto const by = segment_end.y; 1005 | 1006 | const auto rl = ( ( cx - ax ) * ( bx - ax ) + ( cy - ay ) * ( by - ay ) ) / ( powf( bx - ax, 2 ) + powf( by - ay, 2 ) ); 1007 | const auto point_line = vector( ax + rl * ( bx - ax ), ay + rl * ( by - ay ), 0 ); 1008 | 1009 | if ( rl < 0 ) 1010 | { 1011 | rs = 0; 1012 | } 1013 | else if ( rl > 1 ) 1014 | { 1015 | rs = 1; 1016 | } 1017 | else 1018 | { 1019 | rs = rl; 1020 | } 1021 | 1022 | auto const is_on_segment = math::NearEqual( rs, rl ); //rs == rl; 1023 | auto const point_segment = is_on_segment ? point_line : vector( ax + rs * ( bx - ax ), ay + rs * ( by - ay ), 0 ); 1024 | 1025 | return projection_info( is_on_segment, point_segment, point_line ); 1026 | } 1027 | 1028 | intersection_result vector::intersection( const vector& line_segment_end, const vector& line_segment2_start, const vector& line_segment2_end ) const 1029 | { 1030 | //Real-Time Collision Detection, Christer Ericson 1031 | //5.1 Closest-point Computations 1032 | auto Signed2DTriArea = []( const vector& a, const vector& b, const vector& c ) 1033 | { 1034 | return ( a.x - c.x ) * ( b.y - c.y ) - ( a.y - c.y ) * ( b.x - c.x ); 1035 | }; 1036 | 1037 | intersection_result result; 1038 | 1039 | auto a = *this; 1040 | // Sign of areas correspond to which side of ab points c and d are 1041 | float a1 = Signed2DTriArea( a, line_segment_end, line_segment2_end ); // Compute winding of abd (+ or -) 1042 | float a2 = Signed2DTriArea( a, line_segment_end, line_segment2_start ); // To intersect, must have sign opposite of a1 1043 | 1044 | // If c and d are on different sides of ab, areas have different signs 1045 | if ( a1 != 0.0f && a2 != 0.0f && a1 * a2 < 0.0f ) 1046 | { 1047 | // Compute signs for a and b with respect to segment cd 1048 | float a3 = Signed2DTriArea( line_segment2_start, line_segment2_end, a ); // Compute winding of cda (+ or -) 1049 | // Since area is constant a1 - a2 = a3 - a4, or a4 = a3 + a2 - a1 1050 | // float a4 = Signed2DTriArea(c, d, b); // Must have opposite sign of a3 1051 | float a4 = a3 + a2 - a1; 1052 | // Points a and b on different sides of cd if areas have different signs 1053 | if ( a3 * a4 < 0.0f ) 1054 | { 1055 | // Segments intersect. Find intersection point along L(t) = a + t * (b - a). 1056 | // Given height h1 of an over cd and height h2 of b over cd, 1057 | // t = h1 / (h1 - h2) = (b*h1/2) / (b*h1/2 - b*h2/2) = a3 / (a3 - a4), 1058 | // where b (the base of the triangles cda and cdb, i.e., the length 1059 | // of cd) cancels out. 1060 | auto t = a3 / ( a3 - a4 ); 1061 | 1062 | result.intersects = true; 1063 | result.point = ( *this ) + ( line_segment_end - *this ) * t; 1064 | } 1065 | } 1066 | 1067 | // Segments not intersecting (or collinear) 1068 | 1069 | auto isCollinear = []( vector const& p, vector const& q, vector const& r ) 1070 | { 1071 | const float collinearRadius = 1.0f; //assume line must be equal to 1px. 1072 | 1073 | float val = ( q.y - p.y ) * ( r.x - q.x ) - 1074 | ( q.x - p.x ) * ( r.y - q.y ); 1075 | 1076 | return fabs( val ) <= collinearRadius;// colinear 1077 | }; 1078 | 1079 | auto isOnSegment = []( vector const& p, vector const& q, vector const& r ) 1080 | { 1081 | return q.x <= std::max( p.x, r.x ) && q.x >= std::min( p.x, r.x ) && 1082 | q.y <= std::max( p.y, r.y ) && q.y >= std::min( p.y, r.y ); 1083 | }; 1084 | 1085 | if ( isCollinear( *this, line_segment_end, line_segment2_start ) && isOnSegment( *this, line_segment2_start, line_segment_end ) ) 1086 | { 1087 | //line1_start, line2_start and line2_end are colinear and line2_start lies on segment of line1 1088 | 1089 | result.intersects = true; 1090 | result.is_collinear = true; 1091 | } 1092 | 1093 | if ( isCollinear( *this, line_segment_end, line_segment2_end ) && isOnSegment( *this, line_segment2_end, line_segment_end ) ) 1094 | { 1095 | //line1_start, line1_end and line2_end are colinear and line2_end lies on segment of line1 1096 | result.intersects = true; 1097 | result.is_collinear = true; 1098 | } 1099 | 1100 | if ( isCollinear( line_segment2_start, line_segment2_end, *this ) && isOnSegment( line_segment2_start, *this, line_segment2_end ) ) 1101 | { 1102 | //line2_start, line2_end and line1_start are colinear and line1_start lies on segment of line2 1103 | 1104 | result.intersects = true; 1105 | result.is_collinear = true; 1106 | } 1107 | 1108 | if ( isCollinear( line_segment2_start, line_segment2_end, line_segment_end ) && isOnSegment( line_segment2_start, line_segment_end, line_segment2_end ) ) 1109 | { 1110 | //line2_start, line2_end and line1_end are colinear and line1_end lies on segment of line2 1111 | 1112 | result.intersects = true; 1113 | result.is_collinear = true; 1114 | } 1115 | 1116 | return result; 1117 | } 1118 | 1119 | bool vector::is_in_fow( ) const 1120 | { 1121 | return navmesh->is_in_fow( *this );; 1122 | } 1123 | 1124 | bool vector::is_wall( ) const 1125 | { 1126 | return static_cast< int >( navmesh->get_collision_flag( *this ) & nav_collision_flags::wall ) != 0; 1127 | } 1128 | 1129 | bool vector::is_wall_of_grass( ) const 1130 | { 1131 | return static_cast< int >( navmesh->get_collision_flag( *this ) & nav_collision_flags::grass ) != 0; 1132 | } 1133 | 1134 | bool vector::is_building( ) const 1135 | { 1136 | return static_cast< int >( navmesh->get_collision_flag( *this ) & nav_collision_flags::building ) != 0; 1137 | } 1138 | 1139 | bool vector::is_on_screen( ) const 1140 | { 1141 | return this->x <= renderer->screen_width( ) && this->x >= 0 && this->y <= renderer->screen_height( ) && this->y >= 0; 1142 | } 1143 | 1144 | bool vector::is_under_ally_turret( ) const 1145 | { 1146 | for ( auto&& t : entitylist->get_ally_turrets( ) ) 1147 | { 1148 | if ( t->is_valid( ) && !t->is_dead( ) && this->distance_squared( t->get_position( ) ) < ( 775.f + t->get_bounding_radius( ) ) * ( 775.f + t->get_bounding_radius( ) ) ) 1149 | return true; 1150 | } 1151 | return false; 1152 | } 1153 | 1154 | bool vector::is_under_enemy_turret( ) const 1155 | { 1156 | for ( auto&& t : entitylist->get_enemy_turrets( ) ) 1157 | { 1158 | if ( t->is_valid( ) && !t->is_dead( ) && this->distance_squared( t->get_position( ) ) < ( 775.f + t->get_bounding_radius( ) ) * ( 775.f + t->get_bounding_radius( ) ) ) 1159 | return true; 1160 | } 1161 | return false; 1162 | } 1163 | 1164 | int vector::count_enemies_in_range( float range ) const 1165 | { 1166 | int count = 0; 1167 | for ( auto&& t : entitylist->get_enemy_heroes( ) ) 1168 | { 1169 | if ( t->is_valid_target( range, *this ) ) 1170 | count++; 1171 | } 1172 | return count; 1173 | } 1174 | 1175 | int vector::count_allys_in_range( float range, game_object_script original_unit ) const 1176 | { 1177 | int count = 0; 1178 | std::uint32_t network_id = ( ( original_unit != nullptr && original_unit->is_valid( ) ) ? original_unit->get_network_id( ) : 0 ); 1179 | 1180 | for ( auto&& t : entitylist->get_ally_heroes( ) ) 1181 | { 1182 | if ( t->is_valid( ) && !t->is_dead( ) && t->get_network_id( ) != network_id && this->distance( t->get_position( ) ) < range ) 1183 | count++; 1184 | } 1185 | return count; 1186 | } 1187 | 1188 | projection_info:: 1189 | projection_info( const bool is_on_segment, vector const& segment_point, vector const& line_point ): 1190 | is_on_segment( is_on_segment ), line_point( line_point ), segment_point( segment_point ) 1191 | { 1192 | } 1193 | 1194 | intersection_result:: 1195 | intersection_result( const bool intersects, vector const& point ): 1196 | intersects( intersects ), point( point ) 1197 | { 1198 | } 1199 | 1200 | prediction_input::prediction_input( ) 1201 | { 1202 | unit = myhero; 1203 | } 1204 | 1205 | vector prediction_input::get_from( ) 1206 | { 1207 | return _from.is_valid( ) ? _from : myhero->get_position( ); 1208 | } 1209 | 1210 | vector prediction_input::get_range_check_from( ) 1211 | { 1212 | return _range_check_from.is_valid( ) 1213 | ? _range_check_from 1214 | : ( _from.is_valid( ) ? _from : myhero->get_position( ) ); 1215 | } 1216 | 1217 | float prediction_input::get_real_radius( ) 1218 | { 1219 | return use_bounding_radius ? radius + unit->get_bounding_radius( ) : radius; 1220 | } 1221 | 1222 | vector prediction_output::get_cast_position( ) 1223 | { 1224 | return _cast_position.is_valid( ) ? _cast_position.set_z( ) : ( input.unit ? input.unit->get_position( ) : vector( 0, 0, 0 ) ); 1225 | } 1226 | 1227 | vector prediction_output::get_unit_position( ) 1228 | { 1229 | return _unit_position.is_valid( ) ? _unit_position.set_z( ) : ( input.unit ? input.unit->get_position( ) : vector( 0, 0, 0 ) ); 1230 | } 1231 | 1232 | int prediction_output::aoe_targets_hit_count( ) 1233 | { 1234 | return std::max( _aoe_targets_hit_count, ( int32_t ) aoe_targets_hit.size( ) ); 1235 | } 1236 | 1237 | namespace antigapcloser 1238 | { 1239 | #define ADD_DASH_DATA_VAR(TYPE, NAME) TYPE NAME = {}; TrackedDashData& set_##NAME( const TYPE& NAME ) { this->NAME = NAME; return *this; } 1240 | 1241 | struct TrackedDashData 1242 | { 1243 | ADD_DASH_DATA_VAR( std::string, name ) 1244 | ADD_DASH_DATA_VAR( std::string, spell_name ) 1245 | 1246 | ADD_DASH_DATA_VAR( std::uint32_t, required_buffhash ) 1247 | ADD_DASH_DATA_VAR( std::uint32_t, spell_name_hash ) 1248 | 1249 | ADD_DASH_DATA_VAR( bool, wait_for_new_path ) 1250 | ADD_DASH_DATA_VAR( bool, is_dangerous ) 1251 | ADD_DASH_DATA_VAR( bool, is_fixed_range ) 1252 | ADD_DASH_DATA_VAR( bool, is_targeted ) 1253 | ADD_DASH_DATA_VAR( bool, is_inverted ) 1254 | ADD_DASH_DATA_VAR( bool, find_target_by_buffhash ) 1255 | ADD_DASH_DATA_VAR( bool, wait_for_targetable ) 1256 | ADD_DASH_DATA_VAR( bool, is_cc ) 1257 | ADD_DASH_DATA_VAR( bool, is_unstoppable ) 1258 | 1259 | ADD_DASH_DATA_VAR( float, delay ) 1260 | ADD_DASH_DATA_VAR( float, speed ) 1261 | ADD_DASH_DATA_VAR( float, range ) 1262 | ADD_DASH_DATA_VAR( float, min_range ) 1263 | ADD_DASH_DATA_VAR( float, extra_range ) 1264 | ADD_DASH_DATA_VAR( float, add_ms_ratio ) 1265 | ADD_DASH_DATA_VAR( float, always_fixed_delay ) 1266 | 1267 | TrackedDashData( ) 1268 | { 1269 | this->speed = FLT_MAX; 1270 | this->spell_name_hash = spell_hash_real( spell_name.c_str( ) ); 1271 | } 1272 | }; 1273 | 1274 | struct TrackedDash 1275 | { 1276 | game_object_script sender; 1277 | game_object_script target; 1278 | 1279 | const TrackedDashData* dash_data; 1280 | 1281 | float start_time; 1282 | float end_time; 1283 | float speed; 1284 | 1285 | vector start_position; 1286 | vector end_position; 1287 | 1288 | bool is_finished_detecting; 1289 | 1290 | TrackedDash( ) 1291 | { 1292 | this->sender = nullptr; 1293 | this->target = nullptr; 1294 | 1295 | this->dash_data = nullptr; 1296 | 1297 | this->start_time = 0; 1298 | this->end_time = 0; 1299 | this->speed = 0; 1300 | 1301 | this->is_finished_detecting = false; 1302 | } 1303 | }; 1304 | 1305 | std::vector< TrackedDash > detected_dashes; 1306 | std::vector< TrackedDashData > dashes_data; 1307 | 1308 | std::vector p_handlers; 1309 | 1310 | TrackedDashData& add_dash( const std::string& spell_name, float range, float speed ) 1311 | { 1312 | TrackedDashData data; 1313 | data.spell_name = spell_name; 1314 | data.range = range; 1315 | data.speed = speed; 1316 | 1317 | dashes_data.push_back( data ); 1318 | 1319 | return dashes_data[ dashes_data.size( ) - 1 ]; 1320 | } 1321 | 1322 | void OnProcessSpellCast( game_object_script sender, spell_instance_script spell ) 1323 | { 1324 | if ( sender->is_enemy( ) ) 1325 | { 1326 | auto name = spell->get_spell_data( )->get_name( ); 1327 | auto it = std::find_if( dashes_data.begin( ), dashes_data.end( ), [ &name ]( const TrackedDashData& x ) { return x.spell_name == name; } ); 1328 | 1329 | if ( it != dashes_data.end( ) ) 1330 | { 1331 | game_object_script target = spell->get_last_target_id( ) != 0 && it->is_targeted 1332 | ? entitylist->get_object( spell->get_last_target_id( ) ) 1333 | : nullptr; 1334 | 1335 | if ( it->find_target_by_buffhash ) 1336 | { 1337 | for ( auto&& t : sender->is_ally( ) ? entitylist->get_enemy_heroes( ) : entitylist->get_ally_heroes( ) ) 1338 | { 1339 | if ( t->is_valid_target( ) && t->has_buff( it->required_buffhash ) ) 1340 | { 1341 | target = t; 1342 | break; 1343 | } 1344 | } 1345 | } 1346 | 1347 | if ( it->is_targeted && target == nullptr ) 1348 | { 1349 | return; 1350 | } 1351 | 1352 | if ( target && it->required_buffhash && !target->has_buff( it->required_buffhash ) ) 1353 | { 1354 | return; 1355 | } 1356 | 1357 | auto start = spell->get_start_position( ); 1358 | auto end = spell->get_end_position( ); 1359 | 1360 | if ( it->min_range > 0 && start.distance_squared( end ) < std::powf( it->min_range, 2 ) ) 1361 | { 1362 | end = start.extend( end, it->min_range ); 1363 | } 1364 | 1365 | if ( it->is_fixed_range || start.distance_squared( end ) > std::powf( it->range, 2 ) ) 1366 | { 1367 | end = start.extend( end, it->range ); 1368 | } 1369 | 1370 | if ( it->is_inverted ) 1371 | { 1372 | end = start - ( end - start ); 1373 | } 1374 | 1375 | if ( target && !it->is_fixed_range ) 1376 | { 1377 | end = target->get_position( ); 1378 | } 1379 | 1380 | if ( it->extra_range > 0 ) 1381 | { 1382 | end = end.extend( start, -it->extra_range ); 1383 | } 1384 | 1385 | TrackedDash new_dash; 1386 | new_dash.sender = sender; 1387 | new_dash.target = target; 1388 | new_dash.dash_data = &( *it ); 1389 | new_dash.start_position = start; 1390 | new_dash.end_position = end; 1391 | new_dash.speed = it->speed + sender->get_move_speed( ) * it->add_ms_ratio; 1392 | 1393 | if ( sender->get_champion( ) == champion_id::Belveth ) 1394 | { 1395 | auto belveth_speed = 750 + 50 * sender->get_spell( spellslot::q )->level( ) + sender->get_move_speed( ); 1396 | 1397 | if ( sender->has_buff( buff_hash( "BelvethRSteroid" ) ) ) 1398 | belveth_speed += belveth_speed / 10.f; 1399 | 1400 | new_dash.speed = belveth_speed; 1401 | } 1402 | 1403 | if ( it->always_fixed_delay > 0 ) 1404 | new_dash.speed = new_dash.start_position.distance( new_dash.end_position ) / it->always_fixed_delay; 1405 | 1406 | new_dash.start_time = gametime->get_time( ); 1407 | new_dash.end_time = new_dash.start_time + it->delay + start.distance( end ) / new_dash.speed; 1408 | 1409 | if ( it->wait_for_targetable ) 1410 | new_dash.end_time = new_dash.start_time + 2.5f; 1411 | 1412 | new_dash.is_finished_detecting = !it->wait_for_new_path && !it->wait_for_targetable; 1413 | 1414 | detected_dashes.push_back( new_dash ); 1415 | } 1416 | } 1417 | } 1418 | 1419 | void OnNewPath( game_object_script sender, const std::vector& path, bool is_dash, float dash_speed ) 1420 | { 1421 | if ( is_dash ) 1422 | { 1423 | float length = path.size( ) > 1 ? geometry::geometry::path_length( path ) : 0; 1424 | 1425 | for ( TrackedDash& dash : detected_dashes ) 1426 | { 1427 | if ( dash.is_finished_detecting || !dash.dash_data->wait_for_new_path || sender != dash.sender ) continue; 1428 | 1429 | dash.start_time = gametime->get_time( ) - dash.dash_data->delay; 1430 | dash.end_time = gametime->get_time( ) + length / dash_speed; 1431 | dash.start_position = path.front( ); 1432 | dash.end_position = path.back( ); 1433 | dash.speed = dash_speed; 1434 | dash.is_finished_detecting = true; 1435 | } 1436 | } 1437 | } 1438 | 1439 | void OnUpdate( ) 1440 | { 1441 | detected_dashes.erase( std::remove_if( detected_dashes.begin( ), detected_dashes.end( ), []( const TrackedDash& dash ) 1442 | { 1443 | return gametime->get_time( ) >= dash.end_time; 1444 | } ), detected_dashes.end( ) ); 1445 | 1446 | for ( TrackedDash& dash : detected_dashes ) 1447 | { 1448 | if ( dash.is_finished_detecting || !dash.dash_data->wait_for_targetable ) continue; 1449 | if ( gametime->get_time( ) - dash.start_time < 0.15f ) continue; 1450 | 1451 | if ( dash.sender->is_targetable( ) ) 1452 | { 1453 | if ( dash.sender->get_distance( myhero ) > 150 ) 1454 | { 1455 | dash.end_time = gametime->get_time( ) - 0.1f; //delete Elise E dash, it was not casted on me 1456 | continue; 1457 | } 1458 | 1459 | dash.end_position = dash.sender->get_position( ); 1460 | 1461 | if ( dash.dash_data->always_fixed_delay > 0 ) 1462 | dash.speed = dash.start_position.distance( dash.end_position ) / dash.dash_data->always_fixed_delay; 1463 | 1464 | dash.start_time = gametime->get_time( ); 1465 | dash.end_time = dash.start_time + dash.dash_data->always_fixed_delay; 1466 | dash.is_finished_detecting = true; 1467 | } 1468 | } 1469 | 1470 | for ( const TrackedDash& dash : detected_dashes ) 1471 | { 1472 | if ( !dash.is_finished_detecting || !dash.sender->is_valid_target( ) ) continue; 1473 | if ( ( dash.target == nullptr || !dash.target->is_me( ) ) && dash.sender->get_distance( myhero ) > 500 ) continue; 1474 | 1475 | antigapcloser_args args; 1476 | args.type = gapcloser_type::skillshot; 1477 | args.target = dash.target; 1478 | args.start_time = dash.start_time; 1479 | args.end_time = dash.end_time; 1480 | args.speed = dash.speed; 1481 | args.start_position = dash.start_position; 1482 | args.end_position = dash.end_position; 1483 | args.is_unstoppable = dash.dash_data->is_unstoppable; 1484 | args.is_cc = dash.dash_data->is_cc; 1485 | 1486 | if ( !dash.dash_data->name.empty( ) ) 1487 | args.type = gapcloser_type::item; 1488 | 1489 | if ( dash.target != nullptr && dash.target->is_me( ) ) 1490 | args.type = gapcloser_type::targeted; 1491 | 1492 | for ( auto const& callback : p_handlers ) 1493 | { 1494 | if ( callback != nullptr ) 1495 | { 1496 | reinterpret_cast< gapcloser_handler >( callback )( dash.sender, &args ); 1497 | } 1498 | } 1499 | } 1500 | } 1501 | 1502 | void add_event_handler( gapcloser_handler p_handler ) 1503 | { 1504 | auto it = std::find( p_handlers.begin( ), p_handlers.end( ), ( void* ) p_handler ); 1505 | 1506 | if ( it == p_handlers.end( ) ) 1507 | { 1508 | p_handlers.push_back( ( void* ) p_handler ); 1509 | } 1510 | 1511 | if ( p_handlers.size( ) == 1 ) 1512 | { 1513 | add_dash( "3152Active", 300.f, 1150.f ).set_name( "Hextech Rocketbelt" ).set_is_fixed_range( true ); 1514 | add_dash( "6671Cast", 425.f, 1350.f ).set_name( "Prowler's Claw" ).set_min_range( 200.f ); 1515 | add_dash( "6693Active", 500.f, 2000.f ).set_name( "Galeforce" ).set_is_targeted( true ).set_delay( 0.2f ); 1516 | 1517 | for ( auto& hero : entitylist->get_enemy_heroes( ) ) 1518 | { 1519 | switch ( hero->get_champion( ) ) 1520 | { 1521 | case champion_id::Aatrox: 1522 | add_dash( "AatroxE", 300.f, 800.f ).set_wait_for_new_path( true ); 1523 | break; 1524 | case champion_id::Ahri: 1525 | add_dash( "AhriTumble", 500.f, 1200.f ).set_is_fixed_range( true ).set_add_ms_ratio( 1.f ); 1526 | break; 1527 | case champion_id::Akali: 1528 | add_dash( "AkaliE", 350.f, 1400.f ).set_is_fixed_range( true ).set_is_inverted( true ).set_delay( 0.2f ); 1529 | add_dash( "AkaliEb", FLT_MAX, 1700.f ).set_is_targeted( true ).set_required_buffhash( buff_hash( "AkaliEMis" ) ).set_find_target_by_buffhash( true ); 1530 | add_dash( "AkaliR", 750.f, 1500.f ).set_is_fixed_range( true ); 1531 | add_dash( "AkaliRb", 800.f, 3000.f ).set_is_fixed_range( true ); 1532 | break; 1533 | case champion_id::Alistar: 1534 | add_dash( "Headbutt", 650.f, 1500.f ).set_is_targeted( true ).set_is_cc( true ); 1535 | break; 1536 | case champion_id::Caitlyn: 1537 | add_dash( "CaitlynE", 390.f, 1000.f ).set_is_fixed_range( true ).set_is_inverted( true ).set_delay( 0.15f ); 1538 | break; 1539 | case champion_id::Camille: 1540 | add_dash( "CamilleEDash2", 800.f, 1050.f ).set_is_cc( true ).set_add_ms_ratio( 1.f ); 1541 | break; 1542 | case champion_id::Corki: 1543 | add_dash( "CarpetBomb", 600.f, 650.f ).set_min_range( 300.f ).set_add_ms_ratio( 1.f ); 1544 | break; 1545 | case champion_id::Diana: 1546 | add_dash( "DianaTeleport", 825.f, 2500.f ).set_is_targeted( true ); 1547 | break; 1548 | case champion_id::Ekko: 1549 | add_dash( "EkkoEAttack", 600.f, 2000.f ).set_is_targeted( true ).set_delay( 0.1f ); 1550 | break; 1551 | case champion_id::Elise: 1552 | add_dash( "EliseSpiderQCast", 475.f, 1200.f ).set_is_targeted( true ); 1553 | add_dash( "EliseSpiderE", 700.f, 1000.f ).set_is_targeted( true ).set_wait_for_targetable( true ).set_always_fixed_delay( 0.5f ); 1554 | break; 1555 | case champion_id::Evelynn: 1556 | add_dash( "EvelynnE2", 400.f, 1900.f ).set_is_targeted( true ); 1557 | break; 1558 | case champion_id::Fiora: 1559 | add_dash( "FioraQ", 450.f, 500.f ).set_add_ms_ratio( 2.f ); 1560 | break; 1561 | case champion_id::Fizz: 1562 | add_dash( "FizzQ", 550.f, 1400.f ).set_is_fixed_range( true ).set_is_targeted( true ); 1563 | break; 1564 | case champion_id::Galio: 1565 | add_dash( "GalioE", 650.f, 2300.f ).set_is_cc( true ).set_delay( 0.4f ); 1566 | break; 1567 | case champion_id::Gnar: 1568 | add_dash( "GnarE", 475.f, 900.f ); 1569 | add_dash( "GnarBigE", 675.f, 1165.f ); 1570 | break; 1571 | case champion_id::Gragas: 1572 | add_dash( "GragasE", 600.f, 900.f ).set_is_cc( true ).set_is_fixed_range( true ); 1573 | break; 1574 | case champion_id::Graves: 1575 | add_dash( "GravesMove", 375.f, 1150.f ).set_wait_for_new_path( true ); 1576 | break; 1577 | case champion_id::Gwen: 1578 | add_dash( "GwenE", 350.f, 1050.f ).set_add_ms_ratio( 1.f ); 1579 | break; 1580 | case champion_id::Hecarim: 1581 | add_dash( "HecarimRampAttack", 900.f, 1200.f ).set_is_cc( true ).set_is_targeted( true ); 1582 | add_dash( "HecarimUlt", 1000.f, 1100.f ).set_is_cc( true ).set_is_unstoppable( true ).set_min_range( 300.f ); 1583 | break; 1584 | case champion_id::Illaoi: 1585 | add_dash( "IllaoiWAttack", 300.f, 800.f ).set_is_targeted( true ); 1586 | break; 1587 | case champion_id::Irelia: 1588 | add_dash( "IreliaQ", 600.f, 1400.f ).set_is_targeted( true ).set_add_ms_ratio( 1.f ); 1589 | break; 1590 | case champion_id::JarvanIV: 1591 | add_dash( "JarvanIVDragonStrike", 850.f, 2000.f ).set_is_cc( true ).set_wait_for_new_path( true ).set_delay( 0.4f ); 1592 | break; 1593 | case champion_id::Jax: 1594 | add_dash( "JaxLeapStrike", 700.f, 1600.f ).set_is_targeted( true ); 1595 | break; 1596 | case champion_id::Jayce: 1597 | add_dash( "JayceToTheSkies", 600.f, 1000.f ).set_is_targeted( true ); 1598 | break; 1599 | case champion_id::Kaisa: 1600 | add_dash( "KaisaR", 3000.f, 3700.f ); 1601 | break; 1602 | case champion_id::Kayn: 1603 | add_dash( "KaynQ", 350.f, 1150.f ); 1604 | add_dash( "KaynRJumpOut", 500.f, 1200.f ).set_wait_for_new_path( true ).set_delay( 3.f ); 1605 | break; 1606 | case champion_id::Khazix: 1607 | add_dash( "KhazixE", 700.f, 1250.f ); 1608 | add_dash( "KhazixELong", 850.f, 1250.f ); 1609 | break; 1610 | case champion_id::Kindred: 1611 | add_dash( "KindredQ", 300.f, 500.f ).set_add_ms_ratio( 1.f ); 1612 | break; 1613 | case champion_id::Kled: 1614 | add_dash( "KledRiderQ", 300.f, 1000.f ).set_is_inverted( true ).set_delay( 0.25f ); 1615 | add_dash( "KledEDash", 700.f, 600.f ).set_add_ms_ratio( 1.f ); 1616 | break; 1617 | case champion_id::Leblanc: 1618 | add_dash( "LeblancW", 600.f, 1450.f ); 1619 | add_dash( "LeblancRW", 600.f, 1450.f ); 1620 | break; 1621 | case champion_id::LeeSin: 1622 | add_dash( "LeeSinQTwo", 2000.f, 2000.f ).set_is_targeted( true ).set_required_buffhash( buff_hash( "LeeSinQOne" ) ).set_find_target_by_buffhash( true ); 1623 | add_dash( "LeeSinWOne", 700.f, 1350.f ).set_is_targeted( true ).set_add_ms_ratio( 1.f ); 1624 | break; 1625 | case champion_id::Leona: 1626 | add_dash( "LeonaZenithBlade", 900.f, 1300.f ).set_is_cc( true ).set_always_fixed_delay( 0.75f ); 1627 | break; 1628 | case champion_id::Lillia: 1629 | add_dash( "LilliaW", 500.f, 1000.f ).set_always_fixed_delay( 0.8f ); 1630 | break; 1631 | case champion_id::Lucian: 1632 | add_dash( "LucianE", 475.f, 1350.f ).set_wait_for_new_path( true ); 1633 | break; 1634 | case champion_id::Malphite: 1635 | add_dash( "UFSlash", 1000.f, 1500.f ).set_is_cc( true ).set_is_unstoppable( true ).set_add_ms_ratio( 1.f ); 1636 | break; 1637 | case champion_id::Maokai: 1638 | add_dash( "MaokaiW", 525.f, 1300.f ).set_is_cc( true ).set_is_unstoppable( true ).set_is_targeted( true ); 1639 | break; 1640 | case champion_id::MonkeyKing: 1641 | add_dash( "MonkeyKingNimbus", 625.f, 1050.f ).set_is_targeted( true ).set_add_ms_ratio( 1.f ); 1642 | break; 1643 | case champion_id::Nidalee: 1644 | add_dash( "Pounce", 750.f, 950.f ).set_wait_for_new_path( true ).set_min_range( 350.f ); 1645 | break; 1646 | case champion_id::Ornn: 1647 | add_dash( "OrnnE", 650.f, 1600.f ).set_is_cc( true ).set_is_fixed_range( true ).set_delay( 0.35f ); 1648 | break; 1649 | case champion_id::Pantheon: 1650 | add_dash( "PantheonW", 600.f, 1100.f ).set_is_cc( true ).set_is_targeted( true ); 1651 | break; 1652 | case champion_id::Poppy: 1653 | add_dash( "PoppyE", 475.f, 1800.f ).set_is_cc( true ).set_is_targeted( true ); 1654 | break; 1655 | case champion_id::Pyke: 1656 | add_dash( "PykeE", 550.f, 2000.f ).set_is_cc( true ).set_is_fixed_range( true ); 1657 | break; 1658 | case champion_id::Qiyana: 1659 | add_dash( "QiyanaE", 550.f, 1100.f ).set_is_fixed_range( true ).set_is_targeted( true ).set_add_ms_ratio( 1.f ); 1660 | break; 1661 | case champion_id::Rakan: 1662 | add_dash( "RakanW", 650.f, 1700.f ).set_is_cc( true ).set_always_fixed_delay( 0.85f ); 1663 | break; 1664 | case champion_id::Rammus: 1665 | add_dash( "Tremors2", 1500.f, 1000.f ).set_is_unstoppable( true ).set_always_fixed_delay( 0.85f ); 1666 | break; 1667 | case champion_id::RekSai: 1668 | add_dash( "RekSaiEBurrowed", 800.f, 800.f ).set_is_fixed_range( true ); 1669 | break; 1670 | case champion_id::Rell: 1671 | add_dash( "RellW_Dismount", 500.f, 1000.f ).set_is_cc( true ).set_always_fixed_delay( 0.85f ); 1672 | break; 1673 | case champion_id::Renekton: 1674 | add_dash( "RenektonDice", 450.f, 760.f ).set_is_fixed_range( true ).set_add_ms_ratio( 1.f ); 1675 | add_dash( "RenektonSliceAndDice", 450.f, 760.f ).set_is_fixed_range( true ).set_add_ms_ratio( 1.f ); 1676 | break; 1677 | case champion_id::Riven: 1678 | add_dash( "RivenFeint", 250.f, 1200.f ).set_is_fixed_range( true ); 1679 | break; 1680 | case champion_id::Samira: 1681 | add_dash( "SamiraE", 650.f, 1600.f ).set_is_fixed_range( true ).set_is_targeted( true ); 1682 | break; 1683 | case champion_id::Sejuani: 1684 | add_dash( "SejuaniQ", 650.f, 1000.f ).set_is_cc( true ); 1685 | break; 1686 | case champion_id::Shen: 1687 | add_dash( "ShenE", 600.f, 800.f ).set_is_cc( true ).set_min_range( 300.f ).set_add_ms_ratio( 1.f ); 1688 | break; 1689 | case champion_id::Shyvana: 1690 | add_dash( "ShyvanaTransformLeap", 950.f, 1100.f ).set_is_unstoppable( true ); 1691 | break; 1692 | case champion_id::Sylas: 1693 | add_dash( "SylasW", 400.f, 1450.f ).set_is_targeted( true ); 1694 | add_dash( "SylasE2", 800.f, 1950.f ).set_is_cc( true ); 1695 | add_dash( "SylasE", 400.f, 1450.f ); 1696 | break; 1697 | case champion_id::Talon: 1698 | add_dash( "TalonQ", 575.f, 1600.f ).set_is_targeted( true ); 1699 | break; 1700 | case champion_id::Tristana: 1701 | add_dash( "TristanaW", 900.f, 1100.f ).set_delay( 0.25f ); 1702 | break; 1703 | case champion_id::Tryndamere: 1704 | add_dash( "TryndamereE", 660.f, 900.f ); 1705 | break; 1706 | case champion_id::Urgot: 1707 | add_dash( "UrgotE", 450.f, 1200.f ).set_is_cc( true ).set_is_fixed_range( true ).set_delay( 0.45f ).set_add_ms_ratio( 1.f ); 1708 | break; 1709 | case champion_id::Vayne: 1710 | add_dash( "VayneTumble", 300.f, 500.f ).set_is_fixed_range( true ).set_add_ms_ratio( 1.f ); 1711 | break; 1712 | case champion_id::Vi: 1713 | add_dash( "ViQ", 725.f, 1400.f ).set_is_cc( true ).set_wait_for_new_path( true ); 1714 | break; 1715 | case champion_id::Viego: 1716 | add_dash( "ViegoR", 500.f, 1000.f ).set_is_unstoppable( true ).set_always_fixed_delay( 0.6f ); 1717 | add_dash( "ViegoW", 300.f, 1000.f ).set_is_cc( true ).set_is_fixed_range( true ); 1718 | break; 1719 | case champion_id::Volibear: 1720 | add_dash( "VolibearR", 700.f, 1000.f ).set_is_unstoppable( true ).set_always_fixed_delay( 1.f ); 1721 | break; 1722 | case champion_id::XinZhao: 1723 | add_dash( "XinZhaoEDash", 1100.f, 2500.f ).set_is_targeted( true ); 1724 | break; 1725 | case champion_id::Yasuo: 1726 | add_dash( "YasuoEDash", 475.f, 750.f ).set_is_fixed_range( true ).set_is_targeted( true ).set_add_ms_ratio( 0.875f ); 1727 | break; 1728 | case champion_id::Yone: 1729 | add_dash( "YoneE", 300.f, 1200.f ).set_is_fixed_range( true ); 1730 | break; 1731 | case champion_id::Zac: 1732 | add_dash( "ZacE", 1800.f, 1000.f ).set_wait_for_new_path( true ); 1733 | break; 1734 | case champion_id::Zed: 1735 | add_dash( "ZedR", 625.f, 1000.f ).set_is_targeted( true ).set_always_fixed_delay( 1.6f ); 1736 | break; 1737 | case champion_id::Zeri: 1738 | add_dash( "ZeriE", 2000.f, 600.f ).set_wait_for_new_path( true ).set_add_ms_ratio( 1.f ); 1739 | break; 1740 | case champion_id::Belveth: 1741 | add_dash( "BelvethQ", 400.f, 800.f + hero->get_move_speed( ) ).set_wait_for_new_path( true ); 1742 | break; 1743 | case champion_id::Nilah: 1744 | add_dash( "NilahE", 450.f, 2200.f ).set_is_targeted( true ); 1745 | break; 1746 | } 1747 | } 1748 | 1749 | event_handler< events::on_new_path >::add_callback( OnNewPath ); 1750 | event_handler< events::on_process_spell_cast >::add_callback( OnProcessSpellCast ); 1751 | event_handler< events::on_update >::add_callback( OnUpdate ); 1752 | } 1753 | } 1754 | 1755 | void remove_event_handler( gapcloser_handler p_handler ) 1756 | { 1757 | auto it = std::find( p_handlers.begin( ), p_handlers.end( ), p_handler ); 1758 | 1759 | if ( it != p_handlers.end( ) ) 1760 | { 1761 | p_handlers.erase( it ); 1762 | } 1763 | 1764 | if ( p_handlers.empty( ) ) 1765 | { 1766 | event_handler< events::on_new_path >::remove_handler( OnNewPath ); 1767 | event_handler< events::on_process_spell_cast >::remove_handler( OnProcessSpellCast ); 1768 | event_handler< events::on_update >::remove_handler( OnUpdate ); 1769 | } 1770 | } 1771 | } 1772 | 1773 | void locked_target_selector::unlock_target( ) 1774 | { 1775 | _last_target_id = 0; 1776 | _last_target_network_id = 0; 1777 | } 1778 | 1779 | game_object_script locked_target_selector::get_target( script_spell* spell, damage_type damage_type ) 1780 | { 1781 | auto last_target = get_last_target( ); 1782 | 1783 | if ( last_target == nullptr || !last_target->is_valid_target( ) || _last_damage_type != damage_type ) 1784 | { 1785 | auto newTarget = target_selector->get_target( spell->range( ), damage_type ); 1786 | 1787 | if ( newTarget != nullptr ) 1788 | { 1789 | _last_target_id = newTarget->get_id( ); 1790 | _last_target_network_id = newTarget->get_network_id( ); 1791 | _last_damage_type = damage_type; 1792 | } 1793 | 1794 | return newTarget; 1795 | } 1796 | 1797 | if ( last_target->is_valid_target( spell->range( ) ) && damage_type == _last_damage_type ) 1798 | { 1799 | return last_target; 1800 | } 1801 | 1802 | auto newTarget2 = target_selector->get_target( spell->range( ), damage_type ); 1803 | 1804 | if ( newTarget2 != nullptr ) 1805 | { 1806 | _last_target_id = newTarget2->get_id( ); 1807 | _last_target_network_id = newTarget2->get_network_id( ); 1808 | _last_damage_type = damage_type; 1809 | } 1810 | 1811 | return newTarget2; 1812 | } 1813 | 1814 | game_object_script locked_target_selector::get_target( float range, damage_type damage_type ) 1815 | { 1816 | auto last_target = get_last_target( ); 1817 | 1818 | if ( last_target == nullptr || !last_target->is_valid_target( ) || _last_damage_type != damage_type ) 1819 | { 1820 | auto newTarget = target_selector->get_target( range, damage_type ); 1821 | 1822 | if ( newTarget != nullptr ) 1823 | { 1824 | _last_target_id = newTarget->get_id( ); 1825 | _last_target_network_id = newTarget->get_network_id( ); 1826 | _last_damage_type = damage_type; 1827 | } 1828 | 1829 | return newTarget; 1830 | } 1831 | 1832 | if ( last_target->is_valid_target( range ) && damage_type == _last_damage_type ) 1833 | { 1834 | return last_target; 1835 | } 1836 | 1837 | auto newTarget2 = target_selector->get_target( range, damage_type ); 1838 | 1839 | if ( newTarget2 != nullptr ) 1840 | { 1841 | _last_target_id = newTarget2->get_id( ); 1842 | _last_target_network_id = newTarget2->get_network_id( ); 1843 | _last_damage_type = damage_type; 1844 | } 1845 | 1846 | return newTarget2; 1847 | } 1848 | 1849 | game_object_script locked_target_selector::get_last_target( ) 1850 | { 1851 | if ( _last_target_id != 0 ) 1852 | { 1853 | auto unit = entitylist->get_object( _last_target_id ); 1854 | 1855 | if ( unit != nullptr && unit->is_valid( ) && unit->get_network_id( ) == _last_target_network_id ) 1856 | { 1857 | return unit; 1858 | } 1859 | } 1860 | 1861 | return nullptr; 1862 | } 1863 | 1864 | script_spell::script_spell( ): _range( FLT_MAX ), slot( spellslot::invalid ) 1865 | { 1866 | 1867 | } 1868 | 1869 | script_spell::script_spell( spellslot slot ): _range( FLT_MAX ) 1870 | { 1871 | this->slot = slot; 1872 | } 1873 | 1874 | script_spell::script_spell( spellslot slot, float range ) 1875 | { 1876 | this->slot = slot; 1877 | this->_range = range; 1878 | } 1879 | script_spell::script_spell( spellslot slot, float range, skillshot_type type, float delay, float speed, float radius, bool collision ) 1880 | { 1881 | this->slot = slot; 1882 | this->_range = range; 1883 | this->type = type; 1884 | this->delay = delay; 1885 | this->speed = speed; 1886 | this->radius = radius; 1887 | this->collision = collision; 1888 | this->from = vector::zero; 1889 | this->range_check_from = vector::zero; 1890 | } 1891 | 1892 | float script_spell::get_damage( game_object_script target, int stage ) 1893 | { 1894 | return damagelib->get_spell_damage( myhero, target, this->slot, false ); 1895 | } 1896 | 1897 | bool script_spell::is_ready( float time ) 1898 | { 1899 | auto spellInfo = myhero->get_spell( this->slot ); 1900 | 1901 | if ( spellInfo && spellInfo->is_learned( ) ) 1902 | { 1903 | auto spell_state = myhero->get_spell_state( this->category == spellslot::invalid ? this->slot : this->category ); 1904 | 1905 | if ( spell_state == 64 ) 1906 | { 1907 | if ( is_charged_spell ) 1908 | { 1909 | if ( gametime->get_time( ) - this->charging_started_time < 0.3f ) 1910 | { 1911 | return true; 1912 | } 1913 | 1914 | auto buff = get_charge_buff( ); 1915 | 1916 | return buff != nullptr && buff->is_valid( ) && buff->is_alive( ); 1917 | } 1918 | } 1919 | 1920 | if ( spell_state == 2 ) 1921 | { 1922 | return true; 1923 | } 1924 | 1925 | if ( spell_state & spell_state::Cooldown ) 1926 | { 1927 | if ( time > 0.f ) 1928 | { 1929 | //cooldown expires in seconds 1930 | return spellInfo->cooldown_start( ) - gametime->get_time( ) <= time; 1931 | } 1932 | } 1933 | } 1934 | 1935 | return false; 1936 | } 1937 | 1938 | int8_t script_spell::icon_index( ) 1939 | { 1940 | auto spellInfo = myhero->get_spell( this->slot ); 1941 | 1942 | if ( !spellInfo || !spellInfo->is_learned( ) ) 1943 | return 0; 1944 | 1945 | return spellInfo->get_icon_index( ); 1946 | } 1947 | 1948 | int script_spell::toogle_state( ) 1949 | { 1950 | auto spellInfo = myhero->get_spell( this->slot ); 1951 | 1952 | if ( !spellInfo || !spellInfo->is_learned( ) ) 1953 | return 0; 1954 | 1955 | return spellInfo->toogle_state( ); 1956 | } 1957 | 1958 | void script_spell::update_chargeable_spell_handle( spellslot slot, bool release_cast ) 1959 | { 1960 | } 1961 | 1962 | spell_data_inst_script script_spell::handle( ) 1963 | { 1964 | return myhero->get_spell( this->slot ); 1965 | } 1966 | 1967 | std::string script_spell::name( ) 1968 | { 1969 | if ( auto handle = this->handle( ) ) 1970 | return handle->get_name( ); 1971 | 1972 | return {}; 1973 | } 1974 | 1975 | uint32_t script_spell::name_hash( ) 1976 | { 1977 | if ( auto handle = this->handle( ) ) 1978 | return handle->get_name_hash( ); 1979 | 1980 | return 0; 1981 | } 1982 | 1983 | float script_spell::range( ) 1984 | { 1985 | if ( !this->is_charged_spell ) 1986 | { 1987 | return this->_range; 1988 | } 1989 | 1990 | if ( this->is_charging( ) ) 1991 | { 1992 | return this->charged_min_range 1993 | + fminf( ( float ) ( this->charged_max_range - this->charged_min_range ), ( float ) ( this->charged_max_range - this->charged_min_range ) * this->charged_percentage( ) ); 1994 | } 1995 | 1996 | return ( float ) this->charged_max_range; 1997 | } 1998 | 1999 | float script_spell::mana_cost( ) 2000 | { 2001 | return myhero->get_mana_for_spell( this->slot ); 2002 | } 2003 | 2004 | float script_spell::charged_percentage( ) 2005 | { 2006 | auto buff = this->get_charge_buff( ); 2007 | 2008 | if ( buff != nullptr && buff->is_valid( ) && buff->is_alive( ) ) 2009 | { 2010 | return ( fmaxf( 0.f, fminf( 1.f, ( gametime->get_time( ) - buff->get_start( ) + 0.25f - ( buff->get_hash_name( ) == buff_hash( "PykeQ" ) ? 0.4f : 0.f ) - ( buff->get_hash_name( ) == buff_hash( "SionQ" ) ? 0.25f : 0.f ) ) / this->charge_duration ) ) ); 2011 | } 2012 | 2013 | return fmaxf( 0.f, fminf( 1.f, this->is_charging( ) ? ( gametime->get_time( ) - this->charging_started_time ) / this->charge_duration : 0.f ) ); 2014 | } 2015 | 2016 | bool script_spell::cast( ) 2017 | { 2018 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2019 | return false; 2020 | 2021 | if ( this->category == spellslot::invalid ) 2022 | myhero->cast_spell( this->slot, true, is_charged_spell ); 2023 | else 2024 | myhero->cast_spell_ex( this->category, this->slot, vector::zero, vector::zero ); 2025 | 2026 | last_cast_spell = gametime->get_time( ); 2027 | return true; 2028 | } 2029 | 2030 | int script_spell::level( ) 2031 | { 2032 | if ( auto handle = this->handle( ) ) 2033 | return handle->level( ); 2034 | 2035 | return 0; 2036 | } 2037 | 2038 | int script_spell::ammo( ) 2039 | { 2040 | if ( auto handle = this->handle( ) ) 2041 | return handle->ammo( ); 2042 | 2043 | return 0; 2044 | } 2045 | 2046 | float script_spell::cooldown_time( ) 2047 | { 2048 | if ( auto handle = this->handle( ) ) 2049 | return handle->cooldown( ); 2050 | 2051 | return 0.f; 2052 | } 2053 | 2054 | void script_spell::set_spell_lock( bool value ) 2055 | { 2056 | //deprecated 2057 | } 2058 | 2059 | bool script_spell::is_spell_locked( ) 2060 | { 2061 | //deprecated 2062 | return false; 2063 | } 2064 | 2065 | vector script_spell::get_cast_on_best_farm_position( int minMinions, bool is_jugnle_mobs ) 2066 | { 2067 | std::vector minionPositions; 2068 | 2069 | auto from_pos = from.is_valid( ) ? from : myhero->get_position( ); 2070 | 2071 | for ( auto minion : ( is_jugnle_mobs ? entitylist->get_jugnle_mobs_minions( ) : entitylist->get_enemy_minions( ) ) ) 2072 | { 2073 | if ( minion->get_position( ).distance( from_pos ) > this->range( ) ) 2074 | continue; 2075 | 2076 | auto input = prediction_input( ); 2077 | input.unit = minion; 2078 | input.delay = this->delay; 2079 | input._from = from_pos; 2080 | input._range_check_from = from_pos; 2081 | input.type = this->type; 2082 | input.speed = this->speed; 2083 | input.spell_slot = this->slot; 2084 | 2085 | minionPositions.push_back( prediction->get_prediction( &input ).get_unit_position( ) ); 2086 | } 2087 | 2088 | if ( minionPositions.empty( ) ) 2089 | return vector( 0, 0, 0 ); 2090 | 2091 | if ( minionPositions.size( ) == 1 ) 2092 | { 2093 | if ( 1 >= minMinions ) 2094 | { 2095 | return minionPositions.front( ); 2096 | } 2097 | else 2098 | return vector( 0, 0, 0 ); 2099 | } 2100 | 2101 | if ( this->type == skillshot_type::skillshot_circle ) 2102 | { 2103 | auto bestPos = vector( ); 2104 | auto count = 0; 2105 | auto startPos = myhero->get_position( ); 2106 | auto width = this->radius; 2107 | auto range = this->range( ); 2108 | auto get_combinations = []( const std::vector& values ) ->std::vector> 2109 | { 2110 | std::vector> result; 2111 | 2112 | for ( int counter = 0; counter < ( 1 << values.size( ) ); ++counter ) 2113 | { 2114 | std::vector subgroup; 2115 | 2116 | for ( size_t i = 0; i < values.size( ); ++i ) 2117 | if ( ( counter & ( 1 << i ) ) == 0 ) 2118 | subgroup.push_back( values[ i ] ); 2119 | 2120 | result.push_back( subgroup ); 2121 | } 2122 | 2123 | return result; 2124 | }; 2125 | 2126 | if ( minionPositions.size( ) < 10 ) 2127 | { 2128 | auto subGroups = get_combinations( minionPositions ); 2129 | 2130 | for ( auto&& subGroup : subGroups ) 2131 | { 2132 | if ( subGroup.size( ) > 0 ) 2133 | { 2134 | auto circle = mec::get_mec( subGroup ); 2135 | 2136 | if ( circle.radius <= width && circle.center.distance_squared( startPos ) <= range * range ) 2137 | { 2138 | if ( subGroup.size( ) >= minMinions ) 2139 | return circle.center; 2140 | 2141 | return vector( 0, 0, 0 ); 2142 | } 2143 | } 2144 | } 2145 | } 2146 | else 2147 | { 2148 | for ( auto&& pos : minionPositions ) 2149 | { 2150 | if ( pos.distance_squared( startPos ) <= range * range ) 2151 | { 2152 | auto c = 0; 2153 | 2154 | for ( auto&& pos2 : minionPositions ) 2155 | { 2156 | if ( pos.distance_squared( pos2 ) <= width * width ) 2157 | ++c; 2158 | } 2159 | 2160 | if ( c >= minMinions ) 2161 | { 2162 | bestPos = pos; 2163 | count = c; 2164 | } 2165 | } 2166 | } 2167 | } 2168 | 2169 | if ( count >= minMinions ) 2170 | { 2171 | return bestPos; 2172 | } 2173 | else 2174 | return vector( 0, 0, 0 ); 2175 | } 2176 | 2177 | if ( this->type == skillshot_type::skillshot_line ) 2178 | { 2179 | auto startPos = myhero->get_position( ); 2180 | auto range = this->range( ); 2181 | auto result = vector( ); 2182 | auto minionCount = 0; 2183 | auto width = this->radius * this->radius; 2184 | std::vector posiblePositions; 2185 | 2186 | posiblePositions.insert( std::end( posiblePositions ), std::begin( minionPositions ), std::end( minionPositions ) ); 2187 | 2188 | auto max = minionPositions.size( ); 2189 | for ( auto i = 0u; i < max; i++ ) 2190 | { 2191 | for ( auto j = 0u; j < max; j++ ) 2192 | { 2193 | if ( minionPositions[ j ] != minionPositions[ i ] ) 2194 | { 2195 | posiblePositions.push_back( ( minionPositions[ j ] + minionPositions[ i ] ) / 2 ); 2196 | } 2197 | } 2198 | } 2199 | 2200 | for ( auto&& pos : posiblePositions ) 2201 | { 2202 | if ( pos.distance_squared( startPos ) <= range * range ) 2203 | { 2204 | auto endPos = startPos + ( pos - startPos ).normalized( ) * range; 2205 | auto count = 0; 2206 | 2207 | for ( auto&& pos2 : minionPositions ) 2208 | { 2209 | if ( pos2.distance( startPos, endPos, true, true ) <= width ) 2210 | ++count; 2211 | } 2212 | 2213 | if ( count >= minionCount ) 2214 | { 2215 | result = endPos; 2216 | minionCount = count; 2217 | } 2218 | } 2219 | } 2220 | 2221 | 2222 | if ( minionCount >= minMinions ) 2223 | { 2224 | return result; 2225 | } 2226 | else 2227 | return vector( 0, 0, 0 ); 2228 | } 2229 | 2230 | return vector( 0, 0, 0 ); 2231 | } 2232 | 2233 | void script_spell::set_damage_type( damage_type type ) 2234 | { 2235 | this->_damage_type = type; 2236 | } 2237 | 2238 | damage_type script_spell::get_damage_type( ) 2239 | { 2240 | return this->_damage_type; 2241 | } 2242 | 2243 | spellslot script_spell::get_slot( ) 2244 | { 2245 | return slot; 2246 | } 2247 | 2248 | float script_spell::get_speed( ) 2249 | { 2250 | return speed; 2251 | } 2252 | 2253 | float script_spell::get_delay( ) 2254 | { 2255 | return delay; 2256 | } 2257 | 2258 | float script_spell::get_radius( ) 2259 | { 2260 | return radius; 2261 | } 2262 | 2263 | bool script_spell::can_cast( game_object_script unit ) 2264 | { 2265 | if ( unit != nullptr && unit->is_valid_target( this->range( ) ) ) 2266 | return this->is_ready( ); 2267 | 2268 | return false; 2269 | } 2270 | 2271 | bool script_spell::is_in_range( game_object_script target, float range ) 2272 | { 2273 | if ( target == nullptr || !target->is_valid( ) ) 2274 | return false; 2275 | 2276 | return target->is_ai_hero( ) 2277 | ? this->is_in_range( target->get_path_controller( )->get_position_on_path( ), range ) 2278 | : this->is_in_range( target->get_position( ), range ); 2279 | } 2280 | 2281 | bool script_spell::is_in_range( vector const& point, float range ) 2282 | { 2283 | auto source = this->range_check_from.is_valid( ) 2284 | ? this->range_check_from 2285 | : ( this->from.is_valid( ) ? this->from : myhero->get_path_controller( )->get_position_on_path( ) ); 2286 | 2287 | auto r = this->range( ); 2288 | auto range_sqr = r * r; 2289 | 2290 | return source.distance_squared( point ) < ( range < 0 ? range_sqr : ( range * range ) ); 2291 | } 2292 | 2293 | void script_spell::set_spell_slot_category( spellslot slot ) 2294 | { 2295 | if ( slot < spellslot::q || slot >= spellslot::r ) 2296 | return; 2297 | 2298 | this->category = slot; 2299 | } 2300 | 2301 | bool script_spell::cast_on_best_farm_position( int minMinions, bool is_jugnle_mobs ) 2302 | { 2303 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2304 | return false; 2305 | 2306 | auto best_pos = get_cast_on_best_farm_position( minMinions, is_jugnle_mobs ); 2307 | 2308 | if ( best_pos.is_valid( ) ) 2309 | { 2310 | if ( !this->is_charged_spell ) 2311 | { 2312 | if ( this->category == spellslot::invalid ) 2313 | myhero->cast_spell( this->slot, best_pos ); 2314 | else 2315 | myhero->cast_spell_ex( this->category, this->slot, best_pos, vector::zero ); 2316 | 2317 | last_cast_spell = gametime->get_time( ); 2318 | return true; 2319 | } 2320 | 2321 | if ( is_charging( ) && gametime->get_time( ) - charging_started_time > 0.f ) 2322 | { 2323 | myhero->update_charged_spell( this->slot, best_pos, true ); 2324 | 2325 | last_cast_spell = gametime->get_time( ); 2326 | return true; 2327 | } 2328 | } 2329 | 2330 | return false; 2331 | } 2332 | 2333 | bool script_spell::cast( game_object_script unit, hit_chance minimum, bool aoe, int min_targets ) 2334 | { 2335 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2336 | return false; 2337 | 2338 | auto output = this->get_prediction( unit ); 2339 | 2340 | if ( output.hitchance >= minimum && output.aoe_targets_hit_count( ) >= min_targets ) 2341 | { 2342 | if ( !this->is_charged_spell ) 2343 | { 2344 | if ( this->category == spellslot::invalid ) 2345 | myhero->cast_spell( this->slot, output.get_cast_position( ) ); 2346 | else 2347 | myhero->cast_spell_ex( this->category, this->slot, output.get_cast_position( ), vector::zero ); 2348 | last_cast_spell = gametime->get_time( ); 2349 | return true; 2350 | } 2351 | 2352 | if ( is_charging( ) && gametime->get_time( ) - charging_started_time > 0.f ) 2353 | { 2354 | myhero->update_charged_spell( this->slot, output.get_cast_position( ), true ); 2355 | 2356 | last_cast_spell = gametime->get_time( ); 2357 | return true; 2358 | } 2359 | } 2360 | return false; 2361 | } 2362 | 2363 | bool script_spell::cast( const vector& position ) 2364 | { 2365 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2366 | return false; 2367 | 2368 | if ( !this->is_charged_spell ) 2369 | { 2370 | if ( this->category == spellslot::invalid ) 2371 | myhero->cast_spell( this->slot, position ); 2372 | else 2373 | myhero->cast_spell_ex( this->category, this->slot, position, vector::zero ); 2374 | 2375 | 2376 | last_cast_spell = gametime->get_time( ); 2377 | return true; 2378 | } 2379 | if ( is_charging( ) && gametime->get_time( ) - charging_started_time > 0.f ) 2380 | { 2381 | myhero->update_charged_spell( this->slot, position, true ); 2382 | 2383 | last_cast_spell = gametime->get_time( ); 2384 | return true; 2385 | } 2386 | return start_charging( ); 2387 | } 2388 | 2389 | bool script_spell::cast( game_object_script unit ) 2390 | { 2391 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2392 | return false; 2393 | 2394 | if ( !this->is_charged_spell ) 2395 | { 2396 | if ( this->category == spellslot::invalid ) 2397 | myhero->cast_spell( this->slot, unit ); 2398 | else 2399 | myhero->cast_spell_ex( this->category, this->slot, vector::zero, vector::zero, unit ); 2400 | 2401 | last_cast_spell = gametime->get_time( ); 2402 | return true; 2403 | } 2404 | if ( is_charging( ) && gametime->get_time( ) - charging_started_time > 0.f ) 2405 | { 2406 | myhero->update_charged_spell( this->slot, unit->get_position( ), true ); 2407 | 2408 | last_cast_spell = gametime->get_time( ); 2409 | return true; 2410 | } 2411 | return false; 2412 | } 2413 | 2414 | bool script_spell::cast( const vector& startPosition, const vector& endPosition ) 2415 | { 2416 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2417 | return false; 2418 | 2419 | if ( this->category == spellslot::invalid ) 2420 | myhero->cast_spell( this->slot, startPosition, endPosition ); 2421 | else 2422 | myhero->cast_spell_ex( this->category, this->slot, startPosition, endPosition ); 2423 | 2424 | last_cast_spell = gametime->get_time( ); 2425 | return true; 2426 | } 2427 | 2428 | bool script_spell::is_charging( ) 2429 | { 2430 | if ( !this->is_ready( ) ) 2431 | return false; 2432 | 2433 | if ( gametime->get_time( ) - this->charging_started_time < 0.3f ) 2434 | { 2435 | return true; 2436 | } 2437 | 2438 | auto buff = get_charge_buff( ); 2439 | 2440 | return buff != nullptr && buff->is_valid( ) && buff->is_alive( ); 2441 | } 2442 | 2443 | bool script_spell::is_fully_charged( ) 2444 | { 2445 | return this->is_charging( ) && this->charged_percentage( ) > 0.975f; 2446 | } 2447 | 2448 | bool script_spell::start_charging( ) 2449 | { 2450 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2451 | return false; 2452 | 2453 | if ( !is_charging( ) && this->is_ready( ) ) 2454 | { 2455 | if ( gametime->get_time( ) < last_cast_spell + 1.5f ) 2456 | return false; 2457 | 2458 | myhero->cast_spell( this->slot, true, true ); 2459 | 2460 | last_cast_spell = gametime->get_time( ); 2461 | 2462 | charging_started_time = gametime->get_time( ) - ( ping->get_ping( ) / 1000.f ); 2463 | return true; 2464 | } 2465 | return this->is_charging( ); 2466 | } 2467 | 2468 | bool script_spell::start_charging( const vector& position ) 2469 | { 2470 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2471 | return false; 2472 | 2473 | if ( !is_charging( ) && this->is_ready( ) ) 2474 | { 2475 | myhero->cast_spell( this->slot, position, true, true ); 2476 | 2477 | last_cast_spell = gametime->get_time( ); 2478 | charging_started_time = gametime->get_time( ) - ( ping->get_ping( ) / 1000.f ); 2479 | return true; 2480 | } 2481 | return this->is_charging( ); 2482 | } 2483 | 2484 | bool script_spell::fast_cast( const vector& position ) 2485 | { 2486 | if ( gametime->get_time( ) < last_cast_spell + sciprt_spell_wait ) 2487 | return false; 2488 | 2489 | if ( is_charging( ) ) 2490 | myhero->update_charged_spell( this->slot, position, true ); 2491 | else 2492 | myhero->cast_spell( this->slot, position ); 2493 | 2494 | last_cast_spell = gametime->get_time( ); 2495 | 2496 | return true; 2497 | } 2498 | 2499 | std::vector script_spell::get_collision_flags( ) 2500 | { 2501 | return collision_flags; 2502 | } 2503 | 2504 | vector script_spell::get_range_check_from( ) 2505 | { 2506 | return this->range_check_from; 2507 | } 2508 | 2509 | vector script_spell::get_from( ) 2510 | { 2511 | return this->from; 2512 | } 2513 | 2514 | void script_spell::set_radius( float radius ) 2515 | { 2516 | this->radius = radius; 2517 | } 2518 | 2519 | void script_spell::set_speed( float speed ) 2520 | { 2521 | this->speed = speed; 2522 | } 2523 | 2524 | void script_spell::set_delay( float delay ) 2525 | { 2526 | this->delay = delay; 2527 | } 2528 | 2529 | void script_spell::set_range( float range ) 2530 | { 2531 | this->_range = range; 2532 | } 2533 | 2534 | void script_spell::set_sollision_flags( const std::vector & flags ) 2535 | { 2536 | this->collision_flags = flags; 2537 | this->collision = flags.size( ) != 0; 2538 | } 2539 | 2540 | void script_spell::set_from( vector const& position ) 2541 | { 2542 | this->from = position; 2543 | } 2544 | 2545 | void script_spell::set_range_check_from( vector const& position ) 2546 | { 2547 | this->range_check_from = position; 2548 | } 2549 | 2550 | void script_spell::set_skillshot( float delay, float radius, float speed, const std::vector & flags, skillshot_type skillshot_type ) 2551 | { 2552 | this->type = skillshot_type; 2553 | this->delay = delay; 2554 | this->radius = radius; 2555 | this->speed = speed; 2556 | this->collision_flags = flags; 2557 | this->collision = flags.size( ) != 0; 2558 | 2559 | if ( this->slot >= spellslot::q && this->slot <= spellslot::r ) 2560 | is_spell_lock_enable = true; 2561 | } 2562 | 2563 | void script_spell::set_charged( float range_min, float range_max, float charge_duration ) 2564 | { 2565 | this->charged_min_range = range_min; 2566 | this->charged_max_range = range_max; 2567 | this->charge_duration = charge_duration; 2568 | this->_range = range_max; 2569 | this->is_charged_spell = true; 2570 | } 2571 | 2572 | prediction_output script_spell::get_prediction( game_object_script target, const vector& origin, const vector& range_check_from ) 2573 | { 2574 | prediction_input x; 2575 | x._from = origin; 2576 | x._range_check_from = range_check_from; 2577 | x.unit = target; 2578 | x.delay = this->delay; 2579 | x.radius = this->radius; 2580 | x.speed = this->speed; 2581 | x.collision_objects = this->collision_flags; 2582 | x.range = this->range( ); 2583 | x.type = this->type; 2584 | x.aoe = false; 2585 | x.spell_slot = this->slot; 2586 | x.use_bounding_radius = this->type != skillshot_type::skillshot_circle; 2587 | 2588 | return prediction->get_prediction( &x ); 2589 | } 2590 | 2591 | std::vector script_spell::get_collision( const vector& from, const std::vector& to_pos, float speedOverride, float delayOverride, float radiusOverride ) 2592 | { 2593 | prediction_input x; 2594 | x._from = from; 2595 | x.delay = delayOverride > 0 ? delayOverride : this->delay; 2596 | x.speed = speedOverride > 0 ? speedOverride : this->speed; 2597 | x.radius = radiusOverride > 0 ? radiusOverride : this->radius; 2598 | x.collision_objects = this->collision_flags; 2599 | x.range = this->range( ); 2600 | x.type = this->type; 2601 | x.spell_slot = this->slot; 2602 | 2603 | return prediction->get_collision( to_pos, &x ); 2604 | } 2605 | 2606 | prediction_output script_spell::get_prediction( game_object_script target, bool aoe, float overrideRange, const std::vector& collisionable ) 2607 | { 2608 | prediction_input x; 2609 | 2610 | x._from = this->from.is_valid( ) ? this->from : myhero->get_position( ); 2611 | x._range_check_from = this->range_check_from.is_valid( ) ? this->range_check_from : x._from; 2612 | x.unit = target; 2613 | x.delay = this->delay; 2614 | x.radius = this->radius; 2615 | x.speed = this->speed; 2616 | x.collision_objects = collisionable.empty( ) ? this->collision_flags : collisionable; 2617 | x.range = overrideRange > 0 ? overrideRange : this->range( ); 2618 | x.type = this->type; 2619 | x.aoe = aoe; 2620 | x.spell_slot = this->slot; 2621 | x.use_bounding_radius = this->type != skillshot_type::skillshot_circle; 2622 | 2623 | return prediction->get_prediction( &x ); 2624 | } 2625 | 2626 | prediction_output script_spell::get_prediction_no_collision( game_object_script target, bool aoe, float overrideRange ) 2627 | { 2628 | prediction_input x; 2629 | 2630 | x._from = this->from.is_valid( ) ? this->from : myhero->get_position( ); 2631 | x._range_check_from = this->range_check_from.is_valid( ) ? this->range_check_from : x._from; 2632 | x.unit = target; 2633 | x.delay = this->delay; 2634 | x.radius = this->radius; 2635 | x.speed = this->speed; 2636 | x.collision_objects = { }; 2637 | x.range = overrideRange > 0 ? overrideRange : this->range( ); 2638 | x.type = this->type; 2639 | x.aoe = aoe; 2640 | x.spell_slot = this->slot; 2641 | x.use_bounding_radius = this->type != skillshot_type::skillshot_circle; 2642 | 2643 | return prediction->get_prediction( &x ); 2644 | } 2645 | 2646 | float script_spell::get_last_cast_spell_time( ) 2647 | { 2648 | return last_cast_spell; 2649 | } 2650 | 2651 | buff_instance_script script_spell::get_charge_buff( ) 2652 | { 2653 | if ( this->is_charged_spell ) 2654 | { 2655 | if ( auto name = this->charge_buff_name ) 2656 | return myhero->get_buff( name ); 2657 | 2658 | switch ( myhero->get_champion( ) ) 2659 | { 2660 | case champion_id::Varus: 2661 | { 2662 | return myhero->get_buff( buff_hash( "VarusQ" ) ); 2663 | } 2664 | case champion_id::Xerath: 2665 | { 2666 | return myhero->get_buff( buff_hash( "XerathArcanopulseChargeUp" ) ); 2667 | } 2668 | case champion_id::Pyke: 2669 | { 2670 | return myhero->get_buff( buff_hash( "PykeQ" ) ); 2671 | } 2672 | case champion_id::Pantheon: 2673 | { 2674 | return myhero->get_buff( buff_hash( "PantheonQ" ) ); 2675 | } 2676 | case champion_id::Sion: 2677 | { 2678 | return myhero->get_buff( buff_hash( "SionQ" ) ); 2679 | } 2680 | case champion_id::Viego: 2681 | { 2682 | return myhero->get_buff( buff_hash( "ViegoW" ) ); 2683 | } 2684 | default: 2685 | return nullptr; 2686 | } 2687 | } 2688 | 2689 | return nullptr; 2690 | } 2691 | 2692 | game_object_script script_spell::get_target( float extra_range ) 2693 | { 2694 | return target_selector->get_target( this->range( ) + extra_range, this->get_damage_type( ) ); 2695 | } 2696 | 2697 | bool math::IsZero( float A ) 2698 | { 2699 | return fabsf( A ) < 1e-6f; 2700 | } 2701 | 2702 | bool math::NearEqual( float A, float B, int maxUlpsDiff ) 2703 | { 2704 | Float_t uA( A ); 2705 | Float_t uB( B ); 2706 | 2707 | // Different signs means they do not match. 2708 | if ( uA.Negative( ) != uB.Negative( ) ) 2709 | { 2710 | // Check for equality to make sure +0==-0 2711 | if ( A == B ) 2712 | return true; 2713 | return false; 2714 | } 2715 | 2716 | // Find the difference in ULPs. 2717 | int ulpsDiff = abs( uA.i - uB.i ); 2718 | if ( ulpsDiff <= maxUlpsDiff ) 2719 | return true; 2720 | return false; 2721 | } 2722 | 2723 | prediction_output prediction_manager::get_prediction( game_object_script unit, float delay ) 2724 | { 2725 | prediction_input input; 2726 | input.unit = unit; 2727 | input.delay = delay; 2728 | 2729 | return prediction->get_prediction( &input ); 2730 | } 2731 | 2732 | prediction_output prediction_manager::get_prediction( game_object_script unit, float delay, float radius ) 2733 | { 2734 | prediction_input input; 2735 | input.unit = unit; 2736 | input.delay = delay; 2737 | input.radius = radius; 2738 | 2739 | return prediction->get_prediction( &input ); 2740 | } 2741 | 2742 | prediction_output prediction_manager::get_prediction( game_object_script unit, float delay, float radius, float speed ) 2743 | { 2744 | prediction_input input; 2745 | input.unit = unit; 2746 | input.delay = delay; 2747 | input.radius = radius; 2748 | input.speed = speed; 2749 | 2750 | return prediction->get_prediction( &input ); 2751 | } 2752 | 2753 | prediction_output prediction_manager::get_prediction( game_object_script unit, float delay, float radius, float speed, std::vector collisionable ) 2754 | { 2755 | prediction_input input; 2756 | input.unit = unit; 2757 | input.delay = delay; 2758 | input.radius = radius; 2759 | input.speed = speed; 2760 | input.collision_objects = collisionable; 2761 | 2762 | return prediction->get_prediction( &input ); 2763 | } 2764 | namespace mec 2765 | { 2766 | mec_circle get_mec( const std::vector& points ) 2767 | { 2768 | auto center = vector( ); 2769 | float radius; 2770 | 2771 | auto ConvexHull = make_convex_hull( points ); 2772 | find_minimal_bounding_circle( ConvexHull, center, radius ); 2773 | return mec_circle( center, radius ); 2774 | } 2775 | 2776 | bool circle_encloses_points( 2777 | const vector& center, 2778 | float radius2, 2779 | const std::vector& points, 2780 | int skip1, 2781 | int skip2, 2782 | int skip3 ) 2783 | { 2784 | for ( auto i = 0u; i < points.size( ); i++ ) 2785 | { 2786 | if ( ( i != skip1 ) && ( i != skip2 ) && ( i != skip3 ) ) 2787 | { 2788 | vector point = points[ i ]; 2789 | float dx = center.x - point.x; 2790 | float dy = center.y - point.y; 2791 | float test_radius2 = dx * dx + dy * dy; 2792 | if ( test_radius2 > radius2 ) return false; 2793 | } 2794 | } 2795 | return true; 2796 | } 2797 | 2798 | void find_circle( const vector& a, const vector& b, const vector& c, vector& center, float& radius2 ) 2799 | { 2800 | auto x1 = ( b.x + a.x ) / 2; 2801 | auto y1 = ( b.y + a.y ) / 2; 2802 | auto dy1 = b.x - a.x; 2803 | auto dx1 = -( b.y - a.y ); 2804 | 2805 | auto x2 = ( c.x + b.x ) / 2; 2806 | auto y2 = ( c.y + b.y ) / 2; 2807 | auto dy2 = c.x - b.x; 2808 | auto dx2 = -( c.y - b.y ); 2809 | 2810 | auto cx = ( y1 * dx1 * dx2 + x2 * dx1 * dy2 - x1 * dy1 * dx2 - y2 * dx1 * dx2 ) / ( dx1 * dy2 - dy1 * dx2 ); 2811 | auto cy = ( cx - x1 ) * dy1 / dx1 + y1; 2812 | center = vector( cx, cy ); 2813 | 2814 | auto dx = cx - a.x; 2815 | auto dy = cy - a.y; 2816 | radius2 = dx * dx + dy * dy; 2817 | } 2818 | 2819 | void find_minimal_bounding_circle( const std::vector& points, vector& center, float& radius ) 2820 | { 2821 | auto hull = make_convex_hull( points ); 2822 | 2823 | auto best_center = points[ 0 ]; 2824 | auto best_radius2 = std::numeric_limits::max( ); 2825 | 2826 | for ( auto i = 0; i < ( std::int32_t ) hull.size( ) - 1; i++ ) 2827 | { 2828 | for ( auto j = i + 1; j < ( std::int32_t ) hull.size( ); j++ ) 2829 | { 2830 | auto test_center = vector( ( hull[ i ].x + hull[ j ].x ) / 2.f, ( hull[ i ].y + hull[ j ].y ) / 2.f ); 2831 | auto dx = test_center.x - hull[ i ].x; 2832 | auto dy = test_center.y - hull[ i ].y; 2833 | auto test_radius2 = dx * dx + dy * dy; 2834 | 2835 | if ( test_radius2 < best_radius2 ) 2836 | { 2837 | if ( circle_encloses_points( test_center, test_radius2, points, i, j, -1 ) ) 2838 | { 2839 | best_center = test_center; 2840 | best_radius2 = test_radius2; 2841 | } 2842 | } 2843 | } 2844 | } 2845 | 2846 | for ( auto i = 0; i < ( std::int32_t ) hull.size( ) - 2; i++ ) 2847 | { 2848 | for ( auto j = i + 1; j < ( std::int32_t ) hull.size( ) - 1; j++ ) 2849 | { 2850 | for ( auto k = j + 1; k < ( std::int32_t ) hull.size( ); k++ ) 2851 | { 2852 | vector test_center; 2853 | float test_radius2; 2854 | find_circle( hull[ i ], hull[ j ], hull[ k ], test_center, test_radius2 ); 2855 | 2856 | if ( test_radius2 < best_radius2 ) 2857 | { 2858 | if ( circle_encloses_points( test_center, test_radius2, points, i, j, k ) ) 2859 | { 2860 | best_center = test_center; 2861 | best_radius2 = test_radius2; 2862 | } 2863 | } 2864 | } 2865 | } 2866 | } 2867 | 2868 | center = best_center; 2869 | if ( best_radius2 == std::numeric_limits::max( ) ) 2870 | { 2871 | radius = 0; 2872 | } 2873 | else 2874 | { 2875 | radius = ( float ) sqrt( best_radius2 ); 2876 | } 2877 | } 2878 | 2879 | void get_min_max_corners( 2880 | const std::vector& points, 2881 | vector& ul, 2882 | vector& ur, 2883 | vector& ll, 2884 | vector& lr ) 2885 | { 2886 | ul = points[ 0 ]; 2887 | ur = ul; 2888 | ll = ul; 2889 | lr = ul; 2890 | 2891 | for ( auto& pt : points ) 2892 | { 2893 | if ( -pt.x - pt.y > -ul.x - ul.y ) 2894 | { 2895 | ul = pt; 2896 | } 2897 | if ( pt.x - pt.y > ur.x - ur.y ) 2898 | { 2899 | ur = pt; 2900 | } 2901 | if ( -pt.x + pt.y > -ll.x + ll.y ) 2902 | { 2903 | ll = pt; 2904 | } 2905 | if ( pt.x + pt.y > lr.x + lr.y ) 2906 | { 2907 | lr = pt; 2908 | } 2909 | } 2910 | } 2911 | 2912 | rectangle_f get_min_max_box( const std::vector& points ) 2913 | { 2914 | vector ul = vector( 0, 0, 0 ), ur = ul, ll = ul, lr = ul; 2915 | get_min_max_corners( points, ul, ur, ll, lr ); 2916 | 2917 | auto xmin = ul.x; 2918 | auto ymin = ul.y; 2919 | 2920 | auto xmax = ur.x; 2921 | if ( ymin < ur.y ) 2922 | { 2923 | ymin = ur.y; 2924 | } 2925 | 2926 | if ( xmax > lr.x ) 2927 | { 2928 | xmax = lr.x; 2929 | } 2930 | auto ymax = lr.y; 2931 | 2932 | if ( xmin < ll.x ) 2933 | { 2934 | xmin = ll.x; 2935 | } 2936 | if ( ymax > ll.y ) 2937 | { 2938 | ymax = ll.y; 2939 | } 2940 | 2941 | auto result = rectangle_f( xmin, ymin, xmax - xmin, ymax - ymin ); 2942 | return result; 2943 | } 2944 | 2945 | std::vector hull_cull( const std::vector& points ) 2946 | { 2947 | std::vector results; 2948 | auto culling_box = get_min_max_box( points ); 2949 | 2950 | for ( auto& pt : points ) 2951 | { 2952 | if ( pt.x <= culling_box.x || pt.x >= culling_box.x + culling_box.width || pt.y <= culling_box.y 2953 | || pt.y >= culling_box.y + culling_box.height ) 2954 | results.push_back( pt ); 2955 | } 2956 | 2957 | 2958 | return results; 2959 | } 2960 | 2961 | float angle_value( float x1, float y1, float x2, float y2 ) 2962 | { 2963 | float t; 2964 | 2965 | auto dx = x2 - x1; 2966 | auto ax = abs( dx ); 2967 | auto dy = y2 - y1; 2968 | auto ay = abs( dy ); 2969 | if ( ax + ay == 0 ) 2970 | { 2971 | t = 360.f / 9.f; 2972 | } 2973 | else 2974 | { 2975 | t = dy / ( ax + ay ); 2976 | } 2977 | if ( dx < 0 ) 2978 | { 2979 | t = 2 - t; 2980 | } 2981 | else if ( dy < 0 ) 2982 | { 2983 | t = 4 + t; 2984 | } 2985 | return t * 90; 2986 | } 2987 | 2988 | std::vector make_convex_hull( std::vector points ) 2989 | { 2990 | points = hull_cull( points ); 2991 | 2992 | vector best_pt = points[ 0 ]; 2993 | 2994 | for ( auto& pt : points ) 2995 | { 2996 | if ( ( pt.y < best_pt.y ) || ( ( pt.y == best_pt.y ) && ( pt.x < best_pt.x ) ) ) 2997 | best_pt = pt; 2998 | } 2999 | 3000 | std::vector hull; 3001 | 3002 | hull.push_back( best_pt ); 3003 | 3004 | points.erase( std::remove( points.begin( ), points.end( ), best_pt ), points.end( ) ); 3005 | 3006 | float sweep_angle = 0; 3007 | for ( ;;) 3008 | { 3009 | if ( points.size( ) == 0 ) 3010 | { 3011 | break; 3012 | } 3013 | 3014 | auto X = hull[ hull.size( ) - 1 ].x; 3015 | auto Y = hull[ hull.size( ) - 1 ].y; 3016 | best_pt = points[ 0 ]; 3017 | float best_angle = 3600; 3018 | 3019 | for ( auto& pt : points ) 3020 | { 3021 | auto test_angle = angle_value( X, Y, pt.x, pt.y ); 3022 | if ( ( test_angle >= sweep_angle ) && ( best_angle > test_angle ) ) 3023 | { 3024 | best_angle = test_angle; 3025 | best_pt = pt; 3026 | } 3027 | } 3028 | 3029 | auto first_angle = angle_value( X, Y, hull[ 0 ].x, hull[ 0 ].y ); 3030 | if ( ( first_angle >= sweep_angle ) && ( best_angle >= first_angle ) ) 3031 | { 3032 | break; 3033 | } 3034 | 3035 | hull.push_back( best_pt ); 3036 | 3037 | points.erase( std::remove( points.begin( ), points.end( ), best_pt ), points.end( ) ); 3038 | 3039 | sweep_angle = best_angle; 3040 | } 3041 | 3042 | return hull; 3043 | } 3044 | 3045 | }; 3046 | 3047 | TreeTextureDescriptor* create_color_texture_descriptor( std::uint32_t color1, std::uint32_t color2 ) 3048 | { 3049 | auto descriptor = new TreeTextureDescriptor( ); 3050 | 3051 | descriptor->color1 = color1; 3052 | descriptor->color2 = color2; 3053 | 3054 | return descriptor; 3055 | } 3056 | 3057 | TreeTextureDescriptor* create_texture_descriptor( void* texture_id, const ImVec4& uv, float rounding, std::uint32_t color ) 3058 | { 3059 | auto descriptor = new TreeTextureDescriptor( ); 3060 | 3061 | descriptor->texture_id = texture_id; 3062 | descriptor->uv = uv; 3063 | descriptor->rounding = rounding; 3064 | descriptor->texture_color = color; 3065 | 3066 | return descriptor; 3067 | } 3068 | 3069 | void TreeEntry::set_texture_info( std::int32_t height, std::int32_t original_height, std::int32_t original_width, bool extend_image ) 3070 | { 3071 | return reinterpret_cast< void( __stdcall* )( TreeEntry*, std::int32_t, std::int32_t, std::int32_t, bool ) >( menu->get_tree_entry_extensions_table( )[ 0 ] )( this, height, original_height, original_width, extend_image ); 3072 | } 3073 | 3074 | const std::vector& TreeEntry::get_prority_sorted_list( ) 3075 | { 3076 | return reinterpret_cast< const std::vector&( __stdcall* )( TreeEntry* ) >( menu->get_tree_entry_extensions_table( )[ 1 ] )( this ); 3077 | } 3078 | 3079 | dragon_type convert_hash_to_dragon_type( std::uint32_t hash ) 3080 | { 3081 | switch ( hash ) 3082 | { 3083 | case buff_hash( "Elder" ): 3084 | return dragon_type::elder; 3085 | case buff_hash( "Chemtech" ): 3086 | return dragon_type::chemtech; 3087 | case buff_hash( "Mountain" ): 3088 | return dragon_type::mountain; 3089 | case buff_hash( "Hextech" ): 3090 | return dragon_type::hextech; 3091 | case buff_hash( "Infernal" ): 3092 | return dragon_type::infernal; 3093 | case buff_hash( "Cloud" ): 3094 | return dragon_type::cloud; 3095 | case buff_hash( "Ocean" ): 3096 | return dragon_type::ocean; 3097 | default: 3098 | return dragon_type::unknown; 3099 | } 3100 | } 3101 | 3102 | bool nav_mesh::is_cell_passable( const vector& pos, game_object_team team ) 3103 | { 3104 | const auto flags = static_cast< std::uint16_t >( this->get_collision_flag( pos ) ); 3105 | const auto special = ( flags & 0xC00 ); 3106 | if ( special != 0 ) 3107 | return ( ( team == game_object_team::order ? 0x400 : ( team == game_object_team::chaos ? 0x800 : 0xC00 ) ) & special ) == special; 3108 | 3109 | return ( flags & 0x2 ) == 0; 3110 | } 3111 | --------------------------------------------------------------------------------