├── favicon.ico
├── FanFields2.png
├── fanfields2-16.png
├── fanfields2-32.png
├── fanfields2-64.png
├── LICENSE
├── iitc_plugin_fanfields2.meta.js
├── README.md
├── .github
└── workflows
│ └── telegram-notifications.yml
└── iitc_plugin_fanfields2.user.js
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Heistergand/fanfields2/HEAD/favicon.ico
--------------------------------------------------------------------------------
/FanFields2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Heistergand/fanfields2/HEAD/FanFields2.png
--------------------------------------------------------------------------------
/fanfields2-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Heistergand/fanfields2/HEAD/fanfields2-16.png
--------------------------------------------------------------------------------
/fanfields2-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Heistergand/fanfields2/HEAD/fanfields2-32.png
--------------------------------------------------------------------------------
/fanfields2-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Heistergand/fanfields2/HEAD/fanfields2-64.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright © 2018-2025 IITC plugin: Fan Fields 2
4 | Copyright © 2018 Justus Kenklies
5 |
6 | Permission to use, copy, modify, and/or distribute this software for
7 | any purpose with or without fee is hereby granted, provided that the
8 | above copyright notice and this permission notice appear in all
9 | copies.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
12 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
13 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
14 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
15 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA
16 | OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
17 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18 | PERFORMANCE OF THIS SOFTWARE.
19 |
--------------------------------------------------------------------------------
/iitc_plugin_fanfields2.meta.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @author Heistergand
3 | // @id fanfields@heistergand
4 | // @name Fan Fields 2
5 | // @category Layer
6 | // @version 2.7.5.20251219
7 | // @description Calculate how to link the portals to create the largest tidy set of nested fields. Enable from the layer chooser.
8 | // @downloadURL https://github.com/Heistergand/fanfields2/raw/master/iitc_plugin_fanfields2.user.js
9 | // @updateURL https://github.com/Heistergand/fanfields2/raw/master/iitc_plugin_fanfields2.meta.js
10 | // @icon https://raw.githubusercontent.com/Heistergand/fanfields2/master/fanfields2-32.png
11 | // @icon64 https://raw.githubusercontent.com/Heistergand/fanfields2/master/fanfields2-64.png
12 | // @supportURL https://github.com/Heistergand/fanfields2/issues
13 | // @namespace https://github.com/Heistergand/fanfields2
14 | // @issueTracker https://github.com/Heistergand/fanfields2/issues
15 | // @homepageURL https://github.com/Heistergand/fanfields2/
16 | // @depends draw-tools@breunigs
17 | // @recommends bookmarks@ZasoGD|draw-tools-plus@zaso|liveInventory@DanielOnDiordna|keys@xelio
18 | // @preview https://raw.githubusercontent.com/Heistergand/fanfields2/master/FanFields2.png
19 | // @match https://intel.ingress.com/*
20 | // @include https://intel.ingress.com/*
21 | // @grant none
22 | // ==/UserScript==
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Fan Fields 2
2 | An INGRESS fan field planner plugin for IITC Desktop and Mobile.
3 |
4 | Use this plugin to easily plan your fanfields. It tells you how many keys you need for each portal, shows the total amount of fields and calculates the AP you will gain just for the links and fields. Works also for star fields and classic multilayer. Does not estimate MU, sorry for that.
5 |
6 | ## Prerequisites
7 | ### Required
8 | - [IITC CE](https://iitc.app) - Ingress Intel Total Conversion Community Edition
9 | - IITC plugin: [Draw tools](https://iitc.app/download_desktop#draw-tools-by-breunigs) by breunings
10 |
11 | ### Supported
12 | - IITC plugin: [Bookmarks for maps and portals](https://iitc.app/download_desktop#bookmarks-by-ZasoGD) by ZasoGD
13 | - IITC plugin: Arc
14 | - IITC plugin: [Keys](https://iitc.app/download_desktop#keys-by-xelio) by xelio
15 | - IITC Plugin: [Live Inventory](https://github.com/IITC-CE/Community-plugins?tab=readme-ov-file#live-inventory-by-eisfrei---fork-by-danielondiordna) by EisFrei - fork by DanielOnDiordna
16 |
17 | ## Features
18 | - Select portals in your area by drawing a polygon around them with DrawTools.
19 | - Let the magic happen: A fanfield Plan will instantly be shown as overlay.
20 | - Add more portals to your plan by just adding more polygons around them.
21 | - Toggle three layers:
22 | - Fanfield Links
23 | - Fanfield Fields
24 | - Fanfield Numbers _(Show statistics on each portal)_
25 | - Show the list of portals
26 | - in the order to visit them
27 | - with the amount of keys you need
28 | - with the keys you already have _(if the Keys plugin is used and maintained)_
29 | - count of outgoing links per portal
30 | - detailed link information:
31 | - link order
32 | - target portal name
33 | - link distance
34 | - Open the path along the Portals in google maps.
35 | - Tweak your plan:
36 | - Move the anchor portal along the hull of the area.
37 | - Set a marker to a portal inside the hull to make it a possible anchor. _(Yes, you can actually make a 360° fan field. Still has issues though.)_
38 | - Toggle the build order clockwise or anticlockwise.
39 | - Toggle the main direction of the fan links: inbounding or outbounding at the anchor portal?
40 | - Set how many SBUL you plan to use
41 | - Toggle the link direction indicator.
42 | - Show respect to the current intel and avoid throwing crosslinks. _(In some cases useful, in others not at all...)_
43 | - Toggle to use bookmarked portals only
44 | - Edit the portal visit order in a portal sequence editior.
45 | - View the straight-line route preview along the portal sequence.
46 | - Copy your plan to Drawtools Plugin.
47 | - Copy your plan to Bookmarks Plugin.
48 | - Copy your plan to Arcs Plugin.
49 | - Gather Stats including Build AP. _(Currently only for links and fields, not for destroying, capturing or deploying resos and mods)_
50 | - mobile support
51 |
52 |
53 | ## Contribute
54 | Don't hesitate to send pull requests to the beta branch.
55 |
56 |
57 | ## Reviews
58 | ### Youtube review by Agent 57Cell
59 | Don't miss this review by Michael Hartley:
60 | https://www.youtube.com/watch?v=Z9TPlpnMYyI
61 |
62 | _Thank you, 57Cell for making awesome Ingress videos. This script wouldn't exist without your fanfields videos._
63 |
64 |
65 | ### Field report by Agent KonnTower
66 | Agent [KonnTower](https://community.ingress.com/en/profile/KonnTower) wrote a report about his [exercise in maxfielding - ~244 fields from 89 portals](https://community.ingress.com/en/discussion/9791/an-exercise-in-maxfielding-244-fields-from-89-portals?)
67 |
68 | _It took three years until I stumbled over this report. It made me really happy to find it._
69 |
70 |
71 | ## Tutorial Video
72 | english: https://youtu.be/jwn6p5xFGNY
73 | german https://youtu.be/IFgYGUdHNcs
74 |
75 | ## Donations
76 | The best way to say thank you is writing reports about where you used it and let me know about it.
77 |
78 | _If you think this is great and you really like to donate something: I have all I need. But the world is not what it seems, so head out and **donate blood** in your area, register as a **bone marrow donor** or **donate money to other charities**. It will change lives._
79 |
80 | ## How it looks (on desktop)
81 | ### New sidebar design (no fieldset box anymore)
82 | 
83 |
84 | ### Overview
85 | You can still see the old fieldset box design here
86 | 
87 |
88 | ### Use of Keys Plugin information
89 | 
90 |
91 | ### Edit the portal visit order in a portal sequence editior
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/.github/workflows/telegram-notifications.yml:
--------------------------------------------------------------------------------
1 | name: Telegram Notifications
2 |
3 | on:
4 | issues:
5 | types: [opened, edited, closed]
6 | issue_comment:
7 | types: [created]
8 | pull_request:
9 | types: [opened, edited, closed, reopened]
10 | pull_request_review_comment:
11 | types: [created]
12 |
13 | jobs:
14 | send_notification:
15 | runs-on: ubuntu-latest
16 | steps:
17 |
18 | # --- Escape Issue Body ---
19 | - name: Escape Issue Body
20 | if: ${{ github.event_name == 'issues' }}
21 | run: |
22 | ESCAPED_BODY=$(echo "${{ github.event.issue.body }}" | sed 's/&/\&/g; s/\</g; s/>/\>/g')
23 | echo "ESCAPED_BODY=$ESCAPED_BODY" >> $GITHUB_ENV
24 |
25 | # --- Escape Comment Body ---
26 | - name: Escape Comment Body
27 | if: ${{ github.event_name == 'issue_comment' || github.event_name == 'pull_request_review_comment' }}
28 | run: |
29 | ESCAPED_COMMENT=$(echo "${{ github.event.comment.body }}" | sed 's/&/\&/g; s/\</g; s/>/\>/g')
30 | echo "ESCAPED_COMMENT=$ESCAPED_COMMENT" >> $GITHUB_ENV
31 |
32 | # --- Escape PR Body ---
33 | - name: Escape PR Body
34 | if: ${{ github.event_name == 'pull_request' }}
35 | run: |
36 | ESCAPED_PR_BODY=$(echo "${{ github.event.pull_request.body }}" | sed 's/&/\&/g; s/\</g; s/>/\>/g')
37 | echo "ESCAPED_PR_BODY=$ESCAPED_PR_BODY" >> $GITHUB_ENV
38 |
39 | # --- Telegram: Issue ---
40 | - name: Send Telegram (Issue)
41 | if: ${{ github.event_name == 'issues' }}
42 | id: tg_issue
43 | continue-on-error: true
44 | uses: appleboy/telegram-action@master
45 | with:
46 | to: ${{ secrets.TELEGRAM_TO }}
47 | token: ${{ secrets.TELEGRAM_TOKEN }}
48 | format: html
49 | disable_web_page_preview: true
50 | message: |
51 | 🚨 Issue Notification 🚨
52 | Issue URL: ${{ github.event.issue.html_url }}
53 | ${{ github.actor }} triggered issue event: ${{ github.event.action }}
54 | Title: ${{ github.event.issue.title }}
55 |
${{ env.ESCAPED_BODY }}56 | 57 | - name: Send Telegram Fallback (Issue) 58 | if: ${{ github.event_name == 'issues' && steps.tg_issue.outcome == 'failure' }} 59 | uses: appleboy/telegram-action@master 60 | with: 61 | to: ${{ secrets.TELEGRAM_TO }} 62 | token: ${{ secrets.TELEGRAM_TOKEN }} 63 | format: html 64 | disable_web_page_preview: true 65 | message: | 66 | 🚨 Issue Notification 🚨 67 | Issue URL: ${{ github.event.issue.html_url }} 68 | ${{ github.actor }} triggered issue event: ${{ github.event.action }} 69 | Title: ${{ github.event.issue.title }} 70 |
cannot parse message to telegram71 | 72 | # --- Telegram: Issue Comment --- 73 | - name: Send Telegram (Issue Comment) 74 | if: ${{ github.event_name == 'issue_comment' }} 75 | id: tg_comment 76 | continue-on-error: true 77 | uses: appleboy/telegram-action@master 78 | with: 79 | to: ${{ secrets.TELEGRAM_TO }} 80 | token: ${{ secrets.TELEGRAM_TOKEN }} 81 | format: html 82 | disable_web_page_preview: true 83 | message: | 84 | 📝 Comment on Issue 📝 85 | Comment URL: ${{ github.event.comment.html_url }} 86 | Issue URL: ${{ github.event.issue.html_url }} 87 | ${{ github.actor }} commented: 88 |
${{ env.ESCAPED_COMMENT }}89 | 90 | - name: Send Telegram Fallback (Issue Comment) 91 | if: ${{ github.event_name == 'issue_comment' && steps.tg_comment.outcome == 'failure' }} 92 | uses: appleboy/telegram-action@master 93 | with: 94 | to: ${{ secrets.TELEGRAM_TO }} 95 | token: ${{ secrets.TELEGRAM_TOKEN }} 96 | format: html 97 | disable_web_page_preview: true 98 | message: | 99 | 📝 Comment on Issue 📝 100 | Comment URL: ${{ github.event.comment.html_url }} 101 | Issue URL: ${{ github.event.issue.html_url }} 102 | ${{ github.actor }} commented: 103 |
cannot parse message to telegram104 | 105 | # --- Telegram: Pull Request --- 106 | - name: Send Telegram (Pull Request) 107 | if: ${{ github.event_name == 'pull_request' }} 108 | id: tg_pr 109 | continue-on-error: true 110 | uses: appleboy/telegram-action@master 111 | with: 112 | to: ${{ secrets.TELEGRAM_TO }} 113 | token: ${{ secrets.TELEGRAM_TOKEN }} 114 | format: html 115 | disable_web_page_preview: true 116 | message: | 117 | 🚀 Pull Request Notification 🚀 118 | PR URL: ${{ github.event.pull_request.html_url }} 119 | ${{ github.actor }} triggered PR event: ${{ github.event.action }} 120 | Title: ${{ github.event.pull_request.title }} 121 |
${{ env.ESCAPED_PR_BODY }}122 | 123 | - name: Send Telegram Fallback (Pull Request) 124 | if: ${{ github.event_name == 'pull_request' && steps.tg_pr.outcome == 'failure' }} 125 | uses: appleboy/telegram-action@master 126 | with: 127 | to: ${{ secrets.TELEGRAM_TO }} 128 | token: ${{ secrets.TELEGRAM_TOKEN }} 129 | format: html 130 | disable_web_page_preview: true 131 | message: | 132 | 🚀 Pull Request Notification 🚀 133 | PR URL: ${{ github.event.pull_request.html_url }} 134 | ${{ github.actor }} triggered PR event: ${{ github.event.action }} 135 | Title: ${{ github.event.pull_request.title }} 136 |
cannot parse message to telegram137 | 138 | # --- Telegram: PR Comment --- 139 | - name: Send Telegram (PR Comment) 140 | if: ${{ github.event_name == 'pull_request_review_comment' }} 141 | id: tg_pr_comment 142 | continue-on-error: true 143 | uses: appleboy/telegram-action@master 144 | with: 145 | to: ${{ secrets.TELEGRAM_TO }} 146 | token: ${{ secrets.TELEGRAM_TOKEN }} 147 | format: html 148 | disable_web_page_preview: true 149 | message: | 150 | 💬 Comment on Pull Request 💬 151 | Comment URL: ${{ github.event.comment.html_url }} 152 | PR URL: ${{ github.event.pull_request.html_url }} 153 | ${{ github.actor }} commented: 154 |
${{ env.ESCAPED_COMMENT }}155 | 156 | - name: Send Telegram Fallback (PR Comment) 157 | if: ${{ github.event_name == 'pull_request_review_comment' && steps.tg_pr_comment.outcome == 'failure' }} 158 | uses: appleboy/telegram-action@master 159 | with: 160 | to: ${{ secrets.TELEGRAM_TO }} 161 | token: ${{ secrets.TELEGRAM_TOKEN }} 162 | format: html 163 | disable_web_page_preview: true 164 | message: | 165 | 💬 Comment on Pull Request 💬 166 | Comment URL: ${{ github.event.comment.html_url }} 167 | PR URL: ${{ github.event.pull_request.html_url }} 168 | ${{ github.actor }} commented: 169 |
cannot parse message to telegram170 | -------------------------------------------------------------------------------- /iitc_plugin_fanfields2.user.js: -------------------------------------------------------------------------------- 1 | // ==UserScript== 2 | // @author Heistergand 3 | // @id fanfields@heistergand 4 | // @name Fan Fields 2 5 | // @category Layer 6 | // @version 2.7.5.20251219 7 | // @description Calculate how to link the portals to create the largest tidy set of nested fields. Enable from the layer chooser. 8 | // @downloadURL https://github.com/Heistergand/fanfields2/raw/master/iitc_plugin_fanfields2.user.js 9 | // @updateURL https://github.com/Heistergand/fanfields2/raw/master/iitc_plugin_fanfields2.meta.js 10 | // @icon https://raw.githubusercontent.com/Heistergand/fanfields2/master/fanfields2-32.png 11 | // @icon64 https://raw.githubusercontent.com/Heistergand/fanfields2/master/fanfields2-64.png 12 | // @supportURL https://github.com/Heistergand/fanfields2/issues 13 | // @namespace https://github.com/Heistergand/fanfields2 14 | // @issueTracker https://github.com/Heistergand/fanfields2/issues 15 | // @homepageURL https://github.com/Heistergand/fanfields2/ 16 | // @depends draw-tools@breunigs 17 | // @recommends bookmarks@ZasoGD|draw-tools-plus@zaso|liveInventory@DanielOnDiordna|keys@xelio 18 | // @preview https://raw.githubusercontent.com/Heistergand/fanfields2/master/FanFields2.png 19 | // @match https://intel.ingress.com/* 20 | // @include https://intel.ingress.com/* 21 | // @grant none 22 | // ==/UserScript== 23 | 24 | /* 25 | 26 | Version History: 27 | -- Version History moved into the code. 28 | 29 | Todo: 30 | 31 | Add a kind of system to have a cluster of Fanfields 32 | Calculate distance to walk for the plan (crow / streets) 33 | Calculate the most efficient possible plan based on ways to walk and keys to farm 34 | Export to Tasks 35 | Calculate amount of possible rebuilds after flippinig the center portal 36 | Click on a link to flip it's direction 37 | 38 | */ 39 | 40 | function wrapper(plugin_info) { 41 | // ensure plugin framework is there, even if iitc is not yet loaded 42 | if(typeof window.plugin !== 'function') window.plugin = function() {}; 43 | plugin_info.buildName = 'main'; 44 | plugin_info.dateTimeVersion = '2025-12-19-021342'; 45 | plugin_info.pluginId = 'fanfields'; 46 | 47 | /* global L, $, dialog, map, portals, links, plugin, formatDistance -- eslint*/ 48 | /* exported setup, changelog -- eslint */ 49 | 50 | let arcname = window.PLAYER.team === 'ENLIGHTENED' ? 'Arc' : '***'; 51 | var changelog = [ 52 | { 53 | version: '2.7.5', 54 | changes: [ 55 | 'NEW: Print your task list.', 56 | 'NEW: Show fields in task list.', 57 | 'FIX: Click on portal in task list now flies to the portal.', 58 | 'FIX: Uniform dialog titles', 59 | ], 60 | }, 61 | { 62 | version: '2.7.4', 63 | changes: [ 64 | 'FIX: Respect Intel not working anymore.', 65 | 'FIX: Dialog width on mobile too small.', 66 | ], 67 | }, 68 | { 69 | version: '2.7.3', 70 | changes: [ 71 | 'FIX: Tooltip must be a glyph sequence.', 72 | 'FIX: Double-Click on leaflet buttons isn\'t zooming the map anymore.', 73 | ], 74 | }, 75 | { 76 | version: '2.7.2', 77 | changes: [ 78 | 'FIX: Code cleanup and refactoring.', 79 | ], 80 | }, 81 | { 82 | version: '2.7.1', 83 | changes: [ 84 | 'FIX: The linking algorithm from version 2.6.6 was not perfect.', 85 | ], 86 | }, 87 | { 88 | version: '2.7.0', 89 | changes: [ 90 | 'NEW: Added portal sequence editor to customise the visit order.', 91 | 'NEW: Added straight-line route preview along the portal sequence.', 92 | ], 93 | }, 94 | { 95 | version: '2.6.6', 96 | changes: [ 97 | 'NEW: New linking algorythm.', 98 | ], 99 | }, 100 | { 101 | version: '2.6.5', 102 | changes: [ 103 | 'FIX: Fixed last fix.', 104 | ], 105 | }, 106 | { 107 | version: '2.6.4', 108 | changes: [ 109 | 'FIX: Fixed compatibility with Inventory Overview plugin.', 110 | ], 111 | }, 112 | { 113 | version: '2.6.3', 114 | changes: [ 115 | 'FIX: Fixed some minor issues like spelling mistakes.', 116 | ], 117 | }, 118 | { 119 | version: '2.6.2', 120 | changes: [ 121 | 'NEW: Task list now contains a single navigation link for each portal.', 122 | ], 123 | }, 124 | { 125 | version: '2.6.1', 126 | changes: [ 127 | 'FIX: Counts of outgoing links and sbul are now correct when respecting intel and using outbounding mode.', 128 | ], 129 | }, 130 | { 131 | version: '2.6.0', 132 | changes: [ 133 | 'NEW: Add control buttons for better ux on mobile.', 134 | ], 135 | }, 136 | { 137 | version: '2.5.6', 138 | changes: [ 139 | 'NEW: Implementing link details in show-as-list dialog.', 140 | ], 141 | }, 142 | { 143 | version: '2.5.5', 144 | changes: [ 145 | 'FIX: Plugin did not work on IITC-Mobile.', 146 | ], 147 | }, 148 | { 149 | version: '2.5.4', 150 | changes: [ 151 | 'NEW: Option to only use bookmarked portals within the Fanfields (Toggle-Button)', 152 | ], 153 | }, 154 | { 155 | version: '2.5.3', 156 | changes: [ 157 | 'NEW: Saving to Bookmarks now creates a folder in the Bookmarks list.', 158 | ], 159 | }, 160 | { 161 | version: '2.5.2', 162 | changes: [ 163 | 'FIX: Prefer LiveInventory Plugin over Keys Plugin (hotfix)', 164 | ], 165 | }, 166 | { 167 | version: '2.5.1', 168 | changes: [ 169 | 'FIX: Prefer LiveInventory Plugin over Keys Plugin', 170 | ], 171 | }, 172 | { 173 | version: '2.5.0', 174 | changes: [ 175 | 'NEW: Integrate key counts from LiveInventory plugin.', 176 | ], 177 | }, 178 | { 179 | version: '2.4.1', 180 | changes: [ 181 | 'FIX: "Show as List" without having the Keys Plugin did not show any Keys.', 182 | ], 183 | }, 184 | { 185 | version: '2.4.0', 186 | changes: [ 187 | 'NEW: Integrate functionality with Key Plugin.', 188 | 'NEW: Replace fieldset box design with a separated sidebar box.', 189 | ], 190 | }, 191 | { 192 | version: '2.3.2', 193 | changes: [ 194 | 'NEW: Introducing code for upcoming multiple fanfields by Drawtools Colors', 195 | 'FIX: some code refactorings', 196 | 'FIX: SBUL defaults to 2 now, assuming most fields are done solo.', 197 | 'FIX: If a marker is not actually snapped onto a portal it does not act as fan point anymore.', 198 | 'FIX: When adding a marker, it\'s now selected as start portal.', 199 | ], 200 | }, 201 | { 202 | version: '2.3.1', 203 | changes: [ 204 | 'FIX: Portals were difficult to select underneath the fanfileds plan.', 205 | ], 206 | }, 207 | { 208 | version: '2.3.0', 209 | changes: [ 210 | 'NEW: Added '+arcname+' support.', 211 | ], 212 | }, 213 | { 214 | version: '2.2.9', 215 | changes: [ 216 | 'FIX: Link direction indicator did not work anymore.', 217 | 'NEW: Link direction indicator is now optional.', 218 | 'NEW: New plugin icon showing a hand fan.', 219 | ], 220 | }, 221 | { 222 | version: '2.2.8', 223 | changes: [ 224 | 'FIX: minor changes', 225 | ], 226 | }, 227 | { 228 | version: '2.2.7', 229 | changes: [ 230 | 'FIX: Menu Buttons in Mobile version are now actually buttons.', 231 | ], 232 | }, 233 | { 234 | version: '2.2.6', 235 | changes: [ 236 | 'NEW: Google Maps Portal Routing', 237 | ], 238 | }, 239 | { 240 | version: '2.2.5', 241 | changes: [ 242 | 'NEW: Set how many SBUL you plan to use.', 243 | 'FIX: Anchor shift button design changed', 244 | ], 245 | }, 246 | { 247 | version: '2.2.4', 248 | changes: [ 249 | 'FIX: Width of dialog boxes did extend screen size', 250 | 'FIX: Fixed what should have been fixed in 2.2.4', 251 | ], 252 | }, 253 | { 254 | version: '2.2.3', 255 | changes: [ 256 | 'FIX: Made Bookmark Plugin optional', 257 | 'NEW: Anchor shifting ("Cycle Start") is now bidirectional.', 258 | 'FIX: Some minor fixes and code formatting.', 259 | ], 260 | }, 261 | { 262 | version: '2.2.2', 263 | changes: [ 264 | 'NEW: Added favicon.ico to script header.', 265 | ], 266 | }, 267 | { 268 | version: '2.2.1', 269 | changes: [ 270 | 'FIX: Merged from Jormund fork (2.1.7): Fixed L.LatLng extension', 271 | ], 272 | }, 273 | 274 | { 275 | version: '2.2.0', 276 | changes: [ 277 | 'FIX: Reintroducing the marker function which was removed in 2.1.7 so that a Drawtools Marker can be used to force a portal inside (or outside) the hull to be the anchor.', 278 | ], 279 | }, 280 | { 281 | version: '2.1.10', 282 | changes: [ 283 | 'FIX: minor fixes', 284 | ], 285 | }, 286 | { 287 | version: '2.1.9', 288 | changes: [ 289 | 'FIX: Fixed blank in header for compatibility with IITC-CE Button.', 290 | 'FIX: Fix for missing constants in leaflet verion 1.6.0.', 291 | ], 292 | }, 293 | { 294 | version: '2.1.8', 295 | changes: [ 296 | 'NEW: Added starting portal advance button to select among the list of perimeter portals.', 297 | ], 298 | }, 299 | { 300 | version: '2.1.7', 301 | changes: [ 302 | 'DEL: Removed marker and random selection of starting point portal.', 303 | 'NEW: Replaced with use of first outer hull portal. This ensures maximum fields will be generated.', 304 | ], 305 | }, 306 | { 307 | version: '2.1.5', 308 | changes: [ 309 | 'FIX: Minor syntax issue affecting potentially more strict runtimes', 310 | ], 311 | }, 312 | { 313 | version: '2.1.4', 314 | changes: [ 315 | 'FIX: Make the clockwise button change its label to "Counterclockwise" when toggled', 316 | ], 317 | }, 318 | { 319 | version: '2.1.3', 320 | changes: [ 321 | 'FIX: added id tags to menu button elements, ...just because.', 322 | ], 323 | }, 324 | { 325 | version: '2.1.2', 326 | changes: [ 327 | 'FIX: Minor issues', 328 | ], 329 | }, 330 | { 331 | version: '2.1.1', 332 | changes: [ 333 | 'FIX: changed List export format to display as a table', 334 | ], 335 | }, 336 | { 337 | version: '2.1.0', 338 | changes: [ 339 | 'NEW: Added save to DrawTools functionality', 340 | 'NEW: Added fanfield statistics', 341 | 'FIX: Changed some menu texts', 342 | 'VER: Increased Minor Version due to DrawTools Milestone', 343 | ], 344 | }, 345 | { 346 | version: '2.0.9', 347 | changes: [ 348 | 'NEW: Added the number of outgoing links to the simple list export', 349 | ], 350 | }, 351 | { 352 | version: '2.0.8', 353 | changes: [ 354 | 'NEW: Toggle the direction of the star-links (Inbound/Outbound) and calculate number of SBUL', 355 | 'FIX: Despite crosslinks, respecting the current intel did not handle done links', 356 | ], 357 | }, 358 | { 359 | version: '2.0.7', 360 | changes: [ 361 | 'FIX: Sorting of the portals was not accurate for far distance anchors when the angle was too equal.', 362 | 'NEW: Added option to respect current intel and not crossing lines.', 363 | ], 364 | }, 365 | { 366 | version: '2.0.6', 367 | changes: [ 368 | 'FIX: Plan messed up on multiple polygons.', 369 | ], 370 | }, 371 | { 372 | version: '2.0.5', 373 | changes: [ 374 | 'FIX: fan links abandoned when Marker was outside the polygon', 375 | 'BUG: Issue found where plan messes up when using more than one polygon (fixed in 2.0.6)', 376 | ], 377 | }, 378 | { 379 | version: '2.0.4', 380 | changes: [ 381 | 'NEW: Added Lock/Unlock button to freeze the plan and prevent recalculation on any events.', 382 | 'NEW: Added a simple text export (in a dialog box)', 383 | 'FIX: Several changes to the algorithm', 384 | 'BUG: Issue found where links are closing fields on top of portals that are successors in the list once you got around the startportal', 385 | ], 386 | }, 387 | { 388 | version: '2.0.3', 389 | changes: [ 390 | 'FIX: Counterclockwise did not work properly', 391 | 'NEW: Save as Bookmarks', 392 | ], 393 | }, 394 | { 395 | version: '2.0.2', 396 | changes: [ 397 | 'NEW: Added Menu', 398 | 'NEW: Added counterclockwise option', 399 | 'FIX: Minor Bugfixes', 400 | ], 401 | }, 402 | { 403 | version: '2.0.1', 404 | changes: [ 405 | 'NEW: Count keys to farm', 406 | 'NEW: Count total fields', 407 | 'NEW: Added labels to portals', 408 | 'FIX: Links were drawn in random order', 409 | 'FIX: Only fields to the center portal were drawn', 410 | ], 411 | }, 412 | ]; 413 | // PLUGIN START //////////////////////////////////////////////////////// 414 | 415 | // use own namespace for plugin 416 | /* jshint shadow:true */ 417 | window.plugin.fanfields = function() {}; 418 | var thisplugin = window.plugin.fanfields; 419 | 420 | // const values 421 | // zoom level used for projecting points between latLng and pixel coordinates. may affect precision of triangulation 422 | thisplugin.PROJECT_ZOOM = 16; 423 | 424 | thisplugin.LABEL_WIDTH = 100; 425 | thisplugin.LABEL_HEIGHT = 49; 426 | 427 | // constants no longer present in leaflet 1.6.0 428 | thisplugin.DEG_TO_RAD = Math.PI / 180; 429 | thisplugin.RAD_TO_DEG = 180 / Math.PI; 430 | 431 | 432 | thisplugin.labelLayers = {}; 433 | 434 | thisplugin.startingpoint = undefined; 435 | thisplugin.availableSBUL = 2; 436 | 437 | thisplugin.locations = []; 438 | thisplugin.fanpoints = []; 439 | thisplugin.sortedFanpoints = []; 440 | thisplugin.perimeterpoints = []; 441 | thisplugin.startingpointIndex = 0; 442 | 443 | 444 | 445 | thisplugin.links = []; 446 | thisplugin.linksLayerGroup = null; 447 | thisplugin.fieldsLayerGroup = null; 448 | thisplugin.numbersLayerGroup = null; 449 | 450 | 451 | // ghi#23 452 | thisplugin.orderPathLayerGroup = null; 453 | thisplugin.showOrderPath = false; 454 | thisplugin.manualOrderGuids = null; 455 | thisplugin.lastPlanSignature = null; 456 | 457 | 458 | thisplugin.selectPolygon = function() {}; 459 | thisplugin.saveBookmarks = function() { 460 | 461 | // loop thru portals and UN-Select them for bkmrks 462 | var bkmrkData, list; 463 | thisplugin.sortedFanpoints.forEach(function(point, index) { 464 | 465 | bkmrkData = window.plugin.bookmarks.findByGuid(point.guid); 466 | if(bkmrkData) { 467 | 468 | list = window.plugin.bookmarks.bkmrksObj.portals; 469 | 470 | delete list[bkmrkData.id_folder].bkmrk[bkmrkData.id_bookmark]; 471 | 472 | $('.bkmrk#'+bkmrkData.id_bookmark + '').remove(); 473 | 474 | window.plugin.bookmarks.saveStorage(); 475 | window.plugin.bookmarks.updateStarPortal(); 476 | 477 | 478 | window.runHooks('pluginBkmrksEdit', {"target": "portal", "action": "remove", "folder": bkmrkData.id_folder, "id": bkmrkData.id_bookmark, "guid":point.guid}); 479 | 480 | console.log('Fanfields2: removed BOOKMARKS portal ('+bkmrkData.id_bookmark+' situated in '+bkmrkData.id_folder+' folder)'); 481 | } 482 | }); 483 | 484 | 485 | let type = "folder"; 486 | let label = 'Fanfields2'; 487 | // Add new folder in the localStorage 488 | let folder_ID = window.plugin.bookmarks.generateID(); 489 | window.plugin.bookmarks.bkmrksObj.portals[folder_ID] = {"label":label,"state":1,"bkmrk":{}}; 490 | 491 | window.plugin.bookmarks.saveStorage(); 492 | window.plugin.bookmarks.refreshBkmrks(); 493 | window.runHooks('pluginBkmrksEdit', {"target": type, "action": "add", "id": folder_ID}); 494 | console.log('Fanfields2: added BOOKMARKS '+type+' '+folder_ID); 495 | 496 | thisplugin.addPortalBookmark = function(guid, latlng, label, folder_ID) { 497 | var bookmark_ID = window.plugin.bookmarks.generateID(); 498 | 499 | // Add bookmark in the localStorage 500 | window.plugin.bookmarks.bkmrksObj.portals[folder_ID].bkmrk[bookmark_ID] = {"guid":guid,"latlng":latlng,"label":label}; 501 | 502 | window.plugin.bookmarks.saveStorage(); 503 | window.plugin.bookmarks.refreshBkmrks(); 504 | window.runHooks('pluginBkmrksEdit', {"target": "portal", "action": "add", "id": bookmark_ID, "guid": guid}); 505 | console.log('Fanfields2: added BOOKMARKS portal '+bookmark_ID); 506 | } 507 | 508 | // loop again: ordered(!) to add them as bookmarks 509 | thisplugin.sortedFanpoints.forEach(function(point, index) { 510 | if (point.guid) { 511 | var p = window.portals[point.guid]; 512 | var ll = p.getLatLng(); 513 | 514 | //plugin.bookmarks.addPortalBookmark(point.guid, ll.lat+','+ll.lng, p.options.data.title); 515 | thisplugin.addPortalBookmark(point.guid, ll.lat+','+ll.lng, p.options.data.title, folder_ID) 516 | } 517 | }); 518 | }; 519 | 520 | thisplugin.updateStartingPoint = function(i) { 521 | thisplugin.startingpointIndex = i; 522 | thisplugin.startingpointGUID = thisplugin.perimeterpoints[thisplugin.startingpointIndex][0]; 523 | thisplugin.startingpoint = this.fanpoints[thisplugin.startingpointGUID]; 524 | 525 | // Reset manual order because the start/anchor changed (ghi#23) 526 | thisplugin.manualOrderGuids = null; 527 | 528 | thisplugin.updateLayer(); 529 | } 530 | 531 | // cycle to next starting point on the convex hull list of portals 532 | thisplugin.nextStartingPoint = function() { 533 | // *** startingpoint handling is duplicated in updateLayer(). 534 | 535 | var i = thisplugin.startingpointIndex + 1; 536 | if (i >= thisplugin.perimeterpoints.length) { 537 | i = 0; 538 | } 539 | thisplugin.updateStartingPoint(i); 540 | }; 541 | 542 | thisplugin.previousStartingPoint = function() { 543 | var i = thisplugin.startingpointIndex - 1; 544 | if (i < 0) { 545 | i = thisplugin.perimeterpoints.length -1; 546 | } 547 | thisplugin.updateStartingPoint(i); 548 | }; 549 | 550 | thisplugin.generateTasks = function() {}; 551 | thisplugin.reset = function() {}; 552 | 553 | 554 | thisplugin.helpDialogWidth = 650; 555 | 556 | thisplugin.help = function() { 557 | var width = thisplugin.helpDialogWidth; 558 | thisplugin.MaxDialogWidth = thisplugin.getMaxDialogWidth(); 559 | if (thisplugin.MaxDialogWidth < thisplugin.helpDialogWidth) { 560 | width = thisplugin.MaxDialogWidth; 561 | } 562 | dialog({ 563 | html: '
Using Drawtools, draw one or more polygons around the portals you want to work with. '+ 564 | 'The Polygons can overlap each other or be completely separated. All portals within the polygons '+ 565 | 'count to your planned fanfield.
'+ 566 | 567 | 'From the layer selector, enable the 3 Fanfields layer for links, fields and numbers. '+ 568 | 'The fanfield will be calculated and shown in red links on the intel. Link directions are indicated '+ 569 | 'by dashed links at the portal to link from.
'+ 570 | 571 | 'The script selects an anchor portal from the hull of all selected portals. Use the Cycle Start '+ 572 | 'Button to select another hull portal as anchor.
'+ 573 | 574 | 'If you want to use portal as anchor, which is inside the hull, (which is totally legitimate), '+ 575 | 'place a marker on a portal to enforce it to be a possible anchor. Again, use the Cycle Start '+ 576 | 'Button until the Start Portal is where you want it to be.
'+ 577 | 578 | 'A Fanfield can be done inbounding by farming many keys at a portal and then link to it by all '+ 579 | 'the other portals. It can also be done outbounding by star-linking from the start portal until the maximum '+ 580 | 'number of outgoing links is reached. You can toggle that for planning accordingly.
'+ 581 | 582 | 'You might need to plan your field around links you cannot or do not want to destroy. This is where the '+ 583 | 'Respect Intel button comes into play. Toggle this to plan your fanfield avoiding crosslinks.
'+ 584 | 585 | 'Use the Lock function to prevent the script from recalculating anything. This is useful '+ 586 | 'if you have a large area and want to zoom into details.
'+ 587 | 588 | 'Try to switch your plan to counterclockwise direction. Your route might be easier or harder '+ 589 | 'if you change directions. Also try different anchors to get one more field out of some portal '+ 590 | 'constellations.
'+ 591 | 592 | 'Copy your fanfield portals to bookmarks or drawtools to extend your possibilities to work '+ 593 | 'with the information.
'+ 594 | 595 | 'Found a bug? Post your issues at GitHub:
https://github.com/Heistergand/fanfields2/issues
| FanPortals: | " + (thisplugin.n-1) + " |
| CenterKeys: | " + thisplugin.centerKeys +" |
| Total links / keys: | " + thisplugin.donelinks.length.toString() +" |
| Fields: | " + thisplugin.triangles.length.toString() +" |
| Build AP (links and fields): | " + (thisplugin.donelinks.length*313 + thisplugin.triangles.length*1250).toString() +" |
| Destroy AP (links and fields): | " + (thisplugin.sortedFanpoints.length*187 + thisplugin.triangles.length*750).toString() + " |
| Pos. | "; 684 | text+="Action | "; 685 | text+="Portal Name | "; 686 | text+="Keys | "; 687 | text+="Links | "; 688 | text+="Fields | "; 689 | 690 | 691 | text+="|
|---|---|---|---|---|---|---|
| ' + (index) + ' | '; 742 | 743 | // Action 744 | 745 | text+=''; 746 | text+=' '; 747 | text+=' '; 748 | text+=' | '; 749 | 750 | 751 | 752 | 753 | 754 | // Portal Name 755 | 756 | text+=''; 757 | const gmapsHref = `https://www.google.com/maps/dir/?api=1&destination=${lat},${lng}&query_destination_id=(${uriTitle})`; 758 | 759 | // Two links are rendered: 760 | // - UI link: uses onclick to interact with IITC (flyToPortal) 761 | // - Print link: real href for PDF / printing 762 | // Visibility is controlled via CSS (@media print or print window styles) 763 | text+=` ${title}`; 764 | text+=` ${title}`; 765 | 766 | 767 | text+=' | '; 768 | 769 | // Keys 770 | text+=''; 771 | // Links 772 | text+=' | ' + portal.outgoing.length + ' | '; 773 | 774 | // Fields (here: empty cell) 775 | text+=''; 776 | 777 | // other 778 | //text+=' | '; 779 | //text+=''; 780 | //text+=' | '; 781 | 782 | // Row End 783 | text+='
| ' + (index) + '.' + thisplugin.sortedFanpoints.indexOf(outPortal) + ' | '; 798 | 799 | // Action 800 | linkDetailText+=''; 801 | linkDetailText+='Link to ' + thisplugin.sortedFanpoints.indexOf(outPortal); 802 | linkDetailText+=' | '; 803 | 804 | let outPortalTitle = 'unknown title'; 805 | if (outPortal.portal !== undefined) { 806 | outPortalTitle = outPortal.portal.options.data.title; 807 | } 808 | // Portal Name (Target) 809 | linkDetailText+=''+ outPortalTitle + ' | '; 810 | 811 | // Keys (here: empty cell) 812 | linkDetailText+=''; 813 | 814 | // Link (Distance) 815 | linkDetailText+=' | ' + formatDistance(distance) + ' | '; 816 | // Fields 817 | let meta = portal.outgoingMeta?.[outPortal.guid]; 818 | let fieldsCreated = meta?.creatingFieldsWith?.length ?? 0; 819 | let triangles = fieldsCreated === 2 ? '▲▲' : fieldsCreated === 1 ? '▲' : ''; 820 | 821 | linkDetailText+='' + triangles + ' | '; 822 | // other 823 | //linkDetailText+=''; 824 | //linkDetailText+=''; 825 | //linkDetailText+=' | '; 826 | // Row End 827 | linkDetailText+='
No Fanfield plan calculated yet.
Draw a polygon and let Fanfields calculate first.
| '; 995 | html += ' | # | '; 996 | html += 'Portal | '; 997 | html += 'Keys | '; 998 | html += 'Links out | '; 999 | html += '⚓ | ' 1014 | : '☰ | '; 1015 | 1016 | html += '
|---|---|---|---|---|
| ' + idx + ' | '; 1019 | html += '' + title + (isAnchor ? ' (anchor)' : '') + ' | '; 1020 | html += '' + keys + ' | '; 1021 | html += '' + out + ' | '; 1022 | html += '