├── .gitignore ├── .DS_Store ├── .gitattributes ├── FS19_simpleIC.zip ├── FS19_simpleIC ├── .DS_Store ├── store.dds ├── modDesc.xml ├── sic_ptoControl.lua ├── sic_motorStartControl.lua ├── sic_drivableControl.lua ├── simpleIC_implementBalls.lua ├── sic_implementControl.lua ├── registerSimpleIC.lua ├── sic_attacherControl.lua ├── sic_lightControl.lua └── simpleIC.lua ├── README.md └── examples.xml /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modelleicher/FS19_simpleIC/HEAD/.DS_Store -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /FS19_simpleIC.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modelleicher/FS19_simpleIC/HEAD/FS19_simpleIC.zip -------------------------------------------------------------------------------- /FS19_simpleIC/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modelleicher/FS19_simpleIC/HEAD/FS19_simpleIC/.DS_Store -------------------------------------------------------------------------------- /FS19_simpleIC/store.dds: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/modelleicher/FS19_simpleIC/HEAD/FS19_simpleIC/store.dds -------------------------------------------------------------------------------- /FS19_simpleIC/modDesc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | modelleicher (LS-Modcompany) 4 | 0.9.3.4 5 | 6 | <de>SimpleIC - Einfaches Interactive Control</de> 7 | <en>SimpleIC - Easy Interactive Control</en> 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | store.dds 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | IC: an/aus schaltenIC: on/off 41 | IC: Interagieren vehicleIC: interact vehicle 42 | IC: Interagieren zu FußIC: interact on foot 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FS19_simpleIC 2 | New Interactive Control Script for FS19 3 | 4 | # Changelog: 5 | 6 | ###### V 0.9.3.2 7 | - simpleIC_implementBalls.lua:207: attempt to index field 'vehicle' fixed (at least I hope that did the trick, I assume this error comes from synch issues not actual bug in script) 8 | - code cleanup ptoControl 9 | - addition of implementControl though not fully implemented yet (thus no example in examples.xml yet) 10 | - addition of disableInvisibleTriggers attribute if you want to disable triggerPoints via visibility. Was on by default in previous versions since implementation. 11 | But when people converted old IC to simpleIC they just set the old buttons to invisible and used the index for IC, now all of those didn't work anymore. 12 | So now by default it SimpleIC doesn't care about visibilty but if you want to disable triggerPoints via visibility you can do that by adding this attribute. (see Examples XML) 13 | ###### V 0.9.3.1 14 | - added motorStartControl and animations 15 | ###### V 0.9.3.0 16 | - lightControl leverAnimations for all types added and synched 17 | - debugPrints removed 18 | ###### V 0.9.2.9 19 | - implementBalls synch Issue possible fix 20 | - lightControl addition (leverAnimations still to do) 21 | ###### V 0.9.2.8 22 | - some bugfixes for the recent additions 23 | ###### V 0.9.2.7 24 | - separated attacherControl into seperate lua 25 | - synchronized attacherControl with setJointMoveDown so its always in synch 26 | - fixed doNotSynch for ptoControl 27 | - added returnToCenter, returnToCenterRaised, returnToCenterLowered for attacherControl leverAnimation 28 | ###### V 0.9.2.6 29 | - separated ptoControl into seperate lua 30 | - synchronized ptoControl animation with turnOnVehicle so its always in synch 31 | - fixed version numbering mishap (yes this is 0.9.2.6 not 0.9.1.6) 32 | ###### V 0.9.2.5 33 | - added attacherControl 34 | - added ptoControl 35 | ###### V 0.9.2.4 36 | - made icFunctions more universally usable for future additions 37 | - icFunction can be "turned off" by setting visibility of triggerPoint to false 38 | ###### V 0.9.2.3 39 | - MP Server fix of vehicle not enterable 40 | ###### V 0.9.2.2 41 | - fix for line 102 error I think, couldn't reproduce but I found an issue with the table indexing and I think it worked fine for most mods but some may have caused the issue to show. 42 | ###### V 0.9.2.1 43 | - possibly fixed dedicated server issue 44 | - removed "cylinder" debug finally 45 | ###### V 0.9.2.0 46 | - addition of SimpleIC-ImplementBalls 47 | ###### V 0.9.1.9 48 | - fixed the issue introduced in the version before last version and partially fixed in the last version. Now fixed completely. I hope.. again. 49 | ###### V 0.9.1.8 50 | - fixed Issue introduced in the last version of indoor buttons only working when the ingame-menu is on, fully removed issue with double-mapping of mouseButtons I hope 51 | ###### V 0.9.1.7 52 | - fixed Error: simpleIC.lua:488: attempt to index field 'spec_motorized' (a nil value) 53 | - fixed Error: simpleIC.lua:318: attempt to call method 'getAttacherVehicle' (a nil value) 54 | - reachDistance can be set per vehicle (optional, default 1.8) to specify how far away a player can reach an IC-point 55 | ###### V 0.9.1.6 56 | - fixed spec insertion so simpleIC now works in every implement, trailer etc. not just drivables 57 | ###### V 0.9.1.5 58 | - fixed Error: simpleIC.lua:248: attempt to index local 'spec' (a nil value) 59 | - added cylinderAnimation for easy animation of struts on windows/doors etc. 60 | ###### V 0.9.1.4 61 | - fixed IC active on all vehicles bug (now only active if vehicle actually has IC functions) 62 | - fixed bug Error: Running LUA method 'update' simpleIC.lua:292: attempt to index a nil value 63 | - added default keymapping 64 | ###### V 0.9.1.3 65 | - multiplayer fix 66 | - added triggerPoint_ON and triggerPoint_OFF as alternative to toggle via triggerPoint 67 | 68 | # the most important thing: 69 | How do I test this? 70 | 1. download FS19_simpleIC.zip and add to modfolder 71 | 2. download my Agrostar 6.61 Edit I released for christmas, it already has simpleIC added. https://youtu.be/lsEg6T7XOkE 72 | (by now there are a lot of SimpleIC ready mods out there already so just find the next best one you like) 73 | 3. go ingame and have fun :D 74 | 75 | # What this is: 76 | This is a new take on the well known Interactive Control Scripts in Farming Simulator. Since there hasn't been a well-working bug-free version of the old scripts in FS19, and since I always didn't like the way the Mouse is used, I created this alternative. 77 | 78 | - This is a global script, which means that it doesn't have to be added to each Mod seperately, no additional modDesc.xml changes like l10n Texts etc. neccessary. 79 | - Obviously the vehicle-xml and i3d still has to be edited, the script can't magically seperate doors and add trigger-points. But as soon as the needed lines are added, IC will be active as long as you have this mod active. 80 | - this also means that people who don't like IC don't have to remove it all vehicle-mods, just not activate this mod. 81 | - this also means that there's only one IC version and not 50 different ones that get into conflict with each other 82 | - updates to IC are global and useable in all mods 83 | 84 | Now for the bad parts 85 | - still Beta 86 | - still Bugs 87 | - not even close to the amount of features the original IC Script had in FS17. (But I'm working on that ;) ) 88 | 89 | # How to add this to my Mod: 90 | - I will create videos explaining the process of adding simpleIC to your vehicle. 91 | 92 | - There is an examples.xml explaining all the current possible XML entrys and what they do. If you're not brandnew to modding this should be enough to get going :) 93 | 94 | If you already know modding well, here's a short explanation: 95 | (look at the linked Deutz Agrostar above to see the full XML lines) 96 | 97 | - outsideInteractionTrigger = playerTrigger in which the player can open doors and other outside-stuff from the outside 98 | - animationName = name of the animation for the door 99 | - animationSpeed = speed of the animation (obvious) 100 | - shared animation = not added yet 101 | - soundVolumeIncreasePercentage = by how much will the sound-volume increase if that door is opened. Values will be added together for more than one door, max is outdoorSoundVolume 102 | - insideTrigger and outsideTrigger = "Triggerpoints" e.g. transformGroups that mark the spot where the IC component can be clicked 103 | - triggerPoint = index / i3dMapping name for the transformGroup 104 | - triggerPointSize = size/radius around the triggerPoint where it still registeres as being clicked 105 | 106 | 107 | -------------------------------------------------------------------------------- /FS19_simpleIC/sic_ptoControl.lua: -------------------------------------------------------------------------------- 1 | -- SimpleIC PTO Control 2 | -- to control PTO Implements e.g. TurnOnVehicle via simpleIC 3 | 4 | sic_ptoControl = {}; 5 | 6 | function sic_ptoControl.prerequisitesPresent(specializations) 7 | return true; 8 | end; 9 | 10 | function sic_ptoControl.registerEventListeners(vehicleType) 11 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", sic_ptoControl); 12 | end; 13 | 14 | function sic_ptoControl:onLoad(savegame) 15 | self.loadPTOControl = sic_ptoControl.loadPTOControl; 16 | self.setPTOControl = sic_ptoControl.setPTOControl; 17 | 18 | -- access tables 19 | if self.sic_at == nil then 20 | self.sic_at = {}; 21 | end; 22 | self.sic_at.attacherIndex_ptoControl = {}; 23 | self.sic_at.ptoControl = {}; 24 | end; 25 | 26 | function sic_ptoControl:loadPTOControl(key, table) 27 | local ptoControl ={}; 28 | -- load xml attributes for ptoControl, attacherIndex is for backwards-compatability, use attacherIndices instead 29 | local attacherIndex = getXMLInt(self.xmlFile, key.."#attacherIndex"); 30 | ptoControl.turnOnSelf = getXMLBool(self.xmlFile, key.."#turnOnSelf"); 31 | local attacherIndices = getXMLString(self.xmlFile, key.."#attacherIndices") 32 | 33 | -- if we turn on self we don't need attacherIndices 34 | if attacherIndex ~= nil or (attacherIndices ~= "" and attacherIndices ~= nil) or ptoControl.turnOnSelf then 35 | 36 | -- split attacherIndices string into attacherIndices/add attacherIndex to table. Also add to create access tables 37 | if attacherIndices ~= nil then 38 | ptoControl.attacherIndices = StringUtil.splitString(" ", attacherIndices); 39 | for _, attacherIndex in pairs(ptoControl.attacherIndices) do 40 | self.sic_at.attacherIndex_ptoControl[tonumber(attacherIndex)] = ptoControl; 41 | end; 42 | else 43 | ptoControl.attacherIndices = {tostring(attacherIndex)}; 44 | self.sic_at.attacherIndex_ptoControl[attacherIndex] = ptoControl; 45 | end; 46 | 47 | -- load optional animation 48 | local animationName = getXMLString(self.xmlFile, key..".leverAnimation#animationName"); 49 | if animationName ~= "" and animationName ~= nil then 50 | ptoControl.leverAnimation = {}; 51 | ptoControl.leverAnimation.animationName = animationName; 52 | ptoControl.leverAnimation.doNotSynch = getXMLBool(self.xmlFile, key..".leverAnimation#doNotSynch"); 53 | end; 54 | 55 | -- add table and access table 56 | table.ptoControl = ptoControl; 57 | self.sic_at.ptoControl[#self.sic_at.ptoControl+1] = ptoControl; 58 | return true; 59 | end; 60 | end; 61 | 62 | function sic_ptoControl:setPTOControl(wantedState, i) 63 | local ptoControl = self.spec_simpleIC.icFunctions[i].ptoControl; 64 | 65 | -- if turnOnSelf its easy just turn on ourselfes lol 66 | if ptoControl.turnOnSelf then 67 | if wantedState == nil then 68 | wantedState = not self.spec_turnOnVehicle.isTurnedOn; 69 | end; 70 | self:setIsTurnedOn(wantedState); 71 | end; 72 | 73 | -- cycle through all attached implements to find the implement attached to the attacherIndex of this icFunction ptoControl 74 | for _ , attacherIndex in pairs(ptoControl.attacherIndices) do 75 | for _, implement in pairs(self.spec_attacherJoints.attachedImplements) do 76 | if tostring(implement.jointDescIndex) == attacherIndex then 77 | if implement.object.spec_turnOnVehicle ~= nil then 78 | 79 | -- wantedState is either true or false when there is a _ON and _OFF trigger, otherwise its nil so we need the != of the current state 80 | if wantedState == nil then 81 | wantedState = not implement.object.spec_turnOnVehicle.isTurnedOn; 82 | end; 83 | -- apply state 84 | implement.object:setIsTurnedOn(wantedState) 85 | 86 | -- run animation if doNotSynch is true, otherwise animation will be run by setIsTurnedOn Append function 87 | if ptoControl.leverAnimation ~= nil and ptoControl.leverAnimation.doNotSynch then 88 | local speed = 1; 89 | if not wantedState then 90 | speed = -1; 91 | end; 92 | self:playAnimation(ptoControl.leverAnimation.animationName, speed, self:getAnimationTime(ptoControl.leverAnimation.animationName), true); 93 | end; 94 | end; 95 | end; 96 | end; 97 | end; 98 | 99 | end; 100 | 101 | 102 | 103 | -- override setIsTurnedOn for playing animation when turned on/off even if it isn't done via SIC Input 104 | function sic_ptoControl.setIsTurnedOnAppend(self, superFunc, isTurnedOn, noEventSend) 105 | superFunc(self, isTurnedOn, noEventSend); 106 | 107 | -- check if we have simpleIC ourselfes 108 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 109 | for _, ptoControl in pairs(self.sic_at.ptoControl) do 110 | if ptoControl.turnOnSelf then 111 | if ptoControl.leverAnimation ~= nil then 112 | local speed = -1; 113 | if isTurnedOn then 114 | speed = 1; 115 | end; 116 | self:playAnimation(ptoControl.leverAnimation.animationName, speed, nil, false); 117 | end; 118 | end; 119 | end; 120 | end; 121 | 122 | -- if we are an implement, we need to get our attacherVehicle 123 | if self.getAttacherVehicle ~= nil then 124 | local attacherVehicle = self:getAttacherVehicle() 125 | -- check if attacherVehicle has simpleIC 126 | if attacherVehicle ~= nil and attacherVehicle.spec_simpleIC ~= nil and attacherVehicle.spec_simpleIC.hasIC then 127 | -- run through implements attached to attacherVehicle to find out attacherIndex 128 | for _, implement in pairs(attacherVehicle.spec_attacherJoints.attachedImplements) do 129 | local ptoControl = attacherVehicle.sic_at.attacherIndex_ptoControl[implement.jointDescIndex]; 130 | -- check if ptoControl has leverAnimation 131 | if ptoControl ~= nil and ptoControl.leverAnimation ~= nil then 132 | if implement.object == self then 133 | -- run animation 134 | local speed = -1; 135 | if isTurnedOn then 136 | speed = 1; 137 | end; 138 | attacherVehicle:playAnimation(ptoControl.leverAnimation.animationName, speed, attacherVehicle:getAnimationTime(ptoControl.leverAnimation.animationName), true); 139 | end; 140 | end; 141 | end; 142 | end; 143 | end; 144 | 145 | end; 146 | 147 | TurnOnVehicle.setIsTurnedOn = Utils.overwrittenFunction(TurnOnVehicle.setIsTurnedOn, sic_ptoControl.setIsTurnedOnAppend); 148 | 149 | -------------------------------------------------------------------------------- /FS19_simpleIC/sic_motorStartControl.lua: -------------------------------------------------------------------------------- 1 | -- SimpleIC Motor Start Control 2 | -- to start/stop engine via IC and animate button/lever 3 | 4 | sic_motorStartControl = {}; 5 | 6 | function sic_motorStartControl.prerequisitesPresent(specializations) 7 | return true; 8 | end; 9 | 10 | function sic_motorStartControl.registerEventListeners(vehicleType) 11 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", sic_motorStartControl); 12 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", sic_motorStartControl) 13 | end; 14 | 15 | 16 | function sic_motorStartControl:onLoad(savegame) 17 | self.loadMotorStartControl = sic_motorStartControl.loadMotorStartControl; 18 | self.setMotorStartControl = sic_motorStartControl.setMotorStartControl; 19 | 20 | self.spec_sic_motorStartControl = {}; 21 | 22 | local spec = self.spec_sic_motorStartControl; 23 | 24 | spec.turnKeyAnimationState = 0; 25 | spec.turnKeyAnimation = nil; 26 | 27 | end; 28 | 29 | 30 | function sic_motorStartControl:loadMotorStartControl(key, table) 31 | local motorStartControl ={}; 32 | 33 | if hasXMLProperty(self.xmlFile, key) then 34 | 35 | local animationName = getXMLString(self.xmlFile, key..".leverAnimation#animationName"); 36 | if animationName ~= "" and animationName ~= nil then 37 | motorStartControl.leverAnimation = {}; 38 | motorStartControl.leverAnimation.animationName = animationName; 39 | motorStartControl.leverAnimation.isTurnKeyAnimation = getXMLBool(self.xmlFile, key..".leverAnimation#isTurnKeyAnimation") 40 | end; 41 | 42 | local animationNameStart = getXMLString(self.xmlFile, key..".leverAnimationStart#animationName"); 43 | if animationNameStart ~= "" and animationNameStart ~= nil then 44 | motorStartControl.leverAnimationStart = {}; 45 | motorStartControl.leverAnimationStart.animationName = animationNameStart; 46 | end; 47 | 48 | local animationNameStop = getXMLString(self.xmlFile, key..".leverAnimationStop#animationName"); 49 | if animationNameStop ~= "" and animationNameStop ~= nil then 50 | motorStartControl.leverAnimationStop = {}; 51 | motorStartControl.leverAnimationStop.animationName = animationNameStop; 52 | end; 53 | 54 | table.motorStartControl = motorStartControl; 55 | return true; 56 | 57 | end; 58 | 59 | end; 60 | 61 | function sic_motorStartControl:setMotorStartControl(wantedState, i) 62 | local motorStartControl = self.spec_simpleIC.icFunctions[i].motorStartControl; 63 | 64 | if wantedState == nil then 65 | wantedState = not self:getIsMotorStarted(); 66 | end; 67 | 68 | if not wantedState then 69 | self:stopMotor(); 70 | elseif wantedState and self:getCanMotorRun() then 71 | self:startMotor() 72 | end; 73 | 74 | end; 75 | 76 | function sic_motorStartControl:onUpdateTick(dt) 77 | if self:getIsActive() then 78 | local spec = self.spec_sic_motorStartControl; 79 | if spec.turnKeyAnimation ~= nil and spec.turnKeyAnimationState == 1 then 80 | if g_currentMission.time > self:getMotorStartTime() then 81 | self:playAnimation(spec.turnKeyAnimation.animationName, -1, self:getAnimationTime(spec.turnKeyAnimation.animationName), true); 82 | self:setAnimationStopTime(spec.turnKeyAnimation.animationName, 0.7); 83 | spec.turnKeyAnimationState = 2; 84 | spec.turnKeyAnimation = nil; 85 | end; 86 | end; 87 | if spec.leverAnimationStart ~= nil then 88 | local animTime = self:getAnimationTime(spec.leverAnimationStart.animationName); 89 | if animTime == 1 then 90 | self:playAnimation(spec.leverAnimationStart.animationName, -1, self:getAnimationTime(spec.leverAnimationStart.animationName), true); 91 | spec.leverAnimationStart = nil; 92 | end; 93 | end; 94 | if spec.leverAnimationStop ~= nil then 95 | local animTime = self:getAnimationTime(spec.leverAnimationStop.animationName); 96 | if animTime < 1 then 97 | self:raiseActive(); 98 | else 99 | self:playAnimation(spec.leverAnimationStop.animationName, -1, self:getAnimationTime(spec.leverAnimationStop.animationName), true); 100 | spec.leverAnimationStop = nil; 101 | end; 102 | end; 103 | end; 104 | end; 105 | 106 | function sic_motorStartControl.startMotorAppend(self, superFunc, noEventSend) 107 | superFunc(self, noEventSend); 108 | 109 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 110 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 111 | if icFunction.motorStartControl ~= nil then 112 | 113 | local spec = self.spec_sic_motorStartControl; 114 | 115 | local leverAnimation = icFunction.motorStartControl.leverAnimation; 116 | if leverAnimation ~= nil then 117 | self:playAnimation(leverAnimation.animationName, 1, self:getAnimationTime(leverAnimation.animationName), true); 118 | if leverAnimation.isTurnKeyAnimation then 119 | spec.turnKeyAnimationState = 1; 120 | spec.turnKeyAnimation = leverAnimation; 121 | end; 122 | end; 123 | 124 | local leverAnimationStart = icFunction.motorStartControl.leverAnimationStart; 125 | if leverAnimationStart ~= nil then 126 | self:playAnimation(leverAnimationStart.animationName, 1, self:getAnimationTime(leverAnimationStart.animationName), true); 127 | spec.leverAnimationStart = leverAnimationStart; 128 | end; 129 | end; 130 | end; 131 | end; 132 | end; 133 | Motorized.startMotor = Utils.overwrittenFunction(Motorized.startMotor, sic_motorStartControl.startMotorAppend) 134 | 135 | function sic_motorStartControl.stopMotor(self, superFunc, noEventSend) 136 | superFunc(self, noEventSend); 137 | 138 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 139 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 140 | if icFunction.motorStartControl ~= nil then 141 | 142 | if icFunction.motorStartControl.leverAnimation ~= nil then 143 | local leverAnimation = icFunction.motorStartControl.leverAnimation; 144 | self:playAnimation(leverAnimation.animationName, -1, self:getAnimationTime(leverAnimation.animationName), true); 145 | end; 146 | 147 | local leverAnimationStop = icFunction.motorStartControl.leverAnimationStop; 148 | if leverAnimationStop ~= nil then 149 | self:playAnimation(leverAnimationStop.animationName, 1, self:getAnimationTime(leverAnimationStop.animationName), true); 150 | self.spec_sic_motorStartControl.leverAnimationStop = leverAnimationStop; 151 | self:raiseActive(); 152 | end; 153 | end; 154 | end; 155 | end; 156 | end; 157 | Motorized.stopMotor = Utils.overwrittenFunction(Motorized.stopMotor, sic_motorStartControl.stopMotor) -------------------------------------------------------------------------------- /FS19_simpleIC/sic_drivableControl.lua: -------------------------------------------------------------------------------- 1 | -- SimpleIC Drivable Control 2 | -- to control stuff that is generally found in drivable vehicles 3 | 4 | 5 | sic_drivableControl = {}; 6 | 7 | function sic_drivableControl.prerequisitesPresent(specializations) 8 | return true; 9 | end; 10 | 11 | function sic_drivableControl.registerEventListeners(vehicleType) 12 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", sic_drivableControl); 13 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", sic_drivableControl); 14 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", sic_drivableControl); 15 | end; 16 | 17 | function sic_drivableControl:onLoad(savegame) 18 | self.loadDrivableControl = sic_drivableControl.loadDrivableControl; 19 | self.setDrivableControl = sic_drivableControl.setDrivableControl; 20 | 21 | -- access tables 22 | if self.sic_at == nil then 23 | self.sic_at = {}; 24 | end; 25 | self.sic_at.drivableControl = {}; 26 | 27 | self.spec_sic_drivableControl = {}; 28 | 29 | 30 | 31 | end; 32 | 33 | function sic_drivableControl:onPostLoad(savegame) 34 | local spec = self.spec_sic_drivableControl; 35 | 36 | spec.tsx_enhancedVehicleLoaded = g_modIsLoaded.TSX_EnhancedVehicle; 37 | if spec.tsx_enhancedVehicleLoaded then 38 | spec.tsx_enhancedVehicle_diffEnabled = TSX_EnhancedVehicle.TSX_EnhancedVehicle.functionDifferentialIsEnabled; 39 | spec.tsx_enhancedVehicle_shuttleEnabled = TSX_EnhancedVehicle.TSX_EnhancedVehicle.functionShuttleIsEnabled; 40 | 41 | spec.vDataBackup = {nil, nil, nil, nil, nil, nil}; 42 | end; 43 | end; 44 | 45 | function sic_drivableControl:loadDrivableControl(key, table) 46 | local drivableControl = {}; 47 | 48 | drivableControl.type = getXMLString(self.xmlFile, key.."#type"); 49 | drivableControl.specific = getXMLString(self.xmlFile, key.."#specific"); 50 | 51 | if drivableControl.type ~= nil and drivableControl.type ~= "" then 52 | 53 | local animationName = getXMLString(self.xmlFile, key..".leverAnimation#animationName"); 54 | if animationName ~= "" and animationName ~= nil then 55 | drivableControl.leverAnimation = {}; 56 | drivableControl.leverAnimation.animationName = animationName; 57 | drivableControl.leverAnimation.doNotSynch = getXMLBool(self.xmlFile, key..".leverAnimation#doNotSynch"); 58 | end; 59 | 60 | table.drivableControl = drivableControl; 61 | self.sic_at.drivableControl[#self.sic_at.drivableControl+1] = drivableControl; 62 | return true; 63 | end; 64 | 65 | end; 66 | 67 | -- get access to TSX functions 68 | function sic_drivableControl.tsx_onRegisterActionEventsAppend(self, superFunc, isSelected, isOnActiveVehicle) 69 | 70 | local returnValues = superFunc(self, isSelected, isOnActiveVehicle); 71 | 72 | self.onActionCall = TSX_EnhancedVehicle.TSX_EnhancedVehicle.onActionCall; 73 | end; 74 | if TSX_EnhancedVehicle ~= nil and TSX_EnhancedVehicle.TSX_EnhancedVehicle ~= nil then 75 | TSX_EnhancedVehicle.TSX_EnhancedVehicle.onRegisterActionEvents = Utils.overwrittenFunction(TSX_EnhancedVehicle.TSX_EnhancedVehicle.onRegisterActionEvents, sic_drivableControl.tsx_onRegisterActionEventsAppend); 76 | end; 77 | 78 | function sic_drivableControl:onUpdate(dt) 79 | if self:getIsActive() then 80 | local spec = self.spec_sic_drivableControl; 81 | 82 | for _, drivableControl in pairs(self.sic_at.drivableControl) do 83 | local leverAnimation = drivableControl.leverAnimation; 84 | if leverAnimation ~= nil then 85 | if spec.tsx_enhancedVehicleLoaded then 86 | local speed = -1; 87 | if spec.tsx_enhancedVehicle_diffEnabled then 88 | if drivableControl.type == "diffLock" and (drivableControl.specific == "front" or drivableControl.specific == "all") then 89 | if spec.vDataBackup[1] ~= self.vData.want[1] then 90 | if self.vData.want[1] then 91 | speed = 1; 92 | end; 93 | self:playAnimation(leverAnimation.animationName, speed, self:getAnimationTime(leverAnimation.animationName), true); 94 | end; 95 | end; 96 | if drivableControl.type == "diffLock" and (drivableControl.specific == "rear" or drivableControl.specific == "all") then 97 | if spec.vDataBackup[2] ~= self.vData.want[2] then 98 | if self.vData.want[2] then 99 | speed = 1; 100 | end; 101 | self:playAnimation(leverAnimation.animationName, speed, self:getAnimationTime(leverAnimation.animationName), true); 102 | end; 103 | end; 104 | if drivableControl.type == "4wd" then 105 | 106 | if spec.vDataBackup[3] ~= self.vData.want[3] then 107 | if self.vData.want[3] then 108 | speed = 1; 109 | end; 110 | print("4wd do") 111 | self:playAnimation(leverAnimation.animationName, speed, self:getAnimationTime(leverAnimation.animationName), true); 112 | end; 113 | end; 114 | end; 115 | if spec.tsx_enhancedVehicle_shuttleEnabled then 116 | if drivableControl.type == "shuttle" then 117 | if spec.vDataBackup[4] ~= self.vData.want[4] then 118 | if self.vData.want[4] then 119 | speed = 1; 120 | end; 121 | self:playAnimation(leverAnimation.animationName, speed, self:getAnimationTime(leverAnimation.animationName), true); 122 | end; 123 | end; 124 | if drivableControl.type == "handbrake" then 125 | if spec.vDataBackup[6] ~= self.vData.want[6] then 126 | if self.vData.want[6] then 127 | speed = 1; 128 | end; 129 | self:playAnimation(leverAnimation.animationName, speed, self:getAnimationTime(leverAnimation.animationName), true); 130 | end; 131 | end; 132 | end; 133 | end; 134 | end; 135 | end; 136 | end; 137 | end; 138 | 139 | 140 | 141 | 142 | function sic_drivableControl:setDrivableControl(wantedState, i) 143 | local drivableControl = self.spec_simpleIC.icFunctions[i].drivableControl; 144 | 145 | local spec = self.spec_sic_drivableControl; 146 | 147 | -- try use TSX 4wd 148 | if spec.tsx_enhancedVehicleLoaded then 149 | 150 | if spec.tsx_enhancedVehicle_diffEnabled then 151 | if drivableControl.type == "4wd" then 152 | self:onActionCall("TSX_EnhancedVehicle_DM"); 153 | end; 154 | if drivableControl.type == "diffLock" then 155 | if drivableControl.specific == "all" then 156 | self:onActionCall("TSX_EnhancedVehicle_BD"); 157 | elseif drivableControl.specific == "front" then 158 | self:onActionCall("TSX_EnhancedVehicle_FD"); 159 | elseif drivableControl.specific == "rear" then 160 | self:onActionCall("TSX_EnhancedVehicle_RD"); 161 | end; 162 | end; 163 | end; 164 | if spec.tsx_enhancedVehicle_shuttleEnabled then 165 | if drivableControl.type == "handbrake" then 166 | self:onActionCall("TSX_EnhancedVehicle_SHUTTLE_PARK"); 167 | end; 168 | if drivableControl.type == "shuttle" then 169 | if drivableControl.specific == "toggle" then 170 | self:onActionCall("TSX_EnhancedVehicle_SHUTTLE_SWITCH"); 171 | elseif drivableControl.specific == "forward" then 172 | self:onActionCall("TSX_EnhancedVehicle_SHUTTLE_FWD"); 173 | elseif drivableControl.specific == "reverse" then 174 | self:onActionCall("TSX_EnhancedVehicle_SHUTTLE_REV"); 175 | end; 176 | end; 177 | end; 178 | end; 179 | 180 | end; -------------------------------------------------------------------------------- /FS19_simpleIC/simpleIC_implementBalls.lua: -------------------------------------------------------------------------------- 1 | -- by modelleicher 2 | -- www.ls-modcompany.com 3 | -- Part of SimpleIC global script. Global script that adds the ability to right-click on a implement attacher to add balls. 4 | 5 | simpleIC_implementBalls = {}; 6 | 7 | 8 | function simpleIC_implementBalls.prerequisitesPresent(specializations) 9 | return true; 10 | end; 11 | 12 | function simpleIC_implementBalls.registerEventListeners(vehicleType) 13 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", simpleIC_implementBalls); 14 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", simpleIC_implementBalls); 15 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", simpleIC_implementBalls); 16 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", simpleIC_implementBalls); 17 | SpecializationUtil.registerEventListener(vehicleType, "saveToXMLFile", simpleIC_implementBalls); 18 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", simpleIC_implementBalls); 19 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", simpleIC_implementBalls); 20 | end; 21 | 22 | function simpleIC_implementBalls:onLoad(savegame) 23 | self.setImplementBalls = simpleIC_implementBalls.setImplementBalls 24 | if g_currentMission.simpleIC_implementBalls == nil then 25 | g_currentMission.simpleIC_implementBalls = {}; 26 | end; 27 | 28 | self.spec_implementBalls = {} 29 | local spec = self.spec_implementBalls; 30 | local i3dFilename = "$data/vehicles/johnDeere/weightSetBall.i3d" 31 | 32 | local ballI3d = g_i3DManager:loadSharedI3DFile(i3dFilename, self.baseDirectory, false, false, false) 33 | 34 | spec.leftNode = I3DUtil.indexToObject(ballI3d, "0", self.i3dMappings) 35 | spec.rightNode = I3DUtil.indexToObject(ballI3d, "1", self.i3dMappings) 36 | 37 | spec.maxDistance = 1.4; 38 | -- check if we have a implement attacher joint 39 | if self.spec_attachable.inputAttacherJoints ~= nil then 40 | local jointTypeWant = AttacherJoints.jointTypeNameToInt["implement"] 41 | spec.implementJoints = {}; 42 | for _ , inputAttacherJoint in pairs(self.spec_attachable.inputAttacherJoints) do 43 | if inputAttacherJoint.jointType == jointTypeWant and inputAttacherJoint.node ~= nil then 44 | inputAttacherJoint.showBalls = false; 45 | spec.implementJoints[#spec.implementJoints+1] = inputAttacherJoint; 46 | end; 47 | end; 48 | if #spec.implementJoints < 1 then 49 | spec = nil; 50 | else 51 | spec.vehicle = self; 52 | table.insert(g_currentMission.simpleIC_implementBalls, spec) 53 | end; 54 | end; 55 | 56 | end; 57 | 58 | function simpleIC_implementBalls:onDelete() 59 | local spec = self.spec_implementBalls; 60 | if spec ~= nil then 61 | for index, specFind in pairs(g_currentMission.simpleIC_implementBalls) do 62 | if specFind == spec then 63 | table.remove(g_currentMission.simpleIC_implementBalls, index) 64 | end; 65 | end; 66 | spec = nil; 67 | end; 68 | end; 69 | 70 | 71 | function simpleIC_implementBalls:onUpdateTick(dt) 72 | local spec = self.spec_implementBalls; 73 | for _, implementJoint in pairs(spec.implementJoints) do 74 | if implementJoint.showX then 75 | local aX, aY, aZ = getWorldTranslation(implementJoint.node) 76 | local x, y, z = project(aX, aY, aZ); 77 | 78 | cameraNode = g_currentMission.player.cameraNode 79 | 80 | local cX, cY, cZ = getWorldTranslation(cameraNode); 81 | local dist = MathUtil.vector3Length(aX-cX, aY-cY, aZ-cZ); 82 | local size = 0.028 / dist; 83 | 84 | local posX, posY, posZ = 0.5, 0.5, 0.5; 85 | local radius = 0.03; 86 | 87 | if posX < (x + radius) and posX > (x - radius) then 88 | if posY < (y + radius) and posY > (y - radius) then 89 | self:renderTextAtProjectedPosition(x ,y ,z, "X", size, 1, 0, 0) 90 | implementJoint.canBeClicked = true; 91 | end; 92 | else 93 | self:renderTextAtProjectedPosition(x ,y ,z, "X", size, 1, 1, 1) 94 | implementJoint.canBeClicked = false; 95 | end; 96 | end; 97 | end; 98 | end; 99 | 100 | function simpleIC_implementBalls:setImplementBalls(index, forceState, noEventSend) 101 | local spec = self.spec_implementBalls; 102 | if spec.implementJoints[index] ~= nil then 103 | spec.implementJoints[index].showBalls = Utils.getNoNil(forceState, not spec.implementJoints[index].showBalls); 104 | setICImplementBallsEvent.sendEvent(self, spec.implementJoints[index].showBalls, index, noEventSend); 105 | 106 | if spec.implementJoints[index].showBalls then 107 | link(spec.implementJoints[index].node, spec.leftNode) 108 | link(spec.implementJoints[index].node, spec.rightNode) 109 | setTranslation(spec.leftNode, 0, 0, -0.432) 110 | setTranslation(spec.rightNode, 0, 0, 0.432) 111 | setRotation(spec.leftNode, 0, math.rad(90), 0) 112 | setRotation(spec.rightNode, 0, math.rad(90), 0) 113 | else 114 | unlink(spec.leftNode) 115 | unlink(spec.rightNode) 116 | end; 117 | end; 118 | end; 119 | 120 | function simpleIC_implementBalls:onPostLoad(savegame) 121 | if self.spec_implementBalls ~= nil and savegame ~= nil then 122 | local spec = self.spec_implementBalls; 123 | local xmlFile = savegame.xmlFile; 124 | 125 | 126 | local key1 = savegame.key..".FS19_simpleIC.simpleIC_implementBalls" 127 | for i, implementJoint in pairs(spec.implementJoints) do 128 | -- load ball state 129 | if implementJoint.showBalls ~= nil then 130 | local state = getXMLBool(xmlFile, key1..".implementBall"..i.."#state"); 131 | if state ~= nil then 132 | self:setImplementBalls(i, state) 133 | end; 134 | end; 135 | end; 136 | end; 137 | end; 138 | 139 | function simpleIC_implementBalls:saveToXMLFile(xmlFile, key) 140 | if self.spec_implementBalls ~= nil then 141 | local spec = self.spec_implementBalls; 142 | 143 | for i, implementJoint in pairs(spec.implementJoints) do 144 | -- save ball state 145 | if implementJoint.showBalls ~= nil then 146 | setXMLBool(xmlFile, key..".implementBall"..i.."#state", implementJoint.showBalls); 147 | end; 148 | end; 149 | 150 | end; 151 | end; 152 | 153 | function simpleIC_implementBalls:onReadStream(streamId, connection) 154 | local spec = self.spec_implementBalls 155 | if spec ~= nil then 156 | if connection:getIsServer() then 157 | for i, implementJoint in pairs(spec.implementJoints) do 158 | local state = streamReadBool(streamId, implementJoint.showBalls) 159 | if state ~= nil then 160 | self:setImplementBalls(i, state) 161 | end; 162 | end; 163 | end; 164 | end; 165 | end 166 | 167 | function simpleIC_implementBalls:onWriteStream(streamId, connection) 168 | local spec = self.spec_implementBalls; 169 | if spec ~= nil then 170 | if not connection:getIsServer() then 171 | for _, implementJoint in pairs(spec.implementJoints) do 172 | streamWriteBool(streamId, implementJoint.showBalls) 173 | end; 174 | end; 175 | end; 176 | end 177 | 178 | 179 | 180 | setICImplementBallsEvent = {}; 181 | setICImplementBallsEvent_mt = Class(setICImplementBallsEvent, Event); 182 | InitEventClass(setICImplementBallsEvent, "setICImplementBallsEvent"); 183 | 184 | function setICImplementBallsEvent:emptyNew() 185 | local self = Event:new(setICImplementBallsEvent_mt ); 186 | self.className="setICImplementBallsEvent"; 187 | return self; 188 | end; 189 | function setICImplementBallsEvent:new(vehicle, state, ballIndex, animationIndex) 190 | self.vehicle = vehicle; 191 | self.state = state; 192 | self.ballIndex = Utils.getNoNil(ballIndex, 1); 193 | return self; 194 | end; 195 | function setICImplementBallsEvent:readStream(streamId, connection) 196 | self.vehicle = NetworkUtil.readNodeObject(streamId); 197 | self.state = streamReadBool(streamId); 198 | self.ballIndex = streamReadUIntN(streamId, 6); 199 | self:run(connection); 200 | end; 201 | function setICImplementBallsEvent:writeStream(streamId, connection) 202 | NetworkUtil.writeNodeObject(streamId, self.vehicle); 203 | streamWriteBool(streamId, self.state ); 204 | streamWriteUIntN(streamId, self.ballIndex, 6); 205 | end; 206 | function setICImplementBallsEvent:run(connection) 207 | if self.vehicle ~= nil then 208 | self.vehicle:setImplementBalls(self.ballIndex, self.state, true); 209 | end; 210 | if not connection:getIsServer() then 211 | g_server:broadcastEvent(setICImplementBallsEvent:new(self.vehicle, self.state, self.ballIndex), nil, connection, self.object); 212 | end; 213 | end; 214 | function setICImplementBallsEvent.sendEvent(vehicle, state, ballIndex, noEventSend) 215 | if noEventSend == nil or noEventSend == false then 216 | if g_server ~= nil then 217 | g_server:broadcastEvent(setICImplementBallsEvent:new(vehicle, state, ballIndex), nil, nil, vehicle); 218 | else 219 | g_client:getServerConnection():sendEvent(setICImplementBallsEvent:new(vehicle, state, ballIndex)); 220 | end; 221 | end; 222 | end; -------------------------------------------------------------------------------- /FS19_simpleIC/sic_implementControl.lua: -------------------------------------------------------------------------------- 1 | -- SimpleIC ImplementControl 2 | -- to control Implements via SimpleIC 3 | 4 | sic_implementControl = {}; 5 | 6 | function sic_implementControl.prerequisitesPresent(specializations) 7 | return true; 8 | end; 9 | 10 | function sic_implementControl.registerEventListeners(vehicleType) 11 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", sic_implementControl); 12 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", sic_implementControl); 13 | end; 14 | 15 | function sic_implementControl:onLoad(savegame) 16 | self.loadImplementControl = sic_implementControl.loadImplementControl; 17 | self.setImplementControl = sic_implementControl.setImplementControl; 18 | 19 | -- access tables 20 | if self.sic_at == nil then 21 | self.sic_at = {}; 22 | end; 23 | self.sic_at.attacherIndex_implementControl = {}; 24 | self.sic_at.implementControl = {}; 25 | end; 26 | 27 | 28 | function sic_implementControl:loadImplementControl(key, table) 29 | local implementControl = {}; 30 | 31 | implementControl.attacherIndices = StringUtil.splitString(" ", getXMLString(self.xmlFile, key.."#attacherIndices")); 32 | implementControl.controlSelf = getXMLBool(self.xmlFile, key.."#controlSelf") 33 | implementControl.isType = {} 34 | if #implementControl.attacherIndices > 0 or implementControl.controlSelf then 35 | 36 | for _ , attacherIndex in pairs(implementControl.attacherIndices) do 37 | self.sic_at.attacherIndex_implementControl[tonumber(attacherIndex)] = implementControl; 38 | end; 39 | 40 | implementControl.types = StringUtil.splitString(" ", getXMLString(self.xmlFile, key.."#types")); 41 | 42 | for _, type in pairs(implementControl.types) do 43 | implementControl.isType[type] = true; 44 | end; 45 | 46 | local animationName = getXMLString(self.xmlFile, key..".leverAnimation#animationName"); 47 | if animationName ~= "" and animationName ~= nil then 48 | implementControl.leverAnimation = {}; 49 | implementControl.leverAnimation.animationName = animationName; 50 | implementControl.leverAnimation.doNotSynch = getXMLBool(self.xmlFile, key..".leverAnimation#doNotSynch"); 51 | 52 | implementControl.leverAnimation.returnToCenter = getXMLBool(self.xmlFile, key..".leverAnimation#returnToCenter"); 53 | implementControl.leverAnimation.returnToCenterRaised = getXMLBool(self.xmlFile, key..".leverAnimation#returnToCenterRaised"); 54 | implementControl.leverAnimation.returnToCenterLowered = getXMLBool(self.xmlFile, key.."leverAnimation#returnToCenterLowered"); 55 | 56 | if implementControl.leverAnimation.returnToCenter or implementControl.leverAnimation.returnToCenterRaised then 57 | self:playAnimation(implementControl.leverAnimation.animationName, 1, animTime, true); 58 | self:setAnimationStopTime(implementControl.leverAnimation.animationName, 0.5); 59 | end; 60 | end; 61 | 62 | table.implementControl = implementControl; 63 | self.sic_at.implementControl[#self.sic_at.implementControl+1] = implementControl; 64 | return true; 65 | end; 66 | 67 | end; 68 | 69 | 70 | function sic_implementControl:setImplementControl(wantedState, i) 71 | local implementControl = self.spec_simpleIC.icFunctions[i].implementControl; 72 | 73 | local currentObject = nil; 74 | 75 | -- if we are controlSelf than currentObject will be self 76 | if implementControl.controlSelf then 77 | currentObject = self; 78 | else 79 | -- cycle through all attacherIndices of the current function 80 | for _, attacherIndex in pairs(implementControl.attacherIndices) do 81 | -- cycle through all attacher implements to see if we find a match 82 | for _, implement in pairs(self.spec_attacherJoints.attachedImplements) do 83 | -- if we found a match, "secure" that as our current implement 84 | if tostring(implement.jointDescIndex) == attacherIndex then 85 | currentObject = implement.object; 86 | break; 87 | end; 88 | 89 | end; 90 | if currentObject ~= nil then 91 | break; 92 | end; 93 | end; 94 | end; 95 | 96 | -- now that we find the prioritised object, find the priority function 97 | if currentObject ~= nil then 98 | for _, type in pairs(implementControl.types) do 99 | 100 | if type == "fold" then 101 | -- try do the folding stuffs 102 | if currentObject.spec_foldable ~= nil then 103 | if #currentObject.spec_foldable.foldingParts > 0 then 104 | local toggleDirection = Utils.getNoNil(wantedState, currentObject:getToggledFoldDirection()); 105 | if toggleDirection == currentObject.spec_foldable.turnOnFoldDirection then 106 | currentObject:setFoldState(toggleDirection, true); 107 | else 108 | currentObject:setFoldState(toggleDirection, false); 109 | end; 110 | break; -- we done, we break 111 | end; 112 | end; 113 | end; 114 | 115 | if type == "tip" then 116 | 117 | print("tiiiiip") 118 | 119 | end; 120 | 121 | end; 122 | end; 123 | end; 124 | 125 | function sic_implementControl:onUpdate(dt) 126 | if self:getIsActive() then 127 | for _, implementControl in pairs(self.sic_at.implementControl) do 128 | if implementControl.leverAnimation ~= nil and implementControl.leverAnimation.dirtySelf ~= nil then 129 | local leverAnimation = implementControl.leverAnimation; 130 | 131 | -- foldable lever animation return to center 132 | local foldTime = leverAnimation.dirtySelf:getFoldAnimTime() 133 | local animTime = self:getAnimationTime(leverAnimation.animationName) 134 | if leverAnimation.returnToCenter then 135 | if foldTime == 0 then 136 | self:playAnimation(leverAnimation.animationName, 1, animTime, true); 137 | self:setAnimationStopTime(leverAnimation.animationName, 0.5); 138 | leverAnimation.dirtySelf = nil; 139 | elseif foldTime == 1 then 140 | self:playAnimation(leverAnimation.animationName, -1, animTime, true); 141 | self:setAnimationStopTime(leverAnimation.animationName, 0.5); 142 | leverAnimation.dirtySelf = nil; 143 | end; 144 | end; 145 | if leverAnimation.returnToCenterRaised then 146 | if foldTime == 1 then 147 | self:playAnimation(leverAnimation.animationName, -1, animTime, true); 148 | self:setAnimationStopTime(leverAnimation.animationName, 0.5); 149 | leverAnimation.dirtySelf = nil; 150 | end; 151 | end; 152 | if leverAnimation.returnToCenterLowered then 153 | if foldTime == 0 then 154 | self:playAnimation(leverAnimation.animationName, 1, animTime, true); 155 | self:setAnimationStopTime(leverAnimation.animationName, 0.5); 156 | leverAnimation.dirtySelf = nil; 157 | end; 158 | end; 159 | 160 | end; 161 | end; 162 | 163 | end; 164 | end; 165 | 166 | 167 | -- Foldable 168 | function sic_implementControl.setFoldStateAppend(self, superFunc, direction, moveToMiddle, noEventSend) 169 | 170 | local returnValues = superFunc(self, direction, moveToMiddle, noEventSend); 171 | 172 | 173 | -- if we are an implement, we need to get our attacherVehicle 174 | if self.getAttacherVehicle ~= nil then 175 | local attacherVehicle = self:getAttacherVehicle() 176 | -- check if attacherVehicle has simpleIC 177 | if attacherVehicle ~= nil and attacherVehicle.spec_simpleIC ~= nil and attacherVehicle.spec_simpleIC.hasIC then 178 | -- run through implements attached to attacherVehicle to find out attacherIndex 179 | for _, implement in pairs(attacherVehicle.spec_attacherJoints.attachedImplements) do 180 | if implement.object == self then 181 | local implementControl = attacherVehicle.sic_at.attacherIndex_implementControl[implement.jointDescIndex]; 182 | if implementControl ~= nil and implementControl.isType["fold"] and implementControl.leverAnimation ~= nil then 183 | -- run animation 184 | attacherVehicle:playAnimation(implementControl.leverAnimation.animationName, direction, attacherVehicle:getAnimationTime(implementControl.leverAnimation.animationName), true); 185 | implementControl.leverAnimation.dirtySelf = self; 186 | end; 187 | end; 188 | end; 189 | end; 190 | end; 191 | 192 | return returnValues; 193 | end; 194 | Foldable.setFoldState = Utils.overwrittenFunction(Foldable.setFoldState, sic_implementControl.setFoldStateAppend); -------------------------------------------------------------------------------- /FS19_simpleIC/registerSimpleIC.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | Changelog 3 | ##### V 0.9.3.4 4 | - Hotfix game not loading without EnhancedVehicle active 5 | ##### V 0.9.3.3 6 | - implementControl leverAnimation fix, addition of returnToCenter, returnToCenterLowered, returnToCenterRaised 7 | - fixed ptoControl for mods using attacherIndex instead of attacherIndices (pre 0.9.3.2 mods) 8 | - drivableControl initial implementation (not done yet) 9 | ##### V 0.9.3.2 10 | - simpleIC_implementBalls.lua:207: attempt to index field 'vehicle' fixed (at least I hope that did the trick, I assume this error comes from synch issues not actual bug in script) 11 | - code cleanup ptoControl 12 | - addition of implementControl though not fully implemented yet (thus no example in examples.xml yet) 13 | - addition of disableInvisibleTriggers attribute if you want to disable triggerPoints via visibility. Was on by default in previous versions since implementation. 14 | But when people converted old IC to simpleIC they just set the old buttons to invisible and used the index for IC, now all of those didn't work anymore. 15 | So now by default it SimpleIC doesn't care about visibilty but if you want to disable triggerPoints via visibility you can do that by adding this attribute. (see Examples XML) 16 | ##### V 0.9.3.1 17 | - added motorStartControl and animations 18 | ## V 0.9.3.0 19 | - lightControl leverAnimations for all types added and synched 20 | - debugPrints removed 21 | ## V 0.9.2.9 22 | - implementBalls synch Issue possible fix 23 | - lightControl addition (leverAnimations still to do) 24 | ## V 0.9.2.8 25 | - some bugfixes for the recent additions 26 | ## V 0.9.2.7 27 | - separated attacherControl into seperate lua 28 | - synchronized attacherControl with setJointMoveDown so its always in synch 29 | - fixed doNotSynch for ptoControl 30 | - added returnToCenter, returnToCenterRaised, returnToCenterLowered for attacherControl leverAnimation 31 | ## V 0.9.2.6 32 | - separated ptoControl into seperate lua 33 | - synchronized ptoControl animation with turnOnVehicle so its always in synch 34 | - fixed version numbering mishap (yes this is 0.9.2.6 not 0.9.1.6) 35 | ## V 0.9.2.5 36 | - added attacherControl 37 | - added ptoControl 38 | ## V 0.9.2.4 39 | - made icFunctions more universally usable for future additions 40 | - icFunction can be "turned off" by setting visibility of triggerPoint to false 41 | ## V 0.9.2.3 42 | - MP Server fix of vehicle not enterable 43 | ## V 0.9.2.2 44 | - fix for line 102 error I think, couldn't reproduce but I found an issue with the table indexing and I think it worked fine for most mods but some may have caused the issue to show. 45 | ## V 0.9.2.1 46 | - possibly fixed dedicated server issue 47 | - removed "cylinder" debug finally 48 | ## V 0.9.2.0 49 | - addition of SimpleIC-ImplementBalls 50 | ## V 0.9.1.9 51 | - fixed the issue introduced in the version before last version and partially fixed in the last version. Now fixed completely. I hope.. again. 52 | ## V 0.9.1.8 53 | - fixed Issue introduced in the last version of indoor buttons only working when the ingame-menu is on, fully removed issue with double-mapping of mouseButtons I hope 54 | ## V 0.9.1.7 55 | - fixed Error: simpleIC.lua:488: attempt to index field 'spec_motorized' (a nil value) 56 | - fixed Error: simpleIC.lua:318: attempt to call method 'getAttacherVehicle' (a nil value) 57 | - reachDistance can be set per vehicle (optional, default 1.8) to specify how far away a player can reach an IC-point 58 | ## V 0.9.1.6 59 | - fixed spec insertion so simpleIC now works in every implement, trailer etc. not just drivables 60 | ## V 0.9.1.5 61 | - fixed Error: simpleIC.lua:248: attempt to index local 'spec' (a nil value) 62 | - added cylinderAnimation for easy animation of struts on windows/doors etc. 63 | ## V 0.9.1.4 64 | - fixed IC active on all vehicles bug (now only active if vehicle actually has IC functions) 65 | - fixed bug Error: Running LUA method 'update' simpleIC.lua:292: attempt to index a nil value 66 | - added default keymapping 67 | ## V 0.9.1.3 68 | - multiplayer fix 69 | - added triggerPoint_ON and triggerPoint_OFF as alternative to toggle via triggerPoint 70 | 71 | 72 | ]] 73 | 74 | 75 | registerSimpleIC = {}; 76 | 77 | registerSimpleIC.version = "0.9.2.6" 78 | 79 | local modName = g_currentModName; 80 | local modDirectory = g_currentModDirectory; 81 | 82 | function init() 83 | VehicleTypeManager.validateVehicleTypes = Utils.prependedFunction(VehicleTypeManager.validateVehicleTypes, validateVehicleTypes) 84 | print("SimpleIC Version "..registerSimpleIC.version.." init."); 85 | end 86 | 87 | 88 | function validateVehicleTypes(vehicleTypeManager) 89 | registerSimpleIC.installSpecializations(g_vehicleTypeManager, g_specializationManager, modDirectory, modName) 90 | end 91 | 92 | 93 | function registerSimpleIC.installSpecializations(vehicleTypeManager, specializationManager, modDirectory, modName) 94 | specializationManager:addSpecialization("sic_drivableControl", "sic_drivableControl", modDirectory.."sic_drivableControl.lua", nil) 95 | specializationManager:addSpecialization("sic_implementControl", "sic_implementControl", modDirectory.."sic_implementControl.lua", nil) 96 | specializationManager:addSpecialization("sic_motorStartControl", "sic_motorStartControl", modDirectory.."sic_motorStartControl.lua", nil) 97 | specializationManager:addSpecialization("sic_lightControl", "sic_lightControl", modDirectory.."sic_lightControl.lua", nil) 98 | specializationManager:addSpecialization("sic_attacherControl", "sic_attacherControl", modDirectory.."sic_attacherControl.lua", nil) 99 | specializationManager:addSpecialization("sic_ptoControl", "sic_ptoControl", modDirectory.."sic_ptoControl.lua", nil) 100 | specializationManager:addSpecialization("simpleIC", "simpleIC", modDirectory.."simpleIC.lua", nil) 101 | specializationManager:addSpecialization("simpleIC_implementBalls", "simpleIC_implementBalls", modDirectory.."simpleIC_implementBalls.lua", nil) 102 | 103 | 104 | for typeName, typeEntry in pairs(vehicleTypeManager:getVehicleTypes()) do 105 | 106 | if typeName ~= "horse" and typeName ~= "pallet" then -- ignore pallets and horse 107 | -- add simpleIC to everything except locomotives 108 | if not SpecializationUtil.hasSpecialization(Locomotive, typeEntry.specializations) then 109 | vehicleTypeManager:addSpecialization(typeName, modName .. ".sic_drivableControl") 110 | vehicleTypeManager:addSpecialization(typeName, modName .. ".sic_implementControl") 111 | vehicleTypeManager:addSpecialization(typeName, modName .. ".sic_motorStartControl") 112 | vehicleTypeManager:addSpecialization(typeName, modName .. ".sic_lightControl") 113 | vehicleTypeManager:addSpecialization(typeName, modName .. ".sic_attacherControl") 114 | vehicleTypeManager:addSpecialization(typeName, modName .. ".sic_ptoControl") 115 | vehicleTypeManager:addSpecialization(typeName, modName .. ".simpleIC") 116 | print("inserted simpleIC to "..tostring(typeName)); 117 | if SpecializationUtil.hasSpecialization(Attachable, typeEntry.specializations) then 118 | vehicleTypeManager:addSpecialization(typeName, modName .. ".simpleIC_implementBalls") 119 | print("inserted simpleIC_implementBalls to "..tostring(typeName)); 120 | end; 121 | end; 122 | end 123 | end 124 | 125 | end 126 | 127 | init() 128 | 129 | -- FIX for double-mapping of mouse buttons by Stephan-S 130 | function registerSimpleIC:mouseEvent(posX, posY, isDown, isUp, button) 131 | if isUp or isDown then 132 | --Check if this is the key assigned to INTERACT 133 | local action = g_inputBinding:getActionByName("INTERACT_IC_VEHICLE"); 134 | for _, binding in ipairs(action.bindings) do 135 | if binding.axisNames[1] ~= nil and binding.axisNames[1] == Input.mouseButtonIdToIdName[button] then 136 | local vehicle = g_currentMission.controlledVehicle 137 | if vehicle ~= nil and vehicle.spec_simpleIC ~= nil then 138 | if isDown then 139 | vehicle.spec_simpleIC.interact_present = true; 140 | if not vehicle.spec_simpleIC.interact_default then 141 | vehicle:doInteraction() 142 | end; 143 | elseif isUp then 144 | vehicle.spec_simpleIC.interact_present = false; 145 | end 146 | end 147 | end 148 | end 149 | end; 150 | end; 151 | 152 | function registerSimpleIC:update(dt) 153 | if g_currentMission.simpleIC_implementBalls ~= nil then -- check if we have implementBalls active 154 | --print("simpleIC_implementBalls not nil") 155 | if g_currentMission.controlPlayer and g_currentMission.player ~= nil and not g_gui:getIsGuiVisible() then -- check if we are the player and no GUI is open 156 | --print("run player") 157 | local x, y, z = getWorldTranslation(g_currentMission.player.rootNode); -- get player pos 158 | for index, spec in pairs(g_currentMission.simpleIC_implementBalls) do -- run through all implementBalls specs 159 | for _, implementJoint in pairs(spec.implementJoints) do -- run through all inputAttachers with implement type of this spec 160 | local aX, aY, aZ = getWorldTranslation(implementJoint.node) -- get pos of implement joint node 161 | 162 | local distance = MathUtil.vector3Length(x - aX, y - aY, z - aZ); -- get distance to player 163 | 164 | --print("distance: "..tostring(distance)) 165 | 166 | if distance < spec.maxDistance then -- if we're close enough activate stuffs 167 | -- if we're in distance, show the X and activate inputBinding 168 | implementJoint.showX = true; 169 | spec.vehicle:raiseActive() 170 | 171 | if not spec.isInputActive then 172 | local specSIC = spec.vehicle.spec_simpleIC; 173 | specSIC.actionEvents = {}; -- create actionEvents table since in case we didn't enter the vehicle yet it does not exist 174 | spec.vehicle:clearActionEventsTable(specSIC.actionEvents); -- also clear it for good measure 175 | local _ , eventId = spec.vehicle:addActionEvent(specSIC.actionEvents, InputAction.INTERACT_IC_ONFOOT, spec.vehicle, simpleIC.INTERACT, false, true, false, true); -- now add the actionEvent 176 | spec.isInputActive = true; 177 | end; 178 | else 179 | if spec.isInputActive then 180 | spec.vehicle:removeActionEvent(spec.vehicle.spec_simpleIC.actionEvents, InputAction.INTERACT_IC_ONFOOT); 181 | spec.isInputActive = false; 182 | implementJoint.showX = false; 183 | end; 184 | end; 185 | end; 186 | end; 187 | end; 188 | end; 189 | 190 | end; 191 | 192 | addModEventListener(registerSimpleIC) -------------------------------------------------------------------------------- /FS19_simpleIC/sic_attacherControl.lua: -------------------------------------------------------------------------------- 1 | -- SimpleIC Attacher Control 2 | -- to control lowering/raising of implements attached to vehicles attacher 3 | 4 | sic_attacherControl = {}; 5 | 6 | function sic_attacherControl.prerequisitesPresent(specializations) 7 | return true; 8 | end; 9 | 10 | function sic_attacherControl.registerEventListeners(vehicleType) 11 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", sic_attacherControl); 12 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", sic_attacherControl); 13 | SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", sic_attacherControl); 14 | 15 | --SpecializationUtil.registerEventListener(vehicleType, "onPreAttachImplement", sic_attacherControl); 16 | --SpecializationUtil.registerEventListener(vehicleType, "onPreDetachImplement", sic_attacherControl); 17 | end; 18 | 19 | function sic_attacherControl:onPreDetachImplement(implement) 20 | for _ , icFunction in pairs(self.spec_simpleIC.icFunctions) do 21 | if icFunction.attacherControl ~= nil and icFunction.attacherControl.attacherIndex == implement.jointDescIndex then 22 | icFunction.attacherControl.isImplementAttached = false; 23 | 24 | if icFunction.leverAnimation ~= nil then 25 | local leverAnimation = icFunction.leverAnimation; 26 | 27 | local animTime = self:getAnimationTime(leverAnimation.animationName); 28 | 29 | if animTime > leverAnimation.detachedAnimTime then 30 | self:playAnimation(leverAnimation.animationName, -1, animTime, true); 31 | self:setAnimationStopTime(leverAnimation.animationName, leverAnimation.detachedAnimTime); 32 | elseif animTime < leverAnimation.detachedAnimTime then 33 | self:playAnimation(leverAnimation.animationName, 1, animTime, true); 34 | self:setAnimationStopTime(leverAnimation.animationName, leverAnimation.detachedAnimTime); 35 | end; 36 | end; 37 | end; 38 | end; 39 | end; 40 | 41 | function sic_attacherControl:onPreAttachImplement(inputJointDescIndex, jointDescIndex) 42 | 43 | local jointDesc = self.spec_attacherJoints.attacherJoints[jointDescIndex] 44 | 45 | for _ , icFunction in pairs(self.spec_simpleIC.icFunctions) do 46 | if icFunction.attacherControl ~= nil and icFunction.attacherControl.attacherIndex == jointDescIndex then 47 | icFunction.attacherControl.isImplementAttached = true; 48 | 49 | if icFunction.leverAnimation ~= nil then 50 | local leverAnimation = icFunction.leverAnimation; 51 | 52 | local animTime = self:getAnimationTime(leverAnimation.animationName); 53 | self:playAnimation(leverAnimation.animationName, -1, animTime, true); 54 | end; 55 | end; 56 | end; 57 | 58 | end; 59 | 60 | function sic_attacherControl:onLoad(savegame) 61 | self.loadAttacherControl = sic_attacherControl.loadAttacherControl; 62 | self.setAttacherControl = sic_attacherControl.setAttacherControl; 63 | 64 | self.sic_attacherControl = {}; 65 | 66 | self.sic_attacherControl.attacherIndexToICFunction = {}; 67 | 68 | end; 69 | 70 | function sic_attacherControl:onPostLoad(savegame) 71 | for _ , icFunction in pairs(self.spec_simpleIC.icFunctions) do 72 | if icFunction.attacherControl and icFunction.attacherControl.leverAnimation ~= nil and not icFunction.attacherControl.isImplementAttached then 73 | local leverAnimation = icFunction.attacherControl.leverAnimation; 74 | local animTime = self:getAnimationTime(leverAnimation.animationName); 75 | if animTime > leverAnimation.detachedAnimTime then 76 | self:playAnimation(leverAnimation.animationName, -1, animTime, true); 77 | self:setAnimationStopTime(leverAnimation.animationName, leverAnimation.detachedAnimTime); 78 | elseif animTime < leverAnimation.detachedAnimTime then 79 | self:playAnimation(leverAnimation.animationName, 1, animTime, true); 80 | self:setAnimationStopTime(leverAnimation.animationName, leverAnimation.detachedAnimTime); 81 | end; 82 | end; 83 | end; 84 | end; 85 | 86 | 87 | function sic_attacherControl:onUpdateTick(dt) 88 | if self:getIsActive() then 89 | 90 | -- code for centering lever after lowering or raising depending on setting in XML 91 | for _ , icFunction in pairs(self.spec_simpleIC.icFunctions) do 92 | if icFunction.attacherControl and icFunction.attacherControl.leverAnimation ~= nil then 93 | if icFunction.attacherControl.attacherIndex ~= nil then 94 | local jointDesc = self.spec_attacherJoints.attacherJoints[icFunction.attacherControl.attacherIndex] 95 | 96 | if jointDesc.moveAlpha ~= nil then 97 | 98 | local moveAlpha = Utils.getMovedLimitedValue(jointDesc.moveAlpha, jointDesc.lowerAlpha, jointDesc.upperAlpha, jointDesc.moveTime, dt, not jointDesc.moveDown) 99 | 100 | local leverAnimation = icFunction.attacherControl.leverAnimation; 101 | 102 | if moveAlpha ~= leverAnimation.moveAlpha then 103 | if (moveAlpha == 1 or moveAlpha == 0) and leverAnimation.returnToCenter then 104 | 105 | local animTime = self:getAnimationTime(leverAnimation.animationName); 106 | if animTime > 0.5 then 107 | self:playAnimation(leverAnimation.animationName, -1, animTime, true); 108 | self:setAnimationStopTime(leverAnimation.animationName, 0.5) 109 | elseif animTime < 0.5 then 110 | self:playAnimation(leverAnimation.animationName, 1, animTime, true); 111 | self:setAnimationStopTime(leverAnimation.animationName, 0.5) 112 | end; 113 | 114 | elseif moveAlpha == 0 and leverAnimation.returnToCenterRaised then 115 | local animTime = self:getAnimationTime(leverAnimation.animationName); 116 | 117 | if animTime < 0.5 then 118 | self:playAnimation(leverAnimation.animationName, 1, animTime, true); 119 | self:setAnimationStopTime(leverAnimation.animationName, 0.5) 120 | end; 121 | elseif moveAlpha == 1 and leverAnimation.returnToCenterLowered then 122 | local animTime = self:getAnimationTime(leverAnimation.animationName); 123 | 124 | if animTime > 0.5 then 125 | self:playAnimation(leverAnimation.animationName, -1, animTime, true); 126 | self:setAnimationStopTime(leverAnimation.animationName, 0.5) 127 | end; 128 | end; 129 | leverAnimation.moveAlpha = moveAlpha; 130 | end; 131 | end; 132 | end; 133 | end; 134 | end; 135 | end; 136 | end; 137 | 138 | 139 | 140 | 141 | 142 | function sic_attacherControl:loadAttacherControl(key, table) 143 | -- load attacherControl attributes 144 | local attacherControl = {} 145 | attacherControl.attacherIndex = getXMLInt(self.xmlFile, key.."#attacherIndex"); 146 | if attacherControl.attacherIndex ~= nil then 147 | 148 | local animationName = getXMLString(self.xmlFile, key..".leverAnimation#animationName"); 149 | if animationName ~= "" and animationName ~= nil then 150 | 151 | attacherControl.leverAnimation = {}; 152 | attacherControl.leverAnimation.animationName = animationName; 153 | attacherControl.leverAnimation.doNotSynch = getXMLBool(self.xmlFile, key..".leverAnimation#doNotSynch"); 154 | 155 | attacherControl.leverAnimation.returnToCenter = getXMLBool(self.xmlFile, key..".leverAnimation#returnToCenter"); 156 | attacherControl.leverAnimation.returnToCenterRaised = getXMLBool(self.xmlFile, key..".leverAnimation#returnToCenterRaised"); 157 | attacherControl.leverAnimation.returnToCenterLowered = getXMLBool(self.xmlFile, key.."leverAnimation#returnToCenterLowered"); 158 | 159 | attacherControl.leverAnimation.detachedAnimTime = Utils.getNoNil(getXMLFloat(self.xmlFile, key..".leverAnimation#detachedAnimTime"), 1); 160 | 161 | attacherControl.leverAnimation.moveAlpha = 0.5; 162 | 163 | end; 164 | 165 | self.sic_attacherControl.attacherIndexToICFunction[attacherControl.attacherIndex] = table; 166 | 167 | table.attacherControl = attacherControl; 168 | return true; 169 | end; 170 | 171 | end; 172 | 173 | function sic_attacherControl:setAttacherControl(wantedState, i) 174 | -- gets called when IC trigger is triggered 175 | local attacherControl = self.spec_simpleIC.icFunctions[i].attacherControl; 176 | local spec_attacherJoints = self.spec_attacherJoints; 177 | 178 | if spec_attacherJoints.attacherJoints[attacherControl.attacherIndex] ~= nil then 179 | if wantedState == nil then 180 | wantedState = not spec_attacherJoints.attacherJoints[attacherControl.attacherIndex].moveDown 181 | end; 182 | 183 | self:setJointMoveDown(attacherControl.attacherIndex, wantedState) 184 | 185 | if attacherControl.leverAnimation ~= nil and attacherControl.leverAnimation.doNotSynch then 186 | local speed = 1; 187 | if not wantedState then 188 | speed = -1; 189 | end; 190 | self:playAnimation(attacherControl.leverAnimation.animationName, speed, self:getAnimationTime(attacherControl.leverAnimation.animationName), true); 191 | end; 192 | 193 | end; 194 | end; 195 | 196 | function sic_attacherControl.setJointMoveDownAppend(self, superFunc, jointDescIndex, moveDown, noEventSend) 197 | 198 | superFunc(self, jointDescIndex, moveDown, noEventSend); 199 | 200 | -- code for running animation if attacher is raised/lowered 201 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 202 | for _ , icFunction in pairs(self.spec_simpleIC.icFunctions) do 203 | if icFunction.attacherControl and icFunction.attacherControl.leverAnimation ~= nil then 204 | if icFunction.attacherControl.attacherIndex == jointDescIndex then 205 | local speed = -1; 206 | if moveDown then 207 | speed = 1; 208 | end; 209 | self:playAnimation(icFunction.attacherControl.leverAnimation.animationName, speed, self:getAnimationTime(icFunction.attacherControl.leverAnimation.animationName), true); 210 | end; 211 | end; 212 | end; 213 | end; 214 | 215 | end; 216 | AttacherJoints.setJointMoveDown = Utils.overwrittenFunction(AttacherJoints.setJointMoveDown, sic_attacherControl.setJointMoveDownAppend); 217 | -------------------------------------------------------------------------------- /examples.xml: -------------------------------------------------------------------------------- 1 | 2 | A few examples on how to set up simpleIC in XML. 3 | The first is the "bare minimum" XML code needed to get simpleIC working. 4 | I have not included the lines used to animate the windows/doors etc. since that is universal as it uses basegame animations. 5 | If you don't know how to do that, look at a tutorial which explains that or ask someone (after you tried to figure out yourself of course). 6 | The second example is the current "normal" code with all the optional stuff. 7 | The third example is using triggerPoint_ON and triggerPoint_OFF which means that instead of one "X" to click as a toggle you've got two marks, one that will 8 | "open" the animation, the other one that will close the "animation". 9 | The triggerPoint stuff is global to the script so if I add features in future that aren't controlling animations, it is still "valid" to use off and on points. 10 | 11 | For an example on how it is actually implemented in a mod, take a look at my Deutz Agrostar Edit I linked in the Readme on Github. (youtube video link) 12 | 13 | 14 | ## absolute basic IC function animation lines: 15 | ______________________________________________ 16 | -- animationName -> name of the animation controlled by this IC function 17 | -- insideTrigger triggerPoint -> index of the transform group that marks the trigger point 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | ## advanced IC function animation lines with optional stuff: 26 | ____________________________________________________________ 27 | -- outsideInteractionTrigger -> index of the playerTrigger for outside (onFoot) interaction 28 | -- animationName -> name of the animation controlled by this IC function 29 | -- animationSpeed -> speed the animation is played at (can also be negative) 30 | -- sharedAnimation -> not yet implemented 31 | -- soundVolumeIncreasePercentage -> percentage by which the volume will increase from insideVolume to outsideVolume if this animation is "on" e.g. door/window is open 32 | -- insideTrigger -> line for the triggerPoints that can be triggered from inside camera 33 | -- outsideTrigger -> line for the triggerPoints that can be triggered from outside camera or player on foot 34 | -- triggerPoint -> index of the transform group that marks the particular trigger point 35 | -- triggerPointSize -> radius around the triggerPoint in cm where it still registeres as triggered 36 | -- reachDistance -> the max. distance to which a player can reach an IC point (indoor camera only, default 1.8) 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | ## advanced IC function animation lines with optional 2 trigger points (on and off point instead of toggle): 45 | ____________________________________________________________________________________________________________ 46 | -- all the same as the one above except: 47 | -- triggerPoint_ON -> index of the transform group that marks the particular triggerPoint for turning the IC function "on" (animation playing towards end) 48 | -- triggerPoint_OFF -> index of the transform group that marks the particular triggerPoint for turning the IC function "off" (animation playing towards start) 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ## IC function with easy cylinder animation: 58 | ____________________________________________ 59 | -- as for the cylinderAnimations: 60 | -- node1 -> index of one of the nodes for the cylinder 61 | -- node2 -> index of the other node for the cylinder 62 | ---------------------------------------------------- 63 | cylinder-nodes are directed at each other, meaning the other part is also acting as reference point. 64 | In order for this to work, the cylinder-parts pivot has to be oriented in a way that the positive Z axis points the direction the cylinder-part points at. 65 | The "rotation" axis is the Y axis (green axis) 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | ## Enabling triggerPoints being disabled when triggerPoint transform-group is invisible 79 | -- disableInvisibleTriggers -> set to true will enable this feature. Default is off to keep backwards-compatible with older mods 80 | 81 | [...] 82 | 83 | 84 | 85 | 86 | ## IC function motorStartControl: 87 | There is no attributes for motorStartControl itself 88 | -- (optional) leverAnimation -> animation to be played for a lever/button. Animation is synchronized with motor start/stop state via other inputs. Animation will play from 0-1 if engine is started and 1-0 if engine is stopped unless isTurnKeyAnimation is true 89 | -- animationName -> name of animation 90 | -- (optional) isTurnKeyAnimation -> if this is set to true the animation will play from 0 to 1 and then back to 0.7 when motor is started. Back to 0 when motor is stopped. 91 | To animate starting via key as you turn it to start position and then let go to run position. 92 | -- (optional) leverAnimationStart -> animation to be played when engine is started. Animation will be played from 0-1 and then back to 0. 93 | -- animationName -> name of animation 94 | -- (optional) leverAnimationStop -> animation to be played when engine is stopped. Animation will be played from 0-1 and then back to 0. 95 | -- animationName -> name of animation 96 | -- triggerPoints work exactly the same as for animations (see above), so you can do triggerPoint_ON and triggerPoint_OFF for starting and stopping engine on different controls 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | ## IC function attacherControl: 106 | -- attacherIndices -> Indices of Attacher in XML (same as for powerTakeoffConfigurations, hoses etc.) you can add multiple attacherIndices because you have one pto for multiple attachers (implement, trailer, low..) 107 | -- (attacherIndex -> depreceated, don't use anymore. Use attacherIndices instead. It still works to be backwards-compatible but better to use attacherIndices) 108 | -- (optional) leverAnimation -> animation to be played for a lever/button, animation is synchronized to lowered/raised state of attacher 109 | -- animationName -> name of animation for leverAnimation 110 | -- (optional) doNotSynch -> don't synch lever animation to inputs with key-binding instead of simpleIC. Not sure why you'd want that but its there if you need it 111 | -- (optional) returnToCenter -> lever will return to center of its animation when implement is finished raising or lowering 112 | -- (optional) returnToCenterRaised -> lever will return to center of its animation when implement is raised only 113 | -- (optional) returnToCenterLowered -> lever will return to center of its animation when implement is lowered only 114 | ! of course only one, either returnToCenter or returnToCenterLowered or returnToCenterRaised should be true anything else doesn't make sense and might not return the expected result ! 115 | -- triggerPoints work exactly the same as for animations (see above), so you can do triggerPoint_ON and triggerPoint_OFF for raising/lowering too 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | ## IC function ptoControl: 126 | -- attacherIndex -> Index of Attacher in XML in which the implement needs to be attached for this pto control to control it 127 | (Everything is the same as with attacherControl) 128 | -- (optional) leverAnimation, doNotSynch (optional) set to true if you don't want to synch animation with turning implement on/off with key-binding instead of IC 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | ## IC function lightControl 137 | This one is a bit more difficult to wrap your head around especially regarding the lever animations since the light Control System in FS19 is rather unintuitive. 138 | I will try my best to explain my thoughts as to how I made the leverAnimations work though. 139 | There are 5 different types of lightControl you can have. The 5 types are "state", "toggle", "turnLightLeft", "turnLightRight" and "turnLightHazard" each of which have their own attributes and animation-attributes. 140 | So I'm going to explain all 5 of them separately 141 | 142 | :: type "state" 143 | -- lightTypes -> the lightTypes this will turn on (see lightTypes in default lights section) in this example "1" is usually the rear worklight 144 | -- (optional) leverAnimation -> animation to be played for a lever/button, animation is synchronized with the lightTypes for this lightControl no matter how that lightType is turned on. (key-binding, IC, toggle) 145 | 146 | 147 | 148 | 149 | 150 | :: type "toggle" 151 | - no additional attributes for this. Toggle does the same as "F" key, it toggles through the lightTypes 152 | -- (optional) leverAnimation -> animation to be played for a lever/button. Animation is synchronized with stuff. See below 153 | -- animStopLightTypesN (replace n with number of lightType) animation will move to this animationTime if this lightType is currently on. Turns to the most significant lightTypes 154 | For example to animate a turn-dial switching between different lightTypes or a key that switches different lights when turned. 155 | If you don't want a lightType to influence the animation simply leave it out. For example just make animStopLightTypes2 and animStopLightTypes3 if you want that. 156 | If you don't quite understand how this works just try it out and see how it behaves. 157 | 158 | 159 | 160 | 161 | 162 | :: type "turnLightLeft" 163 | - no additional attributes for this. Just turning on/off left turnlight 164 | -- (optional) leverAnimation -> animation to be played for a lever/button. Animation is synchronized with stuff. See below 165 | -- (optional) isTurnlightSharedAnimation -> set this to true if the left and right turnlight use the same lever/button animation. 166 | Then the animation will default rest in 0.5 position, play to 0 position for right turnlight and 1 position for left turnlight. 167 | If you leave this away/false then the animation will just play to 1 if turnlight is on, 0 if its off. 168 | 169 | 170 | 171 | 172 | 173 | :: type "turnLightRight" (same as left) 174 | - no additional attributes for this. Just turning on/off left turnlight 175 | -- (optional) leverAnimation -> animation to be played for a lever/button. Animation is synchronized with stuff. See below 176 | -- (optional) isTurnlightSharedAnimation -> set this to true if the left and right turnlight use the same lever/button animation. 177 | Then the animation will default rest in 0.5 position, play to 0 position for right turnlight and 1 position for left turnlight. 178 | If you leave this away/false then the animation will just play to 1 if turnlight is on, 0 if its off. 179 | 180 | 181 | 182 | 183 | 184 | :: type "turnLightHazard" 185 | - no additional attributes for this. Just turning on/off left turnlight 186 | -- (optional) leverAnimation -> animation to be played for a lever/button. Animation is synchronized with hazards. Plays to 1 if on, 0 if off. Nothing special here. 187 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /FS19_simpleIC/sic_lightControl.lua: -------------------------------------------------------------------------------- 1 | -- SimpleIC Light Control 2 | -- to control Vehicle Lights via SimpleIC 3 | 4 | sic_lightControl = {}; 5 | 6 | function sic_lightControl.prerequisitesPresent(specializations) 7 | return true; 8 | end; 9 | 10 | function sic_lightControl.registerEventListeners(vehicleType) 11 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", sic_lightControl); 12 | end; 13 | 14 | 15 | function sic_lightControl:onLoad(savegame) 16 | self.loadLightControl = sic_lightControl.loadLightControl; 17 | self.setLightControl = sic_lightControl.setLightControl; 18 | self.getBinaryFromDecimal = sic_lightControl.getBinaryFromDecimal; 19 | end; 20 | 21 | 22 | function sic_lightControl:loadLightControl(key, table) 23 | 24 | local type = getXMLString(self.xmlFile, key.."#type"); 25 | if type ~= "" and type ~= nil then 26 | local lightControl = {}; 27 | lightControl.type = type; 28 | 29 | lightControl.lightTypes = { StringUtil.getVectorFromString(getXMLString(self.xmlFile, key.."#lightTypes")) } 30 | 31 | local animationName = getXMLString(self.xmlFile, key..".leverAnimation#animationName"); 32 | if animationName ~= "" and animationName ~= nil then 33 | lightControl.leverAnimation = {}; 34 | lightControl.leverAnimation.animationName = animationName; 35 | --lightControl.leverAnimation.doNotSynch = getXMLBool(self.xmlFile, key..".leverAnimation#doNotSynch"); 36 | lightControl.leverAnimation.isTurnlightSharedAnimation = getXMLBool(self.xmlFile, key..".leverAnimation#isTurnlightSharedAnimation") 37 | 38 | if lightControl.type == "toggle" then 39 | lightControl.leverAnimation.animStops = {}; 40 | for i = 1, self.spec_lights.numLightTypes do 41 | local animStop = getXMLFloat(self.xmlFile, key..".leverAnimation#animStopLightTypes"..i-1); 42 | if animStop ~= nil then 43 | lightControl.leverAnimation.animStops[i] = animStop; 44 | end; 45 | end; 46 | end; 47 | 48 | end; 49 | 50 | table.lightControl = lightControl; 51 | return true; 52 | end; 53 | 54 | 55 | end; 56 | 57 | function sic_lightControl:setLightControl(wantedState, i) 58 | local lightControl = self.spec_simpleIC.icFunctions[i].lightControl; 59 | 60 | if lightControl ~= nil then 61 | 62 | local spec = self.spec_lights; 63 | 64 | -- get type and set 65 | if lightControl.type == "state" and #lightControl.lightTypes ~= 0 then 66 | local lightsTypesMask = 0; 67 | for i=1, #lightControl.lightTypes do 68 | lightsTypesMask = bitXOR(spec.lightsTypesMask, 2^lightControl.lightTypes[i]); 69 | end; 70 | --print(tostring(lightsTypesMask)) 71 | self:setLightsTypesMask(lightsTypesMask); 72 | 73 | elseif lightControl.type == "toggle" then 74 | self:setNextLightsState(); 75 | 76 | elseif lightControl.type == "turnLightLeft" then 77 | local state = Lights.TURNLIGHT_OFF; 78 | if spec.turnLightState ~= Lights.TURNLIGHT_LEFT then 79 | state = Lights.TURNLIGHT_LEFT; 80 | end; 81 | if wantedState ~= nil then 82 | if wantedState then 83 | state = Lights.TURNLIGHT_LEFT; 84 | else 85 | state = Lights.TURNLIGHT_OFF; 86 | end; 87 | end; 88 | self:setTurnLightState(state); 89 | 90 | elseif lightControl.type == "turnLightRight" then 91 | local state = Lights.TURNLIGHT_OFF; 92 | if spec.turnLightState ~= Lights.TURNLIGHT_RIGHT then 93 | state = Lights.TURNLIGHT_RIGHT; 94 | end; 95 | if wantedState ~= nil then 96 | if wantedState then 97 | state = Lights.TURNLIGHT_RIGHT; 98 | else 99 | state = Lights.TURNLIGHT_OFF; 100 | end; 101 | end; 102 | self:setTurnLightState(state); 103 | 104 | elseif lightControl.type == "turnLightHazard" then 105 | local state = Lights.TURNLIGHT_OFF; 106 | if spec.turnLightState ~= Lights.TURNLIGHT_HAZARD then 107 | state = Lights.TURNLIGHT_HAZARD; 108 | end; 109 | if wantedState ~= nil then 110 | if wantedState then 111 | state = Lights.TURNLIGHT_HAZARD; 112 | else 113 | state = Lights.TURNLIGHT_OFF; 114 | end; 115 | end; 116 | self:setTurnLightState(state); 117 | 118 | elseif lightControl.type == "beaconLights" then 119 | wantedState = Utils.getNoNil(wantedState, not spec.beaconLightsActive) 120 | self:setBeaconLightsVisibility(not spec.beaconLightsActive); 121 | end; 122 | 123 | end; 124 | end; 125 | 126 | 127 | function sic_lightControl:getBinaryFromDecimal(decimal) 128 | local quot = decimal; 129 | local binary = {}; 130 | local i = 1; 131 | while quot ~= 0 do 132 | local remainder = math.fmod(quot, 2); 133 | quot = math.floor(quot / 2); 134 | binary[i] = remainder; 135 | i = i+1; 136 | end; 137 | return binary; 138 | end; 139 | 140 | function sic_lightControl.setLightsTypesMaskAppend(self, superFunc, lightsTypesMask, force, noEventSend) 141 | superFunc(self, lightsTypesMask, force, noEventSend); 142 | local spec = self.spec_lights; 143 | 144 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 145 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 146 | if icFunction.lightControl ~= nil and icFunction.lightControl.leverAnimation ~= nil then 147 | local lightControl = icFunction.lightControl; 148 | local binary = self:getBinaryFromDecimal(lightsTypesMask); 149 | 150 | if lightControl.type == "state" and #lightControl.lightTypes ~= 0 then 151 | local isOn = false; 152 | for i=1, #lightControl.lightTypes do 153 | if binary[lightControl.lightTypes[i]+1] ~= nil and binary[lightControl.lightTypes[i]+1] ~= 0 then 154 | --print("lightTypes("..i.."): "..tostring(lightControl.lightTypes[i])) 155 | --print("binary Index: "..tostring(binary[lightControl.lightTypes[i]]+1)) 156 | isOn = true; 157 | end; 158 | end; 159 | if isOn then 160 | self:playAnimation(lightControl.leverAnimation.animationName, 1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 161 | else 162 | self:playAnimation(lightControl.leverAnimation.animationName, -1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 163 | end; 164 | end; 165 | 166 | if lightControl.type == "toggle" then 167 | if #binary > 0 then 168 | 169 | local wantedStopTime = 0; 170 | 171 | for i = 1, spec.numLightTypes do 172 | if binary[i] ~= nil and binary[i] ~= 0 then 173 | if lightControl.leverAnimation.animStops[i] ~= nil then 174 | wantedStopTime = lightControl.leverAnimation.animStops[i]; 175 | end; 176 | end; 177 | end; 178 | 179 | if wantedStopTime ~= 0 then 180 | local animTime = self:getAnimationTime(lightControl.leverAnimation.animationName) 181 | if animTime > wantedStopTime then 182 | self:playAnimation(lightControl.leverAnimation.animationName, -1, animTime, true); 183 | self:setAnimationStopTime(lightControl.leverAnimation.animationName, wantedStopTime); 184 | elseif animTime < wantedStopTime then 185 | self:playAnimation(lightControl.leverAnimation.animationName, 1, animTime, true); 186 | self:setAnimationStopTime(lightControl.leverAnimation.animationName, wantedStopTime); 187 | end; 188 | end; 189 | else 190 | self:playAnimation(lightControl.leverAnimation.animationName, -1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 191 | end; 192 | end; 193 | end; 194 | end; 195 | end; 196 | 197 | end; 198 | Lights.setLightsTypesMask = Utils.overwrittenFunction(Lights.setLightsTypesMask, sic_lightControl.setLightsTypesMaskAppend); 199 | 200 | 201 | -- beaconlights animation 202 | function sic_lightControl.setBeaconLightsVisibilityAppend(self, superFunc, visibility, force, noEventSend) 203 | superFunc(self, visibility, force, noEventSend); 204 | 205 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 206 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 207 | if icFunction.lightControl ~= nil and icFunction.lightControl.leverAnimation ~= nil then 208 | if icFunction.lightControl.type == "beaconLights" then 209 | if visibility then 210 | self:playAnimation(lightControl.leverAnimation.animationName, 1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 211 | else 212 | self:playAnimation(lightControl.leverAnimation.animationName, -1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 213 | end; 214 | end; 215 | end; 216 | end; 217 | end; 218 | end; 219 | Lights.setBeaconLightsVisibility = Utils.overwrittenFunction(Lights.setBeaconLightsVisibility, sic_lightControl.setBeaconLightsVisibilityAppend); 220 | 221 | 222 | -- Turnlight Animations 223 | function sic_lightControl.setTurnLightStateAppend(self, superFunc, state, force, noEventSend) 224 | superFunc(self, state, foce, noEventSend); 225 | 226 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 227 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 228 | if icFunction.lightControl ~= nil and icFunction.lightControl.leverAnimation ~= nil then 229 | local lightControl = icFunction.lightControl; 230 | 231 | local isSharedAnim = lightControl.leverAnimation.isTurnlightSharedAnimation; 232 | 233 | -- animation is shared for left and right turnlight 234 | if isSharedAnim then 235 | if lightControl.type == "turnLightLeft" then 236 | if state == Lights.TURNLIGHT_LEFT then 237 | self:playAnimation(lightControl.leverAnimation.animationName, 1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 238 | elseif state == Lights.TURNLIGHT_OFF then 239 | local animTime = self:getAnimationTime(lightControl.leverAnimation.animationName); 240 | if animTime > 0.5 then 241 | self:playAnimation(lightControl.leverAnimation.animationName, -1, animTime, true); 242 | self:setAnimationStopTime(lightControl.leverAnimation.animationName, 0.5); 243 | end; 244 | end; 245 | end; 246 | if lightControl.type == "turnLightRight" then 247 | if state == Lights.TURNLIGHT_RIGHT then 248 | self:playAnimation(lightControl.leverAnimation.animationName, -1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 249 | elseif state == Lights.TURNLIGHT_OFF then 250 | local animTime = self:getAnimationTime(lightControl.leverAnimation.animationName); 251 | if animTime < 0.5 then 252 | self:playAnimation(lightControl.leverAnimation.animationName, 1, animTime, true); 253 | self:setAnimationStopTime(lightControl.leverAnimation.animationName, 0.5); 254 | end; 255 | end; 256 | end; 257 | else -- if animation isn't shared animate normally and independently 258 | if lightControl.type == "turnLightLeft" then 259 | if state == Lights.TURNLIGHT_LEFT then 260 | self:playAnimation(lightControl.leverAnimation.animationName, 1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 261 | elseif state == Lights.TURNLIGHT_OFF then 262 | self:playAnimation(lightControl.leverAnimation.animationName, -1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 263 | end; 264 | end; 265 | if lightControl.type == "turnLightRight" then 266 | if state == Lights.TURNLIGHT_RIGHT then 267 | self:playAnimation(lightControl.leverAnimation.animationName, 1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 268 | elseif state == Lights.TURNLIGHT_OFF then 269 | self:playAnimation(lightControl.leverAnimation.animationName, -1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 270 | end; 271 | end; 272 | end; 273 | 274 | -- hazards animation is independent 275 | if lightControl.type == "turnLightHazard" then 276 | if state == Lights.TURNLIGHT_HAZARD then 277 | self:playAnimation(lightControl.leverAnimation.animationName, 1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 278 | elseif state == Lights.TURNLIGHT_OFF then 279 | self:playAnimation(lightControl.leverAnimation.animationName, -1, self:getAnimationTime(lightControl.leverAnimation.animationName), true); 280 | end; 281 | end; 282 | end; 283 | end; 284 | end; 285 | 286 | end; 287 | Lights.setTurnLightState = Utils.overwrittenFunction(Lights.setTurnLightState, sic_lightControl.setTurnLightStateAppend); -------------------------------------------------------------------------------- /FS19_simpleIC/simpleIC.lua: -------------------------------------------------------------------------------- 1 | -- by modelleicher 2 | -- 13.04.2019 3 | 4 | -- Script for Interactive Control. Released on Github January 2020. 5 | 6 | 7 | simpleIC = {}; 8 | 9 | function simpleIC.prerequisitesPresent(specializations) 10 | return true; 11 | end; 12 | 13 | 14 | 15 | function simpleIC.registerEventListeners(vehicleType) 16 | SpecializationUtil.registerEventListener(vehicleType, "onLoad", simpleIC); 17 | SpecializationUtil.registerEventListener(vehicleType, "onDraw", simpleIC); 18 | SpecializationUtil.registerEventListener(vehicleType, "onUpdate", simpleIC); 19 | SpecializationUtil.registerEventListener(vehicleType, "onEnterVehicle", simpleIC); 20 | SpecializationUtil.registerEventListener(vehicleType, "onRegisterActionEvents", simpleIC); 21 | SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", simpleIC); 22 | SpecializationUtil.registerEventListener(vehicleType, "onDelete", simpleIC); 23 | SpecializationUtil.registerEventListener(vehicleType, "onPostLoad", simpleIC); 24 | SpecializationUtil.registerEventListener(vehicleType, "saveToXMLFile", simpleIC); 25 | SpecializationUtil.registerEventListener(vehicleType, "onReadStream", simpleIC); 26 | SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", simpleIC); 27 | end; 28 | 29 | 30 | function simpleIC.onRegisterActionEvents(self, isActiveForInput) 31 | local spec = self.spec_simpleIC; 32 | spec.actionEvents = {}; 33 | self:clearActionEventsTable(spec.actionEvents); 34 | 35 | if self:getIsActive() and self.spec_simpleIC.hasIC then 36 | self:addActionEvent(spec.actionEvents, InputAction.TOGGLE_ONOFF, self, simpleIC.TOGGLE_ONOFF, true, true, false, true, nil); 37 | if spec.icTurnedOn_inside then 38 | local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.INTERACT_IC_VEHICLE, self, simpleIC.INTERACT, true, true, false, true, nil); 39 | g_inputBinding:setActionEventTextVisibility(actionEventId, false); 40 | spec.interactionButtonActive = true; 41 | end; 42 | end; 43 | end; 44 | 45 | function simpleIC:onLoad(savegame) 46 | self.setICAnimation = simpleIC.setICAnimation; 47 | self.outsideInteractionTriggerCallback = simpleIC.outsideInteractionTriggerCallback; 48 | self.updateSoundAttributes = simpleIC.updateSoundAttributes; 49 | self.addSoundChangeIndex = simpleIC.addSoundChangeIndex; 50 | self.renderTextAtProjectedPosition = simpleIC.renderTextAtProjectedPosition; 51 | self.checkInteraction = simpleIC.checkInteraction; 52 | self.updateActionEventsSIC = simpleIC.updateActionEventsSIC; 53 | self.setICState = simpleIC.setICState; 54 | self.resetCanBeTriggered = simpleIC.resetCanBeTriggered; 55 | self.doInteraction = simpleIC.doInteraction; 56 | self.isCameraInsideCheck = simpleIC.isCameraInsideCheck; 57 | self.loadICAnimation = simpleIC.loadAnimation; 58 | self.loadICFunctions = simpleIC.loadICFunctions; 59 | 60 | 61 | self.spec_simpleIC = {}; 62 | 63 | local spec = self.spec_simpleIC; 64 | 65 | -- for now all we have is animations, no other types of functions 66 | spec.icFunctions = {}; 67 | 68 | -- load the animations from XML 69 | self:loadICFunctions("vehicle.simpleIC.animation", self.loadICAnimation); 70 | 71 | -- load attacherControl 72 | self:loadICFunctions("vehicle.simpleIC.attacherControl", self.loadAttacherControl); 73 | 74 | -- load pto control 75 | self:loadICFunctions("vehicle.simpleIC.ptoControl", self.loadPTOControl); 76 | 77 | -- load light control 78 | self:loadICFunctions("vehicle.simpleIC.lightControl", self.loadLightControl); 79 | 80 | -- load motor start control 81 | self:loadICFunctions("vehicle.simpleIC.motorStartControl", self.loadMotorStartControl); 82 | 83 | -- load implement control 84 | self:loadICFunctions("vehicle.simpleIC.implementControl", self.loadImplementControl); 85 | 86 | -- load drivable control 87 | self:loadICFunctions("vehicle.simpleIC.drivableControl", self.loadDrivableControl); 88 | 89 | if #spec.icFunctions > 0 then 90 | spec.hasIC = true; 91 | 92 | spec.disableInvisibleTriggers = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.simpleIC#disableInvisibleTriggers"), false); 93 | 94 | spec.outsideInteractionTrigger = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.simpleIC#outsideInteractionTrigger"), self.i3dMappings); 95 | 96 | spec.playerInOutsideInteractionTrigger = false; 97 | 98 | spec.interactionMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.simpleIC#interactionMarker"), self.i3dMappings) 99 | 100 | if spec.outsideInteractionTrigger ~= nil then 101 | spec.outsideInteractionTriggerId = addTrigger(spec.outsideInteractionTrigger, "outsideInteractionTriggerCallback", self); 102 | end; 103 | 104 | spec.soundVolumeIncreasePercentageAll = 1; 105 | spec.soundChangeIndexList = {}; 106 | 107 | spec.reachDistance = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.simpleIC#reachDistance"), 1.8) 108 | 109 | 110 | -- 111 | spec.icTurnedOn_inside = false; 112 | spec.icTurnedOn_outside = false; 113 | 114 | spec.icTurnedOn_inside_backup = false; 115 | 116 | spec.interact_present = false; 117 | spec.interact_default = false; 118 | 119 | if self.spec_motorized ~= nil then -- back up samples if we are a motorized vehicle 120 | for i, sample in pairs(self.spec_motorized.samples) do 121 | sample.indoorAttributes.volumeBackup = sample.indoorAttributes.volume; 122 | end; 123 | for i, sample in pairs(self.spec_motorized.motorSamples) do 124 | sample.indoorAttributes.volumeBackup = sample.indoorAttributes.volume; 125 | end; 126 | end; 127 | end; 128 | 129 | spec.cylinderAnimations = {}; 130 | local c = 0; 131 | while true do 132 | local node1 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.simpleIC.cylinderAnimations.cylinder("..c..")#node1"), self.i3dMappings) 133 | local node2 = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.simpleIC.cylinderAnimations.cylinder("..c..")#node2"), self.i3dMappings) 134 | if node1 ~= nil and node2 ~= nil then 135 | spec.cylinderAnimations[c+1] = {node1 = node1, node2 = node2} 136 | else 137 | break; 138 | end; 139 | 140 | c = c + 1; 141 | end; 142 | end; 143 | 144 | function simpleIC:loadICFunctions(keyOrig, loadFunc) 145 | local spec = self.spec_simpleIC; 146 | local i = 0; 147 | while true do 148 | local icFunction = {}; 149 | local hasFunction = false; 150 | local key = keyOrig.."("..i..")" 151 | 152 | hasFunction = loadFunc(self, key, icFunction); 153 | 154 | if hasFunction then 155 | icFunction.currentState = false; 156 | 157 | icFunction.inTP = {}; 158 | icFunction.inTP.triggerPoint = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key..".insideTrigger#triggerPoint"), self.i3dMappings); 159 | icFunction.inTP.triggerPointRadius = Utils.getNoNil(getXMLFloat(self.xmlFile, key..".insideTrigger#triggerPointSize"), 0.04); 160 | icFunction.inTP.triggerDistance = Utils.getNoNil(getXMLFloat(self.xmlFile, key..".insideTrigger#triggerDistance"), 1); 161 | 162 | if icFunction.inTP.triggerPoint == nil then 163 | icFunction.inTP.triggerPoint_ON = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key..".insideTrigger#triggerPoint_ON"), self.i3dMappings); 164 | icFunction.inTP.triggerPoint_OFF = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key..".insideTrigger#triggerPoint_OFF"), self.i3dMappings); 165 | end; 166 | 167 | 168 | icFunction.outTP = {}; 169 | icFunction.outTP.triggerPoint = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key..".outsideTrigger#triggerPoint"), self.i3dMappings); 170 | icFunction.outTP.triggerPointRadius = Utils.getNoNil(getXMLFloat(self.xmlFile, key..".outsideTrigger#triggerPointSize"), 0.04); 171 | icFunction.outTP.triggerDistance = Utils.getNoNil(getXMLFloat(self.xmlFile, key..".outsideTrigger#triggerDistance"), 1); 172 | 173 | if icFunction.outTP.triggerPoint == nil then 174 | icFunction.outTP.triggerPoint_ON = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key..".outsideTrigger#triggerPoint_ON"), self.i3dMappings); 175 | icFunction.outTP.triggerPoint_OFF = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, key..".outsideTrigger#triggerPoint_OFF"), self.i3dMappings); 176 | end; 177 | 178 | table.insert(spec.icFunctions, icFunction); 179 | else 180 | break; 181 | end; 182 | i = i+1; 183 | end; 184 | 185 | end; 186 | 187 | 188 | 189 | 190 | 191 | function simpleIC:loadAnimation(key, table) 192 | 193 | local anim = {}; 194 | anim.animationName = getXMLString(self.xmlFile, key.."#animationName"); 195 | if anim.animationName ~= "" and anim.animationName ~= nil then 196 | 197 | anim.animationSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#animationSpeed"), 1); 198 | anim.sharedAnimation = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#sharedAnimation"), false); 199 | anim.currentState = false; 200 | 201 | if not anim.sharedAnimation then 202 | self:playAnimation(anim.animationName, -anim.animationSpeed, self:getAnimationTime(anim.animationName), true); 203 | end; 204 | 205 | anim.duration = self:getAnimationDuration(anim.animationName); 206 | anim.soundVolumeIncreasePercentage = Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#soundVolumeIncreasePercentage"), false); 207 | 208 | table.animation = anim; 209 | return true; 210 | end; 211 | 212 | return false; 213 | end; 214 | 215 | function simpleIC:onEnterVehicle(isControlling, playerStyle, farmId) 216 | local spec = self.spec_simpleIC; 217 | if self.spec_enterable ~= nil and spec.hasIC then 218 | 219 | local inside = self:isCameraInsideCheck(); 220 | if inside then 221 | self:setICState(spec.icTurnedOn_inside, false); 222 | end; 223 | spec.lastCameraInside = Utils.getNoNil(inside, false); 224 | end; 225 | end; 226 | 227 | -- indoor camera -> IC is always on by default 228 | -- outdoor camera -> IC is on when button is pressed 229 | -- inside IC can be turned off 230 | -- marker turn of "globally" 231 | 232 | function simpleIC:onPostLoad(savegame) 233 | if self.spec_simpleIC ~= nil and savegame ~= nil and self.spec_simpleIC.hasIC then 234 | local spec = self.spec_simpleIC; 235 | local xmlFile = savegame.xmlFile; 236 | 237 | local i = 1; 238 | local key1 = savegame.key..".FS19_simpleIC.simpleIC.icFunctions" 239 | for _, icFunction in pairs(spec.icFunctions) do 240 | 241 | -- load animation state 242 | if icFunction.animation ~= nil then 243 | local state = getXMLBool(xmlFile, key1..".icFunction"..i.."#animationState"); 244 | if state ~= nil then 245 | self:setICAnimation(state, i, true) 246 | end; 247 | end; 248 | 249 | i = i+1; 250 | end; 251 | end; 252 | end; 253 | 254 | function simpleIC:saveToXMLFile(xmlFile, key) 255 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 256 | local spec = self.spec_simpleIC; 257 | 258 | local i = 1; 259 | local key1 = key..".icFunctions"; 260 | for _, icFunction in pairs(spec.icFunctions) do 261 | -- save animations state 262 | if icFunction.animation ~= nil then 263 | setXMLBool(xmlFile, key1..".icFunction"..i.."#animationState", icFunction.animation.currentState); 264 | end; 265 | i = i+1; 266 | end; 267 | 268 | end; 269 | end; 270 | 271 | function simpleIC:onReadStream(streamId, connection) 272 | local spec = self.simpleIC 273 | if spec ~= nil and spec.hasIC then 274 | if connection:getIsServer() then 275 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 276 | if icFunction.animation ~= nil then 277 | local state = streamReadBool(streamId) 278 | icFunction.animation.currentState = state; 279 | end; 280 | end; 281 | end; 282 | end; 283 | end 284 | 285 | function simpleIC:onWriteStream(streamId, connection) 286 | local spec = self.simpleIC; 287 | if spec ~= nil and spec.hasIC then 288 | if not connection:getIsServer() then 289 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 290 | if icFunction.animation ~= nil then 291 | streamWriteBool(streamId, icFunction.animation.currentState) 292 | end; 293 | end; 294 | end; 295 | end; 296 | end 297 | 298 | function simpleIC:onDelete() 299 | local spec = self.spec_simpleIC; 300 | if spec.outsideInteractionTrigger ~= nil then 301 | removeTrigger(spec.outsideInteractionTrigger) 302 | end; 303 | end; 304 | 305 | function simpleIC:INTERACT(actionName, inputValue) 306 | if inputValue > 0.5 then 307 | self.spec_simpleIC.interact_default = true; 308 | if not self.spec_simpleIC.interact_present then 309 | self:doInteraction() 310 | end; 311 | else 312 | self.spec_simpleIC.interact_default = false; 313 | end; 314 | end; 315 | 316 | 317 | 318 | function simpleIC:doInteraction() 319 | local spec = self.spec_simpleIC; 320 | 321 | if spec ~= nil and spec.hasIC then 322 | if spec.icTurnedOn_inside or spec.icTurnedOn_outside or spec.playerInOutsideInteractionTrigger then 323 | local i = 1; 324 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do 325 | if icFunction.canBeTriggered then 326 | -- trigger animation 327 | if icFunction.animation ~= nil then 328 | self:setICAnimation(not icFunction.animation.currentState, i); 329 | end; 330 | 331 | if icFunction.attacherControl ~= nil then 332 | self:setAttacherControl(nil, i) 333 | end; 334 | 335 | if icFunction.ptoControl ~= nil then 336 | self:setPTOControl(nil, i) 337 | end; 338 | 339 | if icFunction.lightControl ~= nil then 340 | self:setLightControl(nil, i) 341 | end; 342 | 343 | if icFunction.motorStartControl ~= nil then 344 | self:setMotorStartControl(nil, i) 345 | end; 346 | 347 | if icFunction.implementControl ~= nil then 348 | self:setImplementControl(nil, i) 349 | end; 350 | if icFunction.drivableControl ~= nil then 351 | self:setDrivableControl(nil, i) 352 | end; 353 | end; 354 | if icFunction.canBeTriggered_ON then 355 | if icFunction.animation ~= nil then 356 | self:setICAnimation(true, i); 357 | end; 358 | if icFunction.attacherControl ~= nil then 359 | self:setAttacherControl(true, i) 360 | end; 361 | if icFunction.ptoControl ~= nil then 362 | self:setPTOControl(true, i) 363 | end; 364 | if icFunction.lightControl ~= nil then 365 | self:setLightControl(true, i) 366 | end; 367 | if icFunction.motorStartControl ~= nil then 368 | self:setMotorStartControl(true, i) 369 | end; 370 | if icFunction.implementControl ~= nil then 371 | self:setImplementControl(true, i) 372 | end; 373 | if icFunction.drivableControl ~= nil then 374 | self:setDrivableControl(true, i) 375 | end; 376 | end; 377 | if icFunction.canBeTriggered_OFF then 378 | if icFunction.animation ~= nil then 379 | self:setICAnimation(false, i); 380 | end; 381 | if icFunction.attacherControl ~= nil then 382 | self:setAttacherControl(false, i) 383 | end; 384 | if icFunction.ptoControl ~= nil then 385 | self:setPTOControl(false, i) 386 | end; 387 | if icFunction.lightControl ~= nil then 388 | self:setLightControl(false, i) 389 | end; 390 | if icFunction.motorStartControl ~= nil then 391 | self:setMotorStartControl(false, i) 392 | end; 393 | if icFunction.implementControl ~= nil then 394 | self:setImplementControl(false, i) 395 | end; 396 | if icFunction.drivableControl ~= nil then 397 | self:setDrivableControl(false, i) 398 | end; 399 | end; 400 | i = i+1; 401 | end; 402 | end; 403 | end; 404 | 405 | -- implement balls 406 | if self.spec_implementBalls ~= nil then 407 | local spec1 = self.spec_implementBalls; 408 | for index, implementJoint in pairs(spec1.implementJoints) do 409 | if implementJoint.canBeClicked then 410 | self:setImplementBalls(index) 411 | end; 412 | end; 413 | end; 414 | end 415 | 416 | -- returns true if camera is inside, returns false if camera is not inside, returns nil if active camera is nil 417 | function simpleIC:isCameraInsideCheck() 418 | if self.spec_enterable ~= nil and self.getActiveCamera ~= nil then 419 | local activeCamera = self:getActiveCamera(); 420 | if activeCamera ~= nil then 421 | return activeCamera.isInside; 422 | end; 423 | end; 424 | return nil; 425 | end; 426 | 427 | function simpleIC:TOGGLE_ONOFF(actionName, inputValue) 428 | local spec = self.spec_simpleIC; 429 | if spec ~= nil and spec.hasIC and self.getAttacherVehicle == nil then 430 | if not self:isCameraInsideCheck() then 431 | if inputValue == 1 then 432 | self:setICState(true, true); 433 | else 434 | self:setICState(false, true); 435 | end; 436 | else 437 | if inputValue == 1 then 438 | self:setICState(not spec.icTurnedOn_inside, false); 439 | end; 440 | end; 441 | end; 442 | end; 443 | 444 | 445 | function simpleIC:setICState(wantedState, wantedOutside) 446 | local spec = self.spec_simpleIC; 447 | 448 | if wantedState ~= nil and wantedOutside ~= nil then 449 | if wantedOutside then 450 | spec.icTurnedOn_inside = false; 451 | spec.icTurnedOn_outside = wantedState; 452 | g_inputBinding:setShowMouseCursor(wantedState) 453 | self.spec_enterable.cameras[self.spec_enterable.camIndex].isActivated = not wantedState; 454 | else 455 | spec.icTurnedOn_outside = false; 456 | spec.icTurnedOn_inside = wantedState; 457 | g_inputBinding:setShowMouseCursor(false) 458 | self.spec_enterable.cameras[self.spec_enterable.camIndex].isActivated = true; 459 | end; 460 | 461 | if wantedState then 462 | if not spec.interactionButtonActive then 463 | local _, actionEventId = self:addActionEvent(spec.actionEvents, InputAction.INTERACT_IC_VEHICLE, self, simpleIC.INTERACT, true, false, false, true, nil); 464 | g_inputBinding:setActionEventTextVisibility(actionEventId, false); 465 | spec.interactionButtonActive = true; 466 | end; 467 | else 468 | if spec.interactionButtonActive then 469 | self:removeActionEvent(spec.actionEvents, InputAction.INTERACT_IC_VEHICLE); 470 | spec.interactionButtonActive = false; 471 | end; 472 | end; 473 | end; 474 | 475 | end; 476 | 477 | function simpleIC:onUpdate(dt) 478 | 479 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 480 | 481 | local spec = self.spec_simpleIC; 482 | if self.spec_simpleIC.playerInOutsideInteractionTrigger then 483 | self:checkInteraction() 484 | self:raiseActive() -- keep vehicle awake as long as player is in trigger 485 | end; 486 | 487 | -- we need to track camera changes from inside to outside and adjust IC accordingly 488 | if self:getIsActiveForInput(true) then 489 | -- if isInside is true and outside turned on or vice versa we changed camera 490 | local inside = self:isCameraInsideCheck() 491 | if inside ~= nil and inside ~= spec.lastCameraInside then 492 | -- if we toggled from inside to outside, store inside state in backup variable and turn off inside 493 | if not inside then 494 | spec.icTurnedOn_inside_backup = spec.icTurnedOn_inside; 495 | self:setICState(false, true); 496 | else -- if we toggled to inside restore backup value 497 | self:setICState(spec.icTurnedOn_inside_backup, false); 498 | end; 499 | spec.lastCameraInside = inside; 500 | self:resetCanBeTriggered(); 501 | end; 502 | end; 503 | 504 | if #spec.cylinderAnimations > 0 then 505 | for i=1, #spec.cylinderAnimations do 506 | local node1 = spec.cylinderAnimations[i].node1; 507 | local node2 = spec.cylinderAnimations[i].node2; 508 | 509 | local ax, ay, az = getWorldTranslation(node1); 510 | local bx, by, bz = getWorldTranslation(node2); 511 | x, y, z = worldDirectionToLocal(getParent(node1), bx-ax, by-ay, bz-az); 512 | 513 | local ux, uy, uz = localDirectionToWorld(node1, 0,1,0) 514 | ux, uy, uz = worldDirectionToLocal(getParent(node1), ux, uy, uz) 515 | 516 | setDirection(node1, x, y, z, ux, uy, uz); 517 | 518 | local ax2, ay2, az2 = getWorldTranslation(node2); 519 | local bx2, by2, bz2 = getWorldTranslation(node1); 520 | x2, y2, z2 = worldDirectionToLocal(getParent(node2), bx2-ax2, by2-ay2, bz2-az2); 521 | 522 | local ux2, uy2, uz2 = localDirectionToWorld(node2, 0,1,0) 523 | ux2, uy2, uz2 = worldDirectionToLocal(getParent(node2), ux2, uy2, uz2) 524 | 525 | setDirection(node2, x2, y2, z2, ux2, uy2, uz2); 526 | end; 527 | end; 528 | end; 529 | end; 530 | 531 | function simpleIC:resetCanBeTriggered() 532 | for _, icFunction in pairs(self.spec_simpleIC.icFunctions) do -- reset all the IC-Functions so they can't be triggered 533 | icFunction.canBeTriggered = false; 534 | icFunction.canBeTriggered_ON = false; 535 | icFunction.canBeTriggered_OFF = false; 536 | end; 537 | end; 538 | 539 | function simpleIC:onLeaveVehicle() 540 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 541 | self:resetCanBeTriggered(); 542 | self.spec_simpleIC.interactionButtonActive = false; 543 | end; 544 | end; 545 | 546 | function simpleIC:setICAnimation(wantedState, animationIndex, noEventSend) 547 | setICAnimationEvent.sendEvent(self, wantedState, animationIndex, noEventSend); 548 | local animation = self.spec_simpleIC.icFunctions[animationIndex].animation; 549 | local spec = self.spec_simpleIC; 550 | 551 | if wantedState then -- if currentState is true (max) then play animation to min 552 | self:playAnimation(animation.animationName, animation.animationSpeed, self:getAnimationTime(animation.animationName), true); 553 | animation.currentState = true; 554 | self:addSoundChangeIndex(animationIndex); 555 | else 556 | self:playAnimation(animation.animationName, -animation.animationSpeed, self:getAnimationTime(animation.animationName), true); 557 | animation.currentState = false; 558 | self:addSoundChangeIndex(animationIndex); 559 | end; 560 | 561 | if self.spec_motorized ~= nil then 562 | spec.soundVolumeIncreasePercentageAll = math.max(1, spec.soundVolumeIncreasePercentageAll); 563 | end; 564 | 565 | end; 566 | 567 | -- goal 568 | -- sound volume needs to change dynamically while the animation is playing 569 | -- sound volume needs to change globally with multiple animations playing at the same time 570 | 571 | -- so when we activate an animation we add that animation index to a sound update index list 572 | 573 | function simpleIC:addSoundChangeIndex(index) 574 | if self.spec_motorized ~= nil then 575 | local animation = self.spec_simpleIC.icFunctions[index].animation; 576 | if animation.soundVolumeIncreasePercentage ~= false then -- check if this even has sound change effects 577 | -- now add it to the table 578 | self.spec_simpleIC.soundChangeIndexList[index] = animation; -- we add it at the index of the animation that way if we try adding the same animation twice it does overwrite itself 579 | end; 580 | end; 581 | end; 582 | 583 | -- next we want to run through that list, get the current animation status of that animation and update the sound volume value 584 | -- if the animation stopped playing, remove it from the list 585 | 586 | function simpleIC:updateSoundAttributes() 587 | local spec = self.spec_simpleIC; 588 | local soundVolumeIncreaseAll = 0; 589 | local updateSound = false; 590 | for _, animation in pairs(spec.soundChangeIndexList) do 591 | -- get time 592 | local animationTime = self:getAnimationTime(animation.animationName); 593 | -- get current sound volume increase 594 | local soundVolumeIncrease = animation.soundVolumeIncreasePercentage * (animationTime ^ 0.5); 595 | soundVolumeIncreaseAll = soundVolumeIncreaseAll + soundVolumeIncrease; 596 | if animationTime == 1 or animationTime == 0 then 597 | animation = nil; -- delete animation from index table if we reached max pos or min pos 598 | end; 599 | updateSound = true; 600 | end; 601 | 602 | if updateSound then 603 | for i, sample in pairs(self.spec_motorized.samples) do 604 | sample.indoorAttributes.volume = math.min(sample.indoorAttributes.volumeBackup * (1 + soundVolumeIncreaseAll), sample.outdoorAttributes.volume); 605 | end; 606 | for i, sample in pairs(self.spec_motorized.motorSamples) do 607 | sample.indoorAttributes.volume = math.min(sample.indoorAttributes.volumeBackup * (1 + soundVolumeIncreaseAll), sample.outdoorAttributes.volume); 608 | end; 609 | end; 610 | end; 611 | 612 | function simpleIC:outsideInteractionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay) 613 | local spec = self.spec_simpleIC; 614 | 615 | if onEnter and g_currentMission.controlPlayer and g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then 616 | spec.playerInOutsideInteractionTrigger = true; 617 | self:raiseActive() 618 | spec.actionEvents = {}; -- create actionEvents table since in case we didn't enter the vehicle yet it does not exist 619 | self:clearActionEventsTable(spec.actionEvents); -- also clear it for good measure 620 | local _ , eventId = self:addActionEvent(spec.actionEvents, InputAction.INTERACT_IC_ONFOOT, self, simpleIC.INTERACT, false, true, false, true); -- now add the actionEvent 621 | elseif onLeave and g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then 622 | spec.playerInOutsideInteractionTrigger = false; 623 | self:removeActionEvent(spec.actionEvents, InputAction.INTERACT_IC_ONFOOT); -- remove the actionEvent again once we leave the trigger 624 | end; 625 | end; 626 | 627 | function simpleIC:onDraw() 628 | self:checkInteraction() 629 | end; 630 | 631 | function simpleIC:checkInteraction() 632 | if self.spec_simpleIC ~= nil and self.spec_simpleIC.hasIC then 633 | local spec = self.spec_simpleIC; 634 | 635 | self:updateSoundAttributes(); 636 | 637 | -- we need to check the positions of our triggerPoints if 638 | -- + somebody is in the outsideInteractionTrigger 639 | -- + the vehicle is active and simpleIC is active 640 | if (self:getIsActive() and spec.icTurnedOn_inside) or (self:getIsActive() and spec.icTurnedOn_outside) or spec.playerInOutsideInteractionTrigger then -- check the points 641 | 642 | -- see if we need to check the outside or the inside points 643 | -- see if we are inside the vehicle or not 644 | local isInside = false; 645 | local isPlayerTrigger = false; 646 | if spec.playerInOutsideInteractionTrigger then 647 | isPlayerTrigger = true; 648 | end; 649 | 650 | if not isPlayerTrigger then 651 | if self:getActiveCamera() ~= nil and self:getActiveCamera().isInside then 652 | isInside = true; 653 | end; 654 | end; 655 | 656 | if not spec.playerInOutsideInteractionTrigger and not spec.icTurnedOn_outside then -- don't render the crosshair if we are outside 657 | renderText(0.5, 0.5, 0.02, "+"); 658 | end; 659 | 660 | -- go through all the functions 661 | local index = 0; 662 | for _, icFunction in pairs(spec.icFunctions) do 663 | index = index + 1; 664 | -- get inside or outside trigger points depending on if we're inside or outside 665 | local tp = icFunction.inTP; 666 | if not isInside then 667 | tp = icFunction.outTP; 668 | end; 669 | 670 | 671 | 672 | if tp.triggerPoint ~= nil or tp.triggerPoint_ON ~= nil or tp.triggerPoint_OFF ~= nil then 673 | 674 | local triggerPoint = {}; 675 | triggerPoint[1] = tp.triggerPoint; 676 | 677 | if tp.triggerPoint_OFF ~= nil and tp.triggerPoint_ON ~= nil then -- multiple trigger points 678 | triggerPoint[2] = tp.triggerPoint_ON; 679 | triggerPoint[3] = tp.triggerPoint_OFF; 680 | end; 681 | 682 | -- set it to false by default 683 | icFunction.canBeTriggered = false; 684 | icFunction.canBeTriggered_ON = false; 685 | icFunction.canBeTriggered_OFF = false; 686 | 687 | for index , triggerPoint in pairs(triggerPoint) do 688 | 689 | -- get visibility of our trigger-point, if it is invisible its deactivated 690 | if not spec.disableInvisibleTriggers or (getVisibility(triggerPoint) and spec.disableInvisibleTriggers) then 691 | 692 | -- get world translation of our trigger point, then project it to the screen 693 | local wX, wY, wZ = getWorldTranslation(triggerPoint); 694 | local cameraNode = 0; 695 | if spec.playerInOutsideInteractionTrigger then 696 | cameraNode = g_currentMission.player.cameraNode 697 | else 698 | cameraNode = self:getActiveCamera().cameraNode 699 | end; 700 | local cX, cY, cZ = getWorldTranslation(cameraNode); 701 | local x, y, z = project(wX, wY, wZ); 702 | 703 | local dist = MathUtil.vector3Length(wX-cX, wY-cY, wZ-cZ); 704 | 705 | 706 | if x > 0 and y > 0 and z > 0 then 707 | 708 | -- the higher the number the smaller the text should be to keep it the same size in 3d space 709 | -- base size is 0.025 710 | -- if the number is higher than 1, make smaller 711 | -- if the number is smaller than 1, make bigger 712 | 713 | local size = 0.028 / dist; 714 | 715 | -- default posX and posY is 0.5 e.g. middle of the screen for selection 716 | local posX, posY, posZ = 0.5, 0.5, 0.5; 717 | 718 | -- if we have MOUSE_Mode enabled, use mouse position instead 719 | if spec.icTurnedOn_outside then 720 | posX, posY, posZ = g_lastMousePosX, g_lastMousePosY, 0; 721 | end; 722 | 723 | 724 | -- check if our position is within the position of the triggerRadius 725 | if posX < (x + tp.triggerPointRadius) and posX > (x - tp.triggerPointRadius) then 726 | if posY < (y + tp.triggerPointRadius) and posY > (y - tp.triggerPointRadius) then 727 | if dist < spec.reachDistance or spec.icTurnedOn_outside then 728 | -- can be clicked 729 | if index == 1 then -- toggle mark 730 | icFunction.canBeTriggered = true; 731 | elseif index == 2 then -- on mark 732 | icFunction.canBeTriggered_ON = true; 733 | elseif index == 3 then -- off mark 734 | icFunction.canBeTriggered_OFF = true; 735 | end; 736 | self:renderTextAtProjectedPosition(x,y,z, "X", size, 1, 0, 0) 737 | end; 738 | end; 739 | end; 740 | if (index == 1 and not icFunction.canBeTriggered) or (index == 2 and not icFunction.canBeTriggered_ON) or (index == 3 and not icFunction.canBeTriggered_OFF) then 741 | self:renderTextAtProjectedPosition(x,y,z, "X", size, 1, 1, 1) 742 | end; 743 | end; 744 | end; 745 | end; 746 | end; 747 | end; 748 | end; 749 | end; 750 | 751 | end; 752 | 753 | function simpleIC:renderTextAtProjectedPosition(projectX,projectY,projectZ, text, textSize, r, g, b) 754 | --local projectX,projectY,projectZ = project(x,y,z); 755 | if projectX > -1 and projectX < 2 and projectY > -1 and projectY < 2 and projectZ <= 1 then 756 | setTextAlignment(RenderText.ALIGN_CENTER); 757 | setTextBold(false); 758 | setTextColor(r, g, b, 1.0); 759 | renderText(projectX, projectY, textSize, text); 760 | setTextAlignment(RenderText.ALIGN_LEFT); 761 | end 762 | end 763 | 764 | 765 | 766 | setICAnimationEvent = {}; 767 | setICAnimationEvent_mt = Class(setICAnimationEvent, Event); 768 | InitEventClass(setICAnimationEvent, "setICAnimationEvent"); 769 | 770 | function setICAnimationEvent:emptyNew() 771 | local self = Event:new(setICAnimationEvent_mt ); 772 | self.className="setICAnimationEvent"; 773 | return self; 774 | end; 775 | function setICAnimationEvent:new(vehicle, wantedState, animationIndex) 776 | self.vehicle = vehicle; 777 | self.wantedState = wantedState; 778 | self.animationIndex = animationIndex; 779 | return self; 780 | end; 781 | function setICAnimationEvent:readStream(streamId, connection) 782 | self.vehicle = NetworkUtil.readNodeObject(streamId); 783 | self.wantedState = streamReadBool(streamId); 784 | self.animationIndex = streamReadUIntN(streamId, 6); 785 | self:run(connection); 786 | end; 787 | function setICAnimationEvent:writeStream(streamId, connection) 788 | NetworkUtil.writeNodeObject(streamId, self.vehicle); 789 | streamWriteBool(streamId, self.wantedState ); 790 | streamWriteUIntN(streamId, self.animationIndex, 6); 791 | end; 792 | function setICAnimationEvent:run(connection) 793 | self.vehicle:setICAnimation(self.wantedState, self.animationIndex, true); 794 | if not connection:getIsServer() then 795 | g_server:broadcastEvent(setICAnimationEvent:new(self.vehicle, self.wantedState, self.animationIndex), nil, connection, self.object); 796 | end; 797 | end; 798 | function setICAnimationEvent.sendEvent(vehicle, wantedState, animationIndex, noEventSend) 799 | if noEventSend == nil or noEventSend == false then 800 | if g_server ~= nil then 801 | g_server:broadcastEvent(setICAnimationEvent:new(vehicle, wantedState, animationIndex), nil, nil, vehicle); 802 | else 803 | g_client:getServerConnection():sendEvent(setICAnimationEvent:new(vehicle, wantedState, animationIndex)); 804 | end; 805 | end; 806 | end; 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | --------------------------------------------------------------------------------