├── .editorconfig
├── .gitignore
├── README.md
├── data
└── shared
│ ├── ic_clickIcons.i3d
│ ├── ic_clickIcons.i3d.shapes
│ ├── ic_clickIcons.mb
│ ├── ic_clickIcons_emissive.dds
│ ├── ic_clickIcons_emissive.gim
│ └── ic_clickIcons_emissive.png
├── documentation
└── interactiveControl.html
├── i18n
├── locale_br.xml
├── locale_cz.xml
├── locale_de.xml
├── locale_en.xml
├── locale_fr.xml
├── locale_it.xml
├── locale_pl.xml
└── locale_ru.xml
├── icon_interactiveControl.dds
├── icon_interactiveControl.gim
├── icon_interactiveControl.png
├── modDesc.xml
└── src
├── events
├── ICNumStateEvent.lua
└── ICStateEvent.lua
├── interactiveControl
├── InteractiveBase.lua
├── InteractiveButton.lua
└── InteractiveClickPoint.lua
├── loader.lua
├── misc
├── AdditionalSettingsManager.lua
├── InteractiveControlManager.lua
├── InteractiveFunctions.lua
└── InteractiveFunctions_externalMods.lua
└── vehicles
└── specializations
├── AddInteractiveControl.lua
└── InteractiveControl.lua
/.editorconfig:
--------------------------------------------------------------------------------
1 | # top-most EditorConfig file
2 | root = true
3 |
4 | [*]
5 | end_of_line = CRLF # CRLF based line ending because of Windows based game.
6 | insert_final_newline = true
7 | trim_trailing_whitespace = true
8 | indent_style = space
9 | indent_size = 4
10 | charset = utf-8
11 | max_line_length = 200
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.svn
2 |
3 | **/.mayaSwatches
4 |
5 | .idea/
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Interactive Control
2 |
3 | 'Interactive Control' is a global script mod for Farming Simulator 22.
4 | While this mod is active, you are able to use many other mods that support Interactive Control.
5 | With IC you have the possibility to interactively control many parts of several (prepared) vehicles. to use many other mods that support Interactive Control. With IC you have the possibility to interactively control many parts of several (prepared) vehicles.
6 |
7 | Possibilities
8 |
9 |
10 | ## Possibilities
11 |
12 | 'Interactive Control' provides different possibilities to interact with your vehicles. You can use click icons that appear when you turn on IC or when you are nearby. Another way for interactive handling is a key binding event. The controls are able to be used as switch or to force a state.
13 | All interactions are generally possible to use from the inside and the outside of a vehicle.
14 |
15 | Using the controls you can steer different things:
16 | * Play animations (e.g. to open/close windows, fold/unfold warning signs, ...)
17 | * Call specific [functions](#FunctionOverview) (e.g. Start/Stop Motor, TurnOn/Off tool, Lift/Lower attacher joints, ...)
18 | * ObjectChanges (to change translation/rotation/visibility/...)
19 |
20 | ## Thanks goes to:
21 | ***Wopster, JoPi, SirJoki80 & Flowsen (for the ui elements) and Face (for the initial idea)***
22 |
23 | ***& AgrarKadabra for many contributions!***
24 |
25 | ***VertexDezign & SchnibblModding for testing and providing demo mods!***
26 |
27 |
28 |
29 | ## Documentation
30 |
31 | The documentation is not finished yet, but should be sufficient for experienced users.
32 | If you are in need of some extra help, take a look into the demonstration mods:
33 | * Fendt Vario 900 Gen 6 / Gen 7
34 | * Modhub: https://farming-simulator.com/mod.php?mod_id=225936
35 | * Kerner Corona Pack
36 | * Modhub: https://farming-simulator.com/mod.php?mod_id=251288
37 | * Faresin 6.26
38 | * Modhub: https://farming-simulator.com/mod.php?mod_id=258842
39 |
40 |
41 | ### XML
42 |
43 | Explained XML documentation [HTML-file](documentation/interactiveControl.html)
44 | ```xml
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | ```
117 |
118 |
119 | ### FunctionOverview:
120 |
121 | Function | Description | Requirements
122 | -------- | -------- | --------
123 | MOTOR_START_STOPP | Toggle vehicle motor start and stop
124 | LIGHTS_TOGGLE | Toggle lights on and off
125 | LIGHTS_WORKBACK_TOGGLE | Toggle worklights back on and off
126 | LIGHTS_WORKFRONT_TOGGLE | Toggle worklights front on and off
127 | LIGHTS_HIGHBEAM_TOGGLE | Toggle highbeamlights on and off
128 | LIGHTS_TURNLIGHT_HAZARD_TOGGLE | Toggle hazard lights on and off
129 | LIGHTS_TURNLIGHT_LEFT_TOGGLE | Toggle turnlight left on and off
130 | LIGHTS_TURNLIGHT_RIGHT_TOGGLE | Toggle turnlight right on and off
131 | LIGHTS_BEACON_TOGGLE | Toggle beaconlight on and off
132 | LIGHTS_PIPE_TOGGLE1 | Toggle pipelight on and off |
133 | CRUISE_CONTROL_TOGGLE | Toggle cruise control on and off
134 | DRIVE_DIRECTION_TOGGLE | Toggle vehicle drive direction
135 | COVER_TOGGLE | Toggle cover state
136 | ATTACHERJOINT_LIFT_LOWER | Lift/lower implement on attacherJoint index or first selected one if more indicies | ".attacherJoint#index" or ".attacherJoint#indicies"
137 | ATTACHERJOINT_TURN_ON_OFF | Turn on/off implement on attacherJoint index or first selected one if more indicies | ".attacherJoint#index"
138 | TURN_ON_OFF | Turn on/off vehicle
139 | ATTACHERJOINT_FOLDING_TOGGLE | Fold/unfold implement on attacherJoint index or first selected one if more indicies | ".attacherJoint#index"
140 | PIPE_FOLDING_TOGGLE1 | Fold/unfold pipe |
141 | FOLDING_TOGGLE | Fold/unfold vehicle
142 | ATTACHERJOINTS_TOGGLE_DISCHARGE | Toggle discharging on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
143 | DISCHARGE_TOGGLE | Toggle discharging on vehicle
144 | CRABSTEERING_TOGGLE | Toggle crab steering mode to next mode
145 | RADIO_TOGGLE | Toggle radio on/off
146 | RADIO_CHANNEL_NEXT1 | Next radio channel |
147 | RADIO_CHANNEL_PREVIOUS1 | Previous radio channel |
148 | RADIO_ITEM_NEXT1 | Next radio item |
149 | RADIO_ITEM_PREVIOUS1 | Previous radio item |
150 | VARIABLE_WORK_WIDTH_LEFT_INCREASE1 | Increase work width left |
151 | VARIABLE_WORK_WIDTH_LEFT_DECREASE1 | Decrease work width left |
152 | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_LEFT_INCREASE1 | Increase work width left on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
153 | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_LEFT_DECREASE1 | Decrease work width left on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
154 | VARIABLE_WORK_WIDTH_RIGHT_INCREASE1 | Increase work width right |
155 | VARIABLE_WORK_WIDTH_RIGHT_DECREASE1 | Decrease work width right |
156 | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_RIGHT_INCREASE1 | Increase work width right on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
157 | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_RIGHT_DECREASE1 | Decrease work width right on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
158 | VARIABLE_WORK_WIDTH_TOGGLE1 | Toggle work width |
159 | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_TOGGLE1 | Toggle work width on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
160 | ATTACHERJOINTS_ATTACH_DETACH1 | Attach or detach vehicle on attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
161 | REVERSEDRIVING_TOGGLE2 | Toggle vehicle reverse driving |
162 | **External Mods**:
163 | GPS_TOGGLE | Toggle [GuidanceSteering](https://farming-simulator.com/mod.php?mod_id=228522) on and off
164 | GPS_TOGGLE_ACTIVE2 | Toggle [GuidanceSteering](https://farming-simulator.com/mod.php?mod_id=228522) active mode |
165 | PF_CROP_SENSOR_TOGGLE1 | Toggle [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) crop sensor mode |
166 | PF_ATTACHERJOINTS_CROP_SENSOR_TOGGLE1 | Toggle [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) crop sensor mode on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
167 | PF_SEED_RATE_MODE1 | Toggle [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) seed rate mode |
168 | PF_ATTACHERJOINTS_SEED_RATE_MODE1 | Toggle [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) seed rate mode on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
169 | PF_SEED_RATE_UP1 | Increase [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual seed rate |
170 | PF_SEED_RATE_DOWN1 | Decrease [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual seed rate |
171 | PF_ATTACHERJOINTS_SEED_RATE_UP1 | Increase [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual seed rate on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
172 | PF_ATTACHERJOINTS_SEED_RATE_DOWN1 | Decrease [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual seed rate on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
173 | PF_SPRAY_AMOUNT_MODE1 | Toggle [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) spray amount mode |
174 | PF_ATTACHERJOINTS_SPRAY_AMOUNT_MODE | Toggle [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) spray amount mode on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
175 | PF_SPRAY_AMOUNT_UP1 | Increase [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual spray amount |
176 | PF_SPRAY_AMOUNT_DOWN1 | Decrease [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual spray amount |
177 | PF_ATTACHERJOINTS_SPRAY_AMOUNT_UP1 | Increase [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual spray amount on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
178 | PF_ATTACHERJOINTS_SPRAY_AMOUNT_DOWN1 | Decrease [PrecisionFarming](https://farming-simulator.com/mod.php?mod_id=238269) manual spray amount on selected attacherJoint if in 'indices' | ".attacherJoint#index" or ".attacherJoint#indicies"
179 | VCA_TOGGLE_AWD1 | Toggle [VehicleControlAddon](https://farming-simulator.com/mod.php?mod_id=228601) all wheel drive mode on and off |
180 | VCA_TOGGLE_DIFFLOCK_FRONT1 | Toggle [VehicleControlAddon](https://farming-simulator.com/mod.php?mod_id=228601) front differential lock on and off |
181 | VCA_TOGGLE_DIFFLOCK_BACK1 | Toggle [VehicleControlAddon](https://farming-simulator.com/mod.php?mod_id=228601) back differential lock on and off |
182 | VCA_TOGGLE_PARKINGBRAKE1 | Toggle [VehicleControlAddon](https://farming-simulator.com/mod.php?mod_id=228601) parking brake on and off |
183 | HEADLAND_MANAGEMENT_TOGGLE1 | Toggle [HeadlandManagement](https://farming-simulator.com/mod.php?mod_id=228759) on and off |
184 | MS_TOGGLE_PUMP2 | Toggle [ManureSystem](https://farming-simulator.com/mod.php?&mod_id=281039) pump on and off |
185 | MS_TOGGLE_PUMP_DIRECTION2 | Toggle [ManureSystem](https://farming-simulator.com/mod.php?&mod_id=281039) pump direction |
186 |
187 | 1Since version 1.1.0.0
188 | 2Since version 1.2.0.0
189 |
190 | ## Copyright
191 |
192 | Copyright (c) 2024, John Deere 6930. All rights reserved.
193 |
--------------------------------------------------------------------------------
/data/shared/ic_clickIcons.i3d:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/data/shared/ic_clickIcons.i3d.shapes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasF92/FS22_interactiveControl/44926c2eba533f36715798d284edbf8dbb2f1c9f/data/shared/ic_clickIcons.i3d.shapes
--------------------------------------------------------------------------------
/data/shared/ic_clickIcons.mb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasF92/FS22_interactiveControl/44926c2eba533f36715798d284edbf8dbb2f1c9f/data/shared/ic_clickIcons.mb
--------------------------------------------------------------------------------
/data/shared/ic_clickIcons_emissive.dds:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasF92/FS22_interactiveControl/44926c2eba533f36715798d284edbf8dbb2f1c9f/data/shared/ic_clickIcons_emissive.dds
--------------------------------------------------------------------------------
/data/shared/ic_clickIcons_emissive.gim:
--------------------------------------------------------------------------------
1 |
2 |
3 | bc7
4 |
--------------------------------------------------------------------------------
/data/shared/ic_clickIcons_emissive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasF92/FS22_interactiveControl/44926c2eba533f36715798d284edbf8dbb2f1c9f/data/shared/ic_clickIcons_emissive.png
--------------------------------------------------------------------------------
/documentation/interactiveControl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | XML Documentation (v1.13.1.1): interactiveControl
5 |
58 |
59 | <vehicle>
60 |
61 | <interactiveControl>
63 | <interactiveControlConfigurations>
65 | <interactiveControlConfiguration>
67 | <interactiveControls>
69 | <interactiveControl negText="$l10n_actionIC_deactivate"Description: Text for negative direction action
Type: String or l10n key
Default:
72 | $l10n_actionIC_deactivate
Required: no
posText="$l10n_actionIC_activate"Description: Text for positive direction action
Type: String or l10n key
Default:
75 | $l10n_actionIC_activate
Required: no
>
77 | <animation initTime="float"Description:
79 | Start animation time
Type: Float
Required: no
name="string"Description:
81 | Animation name
Type: String
Required: no
speedScale="1"Description: Speed
83 | factor animation is played
Type: Float
Default: 1
Required: no
/>
85 | <button animMaxLimit="1"Description:
87 | Max. anim limit
Type: Float
Default: 1
Required: no
animMinLimit="0"Description:
89 | Min. anim limit
Type: Float
Default: 0
Required: no
animName="string"Description:
91 | Animation name
Type: String
Required: no
foldMaxLimit="1"Description:
93 | Fold max. limit
Type: Float
Default: 1
Required: no
foldMinLimit="0"Description:
95 | Fold min. limit
Type: Float
Default: 0
Required: no
forcedState="boolean"Description: Forced state at execution
Type: Boolean
Required:
98 | no
input="string"Description: Name of button
Type:
100 | String
Required: yes
range="5"Description: Range of button
Type: Float
Default:
102 | 5
Required: no
refNode="node"Description: Reference node used to calculate the
104 | range. If not set, vehicle rootNode is used.
Type: Index to i3d node or i3d mapping
105 | identifier
Required: no
type="UNKNOWN"Description: Types of interactive object
Type:
107 | String
Default: UNKNOWN
Required: yes
/>
109 | <clickPoint alignToCamera="true"Description:
111 | Aligns clickpoint to current camera
Type: Boolean
Default: true
Required: no
112 | animMaxLimit="1"Description: Max. anim limit
Type: Float
Default: 1
Required:
114 | no
animMinLimit="0"Description: Min. anim limit
Type: Float
Default:
116 | 0
Required: no
animName="string"Description: Animation name
Type:
118 | String
Required: no
blinkSpeedScale="1"Description:
120 | Speed scale of size scaling
Type: Float
Default: 1
Required: no
foldMaxLimit="1"Description: Fold max. limit
Type: Float
Default: 1
Required:
123 | no
foldMinLimit="0"Description: Fold min. limit
Type: Float
Default:
125 | 0
Required: no
forcedState="boolean"Description: Forced state at execution
Type:
127 | Boolean
Required: no
iconType="CROSS"Description: Types of click point: TURNLIGHT_RIGHT
129 | GPS TURNLIGHT_LEFT UNKNOWN CRUISE_CONTROL IGNITIONKEY ATTACHERJOINT_LOWER ATTACHERJOINT_LIFT
130 | ATTACHERJOINT TURN_ON LIGHT BEACON_LIGHT ARROW PIPE_FOLDING LIGHT_HIGH CROSS
Type: String
Default:
131 | CROSS
Required: yes
invertX="false"Description: Invert click icon on x-axis
Type:
133 | Boolean
Default: false
Required: no
invertZ="false"Description:
135 | Invert click icon on x-axis
Type: Boolean
Default: false
Required: no
node="node"Description: Click point node
Type: Index to i3d node or i3d mapping
138 | identifier
Required: yes
scaleOffset="float"Description:
140 | Scale offset
Type: Float
Default: size / 10
Required: no
size="0.04"Description: Size of click point
Type: Float
Default: 0.04
Required:
143 | no
type="UNKNOWN"Description: Types of interactive object
Type:
145 | String
Default: UNKNOWN
Required: yes
/>
147 | <configurationsRestrictions>
149 | <restriction indicies="1 2 .. n"Description:
151 | Configuration indicies to block interactive control
Type: Multiple values
Default:
152 | true
Required: no
name="string"Description: Configuration name
Type:
154 | String
Required: no
/>
155 | </configurationsRestrictions>
156 | <dashboard activeTime="1"Description: (IC)
158 | Time to hold dashboard active
Type: Time in seconds
Default: 1
Required: no
159 | animName="string"Description: (ANIMATION) Animation name
Type: String
Required:
161 | no
baseColor="string"Description: (EMITTER) Base color (DashboardColor OR
163 | BrandColor OR r g b a)
Type: String
Required: no
displayType="string"Description:
165 | Display type name
Type: String
Required: no
doInterpolation="false"Description: Do interpolation
Type: Boolean
Default: false
Required:
168 | no
emissiveScale="0.2"Description: (NUMBER) Scale of emissive map
Type:
170 | Float
Default: 0.2
Required: no
emitColor="string"Description:
172 | (EMITTER) Emit color (DashboardColor OR BrandColor OR r g b a)
Type: String
Required:
173 | no
font="DIGIT"Description: (NUMBER) Name of font to apply to
175 | mesh
Type: String
Default: DIGIT
Required: no
fontThickness="1"Description:
177 | (TEXT) Thickness factor for font characters
Type: Float
Default: 1
Required:
178 | no
groups="string"Description: List of groups
Type:
180 | String
Required: no
hasNormalMap="false"Description:
182 | (NUMBER) Normal map will be applied to number decals
Type: Boolean
Default: false
Required:
183 | no
hiddenColor="string"Description: (TEXT) Color of hidden character (if
185 | defined a '0' in this color is display instead of nothing)
Type: String
Required:
186 | no
idleValue="0"Description: Idle value
Type: Float
Default:
188 | 0
Required: no
intensity="1"Description: (EMITTER) Intensity
Type:
190 | Float
Default: 1
Required: no
interpolationSpeed="0.005"Description: Interpolation speed
Type: Float
Default: 0.005
Required:
193 | no
maxRot="string"Description: (ROT) Min. rotation (Rotation value if
195 | rotAxis is given | Rotation Vector of rotAxis is not given)
Type: String
Required:
196 | no
maxValueAnim="float"Description: (ANIMATION) Max. reference value for
198 | animation
Type: Float
Required: no
maxValueRot="float"Description:
200 | (ROT) Max. reference value for rotation
Type: Float
Required: no
maxValueSlider="float"Description: (SLIDER) Max. reference value for slider
Type: Float
Required:
203 | no
minRot="string"Description: (ROT) Min. rotation (Rotation value if
205 | rotAxis is given | Rotation Vector of rotAxis is not given)
Type: String
Required:
206 | no
minValueAnim="float"Description: (ANIMATION) Min. reference value for
208 | animation
Type: Float
Required: no
minValueRot="float"Description:
210 | (ROT) Min. reference value for rotation
Type: Float
Required: no
minValueSlider="float"Description: (SLIDER) Min. reference value for slider
Type: Float
Required:
213 | no
node="node"Description: (EMITTER | ROT | VISIBILITY)
215 | Node
Type: Index to i3d node or i3d mapping identifier
Required: no
numberColor="string"Description: (NUMBER) Numbers color (DashboardColor OR BrandColor OR r g b a)
Type:
218 | String
Required: no
numbers="node"Description: (NUMBER) Numbers node
Type: Index to
220 | i3d node or i3d mapping identifier
Required: no
onICActivate="true"Description:
222 | (IC) Use dashboard on activate ic action
Type: Boolean
Default: true
Required:
223 | no
onICDeactivate="true"Description: (IC) Use dashboard on deactivate ic
225 | action
Type: Boolean
Default: true
Required: no
precision="1"Description:
227 | (NUMBER) Precision
Type: Integer
Default: 1
Required: no
raiseTime="1"Description: (IC) Time to raise dashboard active
Type: Time in seconds
Default:
230 | 1
Required: no
rotAxis="float"Description: (ROT) Rotation axis
Type:
232 | Float
Required: no
textAlignment="RIGHT"Description: (TEXT) Alignment of text (LEFT | RIGHT | CENTER)
Type:
235 | String
Default: RIGHT
Required: no
textColor="string"Description:
237 | (TEXT) Font color (DashboardColor OR BrandColor OR r g b a)
Type: String
Required:
238 | no
textMask="00.0"Description: (TEXT) Font Mask
Type:
240 | String
Default: 00.0
Required: no
textScaleX="1"Description:
242 | (TEXT) Global X scale of text
Type: Float
Default: 1
Required: no
textScaleY="1"Description: (TEXT) Global Y scale of text
Type: Float
Default: 1
Required:
245 | no
textSize="0.03"Description: (TEXT) Size of font in meter
Type:
247 | Float
Default: 0.03
Required: no
valueType="string"Description:
249 | Value type name (Available: ic_state | ic_stateValue | ic_action)
Type: String
Required:
250 | no
>
251 | <state rotation="x y z"Description:
253 | (MULTI_STATE) Rotation while state is active
Type: Rotation values (x, y, z)
Required:
254 | no
scale="x y
255 | z"Description: (MULTI_STATE) Scale while state is active
Type: Scale
256 | values (x, y, z)
Required: no
translation="x y z"Description:
258 | (MULTI_STATE) Translation while state is active
Type: Translation values (x, y, z)
Required:
259 | no
value="1 2 ..
260 | n"Description: (MULTI_STATE) One or multiple values separated by space to
261 | activate the state
Type: Multiple values
Required: no
visibility="boolean"Description:
263 | (MULTI_STATE) Visibility while state is active
Type: Boolean
Required:
264 | no
/>
265 | </dashboard>
266 | <dependingDashboards animName="string"Description: Dashboard animName
Type: String
Required: no
dashboardActive="true"Description: (IC) Dashboard state while control is active
Type: Boolean
Default:
271 | true
Required: no
dashboardInactive="true"Description: (IC) Dashboard state while control is inactive
Type:
274 | Boolean
Default: true
Required: no
dashboardValueActive="float"Description: (IC) Dashboard value while control is active
Type: Float
Required:
277 | no
dashboardValueInactive="float"Description: (IC) Dashboard value while control is
279 | inactive
Type: Float
Required: no
node="node"Description:
281 | Dashboard node
Type: Index to i3d node or i3d mapping identifier
Required: no
282 | numbers="node"Description: Dashboard numbers
Type: Index to i3d node or i3d mapping
284 | identifier
Required: no
/>
285 | <dependingInteractiveControl blockState="boolean"Description: Interactive control state to block depending control
Type:
288 | Boolean
Required: no
forcedBlockedState="boolean"Description: Forced state of depending control if blocked
Type: Boolean
Required:
291 | no
index="integer"Description: Index of depending interactive
293 | control
Type: Integer
Required: no
/>
295 | <dependingMovingPart isInactive="true"Description: (IC) Is moving part active while control is used
Type:
298 | Boolean
Default: true
Required: no
node="node"Description: Moving
300 | part node
Type: Index to i3d node or i3d mapping identifier
Required:
301 | no
/>
302 | <dependingMovingTool isInactive="true"Description: (IC) Is moving tool active while control is used
Type:
305 | Boolean
Default: true
Required: no
node="node"Description: Moving
307 | tool node
Type: Index to i3d node or i3d mapping identifier
Required:
308 | no
/>
309 | <function name="string"Description:
311 | Function name (Avaiable: | MOTOR_START_STOPP | LIGHTS_TOGGLE | LIGHTS_WORKBACK_TOGGLE |
312 | LIGHTS_WORKFRONT_TOGGLE | LIGHTS_HIGHBEAM_TOGGLE | LIGHTS_TURNLIGHT_HAZARD_TOGGLE |
313 | LIGHTS_TURNLIGHT_LEFT_TOGGLE | LIGHTS_TURNLIGHT_RIGHT_TOGGLE | LIGHTS_BEACON_TOGGLE | LIGHTS_PIPE_TOGGLE
314 | | CRUISE_CONTROL_TOGGLE | DRIVE_DIRECTION_TOGGLE | COVER_TOGGLE | ATTACHERJOINT_LIFT_LOWER |
315 | ATTACHERJOINT_TURN_ON_OFF | TURN_ON_OFF | ATTACHERJOINT_FOLDING_TOGGLE | PIPE_FOLDING_TOGGLE |
316 | FOLDING_TOGGLE | ATTACHERJOINTS_TOGGLE_DISCHARGE | DISCHARGE_TOGGLE | CRABSTEERING_TOGGLE | RADIO_TOGGLE
317 | | RADIO_CHANNEL_NEXT | RADIO_CHANNEL_PREVIOUS | RADIO_ITEM_NEXT | RADIO_ITEM_PREVIOUS |
318 | VARIABLE_WORK_WIDTH_LEFT_INCREASE | VARIABLE_WORK_WIDTH_LEFT_DECREASE |
319 | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_LEFT_INCREASE | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_LEFT_DECREASE |
320 | VARIABLE_WORK_WIDTH_RIGHT_INCREASE | VARIABLE_WORK_WIDTH_RIGHT_DECREASE |
321 | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_RIGHT_INCREASE | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_RIGHT_DECREASE |
322 | VARIABLE_WORK_WIDTH_TOGGLE | ATTACHERJOINTS_VARIABLE_WORK_WIDTH_TOGGLE | ATTACHERJOINTS_ATTACH_DETACH |
323 | REVERSEDRIVING_TOGGLE | GPS_TOGGLE | PF_CROP_SENSOR_TOGGLE | PF_ATTACHERJOINTS_CROP_SENSOR_TOGGLE |
324 | PF_SEED_RATE_MODE | PF_ATTACHERJOINTS_SEED_RATE_MODE | PF_SEED_RATE_UP | PF_SEED_RATE_DOWN |
325 | PF_ATTACHERJOINTS_SEED_RATE_UP | PF_ATTACHERJOINTS_SEED_RATE_DOWN | PF_SPRAY_AMOUNT_MODE |
326 | PF_ATTACHERJOINTS_SPRAY_AMOUNT_MODE | PF_SPRAY_AMOUNT_UP | PF_SPRAY_AMOUNT_DOWN |
327 | PF_ATTACHERJOINTS_SPRAY_AMOUNT_UP | PF_ATTACHERJOINTS_SPRAY_AMOUNT_DOWN | VCA_TOGGLE_AWD |
328 | VCA_TOGGLE_DIFFLOCK_FRONT | VCA_TOGGLE_DIFFLOCK_BACK | VCA_TOGGLE_PARKINGBRAKE |
329 | HEADLAND_MANAGEMENT_TOGGLE | MS_TOGGLE_PUMP | MS_TOGGLE_PUMP_DIRECTION)
Type: String
Required:
330 | no
>
331 | <attacherJoint index="integer"Description: Attacher joint index to be controlled
Type: Integer
Required:
334 | no
indicies="1 2 ..
335 | n"Description: Attacher joint indicies to be controlled
Type: Multiple
336 | values
Default: true
Required: no
/>
338 | </function>
339 | <objectChange centerOfMassActive="x y z"Description: center of mass if object change is active
Type: Multiple values (x, y,
342 | z)
Required: no
centerOfMassInactive="x y z"Description: center of mass if object change is in active
Type: Multiple values (x,
345 | y, z)
Required: no
compoundChildActive="boolean"Description: compound child state if object change is active
Type:
348 | Boolean
Required: no
compoundChildInactive="boolean"Description: compound child state if object change is in active
Type:
351 | Boolean
Required: no
interpolation="false"Description: Value will be interpolated
Type: Boolean
Default: false
Required:
354 | no
interpolationTime="1"Description: Time for interpolation
Type: Time in
356 | seconds
Default: 1
Required: no
massActive="float"Description:
358 | mass if object change is active
Type: Float
Required: no
massInactive="float"Description: mass if object change is in active
Type: Float
Required:
361 | no
node="node"Description: Object change node
Type: Index to i3d
363 | node or i3d mapping identifier
Required: no
parentNodeActive="node"Description: parent node if object change is active
Type: Index to i3d node or i3d
366 | mapping identifier
Required: no
parentNodeInactive="node"Description: parent node if object change is in active
Type: Index to i3d node or
369 | i3d mapping identifier
Required: no
rigidBodyTypeActive="string"Description: rigid body type if object change is active
Type: String
Required:
372 | no
rigidBodyTypeInactive="string"Description: rigid body type if object change is in
374 | active
Type: String
Required: no
rotationActive="x y z"Description: rotation if object change is active
Type: Rotation values (x, y,
377 | z)
Required: no
rotationInactive="x y z"Description: rotation if object change is in active
Type: Rotation values (x, y,
380 | z)
Required: no
scaleActive="x y z"Description: scale if object change is
382 | active
Type: Scale values (x, y, z)
Required: no
scaleInactive="x y z"Description: scale if object change is in active
Type: Scale values (x, y,
385 | z)
Required: no
shaderParameter="string"Description: Shader parameter name
Type: String
Required: no
388 | shaderParameterActive="x y z w"Description: shaderParameter if object change is active
Type: Multiple values (x, y,
390 | z, w)
Required: no
shaderParameterInactive="x y z w"Description: shaderParameter if object change is in active
Type: Multiple values (x,
393 | y, z, w)
Required: no
sharedShaderParameter="false"Description: Shader parameter is applied on all objects with the same material
Type:
396 | Boolean
Default: false
Required: no
translationActive="x y z"Description: translation if object change is active
Type: Translation values (x, y,
399 | z)
Required: no
translationInactive="x y z"Description: translation if object change is in active
Type: Translation values (x,
402 | y, z)
Required: no
visibilityActive="boolean"Description: visibility if object change is active
Type: Boolean
Required:
405 | no
visibilityInactive="boolean"Description: visibility if object change is in
407 | active
Type: Boolean
Required: no
/>
409 | <soundModifier delayedSoundAnimationTime="float"Description: Delayed sound animation time
Type: Float
Required:
412 | no
indoorFactor="float"Description: Indoor sound modifier factor for active
414 | interactive control
Type: Float
Required: no
name="string"Description:
416 | Animation name, if not set, first animation will be used
Type: String
Required:
417 | no
/>
418 | </interactiveControl>
419 | <outdoorTrigger node="node"Description: Outdoor trigger node
Type: Index to i3d node or i3d mapping
422 | identifier
Required: no
/>
423 | </interactiveControls>
424 | <objectChange centerOfMassActive="x y z"Description: center of mass if object change is active
Type: Multiple values (x, y,
427 | z)
Required: no
centerOfMassInactive="x y z"Description: center of mass if object change is in active
Type: Multiple values (x,
430 | y, z)
Required: no
compoundChildActive="boolean"Description: compound child state if object change is active
Type:
433 | Boolean
Required: no
compoundChildInactive="boolean"Description: compound child state if object change is in active
Type:
436 | Boolean
Required: no
interpolation="false"Description: Value will be interpolated
Type: Boolean
Default: false
Required:
439 | no
interpolationTime="1"Description: Time for interpolation
Type: Time in
441 | seconds
Default: 1
Required: no
massActive="float"Description:
443 | mass if object change is active
Type: Float
Required: no
massInactive="float"Description: mass if object change is in active
Type: Float
Required:
446 | no
node="node"Description: Object change node
Type: Index to i3d
448 | node or i3d mapping identifier
Required: no
parentNodeActive="node"Description: parent node if object change is active
Type: Index to i3d node or i3d
451 | mapping identifier
Required: no
parentNodeInactive="node"Description: parent node if object change is in active
Type: Index to i3d node or
454 | i3d mapping identifier
Required: no
rigidBodyTypeActive="string"Description: rigid body type if object change is active
Type: String
Required:
457 | no
rigidBodyTypeInactive="string"Description: rigid body type if object change is in
459 | active
Type: String
Required: no
rotationActive="x y z"Description: rotation if object change is active
Type: Rotation values (x, y,
462 | z)
Required: no
rotationInactive="x y z"Description: rotation if object change is in active
Type: Rotation values (x, y,
465 | z)
Required: no
scaleActive="x y z"Description: scale if object change is
467 | active
Type: Scale values (x, y, z)
Required: no
scaleInactive="x y z"Description: scale if object change is in active
Type: Scale values (x, y,
470 | z)
Required: no
shaderParameter="string"Description: Shader parameter name
Type: String
Required: no
473 | shaderParameterActive="x y z w"Description: shaderParameter if object change is active
Type: Multiple values (x, y,
475 | z, w)
Required: no
shaderParameterInactive="x y z w"Description: shaderParameter if object change is in active
Type: Multiple values (x,
478 | y, z, w)
Required: no
sharedShaderParameter="false"Description: Shader parameter is applied on all objects with the same material
Type:
481 | Boolean
Default: false
Required: no
translationActive="x y z"Description: translation if object change is active
Type: Translation values (x, y,
484 | z)
Required: no
translationInactive="x y z"Description: translation if object change is in active
Type: Translation values (x,
487 | y, z)
Required: no
visibilityActive="boolean"Description: visibility if object change is active
Type: Boolean
Required:
490 | no
visibilityInactive="boolean"Description: visibility if object change is in
492 | active
Type: Boolean
Required: no
/>
494 | </interactiveControlConfiguration>
495 | </interactiveControlConfigurations>
496 | <registers>
498 | <clickIcon blinkSpeed="float"Description:
500 | Blinkspeed of clickIcon
Type: Float
Default: true
Required: no
filename="string"Description: ClickIcon filename
Type: String
Default: true
Required:
503 | no
name="string"Description: ClickIcon identification name
Type:
505 | String
Default: true
Required: no
node="string"Description:
507 | ClickIcon node to load dynamic
Type: String
Default: true
Required:
508 | no
/>
509 | </registers>
510 | </interactiveControl>
511 |
512 | <parentFile xmlFilename="string"Description:
514 | Remove vehicle if unit empty
Type: String
Required: no
>
516 | <attributes>
518 | <clearList keepIndex="integer"Description:
520 | Index of list to keep
Type: Integer
Required: no
path="string"Description: List
522 | to clear but keep one item
Type: String
Required: no
/>
524 | <remove path="string"Description: Path
526 | to remove from parent xml
Type: String
Required: no
/>
528 | <set path="string"Description: Path
530 | change in parent xml
Type: String
Required: no
value="string"Description:
532 | Target value to set in parent file
Type: String
Required: no
/>
534 | </attributes>
535 | </parentFile>
536 | </vehicle>
--------------------------------------------------------------------------------
/i18n/locale_br.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/i18n/locale_cz.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/i18n/locale_de.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/i18n/locale_en.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/i18n/locale_fr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/i18n/locale_it.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/i18n/locale_pl.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
--------------------------------------------------------------------------------
/i18n/locale_ru.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/icon_interactiveControl.dds:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasF92/FS22_interactiveControl/44926c2eba533f36715798d284edbf8dbb2f1c9f/icon_interactiveControl.dds
--------------------------------------------------------------------------------
/icon_interactiveControl.gim:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 |
--------------------------------------------------------------------------------
/icon_interactiveControl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TobiasF92/FS22_interactiveControl/44926c2eba533f36715798d284edbf8dbb2f1c9f/icon_interactiveControl.png
--------------------------------------------------------------------------------
/modDesc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Vertex Dezign
4 | 1.2.0.0
5 |
6 |
7 | Interactive Control
8 |
9 |
10 |
11 | README on Github.
22 |
23 | Changelog (1.2.0.0):
24 | - Added translations: french, portuguese (BR)
25 | - Added support for ManureSystem
26 | - Added clickpoint disabling using node visibility
27 | - Added depending dashboard control
28 | - Fixed rare LUA-Error (InteractiveControl.lua:1244: attempt to compare number with nil)
29 |
30 | Changelog (1.1.1.0):
31 | - Updated translations: russian
32 | - Fixed InteractiveButtons not working
33 |
34 | Changelog (1.1.0.0):
35 | - Added translations: italian, russian, polish, czech
36 | - Added mod compatibility with 'FS22_additionalGameSettings'
37 | - Added auto spec insertion into more vehicleTypes
38 | - Added various warning messages while functions are called
39 | - Added various new functions, see README
40 | - Added various settings in game menu
41 | - Fixed not working while indoor cameras use outdoor sounds
42 | - Fixed various small things
43 | - Optimized function for attacherJoints lift/lower for more functionality
44 |
45 | ]]>
46 | README auf Github.
57 |
58 | Changelog (1.2.0.0):
59 | - Übersetzungen hinzugefügt: französisch, portugiesisch (BR)
60 | - Untersützung für ManureSystem hinzugefügt
61 | - Clickpoint Funktion über Sichtbarkeit schaltbar hinzugefügt
62 | - Dashboard Steuerung über IC hinzugefügt
63 | - Seltener LUA-Fehler behoben (InteractiveControl.lua:1244: attempt to compare number with nil)
64 |
65 | Changelog (1.1.1.0):
66 | - Übersetzungen aktualisiert: russisch
67 | - Behoben, dass Interactive Buttons nicht mehr funktionierten
68 |
69 | Changelog (1.1.0.0):
70 | - Übersetzungen hinzugefügt: italienisch, russisch, polnisch, tschechisch
71 | - Kompatibilität mit 'FS22_additionalGameSettings' hinzugefügt
72 | - Automatische Installation der Spezialisierung in mehr Fahrzeugtypen hinzugefügt
73 | - Verschiedene Warnmeldungen beim Aufruf von Funktionen hinzugefügt
74 | - Diverse neue Funktionen hinzugefügt, siehe README
75 | - Verschiedene Einstellungsoptionen im Spielmenu hinzugefügt
76 | - Behoben, dass Controls nicht funktionierten, wenn Kamera Außensounds genutzt hat
77 | - Verschiedene kleinere Fehler behoben
78 | - Optimierte Funktion für AttacherJoints heben/senken für mehr Funktionalität
79 |
80 | ]]>
81 |
82 |
83 | icon_interactiveControl.png
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/src/events/ICNumStateEvent.lua:
--------------------------------------------------------------------------------
1 | ---@class ICNumStateEvent
2 | ICNumStateEvent = {}
3 |
4 | local icNumStateEvent_mt = Class(ICNumStateEvent, Event)
5 |
6 | InitEventClass(ICNumStateEvent, "ICNumStateEvent")
7 |
8 | ---@return ICNumStateEvent
9 | function ICNumStateEvent.emptyNew()
10 | local self = Event.new(icNumStateEvent_mt)
11 | return self
12 | end
13 |
14 | function ICNumStateEvent.new(object, index, state, doAction)
15 | local self = ICNumStateEvent.emptyNew()
16 |
17 | self.object = object
18 | self.index = index
19 | self.state = state
20 | self.doAction = doAction
21 |
22 | return self
23 | end
24 |
25 | function ICNumStateEvent:readStream(streamId, connection)
26 | self.object = NetworkUtil.readNodeObject(streamId)
27 | self.index = streamReadInt8(streamId)
28 | self.state = streamReadBool(streamId)
29 | self.doAction = streamReadBool(streamId)
30 | self:run(connection)
31 | end
32 |
33 | function ICNumStateEvent:writeStream(streamId, connection)
34 | NetworkUtil.writeNodeObject(streamId, self.object)
35 | streamWriteInt8(streamId, self.index)
36 | streamWriteBool(streamId, self.state)
37 | streamWriteBool(streamId, self.doAction)
38 | end
39 |
40 | function ICNumStateEvent:run(connection)
41 | if not connection:getIsServer() then
42 | g_server:broadcastEvent(self, false, connection, self.object)
43 | end
44 |
45 | self.object:setControlStateByIndex(self.index, self.state, self.doAction, true)
46 | end
47 |
48 | function ICNumStateEvent.sendEvent(object, index, state, doAction, noEventSend)
49 | if noEventSend == nil or noEventSend == false then
50 | if g_server ~= nil then
51 | g_server:broadcastEvent(ICNumStateEvent.new(object, index, state, doAction), nil, nil, object)
52 | else
53 | g_client:getServerConnection():sendEvent(ICNumStateEvent.new(object, index, state, doAction))
54 | end
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/src/events/ICStateEvent.lua:
--------------------------------------------------------------------------------
1 | ---@class ICStateEvent
2 | ICStateEvent = {}
3 |
4 | local icStateEvent_mt = Class(ICStateEvent, Event)
5 |
6 | InitEventClass(ICStateEvent, "ICStateEvent")
7 |
8 | ---@return ICStateEvent
9 | function ICStateEvent.emptyNew()
10 | local self = Event.new(icStateEvent_mt)
11 | return self
12 | end
13 |
14 | function ICStateEvent.new(object, state)
15 | local self = ICStateEvent.emptyNew()
16 |
17 | self.object = object
18 | self.state = state
19 |
20 | return self
21 | end
22 |
23 | function ICStateEvent:readStream(streamId, connection)
24 | self.object = NetworkUtil.readNodeObject(streamId)
25 | self.state = streamReadBool(streamId)
26 | self:run(connection)
27 | end
28 |
29 | function ICStateEvent:writeStream(streamId, connection)
30 | NetworkUtil.writeNodeObject(streamId, self.object)
31 | streamWriteBool(streamId, self.state)
32 | end
33 |
34 | function ICStateEvent:run(connection)
35 | if not connection:getIsServer() then
36 | g_server:broadcastEvent(self, false, connection, self.object)
37 | end
38 |
39 | self.object:setICActive(self.state, true)
40 | end
41 |
42 | function ICStateEvent.sendEvent(object, state, noEventSend)
43 | if noEventSend == nil or noEventSend == false then
44 | if g_server ~= nil then
45 | g_server:broadcastEvent(ICStateEvent.new(object, state), nil, nil, object)
46 | else
47 | g_client:getServerConnection():sendEvent(ICStateEvent.new(object, state))
48 | end
49 | end
50 | end
51 |
--------------------------------------------------------------------------------
/src/interactiveControl/InteractiveBase.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- InteractiveBase
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Base functionality of interactive object
5 | --
6 | -- @author John Deere 6930 @VertexDezign
7 | ----------------------------------------------------------------------------------------------------
8 |
9 | ---@class InteractiveBase
10 |
11 | InteractiveBase = {}
12 | local interactiveBase_mt = Class(InteractiveBase)
13 |
14 | InteractiveBase.TYPE_UNKNOWN = 0
15 | InteractiveBase.TYPE_INDOOR = 1
16 | InteractiveBase.TYPE_OUTDOOR = 2
17 | InteractiveBase.TYPE_INDOOR_OUTDOOR = 3
18 |
19 | function InteractiveBase.registerInteractiveBaseSchema(schema, key)
20 | schema:register(XMLValueType.STRING, key .. "#type", "Types of interactive object", "UNKNOWN", true)
21 | schema:register(XMLValueType.BOOL, key .. "#forcedState", "Forced state at execution")
22 | schema:register(XMLValueType.FLOAT, key .. "#foldMinLimit", "Fold min. limit", 0)
23 | schema:register(XMLValueType.FLOAT, key .. "#foldMaxLimit", "Fold max. limit", 1)
24 | schema:register(XMLValueType.STRING, key .. "#animName", "Animation name")
25 | schema:register(XMLValueType.FLOAT, key .. "#animMinLimit", "Min. anim limit", 0)
26 | schema:register(XMLValueType.FLOAT, key .. "#animMaxLimit", "Max. anim limit", 1)
27 | end
28 |
29 | ---Create new instance of InteractiveBase
30 | function InteractiveBase.new(modName, modDirectory, customMt)
31 | local self = setmetatable({}, customMt or interactiveBase_mt)
32 |
33 | self.modName = modName
34 | self.modDirectory = modDirectory
35 |
36 | self.state = false
37 |
38 | return self
39 | end
40 |
41 | ---Loads InteractiveBase data from xmlFile, returns true if loading was successful, false otherwise
42 | ---@param xmlFile table xmlFile handler
43 | ---@param key string xml key
44 | ---@param target table target metatable
45 | ---@param interactiveControl table interactive control table
46 | ---@return boolean loaded loaded state
47 | function InteractiveBase:loadFromXML(xmlFile, key, target, interactiveControl)
48 | if target == nil or interactiveControl == nil then
49 | return false
50 | end
51 |
52 | local typeName = xmlFile:getValue(key .. "#type")
53 | typeName = "TYPE_" .. typeName:upper()
54 | local type = InteractiveBase[typeName]
55 | if type == nil then
56 | Logging.xmlWarning(xmlFile, "Unable to find type '%s' for clickPoint '%s'", typeName, key)
57 | return false
58 | end
59 |
60 | if type == InteractiveBase.TYPE_UNKNOWN then
61 | Logging.xmlWarning(xmlFile, "Type is UNKNOWN for clickPoint '%s'", typeName, key)
62 | return false
63 | end
64 |
65 | self.type = type
66 | self.target = target
67 | self.interactiveControl = interactiveControl
68 |
69 | self.forcedState = xmlFile:getValue(key .. "#forcedState")
70 | self.foldMinLimit = xmlFile:getValue(key .. "#foldMinLimit", 0)
71 | self.foldMaxLimit = xmlFile:getValue(key .. "#foldMaxLimit", 1)
72 |
73 | self.animName = xmlFile:getValue(key .. "#animName")
74 | self.animMinLimit = xmlFile:getValue(key .. "#animMinLimit", 0)
75 | self.animMaxLimit = xmlFile:getValue(key .. "#animMaxLimit", 1)
76 |
77 | self.currentUpdateDistance = math.huge
78 | return true
79 | end
80 |
81 | ---Called on delete
82 | function InteractiveBase:delete()
83 | end
84 |
85 | ---Sets isActive state
86 | ---@param state boolean is control active
87 | function InteractiveBase:setIsActive(state)
88 | if state ~= nil and state ~= self.state then
89 | self.state = state
90 | end
91 | end
92 |
93 | ---Returns true if is active, false otherwise
94 | ---@return boolean state
95 | function InteractiveBase:isActive()
96 | return self.state
97 | end
98 |
99 | ---Returns true if is active, false otherwise
100 | ---@return boolean isActivatable
101 | function InteractiveBase:isActivatable()
102 | -- check foldAnim time
103 | if self.target.getFoldAnimTime ~= nil then
104 | local time = self.target:getFoldAnimTime()
105 |
106 | if self.foldMaxLimit < time or time < self.foldMinLimit then
107 | return false
108 | end
109 | end
110 |
111 | -- check animation time
112 | if self.target.getAnimationTime ~= nil and self.animName ~= nil then
113 | local animTime = self.target:getAnimationTime(self.animName)
114 |
115 | if self.animMaxLimit < animTime or animTime < self.animMinLimit then
116 | return false
117 | end
118 | end
119 |
120 | -- check forced state
121 | if self.forcedState ~= nil then
122 | local targetState = self.target:getControlState(self.interactiveControl)
123 | return targetState == self.forcedState
124 | end
125 |
126 | return true
127 | end
128 |
129 | ---Returns true if is executable, false otherwise
130 | ---@return boolean isExecutable
131 | function InteractiveBase:isExecutable()
132 | return self:isActive()
133 | end
134 |
135 | ---Executes interactive event, returns true if success, false otherwise
136 | ---@return boolean success
137 | function InteractiveBase:execute()
138 | return self.target:toggleControlState(self.interactiveControl, self.forcedState)
139 | end
140 |
141 | ---Returns true if click point is indoor active, false otherwise
142 | ---@return boolean isIndoor
143 | function InteractiveBase:isIndoorActive()
144 | return self.type == InteractiveBase.TYPE_INDOOR or self.type == InteractiveBase.TYPE_INDOOR_OUTDOOR
145 | end
146 |
147 | ---Returns true if click point is outdoor active, false otherwise
148 | ---@return boolean isOutdoor
149 | function InteractiveBase:isOutdoorActive()
150 | return self.type == InteractiveBase.TYPE_OUTDOOR or self.type == InteractiveBase.TYPE_INDOOR_OUTDOOR
151 | end
152 |
--------------------------------------------------------------------------------
/src/interactiveControl/InteractiveButton.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- InteractiveButton
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Functionality for interactive button
5 | --
6 | -- @author John Deere 6930 @VertexDezign
7 | ----------------------------------------------------------------------------------------------------
8 |
9 | ---@class InteractiveButton
10 |
11 | InteractiveButton = {}
12 | local interactiveButton_mt = Class(InteractiveButton, InteractiveBase)
13 |
14 | function InteractiveButton.registerButtonSchema(schema, key)
15 | local buttonPath = key .. ".button(?)"
16 | InteractiveBase.registerInteractiveBaseSchema(schema, buttonPath)
17 |
18 | schema:register(XMLValueType.STRING, buttonPath .. "#input", "Name of button", nil, true)
19 | schema:register(XMLValueType.FLOAT, buttonPath .. "#range", "Range of button", 5.0)
20 | schema:register(XMLValueType.NODE_INDEX, buttonPath .. "#refNode", "Reference node used to calculate the range. If not set, vehicle rootNode is used.")
21 | end
22 |
23 | ---Create new instance of InteractiveButton
24 | function InteractiveButton.new(modName, modDirectory, customMt)
25 | local self = InteractiveBase.new(modName, modDirectory, customMt or interactiveButton_mt)
26 |
27 | self.inputButton = nil
28 | self.range = 0.0
29 |
30 | return self
31 | end
32 |
33 | ---Loads InteractiveButton data from xmlFile, returns true if loading was successful, false otherwise
34 | ---@param xmlFile table xmlFile handler
35 | ---@param key string xml key
36 | ---@param target table target metatable
37 | ---@return boolean loaded loaded state
38 | function InteractiveButton:loadFromXML(xmlFile, key, target, interactiveControl)
39 | if not InteractiveButton:superClass().loadFromXML(self, xmlFile, key, target, interactiveControl) then
40 | return false
41 | end
42 |
43 | local inputButtonStr = xmlFile:getValue(key .. "#input")
44 | if inputButtonStr ~= nil then
45 | self.inputButton = InputAction[inputButtonStr]
46 | end
47 |
48 | if self.inputButton == nil then
49 | return false
50 | end
51 |
52 | self.range = xmlFile:getValue(key .. "#range", 5.0)
53 | self.refNode = xmlFile:getValue(key .. "#refNode", nil, target.components, target.i3dMappings)
54 |
55 | return true
56 | end
57 |
58 | ---Updates if interactive object is in range
59 | ---@param currentUpdateDistance number current distance to use
60 | function InteractiveButton:updateDistance(currentUpdateDistance)
61 | if self.refNode ~= nil then
62 | self.currentUpdateDistance = calcDistanceFrom(self.refNode, getCamera())
63 | else
64 | self.currentUpdateDistance = currentUpdateDistance
65 | end
66 | end
67 |
68 | ---Returns true if button is in interaction range, false otherwise
69 | ---@return boolean isInRange
70 | function InteractiveButton:isInRange()
71 | return self.currentUpdateDistance < self.range
72 | end
73 |
--------------------------------------------------------------------------------
/src/interactiveControl/InteractiveClickPoint.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- InteractiveClickPoint
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Functionality for interactive clickPoint
5 | --
6 | -- @author John Deere 6930 @VertexDezign
7 | ----------------------------------------------------------------------------------------------------
8 |
9 | ---@class InteractiveClickPoint
10 |
11 | InteractiveClickPoint = {}
12 | local InteractiveClickPoint_mt = Class(InteractiveClickPoint, InteractiveBase)
13 |
14 | InteractiveClickPoint.CLICK_ICON_ID = {
15 | UNKNOWN = 0
16 | }
17 | InteractiveClickPoint.CLICK_ICONS = {}
18 |
19 | local lastId = InteractiveClickPoint.CLICK_ICON_ID.UNKNOWN
20 | ---Returns next clickPoint id
21 | ---@return integer id
22 | local function getNextId()
23 | lastId = lastId + 1
24 | return lastId
25 | end
26 |
27 | ---Registers new click icon type
28 | ---@param name string name of click icon
29 | ---@param filename string filename of i3d file
30 | ---@param node string index string in i3d file
31 | ---@param blinkSpeed number blink speed
32 | function InteractiveClickPoint.registerIconType(name, filename, node, blinkSpeed, customEnvironment)
33 | if name == nil or name == "" then
34 | Logging.warning("InteractiveControl: Unable to register clickIcon, invalid name!")
35 | return false
36 | end
37 |
38 | name = name:upper()
39 |
40 | if customEnvironment ~= nil and customEnvironment ~= "" then
41 | name = ("%s.%s"):format(customEnvironment, name)
42 | end
43 |
44 | if InteractiveClickPoint.CLICK_ICON_ID[name] ~= nil then
45 | -- clickIcon already registred, but don't write a warning
46 | return false
47 | end
48 |
49 | if filename == nil or filename == "" then
50 | Logging.warning("InteractiveControl: Unable to register clickIcon '%s', invalid filename!", name)
51 | return false
52 | end
53 |
54 | InteractiveClickPoint.CLICK_ICON_ID[name] = getNextId()
55 | local clickIcon = {}
56 | clickIcon.filename = filename
57 | clickIcon.node = Utils.getNoNil(node, "0")
58 | clickIcon.blinkSpeed = Utils.getNoNil(blinkSpeed, 0.05)
59 |
60 | InteractiveClickPoint.CLICK_ICONS[InteractiveClickPoint.CLICK_ICON_ID[name]] = clickIcon
61 | log((" InteractiveControl: Register clickIcon '%s'"):format(name))
62 | return true
63 | end
64 |
65 | function InteractiveClickPoint.registerClickPointSchema(schema, key)
66 | local clickPointPath = key .. ".clickPoint(?)"
67 | InteractiveClickPoint.registerInteractiveBaseSchema(schema, clickPointPath)
68 |
69 | schema:register(XMLValueType.NODE_INDEX, clickPointPath .. "#node", "Click point node", nil, true)
70 | schema:register(XMLValueType.FLOAT, clickPointPath .. "#size", "Size of click point", 0.04)
71 | schema:register(XMLValueType.FLOAT, clickPointPath .. "#blinkSpeedScale", "Speed scale of size scaling", 1)
72 | schema:register(XMLValueType.FLOAT, clickPointPath .. "#scaleOffset", "Scale offset", "size / 10")
73 |
74 | local iconTypes = ""
75 | for name, _ in pairs(InteractiveClickPoint.CLICK_ICON_ID) do
76 | iconTypes = string.format("%s %s", iconTypes, name)
77 | end
78 |
79 | schema:register(XMLValueType.STRING, clickPointPath .. "#iconType", ("Types of click point: %s"):format(iconTypes), "CROSS", true)
80 | schema:register(XMLValueType.BOOL, clickPointPath .. "#alignToCamera", "Aligns clickpoint to current camera", true)
81 | schema:register(XMLValueType.BOOL, clickPointPath .. "#invertX", "Invert click icon on x-axis", false)
82 | schema:register(XMLValueType.BOOL, clickPointPath .. "#invertZ", "Invert click icon on x-axis", false)
83 | end
84 |
85 | ---Create new instance of InteractiveClickPoint
86 | function InteractiveClickPoint.new(modName, modDirectory, customMt)
87 | local self = InteractiveBase.new(modName, modDirectory, customMt or InteractiveClickPoint_mt)
88 |
89 | self.screenPosX = 0
90 | self.screenPosY = 0
91 | self.size = 0
92 |
93 | self.clickable = false
94 | self.blinkSpeed = 0
95 | self.blinkSpeedScale = 1
96 |
97 | self.alignToCamera = true
98 | self.invertX = false
99 | self.invertZ = false
100 | self.sharedLoadRequestId = nil
101 |
102 | return self
103 | end
104 |
105 | ---Loads InteractiveClickPoint data from xmlFile, returns true if loading was successful, false otherwise
106 | ---@param xmlFile table xmlFile handler
107 | ---@param key string xml key
108 | ---@param target table target metatable
109 | ---@return boolean loaded loaded state
110 | function InteractiveClickPoint:loadFromXML(xmlFile, key, target, interactiveControl)
111 | if not InteractiveClickPoint:superClass().loadFromXML(self, xmlFile, key, target, interactiveControl) then
112 | return false
113 | end
114 |
115 | local node = xmlFile:getValue(key .. "#node", target.rootNode, target.components, target.i3dMappings)
116 | if node == nil then
117 | return false
118 | end
119 |
120 | self.node = node
121 | self.size = xmlFile:getValue(key .. "#size", 0.04)
122 | self.blinkSpeedScale = xmlFile:getValue(key .. "#blinkSpeedScale", 1) * 0.016
123 |
124 | local scaleOffset = xmlFile:getValue(key .. "#scaleOffset", self.size / 10)
125 | self.scaleMin = self.size - scaleOffset
126 | self.scaleMax = self.size + scaleOffset
127 |
128 | local typeName = xmlFile:getValue(key .. "#iconType", "CROSS")
129 | local iconType = InteractiveClickPoint.CLICK_ICON_ID[typeName:upper()]
130 |
131 | if iconType == nil and self.target.customEnvironment ~= nil and self.target.customEnvironment ~= "" then
132 | local cIconType = ("%s.%s"):format(self.target.customEnvironment, typeName:upper())
133 | iconType = InteractiveClickPoint.CLICK_ICON_ID[cIconType]
134 | end
135 | if iconType == nil then
136 | Logging.xmlWarning(xmlFile, "Unable to find iconType '%s' for clickPoint '%s'", typeName, key)
137 | return false
138 | end
139 |
140 | self.alignToCamera = xmlFile:getValue(key .. "#alignToCamera", true)
141 | self.invertX = xmlFile:getValue(key .. "#invertX", false)
142 | self.invertZ = xmlFile:getValue(key .. "#invertZ", false)
143 | self.sharedLoadRequestId = self:loadIconType(iconType, target)
144 |
145 | return true
146 | end
147 |
148 | ---Called on delete
149 | function InteractiveClickPoint:delete()
150 | if self.sharedLoadRequestId ~= nil then
151 | g_i3DManager:releaseSharedI3DFile(self.sharedLoadRequestId)
152 | self.sharedLoadRequestId = nil
153 | end
154 |
155 | InteractiveClickPoint:superClass().delete(self)
156 | end
157 |
158 | ---Returns true if is active, false otherwise
159 | ---@return boolean isActivatable
160 | function InteractiveClickPoint:isActivatable()
161 | -- check node visibility
162 | if not getVisibility(self.node) then
163 | return false
164 | end
165 |
166 | return InteractiveClickPoint:superClass().isActivatable(self)
167 | end
168 |
169 | ---Updates screen position of clickPoint
170 | ---@param mousePosX number x position of mouse
171 | ---@param mousePosY number y position of mouse
172 | function InteractiveClickPoint:updateScreenPosition(mousePosX, mousePosY)
173 | local x, y, z = getWorldTranslation(self.node)
174 | local sx, sy, sz = project(x, y, z)
175 |
176 | self.screenPosX = sx
177 | self.screenPosY = sy
178 |
179 | local nodeIsVisible = getVisibility(self.node)
180 | local isOnScreen = sx > -1 and sx < 2 and sy > -1 and sy < 2 and sz <= 1
181 | self:setIsActive(nodeIsVisible and isOnScreen)
182 |
183 | if isOnScreen then
184 | if self.alignToCamera then
185 | local cameraNode = getCamera()
186 | if entityExists(cameraNode) then
187 | local xC, yC, zC = getWorldTranslation(cameraNode)
188 | local dirX, dirY, dirZ = xC - x, yC - y, zC - z
189 |
190 | if self.invertZ then
191 | dirX = -dirX
192 | dirY = -dirY
193 | dirZ = -dirZ
194 | end
195 |
196 | I3DUtil.setWorldDirection(self.node, dirX, dirY, dirZ, 0, 1, 0)
197 | end
198 | else
199 | --Todo: block active if not in range
200 | end
201 |
202 | self:updateClickable(mousePosX, mousePosY)
203 | end
204 | end
205 |
206 | ---Updates clickable state by mouse position
207 | ---@param mousePosX number x position of mouse
208 | ---@param mousePosY number y position of mouse
209 | function InteractiveClickPoint:updateClickable(mousePosX, mousePosY)
210 | if mousePosX ~= nil and mousePosY ~= nil then
211 | local halfSize = self.size / 2
212 | local isMouseOver = mousePosX > self.screenPosX - halfSize and mousePosX < self.screenPosX + halfSize
213 | and mousePosY > self.screenPosY - halfSize and mousePosY < self.screenPosY + halfSize
214 |
215 | if self.clickIconNode ~= nil then
216 | local scale = getScale(self.clickIconNode)
217 | scale = math.abs(scale)
218 | if isMouseOver then
219 | if (scale >= self.scaleMax) or (scale <= self.scaleMin) then
220 | self.blinkSpeed = self.blinkSpeed * -1
221 | end
222 | scale = scale + self.blinkSpeed * self.blinkSpeedScale
223 | else
224 | if scale ~= self.size then
225 | self.size = scale
226 | end
227 | end
228 | local xScale = self.invertX and -1 or 1
229 | setScale(self.clickIconNode, scale * xScale, scale, scale)
230 | end
231 |
232 | self:setClickable(isMouseOver)
233 | else
234 | self:setClickable(false)
235 | end
236 | end
237 |
238 | ---Sets isActive state
239 | ---@param state boolean active state value
240 | function InteractiveClickPoint:setIsActive(state)
241 | InteractiveClickPoint:superClass().setIsActive(self, state)
242 |
243 | if self.clickIconNode ~= nil then
244 | setVisibility(self.clickIconNode, self.state)
245 | end
246 |
247 | if not self.state then
248 | self:setClickable(self.state)
249 | end
250 | end
251 |
252 | ---Sets clickable state
253 | ---@param state boolean clickable state value
254 | function InteractiveClickPoint:setClickable(state)
255 | if state ~= nil and state ~= self.clickable then
256 | self.clickable = state
257 | end
258 | end
259 |
260 | ---Returns true if click point is clickable
261 | ---@return boolean clickable is clickable
262 | function InteractiveClickPoint:isClickable()
263 | return self.clickable
264 | end
265 |
266 | ---Returns true if is executable
267 | ---@return boolean executable is executable
268 | function InteractiveClickPoint:isExecutable()
269 | return InteractiveClickPoint:superClass().isExecutable(self) and self:isClickable()
270 | end
271 |
272 | ---Loads fixed iconType loading
273 | ---@param iconType integer iconType integer
274 | ---@return table sharedLoadRequestId sharedLoadRequestId table
275 | function InteractiveClickPoint:loadIconType(iconType, target)
276 | local clickIcon = InteractiveClickPoint.CLICK_ICONS[iconType]
277 | local filename = Utils.getFilename(clickIcon.filename, g_currentMission.interactiveControl.modDirectory)
278 |
279 | -- load external registered icon files
280 | if not fileExists(filename) and self.target.baseDirectory ~= nil then
281 | filename = Utils.getFilename(clickIcon.filename, self.target.baseDirectory)
282 | end
283 |
284 | return target:loadSubSharedI3DFile(filename, false, false, self.onIconTypeLoading, self, { clickIcon })
285 | end
286 |
287 | ---Called on i3d iconType loading
288 | ---@param i3dNode integer integer of i3d node
289 | ---@param failedReason any
290 | ---@param args table argument table
291 | function InteractiveClickPoint:onIconTypeLoading(i3dNode, failedReason, args)
292 | if i3dNode ~= 0 then
293 | local clickIcon = unpack(args)
294 |
295 | local node = I3DUtil.indexToObject(i3dNode, clickIcon.node, nil)
296 | setTranslation(node, 0, 0, 0)
297 | local yRot = self.invertZ and math.rad(-180) or 0
298 | setRotation(node, 0, yRot, 0)
299 | local xScale = self.invertX and -1 or 1
300 | setScale(node, self.size * xScale, self.size, self.size)
301 | setVisibility(node, false)
302 |
303 | self.clickIconNode = node
304 | self.blinkSpeed = clickIcon.blinkSpeed
305 |
306 | link(self.node, node)
307 | delete(i3dNode)
308 | end
309 | end
310 |
311 | InteractiveClickPoint.registerIconType("CROSS", "data/shared/ic_clickIcons.i3d", "0", 0.05)
312 | InteractiveClickPoint.registerIconType("IGNITIONKEY", "data/shared/ic_clickIcons.i3d", "1", 0.05)
313 | InteractiveClickPoint.registerIconType("CRUISE_CONTROL", "data/shared/ic_clickIcons.i3d", "2", 0.05)
314 | InteractiveClickPoint.registerIconType("GPS", "data/shared/ic_clickIcons.i3d", "3", 0.05)
315 | InteractiveClickPoint.registerIconType("TURN_ON", "data/shared/ic_clickIcons.i3d", "4", 0.05)
316 | InteractiveClickPoint.registerIconType("ATTACHERJOINT_LOWER", "data/shared/ic_clickIcons.i3d", "5", 0.05)
317 | InteractiveClickPoint.registerIconType("ATTACHERJOINT_LIFT", "data/shared/ic_clickIcons.i3d", "6", 0.05)
318 | InteractiveClickPoint.registerIconType("ATTACHERJOINT", "data/shared/ic_clickIcons.i3d", "7", 0.05)
319 | InteractiveClickPoint.registerIconType("LIGHT_HIGH", "data/shared/ic_clickIcons.i3d", "8", 0.05)
320 | InteractiveClickPoint.registerIconType("LIGHT", "data/shared/ic_clickIcons.i3d", "9", 0.05)
321 | InteractiveClickPoint.registerIconType("TURNLIGHT_LEFT", "data/shared/ic_clickIcons.i3d", "10", 0.05)
322 | InteractiveClickPoint.registerIconType("TURNLIGHT_RIGHT", "data/shared/ic_clickIcons.i3d", "11", 0.05)
323 | InteractiveClickPoint.registerIconType("BEACON_LIGHT", "data/shared/ic_clickIcons.i3d", "12", 0.05)
324 | InteractiveClickPoint.registerIconType("ARROW", "data/shared/ic_clickIcons.i3d", "13", 0.05)
325 | InteractiveClickPoint.registerIconType("PIPE_FOLDING", "data/shared/ic_clickIcons.i3d", "14", 0.05)
326 |
--------------------------------------------------------------------------------
/src/loader.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- Loader
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Loader SourceCode for Interactive Control
5 | --
6 | -- @author John Deere 6930 @VertexDezign
7 | ----------------------------------------------------------------------------------------------------
8 | -- Thanks goes to: Wopster, JoPi, SirJoki80 & Flowsen (for the ui elements) and Face (for the initial idea) & AgrarKadabra for many contributions!
9 | ----------------------------------------------------------------------------------------------------
10 |
11 | local modDirectory = g_currentModDirectory
12 | local modName = g_currentModName
13 | local modEnvironment
14 |
15 | g_interactiveControlModName = modName
16 |
17 | ---load all needed lua files
18 | local sourceFiles = {
19 | -- settings
20 | "src/misc/AdditionalSettingsManager.lua",
21 |
22 | -- interactiveControl
23 | "src/misc/InteractiveControlManager.lua",
24 | "src/misc/InteractiveFunctions.lua",
25 | "src/misc/InteractiveFunctions_externalMods.lua",
26 |
27 | "src/interactiveControl/InteractiveBase.lua",
28 | "src/interactiveControl/InteractiveButton.lua",
29 | "src/interactiveControl/InteractiveClickPoint.lua",
30 |
31 | -- network
32 | "src/events/ICStateEvent.lua",
33 | "src/events/ICNumStateEvent.lua",
34 | }
35 |
36 | for _, sourceFile in ipairs(sourceFiles) do
37 | source(Utils.getFilename(sourceFile, modDirectory))
38 | end
39 |
40 | ---Returns true when the current mod env is loaded, false otherwise.
41 | local function isLoaded()
42 | return modEnvironment ~= nil
43 | end
44 |
45 | ---Load the mod.
46 | local function load(mission)
47 | assert(modEnvironment == nil)
48 | modEnvironment = InteractiveControlManager.new(mission, g_inputBinding, g_i18n, modName, modDirectory)
49 |
50 | mission.interactiveControl = modEnvironment
51 |
52 | InteractiveControlManager.overwrite_additionalGameSettings()
53 |
54 | -- load settings
55 | if modEnvironment.settings ~= nil then
56 | AdditionalSettingsManager.loadFromXML(modEnvironment.settings)
57 | end
58 | end
59 |
60 | ---Unload the mod when the mod is unselected and savegame is (re)loaded or game is closed.
61 | local function unload()
62 | if not isLoaded() then
63 | return
64 | end
65 |
66 | if modEnvironment ~= nil then
67 | modEnvironment:delete()
68 | modEnvironment = nil
69 |
70 | if g_currentMission ~= nil then
71 | g_currentMission.interactiveControl = nil
72 | end
73 | end
74 | end
75 |
76 | ---Injects interactiveControl installation
77 | ---@param typeManager table typeManager table
78 | local function validateTypes(typeManager)
79 | if typeManager.typeName == "vehicle" then
80 | InteractiveControlManager.installSpecializations(g_vehicleTypeManager, g_specializationManager, modDirectory, modName)
81 | end
82 | end
83 |
84 | ---Overwritten function: SoundManager.getModifierFactor
85 | ---Injects the InteractiveControl sound modifier
86 | ---@param soundManager table soundManager table
87 | ---@param superFunc function original function
88 | ---@param sample table sample table
89 | ---@param modifierName string modifier name
90 | ---@return number modifierFactor factor of modifier
91 | local function getModifierFactor(soundManager, superFunc, sample, modifierName)
92 | if isLoaded() then
93 | return modEnvironment:getModifierFactor(soundManager, superFunc, sample, modifierName)
94 | end
95 |
96 | return superFunc(soundManager, sample, modifierName)
97 | end
98 |
99 | ---Overwritten function: Dashboard.defaultDashboardStateFunc
100 | ---Injects InteractiveControl dashboard overwriting
101 | ---@param vehicle Vehicle Instance of vehicle
102 | ---@param superFunc function original function
103 | ---@param dashboard table Dashboard entry
104 | ---@param newValue any
105 | ---@param minValue any
106 | ---@param maxValue any
107 | ---@param isActive any
108 | local function defaultDashboardStateFunc(vehicle, superFunc, dashboard, newValue, minValue, maxValue, isActive)
109 | if vehicle.getICDashboardByIdentifier ~= nil then
110 | local dependingDashboard = nil
111 |
112 | if dashboard.node ~= nil then
113 | dependingDashboard = vehicle:getICDashboardByIdentifier(dashboard.node)
114 | end
115 |
116 | if dependingDashboard == nil and dashboard.numbers ~= nil then
117 | dependingDashboard = vehicle:getICDashboardByIdentifier(dashboard.numbers)
118 | end
119 |
120 | if dependingDashboard == nil and dashboard.animName ~= nil then
121 | dependingDashboard = vehicle:getICDashboardByIdentifier(dashboard.animName)
122 | end
123 |
124 | if dependingDashboard ~= nil then
125 | local interactiveControl = dependingDashboard.interactiveControl
126 |
127 | if interactiveControl.isEnabled then
128 | if interactiveControl.state then
129 | isActive = dependingDashboard.dashboardActive
130 |
131 | if dependingDashboard.dashboardValueActive ~= nil then
132 | newValue = dependingDashboard.dashboardValueActive
133 | end
134 | else
135 | isActive = dependingDashboard.dashboardInactive
136 |
137 | if dependingDashboard.dashboardValueInactive ~= nil then
138 | newValue = dependingDashboard.dashboardValueInactive
139 | end
140 | end
141 | end
142 | end
143 | end
144 |
145 | superFunc(vehicle, dashboard, newValue, minValue, maxValue, isActive)
146 | end
147 |
148 | ---Appended function: InGameMenuGeneralSettingsFrame.onFrameOpen
149 | ---Adds initialization of settings gui elements
150 | ---@param settingsFrame InGameMenuGeneralSettingsFrame instance of InGameMenuGeneralSettingsFrame
151 | ---@param element GuiElement gui element
152 | local function initGui(settingsFrame, element)
153 | if not isLoaded() then
154 | return
155 | end
156 |
157 | AdditionalSettingsManager.initGui(settingsFrame, element, modEnvironment)
158 | end
159 |
160 | ---Appended function: InGameMenuGeneralSettingsFrame.updateGeneralSettings
161 | ---Adds updating of settings gui elements
162 | ---@param settingsFrame InGameMenuGeneralSettingsFrame instance of InGameMenuGeneralSettingsFrame
163 | local function updateGui(settingsFrame)
164 | if not isLoaded() then
165 | return
166 | end
167 |
168 | AdditionalSettingsManager.updateGui(settingsFrame, modEnvironment)
169 | end
170 |
171 | ---Appended function: GameSettings.saveToXMLFile
172 | ---Adds saving of additional settings
173 | ---@param xmlFile XMLFile instance of xml file to save settings to
174 | local function saveSettingsToXML(xmlFile)
175 | if not isLoaded() then
176 | return
177 | end
178 |
179 | if g_currentMission.interactiveControl ~= nil and g_currentMission.interactiveControl.settings ~= nil then
180 | AdditionalSettingsManager.saveToXMLFile(g_currentMission.interactiveControl.settings)
181 | end
182 | end
183 |
184 | ---Initialize the mod
185 | local function init()
186 | FSBaseMission.delete = Utils.appendedFunction(FSBaseMission.delete, unload)
187 | Mission00.load = Utils.prependedFunction(Mission00.load, load)
188 |
189 | TypeManager.validateTypes = Utils.prependedFunction(TypeManager.validateTypes, validateTypes)
190 | SoundManager.getModifierFactor = Utils.overwrittenFunction(SoundManager.getModifierFactor, getModifierFactor)
191 | Dashboard.defaultDashboardStateFunc = Utils.overwrittenFunction(Dashboard.defaultDashboardStateFunc, defaultDashboardStateFunc)
192 |
193 | InGameMenuGeneralSettingsFrame.onFrameOpen = Utils.appendedFunction(InGameMenuGeneralSettingsFrame.onFrameOpen, initGui)
194 | InGameMenuGeneralSettingsFrame.updateGeneralSettings = Utils.appendedFunction(InGameMenuGeneralSettingsFrame.updateGeneralSettings, updateGui)
195 | GameSettings.saveToXMLFile = Utils.appendedFunction(GameSettings.saveToXMLFile, saveSettingsToXML)
196 | end
197 |
198 | init()
199 |
--------------------------------------------------------------------------------
/src/misc/AdditionalSettingsManager.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- AdditionalSettingsManager
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Manager for addtional mod settings
5 | --
6 | -- @author John Deere 6930 @VertexDezign
7 | ----------------------------------------------------------------------------------------------------
8 |
9 | ---@class AdditionalSettingsManager
10 | AdditionalSettingsManager = {}
11 |
12 | AdditionalSettingsManager.TYPE_CHECKBOX = 0
13 | AdditionalSettingsManager.TYPE_MULTIBOX = 1
14 |
15 | AdditionalSettingsManager.CLONE_REF = {
16 | [AdditionalSettingsManager.TYPE_CHECKBOX] = "checkUseEasyArmControl",
17 | [AdditionalSettingsManager.TYPE_MULTIBOX] = "multiCameraSensitivity"
18 | }
19 |
20 | local AdditionalSettingsManager_mt = Class(AdditionalSettingsManager)
21 | local settingsDirectory = g_currentModSettingsDirectory
22 | if Platform.isConsole then
23 | settingsDirectory = getUserProfileAppPath()
24 | end
25 |
26 | ---Create new instance of AdditionalSettingsManager
27 | function AdditionalSettingsManager.new(title, target, modName, modDirectory, customMt)
28 | local self = setmetatable({}, customMt or AdditionalSettingsManager_mt)
29 |
30 | self.title = title
31 | self.target = target
32 |
33 | self.modName = modName
34 | self.modDirectory = modDirectory
35 |
36 | self.settings = {}
37 | self.settingsByName = {}
38 | self.settingsCreated = false
39 |
40 | self.settingsSaveDirectory = settingsDirectory .. "settings.xml"
41 | if Platform.isConsole then
42 | registerProfileFile(self.settingsSaveDirectory)
43 | else
44 | createFolder(settingsDirectory)
45 | end
46 |
47 | return self
48 | end
49 |
50 | ---Load settings from xml file
51 | function AdditionalSettingsManager:loadFromXML()
52 | local xmlFile = XMLFile.loadIfExists("SettingsXMLFile", self.settingsSaveDirectory, AdditionalSettingsManager.xmlSchema)
53 |
54 | if xmlFile ~= nil then
55 | xmlFile:iterate("settings.setting", function(_, settingKey)
56 | local name = xmlFile:getValue(settingKey .. "#name")
57 |
58 | local existingSetting = self.settingsByName[name]
59 | if existingSetting ~= nil then
60 | local value
61 | if existingSetting.type == AdditionalSettingsManager.TYPE_CHECKBOX then
62 | value = xmlFile:getValue(settingKey .. "#boolean", false)
63 | elseif existingSetting.type == AdditionalSettingsManager.TYPE_MULTIBOX then
64 | value = xmlFile:getValue(settingKey .. "#integer", 1)
65 | end
66 |
67 | if value ~= nil then
68 | self:setSetting(name, value)
69 | end
70 | end
71 | end)
72 |
73 | xmlFile:delete()
74 | end
75 | end
76 |
77 | ---Save settings to xml file
78 | function AdditionalSettingsManager:saveToXMLFile()
79 | local xmlFile = XMLFile.create("SettingsXMLFile", self.settingsSaveDirectory, "settings", AdditionalSettingsManager.xmlSchema)
80 |
81 | if xmlFile ~= nil then
82 | local baseKey = "settings.setting"
83 | local i = 0
84 |
85 | for _, setting in ipairs(self.settings) do
86 | local settingKey = ("%s(%d)"):format(baseKey, i)
87 |
88 | xmlFile:setValue(settingKey .. "#name", setting.name)
89 |
90 | if setting.type == AdditionalSettingsManager.TYPE_CHECKBOX then
91 | xmlFile:setValue(settingKey .. "#boolean", setting.value)
92 | elseif setting.type == AdditionalSettingsManager.TYPE_MULTIBOX then
93 | xmlFile:setValue(settingKey .. "#integer", setting.value)
94 | end
95 |
96 | i = i + 1
97 | end
98 |
99 | xmlFile:save()
100 | xmlFile:delete()
101 | end
102 | end
103 |
104 | ---Sets value of given setting by name
105 | ---@param name string setting name
106 | ---@param value any value to set
107 | function AdditionalSettingsManager:setSetting(name, value)
108 | local setting = self.settingsByName[name]
109 |
110 | if setting == nil then
111 | Logging.warning("Warning: AdditionalSettingsManager.setSetting: Invalid setting name given!")
112 | return
113 | end
114 |
115 | setting.value = value
116 |
117 | local messageType = MessageType.SETTING_CHANGED[name]
118 | if messageType ~= nil then
119 | g_messageCenter:publish(messageType, value)
120 | end
121 | end
122 |
123 | ---Returns value of given setting by name
124 | ---@param name string setting name
125 | ---@return any value
126 | function AdditionalSettingsManager:getSetting(name)
127 | local setting = self.settingsByName[name]
128 |
129 | if setting == nil then
130 | Logging.warning("Warning: AdditionalSettingsManager.getSetting: Invalid setting name given!")
131 | return
132 | end
133 |
134 | return setting.value
135 | end
136 |
137 | ---Add new setting to manager
138 | ---@param name string name of setting
139 | ---@param type integer Type of setting
140 | ---@param title string title of setting
141 | ---@param toolTip string tool tip of setting
142 | ---@param initValue any initial value
143 | ---@param options table Table of strings for multi option box
144 | ---@param callback string callback
145 | ---@param callbackTarget Class callback target
146 | function AdditionalSettingsManager:addSetting(name, type, title, toolTip, initValue, options, callback, callbackTarget)
147 | if name == nil or name == "" then
148 | Logging.error("Error: Could not add setting for interactive control without name!")
149 | return
150 | end
151 |
152 | if type == nil then
153 | Logging.error("Error: Could not add setting for interactive control without type!")
154 | return
155 | end
156 |
157 | if type == AdditionalSettingsManager.TYPE_CHECKBOX then
158 | if callback == nil then
159 | callback = "onSettingChangedCheckbox"
160 | end
161 | if initValue == nil then
162 | initValue = false
163 | end
164 | elseif type == AdditionalSettingsManager.TYPE_MULTIBOX then
165 | if callback == nil then
166 | callback = "onSettingChangedMultibox"
167 | end
168 | if initValue == nil then
169 | initValue = 1
170 | end
171 | end
172 | name = name:upper()
173 |
174 | local setting = {
175 | name = name,
176 | type = type,
177 | title = title,
178 | toolTip = toolTip,
179 | value = initValue,
180 | options = options,
181 | callback = callback,
182 | callbackTarget = callbackTarget
183 | }
184 |
185 | table.addElement(self.settings, setting)
186 | self.settingsByName[name] = self.settings[#self.settings]
187 |
188 | MessageType.SETTING_CHANGED[name] = nextMessageTypeId()
189 | end
190 |
191 | ----------------
192 | -------GUI------
193 | ----------------
194 |
195 | ---Create new Gui setting element by setting
196 | ---@param settingsFrame table gui element save table
197 | ---@param setting table setting data
198 | ---@param target Class|AdditionalSettingsManager callback target class, AdditionalSettingsManager by default
199 | ---@return nil|GuiElement element
200 | function AdditionalSettingsManager.createGuiElement(settingsFrame, setting, target)
201 | local cloneRef = AdditionalSettingsManager.CLONE_REF[setting.type]
202 | if cloneRef == nil then
203 | return nil
204 | end
205 |
206 | cloneRef = settingsFrame[cloneRef]
207 | if cloneRef == nil then
208 | return nil
209 | end
210 |
211 | local element = cloneRef:clone()
212 | element.target = setting.callbackTarget or target
213 | element.id = setting.name
214 | element:setCallback("onClickCallback", setting.callback)
215 |
216 | local settingTitle = element.elements[4]
217 | local toolTip = element.elements[6]
218 | settingTitle:setText(setting.title)
219 | toolTip:setText(setting.toolTip)
220 |
221 | if setting.type == AdditionalSettingsManager.TYPE_CHECKBOX then
222 | element:setIsChecked(setting.value)
223 | elseif setting.type == AdditionalSettingsManager.TYPE_MULTIBOX then
224 | element:setTexts(setting.options)
225 | element:setState(setting.value, false)
226 | end
227 |
228 | element:reloadFocusHandling(true)
229 |
230 | return element
231 | end
232 |
233 | ---Injects a checkbox in the InGameMenuGameSettingsFrame
234 | ---@param settingsFrame InGameMenuGeneralSettingsFrame Settings frame gui element
235 | ---@param element GuiElement gui element
236 | ---@param modEnvironment metatable mod environment class
237 | function AdditionalSettingsManager.initGui(settingsFrame, element, modEnvironment)
238 | local settingsManager = modEnvironment.settings
239 | local settingsElements = settingsFrame[settingsManager.title]
240 |
241 | if settingsElements == nil and not settingsManager.settingsCreated then
242 | local title = TextElement.new()
243 | title:applyProfile("settingsMenuSubtitle", true)
244 | title:setText(settingsManager.title)
245 | settingsFrame.boxLayout:addElement(title)
246 |
247 | settingsElements = {}
248 |
249 | for _, setting in ipairs(settingsManager.settings) do
250 | local createdElement = AdditionalSettingsManager.createGuiElement(settingsFrame, setting, settingsManager)
251 |
252 | if createdElement ~= nil then
253 | settingsElements[setting.name] = createdElement
254 | settingsFrame.boxLayout:addElement(createdElement)
255 | end
256 | end
257 |
258 | settingsManager.settingsCreated = true
259 | end
260 | end
261 |
262 | ---Updates the checkbox once the InGameMenuGameSettingsFrame is opened
263 | ---@param settingsFrame InGameMenuGeneralSettingsFrame Settings frame gui element
264 | ---@param modEnvironment metatable mod environment class
265 | function AdditionalSettingsManager.updateGui(settingsFrame, modEnvironment)
266 | local settingsManager = modEnvironment.settings
267 | local settingsElements = settingsFrame[settingsManager.title]
268 |
269 | if settingsManager ~= nil and settingsElements ~= nil then
270 | for _, setting in ipairs(settingsManager.settings) do
271 | local element = settingsElements[setting.name]
272 |
273 | if element ~= nil then
274 | if setting.type == AdditionalSettingsManager.TYPE_CHECKBOX then
275 | element:setIsChecked(setting.value == CheckedOptionElement.STATE_CHECKED)
276 | elseif setting.type == AdditionalSettingsManager.TYPE_MULTIBOX then
277 | element:setState(setting.value)
278 | end
279 | end
280 | end
281 | end
282 | end
283 |
284 | ---Called on checkbox change
285 | ---@param state integer state checked
286 | ---@param element GuiElement changed gui element
287 | function AdditionalSettingsManager:onSettingChangedCheckbox(state, element)
288 | self:setSetting(element.id, state == CheckedOptionElement.STATE_CHECKED)
289 | end
290 |
291 | ---Called on multibox change
292 | ---@param state integer multi state
293 | ---@param element GuiElement changed gui element
294 | function AdditionalSettingsManager:onSettingChangedMultibox(state, element)
295 | self:setSetting(element.id, state)
296 | end
297 |
298 | g_xmlManager:addCreateSchemaFunction(function()
299 | AdditionalSettingsManager.xmlSchema = XMLSchema.new("additionalSettingsManager")
300 | end)
301 |
302 | g_xmlManager:addInitSchemaFunction(function()
303 | local schema = AdditionalSettingsManager.xmlSchema
304 | local settingKey = "settings.setting(?)"
305 |
306 | schema:register(XMLValueType.STRING, settingKey .. "#name", "Name of setting", nil, true)
307 | schema:register(XMLValueType.BOOL, settingKey .. "#boolean", "Boolean value of setting")
308 | schema:register(XMLValueType.INT, settingKey .. "#integer", "Integer value of setting")
309 | end)
310 |
--------------------------------------------------------------------------------
/src/misc/InteractiveControlManager.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- InteractiveControlManager
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Manager for interactive control
5 | --
6 | -- @author John Deere 6930 @VertexDezign
7 | ----------------------------------------------------------------------------------------------------
8 |
9 | ---@class InteractiveControlManager
10 | InteractiveControlManager = {}
11 |
12 | InteractiveControlManager.SETTING_STATE_TOGGLE = 1
13 | InteractiveControlManager.SETTING_STATE_ALWAYS_ON = 2
14 | InteractiveControlManager.SETTING_STATE_OFF = 3
15 |
16 | local InteractiveControlManager_mt = Class(InteractiveControlManager)
17 |
18 | ---Create new instance of InteractiveControlManager
19 | function InteractiveControlManager.new(mission, inputBinding, i18n, modName, modDirectory, customMt)
20 | local self = setmetatable({}, customMt or InteractiveControlManager_mt)
21 |
22 | self:mergeModTranslations(i18n)
23 |
24 | self.modName = modName
25 | self.modDirectory = modDirectory
26 |
27 | self.isServer = mission:getIsServer()
28 | self.isClient = mission:getIsClient()
29 |
30 | self.mission = mission
31 | self.inputBinding = inputBinding
32 |
33 | self.activeController = nil
34 | self.actionEventId = nil
35 | self.playerInRange = false
36 |
37 | local title = g_i18n:getText("settingsIC_title", self.customEnvironment)
38 | self.settings = AdditionalSettingsManager.new(title, self, modName, modDirectory)
39 |
40 | local options = {
41 | g_i18n:getText("settingsIC_state_option01", self.customEnvironment),
42 | g_i18n:getText("settingsIC_state_option02", self.customEnvironment),
43 | g_i18n:getText("settingsIC_state_option03", self.customEnvironment),
44 | }
45 |
46 | title = g_i18n:getText("settingsIC_state_title", self.customEnvironment)
47 | local tooltip = g_i18n:getText("settingsIC_state_tooltip", self.customEnvironment)
48 | self.settings:addSetting("IC_STATE", AdditionalSettingsManager.TYPE_MULTIBOX, title, tooltip, 1, options)
49 |
50 | title = g_i18n:getText("settingsIC_keepAlive_title", self.customEnvironment)
51 | tooltip = g_i18n:getText("settingsIC_keepAlive_tooltip", self.customEnvironment)
52 | self.settings:addSetting("IC_KEEP_ALIVE", AdditionalSettingsManager.TYPE_CHECKBOX, title, tooltip, false)
53 |
54 | return self
55 | end
56 |
57 | ---Called on delete
58 | function InteractiveControlManager:delete()
59 | self.mission.messageCenter:unsubscribeAll(self)
60 | end
61 |
62 | ---Sets active interactiveControl
63 | ---@param target table
64 | ---@param inputButton string
65 | function InteractiveControlManager:setActiveInteractiveControl(target, inputButton)
66 | if target ~= self.activeController then
67 | self:unregisterActionEvents()
68 |
69 | if target ~= nil then
70 | self:registerActionEvents(inputButton)
71 | end
72 |
73 | self.activeController = self.actionEventId == nil and nil or target
74 | end
75 | end
76 |
77 | ---Returns true if manager has active control, false otherwise
78 | ---@return boolean hasActiveController
79 | function InteractiveControlManager:isICActive()
80 | local controlledVehicle = self.mission.controlledVehicle
81 |
82 | if controlledVehicle == nil then
83 | return self.playerInRange
84 | end
85 |
86 | if controlledVehicle.isICActive ~= nil then
87 | return controlledVehicle:isICActive()
88 | end
89 |
90 | return false
91 | end
92 |
93 | ---Returns modifier factor
94 | ---@param soundManager SoundManager instance of SoundManager
95 | ---@param superFunc function original function
96 | ---@param sample table sound sample
97 | ---@param modifierName string modifier string
98 | ---@return number volume
99 | function InteractiveControlManager:getModifierFactor(soundManager, superFunc, sample, modifierName)
100 | if modifierName == "volume" and self.mission.controlledVehicle ~= nil then
101 | local volume = superFunc(soundManager, sample, modifierName)
102 |
103 | if self.mission.controlledVehicle.getIndoorModifiedSoundFactor ~= nil then
104 | volume = volume * self.mission.controlledVehicle:getIndoorModifiedSoundFactor()
105 | end
106 |
107 | return volume
108 | else
109 | return superFunc(soundManager, sample, modifierName)
110 | end
111 | end
112 |
113 | ---Installs InteractiveControl spec in all vehicles
114 | function InteractiveControlManager.installSpecializations(vehicleTypeManager, specializationManager, modDirectory, modName)
115 | local specFilename = Utils.getFilename("src/vehicles/specializations/InteractiveControl.lua", modDirectory)
116 | specializationManager:addSpecialization("interactiveControl", "InteractiveControl", specFilename, nil)
117 |
118 | local function getInteractiveControlForced(specializations)
119 | for _, spec in ipairs(specializations) do
120 | if spec.ADD_INTERACTIVE_CONTROL then
121 | return true
122 | end
123 | end
124 |
125 | return false
126 | end
127 |
128 | for typeName, typeEntry in pairs(vehicleTypeManager:getTypes()) do
129 | local add = SpecializationUtil.hasSpecialization(Enterable, typeEntry.specializations)
130 | or SpecializationUtil.hasSpecialization(Attachable, typeEntry.specializations)
131 |
132 | if not add then
133 | add = getInteractiveControlForced(typeEntry.specializations)
134 | end
135 |
136 | if add then
137 | vehicleTypeManager:addSpecialization(typeName, modName .. ".interactiveControl")
138 | end
139 | end
140 | end
141 |
142 | ------------------
143 | ---ActionEvents---
144 | ------------------
145 |
146 | ---Sets has player in range state
147 | ---@param playerInRange boolean player is in range
148 | function InteractiveControlManager:setHasPlayerInRange(playerInRange)
149 | if playerInRange ~= self.playerInRange then
150 | self.playerInRange = playerInRange
151 | end
152 | end
153 |
154 | ---Register action events
155 | ---@param inputButton string
156 | function InteractiveControlManager:registerActionEvents(inputButton)
157 | inputButton = Utils.getNoNil(inputButton, InputAction.IC_CLICK)
158 |
159 | local _, actionEventId = self.inputBinding:registerActionEvent(inputButton, self, self.actionEventExecuteIC, false, true, false, true, nil, true)
160 | self.inputBinding:setActionEventTextPriority(actionEventId, GS_PRIO_VERY_HIGH)
161 | self.inputBinding:setActionEventTextVisibility(actionEventId, false)
162 | self.inputBinding:setActionEventActive(actionEventId, false)
163 |
164 | self.actionEventId = actionEventId
165 | end
166 |
167 | ---Unregister action events
168 | function InteractiveControlManager:unregisterActionEvents()
169 | self.inputBinding:removeActionEvent(self.actionEventId)
170 | end
171 |
172 | ---Sets interactive action event text and state
173 | ---@param text string
174 | ---@param active boolean
175 | function InteractiveControlManager:setClickAction(text, active)
176 | if self.actionEventId ~= nil then
177 | self.inputBinding:setActionEventText(self.actionEventId, text)
178 | self.inputBinding:setActionEventTextVisibility(self.actionEventId, active and text ~= "")
179 | self.inputBinding:setActionEventActive(self.actionEventId, active and text ~= "")
180 | end
181 | end
182 |
183 | ---Action Event Callback: execute interactive control
184 | function InteractiveControlManager:actionEventExecuteIC()
185 | if self.activeController ~= nil then
186 | if self.activeController:isExecutable() then
187 | self.activeController:execute()
188 | end
189 | end
190 | end
191 |
192 | ---Merge local i18n texts into global table
193 | ---@param i18n table
194 | function InteractiveControlManager:mergeModTranslations(i18n)
195 | -- Thanks for blocking the getfenv Giants..
196 | local modEnvMeta = getmetatable(_G)
197 | local env = modEnvMeta.__index
198 |
199 | local global = env.g_i18n.texts
200 | for key, text in pairs(i18n.texts) do
201 | global[key] = text
202 | end
203 | end
204 |
205 | ----------------
206 | ---Overwrites---
207 | ----------------
208 |
209 | ---Overwrite FS22_additionalGameSettings functions
210 | function InteractiveControlManager.overwrite_additionalGameSettings()
211 | if not g_modIsLoaded["FS22_additionalGameSettings"] then
212 | return
213 | end
214 |
215 | ---Overwritten function: FS22_additionalGameSettings.CrosshairSetting.getIsICActive
216 | ---Injects the InteractiveControl is active state for crosshair rendering
217 | ---@param static nil
218 | ---@param superFunc function original function
219 | ---@return boolean isICActive
220 | local function inject_getIsICActive(static, superFunc)
221 | if superFunc() then
222 | return true
223 | end
224 |
225 | if g_currentMission.interactiveControl ~= nil then
226 | return g_currentMission.interactiveControl:isICActive()
227 | end
228 |
229 | return false
230 | end
231 |
232 | if FS22_additionalGameSettings ~= nil and FS22_additionalGameSettings.CrosshairSetting ~= nil then
233 | local settings = FS22_additionalGameSettings.CrosshairSetting
234 | settings.getIsICActive = Utils.overwrittenFunction(settings.getIsICActive, inject_getIsICActive)
235 | end
236 | end
237 |
--------------------------------------------------------------------------------
/src/misc/InteractiveFunctions_externalMods.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- InteractiveFunctions_externalMods
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Storage for shared functionalities for external mods
5 | --
6 | -- @author John Deere 6930 @VertexDezign
7 | ----------------------------------------------------------------------------------------------------
8 |
9 | ---Extension of "src/misc/InteractiveFunctions.lua" for external mods
10 | ---@tablelib InteractiveFunctions for external mods
11 |
12 | ---Returns modClass in modEnvironment if existing, nil otherwise.
13 | ---If no modClassName is passed, the modEnvironment will be retured.
14 | ---@param modEnvironmentName string name of the mod environment (modName)
15 | ---@param modClassName? string|nil name of the mod class
16 | ---@return Class|nil modClass
17 | ---@return nil|boolean isEnvironment
18 | local function getExternalModClass(modEnvironmentName, modClassName)
19 | if not g_modIsLoaded[modEnvironmentName] then
20 | return nil, nil
21 | end
22 |
23 | local modEnvironment = _G[modEnvironmentName]
24 | if modEnvironment == nil then
25 | return nil, nil
26 | end
27 |
28 | if modClassName == nil then
29 | return modEnvironment, true
30 | end
31 |
32 | return modEnvironment[modClassName], false
33 | end
34 |
35 | ---------------------------
36 | ---FS22_guidanceSteering---
37 | ---------------------------
38 |
39 | ---FUNCTION_GPS_TOGGLE
40 | InteractiveFunctions.addFunction("GPS_TOGGLE", {
41 | posFunc = function(target, data, noEventSend)
42 | local GlobalPositioningSystem = getExternalModClass("FS22_guidanceSteering", "GlobalPositioningSystem")
43 |
44 | if GlobalPositioningSystem ~= nil then
45 | if target.spec_globalPositioningSystem ~= nil and GlobalPositioningSystem.actionEventEnableSteering ~= nil then
46 | GlobalPositioningSystem.actionEventEnableSteering(target)
47 | end
48 | end
49 | end,
50 | updateFunc = function(target, data)
51 | if target.spec_globalPositioningSystem ~= nil then
52 | return target.spec_globalPositioningSystem.guidanceSteeringIsActive
53 | end
54 | return nil
55 | end,
56 | isEnabledFunc = function(target, data)
57 | if target.spec_globalPositioningSystem ~= nil then
58 | return target.spec_globalPositioningSystem.guidanceIsActive
59 | end
60 | return false
61 | end
62 | })
63 |
64 | ---FUNCTION_GPS_TOGGLE_ACTIVE
65 | InteractiveFunctions.addFunction("GPS_TOGGLE_ACTIVE", {
66 | posFunc = function(target, data, noEventSend)
67 | local GlobalPositioningSystem = getExternalModClass("FS22_guidanceSteering", "GlobalPositioningSystem")
68 |
69 | if GlobalPositioningSystem ~= nil then
70 | if target.spec_globalPositioningSystem ~= nil and GlobalPositioningSystem.actionEventToggleGuidanceSteering ~= nil then
71 | GlobalPositioningSystem.actionEventToggleGuidanceSteering(target)
72 | end
73 | end
74 | end,
75 | updateFunc = function(target, data)
76 | if target.spec_globalPositioningSystem ~= nil then
77 | return target.spec_globalPositioningSystem.guidanceIsActive
78 | end
79 | return nil
80 | end,
81 | isEnabledFunc = function(target, data)
82 | return target.spec_globalPositioningSystem ~= nil
83 | end
84 | })
85 |
86 | ---------------------------
87 | ---FS22_precisionFarming---
88 | ---------------------------
89 |
90 | ---FUNCTION_PF_CROP_SENSOR_TOGGLE
91 | InteractiveFunctions.addFunction("PF_CROP_SENSOR_TOGGLE", {
92 | posFunc = function(target, data, noEventSend)
93 | local CropSensor = getExternalModClass("FS22_precisionFarming", "CropSensor")
94 |
95 | if CropSensor ~= nil then
96 | if target.spec_cropSensor.isAvailable and CropSensor.actionEventToggle ~= nil then
97 | CropSensor.actionEventToggle(target)
98 | end
99 | end
100 | end,
101 | updateFunc = function(target, data)
102 | if target.spec_cropSensor ~= nil then
103 | return target.spec_cropSensor.isActive
104 | end
105 | return nil
106 | end,
107 | isEnabledFunc = function(target, data)
108 | if target.spec_cropSensor ~= nil then
109 | return target.spec_cropSensor.isAvailable
110 | end
111 | return false
112 | end
113 | })
114 |
115 | ---FUNCTION_PF_ATTACHERJOINTS_CROP_SENSOR_TOGGLE
116 | InteractiveFunctions.addFunction("PF_ATTACHERJOINTS_CROP_SENSOR_TOGGLE", {
117 | posFunc = function(target, data, noEventSend)
118 | local CropSensor = getExternalModClass("FS22_precisionFarming", "CropSensor")
119 |
120 | if CropSensor ~= nil then
121 | local attachedObject = data.currentAttachedObject
122 |
123 | if attachedObject ~= nil and CropSensor.actionEventToggle ~= nil then
124 | CropSensor.actionEventToggle(attachedObject)
125 | end
126 | end
127 | end,
128 | updateFunc = function(target, data)
129 | local attachedObject = data.currentAttachedObject
130 |
131 | if attachedObject ~= nil then
132 | return attachedObject.spec_cropSensor.isActive
133 | end
134 | return nil
135 | end,
136 | schemaFunc = InteractiveFunctions.attacherJointsSchema,
137 | loadFunc = function(xmlFile, key, data)
138 | return InteractiveFunctions.attacherJointsLoad(xmlFile, key, data, "PF_ATTACHERJOINTS_CROP_SENSOR_TOGGLE")
139 | end,
140 | isEnabledFunc = function(target, data)
141 | if getExternalModClass("FS22_precisionFarming") == nil then
142 | return false
143 | end
144 |
145 | local _, attachedObject = InteractiveFunctions.getAttacherJointObjectToUse(data, target, function(object)
146 | return object.spec_cropSensor ~= nil and object.spec_cropSensor.isAvailable
147 | end)
148 |
149 | return attachedObject ~= nil
150 | end
151 | })
152 |
153 | ---FUNCTION_PF_SEED_RATE_MODE
154 | InteractiveFunctions.addFunction("PF_SEED_RATE_MODE", {
155 | posFunc = function(target, data, noEventSend)
156 | local ExtendedSowingMachine = getExternalModClass("FS22_precisionFarming", "ExtendedSowingMachine")
157 |
158 | if ExtendedSowingMachine ~= nil then
159 | if target.spec_extendedSowingMachine and ExtendedSowingMachine.actionEventToggleAuto ~= nil then
160 | ExtendedSowingMachine.actionEventToggleAuto(target)
161 | end
162 | end
163 | end,
164 | updateFunc = function(target, data)
165 | if target.spec_extendedSowingMachine ~= nil then
166 | return target.spec_extendedSowingMachine.seedRateAutoMode
167 | end
168 | return nil
169 | end,
170 | isEnabledFunc = function(target, data)
171 | return target.spec_extendedSowingMachine ~= nil
172 | end
173 | })
174 |
175 | ---FUNCTION_PF_ATTACHERJOINTS_SEED_RATE_MODE
176 | InteractiveFunctions.addFunction("PF_ATTACHERJOINTS_SEED_RATE_MODE", {
177 | posFunc = function(target, data, noEventSend)
178 | local ExtendedSowingMachine = getExternalModClass("FS22_precisionFarming", "ExtendedSowingMachine")
179 |
180 | if ExtendedSowingMachine ~= nil then
181 | local attachedObject = data.currentAttachedObject
182 |
183 | if attachedObject ~= nil and ExtendedSowingMachine.actionEventToggleAuto ~= nil then
184 | ExtendedSowingMachine.actionEventToggleAuto(attachedObject)
185 | end
186 | end
187 | end,
188 | updateFunc = function(target, data)
189 | local attachedObject = data.currentAttachedObject
190 |
191 | if attachedObject ~= nil then
192 | return attachedObject.spec_extendedSowingMachine.seedRateAutoMode
193 | end
194 | return nil
195 | end,
196 | schemaFunc = InteractiveFunctions.attacherJointsSchema,
197 | loadFunc = function(xmlFile, key, data)
198 | return InteractiveFunctions.attacherJointsLoad(xmlFile, key, data, "PF_ATTACHERJOINTS_SEED_RATE_MODE")
199 | end,
200 | isEnabledFunc = function(target, data)
201 | if getExternalModClass("FS22_precisionFarming") == nil then
202 | return false
203 | end
204 |
205 | local _, attachedObject = InteractiveFunctions.getAttacherJointObjectToUse(data, target, function(object)
206 | return object.spec_extendedSowingMachine ~= nil
207 | end)
208 |
209 | return attachedObject ~= nil
210 | end
211 | })
212 |
213 | ---FUNCTION_PF_SEED_RATE_UP
214 | InteractiveFunctions.addFunction("PF_SEED_RATE_UP", {
215 | posFunc = function(target, data, noEventSend)
216 | local ExtendedSowingMachine = getExternalModClass("FS22_precisionFarming", "ExtendedSowingMachine")
217 |
218 | if ExtendedSowingMachine ~= nil then
219 | if target.spec_extendedSowingMachine and ExtendedSowingMachine.actionEventChangeSeedRate ~= nil then
220 | ExtendedSowingMachine.actionEventChangeSeedRate(target, nil, 1)
221 | end
222 | end
223 | end,
224 | isEnabledFunc = function(target, data)
225 | if target.spec_extendedSowingMachine ~= nil then
226 | return not target.spec_extendedSowingMachine.seedRateAutoMode
227 | end
228 | return false
229 | end
230 | })
231 |
232 | ---FUNCTION_PF_SEED_RATE_DOWN
233 | InteractiveFunctions.addFunction("PF_SEED_RATE_DOWN", {
234 | posFunc = function(target, data, noEventSend)
235 | local ExtendedSowingMachine = getExternalModClass("FS22_precisionFarming", "ExtendedSowingMachine")
236 |
237 | if ExtendedSowingMachine ~= nil then
238 | if target.spec_extendedSowingMachine and ExtendedSowingMachine.actionEventChangeSeedRate ~= nil then
239 | ExtendedSowingMachine.actionEventChangeSeedRate(target, nil, -1)
240 | end
241 | end
242 | end,
243 | isEnabledFunc = function(target, data)
244 | if target.spec_extendedSowingMachine ~= nil then
245 | return not target.spec_extendedSowingMachine.seedRateAutoMode
246 | end
247 | return false
248 | end
249 | })
250 |
251 | ---FUNCTION_PF_ATTACHERJOINTS_SEED_RATE_UP
252 | InteractiveFunctions.addFunction("PF_ATTACHERJOINTS_SEED_RATE_UP", {
253 | posFunc = function(target, data, noEventSend)
254 | local ExtendedSowingMachine = getExternalModClass("FS22_precisionFarming", "ExtendedSowingMachine")
255 |
256 | if ExtendedSowingMachine ~= nil then
257 | local attachedObject = data.currentAttachedObject
258 |
259 | if attachedObject ~= nil and ExtendedSowingMachine.actionEventChangeSeedRate ~= nil then
260 | ExtendedSowingMachine.actionEventChangeSeedRate(attachedObject, nil, 1)
261 | end
262 | end
263 | end,
264 | schemaFunc = InteractiveFunctions.attacherJointsSchema,
265 | loadFunc = function(xmlFile, key, data)
266 | return InteractiveFunctions.attacherJointsLoad(xmlFile, key, data, "PF_ATTACHERJOINTS_SEED_RATE_UP")
267 | end,
268 | isEnabledFunc = function(target, data)
269 | if getExternalModClass("FS22_precisionFarming") == nil then
270 | return false
271 | end
272 |
273 | local _, attachedObject = InteractiveFunctions.getAttacherJointObjectToUse(data, target, function(object)
274 | return object.spec_extendedSowingMachine ~= nil and not object.spec_extendedSowingMachine.seedRateAutoMode
275 | end)
276 |
277 | return attachedObject ~= nil
278 | end
279 | })
280 |
281 | ---FUNCTION_PF_ATTACHERJOINTS_SEED_RATE_DOWN
282 | InteractiveFunctions.addFunction("PF_ATTACHERJOINTS_SEED_RATE_DOWN", {
283 | posFunc = function(target, data, noEventSend)
284 | local ExtendedSowingMachine = getExternalModClass("FS22_precisionFarming", "ExtendedSowingMachine")
285 |
286 | if ExtendedSowingMachine ~= nil then
287 | local attachedObject = data.currentAttachedObject
288 |
289 | if attachedObject ~= nil and ExtendedSowingMachine.actionEventChangeSeedRate ~= nil then
290 | ExtendedSowingMachine.actionEventChangeSeedRate(attachedObject, nil, -1)
291 | end
292 | end
293 | end,
294 | schemaFunc = InteractiveFunctions.attacherJointsSchema,
295 | loadFunc = function(xmlFile, key, data)
296 | return InteractiveFunctions.attacherJointsLoad(xmlFile, key, data, "PF_ATTACHERJOINTS_SEED_RATE_DOWN")
297 | end,
298 | isEnabledFunc = function(target, data)
299 | if getExternalModClass("FS22_precisionFarming") == nil then
300 | return false
301 | end
302 |
303 | local _, attachedObject = InteractiveFunctions.getAttacherJointObjectToUse(data, target, function(object)
304 | return object.spec_extendedSowingMachine ~= nil and not object.spec_extendedSowingMachine.seedRateAutoMode
305 | end)
306 |
307 | return attachedObject ~= nil
308 | end
309 | })
310 |
311 | ---FUNCTION_PF_SPRAY_AMOUNT_MODE
312 | InteractiveFunctions.addFunction("PF_SPRAY_AMOUNT_MODE", {
313 | posFunc = function(target, data, noEventSend)
314 | local ExtendedSprayer = getExternalModClass("FS22_precisionFarming", "ExtendedSprayer")
315 |
316 | if ExtendedSprayer ~= nil then
317 | if target.spec_extendedSprayer and ExtendedSprayer.actionEventToggleAuto ~= nil then
318 | ExtendedSprayer.actionEventToggleAuto(target)
319 | end
320 | end
321 | end,
322 | updateFunc = function(target, data)
323 | if target.spec_extendedSprayer ~= nil then
324 | return target.spec_extendedSprayer.sprayAmountAutoMode
325 | end
326 | return nil
327 | end,
328 | isEnabledFunc = function(target, data)
329 | return target.spec_extendedSprayer ~= nil
330 | end
331 | })
332 |
333 | ---FUNCTION_PF_ATTACHERJOINTS_SPRAY_AMOUNT_MODE
334 | InteractiveFunctions.addFunction("PF_ATTACHERJOINTS_SPRAY_AMOUNT_MODE", {
335 | posFunc = function(target, data, noEventSend)
336 | local ExtendedSprayer = getExternalModClass("FS22_precisionFarming", "ExtendedSprayer")
337 |
338 | if ExtendedSprayer ~= nil then
339 | local attachedObject = data.currentAttachedObject
340 |
341 | if attachedObject ~= nil and ExtendedSprayer.actionEventToggleAuto ~= nil then
342 | ExtendedSprayer.actionEventToggleAuto(attachedObject)
343 | end
344 | end
345 | end,
346 | updateFunc = function(target, data)
347 | local attachedObject = data.currentAttachedObject
348 |
349 | if attachedObject ~= nil then
350 | return attachedObject.spec_extendedSprayer.sprayAmountAutoMode
351 | end
352 | return nil
353 | end,
354 | schemaFunc = InteractiveFunctions.attacherJointsSchema,
355 | loadFunc = function(xmlFile, key, data)
356 | return InteractiveFunctions.attacherJointsLoad(xmlFile, key, data, "PF_ATTACHERJOINTS_SPRAY_AMOUNT_MODE")
357 | end,
358 | isEnabledFunc = function(target, data)
359 | if getExternalModClass("FS22_precisionFarming") == nil then
360 | return false
361 | end
362 |
363 | local _, attachedObject = InteractiveFunctions.getAttacherJointObjectToUse(data, target, function(object)
364 | return object.spec_extendedSprayer ~= nil
365 | end)
366 |
367 | return attachedObject ~= nil
368 | end
369 | })
370 |
371 | ---FUNCTION_PF_SPRAY_AMOUNT_UP
372 | InteractiveFunctions.addFunction("PF_SPRAY_AMOUNT_UP", {
373 | posFunc = function(target, data, noEventSend)
374 | local ExtendedSprayer = getExternalModClass("FS22_precisionFarming", "ExtendedSprayer")
375 |
376 | if ExtendedSprayer ~= nil then
377 | if target.spec_extendedSprayer and ExtendedSprayer.actionEventToggleAuto ~= nil then
378 | ExtendedSprayer.actionEventChangeSprayAmount(target, nil, 1)
379 | end
380 | end
381 | end,
382 | isEnabledFunc = function(target, data)
383 | if target.spec_extendedSprayer ~= nil then
384 | return not target.spec_extendedSprayer.sprayAmountAutoMode
385 | end
386 | return false
387 | end
388 | })
389 |
390 | ---FUNCTION_PF_SPRAY_AMOUNT_DOWN
391 | InteractiveFunctions.addFunction("PF_SPRAY_AMOUNT_DOWN", {
392 | posFunc = function(target, data, noEventSend)
393 | local ExtendedSprayer = getExternalModClass("FS22_precisionFarming", "ExtendedSprayer")
394 |
395 | if ExtendedSprayer ~= nil then
396 | if target.spec_extendedSprayer and ExtendedSprayer.actionEventToggleAuto ~= nil then
397 | ExtendedSprayer.actionEventChangeSprayAmount(target, nil, -1)
398 | end
399 | end
400 | end,
401 | isEnabledFunc = function(target, data)
402 | if target.spec_extendedSprayer ~= nil then
403 | return not target.spec_extendedSprayer.sprayAmountAutoMode
404 | end
405 | return false
406 | end
407 | })
408 |
409 | ---FUNCTION_PF_ATTACHERJOINTS_SPRAY_AMOUNT_UP
410 | InteractiveFunctions.addFunction("PF_ATTACHERJOINTS_SPRAY_AMOUNT_UP", {
411 | posFunc = function(target, data, noEventSend)
412 | local ExtendedSprayer = getExternalModClass("FS22_precisionFarming", "ExtendedSprayer")
413 |
414 | if ExtendedSprayer ~= nil then
415 | local attachedObject = data.currentAttachedObject
416 |
417 | if attachedObject ~= nil and ExtendedSprayer.actionEventChangeSprayAmount ~= nil then
418 | ExtendedSprayer.actionEventChangeSprayAmount(attachedObject, nil, 1)
419 | end
420 | end
421 | end,
422 | schemaFunc = InteractiveFunctions.attacherJointsSchema,
423 | loadFunc = function(xmlFile, key, data)
424 | return InteractiveFunctions.attacherJointsLoad(xmlFile, key, data, "PF_ATTACHERJOINTS_SPRAY_AMOUNT_UP")
425 | end,
426 | isEnabledFunc = function(target, data)
427 | if getExternalModClass("FS22_precisionFarming") == nil then
428 | return false
429 | end
430 |
431 | local _, attachedObject = InteractiveFunctions.getAttacherJointObjectToUse(data, target, function(object)
432 | return object.spec_extendedSprayer ~= nil and not object.spec_extendedSprayer.sprayAmountAutoMode
433 | end)
434 |
435 | return attachedObject ~= nil
436 | end
437 | })
438 |
439 | ---FUNCTION_PF_ATTACHERJOINTS_SPRAY_AMOUNT_DOWN
440 | InteractiveFunctions.addFunction("PF_ATTACHERJOINTS_SPRAY_AMOUNT_DOWN", {
441 | posFunc = function(target, data, noEventSend)
442 | local ExtendedSprayer = getExternalModClass("FS22_precisionFarming", "ExtendedSprayer")
443 |
444 | if ExtendedSprayer ~= nil then
445 | local attachedObject = data.currentAttachedObject
446 |
447 | if attachedObject ~= nil and ExtendedSprayer.actionEventChangeSprayAmount ~= nil then
448 | ExtendedSprayer.actionEventChangeSprayAmount(attachedObject, nil, -1)
449 | end
450 | end
451 | end,
452 | schemaFunc = InteractiveFunctions.attacherJointsSchema,
453 | loadFunc = function(xmlFile, key, data)
454 | return InteractiveFunctions.attacherJointsLoad(xmlFile, key, data, "PF_ATTACHERJOINTS_SPRAY_AMOUNT_DOWN")
455 | end,
456 | isEnabledFunc = function(target, data)
457 | if getExternalModClass("FS22_precisionFarming") == nil then
458 | return false
459 | end
460 |
461 | local _, attachedObject = InteractiveFunctions.getAttacherJointObjectToUse(data, target, function(object)
462 | return object.spec_extendedSprayer ~= nil and not object.spec_extendedSprayer.sprayAmountAutoMode
463 | end)
464 |
465 | return attachedObject ~= nil
466 | end
467 | })
468 |
469 | ------------------------------
470 | ---FS22_VehicleControlAddon---
471 | ------------------------------
472 |
473 | ---FUNCTION_VCA_TOGGLE_AWD
474 | InteractiveFunctions.addFunction("VCA_TOGGLE_AWD", {
475 | posFunc = function(target, data, noEventSend)
476 | local vehicleControlAddon = getExternalModClass("FS22_VehicleControlAddon", "vehicleControlAddon")
477 |
478 | if vehicleControlAddon ~= nil and target.spec_vca ~= nil then
479 | vehicleControlAddon.actionCallback(target, "vcaDiffLockM")
480 | end
481 | end,
482 | updateFunc = function(target, data)
483 | if target.spec_vca ~= nil then
484 | return target.spec_vca.diffLockAWD
485 | end
486 | return nil
487 | end,
488 | isEnabledFunc = function(target, data)
489 | if target.spec_vca ~= nil and target.spec_vca.diffHasM and target.spec_vca.diffManual then
490 | return true
491 | end
492 | return false
493 | end
494 | })
495 |
496 | ---FUNCTION_VCA_TOGGLE_DIFFLOCK_FRONT
497 | InteractiveFunctions.addFunction("VCA_TOGGLE_DIFFLOCK_FRONT", {
498 | posFunc = function(target, data, noEventSend)
499 | local vehicleControlAddon = getExternalModClass("FS22_VehicleControlAddon", "vehicleControlAddon")
500 |
501 | if vehicleControlAddon ~= nil and target.spec_vca ~= nil then
502 | vehicleControlAddon.actionCallback(target, "vcaDiffLockF")
503 | end
504 | end,
505 | updateFunc = function(target, data)
506 | if target.spec_vca ~= nil then
507 | return target.spec_vca.diffLockFront
508 | end
509 | return nil
510 | end,
511 | isEnabledFunc = function(target, data)
512 | if target.spec_vca ~= nil and target.spec_vca.diffHasF and target.spec_vca.diffManual then
513 | return true
514 | end
515 | return false
516 | end
517 | })
518 |
519 | ---FUNCTION_VCA_TOGGLE_DIFFLOCK_BACK
520 | InteractiveFunctions.addFunction("VCA_TOGGLE_DIFFLOCK_BACK", {
521 | posFunc = function(target, data, noEventSend)
522 | local vehicleControlAddon = getExternalModClass("FS22_VehicleControlAddon", "vehicleControlAddon")
523 |
524 | if vehicleControlAddon ~= nil and target.spec_vca ~= nil then
525 | vehicleControlAddon.actionCallback(target, "vcaDiffLockB")
526 | end
527 | end,
528 | updateFunc = function(target, data)
529 | if target.spec_vca ~= nil then
530 | return target.spec_vca.diffLockBack
531 | end
532 | return nil
533 | end,
534 | isEnabledFunc = function(target, data)
535 | if target.spec_vca ~= nil and target.spec_vca.diffHasB and target.spec_vca.diffManual then
536 | return true
537 | end
538 | return false
539 | end
540 | })
541 |
542 | ---FUNCTION_VCA_TOGGLE_PARKINGBRAKE
543 | InteractiveFunctions.addFunction("VCA_TOGGLE_PARKINGBRAKE", {
544 | posFunc = function(target, data, noEventSend)
545 | local vehicleControlAddon = getExternalModClass("FS22_VehicleControlAddon", "vehicleControlAddon")
546 |
547 | if vehicleControlAddon ~= nil and target.spec_vca ~= nil then
548 | vehicleControlAddon.actionCallback(target, "vcaHandbrake")
549 | end
550 | end,
551 | updateFunc = function(target, data)
552 | if target.spec_vca ~= nil then
553 | return target.spec_vca.handbrake
554 | end
555 | return nil
556 | end,
557 | isEnabledFunc = function(target, data)
558 | if target.spec_vca ~= nil then
559 | return true
560 | end
561 | return false
562 | end
563 | })
564 |
565 | -----------------------------
566 | ---FS22_HeadlandManagement---
567 | -----------------------------
568 |
569 | ---FUNCTION_HEADLAND_MANAGEMENT_TOGGLE
570 | InteractiveFunctions.addFunction("HEADLAND_MANAGEMENT_TOGGLE", {
571 | posFunc = function(target, data, noEventSend)
572 | local HeadlandManagement = getExternalModClass("FS22_HeadlandManagement", "HeadlandManagement")
573 |
574 | if HeadlandManagement ~= nil then
575 | if target.spec_HeadlandManagement ~= nil and HeadlandManagement.TOGGLESTATE ~= nil then
576 | HeadlandManagement.TOGGLESTATE(target, "HLM_TOGGLESTATE")
577 | end
578 | end
579 | end,
580 | updateFunc = function(target, data)
581 | if target.spec_HeadlandManagement ~= nil then
582 | return target.spec_HeadlandManagement.isActive
583 | end
584 | return nil
585 | end,
586 | isEnabledFunc = function(target, data)
587 | if target.spec_HeadlandManagement ~= nil then
588 | return target.spec_HeadlandManagement.exists
589 | end
590 | return false
591 | end
592 | })
593 |
594 | -----------------------
595 | ---FS22_manureSystem---
596 | -----------------------
597 |
598 | ---FUNCTION_MS_TOGGLE_PUMP
599 | InteractiveFunctions.addFunction("MS_TOGGLE_PUMP", {
600 | posFunc = function(target, data, noEventSend)
601 | local ManureSystemPumpMotor = getExternalModClass("FS22_manureSystem", "ManureSystemPumpMotor")
602 |
603 | if ManureSystemPumpMotor ~= nil then
604 | ManureSystemPumpMotor.actionEventTogglePump(target)
605 | end
606 | end,
607 | updateFunc = function(target, data)
608 | if target.spec_manureSystemPumpMotor ~= nil then
609 | return target.spec_manureSystemPumpMotor.pumpIsRunning
610 | end
611 | return nil
612 | end,
613 | isEnabledFunc = function(target, data)
614 | if target.spec_manureSystemPumpMotor ~= nil then
615 | return true
616 | end
617 | return false
618 | end
619 | })
620 |
621 | InteractiveFunctions.addFunction("MS_TOGGLE_PUMP_DIRECTION", {
622 | posFunc = function(target, data, noEventSend)
623 | local ManureSystemPumpMotor = getExternalModClass("FS22_manureSystem", "ManureSystemPumpMotor")
624 |
625 | if ManureSystemPumpMotor ~= nil then
626 | ManureSystemPumpMotor.actionEventTogglePumpDirection(target)
627 | end
628 | end,
629 | updateFunc = function(target, data)
630 | if target.spec_manureSystemPumpMotor and target.spec_manureSystemPumpMotor.pumpDirection == 1 then
631 | return true
632 | end
633 | return false
634 | end,
635 | isEnabledFunc = function(target, data)
636 | if target.spec_manureSystemPumpMotor ~= nil then
637 | return true
638 | end
639 | return false
640 | end
641 | })
642 |
--------------------------------------------------------------------------------
/src/vehicles/specializations/AddInteractiveControl.lua:
--------------------------------------------------------------------------------
1 | ----------------------------------------------------------------------------------------------------
2 | -- AddInteractiveControl
3 | ----------------------------------------------------------------------------------------------------
4 | -- Purpose: Specialization placeholder for interactive control installation.
5 | --
6 | -- Usage: Copy this specialization into your mod and add this specialization to your custom vehicle
7 | -- type if the interactiveControl specialization isn't installed automatically.
8 | --
9 | -- @author John Deere 6930 @VertexDezign
10 | ----------------------------------------------------------------------------------------------------
11 |
12 | ---@class AddInteractiveControl
13 |
14 | AddInteractiveControl = {}
15 | AddInteractiveControl.ADD_INTERACTIVE_CONTROL = true
16 |
17 | function AddInteractiveControl.prerequisitesPresent(specializations)
18 | return true
19 | end
20 |
--------------------------------------------------------------------------------