| Key | Function |
|---|---|
| ~ or ` | Toggle console |
| F2 | Show frame rate |
| F3 | Toggle frame rate limiter |
| F4 | Wireframe mode |
| F5 | Toggle debug rendering |
| F7 | Toggle camera |
| F8 | Toggle stats |
List all available commands
Show frames per second
Enable/disable frames-per-second limiter
Frames per second limit attribute
Print detailed entity list
Print detailed resource list
Enable parallel rendering and physics
Quit the application













| Key | Function |
|---|---|
| W A S D | Navigation |
| Alt + LMB | Rotate camera |
| Alt + MMB | Pan camera |
| Alt + RMB | Zoom camera |
| Alt + LMB + RMB | Move camera up or down and left or right camera |
| F | Frame selected object |
| - | Decrease navigation speed |
| + | Increase navigation speed |
| 4 | Wireframe mode |
| 6 | Shaded mode |
| Ctrl-S | Save |
| Ctrl-Z | Undo |
| Ctrl-W | Replace Dialog |
| Ctrl-X | Cut |
| Ctrl-C | Copy |
| Ctrl-V | Paste |
| Ctrl-Shift-C | Copy X,Y,Z components at once |
| Ctrl-Shift-V | Paste X,Y,Z components at once (can also be copied from a text source in the format "x y z") |
| Delete | Delete |
| Ctrl-D | Duplicate |
| Ctrl-F | Move to Camera |
| Ctrl-B | Interactive placement (hold left mouse button to move around) |
| Shift | Interactive placement paint |
| Ctrl | Interactive placement paint with random rotation around y axis |
| Ctrl-H | Hide object |
| Shift-H | Show object |
| Ctrl-G | Group objects |
| Ctrl-R | Pick replace value in viewport (Terrain edit modes) |
| V | Decrease brush radius |
| B | Increase brush radius |
| N | Decrease brush opacity |
| M | Increase brush opacity |
| F8 | Toggle stats |
| Shift + Enter | Execute Script (Script Window) |
| X | Absolute grid snapping |
| J | Relative grid snapping |
| Delete | Delete spline control vertex |
| Insert | Insert new spline control vertex |
| Left | Previous spline control vertex |
| Right | Next spline control vertex |
| Up or Down | First spline control vertex |
| S | Stitch spline endpoints |
| O | Toggle spline open/close |
| R | Reverse spline |
| Ctrl-L | Create light |
The GIANTS Studio is a new tool for creating, editing and debugging script mods. It works as an editor and a “remote” debugger in one. It interacts with the game state and provides you with information about the game state.
Possible use-case scenarios:
The Debugger can be used on all kinds of mod configurations. These are the most common three configurations:
Working on a single mod (fast iteration time, but only compatible with singeplayer):
Unzip your mod and put the mod in the “mods” directory (by default in Documents/My Games/FarmingSimulator2019/mods).
Make sure that the layout of the mod is correct, i.e. the modDesc.xml is directly in the mod folder (i.e. mods/
Working on multiple mods at the same time (fast iteration time, but only compatible with singeplayer):
Follow the instructions above, but when you create a project in the Debugger, set the “Mod Directory” to “mods” directory instead of the specific mod directory.
This way you can edit several mods at the same time.
In this configuration, the “Mod Name” is not important, you can call it anything you like.
Working on a single mod outside the “mods” directory (also compatible with multiplayer):
This is the most complex configuration, and it is the only one that lets you debug mods in multiplayer.
Place the mod zip file in the “mods” directory and unzip it somewhere else. In “Project Settings” set
the “Mod Name” to match with the name of the zip file but without .zip at the end) and set the “Mod Directory”
as a path to the unzipped directory.
For every change you make in the Debugger, you will have to re-create the mod zip file and copy it to the “mods” directory before you start the game.
External tools for zipping are useful for this purpose (not included).
First you need to open or create a project.
Most of the required parameters are self-explanatory. The “Mod Directory” can be a path to a single mod or a “mods” directory if you are working on several mods at the same time. If you are working on a single mod, the “Mod Name” must have the same name as the .zip file. The “User Profile App Directory” should be saved as a path to a folder where the game stores user data (changes are not needed by default if the game configuration has not been changed manually). The debugger uses sockets to communicate with the game and listens at the port defined as the “Debug Network Port”.
The function of the “Globals” and “Locals” panels is the same: to explore the value of local and global variables at a current execution point (using breakpoints to halt execution and observe these variables, or by hitting the ‘break all’ button). By right-clicking on a variable, you can add it as a “Watch” or a “Breakpoint”.
The ”Watch” panel is similar to the “Globals” and “Locals” panel, but instead of global or local variables, you can select variables that you would like to ‘watch’. Values are updated every time a breakpoint is hit or when you execute the code step-by-step. Variable names can be written like most other simple Lua statements:
The “Callstack” panel shows you the callstack at a current execution point. Double-click on a line to open a file at the location of the call. The Debugger even allows you to step through some of the base game functions for which the source code has been made public. Grayed-out lines are parts of code that have not been made public.
The “Script Console” executes user-defined Lua code when execution is paused (by hitting a breakpoint or break-all being pressed). Enter your code in the bottom part of the panel and press Ctrl + Enter to execute it.
The “Breakpoints” panel is where you manage breakpoints.
Regular breakpoints can be placed in the code by clicking on the vertical gray bar on the left of the code panel. You can also use F9 to enable/disable a breakpoint at the current position. The program will stop running when it tries to execute a line with a breakpoint.
Data breakpoints can be placed by right-clicking on a variable name in the “Globals”/”Locals”/”Watch” panel or by right-clicking on a name in the text editor. A data breakpoint will be hit when the data for the specified variable changes.
You can fine-tune a breakpoint by adding a conditional expression and/or hit count condition to it. These conditions are evaluated each time a breakpoint is hit and the code will only stop executing once the conditions are met.
The “Output” panel shows the game’s log output. If a script error is reported, this panel will provide you with the file name and line number, so that you can double-click on it and view the code causing the error. The output is the same as the log file written by the game.
The “Find Results” panel shows search results from the “Find in Files” dialog. Double-clicking on a result opens a file at the search hit location.
The “Find Replace” window behaves in a similar way to other text editors. “Find” and “Replace” directly show the results in the text editor; while “Find in Files” searches through the entire file and shows results in the “Find Results” panel. Other search modes are available too:
| Key | Function |
|---|---|
| Ctrl-N | New file |
| Ctrl-O | Open file |
| Ctrl-S | Save file |
| Ctrl-Shift-S | Save all files |
| Ctrl-Q | Quit application |
| Ctrl-Z | Undo |
| Ctrl-Y | Redo |
| Ctrl-F | Find |
| Ctrl-H | Replace |
| Ctrl-Shift-F | Find in files |
| F3 | Find next |
| Shift-F3 | Find previous |
| Ctrl-Shift-G | Go to file |
| Ctrl-G | Go to line |
| Ctrl-K | Comment lines |
| Ctrl-Shift-K | Uncomment lines |
| Ctrl-W | Close tab |
| Ctrl-Shift-T | Reopen tab |
| Alt-Up | Move line up |
| Alt-Down | Move line down |
| Alt-Left | Navigate backwards |
| Alt-Right | Navigate forward |
| F5 | Run / Continue execution |
| Ctrl-F5 | Run Without Debugging |
| F7 | Pause execution |
| F6 | Stop execution |
| F9 | Toggle Breakpoint |
| F11 | Step Into |
| F10 | Step Over |
| Shift-F11 | Step Out |
Achievement message display element.
-- Used to display a message in the HUD when a new achievement is unlocked.
--@category GUI
Create a new instance of AchievementMessage.Definition
new(string hudAtlasPath, table inputManager, table guiSoundPlayer, ContextActionDisplay ContextActionDisplay)Arguments
| string | hudAtlasPath | Path the HUD texture atlas |
| table | inputManager | InputBinding reference for achievement acknowledgment input |
| table | guiSoundPlayer | GuiSoundPlayer reference for playing a sound cue |
| ContextActionDisplay | ContextActionDisplay | reference for visibility checks (it occupies the same space on the HUD) |
| table | AchievementMessage | instance |
Handle changes in menu visibility.Definition
Keeps track of menu visibility state to avoid showing (or updating) an achievement message when the menu is active.
onMenuVisibilityChange()
Show an achievement message.Definition
The message will be shown as soon as the player is not in a menu and there is no context action display visible.
Messages will however not be checked for overlaps. The caller has to make sure that achievements are displayed
sequentially.
showMessage(string title, string description, string iconFilename, table iconUVs, float duration)Arguments
| string | title | Achievement title text |
| string | description | Achievement description text |
| string | iconFilename | Path to the achievement icon texture |
| table | iconUVs | UV coordinates of achievement icon with icon texture |
| float | duration | Maximum display duration in milliseconds |
Update display state.Definition
update()
Actually start showing the message when nothing is obstructing the view.Definition
beginShowMessage()
Hide the message, including animation.Definition
Also removes the acknowledgment input events.
hideMessage()
Draw the achievement message if necessary.Definition
draw()
Store scaled positioning, size and offset values.Definition
storeScaledValues()
Set this element's scale.Definition
setScale()
Get the position of the background element, which provides this element's absolute position.Definition
getBackgroundPosition(scale Current, float width)Arguments
| scale | Current | UI scale |
| float | width | Scaled background width in pixels |
| float | X | position in screen space |
| float | Y | position in screen space |
Create the empty background overlayDefinition
createBackground()
Create required display components.Definition
createComponents()
Create the message frame.Definition
createFrame()
Create the achievement icon.Definition
createIcon()
Animal Buying Screen.
AnimalScreen = {}
ConstructorDefinition
new(table target, table metatable)Arguments
| table | target | |
| table | metatable |
| table | self | instance |
| 53 | function AnimalScreen:new(target, custom_mt, animalController, l10n, messageCenter) |
| 54 | local self = ScreenElement:new(target, custom_mt or AnimalScreen_mt) |
| 55 | |
| 56 | self:registerControls(AnimalScreen.CONTROLS) |
| 57 | |
| 58 | self.l10n = l10n |
| 59 | self.messageCenter = messageCenter |
| 60 | self.animalController = animalController |
| 61 | |
| 62 | self.animalController:setSourceUpdateCallback(self.onSourceUpdate, self) |
| 63 | self.animalController:setTargetUpdateCallback(self.onTargetUpdate, self) |
| 64 | self.animalController:setNoValidHusbandryCallback(self.onNoValidHusbandry, self) |
| 65 | self.animalController:setHusbandryIsFullCallback(self.onHusbandryIsFull, self) |
| 66 | self.animalController:setTrailerFullCallback(self.onTrailerIsFull, self) |
| 67 | self.animalController:setInvalidAnimalTypeCallback(self.onInvalidAnimalType, self) |
| 68 | self.animalController:setAnimalNotSupportedByTrailerCallback(self.onAnimalNotSupportedByTrailer, self) |
| 69 | self.animalController:setNotEnoughMoneyCallback(self.onNotEnoughMoney, self) |
| 70 | self.animalController:setCanNotAddToTrailerCallback(self.onCanNotAddToTrailer, self) |
| 71 | self.animalController:setAnimalInUseCallback(self.onAnimalInUse, self) |
| 72 | |
| 73 | self.isSourceSelected = true |
| 74 | |
| 75 | self.isOpen = false |
| 76 | self.lastBalance = 0 |
| 77 | self.sourceElementToAnimal = {} |
| 78 | self.targetElementToAnimal = {} |
| 79 | |
| 80 | self.dofState = DepthOfFieldState.createFullscreenBlur() |
| 81 | |
| 82 | return self |
| 83 | end |
Callback on openDefinition
onOpen()Code
| 87 | function AnimalScreen:onOpen() |
| 88 | AnimalScreen:superClass().onOpen(self) |
| 89 | |
| 90 | self.isOpen = true |
| 91 | self.isUpdating = false |
| 92 | |
| 93 | g_gameStateManager:setGameState(GameState.MENU_ANIMAL_SHOP) |
| 94 | |
| 95 | self.dofState:recordState() -- record current state before changing |
| 96 | self.dofState:apply() -- apply blur effect |
| 97 | |
| 98 | self:updateScreen() |
| 99 | |
| 100 | self.messageCenter:subscribe(MessageType.HUSBANDRY_ANIMALS_CHANGED, self.onAnimalsChanged, self) |
| 101 | end |
Callback on closeDefinition
onClose(table element)Arguments
| table | element |
| 106 | function AnimalScreen:onClose(element) |
| 107 | AnimalScreen:superClass().onClose(self) |
| 108 | self.animalController:close() |
| 109 | self.isOpen = false |
| 110 | self.sourceElementToAnimal = {} |
| 111 | |
| 112 | g_currentMission:resetGameState() |
| 113 | |
| 114 | self.messageCenter:unsubscribeAll(self) |
| 115 | |
| 116 | self.dofState:reset() -- reset depth of field parameters (blur effect) |
| 117 | end |
Callback on click backDefinition
onClickBack()Code
| 262 | function AnimalScreen:onClickBack() |
| 263 | AnimalScreen:superClass().onClickBack(self) |
| 264 | self:changeScreen(nil) |
| 265 | end |
Callback on click cancelDefinition
onClickOk()Code
| 269 | function AnimalScreen:onClickOk() |
| 270 | AnimalScreen:superClass().onClickOk(self) |
| 271 | |
| 272 | if self.isSourceSelected then |
| 273 | self.animalController:moveToTarget(self.listSource.selectedIndex) |
| 274 | else |
| 275 | self.animalController:moveToSource(self.listTarget.selectedIndex) |
| 276 | end |
| 277 | end |
Input context name for identification, does not take care of proper context handling
Create a new base missionDefinition
new(string baseDirectory, table customMt, table missionCollaborators)Arguments
| string | baseDirectory | Mission scripts base directory |
| table | customMt | Sub-class meta table |
| table | missionCollaborators | MissionCollaborators object containing a defined collection required object references for a mission. |
Initialize mission after instantiation.Definition
Create complex members and call dependency methods in here so that mission instantiation cannot fail.
initialize()
Create the in-game HUD display.Definition
createHUD()
Deprecated: Replaced by InputBinding:setActionEventText()Definition
addHelpButtonText()
Subscribe to relevant game settings changes.Definition
subscribeSettingsChangeMessages()
Subscribe to GUI notifications of opening and closing.Definition
subscribeGuiOpenCloseMessages()
Handle a menu open event.Definition
onBeforeMenuOpen()
Handle a menu closed event.Definition
onAfterMenuClose()
Register required input action events.Definition
registerActionEvents()
Register action events for pause actions.Definition
Event registration in this method is enclosed in an input binding registration context (InputBinding:beginActionEventsModification()).
Make sure that this is only called when all other registration-context altering code is done.
registerPauseActionEvents()
Input event for "pause".Definition
onPause()
Input event for breaking pause on consoles.Definition
onConsoleAcceptPause()
Input event for "toggle help text".Definition
onToggleHelpText()
Input event for "switch vehicle".Definition
onSwitchVehicle(directionValue Numeric)Arguments
| directionValue | Numeric | value for switch direction, 1 is the next, -1 is the previous vehicle |
Binding of input to actions.
--@category Input
--@xmlConfig binding#device Device ID, must match a value returned by engine function getGamepadId() or one of the
constants of InputDevice.DEFAULT_DEVICE_NAMES.
Create a new Binding instance.Definition
new(action InputAction, deviceId ID, axisNames List, axisComponent Component, inputComponent Component, index Binding)Arguments
| action | InputAction | reference which is being bound to an input |
| deviceId | ID | of the input device whose input is being bound to the given action |
| axisNames | List | of input axis names. If the list contains more than one axis, any axis before the last one is a modifier. |
| axisComponent | Component | of the last given axis to bind to the given action. |
| inputComponent | Component | of a physical axis (e.g. analog stick) to bind |
| index | Binding | index, 1 primary, 2 secondary, etc. |
| bool | true | if allowed |
| New | Binding | instance |
Create a new Binding instance from an XML element.Definition
createFromXML(int xmlFile, string elementTag, bool isLocked)Arguments
| int | xmlFile | XML file handle |
| string | elementTag | Tag of the element to parse as a Binding |
| bool | isLocked | If true, the binding belongs to a locked action |
| boolean | isAllowed | true if fillType is supported else false |
| Binding | instance | initialized with values from XML and the given parameters |
Save this binding to XML.Definition
saveToXMLFile(xmlFile Input, elementTag Element)Arguments
| xmlFile | Input | binding settings XML file handle |
| elementTag | Element | tag of this binding |
| boolean | isSupported | true if fillType is supported else false |
Update a binding with a new input target.Definition
Always use this method to change existing bindings or risk invalid state.
updateData(deviceId (New), axisNames List, inputComponent [optional])Arguments
| deviceId | (New) | device ID |
| axisNames | List | of input axis names. Make sure these are valid in the context of this binding's state (i.e. device and action axis type). |
| inputComponent | [optional] | Direction of a physical axis (e.g. analog stick) to bind, defaults to positive. |
| float | freeCapacity | free capacity |
Update this binding's input state.Definition
updateInput(inputValue Current)Arguments
| inputValue | Current | input value of the last axis in self.axisNames |
| boolean | isAllowed | true if toolType is allowed else false |
Set this binding's analog input flag.Definition
setIsAnalog()Return Values
| table | instance | instance of object |
Set this binding's index and update internal state.Definition
setIndex()Return Values
| table | instance | instance of object |
Set this binding's active state.Definition
Only active bindings are updated and can trigger action events.
setActive()Return Values
| integer | harvestPixelsSum | harvest of pixels sum |
| integer | harvestNumPixels | harvest number of pixels |
| float | sprayFactor | spray factor |
| float | plowFactor | plow factor |
| float | limeFactor | lime factor |
| float | weedFactor | weed factor |
| integer | growthState | growth state |
| float | maxArea | max area |
Set the frame trigger flag which shows if this binding has triggered an event this frame.Definition
setFrameTriggered()
Get the frame trigger flag which shows if this binding has triggered an event this frame.Definition
getFrameTriggered()
Set and store the combo bit mask for this binding.Definition
This is agnostic of the actually bound device because a binding can only map input on one specific device.
setComboMask(int comboMask)Arguments
| int | comboMask | Combo mask |
Get the combo bit mask.Definition
getComboMask()Return Values
| int | Combo | bit mask |
Check if there is a collision between this binding and another one.Definition
Two bindings collide if they bind the same input on the same axes of the same device.
hasCollisionWith(otherBinding Other)Arguments
| otherBinding | Other | binding to check for collision |
| True | if | there is a collision. |
Check if this binding is an alternative to another one on the same action.Definition
Two bindings are alternatives if they bind different input on the same axis of the same device category to the same action.
isAlternativeTo(otherBinding Other)Arguments
| otherBinding | Other | binding to check if it's an alternative |
| True | if | this and the other binding are alternatives of each other |
Create a new instance of Binding with the same state as this instance.Definition
clone()Return Values
| Cloned | Binding | instance |
Copy binding input state from another binding.Definition
copyInputStateFrom()Return Values
| integer | ret | ret |
| integer | total | total |
Check if this binding occupies the same binding slot as another binding on the same action.Definition
isSameSlot()
Get the opposite axis component for a given axis component.Definition
getOppositeAxisComponent(axisComponent One)Arguments
| axisComponent | One | of the constant values of Binding.AXIS_COMPONENT |
| integer | changedValue | changed value |
Get the opposite input component for a given input component.Definition
getOppositeInputComponent(inputComponent One)Arguments
| inputComponent | One | of the constant values of Binding.INPUT_COMPONENT |
| integer | changedArea | changed area pixels |
| integer | totalArea | total area |
Make and assign a binding ID.Definition
makeId()
Determine if we need to swap buttons for Japanese Playstation controllers by their convention.Definition
needJapanesePlaystationButtonSwap()Return Values
| integer | changedArea | changed area pixels |
| integer | totalArea | total area pixles |
Swaps buttons for Japanese Playstation controllers if contained in the given axis names.Definition
swapJapanesePlaystationButtons(table axisNames)Arguments
| table | axisNames | Axis names array, will be changed in place |
Get a string representation of this binding.Definition
toString()Return Values
| integer | numPixels | number of pixels |
| integer | totalNumPixels | total number of pixels |
Display element for images.
-- Used layers: "image" for the display image.
--@xmlConfig GuiElement#offset string [optional] Position offset of the displayed image relative to this element's origin in reference resolution, defaults to [0, 0]. Format: "[x]px [y]px".
Set this element's image color.Definition
Omitted (nil value) color values have no effect and the previously set value for that channel is used.
setImageColor(state GuiOverlay, r Red, g Green, b Blue, a Alpha)Arguments
| state | GuiOverlay | state for which the color is changed, use nil to set the default color |
| r | Red | color value |
| g | Green | color value |
| b | Blue | color value |
| a | Alpha | value (transparency) |
Set this element's image overlay's rotation.Definition
setImageRotation(float rotation)Arguments
| float | rotation | Rotation in radians |
Layout element which lays out child elements in regular rows or columns.
-- Exceptions are elements whose "layoutIgnore" property is true.
-- Used layers: "image" for a background image.
--@category GUI
--@xmlConfig GuiElement#alignmentX string [optional] Horizontal alignment of the layout, defaults to "left". Valid values are "left", "right" and "center".
Extract a flow / cell data table from this box layout's elements.Definition
getLayoutCells(ignoreVisibility Visibility)Arguments
| ignoreVisibility | Visibility | flag for element eligibility |
| Flows | and | cells as nested tables: [flowIndex][cell index] = cell data {element, flowSize, lateralSize} |
Calculate layout sizes based on flow cell data.Definition
getLayoutSizes()Return Values
| List | of | lateral flow sizes, total lateral sizes (sum of lateral flow sizes), maximum flow size in direction of flow |
Get the layout space starting position offset for elements based on flow dimensions and alignments.Definition
getAlignmentOffset(flowSize Flow, flowLateralSize Total)Arguments
| flowSize | Flow | size in flow direction (e.g. height for vertical flows) |
| flowLateralSize | Total | flow size orthogonal to flow direction (e.g. combined width of all columns for vertical flows) |
| Layout | X | starting offset, layout Y starting offset, Layout X direction {-1|1}, Layout Y direction {-1|1} |
Calculate element offsets for margins and to counteract alignment updates in GuiElement.updateAbsolutePositions()Definition
getElementAlignmentOffset(directionX Layout, directionY Layout)Arguments
| directionX | Layout | X direction as a signed factor {-1|1} |
| directionY | Layout | Y direction as a signed factor {-1|1} |
| element | X | offset, element Y offset |
Apply layout positions to all given cells' elements.Definition
applyCellPositions(flowCells List, offsetStartX Layout, offsetStartY Layout, directionX Layout, directionY Layout, lateralFlowSizes List)Arguments
| flowCells | List | of flows and their cell data |
| offsetStartX | Layout | X starting offset |
| offsetStartY | Layout | Y starting offset |
| directionX | Layout | X direction as a signed factor {-1|1} |
| directionY | Layout | Y direction as a signed factor {-1|1} |
| lateralFlowSizes | List | of lateral flow sizes (e.g. widths of columns in vertical flows) |
Link layout cells' elements for focus navigation.Definition
focusLinkCells(flowCells List)Arguments
| flowCells | List | of flows and their cell data |
Link a child element to others for focus navigationDefinition
focusLinkChildElement(element Current, previousElement Element, firstElement First, lastElement Last)Arguments
| element | Current | element |
| previousElement | Element | at previous position in layout flow, will be nil when the first element is being processed |
| firstElement | First | element in layout |
| lastElement | Last | element in layout |
Invalidate the layout, will reposition all elements and update focus navigation.Definition
invalidateLayout(ignoreVisibility If)Arguments
| ignoreVisibility | If | true, elements will be considered for layouting even if invisible |
Return the actual focus target when this box layout is being focused. This can be a previously focused element withinDefinition
the layout (depends on rememberLastFocus attribute), a programmatically defined entry point based on the incoming
direction or simply the first layout element (when no other options apply).
getFocusTarget(incomingDirection Focus, moveDirection Actual)Arguments
| incomingDirection | Focus | navigation direction from where this layout is being entered |
| moveDirection | Actual | focus navigation direction per input |
| GuiElement | which | should receive focus instead |
Clickable button element.
-- Used layers: "image" for the background, "icon" for a button glyph.
-- All button UI callbacks do not require or provide any arguments.
--@category GUI
--@xmlConfig GuiElement#iconSize string [optional] Pixel size of button glyph in reference resolution. Format: "[width]px [height]px"
Load glyph overlay colors.Definition
loadInputGlyphColors(profile If, xmlFile If, key XML)Arguments
| profile | If | set, loads overlay properties from this button's GUI profile |
| xmlFile | If | set, loads overlay properties from this button's XML configuration |
| key | XML | base configuration node of this button |
Load the actual input glyph symbols to display if an input action is defined on the button.Definition
loadInputGlyph()
Set the keyboard mode flag.Definition
setKeyboardMode()
Set the input action for the display glyph by name.Definition
setInputAction()
Set UV coordinates for the button background and/or icon.Definition
setImageUVs()
Determine if this button is selectedDefinition
getIsSelected()
Determine if this button is highlightedDefinition
getIsHighlighted()
Get modified text offset including changes from icon position and dimensions.Definition
getIconModifiedTextOffset(float textOffsetX, float textOffsetY)Arguments
| float | textOffsetX | Screen space text X offset |
| float | textOffsetY | Screen space text Y offset |
| float | Modified | X offset |
| float | Modified | Y offset |
Get text offset from element position including modifications from icon.Definition
getTextOffset()
Get shadow text offset from element position including modifications from icon.Definition
getText2Offset()
Get the current icon size in screen space.Definition
getIconSize()
Update size of element depending on contentDefinition
updateSize()
Keyboard button display overlay.
-- Overlay type which displays a keyboard key button symbol.
--@category GUI
Delete this button overlay.Definition
delete()
Set this overlay's background color.Definition
setColor(r Red, g Green, b Blue, a Alpha)Arguments
| r | Red | channel [0, 1] |
| g | Green | channel [0, 1] |
| b | Blue | channel [0, 1] |
| a | Alpha | (transparency) channel [0, 1], 0 is fully transparent, 1 is opaque |
Render this overlay with the given parameters.Definition
renderButton(buttonText Text, posX Screen, posY Screen, height Button, alignment Text)Arguments
| buttonText | Text | to display as the key value, e.g. "A", "Space", "Ctrl", etc. |
| posX | Screen | x position |
| posY | Screen | y position |
| height | Button | display height |
| alignment | Text | alignment, one of the constants of RenderText.ALIGN_[...] |
Get the total display width of this button overlay for a given button text and heightDefinition
getButtonWidth()
Career Screen.
Displays available save game slots.
-- @field savegameList Save game list
Set UI waiting state, which displays or hides a dialog that says to wait.Definition
setIsWaitingForSaveGameInfo()
Character Selection Screen.
Set the index of the character. Is turned into a player and body pair.Definition
setCharacterIndex()
Update the character with the currently configured options.Definition
updateCharacterWithSettings()
Update the character to match given options. Only reloads object if needed.Definition
updateCharacter()
Loading of the character object finishedDefinition
loadCharacterFinished()
Update the existing character with options that can be changedDefinition
updateCharacterOptions()
Set the visbility of the 'hat hair': special hair when wearing a hatDefinition
setHatHairNodeVisibility()
Multiplayer chat dialog
-- @field textElement User chat message input element
Handle menu up/down input.Definition
Scrolls through the chat history.
onMenuAxisUpDown()
HUD chat window.
-- Displays chat messages.
--@category GUI
Create a new ChatWindow.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD atlas texture. |
| table | ChatWindow | instance |
Set the chat message history reference for displaying.Definition
The array is owned by the caller and must not be modified.
setChatMessages(table messages)Arguments
| table | messages | Messages array as {i={msg=<message text>, sender=<sender user nickname>}} |
Scroll chat messages by a given amount.Definition
scrollChatMessages(int delta, int numMessages)Arguments
| int | delta | Number of lines (positive or negative) to scroll |
| int | numMessages | Number of currently stored chat messages |
Handle menu visibility state change.Definition
onMenuVisibilityChange()
Update element state.Definition
update()
Draw the chat window.Definition
draw()
Set this element's UI scale.Definition
setScale()
Get this element's base background position.Definition
getBackgroundPosition(float uiScale)Arguments
| float | uiScale | Current UI scale factor |
Store scaled positioning, size and offset values.Definition
storeScaledValues()
Create the background overlay.Definition
createBackground()
Color Picker Dialog
-- Lets the player pick from a set of colors.
-- @field buttonTemplate Color button template which is cloned per color
Set colors after opening.Definition
setColors(table colors, table defaultColor)Arguments
| table | colors | Array of colors {i={r, g, b, a}, or {i={name="name", color={r,g,b,a}}} |
| table | defaultColor | Default color setting as {r, g, b, a}, must be contained in colors array |
Apply custom grid-based focus linkingDefinition
focusLinkColorButtons()
Set the initial focus when entering this dialog.Definition
setInitialFocus()
Set the dialog callback.Definition
setCallback()
Handle activation of a color button.Definition
onClickColorButton()
Player context action display element.
-- Displays information about the current interaction context. Includes action names and current input scheme button
glyphs.
--@category GUI
Create a new instance of ContextActionDisplay.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
Sets the current action context.Definition
This must be called each frame when a given context is active. The highest priority context is displayed or the one
which was set the latest if two or more contexts have the same priority.
setContext(string contextAction, string contextIconName, string targetText, int priority, string actionText)Arguments
| string | contextAction | Input action name of the context action |
| string | contextIconName | Name of the icon to display for the action context, use one of ContextActionDisplay.CONTEXT_ICON |
| string | targetText | Display text which describes the context action target |
| int | priority | [optional, default=0] Context priority, a higher number has higher priority. |
| string | actionText | [optional] Context action description, if different from context action description |
Update the context action display state.Definition
update()
Reset context state after drawing.Definition
The context must be set anew on each frame.
resetContext()
Draw the context action display.Definition
draw()
Set the scale of this element.Definition
setScale()
Store scaled positioning, size and offset values.Definition
storeScaledValues()
Get the position of the background element, which provides this element's absolute position.Definition
getBackgroundPosition(scale Current, float width)Arguments
| scale | Current | UI scale |
| float | width | Scaled background width in pixels |
| float | X | position in screen space |
| float | Y | position in screen space |
Create an empty background overlay as a base frame for this element.Definition
createBackground()
Create display components.Definition
createComponents(string hudAtlasPath, table inputDisplayManager)Arguments
| string | hudAtlasPath | Path to HUD atlas texture |
| table | inputDisplayManager | InputDisplayManager reference |
Create the input glyph element.Definition
createInputGlyph()
Create the context display frame.Definition
createFrame()
Create action context icons.Definition
Only one of these will be visible at any time.
createActionIcons()
Controls settings controller.
-- Handles the input controls model and control logic for settings screen controls pages.
--@category GUI
Create a new ControlsController instance.Definition
new()
Set a callback for displaying status messages.Definition
Callback signature: messageCallback(messageId, additionalText, addLine)
setMessageCallback(messageCallback A)Arguments
| messageCallback | A | function which takes a message ID (see class constants), a optional list of additional strings and an optional addition flag (add instead of overwrite) to display a message. |
Set a callback to react on input capture completion.Definition
Callback signature: inputDoneCallback(madeChange)
setInputDoneCallback(inputDoneCallback A)Arguments
| inputDoneCallback | A | function to notify a collaborator that input gathering has finished and if there were any changes. |
Create a display action for displaying from bindings for a given axis direction.Definition
createDisplayAction(deviceCategory Device, actionBinding Action, isAxisPositive If)Arguments
| deviceCategory | Device | category, only bindings of devices of this category are displayed |
| actionBinding | Action | binding table reference: {action=Action, bindings={1=binding1, 2=binding2, ...}} |
| isAxisPositive | If | true, the display action should show a positive action axis. |
| {name="internalName", | displayName="displayName", | inputTexts={1=text1, 2=text2, ...}, inputBindings={1=binding1, 2=binding2, ...}, positiveInput=[true|false]} |
Get available action bindings per device category, including current changes (even before saving).Definition
getDeviceCategoryActionBindings(deviceCategory Device)Arguments
| deviceCategory | Device | category name |
| List | of | DisplayActionBinding instances |
Get display text for a single mouse axis.Definition
getMouseAxisDisplayText()
Get display text for a single gamepad axis.Definition
getGamepadAxisDisplayText()
Get display text for input axis names of a binding.Definition
getBindingInputDisplayText(Binding Binding)Arguments
| Binding | Binding | reference |
| Display |
Save changed bindings to user configuration.Definition
saveChanges()
Discard any previously made binding changes.Definition
discardChanges()
Load action input bindings.Definition
loadBindings()
Handles clicking an input binding button for a given device category and action.Definition
onClickInput(deviceCategory Input, bindingId ID, actionBinding Action)Arguments
| deviceCategory | Input | device category |
| bindingId | ID | of binding on the current device. E.g. 1 for main binding, 2 for secondary binding, etc. |
| actionBinding | Action | binding table for the clicked action |
| True | if | the controller starts listening for input, false otherwise (also if it is already listening!) |
Begin waiting for input.Definition
Overrides the global input hooks keyEvent() and mouseEvent() as well as the global update() loop, depending on which
device is being scanned. Also see InputBinding:startInputCapture().
beginWaitForInput(deviceCategory Category, bindingIndex Binding, DisplayActionBinding DisplayActionBinding)Arguments
| deviceCategory | Category | of device which is being scanned for input |
| bindingIndex | Binding | (primary, secondary, etc.) index which the scanned input will be assigned to. |
| DisplayActionBinding | DisplayActionBinding | instance which describes the action binding to re-map. |
Input gathering abort event.Definition
onAbortInputGathering()
Input gathering binding deletion event.Definition
onDeleteInputBinding(DisplayActionBinding DisplayActionBinding, gatheringState {binding=DisplayActionBinding)Arguments
| DisplayActionBinding | DisplayActionBinding | reference of the binding to delete |
| gatheringState | {binding=DisplayActionBinding | reference of the binding to change, bindingIndex=Binding index} |
Input gathering keyboard input event.Definition
onCaptureKeyboardInput(keyName Key, isModifier True, inputValue 1, gatheringState {binding=DisplayActionBinding)Arguments
| keyName | Key | name as defined in Input |
| isModifier | True | if the key is used as a modifier for a combined input |
| inputValue | 1 | for pressed, 0 for inactive |
| gatheringState | {binding=DisplayActionBinding | reference of the binding to change, bindingIndex=Binding index, keyState={[keyName]=[lastValue]}} |
Input gathering mouse input event.Definition
onCaptureMouseInput(inputAxisName Mouse, isModifier True, inputValue Input, gatheringState {binding=DisplayActionBinding)Arguments
| inputAxisName | Mouse | axis or button name as defined in Input |
| isModifier | True | if the input axis is used as a modifier for a combined input |
| inputValue | Input | value of the input axis |
| gatheringState | {binding=DisplayActionBinding | reference of the binding to change, bindingIndex=Binding index, mouseState={[axisName]=[lastValue]}} |
Input gathering gamepad / controller event.Definition
onCaptureGamepadInput(deviceId Device, inputAxisName Axis, isModifier True, inputValue Input, gatheringState {binding=DisplayActionBinding)Arguments
| deviceId | Device | ID of the gamepad / controller which has received input |
| inputAxisName | Axis | or button name as defined in Input |
| isModifier | True | if the input axis is used as a modifier for a combined input |
| inputValue | Input | value of the input axis |
| gatheringState | {binding=DisplayActionBinding | reference of the binding to change, bindingIndex=Binding index, gamepadState={[deviceId]={[axisName]=[lastValue]}}} |
Stop waiting for input.Definition
Lifts the input override and notifies the screen listener.
endWaitForInput(madeChange If)Arguments
| madeChange | If | true, the player has changed at least one binding. |
Lock all UI input after capturing a new binding.Definition
lockInput()
Delete a binding from its action binding table.Definition
deleteBinding()Return Values
| bool | True | if an actual binding was deleted, false otherwise |
Assign a (new) keyboard input binding.Definition
assignKeyboardBinding(DisplayActionBinding DisplayActionBinding, bindingIndex Binding, keyames List)Arguments
| DisplayActionBinding | DisplayActionBinding | instance which holds information about the binding to be added or updated |
| bindingIndex | Binding | index to set, 1 primary, 2 secondary, etc. |
| keyames | List | of input key names to bind |
| True | if | a binding change was made, false otherwise |
Check if the given axis names contain a supported mouse combo.Definition
validateMouseCombo()
Assign a (new) mouse input binding.Definition
assignMouseBinding(DisplayActionBinding DisplayActionBinding, inputAxisNames List, inputDirection Numeric)Arguments
| DisplayActionBinding | DisplayActionBinding | instance which holds information about the binding to be added or updated |
| inputAxisNames | List | of input axis names (axes and buttons) to bind |
| inputDirection | Numeric | value which defines the physical input axis direction component to bind. Values greater than or equal to 0 will be interpreted as positive, lesser than 0 as negative. |
| True | if | a binding change was made, false otherwise |
Assign a (new) gamepad / controller input binding.Definition
assignGamepadBinding(DisplayActionBinding DisplayActionBinding, bindingIndex Binding, deviceId Gamepad, inputAxisNames List, inputDirection Numeric)Arguments
| DisplayActionBinding | DisplayActionBinding | instance which holds information about the binding to be added or updated |
| bindingIndex | Binding | index to set, 1 primary, 2 secondary, etc. |
| deviceId | Gamepad | / controller device ID of the input to bind |
| inputAxisNames | List | of input axis names (axes and buttons) to bind |
| inputDirection | Numeric | value which defines the physical input axis direction component to bind. Values greater than or equal to 0 will be interpreted as positive, lesser than 0 as negative. |
| True | if | a binding change was made, false otherwise |
Assign a (new) binding.Definition
First tries to update an existing binding. If no binding with the given parameters exists to be updated, a new
binding is created instead, unless this would lead to an number of alternative bindings exceeding the maximum.
assignBinding(deviceId Binding, displayAction Cloned, bindingIndex Intended, inputAxisNames List, isPositiveAxis If, inputDirection Numeric, previousDeviceId [optional])Arguments
| deviceId | Binding | device ID |
| displayAction | Cloned | InputAction passed back from display and capture process |
| bindingIndex | Intended | binding index, 1 primary, 2 secondary, etc. |
| inputAxisNames | List | of input axis names to bind |
| isPositiveAxis | If | true, the binding covers the positive part of the action's logical axis (always true for half-axis / button / key actions). False covers the negative part. |
| inputDirection | Numeric | value which defines the physical input axis direction component to bind. Values greater than or equal to 0 will be interpreted as positive, lesser than 0 as negative. |
| previousDeviceId | [optional] | Previous device ID of an existing binding. If not set, will use the given deviceId parameter instead. |
| True | if | binding has been assigned; reference to first colliding binding or nil |
Load default input binding settings from the game's profile template.Definition
Handle with care, this will overwrite all the player's input binding settings.
loadDefaultSettings()
Creating data grid
@param table customMt custom metatableDefinition
new()Return Values
| table | instance | instance of object |
| 19 | function DataGrid:new(numRows, numColumns, customMt) |
| 20 | local self = {} |
| 21 | setmetatable(self, customMt or DataGrid_mt) |
| 22 | |
| 23 | self.grid = {} |
| 24 | self.numRows = numRows |
| 25 | self.numColumns = numColumns |
| 26 | for i=1, numRows do |
| 27 | table.insert(self.grid, {}) |
| 28 | end |
| 29 | |
| 30 | return self |
| 31 | end |
Deletes data gridDefinition
delete()Code
| 35 | function DataGrid:delete() |
| 36 | self.grid = nil |
| 37 | end |
@param integer colIndex index of columnDefinition
getValue()Return Values
| table | value | value at the given position |
| 44 | function DataGrid:getValue(rowIndex, colIndex) |
| 45 | if rowIndex < 1 or rowIndex > self.numRows then |
| 46 | g_logManager:error("rowIndex out of bounds!") |
| 47 | printCallstack() |
| 48 | return nil |
| 49 | end |
| 50 | if colIndex < 1 or colIndex > self.numColumns then |
| 51 | g_logManager:error("colIndex out of bounds!") |
| 52 | printCallstack() |
| 53 | return nil |
| 54 | end |
| 55 | |
| 56 | return self.grid[rowIndex][colIndex] |
| 57 | end |
@param integer colIndex index of columnDefinition
setValue(table value)Arguments
| table | value | value at the given position |
| 64 | function DataGrid:setValue(rowIndex, colIndex, value) |
| 65 | if rowIndex < 1 or rowIndex > self.numRows then |
| 66 | g_logManager:error("rowIndex out of bounds!") |
| 67 | printCallstack() |
| 68 | return false |
| 69 | end |
| 70 | if colIndex < 1 or colIndex > self.numColumns then |
| 71 | g_logManager:error("colIndex out of bounds!") |
| 72 | printCallstack() |
| 73 | return false |
| 74 | end |
| 75 | |
| 76 | self.grid[rowIndex][colIndex] = value |
| 77 | return true |
| 78 | end |
Depth of field shader state.
-- This class wraps the depth of field shader parameter calls to allow convenient setting and resetting a specific
shader parameter state.
--@category GUI
Create a new depth of field state.Definition
This will record the currently set parameters which are restored when calling reset().
new(float nearCoCRadius, float nearBlurEnd, float farCoCRadius, float farBlurStart, float farBlurEnd)Arguments
| float | nearCoCRadius | Near circle of confusion radius (nearCoCRadius = 0 means no near blur (pinhole camera)) |
| float | nearBlurEnd | Distance from the camera center where near blur ends |
| float | farCoCRadius | Far circle of confusion radius (farCoCRadius = 0 means no far blur (pinhole camera)) |
| float | farBlurStart | Distance from the camera center where far blur starts |
| float | farBlurEnd | Distance from the camera center where far blur ends |
Record the current shader parameters for reset.Definition
This is called on instantiation. If another state should be recorded, call this again afterwards.
recordState()
Set one or more parameters after instantiation.Definition
If any of the parameters is omitted, the currently stored value is used.
setParameters(float nearCoCRadius, float nearBlurEnd, float farCoCRadius, float farBlurStart, float farBlurEnd)Arguments
| float | nearCoCRadius | Near circle of confusion radius (nearCoCRadius = 0 means no near blur (pinhole camera)) |
| float | nearBlurEnd | Distance from the camera center where near blur ends |
| float | farCoCRadius | Far circle of confusion radius (farCoCRadius = 0 means no far blur (pinhole camera)) |
| float | farBlurStart | Distance from the camera center where far blur starts |
| float | farBlurEnd | Distance from the camera center where far blur ends |
Apply shader parameters which were provided on instantiation.Definition
apply()
Reset the shader parameters to the state they were in when this object had been created.Definition
reset()
Create a depth of field state for a full screen blur effect.Definition
createFullscreenBlur()
Vehicle selling or customization dialog
-- Opened when activating vehicle selling points.
-- @field headerText Dialog header text element
Create a new DirectSellDialog instance.Definition
new(table target, table custom_mt, table shopConfigScreen, table messageCenter)Arguments
| table | target | DialogElement controller reference |
| table | custom_mt | [optional] Sub-class meta table for inheritance |
| table | shopConfigScreen | ShopConfigScreen reference for vehicle selling / customization |
| table | messageCenter | MessageCenter reference for local network UI event handling |
| table | DirectSellDialog | instance |
Handle "sell" button event.Definition
onClickOk()
Handle "customize" button event.Definition
onClickActivate()
Holds information about an action binding for display purposes.
-- This class is used to transfer input binding information between ControlsController and UI components.
--@category Input
Creates a new DisplayActionBinding.Definition
new(action InputAction, isPositive If, displayName Display, Binding Bindings)Arguments
| action | InputAction | of this binding |
| isPositive | If | true, this display action binding shows the binding for the positive input axis (always true for half-axis actions) |
| displayName | Display | name of this action binding for the given input axis direction. |
| Binding | Bindings | associated with the input axis component for the given action |
Set display information for binding.Definition
setBindingDisplay(Binding Binding, text Binding, column Display)Arguments
| Binding | Binding | reference contained in this instance |
| text | Binding | display text (button/key/axes names) |
| column | Display | column index (1 for primary, 2 for secondary, etc) |
| integer | numPixels | number of pixels |
| integer | totalNumPixels | total number of pixels |
Farm edit and create dialog.
-- Lets a player edit a farm's properties or create a new farm.
-- @field buttonTemplate Color button template which is cloned per color
Create a new instance of EditFarmDialog.Definition
new(table target, table custom_mt, table l10n, table farmManager)Arguments
| table | target | Optional input target override |
| table | custom_mt | Sub-class metatable |
| table | l10n | I18N reference for localization |
| table | farmManager | FarmManager reference for farm data access |
Set existing farm ID whose properties are to be modified.Definition
If no farm ID is given, a new farm will be created.
setExistingFarm()
Find available farm colors and store them in fields.Definition
storeAvailableColors()
Handle activation of a color button.Definition
Shadows parent method which exits the dialog right away.
onClickColorButton()
Handle dialog confirmation click / activation.Definition
Depending on the mode, this will create a new farm or update an existing one.
onClickActivate()
Permission types.
Merge another farm into this farm. Used for creating an SP game from an MP game. (this is mutating)Definition
merge(table other)Arguments
| table | other | Another Farm |
Get the farmhouse associated with the farm.Definition
getFarmhouse()Return Values
| table | farmhouse | or nil |
Get the spawnpoint associated with the farm(house).Definition
getSpawnPoint()Return Values
| integer | spawnpoint | node or the career spawnpoint node. |
Get the sleep camera.Definition
getSleepCamera()Return Values
| integer | Camera | or 0 if no farmhouse. |
Get a list of active users. Useful for using their connection IDDefinition
getActiveUsers()
Determine if a user is a manager of this farm.Definition
isUserFarmManager(userId User)Arguments
| userId | User | ID |
| bool | True | if the user is a manager of this farm, false otherwise |
Get the farm permissions of a user.Definition
getUserPermissions(userId User)Arguments
| userId | User | ID |
| table | Permission | hash table {permission=<hasPermission>} |
Set a user's permission in this farm.Definition
setUserPermission(userId User, string permission, bool hasPermission)Arguments
| userId | User | ID |
| string | permission | Permission key from Farm.PERMISSION |
| bool | hasPermission | True if the permission is to be granted, false to be denied |
Promote a user to farm manager.Definition
promoteUser()
Demote a user from farm manager.Definition
demoteUser()
Update contracting statusDefinition
setIsContractingFor(noSendEvent boolean)Arguments
| noSendEvent | boolean | Send no event, forces setting of actual value without server feedback |
Add or remove money from the farmDefinition
changeBalance(number amount)Arguments
| number | amount | Amount to add (positive) or remove (negative) |
Get the current account balance of the farm.Definition
getBalance()Return Values
| float | Account | balance |
Get the current loan of the farm.Definition
getLoan()Return Values
| float |
Get a list of filenames for accessible handtoolsDefinition
getHandTools()
Add a new user to the farm. This adds it to the players and active players list.Definition
And also gives default permissions.
addUser()
Remove a user from the farm.Definition
removeUser()
Called when a user joins the game. Active users is updated, and for spectator.Definition
a new user might be added. Server only.
onUserJoinGame()
Called when a user quits the game. The active user list is updated. Server only.Definition
onUserQuitGame()
Multiplayer
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 27 | function FarmManager:new(customMt) |
| 28 | local self = AbstractManager:new(customMt or FarmManager_mt) |
| 29 | |
| 30 | return self |
| 31 | end |
Load data on map loadDefinition
loadMapData()Return Values
| boolean | true | if loading was successful else false |
| 43 | function FarmManager:loadMapData(xmlFile) |
| 44 | FarmManager:superClass().loadMapData(self) |
| 45 | |
| 46 | if g_currentMission:getIsServer() then |
| 47 | g_currentMission:addUpdateable(self) |
| 48 | |
| 49 | -- Create spectator farm |
| 50 | local spectatorFarm = Farm:new(true, g_client ~= nil, nil, true) |
| 51 | spectatorFarm.farmId = FarmManager.SPECTATOR_FARM_ID |
| 52 | spectatorFarm.isSpectator = true |
| 53 | |
| 54 | spectatorFarm:register() |
| 55 | |
| 56 | table.insert(self.farms, spectatorFarm) |
| 57 | self.farmIdToFarm[spectatorFarm.farmId] = spectatorFarm |
| 58 | end |
| 59 | |
| 60 | addConsoleCommand("gsSetFarm", "Set farm for current player or vehicle", "consoleCommandSetFarm", self) |
| 61 | |
| 62 | if g_addTestCommands then |
| 63 | addConsoleCommand("debugCreateFarm", "Create a new farm", "consoleCommandCreateFarm", self) |
| 64 | end |
| 65 | end |
Unload data on mission deleteDefinition
unloadMapData()Code
| 69 | function FarmManager:unloadMapData() |
| 70 | g_currentMission:removeUpdateable(self) |
| 71 | |
| 72 | removeConsoleCommand("gsSetFarm") |
| 73 | if g_addTestCommands then |
| 74 | removeConsoleCommand("debugCreateFarm") |
| 75 | end |
| 76 | |
| 77 | FarmManager:superClass().unloadMapData(self) |
| 78 | end |
Write field mission data to savegame fileDefinition
saveToXMLFile(string xmlFilename)Arguments
| string | xmlFilename | file path |
| boolean | true | if loading was successful else false |
| 84 | function FarmManager:saveToXMLFile(xmlFilename) |
| 85 | local xmlFile = createXMLFile("farmsXML", xmlFilename, "farms"); |
| 86 | |
| 87 | local index = 0 |
| 88 | for i, farm in ipairs(self.farms) do |
| 89 | if farm.farmId ~= 0 then |
| 90 | local key = string.format("farms.farm(%d)", index) |
| 91 | |
| 92 | farm:saveToXMLFile(xmlFile, key) |
| 93 | index = index + 1 |
| 94 | end |
| 95 | end |
| 96 | |
| 97 | saveXMLFile(xmlFile) |
| 98 | delete(xmlFile) |
| 99 | end |
Load fieldjob data from xml savegame fileDefinition
loadFromXMLFile(string filename)Arguments
| string | filename | xml filename |
| 104 | function FarmManager:loadFromXMLFile(xmlFilename) |
| 105 | if xmlFilename == nil then |
| 106 | self:loadDefaults() |
| 107 | return false |
| 108 | end |
| 109 | |
| 110 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 111 | if not xmlFile then |
| 112 | return false |
| 113 | end |
| 114 | |
| 115 | local i = 0 |
| 116 | while true do |
| 117 | local key = string.format("farms.farm(%d)", i) |
| 118 | if not hasXMLProperty(xmlFile, key) then |
| 119 | break |
| 120 | end |
| 121 | |
| 122 | local farm = Farm:new(true, g_client ~= nil) |
| 123 | |
| 124 | if not farm:loadFromXMLFile(xmlFile, key) then |
| 125 | farm:delete() |
| 126 | else |
| 127 | farm:register() |
| 128 | |
| 129 | table.insert(self.farms, farm) |
| 130 | self.farmIdToFarm[farm.farmId] = farm |
| 131 | end |
| 132 | |
| 133 | i = i + 1 |
| 134 | end |
| 135 | |
| 136 | self:mergeFarmsForSingleplayer() |
| 137 | |
| 138 | -- Emulate an MP player join for local MP or SP game |
| 139 | if g_currentMission:getIsClient() then |
| 140 | local uniqueUserId = g_currentMission.missionDynamicInfo.isMultiplayer and getUniqueUserId() or FarmManager.SINGLEPLAYER_UUID |
| 141 | self:playerJoinedGame(uniqueUserId, g_currentMission:getServerUserId()) |
| 142 | end |
| 143 | |
| 144 | g_fieldManager:updateFieldOwnership() |
| 145 | |
| 146 | delete(xmlFile) |
| 147 | |
| 148 | return true |
| 149 | end |
Second step of merging: transfer all lands to the singleplayer farmDefinition
mergeFarmlandsForSingleplayer()
Third step of merging: move vehicles and bales to singleplayer farmDefinition
mergeObjectsForSingleplayer()
Deletes field mission managerDefinition
delete()Code
| 236 | function FarmManager:delete() |
| 237 | end |
Updates field mission ownage data from xml savegame fileDefinition
update(string filename)Arguments
| string | filename | xml filename |
| 242 | function FarmManager:update(dt) |
| 243 | if g_currentMission:getIsClient() then |
| 244 | if self.spFarmWasMerged and not self.mergedMessageShown then |
| 245 | g_gui:showInfoDialog({visible=true, text=g_i18n:getText("ui_farmedMergedSP"), dialogType=DialogElement.TYPE_INFO, isCloseAllowed=true}) |
| 246 | self.mergedMessageShown = true |
| 247 | end |
| 248 | end |
| 249 | end |
Get farm for given userId. To be used when player is not in the gameDefinition
getFarmForUniqueUserId()
Get farm for given userId. To be used when player is in the game.Definition
getFarmByUserId()
On client, update the list of farms and set farm for given farmIdDefinition
updateFarms()
Get the array of known farms.Definition
Callers should not modify this array.
getFarms()
Transfer an amount of money from the current user's farm to a destination farm.Definition
Triggers a network event which checks farm balances and applies the change. Successful execution requires the current
user to have permission to transfer money as well as their current farm to have a sufficient balance.
transferMoney()
Remove player from their farm. Only works if the caller has permission (master user, farm manager)Definition
removeUserFromFarm()
Farm has been destroyed. Remove from listsDefinition
removeFarm()
HUD field information display element.
-- Displays dynamic information about the field which the player is currently standing in (or close by).
--@category GUI
Create a new instance of FieldInfoDisplay.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
Set up rows data structures.Definition
setupRows()
Set the local player reference.Definition
setPlayer()
Set the current fruit type to display.Definition
setFruitType(int fruitTypeIndex, int fruitGrowthState)Arguments
| int | fruitTypeIndex | Index of the field fruit type or a number <= 0 to clear the fruit type. |
| int | fruitGrowthState | Current growth state of the given fruit type |
Set the field ownerDefinition
setOwnerFarmId(int ownerFarmId)Arguments
| int | ownerFarmId | Current owner farm id |
Set the fertilization factor info to display.Definition
setFertilization(float fertilizationFactor)Arguments
| float | fertilizationFactor | Current fertilization factor to display, this will be converted to a percentage. Values < 0 clear this info. |
Set the weed factor info to display.Definition
setWeed(float fertilizationFactor)Arguments
| float | fertilizationFactor | Current weed factor to display, this will be converted to a percentage. Values < 0 clear this info. |
Set the plowing required display.Definition
setPlowingRequired(bool isRequired)Arguments
| bool | isRequired | If true, will display that the current field needs to be plowed. Otherwise, the info is hidden. |
Set the lime required display.Definition
setLimeRequired(bool isRequired)Arguments
| bool | isRequired | If true, will display that the current field needs lime. Otherwise, the info is hidden. |
Add a custom text row.Definition
The custom text will be added in order of calls to this function after the default information.
addCustomText(string leftText, string rightText, table leftColor)Arguments
| string | leftText | Text to be displayed on the left side in bold print |
| string | rightText | [optional] Text to be displayed on the right side in regular print |
| table | leftColor | [optional, default=<white>] Color of left text as an array {r, g, b, a} |
| int | Display | row index of the newly added custom text or 0 if it could not be added |
Clear a custom text row previously added by FieldInfoDisplay:addCustomText().Definition
clearCustomText(int rowIndex)Arguments
| int | rowIndex | [optional] Custom text row index as returned by FieldInfoDisplay:addCustomText(). If no value is provided, all custom text is cleared. |
Clear a single info row's data.Definition
clearInfoRow()
Clear all previously set field data.Definition
This does not clear custom text rows, which need to be cleared specifically using clearCustomText().
clearFieldData()
Called when FSDensityMapUtil.getFieldStatusAsync() in update() has finished.Definition
onFieldDataUpdateFinished(table data)Arguments
| table | data | Field information data as provided by processing in FSDensityMapUtil.getFieldStatusAsync() |
Update the info display size depending on used rows.Definition
updateSize()
Draw the display.Definition
draw()
Draw text parts of this display element.Definition
drawText()
Get the scaled background position.Definition
getBackgroundPosition()
Set this element's UI scale factor.Definition
setScale(float uiScale)Arguments
| float | uiScale | UI scale factor |
Store scaled position and size values.Definition
storeScaledValues()
Create the background overlay.Definition
createBackground()
Create required display components.Definition
createComponents()
Create the background frame element.Definition
createFrame()
Create row list container.Definition
createRowListContainer()
Create row separators.Definition
createSeparators()
Vehicle HUD fill levels display element.
-- Displays fill level bars for the current vehicle configuration
--@category GUI
Creates a new FillLevelsDisplay instance.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas. |
Set the currently controlled vehicle which provides display data.Definition
setVehicle(table vehicle)Arguments
| table | vehicle | Currently controlled vehicle |
Update fill levels data.Definition
updateFillLevelBuffers()
Update fill level frames display state.Definition
updateFillLevelFrames()
Update the fill levels state.Definition
update()
Draw this element.Definition
draw()
Set this element's scale.Definition
setScale()
Get the position of the background element, which provides this element's absolute position.Definition
getBackgroundPosition(scale Current, float width)Arguments
| scale | Current | UI scale |
| float | width | Scaled background width in pixels |
| float | X | position in screen space |
| float | Y | position in screen space |
Calculate and store scaling values based on the current UI scale.Definition
storeScaledValues()
Create an empty background overlay as a base frame for this element.Definition
createBackground()
Refresh fill type data and elements.Definition
refreshFillTypes(table fillTypeManager)Arguments
| table | fillTypeManager | FillTypeManager reference |
Create fill type frames for all known fill types.Definition
createFillTypeFrames()
Create a fill type frame for the display of a fill type level state.Definition
createFillTypeFrame()
Create an icon for a fill type.Definition
createFillTypeIcon()
Create a fill type bar used to display a fill level.Definition
The newly created bar is added to the given parent frame and an internal collection indexable by fill type index.
createFillTypeBar(string hudAtlasPath, table frame, float baseX, float baseY, table fillType)Arguments
| string | hudAtlasPath | Path to HUD texture atlas |
| table | frame | Parent frame HUD element |
| float | baseX | Origin X position in screen space |
| float | baseY | Origin Y position in screen space |
| table | fillType | Fill type whose fill level is represented by the created bar |
The FocusManager controls which element in the menu system is currently focused
and allows menu control with only keyboard or gamepad.
For each participating gui element the focus state and the next focused gui element
in each direction is stored. This data is set up directly in the xml file of the gui screen
and loaded through the loadElementFromXML() function or manually loaded within code by using
the loadElementFromCustomValues() method.
Focus handling is independent for every screen in the GUI. To swap screens the setGui() method
has to be used.
The focus system is then controlled with 5 actions: MENU_UP, MENU_DOWN, MENU_RIGHT and MENU_LEFT to
change the currently focused element in the specified direction and MENU_ACCEPT to activate
the currently focused element.
-- When using dynamically changing objects which cannot be set directly in the XML file of the screen
the method createLinkageSystemForElements() can be used to set up direction links between the
passed elements automatically.
--@category GUI
Set the active GUI for focus input.Definition
setGui(Gui Screen)Arguments
| Gui | Screen | root GuiElement |
Get a focusable GuiElement in the current view by its ID.Definition
getElementById()
Get the currently focused GuiElementDefinition
getFocusedElement()
Get a new automatic focus ID.Definition
It's based on a simple integer increment and will be unique unless billions of elements require an ID.
serveAutoFocusId()
Load GuiElement focus data from its XML definition.Definition
This is called at the end of GuiElement:loadFromXML().
loadElementFromXML()
Add an element to the focus system with custom values.Definition
The caller should ensure that explicitly set focus IDs are unique. If a duplicate ID is encountered, only the first
element with that focus ID is considered for focusing. The method returns a boolean value to indicate any problems
with data assignment. Callers can evaluate the value to check if the given parameters were valid. If in doubt or
when no elaborate focus navigation is needed, rely on automatic focus ID generation by omitting the ID parameter
(or set it to nil).
loadElementFromCustomValues(element Element, focusId Focus, focusChangeData Custom, focusActive If, isAlwaysFocusedOnOpen If)Arguments
| element | Element | to add to focus system |
| focusId | Focus | ID for element |
| focusChangeData | Custom | focus navigation data for the element (map of direction to focus ID) |
| focusActive | If | true, the element should be focused right now |
| isAlwaysFocusedOnOpen | If | true, the element is supposed to be focused when its parent view is opened. |
| True | if | the element and all of its children could be set up with the given values, false otherwise. |
Remove a GuiElement from the current focus context.Definition
removeElement()
Links an element's focus navigation to another element for a given direction.Definition
The link is unidirectional from source to target. If bi-directional links are desired, call this method again with
swapped arguments.
linkElements(sourceElement Source, direction Navigation, targetElement Target)Arguments
| sourceElement | Source | element which receives the focus link. |
| direction | Navigation | direction for the link, is not required to be the actual visual direction. |
| targetElement | Target | element |
Handles input and changes focus if required and possible.Definition
inputEvent(action Name, value Input, eventUsed Usage)Arguments
| action | Name | of navigation action which triggered the event, see InputAction |
| value | Input | value [-1, 1] |
| eventUsed | Usage | flag, no action is taken if this is true |
| True | if | the input event has been consumed, false otherwise |
Get a direction value for a given menu input action and valueDefinition
getDirectionForAxisValue()
Checks if the focus manager has an input lock on input.Definition
isFocusInputLocked(inputAxis InputAction, value Axis, True if)Arguments
| inputAxis | InputAction | axis or action code |
| value | Axis | value [-1, 1] or nil if not a directional axis |
| True | if | locked, false otherwise |
Locks a given input axis action's input for a time. Until the delay has passed, the focus manager will not react toDefinition
that input.
lockFocusInput(inputAxis InputAction, delay Delay, value Axis)Arguments
| inputAxis | InputAction | axis or action code |
| delay | Delay | in ms |
| value | Axis | value [-1, 1], only relevant to identify directional axes |
Release a focus movement input lock on an action.Definition
Called by the UI input handling code. Avoid calling this for anything else.
releaseMovementFocusInput(action Focus)Arguments
| action | Focus | movement input action name |
Reset all locks of focus input.Definition
resetFocusInputLocks()
Given a point and bounding box, get the closest other point on the bounding box circumference. If the point liesDefinition
within the bounding box, it is returned unchanged.
getClosestPointOnBoundingBox(x Point, y Point, boxMinX Bounding, boxMinY Bounding, boxMaxX Bounding, boxMaxY Bounding)Arguments
| x | Point | X |
| y | Point | Y |
| boxMinX | Bounding | box minimum point X |
| boxMinY | Bounding | box minimum point Y |
| boxMaxX | Bounding | box maximum point X |
| boxMaxY | Bounding | box maximum point Y |
| Closest | point | x, y |
Calculate the shortest connecting line segment between two bounding boxes. Overlapping boxes will result in flippedDefinition
directions, so take care.
getShortestBoundingBoxVector()
Checks the distance between two GuiElements with the aim of incrementally finding the closest other element in aDefinition
direction within a screen view.
checkElementDistance(curElement Current, other Other, dirX Scan, dirY Scan, curElementOffsetY Position, closestOther Previously, closestDistanceSq Squared)Arguments
| curElement | Current | checking GuiElement |
| other | Other | GuiElement to compare |
| dirX | Scan | direction vector x component, normalized to unit length |
| dirY | Scan | direction vector y component, normalized to unit length |
| curElementOffsetY | Position | y offset of current element's bounding volume, used when checking for wrap-around |
| closestOther | Previously | closest other GuiElement |
| closestDistanceSq | Squared | distance from the current checking element to the previously closest other GuiElement |
Find the next other element to the one provided in a given navigation directionDefinition
getNextFocusElement(element GUI, direction Direction)Arguments
| element | GUI | element which needs a focus link |
| direction | Direction | constant [TOP | BOTTOM | LEFT | RIGHT] |
| Next | GUI | element in given direction which can be linked, actual scanning direction used (may change in wrap around scenarios) |
Get an element's focus target at the deepest nesting depth, e.g. when multiple nested layouts point down to theirDefinition
child elements until only a single element is left which points to itself.
getNestedFocusTarget(element GuiElement, direction Focus)Arguments
| element | GuiElement | whose focus target needs to be retrieved |
| direction | Focus | navigation direction |
| Focus |
Update the current focus target.Definition
updateFocus(element GuiElement, isFocusMoving Only, direction Focus, updateOnly If)Arguments
| element | GuiElement | which should be the new focus target |
| isFocusMoving | Only | move focus if this is true |
| direction | Focus | navigation movement direction, one of FocusManager.[TOP | BOTTOM | LEFT | RIGHT] |
| updateOnly | If | true, only updates the lock state of focus movement for the given parameters |
Activate a highlight on an element. Highlighted elements are only visually marked and do not receive focus activation.Definition
Only one element will be highlighted at any time, usually corresponding to the current mouse over target.
setHighlight(element Element)Arguments
| element | Element | to be highlighted. |
Remove highlight status from an element.Definition
unsetHighlight(element Highlighted)Arguments
| element | Highlighted | element to revert |
Set focus on a GuiElement or its focus target.Definition
Applies overlay state and triggers onFocusEnter() on the target.
setFocus(element Element, direction Focus, ... Variable)Arguments
| element | Element | whose focus target (usually itself) receives focus. |
| direction | Focus | navigation direction |
| ... | Variable | arguments to pass on to the onFocusEnter callback of the target element |
| True | if | focus has changed, false otherwise |
Removes focus from an element.Definition
Applies overlay state and triggers onFocusLeave() on the target.
unsetFocus(element Element, ... Variable)Arguments
| element | Element | which should lose focus |
| ... | Variable | arguments to pass on to the onFocusLeave callback of the target element |
Set an elements focus overlay state for displaying.Definition
setElementFocusOverlayState(element Target, isFocus If, handlePreviousState [optional])Arguments
| element | Target | element |
| isFocus | If | true, the element's state will be set to focused. Otherwise, it's state will be either restored or set to normal. |
| handlePreviousState | [optional] | If true or undefined, makes the element store its previous overlay state before modification or restore it when isFocused is false. |
Globally lock focus input.Definition
requireLock()
Release the global focus input lock.Definition
releaseLock()
Check if focus input is locked.Definition
isLocked()
Determine if focus navigation in a given direction is currently locked.Definition
isDirectionLocked(direction Navigation)Arguments
| direction | Navigation | direction as defined in constants |
| True | if | navigation in given direction is locked |
Determine if a GuiElement is currently focused.Definition
hasFocus()
Get a closure override function for elements' getFocusOverride() methods.Definition
getFocusOverrideFunction(forDirections List, substitute Element, useSubstituteForFocus (Optional))Arguments
| forDirections | List | of directions to override |
| substitute | Element | to substitute as focus target in overridden direction |
| useSubstituteForFocus | (Optional) | If true, the substitute parameter will be used as the origin for finding the next focus target in the overridden direction. |
Base display frame element. All GUI views (partial and full screen) inherit from this.
-- This element provides the functionality to register control IDs, which are then exposed as fields of the concrete
descendant class (e.g. MainScreen or PasswordDialog). The control IDs must be assigned verbatim to any control
in the corresponding configuration XML file. If a registered ID is not used, the field will not be assigned and
access will fail. Available field IDs are documented as field properties per class. When creating a new view, take
care to include the call to registerControls() in the constructor to declare and expose control elements as fields.
--@category GUI
Override of GuiElement:clone().Definition
Also exposes registered control element fields.
clone()
Override of GuiElement:copyAttributes().Definition
Also resets registered control IDs so they can be exposed as fields again.
copyAttributes()
Get the frame's root GuiElement instance.Definition
This is the first and only direct child of a FrameElement, as defined by the GUI instantiation logic. This method
will always return a GuiElement instance, even if a new one must be created first.
getRootElement()
Register a collection of control IDs for direct access in GUI views.Definition
registerControls(controlIDs Table)Arguments
| controlIDs | Table | which holds control IDs as values, as they are required to be present in the view configuration. |
Adds registered controls as fields to this FrameElement instance.Definition
Called by the GUI system after loading.
The new fields will have the same name as the registered ID, so make sure there are no collision to avoid overrides
and that IDs are also valid as identifiers in Lua. If a control has been registered but no corresponding element is
available (e.g. when sub-classing and omitting some elements), the field will remain undefined. It's up to callers
to ensure that field configuration and usage in views matches.
exposeControlsAsFields(viewName View)Arguments
| viewName | View | name of this frame element |
Set input disabling to a given duration.Definition
disableInputForDuration(float duration)Arguments
| float | duration | Input disabling duration in milliseconds |
Check if input is currently disabled.Definition
isInputDisabled()
Set a callback for requesting a view change from within a frame or screen view.Definition
setChangeScreenCallback(func callback)Arguments
| func | callback | Function reference, signature: function(sourceFrameElement, targetScreenClass, returnScreenClass) |
Set a callback function for requesting a custom menu input context for this frame.Definition
setInputContextCallback(func callback)Arguments
| func | callback | Function reference, signature: function(isContextActive) |
Set a callback function for requesting to play a sound sample.Definition
setPlaySampleCallback(func callback)Arguments
| func | callback | Function reference, signature: function(sampleName) |
Request a view change via the callback defined by setChangeScreenCallback().Definition
changeScreen(table targetScreenClass)Arguments
| table | targetScreenClass | Class table of requested view (ScreenElement descendant, must be full view) |
Request toggling of a custom menu input context for this frame via the callback defined by setInputContextCallback().Definition
toggleCustomInputContext(bool isContextActive, string contextName)Arguments
| bool | isContextActive | If true, will activate a custom menu input context. Otherwise, will clear a previously activated context. |
| string | contextName | Name of the custom input context. Use a unique identifier value. |
Request playing a sound sample identified by name.Definition
playSample(string sampleName)Arguments
| string | sampleName | Sample name, use one of GuiSoundPlayer.SOUND_SAMPLES |
(Un)loading stations
Initialize mission after instantiation.Definition
Create complex members and call dependency methods in here so that mission instantiation cannot fail.
initialize()
Sets harvest ratios for fertilizer, plow, lime and weed factorDefinition
setHarvestScaleRatio(float sprayRatio, float plowRatio, float limeRatio, float weedRatio)Arguments
| float | sprayRatio | fertilizer ratio |
| float | plowRatio | plow ratio |
| float | limeRatio | lime ratio |
| float | weedRatio | weed ratio |
Get harvest multiplier based on fertilizer, plow, lime and weed factor, 1 = bestDefinition
getHarvestScaleMultiplier(integer fruitTypeIndex, float sprayFactor, float plowFactor, float limeFactor, float weedFactor)Arguments
| integer | fruitTypeIndex | fruit type index |
| float | sprayFactor | fertilizer factor |
| float | plowFactor | plow factor |
| float | limeFactor | lime factor |
| float | weedFactor | weed factor |
| float | multiplier | harvest multiplier |
Check which vehicles are accessible and notify the menu.Definition
updateMenuAccessibleVehicles()
Add an item to the owned items collection.Definition
Also notifies the shop.
addOwnedItem()
Remove an item from the owned items collection.Definition
Also notifies the shop.
removeOwnedItem()
Add an item to the leased items collection.Definition
Also notifies the shop.
addLeasedItem()
Remove an item from the leased items collection.Definition
Also notifies the shop.
removeLeasedItem()
Set or revert the pause input context based on current state.Definition
updatePauseInputContext()
Update saving process.Definition
Notifies the UI to display dialogs.
updateSaving()
Savegame device selection confirmation dialog response callback.Definition
onYesNoSavegameSelectDevice()
Savegame overwrite completion callback.Definition
onSaveGameUpdateComplete(int errorCode)Arguments
| int | errorCode | Saving process status code, one of Savegame.ERROR_... |
Savegame overwrite confirmation dialog response callback.Definition
onYesNoSavegameOverwrite()
Display vehicle attachment context information for the current frame.Definition
showAttachContext()
Display vehicle tipping context information for the current frame.Definition
showTipContext()
Display vehicle refueling context information for the current frame.Definition
showFuelContext()
Display dog companion food bowl fill context information for the current frame.Definition
showFillDogBowlContext()
Get money for farm or current player farmDefinition
getMoney(farmId integer)Arguments
| farmId | integer | if nil, then current farm (client only) |
Get whether the current player has given permission.Definition
getHasPlayerPermission(string permission, table connection, integer farmId, boolean checkClient)Arguments
| string | permission | Permission to check |
| table | connection | [optional] Client connection to check permission for |
| integer | farmId | [optional] Limit permission to given farm (otherwise any farm) |
| boolean | checkClient | [optional] if true it will only check the client permission |
Get the in-game map rendering component.Definition
Should only be used for UI.
getIngameMap()
Start saving the game.Definition
startSaveCurrentGame(bool isDediSaving)Arguments
| bool | isDediSaving | If true, this is a saving call on a dedicated server |
Actually save the game locally.Definition
saveSavegame()
Returns a doghouseDefinition
getDoghouse(integer farmId)Arguments
| integer | farmId |
| table | instance | of the doghouse |
| 3144 | function FSBaseMission:getDoghouse(farmId) |
| 3145 | for _, doghouse in pairs(self.doghouses) do |
| 3146 | if doghouse:getOwnerFarmId() == farmId then |
| 3147 | return doghouse |
| 3148 | end |
| 3149 | end |
| 3150 | return nil |
| 3151 | end |
Register a husbandry in the mission.Definition
registerHusbandry()
Unregister a husbandry in the mission.Definition
unregisterHusbandry()
Extract a concrete vehicle's display name from its store data.Definition
getVehicleName(table vehicle)Arguments
| table | vehicle | Vehicle instance |
| string | Vehicle | display name |
Register required input action events.Definition
registerActionEvents()
Register action events for pause actions.Definition
Event registration in this method is enclosed in an input binding registration context (InputBinding:beginActionEventsModification()).
Make sure that this is only called when all other registration-context altering code is done.
registerPauseActionEvents()
Input event for "open menu".Definition
onToggleMenu()
Input event for "open shop".Definition
onToggleStore()
Toggle chat display.Definition
Called from input to open the chat dialog and from the chat dialog to close it again.
toggleChat()
Input event for "toggle radio".Definition
onToggleRadio()
Input event for "increase timescale" and "decrease timescale"Definition
onChangeTimescale()
Handle a settings change for showing help icons.Definition
onShowHelpIconsChanged()
Handle a settings change for the radio vehicle only setting.Definition
If the current radio state is active, the radio will start playing if the context fits (e.g. turn to global, and
player is on foot).
onRadioVehicleOnlyChanged()
Handle a settings change for the radio active state.Definition
onRadioIsActiveChanged()
Set the active state for the radio action events.Definition
setRadioActionEventsState()
Subscribe to FS-specific event messages.Definition
The BaseMission parent class unsubscribes from messages in delete(). No clean-up required in sub-classes like this.
subscribeMessages()
Handle a local client changing the farm.Definition
notifyPlayerFarmChanged()
HUD general game information display.
-- Displays current game information. This includes weather, current account balance and time settings.
--@category GUI
Create a new instance of GameInfoDisplay.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
Set the money unit for displaying the account balance.Definition
setMoneyUnit(int moneyUnit)Arguments
| int | moneyUnit | Money unit ID, any of [GS_MONEY_EURO | GS_MONEY_POUND | GS_MONEY_DOLLAR]. Invalid values are substituted by GS_MONEY_DOLLAR. |
Set the MissionStats reference for displaying information.Definition
setMissionStats(table missionStats)Arguments
| table | missionStats | MissionStats reference, do not change |
Set the mission information reference for base information display.Definition
setMissionInfo(table missionInfo)Arguments
| table | missionInfo | MissionInfo reference, do not change |
Set the environment reference to use for weather information display.Definition
setEnvironment(table environment)Arguments
| table | environment | Environment reference, do not change |
Set visibility of the money display.Definition
setMoneyVisible()
Set visibility of time display.Definition
setTimeVisible()
Set visibility of temperature display.Definition
setTemperatureVisible()
Set visibility of weather display.Definition
setWeatherVisible()
Set visibility of tutorial progress display.Definition
setTutorialVisible()
Set the current tutorial progress values.Definition
setTutorialProgress(float progress)Arguments
| float | progress | Progress expressed as a number between 0 and 1 |
Update the game info display state.Definition
update()
Update time display.Definition
updateTime()
Update temperature display.Definition
updateTemperature()
Update weather displayDefinition
updateWeather()
Get the game info display's width based on its visible info boxes.Definition
getVisibleWidth()Return Values
| float | Game | info display width of visible elements in screen space |
Update sizes and positions of this elements and its children.Definition
updateSizeAndPositions()
Draw the game info display.Definition
draw()
Draw the text part of the money display.Definition
drawMoneyText()
Draw the text part of the time display.Definition
drawTimeText()
Draw the text part of the temperature display.Definition
drawTemperatureText()
Draw the text part of the tutorial progress display.Definition
drawTutorialText()
Make an animation for a weather change.Definition
animateWeatherChange()
Animate a weather icon becoming active.Definition
addActiveWeatherAnimation()
Animate a weather icon becoming inactive.Definition
addInactiveWeatherAnimation()
Animate a weather icon becoming the current weather icon.Definition
addBecomeCurrentWeatherAnimation()
Animate weather icon position changes.Definition
addWeatherPositionAnimation()
Get this element's base background position.Definition
getBackgroundPosition(float uiScale)Arguments
| float | uiScale | Current UI scale factor |
Set this element's UI scale factor.Definition
setScale(float uiScale)Arguments
| float | uiScale | UI scale factor |
Store scaled positioning, size and offset values.Definition
storeScaledValues()
Create the background overlay.Definition
createBackground()
Create required display components.Definition
Also adds a separator HUDElement instance as a field (".separator") to all info boxes.
createComponents()
Create the money display box.Definition
createMoneyBox()
Create the time display box.Definition
createTimeBox()
Create the temperature display box.Definition
createTemperatureBox()
Create the weather display box.Definition
createWeatherBox()
Create a weather icon for current and upcoming weather conditions.Definition
createWeatherIcon(string hudAtlasPath, int weatherId, float boxHeight, table uvs, table color)Arguments
| string | hudAtlasPath | Path to HUD texture atlas |
| int | weatherId | Weather condition ID, as defined in WeatherType... constants |
| float | boxHeight | Screen space height of the box which will hold this icon |
| table | uvs | UV coordinates of the weather icon in the HUD texture atlas |
| table | color | Color RGBA array |
| table | Weather | icon HUDElement instance |
Create a temperature icon to display stable or changing temperatures.Definition
createTemperatureIcon(string hudAtlasPath, float leftX, float bottomY, float boxHeight, table uvs, table color)Arguments
| string | hudAtlasPath | Path to HUD texture atlas |
| float | leftX | Screen space left X position of newly created icon |
| float | bottomY | Screen space bottom Y position of the parent box |
| float | boxHeight | Screen space height of the parent box |
| table | uvs | UV coordinates of the icon in the HUD texture atlas |
| table | color | Color RGBA array |
| table | Temperature | icon HUDElement instance |
Create a rotatable clock hand icon element.Definition
createClockHand(string hudAtlasPath, float posX, float posY, table size, table uvs, table color, table pivot)Arguments
| string | hudAtlasPath | Path to HUD texture atlas |
| float | posX | Screen space X position of the clock hand |
| float | posY | Screen space Y position of the clock hand |
| table | size | Pixel size vector {width, height} |
| table | uvs | UV coordinates of the icon in the HUD texture atlas |
| table | color | Color RGBA array |
| table | pivot | UV pixel space rotation pivot coordinates |
| table | Clock | hand HUDElement instance |
Create a time scale arrow icon element.Definition
createTimeScaleArrow(string hudAtlasPath, float posX, float posY, table size, table uvs)Arguments
| string | hudAtlasPath | Path to HUD texture atlas |
| float | posX | Screen space X position of the arrow |
| float | posY | Screen space Y position of the arrow |
| table | size | Pixel size vector {width, height} |
| table | uvs | UV coordinates of the icon in the HUD texture atlas |
| table | Time | scale arrow icon HUDElement instance |
Create and return a vertical separator element.Definition
createVerticalSeparator()
Create the tutorial progress box.Definition
createTutorialBox()
Sign-In Screen before Main Menu.
-- Used in console version.
-- @field startText Button prompt text to start the game.
GUI input event callback.Definition
See GuiElement:inputEvent().
inputEvent()
Event function for button sign in.Definition
signIn()
HUD game pause display element.
-- Displays a customizable message when the game is paused.
--@category GUI
Create a new GamePausedDisplay.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD atlas texture. |
| table | GamePausedDisplay | instance |
Set a custom text to display.Definition
setPauseText()
Handle menu visibility state change.Definition
onMenuVisibilityChange()
Set uniform UI scale.Definition
setScale()
Store scaled positioning, size and offset values.Definition
storeScaledValues()
Get this element's base background position.Definition
createBackground()
Create required display components.Definition
createComponents()
Graphical User Interface controller.
-- Builds UI from configurations, provides dialog display and propagates UI input.
--@category GUI
Load UI profile data from XML.Definition
loadProfiles(xmlFilename UI)Arguments
| xmlFilename | UI | profiles definition XML file path, relative to application root. |
Load a UI screen view's elements from an XML definition.Definition
loadGui(xmlFilename View, name Screen, controller FrameElement, isFrame [optional,)Arguments
| xmlFilename | View | definition XML file path, relative to application root. |
| name | Screen | name |
| controller | FrameElement | instance which serves as the controller for loaded elements |
| isFrame | [optional, | default=false] If true, will interpret the loaded view as a frame to be used in multiple places. |
| Root | GuiElement | instance of loaded view or nil if the definition XML file could not be loaded. |
Recursively load and build a UI configuration.Definition
loadGuiRec(xmlFile Opened, xmlNodePath Current, parentGuiElement Current, target Target)Arguments
| xmlFile | Opened | GUI configuration XML file |
| xmlNodePath | Current | XML node path |
| parentGuiElement | Current | parent GuiElement |
| target | Target | of newly instantiated elements |
Tries resolving a frame reference.Definition
If no frame has been loaded with the name given by the reference, then the reference element itself is returned.
Otherwise, the registered frame is cloned and returned.
resolveFrameReference(self Gui, frameRefElement FrameReferenceElement)Arguments
| self | Gui | instance |
| frameRefElement | FrameReferenceElement | instance to resolve |
| Cloned | FrameElement | instance or frameRefElement if resolution failed. |
Get a UI profile by name.Definition
getProfile()
Determine if any menu or dialog is visible.Definition
getIsGuiVisible()
Determine if any dialog is visible.Definition
getIsDialogVisible()
Get a menu action event ID by its name.Definition
Modify GUI action events with care, as they are globally defined for an entire session.
getActionEventId()
Display and return a screen identified by name.Definition
showGui()Return Values
| Root | GuiElement | of screen or nil if the name did not match any known screen. |
Display a dialog identified by name.Definition
showDialog()Return Values
| Root | GuiElement | of dialog or nil if the name did not match any known dialog. |
Close a dialog identified by name.Definition
closeDialogByName()
Close a dialog identified by its root GuiElement.Definition
This is always called when a dialog is closed, usually by itself.
closeDialog()
Close all open dialogs.Definition
closeAllDialogs()
Register menu input.Definition
registerMenuInput()
GUI mouse event hook.Definition
This is used primarily for mouse location checks, as button inputs are handled by InputBinding.
mouseEvent()
GUI key event hook.Definition
This is used for GuiElements which need to have direct access to raw key input, such as TextInputElement.
keyEvent()
Update the GUI.Definition
Propagates update to all active UI elements.
update()
Draw the GUI.Definition
Propagates draw calls to all active UI elements.
draw()
Notify controls of an action input with a value.Definition
notifyControls(action Action, value Action)Arguments
| action | Action | name as defined by loaded actions, see also scripts/input/InputAction.lua |
| value | Action | value [-1, 1] |
| True | if | any control has consumed the action event |
Event callback for menu input.Definition
onMenuInput()
Event callback for released movement menu input.Definition
onReleaseMovement()
Determine if a given GuiElement has input focus.Definition
hasElementInputFocus()
Get a screen controller instance by class for special cases.Definition
getScreenInstanceByClass(table screenClass)Arguments
| table | screenClass | Class table of the requested screen |
| table | ScreenElement | descendant instance of the given class or nil if no such instance was registered |
Change the currently displayed screen.Definition
changeScreen(table source, table screenClass, table returnScreenClass)Arguments
| table | source | Source screen instance |
| table | screenClass | Class table of the requested screen to change to or nil to close the GUI |
| table | returnScreenClass | [optional] Class table of the screen which will be opened on a "back" action in the new screen. |
| table | Root | GuiElement instance of target screen |
Make a change screen callback function which encloses the Gui self reference.Definition
This avoids passing the reference around as a parameter or global reference.
makeChangeScreenClosure()
Toggle a custom menu input context for one of the managed frames or screens.Definition
toggleCustomInputContext()
Make a toggle custom input context function which encloses the Gui self reference.Definition
makeToggleCustomInputContextClosure()
Make a play sample function which encloses the Gui self reference.Definition
makePlaySampleClosure()
Assign the play sample closure to a GUI element which has the PlaySampleMixin included.Definition
assignPlaySampleCallback(table guiElement)Arguments
| table | guiElement | GuiElement instance |
| table | The | GuiElement instance given in the guiElement parameter |
Enter a new menu input context.Definition
Menu views which require special input should provide a context name to not collide with the base menu input scheme.
enterMenuContext(string contextName)Arguments
| string | contextName | [optional] Custom menu input context name |
Leave a menu input context.Definition
This wraps the input context setting to check if the menu is actually active and the context should be reverted to
the state before entering the menu (or a custom menu input context within the menu).
leaveMenuContext()
Add the instance of a specific reusable GUI frame.Definition
addFrame()
Add the instance of a specific GUI screen.Definition
addScreen()
Set the current mission reference for GUI screens.Definition
setCurrentMission()
Set the current economy manager for GUI screens.Definition
The manager is initialized during mission loading.
setEconomyManager()
Let the GUI (and its components) process map data when it's loaded.Definition
loadMapData(int mapXmlFile)Arguments
| int | mapXmlFile | Map configuration XML file handle, do not close, will be handled by caller. |
Set the network client reference for GUI screens.Definition
setClient()
Set multiplayer state on screens which need to know about it.Definition
setIsMultiplayer(bool isMultiplayer)Arguments
| bool | isMultiplayer | If true, the GUI needs work in multiplayer mode. Otherwise, it's single player. |
Source in UI modules.Definition
initGuiLibrary(baseDir Base)Arguments
| baseDir | Base | scripts directory |
GUI Element base class.
All elements displayed in the game UI must be instances or descendants of this class.
-- All XML configuration properties as declared below (and in subclasses) are mirrored in guiProfiles.xml as key-value
pairs in the form of <Value name="property_name" value="value" />. Profiles are able to inherit from other
profiles, so take care to check their hierarchy if any of your settings do not seem to have any effect. Directly set
properties in the XML configuration will always override profile values, however.
-- Layer properties, prefixed with "[layer]", interact with an overlay system and provide display images. Usable
layers, whose names are substituted for the prefix, are primarily "image" and "icon". UI elements define layer names
on their own and read them from these generated properties. Whenever an element requires a layer, it is described
in its documentation such as this one. Example for an icon layer focus color property:
iconFocusedColor="0.9 0.1 0.5 1.0".
-- A note regarding callbacks: All callbacks are called on an element's target first. When GUI elements are created from
configuration, their top-level view (e.g. MainScreen) is the callback target, i.e. MainScreen:callbackName() is
executed. Unless an element's target has been set to another value explicitly via code, this will always be the case.
--@category GUI
--@xmlConfig GuiElement#id string [optional] Element ID. Must be unique per GUI screen. If set on an element, will expose it as an indexable field on the view class (e.g. id="buttonOk" -> MainScreen.buttonOk). Use this feature sparingly, as it creates implicit coupling which leads to tedious debugging.
Create a new GuiElement.Definition
new(target Target)Arguments
| target | Target | ScreenElement instance |
Load element data from an XML definition file.Definition
loadFromXML(xmlFile Definition, key XML)Arguments
| xmlFile | Definition | XML file handle |
| key | XML | node path to this element's definition |
Load profile data for this element.Definition
loadProfile(profile Loaded, applyProfile If)Arguments
| profile | Loaded | GUI profile |
| applyProfile | If | true, will re-calculate some dynamic properties. Use this when setting profiles dynamically at run time. |
Apply a GUI profile with a given name to this element.Definition
applyProfile(profileName Name, bool force)Arguments
| profileName | Name | of the profile to apply to this element |
| bool | force | [optional] If true, will apply all profile settings, including position and size. |
Delete any created frame overlays.Definition
deleteFrame()
Delete this GuiElement.Definition
Also deletes all child elements and removes itself from its parent and focus.
delete()
Create a deep copy clone of this GuiElement.Definition
clone(parent Target, includeId [optional,, suppressOnCreate [optional,)Arguments
| parent | Target | parent element of the cloned element |
| includeId | [optional, | default=false] If true, will also clone ID values |
| suppressOnCreate | [optional, | default=false] If true, will not trigger the "onCreate" callback |
Copy all attributes from a source GuiElement to this GuiElement.Definition
copyAttributes()
Called on a screen view's root GuiElement when all elements in a screen view have been created.Definition
The event is propagated to all children, depth-first.
onGuiSetupFinished()
Create frame overlays if this element has a frame.Definition
createFrame()
Toggle a frame side's visibility identified by index.Definition
If this element has no frame this will have no effect.
toggleFrameSide(int sideIndex)Arguments
| int | sideIndex | Index of the frame side, use one of GuiElement.FRAME_... |
Update the frame overlay positions if necessary.Definition
updateFramePosition()
Cut horizontal frame borders if a vertical frame side is thicker.Definition
cutFrameBordersHorizontal()
Cut vertical frame borders if a horizontal frame side is thicker.Definition
cutFrameBordersVertical()
Mouse event hook for mouse movement checks.Definition
mouseEvent()
Handles an input event on a menu input action.Definition
Input is first passed to the current GUI view, then to the focused element, then to the focus manager for navigation.
When a GUI element receives input, it should always propagate the input to its parent element first so they may
override behavior and/or set the event used flag. If properly inherited from GuiElement and descendants, this
behavior is guaranteed.
inputEvent(action Name, value Input, eventUsed If)Arguments
| action | Name | of input action which was triggered. |
| value | Input | value in the range of [-1, 1] for full axes and [0, 1] for half axes (includes buttons) |
| eventUsed | If | true, the event has been used by an input handler and should only be acted upon in exceptional cases. |
| True | if | the input event has been handled, false otherwise. |
Key event hook for raw keyboard input.Definition
keyEvent()Return Values
| True | if | the keyboard input has been processed by this element. |
Update this GuiElement.Definition
update()
Draw this GuiElement.Definition
If defined, triggers the "onDrawCallback".
draw()
Called on the root element of a screen view when it is opened.Definition
This raises the "onOpenCallback" if defined and propagates to all children.
onOpen()
Called on the root element of a screen view when it is closed.Definition
This raises the "onCloseCallback" if defined and propagates to all children.
onClose()
Determine if this GuiElement should change focus in a given direction.Definition
shouldFocusChange()
Determine if this GuiElement can receive focus.Definition
canReceiveFocus()
Called when this element loses focus.Definition
This propagates to all children.
onFocusLeave()
Called when this element becomes focused.Definition
This propagates to all children.
onFocusEnter()
Called when this element has focus and the focus activation action is triggered.Definition
This propagates to all children.
onFocusActivate()
Called when this element is highlighted.Definition
This propagates to all children.
onHighlight()
Called when this element loses the highlight.Definition
This propagates to all children.
onHighlightRemove()
Definition
Store the current overlay state while another overrides it (e.g. pressed state)
storeOverlayState()
Restore a previously stored overlay state after an overriding state has expiredDefinition
restoreOverlayState()
Determine if this element can receive focus.Definition
getHandleFocus()
Set this elements capability to receive focus.Definition
setHandleFocus()
Add a child GuiElement to this GuiElement.Definition
addElement()
Remove a child GuiElement from this GuiElement.Definition
removeElement()
Safely remove this GuiElement from its parent, if it has a parent.Definition
unlinkElement()
Update this elements absolute screen position.Definition
This needs to be called whenever a position, alignment, origin or size value changes.
updateAbsolutePosition()
Resets the state of this GuiElement and its children.Definition
reset()
Check if this element is the child of another element.Definition
This checks the full parent hierarchy.
isChildOf()
Get the actual focus target, in case a child or parent element needs to be targeted instead.Definition
getFocusTarget(incomingDirection (Optional), moveDirection (Optional))Arguments
| incomingDirection | (Optional) | If specified, may return different targets for different incoming directions. |
| moveDirection | (Optional) | Actual movement direction per input. This is the opposing direction of incomingDirection. |
| GuiElement | Actual | element to focus. |
Set this element's position.Definition
setPosition()
Modify this element's position (i.e. translate position).Definition
move()
Directly set the absolute screen position of this GuiElement.Definition
Also updates children accordingly.
setAbsolutePosition()
Set this element's size.Definition
setSize()
Set this element's visibility.Definition
setVisible()
Determine if this element is visible.Definition
This checks both its current alpha value (set by fadeIn() / fadeOut()) as well as the visibility flag. If the parent
is invisible, then so is this element.
getIsVisible()
Set this element's disabled state.Definition
Disabled elements can be displayed differently and do not respond to input actions.
setDisabled(disabled If, doNotUpdateChildren If)Arguments
| disabled | If | true, disables the element. False enables it again. |
| doNotUpdateChildren | If | true, does not apply the disabled state to child elements. |
Determine if this element is disabled.Definition
getIsDisabled()
Determine if this element is currently selected.Definition
getIsSelected()
Determine if this element is currently highlighted.Definition
getIsHighlighted()
Fade this element into visibility.Definition
fadeIn()
Fade this element out of visibility.Definition
fadeOut()
Directly set this element's alpha (transparency) valueDefinition
setAlpha(alpha Transparency)Arguments
| alpha | Transparency | value in the floating point range of [0, 1], where 0 is invisible and 1 is opaque. |
Determine if this element is active (not disabled and visible).Definition
Does not take alpha value into account.
getIsActive()
Toggle a flag to suppress UI sounds issued by this element or by the FocusManager when handling this element.Definition
This setting will propagate to children.
setSoundSuppressed()
Get the sound suppression flag from this element.Definition
If the flag is set to true, no sounds should be played when interacting with this element.
getSoundSuppressed()
Definition
Recursively add descendant elements of a root to an accumulator list. If a predicate function is given, it is
evaluated per element and only elements which yield a true value for the function are added to the accumulator.
findDescendantsRec(accumulator List, rootElement Current, predicateFunction [optional])Arguments
| accumulator | List | which receives descendant elements |
| rootElement | Current | element root whose direction children are added (after optional evaluation) |
| predicateFunction | [optional] | If specified, will be evaluated per element (see getDescendants) |
Get all contained elements of this element in the entire hierarchy.Definition
Descendants are traversed depth-first, meaning that if elements have been properly added, the element order mirrors
the order in the XML configuration (lines). Use this method sparingly, especially on high-level elements.
Optionally, a predicate function can be passed which filters descendant elements. The function must return true for
any desired element and false otherwise.
getDescendants(predicateFunction [optional])Arguments
| predicateFunction | [optional] | A function which determines if a descendant element should be returned. Must take a GuiElement as an argument and return true if that element should be returned or false otherwise. |
| List | of | this element's descendants in depth-first order with contiguous numeric indices. |
Get the first descendant element of this element which matches a predicate function.Definition
This is a shorthand for getDescendants() which returns just the first element matching the predicate function or nil
if no matching element exists.
getFirstDescendant(predicateFunction A)Arguments
| predicateFunction | A | function which determines if a descendant element should be returned. Must take a GuiElement as an argument and return true if that element should be returned or false otherwise. |
| First | matching | descendant element in depth-first order or nil, if no element matched the predicate function |
Get a descendant element of this element by its ID.Definition
This is a shorthand for getDescendants() with an ID matching predicate function.
getDescendantById(id Element)Arguments
| id | Element | id |
| element | or | nil |
Get a descendant element of this element by its name.Definition
This is a shorthand for getDescendants() with an ID matching predicate function.
getDescendantByName(name Element)Arguments
| name | Element | name |
| element | or | nil |
Get the bit mask value for a position origin string value.Definition
updatePositionForOrigin()
Get the bit mask value for a screen alignment string value.Definition
updateScreenAlign()
Get the bottom left and top right corners of this element's parent's border rectangle.Definition
If this element has no parent, the full screen's borders are returned (i.e. {0, 0, 1, 1})
getParentBorders()Return Values
| parent | element | or full screen borders in an array: {minX, minY, maxX, maxY} |
Get this element's border rectangle represented by minimum and maximum points.Definition
getBorders()Return Values
| element | borders | in an array: {minX, minY, maxX, maxY} |
Get this element's center position.Definition
getCenter()
Apply screen alignment to this element and its children.Definition
Scales size, position and margin depending on alignment settings.
applyScreenAlignment()
Set this element's overlay stateDefinition
setOverlayState(overlayState Overlay)Arguments
| overlayState | Overlay | state identified by one of the GuiOverlay.STATE_[...] constants. |
Get this element's overlay state.Definition
getOverlayState()
Add a callback to this element which was defined in its XML definition.Definition
If this element has a target, the given function name will be called on the target. Otherwise, the function is
assumed to be global.
addCallback(xmlFile XML, key XML, funcName Name)Arguments
| xmlFile | XML | file handle |
| key | XML | node path of this GuiElement's definition. |
| funcName | Name | of the callback function |
Raise a previously added callback by name.Definition
raiseCallback()
Try to extract a field name and index from an element ID.Definition
IDs in configurations may be indexed on definition (e.g. fillTypes[2]). This function extracts the list name and
index if such a case is found. Otherwise, it will return no index and the original element ID.
extractIndexAndNameFromID(elementId Element)Arguments
| elementId | Element | ID, to be used as a field name on a ScreenElement view. |
| index | or | nil, field name |
Try setting this element's ID from its XML definition.Definition
setId()
Include a mixin in this element.Definition
See GuiMixin.lua for the details on usage and implementation of mixins.
include(guiMixinType Class)Arguments
| guiMixinType | Class | table reference of a descendant of GuiMixin |
Get a nice string representation for this GUI element.Definition
toString()
GuiElement mixin base class.
-- Implements base functionality for GUI element mixins. All other GUI mixins should be descendants of this class.
-- Added methods:
GuiElement:hasIncluded(mixinType) Test if the GuiElement has included a mixin of the given type (class).
--@category GUI
Create a new GuiMixin instance.Definition
Subclasses need to provide their class type table for identification.
new(class Class, mixinType Class)Arguments
| class | Class | metatable |
| mixinType | Class | type table |
| New |
Add a mixin to a GuiElement.Definition
Adds mixin methods to the element which can then be used. A mixin's state is located in "element[mixinType]".
addTo()
Determine if a GuiElement has a mixin type included.Definition
hasIncluded(GuiElement GuiElement, mixinType GuiMixin)Arguments
| GuiElement | GuiElement | instance |
| mixinType | GuiMixin | class reference |
Clone mixin states for a mixin type from a source to a destination GuiElement instance.Definition
cloneMixin()
Clone this mixin's state from a source to a destination GuiElement instance.Definition
clone()
GUI overlay manager.
-- Handles creation, loading and basic rendering of GUI overlays. This module has no interaction with Overlay.lua.
--@category GUI
Loads overlay data from XML or a profile into a table to turn it into an overlay.Definition
loadOverlay()
Load overlay UV data from XML.Definition
loadXMLUVs()
Load overlay UV data from a profile.Definition
loadProfileUVs()
Load overlay color data from XML.Definition
loadXMLColors()
Load overlay color data from a profile.Definition
loadProfileColors()
(Re-)Create an overlay.Definition
createOverlay(Overlay Overlay, filename Path)Arguments
| Overlay | Overlay | table, see loadOverlay() |
| filename | Path | to image file (can also be a URL for web images) |
| Overlay | table | with added image data |
Copy an overlay.Definition
copyOverlay()
Delete an overlay.Definition
Primarily releases the associated image file handle.
deleteOverlay()
Get an overlay's color for a given overlay state.Definition
getOverlayColor(Overlay Overlay, state GuiOverlay.STATE_[...])Arguments
| Overlay | Overlay | table |
| state | GuiOverlay.STATE_[...] | constant value |
| Color | as | {red, green, blue, alpha} with all values in the range of [0, 1] |
Get an overlay's UV coordinates for a given overlay state.Definition
getOverlayUVs(Overlay Overlay, state GuiOverlay.STATE_[...])Arguments
| Overlay | Overlay | table |
| state | GuiOverlay.STATE_[...] | constant value |
| UV | coordinates | as {u1, v1, u2, v2, u3, v3, u4x, v4} with all values in the range of [0, 1] |
Renders an overlay with the given parameters.Definition
renderOverlay(Overlay Overlay, posX Screen, posY Screen, sizeX Screen, sizeY Screen, state GuiOverlay.STATE_[...])Arguments
| Overlay | Overlay | table |
| posX | Screen | x position |
| posY | Screen | y position |
| sizeX | Screen | x size |
| sizeY | Screen | y size |
| state | GuiOverlay.STATE_[...] | constant for the required display state |
GUI element display profile.
-- Holds GuiElement property data for re-use similar to a HTML/CSS definition.
--@category GUI
Create a new GuiProfile.Definition
new(profiles Reference, traits Reference)Arguments
| profiles | Reference | to loaded profiles table for inheritance checking. |
| traits | Reference | to loaded traits table for inheritance checking. |
| New | GuiProfile | instance |
Load profile data from XML.Definition
loadFromXML(xmlFile XML, key Profile, presets Table, isTrait Whether)Arguments
| xmlFile | XML | file handle |
| key | Profile | XML element node path |
| presets | Table | of presets for symbol resolution, {preset name=preset value} |
| isTrait | Whether | this profile is a trait |
| True | if | profile values could be loaded, false otherwise. |
Get a string value from this profile (and its ancestors) by name.Definition
getValue(name Name, default Default)Arguments
| name | Name | of attribute value to retrieve |
| default | Default | value to use if the attribute is not defined. |
Get a boolean value from this profile (and its ancestors) by name.Definition
getBool(name Name, default Default)Arguments
| name | Name | of attribute value to retrieve |
| default | Default | value to use if the attribute is not defined. |
Get a number value from this profile (and its ancestors) by name.Definition
getNumber(name Name, default Default)Arguments
| name | Name | of attribute value to retrieve |
| default | Default | value to use if the attribute is not defined. |
GUI sound player.
-- This class loads known GUI sound samples as non-spatial samples to be played in the GUI (menu and HUD).
--@category GUI
Create a new GuiSoundPlayer instance.Definition
new(table soundManager)Arguments
| table | soundManager | SoundManager reference |
Load GUI sound samples from definitions.Definition
loadSounds()
Play a GUI sound sample identified by name.Definition
The sample must have been loaded when the GUI was created.
playSample(string sampleName)Arguments
| string | sampleName | Name of the sample to play, use one of the identifiers in GuiSoundPlayer.SOUND_SAMPLES. |
GUI utility functions.
--@category GUI
Transform an attribute string representing a list of numbers into an array and normalize the values.Definition
getNormalizedValues(str Attribute, refSize Reference, defaultValue Default)Arguments
| str | Attribute | string containing numbers, either raw or with a pixel unit designation on each number (e.g. "12 24" or "12px 24px") |
| refSize | Reference | size for normalization, e.g. a reference screen resolution used to scale pixel values, {sizeX, sizeY} |
| defaultValue | Default | value to return if the "str" parameter value is nil |
| array | of | normalized values |
Transform an attribute string representing a 2D array into an actual array.Definition
get2DArray(str Attribute, defaultValue Default)Arguments
| str | Attribute | string containing exactly 2 numbers |
| defaultValue | Default | value to return if the "str" parameter value is nil or invalid for transformation. |
| array | of | the 2 converted values as numbers: {value1, value2} |
Transform an attribute string representing a 4D array into an actual array.Definition
get4DArray(str Attribute, defaultValue Default)Arguments
| str | Attribute | string containing exactly 4 numbers |
| defaultValue | Default | value to return if the "str" parameter value is nil or invalid for transformation. |
| array | of | the 4 converted values as numbers: {value1, value2, value3, value4} |
Transform an attribute string representing a 4D color array into an actual array.Definition
getColorArray(str Attribute, defaultValue Default)Arguments
| str | Attribute | string containing exactly 4 numbers |
| defaultValue | Default | value to return if the "str" parameter value is nil or invalid for transformation. |
| array | of | the 4 converted values as numbers: {red, green, blue, alpha} |
Transform an attribute string representing a UV array into an actual array and normalize the values.Definition
getUVs(str Attribute, ref Texture, defaultValue Default)Arguments
| str | Attribute | string containing exactly 4 numbers, order and format: "x[px] y[px] sizeX[px] sizeY[px]" |
| ref | Texture | reference size used to normalize pixel UV coordinates into unit sized UV coordinates |
| defaultValue | Default | value to return if the "str" parameter value is nil or invalid for transformation. |
| array | of | the UV coordinates as {u1, v1, u2, v2, u3, v3, u4, v4} |
Check if a point lies within or a hotspot overlaps an overlay.Definition
checkOverlayOverlap(posX Point, posY Point, overlayX Overlay, overlayY Overlay, overlaySizeX Overlay, overlaySizeY Overlay, hotspot If)Arguments
| posX | Point | or hotspot x position |
| posY | Point | or hotspot y position |
| overlayX | Overlay | x position |
| overlayY | Overlay | y position |
| overlaySizeX | Overlay | width |
| overlaySizeY | Overlay | height |
| hotspot | If | provided as an array having 4 numbers for the bounding points of a rectangle {minX, minY, maxX, maxY}, will be checked if it overlaps the overlay area given by the other parameters. |
HighPressureWasher Activatable
Creating placeable high pressure washerDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 20 | function HighPressureWasher:new(isServer, isClient, customMt) |
| 21 | local mt = customMt |
| 22 | if mt == nil then |
| 23 | mt = HighPressureWasher_mt |
| 24 | end |
| 25 | |
| 26 | local self = Placeable:new(isServer, isClient, mt) |
| 27 | registerObjectClassName(self, "HighPressureWasher") |
| 28 | |
| 29 | return self |
| 30 | end |
Load high pressure washerDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 43 | function HighPressureWasher:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 44 | if not HighPressureWasher:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 45 | return false |
| 46 | end |
| 47 | |
| 48 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 49 | |
| 50 | self.lanceNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.highPressureWasher.lance#index")) |
| 51 | self.handtoolXML = Utils.getFilename(getXMLString(xmlFile, "placeable.highPressureWasher.handtool#filename"), self.baseDirectory) |
| 52 | self.playerInRangeDistance = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.playerInRangeDistance"), 3) |
| 53 | self.actionRadius = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.highPressureWasher.actionRadius#distance"), 15) |
| 54 | |
| 55 | if self.isClient then |
| 56 | self.hpwSamples = {} |
| 57 | if self.isClient then |
| 58 | self.hpwSamples.compressor = g_soundManager:loadSampleFromXML(xmlFile, "placeable.highPressureWasher.sounds", "compressor", self.baseDirectory, self.nodeId, 0, AudioGroup.VEHICLE, nil, self) |
| 59 | self.hpwSamples.switch = g_soundManager:loadSampleFromXML(xmlFile, "placeable.highPressureWasher.sounds", "switch", self.baseDirectory, self.nodeId, 1, AudioGroup.VEHICLE, nil, self) |
| 60 | end |
| 61 | |
| 62 | local filename = getXMLString(xmlFile, "placeable.highPressureWasher.exhaust#filename") |
| 63 | if filename ~= nil then |
| 64 | local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false) |
| 65 | if i3dNode ~= 0 then |
| 66 | local linkNode = Utils.getNoNil(I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.highPressureWasher.exhaust#index")), self.nodeId) |
| 67 | self.exhaustFilename = filename |
| 68 | self.exhaustNode = getChildAt(i3dNode, 0) |
| 69 | link(linkNode, self.exhaustNode) |
| 70 | setVisibility(self.exhaustNode, false) |
| 71 | delete(i3dNode) |
| 72 | end |
| 73 | end |
| 74 | end |
| 75 | |
| 76 | delete(xmlFile) |
| 77 | |
| 78 | self.isPlayerInRange = false |
| 79 | self.isTurnedOn = false |
| 80 | self.isTurningOff = false |
| 81 | self.turnOffTime = 0 |
| 82 | self.turnOffDuration = 500 |
| 83 | self.activatable = HighPressureWasherActivatable:new(self) |
| 84 | self.lastInRangePosition = {0,0,0} |
| 85 | |
| 86 | return true |
| 87 | end |
Deleting placeable high pressure washerDefinition
delete()Code
| 91 | function HighPressureWasher:delete() |
| 92 | self:setIsTurnedOn(false, nil, false) |
| 93 | if self.isClient then |
| 94 | if self.exhaustFilename ~= nil then |
| 95 | g_i3DManager:releaseSharedI3DFile(self.exhaustFilename, self.baseDirectory, true) |
| 96 | end |
| 97 | g_soundManager:deleteSamples(self.hpwSamples) |
| 98 | end |
| 99 | |
| 100 | unregisterObjectClassName(self) |
| 101 | g_currentMission:removeActivatableObject(self.activatable) |
| 102 | HighPressureWasher:superClass().delete(self) |
| 103 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 109 | function HighPressureWasher:readStream(streamId, connection) |
| 110 | HighPressureWasher:superClass().readStream(self, streamId, connection) |
| 111 | if connection:getIsServer() then |
| 112 | local isTurnedOn = streamReadBool(streamId) |
| 113 | if isTurnedOn then |
| 114 | local player = NetworkUtil.readNodeObject(streamId) |
| 115 | if player ~= nil then |
| 116 | self:setIsTurnedOn(isTurnedOn, player, true) |
| 117 | end |
| 118 | end |
| 119 | end |
| 120 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 126 | function HighPressureWasher:writeStream(streamId, connection) |
| 127 | HighPressureWasher:superClass().writeStream(self, streamId, connection) |
| 128 | if not connection:getIsServer() then |
| 129 | streamWriteBool(streamId, self.isTurnedOn) |
| 130 | if self.isTurnedOn then |
| 131 | NetworkUtil.writeNodeObject(streamId, self.currentPlayer) |
| 132 | end |
| 133 | end |
| 134 | end |
Activate hand toolDefinition
activateHandtool(table player)Arguments
| table | player | player |
| 139 | function HighPressureWasher:activateHandtool(player) |
| 140 | self:setIsTurnedOn(true, player, true) |
| 141 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 146 | function HighPressureWasher:update(dt) |
| 147 | HighPressureWasher:superClass().update(self, dt) |
| 148 | |
| 149 | if self.currentPlayer ~= nil then |
| 150 | local isPlayerInRange = self:getIsPlayerInRange(self.actionRadius, self.currentPlayer) |
| 151 | if isPlayerInRange then |
| 152 | self.lastInRangePosition = {getTranslation(self.currentPlayer.rootNode)} |
| 153 | else |
| 154 | local kx, _, kz = getWorldTranslation(self.nodeId) |
| 155 | local px, py, pz = getWorldTranslation(self.currentPlayer.rootNode) |
| 156 | local len = MathUtil.vector2Length(px-kx, pz-kz) |
| 157 | |
| 158 | local x,y,z = unpack(self.lastInRangePosition) |
| 159 | x = kx + ((px-kx) / len) * (self.actionRadius-0.00001*dt) |
| 160 | z = kz + ((pz-kz) / len) * (self.actionRadius-0.00001*dt) |
| 161 | self.currentPlayer:moveToAbsoluteInternal(x, py, z) |
| 162 | self.lastInRangePosition = {x,y,z} |
| 163 | |
| 164 | if self.currentPlayer == g_currentMission.player then |
| 165 | g_currentMission:showBlinkingWarning(g_i18n:getText("warning_hpwRangeRestriction"), 4000) |
| 166 | end |
| 167 | end |
| 168 | end |
| 169 | |
| 170 | if self.isClient then |
| 171 | if self.isTurningOff then |
| 172 | if g_currentMission.time > self.turnOffTime then |
| 173 | self.isTurningOff = false |
| 174 | g_soundManager:stopSample(self.hpwSamples.compressor) |
| 175 | end |
| 176 | end |
| 177 | end |
| 178 | |
| 179 | self:raiseActive() |
| 180 | end |
updateTickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| 197 | function HighPressureWasher:updateTick(dt) |
| 198 | HighPressureWasher:superClass().updateTick(self, dt) |
| 199 | local isPlayerInRange, player = self:getIsPlayerInRange(self.playerInRangeDistance) |
| 200 | |
| 201 | if isPlayerInRange and g_currentMission.accessHandler:canPlayerAccess(self, player) then |
| 202 | self.playerInRange = player |
| 203 | self.isPlayerInRange = true |
| 204 | g_currentMission:addActivatableObject(self.activatable) |
| 205 | else |
| 206 | self.playerInRange = nil |
| 207 | self.isPlayerInRange = false |
| 208 | g_currentMission:removeActivatableObject(self.activatable) |
| 209 | end |
| 210 | end |
Set is turned onDefinition
setIsTurnedOn(boolean isTurnedOn, table player, boolean noEventSend)Arguments
| boolean | isTurnedOn | is turned on |
| table | player | player |
| boolean | noEventSend | no event send |
| 217 | function HighPressureWasher:setIsTurnedOn(isTurnedOn, player, noEventSend) |
| 218 | HPWPlaceableTurnOnEvent.sendEvent(self, isTurnedOn, player, noEventSend) |
| 219 | |
| 220 | if self.isTurnedOn ~= isTurnedOn then |
| 221 | if isTurnedOn then |
| 222 | self.isTurnedOn = isTurnedOn |
| 223 | |
| 224 | if player ~= nil then |
| 225 | self.currentPlayer = player |
| 226 | self.currentPlayer:addDeleteListener(self, "onPlayerDelete") |
| 227 | if noEventSend ~= true then |
| 228 | self.currentPlayer:equipHandtool(self.handtoolXML, true, noEventSend) |
| 229 | self.currentPlayer.baseInformation.currentHandtool:addDeleteListener(self, "onHandtoolDelete") |
| 230 | end |
| 231 | end |
| 232 | |
| 233 | if self.isClient then |
| 234 | g_soundManager:playSample(self.hpwSamples.switch) |
| 235 | g_soundManager:playSample(self.hpwSamples.compressor) |
| 236 | |
| 237 | if self.isTurningOff then |
| 238 | self.isTurningOff = false |
| 239 | end |
| 240 | setVisibility(self.lanceNode, false) |
| 241 | end |
| 242 | else |
| 243 | self:onDeactivate() |
| 244 | end |
| 245 | if self.exhaustNode ~= nil then |
| 246 | setVisibility(self.exhaustNode, isTurnedOn) |
| 247 | end |
| 248 | end |
| 249 | end |
onPlayerDelete()Code
| 253 | function HighPressureWasher:onPlayerDelete() |
| 254 | self.currentPlayer = nil |
| 255 | self:setIsTurnedOn(false, nil, nil) |
| 256 | end |
onHandtoolDelete()Code
| 260 | function HighPressureWasher:onHandtoolDelete() |
| 261 | self.currentPlayer = nil |
| 262 | self:setIsTurnedOn(false, nil, nil) |
| 263 | end |
On deactivateDefinition
onDeactivate()Code
| 267 | function HighPressureWasher:onDeactivate() |
| 268 | if self.isClient then |
| 269 | g_soundManager:playSample(self.hpwSamples.switch) |
| 270 | g_soundManager:stopSample(self.hpwSamples.washing, true) |
| 271 | self.isTurningOff = true |
| 272 | self.turnOffTime = g_currentMission.time + self.turnOffDuration |
| 273 | end |
| 274 | self.isTurnedOn = false |
| 275 | setVisibility(self.lanceNode, true) |
| 276 | if self.currentPlayer ~= nil then |
| 277 | if self.currentPlayer:hasHandtoolEquipped() then |
| 278 | self.currentPlayer.baseInformation.currentHandtool:removeDeleteListener(self, "onHandtoolDelete") |
| 279 | self.currentPlayer:unequipHandtool() |
| 280 | end |
| 281 | self.currentPlayer:removeDeleteListener(self, "onPlayerDelete") |
| 282 | self.currentPlayer = nil |
| 283 | end |
| 284 | end |
Get is active for inputDefinition
getIsActiveForInput()Return Values
| boolean | isActiveForInput | is active for input |
| 289 | function HighPressureWasher:getIsActiveForInput() |
| 290 | if self.isTurnedOn and self.currentPlayer == g_currentMission.player and not g_gui:getIsGuiVisible() then |
| 291 | return true |
| 292 | end |
| 293 | return false |
| 294 | end |
Get is active for soundDefinition
getIsActiveForSound()Return Values
| boolean | isActiveForSound | is active for sound |
| 299 | function HighPressureWasher:getIsActiveForSound() |
| 300 | return self:getIsActiveForInput() |
| 301 | end |
Get can be sold in current stateDefinition
canBeSold()Return Values
| boolean | ||
| string | warningMessage | warning message displayed in the shop |
| 307 | function HighPressureWasher:canBeSold() |
| 308 | local warning = g_i18n:getText("shop_messageReturnVehicleInUse") |
| 309 | if self.currentPlayer ~= nil then |
| 310 | return false, warning |
| 311 | end |
| 312 | return true, nil |
| 313 | end |
Minimum daily riding time for a horse below which it will lose fitness.
new()Code
| 22 | function Horse:new(isServer, isClient, owner, fillTypeIndex, customMt) |
| 23 | local self = RideableAnimal:new(isServer, isClient, owner, fillTypeIndex, customMt or Horse_mt) |
| 24 | |
| 25 | self.name = g_animalNameManager:getRandomName() |
| 26 | |
| 27 | self.fitnessScale = 0.0 |
| 28 | self.fitnessScaleSent = 0.0 |
| 29 | |
| 30 | self.healthScale = 1.0 |
| 31 | self.healthScaleSent = 1.0 |
| 32 | |
| 33 | self.ridingTimer = 0.0 |
| 34 | |
| 35 | self.horseDirtyFlag = self:getNextDirtyFlag() |
| 36 | |
| 37 | return self |
| 38 | end |
readStream()Code
| 42 | function Horse:readStream(streamId) |
| 43 | Horse:superClass().readStream(self, streamId) |
| 44 | |
| 45 | self.name = streamReadString(streamId) |
| 46 | self.fitnessScale = streamReadFloat32(streamId) |
| 47 | self.healthScale = streamReadFloat32(streamId) |
| 48 | self.ridingTimer = streamReadFloat32(streamId) |
| 49 | end |
writeStream()Code
| 53 | function Horse:writeStream(streamId) |
| 54 | Horse:superClass().writeStream(self, streamId) |
| 55 | |
| 56 | streamWriteString(streamId, self.name) |
| 57 | streamWriteFloat32(streamId, self.fitnessScale) |
| 58 | streamWriteFloat32(streamId, self.healthScale) |
| 59 | streamWriteFloat32(streamId, self.ridingTimer) |
| 60 | end |
readUpdateStream()Code
| 64 | function Horse:readUpdateStream(streamId, timestamp, connection) |
| 65 | Horse:superClass().readUpdateStream(self, streamId, timestamp, connection) |
| 66 | |
| 67 | -- server to client only |
| 68 | if connection:getIsServer() then |
| 69 | if streamReadBool(streamId) then |
| 70 | self.fitnessScale = NetworkUtil.readCompressedPercentages(streamId, 7) |
| 71 | self.healthScale = NetworkUtil.readCompressedPercentages(streamId, 7) |
| 72 | end |
| 73 | end |
| 74 | end |
writeUpdateStream()Code
| 78 | function Horse:writeUpdateStream(streamId, connection, dirtyMask) |
| 79 | Horse:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
| 80 | |
| 81 | -- server to client only |
| 82 | if not connection:getIsServer() then |
| 83 | if streamWriteBool(streamId, bitAND(dirtyMask, self.horseDirtyFlag) ~= 0) then |
| 84 | NetworkUtil.writeCompressedPercentages(streamId, self.fitnessScaleSent, 7) |
| 85 | NetworkUtil.writeCompressedPercentages(streamId, self.healthScaleSent, 7) |
| 86 | end |
| 87 | end |
| 88 | end |
loadFromXMLFile()Code
| 92 | function Horse:loadFromXMLFile(xmlFile, key) |
| 93 | Horse:superClass().loadFromXMLFile(self, xmlFile, key) |
| 94 | |
| 95 | self.name = getXMLString(xmlFile, key.."#name") or self.name |
| 96 | self.fitnessScale = getXMLFloat(xmlFile, key.."#fitnessScale") or self.fitnessScale |
| 97 | self.healthScale = getXMLFloat(xmlFile, key.."#healthScale") or self.healthScale |
| 98 | self.ridingTimer = getXMLFloat(xmlFile, key.."#ridingTimer") or self.ridingTimer |
| 99 | self.fitnessScaleSent = self.fitnessScale |
| 100 | end |
saveToXMLFile()Code
| 104 | function Horse:saveToXMLFile(xmlFile, key, usedModNames) |
| 105 | Horse:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 106 | |
| 107 | setXMLString(xmlFile, key.."#name", self.name) |
| 108 | setXMLFloat(xmlFile, key.."#fitnessScale", self.fitnessScale) |
| 109 | setXMLFloat(xmlFile, key.."#healthScale", self.healthScale) |
| 110 | setXMLFloat(xmlFile, key.."#ridingTimer", self.ridingTimer) |
| 111 | end |
setName()Code
| 115 | function Horse:setName(name) |
| 116 | |
| 117 | if self.name ~= nil and self.name ~= name then |
| 118 | self.name = name |
| 119 | g_messageCenter:publish(MessageType.HUSBANDRY_ANIMALS_CHANGED, self.owner) |
| 120 | end |
| 121 | end |
getName()Code
| 125 | function Horse:getName() |
| 126 | return self.name |
| 127 | end |
getValueScale()Code
| 131 | function Horse:getValueScale() |
| 132 | -- dirt scale should only count 10% |
| 133 | return 0.90 * (self.fitnessScale * self.healthScale) + 0.10 * (1-self.dirtScale) |
| 134 | end |
setFitnessScale()Code
| 138 | function Horse:setFitnessScale(scale, noEventSend) |
| 139 | self.fitnessScale = scale |
| 140 | if math.abs(self.fitnessScaleSent - self.fitnessScale) > 0.01 then |
| 141 | self.fitnessScaleSent = self.fitnessScale |
| 142 | g_messageCenter:publish(MessageType.HUSBANDRY_ANIMALS_CHANGED, self.owner) |
| 143 | |
| 144 | if noEventSend == nil or not noEventSend then |
| 145 | self:raiseDirtyFlags(self.horseDirtyFlag) |
| 146 | end |
| 147 | end |
| 148 | end |
getFitnessScale()Code
| 152 | function Horse:getFitnessScale() |
| 153 | return self.fitnessScale |
| 154 | end |
setHealthScale()Code
| 158 | function Horse:setHealthScale(scale, noEventSend) |
| 159 | self.healthScale = scale |
| 160 | if math.abs(self.healthScaleSent - self.healthScale) > 0.01 then |
| 161 | self.healthScaleSent = self.healthScale |
| 162 | g_messageCenter:publish(MessageType.HUSBANDRY_ANIMALS_CHANGED, self.owner) |
| 163 | |
| 164 | if noEventSend == nil or not noEventSend then |
| 165 | self:raiseDirtyFlags(self.horseDirtyFlag) |
| 166 | end |
| 167 | end |
| 168 | end |
getHealthScale()Code
| 172 | function Horse:getHealthScale() |
| 173 | return self.healthScale |
| 174 | end |
Get the riding time of this horse for the current day in milliseconds.Definition
getTodaysRidingTime()
deactivateRiding()Code
| 183 | function Horse:deactivateRiding(noEventSend) |
| 184 | if self.rideableVehicle ~= nil then |
| 185 | self.rideableVehicle:setFitnessChangedCallback(nil, nil) |
| 186 | end |
| 187 | |
| 188 | Horse:superClass().deactivateRiding(self, noEventSend) |
| 189 | end |
onLoadedRideable()Code
| 193 | function Horse:onLoadedRideable(rideableVehicle, vehicleLoadState, arguments) |
| 194 | Horse:superClass().onLoadedRideable(self, rideableVehicle, vehicleLoadState, arguments) |
| 195 | |
| 196 | if self.rideableVehicle ~= nil then |
| 197 | self.rideableVehicle:setFitnessChangedCallback(self.onFitnessChangedCallback, self) |
| 198 | end |
| 199 | end |
onFitnessChangedCallback()Code
| 203 | function Horse:onFitnessChangedCallback(deltaTime) |
| 204 | self.ridingTimer = self.ridingTimer + deltaTime |
| 205 | end |
updateFitness()Code
| 209 | function Horse:updateFitness() |
| 210 | if self.isServer then |
| 211 | local fitness = self:getFitnessScale() |
| 212 | |
| 213 | if self.ridingTimer < Horse.DAILY_MINIMUM_RIDING_TIME then |
| 214 | fitness = fitness - 0.02 |
| 215 | else -- add capped gain |
| 216 | local fitnessGain = math.min(0.1, 0.1 * self.ridingTimer / Horse.DAILY_TARGET_RIDING_TIME) |
| 217 | fitness = fitness + fitnessGain |
| 218 | end |
| 219 | |
| 220 | fitness = MathUtil.clamp(fitness, 0.0, 1.0) |
| 221 | self:setFitnessScale(fitness) |
| 222 | end |
| 223 | |
| 224 | self.ridingTimer = 0.0 |
| 225 | end |
Heads-up display.
-- The HUD displays information for the player when in game.
--@category GUI
Create a new HUD instance.Definition
new(bool isServer, bool isServer, bool isConsoleVersion, table messageCenter, table l10n, table inputManager, table inputDisplayManager, table modManager, table fillTypeManager, table fruitTypeManager, table guiSoundPlayer)Arguments
| bool | isServer | If true, the running game instance is a server |
| bool | isServer | If true, the running game instance is a client |
| bool | isConsoleVersion | If true, we are running on console |
| table | messageCenter | MessageCenter reference for message subscriptions |
| table | l10n | I18N reference for text localization |
| table | inputManager | InputBinding reference |
| table | inputDisplayManager | InputDisplayManager reference |
| table | modManager | ModManager reference |
| table | fillTypeManager | FillTypeManager reference |
| table | fruitTypeManager | FruitTypeManager reference |
| table | guiSoundPlayer | GuiSoundPlayer reference |
Create all required display components.Definition
createDisplayComponents(uiScale Current)Arguments
| uiScale | Current | UI scale |
Delete the HUD and all its display components.Definition
delete()
Subscribe to relevant state messages.Definition
subscribeMessages()
Set the scale of the HUD.Definition
setScale(float scale)Arguments
| float | scale | New scale value |
Draw the HUD components for the currently controlled entity (player or vehicle).Definition
drawControlledEntityHUD()
Draw the input help display panel and vehicle schema.Definition
drawInputHelp()
Draw a notification at the top center of the screen if one has been set.Definition
drawTopNotification()
Draw a blinking warning if necessary.Definition
drawBlinkingWarning()
Draw the presentation mode logo.Definition
drawPresentationVersion()
Draw the screen fade effect.Definition
drawFading()
Draw an overlay at a given screen space position with given dimensions.Definition
Omitting a parameter will use the last setting of that parameter (or initial setting if parameter is never supplied).
drawOverlayAtPositionWithDimensions(table overlay, float screenX, float screenY, float screenWidth, float screenHeight)Arguments
| table | overlay | Overlay instance to draw |
| float | screenX | Drawing origin X position in screen space |
| float | screenY | Drawing origin Y position in screen space |
| float | screenWidth | Drawing horizontal size in screen space |
| float | screenHeight | Drawing vertical size in screen space |
Draw an overlay at a given screen space position.Definition
Omitting a parameter will use the last setting of that parameter.
drawOverlayAtPosition(table overlay, float screenX, float screenY)Arguments
| table | overlay | Overlay instance to draw |
| float | screenX | Drawing origin X position in screen space |
| float | screenY | Drawing origin Y position in screen space |
Draw notification texts at the side of the screen.Definition
drawSideNotification()
Draw the regular HUD for career mode.Definition
drawBaseHUD()
Draw the platform communication display (chat or speaker icons).Definition
drawCommunicationDisplay()
Set the tutorial progress.Definition
setTutorialProgress(float progress)Arguments
| float | progress | Progress expressed as a number between 0 and 1 |
Draw a pause notification with a message text and synchronizing background if the menu is visible.Definition
drawGamePaused(bool beforeMissionStart)Arguments
| bool | beforeMissionStart | If true, draw a special overlay for cases when the menu has not yet loaded but the world is already visible. |
Draw the current vehicle name if it's active.Definition
drawVehicleName()
Draw the message window and icon at the bottom of the screen.Definition
drawInGameMessageAndIcon()
Show a "mission accomplished" message.Definition
drawMissionCompleted()
Show a "mission failed" message.Definition
drawMissionFailed()
Display an in-game message in a window at the bottom.Definition
This calls InGameMessage:showMessage(title, message, duration, controls, callback, target).
showInGameMessage()
Display a blinking warning text.Definition
showBlinkingWarning(string text, int duration, int priority)Arguments
| string | text | Warning text |
| int | duration | [optional, default=2000] Duration of warning visibility in milliseconds |
| int | priority | [optional, default=0] Warning priority value. If a new warning is triggered with the same or higher priority as a current one, the current warning is replaced. |
Accumulate a money transaction amount by type until shown by HUD.showMoneyChange().Definition
addMoneyChange(int moneyType, float amount)Arguments
| int | moneyType | Type of transaction from EconomyManager.MONEY_TYPE_XYZ |
| float | amount | Amount of money |
Display a money change notification at the right side of the screen.Definition
showMoneyChange(int moneyType, string text)Arguments
| int | moneyType | Type of transaction from EconomyManager.MONEY_TYPE_XYZ |
| string | text | Specific money type label (e.g. "loan interest") |
Add an extra text to display in the input help panel for this frame.Definition
addExtraPrintText()
Display the name of a vehicle at the bottom of the screen for a short time.Definition
showVehicleName(string vehicleName)Arguments
| string | vehicleName | Name of a vehicle |
Add a notification to be displayed at the right side of the screen.Definition
addSideNotification(table color)Arguments
| table | color | Color as an RGBA array |
Add a notification to be displayed in a frame at the top of the screen.Definition
addTopNotification(string title, string text, string info, table iconKey, int duration)Arguments
| string | title | Notification title |
| string | text | Notification message text |
| string | info | Additional info text |
| table | iconKey | [optional] Icon key for a display icon, use a value from TopNotification.ICON |
| int | duration | [optional] Display duration in milliseconds. Negative values or nil default to a long-ish standard duration. |
Hide a current notification shown by HUD:addTopNotification().Definition
hideTopNotification()
Check if the HUD is currently fading the screen.Definition
getIsFading()
Set the visibility of the game info display parts by flags.Definition
setGameInfoPartVisibility(partFlags Combination)Arguments
| partFlags | Combination | of values from HUD.GAME_INFO_PART |
Handle menu visibility changes.Definition
Keeps track of the menu visibility to block relevant processes.
onMenuVisibilityChange()
Handle game pause state changes.Definition
onPauseGameChange(bool isPaused, string pauseText)Arguments
| bool | isPaused | [optional] If true, the game is currently paused. If not set, will not change the pause state. |
| string | pauseText | [optional] Text to display on the pause element |
Set HUD visibility.Definition
setIsVisible(bool isVisible)Arguments
| bool | isVisible | If true, the HUD is made visible. If false, it's made invisible. |
Set input help visibility.Definition
setInputHelpVisible(bool isVisible)Arguments
| bool | isVisible | If true, the input help is displayed. If false, it's made invisible |
Set field info display visibility.Definition
setFieldInfoVisible(bool isVisible)Arguments
| bool | isVisible | If true, the field info display is shown. If false, it's hidden. |
Add a custom input help entry which is displayed until removed.Definition
Custom entries will be displayed in order of addition after any automatically detected input help entries and before
vehicle extensions.
addCustomInputHelpEntry()
Clear all custom input help entries.Definition
clearCustomInputHelpEntries()
Check if the HUD is visible.Definition
getIsVisible()Return Values
| bool | If | true, the HUD is currently visible. |
Set current controlled vehicle.Definition
setControlledVehicle(table vehicle)Arguments
| table | vehicle | Vehicle reference or nil (not controlling a vehicle) |
Set current player controlling state.Definition
setIsControllingPlayer(bool isControllingPlayer)Arguments
| bool | isControllingPlayer | If true, the player is controlling their character on foot. |
Set the money unit (currency) to display.Definition
setMoneyUnit(int unit)Arguments
| int | unit | One of [GS_MONEY_EURO | GS_MONEY_POUND], any other value will display a universal dollar sign. |
Display an achievement message.Definition
showAchievementMessage()
Display vehicle attachment context information for the current frame.Definition
showAttachContext()
Display vehicle tipping context information for the current frame.Definition
showTipContext()
Display vehicle refueling context information for the current frame.Definition
showFuelContext()
Display dog bowl refilling context information for the current frame.Definition
showFillDogBowlContext()
Set the reference to the current player.Definition
setPlayer()
Set the references to the currently connected users.Definition
setConnectedUsers()
Update in-game message window and icon.Definition
updateMessageAndIcon()
Update base HUD state.Definition
update()
Update the blinking warning if one is set.Definition
updateBlinkingWarning()
Update the in-game map.Definition
updateMap()
Update vehicle name display when a vehicle was entered.Definition
updateVehicleName()
Fade the screen.Definition
fadeScreen(int direction, int duration, func callbackFunc, table callbackTarget)Arguments
| int | direction | If > 0 will fade to opaque (i.e. black screen), if < 0 will fade to transparent (i.e. clear) |
| int | duration | Target duration of the fade animation in milliseconds |
| func | callbackFunc | [optional] Callback function which is called when the fading animation completes |
| table | callbackTarget | [optional] Callback function target which is passed to the callback function as its first argument if provided |
Load graphics and settings for the in-game map.Definition
loadIngameMap(string ingameMapFilename, int ingameMapWidth, int ingameMapHeight)Arguments
| string | ingameMapFilename | Path to map image |
| int | ingameMapWidth | Width of the map (X) |
| int | ingameMapHeight | Height of the map (Y) |
Set the size index of the in-game map.Definition
setIngameMapSize(int sizeIndex)Arguments
| int | sizeIndex | One of [IngameMap.STATE_MINIMAP | IngameMap.STATE_MAP | IngameMap.STATE_OFF] |
Get the in-game map reference.Definition
getIngameMap()
Check if the in-game message is currently active.Definition
isInGameMessageActive()
Mouse event function.Definition
Only delegates input to components.
mouseEvent()
Set the environment reference to use for weather information display.Definition
setEnvironment(table environment)Arguments
| table | environment | Environment reference, do not change |
Set the mission information reference for base information display.Definition
setMissionInfo(table missionInfo)Arguments
| table | missionInfo | MissionInfo reference, do not change |
Set the mission statistics reference for base information display.Definition
setMissionStats(table missionStats)Arguments
| table | missionStats | MissionStats reference, do not change |
Set the in-game icon display when picking up an egg or nugget.Definition
setInGameIconOnPickup()
Scroll chat messages by a given amount.Definition
scrollChatMessages(int delta, int numMessages)Arguments
| int | delta | Number of lines (positive or negative) to scroll |
| int | numMessages | Number of currently stored chat messages |
Set chat window visibility.Definition
When disabled, the chat display will linger for some time.
setChatWindowVisible()
Set the chat messages array reference for the chat window.Definition
The array is owned by the caller and must not be modified by consumers.
setChatMessagesReference()
Register input events of HUD components.Definition
registerInput()
HUD display element whose subclasses implement more complex HUD display subsystems.
--@category GUI
Create a new HUD display element.Definition
new(table subClass, table overlay, table parentHudElement)Arguments
| table | subClass | Subclass metatable for inheritance |
| table | overlay | Wrapped Overlay instance |
| table | parentHudElement | [optional] Parent HUD element of the newly created HUD element |
| table | HUDDisplayElement | instance |
Set this element's visibility with optional animation.Definition
setVisible(bool isVisible, bool animate)Arguments
| bool | isVisible | True is visible, false is not. |
| bool | animate | If true, the element will play an animation before applying the visibility change. |
Simplification of scale setter because these high-level elements always use a uniform scale.Definition
setScale()
Store the current element position as its original positions.Definition
storeOriginalPosition()
Get the screen space translation for hiding.Definition
Override in sub-classes if a different translation is required.
getHidingTranslation()Return Values
| float | Screen | space X translation |
| float | Screen | space Y translation |
Animation setter function for X position.Definition
animationSetPositionX()
Animation setter function for Y position.Definition
animationSetPositionY()
Animate this element on hiding.Definition
animateHide()
Animate this element on showing.Definition
animateShow()
Called when a hiding or showing animation has finished.Definition
onAnimateVisibilityFinished()
Lightweight HUD UI element.
-- Wraps an Overlay instance to display and provides a transform hierarchy of child HUDElement instances.
--@category GUI
Create a new HUD element.Definition
new(table subClass, table overlay, table parentHudElement)Arguments
| table | subClass | Subclass metatable for inheritance |
| table | overlay | Wrapped Overlay instance |
| table | parentHudElement | [optional] Parent HUD element of the newly created HUD element |
| table | HUDElement | instance |
Delete this HUD element and all its children.Definition
This will also delete the overlay and thus release its engine handle.
delete()
Add a child HUD element to this element.Definition
addChild(table childHudElement)Arguments
| table | childHudElement | HUDElement instance which is added as a child. |
Remove a child HUD element from this element.Definition
removeChild(table childHudElement)Arguments
| table | childHudElement | HUDElement instance which is removed as a child. |
Set a HUD element's absolute screen space position.Definition
If the element has any children, they will be moved with this element.
setPosition()
Set this HUD element's rotation.Definition
Does not affect children. If no center position is given, the element's pivot values are used (default to 0)
setRotation(float rotation, float centerX, float centerY)Arguments
| float | rotation | Rotation in radians |
| float | centerX | [optional] Rotation pivot X position offset from overlay position in screen space |
| float | centerY | [optional] Rotation pivot Y position offset from overlay position in screen space |
Set this HUD element's rotation pivot point.Definition
setRotationPivot(float pivotX, float pivotY)Arguments
| float | pivotX | Pivot x position offset from element position in screen space |
| float | pivotY | Pivot y position offset from element position in screen space |
Get this HUD element's rotation pivot point.Definition
getRotationPivot()Return Values
| float | Pivot | x position offset from element position in screen space |
| float | Pivot | y position offset from element position in screen space |
Get this HUD element's position.Definition
getPosition()Return Values
| float | X | position in screen space |
| float | Y | position in screen space |
Set this HUD element's scale.Definition
This will move and scale children proportionally.
setScale(float scaleWidth, float scaleHeight)Arguments
| float | scaleWidth | Width scale factor |
| float | scaleHeight | Height scale factor |
Get this HUD element's scale.Definition
getScale()Return Values
| width | scale | factor |
| height | scale | factor |
Set this HUD element's positional alignment.Definition
See Overlay:setAlignment for positioning logic.
setAlignment(int vertical, int horizontal)Arguments
| int | vertical | Vertical alignment value [Overlay.ALIGN_VERTICAL_BOTTOM | Overlay.ALIGN_VERTICAL_MIDDLE | Overlay.ALIGN_VERTICAL_TOP] |
| int | horizontal | Horizontal alignment value [Overlay.ALIGN_HORIZONTAL_LEFT | Overlay.ALIGN_HORIZONTAL_CENTER | Overlay.ALIGN_HORIZONTAL_RIGHT] |
Set this HUD element's visibility.Definition
setVisible()
Get this HUD element's visibility.Definition
getVisible()
Get this HUD element's color.Definition
getColor()Return Values
| float | Red | value |
| float | Green | value |
| float | Blue | value |
| float | Alpha | value |
Get this HUD element's color alpha value.Definition
getAlpha()Return Values
| float | Alpha | value |
Get this HUD element's width in screen space.Definition
getWidth()
Get this HUD element's height in screen space.Definition
getHeight()
Set this HUD element's width and height.Definition
Either value can be omitted (== nil) for no change.
setDimension()
Reset this HUD element's dimensions to their default values.Definition
Resets width, height, scale and pivot.
resetDimensions()
Set this HUD element overlay's color.Definition
Children are unaffected.
setColor()
Set this HUD element overlay's color alpha value only.Definition
setAlpha()
Set this HUD element overlay's image file.Definition
setImage()
Set this HUD element overlay's UV coordinates.Definition
setUVs()
Update this HUD element's state.Definition
update()
Draw this HUD element and all of its children in order of addition.Definition
draw()
Convert a vector from pixel values into scaled screen space values.Definition
scalePixelToScreenVector(table vector2D)Arguments
| table | vector2D | Array of two pixel values |
Convert a vertical pixel value into scaled screen space value.Definition
scalePixelToScreenHeight(float height)Arguments
| float | height | Vertical pixel value |
Convert a horizontal pixel value into scaled screen space value.Definition
scalePixelToScreenWidth(float width)Arguments
| float | width | Horizontal pixel value |
Convert a texture space pivot to an element-local pivot.Definition
normalizeUVPivot(table uvPivot, table uvs)Arguments
| table | uvPivot | Array of two pixel pivot coordinates in texture space |
| table | uvs | Array of UV coordinates as {x, y, width, height} |
HUD background frame element.
-- Displays a transparent frame with a thick bottom bar for use as a background in HUD elements.
--@category GUI
Create a new instance of FrameElement.Definition
new(string hudAtlasPath, float posX, float posY, float width, float height, table parent)Arguments
| string | hudAtlasPath | Path to the HUD atlas texture |
| float | posX | Initial X position in screen space |
| float | posY | Initial Y position in screen space |
| float | width | Frame width in screen space |
| float | height | Frame height in screen space |
| table | parent | [optional] Parent HUDElement which will receive this frame as its child element |
Create display components.Definition
createComponents()
Set frame element dimensions.Definition
Override from HUDElement to preserve border positioning and sizes.
setDimension()
HUD popup message.
-- Displays a modal popup message which requires a player input to be accepted / dismissed or expires after a given
time.
--@category GUI
Create a new instance of HUDPopupMessage.Definition
new(string hudAtlasPath, l10n I18N, inputManager InputBinding, InputDisplayManager InputDisplayManager, IngameMap IngameMap)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
| l10n | I18N | reference for text localization |
| inputManager | InputBinding | reference for custom input context handling |
| InputDisplayManager | InputDisplayManager | for input glyph display |
| IngameMap | IngameMap | reference used to notify the map when a message is shown |
| table | HUDPopupMessage | instance |
Show a new message.Definition
showMessage(string title, string message, int duration, table controls, function callback, table target)Arguments
| string | title | Title text |
| string | message | Main message text |
| int | duration | Message display duration in milliseconds. If set to 0, will cause the message to be displayed for a duration derived from the message length. If set to <0, will cause the message to be displayed for a very long time. |
| table | controls | [optional] Array of InputHelpElement instance for input hint row display |
| function | callback | [optional] Function to be called when the message is acknowledged or expires |
| table | target | [optional] Callback target which is passed as the first argument to the given callback function |
Set the game's paused state on this element.Definition
If the game is paused, the message timer is stopped and no new messages are displayed.
setPaused()
Get this HUD element's visibility.Definition
getVisible()
Get the screen space translation for hiding.Definition
Override in sub-classes if a different translation is required.
DisplayElement:getHidingTranslation()Return Values
| float | Screen | space X translation |
| float | Screen | space Y translation |
Handle menu visibility changes.Definition
onMenuVisibilityChange()
Assign a new current message and adjust display state accordingly.Definition
This also resizes the message box according to the required space.
assignCurrentMessage()
Get the display height of the current message's title.Definition
getTitleHeight()
Get the display height of the current message's text.Definition
getTextHeight()
Get the display height of the current message's input rows.Definition
getInputRowsHeight()
Animate this element on showing.Definition
animateHide()
Start displaying a message dequeued from the currently pending messages.Definition
Sets all required display and input state.
startMessage()
Finish displaying a message after it has either elapsed or been acknowledged by the player.Definition
Resets display and input state and triggers any provided message callback.
finishMessage()
Update this element's state.Definition
update()
Update the current message.Definition
Disables this popup when time runs out and dequeues a pending messages for displaying.
updateCurrentMessage()
Update button glyphs when the player input mode has changed.Definition
updateButtonGlyphs()
Enable / disable input events for message confirmation / skipping.Definition
setInputActive()
Event function for either InputAction.SKIP_MESSAGE_BOX or InputAction.MENU_ACCEPT.Definition
onConfirmMessage()
Draw the message.Definition
draw()
Get this element's base background position.Definition
getBackgroundPosition(float uiScale)Arguments
| float | uiScale | Current UI scale factor |
Set uniform UI scale.Definition
setScale()
Set this HUD element's width and height.Definition
setDimension()
Store scaled positioning, size and offset values.Definition
storeScaledValues()
Create the background overlay.Definition
createBackground()
Create required display components.Definition
createComponents()
Create components for an input button row.Definition
createInputRow()
HUD text display.
-- Displays a formatted single-line text with optional animations.
--@category GUI
Create a new HUDTextDisplay.Definition
new(float posX, float posY, float textSize, int textAlignment, table textColor, bool textBool)Arguments
| float | posX | Screen space X position of the text display |
| float | posY | Screen space Y position of the text display |
| float | textSize | Text size in reference resolution pixels |
| int | textAlignment | Text alignment as one of RenderText.[ALIGN_LEFT | ALIGN_CENTER | ALIGN_RIGHT] |
| table | textColor | Text display color as an array {r, g, b, a} |
| bool | textBool | If true, will render the text in bold |
| table | HUDTextDisplay | instance |
Set the text to display.Definition
setText(string text, float textSize, int textAlignment, table textColor, bool textBool)Arguments
| string | text | Display text |
| float | textSize | Text size in reference resolution pixels |
| int | textAlignment | Text alignment as one of RenderText.[ALIGN_LEFT | ALIGN_CENTER | ALIGN_RIGHT] |
| table | textColor | Text display color as an array {r, g, b, a} |
| bool | textBool | If true, will render the text in bold |
Set the text display UI scale.Definition
setScale()
Set this element's visibility.Definition
setVisible(bool isVisible, bool animate)Arguments
| bool | isVisible | Visibility state |
| bool | animate | If true, will play the currently set animation on becoming visible or and reset it when necessary. |
Set the global alpha value for this text display.Definition
The alpha value will be multiplied with any text color alpha channel value.
setAlpha()
Set the text color by channels.Definition
Use for dynamic changes and animation.
setTextColorChannels()
Set the text shadow state.Definition
setTextShadow(bool isShadowEnabled, table shadowColor)Arguments
| bool | isShadowEnabled | If true, will cause a shadow to be rendered under the text |
| table | shadowColor | Shadow text color as an array {r, g, b, a} |
Set an animation tween (sequence) for this text display.Definition
The animation can be played when calling HUDTextDisplay:setVisible() with the "animate" paramter set to true.
setAnimation()
Update this element's state.Definition
update()
Draw the text.Definition
draw()
Creating manager
Deletes instanceDefinition
delete()Code
| 29 | function HusbandryModuleFoodSpillage:delete() |
| 30 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 34 | function HusbandryModuleFoodSpillage:initDataStructures() |
| 35 | HusbandryModuleFoodSpillage:superClass().initDataStructures(self) |
| 36 | |
| 37 | self.spillageAreas = {} |
| 38 | self.foodToDrop = 0 |
| 39 | self.spillageFillType = FillType.UNKNOWN |
| 40 | self.lineOffset = 0 |
| 41 | self.cleanlinessFactor = 0.0 |
| 42 | self.hasCleanliness = false |
| 43 | end |
Loads data from xmlDefinition
load(table xmlFile, string xmlKey, table rootNode)Arguments
| table | xmlFile | handle |
| string | xmlKey | from which to read the configuration |
| table | rootNode | of the husbandry |
| boolean | true | if loading was successful else false |
| 51 | function HusbandryModuleFoodSpillage:load(xmlFile, configKey, rootNode, owner) |
| 52 | if not HusbandryModuleFoodSpillage:superClass().load(self, xmlFile, configKey, rootNode, owner) then |
| 53 | return false |
| 54 | end |
| 55 | |
| 56 | if not hasXMLProperty(xmlFile, configKey) then |
| 57 | return false |
| 58 | end |
| 59 | |
| 60 | local i = 0 |
| 61 | while true do |
| 62 | local areaKey = string.format("%s.area(%d)", configKey, i) |
| 63 | if not hasXMLProperty(xmlFile, areaKey) then |
| 64 | break |
| 65 | end |
| 66 | local start = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, areaKey .. "#startNode")) |
| 67 | local width = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, areaKey .. "#widthNode")) |
| 68 | local height = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, areaKey .. "#heightNode")) |
| 69 | |
| 70 | if start ~= nil and width ~= nil and height ~= nil then |
| 71 | table.insert(self.spillageAreas, {start = start, width = width, height = height}) |
| 72 | end |
| 73 | i = i + 1 |
| 74 | end |
| 75 | |
| 76 | local spillageFillType = getXMLString(xmlFile, configKey .. "#fillType") |
| 77 | if spillageFillType ~= nil then |
| 78 | local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(spillageFillType) |
| 79 | if fillTypeIndex ~= nil then |
| 80 | self.spillageFillType = fillTypeIndex |
| 81 | end |
| 82 | end |
| 83 | |
| 84 | self.hasCleanliness = true |
| 85 | |
| 86 | return self.spillageFillType ~= nil and #self.spillageAreas > 0 |
| 87 | end |
Reads network streamDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | network stream identification |
| table | connection | connection information |
| 94 | function HusbandryModuleFoodSpillage:readStream(streamId, connection) |
| 95 | HusbandryModuleFoodSpillage:superClass().readStream(self, streamId, connection) |
| 96 | |
| 97 | if self.hasCleanliness then |
| 98 | self.cleanlinessFactor = streamReadUInt8(streamId) / 255 |
| 99 | end |
| 100 | end |
Writes network streamDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | network stream identification |
| table | connection | connection information |
| 106 | function HusbandryModuleFoodSpillage:writeStream(streamId, connection) |
| 107 | HusbandryModuleFoodSpillage:superClass().writeStream(self, streamId, connection) |
| 108 | |
| 109 | if self.hasCleanliness then |
| 110 | local cleanliness = math.floor(self.cleanlinessFactor * 255 + 0.5) |
| 111 | streamWriteUInt8(streamId, cleanliness) |
| 112 | end |
| 113 | end |
Read updates from network streamDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | network stream identification |
| integer | timestamp | |
| table | connection | connection information |
| 120 | function HusbandryModuleFoodSpillage:readUpdateStream(streamId, timestamp, connection) |
| 121 | HusbandryModuleFoodSpillage:superClass().readUpdateStream(self, streamId, timestamp, connection) |
| 122 | |
| 123 | if self.hasCleanliness then |
| 124 | self.cleanlinessFactor = streamReadUInt8(streamId) / 255 |
| 125 | end |
| 126 | end |
Write updates from network streamDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | network stream identification |
| table | connection | connection information |
| integer | dirtyMask | is used to check if we need to update |
| 133 | function HusbandryModuleFoodSpillage:writeUpdateStream(streamId, connection, dirtyMask) |
| 134 | HusbandryModuleFoodSpillage:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
| 135 | |
| 136 | if self.hasCleanliness then |
| 137 | local cleanliness = math.floor(self.cleanlinessFactor * 255 + 0.5) |
| 138 | streamWriteUInt8(streamId, cleanliness) |
| 139 | end |
| 140 | end |
Update water usageDefinition
onIntervalUpdate(float dayToInterval)Arguments
| float | dayToInterval |
| 145 | function HusbandryModuleFoodSpillage:onIntervalUpdate(dayToInterval) |
| 146 | HusbandryModuleFoodSpillage:superClass().onIntervalUpdate(self, dayToInterval) |
| 147 | self:updateCleanlinessFactor(dayToInterval) |
| 148 | end |
Loads information from attributes and node. Retrives from xml file information.Definition
loadFromXMLFile(table xmlFile, string key)Arguments
| table | xmlFile | XML file handler |
| string | key | XML base key |
| 183 | function HusbandryModuleFoodSpillage:loadFromXMLFile(xmlFile, key) |
| 184 | HusbandryModuleFoodSpillage:superClass().loadFromXMLFile(self, xmlFile, key) |
| 185 | |
| 186 | if self.hasCleanliness then |
| 187 | self.cleanlinessFactor = Utils.getNoNil(getXMLFloat(xmlFile, key.."#cleanlinessFactor"), self.cleanlinessFactor) |
| 188 | self.foodToDrop = Utils.getNoNil(getXMLFloat(xmlFile, key.."#foodToDrop"), self.foodToDrop) |
| 189 | end |
| 190 | end |
Update spillage mechanics.Definition
updateFoodSpillage(float spillageDelta)Arguments
| float | spillageDelta | amount of food to increase |
| float | food | dropped |
| 205 | function HusbandryModuleFoodSpillage:updateFoodSpillage(spillageDelta) |
| 206 | local foodDropped = 0 |
| 207 | |
| 208 | if self.hasCleanliness and self.cleanlinessFactor > 0 and spillageDelta > g_densityMapHeightManager:getMinValidLiterValue(self.spillageFillType) then |
| 209 | local i = math.random(1, #self.spillageAreas) |
| 210 | local spillageArea = self.spillageAreas[i] |
| 211 | local xs,_,zs = getWorldTranslation(spillageArea.start) |
| 212 | local xw,_,zw = getWorldTranslation(spillageArea.width) |
| 213 | local xh,_,zh = getWorldTranslation(spillageArea.height) |
| 214 | local ux, uz = xw - xs, zw - zs |
| 215 | local vx, vz = xh - xs, zh - zs |
| 216 | local vLength = MathUtil.vector2Length(vx,vz) |
| 217 | local sx = xs + (math.random() * ux) + (math.random() * vx) |
| 218 | local sz = zs + (math.random() * uz) + (math.random() * vz) |
| 219 | local ex = xs + (math.random() * ux) + (math.random() * vx) |
| 220 | local ez = zs + (math.random() * uz) + (math.random() * vz) |
| 221 | local sy = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, sx, 0.0, sz) |
| 222 | local ey = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, ex, 0.0, ez) |
| 223 | local dropped, lineOffset = DensityMapHeightUtil.tipToGroundAroundLine(nil, spillageDelta, self.spillageFillType, sx,sy,sz, ex,ey,ez, 0, vLength, self.lineOffset, false, nil) |
| 224 | |
| 225 | foodDropped = dropped |
| 226 | self.lineOffset = lineOffset |
| 227 | end |
| 228 | |
| 229 | return foodDropped |
| 230 | end |
Get spillage levelDefinition
getFoodSpillageLevel()Return Values
| float | spillage | total |
| 235 | function HusbandryModuleFoodSpillage:getFoodSpillageLevel() |
| 236 | local totalLevel = 0 |
| 237 | for _, spillageArea in ipairs(self.spillageAreas) do |
| 238 | local xs,_,zs = getWorldTranslation(spillageArea.start) |
| 239 | local xw,_,zw = getWorldTranslation(spillageArea.width) |
| 240 | local xh,_,zh = getWorldTranslation(spillageArea.height) |
| 241 | local fillLevel = DensityMapHeightUtil.getFillLevelAtArea(self.spillageFillType, xs, zs, xw, zw, xh, zh) |
| 242 | totalLevel = totalLevel + fillLevel |
| 243 | end |
| 244 | return totalLevel |
| 245 | end |
getSpillageFactor()Code
| 249 | function HusbandryModuleFoodSpillage:getSpillageFactor() |
| 250 | if self.hasCleanliness then |
| 251 | return self.cleanlinessFactor |
| 252 | end |
| 253 | |
| 254 | return nil |
| 255 | end |
Sets the world direction for a given object
Checks if given index is validDefinition
checkChildIndex(integer node, string index)Arguments
| integer | node | id of an object |
| string | index | index |
| boolean | isActivateable | is activateable |
| boolean | isValid | true if index is valid else false |
Returns index of objectDefinition
indexToObject(table components, string index, table mappings)Arguments
| table | components | components (also (integer) id of root node possible) |
| string | index | index |
| table | mappings | id to index mapping table |
| table | instance | instance of object |
| integer | id | id of object |
| integer | id | id of used root node |
Sets the number shader attributesDefinition
setNumberShaderByValue(integer numbers, float value, integer precision, boolean showZero)Arguments
| integer | numbers | id of the node containing the number shapes |
| float | value | value |
| integer | precision | precision |
| boolean | showZero | true if zero should be shown |
| table | self | returns the instance |
Wake up a collision objectDefinition
wakeUpObject(integer node)Arguments
| integer | node | id of a collision object |
| bool | return | true if successful |
limitedAxis 3: 0 degree = x axis, 90 degree = y axisDefinition
setWorldDirection(integer node, float dirX, float dirY, float dirZ, float upX, float upY, float upZ, integer limitedAxis, float minRot, float maxRot)Arguments
| integer | node | id of object |
| float | dirX | dirX |
| float | dirY | dirY |
| float | dirZ | dirZ |
| float | upX | upX |
| float | upY | upY |
| float | upZ | upZ |
| integer | limitedAxis | index of limited axis |
| float | minRot | minRot |
| float | maxRot | maxRot |
| float | returns | the change delta |
Sets direction for a given object (only if the direction is valid)Definition
setDirection(integer node, float dirX, float dirY, float dirZ, float upX, float upY, float upZ)Arguments
| integer | node | id of object |
| float | dirX | dirX |
| float | dirY | dirY |
| float | dirZ | dirZ |
| float | upX | upX |
| float | upY | upY |
| float | upZ | upZ |
| table | self | returns the instance |
Sets a shader parameter recursivlyDefinition
setShaderParameterRec(integer node, string shaderParam, float x, float y, float z, float w)Arguments
| integer | node | id of the top node |
| string | shaderParam | the name of the shader param |
| float | x | x |
| float | y | y |
| float | z | z |
| float | w | w |
| bool | return | true if successful |
Gets a list of nodes with shaderParam assigned to itDefinition
getNodesByShaderParam(integer node, string shaderParam, table nodes)Arguments
| integer | node | id of the top node |
| string | shaderParam | the name of the shader param |
| table | nodes | a list of nodes |
Index change subject mixin.
-- Add this mixin to a GuiElement to implement an observer pattern for index changes (e.g. paging, options, lists).
-- Added methods:
GuiElement:addIndexChangeObserver(observer, indexChangeCallback): Register an observer to be notified on index changes
observer is passed to the index change callback as the first argument, typically you would use "self"
indexChangeCallback must be a function with the signature function(observer, index, count)
GuiElement:notifyIndexChange(index, count): Called by the decorated GuiElement when the index or number of indexed items changes, triggers callbacks
index is the new index after the current change
count is the number of indexed items which may or may not have changed
--@category GUI
See GuiMixin:addTo().Definition
addTo()
Add an index change observer with a callback.Definition
addIndexChangeObserver(GuiElement Decorated, observer Observer, indexChangeCallback Function(observer,)Arguments
| GuiElement | Decorated | GuiElement instance which has received this method |
| observer | Observer | object instance |
| indexChangeCallback | Function(observer, | index, count), where index is the new index and count the current number of indexable items |
Notify observers of an index change.Definition
notifyIndexChange(GuiElement Decorated, index New, count Indexable)Arguments
| GuiElement | Decorated | GuiElement instance which has received this method |
| index | New | index |
| count | Indexable | item count |
Clone this mixin's state from a source to a destination GuiElement instance.Definition
clone()
Index state display element.
-- Displays a visual element per screen page to let the player see an indication of their current list, page or multi-
option selection index. The element points at a UI element which supports indexed access, such as ListElement,
PageElement or MultiTextOption. For other cases, UI views directly manipulate the states (e.g. cycling hints in the
loading screen).
--@category GUI
--@xmlConfig GuiElement#stateElementTemplateId string ID of a descendant, sibling or sibling's descendant GUI element which is duplicated per page. The element should be configured to have different visuals for normal and selected GuiOverlay states (layers).
Find and store the state element template which is configured in #stateElementTemplateId.Definition
The function searches from this elements parent downwards, so any descendant of this element or its siblings may
contain the state element template.
locateStateElementTemplate()
Find and store the indexable element which is configured in #indexableElementId.Definition
The function first locates the current view root (if possible) and then searches downwards. The search is broader
than locateStateElementTemplate() to allow more flexibility in UI design and configuration.
locateIndexableElement()
Event handler for index changes.Definition
onIndexChange(index New, count (New))Arguments
| index | New | index |
| count | (New) | item count |
Set the page count.Definition
Clears current page elements and rebuilds as many as needed from the configured template.
setPageCount(count Number, initialIndex [optional])Arguments
| count | Number | of page indicators to display |
| initialIndex | [optional] | Page index to set after rebuilding |
Set the current page index and update display states.Definition
setPageIndex(index New)Arguments
| index | New | page index |
In-game map display element.
-- This class is used to display the game map both in the HUD as well as in the in-game menu.
--@category GUI
Create a new instance of IngameMap.Definition
new(string hudAtlasPath, table inputDisplayManager)Arguments
| string | hudAtlasPath | Path to the HUD atlas texture |
| table | inputDisplayManager | InputDisplayManager reference |
Delete this element and all of its components.Definition
delete()
Set full-screen mode (for map overview) without affecting the mini-map state.Definition
setFullscreen()
Get the index of a hotspot in the hotspots list.Definition
getHotspotIndex()
Get the next or previous visible hotspot in order from a current hotspot.Definition
cycleVisibleHotspot(table currentHotspot, table categoriesHash, int direction)Arguments
| table | currentHotspot | Currently selected hotspot |
| table | categoriesHash | Table of valid hotspot categories to cycle, keys are categories |
| int | direction | 1 for next, -1 for previous |
Override from HUDElement.Definition
Return zero height when turned off, because the map is only invisible.
getHeight()
Get required display height on screen including all auxiliary elements and texts.Definition
getRequiredHeight()
Set the map size.Definition
setSize(float width, float height)Arguments
| float | width | Width of the map in screen space |
| float | height | Height of the map in screen space |
Set the map's position.Definition
This sets the actual map display's position to the given values. This elements background is offset accordingly.
setPosition(float posX, float posY)Arguments
| float | posX | New map X position in screen space |
| float | posY | New map Y position in screen space |
Set the map's visibility (= active state).Definition
setIsVisible()
Called when the input for map size toggle is pressed and then released.Definition
onToggleMapSize()
Register map size toggle input event.Definition
registerInput()
Update the map's state.Definition
update()
Update the data about the player's current position.Definition
updatePlayerPosition()
Update the arrow indicating the player's current position.Definition
updatePlayerArrow()
Update map zoom and visibility state.Definition
updateMapHeightZoomFactor()Return Values
| bool | True | if the left border of the map has been reached |
| bool | True | if the right border of the map has been reached |
| bool | True | if the top border of the map has been reached |
| bool | True | if the bottom border of the map has been reached |
Update input display glyphs with the current input context.Definition
updateInputGlyphs()
Update map overlay UVs based on the currently focused position, e.g. a player or hotspot.Definition
updateMapUVs()Return Values
| boolean | true | if loading was successful else false |
| bool | True | if the left border of the map has been reached |
| bool | True | if the right border of the map has been reached |
| bool | True | if the top border of the map has been reached |
| bool | True | if the bottom border of the map has been reached |
Draw the map as hud elementDefinition
draw()Return Values
| boolean | true | if loading was successful else false |
Draw the current player's arrow.Definition
drawPlayerArrow()Return Values
| table | achievement | achievement object |
Draw the map label on top of the map display.Definition
drawMapLabel()Return Values
| table | instance | instance of object |
Draw the map.Definition
drawMap(float alpha, bool isStandalone)Arguments
| float | alpha | [optional] Map opacity value |
| bool | isStandalone | [optional] If true, will draw the map border and other elements. |
| boolean | true | if loading was successful else false |
Draw map hotspot and arrow elements.Definition
drawPointsOfInterest()Return Values
| boolean | true | if loading was successful else false |
Draw arrows for other players' positions.Definition
drawOtherPlayerArrows()Return Values
| boolean | true | if added successful else false |
Draw arrows for enterable vehicles controlled by players.Definition
drawEnterableArrows()Return Values
| table | helper | a random helper object |
Draw all known hotspots on the map.Definition
renderHotspots()Return Values
| integer | helperIndex | a random helper index |
Draw a single hotspot on the map.Definition
drawHotspot()Return Values
| table | helper | the helper object |
Draw the player's current coordinates as text.Definition
drawPlayersCoordinates()Return Values
| table | helper | the helper object |
Draw current latency to server as text.Definition
drawLatencyToServer()Return Values
| boolean | success | true if helper is marked else false |
Set this element's scale.Definition
setScale(float uiScale)Arguments
| float | uiScale | Current UI scale applied to both width and height of elements |
| integer | numOfHelpers | total number of helpers |
Store scaled positioning, size and offset values.Definition
storeScaledValues()Return Values
| table | instance | instance of object |
Get the base position of the entire element.Definition
getBackgroundPosition()Return Values
| boolean | true | if loading was successful else false |
Create the empty background overlay.Definition
createBackground()Return Values
| boolean | true | if loading was successful else false |
Create required display components.Definition
createComponents(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
| boolean | true | if loading was successful else false |
Create the map frame.Definition
createFrame()Return Values
| boolean | true | if added successful else false |
Create the input glyph for map size toggling.Definition
createToggleMapSizeGlyph()Return Values
| table | npc | a random npc object |
Create the map arrow for the player's position.Definition
createPlayerMapArrow()Return Values
| integer | npcIndex | a random npc index |
Create an arrow overlay used for other player's and their vehicles in multiplayer.Definition
createOtherMapArrowOverlay()Return Values
| table | npc | the npc object |
In-game map element.
-- Controls input on the map in the in-game menu with objectives, vehicles, etc. The actual map rendering is deferred to
the map component of the current mission. The map reference and terrain size must be set during mission
initialization via the setIngameMap() and setTerrainSize() methods.
--@category GUI
--@xmlConfig GuiElement#cursorId string ID of element to use as a cursor on the map.
Add a dead zone wherein the map will not react to cursor inputs.Definition
Used this to designate areas where other controls should receive cursor input which would otherwise be used up by
the map (e.g. in full-screen mode in the map overview screen in-game). The deadzones will also restrict cursor
movement.
addCursorDeadzone()
Clear cursor dead zones.Definition
clearCursorDeadzones()
Check if a cursor position is within one of the stored deadzones.Definition
isCursorInDeadzones()
Custom mouse event handling for the in-game map.Definition
Directly handles zoom, click and drag events on the map. See input events and IngameMapElement:checkAndResetMouse()
for the state checking code required to bypass player mouse input bindings.
mouseEvent()
Update base map element values for displaying as an embedded UI element.Definition
updateBaseMapValues()
Set the IngameMap reference to use for display.Definition
setIngameMap()
Set the current map's terrain size for map display.Definition
setTerrainSize()
Register non-GUI input action events.Definition
registerActionEvents()
Remove non-GUI input action events.Definition
removeActionEvents()
Event function for horizontal cursor input bound to InputAction.AXIS_LOOK_LEFTRIGHT_VEHICLE.Definition
onHorizontalCursorInput()
Event function for vertical cursor input bound to InputAction.AXIS_LOOK_UPDOWN_VEHICLE.Definition
onVerticalCursorInput()
Event function for gamepad cursor accept input bound to InputAction.INGAMEMAP_ACCEPT.Definition
onAccept()
Event function for map zoom input bound to InputAction.AXIS_ACCELERATE_VEHICLE and InputAction.AXIS_BRAKE_VEHICLE.Definition
onZoomInput(inputValue Zoom, direction Zoom)Arguments
| inputValue | Zoom | input value |
| direction | Zoom | input sign value, 1 for zoom in, -1 for zoom out |
Check if mouse input was active before a bound input was triggered and queue a reset of the mouse state for the nextDefinition
frame.
Mouse input continuously sets the mouse input flag (self.useMouse) but does not receive any events when the mouse
is inert. Therefore we need to set and reset the state each frame to make sure we can seamlessly switch between mouse
and gamepad input on the map element while at the same time preventing any player bindings from interfering with the
custom mouse input logic of this class.
checkAndResetMouse()
In-Game Menu.
-- Displays the main in-game menu with several pages, depending on the game state and mode (e.g. tutorial or
multiplayer). This menu can be extended and modified by adding and removing pages. Default pages for the base game
are always loaded but can also be removed (effectively just disabled). Custom pages can be entirely new or modified
sub-classes of the default pages. See methods InGameMenu:addPage() and InGameMenu:removePage() for details.
--@category GUI
-- @field header Header panel
Create a new instance of InGameMenu.Definition
new(table target, table custom_mt, table messageCenter, table l10n, table savegameController, table inputManager, table fruitTypeManager, table fillTypeManager, table storeManager, table shopController, table shopConfigScreen, table placementScreen, bool isConsoleVersion)Arguments
| table | target | Callback target |
| table | custom_mt | Sub-class meta table |
| table | messageCenter | MessageCenter reference |
| table | l10n | I18N reference for text localization |
| table | savegameController | SavegameController reference for savegame persistence |
| table | inputManager | InputBinding reference |
| table | fruitTypeManager | FruitTypeManager reference for fruit type information access |
| table | fillTypeManager | FillTypeManager reference for fill type information access |
| table | storeManager | StoreManager reference |
| table | shopController | ShopController reference |
| table | shopConfigScreen | ShopConfigScreen reference |
| table | placementScreen | PlacementScreen reference |
| bool | isConsoleVersion | If true, the game is running on a console |
| table | instance | instance of object |
Set the menu mode.Definition
Switches the menu between pause menu mode or shop mode.
setMode(int menuMode)Arguments
| int | menuMode | One of [InGameMenu.MODE_MENU | InGameMenu.MODE_SHOP] |
| table | instance | instance of object |
Set the in-game map component to use by pages.Definition
setInGameMap()Return Values
| table | brandColor | brandColor object |
Set the current terrain size to use by pages.Definition
setTerrainSize()Return Values
| table | farmhouse | or nil |
Set the known fruit types when loaded for the mission.Definition
setMissionFruitTypes()Return Values
| integer | spawnpoint | node or the career spawnpoint node. |
Set the current list of selling stations for displaying.Definition
setSellingStations()Return Values
| integer | Camera | or 0 if no farmhouse. |
Set the current list of accessible vehicles for displaying.Definition
setAccessibleVehicles()Return Values
| bool | True | if the user is a manager of this farm, false otherwise |
Set the reference to the ban storage.Definition
setBanStorage()Return Values
| table | Permission | hash table {permission=<hasPermission>} |
Set the current list of connected users for displaying.Definition
setConnectedUsers()Return Values
| float | Account | balance |
Set the network client reference.Definition
setClient()Return Values
| float |
Set the network server reference.Definition
setServer()Return Values
| table | instance | instance of object |
Update master rights status.Definition
updateHasMasterRights()Return Values
| boolean | true | if loading was successful else false |
Update garage items display data.Definition
updateGarageItems()Return Values
| boolean | true | if loading was successful else false |
Called when the mission is fully loaded.Definition
Used for late initialization of UI components which rely on mission information.
onLoadMapFinished()Return Values
| table | data | Data for the menu |
Initialize pages for the pause mode.Definition
initializePausePages()Return Values
| table | instance | Instance of object |
Initialize pages for the shop mode.Definition
initializeShopPages()Return Values
| boolean | true | if loading was successful else false |
Set up displayed menu pages and their tabs.Definition
setupMenuPages()Return Values
| table | instance | instance of object |
Define default properties and retrieval collections for menu buttons.Definition
setupMenuButtonInfo()Return Values
| boolean | true | if loading was successful else false |
Add a page tab in the menu header.Definition
Call this synchronously with InGameMenu:registerPage() to ensure a correct order of pages and tabs.
addPageTab()Return Values
| fruit | type | index to be planted |
Set enabled state of a page tab in the header.Definition
setPageTabEnabled()Return Values
| float | sprayFactor | the spray factor of the given field |
Rebuild page tab list in order.Definition
rebuildTabList()Return Values
| float | plowFactor | the plow factor of the given field |
Set environment reference on loading.Definition
setEnvironment()Return Values
| float | plowFactor | the lime factor of the given field |
Set mission info data on loading.Definition
setMissionInfo()Return Values
| float | plowFactor | the weed factor of the given field |
Set the player's current farm reference.Definition
setPlayerFarm()Return Values
| float | area | area found |
| float | totalArea | total area checked |
Set the reference to the current player.Definition
setPlayer()
Set the current user ID.Definition
setCurrentUserId()Return Values
| table | instance | instance of object |
Set manure triggers of the current map/mission.Definition
setManureTriggers()Return Values
| boolean | true | if loading was successful else false |
Set the reference to the current husbandries collection.Definition
setHusbandries()Return Values
| boolean | true | if loading was successful else false |
Reset menu state and go back to the main menu.Definition
leaveCurrentGame()Return Values
| List | of | vehicles. Each element is a table with filanema and configuration properties. |
| Reward | ||
Exit the menu if allowed.Definition
exitMenu()
Exit the menu from the shop configuration screen.Definition
exitMenuFromConfig()
Reset menu state (and all pages).Definition
reset()Return Values
| float | multiplier | harvest multiplier |
Handle in-game menu opening event.Definition
onOpen()Return Values
| table | instance | of the doghouse |
Handle in-game menu closing event.Definition
onClose()Return Values
| string | Vehicle | display name |
Button function for saving the game.Definition
onButtonSaveGame()Return Values
| mixed | value | Value of the setting. The type depends on the setting |
Button function for backing out of the menu.Definition
onButtonBack()Return Values
| boolean | successful | Returns true, if the setting was changed |
Button function for quitting the game to the main menu.Definition
onButtonQuit()Return Values
| table | instance | instance of object |
Button function for switching to the garage view.Definition
onButtonGarage()Return Values
| table | self | instance |
Button function for switching to the shop view from the garage.Definition
onButtonShop()Return Values
| True | if | the element and all of its children could be set up with the given values, false otherwise. |
Button function for repairing an owned vehicle in the garage.Definition
onButtonRepair()Return Values
| True | if | the input event has been consumed, false otherwise |
Handle confirmation of vehicle repairing.Definition
onYesNoRepairDialog()Return Values
| Closest | point | x, y |
Handle a local vehicle repaired event.Definition
Updates the garage view buttons if currently viewing the repaired vehicle.
onVehicleRepairEvent()Return Values
| Next | GUI | element in given direction which can be linked, actual scanning direction used (may change in wrap around scenarios) |
Button function for explicit clicking of item details action buttons.Definition
onButtonAcceptItem()Return Values
| Focus |
Button function for switching owned / leased items in the garage.Definition
onButtonSwitchOwnedLeased()Return Values
| True | if | focus has changed, false otherwise |
Enter placement mode.Definition
startPlacementMode()Return Values
| True | if | navigation in given direction is locked |
Enter saving mode.Definition
startSavingGameDisplay()Return Values
| Root | GuiElement | instance of loaded view or nil if the definition XML file could not be loaded. |
Update page enabled states.Definition
updatePages()Return Values
| Cloned | FrameElement | instance or frameRefElement if resolution failed. |
Update page tabs display after any page changes.Definition
updatePageTabDisplay()Return Values
| Root | GuiElement | of screen or nil if the name did not match any known screen. |
Clear menu button actions, events and callbacks.Definition
clearMenuButtonActions()Return Values
| Root | GuiElement | of dialog or nil if the name did not match any known dialog. |
Assign menu button information to the in-game menu buttons.Definition
assignMenuButtonInfo()Return Values
| True | if | any control has consumed the action event |
Get page titles from currently visible pages and apply to the selector element.Definition
setPageSelectorTitles()Return Values
| table | ScreenElement | descendant instance of the given class or nil if no such instance was registered |
Switch to or from shop detail mode.Definition
This can switch to a category's item list, the owned object or leased object overview by passing in the corresponding
detail page.
setShopDetailMode()Return Values
| table | Root | GuiElement instance of target screen |
Update the menu state each frame.Definition
This uses a state machine approach for the game saving process.
update()Return Values
| table | The | GuiElement instance given in the guiElement parameter |
Directly switch to the finances screen.Definition
openFinancesScreen()Return Values
| New |
Directly switch to the farms screen.Definition
openFarmsScreen()Return Values
| table | with added image data |
Notify the menu that the master server could not be connected to.Definition
setMasterServerConnectionFailed()Return Values
| Color | as | {red, green, blue, alpha} with all values in the range of [0, 1] |
Set the menu state to master user.Definition
Called by the mission if the current player is a master user.
setMasterUserLocal()Return Values
| UV | coordinates | as {u1, v1, u2, v2, u3, v3, u4x, v4} with all values in the range of [0, 1] |
Show the vehicle configuration screen for a given store item.Definition
showConfigurationScreen()Return Values
| New | GuiProfile | instance |
Custom input handling to check shop toggle button when in shop mode.Definition
inputEvent()Return Values
| True | if | profile values could be loaded, false otherwise. |
Handle a menu action click by calling one of the menu button callbacks.Definition
onMenuActionClick()Return Values
| int | Camera | node ID (view point, child of camera base node) |
| int | Camera | base node ID (view target, parent of camera node) |
| bool | True | if no callback was present and no action was taken, false otherwise |
Handle menu confirmation input event.Definition
onClickOk()
Handle menu back input event.Definition
onClickBack()Return Values
| Camera | X | world space position |
| Camera | Z | world space position |
| Camera | Y | rotation in radians |
Handle menu cancel input event.Definition
Bound to quite the game to the main menu.
onClickCancel()
Handle menu active input event.Definition
Bound to save the game.
onClickActivate()
Handle a balance change in game.Definition
onMoneyChanged()
Handle a change of the current slot usage.Definition
onSlotUsageChanged()Return Values
| x | direction | movement [-1, 1] |
| Z | direction | movement [-1, 1] |
Handle selection of a detail item element when in buy mode.Definition
onSelectItemBuyDetail()
Handle selection of a detail item element in the garage.Definition
onSelectItemSellDetail()Return Values
| array | of | normalized values |
Handle tutorial restart confirmation dialog response.Definition
onYesNoRestartTutorial()Return Values
| array | of | the 2 converted values as numbers: {value1, value2} |
Server end game confirmation dialog response callback.Definition
onYesNoEnd()Return Values
| array | of | the 4 converted values as numbers: {value1, value2, value3, value4} |
Handle activation of page selection.Definition
onClickPageSelection()Return Values
| array | of | the 4 converted values as numbers: {red, green, blue, alpha} |
Handle previous page event.Definition
onPagePrevious()Return Values
| array | of | the UV coordinates as {u1, v1, u2, v2, u3, v3, u4, v4} |
Handle next page event.Definition
onPageNext()Return Values
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Handle changing to another menu page.Definition
onPageChange()Return Values
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Update the buttons panel when a given page is visible.Definition
updateButtonsPanel()Return Values
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Get button actions and display information for a given menu page.Definition
getPageButtonInfo()Return Values
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Handle a page being disabled.Definition
onPageUpdate()Return Values
| array | of | display information, {i={colors={true=[{r,g,b,a} colorblind], false=[{r,g,b,a} default], iconFilename=path, iconUVs={u1, v1, u2, v2, u3, v3, u4, v4}, description=text, fruitTypeIndex=index}} |
Handle saving game confirmation when losing master server connection.Definition
onConnectionFailedDialogClick()Return Values
| array | of | display information, {i={colors={true={i={r,g,b,a}}, false={i={r,g,b,a}}}, description=text}} |
Called from the mission controller when vehicles are modified.Definition
onVehiclesChanged()Return Values
| array | of | display information, {i={colors={true={i={r,g,b,a}}, false={i={r,g,b,a}}}, description=text}} |
Handle clicking on a brand in shop mode.Definition
onClickBrand()Return Values
| float | X | position in screen space |
| float | Y | position in screen space |
Handle clicking on an item category in shop mode.Definition
onClickItemCategory()
Notify the player when validating the savegame list.Definition
Will prompt to select a storage device or inform that the chosen device has no more space available.
notifyValidateSavegameList()Return Values
| float | Width | scale factor |
| float | Height | scale factor |
Notify the player that the game is saving, block input via dialog until the operation finishes.Definition
notifyStartSaving()
Notify the menu when a save completes successfully so we can set the saving state flag.Definition
notifySaveComplete()Return Values
| table | SettingsModel | instance |
Notify the player that the savegame could not be saved.Definition
notifySavegameNotSaved()Return Values
| table | Currently | active (changed) settings value |
Prompt the player to confirm overwriting an existing savegame.Definition
notifyOverwriteSavegame()Return Values
| bool | True | if any setting has been changed, false otherwise |
Notify the player that saving failed because the current device has no more space, ask to select another device.Definition
notifySaveFailedNoSpace()Return Values
| bool | True | if any setting has been changed, false otherwise |
Register a page frame element in the menu.Definition
This does not add the page to the paging component of the menu.
registerPage(table pageFrameElement, int position, function enablingPredicateFunction)Arguments
| table | pageFrameElement | Page FrameElement instance |
| int | position | [optional] Page position index in menu |
| function | enablingPredicateFunction | [optional] A function which returns the current enabling state of the page at any time. If the function returns true, the page should be enabled. If no argument is given, the page is always enabled. |
| bool | True | if no callback was present and no action was taken, false otherwise |
Unregister a page frame element identified by class from the menu.Definition
This does not remove the page from the paging component of the menu or the corresponding page tab from the header.
unregisterPage(table pageFrameClass)Arguments
| table | pageFrameClass | FrameElement descendant class of a page which was previously registered |
| bool | True | if there was a page of the given class and it was unregistered |
| table | Unregistered | page controller instance or nil |
| table | Unregistered | page root GuiElement instance or nil |
| table | Unregistered | page tab ListElement instance of nil |
| bool | True | if there was a page of the given class and it was unregistered |
| table | Unregistered | page controller instance or nil |
| table | Unregistered | page root GuiElement instance or nil |
| table | Unregistered | page tab ListElement instance of nil |
Add a page frame to be displayed in the menu at runtime.Definition
The page will be part of the in-game menu until restarting the game or removing the page again.
-- @usage
local frameController = CustomFrameElement:new(...) -- create controller instance
g_gui:loadGui("<frame layout XML path>", "<frame name>", frameController, true) -- load UI components, attach controller
local enablePage = function() -- define predicate function which returns true when the page should be enabled
return self.example.isSkyBlue
end
self:addPage(frameController, position, enablePage) -- add page to menu (self)
-- @param table pageFrameElement FrameElement instance which is used as a page.
addPage(int position, string tabIconFilename, table tabIconUVs, function enablingPredicateFunction)Arguments
| int | position | Position index of added page, starting from left at 1 going to right. |
| string | tabIconFilename | Path to the texture file which contains the icon for the page tab in the header |
| table | tabIconUVs | UV array for the tab icon. Format: {x, y, width, height} in pixels on the texture. |
| function | enablingPredicateFunction | [optional] A function which returns the current enabling state of the page at any time. If the function returns true, the page should be enabled. If no argument is given, the page is always enabled. |
Remove a page from the menu at runtime by its class type.Definition
The removed page is also deleted, including all of its children. Note that this method removes the page for an entire
game run, because the UI is loaded on game start. If you only need to disable a page, use InGameMenu:setPageEnabled()
instead.
-- The method will not remove game default pages, but only disable them.
-- @usage
self:removePage(CustomFrameElement) -- where CustomFrameElement is a sub-class of FrameElement
-- @param table pageFrameClass Class table of a FrameElement sub-class
removePage()
Set the enabled state of a page identified by its controller class type.Definition
This will also set the controller's state, so it can react to being enabled/disabled. The setting will persist
through calls to InGameMenu:reset() and must be reverted manually, if necessary.
setPageEnabled(table pageFrameClass, bool isEnabled)Arguments
| table | pageFrameClass | Class table of a FrameElement sub-class |
| bool | isEnabled | True for enabled, false for disabled |
Update the button information for the garage.Definition
updateGarageButtonInfo()Return Values
| {name="internalName", | displayName="displayName", | inputTexts={1=text1, 2=text2, ...}, inputBindings={1=binding1, 2=binding2, ...}, positiveInput=[true|false]} |
Make a callback which encloses the self reference and handles arbitrary arguments afterwards.Definition
makeSelfCallback()Return Values
| List | of | DisplayActionBinding instances |
In-game menu animals statistics frame.
-- Displays information for all owned animal pens and horses.
--@category GUI
Create a new InGameMenuAnimalsFrame instance.Definition
new(table subclass_mt, table messageCenter, table l10n, table animalManager, table animalFoodManager, table fillTypeManager)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| table | messageCenter | MessageCenter reference for local message subscriptions |
| table | l10n | I18N reference for localization |
| table | animalManager | AnimalManager reference for animal type and information resolution |
| table | animalFoodManager | AnimalFoodManager reference for food mixture information |
| table | fillTypeManager | FillTypeManager reference for fill type resolution |
| Display | ||
| table | InGameMenuAnimalsFrame | instance |
Late initialization.Definition
initialize()Return Values
| True | if | the controller starts listening for input, false otherwise (also if it is already listening!) |
Set the reference to the husbandries collection.Definition
The husbandries are defined as {<husbandry ID> = AnimalHusbandry}
setHusbandries()Return Values
| bool | True | if an actual binding was deleted, false otherwise |
Build a livestock list item for a husbandry.Definition
buildLivestockListItem()Return Values
| True | if | a binding change was made, false otherwise |
Build animal list items for all horses in a horse husbandry.Definition
buildHorseListItems()Return Values
| True | if | a binding change was made, false otherwise |
Rebuild the animal list from husbandry data.Definition
rebuildAnimalList()Return Values
| True | if | a binding change was made, false otherwise |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| True | if | binding has been assigned; reference to first colliding binding or nil |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | DirectSellDialog | instance |
Modify a status bar's value, applying the correct size and color as needed.Definition
setStatusBarValue(table statusBarElement, float value, float startOffset, table profiles, float overrideStatusValue)Arguments
| table | statusBarElement | BitmapElement instance which receives a new size and profile depending on the value |
| float | value | Fractional value in the range of [0, 1] |
| float | startOffset | Start offset value in the range of [0, 1], value will be displayed from there on until 1. |
| table | profiles | Large or small status bar profile table, either InGameMenuAnimalsFrame.PROFILE.STATUS_BAR_LARGE or InGameMenuAnimalsFrame.PROFILE.STATUS_BAR_SMALL |
| float | overrideStatusValue | [optional] If provided, tests this value against the color thresholds instead of the given status bar value |
| Flows | and | cells as nested tables: [flowIndex][cell index] = cell data {element, flowSize, lateralSize} |
Display a husbandry requirements row with the given data.Definition
If no label text is specified, the row will be hidden.
displayRequirement()Return Values
| List | of | lateral flow sizes, total lateral sizes (sum of lateral flow sizes), maximum flow size in direction of flow |
Display a husbandry conditions row with the given data.Definition
If no label text is specified, the row will be hidden.
displayCondition()Return Values
| Layout | X | starting offset, layout Y starting offset, Layout X direction {-1|1}, Layout Y direction {-1|1} |
Sum up the values of an array of animal husbandry fill level info tables.Definition
The fill level info tables have the form of {fillType=fillType, fillLevel=fillLevel, capacity=capacity}. The method
assumes that the array contains only different instances of the same fill type.
sumFillLevelInfos()Return Values
| element | X | offset, element Y offset |
Update production display for a livestock husbandry.Definition
WIP, no assets ready for testing!
updateLivestockHusbandryProductionDisplay()Return Values
| which | should receive focus instead |
Update base conditions (dirt, water, straw) display for a husbandry.Definition
updateHusbandryConditionsDisplay()Return Values
| float | Modified | X offset |
| float | Modified | Y offset |
Update food levels and capacities display for a husbandry.Definition
updateHusbandryFoodDisplay()
Display data of a single horse in the detail view.Definition
displayHorse()Return Values
| True | if | the input event has been handled, false otherwise. |
Display data of a livestock husbandry (pigs, cows, etc.) in the detail view.Definition
displayLivestock()Return Values
| True | if | the keyboard input has been processed by this element. |
Update contextual menu buttons.Definition
updateMenuButtons()Return Values
| Actual | element to focus. |
Rename the currently selected horse if the new name has been confirmed.Definition
renameCurrentHorse()Return Values
| List | of | this element's descendants in depth-first order with contiguous numeric indices. |
Get a formatted food description text for an animal type.Definition
getFoodDescription()Return Values
| First | matching | descendant element in depth-first order or nil, if no element matched the predicate function |
Handle "rename" button activation when a horse is selected.Definition
onButtonRename()Return Values
| element | or | nil |
Handle animal list selection changes.Definition
onListSelectionChanged()Return Values
| element | or | nil |
Exposed controls usable as fields in this frame, identified in configuration.
Create a new InGameMenuContractsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| parent | element | or full screen borders in an array: {minX, minY, maxX, maxY} |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| element | borders | in an array: {minX, minY, maxX, maxY} |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| float | X | aspect scale factor |
| float | Y | aspect scale factor |
Update the state of the frame buttons.Definition
setButtonsForState()
Request a mission list update, but remember the position of the selectionDefinition
If the mission is still in the list, the selection stays
updateList()Return Values
| index | or | nil, field name |
Update the contents of the contract detail screen (context sensitive)Definition
updateDetailContents()Return Values
| Actual | element to focus. |
Update the content of the box with farmer information, using the field.Definition
updateFarmersBox()Return Values
| bool | True | if the page changed |
Financial overview of the player's farm for the in-game menu.
-- Displays current balance and loan situation as well as past and present incomes and expenses.
--@category GUI
Create a new InGameMenuFinancesFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| Page |
Initialize the finances frame after component creation.Definition
initialize()Return Values
| Page | container | GuiElement instance |
Set networking client reference.Definition
setClient()Return Values
| int | Page | index |
Set the mission environment reference.Definition
setEnvironment()Return Values
| int | Page | mapping index |
Set the player's current farm reference.Definition
setPlayerFarm()Return Values
| bool | True | if the event was not used, false if it was used. |
Set the player's master rights status.Definition
setHasMasterRights()Return Values
| bool | True | if the event was not used, false if it was used. |
Get the array of days before today counting back to the number of displayed past days.Definition
The method resolves days to display labels which can be used directly.
getPastDays()Return Values
| bool | True | if the event was not used, false if it was used. |
Late setup of finances table called on initialization.Definition
setupFinancesTable()Return Values
| bool | True | if the event was not used, false if it was used. |
Update the current balance display text.Definition
updateBalance()Return Values
| bool | True | if the event was not used, false if it was used. |
Update the loan display text.Definition
updateLoan()Return Values
| bool | True | if the event was not used, false if it was used. |
Update the expenses/incomes total for all displayed days.Definition
updateDayTotals()Return Values
| number | of | list items in data source |
Update statically positioned totals display.Definition
updateFinancesFooter()Return Values
| table | Array | of button info as {i={inputAction=<action name>, text=<optional display text>, callback=<optional callback>}} |
Update table display data.Definition
updateFinancesTable()Return Values
| DataCell | or | nil if not found. |
Update finances display.Definition
updateFinances()Return Values
| DataCell | or | nil if not found. |
Update loan button active states.Definition
updateFinancesLoanButtons()Return Values
| List | of | SortCell for the requested column name |
Update the current money unit.Definition
Also applies the unit change to the borrow/repay button labels.
updateMoneyUnit()Return Values
| index | of | selected row |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| dataRow | instance | or nil, selected data index or 0 |
Build a table data row for a given financial statistic.Definition
buildDataRow()Return Values
| dataRow | instance | or nil (element not part of table row) |
Handle borrow money action.Definition
onButtonBorrow()Return Values
| number | of | data view rows |
Handle repay money action.Definition
onButtonRepay()Return Values
| Actual | element to focus. |
Base class for frame elements for the in-game menu.
--@category GUI
Create a new InGameMenuFrameElement instance.Definition
new()
Late initialization of a menu frame.Definition
Override in sub-classes.
initialize()
Check if this menu frame requires menu button customization.Definition
getHasCustomMenuButtons()
Get custom menu button information.Definition
getMenuButtonInfo()Return Values
| table | Array | of button info as {i={inputAction=<action name>, text=<optional display text>, callback=<optional callback>}} |
Set custom menu button information.Definition
setMenuButtonInfo(table menuButtonInfo)Arguments
| table | menuButtonInfo | Array of button info as {i={inputAction=<action name>, text=<optional display text>, callback=<optional callback>}} or nil to reset. |
Set the menu button info dirty flag which causes the menu to update the buttons from this element's information.Definition
setMenuButtonInfoDirty()
Get the menu button info dirty state (has changed).Definition
isMenuButtonInfoDirty()
Clear menu button dirty flag.Definition
clearMenuButtonInfoDirty()
Get the frame's main content element's screen size.Definition
getMainElementSize()
Get the frame's main content element's screen position.Definition
getMainElementPosition()
Request to close the frame.Definition
Frames can contain logic (e.g. saving pending changes) which should be handled before closing. Use this method in
sub-classes request closing the frame so it can wrap up first. If a callback is provided and the initial request
could not close the frame, the callback will be called as soon as the frame can be closed.
requestClose()
Called when this frame is opened by its container.Definition
onFrameOpen()
Called when this frame is closed by its container.Definition
onFrameClose()
Current savegame settings page for the in-game menu.
--@category GUI
Create a new InGameMenuGameSettingsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| The | new | sorting order |
Initialize the game settings frame with concrete page references.Definition
This must be called after loading, since page frames are cloned and references cannot be injected in the constructor.
initialize()Return Values
| table | AchievementMessage | instance |
Set the current mission's info.Definition
Required to get current settings and to modify game name.
setMissionInfo()Return Values
| float | X | position in screen space |
| float | Y | position in screen space |
Set the currently known manure triggers in the game for helper manure refill settings.Definition
setManureTriggers()
Set master rights status of the current game instance / player.Definition
setHasMasterRights()Return Values
| table | ChatWindow | instance |
Update settings display with values from the current mission info / savegame.Definition
updateGameSettings()Return Values
| float | X | position in screen space |
| float | Y | position in screen space |
Update the pause buttons visuals for pausing/unpausing based on the game state.Definition
updatePauseButtonState()
Assign static option settings texts.Definition
assignStaticTexts()Return Values
| int | Display | row index of the newly added custom text or 0 if it could not be added |
Assign time scale setting texts.Definition
assignTimeScaleTexts()Return Values
| float | X | position in screen space |
| float | Y | position in screen space |
Assign dirt setting texts.Definition
assignDirtTexts()
Assign plant growth texts.Definition
assignPlantGrowthTexts()Return Values
| float | Game | info display width of visible elements in screen space |
Assign auto save texts.Definition
assignAutoSaveTexts()Return Values
| table | Weather | icon HUDElement instance |
Assign game state dependent setting texts.Definition
assignDynamicTexts()Return Values
| table | Temperature | icon HUDElement instance |
Update visibility of tool tip box, only show when there is text to display.Definition
updateToolTipBoxVisibility()Return Values
| table | Clock | hand HUDElement instance |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | Time | scale arrow icon HUDElement instance |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | GamePausedDisplay | instance |
Handle accepting savegame name input.Definition
onEnterPressedSavegameName()Return Values
| bool | If | true, the HUD is currently visible. |
Handle changing of the tool tip text.Definition
onToolTipBoxTextChanged()Return Values
| table | HUDDisplayElement | instance |
General game settings page for the in-game menu.
--@category GUI
Create a new InGameMenuGeneralSettingsFrame instance.Definition
new(table subclass_mt, table settingsModel)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| table | settingsModel | SettingsModel reference which handles settings display state across the UI |
| float | Screen | space X translation |
| float | Screen | space Y translation |
Update display values from settings.Definition
updateGeneralSettings()
Update visibility of tool tip box, only show when there is text to display.Definition
updateToolTipBoxVisibility()Return Values
| table | HUDElement | instance |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| float | Pivot | x position offset from element position in screen space |
| float | Pivot | y position offset from element position in screen space |
Get the frame's main content element's screen position.Definition
getMainElementPosition()
Handle clicks on a check box.Definition
onClickCheckbox()Return Values
| float | X | position in screen space |
| float | Y | position in screen space |
Handle clicks on a multi option element.Definition
onClickMultiOption()
Handle a button click on the native help button which is only visible and active on XBOX.Definition
onClickNativeHelp()Return Values
| width | scale | factor |
| height | scale | factor |
Handle changing of the tool tip text.Definition
onToolTipBoxTextChanged()
Exposed controls usable as fields in this frame, identified in configuration.
Create a new InGameMenuHelpFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| float | Red | value |
| float | Green | value |
| float | Blue | value |
| float | Alpha | value |
Set the current mission's base directory to let this frame load images from that location.Definition
setMissionBaseDirectory()
Set up UI elements for a given category index.Definition
createList()
Load help category texts.Definition
loadHelpLine()
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| float | Alpha | value |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | HUDPopupMessage | instance |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| float | Screen | space X translation |
| float | Screen | space Y translation |
Get the frame's main content element's screen position.Definition
getMainElementPosition()
Handle a change of row in the current category's help list.Definition
onHelpLineListSelectionChanged()Return Values
| table | HUDTextDisplay | instance |
Map overview frame of the in-game menu.
-- Displays the current map with markers for points of interest and terrain overlays.
--@category GUI
Create a new InGameMenuMapFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| bool | True | if the left border of the map has been reached |
| bool | True | if the right border of the map has been reached |
| bool | True | if the top border of the map has been reached |
| bool | True | if the bottom border of the map has been reached |
Create input help glyphs.Definition
createInputGlyphs()
Initialize map frame after GUI setup.Definition
initialize()
Called by InGameMenu when a map has finished loading.Definition
onLoadMapFinished()
Toggle map overview-specific input.Definition
Make sure this is called exactly once for activation and deactivation each when the map frame is opened or closed.
toggleMapInput()Return Values
| bool | True | if the left border of the map has been reached |
| bool | True | if the right border of the map has been reached |
| bool | True | if the top border of the map has been reached |
| bool | True | if the bottom border of the map has been reached |
Disable alternate bindings for menu navigation.Definition
This will disable some default bindings which interfere with camera controls (e.g. D-Pad on controller). Whenever any
input event is modified, this method must be called again afterwards.
disableAlternateBindings()
Get the context box position and orientation for a given hotspot.Definition
getContextBoxPositionAndOrientation()Return Values
| float | Screen | space X position |
| float | Screen | space Y position |
| string | Orientation | value, one of InGameMenuMapFrame.CONTEXT_BOX_ORIENTATION |
| float | Rotation | angle value |
Update the position of the hotspot context box if it's active.Definition
updateContextBoxPosition()
Set the IngameMap reference in this frame's required IngameMapElement for display.Definition
setInGameMap()Return Values
| float | X | position in screen space after the last glyph |
Set the terrain size for use in the ingameMapElement.Definition
setTerrainSize()Return Values
| float | X | position in screen space after the last glyph |
Set the fruit types used in the current mission.Definition
setMissionFruitTypes()Return Values
| float | Screen | space height used by the combo header (0 if invisible) |
Set the client connection object for event dispatching.Definition
setClient()Return Values
| table | Input | help elements |
| float | Screen | space height used by the returned help elements |
Set the current player's farm ID.Definition
setPlayerFarm()
Assign filter data after map loading.Definition
assignFilterData()Return Values
| int | Maximum | number of entries to show |
Assign display data for the crop type filters.Definition
assignCropTypeFilterData()Return Values
| boolean | isAllowed | isAllowed |
Assign display data for the ground state filters (growth and soil state).Definition
assignGroundStateFilterData()Return Values
| table | Combo | InputGlyphElement instance |
Assign colors for ground state (growth or soil) elements.Definition
assignGroundStateColors()Return Values
| table | Combo | header HUDElement |
Reset the UI dead zones for the map.Definition
resetUIDeadzones()Return Values
| float | Display | height in screen space |
Set a static UI dead zone for the map to make it ignore cursor/mouse input within that screen region.Definition
This dead zone is always in effect, regardless of map mode.
setStaticUIDeadzone()Return Values
| float | Modified | input help panel drawing vertical offset |
Called when the overview overlay is finished for display.Definition
onOverviewOverlayFinished()Return Values
| table | SideNotification | instance |
Called when the farmland overlay is finished for display.Definition
onFarmlandOverlayFinished()Return Values
| table | SpeakerDisplay | instance |
(Re-)Generate the map overlay for crop types, growth and soil states.Definition
generateOverviewOverlay()Return Values
| table | Overlay | instance |
(Re-)Generate the map overlay for farmlands.Definition
generateFarmlandOverlay()Return Values
| table | HUDElement | instance |
Set the display state of a filter icon based on the current filter settings.Definition
setFilterIconState()Return Values
| table | TopNotification | instance |
Toggle hotspot filter settings for the farmlands view mode.Definition
toggleFarmlandsHotspotFilterSettings()Return Values
| float | Screen | space X translation |
| float | Screen | space Y translation |
Show input buttons based on the current map selection context.Definition
showContextInput()
Update the context input bar's visibility.Definition
Turns it invisible if there are no buttons visile, otherwise turns it visible.
updateContextInputBarVisibility()Return Values
| bool | If | true, the HUD extension should be drawn in the current frame. |
Show marker button according to context.Definition
showContextMarker()Return Values
| float | Modified | input help panel drawing vertical offset |
Show the context box for a selected hotspot.Definition
showContextBox()Return Values
| table | HUD | extension instance or nil of no extension has been registered for the given specialization |
Hide the hotspot context box.Definition
hideContextBox()Return Values
| table | Array | of overlay descriptions: {overlay=overlay, x=0, y=0, rotation=0, invertX=false, invisibleBorderRight=vehicle.schemaOverlay.invisibleBorderRight, invisibleBorderLeft=vehicle.schemaOverlay.invisibleBorderLeft} |
| float | Screen | space height of root vehicle schema overlay |
Set the map selection to a hotspot.Definition
setMapSelectionItem()
Get display data for a selectable map hotspot.Definition
getHotspotData()Return Values
| float | Minimum | X position (left) |
| float | Maximum | X position (right) |
| string | Description | text |
| string | Display | image file path |
| table | Display | image UVs |
| table | Vehicle | instance if a vehicle has been selected, or nil otherwise |
Set the current color blind mode.Definition
Updates colors on buttons and overlays.
setColorBlindMode(bool isActive)Arguments
| bool | isActive | If true, color blind mode is active |
Initialize filter buttons with the current filter state.Definition
initializeFilterButtonState()Return Values
| table | Schema | Overlay instance |
Reset farmland selection state.Definition
resetFarmlandSelection()Return Values
| table | InGameMenuAnimalsFrame | instance |
Check if there are any placeables owned by the player on a given farmland.Definition
checkPlaceablesOnFarmland()Return Values
| float | Screen | space X position |
| float | Screen | space Y position |
| string | Orientation | value, one of InGameMenuMapFrame.CONTEXT_BOX_ORIENTATION |
| float | Rotation | angle value |
Handle changes to farm balance values.Definition
onMoneyChanged(int farmId, float newBalance)Arguments
| int | farmId | ID of farm whose current balance has changed |
| float | newBalance | New balance value of the given farm |
Called after the in-game map has been drawn.Definition
Draws the mode-dependent state overlay on top of the map
onDrawPostIngameMap()
Handle clicking the map overview selector which switches map overlay context (crop types, growth states, soil states).Definition
onClickMapOverviewSelector()
Handle filter button focus or highlight activation.Definition
onFilterButtonSelect()Return Values
| string | Description | text |
| string | Display | image file path |
| table | Display | image UVs |
| table | Vehicle | instance if a vehicle has been selected, or nil otherwise |
Handle filter button focus or highlight deactivation.Definition
onFilterButtonUnselect()
Set the display state of a filter button.Definition
setFilterButtonDisplayEnabled()
Toggle filter state on filter button click.Definition
toggleFilter()
Handle activation of a crop type filter.Definition
onClickCropFilter(table element, int fruitTypeIndex)Arguments
| table | element | Clicked button element |
| int | fruitTypeIndex | Fruit type index as valid in the current mission. |
| int | List | index corresponding to the farm ID or 1 if it could not be found or was the spectator farm ID |
Handle activation of a growth state filter.Definition
onClickGrowthFilter(table element, int growthStateIndex)Arguments
| table | element | Clicked button element |
| int | growthStateIndex | Growth state index |
| dataRow | instance | with prices data for the given selling point |
Handle activation of a soil state filter.Definition
onClickSoilFilter(table element, int growthStateIndex)Arguments
| table | element | Clicked button element |
| int | growthStateIndex | Soil state index |
| float | Sorting | value, see TableElement:setCustomSortFunction(...) |
Handle clicking / activating a map hotspot.Definition
onClickHotspot()Return Values
| float | Current | fill level >= 0 or a value < 0 if no storage exists for the requested fill type index |
| float | Total | capacity for the fill type >= 0 or a value < 0 if no storage exists for the requested fill type index |
Handle clicking within the map.Definition
onClickMap()
Called from FarmlandManager whenever a significant farmland change (mainly ownership) occurs.Definition
onFarmlandStateChanged()Return Values
| table | TableElement.DataRow | instance |
Handle ResetVehicleEvent local event.Definition
onVehicleReset()Return Values
| dataRow | instance | with vehicle data for the given vehicle |
Handle activation of map mode switch button.Definition
onClickSwitchMapMode()Return Values
| float | Sorting | value, see TableElement:setCustomSortFunction(...) |
Dialog confirmation callback for resetting a vehicle.Definition
onYesNoReset()Return Values
| True | if | the server matches the current filter settings, false otherwise |
Notify this frame when pausing the game.Definition
notifyPause()Return Values
| dataRow | instance | with server data |
Select the first visible hotspot on the mapDefinition
selectFirstHotspot()Return Values
| table | LandscapingScreen | instance |
Fit an input glyph into its corresponding placeholder GuiElement instance.Definition
updateInputGlyphTransform()Return Values
| table | new | LandscapingScreenController instance |
Update input glyphs when input context changes.Definition
updateInputGlyphs()Return Values
| int | Node | ID of the actual indicator shape in the loaded indicator asset |
| int | Node | ID of the attached light source of the loaded indicator asset |
Handle confirmation result of farmland buying dialog.Definition
onYesNoBuyFarmland()
Handle confirmation result of farmland selling dialog.Definition
onYesNoSellFarmland()Return Values
| table | categories | list of categories |
Register input actions.Definition
registerInput()Return Values
| table | PlacementScreen | instance |
Menu activate event, bound to vehicle enter, place visit or buying farmlands.Definition
onMenuActivate()Return Values
| True | if | the placement is valid, false otherwise |
| string | Reason | of being invalid |
Menu cancel event, bound to hotspot tagging, vehicle reset or selling farmlands.Definition
onMenuCancel()
Switch vehicle action event, bound to cycle visible hotspots.Definition
onSwitchVehicle()Return Values
| table | ShopConfigScreen | instance |
Exposed controls usable as fields in this frame, identified in configuration.
Create a new InGameMenuMultiplayerFarmsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| float | Fuel | capacity in liters |
Late initialization after frame cloning.Definition
initialize()Return Values
| float | Base | price |
| float | Upgrade | price |
| bool | True | if there are changes |
Set the reference to the current user.Definition
setCurrentUserId()
Set the current users reference.Definition
setUsers()
Set the reference to the current player.Definition
setPlayer()Return Values
| int | Number | of used config option elements |
Set the reference to the current player's farm.Definition
setPlayerFarm()Return Values
| table | New | collection of filtered owned items |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | Array | of brands in the form of {i={id = brand.index, iconFilename = brand.image, label = brand.title}} |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | Array | of vehicle categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
Build the farm list items from the known farms.Definition
buildFarmListItems()Return Values
| table | Array | of tool categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
Update farm list.Definition
updateFarmList()Return Values
| table | Array | of object categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
Get the list index for a given farm ID.Definition
getListFarmIndex(int farmId)Arguments
| int | farmId | Farm ID |
| table | Array | of placeable categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
| int | List | index corresponding to the farm ID or 1 if it could not be found or was the spectator farm ID |
Update a farm's balance display.Definition
updateFarmBalance()Return Values
| int | Next | usable attribute slot index after these fill types |
Update displayed player names on a farm.Definition
updateFarmPlayers()Return Values
| int | Number | of attributes used for text data |
Update context menu buttons.Definition
updateMenuButtons()Return Values
| table | instance | Instance of object |
Let the current player join a given farm.Definition
The player will leave their current farm (can be spectator farm) and join the new farm if possible. This sends
a PlayerSetFarmEvent which will check any farm password on the server side. In response, a PlayerSetFarmAnswerEvent
is sent back. The method will try using a previously recorded farm password if one has been received during a join
event.
joinFarm()Return Values
| boolean | success | success |
Let the current player leave their farm.Definition
The player will join the spectator farm and be able to choose a new farm to join.
leaveFarm()Return Values
Delete a given farmDefinition
The current player must have admin privileges on the server and the farm must be empty.
deleteFarm()Return Values
| float | ||
| float | ||
| float | ||
| float | ||
| float | ||
| float |
Show a dialog to edit farm properties.Definition
The current player must have farm manager or administrator privileges on the server.
editFarm()
Show a dialog to create a new farmDefinition
The current player must have administrator privileges on the server.
createFarm()
Handle an answer to PlayerSetFarmEvent.Definition
onPlayerSetFarmAnswer(int answerState, int farmId, string password)Arguments
| int | answerState | PlayerSetFarmAnswerEvent.STATE member |
| int | farmId | If the state is OK, will contain the newly joined farm |
| string | password | If the state is OK, will contain the newly joined farm's password |
Handle confirmation of farm password input when joining a farm.Definition
onFarmPasswordEntered()
Handle changes to user permissions.Definition
If the current user's permissions change, update the menu buttons in case they were elevated to farm manager status.
onPermissionChanged()
Handle creation of a new farm.Definition
onFarmCreated()Return Values
| table | self | instance of class event |
Handle state update of any farm.Definition
onFarmsChanged()Return Values
| table | instance | instance of event |
Handle a player changing their farm.Definition
onPlayerFarmChanged()Return Values
| table | self | instance of class event |
Handle a balance change of a farm.Definition
onFarmMoneyChanged()Return Values
| table | instance | instance of event |
Handle clicking on the left navigation button.Definition
onClickLeft()Return Values
| table | self | instance of class event |
Handle clicking on the right navigation button.Definition
onClickRight()Return Values
| table | instance | instance of event |
Handle a click / activation of a farm item.Definition
onClickFarm()Return Values
| table | instance | Instance of object |
Handle a double click of a farm item.Definition
onDoubleClickFarm()Return Values
| boolean | success | success |
Handle a farm selection change.Definition
onSelectionChanged()Return Values
| float | dailyUpkeep | daily up keep |
Handle farm deletion dialog confirmation.Definition
onDeleteFarmYesNo()Return Values
| float | sellPrice | sell price |
Multiplayer user management frame for the in-game menu.
-- Displays a user list and allows modification of permissions as well as money transfers between farms. Administrators
can log in using a password and kick/ban/unban users when logged in.
--@category GUI
Create a new InGameMenuMultiplayerUsersFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| boolean | isActiveForInput | is active for input |
Late initialization.Definition
initialize()Return Values
| table | self | instance of class event |
Called when this frame is opened by its container.Definition
onFrameOpen()Return Values
| table | instance | instance of event |
Called when this frame is closed by its container.Definition
onFrameClose()Return Values
| table | instance | instance of object |
Set up the user list to store an instance flag when navigating users or not for menu button context.Definition
setupUserListFocusContext()Return Values
| integer | id | i3d rootnode |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| boolean | isValid | true if index is valid else false |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| integer | id | id of object |
| integer | id | id of used root node |
Set the reference to the current player's farm.Definition
setPlayerFarm()
Set the current user ID.Definition
setCurrentUserId()Return Values
| New | Binding | instance |
Set the ban storage reference.Definition
setBanStorage()Return Values
| instance | initialized with values from XML and the given parameters |
Set the current users reference.Definition
setUsers()Return Values
| int | Combo | bit mask |
Get a new sorted array of known users for displaying.Definition
Users are grouped by farms, starting with the current player's farm (unless they're a spectator). Within groups,
users are sorted alphabetically by name.
getSortedUsers()Return Values
| True | if | there is a collision. |
Build a display name for a given user and their special privilege flags.Definition
buildUserDisplayInfo()Return Values
| Cloned | Binding | instance |
Update connected player information.Definition
rebuildUserList(table missionUsers)Arguments
| table | missionUsers | Array of users managed by the current mission instance. |
Update menu buttons based on the current user's admin status.Definition
updateMenuButtons()Return Values
| instance | initialized with values from XML |
Update the current balance display.Definition
updateBalance()Return Values
| Cloned | InputAction | instance |
Set the current money balance display.Definition
setCurrentBalance(float balance, string balanceString)Arguments
| float | balance | Current balance of the current player |
| string | balanceString | Properly formatted money string |
| table | InputBinding | instance |
Update all display states and text.Definition
updateDisplay()Return Values
| table | Set | of required device categories, {<category> = true} |
Handle a ban button activation.Definition
onButtonBan()Return Values
| bool | True | if there are any configured bindings for the given device, false otherwise |
Handle ban confirmation dialog result.Definition
onYesNoBan()Return Values
| GS_INPUT_HELP_MODE_KEYBOARD | or | GS_INPUT_HELP_MODE_GAMEPAD |
Handle a kick button activation.Definition
onButtonKick()Return Values
| GS_INPUT_HELP_MODE_KEYBOARD | or | GS_INPUT_HELP_MODE_GAMEPAD |
Handle kick confirmation dialog result.Definition
onYesNoKick()Return Values
| True | if | the parameters are valid, false otherwise. |
Handle an unban button activationDefinition
onButtonUnBan()Return Values
| bool | True | if the event could be registered, false otherwise |
| string | event | ID if successful, empty string otherwise |
| table | Action | reference of a colliding action if there would be a collision, nil otherwise |
Handle a show user profile button activation.Definition
onButtonShowProfile()
Handle an invite friends button activationDefinition
onButtonInviteFriends()
Handle an admin login button activation.Definition
onButtonAdminLogin()Return Values
| bool | True | if there would be a collision, false otherwise |
| table | Colliding | action if there would be a collision, nil otherwise |
Handle selection changes in the user list.Definition
onUserSelected()
Handle clicking a permission check box.Definition
onClickPermission(table checkboxElement, bool isActive)Arguments
| table | checkboxElement | ToggleButtonElement which received the click |
| bool | isActive | New checked state |
| array | of | tuples: {i={action=InputAction, event=InputEvent}} |
Handle clicking the transfer buttonDefinition
onClickTransferButton()Return Values
| Gamepad | combo | mask, Mouse combo mask |
Transfer money from the current player's farm to the selected farm.Definition
transferMoney()Return Values
| instance | or nil if ID is invalid |
Handle clicking the remove player from farm button.Definition
onClickRemoveFromFarm()Return Values
| instance | or nil if ID is invalid |
Handle remove player from farm confirmation dialog result.Definition
onYesNoRemoveFromFarm()Return Values
| instance | or nil if ID is invalid |
Handle clicking the promote player to farm manager button.Definition
onClickPromote()Return Values
| float | Mouse | cursor X position in screen space |
| float | Mouse | cursor Y position in screen space |
Handle promote user confirmation dialog result.Definition
onYesNoPromoteToFarmManager()
Handle clicking the contractor toggle button.Definition
onClickContractor()Return Values
| previousDevices | Table | of internal ID -> InputDevice before the current initialization |
Handle contractor state toggle confirmation dialog result.Definition
onYesNoToggleContractorState()Return Values
| List | of | device information tables, format: {deviceId=[device ID], name=[device name]} |
Handle a local money change event for any farm.Definition
onFarmMoneyChanged()Return Values
| Modifier | bit | mask |
Handle state update of any farm.Definition
onFarmsChanged()Return Values
| True | if | the binding's device ID could be resolved, false otherwise |
Handle a player changing their farm.Definition
onPlayerFarmChanged()Return Values
| function | results | table {checkFunctionRef=[functionResult]} |
Handle a local player permission change event.Definition
onPermissionChanged()Return Values
Handle a local contracting state change event.Definition
onContractingStateChanged()Return Values
| bool | True | if the new binding could be added |
| table | Reference | to an Action containing a binding which collided with the added binding or nil |
Handle admin login password dialog response.Definition
onAdminPassword()
Handle successfully logging in as an admin.Definition
onAdminLoginSuccess()Return Values
| True | if | a binding could be found and updated, false if there was a collision |
| and | Action reference of a collision as {collisionBinding=Binding, collisionAction=Action} or nil |
Prices overview frame of the in-game menu.
-- Displays current prices of sellable goods and produce.
--@category GUI
Create a new InGameMenuPricesFrame instance.Definition
new(table subclass_mt, table l10n, table fillTypeManager)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| table | l10n | I18N reference |
| table | fillTypeManager | FillTypeManager reference |
Create a focus override function for the prices table which scrolls horizontally if possible instead of exiting theDefinition
table.
makeTableFocusOverrideFunction()Return Values
| True | if | all given axes are currently active, input value of the non-modified axis (last in list) |
Get the display name of a selling station.Definition
getStationName()Return Values
| True | if | all axes are active (digital interpretation), input value of the non-modified axis (last in list) |
Set the current list of selling stations for displaying.Definition
setSellingStations()Return Values
| Input | value | of the axis in the range [-1, 1] for full axes or [0, 1] for half axes. |
Update item counts and handle size of the vertical slider.Definition
updateVerticalSlider()Return Values
Initial setup of the prices table and required data.Definition
setupPriceTable()Return Values
| Ordered | action | bindings in the form of {i={action=InputAction, bindings={Binding}}} |
Update prices table display data.Definition
updatePriceTable()Return Values
Update table headers with fill type icons according to current horizontal slider position.Definition
updateHeaderIcons()Return Values
| table | Array | of events |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | First | active event or InputEvent.NO_EVENT |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| x | motion | scale, Y motion scale |
Handle horizontal slider changes.Definition
onChangedPriceSlider()Return Values
| True | if | the user settings have passed the integrity check, false otherwise. |
Handle price table header clicks.Definition
Causes the table to be sorted by the clicked header column's numeric price values.
onClickPriceHeader()Return Values
| True | if | the axis name represents a physical full axis, false otherwise |
Handle selling point table header clicks.Definition
Causes the table to be sorted by selling point names.
onClickSellingPointHeader()Return Values
| True | if | the input name represents an analog input |
Handle table row clicks.Definition
onClickPrices()Return Values
| True | if | the axis is at zero position, false otherwise |
Handle table row double-clicks.Definition
Sets a map marker on the selected selling point or clears it again.
onDoubleClickPrices()Return Values
| New | InputDevice | instance |
Bind an element to the selling point data.Definition
onDataBindSellingPoint()Return Values
| Device | ID | or empty string |
Bind an element to the indexed price data.Definition
onDataBindPrice()Return Values
| Device | name | or empty string |
Bind an element to the silo capacity label data.Definition
onDataBindSiloCapacityLabel()Return Values
| Prefix | number, | or -1 if none exists; raw engine-issued device ID |
Binding an element to the indexed silo capacity value data.Definition
onDataBindSiloCapacityValue()Return Values
| instance | containing the symbol overlays in its "buttons" field |
Set data for a selling point table cell.Definition
setSellingPointData(table dataCell, table sellingStation)Arguments
| table | dataCell | TableElement.DataCell instance for a selling point cell |
| table | sellingStation | SellingStation instance of the current data row |
| array | of | help elements (see InputDisplayManager:makeHelpElement() for structure) |
Set data for a price table cell.Definition
setPriceData(table dataCell, int priceIndex, table sellingStation)Arguments
| table | dataCell | TableElement.DataCell instance for a price cell |
| int | priceIndex | Price column index |
| table | sellingStation | SellingStation instance of the current data row |
| Hash | table | of currently active combo button action names, {action name=true} |
Build a DataRow from selling point properties to add to the prices tableDefinition
buildDataRow(table sellingStation)Arguments
| table | sellingStation | SellingStation reference |
| Axis | name | or nil if not found, internal ID of binding device |
| dataRow | instance | with prices data for the given selling point |
Table sorting function for prices.Definition
sortPrices(table sortCell1, table sortCell1)Arguments
| table | sortCell1 | TableElement.SortCell instance representing the first cell to compare |
| table | sortCell1 | TableElement.SortCell instance representing the second cell to compare |
| instance | or nil if no overlay is available for the action's bindings | |
| float | Sorting | value, see TableElement:setCustomSortFunction(...) |
Get the storage fill level for a given fill type index.Definition
getStorageFillLevel(int index, bool farmSilo)Arguments
| int | index | Fill type index of the requested storage fill level |
| bool | farmSilo | If true, only counts storage of owned farm silos |
| key | text | (e.g. "F" for "KEY_f") for the keyboard action binding or nil if no resolution is possible |
| float | Current | fill level >= 0 or a value < 0 if no storage exists for the requested fill type index |
| float | Total | capacity for the fill type >= 0 or a value < 0 if no storage exists for the requested fill type index |
Game statistics display frame for the in-game menu.
--@category GUI
Create a new InGameMenuStatisticsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| controller | symbols | configuration key name |
Update statistics data in tables.Definition
updateStatistics()Return Values
| controller | symbols | configuration key name |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| New | InputEvent | instance |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | instance | instance of object |
Build a table data row for a given statistics attribute.Definition
buildDataRow(table statAttribute)Arguments
| table | statAttribute | Statistics attribute as defined in FarmStats |
| boolean | true | if loading was successful else false |
| table | TableElement.DataRow | instance |
Exposed controls usable as fields in this frame, identified in configuration.
Create a new InGameMenuFinancesFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| string | cutterEffectType | the real cutterEffect name, nil if not defined |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | cutterEffects | cutter effects |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | instance | instance of object |
Exposed controls usable as fields in this frame, identified in configuration.
Create a new InGameMenuVehiclesFrame instance.Definition
new(table subclass_mt, table messageCenter, table l10n, table storeManager, table brandManager)Arguments
| table | subclass_mt | Sub-class meta table |
| table | messageCenter | MessageCenter reference for notifications |
| table | l10n | I180N reference for display text resolution |
| table | storeManager | StoreManager reference for vehicle store data look-ups |
| table | brandManager | BrandManager reference for vehicle brand data look-ups |
| boolean | true | if loading was successful else false |
Late initialization.Definition
initialize()Return Values
| string | materialType | the real material name, nil if not defined |
Update the garage view.Definition
updateGarage()Return Values
| integer | materialId | id of material |
Update item counts and handle size of the vertical slider.Definition
updateVerticalSlider()Return Values
| table | instance | instance of object |
Set a list of vehicles currently accessible by the player.Definition
setAccessibleVehicles()Return Values
| boolean | true | if loading was successful else false |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| string | particleType | the real particle name, nil if not defined |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | particleSystem | particleSystem |
Create a focus override function for a table header which modifies the scroll bar instead of navigation whenDefinition
vertical navigation input is received.
makeTableHeaderFocusOverrideFunction()Return Values
| table | instance | instance of object |
Set data for a vehicle name table cell.Definition
setNameData()Return Values
| boolean | true | if loading was successful else false |
Set data for a vehicle age table cell.Definition
setAgeData()Return Values
| boolean | true | if loading was successful else false |
Set data for a vehicle operating hours table cell.Definition
setOperatingHoursData()Return Values
| table | instance | instance of object |
Set data for a vehicle damage table cell.Definition
setDamageData()Return Values
| table | instance | instance of object |
Set data for a vehicle leasing costs table cell.Definition
setLeasingData()Return Values
| table | instance | instance of object |
Set data for a vehicle selling value table cell.Definition
setValueData()Return Values
| boolean | true | if loading was successful else false |
Build a DataRow from vehicle properties to add to the table.Definition
buildDataRow(table vehicle)Arguments
| table | vehicle | Vehicle reference |
| table | baleType | baleType object |
| dataRow | instance | with vehicle data for the given vehicle |
Table sorting function for numeric values.Definition
sortAttributes(table sortCell1, table sortCell1)Arguments
| table | sortCell1 | TableElement.SortCell instance representing the first cell to compare |
| table | sortCell1 | TableElement.SortCell instance representing the second cell to compare |
| table | baleType | baleType object |
| float | Sorting | value, see TableElement:setCustomSortFunction(...) |
Handle clicks on the vehicle name header.Definition
Switches table to default string sorting.
onClickVehicleHeader()Return Values
| table | bale | bale |
Handle clicks on an attribute header.Definition
Switches table to numerical sorting.
onClickAttributeHeader()Return Values
| string | baleKey | bale key |
Logical input action.
-- Game components react to actions to alter their state. Available actions are loaded from "dataS/inputActions.xml".
Default action names are available for use as constants in the form of InputAction.ACTION_NAME (see end of file).
Additional actions (e.g. from mods) will be dynamically added as such constants at run time.
--@category Input
--@xmlConfig action#name Action name. This serves as a unique identifier in both configuration and code.
Create a new InputAction instanceDefinition
new(name Action, axisType Type, isLocked If, ignoreComboMask If, displayNamePositive [optional], displayNameNegative [optional])Arguments
| name | Action | name, unique |
| axisType | Type | of action axis (HALF or FULL) |
| isLocked | If | true, bindings for this action cannot be changed in the game |
| ignoreComboMask | If | true, non-combo bindings will trigger this action even if a combo button is currently active |
| displayNamePositive | [optional] | Display name of the action in positive direction for the current localization setting. This is the default display name. |
| displayNameNegative | [optional] | Display name of the action in negative direction for the current localization setting. Only used for full axes. |
| InputAction |
Create a new InputAction instance from an XML element.Definition
createFromXML(xmlFile XML, elementTag Tag)Arguments
| xmlFile | XML | file handle |
| elementTag | Tag | of the element to parse as an InputAction |
| integer | numPixels | number of pixels |
| InputAction | instance | initialized with values from XML |
Add a Binding to this action.Definition
addBinding()Return Values
| integer | changedArea | changed area pixels |
| integer | totalArea | total area pixels |
Remove a binding from this action.Definition
removeBinding()
Get the bindings for this action.Definition
getBindings()Return Values
| integer | changedArea | changed area pixels |
| integer | totalArea | total area pixels |
Clear this action's bindings.Definition
clearBindings()
Let this action know which binding is its primary keyboard binding.Definition
Stores the input axes as a concatenated string for comparisons.
setPrimaryKeyboardBinding(Binding Binding)Arguments
| Binding | Binding | instance which is the primary keyboard binding for this action |
| integer | numChangedPixels | number of changed pixels |
Determine if this action represents a logical full axis.Definition
isFullAxis()Return Values
| table | color | tire track color |
Determine if bindings of this action should trigger any associated events if the bindings are not combos but a comboDefinition
button is currently pressed.
getIgnoreComboMask()Return Values
| string | encodedString | the encoded string |
Create a new InputAction instance with the same state as this instance.Definition
clone()Return Values
| string | decodedString | the decoded string |
| Cloned | InputAction | instance |
Get a string representation for this InputAction.Definition
toString()Return Values
| table | copy | the copied table |
Input binding manager.
-- Loads and saves action input bindings and provides action trigger information.
-- Methods of note (see the documentation of the actual methods for more detailed descriptions):
registerActionEvent() Register an event callback for an action (for action values, see InputAction.lua)
removeActionEvent() Removes a previously registered event. Also see removeActionEventsByTarget().
setActionEvent...() Group of methods to adjust event states, e.g. activity or input display hint parameters.
-- Input event usage scenarios:
1. Long lifetime components / much interaction: Register event at creation of a component, modify event in component
update method depending on its state, remove event at component destruction
2. Short lifetime components / little interaction: Register event when necessary (e.g. when in range of an object),
remove again when no interaction is possible anymore
-- Player input settings are loaded from the file "inputBindings.xml" in their profile directory. If the file is not
present or corrupted whenever input is loaded, it will be restored from a suitable template. If a binding of a locked
action is changed directly on the file system, it will be overwritten with its template counterpart on the next
loading call to ensure that critical inputs are always available (e.g. menu navigation).
--@category Input
Create the InputBinding instance.Definition
new(table logManager, table modManager, table messageCenter, bool isConsoleVersion)Arguments
| table | logManager | LogManager reference |
| table | modManager | ModManager reference |
| table | messageCenter | MessageCenter reference |
| bool | isConsoleVersion | If true, the game is running on console |
| table | copy | the copied table |
| table | InputBinding | instance |
Load the input binding configuration.Definition
load(bool isInitializing, bool forceDefaultBindings)Arguments
| bool | isInitializing | [optional, default=false] If true, loads the input bindings in initialization mode and saves at the end of loading to store any resolved device IDs to the user settings. |
| bool | forceDefaultBindings | [optional, default=false] If true, will forcibly load default bindings and override any user input settings. |
| boolean | isElementOfList | true if element is part of the list, else false |
Load default input bindings.Definition
Will apply defaults for newly connected devices or override all bindings with template defaults if requested.
loadDefaultBindings(bool forceReplace)Arguments
| bool | forceReplace | [optional, default=false] If true, will overwrite all user bindings with the default templates. |
| integer | index | index of first occurrence |
Load actions defined by mods.Definition
loadModActions()Return Values
| boolean | areEqual | true if lists are equal, else false |
Load default mod input binding data.Definition
loadModBindingDefaults()Return Values
| any | value | value of the random element |
Assign bindings configuration file paths depending on platform and devices.Definition
assignPlatformBindingPaths()Return Values
| table | set | the converted set |
Overwrites user input settings with the default template.Definition
overwriteSettingsWithDefault(forceOverwrite If)Arguments
| forceOverwrite | If | true, will overwrite an existing settings file. If false, will only perform the operation if no user settings are present. |
| table | list | the converted list |
Restore input contexts after reloading the input settings, e.g. when devices change.Definition
restoreInputContexts()Return Values
| table | hash | the converted hash |
Set the visibility state of the mouse cursor.Definition
setShowMouseCursor(bool doShow, bool saveCursorPosition)Arguments
| bool | doShow | If true, the cursor will be shown. Otherwise, it will be hidden. |
| bool | saveCursorPosition | [optional] If true, saves the current cursor position to restore when showing it again |
| boolean | areEqual | true if sets are equal, else false |
Determine if the mouse cursor is currently being shown.Definition
getShowMouseCursor()Return Values
| boolean | isSubset | true if set1 is a subset of set2 |
Get the current input help mode for the input hint display.Definition
If the input help mode has been set to automatic, the last button or key input will determine which device scheme
is used.
getInputHelpMode()Return Values
| boolean | isSubset | true if set1 is a real subset of set2 |
| GS_INPUT_HELP_MODE_KEYBOARD | or | GS_INPUT_HELP_MODE_GAMEPAD |
Get the current input mode, independent of input help mode settings.Definition
getLastInputMode()Return Values
| boolean | hasIntersection | true if set1 and set2 have an intersection |
| GS_INPUT_HELP_MODE_KEYBOARD | or | GS_INPUT_HELP_MODE_GAMEPAD |
Validate parameters for registerActionEvent().Definition
Prints warnings if there are problems and returns the validation status.
validateActionEventParameters()Return Values
| table | intersection | the intersection of both sets |
| True | if | the parameters are valid, false otherwise. |
Register an event callback for an input action.Definition
Use InputAction constants for action names. At least one of the trigger parameters must be set to true to receive
events. This methods also checks if there would be any input binding collision when an event is added for the given
action. If there are any collisions, the registration will fail.
-- Note the interaction of the "down" and "always" triggers: If "down" is set and not "always", only an input down-flank
will raise an event. If both "down" and "always" are set, an event is raised as long as the input is pressed beyond a
threshold. If only "always" and not "down" is set, an event will be raised on each frame with the current input value
even if no input is active.
-- @param string actionName Name of action, see InputAction
registerActionEvent(table targetObject, function eventCallback, bool triggerUp, bool triggerDown, bool triggerAlways, bool startActive, table callbackState)Arguments
| table | targetObject | Event target, first argument to event callback |
| function | eventCallback | Event callback, called when the action has input. Signature: callback(targetObject, actionName, inputValue, callbackState) |
| bool | triggerUp | If true, the event fires once when an input signal of the given action goes to "up" state, e.g. a released key. |
| bool | triggerDown | If true, the event fires once when an input signal of the given action goes to "down" state, e.g. a pressed key. |
| bool | triggerAlways | If true, the event fires on any signal input change, potentially every frame. This is mostly useful for required continuous axis input, e.g. steering actions. |
| bool | startActive | [optional] If true, the event is active right after registration and will be checked for input collisions. Otherwise, set its activation state using InputBinding:setActionEventActive() and handle collisions yourself. |
| table | callbackState | [optional] An arbitrary value or reference which serves as a closure state within callbacks, useful e.g. when checking input state between events without requiring additional class fields. |
| table | substraction | the substraction of both set |
| bool | True | if the event could be registered, false otherwise |
| string | event | ID if successful, empty string otherwise |
| table | Action | reference of a colliding action if there would be a collision, nil otherwise |
Check if adding an event for a given action name would cause colliding input bindings to be active.Definition
checkEventCollision(string actionName)Arguments
| string | actionName | Name of an InputAction |
| table | union | the union of both sets |
| bool | True | if there would be a collision, false otherwise |
| table | Colliding | action if there would be a collision, nil otherwise |
Start registering action events in a given context.Definition
This method must be accompanied by a finalizing call to InputBinding:endActionEventsModification() when registration is
complete. While a registration context is set, all calls to InputBinding:registerActionEvent() will add events to
that context. Also, derived event collections (i.e. data structures for display and fast iteration) will only be
updated when InputBinding:endActionEventsModification() is called instead of each time an event is added.
If no context with the given name exists, a new empty one will be created and added to the known contexts.
beginActionEventsModification(string inContext, bool createNew)Arguments
| string | inContext | Name of the input context which receives any registered events until endActionEventsModification is called. |
| bool | createNew | [optional, default=false] If true, will create a new context of the given name, potentially overwriting any existing one. |
| table | filtered | list (non-deep copy) |
End registering action events in a context.Definition
Resets the registration context set in InputBinding:beginActionEventsModification() for any later registrations.
endActionEventsModification()Return Values
| table | filtered | list (non-deep copy) |
Refresh derived event collections for retrieval purposes.Definition
refreshEventCollections()Return Values
| integer | sign | the sign of the value |
Store an array of action-event tuples for all action events which need an input hint display.Definition
storeDisplayActionEvents()Return Values
| boolean | true | if value is nan else false |
Store all bindings associated with registered and active events in collections for iteration.Definition
storeEventBindings()Return Values
| number | rounted | value |
Iterates the action event collection, calling a given function on each event.Definition
If the processing function returns a "truthy" value, the iteration is stopped.
iterateEvents(processingFunction function(event,)Arguments
| processingFunction | function(event, | actionName, actionEventList, actionEventListIndex) |
| float | value | radian angle |
Internal function for event removal.Definition
removeEventInternal()Return Values
| float | value | interpolated value |
Remove a previously registered action event by ID.Definition
removeActionEvent(eventId Event)Arguments
| eventId | Event | ID as returned by registerActionEvent(). |
| float | value | interpolated value |
Remove all previously registered action events which are triggered by an action identified by name.Definition
removeActionEventsByActionName(actionName If)Arguments
| actionName | If | an event is triggered by the action of that name, it is removed. |
| float | alpha | alpha |
Remove all previously registered action events which have a given target.Definition
removeActionEventsByTarget(targetObject If)Arguments
| targetObject | If | an event has this object as a target, it is removed. |
| float | value | value |
Get action-event tuples for registered events which require an input hint.Definition
Note that the data is live and should be treated as read-only.
getDisplayActionEvents()Return Values
| float | isOutOfBounds | is out of bounds |
| array | of | tuples: {i={action=InputAction, event=InputEvent}} |
Set a specific action text to display as an input hint for a given event.Definition
Use this to adjust display texts depending on context, e.g. "Attach" / "Detach" for the vehicle attach action.
Additionally, it serves as a way to display neutral full axis action names, e.g. "Move player" instead of any
specific label depending on the input direction.
setActionEventText(eventId ID, actionText Localized)Arguments
| eventId | ID | of a registered event |
| actionText | Localized | display text for the event's action |
| float | value | the floored percent value |
Set the name of an icon to display as an input hint instead of text for a given event.Definition
If an icon has been specified for an event with this function, any text setting will have no effect.
setActionEventIcon(eventId ID, iconName Input)Arguments
| eventId | ID | of a registered event |
| iconName | Input | hint icon name as defined in axisIcons.xml or InputHelpElement.AXIS_ICON |
| float | value | the floored clamped value |
Set the visibility of an action event input hint display.Definition
The most basic controls, such as player movement, do not need any input hints and can be hidden this way. If more
than one event is registered on an action, make sure to only set one of them to visible. The system will
automatically set all events after the first per action to invisible to ensure a valid ground state.
setActionEventTextVisibility(eventId ID, isVisible If)Arguments
| eventId | ID | of a registered event |
| isVisible | If | false, hides the input hint for this event. Default is visible. |
| float | angle | the resized angle in the range -pi to pi |
Set the priority of an action event input hint display.Definition
Use any of the following script constants: GS_PRIO_VERY_HIGH, GS_PRIO_HIGH, GS_PRIO_NORMAL, GS_PRIO_LOW,
GS_PRIO_VERY_LOW. Higher priority will be shown first in the input hint display box.
setActionEventTextPriority(eventId ID, priority Priority)Arguments
| eventId | ID | of a registered event |
| priority | Priority | number value, lower is more important. Use GS_PRIO_[...] constants. |
| float | angle | the radian difference in range -pi to pi |
Set the active state of an action event.Definition
Only active events can be triggered by input and be displayed as input hints.
setActionEventActive(eventId ID, isActive New)Arguments
| eventId | ID | of a registered event |
| isActive | New | active state |
| float | length | length |
Set the active state of all action events targeting a given object.Definition
Only active events can be triggered by input and be displayed as input hints.
setActionEventsActiveByTarget(targetObject Action, isActive New)Arguments
| targetObject | Action | event target object |
| isActive | New | active state |
| float | length | square length |
Get the combo input masks for any currently active gamepad and mouse combo inputs.Definition
The returned masks are bit-wise combined masks made from the InputBinding.COMBO_MASK_[...] constants.
getComboCommandPressedMask()Return Values
| float | x | normalized x |
| float | y | normalized y |
| Gamepad | combo | mask, Mouse combo mask |
Get the combo action name for a given set of modifier axes.Definition
getComboActionNameForAxisSet()
Resolve a device ID sting to the engine-internal integer ID.Definition
getInternalIdByDeviceId()Return Values
| float | length | length |
Retrieve an InputDevice by its internal device ID.Definition
getDeviceByInternalId(internalDeviceId Internally)Arguments
| internalDeviceId | Internally | assigned device ID (= device index assigned by engine, zero based) |
| float | length | square length |
| InputDevice | instance | or nil if ID is invalid |
Assign an input help mode as the last used mode.Definition
This triggers a message center notification to all subscribed components if the input help mode has changed.
assignLastInputHelpMode()Return Values
| float | x | normalized x |
| float | y | normalized y |
| float | z | normalized z |
Prepare for incoming binding changes.Definition
Must be called before InputBinding:startInputCapture() to guarantee a valid state.
startBindingChanges()
Commit all binding changes since the last call to InputBinding:startBindingChanges().Definition
Accepts the current, modified binding state and notifies any change listeners.
commitBindingChanges()
Rolls back all binding changes since the last calls to InputBinding:startBindingChanges().Definition
rollbackBindingChanges()Return Values
| float | x | scaled x |
| float | y | scaled y |
| float | z | scaled z |
Capture all input and run a callback if input has been received on a device.Definition
Overrides the global input event functions.
startInputCapture(isKeyboard True, isMouse True, callbackTarget If, callbackState Free, inputCallback Called, abortCallback Called, deleteCallback Called)Arguments
| isKeyboard | True | if target device is keyboard input |
| isMouse | True | if target device is mouse input |
| callbackTarget | If | specified, will be supplied as the first argument to the callback function (usually caller self) |
| callbackState | Free | parameter which is returned with callbacks to the callback target |
| inputCallback | Called | when the target device receives input. function(deviceId, inputAxisName, isModifier, inputValue, callbackState) |
| abortCallback | Called | when an abort input is received on any device. function() |
| deleteCallback | Called | when a delete input is received on any device. function(callbackState) |
Capture keyboard input.Definition
Fires an input callback as soon as a key is pressed. Modifier keys (e.g. shift, alt) only trigger callbacks when
they are pressed alongside a regular key. Modifier callbacks always arrive before non-modifier key callbacks.
captureKeyboardInput(abortCallback function(), deleteCallback function(), inputCallback function(deviceId,)Arguments
| abortCallback | function() | to be called when the abort key is pressed |
| deleteCallback | function() | to be called when the delete key is pressed |
| inputCallback | function(deviceId, | axisName, isModifier, inputValue), see startInputCapture() |
Capture mouse input.Definition
Axis input continuously fires callbacks while the player is moving the mouse. Button callbacks are triggered once
when a mouse button is held down and again when it is released.
captureMouseInput(callback function(deviceId,)Arguments
| callback | function(deviceId, | axisName, isModifier, inputValue), see startInputCapture() |
| float | x | clamped x |
| float | y | clamped y |
| float | z | clamped z |
Capture gamepad / controller input.Definition
Continously fires callbacks for all known gamepad buttons and axes for each frame.
captureGamepadInput(callback function(deviceId,)Arguments
| callback | function(deviceId, | axisName, isModifier, inputValue), see startInputCapture() |
Stop the current input gathering run.Definition
Restores global input event functions.
stopInputGathering()
Restore default bindingsDefinition
restoreDefaultBindings()Return Values
| float | x | interpolated x |
| float | y | interpolated y |
| float | z | interpolated z |
Clear relevant state for loading and resetting.Definition
clearState()
Load input actions which are later bound to input axes.Definition
loadActions(xmlFile Action, modName [optional])Arguments
| xmlFile | Action | definition XML file handle |
| modName | [optional] | Name of the mod which defines actions. If not set, actions are assumed to be default game actions. |
Reset stored device information.Definition
Clears all device information and returns the previous device list for comparisons. Call this for
initialization and before enumerating system devices.
resetDeviceInformation()Return Values
| float | x | interpolated x |
| float | y | interpolated y |
| float | z | interpolated z |
| previousDevices | Table | of internal ID -> InputDevice before the current initialization |
(Re-)Creates the internal keyboard and mouse virtual device.Definition
createDefaultDevices()
Find gamepad devices in the system and store their information.Definition
enumerateGamepadDevices(previousDevices Table)Arguments
| previousDevices | Table | of internal ID -> InputDevice before the current initialization |
Get a list of recognized gamepad (and other controller) input devices.Definition
getGamepadDevices()Return Values
| float | x | transformed x |
| float | y | transformed y |
| float | z | transformed z |
| List | of | device information tables, format: {deviceId=[device ID], name=[device name]} |
Load device input settings (other than bindings) from an input binding XML file.Definition
loadDeviceSettingsFromXML(xmlFile XML)Arguments
| xmlFile | XML | file handle |
Apply deadzone settings to all gamepad device axes.Definition
If no deadzone value had been configured for a valid axis, the global default deadzone value is applied.
applyGamepadDeadzones()
Initialize gamepad settings from XML.Definition
initializeGamepadMapping()Return Values
| dot | the | dot product |
Check if combo action bindings are valid and restore proper bindings if necessary.Definition
validateAndRepairComboActionBindings()Return Values
| float | x | x |
| float | y | y |
| float | z | z |
Load bindings of actions to input axes from a configuration XML file.Definition
loadActionBindingsFromXML(xmlFile Configuration, silentIgnoreDuplicates [optional], modName [optional], doNotReplace [optional])Arguments
| xmlFile | Configuration | XML file handle |
| silentIgnoreDuplicates | [optional] | If true, will not give a warning on duplicate bindings and just ignore them |
| modName | [optional] | Name of the mod for which action bindings are loaded. If not set, will load bindings for the default game. |
| doNotReplace | [optional] | If true, will not replace existing bindings with a loaded ones |
Load all bindings within an XML actionBindings definition element.Definition
loadBindingsFromXML()
Store input axis names of combo command actions for combo mask checking.Definition
storeComboInputMappings()Return Values
| float | y | rotation |
Get a combo modifier mask for a binding.Definition
Because input axis names are different, this is safe to use for any binding as long as the calling context sticks
to either gamepad or mouse input bindings to check.
getBindingComboMask(Binding Binding)Arguments
| Binding | Binding | instance |
| float | x | x direction |
| float | z | z direction |
| Modifier | bit | mask |
Assign combination input bitmasks on actions based on their bindings.Definition
This will update all actions and should be run whenever any binding changes. Axis inputs will have two valid bindings
for modifiers (component + and -). Ensure that players cannot set differently modified inputs on a single axis
action.
assignComboMasks()
Store binding links based on connections set in InputAction.LINKED_ACTIONS.Definition
storeLinkedBindings()Return Values
| float | x | limited x direction |
| float | z | limited z direction |
Assign primary keyboard bindings to actions if applicable.Definition
assignActionPrimaryBindings()
Adjust a binding's slot index.Definition
adjustBindingSlotIndex(Binding Binding, usedSlots Set)Arguments
| Binding | Binding | to adjust |
| usedSlots | Set | of used slot indices for the binding's context (action, device, axis) |
| float | x | x position |
| float | x | z position |
Resolve a binding's device ID to an engine-issued internal device ID.Definition
Replaces default device IDs in bindings with the ID of the first device of the corresponding category. This
effectively binds default or template bindings to first devices. Also updates the binding's internal device ID field.
If a binding's device is missing, the method tries resolving a suitable replacement device. First, device IDs are
checked to see if the missing device simply changed order (and therefore its prefix). Then, the device names are
compared. If no identifiers match, the binding is assigned to the first device of the category of the missing device.
If all else fails, the binding is assigned to the first controller, possibly losing some bindings due to collisions.
resolveBindingDevice(Binding Binding)Arguments
| Binding | Binding | whose device ID is checked and replaced, if necessary |
| True | if | the binding's device ID could be resolved, false otherwise |
Resolve and assign the first device of a given category to a binding.Definition
resolveFirstDeviceOfCategoryToBinding()Return Values
| float | dot | product |
Resolve and assign the last device with a given base ID (without prefix) to a binding.Definition
resolveSameIdDeviceWithPrefixToBinding()Return Values
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Resolve and assign the last device with a given name to a binding.Definition
resolveMissingDeviceByNameToBinding()
Resolve and assign the first known device to a binding.Definition
resolveDefaultDeviceToBinding()
Run a a list of functions on all known bindings in the context of a given action if provided.Definition
The functions' return values are written to a results table which is returned at the end. Callers can retrieve
function results by indexing the results table with the corresponding function reference.
checkBindings(action Action, checkFunctions List)Arguments
| action | Action | reference for the current context, e.g. check bindings for an action |
| checkFunctions | List | of functions with the signature function(Binding), return [bool hasResult], result |
| function | results | table {checkFunctionRef=[functionResult]} |
Validate a new binding before it's added to the system.Definition
Returns boolean flags for constraints and a colliding binding if there was one.
validateBinding(Binding New)Arguments
| Binding | New | binding instance |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Add a new input binding.Definition
Checks for duplicates and collisions and stores the binding only if everything's okay.
addBinding(table binding, bool silentIgnoreDuplicates, bool doNotReplace)Arguments
| table | binding | New binding |
| bool | silentIgnoreDuplicates | If true, will not print warnings on duplicate bindings |
| bool | doNotReplace | If true, will not replace existing bindings |
| bool | True | if the new binding could be added |
| table | Reference | to an Action containing a binding which collided with the added binding or nil |
Update an existing binding with new device ID, input axes and input component data.Definition
updateBinding(findDeviceId Binding, findActionName Name, findBindingIndex Binding, findAxisComponent Logical, deviceId New, axisNames List, inputComponent Physical)Arguments
| findDeviceId | Binding | device ID |
| findActionName | Name | of binding action |
| findBindingIndex | Binding | index, 1 primary, 2 secondary, etc. |
| findAxisComponent | Logical | binding axis direction component to assign, always positive for buttons/keys/half-axes |
| deviceId | New | device ID |
| axisNames | List | of new input axis names |
| inputComponent | Physical | input axis direction component to bind (direction of last axis in axisNames) |
| True | if | a binding could be found and updated, false if there was a collision |
| Binding | and | Action reference of a collision as {collisionBinding=Binding, collisionAction=Action} or nil |
Delete a binding.Definition
deleteBinding(findDeviceId Binding, findActionName Name, findBindingIndex Binding, findAxisComponent Logical)Arguments
| findDeviceId | Binding | device ID |
| findActionName | Name | of binding action |
| findBindingIndex | Binding | index, 1 primary, 2 secondary, etc. |
| findAxisComponent | Logical | binding axis direction component to assign, always positive for buttons/keys/half-axes |
Check if a keyboard or mouse input is active.Definition
getKeyboardMouseInputActiveAndValue(axes List, axisDirection 1, inputDirection 1)Arguments
| axes | List | of input axis names which are all required to be pressed for the input to be active |
| axisDirection | 1 | or -1, determining the direction of the bound logical axis. |
| inputDirection | 1 | or -1, determining the direction of the requested physical axis component. |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
| True | if | all given axes are currently active, input value of the non-modified axis (last in list) |
Scan gamepad / controller binding input.Definition
Both buttons and controller physical axes are checked for a given device and list of logical axes.
getGamepadInputActiveAndValue(internalDeviceId Engine-internal, axes List, axisDirection 1, inputDirection 1)Arguments
| internalDeviceId | Engine-internal | device ID of device to scan |
| axes | List | of axis names to scan |
| axisDirection | 1 | or -1, determining the direction of the bound logical axis component. |
| inputDirection | 1 | or -1, determining the direction of the requested physical axis component. |
| True | if | all axes are active (digital interpretation), input value of the non-modified axis (last in list) |
Scan a controller axis on a device for input.Definition
getGamepadAxisValue()Return Values
| Input | value | of the axis in the range [-1, 1] for full axes or [0, 1] for half axes. |
Update input for the current frame.Definition
update(dt Delta)Arguments
| dt | Delta | time in milliseconds |
Check if any gamepad button or axis is pressed and set the input help mode if necessary.Definition
checkGamepadActive()Return Values
| float | rotation | the final rotation |
Update mouse input state.Definition
updateMouseInput()Return Values
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Finalize mouse input processing for the current frame.Definition
Wraps the mouse position around if required and resets movement accumulators.
finalizeMouseInput()
Clear an active binding buffer for re-use to avoid table allocation each frame.Definition
Automatically adds device entries if new devices are added.
clearActiveBindingBuffer()
Update input bindings associated with all registered and active events.Definition
updateEventBindings()
Check if there is currently any binding matching the pressed mouse combo mask.Definition
This is used to override the combo mask ignore flag for mouse look in special cases. E.g. when rotating a tool axis
with a mouse dragging movement, mouse look should be disabled for the other axis as well, even if it has no active
binding with the same combo button (which would have been handled by input shadowing).
hasBindingForPressedMouseComboMask()Return Values
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Apply binding input shadow flag to bindings of linked actions for currently active input.Definition
shadowLinkedBindings()
Update binding input for combo action bindings.Definition
updateComboBindings()
Update input bindings with the current input state and trigger events.Definition
updateInput()
Update the input state of a single action-to-input binding.Definition
updateBindingInput()Return Values
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Reset binding input state for bindings used in continuous trigger events.Definition
resetContinuousEventBindings(bool checkComboMasks, int gamepadComboMask, int gamepadMouseMask)Arguments
| bool | checkComboMasks | [optional, default=false] If true, checks binding combo masks against provided parameters |
| int | gamepadComboMask | [optional] Gamepad combo input bit mask |
| int | gamepadMouseMask | [optional] Mouse combo input bit mask |
Set an event's binding's input to a neutral value and notify the event's listeners of that neutral position.Definition
neutralizeEventBindingInput()
Update and debug information display.Definition
updateDebugDisplay()
Save action bindings to player custom settings.Definition
saveToXMLFile()Return Values
| float | distance | distance to rectangle |
Save all device settings.Definition
Updates and adds settings for active devices.
saveDeviceSettings(xmlFile Input)Arguments
| xmlFile | Input | settings XML file handle |
| float | distance | distance to line segment |
Get a deep copy of the action definitions list.Definition
getActionList()Return Values
| boolean | hasIntersection | true if both lines intersect |
| float | t1 | x position |
| float | t2 | z position |
Get a loaded InputAction by name.Definition
Only use the returned action to assign references and do not change its state.
getActionByName(actionName Name)Arguments
| actionName | Name | of the action to be returned |
| InputAction |
Disable alternate bindings for a given action.Definition
This setting is only valid in the current input context and will be overwritten the next time any input event is
changed.
disableAlternateBindingsForAction(string actionName)Arguments
| string | actionName | InputAction name |
Set the current input context.Definition
If no context of the given name exists, it will be created.
setContext(string name, bool createNew, bool deletePrevious)Arguments
| string | name | Input context name |
| bool | createNew | [optional, default=false] If true, will create a clear context with the given name, overriding any other with the same name. |
| bool | deletePrevious | [optional, default=false] If true, will delete the previous context after setting the new context. |
| boolean | hasIntersection | true if the line segment is completly in the rectangle OR if it intersects with one of the four rectangle sides |
Revert the input context to the previously set input context.Definition
Use this method only if you can guarantee a valid state, e.g. when calling setContext in a modal interaction context.
revertContext(bool deleteCurrent)Arguments
| bool | deleteCurrent | [optional, default=false] If true, will delete the current input context before reverting to the previous context. |
| float | pos1X | x pos of intersection 1, else nil |
| float | pos1Y | y pos of intersection 1, else nil |
| float | pos2X | x pos of intersection 2, else nil |
| float | pos2Z | z pos of intersection 2, else nil |
Set the previous context for another context.Definition
This modifies the previous context value which is used to revert input contexts.
setPreviousContext(string forContextName, string previousContextName)Arguments
| string | forContextName | Name of the input context whose previous context will be set |
| string | previousContextName | New previous context name |
Clear all contexts except the root.Definition
The current context will also be set to root.
clearAllContexts()
Get the name of the current input context.Definition
getContextName()
Get an ordered copy of the action bindings table for manipulation.Definition
getActionBindingsCopy(onlyAssignable If)Arguments
| onlyAssignable | If | true, will only copy assignable action bindings |
| boolean | hasIntersection | true if spheres have an intersection else false |
| Ordered | action | bindings in the form of {i={action=InputAction, bindings={Binding}}} |
Get the current action bindings table.Definition
The returned table is the current live state and can thus contain unconfirmed modifications. Treat the table as
read-only so as not to interfere with the correct workings of this class. Only use this getter for initializations
and otherwise rely on setBindingChangeCallback() to receive a confirmed working state.
getActionBindings()Return Values
| float | area | area in hectars |
Get an array of registered events in the current input context for a given action name.Definition
getEventsForActionName(string actionName)Arguments
| string | actionName | Action name as valid in InputAction |
| float | brightness | brightness |
| table | Array | of events |
Get the first active event in the current input context for a given action name.Definition
getFirstActiveEventForActionName(string actionName)Arguments
| string | actionName | Action name as valid in InputAction |
| float | averageSpeed | average speed |
| table | First | active event or InputEvent.NO_EVENT |
Set the mouse motion scale.Definition
The given scale factor is multiplied with the default mouse motion scale of 0.75 on the X and Y axes.
setMouseMotionScale(scale Mouse)Arguments
| scale | Mouse | motion scale factor |
| float | speed | speed |
Get the current mouse motion scale.Definition
getMouseMotionScale()Return Values
| float | speedRandom | speed random |
| x | motion | scale, Y motion scale |
Set a callback for binding changes.Definition
The callback function must accept the action bindings table as a single argument.
setBindingChangeCallback(callback Callback)Arguments
| callback | Callback | function which takes a single table parameter in the form of {InputAction: {i=Binding}} |
| float | normalSpeed | normal speed |
Set a callback for event changes.Definition
The callback function must accept the action-event tuples in an array as a single argument.
setEventChangeCallback(callback Callback)Arguments
| callback | Callback | function which takes a single table parameter in the form of {i={action=InputAction, event=InputEvent}} |
| float | tangentSpeed | tangent speed |
Notify a listener of binding changes.Definition
The notification callback is set via setBindingChangeCallback().
notifyBindingChanges()Return Values
| float | spriteScaleX | X sprite scale |
Notify a listener of event changes.Definition
The notification callback is set via setEventChangeCallback().
notifyEventChanges()Return Values
| float | spriteScaleY | Y sprite scale |
Notify components of an input mode change via the message center.Definition
notifyInputModeChange()Return Values
| float | spriteScaleXGain | X sprite scale gain |
Check if user input bindings are corrupted and compare input configuration XML file versions.Definition
checkSettingsIntegrity()Return Values
| float | spriteScaleYGain | Y sprite scale gain |
| True | if | the user settings have passed the integrity check, false otherwise. |
Checks loaded default input bindings according to exclusive locked action groups defined in InputAction.Definition
This is a developer check to verify critical input plausibility.
checkDefaultInputExclusiveActionBindings()Return Values
| True | if | there is a collision, false otherwise |
Determine if an axis name represents a physical full axis on any device.Definition
getIsPhysicalFullAxis(inputAxisName Axis)Arguments
| inputAxisName | Axis | name |
| object | instance or nil if no object could be created | |
| True | if | no placeable object could be created at the requested position because there was no valid space, false otherwise |
| True | if | the axis name represents a physical full axis, false otherwise |
Determine if an axis name represents a physical half-axis on any device.Definition
getIsHalfAxis(inputAxisName Axis, True if)Arguments
| inputAxisName | Axis | name |
| True | if | the axis name represents a physical half-axis, false otherwise |
Determine if an input name represents an analog (i.e. continuous) physical input.Definition
getIsAnalogInput(inputName Input)Arguments
| inputName | Input | name (potentially a key, button or axis identifier) |
| object | instance or nil if no object could be created | |
| True | if | no placeable object could be created at the requested position because there was no valid space, false otherwise |
| True | if | the input name represents an analog input |
Determine if an input name list represents keyboard input.Definition
getIsKeyboardInput()
Determine if an input name list represents mouse input.Definition
getIsMouseInput()Return Values
| float | Ray | origin X position in world space |
| float | Ray | origin Y position in world space |
| float | Ray | origin Z position in world space |
| float | Ray | direction X component |
| float | Ray | direction Y component |
| float | Ray | direction Z component |
Determine if an input name list represents a single mouse wheel step input.Definition
getIsMouseWheelInput()
Determine if an input name list represents gamepad / controller input.Definition
getIsGamepadInput()
Check if an input name list represents a single D-Pad input.Definition
getIsDPadInput()
Check if the input value of an axis can be considered zero.Definition
isAxisZero(value Axis)Arguments
| value | Axis | input value |
| True | if | the axis is at zero position, false otherwise |
Get the maximum number of bindings per action for a given device ID.Definition
getMaximumBindingCountForDeviceId()
Get the device category for a device identified by its engine-internal IDDefinition
getDeviceCategory()Return Values
| any_type | unpackedValues | returns unpacked values found in string |
Game input device.
-- The class stores name and IDs of a device as well as physical to logical key / button / axis mappings.
--@category Input
--@xmlConfig device#id Device ID, must match a value returned by engine function getGamepadId() or one of the constants
of InputDevice.DEFAULT_DEVICE_NAMES.
Create a new InputDevice instance.Definition
new(internalId Internal, deviceId Device, deviceName Device, category Device)Arguments
| internalId | Internal | device ID as issued by the engine (~ device index) |
| deviceId | Device | ID as received by the operating system |
| deviceName | Device | name as assembled by the engine |
| category | Device | category, use one of the values of InputDevice.CATEGORY |
| table | values | values |
| New | InputDevice | instance |
Load device settings from an XML file.Definition
This will load settings other than input bindings, e.g. sensitivity for gamepad axes.
loadSettingsFromXML(xmlFile XML, deviceElement XML)Arguments
| xmlFile | XML | file handle |
| deviceElement | XML | data element for this device |
| table | values | radian values |
Save device settings to an XML file.Definition
saveSettingsToXML(xmlFile XML, deviceElement XML)Arguments
| xmlFile | XML | file handle |
| deviceElement | XML | data element for this device |
| table | result | text elements |
Determine if this device is a controller/gamepad.Definition
isController()Return Values
| boolean | startsWidth | true if given string starts with pattern else false |
Load a device's ID from an XML file.Definition
loadIdFromXML(xmlFile XML, deviceElement XML)Arguments
| xmlFile | XML | file handle |
| deviceElement | XML | data element for this device |
| boolean | startsWidth | true if given string ends with pattern else false |
| Device | ID | or empty string |
Load a device's name from an XML file.Definition
loadNameFromXML(xmlFile XML, deviceElement XML)Arguments
| xmlFile | XML | file handle |
| deviceElement | XML | data element for this device |
| string | trimedString | trimed text |
| Device | name | or empty string |
Load a device's category from an XML file.Definition
loadCategoryFromXML(xmlFile XML, deviceElement XML, Device category)Arguments
| xmlFile | XML | file handle |
| deviceElement | XML | data element for this device |
| Device | category |
| any_type | value | not nil value |
Get a device ID prefix' numeric value, if it exists.Definition
getDeviceIdPrefix()Return Values
| float | lx | x direction |
| float | lz | z direction |
| Prefix | number, | or -1 if none exists; raw engine-issued device ID |
Get a device ID prefixed with a given number.Definition
getPrefixedDeviceId()
Get a string representation for this device.Definition
toString()Return Values
| float | lx | average x direction |
| float | lz | average z direction |
Bridge-component between input handling and UI.
-- Serves data and display elements for input controls. Accesses InputBinding data and is being accessed by UI
components.
--@category Input
Load the required base data for this manager.Definition
Loads controller symbols / UI overlays.
load()
Set up development-only gamepad label function if necessary.Definition
setDevGamepadLabelMapping()Return Values
| boolean | isAttached | is attached |
Load controller symbols and create UI overlays.Definition
Reads a UV values from an XML configuration to create UI overlays from a texture atlas.
loadControllerSymbolsAndOverlays()Return Values
| boolean | doesBlock | implement does block |
Create a single button overlay.Definition
createButtonOverlay()Return Values
| integer | aiToolReverserDirectionNode | reverser direction node of ai tool |
Load vehicle axis input hint icons.Definition
The icons are kept in memory as overlays until the game is closed.
loadAxisIcons(xmlFile XML, modPath [optional])Arguments
| xmlFile | XML | file handle |
| modPath | [optional] | Path to loaded mod, will load defaults if set to nil |
| float | maxTurnRadius | max turn radius |
Load axis icons defined by mods.Definition
loadModAxisIcons()Return Values
| float | leftAreaPercentage | left area percentage |
| float | rightAreaPercentage | right area percentage |
Add bindings of an action to a list if they fit the required context (gamepad or keyboard and mouse),Definition
addContextBindings(contextBindings Bindings, action InputAction, isContextGamepad If)Arguments
| contextBindings | Bindings | list for the required context |
| action | InputAction | reference |
| isContextGamepad | If | true, we require gamepad bindings. Otherwise, keyboard and mouse is needed. |
Get bindings from one or two actions for the current input context (either gamepad or keyboard and mouse).Definition
getActionBindingsForContext()Return Values
| float | area | area found |
| float | totalArea | total area checked |
Get bindings for one or two actions for keyboard input, exclusively.Definition
getKeyboardBindings()
Resolve modifier symbols and add them to an overlays collection.Definition
resolveModifierSymbols(table overlays, table separators, table firstContextBinding)Arguments
| table | overlays | Overlays collection which receives resolved modifier symbols |
| table | separators | Array of separator types inbetween symbols |
| table | firstContextBinding | Binding reference |
| float | area | area found |
| float | totalArea | total area checked |
Recursively permutate symbol names to resolve dynamically ordered names to fixed definitions.Definition
resolveAccumulatedSymbolPermutations()
Resolve unmodified binding axis names to symbol overlays and add those to the given overlays collection.Definition
resolveUnmodifiedSymbols(table overlays, table contextBindings, bool isContextGamepad)Arguments
| table | overlays | Overlays collection which receives resolved symbols |
| table | contextBindings | Array of Binding instances |
| bool | isContextGamepad | If true, resolve gamepad symbols. Otherwise, resolve mouse symbols. |
| float | area | area found |
| float | totalArea | total area checked |
Add regular input display symbols to an overlays array.Definition
addRegularSymbols()
Add combo input display symbols to an overlays array.Definition
addComboSymbols()Return Values
| table | instance | instance of object |
Get input symbol overlays for one or two actions.Definition
Use the second action parameter to display complementary actions, e.g. action1 is moving forward/backward, action 2
is moving left/right.
getControllerSymbolOverlays(actionName1 First, actionName2 [optional])Arguments
| actionName1 | First | action name as defined and/or loaded in InputAction |
| actionName2 | [optional] | Second action name as defined and/or loaded in InputAction |
| integer | numOfConfigurationTypes | number of configuration types |
| InputHelpElement | instance | containing the symbol overlays in its "buttons" field |
Determine if an action and bindings combinations requires their input symbols to be accumulated to get a combinedDefinition
symbol for better display.
requireSymbolAccumulation()Return Values
| integer | numOfConfigurationTypes | number of configuration types |
Make a help element to display in the HUD.Definition
makeHelpElement()Return Values
| string | name | name of config |
Called when action events in the input system have changed.Definition
onActionEventsChanged(displayActionEvents Array)Arguments
| displayActionEvents | Array | of action-event tuples which require an input hint display, {i={action=InputAction, event=InputEvent}} |
| integer | index | index of config |
Table sorting function for event help elements.Definition
Priority > primaryKeyboardInput > text
sortEventHelpElements()Return Values
| table | configuration | configuration |
Sorting function for gamepad modeDefinition
sortEventHelpElementsGamepad()Return Values
| any_type | value | value of attribute |
Store display help elements for currently active action events.Definition
storeEventHelpElements()Return Values
| boolean | configurationHasBeenBought | configuration has been bought |
Store combo button help elements for any modifier buttons required by currently active action events.Definition
storeComboHelpElements()Return Values
| table | color | color (r, g, b) |
Get the input help element for a given action.Definition
getEventHelpElementForAction(inputActionName Name)Arguments
| inputActionName | Name | of the requested input action. |
| integer | material | material |
Get input help elements for the current input context.Definition
getEventHelpElements(pressedComboMask Bit, isContextGamepad If)Arguments
| pressedComboMask | Bit | mask of the currently pressed combo button input |
| isContextGamepad | If | true, the player should be shown input hints for gamepad input. Otherwise, keyboard / mouse input help is required. |
| any_type | value | value of config |
| array | of | help elements (see InputDisplayManager:makeHelpElement() for structure) |
Get input help elements for currently available combo buttons.Definition
getComboHelpElements(isContextGamepad If)Arguments
| isContextGamepad | If | true, the player should be shown input hints for gamepad input. Otherwise, keyboard / mouse input help is required. |
| string | configKey | key of configuration |
| integer | configIndex | index of configuration |
| Hash | table | of currently active combo button action names, {action name=true} |
Get the controller symbol prefix for a device identified by its internal ID.Definition
getPrefix()
Get the "plus" sign overlay.Definition
This overlay is (re-)used in several places. Take care not to invalidate its state between uses.
getPlusOverlay()Return Values
| table | color | color (r, g, b) |
Get the "or" sign overlay.Definition
This overlay is (re-)used in several places. Take care not to invalidate its state between uses.
getOrOverlay()Return Values
| table | instance | instance of object |
Get the keyboard key glyph overlay (ButtonOverlay instance).Definition
This overlay is (re-)used in several places. Take care not to invalidate its state between uses.
getKeyboardKeyOverlay()Return Values
| boolean | true | if loading was successful else false |
Get the first input axis name of the first binding of an input action identified by name.Definition
getFirstBindingAxisAndDeviceForActionName(inputActionName Name, axisComponent [optional,, isGamepad [optional,)Arguments
| inputActionName | Name | of an action as defined in InputAction |
| axisComponent | [optional, | default=POSITIVE] Binding.AXIS_COMPONENT value |
| isGamepad | [optional, | default=false] If true, return the first gamepad binding. Otherwise, keyboard is requested. |
| boolean | success | true if added else false |
| Axis | name | or nil if not found, internal ID of binding device |
Get an input action glyph overlay for the first gamepad button of the first binding of a given input action.Definition
The method is simplified by design to mainly provide menu button glyphs. It will only return an overlay for the
first input axis, i.e. a simple button binding without modifiers. The returned overlay is a new instance and callers
need to delete it after use via GuiOverlay.deleteOverlay().
getGamepadInputActionOverlay(inputActionName Name, axisComponent [optional,)Arguments
| inputActionName | Name | of an action as defined in InputAction |
| axisComponent | [optional, | default=POSITIVE] Binding.AXIS_COMPONENT value |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| Overlay | instance | or nil if no overlay is available for the action's bindings |
Get an input action key text for the key of the first binding of a given input action.Definition
getKeyboardInputActionKey(inputActionName Name, axisComponent [optional,)Arguments
| inputActionName | Name | of an action as defined in InputAction |
| axisComponent | [optional, | default=POSITIVE] Binding.AXIS_COMPONENT value |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| key | text | (e.g. "F" for "KEY_f") for the keyboard action binding or nil if no resolution is possible |
Get the controller symbol key name for a single gamepad / controller binding.Definition
For full axes, call this for each axis component binding and concatenate the symbol keys with a space inbetween.
getGamepadInputSymbolName(internalDeviceId Internal, axisName Input, isAxisInput If)Arguments
| internalDeviceId | Internal | gamepad device ID |
| axisName | Input | axis name |
| isAxisInput | If | true, the bound input axis is an analog physical axis |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| controller | symbols | configuration key name |
Get the controller symbol key name for a single mouse binding.Definition
For full axes, call this for each axis component binding and concatenate the symbol keys with a space inbetween.
getMouseInputSymbolName(axisNames Mouse)Arguments
| axisNames | Mouse | binding axis names |
| boolean | canStart | can start ai |
| controller | symbols | configuration key name |
Called when action bindings have changed.Definition
onActionBindingsChanged(actionBindings Action)Arguments
| actionBindings | Action | bindings table, {InputAction: {Binding}} |
| boolean | canContiue | can contiue ai |
Input event.
-- Holds information about an input event for an action, target, event callback and event trigger conditions. Also
stores input hint display information, because only actions with associated events need hints. When an action has
several events, callers need to make sure only one of the events' visibility is active.
--@category Input
Create a new instance of InputEvent.Definition
This constructor also assigns an ID to the instance, which should be used as a handle in most cases instead of the
actual instance reference.
new(actionName Name, targetObject Event, eventCallback Event, triggerUp If, triggerDown If, triggerAlways If, startActive [optional,, callbackState [optional])Arguments
| actionName | Name | of action, see InputAction |
| targetObject | Event | target, first argument to event callback |
| eventCallback | Event | callback, called when the action has input. Signature: callback(targetObject, actionName, inputValue, callbackState, isAnalog) |
| triggerUp | If | true, the event fires once when an input signal of the given action goes to "up" state, e.g. a released key. |
| triggerDown | If | true, the event fires once when an input signal of the given action goes to "down" state, e.g. a pressed key. |
| triggerAlways | If | true, the event fires on any signal input change, potentially every frame. This is mostly useful for required continuous axis input, e.g. steering actions. |
| startActive | [optional, | default=false] If true, the event is active right after registration. |
| callbackState | [optional] | An arbitrary value or reference which serves as a closure state within callbacks, useful e.g. when checking input state between events without requiring class fields. |
| float | direction | shape angle |
| New | InputEvent | instance |
Set the ignore flag for input binding combo masks.Definition
If true, bindings which can trigger this event can be activated even if the do not match the currently pressed combo
mask. This is required for special cases, mostly player camera input, which should still be active when a combo
button is pressed.
setIgnoreComboMask()Return Values
| boolean | isNeeded | collision box is needed |
Get the ignore flag for input binding combo masks.Definition
getIgnoreComboMask()Return Values
| string | attributes | stats attributes |
Notify this event of the input status of an associated action.Definition
-- Note the interaction of the "down" and "always" triggers: If "down" is set and not "always", only an input down-flank
will raise an event. If both "down" and "always" are set, an event is raised as long as the input is pressed beyond a
threshold. If only "always" and not "down" is set, an event will be raised on each frame with the current input value
even if no input is active.
-- @param isUp If true, the action input had an "up" flank, i.e. the input was in "pressed" state and has returned to zero position
notifyInput(isDown If, isPressed If, inputValue Action, isAnalog True)Arguments
| isDown | If | true, the action input had a "down" flank, i.e. the input has crossed the "down" threshold |
| isPressed | If | true, the action input is in "pressed" state, i.e. the magnitude of the input is above the "down" threshold |
| inputValue | Action | input value. This is not guaranteed to be in the range [-1, 1], because of sensitivity settings. Event handlers may clamp input values at their own discretion. |
| isAnalog | True | if the binding which provides the input value is analog. |
| boolean | deactivateOnLeave | deactivate on leaving |
Reset this event after a frame.Definition
frameReset()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Make this InputEvent instance's ID.Definition
makeId()Return Values
| boolean | exists | animation axists |
Return trigger codeDefinition
getTriggerCode()Return Values
| boolean | isPlaying | animation is playing |
Initialize this event's input hint display text with information from its associated action.Definition
Defaults to the action's positive display name. For most half-axis actions, this will be correct. If a more specific
text is required depending on the current player context, apply a different text through the input system using
InputBinding:setActionEventText().
initializeDisplayText(InputAction InputAction)Arguments
| InputAction | InputAction | which this event references. |
| float | animTime | real animation time in ms |
Input glyph display element.
-- Displays a key or button glyph for an input.
--@category GUI
Create a new instance of InputGlyphElement.Definition
new(table inputDisplayManager, float baseWidth, float baseHeight)Arguments
| table | inputDisplayManager | InputDisplayManager reference |
| float | baseWidth | Default width of this element in screen space |
| float | baseHeight | Default height of this element in screen space |
| table | npc | the npc object |
Delete this element and release its resources.Definition
delete()Return Values
| table | instance | instance of object |
Delete any overlay copies.Definition
deleteOverlayCopies()Return Values
| boolean | true | if loading was successful else false |
Set the scale of this element.Definition
setScale(float widthScale, float heightScale)Arguments
| float | widthScale | Width scale factor |
| float | heightScale | Height scale factor |
| bool | true | if successful |
Set the glyph text to be displayed in all upper case or not.Definition
This resets the lower case setting if upper case is enabled.
setUpperCase()Return Values
| bool | true | if successful |
Set the glyph text to be displayed in all lower case or not.Definition
This resets the upper case setting if lower case is enabled.
setLowerCase()Return Values
| bool | true | if normalized |
Set the glyph text to be displayed in bold print or not.Definition
setBold()Return Values
| bool | true | if normalized |
Set the button frame color for the keyboard glyphs.Definition
setKeyboardGlyphColor(table color)Arguments
| table | color | Color as an RGBA array |
| table | returns | a food group or nil if nothing is found |
Set the color for button glyphs.Definition
setButtonGlyphColor(table color)Arguments
| table | color | Color as an RGBA array |
| table | returns | a food group or nil if nothing is found |
Set the action whose input glyphs need to be displayed by this element.Definition
setAction(string actionName, string actionText, float actionTextSize, bool noModifiers, bool copyOverlays)Arguments
| string | actionName | InputAction name |
| string | actionText | [optional] Additional action text to display after the glyph |
| float | actionTextSize | [optional] Additional action text size in screen space |
| bool | noModifiers | [optional] If true, will only show the input glyph of the last unmodified input binding axis |
| bool | copyOverlays | [optional] If true, will create and handle a separate copy of an input glyph. Do not use this when updating the action each frame! |
| table | returns | a food mixture or nil if nothing is found |
Set multiple actions whose input glyphs need to be displayed by this element.Definition
If exactly two actions are passed in, they will be interpreted as belonging to the same axis and the system tries
to resolved the actions to a combined glyph. Otherwise, the glyphs will just be displayed in order of the actions.
setActions(table actionNames, string actionText, float actionTextSize, bool noModifiers, bool copyOverlays)Arguments
| table | actionNames | InputAction names array |
| string | actionText | [optional] Additional action text to display after the glyph |
| float | actionTextSize | [optional] Additional action text size in screen space |
| bool | noModifiers | [optional] If true, will only show the input glyph of the last unmodified input binding axis |
| bool | copyOverlays | [optional] If true, will create and handle a separate copy of an input glyph. Do not use this when updating the action each frame! |
| int | Food | consumption type, one of [AnimalFoodManager.FOOD_CONSUME_TYPE_SERIAL | AnimalFoodManager.FOOD_CONSUME_TYPE_PARALLEL] |
Update the display text from the set action text according to current casing settings.Definition
updateDisplayText()Return Values
| table | returns | a food group or nil if not found |
Get the screen space width required by the glyphs used to display input in the current input context.Definition
getGlyphWidth()Return Values
| float | returns | a production weight between 0 and 1 |
Draw the input glyph(s).Definition
draw()Return Values
| table | instance | Instance of object |
Draw controller button glyphs.Definition
drawControllerButtons(table Array, float posX, float posY)Arguments
| table | Array | of controller button glyph overlays |
| float | posX | Initial drawing X position in screen space |
| float | posY | Initial drawing Y position in screen space |
| bool | returns | true if loading is fine |
| float | X | position in screen space after the last glyph |
Draw keyboard key glyphs.Definition
drawKeyboardKeys(table Array, float posX, float posY)Arguments
| table | Array | of keyboard key names |
| float | posX | Initial drawing X position in screen space |
| float | posY | Initial drawing Y position in screen space |
| bool | returns | true if module init after placement is fine |
| float | X | position in screen space after the last glyph |
Draw the action text after the input glyphs.Definition
drawActionText(float posX, float posY)Arguments
| float | posX | Drawing X position in screen space |
| float | posY | Drawing Y position in screen space |
HUD input help display.
-- Displays controls and further information for the current input context.
--@category GUI
Create a new instance of InputHelpDisplay.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
Add a help text line for this frame.Definition
Will be cleared after the current frame.
addHelpText()Return Values
| bool | returns | true if initialization is fine |
Set the currently controlled vehicle.Definition
setVehicle(table vehicle)Arguments
| table | vehicle | Vehicle reference or nil if no vehicle is controlled. |
| string | fill | level |
Override of HUDDisplayElement.Definition
Moves out to the left to hide.
getHidingTranslation()Return Values
| integer | total | animals. Default is zero |
Add a custom input help entry which is displayed in the current input context until removed.Definition
Custom entries will be displayed in order of addition after any automatically detected input help entries and before
vehicle extensions.
addCustomEntry()Return Values
| string | return | animal type. nil if not available |
Clear all custom input help entries in the current context.Definition
clearCustomEntries()Return Values
| table | of | cosumed food (result[fillTypeIndex]=amount) |
Update the input help's state.Definition
update()Return Values
| table | Production | fill type information as {i={j={fillType=fillType, fillLevel=fillLevel, capacity=capacity}}} |
Update sizes and positions of this elements and its children.Definition
updateSizeAndPositions()Return Values
Create any required HUD extensions when the current vehicle configuration changes.Definition
refreshHUDExtensions()Return Values
| bool | true | if registration went well |
Update HUD extensions if controlled vehicles have changed.Definition
updateHUDExtensions()Return Values
| bool | true | if registration went well |
Recursively get vehicle specializations for a vehicle configuration.Definition
collectVehicleSpecializations()Return Values
| float | dirty | factor [0-1] |
Get the available screen space height for displaying input help.Definition
getAvailableHeight()Return Values
| table | instance | instance of object |
Update display elements with the current input context.Definition
updateInputContext()Return Values
| boolean | true | if loading was successful else false |
Update entry glyphs and visibility with the current input help elements.Definition
updateEntries(table helpElements)Arguments
| table | helpElements | Array of InputHelpElement for the current input context |
| bool | true | if successful |
Update combo header state.Definition
updateComboHeaders(bool useGamepadButtons, int pressedComboMaskMouse, int pressedComboMaskGamepad)Arguments
| bool | useGamepadButtons | If true, check gamepad input. Otherwise, check keyboard / mouse. |
| int | pressedComboMaskMouse | Bit mask of pressed mouse combo actions |
| int | pressedComboMaskGamepad | Bit mask of pressed gamepad combo actions |
| bool | true | if animal is in the husbandry |
| float | Screen | space height used by the combo header (0 if invisible) |
Update visibility and color of combo input glyphs.Definition
updateComboInputGlyphs(table comboActionStatus, int pressedComboMaskMouse, int pressedComboMaskGamepad)Arguments
| table | comboActionStatus | Hashtable of combo action names which are currently available |
| int | pressedComboMaskMouse | Bit mask of pressed mouse combo actions |
| int | pressedComboMaskGamepad | Bit mask of pressed gamepad combo actions |
| table |
Get input help elements based on the current input context.Definition
getInputHelpElements(float availableHeight, int pressedComboMaskGamepad, int pressedComboMaskMouse, bool useGamepadButtons)Arguments
| float | availableHeight | Maximum available height to use for input help elements |
| int | pressedComboMaskGamepad | Bit mask of pressed gamepad combo buttons |
| int | pressedComboMaskMouse | Bit mask of pressed mouse combo buttons |
| bool | useGamepadButtons | If true, we should draw gamepad / controller combo button glyphs |
| table | instance | instance of object |
| table | Input | help elements |
| float | Screen | space height used by the returned help elements |
Set the current animation value for available height.Definition
setAnimationAvailableHeight()Return Values
| boolean | true | if loading was successful else false |
Set the current animation position offset.Definition
setAnimationOffset()Return Values
| bool | true | if successful |
Animate this element on hiding.Definition
animateHide()Return Values
| bool | true | if successful |
Animate this element on showing.Definition
animateShow()Return Values
| float | amount | effectively changed |
Called when a hiding or showing animation has finished.Definition
onAnimateVisibilityFinished()Return Values
| float | delta | of amount changed |
Called when the connected input devices have changed.Definition
onInputDevicesChanged()Return Values
| float | returns | the fillLevel of a fillType |
Draw the input help.Definition
Only draws if the element is visible and there are any help elements.
draw()Return Values
| float | returns | the fillLevel of all fillType |
Draw the "controls" label on top of the display frame.Definition
drawControlsLabel()Return Values
| float | returns | the capacity of a fillType |
Draw icons and text in help entries.Definition
drawHelpInfos()Return Values
| float | returns | a progress of the fillType between 0 and 1. Default is 0. |
Draw vehicle HUD extensions.Definition
drawVehicleHUDExtensions()Return Values
| float | actual | delta filled |
Set this element's UI scale.Definition
setScale(float uiScale)Arguments
| float | uiScale | UI scale factor |
Store scaled positioning, size and offset values.Definition
storeScaledValues()Return Values
| table | module | instance or nil of no module has been registered |
Get this element's base background position in screen space.Definition
getBackgroundPosition()Return Values
| table | instance | instance of object |
Get the current top left position of the input help, including animation.Definition
getTopLeftPosition()Return Values
| boolean | true | if loading was successful else false |
Get the maximum number of help entries which may be shown at any time.Definition
getMaxEntryCount()Return Values
| float | returns | the capacity of a fillType |
| int | Maximum | number of entries to show |
Returns if it is still allowed to add more help elementsDefinition
getIsHelpElementAllowed(table helpElements, table helpElement)Arguments
| table | helpElements | existing table of help elements |
| table | helpElement | help element to add |
| float | amount | effectively changed |
| boolean | isAllowed | isAllowed |
Set this element's dimensions.Definition
Override from HUDElement which adjusts frame with offset.
setDimension()Return Values
| boolean | true | if loading was successful else false |
Create the background overlay for positioning.Definition
createBackground()Return Values
| float | food | dropped |
Create required display components.Definition
createComponents()Return Values
| float | spillage | total |
Create the frame around input help elements.Definition
createFrame()Return Values
| table | instance | instance of object |
Create a vertical separator element.Definition
createVerticalSeparator()Return Values
| boolean | true | if loading was successful else false |
Create a horizontal separator element.Definition
createHorizontalSeparator()Return Values
| table | instance | instance of object |
Create an input glyph for displaying combo input buttons.Definition
createComboInputGlyph(float posX, float posY, float width, float height, string actionName)Arguments
| float | posX | Screen space X position |
| float | posY | Screen space Y position |
| float | width | Screen space width |
| float | height | Screen space height |
| string | actionName | InputAction name of the combo action whose input glyphs need to be displayed |
| boolean | true | if loading was successful else false |
| table | Combo | InputGlyphElement instance |
Create a combo input glyph header.Definition
createComboHeader(hudAtlasPath Path, frameX Screen, frameY Screen, table combos, table boxSize, table separatorPositions)Arguments
| hudAtlasPath | Path | to HUD texture atlas |
| frameX | Screen | space X position of the display frame |
| frameY | Screen | space Y position of the display frame |
| table | combos | Array of input combination descriptions as defined in InputBinding |
| table | boxSize | 2D vector which holds the pixel size of one combo input box within the header |
| table | separatorPositions | Array of 2D vectors of separator pixel positions |
| float | manure | dropped |
| table | Combo | header HUDElement |
Create the mouse combo header.Definition
createMouseComboHeader()Return Values
| float | manure | fill level |
Create the gamepad / controller combo header.Definition
createControllerComboHeader()Return Values
| float | manure | used |
Create an input help entry box.Definition
createEntry()Return Values
| table | instance | instance of object |
Create all required input help entry boxes.Definition
The boxes are modified as required to reflect the current input context.
createEntries()Return Values
| boolean | true | if loading was successful else false |
Input help element.
-- Holds help and hint information about the current input context for display in the HUD.
--@category Input
Get an array of action names which were validly set in the constructor.Definition
getActionNames()Return Values
| float | animTime | animation time [0..1] |
Maps key IDs to localization glyph symbols.
Get a display string for a list of mouse input axis namesDefinition
MouseHelper.getInputDisplayText()Return Values
| float | duration | duration in ms |
Get a display name for a given key ID.Definition
Encapsulates engine getKeyName() function.
KeyboardHelper.getDisplayKeyName()Return Values
| boolean | rightOrder | returns true if parts are in right order |
Get a display string for a list of keyboard input keys.Definition
KeyboardHelper.getInputDisplayText()Return Values
| boolean | rightOrder | returns true if parts are in reverse right order |
Get a display string for a list of gamepad/controller input button/axis names and an associated device.Definition
GamepadHelper.getInputDisplayText(inputList List, internalDeviceId ID)Arguments
| inputList | List | of input button / axis names |
| internalDeviceId | ID | of device as issued by the engine |
| float | ret | limited value |
Join Multiplayer Game Screen.
-- @field mainBox Layout box for most of the screen, used to quickly show / hide everything.
Handle clicks on table header elementsDefinition
Triggers sorting and a view update.
onClickHeader()Return Values
| table | instance | instance of object |
Handle changes in the server name filter fieldDefinition
onServerNameChanged(element TextInputElement, text New)Arguments
| element | TextInputElement | instance |
| text | New | text |
| table | baleType | baleType object |
Save the current filter settings.Definition
saveFilterSettings()Return Values
| table | brandColor | brandColor object |
Get the server info of the currently selected serverDefinition
getSelectedServer()Return Values
| table | instance | instance of object |
Assign the "Start Game" button state.Definition
Enables the button if the currently selected server is valid for playing. Disables it otherwise.
setStartButtonState()Return Values
| boolean | true | if loading was successful else false |
Filter a server with the current filter settings.Definition
filterServer(server Server)Arguments
| server | Server | information (see onServerInfo()) for data structure |
| boolean | true | if loading was successful else false |
| True | if | the server matches the current filter settings, false otherwise |
Apply server filters to server list.Definition
setTableFiltersAndSorting()Return Values
| boolean | success | success |
Update the server table.Definition
This causes a full rebuild of the table data.
rebuildServerList()Return Values
| table | instance | instance of object |
Build a DataRow from server properties to add to the server list GuiElement.Definition
buildServerDataRow()Return Values
| table | instance | instance of object |
| dataRow | instance | with server data |
List item element to be used and laid out by ListElement and TableElement.
-- Used layers: "image" for a background image.
--@category GUI
--@xmlConfig GuiElement#allowSelected bool [optional] If false, this element cannot be selected, only focused or activated.
Get the actual focus target, in case a child or parent element needs to be targeted instead.Definition
Override from BitmapElement: Only focuses children if #autoSelectChildren is true
getFocusTarget(incomingDirection (Optional), moveDirection (Optional))Arguments
| incomingDirection | (Optional) | If specified, may return different targets for different incoming directions. |
| moveDirection | (Optional) | Actual movement direction per input. This is the opposing direction of incomingDirection. |
| GuiElement | Actual | element to focus. |
Main Menu Screen.
-- @field backgroundImage Main background image
Set up notifications display and animation.Definition
setupNotifications()Return Values
| float | nearCoCRadius | Near circle of confusion radius (nearCoCRadius = 0 means no near blur (pinhole camera)) |
| float | nearBlurEnd | Distance from the camera center where near blur ends |
| float | farCoCRadius | Far circle of confusion radius (farCoCRadius = 0 means no far blur (pinhole camera)) |
| float | farBlurStart | Distance from the camera center where far blur starts |
| float | farBlurEnd | Distance from the camera center where far blur ends |
Set notification buttons disabled state.Definition
If fewer than 2 notifications are available to show, the cycle buttons will always be disabled.
setNotificationButtonsDisabled()
Reset notification data and display.Definition
resetNotifications()
Cycle through available notifications.Definition
cycleNotification(int signedDelta)Arguments
| int | signedDelta | 1 or -1 for next or previous |
Handle an activation of the notification open button or input action.Definition
onNotificationBoxClick()
Update notifications.Definition
updateNotifications()Return Values
| float | nearCoCRadius | Near circle of confusion radius (nearCoCRadius = 0 means no near blur (pinhole camera)) |
| float | nearBlurEnd | Distance from the camera center where near blur ends |
| float | farCoCRadius | Far circle of confusion radius (farCoCRadius = 0 means no far blur (pinhole camera)) |
| float | farBlurStart | Distance from the camera center where far blur starts |
| float | farBlurEnd | Distance from the camera center where far blur ends |
Update screen fading values.Definition
updateFading()
Creating data grid
@param table customMt custom metatableDefinition
new()Return Values
| table | instance | instance of object |
| 19 | function MapDataGrid:new(mapSize, blocksPerRowColumn, customMt) |
| 20 | local self = DataGrid:new(blocksPerRowColumn, blocksPerRowColumn, customMt or MapDataGrid_mt) |
| 21 | |
| 22 | self.blocksPerRowColumn = blocksPerRowColumn |
| 23 | self.mapSize = mapSize |
| 24 | self.blockSize = self.mapSize/self.blocksPerRowColumn |
| 25 | |
| 26 | return self |
| 27 | end |
@param float worldZ world position zDefinition
getValueAtWorldPos()Return Values
| table | value | value at the given position |
| 34 | function MapDataGrid:getValueAtWorldPos(worldX, worldZ) |
| 35 | local rowIndex, colIndex = self:getRowColumnFromWorldPos(worldX, worldZ) |
| 36 | return self:getValue(rowIndex, colIndex), rowIndex, colIndex |
| 37 | end |
@param float worldZ world position zDefinition
setValueAtWorldPos(table value)Arguments
| table | value | value at the given position |
| 44 | function MapDataGrid:setValueAtWorldPos(worldX, worldZ, value) |
| 45 | local rowIndex, colIndex = self:getRowColumnFromWorldPos(worldX, worldZ) |
| 46 | self:setValue(rowIndex, colIndex, value) |
| 47 | end |
@param float worldZ world position zDefinition
getRowColumnFromWorldPos()Return Values
| integer | row | row |
| integer | column | column |
| 55 | function MapDataGrid:getRowColumnFromWorldPos(worldX, worldZ) |
| 56 | local mapSize = self.mapSize |
| 57 | local blocksPerRowColumn = self.blocksPerRowColumn |
| 58 | |
| 59 | local x = (worldX + mapSize*0.5) / mapSize |
| 60 | local z = (worldZ + mapSize*0.5) / mapSize |
| 61 | |
| 62 | local row = MathUtil.clamp(math.ceil(blocksPerRowColumn*z), 1, blocksPerRowColumn) |
| 63 | local column = MathUtil.clamp(math.ceil(blocksPerRowColumn*x), 1, blocksPerRowColumn) |
| 64 | |
| 65 | -- log(worldX, worldZ, " -> ", (worldX + self.mapSize*0.5), (worldZ + self.mapSize*0.5), z, x, row, column) |
| 66 | |
| 67 | return row, column |
| 68 | end |
UV coordinates in the map hotspot atlas.
Set the text offset using a raw value string in the form of "<X>px <Y>px".Definition
setRawTextOffset()Return Values
| boolean | true | if loading was successful else false |
Set the farm this hotspot should be shown for. Use AccessHandler.EVERYONE for showing it to everybody (default)Definition
setOwnerFarmId()Return Values
| bool | is | true if available Pallet has been assigned with a vehicle object |
Map overlay generator.
-- Provides density map based data overlays on top of an in-game map.
Create a MapOverlayGenerator instance.Definition
new(table l10n, table fruitTypeManager, table fillTypeManager, table farmlandManager, table farmManager)Arguments
| table | l10n | I18N reference for text localization |
| table | fruitTypeManager | FruitTypeManager reference for fruit type resolution |
| table | fillTypeManager | FillTypeManager reference for fruit fill type resolution |
| table | farmlandManager | FarmlandManager reference for farm land data access |
| table | farmManager | FarmManager reference for farm land ownership data access |
Delete this instance and any used overlays.Definition
delete()
Set the valid fruit types of the current mission.Definition
setMissionFruitTypes()
Set the color blind mode for overlay creation.Definition
setColorBlindMode()
Build the map overlay for fruit types.Definition
buildFruitTypeMapOverlay()
Build the map overlay for growth states.Definition
buildGrowthStateMapOverlay()
Build the map overlay for soil states.Definition
buildSoilStateMapOverlay()
Build the map overlay for farm lands.Definition
buildFarmlandsMapOverlay()
Generate a map overlay of a given type.Definition
This is an internal generic interfacing method and should not be called externally. Consumers should use one of the
specific public methods instead, e.g. MapOverlayGenerator:generateFruitTypeOverlay().
generateOverlay(int mapOverlayType, function finishedCallback, table overlayState)Arguments
| int | mapOverlayType | Overlay type as one of MapOverlayGenerator.OVERLAY_TYPE.[CROPS|GROWTH|SOIL|FARMLANDS] |
| function | finishedCallback | Function which is called with the overlay ID as its argument when processing is finished, signature: function(overlayId) |
| table | overlayState | Overlay state data which defines parameters (e.g. colors) of the map overlays. |
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Generate a fruit type overlay.Definition
generateFruitTypeOverlay(function finishedCallback, table fruitTypeFilter)Arguments
| function | finishedCallback | Called when generation is finished, signature: function(overlayId) |
| table | fruitTypeFilter | Map of fruit type indices to booleans. If the value is true, the fruit type will be displayed. |
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Generate a growth state overlay.Definition
generateGrowthStateOverlay(function finishedCallback, table fruitTypeFilter)Arguments
| function | finishedCallback | Called when generation is finished, signature: function(overlayId) |
| table | fruitTypeFilter | Map of growth state indices (MapOverlayGenerator.GROWTH_STATE_INDEX members) to booleans. If the value is true, the growth state will be displayed. |
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Generate a soil state overlay.Definition
generateSoilStateOverlay(function finishedCallback, table fruitTypeFilter)Arguments
| function | finishedCallback | Called when generation is finished, signature: function(overlayId) |
| table | fruitTypeFilter | Map of soil state indices (MapOverlayGenerator.SOIL_STATE_INDEX members) to booleans. If the value is true, the soil state will be displayed. |
| bool | True | if overlay generation has started, false if generation is already in progress or an invalid overlay type has been provided |
Generate a farm land overlay.Definition
generateFarmlandOverlay(function finishedCallback, table mapPosition)Arguments
| function | finishedCallback | Called when generation is finished, signature: function(overlayId) |
| table | mapPosition | Map position vector of a selection position. If the position is within a farm land, it will be highlighted. |
Check if any overlay is currently being generated and triggers a callback when it's finished.Definition
checkOverlayFinished()
Reset overlay generation state.Definition
reset()
Update the state each frame.Definition
Checks the overlay generation state.
update()
Get display information for crop types.Definition
Override to add new crop types or change information.
getDisplayCropTypes()Return Values
| array | of | display information, {i={colors={true=[{r,g,b,a} colorblind], false=[{r,g,b,a} default], iconFilename=path, iconUVs={u1, v1, u2, v2, u3, v3, u4, v4}, description=text, fruitTypeIndex=index}} |
| 373 | function MapOverlayGenerator:getDisplayCropTypes() |
| 374 | local cropTypes = {} |
| 375 | -- load crop type icon definitions in order of map configuration |
| 376 | for i, fruitType in ipairs(self.missionFruitTypes) do |
| 377 | if fruitType.shownOnMap and fruitType.fruitTypeIndex ~= FruitType.WEED then -- weed is known as a fruit type, but is handled as a soil state |
| 378 | local fillableIndex = self.fruitTypeManager:getFillTypeIndexByFruitTypeIndex(fruitType.fruitTypeIndex) |
| 379 | local fillable = self.fillTypeManager:getFillTypeByIndex(fillableIndex) |
| 380 | |
| 381 | local iconFilename = fillable.hudOverlayFilenameSmall |
| 382 | local iconUVs = Overlay.DEFAULT_UVS -- default crop type icons are separate files, use full texture |
| 383 | local description = fillable.title |
| 384 | |
| 385 | table.insert(cropTypes, { |
| 386 | colors = {[false] = fruitType.defaultColor, [true] = fruitType.colorBlindColor}, |
| 387 | iconFilename = iconFilename, |
| 388 | iconUVs = iconUVs, |
| 389 | description = description, |
| 390 | fruitTypeIndex = fruitType.fruitTypeIndex, |
| 391 | foliageId = fruitType.foliageId |
| 392 | }) |
| 393 | end |
| 394 | end |
| 395 | |
| 396 | return cropTypes |
| 397 | end |
Get display information for growth states.Definition
Growth states can be represented in multiple colors per state, so colors are defined in arrays per color blind mode.
Override to add new growth states or change information.
getDisplayGrowthStates()Return Values
| array | of | display information, {i={colors={true={i={r,g,b,a}}, false={i={r,g,b,a}}}, description=text}} |
| 404 | function MapOverlayGenerator:getDisplayGrowthStates() |
| 405 | return { |
| 406 | -- indices are contiguous, so this definition is a valid array: |
| 407 | [MapOverlayGenerator.GROWTH_STATE_INDEX.CULTIVATED] = { |
| 408 | colors = { |
| 409 | [true] = {MapOverlayGenerator.FRUIT_COLOR_CULTIVATED[true]}, |
| 410 | [false] = {MapOverlayGenerator.FRUIT_COLOR_CULTIVATED[false]} |
| 411 | }, |
| 412 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.GROWTH_MAP_CULTIVATED) |
| 413 | }, |
| 414 | [MapOverlayGenerator.GROWTH_STATE_INDEX.GROWING] = { |
| 415 | colors = MapOverlayGenerator.FRUIT_COLORS_GROWING, |
| 416 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.GROWTH_MAP_GROWING) |
| 417 | }, |
| 418 | [MapOverlayGenerator.GROWTH_STATE_INDEX.HARVEST] = { |
| 419 | colors = MapOverlayGenerator.FRUIT_COLORS_HARVEST, |
| 420 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.GROWTH_MAP_HARVEST) |
| 421 | }, |
| 422 | [MapOverlayGenerator.GROWTH_STATE_INDEX.HARVESTED] = { |
| 423 | colors = { |
| 424 | [true] = {MapOverlayGenerator.FRUIT_COLOR_CUT}, |
| 425 | [false] = {MapOverlayGenerator.FRUIT_COLOR_CUT} |
| 426 | }, |
| 427 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.GROWTH_MAP_HARVESTED) |
| 428 | }, |
| 429 | [MapOverlayGenerator.GROWTH_STATE_INDEX.PLOWED] = { |
| 430 | colors = { |
| 431 | [true] = {MapOverlayGenerator.FRUIT_COLOR_PLOWED[true]}, |
| 432 | [false] = {MapOverlayGenerator.FRUIT_COLOR_PLOWED[false]} |
| 433 | }, |
| 434 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.GROWTH_MAP_PLOWED) |
| 435 | }, |
| 436 | [MapOverlayGenerator.GROWTH_STATE_INDEX.TOPPING] = { |
| 437 | colors = { |
| 438 | [true] = {MapOverlayGenerator.FRUIT_COLOR_REMOVE_TOPS[true]}, |
| 439 | [false] = {MapOverlayGenerator.FRUIT_COLOR_REMOVE_TOPS[false]} |
| 440 | }, |
| 441 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.GROWTH_MAP_TOPPING) |
| 442 | }, |
| 443 | [MapOverlayGenerator.GROWTH_STATE_INDEX.WITHERED] = { |
| 444 | colors = { |
| 445 | [true] = {MapOverlayGenerator.FRUIT_COLOR_WITHERED[true]}, |
| 446 | [false] = {MapOverlayGenerator.FRUIT_COLOR_WITHERED[false]} |
| 447 | }, |
| 448 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.GROWTH_MAP_WITHERED) |
| 449 | } |
| 450 | } |
| 451 | end |
Get display information for soil states.Definition
Soil states can be represented in multiple colors per state, so colors are defined in arrays per color blind mode.
Override to add new soil states or change information.
getDisplaySoilStates()Return Values
| array | of | display information, {i={colors={true={i={r,g,b,a}}, false={i={r,g,b,a}}}, description=text}} |
| 458 | function MapOverlayGenerator:getDisplaySoilStates() |
| 459 | local weedType = self.fruitTypeManager:getWeedFruitType() |
| 460 | local fillableIndex = self.fruitTypeManager:getFillTypeIndexByFruitTypeIndex(FruitType.WEED) |
| 461 | local weedFillable = self.fillTypeManager:getFillTypeByIndex(fillableIndex) |
| 462 | |
| 463 | return { |
| 464 | [MapOverlayGenerator.SOIL_STATE_INDEX.WEEDS] = { |
| 465 | colors = { |
| 466 | [true] = {weedType.colorBlindMapColor}, |
| 467 | [false] = {weedType.defaultMapColor} |
| 468 | }, |
| 469 | description = weedFillable.title |
| 470 | }, |
| 471 | [MapOverlayGenerator.SOIL_STATE_INDEX.FERTILIZED] = { |
| 472 | colors = MapOverlayGenerator.FRUIT_COLORS_FERTILIZED, |
| 473 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.SOIL_MAP_FERTILIZED) |
| 474 | }, |
| 475 | [MapOverlayGenerator.SOIL_STATE_INDEX.NEEDS_PLOWING] = { |
| 476 | colors = { |
| 477 | [true] = {MapOverlayGenerator.FRUIT_COLOR_NEEDS_PLOWING[true]}, |
| 478 | [false] = {MapOverlayGenerator.FRUIT_COLOR_NEEDS_PLOWING[false]} |
| 479 | }, |
| 480 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.SOIL_MAP_NEED_PLOWING) |
| 481 | }, |
| 482 | [MapOverlayGenerator.SOIL_STATE_INDEX.NEEDS_LIME] = { |
| 483 | colors = { |
| 484 | [true] = {MapOverlayGenerator.FRUIT_COLOR_NEEDS_LIME[true]}, |
| 485 | [false] = {MapOverlayGenerator.FRUIT_COLOR_NEEDS_LIME[false]} |
| 486 | }, |
| 487 | description = self.l10n:getText(MapOverlayGenerator.L10N_SYMBOL.SOIL_MAP_NEED_LIME) |
| 488 | } |
| 489 | } |
| 490 | end |
Map Selection Screen.
-- Used when starting a new game.
-- @field mapSelector Map selection option at top of view
Creation event for map preview BitmapElement instances.Definition
This gets called during onOpen when map data is iterated and added, so we can set the image state according to the
currently processed map.
onCreateMapImage(element BitmapElement)Arguments
| element | BitmapElement | instance which shows a map preview image |
Message center message types.
Use these identifiers to subscribe to and publish messages.
Subscribe the target to given message type.Definition
subscribe(integer messageType, function callback, table callbackTarget, any argument)Arguments
| integer | messageType | Type of message |
| function | callback | Callback function |
| table | callbackTarget | Optional target |
| any | argument | Optional argument, will be last in callback argument list |
| table | implement | implement |
Unsubscribe the observer from messageDefinition
This is relatively slow, do not use in :update or :draw
unsubscribe(integer messageType, table callbackTarget)Arguments
| integer | messageType | Type of message |
| table | callbackTarget | Observer object |
| boolean | success | success |
Unsubscribe the observer from all messagesDefinition
unsubscribeAll(table callbackTarget)Arguments
| table | callbackTarget | Observer object |
| boolean | supportsHardAttach | attacher joint supports hard attach |
Publish a message with given type and possible argumentsDefinition
publish(integer messageType, table arguments)Arguments
| integer | messageType | Type of the message, used with the subscriptions |
| table | arguments | Optional arguments passed to function |
| boolean | success | success |
Publish a message with given type and possible arguments. This message is handled in the next frame.Definition
Useful for within networking code
publishDelayed(integer messageType, table arguments)Arguments
| integer | messageType | Type of the message, used with the subscriptions |
| table | arguments | Optional arguments passed to function |
| float | totalMass | total mass |
Mission class collaborator collection for initialization.
-- Serves as an explicitly defined value-object for Mission class constructors to pass in required collaborator
references without needing to change the constructor signature for each new collaborator class.
Create a new MissionCollaborators instance.Definition
The constructor declares fields for reference which are used by the Mission class constructors. Stick to only
assigning to these fields when using this class or add a suitable declaration below when more references are needed.
new()Return Values
| float | total | amount of consumed pto torque in kNm |
Dismiss a (finished or cancelled) mission. Deletes it completely. Calls dismiss on the mission to handle money exchange to farm.
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 31 | function MissionManager:new(customMt) |
| 32 | local self = AbstractManager:new(customMt or MissionManager_mt) |
| 33 | |
| 34 | self.missionTypes = {} |
| 35 | self.missionTypeIdToType = {} |
| 36 | |
| 37 | self.defaultMissionMapWidth = 512 |
| 38 | self.defaultMissionMapHeight = 512 |
| 39 | self.missionMapNumChannels = 4 |
| 40 | |
| 41 | -- Only on new game start. Reset is done when quitting a game. |
| 42 | -- This value is changed very early in the loading process |
| 43 | self.numTransportTriggers = 0 |
| 44 | self.transportTriggers = {} |
| 45 | |
| 46 | return self |
| 47 | end |
Load data on map loadDefinition
loadMapData()Return Values
| boolean | true | if loading was successful else false |
| 74 | function MissionManager:loadMapData(xmlFile) |
| 75 | MissionManager:superClass().loadMapData(self) |
| 76 | |
| 77 | self:createMissionMap() |
| 78 | |
| 79 | if g_currentMission:getIsServer() then |
| 80 | g_currentMission:addUpdateable(self) |
| 81 | |
| 82 | self.missionNextGenerationTime = g_currentMission.time + MissionManager.MISSION_GENERATION_INTERVAL |
| 83 | |
| 84 | self.fieldDataDmod = DensityMapModifier:new(g_currentMission.terrainDetailId, g_currentMission.sprayFirstChannel, g_currentMission.sprayNumChannels) |
| 85 | self.fieldDataFilter = DensityMapFilter:new(g_currentMission.terrainDetailId, g_currentMission.terrainDetailTypeFirstChannel, g_currentMission.terrainDetailTypeNumChannels) |
| 86 | self.fieldDataFilter:setValueCompareParams("greater", 0) |
| 87 | end |
| 88 | |
| 89 | local p = getXMLString(xmlFile, "map.transportMissions#filename") |
| 90 | if p ~= nil then |
| 91 | local path = Utils.getFilename(p, g_currentMission.baseDirectory) |
| 92 | if path ~= nil and path ~= "" then |
| 93 | self:loadTransportMissions(path) |
| 94 | end |
| 95 | end |
| 96 | |
| 97 | local p = getXMLString(xmlFile, "map.missionVehicles#filename") |
| 98 | if p ~= nil then |
| 99 | local path = Utils.getFilename(p, g_currentMission.baseDirectory) |
| 100 | if path ~= nil then |
| 101 | self:loadMissionVehicles(path) |
| 102 | end |
| 103 | end |
| 104 | |
| 105 | if g_currentMission:getIsServer() then |
| 106 | -- Generate a weighted list of transport missions |
| 107 | for _, missionType in ipairs(self.missionTypes) do |
| 108 | if missionType.category == MissionManager.CATEGORY_TRANSPORT then |
| 109 | for i = 1, missionType.priority do |
| 110 | table.insert(self.possibleTransportMissionsWeighted, missionType) |
| 111 | end |
| 112 | end |
| 113 | end |
| 114 | end |
| 115 | |
| 116 | g_messageCenter:subscribe(MessageType.MISSION_DELETED, self.onMissionDeleted, self) |
| 117 | |
| 118 | if g_addTestCommands then |
| 119 | addConsoleCommand("gsGenerateFieldMission", "Force generating a new mission for given field", "consoleGenerateFieldMission", self) |
| 120 | addConsoleCommand("gsMissionLoadAllVehicles", "Loading and unloading all field mission vehicles", "consoleLoadAllFieldMissionVehicles", self) |
| 121 | |
| 122 | addConsoleCommand("gsMissionHarvestField", "Harvest a field and print the liters", "consoleHarvestField", self) |
| 123 | end |
| 124 | end |
Load map-configured transport missionsDefinition
loadTransportMissions()
Load vehicles for use with field missionsDefinition
loadMissionVehicles()
Unload data on mission deleteDefinition
unloadMapData()Code
| 349 | function MissionManager:unloadMapData() |
| 350 | g_messageCenter:unsubscribeAll() |
| 351 | g_currentMission:removeUpdateable(self) |
| 352 | |
| 353 | self.numTransportTriggers = 0 |
| 354 | self.transportTriggers = {} |
| 355 | |
| 356 | self.fieldDataDmod = nil |
| 357 | self.fieldDataFilter = nil |
| 358 | |
| 359 | self:destroyMissionMap() |
| 360 | |
| 361 | if g_addTestCommands then |
| 362 | removeConsoleCommand("gsGenerateFieldMission") |
| 363 | removeConsoleCommand("gsMissionLoadAllVehicles") |
| 364 | removeConsoleCommand("gsMissionHarvestField") |
| 365 | end |
| 366 | |
| 367 | MissionManager:superClass().unloadMapData(self) |
| 368 | end |
Write field mission data to savegame fileDefinition
saveToXMLFile(string xmlFilename)Arguments
| string | xmlFilename | file path |
| boolean | true | if loading was successful else false |
| 374 | function MissionManager:saveToXMLFile(xmlFilename) |
| 375 | local xmlFile = createXMLFile("missionXML", xmlFilename, "missions") |
| 376 | |
| 377 | if xmlFile ~= nil then |
| 378 | for k, mission in ipairs(self.missions) do |
| 379 | local missionKey = string.format("missions.mission(%d)", k - 1) |
| 380 | |
| 381 | setXMLString(xmlFile, missionKey.."#type", mission.type.name) |
| 382 | if mission.activeMissionId ~= nil then |
| 383 | setXMLInt(xmlFile, missionKey.."#activeId", mission.activeMissionId) |
| 384 | end |
| 385 | |
| 386 | mission:saveToXMLFile(xmlFile, missionKey) |
| 387 | end |
| 388 | |
| 389 | saveXMLFile(xmlFile) |
| 390 | delete(xmlFile) |
| 391 | end |
| 392 | |
| 393 | return false |
| 394 | end |
Load fieldjob data from xml savegame fileDefinition
loadFromXMLFile(string filename)Arguments
| string | filename | xml filename |
| 399 | function MissionManager:loadFromXMLFile(xmlFilename) |
| 400 | if xmlFilename == nil then |
| 401 | return false |
| 402 | end |
| 403 | |
| 404 | local xmlFile = loadXMLFile("missionsXML", xmlFilename) |
| 405 | if not xmlFile then |
| 406 | return false |
| 407 | end |
| 408 | |
| 409 | -- Active missions |
| 410 | local i = 0 |
| 411 | while true do |
| 412 | local key = string.format("missions.mission(%d)", i) |
| 413 | if not hasXMLProperty(xmlFile, key) then |
| 414 | break |
| 415 | end |
| 416 | |
| 417 | local missionTypeName = getXMLString(xmlFile, key.."#type") |
| 418 | local missionType = self:getMissionType(missionTypeName) |
| 419 | |
| 420 | if missionType ~= nil then |
| 421 | local mission = missionType.class:new(true, g_client ~= nil) |
| 422 | mission.type = missionType |
| 423 | mission.activeMissionId = getXMLInt(xmlFile, key .. "#activeId") -- can be nil |
| 424 | self:assignGenerationTime(mission) |
| 425 | |
| 426 | if not mission:loadFromXMLFile(xmlFile, key) then |
| 427 | mission:delete() |
| 428 | else |
| 429 | if mission.field ~= nil then |
| 430 | self.fieldToMission[mission.field.fieldId] = mission |
| 431 | end |
| 432 | |
| 433 | if mission.type.category == MissionManager.CATEGORY_TRANSPORT then |
| 434 | self.numTransportMissions = self.numTransportMissions + 1 |
| 435 | end |
| 436 | |
| 437 | mission:register() |
| 438 | table.insert(self.missions, mission) |
| 439 | end |
| 440 | else |
| 441 | print("Warning: Mission type '" .. tostring(missionType) .. "' not found!") |
| 442 | end |
| 443 | |
| 444 | i = i + 1 |
| 445 | end |
| 446 | |
| 447 | -- If there are any active missions, find any associated vehicles |
| 448 | if table.getn(self.missions) > 0 then |
| 449 | for _, vehicle in pairs(g_currentMission.vehicles) do |
| 450 | if vehicle.activeMissionId ~= nil then |
| 451 | local mission = self:getMissionForActiveMissionId(vehicle.activeMissionId) |
| 452 | if mission ~= nil and mission.vehicles ~= nil then |
| 453 | table.insert(mission.vehicles, vehicle) |
| 454 | end |
| 455 | end |
| 456 | end |
| 457 | end |
| 458 | |
| 459 | delete(xmlFile) |
| 460 | |
| 461 | return true |
| 462 | end |
Deletes field mission managerDefinition
delete()Code
| 466 | function MissionManager:delete() |
| 467 | end |
Updates field mission ownage data from xml savegame fileDefinition
update(string filename)Arguments
| string | filename | xml filename |
| 472 | function MissionManager:update(dt) |
| 473 | if g_currentMission:getIsServer() then |
| 474 | self.generationTimer = self.generationTimer - g_currentMission.missionInfo.timeScale * dt |
| 475 | |
| 476 | self:updateMissions(dt) |
| 477 | |
| 478 | if table.getn(self.missions) < MissionManager.MAX_MISSIONS and self.generationTimer < g_time then |
| 479 | self:generateMissions(dt) |
| 480 | |
| 481 | -- Limit generation |
| 482 | self.generationTimer = MissionManager.MISSION_GENERATION_INTERVAL |
| 483 | end |
| 484 | end |
| 485 | end |
Get mission configuration given the nameDefinition
getTransportMissionConfig()
Get a mission config given an ID. Used by TransportMission:readStreamDefinition
getTransportMissionConfigById()
Get whether given farm has an active missionDefinition
hasFarmActiveMission()
Start given mission for a farm.Definition
startMission()
Cancel mission: sets it to finished without successDefinition
cancelMission()
Delete a missionDefinition
deleteMission()
On a client it sends an event insteadDefinition
dismissMission()
Get a list of active missionsDefinition
getActiveMissions()
Get whether any mission is currently runningDefinition
getIsAnyMissionActive()
Test whether the given mission is still able to runDefinition
canMissionStillRun()
Add a new transport trigger. Requires triggerId and index properties.Definition
addTransportMissionTrigger()
Remove a transport trigger.Definition
removeTransportMissionTrigger()
Generate a mission for given field. Returns nil if field already has an active missionDefinition
generateNewFieldMission()
Generation time is used for reliably sortingDefinition
assignGenerationTime()
Get a randomly chosen group of vehicles fitting for given mission type and field sizeDefinition
getRandomVehicleGroup(missionType type, fieldSize size)Arguments
| missionType | type | of mission (string) |
| fieldSize | size | of field: 'SMALL', 'MEDIUM', 'LARGE' |
| List | of | vehicles. Each element is a table with filanema and configuration properties. |
| Reward | ||
Used on the client. Make sure it never crashes on nil (patches)Definition
getVehicleGroupFromIdentifier()
Get a free activeMissionId, used for active missions only.Definition
getFreeActiveMissionId()
Validate missions on given field, after something happened to the field (event)Definition
validateMissionOnField()
Get a value at given world coordinatesDefinition
getMissionMapValue()
Get the mission associated with the activeMissionIdDefinition
getMissionForActiveMissionId()
Custom HUD drawing extension for MixerWagon.
-- Displays the fill levels of the mixer wagon.
--@category GUI
Create a new instance of MixerWagonHUDExtension.Definition
new(table vehicle, float uiScale, table uiTextColor, float uiTextSize)Arguments
| table | vehicle | Vehicle which has the specialization required by a sub-class |
| float | uiScale | Current UI scale |
| table | uiTextColor | HUD text drawing color as an RGBA array |
| float | uiTextSize | HUD text size |
| table | instance | instance of object |
Determine if the HUD extension should be drawn.Definition
canDraw()Return Values
| boolean | true | if loading was successful else false |
Get this HUD extension's display height.Definition
getDisplayHeight()Return Values
| table | instance | instance of object |
| float | Display | height in screen space |
Draw mixing ratio information for a mixing wagon when it is active.Definition
draw(float leftPosX, float rightPosX, float posY)Arguments
| float | leftPosX | Left input help panel column start position |
| float | rightPosX | Right input help panel column start position |
| float | posY | Current input help panel drawing vertical offset |
| boolean | true | if loading was successful else false |
| float | Modified | input help panel drawing vertical offset |
ModHub categories frame
-- Displays categories.
--@category GUI
Create a new ModHubCategoriesFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
Initialize with categories to be displayed.Definition
Categories must be provided as an array of tables like {id=<id>, iconFilename=<path>, label=<text>}. This will add
a category element per entry to the display list.
initialize(table categories, function categoryClickedCallback, table headerIconUVs, string headerText)Arguments
| table | categories | Category definitions array |
| function | categoryClickedCallback | Notification callback for category activation(click/enter), signature: function(selectedId, baseCategoryIconUVs, baseCategoryLabel, clickedCategoryLabel) |
| table | headerIconUVs | UV coordinates of the header icon to display |
| string | headerText | Header text to display |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| float | nearCoCRadius | Near circle of confusion radius (nearCoCRadius = 0 means no near blur (pinhole camera)) |
| float | nearBlurEnd | Distance from the camera center where near blur ends |
| float | farCoCRadius | Far circle of confusion radius (farCoCRadius = 0 means no far blur (pinhole camera)) |
| float | farBlurStart | Distance from the camera center where far blur starts |
| float | farBlurEnd | Distance from the camera center where far blur ends |
Get the frame's main content element's screen position.Definition
getMainElementPosition()
Update scroll button visibility based on the currently visible list items.Definition
updateScrollButtons()
Handle a click / button activation on a category.Definition
onClickCategory()
Handle a double click on a category.Definition
onDoubleClickCategory()
Handle navigation selection of a category element.Definition
onCategorySelected()Return Values
| boolean | isAllowed | state change is allowed |
Handle click on left navigation button.Definition
onClickLeft()Return Values
| table | instance | instance of object |
Handle click on right navigation button.Definition
onClickRight()Return Values
| boolean | true | if loading was successful else false |
Handle a list scrolling event.Definition
onScroll()Return Values
| boolean | success | success |
Shop items frame for the in-game menu shop.
-- Displays purchasable items of a common category in a horizontal list layout.
--@category GUI
Create a new ModHubDetailsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| integer | fillTypeIndex | the fillType index |
Set the category to display.Definition
setCategory()Return Values
| table | fillType | the fillType object |
Set an ordered array of ShopDisplayItem instances to display in this frame.Definition
setModInfo()Return Values
| table | fillTypes | list of fillTypes |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | fillTypeCategory | fillType category object |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | success | true if added else false |
Shop items frame for the in-game menu shop.
-- Displays purchasable items of a common category in a horizontal list layout.
--@category GUI
Create a new ModHubItemsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| table | fillTypes | list of fillTypes |
Set the callback to use when an item is activated (for buying or selling).Definition
setItemClickCallback()Return Values
| table | fillTypes | fill types |
Set the callback to use when an item is selected in the view.Definition
setItemSelectCallback()Return Values
| integer | converterIndex | index of converterIndex |
Set the category to display.Definition
setCategory()Return Values
| table | converterData | converter data |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | sample | sample |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | instance | instance of object |
Update scroll button visibility based on the currently visible list items.Definition
updateScrollButtons()Return Values
| boolean | true | if loading was successful else false |
Handle a click / button activation on an item.Definition
onClickItem()Return Values
| boolean | success | success |
Handle a double-click on an item.Definition
onDoubleClickItem()Return Values
| table | fruitType | fruitType type object |
Handle click on left navigation button.Definition
onClickLeft()Return Values
| table | fruit | the fruit object |
Handle click on right navigation button.Definition
onClickRight()Return Values
| table | fruit | the fruit object |
Handle selection of an item.Definition
onItemSelected()Return Values
| table | fruitTypes | a list of fruitTypes |
Mode
Create a new instance of ModHubScreen.Definition
new(table target, table custom_mt, table messageCenter, table l10n, table inputManager, table modHubController, bool isConsoleVersion)Arguments
| table | target | Callback target |
| table | custom_mt | Sub-class meta table |
| table | messageCenter | MessageCenter reference |
| table | l10n | I18N reference for text localization |
| table | inputManager | InputBinding reference |
| table | modHubController | modHubController reference |
| bool | isConsoleVersion | If true, the game is running on a console |
| table | fruitTypeCategory | fruitType category object |
Reset menu state (and all pages).Definition
reset()Return Values
| table | success | true if added else false |
Button function for backing out of the menu.Definition
clickBackCallback()Return Values
| table | fruitTypes | list of fruitTypes |
Clear menu button actions, events and callbacks.Definition
clearMenuButtonActions()Return Values
| table | fruitTypes | fruit types |
Assign menu button information to the in-game menu buttons.Definition
assignMenuButtonInfo()Return Values
| table | fillTypes | fill types |
Handle a menu action click by calling one of the menu button callbacks.Definition
onMenuActionClick()Return Values
| table | fillTypes | fill types |
| bool | True | if no callback was present and no action was taken, false otherwise |
Handle menu confirmation input event.Definition
onClickOk()Return Values
| float | literPerSqm | liter per sqm |
Handle menu back input event.Definition
onClickBack()Return Values
| integer | converterIndex | index of converterIndex |
Handle menu cancel input event.Definition
Bound to quite the game to the main menu.
onClickCancel()Return Values
| table | converterData | converter data |
Handle menu active input event.Definition
Bound to save the game.
onClickActivate()Return Values
| table | instance | instance of object |
Update the buttons panel when a given page is visible.Definition
updateButtonsPanel()Return Values
| boolean | true | if loading was successful else false |
Make a callback which encloses the self reference and handles arbitrary arguments afterwards.Definition
makeSelfCallback()Return Values
| table | gameplayHintGroup | a random gameplay hint group |
Multiple choice text element.
-- This element requires a specific configuration setup to be properly used: In the configuration, it must contain the
following child elements in this order: 1. ButtonElement, 2. ButtonElement, 3. TextElement, 4. TextElement. The first
three elements are mandatory, as they are the buttons to change this element's value and the label which displays the
value. The fourth (text) element is optional and used as a header label for this element if defined.
--@category GUI
--@xmlConfig GuiElement#wrap bool [optional] If false, values will not cycle when the end or start of the value list is reached.
Disable automatic playing of sound samples in child buttons.Definition
disableButtonSounds()
Trigger a "left" input.Definition
inputLeft(isShoulderButton If)Arguments
| isShoulderButton | If | true, assume a shoulder button input (PC or console) |
Trigger a "right" input.Definition
inputRight(isShoulderButton If)Arguments
| isShoulderButton | If | true, assume a shoulder button input (PC or console) |
Tween class which handles multiple values at the same time.
-- Start and end values must be passed in as arrays. The setter function must be able to handle as many arguments as
there were entries in the start and end values arrays: setter called as function(unpack(values)).
--@category GUI
Create a new Tween.Definition
new(table subClass, function setterFunction, table startValues, table endValues, float duration)Arguments
| table | subClass | Subclass metatable for inheritance |
| function | setterFunction | Values setter function. Signature: callback(v1, ..., vn) or callback(target, v1, ..., vn). |
| table | startValues | Original values |
| table | endValues | Target values |
| float | duration | Duration of tween in milliseconds |
Set a callback target for this tween.Definition
If a target has been set, the setter function must support receiving the target as its first argument.
setTarget()
Get the current tween value.Definition
tweenValue()
Apply a value via the setter function.Definition
applyValue()
Image display overlay.
-- This class is used to display textures or usually parts thereof as rectangular panels in the UI or on the HUD.
Example usages include button icons, showing images in the main menu or drawing the in-game map.
--@category GUI
Create a new Overlay.Definition
new(overlayFilename File, x Screen, y Screen, width Display, height Display)Arguments
| overlayFilename | File | path of the source texture |
| x | Screen | position x |
| y | Screen | position y |
| width | Display | width |
| height | Display | height |
Delete this overlay.Definition
Releases the texture file handle.
delete()
Set this overlay's color.Definition
The color is multiplied with the texture color. For no modification of the texture color, use full opaque white,
i.e. {1, 1, 1, 1}.
setColor(r Red, g Green, b Blue, a Alpha)Arguments
| r | Red | channel |
| g | Green | channel |
| b | Blue | channel |
| a | Alpha | channel |
Set this overlay's UVs which define the area to be displayed within the target texture.Definition
setUVs(uvs UV)Arguments
| uvs | UV | coordinates in the form of {u1, v1, u2, v2, u3, v3, u4, v4} |
Set this overlay's position.Definition
setPosition()
Get this overlay's position.Definition
getPosition()Return Values
| float | X | position in screen space |
| float | Y | position in screen space |
Set this overlay's width and height.Definition
Either value can be omitted (== nil) for no change.
setDimension()
Reset width, height and scale to initial values set in the constructor.Definition
resetDimensions()
Set horizontal flipping state.Definition
setInvertX(invertX If)Arguments
| invertX | If | true, will set the overlay to display its image flipped horizontally |
Set this overlay's rotation.Definition
setRotation(rotation Rotation, centerX Rotation, centerY Rotation)Arguments
| rotation | Rotation | in radians |
| centerX | Rotation | pivot X position offset from overlay position in screen space |
| centerY | Rotation | pivot Y position offset from overlay position in screen space |
Set this overlay's scale.Definition
Multiplies the scale values with the initial dimensions and sets those as the current dimensions.
setScale(float scaleWidth, float scaleHeight)Arguments
| float | scaleWidth | Width scale factor |
| float | scaleHeight | Height scale factor |
Get this overlay's scale values.Definition
getScale()Return Values
| float | Width | scale factor |
| float | Height | scale factor |
Render this overlay.Definition
render()
Set this overlay's alignment.Definition
setAlignment(vertical Vertical, horizontal Horizontal)Arguments
| vertical | Vertical | alignment value, one of Overlay.ALIGN_VERTICAL_[...] |
| horizontal | Horizontal | alignment value, one of Overlay.ALIGN_HORIZONTAL_[...] |
Set this overlay's visibility.Definition
setIsVisible()
Set a different image for this overlay.Definition
The previously targeted image's handle will be released.
setImage(overlayFilename File)Arguments
| overlayFilename | File | path to new target image |
Paging control element.
-- Organizes grouped elements into pages to be displayed one at a time. To set it up, one defines several
same-sized container elements (e.g. bare GuiElement) as children of the PagingElement to hold the pages' contents.
The pages should be given #name properties which are resolved to a localization text with a prepended "ui_" prefix.
On loading, any named child element of this PagingElement will be added as a page.
--@category GUI
--@xmlConfig GuiElement#onPageChange callback [optional] onPageChangeCallback(pageIndex, pageMappingIndex, element) Called when the page changes. Receives the current index in all pages, the current index in all enabled pages and this element.
Set the current page index.Definition
Indices are based on mapped pages, which are all available and visible pages added to this element.
setPage(pageMappingIndex Index)Arguments
| pageMappingIndex | Index | of page in page mappings |
| bool | True | if the page changed |
Get a new page ID based on an internal counter.Definition
getNextID()
Add a new page.Definition
addPage(id Page, element Page, title Page, index [optional])Arguments
| id | Page | ID |
| element | Page | container GuiElement |
| title | Page | title for displaying |
| index | [optional] | Insertion index for page. If undefined or outside of valid range, will add the page at the end. |
Get the number of currently visible pages.Definition
getVisiblePagesCount()
Get a page ID by the page container element reference.Definition
getPageIdByElement(element Page)Arguments
| element | Page | container GuiElement instance |
| Page |
Get a page container element by page index.Definition
getPageElementByIndex(pageIndex Index)Arguments
| pageIndex | Index | of page in all (incl. disabled and invisible) pages |
| Page | container | GuiElement instance |
Get the page index in this element's page array for a given element reference.Definition
getPageIndexByElement(table element)Arguments
| table | element | GuiElement instance |
| int | Page | index |
Get the page index in this elements page mapping array (visible pages) for a given element reference.Definition
getPageMappingIndexByElement(table element)Arguments
| table | element | GuiElement instance |
| int | Page | mapping index |
Remove a page, identified by its page container element, from this element.Definition
The method only removes the page from the display collection. Callers must take care of the page element's clean-up,
e.g. removing it from the element hierarchy.
removePageByElement(pageElement Page)Arguments
| pageElement | Page | container element |
Get the page ID of the currently displayed page.Definition
getCurrentPageId()
Get the index of a page in the page mappings (only visible pages) by page ID.Definition
getPageMappingIndex()
Determine if a page, identified by page ID, is disabled.Definition
getIsPageDisabled()
Get a page by ID.Definition
getPageById()
Set a page's disabled state.Definition
setPageIdDisabled(pageId Page, disabled True)Arguments
| pageId | Page | ID |
| disabled | True | for disabled, false for enabled |
Get page titles of all pages which were visible at the latest call to updatePageMapping()Definition
getPageTitles()
Base Class for placeables
-- Note about terrain modification on placement:
If terrain modification is enabled using the placeable.leveling#requireLeveling attribute, the configuration needs
at least one leveling area to be defined (ramps are optional). These areas represent parallelograms which are passed
to terrain modification functions. They are defined by start, width and height nodes which in this case should be
set up to form rectangular shapes. For the best results, create a new transform group for each area at the starting
positions and add separate nodes for the three defining points in the placeable object I3D file.
--@category Placeables
--@xmlConfig placeable.filename string File name of associated I3D file
On create glow materialDefinition
onCreateGlowMaterial(empty empty, integer id)Arguments
| empty | empty | empty |
| integer | id | id of node |
| 90 | function Placeable.onCreateGlowMaterial(_, id) |
| 91 | if getHasShaderParameter(id, "colorScale") then |
| 92 | Placeable.GLOW_MATERIAL = getMaterial(id, 0) |
| 93 | end |
| 94 | end |
Creating placeableDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 106 | function Placeable:new(isServer, isClient, customMt) |
| 107 | local self = Object:new(isServer, isClient, customMt or Placeable_mt) |
| 108 | self.nodeId = 0 |
| 109 | |
| 110 | self.useRandomYRotation = false |
| 111 | self.useManualYRotation = false |
| 112 | self.placementSizeX = 1 |
| 113 | self.placementSizeZ = 1 |
| 114 | self.placementTestSizeX = 1 |
| 115 | self.placementTestSizeZ = 1 |
| 116 | self.requireLeveling = false |
| 117 | self.maxSmoothDistance = 3 |
| 118 | self.maxSlope = MathUtil.degToRad(45) |
| 119 | self.maxEdgeAngle = MathUtil.degToRad(45) |
| 120 | self.smoothingGroundType = nil |
| 121 | |
| 122 | self.triggerMarkers = {} |
| 123 | self.clearAreas = {} |
| 124 | self.levelAreas = {} |
| 125 | self.rampAreas = {} |
| 126 | self.foliageAreas = {} |
| 127 | self.samples = {} |
| 128 | self.pickObjects = {} |
| 129 | self.animatedObjects = {} |
| 130 | self.triggerMarkers = {} |
| 131 | self.mapHotspots = {} |
| 132 | self.isolated = false |
| 133 | self.isDeleted = false |
| 134 | self.useMultiRootNode = false |
| 135 | self.price = 0 |
| 136 | self.age = 0 |
| 137 | self.isInPreviewMode = nil |
| 138 | self.placementPositionSnapSize = 0 |
| 139 | self.placementRotationSnapAngle = 0 |
| 140 | |
| 141 | -- defines that a placeable is placed on a map directly, and with a unique ID |
| 142 | self.mapBoundId = nil |
| 143 | |
| 144 | registerObjectClassName(self, "Placeable") |
| 145 | return self |
| 146 | end |
Deleting placeableDefinition
delete()Code
| 150 | function Placeable:delete() |
| 151 | self.isDeleted = true |
| 152 | if self.i3dFilename ~= nil then |
| 153 | g_i3DManager:releaseSharedI3DFile(self.i3dFilename, nil, true) |
| 154 | end |
| 155 | |
| 156 | for _, node in ipairs(self.triggerMarkers) do |
| 157 | g_currentMission:removeTriggerMarker(node) |
| 158 | end |
| 159 | |
| 160 | if #self.rampAreas > 0 then |
| 161 | for _, area in pairs(self.rampAreas) do |
| 162 | if area.previewShape then |
| 163 | g_i3DManager:releaseSharedI3DFile(Placeable.RAMP_PREVIEW_PATH, nil, true) |
| 164 | end |
| 165 | end |
| 166 | end |
| 167 | |
| 168 | for _, animatedObject in ipairs(self.animatedObjects) do |
| 169 | animatedObject:delete() |
| 170 | end |
| 171 | |
| 172 | for _, hotspot in ipairs(self.mapHotspots) do |
| 173 | g_currentMission.hud:removeMapHotspot(hotspot) |
| 174 | hotspot:delete() |
| 175 | end |
| 176 | |
| 177 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
| 178 | g_currentMission.environment:removeDayChangeListener(self) |
| 179 | g_currentMission.environment:removeWeatherChangeListener(self) |
| 180 | g_currentMission.environment:removeHourChangeListener(self) |
| 181 | end |
| 182 | |
| 183 | unregisterObjectClassName(self) |
| 184 | g_currentMission:removeItemToSave(self) |
| 185 | g_currentMission:removePlaceable(self) |
| 186 | for _, node in pairs(self.pickObjects) do |
| 187 | g_currentMission:removeNodeObject(node) |
| 188 | end |
| 189 | |
| 190 | if self.nodeId ~= 0 and entityExists(self.nodeId) then |
| 191 | self:setCollisionMask(self.nodeId, 0) |
| 192 | setVisibility(self.nodeId, false) |
| 193 | else |
| 194 | self.nodeId = 0 |
| 195 | end |
| 196 | |
| 197 | if self.isClient then |
| 198 | g_soundManager:deleteSamples(self.samples) |
| 199 | end |
| 200 | |
| 201 | g_currentMission:removeOwnedItem(self) |
| 202 | |
| 203 | g_currentMission:addPlaceableToDelete(self, 300) |
| 204 | Placeable:superClass().delete(self) |
| 205 | end |
Deleting placeable finalDefinition
deleteFinal()Code
| 209 | function Placeable:deleteFinal() |
| 210 | if self.nodeId ~= 0 then |
| 211 | delete(self.nodeId) |
| 212 | self.nodeId = 0 |
| 213 | end |
| 214 | end |
Set collision mask of node and its childrenDefinition
setCollisionMask(integer nodeId, integer mask)Arguments
| integer | nodeId | id of node |
| integer | mask | collision mask |
| 220 | function Placeable:setCollisionMask(nodeId, mask) |
| 221 | setCollisionMask(nodeId, mask) |
| 222 | local numChildren = getNumOfChildren(nodeId) |
| 223 | for i=0,numChildren-1 do |
| 224 | local childId = getChildAt(nodeId, i) |
| 225 | self:setCollisionMask(childId, mask) |
| 226 | end |
| 227 | end |
Returns true if player is in rangeDefinition
getIsPlayerInRange(float distance, table player)Arguments
| float | distance | distance |
| table | player | player |
| boolean | isInRange | is in range |
| 234 | function Placeable:getIsPlayerInRange(distance, player) |
| 235 | if self.nodeId ~= 0 then |
| 236 | distance = Utils.getNoNil(distance, 10) |
| 237 | if player == nil then |
| 238 | for _, player in pairs(g_currentMission.players) do |
| 239 | if self:isInActionDistance(player, self.nodeId, distance) then |
| 240 | return true, player |
| 241 | end |
| 242 | end |
| 243 | else |
| 244 | return self:isInActionDistance(player, self.nodeId, distance), player |
| 245 | end |
| 246 | end |
| 247 | return false, nil |
| 248 | end |
Returns true if player is in rangeDefinition
isInActionDistance(table player, integer refNode, float distance)Arguments
| table | player | player |
| integer | refNode | id of reference node |
| float | distance | distance |
| boolean | isInRange | is in range |
| 256 | function Placeable:isInActionDistance(player, refNode, distance) |
| 257 | local x,_,z = getWorldTranslation(refNode) |
| 258 | local px,_,pz = getWorldTranslation(player.rootNode) |
| 259 | local dx,dz = px-x, pz-z |
| 260 | if dx*dx + dz*dz < distance*distance then |
| 261 | return true |
| 262 | end |
| 263 | |
| 264 | return false |
| 265 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 271 | function Placeable:readStream(streamId, connection) |
| 272 | Placeable:superClass().readStream(self, streamId, connection) |
| 273 | if connection:getIsServer() then |
| 274 | local configFileName = NetworkUtil.convertFromNetworkFilename(streamReadString(streamId)) |
| 275 | local x=streamReadFloat32(streamId) |
| 276 | local y=streamReadFloat32(streamId) |
| 277 | local z=streamReadFloat32(streamId) |
| 278 | local rx=NetworkUtil.readCompressedAngle(streamId) |
| 279 | local ry=NetworkUtil.readCompressedAngle(streamId) |
| 280 | local rz=NetworkUtil.readCompressedAngle(streamId) |
| 281 | local isNew = self.configFileName == nil |
| 282 | if isNew then |
| 283 | self:load(configFileName, x,y,z, rx,ry,rz, false, false) |
| 284 | end |
| 285 | self.age = streamReadUInt16(streamId) |
| 286 | self.price = streamReadInt32(streamId) |
| 287 | |
| 288 | if isNew then |
| 289 | self:finalizePlacement() |
| 290 | end |
| 291 | |
| 292 | for _, animatedObject in ipairs(self.animatedObjects) do |
| 293 | local animatedObjectId = NetworkUtil.readNodeObjectId(streamId) |
| 294 | animatedObject:readStream(streamId, connection) |
| 295 | g_client:finishRegisterObject(animatedObject, animatedObjectId) |
| 296 | end |
| 297 | end |
| 298 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 304 | function Placeable:writeStream(streamId, connection) |
| 305 | Placeable:superClass().writeStream(self, streamId, connection) |
| 306 | if not connection:getIsServer() then |
| 307 | streamWriteString(streamId, NetworkUtil.convertToNetworkFilename(self.configFileName)) |
| 308 | local x,y,z = getTranslation(self.nodeId) |
| 309 | local x_rot,y_rot,z_rot = getRotation(self.nodeId) |
| 310 | streamWriteFloat32(streamId, x) |
| 311 | streamWriteFloat32(streamId, y) |
| 312 | streamWriteFloat32(streamId, z) |
| 313 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
| 314 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
| 315 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
| 316 | streamWriteUInt16(streamId, self.age) |
| 317 | streamWriteInt32(streamId, self.price) |
| 318 | |
| 319 | for _, animatedObject in ipairs(self.animatedObjects) do |
| 320 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(animatedObject)) |
| 321 | animatedObject:writeStream(streamId, connection) |
| 322 | g_server:registerObjectInStream(connection, animatedObject) |
| 323 | end |
| 324 | end |
| 325 | end |
Create nodeDefinition
createNode(string i3dFilename)Arguments
| string | i3dFilename | i3d file name |
| boolean | success | success |
| 362 | function Placeable:createNode(i3dFilename) |
| 363 | self.i3dFilename = i3dFilename |
| 364 | local nodeRoot = g_i3DManager:loadSharedI3DFile(i3dFilename, nil, false, false) |
| 365 | if nodeRoot == 0 then |
| 366 | return false |
| 367 | end |
| 368 | |
| 369 | if self.useMultiRootNode then |
| 370 | link(getRootNode(), nodeRoot) |
| 371 | self.nodeId = nodeRoot |
| 372 | else |
| 373 | local nodeId = getChildAt(nodeRoot, 0) |
| 374 | if nodeId == 0 then |
| 375 | delete(nodeRoot) |
| 376 | return false |
| 377 | end |
| 378 | link(getRootNode(), nodeId) |
| 379 | delete(nodeRoot) |
| 380 | self.nodeId = nodeId |
| 381 | end |
| 382 | |
| 383 | return true |
| 384 | end |
Sets the preview material to all nodes in the placeableDefinition
setPreviewMaterials(integer node, table nodeTable)Arguments
| integer | node | id of node |
| table | nodeTable | table to save the nodes |
| 390 | function Placeable:setPreviewMaterials(node, nodeTable) |
| 391 | if getHasClassId(node, ClassIds.SHAPE) then |
| 392 | nodeTable[node] = node |
| 393 | setMaterial(node, Placeable.GLOW_MATERIAL, 0) |
| 394 | end |
| 395 | |
| 396 | local numChildren = getNumOfChildren(node) |
| 397 | for i=0, numChildren-1 do |
| 398 | self:setPreviewMaterials(getChildAt(node, i), nodeTable) |
| 399 | end |
| 400 | end |
Set placable preview stateDefinition
setPlaceablePreviewState(int state)Arguments
| int | state | Placement state [Placeable.PREVIEW_STATE.CHECKING | Placeable.PREVIEW_STATE.VALID | Placeable.PREVIEW_STATE.INVALID] |
| 405 | function Placeable:setPlaceablePreviewState(state) |
| 406 | if not self.isInPreviewMode then |
| 407 | self.isInPreviewMode = true |
| 408 | |
| 409 | self.previewGlowingNodes = {} |
| 410 | if Placeable.GLOW_MATERIAL ~= nil then |
| 411 | -- replace materials with glowing material |
| 412 | self:setPreviewMaterials(self.nodeId, self.previewGlowingNodes) |
| 413 | |
| 414 | -- load preview shapes for ramps |
| 415 | for _, area in pairs(self.rampAreas) do |
| 416 | local rampNode = g_i3DManager:loadSharedI3DFile(Placeable.RAMP_PREVIEW_PATH) |
| 417 | if rampNode ~= 0 then |
| 418 | link(area.root, rampNode) -- link the ramp root to the area -> can rotate the entire area hierarchy for preview |
| 419 | area.previewNode = rampNode |
| 420 | |
| 421 | local rampRootTransform = getChildAt(rampNode, 0) |
| 422 | local rampShape = getChildAt(rampRootTransform, 0) |
| 423 | area.previewShape = rampShape |
| 424 | |
| 425 | setMaterial(rampShape, Placeable.GLOW_MATERIAL, 0) |
| 426 | |
| 427 | -- scale the ramp preview to the area dimensions, this assumes a rectangular area |
| 428 | local scaleX, _, scaleZ = getScale(rampShape) |
| 429 | local startX, startY, startZ = getWorldTranslation(area.start) |
| 430 | local widthX, widthY, widthZ = getWorldTranslation(area.width) |
| 431 | local heightX, heightY, heightZ = getWorldTranslation(area.height) |
| 432 | |
| 433 | local width = MathUtil.vector3Length(widthX - startX, widthY - startY, widthZ - startZ) |
| 434 | local height = MathUtil.vector3Length(heightX - startX, heightY - startY, heightZ - startZ) |
| 435 | |
| 436 | setScale(rampShape, width, Placeable.RAMP_PREVIEW_THICKNESS, height) |
| 437 | end |
| 438 | end |
| 439 | end |
| 440 | end |
| 441 | if Placeable.GLOW_MATERIAL ~= nil then |
| 442 | local color = Placeable.PREVIEW_COLOR[state] |
| 443 | for node in pairs(self.previewGlowingNodes) do |
| 444 | setShaderParameter(node, "colorScale", color[1], color[2], color[3], color[4], false) |
| 445 | end |
| 446 | |
| 447 | local rampColor = Placeable.PREVIEW_RAMP_COLOR[state] |
| 448 | for _, area in pairs(self.rampAreas) do |
| 449 | if area.previewShape ~= nil then |
| 450 | setShaderParameter(area.previewShape, "colorScale", rampColor[1], rampColor[2], rampColor[3], rampColor[4], false) |
| 451 | end |
| 452 | end |
| 453 | end |
| 454 | end |
Load placeableDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 467 | function Placeable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 468 | self.configFileName = xmlFilename |
| 469 | self.customEnvironment, self.baseDirectory = Utils.getModNameAndBaseDirectory(xmlFilename) |
| 470 | |
| 471 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 472 | if xmlFile == 0 then |
| 473 | return false |
| 474 | end |
| 475 | local i3dFilename = getXMLString(xmlFile, "placeable.filename") |
| 476 | self.placementSizeX = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.placement#sizeX"), self.placementSizeX) |
| 477 | self.placementSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.placement#sizeZ"), self.placementSizeZ) |
| 478 | self.placementTestSizeX = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.placement#testSizeX"), self.placementSizeX) |
| 479 | self.placementTestSizeZ = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.placement#testSizeZ"), self.placementSizeZ) |
| 480 | self.useRandomYRotation = Utils.getNoNil(getXMLBool(xmlFile, "placeable.placement#useRandomYRotation"), self.useRandomYRotation) |
| 481 | self.useManualYRotation = Utils.getNoNil(getXMLBool(xmlFile, "placeable.placement#useManualYRotation"), self.useManualYRotation) |
| 482 | self.alignToWorldY = Utils.getNoNil(getXMLBool(xmlFile, "placeable.placement#alignToWorldY"), true) |
| 483 | self.placementPositionSnapSize = math.abs(Utils.getNoNil(getXMLFloat(xmlFile, "placeable.placement#placementPositionSnapSize"), 0.0)) |
| 484 | self.placementRotationSnapAngle = math.rad(math.abs(Utils.getNoNil(getXMLFloat(xmlFile, "placeable.placement#placementRotationSnapAngle"), 0.0))) |
| 485 | |
| 486 | self.incomePerHour = getXMLFloat(xmlFile, "placeable.incomePerHour" .. g_currentMission.missionInfo.difficulty) |
| 487 | -- fallback for old single value format |
| 488 | if self.incomePerHour == nil then |
| 489 | self.incomePerHour = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.incomePerHour"), 0) |
| 490 | if g_currentMission.missionInfo.difficulty == 1 then |
| 491 | self.incomePerHour = self.incomePerHour * 1.5 |
| 492 | elseif g_currentMission.missionInfo.difficulty == 3 then |
| 493 | self.incomePerHour = self.incomePerHour / 1.5 |
| 494 | end |
| 495 | end |
| 496 | |
| 497 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 498 | if storeItem ~= nil then |
| 499 | if self.price == 0 or self.price == nil then |
| 500 | self.price = StoreItemUtil.getDefaultPrice(storeItem) |
| 501 | end |
| 502 | |
| 503 | if g_currentMission ~= nil and storeItem.canBeSold then |
| 504 | g_currentMission.environment:addDayChangeListener(self) |
| 505 | end |
| 506 | end |
| 507 | |
| 508 | if i3dFilename == nil then |
| 509 | delete(xmlFile) |
| 510 | return false |
| 511 | end |
| 512 | self.i3dFilename = Utils.getFilename(i3dFilename, self.baseDirectory) |
| 513 | |
| 514 | if not self:createNode(self.i3dFilename) then |
| 515 | delete(xmlFile) |
| 516 | return false |
| 517 | end |
| 518 | self:initPose(x,y,z, rx,ry,rz, initRandom) |
| 519 | |
| 520 | if hasXMLProperty(xmlFile, "placeable.dayNightObjects") then |
| 521 | local i = 0 |
| 522 | while true do |
| 523 | local key = string.format("placeable.dayNightObjects.dayNightObject(%d)", i) |
| 524 | if not hasXMLProperty(xmlFile, key) then |
| 525 | break |
| 526 | end |
| 527 | |
| 528 | local node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key.."#node")) |
| 529 | if node ~= nil then |
| 530 | if self.dayNightObjects == nil then |
| 531 | self.dayNightObjects = {} |
| 532 | g_currentMission.environment:addWeatherChangeListener(self) |
| 533 | end |
| 534 | |
| 535 | local visibleDay = getXMLBool(xmlFile, key.."#visibleDay") |
| 536 | local visibleNight = getXMLBool(xmlFile, key.."#visibleNight") |
| 537 | local intensityDay = getXMLFloat(xmlFile, key.."#intensityDay") |
| 538 | local intensityNight = getXMLFloat(xmlFile, key.."#intensityNight") |
| 539 | |
| 540 | table.insert(self.dayNightObjects, {node=node, visibleDay=visibleDay, visibleNight=visibleNight, intensityDay=intensityDay, intensityNight=intensityNight}) |
| 541 | end |
| 542 | i = i + 1 |
| 543 | end |
| 544 | end |
| 545 | |
| 546 | -- load leveling properties |
| 547 | self.requireLeveling = Utils.getNoNil(getXMLBool(xmlFile, "placeable.leveling#requireLeveling"), self.requireLeveling) |
| 548 | if self.requireLeveling then |
| 549 | self.maxSmoothDistance = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.leveling#maxSmoothDistance"), 3) |
| 550 | self.maxSlope = MathUtil.degToRad(Utils.getNoNil(getXMLFloat(xmlFile, "placeable.leveling#maxSlope"), 45)) |
| 551 | self.maxEdgeAngle = MathUtil.degToRad(Utils.getNoNil(getXMLFloat(xmlFile, "placeable.leveling#maxEdgeAngle"), 45)) |
| 552 | self.smoothingGroundType = getXMLString(xmlFile, "placeable.leveling#smoothingGroundType") |
| 553 | end |
| 554 | |
| 555 | self:loadAreasFromXML(self.clearAreas, xmlFile, "placeable.clearAreas.clearArea(%d)", false, false) |
| 556 | self:loadAreasFromXML(self.levelAreas, xmlFile, "placeable.leveling.levelAreas.levelArea(%d)", false, true) -- load leveling info |
| 557 | self:loadAreasFromXML(self.rampAreas, xmlFile, "placeable.leveling.rampAreas.rampArea(%d)", true, true) -- load ramps and leveling info |
| 558 | self:loadAreasFromXML(self.foliageAreas, xmlFile, "placeable.foliageAreas.foliageArea(%d)", false, false, true) -- load ramps and leveling info |
| 559 | |
| 560 | if hasXMLProperty(xmlFile, "placeable.tipOcclusionUpdateArea") then |
| 561 | local sizeX = getXMLFloat(xmlFile, "placeable.tipOcclusionUpdateArea#sizeX") |
| 562 | local sizeZ = getXMLFloat(xmlFile, "placeable.tipOcclusionUpdateArea#sizeZ") |
| 563 | |
| 564 | if sizeX ~= nil and sizeZ ~= nil then |
| 565 | local centerX = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.tipOcclusionUpdateArea#centerX"), 0) |
| 566 | local centerZ = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.tipOcclusionUpdateArea#centerZ"), 0) |
| 567 | self.tipOcclusionUpdateArea = {centerX, centerZ, sizeX, sizeZ} |
| 568 | end |
| 569 | end |
| 570 | |
| 571 | if not self.alignToWorldY then |
| 572 | self.pos1Node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.placement#pos1Node")) |
| 573 | self.pos2Node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.placement#pos2Node")) |
| 574 | self.pos3Node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.placement#pos3Node")) |
| 575 | if self.pos1Node == nil or self.pos2Node == nil or self.pos3Node == nil then |
| 576 | self.alignToWorldY = true |
| 577 | print("Warning: Pos1Node, Pos2Node and Pos3Node has to be set when alignToWorldY is false!") |
| 578 | end |
| 579 | end |
| 580 | |
| 581 | if hasXMLProperty(xmlFile, "placeable.animatedObjects") then |
| 582 | local i = 0 |
| 583 | while true do |
| 584 | local animationKey = string.format("placeable.animatedObjects.animatedObject(%d)", i) |
| 585 | if not hasXMLProperty(xmlFile, animationKey) then |
| 586 | break |
| 587 | end |
| 588 | |
| 589 | local animatedObject = AnimatedObject:new(self.isServer, self.isClient) |
| 590 | if not animatedObject:load(self.nodeId, xmlFile, animationKey) then |
| 591 | print("Error: Failed to load animated object " .. tostring(i)) |
| 592 | else |
| 593 | if self.isServer then |
| 594 | animatedObject:register(true) |
| 595 | end |
| 596 | |
| 597 | table.insert(self.animatedObjects, animatedObject) |
| 598 | end |
| 599 | |
| 600 | i = i + 1 |
| 601 | end |
| 602 | end |
| 603 | |
| 604 | local i = 0 |
| 605 | while true do |
| 606 | local triggerMarkerKey = string.format("placeable.triggerMarkers.triggerMarker(%d)", i) |
| 607 | if not hasXMLProperty(xmlFile, triggerMarkerKey) then |
| 608 | break |
| 609 | end |
| 610 | |
| 611 | local node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, triggerMarkerKey.."#node")) |
| 612 | if node ~= nil then |
| 613 | table.insert(self.triggerMarkers, node) |
| 614 | g_currentMission:addTriggerMarker(node) |
| 615 | end |
| 616 | |
| 617 | i = i + 1 |
| 618 | end |
| 619 | |
| 620 | if hasXMLProperty(xmlFile, "placeable.hotspots") then |
| 621 | local i = 0 |
| 622 | while true do |
| 623 | local hotspotKey = string.format("placeable.hotspots.hotspot(%d)", i) |
| 624 | if not hasXMLProperty(xmlFile, hotspotKey) then |
| 625 | break |
| 626 | end |
| 627 | |
| 628 | local hotspot = self:loadHotspotFromXML(xmlFile, hotspotKey) |
| 629 | if hotspot ~= nil then |
| 630 | g_currentMission:addMapHotspot(hotspot) |
| 631 | table.insert(self.mapHotspots, hotspot) |
| 632 | end |
| 633 | |
| 634 | i = i + 1 |
| 635 | end |
| 636 | end |
| 637 | |
| 638 | if self.isClient then |
| 639 | self.samples.idle = g_soundManager:loadSampleFromXML(xmlFile, "placeable.sounds", "idle", self.baseDirectory, self.nodeId, 1, AudioGroup.ENVIRONMENT, nil, nil) |
| 640 | end |
| 641 | |
| 642 | delete(xmlFile) |
| 643 | |
| 644 | return true |
| 645 | end |
Load area definitions from XML into an area array.Definition
loadAreasFromXML(table areaArray, string xmlFile, string xmlPathTemplate, bool isRamp)Arguments
| table | areaArray | Array instance which receives loaded area definitions. |
| string | xmlFile | Loaded XML file handle |
| string | xmlPathTemplate | XML element path template for an area type |
| bool | isRamp | If true, the targeted areas are ramps |
Load a single area definition from XML.Definition
Areas are defined by three nodes: start, width and height. The start node denotes the origin of the area, while width
and height provide the dimensions of the spanned parallelogram. Usage of areas include clear areas (foliage is
cleared) and leveling areas (terrain is leveled) around placeable objects.
loadAreaFromXML(table area, integer xmlFile, string key, bool isRamp)Arguments
| table | area | Empty area definition table which receives the loaded node IDs. |
| integer | xmlFile | ID of the XML file |
| string | key | String Key to the XML element |
| bool | isRamp | If true, the targeted areas are ramps |
| boolean | success | success |
| 685 | function Placeable:loadAreaFromXML(area, xmlFile, key, isRamp, isLeveling) |
| 686 | local start = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key .. "#startNode")) |
| 687 | local width = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key .. "#widthNode")) |
| 688 | local height = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key .. "#heightNode")) |
| 689 | |
| 690 | if start ~= nil and width ~= nil and height ~= nil then |
| 691 | area.root = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key .. "#rootNode")) |
| 692 | area.start = start |
| 693 | area.width = width |
| 694 | area.height = height |
| 695 | area.texture = getXMLString(xmlFile, key .. "#texture") |
| 696 | |
| 697 | if isRamp then |
| 698 | local rx, ry, rz = getRotation(area.root) |
| 699 | area.baseRotation = {rx, ry, rz} -- store base rotation for resetting during preview |
| 700 | local rampSlope = getXMLFloat(xmlFile, key .. "#maxSlope") |
| 701 | area.maxSlope = rampSlope and MathUtil.degToRad(rampSlope) or self.maxSlope |
| 702 | end |
| 703 | |
| 704 | if isLeveling then |
| 705 | area.groundType = getXMLString(xmlFile, key .. "#groundType") |
| 706 | end |
| 707 | |
| 708 | return true |
| 709 | end |
| 710 | |
| 711 | return false |
| 712 | end |
Load foliage definitons from XML.Definition
loadFoliageAreaFromXML()Code
| 716 | function Placeable:loadFoliageAreaFromXML(area, xmlFile, key) |
| 717 | local rootNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key .. "#rootNode")) |
| 718 | local fruitType = getXMLString(xmlFile, key .. "#fruitType") |
| 719 | local fruitTypeDesc = g_fruitTypeManager:getFruitTypeByName(fruitType) |
| 720 | local state = getXMLInt(xmlFile, key .. "#state") |
| 721 | |
| 722 | if rootNode ~= nil and fruitTypeDesc ~= nil then |
| 723 | area.fruitType = fruitTypeDesc.index |
| 724 | area.fieldDimensions = rootNode |
| 725 | area.fruitState = Utils.getNoNil(state, fruitTypeDesc.maxHarvestingGrowthState - 1) |
| 726 | |
| 727 | return true |
| 728 | end |
| 729 | |
| 730 | return false |
| 731 | end |
Load hotspot from XML definitonsDefinition
loadHotspotFromXML()Code
| 735 | function Placeable:loadHotspotFromXML(xmlFile, key) |
| 736 | local name = Utils.getNoNil(getXMLString(xmlFile, key.."#name"), "") |
| 737 | |
| 738 | local category = Utils.getNoNil(getXMLString(xmlFile, key.."#category"), "CATEGORY_TRIGGER") |
| 739 | if MapHotspot[category] ~= nil then |
| 740 | category = MapHotspot[category] |
| 741 | else |
| 742 | category = MapHotspot.CATEGORY_DEFAULT |
| 743 | end |
| 744 | |
| 745 | local hotspot = MapHotspot:new(name, category) |
| 746 | |
| 747 | local text = g_i18n:convertText(Utils.getNoNil(getXMLString(xmlFile, key.."#fullName"), "")) |
| 748 | if text ~= "" then |
| 749 | local showName = Utils.getNoNil(getXMLBool(xmlFile, key.."#showName"), false) |
| 750 | |
| 751 | hotspot:setText(text, not showName) |
| 752 | end |
| 753 | |
| 754 | local imageFilename = getXMLString(xmlFile, key.."#imageFilename") |
| 755 | if imageFilename ~= nil then |
| 756 | imageFilename = Utils.getFilename(imageFilename, self.baseDirectory) |
| 757 | end |
| 758 | |
| 759 | local imageUVs = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#imageUVs"), 4) |
| 760 | local imageName = getXMLString(xmlFile, key.."#imageName") |
| 761 | if imageName ~= nil and MapHotspot.UV[imageName] ~= nil then |
| 762 | imageUVs = MapHotspot.UV[imageName] |
| 763 | end |
| 764 | if imageUVs ~= nil then |
| 765 | imageUVs = getNormalizedUVs(imageUVs) |
| 766 | end |
| 767 | if imageUVs ~= nil then |
| 768 | local baseColor = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#baseColor"), 4) |
| 769 | hotspot:setBorderedImage(imageFilename, imageUVs, baseColor) |
| 770 | end |
| 771 | |
| 772 | local linkNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key .. "#linkNode")) |
| 773 | if linkNode == nil then |
| 774 | linkNode = self.nodeId |
| 775 | end |
| 776 | hotspot:setLinkedNode(linkNode) |
| 777 | |
| 778 | local width = getXMLFloat(xmlFile, key.."#width") |
| 779 | local height = getXMLFloat(xmlFile, key.."#height") |
| 780 | if width ~= nil and heigth ~= nil then |
| 781 | hotspot:setSize(width, height) |
| 782 | end |
| 783 | |
| 784 | hotspot:setBlinking(Utils.getNoNil(getXMLBool(xmlFile, key.."#blinking"), false)) |
| 785 | hotspot:setPersistent(Utils.getNoNil(getXMLBool(xmlFile, key.."#persistent"), false)) |
| 786 | hotspot:setRenderLast(Utils.getNoNil(getXMLBool(xmlFile, key.."#renderLast"), false)) |
| 787 | |
| 788 | local textSize = getXMLInt(xmlFile, key.."#textSize") |
| 789 | if textSize ~= nil then |
| 790 | _, textSize = getNormalizedScreenValues(0, textSize) |
| 791 | hotspot:setTextOptions(textSize) |
| 792 | end |
| 793 | |
| 794 | local hotspotTextOffset = Utils.getNoNil(getXMLString(xmlFile, key .. "#hotspotTextOffset"), "0px 0px") |
| 795 | hotspot:setRawTextOffset(hotspotTextOffset) |
| 796 | |
| 797 | local textColor = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#textColor"), 4) |
| 798 | hotspot:setTextOptions(nil, nil, nil, textColor) |
| 799 | |
| 800 | return hotspot |
| 801 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 805 | function Placeable:finalizePlacement() |
| 806 | if self.isInPreviewMode then |
| 807 | print("Error: Can't finalize placement of preview placeables") |
| 808 | end |
| 809 | if not self.isolated then |
| 810 | |
| 811 | self:alignToTerrain() |
| 812 | |
| 813 | addToPhysics(self.nodeId) |
| 814 | g_currentMission:addPlaceable(self) |
| 815 | g_currentMission:addItemToSave(self) |
| 816 | g_currentMission:addOwnedItem(self) |
| 817 | self:collectPickObjects(self.nodeId) |
| 818 | |
| 819 | for _, node in pairs(self.pickObjects) do |
| 820 | g_currentMission:addNodeObject(node, self) |
| 821 | end |
| 822 | |
| 823 | local missionInfo = g_currentMission.missionInfo |
| 824 | if self.isServer then |
| 825 | if not self.isLoadedFromSavegame or |
| 826 | (missionInfo.isValid and not (missionInfo:getIsTipCollisionValid(g_currentMission) and missionInfo:getIsPlacementCollisionValid(g_currentMission))) then |
| 827 | self:setTipOcclusionAreaDirty() |
| 828 | end |
| 829 | end |
| 830 | end |
| 831 | |
| 832 | if self.isClient then |
| 833 | g_soundManager:playSample(self.samples.idle) |
| 834 | end |
| 835 | |
| 836 | -- initially update dayNightObjects |
| 837 | self:weatherChanged() |
| 838 | g_currentMission.environment:addHourChangeListener(self) |
| 839 | |
| 840 | local x,_,z = getWorldTranslation(self.nodeId) |
| 841 | self.farmlandId = g_farmlandManager:getFarmlandIdAtWorldPosition(x, z) |
| 842 | |
| 843 | for _, hotspot in ipairs(self.mapHotspots) do |
| 844 | hotspot:setOwnerFarmId(self:getOwnerFarmId()) |
| 845 | end |
| 846 | |
| 847 | g_messageCenter:publish(MessageType.FARM_PROPERTY_CHANGED, {self:getOwnerFarmId()}) |
| 848 | end |
Set tip occlusion area dirtyDefinition
setTipOcclusionAreaDirty()Code
| 852 | function Placeable:setTipOcclusionAreaDirty() |
| 853 | if self.tipOcclusionUpdateArea ~= nil and self.nodeId ~= 0 then |
| 854 | local x, z, sizeX, sizeZ = unpack(self.tipOcclusionUpdateArea) |
| 855 | local x1,_,z1 = localToWorld(self.nodeId, x+sizeX*0.5, 0, z+sizeZ*0.5) |
| 856 | local x2,_,z2 = localToWorld(self.nodeId, x-sizeX*0.5, 0, z+sizeZ*0.5) |
| 857 | local x3,_,z3 = localToWorld(self.nodeId, x+sizeX*0.5, 0, z-sizeZ*0.5) |
| 858 | local x4,_,z4 = localToWorld(self.nodeId, x-sizeX*0.5, 0, z-sizeZ*0.5) |
| 859 | local minX = math.min(math.min(x1, x2), math.min(x3, x4)) |
| 860 | local maxX = math.max(math.max(x1, x2), math.max(x3, x4)) |
| 861 | local minZ = math.min(math.min(z1, z2), math.min(z3, z4)) |
| 862 | local maxZ = math.max(math.max(z1, z2), math.max(z3, z4)) |
| 863 | g_densityMapHeightManager:setCollisionMapAreaDirty(minX, minZ, maxX, maxZ) |
| 864 | end |
| 865 | end |
Align placeable to terrainDefinition
alignToTerrain()Code
| 869 | function Placeable:alignToTerrain() |
| 870 | if not self.alignToWorldY then |
| 871 | local x1,y1,z1 = getWorldTranslation(self.nodeId) |
| 872 | y1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1,y1,z1) |
| 873 | setTranslation(self.nodeId, x1,y1,z1) |
| 874 | local x2,y2,z2 = getWorldTranslation(self.pos1Node) |
| 875 | y2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2,y2,z2) |
| 876 | local x3,y3,z3 = getWorldTranslation(self.pos2Node) |
| 877 | y3 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x3,y3,z3) |
| 878 | local x4,y4,z4 = getWorldTranslation(self.pos3Node) |
| 879 | y4 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x4,y4,z4) |
| 880 | local dirX = x2 - x1 |
| 881 | local dirY = y2 - y1 |
| 882 | local dirZ = z2 - z1 |
| 883 | local dir2X = x3 - x4 |
| 884 | local dir2Y = y3 - y4 |
| 885 | local dir2Z = z3 - z4 |
| 886 | local upX,upY,upZ = MathUtil.crossProduct(dir2X, dir2Y, dir2Z, dirX, dirY, dirZ) |
| 887 | setDirection(self.nodeId, dirX, dirY, dirZ, upX,upY,upZ) |
| 888 | end |
| 889 | end |
Clear foliage and tipAny from clearAreasDefinition
clearFoliageAndTipAreas()Code
| 893 | function Placeable:clearFoliageAndTipAreas() |
| 894 | if self.isServer then |
| 895 | for _, areas in pairs{self.clearAreas, self.levelAreas, self.rampAreas} do |
| 896 | for _, area in pairs(areas) do |
| 897 | local x,_,z = getWorldTranslation(area.start) |
| 898 | local x1,_,z1 = getWorldTranslation(area.width) |
| 899 | local x2,_,z2 = getWorldTranslation(area.height) |
| 900 | -- clear foliage |
| 901 | FSDensityMapUtil.removeFieldArea(x, z, x1, z1, x2, z2) |
| 902 | FSDensityMapUtil.removeWeedArea(x, z, x1, z1, x2, z2) |
| 903 | FSDensityMapUtil.eraseTireTrack(x, z, x1, z1, x2, z2) |
| 904 | -- clear tipAny |
| 905 | DensityMapHeightUtil.clearArea(x, z, x1, z1, x2, z2) |
| 906 | end |
| 907 | end |
| 908 | |
| 909 | -- Add foliage again |
| 910 | for _, area in pairs(self.foliageAreas) do |
| 911 | FieldUtil.setAreaFruit(area.fieldDimensions, area.fruitType, area.fruitState) |
| 912 | end |
| 913 | end |
| 914 | end |
Initialize poseDefinition
initPose(float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| float | x | x world position |
| float | y | y world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| 925 | function Placeable:initPose(x,y,z, rx,ry,rz, initRandom) |
| 926 | setTranslation(self.nodeId, x, y, z) |
| 927 | setRotation(self.nodeId, rx, ry, rz) |
| 928 | end |
Collect pick objectsDefinition
collectPickObjects(integer node)Arguments
| integer | node | node id |
| 933 | function Placeable:collectPickObjects(node) |
| 934 | if getRigidBodyType(node) ~= "NoRigidBody" then |
| 935 | table.insert(self.pickObjects, node) |
| 936 | end |
| 937 | local numChildren = getNumOfChildren(node) |
| 938 | for i=1, numChildren do |
| 939 | self:collectPickObjects(getChildAt(node, i-1)) |
| 940 | end |
| 941 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 949 | function Placeable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 950 | local x,y,z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#position")) |
| 951 | local xRot,yRot,zRot = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotation")) |
| 952 | if x == nil or y == nil or z == nil or xRot == nil or yRot == nil or zRot == nil then |
| 953 | return false |
| 954 | end |
| 955 | |
| 956 | xRot = math.rad(xRot) |
| 957 | yRot = math.rad(yRot) |
| 958 | zRot = math.rad(zRot) |
| 959 | |
| 960 | local xmlFilename = getXMLString(xmlFile, key.."#filename") |
| 961 | if xmlFilename == nil then |
| 962 | return false |
| 963 | end |
| 964 | xmlFilename = NetworkUtil.convertFromNetworkFilename(xmlFilename) |
| 965 | |
| 966 | if self:load(xmlFilename, x,y,z, xRot, yRot, zRot, false, false) then |
| 967 | self.age = Utils.getNoNil(getXMLFloat(xmlFile, key.."#age"), 0) |
| 968 | self.price = Utils.getNoNil(getXMLInt(xmlFile, key.."#price"), self.price) |
| 969 | |
| 970 | -- Use a call so any sub-objects of the placeable can be updated |
| 971 | self:setOwnerFarmId(Utils.getNoNil(getXMLInt(xmlFile, key .. "#farmId"), AccessHandler.EVERYONE), true) |
| 972 | |
| 973 | self.mapBoundId = Utils.getNoNil(getXMLString(xmlFile, key .. "#mapBoundId"), self.mapBoundId) |
| 974 | self.isLoadedFromSavegame = true |
| 975 | self:finalizePlacement() |
| 976 | |
| 977 | for i, animatedObject in ipairs(self.animatedObjects) do |
| 978 | animatedObject:loadFromXMLFile(xmlFile, string.format("%s.animatedObjects.animatedObject(%d)", key, i - 1)) |
| 979 | end |
| 980 | |
| 981 | return true |
| 982 | else |
| 983 | return false |
| 984 | end |
| 985 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 992 | function Placeable:saveToXMLFile(xmlFile, key, usedModNames) |
| 993 | local x,y,z = getTranslation(self.nodeId) |
| 994 | local xRot,yRot,zRot = getRotation(self.nodeId) |
| 995 | |
| 996 | setXMLString(xmlFile, key.."#filename", HTMLUtil.encodeToHTML(NetworkUtil.convertToNetworkFilename(self.configFileName))) |
| 997 | setXMLString(xmlFile, key.."#position", string.format("%.4f %.4f %.4f", x, y, z)) |
| 998 | setXMLString(xmlFile, key.."#rotation", string.format("%.4f %.4f %.4f", math.deg(xRot), math.deg(yRot), math.deg(zRot))) |
| 999 | setXMLInt(xmlFile, key.."#age", self.age) |
| 1000 | setXMLFloat(xmlFile, key.."#price", self.price) |
| 1001 | setXMLInt(xmlFile, key.."#farmId", self:getOwnerFarmId()) |
| 1002 | |
| 1003 | if self.mapBoundId ~= nil then |
| 1004 | setXMLString(xmlFile, key.."#mapBoundId", self.mapBoundId) |
| 1005 | end |
| 1006 | |
| 1007 | for i, animatedObject in ipairs(self.animatedObjects) do |
| 1008 | animatedObject:saveToXMLFile(xmlFile, string.format("%s.animatedObjects.animatedObject(%d)", key, i - 1), usedModNames) |
| 1009 | end |
| 1010 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 1020 | function Placeable:update(dt) |
| 1021 | end |
Returns priceDefinition
getPrice()Return Values
| integer | price | price |
| 1029 | function Placeable:getPrice() |
| 1030 | return self.price |
| 1031 | end |
Returns true if we can place a buildingDefinition
checking item count
canBuy()Return Values
| 1037 | function Placeable:canBuy() |
| 1038 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 1039 | local enoughItems = storeItem.maxItemCount == nil or (storeItem.maxItemCount ~= nil and g_currentMission:getNumOfItems(storeItem, g_currentMission:getFarmId()) < storeItem.maxItemCount) |
| 1040 | return enoughItems |
| 1041 | end |
Called on buyDefinition
onBuy()Code
| 1053 | function Placeable:onBuy() |
| 1054 | end |
Called on sellDefinition
onSell()Code
| 1058 | function Placeable:onSell() |
| 1059 | if self.isServer then |
| 1060 | self:setTipOcclusionAreaDirty() |
| 1061 | end |
| 1062 | |
| 1063 | g_messageCenter:publish(MessageType.FARM_PROPERTY_CHANGED, {self:getOwnerFarmId()}) |
| 1064 | end |
Returns daily up keepDefinition
getDailyUpkeep()Return Values
| integer | dailyUpkeep | daily up keep |
| 1069 | function Placeable:getDailyUpkeep() |
| 1070 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 1071 | local multiplier = 1 |
| 1072 | if storeItem.lifetime ~= nil and storeItem.lifetime ~= 0 then |
| 1073 | local ageMultiplier = math.min(self.age/storeItem.lifetime, 1) |
| 1074 | multiplier = 1 + EconomyManager.MAX_DAILYUPKEEP_MULTIPLIER * ageMultiplier |
| 1075 | end |
| 1076 | return StoreItemUtil.getDailyUpkeep(storeItem, nil) * multiplier |
| 1077 | end |
Returns sell priceDefinition
getSellPrice()Return Values
| integer | sellPrice | sell price |
| 1082 | function Placeable:getSellPrice() |
| 1083 | local priceMultiplier = 0.5 |
| 1084 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 1085 | local maxVehicleAge = storeItem.lifetime |
| 1086 | |
| 1087 | if maxVehicleAge ~= nil and maxVehicleAge ~= 0 then |
| 1088 | priceMultiplier = priceMultiplier * math.exp(-3.5 * math.min(self.age/maxVehicleAge, 1)) |
| 1089 | end |
| 1090 | |
| 1091 | return math.floor(self.price * math.max(priceMultiplier, 0.05)) |
| 1092 | end |
Get wether the placeable is bound to the map and should be matched with the original map definition.Definition
Does not currently influence anything. Once it doe: the position and rotation of the placeable will be updated
By the map info. It will also be removed if it was from the map.
isMapBound()
Called if hour changedDefinition
hourChanged()Code
| 1103 | function Placeable:hourChanged() |
| 1104 | if self.isServer then |
| 1105 | if self.incomePerHour ~= 0 then |
| 1106 | g_currentMission:addMoney(self.incomePerHour, self:getOwnerFarmId(), "propertyIncome") |
| 1107 | g_currentMission:addMoneyChange(self.incomePerHour, self:getOwnerFarmId(), EconomyManager.MONEY_TYPE_PROPERTY_INCOME) |
| 1108 | end |
| 1109 | end |
| 1110 | end |
Called if day changedDefinition
dayChanged()Code
| 1114 | function Placeable:dayChanged() |
| 1115 | self.age = self.age + 1 |
| 1116 | end |
Called if weather changedDefinition
weatherChanged()Code
| 1120 | function Placeable:weatherChanged() |
| 1121 | if g_currentMission ~= nil and g_currentMission.environment ~= nil and self.dayNightObjects ~= nil then |
| 1122 | for _, dayNightObject in pairs(self.dayNightObjects) do |
| 1123 | if dayNightObject.visibleDay ~= nil and dayNightObject.visibleNight ~= nil then |
| 1124 | setVisibility(dayNightObject.node, (g_currentMission.environment.isSunOn and dayNightObject.visibleDay) or (dayNightObject.visibleNight and (not g_currentMission.environment.isSunOn and not g_currentMission.environment.weather:getIsRaining()))) |
| 1125 | |
| 1126 | elseif dayNightObject.intensityDay ~= nil and dayNightObject.intensityNight ~= nil then |
| 1127 | local intensity = dayNightObject.intensityNight |
| 1128 | if g_currentMission.environment.isSunOn then |
| 1129 | intensity = dayNightObject.intensityDay |
| 1130 | end |
| 1131 | |
| 1132 | local _,y,z,w = getShaderParameter(dayNightObject.node, "lightControl") |
| 1133 | setShaderParameter(dayNightObject.node, "lightControl", intensity, y, z, w, false) |
| 1134 | end |
| 1135 | end |
| 1136 | end |
| 1137 | end |
Get an array of areas which this placeable is going to modify on placement.Definition
These areas are used to modify the placement blocking bit vector map to avoid interfering with terrain modifications
of other placeables.
getTerrainModificationBlockingAreas()Return Values
| {i | = | {x, y, z, side1x, side1y, side1z, side2x, side2y, side2z}}, all numbers in world space coordinates |
Loads capacity spec valueDefinition
loadSpecValueIncomePerHour(integer xmlFile, string customEnvironment)Arguments
| integer | xmlFile | id of xml object |
| string | customEnvironment | custom environment |
| table | capacityAndUnit | capacity and unit |
| 1167 | function Placeable.loadSpecValueIncomePerHour(xmlFile, customEnvironment) |
| 1168 | if not hasXMLProperty(xmlFile, "placeable.incomePerHour1") then |
| 1169 | return nil |
| 1170 | end |
| 1171 | |
| 1172 | local incomePerHour = {} |
| 1173 | incomePerHour[1] = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.incomePerHour1"), 0) |
| 1174 | incomePerHour[2] = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.incomePerHour2"), 0) |
| 1175 | incomePerHour[3] = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.incomePerHour3"), 0) |
| 1176 | return incomePerHour |
| 1177 | end |
Returns value of income per hourDefinition
getSpecValueIncomePerHour(table storeItem, table realItem)Arguments
| table | storeItem | store item |
| table | realItem | real item |
| integer | incomePerHour | income per hour |
| 1184 | function Placeable.getSpecValueIncomePerHour(storeItem, realItem) |
| 1185 | if storeItem.specs.incomePerHour == nil then |
| 1186 | return nil |
| 1187 | end |
| 1188 | return string.format(g_i18n:getText("shop_incomeValue"), g_i18n:formatMoney(storeItem.specs.incomePerHour[g_currentMission.missionInfo.difficulty])) |
| 1189 | end |
Object Placement HUD Screen.
-- Shown in-game when placing objects.
-- @field messageText Hint message display text
Create a new PlacementScreen instance.Definition
new(table target, table custom_mt, table messageCenter, table placementController)Arguments
| table | target | ScreenElement controller instance |
| table | custom_mt | [optional] Sub-class meta table for inheritance |
| table | messageCenter | MessageCenter reference for local network UI event handling |
| table | placementController | PlacementScreenController instance which handles placement logic |
| table | instance | instance of object |
| table | PlacementScreen | instance |
Handle mouse input event.Definition
Custom logic for PC version mouse input.
mouseEvent()Return Values
| table | instance | instance of object |
Update message text, if present.Definition
updateMessageText()Return Values
| boolean | true | if loading was successful else false |
Set a placeable object for placement or selling.Definition
setPlacementItem(item Placement, isSellMode If, obj Sellable)Arguments
| item | Placement | item from the store |
| isSellMode | If | true, we want to sell the given item |
| obj | Sellable | object |
| boolean | success | success |
Called when the mouse input mode changes.Definition
onMouseModeChanged()Return Values
| table | instance | instance of object |
Handles messages dispatched to this view by the controller.Definition
handleControllerMessage(messageId Message, text Message, callback [optional], callbackTarget [optional])Arguments
| messageId | Message | ID as defined in PlacementScreenController.MESSAGE |
| text | Message | text to display |
| callback | [optional] | Callback for message dialog completion |
| callbackTarget | [optional] | Callback target which is given as the first argument to the callback if supplied |
| boolean | true | if loading was successful else false |
Placeable placement controller.
-- Handles placement and selling logic for placeable objects in the PlacementScreen view.
--@category GUI
Create a new PlacementScreenController instance.Definition
new(table l10n, table inputManager, table placeableTypeManager)Arguments
| table | l10n | I18N reference for string localization. |
| table | inputManager | InputBinding reference for input action registration |
| table | placeableTypeManager | PlaceableTypeManager reference |
| table | categories | a list of categories |
Set the callback used to trigger a mouse mode change.Definition
setMouseModeChangeCallback(function callback)Arguments
| function | callback | Mouse mode callback function, signature: function(isMouseMode) |
| table | category | the corresponding category |
Set the callback used to dispatch UI messages.Definition
setMessageDispatchCallback(function callback)Arguments
| function | callback | Message dispatch callback function, signature: function(messageId, text, callbackFunction, callbackTarget) |
| table | instance | instance of object |
Set the callback used to exit placement mode.Definition
setExitCallback(function callback)Arguments
| function | callback | Exit callback, signature: function() |
| table | instance | instance of object |
Set the reference to the network client.Definition
setClient()Return Values
| table | instance | instance of object |
Set the reference to the current mission controller when starting a game.Definition
setCurrentMission()Return Values
| table | mod | the mod object |
Set the reference to the in-game HUD.Definition
setHUD()Return Values
| boolean | success | true if mod was removed, else false |
Initialize the controller.Definition
initialize()Return Values
| table | mod | the mod object |
Set up the placement camera.Definition
initializeCamera()Return Values
| table | mod | the mod object |
Reset controller state, e.g. when leaving the controlled view.Definition
reset()Return Values
| table | mod | the mod object |
Update camera position and orientation based on player input.Definition
updateCameraMovement(dt Delta, movementMultiplier Speed)Arguments
| dt | Delta | time in milliseconds |
| movementMultiplier | Speed | factor for movement |
| table | mods | a list of mods |
Get camera movement for mouse edge scrolling.Definition
getMouseEdgeScrollingMovement()Return Values
| table | mods | a list of multiplayer mods |
| x | direction | movement [-1, 1] |
| Z | direction | movement [-1, 1] |
Apply a movement to the camera (and view).Definition
applyCameraMovement(moveX X, moveZ Z, movementMultiplier Movement)Arguments
| moveX | X | direction movement [-1, 1] |
| moveZ | Z | direction movement [-1, 1] |
| movementMultiplier | Movement | speed factor |
| table | mods | a list of active mods |
Update the camera position and orientation based on terrain and zoom state.Definition
updateCameraPosition()Return Values
| integer | numMods | number of mods |
Update the placeable preview.Definition
updatePlaceablePreview()Return Values
| integer | numMods | number of valid mods |
Accept the current selection (selling or placing object).Definition
acceptSelection()Return Values
| boolean | areAvailable | true if all hashes are available else false |
Called when the player acknowledges the sell warning.Definition
sellWarningInfoOk()Return Values
| boolean | isAvailable | true if hash is available else false |
Called when the player confirms selling a placeable.Definition
onSellCallback()Return Values
| table | instance | instance of object |
Update placeable object slots usage.Definition
updateSlots()Return Values
| boolean | true | if loading was successful else false |
Called on BuyPlaceableEvent success.Definition
onPlaceableBought()Return Values
| boolean | success | true if added else false |
Called on BuyPlaceableEvent failure.Definition
onPlaceableBuyFailed()Return Values
| table | instance | instance of object |
Called on SellPlaceableEvent success.Definition
onPlaceableSold()Return Values
| table | instance | instance of object |
Called on SellPlaceableEvent failure.Definition
onPlaceableSellFailed()Return Values
| table | instance | instance of object |
Buy a currently previewed placeable.Definition
This operation also makes some final checks and dispatches information dialogs if anything goes wrong.
buyPlaceable()Return Values
| boolean | true | if loading was successful else false |
Find a sellable object under a cursor position.Definition
The results of the search are processed in PlacementScreenController.onSellObjectRaycast().
findSellObjectAt(posX Cursor, posY Cursor)Arguments
| posX | Cursor | X position in screen space |
| posY | Cursor | Y position in screen space |
| table | baleType | baleType object |
Sell a given placeable object.Definition
sellPlaceable()Return Values
| integer | toolTypeIndex | tool type index |
Set a placeable object for placement or selling.Definition
setPlacementItem(item Placement, isSellMode If, obj Sellable)Arguments
| item | Placement | item from the store |
| isSellMode | If | true, we want to sell the given item |
| obj | Sellable | object |
| table | instance | instance of object |
Calculate the placement height for a given world position based on the current player input height factor.Definition
calculatePlacementHeight()Return Values
| boolean | true | if loading was successful else false |
Check if a preview placement is valid (without terrain modification).Definition
isPlacementValid(Placeable Placeable, x Preview, y Preview, Z Preview, yRot Placeable, distance Distance)Arguments
| Placeable | Placeable | instance |
| x | Preview | X position in world space |
| y | Preview | Y position in world space |
| Z | Preview | Z position in world space |
| yRot | Placeable | Y rotation in radians |
| distance | Distance | from checking ray origin to ground |
| boolean | true | if loading was successful else false |
| True | if | the placement is valid, false otherwise |
| string | Reason | of being invalid |
Called when the placement raycast finishes.Definition
onPlacementRaycast(hitObjectId ID, x Raycast, y Raycast, Z Raycast, distance Distance)Arguments
| hitObjectId | ID | of the first hit object |
| x | Raycast | hit X position in world space |
| y | Raycast | hit Y position in world space |
| Z | Raycast | hit Z position in world space |
| distance | Distance | from ray origin to hit position |
| table | sprayType | sprayType object |
Start validation of a required terrain deformation for the current placement preview.Definition
startPlacementTerrainValidation()Return Values
| table | sprayType | the sprayType object |
Add a leveling area of a placeable to a terrain deformation object.Definition
addPlaceableLevelingArea(terrainDeform TerrainDeformation, levelArea Table, terrainBrushId Terrain)Arguments
| terrainDeform | TerrainDeformation | instance |
| levelArea | Table | which holds area nodes, {start=origin node, width=first side area delimiter node, height=second side area delimiter node} |
| terrainBrushId | Terrain | brush ID, currently this is a map layer index (zero-based) |
| table | sprayType | the sprayType object |
Add a leveling area for a ramp of a placeable to a terrain deformation object.Definition
addPlaceableRampArea(terrainDeform TerrainDeformation, rampArea Table, terrainBrushId Terrain, maxRampSlope Maximum, terrainRootNode Root)Arguments
| terrainDeform | TerrainDeformation | instance |
| rampArea | Table | which holds area nodes, {start=origin node, width=first side area delimiter node, height=second side area delimiter node, baseRotation={rx, ry, rz}, previewShape=nodeId} |
| terrainBrushId | Terrain | brush ID, currently this is a map layer index (zero-based) |
| maxRampSlope | Maximum | ramp slope as an angle in radians |
| terrainRootNode | Root | node of the terrain for height map check |
| string | fillTypeName | the sprayType name |
Called when a terrain deformation validation has finished.Definition
onTerrainValidationFinished(canDeform If, displacedVolume Volume, overlapsBlockedArea If)Arguments
| canDeform | If | true, the terrain deformation could be performed |
| displacedVolume | Volume | displaced by the terrain deformation operation in cubic meters (m3) |
| overlapsBlockedArea | If | true, the terrain modification overlaps a blocked area. |
| integer | fillTypeIndex | the sprayType index |
Called when an actual (not just preview) terrain deformation operation has finished.Definition
onTerrainDeformationFinished(canDeform If, displacedVolume Volume)Arguments
| canDeform | If | true, the terrain deformation was performed |
| displacedVolume | Volume | displaced by the terrain deformation operation in cubic meters (m3) |
| table | sprayType | the sprayType object |
Cancel a currently active terrain deformation process, if necessary.Definition
This will cancel any ongoing deformation checks and operations and release all relevant blocking flags.
cancelTerrainDeformation()Return Values
| integer | sprayTypeIndex | the sprayType index |
Called when the sell object raycast finishes.Definition
onSellObjectRaycast(hitObjectId ID, x Raycast, y Raycast, Z Raycast, distance Distance)Arguments
| hitObjectId | ID | of the first hit object |
| x | Raycast | hit X position in world space |
| y | Raycast | hit Y position in world space |
| Z | Raycast | hit Z position in world space |
| distance | Distance | from ray origin to hit position |
| table | sprayTypes | list of sprayTypes |
Determine the current camera position and orientation.Definition
determineCameraPosition()Return Values
| table | instance | instance of object |
| Camera | X | world space position |
| Camera | Z | world space position |
| Camera | Y | rotation in radians |
Determine if there is enough storage room to buy a currently previewed placeable object.Definition
canBuy()Return Values
| table | instance | instance of object |
Determine if a previewed placeable object can be placed at its current position.Definition
canPlace()Return Values
| boolean | true | if loading was successful else false |
Reset event input state.Definition
resetInputState()Return Values
| table | baleType | baleType object |
Register required action events.Definition
registerActionEvents()Return Values
| string | toolTypeName | tool type name |
Remove action events registered on this screen.Definition
removeActionEvents()Return Values
| integer | toolTypeIndex | tool type index |
Update action event activity states.Definition
updateActionEvents()Return Values
| integer | numToolTypes | number of tool types |
Player farm setting answer event.
-- Triggered in response to PlayerSetFarmEvent.
Create an empty instanceDefinition
emptyNew()Return Values
| table | instance | Instance of object |
| 27 | function PlayerSetFarmAnswerEvent:emptyNew() |
| 28 | local self = Event:new(PlayerSetFarmAnswerEvent_mt) |
| 29 | return self |
| 30 | end |
Create an instance of PlayerSetFarmAnswerEvent.Definition
new(int answerState, int farmId, string password)Arguments
| int | answerState | |
| int | farmId | Farm ID |
| string | password | Password used for PlayerSetFarmEvent |
| table | instance | Instance of PlayerSetFarmAnswerEvent |
| 38 | function PlayerSetFarmAnswerEvent:new(answerState, farmId, password) |
| 39 | local self = PlayerSetFarmAnswerEvent:emptyNew() |
| 40 | |
| 41 | self.answerState = answerState |
| 42 | self.farmId = farmId |
| 43 | self.password = password |
| 44 | |
| 45 | return self |
| 46 | end |
Writes network streamDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | network stream identification |
| table | connection | connection information |
| 52 | function PlayerSetFarmAnswerEvent:writeStream(streamId, connection) |
| 53 | streamWriteUIntN(streamId, self.answerState, PlayerSetFarmAnswerEvent.SEND_NUM_BITS) |
| 54 | streamWriteUIntN(streamId, self.farmId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 55 | |
| 56 | local passwordCorrect = self.answerState == PlayerSetFarmAnswerEvent.STATE.OK |
| 57 | local passwordSet = self.password ~= nil |
| 58 | if streamWriteBool(streamId, passwordCorrect and passwordSet) then |
| 59 | streamWriteString(streamId, self.password) |
| 60 | end |
| 61 | end |
Reads network streamDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | network stream identification |
| table | connection | connection information |
| 67 | function PlayerSetFarmAnswerEvent:readStream(streamId, connection) |
| 68 | self.answerState = streamReadUIntN(streamId, PlayerSetFarmAnswerEvent.SEND_NUM_BITS) |
| 69 | self.farmId = streamReadUIntN(streamId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 70 | |
| 71 | if streamReadBool(streamId) then |
| 72 | self.password = streamReadString(streamId) |
| 73 | end |
| 74 | |
| 75 | self:run(connection) |
| 76 | end |
Run eventDefinition
run(table connection)Arguments
| table | connection | connection information |
| 81 | function PlayerSetFarmAnswerEvent:run(connection) |
| 82 | if not connection:getIsServer() then -- server side, should not happen |
| 83 | g_logManager:devWarning("PlayerSetFarmAnswerEvent is a server to client only event") |
| 84 | else -- client side |
| 85 | if self.answerState == PlayerSetFarmAnswerEvent.STATE.OK then |
| 86 | g_messageCenter:publish(PlayerSetFarmAnswerEvent, {self.answerState, self.farmId, self.password}) |
| 87 | elseif self.answerState == PlayerSetFarmAnswerEvent.STATE.PASSWORD_REQUIRED then |
| 88 | g_messageCenter:publish(PlayerSetFarmAnswerEvent, {self.answerState, self.farmId}) |
| 89 | end |
| 90 | end |
| 91 | end |
Play UI sound sample mixin.
-- Add this mixin to a GuiElement to enable it to play UI sounds.
-- Added methods:
GuiElement:setPlaySampleCallback(callback): Set a callback for playing UI sound samples, signature: function(sampleName).
GuiElement:playSample(index, count): Called by the decorated GuiElement to play a sound sample using a name from GuiSoundPlayer.SOUND_SAMPLES.
GuiElement:disablePlaySample(): Permanently disables playing samples for special cases (i.e. separate sound logic)
--@category GUI
See GuiMixin:addTo().Definition
addTo()
Set a callback to play a UI sound sample.Definition
setPlaySampleCallback(table guiElement, function callback)Arguments
| table | guiElement | GuiElement instance |
| function | callback | Play sample callback, signature: function(sampleName) |
Request playing a UI sound sample identified by name.Definition
playSample(table guiElement, string sampleName)Arguments
| table | guiElement | GuiElement instance |
| string | sampleName | Sample name, use one of GuiSoundPlayer.SOUND_SAMPLES. |
Permanently disable playing samples on the decorated GuiElement for special cases.Definition
disablePlaySample()
Clone this mixin's state from a source to a destination GuiElement instance.Definition
clone()
Creating data grid
@param table customMt custom metatableDefinition
new()Return Values
| table | instance | instance of object |
| 19 | function PolygonChain:new(customMt) |
| 20 | local self = {} |
| 21 | setmetatable(self, customMt or PolygonChain_mt) |
| 22 | |
| 23 | self.controlNodes = {} |
| 24 | |
| 25 | return self |
| 26 | end |
Deletes data gridDefinition
delete()Code
| 30 | function PolygonChain:delete() |
| 31 | self.controlNodes = nil |
| 32 | end |
Render display as an overlay
--@category GUI
--@xmlConfig RenderElement#filename string Path to the i3d to be rendered in the overlay
Create the scene and the overlay. Call destroyScene to clean up resources.Definition
createScene()
Destroy the scene and the overlay, cleaning up resources.Definition
destroyScene()
Savegame persistence controller.
-- Handles loading and saving of mission game states.
Create a new instance of SavegameController.Definition
new()Return Values
| float | maxPtoRpm | max pto rpm |
Load the savegame meta data list.Definition
loadSavegames()Return Values
| float | neededPower | needed power |
Reset the storage device selection for saving.Definition
resetStorageDeviceSelection()Return Values
| string | l10n | l10n text |
| float | neededPower | needed power in kw |
| float | neededPower | needed power in hp |
Start updating the savegame list in the engine.Definition
updateSavegames()
Called when updating the save game list in the engine has finished.Definition
onSaveGameUpdateComplete()
Locate backups for a savegame instance in its backup path.Definition
locateBackups(string backupBasePath, string backupDirBase)Arguments
| string | backupBasePath | Backup base path |
| string | backupDirBase | Savegame-specific backup base directory |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Check and mark backups for deletion, based on creation time.Definition
assignBackupDeleteFlags()Return Values
| boolean |
Create an actual savegame backup.Definition
createBackup(table savegame, string backupBasePath, string backupDirFull, string backupDirBase)Arguments
| table | savegame | Savegame to back up |
| string | backupBasePath | Backup base path |
| string | backupDirFull | Full savegame-specific backup directory |
| string | backupDirBase | Savegame-specific backup base directory |
| boolean | updated | part was updated |
Create a backup of the given savegame.Definition
backupSavegame()Return Values
| boolean | isActive | is active |
Called when saving can begin or there is an IO problem with the target path.Definition
onSaveStartComplete()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Called when the saving game process has finished.Definition
onSaveComplete()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Called when a savegame has been deleted by the engine or an error occurred.Definition
onSavegameDeleted()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Check if a savegame can be started.Definition
getCanStartGame()Return Values
| boolean | canFold | ridge markers can be folden |
Check if a savegame can be deleted.Definition
getCanDeleteGame()Return Values
| boolean | success | success |
Get a savegame by index.Definition
getSavegame()Return Values
| boolean | success | success |
Check if a game is being saved.Definition
getIsSaving()Return Values
| boolean | isActive | speed rotating part is active |
Get the current saving error code.Definition
getSavingErrorCode()Return Values
| boolean | isActive | work area is active |
Check if the save game info is currently being updated.Definition
getIsWaitingForSavegameInfo()Return Values
| float | dirtMultiplier | current wear multiplier |
Get the number of known savegames from the engine.Definition
getNumberOfSavegames()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Check if the storage device is unavailableDefinition
isStorageDeviceUnavailable()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Base screen element. All full-screen GUI views inherit from this.
-- ScreenElement inherits from FrameElement and has no additional configuration, but contains UI logic shared across all
full screen views.
--@category GUI
-- @field pageSelector Paging navigation controls container, only defined if the view supports paging by configuration
Handle OK click event.Definition
onClickOk()Return Values
| bool | True | if the event was not used, false if it was used. |
Handle activate click event.Definition
onClickActivate()Return Values
| bool | True | if the event was not used, false if it was used. |
Handle cancel click event.Definition
onClickCancel()Return Values
| bool | True | if the event was not used, false if it was used. |
Handle previous page event.Definition
onPagePrevious()
Handle next page event.Definition
onPageNext()
Handle back click event.Definition
onClickBack(bool forceBack, bool usedMenuButton)Arguments
| bool | forceBack | If true, the screen must allow going back |
| bool | usedMenuButton | If true, the menu action key/button was used to trigger this event |
| bool | True | if the event was not used, false if it was used. |
Set the class of the return screen which should be opened when the "back" action is triggered on this screen.Definition
setReturnScreenClass()
Mute the next click sound. Used to override click sounds for the activate/cancel actionsDefinition
setNextScreenClickSoundMuted()
Advanced graphic settings frame.Parent
--@category GUI
SettingsAdvancedFrame = {}
InGameMenuFrameElement
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| boolean | true | if loading was successful else false |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | self | new instance of object |
Console settings frame.Parent
--@category GUI
SettingsConsoleFrame = {}
InGameMenuFrameElement
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| boolean | success | success |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | self | new instance of object |
Controls binding settings frame.
-- This is only supposed to be active in the PC version of the game.
--@category GUI
Create a new SettingsControlsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| boolean | success | success |
Late initialization.Definition
initialize(table controlsController)Arguments
| table | controlsController | New controls controller instance which provides input capture logic for control bindings. This controller must not be shared between components. |
| float | x | x translation |
| float | y | y translation |
| float | z | z translation |
| float | rx | x rotation |
| float | ry | y rotation |
| float | rz | z rotation |
| float | sx | x scale |
| float | sy | y scale |
| float | sz | z scale |
| integer | visibility | visibility |
Request to close this frame.Definition
If there are pending changes, a saving prompt will be shown and direct closing denied.
requestClose()
Handle saving confirmation on leaving the frame.Definition
onYesNoSaveControls()
Revert any binding changes since the last save.Definition
revertChanges()
Save binding changes.Definition
saveChanges()
Update menu button information.Definition
updateMenuButtons()
Update header elements' state.Definition
updateHeader()
Set the device page.Definition
setDevicePage(bool toKeyboard)Arguments
| bool | toKeyboard | If true, set the page to keyboard, otherwise set it to gamepad |
Switch the current binding device (kb / mouse or gamepads).Definition
Switches between tables if possible.
switchDevice()
Set up the controls view with input binding tables.Definition
setupControlsView()
Assigns table data to all device input binding table elements.Definition
If called after initialization, this will completely rebuild the data. This may be desired or required when new
devices are detected.
assignDeviceTableData()Return Values
| boolean | success | success |
Set a callback for requesting a button display update (e.g. when switching device pages).Definition
setRequestButtonUpdateCallback()Return Values
| table | instance | Instance of object |
Set the controls notification message.Definition
setControlsMessage(messageId ID, additionalText [optional], addLine [optional])Arguments
| messageId | ID | of message as defined as a constant in ControlsController. |
| additionalText | [optional] | More text to add to the message. Must always be a list, since it can be used to hold formatting arguments. |
| addLine | [optional] | If true, will add the message to the previous text instead of overwriting it. |
| boolean | success | success |
Notify the screen that input gathering has finished.Definition
Unlocks navigation and closes any open dialogs.
notifyInputGatheringFinished(madeChange True)Arguments
| madeChange | True | if any action has been assigned a key/button |
| integer | fillType | current fill type id |
Show an input prompt dialog.Definition
showInputPrompt()Return Values
| integer | fillLevel | current fill level |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| boolean | canBeSold | bale can be sold |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| table | instance | Instance of object |
Store data bindings for a device category.Definition
The data bindings have an abstract field name (e.g. "action") as key and a table column name as value. The column
name must correspond to the name of a table row template element's name, which is also configured as a column name
in the table configuration.
bindControls(table bindings, int deviceCategory)Arguments
| table | bindings | Data binding table {name: column} |
| int | deviceCategory | Device category for which these bindings are stored |
| table | instance | Instance of object |
Get the action data associated with a clicked action input button.Definition
getActionDataFromClickedTableButton(tableButton Clicked, isKeyboard If)Arguments
| tableButton | Clicked | action input button element |
| isKeyboard | If | true, the input action device is keyboard and mouse, otherwise it's a gamepad/controller |
| boolean | success | success |
Update the controls display data.Definition
updateDisplay()Return Values
| boolean | success | success |
Handle clicks on input action buttons.Definition
onInputClicked(int deviceCategory, int bindingId, table actionData)Arguments
| int | deviceCategory | Input action device category |
| int | bindingId | Binding ID as defined in ControlsController (primary, secondary, tertiary) |
| table | actionData | Input action data |
| table | instance | Instance of object |
Handle "reset to defaults" button activation.Definition
onClickDefaults()Return Values
| boolean | success | success |
Handle clicking the keyboard controls header.Definition
onClickKeyboardHeader()Return Values
| boolean | success | success |
Handle clicking the gamepad controls header.Definition
onClickGamepadHeader()Return Values
| float | offset | offset |
Console settings frame.Parent
--@category GUI
SettingsDeviceFrame = {}
InGameMenuFrameElement
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| boolean | isCloserToFront | is closer to front |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| boolean | canInteract | can interact |
Get the frame's main content element's screen position.Definition
update()Return Values
| boolean | canClose | can close silo |
Update controller and mouse sensitivity and deadzone settings values.Definition
updateController()Return Values
| boolean | canOpen | can open silo |
General game settings frame.
--@category GUI
-- @field inputHelpMode In-game input display help mode option (auto, keyboard, gamepad)
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| float | x | x world position |
| float | y | y world position |
| float | z | z world position |
Get the frame's main content element's screen position.Definition
getMainElementPosition()
Settings menu model.
-- Provides an interface model between game settings and the UI for re-use between several components. The model keeps
a common, transient state until saved. When saving, the settings are applied to the global game settings and written
to the player's configuration file.
--@category GUI
Create a new instance.Definition
new(table gameSettings, int settingsFileHandle, table l10n, table soundMixer)Arguments
| table | gameSettings | GameSettings object which holds the currently active and applied game settings |
| int | settingsFileHandle | Engine file handle of the player's settings file |
| table | l10n | I18N reference for localized display string resolution |
| table | soundMixer | SoundMixer reference for direct application of volume settings |
| table | SettingsModel | instance |
Initialize model.Definition
Read current configuration settings and populate valid display and configuration option values.
initialize()
Add managed valid settings which receive their initial value from the loaded game settings or the engine.Definition
addManagedSettings()
Add a setting to the model.Definition
Reader and writer functions need to be provided which transform display values (usually indices) to actual setting
values and interact with the current game setting or engine states. Writer function can have side-effects, such as
directly applying values to the engine state or modifying dependent sub-settings.
addSetting(string gameSettingsKey, function readerFunction, function writerFunction, boolean noRestartRequired)Arguments
| string | gameSettingsKey | Key of the setting in GameSettings |
| function | readerFunction | Function which reads and processes the setting value identified by the key, signature: function(settingsKey) |
| function | writerFunction | Function which processes and writes the setting value identified by the key, signature: function(value, settingsKey) |
| boolean | noRestartRequired | true if no restart is required to apply the setting |
Set a settings value.Definition
setValue(string settingKey, table value)Arguments
| string | settingKey | Setting key, use one of the values in SettingsModel.SETTING. |
| table | value | New setting value |
Get a settings value.Definition
getValue(string settingKey)Arguments
| string | settingKey | Setting key, use one of the values in SettingsModel.SETTING. |
| table | Currently | active (changed) settings value |
Set the settings file handle when it changes (e.g. possible in the gamepad sign-in process).Definition
setSettingsFileHandle()
Refresh settings values from their reader functions.Definition
Use this when other components might have changed the settings state and the model needs to reflect those changes
now.
refresh()
Refresh settings values from their reader functions.Definition
Use this when other components might have changed the settings state and the model needs to reflect those changes
now.
refreshChangedValue()
Check if any setting has been changed in the model.Definition
hasChanges()Return Values
| bool | True | if any setting has been changed, false otherwise |
Check if any setting has been changed in the model.Definition
needsRestartToApplyChanges()Return Values
| bool | True | if any setting has been changed, false otherwise |
Apply the currently held, transient settings to the game settings.Definition
applyChanges(bool doSave)Arguments
| bool | doSave | If true, the changes will also be persisted to storage. |
Save the game settings which may have been modified by this model.Definition
This will not apply transient changes but only persist the currently applied game settings.
saveChanges()
Populate value and string lists for control elements display.Definition
createControlDisplayValues()
Get valid brightness option texts.Definition
getBrightnessTexts()
Get valid FOV Y option texts.Definition
getFovYTexts()
Get valid UI scale texts.Definition
getUiScaleTexts()
Get valid audio volume texts.Definition
getAudioVolumeTexts()
Get valid camera sensitivity texts.Definition
getCameraSensitivityTexts()
Get valid camera sensitivity texts.Definition
getVehicleArmSensitivityTexts()
Get valid camera sensitivity texts.Definition
getSteeringBackSpeedTexts()
Get valid money unit (=currency) texts.Definition
getMoneyUnitTexts()
Get valid distance unit texts.Definition
getDistanceUnitTexts()
Get valid temperature unit texts.Definition
getTemperatureUnitTexts()
Get valid area unit texts.Definition
getAreaUnitTexts()
Get valid radio mode texts.Definition
getRadioModeTexts()
Build the default reader function.Definition
Reads a value directly from the game settings.
makeDefaultReaderFunction()
Build the default writer function.Definition
Writes a value directly to the game settings.
makeDefaultWriterFunction()
Add a setting which can be directly read and written from and to the game settings.Definition
addDirectSetting()
Add the performance class setting.Definition
addPerformanceClassSetting()
Add the window mode setting.Definition
addWindowModeSetting()
Add the language setting.Definition
addLanguageSetting()
Add the language setting.Definition
addMPLanguageSetting()
Add the brightness setting.Definition
addBrightnessSetting()
Add the vertical synchronization setting.Definition
addVSyncSetting()
Add the vertical field of view setting.Definition
addFovYSetting()
Add the UI scale setting.Definition
addUIScaleSetting()
Add the camera sensitivity setting.Definition
addCameraSensitivitySetting()
Add the vehicleArm sensitivity setting.Definition
addVehicleArmSensitivitySetting()
Add the master volume setting.Definition
addMasterVolumeSetting()
Add the music volume setting.Definition
addMusicVolumeSetting()
Add the environment volume setting.Definition
addEnvironmentVolumeSetting()
Add the vehicle volume setting.Definition
addVehicleVolumeSetting()
Add the radio volume setting.Definition
addRadioVolumeSetting()
Add the gui volume setting.Definition
addVolumeGUISetting()
Add the steering back speed setting.Definition
addSteeringBackSpeedSetting()
Convert a settings brightness value to a game gamma value.Definition
convertBrightnessToGamma()
Convert a game gamma value to a settings brightness value.Definition
convertGammaToBrightness()
Convert a v-sync option value to boolean.Definition
getVSyncByIndex()
Convert a v-sync boolean value to an index value.Definition
getVSyncIndex()
Main Menu Settings Screen.
--@category GUI
-- @field pagingElement Paging controller element
Update page enabled states.Definition
updatePages()
Register a page frame element in the menu.Definition
This does not add the page to the paging component of the menu.
registerPage(table pageFrameElement, int position, function enablingPredicateFunction)Arguments
| table | pageFrameElement | Page FrameElement instance |
| int | position | [optional] Page position index in menu |
| function | enablingPredicateFunction | [optional] A function which returns the current enabling state of the page at any time. If the function returns true, the page should be enabled. If no argument is given, the page is always enabled. |
| table | self | instance of class event |
Unregister a page frame element identified by class from the menu.Definition
This does not remove the page from the paging component of the menu or the corresponding page tab from the header.
unregisterPage(table pageFrameClass)Arguments
| table | pageFrameClass | FrameElement descendant class of a page which was previously registered |
| table | instance | instance of event |
| bool | True | if there was a page of the given class and it was unregistered |
| table | Unregistered | page controller instance or nil |
| table | Unregistered | page root GuiElement instance or nil |
| table | Unregistered | page tab ListElement instance of nil |
Add a page tab in the menu header.Definition
Call this synchronously with SettingsScreen:registerPage() to ensure a correct order of pages and tabs.
addPageTab()Return Values
| table | self | instance of class event |
Update page tabs display after any page changes.Definition
updatePageTabDisplay()Return Values
| table | instance | instance of event |
Set enabled state of a page tab in the header.Definition
setPageTabEnabled()Return Values
| table | instance | Instance of object |
Rebuild page tab list in order.Definition
rebuildTabList()Return Values
| table | instance | Instance of object |
Define default properties and retrieval collections for menu buttons.Definition
setupMenuButtonInfo()Return Values
| table | instance | Instance of object |
Handle activation of page selection.Definition
onClickPageSelection()Return Values
| table | instance | Instance of object |
Handle previous page event.Definition
onPagePrevious()Return Values
| table | instance | Instance of object |
Handle next page event.Definition
onPageNext()Return Values
| table | instance | Instance of object |
Handle changing to another menu page.Definition
onPageChange()Return Values
| boolean | allowsAutoDelete | allows auto delete |
Update the buttons panel when a given page is visible.Definition
updateButtonsPanel()Return Values
| boolean | hasMoved | has moved |
Clear menu button actions, events and callbacks.Definition
clearMenuButtonActions()Return Values
| boolean | inScope | in scope |
Assign menu button information to the in-game menu buttons.Definition
assignMenuButtonInfo()Return Values
| float | priority | priority |
Initialize page display labels from page titles.Definition
initializePageDisplay()Return Values
| string | rigidBodyType | rigid body type |
Handle menu confirmation input event.Definition
onClickOk()Return Values
| table | instance | Instance of object |
Shop categories frame for the in-game menu shop.
-- Displays categories/brands or purchasable items in a tile-layout.
--@category GUI
Create a new ShopCategoriesFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| boolean | success | success |
Initialize with categories to be displayed.Definition
Categories must be provided as an array of tables like {id=<id>, iconFilename=<path>, label=<text>}. This will add
a category element per entry to the display list.
initialize(table categories, function categoryClickedCallback, table headerIconUVs, string headerText)Arguments
| table | categories | Category definitions array |
| function | categoryClickedCallback | Notification callback for category activation(click/enter), signature: function(selectedId, baseCategoryIconUVs, baseCategoryLabel, clickedCategoryLabel) |
| table | headerIconUVs | UV coordinates of the header icon to display |
| string | headerText | Header text to display |
| table | instance | Instance of object |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | self | new instance of object |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| boolean | success | success |
Update scroll button visibility based on the currently visible list items.Definition
updateScrollButtons()Return Values
| boolean | success | success |
Handle a click / button activation on a category.Definition
onClickCategory()Return Values
| boolean | allow | allow fill type |
Handle a double click on a category.Definition
onDoubleClickCategory()Return Values
| float | fillLevel | fill level |
Handle navigation selection of a category element.Definition
onCategorySelected()Return Values
| float | freeCapacity | free capacity |
Handle click on left navigation button.Definition
onClickLeft()Return Values
| table | instance | Instance of object |
Handle click on right navigation button.Definition
onClickRight()Return Values
| table | instance | Instance of object |
Handle a list scrolling event.Definition
onScroll()Return Values
| table | instance | Instance of object |
Vehicle Shop and Configuration Screen.
--@category GUI
-- @field shopMoneyBox Layout box for player money information
Create the shop configuration screen.Definition
new(table shopController, table messageCenter, table l10n, table i3dManager, table brandManager, table configurationManager, table vehicleTypeManager, table inputManager, table inputDisplayManager)Arguments
| table | shopController | ShopController instance, shared across all shop screens. |
| table | messageCenter | MessageCenter reference for local network UI event handling |
| table | l10n | I18N localization manager reference |
| table | i3dManager | I3DManager reference for asset loading |
| table | brandManager | BrandManager reference |
| table | configurationManager | ConfigurationManager reference |
| table | vehicleTypeManager | VehicleTypeManager reference |
| table | inputManager | InputBinding reference for camera input handling |
| table | inputDisplayManager | InputDisplayManager reference for camera input glyph display |
| boolean | isActivateable | is activateable |
| table | ShopConfigScreen | instance |
Create input help glyphs.Definition
createInputGlyphs()Return Values
| table | instance | Instance of object |
Create animations for fading the screen.Definition
createFadeAnimations()Return Values
| boolean | success | success |
Setter function for fading animations.Definition
fadeScreen()Return Values
| table | instance | Instance of object |
Load the workshop background geometry.Definition
This will not yet link the workshop into the scene graph. That operation is done on the fly on open and close.
createWorkshop()Return Values
| boolean | success | success |
Set the node ID of the workshop model when loaded.Definition
setWorkshopNode()Return Values
| boolean | success | success |
Create and initialize the workshop camera.Definition
createCamera()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Reset camera rotation and distance fields.Definition
The changes will be applied on the next update when the camera is repositioned.
resetCamera()
Delete this screen instance and release resources.Definition
delete()Return Values
| table | instance | Instance of object |
Update the current balance display.Definition
updateBalanceText()Return Values
| boolean | success | success |
Process and return daily upkeep cost of a store item.Definition
processStoreItemUpkeep()Return Values
| boolean | success | success |
Process and return the power ouput of a store item.Definition
processStoreItemPowerOutput()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Process and return the fuel capacity of a store item.Definition
processStoreItemFuelCapacity()
Process and return the fuel capacity of a store item.Definition
processStoreItemDefCapacity()Return Values
| table | instance | Instance of object |
Process and retuen the maximum speed of a store item.Definition
processStoreItemMaxSpeed()Return Values
| boolean | success | success |
Process and return the fill capacity and unit of a store item.Definition
processStoreItemCapacity()Return Values
| table | instance | Instance of object |
Process and return the working width of a store item.Definition
processStoreItemWorkingWidth()Return Values
Process and return the working speed of a store item.Definition
processStoreItemWorkingSpeed()Return Values
Process and return the power requirement of a store item.Definition
processStoreItemPowerNeeded()Return Values
| bool | returns | true if placement successful |
Process a store item's attribute data.Definition
Changes icons and display texts.
processAttributeData()Return Values
| boolean | success | success |
Check the base and upgrade cost of a storeItem or current vehicle and whether or not there are any changes.Definition
getConfigurationCostsAndChanges()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
| float | Base | price |
| float | Upgrade | price |
| bool | True | if there are changes |
Update price display data.Definition
updatePriceData()
Update display data for a store item and/or concrete vehicle.Definition
updateData()Return Values
| bool | return | true if doghouse can be activated to fill bowl |
Override an option element's focus behavior.Definition
If the first option is focused and navigation goes up, the option slider should be triggered instead. Vice versa for
the last option and down direction.
overrideOptionFocus()Return Values
Get the default color index of a color configuration.Definition
getDefaultConfigurationColorIndex(string configName, table configItems, table vehicle)Arguments
| string | configName | Configuration attribute name of the color |
| table | configItems | Array of configuration attribute options |
| table | vehicle | [optional] Existing vehicle which is being configured |
| bool | true | if registration went well |
Disable unused option elements.Definition
During configuration loading, the UI elements are populated in order until no more elements or attributes are
available. If there are more UI elements than attributes, disable those elements now.
disableUnusedOptions(int currentOptionIndex, int currentColorIndex)Arguments
| int | currentOptionIndex | Index of first unused configuration option element |
| int | currentColorIndex | Index of first unused color picker element |
| bool | true | if registration went well |
Update button states.Definition
updateButtons()Return Values
| table | instance | Instance of object |
Load the current configuration of a given vehicle store item.Definition
loadCurrentConfiguration()Return Values
| boolean | success | success |
Handles asynchronous vehicle loading event.Definition
onVehicleLoaded()Return Values
| boolean | isActivateable | is activateable |
Update visibility and values of the options slider.Definition
updateSlider()Return Values
| table | instance | Instance of object |
Handle slider change events.Definition
Function must be targeted by screen configuration.
onSliderChanged()Return Values
| boolean | success | success |
Update all display data based on a store item and / or vehicle.Definition
updateDisplay(table storeItem, table vehicle, int scrollValue, bool doNotReload)Arguments
| table | storeItem | Store item reference |
| table | vehicle | Vehicle reference (for customizing) |
| int | scrollValue | Current scroll value in cases where there are more options than display elements. |
| bool | doNotReload | If true, the vehicle is not reloaded (use e.g. when scrolling) |
| boolean | success | success |
Set the current mission reference at the start of the mission.Definition
setCurrentMission()Return Values
| boolean | success | success |
Set the economy manager reference at the start of the missionDefinition
setEconomyManager()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Process map data.Definition
Extracts the workshop position if possible.
loadMapData()
Set the workshop world position if a map has a custom position defined.Definition
setWorkshopWorldPosition()Return Values
| boolean | showInfo | show info |
Delete all preview vehicles and clear stored array.Definition
deletePreviewVehicles()Return Values
| float | fillDelta | real fill delta |
Set the current store item or vehicle to be modified.Definition
setStoreItem()Return Values
| float | isAllowed | is allowed |
Set a callback to request exiting this screen and the entire shop at once.Definition
setRequestExitCallback()Return Values
| table | self | instance of class event |
Override and shadow of GuiElement:shouldFocusChange().Definition
Always allows focus change.
shouldFocusChange()Return Values
| table | instance | instance of event |
Set the price label for a given configuration.Definition
setConfigPrice()Return Values
| table | instance | Instance of object |
Handle the result of the color picking dialog.Definition
onPickColor()Return Values
| boolean | success | success |
Select the first configuration option.Definition
selectFirstConfig()Return Values
| table | instance | Instance of object |
Process a configuration set of a store item.Definition
processStoreItemConfigurationSet(table storeItem, table configSet, table vehicle)Arguments
| table | storeItem | StoreItem reference |
| table | configSet | StoreItem configuration set {name = name, configurations={i=configName}} |
| table | vehicle | [optional] Existing Vehicle instance reference |
| boolean | success | success |
Process a sub-configuration option of a store item.Definition
Sub-configurations are fine-grained configuration sets within vehicle configurations, e.g. wheel brands with distinct
options per brand.
processStoreItemSubConfigurationOption(table storeItem, string configName, table vehicle)Arguments
| table | storeItem | StoreItem reference |
| string | configName | Vehicle configuration name |
| table | vehicle | [optional] Existing Vehicle instance |
| boolean | isActiveForInput | is active for input |
Process a configuration option of a store item.Definition
processStoreItemConfigurationOption(table storeItem, string configName, table configItems, table vehicle, bool isSubConfigOption)Arguments
| table | storeItem | StoreItem reference |
| string | configName | Vehicle configuration name |
| table | configItems | Array of configuration items (see StoreItemUtil.addConfigurationItem() for structure) |
| table | vehicle | [optional] Existing Vehicle instance |
| bool | isSubConfigOption | [optional, default=false] If true, treats this option as the selection of a sub-configuration set |
| boolean | isActiveForSound | is active for sound |
Process a color option of a store item.Definition
processStoreItemColorOption(table storeItem, string configName, table colorItems, int colorPickerIndex, table vehicle)Arguments
| table | storeItem | StoreItem reference |
| string | configName | Vehicle configuration name |
| table | colorItems | Array of color configuration items |
| int | colorPickerIndex | Element index in this screen of the color picker to populate with the configuration data |
| table | vehicle | [optional] Existing Vehicle instance |
| boolean | ||
| string | warningMessage | warning message displayed in the shop |
Process store item configurations into more convenient data structures for display.Definition
The processed data is stored in instance fields.
processStoreItemConfigurations()
Update a configuration option element with the configuration set selection.Definition
updateConfigSetOptionElement()Return Values
| table | self | instance of class event |
Update a configuration option element with a regular configuration option.Definition
updateConfigOptionElement()Return Values
| table | instance | instance of event |
Update a configuration option element with a sub-configuration selection.Definition
updateSubConfigOptionElement()Return Values
| table | instance | Instance of object |
Update display data in config options based on the current scroll value.Definition
updateConfigOptionsData()Return Values
| boolean | isInRange | is in range |
| int | Number | of used config option elements |
Update config option navigation behavior.Definition
Overrides focus navigation between elements and including scrolling.
updateConfigOptionsNavigation(int scrollValue, int usedConfigElementCount, int usedColorElementCount)Arguments
| int | scrollValue | Current scrolling value |
| int | usedConfigElementCount | Number of config option elements used |
| int | usedColorElementCount | Number of color option elements used |
| boolean | isInRange | is in range |
Update configuration display elements based on the currently available configurations and scrolling value.Definition
updateConfigOptionsDisplay()Return Values
| boolean | success | success |
Update the configuration screen's state.Definition
update()Return Values
| boolean | success | success |
Update camera orientation and position.Definition
updateCamera()Return Values
| boolean | success | success |
Update the depth of field blur effect.Definition
updateDepthOfField()Return Values
| boolean | success | success |
Draw the shop config screen.Definition
Override for custom drawing.
draw()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Event function called when opening this screen.Definition
onOpen()
Event function called when closing this screen.Definition
onClose()Return Values
| integer | price | price |
Handle a click or button activation of the "Buy" button.Definition
onClickOk()Return Values
Buying confirmation dialog callback.Definition
onYesNoBuy()Return Values
| integer | dailyUpkeep | daily up keep |
Handle vehicle buy event.Definition
onVehicleBought()Return Values
| integer | sellPrice | sell price |
Handle a click or button activation of the "Lease" button.Definition
onClickActivate()Return Values
| {i | = | {x, y, z, side1x, side1y, side1z, side2x, side2y, side2z}}, all numbers in world space coordinates |
Handle leasing dialog confirmation.Definition
onYesNoLease()Return Values
| table | capacityAndUnit | capacity and unit |
Handle back button activation.Definition
onClickBack()Return Values
| integer | incomePerHour | income per hour |
Trigger the callback set in setCallbacks() to notify another component of the current configuration.Definition
onCallback(bool leaseItem, table storeItem, table configurations, float price)Arguments
| bool | leaseItem | If true, the item should be leased |
| table | storeItem | StoreItem instance |
| table | configurations | Item configuration attributes table |
| float | price | Item configuration price |
| table | instance | Instance of object |
Update input glyphs when input context changes.Definition
updateInputGlyphs()Return Values
| boolean | success | success |
Register required input action events.Definition
registerInputActions()Return Values
| boolean | success | success |
Disable alternate bindings for menu navigation.Definition
This will disable some default bindings which interfere with camera controls (e.g. D-Pad on controller). Whenever any
input event is modified, this method must be called again afterwards.
disableAlternateBindings()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Handle input for camera left/right.Definition
onCameraLeftRight()
Handle input for camera up down.Definition
onCameraUpDown()Return Values
| boolean | success | success |
Handle input for camera zoom.Definition
onCameraZoom()Return Values
| boolean | success | success |
Update input for this frame.Definition
updateInput()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Limit camera X rotation by the constant maximum and the maximum camera height.Definition
limitXRotation()
Update input context and activate suitable events.Definition
updateInputContext()Return Values
| table | capacityAndUnit | capacity and unit |
Shop buying/selling process controller.
-- Handle buying and selling logic, display data and synchronization of shop screens.
--@category GUI
Create a new ShopController.Definition
new(table messageCenter, table l10n, table storeManager, table brandManager, table fillTypeManager)Arguments
| table | messageCenter | MessageCenter reference for local network UI event handling |
| table | l10n | I18N reference |
| table | storeManager | StoreManager reference for store item loading |
| table | brandManager | BrandManager reference for brands loading |
| table | fillTypeManager | FillTypeManager reference for fill type data access |
| integer | incomePerHour | income per hour |
Subscribe to receive notifications for shop events.Definition
subscribeEvents()Return Values
| table | instance | Instance of object |
Add a brand loaded from store items to the display collection.Definition
addBrandForDisplay()Return Values
| boolean | success | success |
Add a category loaded from store items to a suitable display collection.Definition
addCategoryForDisplay()Return Values
| boolean | success | success |
Load brands and items category data.Definition
load()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Set the network client on mission loading.Definition
setClient()
Set the current mission after loading.Definition
setCurrentMission()Return Values
| table | capacityAndUnit | capacity and unit |
Set the callback to trigger a shop item list update.Definition
setUpdateShopItemsCallback()Return Values
| integer | incomePerHour | income per hour |
Set the callback to trigger a full shop update.Definition
setUpdateAllItemsCallback()Return Values
| table | instance | Instance of object |
Set the callback to switch views to the vehicle configuration screen.Definition
setSwitchToConfigurationCallback()Return Values
| boolean | success | success |
Set the callback to enter placement mode.Definition
setStartPlacementModeCallback()Return Values
| boolean | success | success |
Filter a collection of owned items by their farm ID.Definition
The given collection will not be modified.
filterOwnedItemsByFarmId(table ownedFarmItems, int farmId)Arguments
| table | ownedFarmItems | Collection of owned farm items (for structure see BaseMission.addItemToList) |
| int | farmId | Farm ID |
| boolean | success | success |
| table | New | collection of filtered owned items |
Set the array of owned items for the garage view of the current player's farm.Definition
setOwnedFarmItems(table ownedFarmItems, int playerFarmId)Arguments
| table | ownedFarmItems | Collection of owned farm items (for structure see BaseMission.addItemToList) |
| int | playerFarmId | Farm ID |
| table | instance | Instance of object |
Set the array of leased items for the garage view of the current player's farm.Definition
setLeasedFarmItems(table leasedFarmItems, int playerFarmId)Arguments
| table | leasedFarmItems | Collection of owned farm items (for structure see BaseMission.addItemToList) |
| int | playerFarmId | Farm ID |
| boolean | success | success |
Update the shop controller state.Definition
Delays buy events by one frame.
update()Return Values
| boolean | success | success |
Make a display item out of a store item and optionally a concrete object.Definition
makeDisplayItem()Return Values
| boolean | success | success |
Get an array of items owned by the player's farm.Definition
getOwnedItems()Return Values
| table | instance | Instance of object |
Get an array of vehicles leased by the player's farm.Definition
getLeasedVehicles()Return Values
| boolean | success | success |
Get the collection of owned farm items for the current player's farm.Definition
getOwnedFarmItems()Return Values
| boolean | isActivateable | is activateable |
Get the collection of leased farm items for the current player's farm.Definition
getLeasedFarmItems()Return Values
| table | instance | Instance of object |
Get an array of known brands structured for display.Definition
getBrands()Return Values
| bool | true | if ok |
| table | Array | of brands in the form of {i={id = brand.index, iconFilename = brand.image, label = brand.title}} |
Get an array of known vehicle categories structured for display.Definition
getVehicleCategories()Return Values
| table | returns | the graphics root node |
| table | Array | of vehicle categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
Get an array of tool categories structured for display.Definition
getToolCategories()Return Values
| bool | true | if input is allowed. |
| table | Array | of tool categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
Get an array of object categories structured for display.Definition
getObjectCategories()Return Values
| float | posX | x position of player |
| float | posY | y position of player |
| float | posZ | z position of player |
| float | graphicsRotY | rotation of the player |
| table | Array | of object categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
Get an array of placeable categories structured for display.Definition
getPlaceableCategories()Return Values
| table | Array | of placeable categories in the form of {i={id = category.index, iconFilename = category.image, label = category.title}} |
Get store items for a given brand ID.Definition
getItemsByBrand()
Get shop display items for a given category name.Definition
getItemsByCategory()
Check if a store item can be bought at a given price and considering the current game state.Definition
canBeBought()Return Values
| bool | returns | true if distance to player root node is lower than clip distance |
Buy an item.Definition
buy(table storeItem, bool outsideBuy)Arguments
| table | storeItem | StoreItem to buy |
| bool | outsideBuy | If true, means that an item is "bought" without anyone paying for it, e.g. as an achievement bonus |
| float | returns | calculated priority |
Buy a vehicle.Definition
buyVehicle()Return Values
| string | that | will be displayed on console |
Buy object confirmation dialog callback.Definition
onYesNoBuyObject()Return Values
| string | that | will be displayed on console |
Buy an object.Definition
buyObject()Return Values
| string | that | will be displayed on console |
Buy a hand tool.Definition
buyHandTool()Return Values
| bool | returns | true object that was hit is valid |
Sell an item.Definition
sell(table item)Arguments
| table | item | Item to sell |
| bool | returns | true object that was hit is valid |
Show dialog for selling confirmation.Definition
sellWarningInfoClickOk()Return Values
| table | returns | the handtool |
Selling confirmation dialog callback.Definition
onSellCallback()Return Values
| string | Filename | of currently equipped hand tool or empty string if no hand tool is equipped |
Event handling function which is called when selling an item has been confirmed.Definition
onSellItem()Return Values
| always | returns | false |
Sell a placeable object.Definition
Activates placement mode if the player owns more than one instance of the given placeable.
sellPlaceable()Return Values
| string | that | will be displayed on console |
Sell a hand tool.Definition
sellHandTool()Return Values
| table | instance | instance of object |
Sell a vehicle.Definition
sellVehicle()Return Values
| boolean | true | if loading was successful else false |
Set a buying configuration is used when sending buy events during update().Definition
setConfigurations()Return Values
| boolean | true | if loading was successful else false |
Finalize a buying (or leasing) process and trigger events according to the requested item type during update().Definition
finalizeBuy()Return Values
| boolean | true | if added successful else false |
Event callback on local SellHandToolEvent execution.Definition
onHandToolSellEvent()Return Values
| table | player | the player object |
Event callback on successful SellHandToolEvent.Definition
onHandToolSold()Return Values
| table | player | the player object |
Event callback on failed SellHandToolEvent.Definition
onHandToolSellFailed()Return Values
| integer | number | number of models |
Event callback on local BuyVehicleEvent execution.Definition
onVehicleBuyEvent()Return Values
| table | instance | Instance of object |
Event callback on successful BuyVehicleEvent.Definition
onVehicleBought()Return Values
| table | instance | Instance of object |
Event callback on failed BuyVehicleEvent.Definition
onVehicleBuyFailed()Return Values
| table | instance | Instance of object |
Event callback on local BuyObjectEvent execution.Definition
onObjectBuyEvent()Return Values
| table | instance | Instance of PlayerSetFarmAnswerEvent |
Event callback on successful BuyObjectEvent.Definition
onObjectBought()Return Values
| table | instance | Instance of object |
Event callback on failed BuyObjectEvent.Definition
onObjectBuyFailed()Return Values
| table | instance | Instance of object |
Event callback on local BuyHandToolEvent execution.Definition
onHandToolBuyEvent()Return Values
| table | instance | Instance of object |
Event callback on successful BuyHandToolEvent.Definition
onHandToolBought()Return Values
| table | instance | Instance of object |
Event callback on failed BuyHandToolEvent.Definition
onHandToolBuyFailed()Return Values
| table | instance | instance of object |
Event callback on local SellVehicleEvent execution.Definition
onVehicleSellEvent()Return Values
| bool | returns | true if player can interact with object |
Event callback on successful SellVehicleEvent.Definition
onVehicleSold()Return Values
| table | instance | instance of object |
Event callback on failed SellVehicleEvent.Definition
onVehicleSellFailed()Return Values
| bool | returns | true if player can feed an animal |
Event callback on local SellPlaceableEvent execution.Definition
onPlaceableSellEvent()Return Values
| table | instance | instance of object |
Event callback on successful SellPlaceableEvent.Definition
onPlaceableSold()Return Values
| bool | returns | true if player can interact with an animal |
Event callback on failed SellPlaceableEvent.Definition
onPlaceableSellFailed()Return Values
| table | instance | instance of object |
Buying process termination (success or failure) information dialog callback.Definition
onBoughtCallback()Return Values
| bool | returns | true if player can pet an animal |
Selling process termination (success or failure) information dialog callback.Definition
onSoldCallback()Return Values
| table | instance | instance of object |
Brand sorting function.Definition
brandSortFunction()Return Values
| bool | returns | true if player can ride an animal |
Category sorting function.Definition
Uses the category order index which was set when loading. This restores category order to the XML definition.
categorySortFunction()Return Values
| table | instance | instance of object |
Display item sorting function (for owned and leased objects in the garage).Definition
First sorts by the category order index provided to the display item, then by price within the same category. As a
final tie-breaker for vehicles owned at mission start, the object ID is compared to guarantee a sort order
resolution.
displayItemSortFunction()Return Values
| table | instance | instance of object |
Display data class for shop items.
-- Both store items and concrete items can be represented by this class to be displayed in the shop for buying or
selling.
Create a new ShopDisplayItem instance.Definition
new(table storeItem, table concreteItem, table attributeIconProfiles, table attributeValues, table fillTypeFilenames, table seedTypeFilenames, string functionText, int orderValue)Arguments
| table | storeItem | Store item definition table, see StoreManager.lua. |
| table | concreteItem | Concrete game object of the given store item type or ShopDisplayItem.NO_CONCRETE_ITEM. |
| table | attributeIconProfiles | Array of UI profiles for attribute icons |
| table | attributeValues | Array of value display strings for attributes which correspond to the icons |
| table | fillTypeFilenames | Array of fill type icon file names |
| table | seedTypeFilenames | Array of seed fill type icon file names |
| string | functionText | Description text of the item's function |
| int | orderValue | Display item category order value, lower is higher order |
Shop items frame for the in-game menu shop.
-- Displays purchasable items of a common category in a horizontal list layout.
--@category GUI
Create a new ShopItemsFrame instance.Definition
new(table subclass_mt)Arguments
| table | subclass_mt | [optional] Meta table of subclass |
| bool | true | if player can crouch |
Set the callback to use when an item is activated (for buying or selling).Definition
setItemClickCallback()Return Values
| table | instance | instance of object |
Set the callback to use when an item is selected in the view.Definition
setItemSelectCallback()Return Values
| bool | true | if player can idle |
Set header icon and text.Definition
setHeader()Return Values
| table | instance | instance of object |
Set the category to display.Definition
setCategory()Return Values
| bool | true | if player can idle |
Set the balance elements' visibility.Definition
setShowBalance()Return Values
| table | instance | instance of object |
Set the navigation header's visibility.Definition
setShowNavigation()Return Values
| bool | true | if state is available |
Set the current money balance display.Definition
setCurrentBalance(float balance, string balanceString)Arguments
| float | balance | Current balance of the current player |
| string | balanceString | Properly formatted money string |
| table | instance | instance of object |
Set the current slot usage display for consoles.Definition
setSlotsUsage()Return Values
| bool | true | if player can idle |
Set an ordered array of ShopDisplayItem instances to display in this frame.Definition
setDisplayItems()Return Values
| table | instance | instance of object |
Update scroll button visibility based on the currently visible list items.Definition
updateScrollButtons()Return Values
| bool | true | if player can jump |
Get a store items price (buy or sell value) for displaying.Definition
getStoreItemDisplayPrice()Return Values
| table | instance | instance of object |
Assign fill types data to detail box.Definition
Creates icons for fill types.
assignItemFillTypesData(string baseIconProfile, table iconFilenames, int attributeIndex)Arguments
| string | baseIconProfile | UI profile for the fill type base icon |
| table | iconFilenames | Array of filenames of fill type icons |
| int | attributeIndex | Index of attribute slot to use |
| table | player | state |
| int | Next | usable attribute slot index after these fill types |
Assign text data to detail box.Definition
assignItemTextData(table displayItem)Arguments
| table | displayItem | ShopDisplayItem which holds item attribute data |
| bool | true | if player state is available |
| int | Number | of attributes used for text data |
Assign display data of a selected ShopDisplayItem to the attribute elements.Definition
assignItemAttributeData(table displayItem)Arguments
| table | displayItem | ShopDisplayItem which holds item attribute data |
| bool | true | if player state is active |
Get the frame's main content element's screen size.Definition
getMainElementSize()Return Values
| table | instance | instance of object |
Get the frame's main content element's screen position.Definition
getMainElementPosition()Return Values
| bool | true | if player can idle |
Handle a click / button activation on an item.Definition
onClickItem()Return Values
| table | instance | instance of object |
Handle a double-click on an item.Definition
onDoubleClickItem()Return Values
| bool | true | if player can run |
Handle click on left navigation button.Definition
onClickLeft()Return Values
| bool | true | if player can run |
Handle click on right navigation button.Definition
onClickRight()Return Values
| table | instance | instance of object |
Handle a list scroll event.Definition
onScroll()Return Values
| bool | true | if player can swim |
Handle selection of an item.Definition
onItemSelected()Return Values
| table | instance | instance of object |
HUD side notification element.
-- Displays notifications issued by other game components at the side of the screen.
--@category GUI
Create a new SideNotification.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD atlas texture |
| table | instance | instance of object |
| table | SideNotification | instance |
Add a notification message to display.Definition
addNotification(string text, table color, int displayDuration)Arguments
| string | text | Display message text |
| table | color | Color array as {r, g, b, a} |
| int | displayDuration | Display duration of message in milliseconds |
| table | instance | instance of object |
Update notifications state.Definition
update()Return Values
| table | self | instance |
Draw the notifications.Definition
draw()Return Values
| table | instance | instance of object |
Get this element's base background position.Definition
getBackgroundPosition(float uiScale)Arguments
| float | uiScale | Current UI scale factor |
| boolean | true | if loading was successful else false |
Set uniform UI scale.Definition
setScale()Return Values
| boolean | true | if loading was successful else false |
Update sizes and positions of this elements and its children.Definition
updateSizeAndPositions()Return Values
| table | animals | list all animals |
Store scaled positioning, size and offset values.Definition
storeScaledValues()Return Values
| table | animal | the animal object |
Create the background overlay.Definition
createBackground()Return Values
| table | animal | the animal object |
Create required display components.Definition
createComponents()Return Values
| table | animal | the animal object |
When trying to sell an extension that is required to store all fills, show a warning before selling the extension and the contents.
Load silo extensionDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 46 | function SiloExtensionPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 47 | if not SiloExtensionPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 48 | return false |
| 49 | end |
| 50 | |
| 51 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 52 | |
| 53 | local storageKey = "placeable.storage" |
| 54 | if hasXMLProperty(xmlFile, storageKey) then |
| 55 | local storageNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, storageKey.."#node")) |
| 56 | if storageNode ~= nil then |
| 57 | self.storage = Storage:new(self.isServer, self.isClient) |
| 58 | self.storage:load(storageNode, xmlFile, storageKey) |
| 59 | else |
| 60 | g_logManager:xmlWarning(xmlFilename, "Missing 'node' for storage '%s'!", storageKey) |
| 61 | end |
| 62 | else |
| 63 | g_logManager:xmlWarning(xmlFilename, "Missing 'storage' for siloExtension '%s'!", xmlFilename) |
| 64 | end |
| 65 | |
| 66 | delete(xmlFile) |
| 67 | |
| 68 | return true |
| 69 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 73 | function SiloExtensionPlaceable:finalizePlacement() |
| 74 | SiloExtensionPlaceable:superClass().finalizePlacement(self) |
| 75 | |
| 76 | local lastFoundUnloadingStations = g_currentMission:getStorageTargetsInRange(g_currentMission.unloadingStations, self.storage) |
| 77 | local lastFoundLoadingStations = g_currentMission:getStorageTargetsInRange(g_currentMission.loadingStations, self.storage) |
| 78 | |
| 79 | self.storage:setOwnerFarmId(self:getOwnerFarmId(), true) |
| 80 | g_currentMission:addStorage(self.storage) |
| 81 | self.storage:register(true) |
| 82 | |
| 83 | g_currentMission:addStorageToUnloadingStations(self.storage, lastFoundUnloadingStations, false) |
| 84 | g_currentMission:addStorageToLoadingStations(self.storage, lastFoundLoadingStations, false) |
| 85 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 91 | function SiloExtensionPlaceable:readStream(streamId, connection) |
| 92 | SiloExtensionPlaceable:superClass().readStream(self, streamId, connection) |
| 93 | if connection:getIsServer() then |
| 94 | local storageId = NetworkUtil.readNodeObjectId(streamId) |
| 95 | self.storage:readStream(streamId, connection) |
| 96 | g_client:finishRegisterObject(self.storage, storageId) |
| 97 | end |
| 98 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 104 | function SiloExtensionPlaceable:writeStream(streamId, connection) |
| 105 | SiloExtensionPlaceable:superClass().writeStream(self, streamId, connection) |
| 106 | if not connection:getIsServer() then |
| 107 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.storage)) |
| 108 | self.storage:writeStream(streamId, connection) |
| 109 | g_server:registerObjectInStream(connection, self.storage) |
| 110 | end |
| 111 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 127 | function SiloExtensionPlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 128 | if not SiloExtensionPlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 129 | return false |
| 130 | end |
| 131 | |
| 132 | if not self.storage:loadFromXMLFile(xmlFile, key..".storage") then |
| 133 | return false |
| 134 | end |
| 135 | |
| 136 | return true |
| 137 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 144 | function SiloExtensionPlaceable:saveToXMLFile(xmlFile, key, usedModNames) |
| 145 | SiloExtensionPlaceable:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 146 | |
| 147 | self.storage:saveToXMLFile(xmlFile, key..".storage", usedModNames) |
| 148 | end |
show a warning before selling the extension and the contents.Definition
canBeSold()
Draggable viewport slider element.
-- If #hasButtons is true or not present, this element requires 2 ButtonElement instances as the first children, which
provide another way to scroll in addition to clicking the bar or dragging the handle.
-- Used layers: "image" for a background image, "sliderImage" for a slider handle background image.
-- Implicit callback:
onSliderValueChanged(element, newValue) is called on all children and the target data element when the slider value
changes, if those elements have the method defined.
--@category GUI
--@xmlConfig GuiElement#direction string [optional] Slider orientation, defaults to "x". Valid values: "x" for horizontal, "y" for vertical.
Update the disabled-ness of the slider buttons depending on current stateDefinition
updateSliderButtons()
Determine if this SliderElement can receive focus.Definition
canReceiveFocus()
Player speaker display for consoles.
-- Displays currently speaking players for consoles only. This display is used in place of the chat window.
--@category GUI
Create a new SpeakerDisplay.Definition
new(string hudAtlasPath, table ingameMap)Arguments
| string | hudAtlasPath | Path to the HUD atlas texture. |
| table | ingameMap | IngameMap reference for positioning |
| string | animal | type |
| table | SpeakerDisplay | instance |
Set the references to the currently connected users.Definition
setUsers()Return Values
| table |
Handle menu visibility state change.Definition
onMenuVisibilityChange()Return Values
| table | store | information [shopItemName, canBeBought, imageFilename, price] |
Update current speaking state for all connected users.Definition
updateSpeakingState()Return Values
| table | instance | instance of object |
Update visibility states based on active speakers.Definition
updateVisibility()Return Values
| boolean | true | if loading was successful else false |
Update the display state each frame.Definition
update()Return Values
| table | returns | a food group or nil if nothing is found |
Set this element's UI scale.Definition
setScale()Return Values
| table | instance | Instance of object |
Get this element's base background position.Definition
getBackgroundPosition(float uiScale)Arguments
| float | uiScale | Current UI scale factor |
| bool | true | if load is successful |
Store scaled positioning, size and offset values.Definition
storeScaledValues()Return Values
| integer | id | of the animal group |
Create the background overlay.Definition
createBackground()Return Values
| integer | number | of trees found |
Create a line for an active speaker.Definition
createSpeakerLine()Return Values
| bool | true | to continue counting trees |
Create required display components.Definition
createComponents()Return Values
| table | instance | instance of object |
Vehicle HUD speed meter display element.
-- Displays gauges for current speed, fuel level and vehicle wear / damage. Also shows operating time, textual speed
display and cruise control state.
--@category GUI
Create a new SpeedMeterDisplay instance.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
| boolean | success | success |
Get this element's base position as a reference for other component's positioning.Definition
getBasePosition()Return Values
| string | attributes | attributes |
| string | nodes | nodes |
Create display components for the speed meter.Definition
Components are created with an implicit scale of 1. Scaling should only ever happen after initialization.
createComponents(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
Delete this instance and all managed components.Definition
delete()Return Values
| boolean | inScope | in scope |
Set the current vehicle which provides the data for the speed meter.Definition
setVehicle(table vehicle)Arguments
| table | vehicle | Vehicle reference |
| float | priority | priority |
Update the state of the speed meter.Definition
update()Return Values
| float | closest | distance squared in m |
Update operating time drawing parameters.Definition
updateOperatingTime()Return Values
| integer | returns | number of animals |
Update cruise control drawing parameters.Definition
updateCruiseControl()Return Values
| bool | returns | true if there is water |
Update a gauge indicator needle.Definition
updateGaugeIndicator(table Indicator, float radiusX, float radiusY, float rotation)Arguments
| table | Indicator | HUDElement |
| float | radiusX | Radius X component of distance to the gauge center |
| float | radiusY | Radius Y component of distance to the gauge center |
| float | rotation | Rotation angle of the indicator in radians |
| float | separateForceX | x component of steering force. default is 0. |
| float | separateForceY | y component of steering force. default is 0. |
| float | separateForceZ | z component of steering force. default is 0. |
Update gauge fill segments.Definition
updateGaugeFillSegments(table fillSegments, float gaugeValue)Arguments
| table | fillSegments | Array of segment elements |
| float | gaugeValue | Current gauge indication value [0, 1] |
Update partial gauge segment elements.Definition
updateGaugePartialSegments(table partialSegments, float indicatorRotation, int rotationDirection, float gaugeRadiusX, float gaugeRadiusY, float gaugeMinAngle, flaot fullSegmentAngle, float detailSegmentAngle, bool isPartialOnly)Arguments
| table | partialSegments | Array of segment elements |
| float | indicatorRotation | Current indicator rotation angle in radians |
| int | rotationDirection | Direction value for the rotation, 1 is counter-clockwise, -1 is clockwise |
| float | gaugeRadiusX | Radius X component of distance to gauge center in screen space |
| float | gaugeRadiusY | Radius Y component of distance to gauge center in screen space |
| float | gaugeMinAngle | Angle of gauge zero position in radians |
| flaot | fullSegmentAngle | Angle which spans a filled segment in radians |
| float | detailSegmentAngle | Smallest angle which can be covered by a partial segment in radians |
| bool | isPartialOnly | If true, the current gauge has no fill segments and uses only partial segments to display values |
Update the speed gauge state.Definition
updateSpeedGauge()Return Values
| float | wanderForceX | x component of steering force. default is 0. |
| float | wanderForceY | y component of steering force. default is 0. |
| float | wanderForceZ | z component of steering force. default is 0. |
| float | wanderAngle | new wandering angle in rad. default is 0. |
Update the damage gauge state.Definition
updateDamageGauge()
Get fuel level and capacity of a vehicle.Definition
getVehicleFuelLevelAndCapacity()
Update the fuel gauge state.Definition
updateFuelGauge()
Override of HUDDisplayElement.Definition
Also updates the scaled values which are relative to the current position.
onAnimateVisibilityFinished()Return Values
| float | seekForceX | x component of steering force. default is 0. |
| float | seekForceY | y component of steering force. default is 0. |
| float | seekForceZ | z component of steering force. default is 0. |
Draw the speed meter.Definition
draw()
Draw vehicle operating time if set.Definition
drawOperatingTimeText(table vehicle)Arguments
| table | vehicle | Current vehicle |
Draw the text portion of the cruise control element.Definition
drawCruiseControlText()Return Values
| bool | true | is any player is close to ground |
Draw the current speed in text.Definition
drawSpeedText()Return Values
| bool | true | is any player is close |
Fade the fuel gauge elements.Definition
fadeFuelGauge()Return Values
| table | instance | Instance of object |
Animate (de-)activation of the fuel gauge.Definition
animateFuelGaugeToggle()Return Values
| bool | returns | true if load is successful |
Fade the damage gauge elements.Definition
fadeDamageGauge()Return Values
| float | x | world position. default is 0 |
| float | x | world position. default is 0 |
| float | x | world position. default is 0 |
Animate (de-)activation of the damage gauge.Definition
animateDamageGaugeToggle()
Set the speed meter scale.Definition
Overrides HUDElement.setScale().
setScale(float uiScale)Arguments
| float | uiScale | UI scale factor, applied to both width and height dimensions |
Calculate and store the gauge center position, including the current UI scale.Definition
storeGaugeCenterPosition(float baseX, float baseY)Arguments
| float | baseX | Gauge background element X position in screen space |
| float | baseY | Gauge background element Y position in screen space |
| bool | returns | true if animals are spawned |
Calculate and store scaling values based on the current UI scale.Definition
storeScaledValues(float baseX, float baseY)Arguments
| float | baseX | Gauge background element X position in screen space |
| float | baseY | Gauge background element Y position in screen space |
| bool | returns | true if all tests are validated |
Get the position of the background element, which provides the SpeedMeterDisplay's absolute position.Definition
getBackgroundPosition(float backgroundWidth)Arguments
| float | backgroundWidth | Scaled background width in pixels |
| integer | number | of trees found |
Create the background overlay for the speed meter.Definition
createBackground(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
| bool | true | to continue counting trees |
| table | Overlay | instance |
Create the gauge background.Definition
createGaugeBackground()Return Values
| bool | return | true is on field |
Create a side gauge background element.Definition
createSideGaugeBackground()Return Values
| bool | returns | true if there is water |
Create gauge icons.Definition
createGaugeIconElements()Return Values
| bool | returns | true if current time in hours range |
Create the horizontal separator HUD element.Definition
createHorizontalSeparator()Return Values
| integer | returns | the number of animals to spawn |
Create the cruise control HUD element.Definition
createCruiseControlElement()Return Values
| bool | returns | true if animals are spawned |
Create the operating time HUD element.Definition
createOperatingTimeElement()Return Values
| string | that | will be displayed on console |
Create a movable indicator needle element.Definition
createIndicator(string hudAtlasPath, table size, table uvs, table color, table pivot)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
| table | size | Pixel size of the indicator as an array {width, height} |
| table | uvs | UV coordinates of the indicator texture part as an array {x, y, width, height} |
| table | color | Color of the indicator as an RGBA array |
| table | pivot | Rotation pivot offset of the indicator as an array {x, y} |
| string | that | will be displayed on console |
| table | HUDElement | instance |
Create fill elements for large segments of a gauge.Definition
createGaugeFillElements(string hudAtlasPath, float baseX, float baseY, float gaugeStartAngle, float gaugeEndAngle, float fillSegmentAngle, table radius, table segmentSize, table segmentPivot, table segmentUVs, table segmentColor)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
| float | baseX | Base X position of the gauge element in screen space |
| float | baseY | Base Y position of the gauge element in screen space |
| float | gaugeStartAngle | Gauge starting angle position |
| float | gaugeEndAngle | Gauge ending angle position |
| float | fillSegmentAngle | Angle spanned by a fill segment |
| table | radius | Radius of the gauge as an array {x, y} |
| table | segmentSize | Pixel size of a fill segment as an array {width, height} |
| table | segmentPivot | Rotation pivot offset of a fill segment as an array {x, y} |
| table | segmentUVs | UV coordinates of a fill segment as an array {x, y, width, height} |
| table | segmentColor | Color of a fill segment as an RGBA array |
| string | that | will be displayed on console |
Create gauge elements which span the spaces inbetween fill segments and their indicator needle.Definition
createGaugePartialElements(string hudAtlasPath, float baseX, float baseY, table fullSegmentSize, table segmentPivot, table segmentColor, table gaugeSegmentUVs)Arguments
| string | hudAtlasPath | Path to the HUD texture atlas |
| float | baseX | Base X position of the gauge element in screen space |
| float | baseY | Base Y position of the gauge element in screen space |
| table | fullSegmentSize | Pixel size of a full segment as an array {width, height} |
| table | segmentPivot | Rotation pivot offset of a segment as an array {x, y} |
| table | segmentColor | Color of a segment as an RGBA array |
| table | gaugeSegmentUVs | Array of segment UV arrays ordered by size {sizeIndex={x, y, width, height}} |
| string | that | will be displayed on console |
Create the indicator needle for the speed gauge.Definition
createSpeedGaugeIndicator()Return Values
Create the segment elements for the speed gauge.Definition
createSpeedGaugeElements()Return Values
Create the indicator needle for the damage gauge.Definition
createDamageGaugeIndicator()Return Values
| string | that | will be displayed on console |
Create the gauge segments for the damage gauge.Definition
createDamageGaugeElements()Return Values
| string | that | will be displayed on console |
Create the indicator needle for the fuel gauge.Definition
createFuelGaugeIndicator()Return Values
| table | instance | instance of object |
Create the segment elements for the fuel gauge.Definition
createFuelGaugeElements()Return Values
| table | instance | instance of object |
Structured data for mission starts.
-- This serves as a data transfer object between GUI screens when setting up a game.
Create a new StartMissionInfo instance.Definition
new()Return Values
| boolean | detachAllowed | detach is allowed |
Reset all information for a new setup.Definition
reset()Return Values
| float | wearMultiplier | current wear multiplier |
Game Startup Screen.
-- Shows splash and intro videos, leads on to main menu.
Duck-typed dummy function to make this class behave like a ScreenElement on initialization.Definition
See Gui:loadGui() for the processing part which requires this.
exposeControlsAsFields()Return Values
| bool | true | if player can idle |
Category type for grouping (e.g. in the shop UI)
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 26 | function StoreManager:new(customMt) |
| 27 | local self = AbstractManager:new(customMt or StoreManager_mt) |
| 28 | |
| 29 | return self |
| 30 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 34 | function StoreManager:initDataStructures() |
| 35 | self.numOfCategories = 0 |
| 36 | self.categories = {} |
| 37 | self.items = {} |
| 38 | self.xmlFilenameToItem = {} |
| 39 | self.modStoreItems = {} |
| 40 | |
| 41 | self.specTypes = {} |
| 42 | self.nameToSpecType = {} |
| 43 | end |
Load manager data on map loadDefinition
loadMapData()Return Values
| boolean | true | if loading was successful else false |
| 48 | function StoreManager:loadMapData(xmlFile, missionInfo, baseDirectory) |
| 49 | StoreManager:superClass().loadMapData(self) |
| 50 | |
| 51 | -- local all store categories |
| 52 | local categoryXMLFile = loadXMLFile("storeCategoriesXML", "dataS/storeCategories.xml") |
| 53 | local i = 0 |
| 54 | while true do |
| 55 | local baseXMLName = string.format("categories.category(%d)", i) |
| 56 | |
| 57 | if not hasXMLProperty(categoryXMLFile, baseXMLName) then |
| 58 | break |
| 59 | end |
| 60 | local name = getXMLString(categoryXMLFile, baseXMLName .. "#name") |
| 61 | local title = getXMLString(categoryXMLFile, baseXMLName .. "#title") |
| 62 | local imageFilename = getXMLString(categoryXMLFile, baseXMLName .. "#image") |
| 63 | local type = getXMLString(categoryXMLFile, baseXMLName .. "#type") |
| 64 | |
| 65 | if title ~= nil and title:sub(1, 6) == "$l10n_" then |
| 66 | title = g_i18n:getText(title:sub(7)) |
| 67 | end |
| 68 | |
| 69 | self:addCategory(name, title, imageFilename, type, "") |
| 70 | |
| 71 | i = i + 1 |
| 72 | end |
| 73 | delete(categoryXMLFile) |
| 74 | |
| 75 | -- now load all storeitems |
| 76 | |
| 77 | local storeItemsFilename = "dataS/storeItems.xml" |
| 78 | if g_isPresentationVersionSpecialStore then |
| 79 | storeItemsFilename = "dataS/storeItems_presentationVersion.xml" |
| 80 | end |
| 81 | |
| 82 | self:loadItemsFromXML(storeItemsFilename) |
| 83 | |
| 84 | if xmlFile ~= nil then |
| 85 | local mapStoreItemsFilename = getXMLString(xmlFile, "map.storeItems#filename") |
| 86 | if mapStoreItemsFilename ~= nil then |
| 87 | mapStoreItemsFilename = Utils.getFilename(mapStoreItemsFilename, baseDirectory) |
| 88 | self:loadItemsFromXML(mapStoreItemsFilename) |
| 89 | end |
| 90 | end |
| 91 | |
| 92 | for _, item in ipairs(self.modStoreItems) do |
| 93 | g_deferredLoadingManager:addSubtask(function() |
| 94 | self:loadItem(item.xmlFilename, item.baseDir, item.customEnvironment, item.isMod, item.isBundleItem, item.dlcTitle) |
| 95 | end) |
| 96 | end |
| 97 | |
| 98 | return true |
| 99 | end |
Adds a new store categoryDefinition
addCategory(string name, string title, string imageFilename, string baseDir)Arguments
| string | name | category index name |
| string | title | category title |
| string | imageFilename | image |
| string | baseDir | base directory |
| boolean | true | if adding was successful else false |
| 128 | function StoreManager:addCategory(name, title, imageFilename, type, baseDir) |
| 129 | if name == nil or name == "" then |
| 130 | print("Warning: Could not register store category. Name is missing or empty!") |
| 131 | return false |
| 132 | end |
| 133 | if not ClassUtil.getIsValidIndexName(name) then |
| 134 | print("Warning: '"..tostring(name).."' is no valid name for a category!") |
| 135 | return false |
| 136 | end |
| 137 | if title == nil or title == "" then |
| 138 | print("Warning: Could not register store category. Title is missing or empty!") |
| 139 | return false |
| 140 | end |
| 141 | if imageFilename == nil or imageFilename == "" then |
| 142 | print("Warning: Could not register store category. Image is missing or empty!") |
| 143 | return false |
| 144 | end |
| 145 | if baseDir == nil then |
| 146 | print("Warning: Could not register store category. Basedirectory not defined!") |
| 147 | return false |
| 148 | end |
| 149 | |
| 150 | name = name:upper() |
| 151 | |
| 152 | if self.categories[name] == nil then |
| 153 | self.numOfCategories = self.numOfCategories + 1 |
| 154 | |
| 155 | local category = {} |
| 156 | category.name = name |
| 157 | category.title = title |
| 158 | category.image = Utils.getFilename(imageFilename, baseDir) |
| 159 | category.type = StoreManager.CATEGORY_TYPE[type] ~= nil and type or StoreManager.CATEGORY_TYPE.NONE |
| 160 | category.orderId = self.numOfCategories |
| 161 | |
| 162 | self.categories[name] = category |
| 163 | return true |
| 164 | end |
| 165 | return false |
| 166 | end |
Removes a store categoryDefinition
removeCategory(string name)Arguments
| string | name | category index name |
| 171 | function StoreManager:removeCategory(name) |
| 172 | if not ClassUtil.getIsValidIndexName(name) then |
| 173 | print("Warning: '"..tostring(name).."' is no valid name for a category!") |
| 174 | return |
| 175 | end |
| 176 | |
| 177 | name = name:upper() |
| 178 | |
| 179 | for _, item in pairs(self.items) do |
| 180 | if item.category == name then |
| 181 | item.category = "MISC" |
| 182 | end |
| 183 | end |
| 184 | self.categories[name] = nil |
| 185 | end |
Gets a store category by nameDefinition
getCategoryByName(string name)Arguments
| string | name | category index name |
| table | category | the category object |
| 191 | function StoreManager:getCategoryByName(name) |
| 192 | if name ~= nil then |
| 193 | return self.categories[name:upper()] |
| 194 | end |
| 195 | return nil |
| 196 | end |
Adds a new spec typeDefinition
addSpecType(string name, string profile, function loadFunc, function getValueFunc)Arguments
| string | name | spec type index name |
| string | profile | spec type gui profile |
| function | loadFunc | the loading function pointer |
| function | getValueFunc | the get value function pointer |
| 204 | function StoreManager:addSpecType(name, profile, loadFunc, getValueFunc) |
| 205 | if not ClassUtil.getIsValidIndexName(name) then |
| 206 | print("Warning: '"..tostring(name).."' is no valid name for a spec type!") |
| 207 | return |
| 208 | end |
| 209 | |
| 210 | name = name |
| 211 | |
| 212 | if self.nameToSpecType == nil then |
| 213 | printCallstack() |
| 214 | end |
| 215 | |
| 216 | if self.nameToSpecType[name] ~= nil then |
| 217 | print("Error: spec type name '" ..name.. "' is already in use!") |
| 218 | return |
| 219 | end |
| 220 | |
| 221 | local specType = {} |
| 222 | specType.name = name |
| 223 | specType.profile = profile |
| 224 | specType.loadFunc = loadFunc |
| 225 | specType.getValueFunc = getValueFunc |
| 226 | |
| 227 | self.nameToSpecType[name] = specType |
| 228 | table.insert(self.specTypes, specType) |
| 229 | end |
Gets all spec typesDefinition
getSpecTypes()Return Values
| table | specTypes | a list of spec types |
| 234 | function StoreManager:getSpecTypes() |
| 235 | return self.specTypes |
| 236 | end |
Gets a spec type by nameDefinition
getSpecTypeByName(string name)Arguments
| string | name | spec type index name |
| table | specType | the corresponding spectype |
| 242 | function StoreManager:getSpecTypeByName(name) |
| 243 | if not ClassUtil.getIsValidIndexName(name) then |
| 244 | print("Warning: '"..tostring(name).."' is no valid name for a spec type!") |
| 245 | return |
| 246 | end |
| 247 | |
| 248 | return self.nameToSpecType[name] |
| 249 | end |
Adds a new store itemDefinition
addItem(table storeItem)Arguments
| table | storeItem | the storeitem object |
| boolean | wasSuccessfull | true if added else false |
| 255 | function StoreManager:addItem(storeItem) |
| 256 | if self.xmlFilenameToItem[storeItem.xmlFilenameLower] ~= nil then |
| 257 | return false |
| 258 | end |
| 259 | |
| 260 | table.insert(self.items, storeItem) |
| 261 | storeItem.id = #self.items |
| 262 | self.xmlFilenameToItem[storeItem.xmlFilenameLower] = storeItem |
| 263 | return true |
| 264 | end |
Removes a storeitem by indexDefinition
removeItemByIndex(integer index)Arguments
| integer | index | storeitem index |
| 269 | function StoreManager:removeItemByIndex(index) |
| 270 | local item = self.items[index] |
| 271 | if item ~= nil then |
| 272 | self.xmlFilenameToItem[item.xmlFilenameLower] = nil |
| 273 | |
| 274 | -- item.id must always match the index in the arry, thus swap the last to the removed position and reduce size |
| 275 | local numItems = table.getn(self.items) |
| 276 | if index < numItems then |
| 277 | self.items[index] = self.items[numItems] |
| 278 | self.items[index].id = index |
| 279 | end |
| 280 | table.remove(self.items, numItems) |
| 281 | end |
| 282 | end |
Gets all storeitemsDefinition
getItems()Return Values
| table | items | a list of all store items |
| 287 | function StoreManager:getItems() |
| 288 | return self.items |
| 289 | end |
Gets a store item by indexDefinition
getItemByIndex(integer index)Arguments
| integer | index | store item index |
| table | storeItem | the storeitem oject |
| 295 | function StoreManager:getItemByIndex(index) |
| 296 | if index ~= nil then |
| 297 | return self.items[index] |
| 298 | end |
| 299 | return nil |
| 300 | end |
Gets a store item xml filenameDefinition
getItemByXMLFilename(string xmlFilename)Arguments
| string | xmlFilename | storeitem xml filename |
| table | storeItem | the storeitem object |
| 306 | function StoreManager:getItemByXMLFilename(xmlFilename) |
| 307 | if xmlFilename ~= nil then |
| 308 | return self.xmlFilenameToItem[xmlFilename:lower()] |
| 309 | end |
| 310 | end |
Gets a store item xml filenameDefinition
getItemByCustomEnvironment(string xmlFilename)Arguments
| string | xmlFilename | storeitem xml filename |
| table | storeItem | the storeitem object |
| 316 | function StoreManager:getItemByCustomEnvironment(customEnvironment) |
| 317 | local items = {} |
| 318 | for _, item in ipairs(self.items) do |
| 319 | if item.customEnvironment == customEnvironment then |
| 320 | table.insert(items, item) |
| 321 | end |
| 322 | end |
| 323 | |
| 324 | return items |
| 325 | end |
Loads a storeitem from xml fileDefinition
loadItem(string xmlFilename, string baseDir, string customEnvironment, boolean isMod, boolean isBundleItem, string dlcTitle)Arguments
| string | xmlFilename | the storeitem xml filename |
| string | baseDir | the base directory |
| string | customEnvironment | a custom environment |
| boolean | isMod | true if item is a mod, else false |
| boolean | isBundleItem | true if item is bundleItem, else false |
| string | dlcTitle | optional dlc title |
| table | storeItem | the storeitem object |
| 340 | function StoreManager:loadItem(xmlFilename, baseDir, customEnvironment, isMod, isBundleItem, dlcTitle) |
| 341 | local xmlFilename = Utils.getFilename(xmlFilename, baseDir) |
| 342 | local xmlFile = loadXMLFile("storeItemXML", xmlFilename) |
| 343 | local baseXMLName = getXMLRootName(xmlFile) |
| 344 | local storeDataXMLName = baseXMLName..".storeData" |
| 345 | |
| 346 | if not hasXMLProperty(xmlFile, storeDataXMLName) then |
| 347 | g_logManager:xmlError(xmlFilename, "No storeData found. StoreItem will be ignored!") |
| 348 | delete(xmlFile) |
| 349 | return nil |
| 350 | end |
| 351 | |
| 352 | local isValid = true |
| 353 | local name = XMLUtil.getXMLI18NValue(xmlFile, storeDataXMLName, getXMLString, "name", nil, customEnvironment, true) |
| 354 | if name == nil then |
| 355 | g_logManager:xmlWarning(xmlFilename, "Name missing for storeitem. Ignoring store item!") |
| 356 | isValid = false |
| 357 | end |
| 358 | |
| 359 | local imageFilename = Utils.getNoNil(getXMLString(xmlFile, storeDataXMLName..".image"), "") |
| 360 | if imageFilename == "" then |
| 361 | g_logManager:xmlWarning(xmlFilename, "Image icon is missing for storeitem. Ignoring store item!") |
| 362 | isValid = false |
| 363 | end |
| 364 | |
| 365 | if not isValid then |
| 366 | delete(xmlFile) |
| 367 | return nil |
| 368 | end |
| 369 | |
| 370 | local storeItem = {} |
| 371 | storeItem.name = name |
| 372 | storeItem.xmlFilename = xmlFilename |
| 373 | storeItem.xmlFilenameLower = xmlFilename:lower() |
| 374 | storeItem.imageFilename = Utils.getFilename(imageFilename, baseDir) |
| 375 | storeItem.functions = StoreItemUtil.getFunctionsFromXML(xmlFile, storeDataXMLName, customEnvironment) |
| 376 | storeItem.specs = StoreItemUtil.getSpecsFromXML(self.specTypes, xmlFile, customEnvironment) |
| 377 | storeItem.brandIndex = StoreItemUtil.getBrandIndexFromXML(xmlFile, storeDataXMLName, xmlFilename) |
| 378 | storeItem.species = Utils.getNoNil(getXMLString(xmlFile, storeDataXMLName..".species"), "") |
| 379 | storeItem.canBeSold = Utils.getNoNil(getXMLBool(xmlFile, storeDataXMLName..".canBeSold"), true) |
| 380 | storeItem.showInStore = Utils.getNoNil(getXMLBool(xmlFile, storeDataXMLName..".showInStore"), not isBundleItem) |
| 381 | storeItem.isBundleItem = isBundleItem |
| 382 | storeItem.allowLeasing = Utils.getNoNil(getXMLBool(xmlFile, storeDataXMLName..".allowLeasing"), true) |
| 383 | storeItem.maxItemCount = getXMLInt(xmlFile, storeDataXMLName..".maxItemCount") |
| 384 | storeItem.rotation = Utils.getNoNilRad(getXMLFloat(xmlFile, storeDataXMLName..".rotation"), 0) |
| 385 | storeItem.shopTranslationOffset = StringUtil.getVectorNFromString(getXMLString(xmlFile, storeDataXMLName..".shopTranslationOffset"), 3) |
| 386 | storeItem.shopRotationOffset = StringUtil.getRadiansFromString(getXMLString(xmlFile, storeDataXMLName..".shopRotationOffset"), 3) |
| 387 | storeItem.shopHeight = Utils.getNoNil(getXMLFloat(xmlFile, storeDataXMLName..".shopHeight"), 0) |
| 388 | storeItem.shopFoldingState = Utils.getNoNil(getXMLBool(xmlFile, storeDataXMLName..".shopFoldingState"), 0) |
| 389 | storeItem.sharedVramUsage, storeItem.perInstanceVramUsage, storeItem.ignoreVramUsage = StoreItemUtil.getVRamUsageFromXML(xmlFile, storeDataXMLName) |
| 390 | storeItem.dlcTitle = dlcTitle |
| 391 | storeItem.isMod = isMod |
| 392 | storeItem.customEnvironment = customEnvironment |
| 393 | |
| 394 | local categoryName = getXMLString(xmlFile, storeDataXMLName..".category") |
| 395 | local category = self:getCategoryByName(categoryName) |
| 396 | if category == nil then |
| 397 | g_logManager:xmlWarning(xmlFilename, "Invalid category '%s' in store data! Using 'misc' instead!", tostring(categoryName)) |
| 398 | category = self:getCategoryByName("misc") |
| 399 | end |
| 400 | storeItem.categoryName = category.name |
| 401 | |
| 402 | storeItem.configurations = StoreItemUtil.getConfigurationsFromXML(xmlFile, baseXMLName, baseDir, customEnvironment, isMod, storeItem) |
| 403 | storeItem.subConfigurations = StoreItemUtil.getSubConfigurationsFromXML(storeItem.configurations) |
| 404 | storeItem.configurationSets = StoreItemUtil.getConfigurationSetsFromXML(storeItem, xmlFile, baseXMLName, baseDir, customEnvironment, isMod) |
| 405 | storeItem.price = Utils.getNoNil(getXMLFloat(xmlFile, storeDataXMLName..".price"), 0) |
| 406 | if storeItem.price < 0 then |
| 407 | g_logManager:xmlWarning(xmlFilename, "Price has to be greater than 0. Using default 10.000 instead!") |
| 408 | storeItem.price = 10000 |
| 409 | end |
| 410 | storeItem.dailyUpkeep = Utils.getNoNil(getXMLFloat(xmlFile, storeDataXMLName..".dailyUpkeep"), 0) |
| 411 | storeItem.runningLeasingFactor = Utils.getNoNil(getXMLFloat(xmlFile, storeDataXMLName..".runningLeasingFactor"), EconomyManager.DEFAULT_RUNNING_LEASING_FACTOR) |
| 412 | storeItem.lifetime = Utils.getNoNil(getXMLFloat(xmlFile, storeDataXMLName..".lifetime"), 600) |
| 413 | |
| 414 | |
| 415 | if hasXMLProperty(xmlFile, storeDataXMLName..".bundleElements") then |
| 416 | local bundleInfo = {bundleItems={}, attacherInfo={}} |
| 417 | local price = 0 |
| 418 | local lifetime = math.huge |
| 419 | local dailyUpkeep = 0 |
| 420 | local runningLeasingFactor = 0 |
| 421 | local i = 0 |
| 422 | while true do |
| 423 | local bundleKey = string.format(storeDataXMLName..".bundleElements.bundleElement(%d)", i) |
| 424 | if not hasXMLProperty(xmlFile, bundleKey) then |
| 425 | break |
| 426 | end |
| 427 | local bundleXmlFile = getXMLString(xmlFile, bundleKey..".xmlFilename") |
| 428 | local offset = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, bundleKey..".offset"), "0 0 0"), 3) |
| 429 | local rotation = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, bundleKey..".yRotation"), 0)) |
| 430 | if bundleXmlFile ~= nil then |
| 431 | local completePath = Utils.getFilename(bundleXmlFile, baseDir) |
| 432 | local item = self:getItemByXMLFilename(completePath) |
| 433 | if item == nil then |
| 434 | item = self:loadItem(bundleXmlFile, baseDir, customEnvironment, isMod, true, dlcTitle) |
| 435 | end |
| 436 | if item ~= nil then |
| 437 | price = price + item.price |
| 438 | dailyUpkeep = dailyUpkeep + item.dailyUpkeep |
| 439 | runningLeasingFactor = runningLeasingFactor + item.runningLeasingFactor |
| 440 | lifetime = math.min(lifetime, item.lifetime) |
| 441 | table.insert(bundleInfo.bundleItems, {item=item, xmlFilename=item.xmlFilename, offset=offset, rotation=rotation, price=item.price}) |
| 442 | end |
| 443 | end |
| 444 | i = i + 1 |
| 445 | end |
| 446 | local i = 0 |
| 447 | while true do |
| 448 | local attachKey = string.format(storeDataXMLName..".attacherInfo.attach(%d)", i) |
| 449 | if not hasXMLProperty(xmlFile, attachKey) then |
| 450 | break |
| 451 | end |
| 452 | local bundleElement0 = getXMLInt(xmlFile, attachKey.."#bundleElement0") |
| 453 | local bundleElement1 = getXMLInt(xmlFile, attachKey.."#bundleElement1") |
| 454 | local attacherJointIndex = getXMLInt(xmlFile, attachKey.."#attacherJointIndex") |
| 455 | local inputAttacherJointIndex = getXMLInt(xmlFile, attachKey.."#inputAttacherJointIndex") |
| 456 | if bundleElement0 ~= nil and bundleElement1 ~= nil and attacherJointIndex ~= nil and inputAttacherJointIndex ~= nil then |
| 457 | table.insert(bundleInfo.attacherInfo, {bundleElement0=bundleElement0, bundleElement1=bundleElement1, attacherJointIndex=attacherJointIndex, inputAttacherJointIndex=inputAttacherJointIndex}) |
| 458 | end |
| 459 | i = i + 1 |
| 460 | end |
| 461 | |
| 462 | storeItem.price = price |
| 463 | storeItem.dailyUpkeep = dailyUpkeep |
| 464 | storeItem.runningLeasingFactor = runningLeasingFactor |
| 465 | storeItem.lifetime = lifetime |
| 466 | storeItem.bundleInfo = bundleInfo |
| 467 | end |
| 468 | |
| 469 | self:addItem(storeItem) |
| 470 | |
| 471 | delete(xmlFile) |
| 472 | |
| 473 | return storeItem |
| 474 | end |
Table GUI element.
-- Allows sorting by columns when clicking on header elements. Header elements should ideally be defined just before
the table itself, but never within the table.
--@category GUI
--@xmlConfig GuiElement#rowTemplateName string Element Name of row template. The template will be replicated to fill the table's view and then discarded during initialization.
Called when the GUI is completely loaded. Link relevant collaborators of this table.Definition
onGuiSetupFinished()
Build table row GUI elements.Definition
First look for the row template, then replicate it as many times as configured (#itemsPerCol attribute)
buildTableRows()
Process cell elements for cell-based navigation.Definition
processCellElements()
Apply alternating background GUI profiles to rows if the required profiles have been specified (see configurationDefinition
properties).
applyAlternatingBackgroundsToRows()
Add a data row to the end of this table.Definition
addRow(dataRow DataRow, refreshView boolean)Arguments
| dataRow | DataRow | which holds an ID and column values as DataCell instances |
| refreshView | boolean | If true, triggers a refresh of the table's view. |
Remove a row from this table.Definition
removeRow(index Index, refreshView boolean)Arguments
| index | Index | of row to be removed. |
| refreshView | boolean | If true, triggers a refresh of the table's view. |
Clear all data from the table.Definition
clearData(refreshView boolean)Arguments
| refreshView | boolean | If true, triggers a refresh of the table's view. |
Get a cell identified by row index and column name from this table's current view data.Definition
getViewDataCell(rowIndex Index, colName string)Arguments
| rowIndex | Index | of row in view |
| colName | string | Name of data column |
| DataCell | or | nil if not found. |
Get a cell identified by row index and column name from this table's data.Definition
getDataCell(rowIndex Index, colName string)Arguments
| rowIndex | Index | of row in data |
| colName | string | Name of data column |
| DataCell | or | nil if not found. |
Set the text of a specific cell.Definition
If the cell element does not have a setText() method, this method has no effect. Make sure to only target TextElement
descendants.
setCellText(rowIndex Index, colName string, text string, refreshView boolean)Arguments
| rowIndex | Index | of row in data |
| colName | string | Name of data column |
| text | string | New text value |
| refreshView | boolean | If true, triggers a refresh of the table's view. Use this when changing sortable column data. |
Set visibility of a specific cell.Definition
setCellVisibility(rowIndex Index, colName string, isVisible boolean)Arguments
| rowIndex | Index | of row in data |
| colName | string | Name of data column |
| isVisible | boolean | New visibility value of the cell element |
Apply a temporary overriding GUI profile to a specific cell.Definition
GUI profiles are defined in dataS/guiProfiles.xml. If this is called without a profile name argument, the original
profile is applied on the next table refresh.
setCellOverrideGuiProfile(rowIndex Index, colName string, profileName string)Arguments
| rowIndex | Index | of row in data |
| colName | string | Name of data column |
| profileName | string | Name of GUI profile to override or nil to reset. |
Disables all sorting, does not restore item order.Definition
disableSorting()
Delete all list items.Definition
Override from ListElement: Clears all data and updates the view.
deleteListItems()
Handle clicks on a header element for sorting.Definition
This must be called by the containing screen on callback. The view update needs to be triggered separately.
onClickHeader(headerElement The)Arguments
| headerElement | The | header which has been clicked. |
Get a columns data in SortCell instances to apply sorting.Definition
getSortableColumn(columnName Name)Arguments
| columnName | Name | of the data column, must be configured in #columnNames |
| List | of | SortCell for the requested column name |
Set a custom sorting function to expand on the default data sorting function.Definition
setCustomSortFunction(sortFunction Sorting, useBeforeData If)Arguments
| sortFunction | Sorting | function which takes two SortCell instances to compare and returns a numeric value; value < 0 : e1 < e2, value == 0 : e1 == e2, value > 0 : e1 > e2 |
| useBeforeData | If | true, the custom function will be the first order function. Otherwise, elements will be ordered by data first and custom sorting second. |
Set a filter function for the application of GUI profile overrides.Definition
setProfileOverrideFilterFunction(filterFunction Must)Arguments
| filterFunction | Must | take a DataRow instance and return true if the override profile should be applied or false otherwise. |
Get the table sorting function. If a custom sorting function has been set, the standard data cell sorting will beDefinition
decorated by it. When setting a custom sorting function, another parameter is given which determines if the custom
function should be the first or second criterion in sorting.
getSortFunction(isAscending True)Arguments
| isAscending | True | if data should be sorted in ascending order, false if descending |
Updates the sorted data view with the required sorting orderDefinition
updateSortedView(columnName Name, sortingOrder TableHeaderElement.SORTING_X)Arguments
| columnName | Name | of the column by which rows will be sorted or nil to revert to unsorted state |
| sortingOrder | TableHeaderElement.SORTING_X | value for sort order |
Rebuild the table layout.Definition
This will reposition all row elements and fade them out. If they receive data, they are made visible again.
invalidateLayout()
Update the selected index after view / data changes.Definition
The method tries getting the new selected index by matching data row IDs.
updateSelectedIndex()
Apply data and attributes from visible items to row elements.Definition
updateRows()
Scrolls to an item so that it naturally comes into view.Definition
If the target index is above, this will set the item at the top. Likewise, if the index is below the current view,
the item will be shown at the bottom.
scrollToItemInView(index Target)Arguments
| index | Target | index to scroll to |
Update the table's view. Use this for external updates after data changes.Definition
Fills in data into row elements according to current data and sorting order.
updateView(refocus If)Arguments
| refocus | If | true, the view will be scrolled to the currently selected item after the update. |
Get item index from a row selection.Definition
Override from ListElement: only handles rows.
getItemIndexByRealRowColumn(realRow Row)Arguments
| realRow | Row | data selection index |
Set selection based on list row and column.Definition
Override from ListElement: reads data index from selected table row to select it.
setSelectionByRealRowAndColumn(realRow Row)Arguments
| realRow | Row | selection index |
Update selection state of table rowsDefinition
updateRowSelection()Return Values
| index | of | selected row |
Get an item factor for visual proportions.Definition
Override from ListElement: Simplified table interaction with slider element to require straight indices only.
Therefore this only needs to return 1 now.
getItemFactor()Return Values
Scroll to a data view index position.Definition
scrollTo(index Data, updateSlider If)Arguments
| index | Data | index. The view will update to show the indexed data item at the top. |
| updateSlider | If | true, will update the slider position if present |
Set the selected index in table data view.Definition
Scrolls to the newly selected row index if necessary.
setSelectedIndex(index Numeric, force If)Arguments
| index | Numeric | index in table data view |
| force | If | true, always forces a selection changed callback, even if the index remains the same |
Get the selected element.Definition
Override from ListElement: Instead of returning raw GuiElement instances, this will provide the selected DataRow.
getSelectedElement()Return Values
| dataRow | instance | or nil, selected data index or 0 |
Get the currently selected, visible TableRow instance.Definition
getSelectedTableRow()
Get the data row which corresponds to the visual position of a given element.Definition
This is useful for cases when getSelectedElement() does not currently represent a valid state, e.g. in click
callbacks of buttons contained in table rows (called before the table's mouse event).
getDataRowForElement(element GuiElement)Arguments
| element | GuiElement | instance which should be part of this table's row elements |
| dataRow | instance | or nil (element not part of table row) |
Get the number of data view rows.Definition
Override from ListElement.
getItemCount()Return Values
| number | of | data view rows |
Update item and data indices of rows.Definition
updateItemPositions()
Determine if focus should change from this element in a given direction.Definition
Override from ListElement: Change focus when moving out to the side or selection is at the start/end of the table.
shouldFocusChange(direction Focus)Arguments
| direction | Focus | navigation direction |
Handle receiving focus eventDefinition
onFocusEnter()
Definition
Get the actual focus target, in case a child or parent element needs to be targeted instead.
getFocusTarget(incomingDirection (Optional), moveDirection (Optional))Arguments
| incomingDirection | (Optional) | If specified, may return different targets for different incoming directions. |
| moveDirection | (Optional) | Actual movement direction per input. This is the opposing direction of incomingDirection. |
| GuiElement | Actual | element to focus. |
Handle mouse button up (after down) eventDefinition
Override from ListElement: also update visual selection status of table row elements.
onMouseUp()
Handle navigation input on this table.Definition
inputEvent()
Lock and delay up and down directions for focus navigation.Definition
delayNavigationInput()
Table header element to use within tables.
-- Children which serve as sorting icons are to be marked in the screen XML configuration by setting the name attribute
to "iconAscending" or "iconDescending" depending on which sorting state they are intended to represent. Headers must
be defined outside of tables, because anything within a table is considered a table item.
--@category GUI
--@xmlConfig GuiElement#targetTableId string Configured ID of the decorated table
Add a child element to this GUI element. This element searches for marked children to use as sorting icons.Definition
addElement(element Element)Arguments
| element | Element | to add. |
Toggle this header's sorting display state, if allowed.Definition
toggleSorting()Return Values
| The | new | sorting order |
Disable sorting on this header by setting its sorting state to OFF.Definition
disableSorting()
Update the header's display with its new state.Definition
updateSortingDisplay()
Terrain deformation.
-- This class wraps terrain deformation engine functions for convenient usage.
--@category Terrain
Create a new terrain deformation object.Definition
Take care to call "apply" on each such object to ensure proper handling and clean-up on the engine side.
new(terrainNode Terrain)Arguments
| terrainNode | Terrain | root node, typically retrieved from "currentMission.terrainRootNode" or similar. |
Add a parallelogram area.Definition
Terrain will be modified to fit to this area and receive the provided material. Several areas can be added per
deformation process.
addArea(x Parallelogram, y Parallelogram, Z Parallelogram, side1X Parallelogram, side1Y Parallelogram, side1Z Parallelogram, side2X Parallelogram, side2Y Parallelogram, side2Z Parallelogram, terrainBrushId Terrain)Arguments
| x | Parallelogram | origin X position in world space |
| y | Parallelogram | origin Y position in world space |
| Z | Parallelogram | origin Z position in world space |
| side1X | Parallelogram | side 1 X position offset relative to origin |
| side1Y | Parallelogram | side 1 Y position offset relative to origin |
| side1Z | Parallelogram | side 1 Z position offset relative to origin |
| side2X | Parallelogram | side 2 X position offset relative to origin |
| side2Y | Parallelogram | side 2 Y position offset relative to origin |
| side2Z | Parallelogram | side 2 Z position offset relative to origin |
| terrainBrushId | Terrain | material ID, currently just the map layer index to apply |
Set terrain material for areas outside of the given parallelograms but within the smoothing radius.Definition
setOutsideAreaBrush(brushId Terrain)Arguments
| brushId | Terrain | material ID, currently just the map layer index to apply |
Set the constraints for smoothing the area outside the added parallelogram areas.Definition
setOutsideAreaConstraints(maxSmoothDistance Radius, maxSlope Maximum, maxEdgeAngle Maximum)Arguments
| maxSmoothDistance | Radius | around parallelogram areas to apply smoothing |
| maxSlope | Maximum | allowed slope of terrain created by smoothing in radians |
| maxEdgeAngle | Maximum | allowed angle between individual polygons in the smoothed terrain in radians, this allows reducing the rate of change of the slope |
Get the size of the area map needed for the terrainDefinition
getBlockedAreaMapSize()
Set the collision mask used to detect dynamic objects that are in the way of the deformationDefinition
setDynamicObjectCollisionMask()
Set the max displacement allowed for dynamic objectsDefinition
setDynamicObjectMaxDisplacement()
Set an area map of terrain which must not be modified.Definition
When a "1" appears in the channel for a terrain cell, the terrain remains unmodified at that point. Smoothing occurs
around the blocked areas as well as around the parallelogram areas.
setBlockedAreaMap(bitVectorMapId ID, channel Bit)Arguments
| bitVectorMapId | ID | of a bit vector map which needs to have the size as returned by getBlockedAreaMapSize() |
| channel | Bit | index of the channel within the bit vector map to test for blocking information |
Set the max displacement allowed for the blocked area before a blocked failure occursDefinition
setBlockedAreaMaxDisplacement()
Apply the terrain deformation.Definition
apply(previewOnly If, callbackFunc Name, callbackObject Object)Arguments
| previewOnly | If | true, will only return callback data without committing changes to the terrain. Otherwise, the modification will be applied directly, if possible. |
| callbackFunc | Name | of a callback function which is called when calculation is finished. Signature: function(callbackObject, canDeform, displacedVolume, blocked) |
| callbackObject | Object | which holds callbackFunc and first argument to callback if set. If no callbackObject is provided, the callback function is assumed to be global and will be called as such. |
Cancel an ongoing terrain deformation process.Definition
This will cancel calculations after apply() has been called and before the callback. Otherwise it has no effect.
cancel()
Text display element
--@category GUI
--@xmlConfig GuiElement#textColor string [optional] Normal state text color, defaults to white [1, 1, 1, 1]. Format: "[r] [g] [b] [a]" where each value is in the range of [0.0, 1.0]. The first three values represent red, green and blue color channels and the last is the alpha (translucency) value. When setting this property in guiProfiles.xml, preset values as defined at the top of that file may also be used.
Get the text position for drawing for the current alignment settings and including modifications to text.Definition
getTextPosition()
Text input dialog.
-- @field textElement Text input element
Create a new TextInputDialog instance.Definition
new()
Text input element which captures strings from player input.
-- Used layers: "cursor" for a text input cursor icon.
-- TODO: IME property docs
--@category GUI
--@xmlConfig GuiElement#imeKeyboardType string [optional] Input method editor keyboard type, defaults to "normal". TODO: add valid types based on engine code
Set input capturing state.Definition
When capturing, the standard input bindings are disabled (input context switch).
setCaptureInput()
Handle GUI input events.Definition
Reacts to confirmation and cancel actions. Also see GuiElement.inputEvent().
inputEvent()
Toggle button
-- TODO: Refactor child display element retrieval
-- Used layers: "image" for the background.
--@category GUI
--@xmlConfig GuiElement#isChecked bool [optional] If true, the button is initialized in checked state.
--@xmlConfig GuiElement#onClick callback [optional] onClick(element, isChecked) Called when the element is clicked. Receives this element and the current toggle state as a boolean (true for checked, false for unchecked).
Get the actual focus target of this element.Definition
getFocusTarget()
HUD top notification element.
-- Displays notifications issued by other game components at the top of the screen.
--@category GUI
Create a new TopNotification.Definition
new(string hudAtlasPath)Arguments
| string | hudAtlasPath | Path to the HUD atlas texture. |
| table | value | value at the given position |
| table | TopNotification | instance |
Set a notification to be displayed in a frame at the top of the screen.Definition
If another notification is being displayed, it is immediately replaced by this new one.
setNotification(string title, string text, string info, table iconKey, int duration)Arguments
| string | title | Notification title |
| string | text | Notification message text |
| string | info | Additional info text |
| table | iconKey | [optional] Icon key for a display icon, use a value from TopNotification.ICON |
| int | duration | [optional] Display duration in milliseconds. Negative values or nil default to a long-ish standard duration. |
| table | instance | instance of object |
Get the screen space translation for hiding.Definition
Override in sub-classes if a different translation is required.
getHidingTranslation()Return Values
| table | value | value at the given position |
| float | Screen | space X translation |
| float | Screen | space Y translation |
Update notification state.Definition
update()Return Values
| integer | row | row |
| integer | column | column |
Draw notification.Definition
draw()
Get this element's base background position.Definition
getBackgroundPosition(float uiScale)Arguments
| float | uiScale | Current UI scale factor |
| table | instance | instance of object |
Set uniform UI scale.Definition
setScale()Return Values
| table | instance | instance of object |
Store scaled positioning, size and offset values.Definition
storeScaledValues()Return Values
| boolean | true | if loading was successful else false |
Create the background overlay.Definition
createBackground()Return Values
| boolean | canTip | can tip to ground |
Create required display components.Definition
createComponents()Return Values
| integer | fillType | fill type found |
Tween class which linearly interpolates a quantity from a start value to an end value over a given duration.
--@category GUI
Create a new Tween.Definition
new(table subClass, function setterFunction, float startValue, float endValue, float duration)Arguments
| table | subClass | Subclass metatable for inheritance |
| function | setterFunction | Value setter function. Signature: callback(value) or callback(target, value). |
| float | startValue | Original value |
| float | endValue | Target value |
| float | duration | Duration of tween in milliseconds |
Get this tween's duration in milliseconds.Definition
getDuration()
Check if this tween has finished.Definition
getFinished()
Reset this tween to play it again.Definition
reset()
Set a callback target for this tween.Definition
If a target has been set, the setter function must support receiving the target as its first argument.
setTarget()
Update the tween's state.Definition
update()
Get the current tween value.Definition
tweenValue()
Apply a value via the setter function.Definition
applyValue()
Tween sequence.
-- Allows setting up more complex tweening by defining sequences of tweens, intervals and callbacks. A sequence is
itself a Tween, so you may even define and add sub-sequences.
-- Before a sequence reacts to update() calls, it must be started with start(). This also applies after resetting.
-- Adding tweens, callbacks and intervals will append them to the current sequence. Insertion of tweens and callbacks
will insert them at the given relative instants, allowing for overlapping tweens and arbitrary callback times.
Inserting an interval will push pack all later instants by the given time.
--@category GUI
Create a new TweenSequence.Definition
new(table functionTarget)Arguments
| table | functionTarget | [optional] Target table which is supplied by default to all tween setter functions and callbacks as the first argument. If not specified, the setters and callbacks will be called with one value only. |
Insert a tween at a given instant.Definition
insertTween(table tween, float instant)Arguments
| table | tween | Tween instance |
| float | instant | Time in milliseconds after sequence start |
Add a tween to the end of the sequence.Definition
addTween(table tween)Arguments
| table | tween | Tween instance |
Insert an interval at the given instant.Definition
This will push back all later instants by the interval. Use this to insert pauses into the sequence.
insertInterval(float interval, float instant)Arguments
| float | interval | Interval time in milliseconds |
| float | instant | Time in milliseconds after sequence start |
Add an interval at the end of the sequence.Definition
Use this to add a pause to the sequence.
addInterval()
Insert a callback at the given instant.Definition
insertCallback(function callback, table callbackState, float instant)Arguments
| function | callback | Callback function with signature of either callback(target, value) or callback(value) |
| table | callbackState | Any value which is passed to the callback as its first (no target) or second (with target) argument |
| float | instant | Time in milliseconds after sequence start |
Add a callback at the end of the sequence.Definition
addCallback(function callback, table callbackState)Arguments
| function | callback | Callback function with signature of either callback(target, value) or callback(value) |
| table | callbackState | Any value which is passed to the callback as its first (no target) or second (with target) argument |
Get this tween's duration in milliseconds.Definition
getDuration()
Set a callback target for this tween.Definition
If a target has been set, the setter function must support receiving the target as its first argument.
setTarget()
Set the looping state for this sequence.Definition
setLooping(bool isLooping)Arguments
| bool | isLooping | If true, will restart the sequence when finished, including callbacks! |
Start the sequence.Definition
A sequence will only update its state when it has been started.
start()
Stop the sequence.Definition
stop()
Reset the sequence to its initial state.Definition
reset()
Update the sequence state over time.Definition
update()
Update active sequence tweens.Definition
updateTweens(float lastInstant, float dt)Arguments
| float | lastInstant | Last instant which received an update |
| float | dt | Delta time |
Update callback states.Definition
updateCallbacks()
Un-ban dialog.
-- Displays a list of banned users to allow administrators to lift their bans.
-- @field dialogElement Main dialog frame GUI element which holds other elements
Create a new UnBanDialog instance.Definition
new()
(Re-)Builds the ban list from ban storage data.Definition
rebuildBanList()
Update unban buttons visibility.Definition
updateButtons()
Set the function which is called when this dialog returns.Definition
setCallback()
Close and call the notification callback.Definition
closeAndCallback()
Handle changes in the ban list selection.Definition
onListSelectionChanged()
Handle activation of the back button event.Definition
onClickBack()
Handle activation of the cancel button event which unbans a single player.Definition
onClickCancel()
Handle activation of the activate button event which unbans all users.Definition
onClickActivate()
Custom vehicle HUD drawing extension.
-- This serves as the base class for custom specific drawing cases of vehicles in the HUD, e.g. MixerWagon fill levels.
-- To create new HUD extensions for vehicle specializations:
1. sub-class this base class
2. source() the sub-class module after its corresponding specialization's table has been declared
3. call VehicleHUDExtension.registerHUDExtension([specialization], [HUDextension]) in sub-class module
--@category GUI
Base constructor for vehicle HUD extensions.Definition
new(table class_mt, table vehicle, float uiScale, table uiTextColor, float uiTextSize)Arguments
| table | class_mt | Sub-class metatable |
| table | vehicle | Vehicle which has the specialization required by a sub-class |
| float | uiScale | Current UI scale |
| table | uiTextColor | HUD text drawing color as an RGBA array |
| float | uiTextSize | HUD text size |
| float | fillLevel | fill level found |
Delete this instance and clean up resources.Definition
delete()Return Values
| float | densityHeight | density height |
| float | deltaDensityHeight | delta of density height to terrain underneath |
Add a display component for cleanup on delete().Definition
Added components must support delete() themselves or they will be ignored.
addComponentForCleanup()
Get this HUD extension's display height.Definition
Override in subclasses.
getDisplayHeight()Return Values
| float | physicsDensityHeight | density height |
| float | deltaPhysicsDensityHeight | delta of physics collision to terrain underneath |
Determine if this HUD extension is in a valid state for a call to draw() in the current frame.Definition
Override in sub-classes with custom logic.
canDraw()Return Values
| bool | If | true, the HUD extension should be drawn in the current frame. |
Draw HUD extension.Definition
draw(float leftPosX, float rightPosX, float posY)Arguments
| float | leftPosX | Left input help panel column start position |
| float | rightPosX | Right input help panel column start position |
| float | posY | Current input help panel drawing vertical offset |
| float | dropped | real fill level dropped |
| float | lineOffset | line offset |
| float | Modified | input help panel drawing vertical offset |
Register a HUD extension for a specialization.Definition
registerHUDExtension(table specializationType, table hudExtensionType)Arguments
| table | specializationType | Vehicle specialization class type table |
| table | hudExtensionType | HUD extension class type table corresponding to the given vehicle specialization |
HUD extension factory method, creates a HUD extension for a given vehicle specialization.Definition
createHUDExtensionForSpecialization(table spec, table vehicle, float uiScale, table uiTextColor, float uiTextSize)Arguments
| table | spec | Specialization reference |
| table | vehicle | Vehicle which has the given specialization |
| float | uiScale | Current UI scale |
| table | uiTextColor | HUD text drawing color as an RGBA array |
| float | uiTextSize | HUD text size |
| float | dropped | real fill level dropped |
| float | lineOffset | line offset |
| table | HUD | extension instance or nil of no extension has been registered for the given specialization |
Check if there is a HUD extension for a given specialization.Definition
hasHUDExtensionForSpecialization()
HUD vehicle schema display.
-- Displays a schematic view of the current vehicle configuration.
--@category GUI
Create a new instance of VehicleSchemaDisplay.Definition
new(table modManager)Arguments
| table | modManager | ModManager reference |
| float | fillLevel | fill level removed |
Delete this element.Definition
Also deletes all loaded vehicle schema overlays.
delete()Return Values
| float | fillLevel | fill level changed |
Load vehicle schema overlays from global and mod definitions.Definition
loadVehicleSchemaOverlays()Return Values
| table | instance | Instance of object |
Load and create vehicle schema overlays from XML definitions.Definition
loadVehicleSchemaOverlaysFromXML(int xmlFile, string modPath)Arguments
| int | xmlFile | XML file handle of vehicle schema definitions |
| string | modPath | Path to the current mod description or nil for the base game |
| boolean | true | if loading was successful else false |
Set the currently controlled vehicle to display its schematic view.Definition
setVehicle(table vehicle)Arguments
| table | vehicle | Vehicle reference |
| table | instance | instance of object |
Animation method to set docked state at a delayed time by callback.Definition
lateSetDocked()Return Values
| boolean | true | if loading was successful else false |
Set the schema's docking state.Definition
This element's position is updated based on the docking state.
setDocked(bool isDocked)Arguments
| bool | isDocked | If true, the schema should be display docked to the HUD input help display. Otherwise, it will take the input help's place in the top left corner. |
| boolean | true | if loading was successful else false |
Draw the vehicle schema display.Definition
Only draws the schema if a controlled vehicle is set.
draw()Return Values
| boolean | true | if loading was successful else false |
Animate docking / undocking from input help display.Definition
animateDocking(float startX, float startY, float targetX, float targetY, bool isDocking)Arguments
| float | startX | Screen space starting X position of animation |
| float | startY | Screen space starting Y position of animation |
| float | targetX | Screen space target X position of animation |
| float | targetY | Screen space target Y position of animation |
| bool | isDocking | If true, moving to docking position. If false, moving to stand-alone position. |
| integer | mapHandle | id of bitvector |
Recursively get vehicle schema overlay parts for a vehicle configuration.Definition
collectVehicleSchemaDisplayOverlays()Return Values
| boolean | isOwned | true if farm owns world position point, else false |
Get a vehicle configuration's schema overlays, including the root vehicle.Definition
getVehicleSchemaOverlays()Return Values
| integer | farmId | id of farm. Returns 0 if land is not owned by anyone |
| table | Array | of overlay descriptions: {overlay=overlay, x=0, y=0, rotation=0, invertX=false, invisibleBorderRight=vehicle.schemaOverlay.invisibleBorderRight, invisibleBorderLeft=vehicle.schemaOverlay.invisibleBorderLeft} |
| float | Screen | space height of root vehicle schema overlay |
Get minimum and maximum screen space X positions of vehicle schema overlay descriptions.Definition
The returned positions are relative to the position of the root vehicle schema overlay.
getSchemaDelimiters(table overlayDescriptions)Arguments
| table | overlayDescriptions | Array of overlay descriptions, see VehicleSchemaDisplay:getVehicleSchemaOverlays() |
| integer | farmlandId | farmland id. if 0, world position is no valid/buyable farmland |
| float | Minimum | X position (left) |
| float | Maximum | X position (right) |
Draw vehicle schema icons for a given vehicle.Definition
drawVehicleSchemaOverlays(table vehicle)Arguments
| table | vehicle | Current vehicle |
| boolean | isValid | true if id is valid, else false |
Get a schema overlay for a given vehicle's schema overlay data and current state.Definition
getSchemaOverlayForState(table schemaOverlayData, bool isTurnedOn, bool isSelected, bool isImplement)Arguments
| table | schemaOverlayData | VehicleSchemaOverlayData instance of the current vehicle |
| bool | isTurnedOn | True if the vehicle is currently turned on, i.e. its function is active |
| bool | isSelected | True if the vehicle is currently selected for input |
| bool | isImplement | True if the vehicle is an implement (i.e. attached to a motorized vehicle), false if it's the root vehicle |
| table | farmland | farmland object |
| table | Schema | Overlay instance |
Set this element's UI scale.Definition
setScale(float uiScale)Arguments
| float | uiScale | UI scale factor |
| table | farmlands | all available farmlands |
Store scaled positioning, size and offset values.Definition
storeScaledValues()Return Values
| farmlandIds | table | list of farmland ids owned by given farm id |
Get the vehicle schema's base background position.Definition
getBackgroundPosition(bool isDocked, float uiScale)Arguments
| bool | isDocked | If true, the vehicle schema is docked to the input help display |
| float | uiScale | Current UI scale |
| float | localPosX | local position x |
| float | localPosZ | local position z |
Create an empty background positioning overlay.Definition
createBackground()
Vehicle schema overlay data.
-- The game HUD draws vehicle schemas based on this data.
--@category Vehicles
Create a new VehicleSchemaOverlayData instance.Definition
new(float offsetX, float offsetY, string schemaNameDefault, string schemaNameOn, string schemaNameSelected, string schemaNameSelectedOn, float invisibleBorderRight, float invisibleBorderLeft)Arguments
| float | offsetX | Schema X position offset as a fraction of the schema overlay size width |
| float | offsetY | Schema Y position offset as a fraction of the schema overlay size height |
| string | schemaNameDefault | Name of schema overlay for the vehicle default state |
| string | schemaNameOn | Name of schema overlay for the vehicle turned on state |
| string | schemaNameSelected | Name of schema overlay for the vehicle selected state |
| string | schemaNameSelectedOn | Name of schema overlay for the vehicle selected and turned on state |
| float | invisibleBorderRight | Right margin width of schema overlay display expressed as a fraction of the overlay width |
| float | invisibleBorderLeft | Left margin width of schema overlay display expressed as a fraction of the overlay width |
Add attacher joint information.Definition
addAttacherJoint(float attacherOffsetX, float attacherOffsetY, float rotation, bool invertX, liftedOffsetX X, liftedOffsetY Y)Arguments
| float | attacherOffsetX | Attached vehicle schema overlay X offset expressed as a fraction of the overlay width |
| float | attacherOffsetY | Attached vehicle schema overlay Y offset expressed as a fraction of the overlay height |
| float | rotation | Attached vehicle schema rotation |
| bool | invertX | If true, the attached vehicle schema needs to be flipped horizontally (e.g. front attachments) |
| liftedOffsetX | X | position offset in lifted position in reference resolution pixels |
| liftedOffsetY | Y | position offset in lifted position in reference resolution pixels |
This is the specialization for switchable work modes
-- @author Stefan Maurus
Checks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
| table | specializations | specializations |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| 21 | function WorkMode.prerequisitesPresent(specializations) |
| 22 | return SpecializationUtil.hasSpecialization(WorkArea, specializations) |
| 23 | and SpecializationUtil.hasSpecialization(AnimatedVehicle, specializations) |
| 24 | end |
Called on loadingDefinition
onLoad(table savegame)Arguments
| table | savegame | savegame |
| 59 | function WorkMode:onLoad(savegame) |
| 60 | local spec = self.spec_workMode |
| 61 | |
| 62 | spec.state = 1 |
| 63 | spec.stateMax = 0 |
| 64 | |
| 65 | local baseKey = "vehicle.workModes" |
| 66 | |
| 67 | spec.foldMaxLimit = Utils.getNoNil(getXMLFloat(self.xmlFile, string.format("%s#foldMaxLimit", baseKey)), 1) |
| 68 | spec.foldMinLimit = Utils.getNoNil(getXMLFloat(self.xmlFile, string.format("%s#foldMinLimit", baseKey)), 0) |
| 69 | spec.allowChangeOnLowered = Utils.getNoNil(getXMLBool(self.xmlFile, string.format("%s#allowChangeOnLowered", baseKey)), true) |
| 70 | |
| 71 | spec.workModes = {} |
| 72 | local i = 0 |
| 73 | while true do |
| 74 | local key = string.format("%s.workMode(%d)", baseKey, i) |
| 75 | if not hasXMLProperty(self.xmlFile, key) then |
| 76 | break |
| 77 | end |
| 78 | |
| 79 | local entry = {} |
| 80 | entry.name = g_i18n:getText( getXMLString(self.xmlFile, key .. "#name") ) |
| 81 | local inputBindingName = getXMLString(self.xmlFile, key .. "#inputBindingName") |
| 82 | if inputBindingName ~= nil then |
| 83 | if InputAction[inputBindingName] ~= nil then |
| 84 | entry.inputAction = InputAction[inputBindingName] |
| 85 | end |
| 86 | end |
| 87 | |
| 88 | entry.turnedOnAnimations = {} |
| 89 | local j = 0 |
| 90 | while true do |
| 91 | local key2 = string.format("%s.turnedOnAnimations.turnedOnAnimation(%d)", key, j) |
| 92 | if not hasXMLProperty(self.xmlFile, key2) then |
| 93 | break |
| 94 | end |
| 95 | |
| 96 | local turnedOnAnimation = {} |
| 97 | turnedOnAnimation.name = getXMLString(self.xmlFile, key2.."#name") |
| 98 | turnedOnAnimation.turnOnFadeTime = Utils.getNoNil(getXMLFloat(self.xmlFile, key2.."#turnOnFadeTime"), 1) * 1000 |
| 99 | turnedOnAnimation.turnOffFadeTime = Utils.getNoNil(getXMLFloat(self.xmlFile, key2.."#turnOffFadeTime"), 1) * 1000 |
| 100 | turnedOnAnimation.speedScale = Utils.getNoNil(getXMLFloat(self.xmlFile, key2.."#speedScale"), 1) |
| 101 | |
| 102 | turnedOnAnimation.speedDirection = 0 |
| 103 | turnedOnAnimation.currentSpeed = 0 |
| 104 | |
| 105 | if self:getAnimationExists(turnedOnAnimation.name) then |
| 106 | table.insert(entry.turnedOnAnimations, turnedOnAnimation) |
| 107 | end |
| 108 | |
| 109 | j = j + 1 |
| 110 | end |
| 111 | |
| 112 | entry.loweringAnimations = {} |
| 113 | j = 0 |
| 114 | while true do |
| 115 | local key2 = string.format("%s.loweringAnimations.loweringAnimation(%d)", key, j) |
| 116 | if not hasXMLProperty(self.xmlFile, key2) then |
| 117 | break |
| 118 | end |
| 119 | |
| 120 | local loweringAnimation = {} |
| 121 | loweringAnimation.name = getXMLString(self.xmlFile, key2.."#name") |
| 122 | loweringAnimation.speed = Utils.getNoNil(getXMLFloat(self.xmlFile, key2.."#speed"), 1) |
| 123 | |
| 124 | if self:getAnimationExists(loweringAnimation.name) then |
| 125 | table.insert(entry.loweringAnimations, loweringAnimation) |
| 126 | end |
| 127 | |
| 128 | j = j + 1 |
| 129 | end |
| 130 | |
| 131 | entry.workAreas = {} |
| 132 | j = 0 |
| 133 | while true do |
| 134 | local key2 = string.format("%s.workAreas.workArea(%d)", key, j) |
| 135 | if not hasXMLProperty(self.xmlFile, key2) then |
| 136 | break |
| 137 | end |
| 138 | |
| 139 | local workArea = {} |
| 140 | workArea.workAreaIndex = Utils.getNoNil(getXMLInt(self.xmlFile, key2.."#workAreaIndex"), j+1) |
| 141 | workArea.dropAreaIndex = Utils.getNoNil(getXMLInt(self.xmlFile, key2.."#dropAreaIndex"), j+1) |
| 142 | |
| 143 | table.insert(entry.workAreas, workArea) |
| 144 | |
| 145 | j = j + 1 |
| 146 | end |
| 147 | |
| 148 | entry.animations = {} |
| 149 | j = 0 |
| 150 | while true do |
| 151 | local animKey = string.format("%s.animation(%d)", key, j) |
| 152 | if not hasXMLProperty(self.xmlFile, animKey) then |
| 153 | break |
| 154 | end |
| 155 | |
| 156 | local animation = {} |
| 157 | animation.animName = getXMLString(self.xmlFile, animKey .. "#name") |
| 158 | animation.animSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, animKey .. "#speed"), 1.0) |
| 159 | animation.stopTime = getXMLFloat(self.xmlFile, animKey .. "#stopTime") |
| 160 | animation.repeatAfterUnfolding = Utils.getNoNil(getXMLBool(self.xmlFile, animKey .. "#repeatAfterUnfolding"), false) |
| 161 | animation.repeatStartTime = getXMLFloat(self.xmlFile, animKey .. "#repeatStartTime") |
| 162 | animation.repeated = false |
| 163 | |
| 164 | if self:getAnimationExists(animation.animName) then |
| 165 | table.insert(entry.animations, animation) |
| 166 | end |
| 167 | |
| 168 | j = j + 1 |
| 169 | end |
| 170 | |
| 171 | entry.windrowerEffects = g_effectManager:loadEffect(self.xmlFile, string.format("%s.windrowerEffect", key), self.components, self, self.i3dMappings) |
| 172 | entry.animationNodes = g_animationManager:loadAnimations(self.xmlFile, key..".animationNodes", self.components, self, self.i3dMappings) |
| 173 | |
| 174 | table.insert(spec.workModes, entry) |
| 175 | i = i + 1 |
| 176 | end |
| 177 | |
| 178 | spec.stateMax = table.getn(spec.workModes) |
| 179 | if spec.stateMax > ((2^WorkMode.WORKMODE_SEND_NUM_BITS) - 1) then |
| 180 | print("Error: WorkMode only supports "..((2^WorkMode.WORKMODE_SEND_NUM_BITS) - 1).." modes!") |
| 181 | end |
| 182 | |
| 183 | if spec.stateMax > 0 then |
| 184 | self:setWorkMode(1, true) |
| 185 | end |
| 186 | |
| 187 | spec.accumulatedFruitType = FruitType.UNKNOWN |
| 188 | spec.dirtyFlag = self:getNextDirtyFlag() |
| 189 | end |
Called on client side on joinDefinition
onReadStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 234 | function WorkMode:onReadStream(streamId, connection) |
| 235 | local spec = self.spec_workMode |
| 236 | if spec.stateMax == 0 then |
| 237 | return |
| 238 | end |
| 239 | |
| 240 | local state = streamReadUIntN(streamId, WorkMode.WORKMODE_SEND_NUM_BITS) |
| 241 | self:setWorkMode(state, true) |
| 242 | end |
Called on server side on joinDefinition
onWriteStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 248 | function WorkMode:onWriteStream(streamId, connection) |
| 249 | local spec = self.spec_workMode |
| 250 | if spec.stateMax == 0 then |
| 251 | return |
| 252 | end |
| 253 | |
| 254 | streamWriteUIntN(streamId, spec.state, WorkMode.WORKMODE_SEND_NUM_BITS) |
| 255 | end |
Called on on updateDefinition
onReadUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| 262 | function WorkMode:onReadUpdateStream(streamId, timestamp, connection) |
| 263 | if connection:getIsServer() then |
| 264 | if streamReadBool(streamId) then |
| 265 | local spec = self.spec_workMode |
| 266 | |
| 267 | local mode = spec.workModes[spec.state] |
| 268 | for _, effect in ipairs(mode.windrowerEffects) do |
| 269 | if streamReadBool(streamId) then |
| 270 | effect.lastChargeTime = g_currentMission.time |
| 271 | end |
| 272 | end |
| 273 | |
| 274 | spec.accumulatedFruitType = streamReadUIntN(streamId, 6) |
| 275 | end |
| 276 | end |
| 277 | end |
Called on on updateDefinition
onWriteUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| 284 | function WorkMode:onWriteUpdateStream(streamId, connection, dirtyMask) |
| 285 | if not connection:getIsServer() then |
| 286 | local spec = self.spec_workMode |
| 287 | if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then |
| 288 | local mode = spec.workModes[spec.state] |
| 289 | |
| 290 | for _, effect in ipairs(mode.windrowerEffects) do |
| 291 | streamWriteBool(streamId, effect.lastChargeTime + 500 > g_currentMission.time) |
| 292 | end |
| 293 | |
| 294 | streamWriteUIntN(streamId, spec.accumulatedFruitType, 6) |
| 295 | end |
| 296 | end |
| 297 | end |
Called on updateDefinition
onUpdate(float dt)Arguments
| float | dt | time since last call in ms |
| 302 | function WorkMode:onUpdate(dt, isActiveForInput, isSelected) |
| 303 | local spec = self.spec_workMode |
| 304 | if spec.stateMax == 0 then |
| 305 | return |
| 306 | end |
| 307 | |
| 308 | if self.isClient then |
| 309 | local mode = spec.workModes[spec.state] |
| 310 | |
| 311 | for _, turnedOnAnimation in pairs(mode.turnedOnAnimations) do |
| 312 | if turnedOnAnimation.speedDirection ~= 0 then |
| 313 | local duration = turnedOnAnimation.turnOnFadeTime |
| 314 | if turnedOnAnimation.speedDirection == -1 then |
| 315 | duration = turnedOnAnimation.turnOffFadeTime |
| 316 | end |
| 317 | turnedOnAnimation.currentSpeed = MathUtil.clamp(turnedOnAnimation.currentSpeed + turnedOnAnimation.speedDirection * dt/duration, 0, 1) |
| 318 | self:setAnimationSpeed(turnedOnAnimation.name, turnedOnAnimation.currentSpeed*turnedOnAnimation.speedScale) |
| 319 | if turnedOnAnimation.speedDirection == -1 and turnedOnAnimation.currentSpeed == 0 then |
| 320 | self:stopAnimation(turnedOnAnimation.name, true) |
| 321 | end |
| 322 | |
| 323 | if turnedOnAnimation.currentSpeed == 1 or turnedOnAnimation.currentSpeed == 0 then |
| 324 | turnedOnAnimation.speedDirection = 0 |
| 325 | end |
| 326 | end |
| 327 | end |
| 328 | |
| 329 | for _, effect in pairs(mode.windrowerEffects) do |
| 330 | if effect.lastChargeTime + 500 > g_currentMission.time then |
| 331 | local fillType = g_fruitTypeManager:getWindrowFillTypeIndexByFruitTypeIndex(spec.accumulatedFruitType) |
| 332 | if fillType ~= nil then |
| 333 | effect:setFillType(fillType) |
| 334 | if not effect:isRunning() then |
| 335 | g_effectManager:startEffect(effect) |
| 336 | end |
| 337 | end |
| 338 | else |
| 339 | if effect.turnOffRequiredEffect == 0 or (effect.turnOffRequiredEffect ~= 0 and not mode.windrowerEffects[effect.turnOffRequiredEffect]:isRunning()) then |
| 340 | g_effectManager:stopEffect(effect) |
| 341 | end |
| 342 | end |
| 343 | end |
| 344 | end |
| 345 | |
| 346 | if self.isServer then |
| 347 | local mode = spec.workModes[spec.state] |
| 348 | |
| 349 | local fruitType |
| 350 | local workAreaCharge |
| 351 | for _, area in ipairs(mode.workAreas) do |
| 352 | local workArea = self.spec_workArea.workAreas[area.workAreaIndex] |
| 353 | if workArea ~= nil then |
| 354 | if workArea.lastValidPickupFruitType ~= FruitType.UNKNOWN then |
| 355 | fruitType = workArea.lastValidPickupFruitType |
| 356 | end |
| 357 | workAreaCharge = workAreaCharge or workArea.lastPickupLiters ~= 0 |
| 358 | end |
| 359 | end |
| 360 | |
| 361 | if fruitType ~= nil then |
| 362 | if fruitType ~= spec.accumulatedFruitType then |
| 363 | spec.accumulatedFruitType = fruitType |
| 364 | self:raiseDirtyFlags(spec.dirtyFlag) |
| 365 | end |
| 366 | end |
| 367 | |
| 368 | for _, effect in pairs(mode.windrowerEffects) do |
| 369 | if workAreaCharge then |
| 370 | effect.lastChargeTime = g_currentMission.time |
| 371 | self:raiseDirtyFlags(spec.dirtyFlag) |
| 372 | end |
| 373 | end |
| 374 | end |
| 375 | end |
Called on drawDefinition
onDraw(boolean isActiveForInput, boolean isSelected)Arguments
| boolean | isActiveForInput | true if vehicle is active for input |
| boolean | isSelected | true if vehicle is selected |
| 421 | function WorkMode:onDraw(isActiveForInput, isSelected) |
| 422 | local spec = self.spec_workMode |
| 423 | if spec.stateMax == 0 then |
| 424 | return |
| 425 | else |
| 426 | local mode = spec.workModes[spec.state] |
| 427 | g_currentMission:addExtraPrintText(string.format(g_i18n:getText("action_workModeSelected"), mode.name)) |
| 428 | |
| 429 | local allowWorkModeChange = self:getIsWorkModeChangeAllowed() |
| 430 | |
| 431 | local actionEvent = spec.actionEvents[InputAction.TOGGLE_WORKMODE] |
| 432 | if actionEvent ~= nil then |
| 433 | g_inputBinding:setActionEventActive(actionEvent.actionEventId, allowWorkModeChange) |
| 434 | end |
| 435 | |
| 436 | for _, workMode in ipairs(spec.workModes) do |
| 437 | if workMode.inputAction ~= nil then |
| 438 | actionEvent = spec.actionEvents[workMode.inputAction] |
| 439 | if actionEvent ~= nil then |
| 440 | g_inputBinding:setActionEventActive(actionEvent.actionEventId, allowWorkModeChange) |
| 441 | end |
| 442 | end |
| 443 | end |
| 444 | end |
| 445 | end |
Called on deactivateDefinition
onDeactivate()Code
| 449 | function WorkMode:onDeactivate() |
| 450 | WorkMode.deactivateWindrowerEffects(self) |
| 451 | end |
Called on deactivateDefinition
deactivateWindrowerEffects()Code
| 455 | function WorkMode:deactivateWindrowerEffects() |
| 456 | if self.isClient then |
| 457 | local spec = self.spec_workMode |
| 458 | for _, mode in pairs(spec.workModes) do |
| 459 | if mode.windrowerEffects ~= nil then |
| 460 | g_effectManager:stopEffects(mode.windrowerEffects) |
| 461 | end |
| 462 | end |
| 463 | end |
| 464 | end |
Change work modeDefinition
setWorkMode(integer state, boolean noEventSend)Arguments
| integer | state | new state |
| boolean | noEventSend | no event send |
| 470 | function WorkMode:setWorkMode(state, noEventSend) |
| 471 | local spec = self.spec_workMode |
| 472 | |
| 473 | if noEventSend == nil or noEventSend == false then |
| 474 | if g_server ~= nil then |
| 475 | g_server:broadcastEvent(SetWorkModeEvent:new(self, state), nil, nil, self) |
| 476 | else |
| 477 | g_client:getServerConnection():sendEvent(SetWorkModeEvent:new(self, state)) |
| 478 | end |
| 479 | end |
| 480 | |
| 481 | if state ~= spec.state then |
| 482 | local currentMode = spec.workModes[spec.state] |
| 483 | if currentMode.animations ~= nil then |
| 484 | for _,anim in pairs(currentMode.animations) do |
| 485 | local curTime = self:getAnimationTime(anim.animName) |
| 486 | if anim.stopTime == nil then |
| 487 | self:playAnimation(anim.animName, -anim.animSpeed, curTime, noEventSend) |
| 488 | end |
| 489 | end |
| 490 | g_animationManager:stopAnimations(currentMode.animationNodes) |
| 491 | end |
| 492 | |
| 493 | local newMode = spec.workModes[state] |
| 494 | if newMode.animations ~= nil then |
| 495 | for _,anim in pairs(newMode.animations) do |
| 496 | local curTime = self:getAnimationTime(anim.animName) |
| 497 | if anim.stopTime ~= nil then |
| 498 | self:setAnimationStopTime(anim.animName, anim.stopTime) |
| 499 | local speed = 1.0 |
| 500 | if curTime > anim.stopTime then |
| 501 | speed = -1.0 |
| 502 | end |
| 503 | self:playAnimation(anim.animName, speed, curTime, noEventSend) |
| 504 | else |
| 505 | self:playAnimation(anim.animName, anim.animSpeed, curTime, noEventSend) |
| 506 | end |
| 507 | end |
| 508 | if self:getIsTurnedOn() then |
| 509 | g_animationManager:stopAnimations(newMode.animationNodes) |
| 510 | end |
| 511 | end |
| 512 | |
| 513 | for _, effect in pairs(currentMode.windrowerEffects) do |
| 514 | g_effectManager:stopEffect(effect) |
| 515 | end |
| 516 | end |
| 517 | |
| 518 | local workAreaSpec = self.spec_workArea |
| 519 | if workAreaSpec ~= nil then |
| 520 | local workAreas = workAreaSpec.workAreas |
| 521 | for _, workArea in pairs(spec.workModes[state].workAreas) do |
| 522 | local workAreaToSet = workAreas[workArea.workAreaIndex] |
| 523 | if workAreaToSet ~= nil then |
| 524 | workAreaToSet.dropWindrowWorkAreaIndex = workArea.dropAreaIndex -- windrower |
| 525 | workAreaToSet.dropAreaIndex = workArea.dropAreaIndex -- mower |
| 526 | end |
| 527 | end |
| 528 | end |
| 529 | |
| 530 | spec.state = state |
| 531 | end |
Returns if work mode change is allowedDefinition
getIsWorkModeChangeAllowed()Return Values
| boolean | isAllowed | is allowed |
| 557 | function WorkMode:getIsWorkModeChangeAllowed() |
| 558 | local spec = self.spec_workMode |
| 559 | |
| 560 | if self.getFoldAnimTime ~= nil then |
| 561 | if self:getFoldAnimTime() > spec.foldMaxLimit or self:getFoldAnimTime() < spec.foldMinLimit then |
| 562 | return false |
| 563 | end |
| 564 | end |
| 565 | |
| 566 | if not spec.allowChangeOnLowered then |
| 567 | local attacherVehicle = self:getAttacherVehicle() |
| 568 | if attacherVehicle ~= nil then |
| 569 | local index = attacherVehicle:getAttacherJointIndexFromObject(self) |
| 570 | local attacherJoint = attacherVehicle:getAttacherJointByJointDescIndex(index) |
| 571 | |
| 572 | if attacherJoint.moveDown then |
| 573 | return false |
| 574 | end |
| 575 | end |
| 576 | end |
| 577 | |
| 578 | return true |
| 579 | end |
Called on turn offDefinition
onTurnedOff(boolean noEventSend)Arguments
| boolean | noEventSend | no event send |
| 584 | function WorkMode:onTurnedOff() |
| 585 | local spec = self.spec_workMode |
| 586 | if spec.stateMax == 0 then |
| 587 | return |
| 588 | end |
| 589 | |
| 590 | if self.isClient then |
| 591 | WorkMode.deactivateWindrowerEffects(self) |
| 592 | local mode = spec.workModes[spec.state] |
| 593 | |
| 594 | for _, turnedOnAnimation in pairs(mode.turnedOnAnimations) do |
| 595 | turnedOnAnimation.speedDirection = -1 |
| 596 | end |
| 597 | g_animationManager:stopAnimations(mode.animationNodes) |
| 598 | end |
| 599 | end |
Called on turn onDefinition
onTurnedOn(boolean noEventSend)Arguments
| boolean | noEventSend | no event send |
| 604 | function WorkMode:onTurnedOn() |
| 605 | local spec = self.spec_workMode |
| 606 | if spec.stateMax == 0 then |
| 607 | return |
| 608 | end |
| 609 | |
| 610 | if self.isClient then |
| 611 | local mode = spec.workModes[spec.state] |
| 612 | |
| 613 | for _, turnedOnAnimation in pairs(mode.turnedOnAnimations) do |
| 614 | turnedOnAnimation.speedDirection = 1 |
| 615 | self:playAnimation(turnedOnAnimation.name, turnedOnAnimation.currentSpeed*turnedOnAnimation.speedScale, self:getAnimationTime(turnedOnAnimation.name), true) |
| 616 | end |
| 617 | g_animationManager:startAnimations(mode.animationNodes) |
| 618 | end |
| 619 | end |
Called on change lowering stateDefinition
onSetLowered(boolean lowered)Arguments
| boolean | lowered | attachable is lowered |
| 624 | function WorkMode:onSetLowered(lowered) |
| 625 | local spec = self.spec_workMode |
| 626 | if spec.stateMax == 0 then |
| 627 | return |
| 628 | end |
| 629 | |
| 630 | if self.getFoldAnimTime ~= nil then |
| 631 | local foldAnimTime = self:getFoldAnimTime() |
| 632 | if foldAnimTime ~= 1 and foldAnimTime ~= 0 and foldAnimTime ~= self.foldMiddleAnimTime then |
| 633 | spec.playDelayedLoweringAnimation = lowered |
| 634 | return |
| 635 | end |
| 636 | end |
| 637 | |
| 638 | local mode = spec.workModes[spec.state] |
| 639 | |
| 640 | for _, loweringAnimation in pairs(mode.loweringAnimations) do |
| 641 | if lowered then |
| 642 | if self:getAnimationTime(loweringAnimation.name) < 1 then |
| 643 | self:playAnimation(loweringAnimation.name, loweringAnimation.speed, nil, true) |
| 644 | end |
| 645 | else |
| 646 | if self:getAnimationTime(loweringAnimation.name) > 0 then |
| 647 | self:playAnimation(loweringAnimation.name, -loweringAnimation.speed, nil, true) |
| 648 | end |
| 649 | end |
| 650 | end |
| 651 | end |
Called on fold state changeDefinition
onFoldStateChanged(integer direction)Arguments
| integer | direction | direction of folding |
| 656 | function WorkMode:onFoldStateChanged(direction, moveToMiddle) |
| 657 | local spec = self.spec_workMode |
| 658 | if spec.stateMax == 0 then |
| 659 | return |
| 660 | end |
| 661 | |
| 662 | if direction > 0 then |
| 663 | local mode = spec.workModes[spec.state] |
| 664 | |
| 665 | for _, anim in pairs(mode.animations) do |
| 666 | if anim.repeatAfterUnfolding then |
| 667 | anim.repeated = false |
| 668 | end |
| 669 | end |
| 670 | end |
| 671 | end |
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 18 | function AnimationManager:new(customMt) |
| 19 | local self = AbstractManager:new(customMt or AnimationManager_mt) |
| 20 | |
| 21 | return self |
| 22 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 26 | function AnimationManager:initDataStructures() |
| 27 | self.runningAnimations = {} |
| 28 | self.registeredAnimationClasses = {} |
| 29 | end |
checks if calculation of box is valid (check for NAN)Definition
validateCollisionBox()
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 18 | function HelperManager:new(customMt) |
| 19 | local self = AbstractManager:new(customMt or HelperManager_mt) |
| 20 | return self |
| 21 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 25 | function HelperManager:initDataStructures() |
| 26 | self.numHelpers = 0 |
| 27 | self.helpers = {} |
| 28 | self.nameToIndex = {} |
| 29 | self.indexToHelper = {} |
| 30 | self.availableHelpers = {} |
| 31 | end |
Load data on map loadDefinition
loadMapData()Return Values
| boolean | true | if loading was successful else false |
| 42 | function HelperManager:loadMapData(xmlFile, missionInfo, baseDirectory) |
| 43 | HelperManager:superClass().loadMapData(self) |
| 44 | |
| 45 | self:loadDefaultTypes() |
| 46 | return XMLUtil.loadDataFromMapXML(xmlFile, "helpers", baseDirectory, self, self.loadHelpers, missionInfo, baseDirectory) |
| 47 | end |
Load data on map loadDefinition
loadHelpers()Return Values
| boolean | true | if loading was successful else false |
| 52 | function HelperManager:loadHelpers(xmlFile, missionInfo, baseDirectory, isBaseType) |
| 53 | local i = 0 |
| 54 | while true do |
| 55 | local key = string.format("map.helpers.helper(%d)", i) |
| 56 | if not hasXMLProperty(xmlFile, key) then |
| 57 | break |
| 58 | end |
| 59 | |
| 60 | local name = getXMLString(xmlFile, key.."#name") |
| 61 | local title = getXMLString(xmlFile, key.."#title") |
| 62 | local filename = getXMLString(xmlFile, key.."#filename") |
| 63 | |
| 64 | self:addHelper(name, title, filename, baseDirectory, isBaseType) |
| 65 | |
| 66 | i = i + 1 |
| 67 | end |
| 68 | |
| 69 | return true |
| 70 | end |
Adds a new helperDefinition
addHelper(string name, string filename, string baseDir)Arguments
| string | name | helper index name |
| string | filename | helper config filename |
| string | baseDir | the base directory |
| boolean | true | if added successful else false |
| 78 | function HelperManager:addHelper(name, title, filename, baseDir, isBaseType) |
| 79 | if not ClassUtil.getIsValidIndexName(name) then |
| 80 | print("Warning: '"..tostring(name).."' is not a valid name for a helper. Ignoring helper!") |
| 81 | return nil |
| 82 | end |
| 83 | |
| 84 | name = name:upper() |
| 85 | |
| 86 | if isBaseType and self.nameToIndex[name] ~= nil then |
| 87 | print("Warning: Helper '"..tostring(name).."' already exists. Ignoring helper!") |
| 88 | return nil |
| 89 | end |
| 90 | |
| 91 | local helper = self.helpers[name] |
| 92 | if helper == nil then |
| 93 | if filename == nil or filename == "" then |
| 94 | print("Warning: Missing helper config file for helper '"..tostring(name).."'. Ignoring helper!") |
| 95 | return nil |
| 96 | end |
| 97 | |
| 98 | self.numHelpers = self.numHelpers + 1 |
| 99 | |
| 100 | helper = {} |
| 101 | helper.name = name |
| 102 | helper.index = self.numHelpers |
| 103 | helper.title = name |
| 104 | if title ~= nil then |
| 105 | helper.title = g_i18n:convertText(title) |
| 106 | end |
| 107 | helper.filename = Utils.getFilename(filename, baseDir) |
| 108 | |
| 109 | self.helpers[name] = helper |
| 110 | self.nameToIndex[name] = self.numHelpers |
| 111 | self.indexToHelper[self.numHelpers] = helper |
| 112 | table.insert(self.availableHelpers, helper) |
| 113 | else |
| 114 | if title ~= nil then |
| 115 | helper.title = g_i18n:convertText(title) |
| 116 | end |
| 117 | if filename ~= nil then |
| 118 | helper.filename = Utils.getFilename(filename, baseDir) |
| 119 | end |
| 120 | end |
| 121 | |
| 122 | return helper |
| 123 | end |
Gets a random helperDefinition
getRandomHelper()Return Values
| table | helper | a random helper object |
| 128 | function HelperManager:getRandomHelper() |
| 129 | return self.availableHelpers[math.random(1, #self.availableHelpers)] |
| 130 | end |
Gets a random helper indexDefinition
getRandomIndex()Return Values
| integer | helperIndex | a random helper index |
| 135 | function HelperManager:getRandomIndex() |
| 136 | return math.random(1, self.numHelpers) |
| 137 | end |
Gets a helper by indexDefinition
getHelperByIndex(integer index)Arguments
| integer | index | the helper index |
| table | helper | the helper object |
| 143 | function HelperManager:getHelperByIndex(index) |
| 144 | if index ~= nil then |
| 145 | return self.indexToHelper[index] |
| 146 | end |
| 147 | return nil |
| 148 | end |
Gets a helper by index nameDefinition
getHelperByName(string name)Arguments
| string | name | the helper index name |
| table | helper | the helper object |
| 154 | function HelperManager:getHelperByName(name) |
| 155 | if name ~= nil then |
| 156 | name = name:upper() |
| 157 | return self.helpers[name] |
| 158 | end |
| 159 | return nil |
| 160 | end |
Marks a helper as 'in use'Definition
useHelper(table helper)Arguments
| table | helper | the helper object |
| boolean | success | true if helper is marked else false |
| 166 | function HelperManager:useHelper(helper) |
| 167 | for k, h in pairs(self.availableHelpers) do |
| 168 | if h == helper then |
| 169 | table.remove(self.availableHelpers, k) |
| 170 | return true |
| 171 | end |
| 172 | end |
| 173 | return false |
| 174 | end |
Marks a helper as 'not in use'Definition
releaseHelper(table helper)Arguments
| table | helper | the helper object |
| 179 | function HelperManager:releaseHelper(helper) |
| 180 | table.insert(self.availableHelpers, helper) |
| 181 | end |
Gets number of helpersDefinition
getNumOfHelpers()Return Values
| integer | numOfHelpers | total number of helpers |
| 186 | function HelperManager:getNumOfHelpers() |
| 187 | return self.numHelpers |
| 188 | end |
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 18 | function NPCManager:new(customMt) |
| 19 | local self = AbstractManager:new(customMt or NPCManager_mt) |
| 20 | return self |
| 21 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 25 | function NPCManager:initDataStructures() |
| 26 | self.numNpcs = 0 |
| 27 | self.npcs = {} |
| 28 | self.nameToIndex = {} |
| 29 | self.indexToNpc = {} |
| 30 | end |
Load data on map loadDefinition
loadMapData()Return Values
| boolean | true | if loading was successful else false |
| 41 | function NPCManager:loadMapData(xmlFile, missionInfo, baseDirectory) |
| 42 | NPCManager:superClass().loadMapData(self) |
| 43 | |
| 44 | self:loadDefaultTypes(missionInfo, baseDirectory) |
| 45 | return XMLUtil.loadDataFromMapXML(xmlFile, "npcs", baseDirectory, self, self.loadNPCs, missionInfo, baseDirectory) |
| 46 | end |
Load data on map loadDefinition
loadNPCs()Return Values
| boolean | true | if loading was successful else false |
| 51 | function NPCManager:loadNPCs(xmlFile, missionInfo, baseDirectory, isBaseType) |
| 52 | |
| 53 | local i = 0 |
| 54 | while true do |
| 55 | local key = string.format("map.npcs.npc(%d)", i) |
| 56 | if not hasXMLProperty(xmlFile, key) then |
| 57 | break |
| 58 | end |
| 59 | |
| 60 | local name = getXMLString(xmlFile, key.."#name") |
| 61 | local title = getXMLString(xmlFile, key.."#title") |
| 62 | local category = getXMLString(xmlFile, key.."#category") |
| 63 | local imageFilename = getXMLString(xmlFile, key.."#imageFilename") |
| 64 | |
| 65 | self:addNPC(name, title, category, imageFilename, baseDirectory, isBaseType) |
| 66 | |
| 67 | i = i + 1 |
| 68 | end |
| 69 | |
| 70 | return true |
| 71 | end |
Write data to savegame fileDefinition
saveToXMLFile(string xmlFilename)Arguments
| string | xmlFilename | file path |
| boolean | true | if loading was successful else false |
| 77 | function NPCManager:saveToXMLFile(xmlFilename) |
| 78 | -- save npcs to xml |
| 79 | local xmlFile = createXMLFile("npcsXML", xmlFilename, "npcs") |
| 80 | if xmlFile ~= nil then |
| 81 | for k, npc in ipairs(self.indexToNpc) do |
| 82 | local npcKey = string.format("npcs.npc(%d)", k-1) |
| 83 | setXMLString(xmlFile, npcKey.."#name", npc.name) |
| 84 | setXMLInt(xmlFile, npcKey.."#finishedMissions", npc.finishedMissions) |
| 85 | end |
| 86 | |
| 87 | saveXMLFile(xmlFile) |
| 88 | delete(xmlFile) |
| 89 | |
| 90 | return true |
| 91 | end |
| 92 | |
| 93 | return false |
| 94 | end |
Load data from xml savegame fileDefinition
loadFromXMLFile(string filename)Arguments
| string | filename | xml filename |
| 99 | function NPCManager:loadFromXMLFile(xmlFilename) |
| 100 | if xmlFilename == nil then |
| 101 | return false |
| 102 | end |
| 103 | |
| 104 | local xmlFile = loadXMLFile("npcXML", xmlFilename) |
| 105 | |
| 106 | if xmlFile == 0 then |
| 107 | return false |
| 108 | end |
| 109 | |
| 110 | local i = 0 |
| 111 | while true do |
| 112 | local key = string.format("npcs.npc(%d)", i) |
| 113 | if not hasXMLProperty(xmlFile, key) then |
| 114 | break |
| 115 | end |
| 116 | |
| 117 | local name = getXMLString(xmlFile, key.."#name") |
| 118 | local npc = self:getNPCByName(name) |
| 119 | if npc ~= nil then |
| 120 | npc.finishedMissions = Utils.getNoNil(getXMLInt(xmlFile, key.."#finishedMissions"), 0) |
| 121 | else |
| 122 | print("Warning: Npc '"..tostring(name).."' not found!") |
| 123 | end |
| 124 | i = i + 1 |
| 125 | end |
| 126 | |
| 127 | delete(xmlFile) |
| 128 | |
| 129 | return true |
| 130 | end |
Adds a new npcDefinition
addNPC(string name, string title, string category, string imageFilename, string baseDir)Arguments
| string | name | npc index name |
| string | title | npc real name |
| string | category | npc category (e.g. young, middle, old, female) |
| string | imageFilename | npc image filename |
| string | baseDir | the base directory |
| boolean | true | if added successful else false |
| 140 | function NPCManager:addNPC(name, title, category, imageFilename, baseDir, isBaseType) |
| 141 | if not ClassUtil.getIsValidIndexName(name) then |
| 142 | print("Warning: '"..tostring(name).."' is not a valid name for a npc. Ignoring npc!") |
| 143 | return nil |
| 144 | end |
| 145 | |
| 146 | name = name:upper() |
| 147 | |
| 148 | if isBaseType and self.nameToIndex[name] ~= nil then |
| 149 | print("Warning: NPC '"..tostring(name).."' already exists. Ignoring npc!") |
| 150 | return nil |
| 151 | end |
| 152 | |
| 153 | local npc = self.npcs[name] |
| 154 | if npc == nil then |
| 155 | if title == nil or title == "" then |
| 156 | print("Warning: '"..tostring(title).."' is not a valid title for a npc. Ignoring npc!") |
| 157 | return nil |
| 158 | end |
| 159 | if category == nil or category == "" then |
| 160 | print("Warning: '"..tostring(category).."' is not a valid category for a npc. Ignoring npc!") |
| 161 | return nil |
| 162 | end |
| 163 | if imageFilename == nil or imageFilename == "" then |
| 164 | print("Warning: Missing npc image file for npc '"..tostring(name).."'. Ignoring npc!") |
| 165 | return nil |
| 166 | end |
| 167 | |
| 168 | self.numNpcs = self.numNpcs + 1 |
| 169 | npc = {} |
| 170 | npc.name = name |
| 171 | npc.title = g_i18n:convertText(title) |
| 172 | npc.index = self.numNpcs |
| 173 | npc.imageFilename = Utils.getFilename(imageFilename, baseDir) |
| 174 | npc.finishedMissions = 0 |
| 175 | |
| 176 | self.npcs[name] = npc |
| 177 | self.nameToIndex[name] = self.numNpcs |
| 178 | self.indexToNpc[self.numNpcs] = npc |
| 179 | else |
| 180 | if title ~= nil and title ~= "" then |
| 181 | npc.title = g_i18n:convertText(title) |
| 182 | end |
| 183 | if imageFilename ~= nil and imageFilename ~= "" then |
| 184 | npc.imageFilename = Utils.getFilename(imageFilename, baseDir) |
| 185 | end |
| 186 | end |
| 187 | |
| 188 | return npc |
| 189 | end |
Gets a random npcDefinition
getRandomNPC()Return Values
| table | npc | a random npc object |
| 194 | function NPCManager:getRandomNPC() |
| 195 | return self.indexToNpc[self:getRandomIndex()] |
| 196 | end |
Gets a random npc indexDefinition
getRandomIndex()Return Values
| integer | npcIndex | a random npc index |
| 201 | function NPCManager:getRandomIndex() |
| 202 | return math.random(1, self.numNpcs) |
| 203 | end |
Gets a npc by indexDefinition
getNPCByIndex(integer index)Arguments
| integer | index | the npc index |
| table | npc | the npc object |
| 209 | function NPCManager:getNPCByIndex(index) |
| 210 | if index ~= nil then |
| 211 | return self.indexToNpc[index] |
| 212 | end |
| 213 | return nil |
| 214 | end |
Gets a npc by index nameDefinition
getNPCByName(string name)Arguments
| string | name | the npc index name |
| table | npc | the npc object |
| 220 | function NPCManager:getNPCByName(name) |
| 221 | if name ~= nil then |
| 222 | name = name:upper() |
| 223 | return self.npcs[name] |
| 224 | end |
| 225 | return nil |
| 226 | end |
Creating managerDefinition
new()Return Values
| table | instance | instance of basket trigger object |
| table | instance | instance of object |
| 17 | function I3DManager:new(customMt) |
| 18 | local self = AbstractManager:new(customMt or I3DManager_mt) |
| 19 | |
| 20 | return self |
| 21 | end |
Initialize data structuresDefinition
initDataStructures()Return Values
| boolean | success | success |
| 25 | function I3DManager:initDataStructures() |
| 26 | self.sharedI3DFiles = {} |
| 27 | self.sharedI3DFilesPendingCallbacks = {} |
| 28 | end |
Loads an i3D file. A cache system is used for faster loadingDefinition
loadSharedI3DFile(string filename, string baseDir, boolean callOnCreate, boolean addToPhysics, boolean verbose, function asyncCallbackFunction, table asyncCallbackObject, table asyncCallbackArguments)Arguments
| string | filename | filename |
| string | baseDir | baseDir |
| boolean | callOnCreate | true if onCreate i3d callbacks should be called |
| boolean | addToPhysics | true if collisions should be added to physics |
| boolean | verbose | verbose |
| function | asyncCallbackFunction | a callback function |
| table | asyncCallbackObject | callback function target object |
| table | asyncCallbackArguments | a list of arguments |
| table | instance | instance |
| integer | id | i3d rootnode |
Called once i3d loading is finishedDefinition
loadSharedI3DFileFinished(integer nodeId, table arguments)Arguments
| integer | nodeId | i3d node id |
| table | arguments | a list of arguments |
| boolean | isActivateable | is activateable |
Adds an i3d file to cacheDefinition
fillSharedI3DFileCache(string filename, string baseDir)Arguments
| string | filename | filename |
| string | baseDir | baseDir |
| table | instance | instance of basket trigger object |
Releases one instance. If autoDelete is true and instance count <= 0. I3d will be removed from cacheDefinition
releaseSharedI3DFile(string filename, string baseDir, boolean autoDelete)Arguments
| string | filename | filename |
| string | baseDir | baseDir |
| boolean | autoDelete | true if file should be removed from cache if instance count is <= 0 |
| boolean | success | success |
Clears i3d cacheDefinition
deleteSharedI3DFiles()Return Values
| table | instance | instance of object |
Class for chainsaws
Creating chainsaw objectDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | instance of object |
| table | instance | Instance of object |
| 35 | function Chainsaw:new(isServer, isClient, customMt) |
| 36 | local self = HandTool:new(isServer, isClient, customMt or Chainsaw_mt) |
| 37 | return self |
| 38 | end |
Load chainsaw from xml fileDefinition
load(string xmlFilename, table player)Arguments
| string | xmlFilename | xml file name |
| table | player | player |
| bool | true | if player can idle |
| boolean | success | success |
| 45 | function Chainsaw:load(xmlFilename, player) |
| 46 | if not Chainsaw:superClass().load(self, xmlFilename, player) then |
| 47 | return false |
| 48 | end |
| 49 | |
| 50 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 51 | |
| 52 | self.rotateInput = 0 |
| 53 | self.activatePressed = false |
| 54 | self.eventIdRotateHandtool = "" |
| 55 | |
| 56 | self.rotationZ = 0 |
| 57 | self.rotationSpeedZ = 0.003 |
| 58 | self.cutSizeY = 1.1 |
| 59 | self.cutSizeZ = 1.0 |
| 60 | self.isCutting = false |
| 61 | self.waitingForResetAfterCut = false |
| 62 | self.cutNode = getChildAt(self.rootNode, 0) |
| 63 | self.graphicsNode = getChildAt(self.cutNode, 0) |
| 64 | self.chainNode = getChildAt(self.graphicsNode, 0) |
| 65 | self.psNode = getChildAt(self.graphicsNode, 1) |
| 66 | self.cutPositionNode = getChildAt(self.graphicsNode, 5) |
| 67 | |
| 68 | self.pricePerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.chainsaw.pricePerMinute"), 50) / 1000 |
| 69 | self.quicktapThreshold = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.chainsaw#quicktapThreshold"), 0.0) * 1000 |
| 70 | if self.isClient then |
| 71 | self.particleSystems = {} |
| 72 | |
| 73 | local i = 0 |
| 74 | while true do |
| 75 | local keyPS = string.format("handTool.chainsaw.particleSystems.emitterShape(%d)", i) |
| 76 | if not hasXMLProperty(xmlFile, keyPS) then |
| 77 | break |
| 78 | end |
| 79 | local emitterShape = I3DUtil.indexToObject(self.rootNode, getXMLString(xmlFile, keyPS.."#node")) |
| 80 | local particleType = getXMLString(xmlFile, keyPS.."#particleType") |
| 81 | if emitterShape ~= nil then |
| 82 | local fillType = FillType.WOODCHIPS |
| 83 | local particleSystem = g_particleSystemManager:getParticleSystem(fillType, particleType) |
| 84 | if particleSystem ~= nil then |
| 85 | table.insert(self.particleSystems, ParticleUtil.copyParticleSystem(xmlFile, keyPS, particleSystem, emitterShape)) |
| 86 | end |
| 87 | end |
| 88 | i = i + 1 |
| 89 | end |
| 90 | |
| 91 | if #self.particleSystems == 0 then |
| 92 | self.particleSystems = nil |
| 93 | end |
| 94 | |
| 95 | self.handNode = Utils.getNoNil(I3DUtil.indexToObject(self.rootNode, getXMLString(xmlFile, "handTool.chainsaw.handNode#node")), self.rootNode) |
| 96 | self.handNodeRotation = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.chainsaw.handNode#rotation"), "0 0 0"), 3) |
| 97 | |
| 98 | self.equipmentUVs = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.chainsaw.equipment#uvs"), "0 0"), 2) |
| 99 | |
| 100 | self.chains = g_animationManager:loadAnimations(xmlFile, "handTool.chainsaw.chain", self.rootNode, self, nil) |
| 101 | self.samples = {} |
| 102 | self.samples.start = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "start", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 103 | self.samples.stop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "stop", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 104 | self.samples.idle = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "idle", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil) |
| 105 | self.samples.cutStart = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutStart", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 106 | self.samples.cutStop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutStop", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 107 | self.samples.cutLoop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "cutLoop", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil) |
| 108 | self.samples.activeStart = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeStart", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 109 | self.samples.activeStop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeStop", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 110 | self.samples.activeLoop = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds", "activeLoop", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, nil) |
| 111 | self.samplesQuicktap = {} |
| 112 | local j = 0 |
| 113 | while true do |
| 114 | local sampleQuicktap = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.sounds.quickTapSounds", string.format("quickTap(%d)", j), self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 115 | if sampleQuicktap == nil then |
| 116 | break |
| 117 | end |
| 118 | table.insert(self.samplesQuicktap, sampleQuicktap) |
| 119 | j = j + 1 |
| 120 | end |
| 121 | self.samplesQuicktapCount = j |
| 122 | |
| 123 | self.samplesTree = {} |
| 124 | self.samplesTree.cut = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.treeSounds", "cut", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 125 | --self.samplesTree.falling = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.treeSounds", "falling", self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 126 | self.samplesBranch = {} |
| 127 | local k = 0 |
| 128 | while true do |
| 129 | local sampleBranch = g_soundManager:loadSampleFromXML(xmlFile, "handTool.chainsaw.branchSounds", string.format("branch(%d)", k), self.baseDirectory, self.rootNode, 1, AudioGroup.VEHICLE, nil, nil) |
| 130 | if sampleBranch == nil then |
| 131 | break |
| 132 | end |
| 133 | table.insert(self.samplesBranch, sampleBranch) |
| 134 | k = k + 1 |
| 135 | end |
| 136 | self.samplesBranchCount = k |
| 137 | self.samplesBranchActiveTimer = 0 |
| 138 | |
| 139 | self.samplesTreeLinkNode = createTransformGroup("cutSoundLinkNode") |
| 140 | link(self.cutNode, self.samplesTreeLinkNode) |
| 141 | |
| 142 | if self.samplesTree.cut ~= nil and self.samplesTree.cut.soundNode ~= nil then |
| 143 | link(self.samplesTreeLinkNode, self.samplesTree.cut.soundNode) |
| 144 | --link(self.samplesTreeLinkNode, self.samplesTree.falling.soundNode) |
| 145 | end |
| 146 | |
| 147 | self.soundFSM = FSMUtil.create() |
| 148 | self.soundFSM:addState(Chainsaw.SOUND_STATES.START, ChainsawSoundStateStart:new(Chainsaw.SOUND_STATES.START, self, self.soundFSM)) |
| 149 | self.soundFSM:addState(Chainsaw.SOUND_STATES.STOP, ChainsawSoundStateStop:new(Chainsaw.SOUND_STATES.STOP, self, self.soundFSM)) |
| 150 | self.soundFSM:addState(Chainsaw.SOUND_STATES.IDLE, ChainsawSoundStateIdle:new(Chainsaw.SOUND_STATES.IDLE, self, self.soundFSM)) |
| 151 | self.soundFSM:addState(Chainsaw.SOUND_STATES.ACTIVE, ChainsawSoundStateActive:new(Chainsaw.SOUND_STATES.ACTIVE, self, self.soundFSM)) |
| 152 | self.soundFSM:addState(Chainsaw.SOUND_STATES.CUT, ChainsawSoundStateCut:new(Chainsaw.SOUND_STATES.CUT, self, self.soundFSM)) |
| 153 | self.soundFSM:addState(Chainsaw.SOUND_STATES.QUICKTAP, ChainsawSoundStateQuicktap:new(Chainsaw.SOUND_STATES.QUICKTAP, self, self.soundFSM)) |
| 154 | |
| 155 | local filename = getXMLString(xmlFile, "handTool.chainsaw.ringSelector#file") |
| 156 | if filename ~= nil then |
| 157 | local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false) |
| 158 | if i3dNode ~= 0 then |
| 159 | self.ringSelectorFilename = filename |
| 160 | self.ringSelector = getChildAt(i3dNode, 0) |
| 161 | self.ringSelectorScaleOffset = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.chainsaw.ringSelector#scaleOffset"), 0.3) |
| 162 | setVisibility(self.ringSelector, false) |
| 163 | link(player.chainsawSplitShapeFocus, self.ringSelector) |
| 164 | delete(i3dNode) |
| 165 | end |
| 166 | end |
| 167 | end |
| 168 | |
| 169 | self.lastWorkTime = 0 |
| 170 | self.maxWorkTime = 300 |
| 171 | |
| 172 | self.moveSpeedY = 0.0001 |
| 173 | self.speedFactor = 0 |
| 174 | self.defaultCutDuration = 8.0 -- in s |
| 175 | self.maxTrunkWidthSq = 1.0 -- in m |
| 176 | self.outDuration = 0.15 -- in s |
| 177 | self.inDuration = 0.15 -- in s |
| 178 | self.cutTimer = 0.0 -- in s |
| 179 | self.outTimer = self.outDuration -- in s |
| 180 | self.transitionAlpha = 0.0 -- [0,1] |
| 181 | self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.NONE -- 0=in 1=cut 2=out |
| 182 | self.minRotationZ = math.rad(90) -- in rad |
| 183 | self.maxRotationZ = math.rad(-90) -- in rad |
| 184 | self.maxModelTranslation = 0.0 -- in m |
| 185 | self.cutFocusDistance = -1.0 |
| 186 | self.startCameraDirectionY = { 0, 1, 0 } |
| 187 | self.startCameraDirectionZ = { 0, 0, 1 } |
| 188 | self.endCameraDirectionY = { 0, 1, 0 } |
| 189 | self.endCameraDirectionZ = { 0, 0, 1 } |
| 190 | self.startChainsawPosition = { 0, 0, 0 } |
| 191 | self.endChainsawPosition = { 0, 0, 0 } |
| 192 | self.showNotOwnedWarning = false |
| 193 | |
| 194 | self.isCutting = false |
| 195 | self.isHorizontalCut = false |
| 196 | |
| 197 | delete(xmlFile) |
| 198 | |
| 199 | return true |
| 200 | end |
Deleting chainsawDefinition
delete()Return Values
| table | instance | instance of object |
| 204 | function Chainsaw:delete() |
| 205 | if self.isClient then |
| 206 | ParticleUtil.deleteParticleSystems(self.particleSystems) |
| 207 | |
| 208 | if self.ringSelector ~= nil then |
| 209 | delete(self.ringSelector) |
| 210 | end |
| 211 | for _,sample in pairs(self.samplesTree) do |
| 212 | g_soundManager:deleteSample(sample) |
| 213 | end |
| 214 | for _,sample in pairs(self.samplesBranch) do |
| 215 | g_soundManager:deleteSample(sample) |
| 216 | end |
| 217 | for _,sample in pairs(self.samples) do |
| 218 | g_soundManager:deleteSample(sample) |
| 219 | end |
| 220 | if self.ringSelectorFilename ~= nil then |
| 221 | g_i3DManager:releaseSharedI3DFile(self.ringSelectorFilename, self.baseDirectory, true) |
| 222 | end |
| 223 | g_animationManager:deleteAnimations(self.chains) |
| 224 | end |
| 225 | |
| 226 | Chainsaw:superClass().delete(self) |
| 227 | end |
Saves raycast informationDefinition
cutRaycastCallback(integer hitObjectId, float x, float y, float z, float distance)Arguments
| integer | hitObjectId | |
| float | x | |
| float | y | |
| float | z | |
| float | distance |
| bool | true | if player can swim |
| 236 | function Chainsaw:cutRaycastCallback(hitObjectId, x, y, z, distance) |
| 237 | setWorldTranslation(self.player.chainsawCameraFocus, x, y, z) |
| 238 | self.cutFocusDistance = distance |
| 239 | end |
Cast ray from the player cameraDefinition
updateCutRaycast()Return Values
| bool | true | if player can swim |
| 243 | function Chainsaw:updateCutRaycast() |
| 244 | self.cutFocusDistance = -1.0 |
| 245 | setTranslation(self.player.chainsawCameraFocus, 0, 0, 0) |
| 246 | local cameraPosition = {getWorldTranslation(self.player.cameraNode)} |
| 247 | local worldDirection = {unProject(0.52, 0.4, 1)} -- Transform vector from screen space into world space |
| 248 | local treeCollisionMask = 16789504 -- bits: 12,13,24 |
| 249 | |
| 250 | worldDirection[1], |
| 251 | worldDirection[2], |
| 252 | worldDirection[3] = MathUtil.vector3Normalize(worldDirection[1], worldDirection[2], worldDirection[3]) |
| 253 | raycastClosest(cameraPosition[1], cameraPosition[2], cameraPosition[3], |
| 254 | worldDirection[1], worldDirection[2], worldDirection[3], |
| 255 | "cutRaycastCallback", self.player.cutDetectionDistance, |
| 256 | self, treeCollisionMask) |
| 257 | end |
testTooLow(integer shape, float minY, float maxY, float minZ, float maxZ)Arguments
| integer | shape | |
| float | minY | |
| float | maxY | |
| float | minZ | |
| float | maxZ |
| table | instance | instance of object |
| 267 | function Chainsaw:testTooLow(shape, minY, maxY, minZ, maxZ) |
| 268 | local cutTooLow = false |
| 269 | local _,y1,_ = localToLocal(self.player.chainsawSplitShapeFocus, shape, 0,minY,minZ) |
| 270 | local _,y3,_ = localToLocal(self.player.chainsawSplitShapeFocus, shape, 0,maxY,minZ) |
| 271 | local _,y4,_ = localToLocal(self.player.chainsawSplitShapeFocus, shape, 0,maxY,maxZ) |
| 272 | |
| 273 | cutTooLow = y1 < 0.01 or y1 < 0.01 or y3 < 0.03 or y4 < 0.01 |
| 274 | if not cutTooLow then |
| 275 | local x1,y1,z1 = localToWorld(self.player.chainsawSplitShapeFocus, 0,minY,minZ) |
| 276 | local x2,y2,z2 = localToWorld(self.player.chainsawSplitShapeFocus, 0,minY,maxZ) |
| 277 | local x3,y3,z3 = localToWorld(self.player.chainsawSplitShapeFocus, 0,maxY,minZ) |
| 278 | local x4,y4,z4 = localToWorld(self.player.chainsawSplitShapeFocus, 0,maxY,maxZ) |
| 279 | local h1 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x1,y1,z1) |
| 280 | local h2 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x2,y2,z2) |
| 281 | local h3 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x3,y3,z3) |
| 282 | local h4 = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x4,y4,z4) |
| 283 | cutTooLow = h1 < 0.01 or h2 < 0.01 or h3 < 0.03 or h4 < 0.01 |
| 284 | end |
| 285 | if cutTooLow then |
| 286 | return true |
| 287 | end |
| 288 | return false |
| 289 | end |
Given a target position we calculate a up and towards direction (result is in world reference)Definition
getLookAt(integer camera, float target, float target, float target)Arguments
| integer | camera | identifier |
| float | target | position x |
| float | target | position y |
| float | target | position z |
| table | instance | Instance of object |
| float | ||
| float | ||
| float | ||
| float | ||
| float | ||
| float |
| 303 | function Chainsaw:getLookAt(cameraNode, targetX, targetY, targetZ) |
| 304 | local xx, xy, xz = 0, 0, 0 |
| 305 | local yx, yy, yz = 0, 0, 0 |
| 306 | local zx, zy, zz = 0, 0, 0 |
| 307 | local nodePosition = {getWorldTranslation(cameraNode)} |
| 308 | local nodeUpDirection = {localDirectionToWorld(getParent(cameraNode), 0, -1, 0)} |
| 309 | |
| 310 | zx = nodePosition[1] - targetX |
| 311 | zy = nodePosition[2] - targetY |
| 312 | zz = nodePosition[3] - targetZ |
| 313 | zx, zy, zz = MathUtil.vector3Normalize(zx, zy, zz) |
| 314 | xx, xy, xz = MathUtil.crossProduct(zx, zy, zz, nodeUpDirection[1], nodeUpDirection[2], nodeUpDirection[3]) |
| 315 | xx, xy, xz = MathUtil.vector3Normalize(xx, xy, xz) |
| 316 | yx, yy, yz = MathUtil.crossProduct(zx, zy, zz, xx, xy, xz) |
| 317 | yx, yy, yz = MathUtil.vector3Normalize(yx, yy, yz) |
| 318 | |
| 319 | return yx, yy, yz, zx, zy, zz |
| 320 | end |
Function to calculate the start and the end of the cut by using the ringSelectorDefinition
getCutStartEnd()Return Values
| table | instance | Instance of object |
| 324 | function Chainsaw:getCutStartEnd() |
| 325 | local selectorPosition = {getWorldTranslation(self.ringSelector)} |
| 326 | local selectorScale = {getScale(self.ringSelector)} |
| 327 | local cutDirection = {localDirectionToWorld(self.ringSelector, 0, 1, 0)} |
| 328 | local cutStartposition = { selectorPosition[1] - 0.5 * selectorScale[1] * cutDirection[1], |
| 329 | selectorPosition[2] - 0.5 * selectorScale[2] * cutDirection[2], |
| 330 | selectorPosition[3] - 0.5 * selectorScale[3] * cutDirection[3] } |
| 331 | local cutEndposition = { selectorPosition[1] + 0.5 * selectorScale[1] * cutDirection[1], |
| 332 | selectorPosition[2] + 0.5 * selectorScale[2] * cutDirection[2], |
| 333 | selectorPosition[3] + 0.5 * selectorScale[3] * cutDirection[3] } |
| 334 | return cutStartposition[1], cutStartposition[2], cutStartposition[3], cutEndposition[1], cutEndposition[2], cutEndposition[3] |
| 335 | end |
calculateCutDuration(float dt, bool true)Arguments
| float | dt | in milliseconds |
| bool | true | if the player is cutting |
| table | instance | Instance of object |
| 341 | function Chainsaw:calculateCutDuration() |
| 342 | local startX, startY, startZ, endX, endY, endZ = self:getCutStartEnd() |
| 343 | local trunkWidthSq = MathUtil.vector3LengthSq(endX - startX, endY - startY, endZ - startZ) |
| 344 | trunkWidthSq = MathUtil.clamp(trunkWidthSq, 0.0, self.maxTrunkWidthSq) |
| 345 | local cutDuration = trunkWidthSq * self.defaultCutDuration / self.maxTrunkWidthSq |
| 346 | return cutDuration |
| 347 | end |
updateCuttingTimers(float dt, bool true)Arguments
| float | dt | in milliseconds |
| bool | true | if the player is cutting |
| table | instance | Instance of object |
| 353 | function Chainsaw:updateCuttingTimers(dt, isCutting) |
| 354 | local dtInSec = dt * 0.001 |
| 355 | self.transitionAlpha = 0.0 |
| 356 | |
| 357 | if isCutting then |
| 358 | local cutDuration = self:calculateCutDuration() |
| 359 | if (self.cutTimer == 0.0) then |
| 360 | self.outTimer = 0.0 |
| 361 | self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.IN |
| 362 | elseif self.cutTimer == self.inDuration then |
| 363 | self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.CUT |
| 364 | end |
| 365 | |
| 366 | if (self.cutTimer >= 0.0) and (self.cutTimer < self.inDuration) then |
| 367 | self.cutTimer = math.min(self.cutTimer + dtInSec, self.inDuration) |
| 368 | self.transitionAlpha = MathUtil.clamp(self.cutTimer, 0.0, self.inDuration) / self.inDuration |
| 369 | elseif (self.cutTimer >= self.inDuration) and (self.cutTimer < cutDuration) then |
| 370 | local restCutDuration = math.max(cutDuration - self.inDuration, 0) |
| 371 | |
| 372 | self.cutTimer = math.min(self.cutTimer + dtInSec, cutDuration) |
| 373 | self.transitionAlpha = MathUtil.clamp(self.cutTimer - self.inDuration, 0, restCutDuration) / restCutDuration |
| 374 | else |
| 375 | self.transitionAlpha = 1.0 |
| 376 | end |
| 377 | else |
| 378 | cutDuration = self.defaultCutDuration |
| 379 | if self.outTimer == 0.0 then |
| 380 | self.cutTimer = 0.0 |
| 381 | self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.OUT |
| 382 | end |
| 383 | |
| 384 | if (self.outTimer >= 0.0) and (self.outTimer < self.outDuration) then |
| 385 | self.outTimer = math.min(self.outTimer + dtInSec, self.outDuration) |
| 386 | self.transitionAlpha = MathUtil.clamp(self.outTimer, 0.0, self.outDuration) / self.outDuration |
| 387 | end |
| 388 | end |
| 389 | end |
resetTransitionState()Return Values
| table | instance | Instance of object |
| 393 | function Chainsaw:resetTransitionState() |
| 394 | if (self.cameraTransitionState ~= Chainsaw.CAMERA_TRANSITION_STATES.NONE) then |
| 395 | self.cameraTransitionState = Chainsaw.CAMERA_TRANSITION_STATES.NONE |
| 396 | end |
| 397 | end |
This function updates the timers: cuttimer and the outTimer, depending if the player is cutting or not: we switch from player camera to cutting camera as to not lose focus. We rotate the cutting camera towards the cut target. We the player has finished cutting we move the cutting camera towards the player camera and switch back to the player camera.Definition
updateCuttingCamera(bool isCutting)Arguments
| bool | isCutting | true if the player is cutting |
| table | instance | Instance of object |
| 402 | function Chainsaw:updateCuttingCamera(isCutting) |
| 403 | if isCutting then |
| 404 | if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.IN then |
| 405 | setRotation(self.player.cuttingCameraNode, 0, 0, 0) |
| 406 | local yx, yy, yz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 1, 0) |
| 407 | local zx, zy, zz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 0, 1) |
| 408 | local startX, startY, startZ, _, _, _ = self:getCutStartEnd() |
| 409 | |
| 410 | self.startCameraDirectionY = {yx, yy, yz} |
| 411 | self.startCameraDirectionZ = {zx, zy, zz} |
| 412 | self.endCameraDirectionY[1], self.endCameraDirectionY[2], self.endCameraDirectionY[3], |
| 413 | self.endCameraDirectionZ[1], self.endCameraDirectionZ[2], self.endCameraDirectionZ[3] = self:getLookAt(self.player.cuttingCameraNode, startX, startY, startZ) |
| 414 | elseif self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.CUT then |
| 415 | local startX, startY, startZ, endX, endY, endZ = self:getCutStartEnd() |
| 416 | |
| 417 | self.startCameraDirectionY[1], self.startCameraDirectionY[2], self.startCameraDirectionY[3], |
| 418 | self.startCameraDirectionZ[1], self.startCameraDirectionZ[2], self.startCameraDirectionZ[3] = self:getLookAt(self.player.cuttingCameraNode, startX, startY, startZ) |
| 419 | self.endCameraDirectionY[1], self.endCameraDirectionY[2], self.endCameraDirectionY[3], |
| 420 | self.endCameraDirectionZ[1], self.endCameraDirectionZ[2], self.endCameraDirectionZ[3] = self:getLookAt(self.player.cuttingCameraNode, endX, endY, endZ) |
| 421 | end |
| 422 | else |
| 423 | if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.OUT then |
| 424 | local yx, yy, yz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 1, 0) |
| 425 | local zx, zy, zz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 0, 1) |
| 426 | self.startCameraDirectionY = {yx, yy, yz} |
| 427 | self.startCameraDirectionZ = {zx, zy, zz} |
| 428 | setRotation(self.player.cuttingCameraNode, 0, 0, 0) |
| 429 | yx, yy, yz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 1, 0) |
| 430 | zx, zy, zz = localDirectionToWorld(self.player.cuttingCameraNode, 0, 0, 1) |
| 431 | self.endCameraDirectionY = {yx, yy, yz} |
| 432 | self.endCameraDirectionZ = {zx, zy, zz} |
| 433 | end |
| 434 | end |
| 435 | |
| 436 | local currentCamera = getCamera() |
| 437 | if isCutting or self.outTimer < self.outDuration then |
| 438 | if (currentCamera ~= self.player.cuttingCameraNode) then |
| 439 | g_currentMission:addSpecialCamera(self.player.cuttingCameraNode) |
| 440 | setCamera(self.player.cuttingCameraNode) |
| 441 | end |
| 442 | |
| 443 | local smoothDirY = { MathUtil.lerp(self.startCameraDirectionY[1], self.endCameraDirectionY[1], self.transitionAlpha), |
| 444 | MathUtil.lerp(self.startCameraDirectionY[2], self.endCameraDirectionY[2], self.transitionAlpha), |
| 445 | MathUtil.lerp(self.startCameraDirectionY[3], self.endCameraDirectionY[3], self.transitionAlpha) } |
| 446 | local smoothDirZ = { MathUtil.lerp(self.startCameraDirectionZ[1], self.endCameraDirectionZ[1], self.transitionAlpha), |
| 447 | MathUtil.lerp(self.startCameraDirectionZ[2], self.endCameraDirectionZ[2], self.transitionAlpha), |
| 448 | MathUtil.lerp(self.startCameraDirectionZ[3], self.endCameraDirectionZ[3], self.transitionAlpha) } |
| 449 | |
| 450 | smoothDirY = { worldDirectionToLocal(getParent(self.player.cuttingCameraNode), smoothDirY[1], smoothDirY[2], smoothDirY[3]) } |
| 451 | smoothDirZ = { worldDirectionToLocal(getParent(self.player.cuttingCameraNode), smoothDirZ[1], smoothDirZ[2], smoothDirZ[3]) } |
| 452 | local d,e,f = getWorldTranslation(self.player.chainsawSplitShapeFocus) |
| 453 | setDirection(self.player.cuttingCameraNode, smoothDirZ[1], smoothDirZ[2], smoothDirZ[3], smoothDirY[1], smoothDirY[2], smoothDirY[3]) |
| 454 | else |
| 455 | if (currentCamera ~= self.player.cameraNode) then |
| 456 | setRotation(self.player.cuttingCameraNode, 0, 0, 0) |
| 457 | g_currentMission:removeSpecialCamera(self.player.cuttingCameraNode) |
| 458 | setCamera(self.player.cameraNode) |
| 459 | end |
| 460 | end |
| 461 | end |
updateChainsawModel(bool isCutting)Arguments
| bool | isCutting |
| table | instance | Instance of object |
| 466 | function Chainsaw:updateChainsawModel(isCutting) |
| 467 | local currentPos = {getWorldTranslation(self.graphicsNode)} |
| 468 | |
| 469 | if isCutting then |
| 470 | local startPos = {} |
| 471 | local endPos = {} |
| 472 | startPos[1], startPos[2], startPos[3], endPos[1], endPos[2], endPos[3] = self:getCutStartEnd() |
| 473 | |
| 474 | if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.IN then |
| 475 | self.startChainsawPosition = currentPos |
| 476 | self.endChainsawPosition = startPos |
| 477 | elseif self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.CUT then |
| 478 | self.startChainsawPosition = startPos |
| 479 | self.endChainsawPosition = endPos |
| 480 | end |
| 481 | else |
| 482 | if self.cameraTransitionState == Chainsaw.CAMERA_TRANSITION_STATES.OUT then |
| 483 | self.startChainsawPosition = currentPos |
| 484 | setTranslation(self.graphicsNode, 0, 0, 0) |
| 485 | self.endChainsawPosition = {getWorldTranslation(self.graphicsNode)} |
| 486 | end |
| 487 | end |
| 488 | |
| 489 | if isCutting or self.outTimer < self.outDuration then |
| 490 | local smoothPosition = { MathUtil.lerp( self.startChainsawPosition[1], self.endChainsawPosition[1], self.transitionAlpha ), |
| 491 | MathUtil.lerp( self.startChainsawPosition[2], self.endChainsawPosition[2], self.transitionAlpha ), |
| 492 | MathUtil.lerp( self.startChainsawPosition[3], self.endChainsawPosition[3], self.transitionAlpha )} |
| 493 | local offset = {localToLocal(self.cutPositionNode, self.graphicsNode, 0,0,0)} |
| 494 | local cutDirection = {localDirectionToWorld(self.ringSelector, 0, 0, offset[3])} |
| 495 | local destination = {smoothPosition[1] - cutDirection[1], smoothPosition[2] - cutDirection[2], smoothPosition[3] - cutDirection[3]} |
| 496 | local modelTranslation = {worldToLocal(getParent(self.graphicsNode), destination[1], destination[2], destination[3])} |
| 497 | local distance = MathUtil.vector3Length(modelTranslation[1], modelTranslation[2], modelTranslation[3]) |
| 498 | |
| 499 | if distance > self.maxModelTranslation then |
| 500 | modelTranslation = {MathUtil.vector3Normalize(modelTranslation[1], modelTranslation[2], modelTranslation[3])} |
| 501 | modelTranslation = {modelTranslation[1]*self.maxModelTranslation, modelTranslation[2]*self.maxModelTranslation, modelTranslation[3]*self.maxModelTranslation} |
| 502 | local screen = { project(destination[1], destination[2], destination[3]) } |
| 503 | setTranslation(self.graphicsNode, modelTranslation[1], modelTranslation[2], modelTranslation[3]) |
| 504 | local graph = {getWorldTranslation(self.graphicsNode)} |
| 505 | local screen2 = { project(graph[1], graph[2], graph[3]) } |
| 506 | local world2 = { unProject(screen[1], screen[2], screen2[3]) } |
| 507 | |
| 508 | setWorldTranslation(self.graphicsNode, world2[1], world2[2], world2[3]) |
| 509 | else |
| 510 | setTranslation(self.graphicsNode, modelTranslation[1], modelTranslation[2], modelTranslation[3]) |
| 511 | end |
| 512 | else |
| 513 | setTranslation(self.graphicsNode, 0, 0, 0) |
| 514 | end |
| 515 | end |
Preparing information for the splitShape methodsDefinition
getCutShapeInformation(float x, float y, float z, float nx, float ny, float nz, float yx, float yy, float yz)Arguments
| float | x | |
| float | y | |
| float | z | |
| float | nx | |
| float | ny | |
| float | nz | |
| float | yx | |
| float | yy | |
| float | yz |
| table | instance | Instance of object |
| 528 | function Chainsaw:getCutShapeInformation() |
| 529 | local x,y,z = getWorldTranslation(self.player.chainsawSplitShapeFocus) |
| 530 | local nx,ny,nz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 1,0,0) |
| 531 | local yx,yy,yz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 0,1,0) |
| 532 | return x, y, z, nx, ny, nz, yx, yy, yz |
| 533 | end |
UpdateDefinition
update(float dt, boolean allowInput)Arguments
| float | dt | time since last call in ms |
| boolean | allowInput | allow input |
| table | instance | instance of object |
| 539 | function Chainsaw:update(dt, allowInput) |
| 540 | Chainsaw:superClass().update(self, dt, allowInput) |
| 541 | |
| 542 | if self.isServer then |
| 543 | local price = self.pricePerSecond * (dt / 1000) |
| 544 | g_farmManager:getFarmById(self.player.farmId).stats:updateStats("expenses", price) |
| 545 | g_currentMission:addMoney(-price, self.player.farmId, "vehicleRunningCost") |
| 546 | end |
| 547 | |
| 548 | if self.isClient then |
| 549 | if not self.isCutting then |
| 550 | self:updateCutRaycast() |
| 551 | end |
| 552 | |
| 553 | if self.showNotOwnedWarning then |
| 554 | g_currentMission:showBlinkingWarning(g_i18n:getText("warning_youDontHaveAccessToThisLand"), 2000) |
| 555 | self.showNotOwnedWarning = false -- reset so it can be set to true later |
| 556 | end |
| 557 | end |
| 558 | |
| 559 | self.shouldDelimb = false |
| 560 | |
| 561 | local lockPlayerInput = false |
| 562 | |
| 563 | if allowInput then |
| 564 | local isCutting = false |
| 565 | local hasBeenCut = false |
| 566 | |
| 567 | setRotation(self.graphicsNode, math.rad(math.random(-1, 1)) * 0.1, math.rad(math.random(-1, 1)) * 0.1, math.rad(-180)) |
| 568 | |
| 569 | if self.curSplitShape == nil then |
| 570 | lockPlayerInput = self.rotateInput ~= 0 |
| 571 | |
| 572 | if self.rotateInput ~= 0 then |
| 573 | self.rotationZ = MathUtil.clamp(self.rotationZ + self.rotationSpeedZ * self.rotateInput * dt, self.maxRotationZ, self.minRotationZ ) |
| 574 | setRotation(self.rootNode, self.handNodeRotation[1], self.handNodeRotation[2], self.handNodeRotation[3] + self.rotationZ) |
| 575 | setRotation(self.player.chainsawCameraFocus, 0, 0, -self.rotationZ) |
| 576 | end |
| 577 | end |
| 578 | |
| 579 | local shape = 0 |
| 580 | if not self.waitingForResetAfterCut then |
| 581 | if self.curSplitShape ~= nil or self.cutTimer == 0 then |
| 582 | local minY,maxY, minZ,maxZ |
| 583 | if self.curSplitShape == nil or not entityExists(self.curSplitShape) then |
| 584 | self.curSplitShape = nil |
| 585 | |
| 586 | local x, y, z, nx, ny, nz, yx, yy, yz = self:getCutShapeInformation() |
| 587 | shape, minY, maxY, minZ, maxZ = findSplitShape(x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ) |
| 588 | |
| 589 | if shape ~= nil and shape ~= 0 then |
| 590 | if self:isCuttingAllowed(x, y, z) then |
| 591 | self.showNotOwnedWarning = false |
| 592 | |
| 593 | local cutTooLow = self:testTooLow(shape, minY, maxY, minZ, maxZ) |
| 594 | local outsideRange = (self.cutFocusDistance < 0.0 or self.cutFocusDistance >= self.player.cutDetectionDistance) |
| 595 | if cutTooLow or outsideRange then |
| 596 | self.player.walkingIsLocked = false |
| 597 | self.curSplitShape = nil |
| 598 | shape, minY,maxY, minZ,maxZ = 0, nil,nil, nil,nil |
| 599 | end |
| 600 | else |
| 601 | self.showNotOwnedWarning = true |
| 602 | end |
| 603 | end |
| 604 | self.curSplitShapeMinY = minY |
| 605 | self.curSplitShapeMaxY = maxY |
| 606 | self.curSplitShapeMinZ = minZ |
| 607 | self.curSplitShapeMaxZ = maxZ |
| 608 | else |
| 609 | shape = self.curSplitShape |
| 610 | end |
| 611 | |
| 612 | self:updateRingSelector(shape) |
| 613 | end |
| 614 | end |
| 615 | |
| 616 | if self.activatePressed then |
| 617 | self.speedFactor = math.min(self.speedFactor + dt/self.maxWorkTime, 1) |
| 618 | |
| 619 | if not self.waitingForResetAfterCut then |
| 620 | local inRange = (self.cutFocusDistance >= self.player.minCutDistance and self.cutFocusDistance < self.player.maxCutDistance) |
| 621 | self.shouldDelimb = inRange |
| 622 | |
| 623 | if (self.curSplitShape ~= nil or self.cutTimer == 0) and inRange then |
| 624 | if self.curSplitShape ~= nil and entityExists(self.curSplitShape) then |
| 625 | lockPlayerInput = true |
| 626 | local x, y, z, nx, ny, nz, yx, yy, yz = self:getCutShapeInformation() |
| 627 | local minY, maxY, minZ, maxZ = testSplitShape(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ) |
| 628 | |
| 629 | if minY == nil then |
| 630 | -- cancel cutting if shape can't be cut anymore from current position |
| 631 | self.player.walkingIsLocked = false |
| 632 | self.curSplitShape = nil |
| 633 | else |
| 634 | local cutTooLow = self:testTooLow(self.curSplitShape, minY,maxY, minZ,maxZ) |
| 635 | if cutTooLow then |
| 636 | self.player.walkingIsLocked = false |
| 637 | self.curSplitShape = nil |
| 638 | end |
| 639 | end |
| 640 | |
| 641 | self.curSplitShapeMinY = minY |
| 642 | self.curSplitShapeMaxY = maxY |
| 643 | self.curSplitShapeMinZ = minZ |
| 644 | self.curSplitShapeMaxZ = maxZ |
| 645 | else |
| 646 | if shape ~= 0 then |
| 647 | self.player.walkingIsLocked = true |
| 648 | self.curSplitShape = shape |
| 649 | end |
| 650 | end |
| 651 | |
| 652 | if self.curSplitShape ~= nil then |
| 653 | local x,y,z, nx,ny,nz, yx,yy,yz = self:getCutShapeInformation() |
| 654 | |
| 655 | if self:isCuttingAllowed(x, y, z) then |
| 656 | isCutting = true |
| 657 | end |
| 658 | |
| 659 | if self.cutTimer > 0 then |
| 660 | self.lastWorkTime = math.min(self.lastWorkTime, self.maxWorkTime*0.7) |
| 661 | end |
| 662 | |
| 663 | local cutDuration = self:calculateCutDuration() |
| 664 | if self.cutTimer >= cutDuration then |
| 665 | if g_currentMission:getIsServer() then |
| 666 | ChainsawUtil.cutSplitShape(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ, self.player.farmId) |
| 667 | else |
| 668 | g_client:getServerConnection():sendEvent(ChainsawCutEvent:new(self.curSplitShape, x, y, z, nx, ny, nz, yx, yy, yz, self.cutSizeY, self.cutSizeZ, self.player.farmId)) |
| 669 | end |
| 670 | |
| 671 | hasBeenCut = true |
| 672 | self.waitingForResetAfterCut = true |
| 673 | self.player.walkingIsLocked = false |
| 674 | self.curSplitShape = nil |
| 675 | self.curSplitShapeMinY = nil |
| 676 | self:updateRingSelector(0) |
| 677 | end |
| 678 | end |
| 679 | end |
| 680 | end |
| 681 | else |
| 682 | self.speedFactor = math.max(self.speedFactor - dt / self.maxWorkTime, 0) |
| 683 | self.waitingForResetAfterCut = false |
| 684 | self.player.walkingIsLocked = false |
| 685 | self.curSplitShape = nil |
| 686 | self.curSplitShapeMinY = nil |
| 687 | self.lastWorkTime = math.max(self.lastWorkTime - dt, 0) |
| 688 | self.workUpPlayed = false |
| 689 | end |
| 690 | |
| 691 | self.player:lockInput(lockPlayerInput) |
| 692 | |
| 693 | self:updateCuttingTimers(dt, isCutting) |
| 694 | self:updateCuttingCamera(isCutting) |
| 695 | self:updateChainsawModel(isCutting) |
| 696 | self:updateDelimb() |
| 697 | self:setCutting(isCutting, self.rotationZ > 0.7, hasBeenCut) |
| 698 | |
| 699 | self.soundFSM:update(dt) |
| 700 | end |
| 701 | self:updateParticles() |
| 702 | |
| 703 | self.rotateInput = 0 |
| 704 | self.activatePressed = false |
| 705 | end |
Update delimb mechanic (removing leaves, ...)Definition
updateDelimb()Return Values
| boolean | true | if loading was successful else false |
| 713 | function Chainsaw:updateDelimb() |
| 714 | if self.shouldDelimb then |
| 715 | local x,y,z = getWorldTranslation(self.player.chainsawSplitShapeFocus) |
| 716 | local nx,ny,nz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 1,0,0) |
| 717 | local yx,yy,yz = localDirectionToWorld(self.player.chainsawSplitShapeFocus, 0,1,0) |
| 718 | if g_server == nil then |
| 719 | g_client:getServerConnection():sendEvent(ChainsawDelimbEvent:new(self.player, x, y, z, nx, ny, nz, yx, yy, yz, false)) |
| 720 | else |
| 721 | local ret = findAndRemoveSplitShapeAttachments(x, y, z, nx, ny, nz, yx, yy, yz, 0.7, self.cutSizeY, self.cutSizeZ) |
| 722 | if ret then |
| 723 | self:setOnDelimb(true) |
| 724 | end |
| 725 | end |
| 726 | end |
| 727 | end |
Update particle systemDefinition
updateParticles()Return Values
| table | the | brand object or nil of an error occured |
| 731 | function Chainsaw:updateParticles() |
| 732 | if self.particleSystems ~= nil then |
| 733 | local active = false |
| 734 | if self.isCutting and ((self.samplesBranchActiveTimer > g_currentMission.time) or (self.curSplitShapeMinY ~= nil and self.curSplitShapeMaxY ~= nil and self.cutTimer > self.inDuration)) then |
| 735 | active = true |
| 736 | end |
| 737 | if self.isCutting and not self.player.isEntered then |
| 738 | active = true |
| 739 | end |
| 740 | for _, ps in pairs(self.particleSystems) do |
| 741 | ParticleUtil.setEmittingState(ps, active) |
| 742 | end |
| 743 | end |
| 744 | end |
Update ring selectorDefinition
updateRingSelector(integer shape)Arguments
| integer | shape |
| table | brand | the brand object |
| 749 | function Chainsaw:updateRingSelector(shape) |
| 750 | if self.ringSelector ~= nil then |
| 751 | local hasShape = (shape ~= nil) and (shape ~= 0) |
| 752 | |
| 753 | if g_woodCuttingMarkerEnabled and hasShape then |
| 754 | local inDetectionRange = false |
| 755 | local inCutRange = false |
| 756 | |
| 757 | if self.cutFocusDistance ~= nil and (self.cutFocusDistance >= 0.0 and self.cutFocusDistance < self.player.cutDetectionDistance) then |
| 758 | inDetectionRange = true |
| 759 | inCutRange = (self.cutFocusDistance >= self.player.minCutDistance and self.cutFocusDistance < self.player.maxCutDistance) |
| 760 | end |
| 761 | if not getVisibility(self.ringSelector) and inDetectionRange then |
| 762 | local x, y, z = getWorldTranslation(self.ringSelector) |
| 763 | if self:isCuttingAllowed(x, y, z) then |
| 764 | setVisibility(self.ringSelector, true) |
| 765 | else |
| 766 | setVisibility(self.ringSelector, false) |
| 767 | end |
| 768 | elseif getVisibility(self.ringSelector) and not inDetectionRange then |
| 769 | setVisibility(self.ringSelector, false) |
| 770 | end |
| 771 | |
| 772 | if getVisibility(self.ringSelector) then |
| 773 | if inCutRange then |
| 774 | setShaderParameter(self.ringSelector, "colorScale", 0.395, 0.925, 0.115, 1, false) |
| 775 | else |
| 776 | setShaderParameter(self.ringSelector, "colorScale", 0.098, 0.450, 0.960, 1, false) |
| 777 | end |
| 778 | |
| 779 | if self.curSplitShapeMinY ~= nil then |
| 780 | local scale = math.max(self.curSplitShapeMaxY - self.curSplitShapeMinY + self.ringSelectorScaleOffset, self.curSplitShapeMaxZ-self.curSplitShapeMinZ + self.ringSelectorScaleOffset) |
| 781 | setScale(self.ringSelector, 1, scale, scale) |
| 782 | |
| 783 | local a,b,c = localToWorld(self.player.chainsawSplitShapeFocus, 0, (self.curSplitShapeMinY+self.curSplitShapeMaxY)*0.5, (self.curSplitShapeMinZ+self.curSplitShapeMaxZ)*0.5) |
| 784 | local x, y, z = worldToLocal(getParent(self.ringSelector), a,b,c) |
| 785 | setTranslation(self.ringSelector, x,y,z) |
| 786 | else |
| 787 | setScale(self.ringSelector, 1, 1, 1) |
| 788 | end |
| 789 | end |
| 790 | elseif getVisibility(self.ringSelector) then |
| 791 | setVisibility(self.ringSelector, false) |
| 792 | end |
| 793 | end |
| 794 | end |
Set cuttingDefinition
setCutting(boolean isCutting, boolean isHorizontalCut, boolean hasBeenCut, boolean noEventSend)Arguments
| boolean | isCutting | is cutting |
| boolean | isHorizontalCut | is horizontal cut |
| boolean | hasBeenCut | |
| boolean | noEventSend | no event send |
| table | brand | the brand object |
| 802 | function Chainsaw:setCutting(isCutting, isHorizontalCut, hasBeenCut, noEventSend) |
| 803 | ChainsawStateEvent.sendEvent(self.player, isCutting, isHorizontalCut, hasBeenCut, noEventSend) |
| 804 | self.isCutting = isCutting |
| 805 | self.isHorizontalCut = isHorizontalCut |
| 806 | self.hasBeenCut = hasBeenCut |
| 807 | |
| 808 | if not self.player.isOwner then |
| 809 | self.player:setCuttingAnim(isCutting, isHorizontalCut) |
| 810 | end |
| 811 | end |
Set on delimbDefinition
setOnDelimb(boolean state)Arguments
| boolean | state | new state |
| integer | brandIndex | the brand index |
| 816 | function Chainsaw:setOnDelimb(state) |
| 817 | if state == true then |
| 818 | end |
| 819 | end |
Set hand nodeDefinition
setHandNode(integer handNode)Arguments
| integer | handNode | hand node id |
| boolean | true | if storeitem is a vehicle, else false |
| 824 | function Chainsaw:setHandNode(handNode) |
| 825 | Chainsaw:superClass().setHandNode(self, handNode) |
| 826 | if self.currentHandNode ~= handNode then |
| 827 | link(handNode, self.rootNode) |
| 828 | self.currentHandNode = handNode |
| 829 | setRotation(self.rootNode, unpack(self.handNodeRotation)) |
| 830 | local x, y, z = getWorldTranslation(self.handNode) |
| 831 | x, y, z = worldToLocal(getParent(self.rootNode), x, y, z) |
| 832 | local a, b, c = getTranslation(self.rootNode) |
| 833 | setTranslation(self.rootNode, a - x, b - y, c - z) |
| 834 | end |
| 835 | end |
On activateDefinition
onActivate(boolean allowInput)Arguments
| boolean | allowInput | allow input |
| boolean | true | if storeitem is an animal, else false |
| 844 | function Chainsaw:onActivate(allowInput) |
| 845 | Chainsaw:superClass().onActivate(self) |
| 846 | |
| 847 | self.rotationZ = 0.0 |
| 848 | setRotation(self.rootNode, self.handNodeRotation[1], self.handNodeRotation[2], self.handNodeRotation[3]) |
| 849 | setRotation(self.player.chainsawCameraFocus, 0, 0, self.rotationZ) |
| 850 | |
| 851 | self.startTime = g_currentMission.time |
| 852 | if not self.player.isOwner then |
| 853 | self.player.visualInformation:setProtectiveUV(self.equipmentUVs) |
| 854 | self.player:setWoodWorkVisibility(true) |
| 855 | end |
| 856 | |
| 857 | if self.isClient then |
| 858 | g_animationManager:startAnimations(self.chains) |
| 859 | end |
| 860 | |
| 861 | self.cutTimer = 0.0 |
| 862 | setTranslation(self.graphicsNode, 0, 0, 0) |
| 863 | |
| 864 | self.soundFSM:changeState(Chainsaw.SOUND_STATES.START) |
| 865 | end |
On deactivateDefinition
onDeactivate(boolean allowInput)Arguments
| boolean | allowInput | allow input |
| boolean | true | if storeitem is a placeable, else false |
| 870 | function Chainsaw:onDeactivate(allowInput) |
| 871 | Chainsaw:superClass().onDeactivate(self) |
| 872 | |
| 873 | self.speedFactor = 0 |
| 874 | self.curSplitShape = nil |
| 875 | self.player:lockInput(false) |
| 876 | self.player.walkingIsLocked = false |
| 877 | self.player:setWoodWorkVisibility(false) |
| 878 | if self.isClient then |
| 879 | g_animationManager:stopAnimations(self.chains) |
| 880 | self.cutTimer = 0.0 |
| 881 | setTranslation(self.graphicsNode, 0, 0, 0) |
| 882 | if self.particleSystems ~= nil then |
| 883 | for _, ps in pairs(self.particleSystems) do |
| 884 | ParticleUtil.resetNumOfEmittedParticles(ps) |
| 885 | ParticleUtil.setEmittingState(ps, false) |
| 886 | end |
| 887 | end |
| 888 | if getVisibility(self.ringSelector) then |
| 889 | setVisibility(self.ringSelector, false) |
| 890 | end |
| 891 | end |
| 892 | self.soundFSM:changeState(Chainsaw.SOUND_STATES.STOP) |
| 893 | end |
registerActionEvents()Return Values
| boolean | true | if storeitem is an object, else false |
| 897 | function Chainsaw:registerActionEvents() |
| 898 | Chainsaw:superClass().registerActionEvents(self, allowInput) |
| 899 | local eventId = "" |
| 900 | _, eventId = g_inputBinding:registerActionEvent(InputAction.AXIS_ROTATE_HANDTOOL, self, self.onInputRotate, false, false, true, true) |
| 901 | g_inputBinding:setActionEventText(eventId, g_i18n:getText("action_rotate")) |
| 902 | self.eventIdRotateHandtool = eventId |
| 903 | end |
Event function for chainsaw rotation bound to InputAction.AXIS_ROTATE_HANDTOOL.Definition
onInputRotate()Return Values
| boolean | true | if storeitem is a handtool, else false |
| 907 | function Chainsaw:onInputRotate(_, inputValue) |
| 908 | self.rotateInput = self.rotateInput + inputValue |
| 909 | end |
Creating instance of sound state.Definition
Start:new()Return Values
| integer | sharedVramUsage | the shared vram usage |
| integer | perInstanceVramUsage | the per instance vram usage |
| boolean | ignoreVramUsage | true if vram usage should be ignored else false |
| 18 | function ChainsawSoundStateStart:new(id, owner, stateMachine, custom_mt) |
| 19 | local self = SimpleState:new(id, owner, stateMachine, ChainsawSoundStateStart_mt) |
| 20 | return self |
| 21 | end |
Activate methodDefinition
Start:activate()Code
| 25 | function ChainsawSoundStateStart:activate(parms) |
| 26 | ChainsawSoundStateStart:superClass().activate(self, parms) |
| 27 | g_soundManager:playSample(self.owner.samples.start) |
| 28 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.IDLE) |
| 29 | end |
Creating instance of sound state.Definition
Stop:new()Code
| 41 | function ChainsawSoundStateStop:new(id, owner, stateMachine, custom_mt) |
| 42 | local self = SimpleState:new(id, owner, stateMachine, ChainsawSoundStateStop_mt) |
| 43 | return self |
| 44 | end |
Activate methodDefinition
Stop:activate()Return Values
| table | configurations | a list of configurations |
| 48 | function ChainsawSoundStateStop:activate(parms) |
| 49 | ChainsawSoundStateStop:superClass().activate(self, parms) |
| 50 | g_soundManager:playSample(self.owner.samples.stop) |
| 51 | end |
Creating instance of sound state.Definition
Idle:new()Return Values
| table | configuration | sets |
| 63 | function ChainsawSoundStateIdle:new(id, owner, stateMachine, custom_mt) |
| 64 | local self = SimpleState:new(id, owner, stateMachine, ChainsawSoundStateIdle_mt) |
| 65 | self.activeTimer = 0.0 |
| 66 | return self |
| 67 | end |
Deactivate methodDefinition
Idle:deactivate()Return Values
| table | instance | instance of object |
| 71 | function ChainsawSoundStateIdle:deactivate() |
| 72 | ChainsawSoundStateIdle:superClass().deactivate(self) |
| 73 | g_soundManager:stopSample(self.owner.samples.idle) |
| 74 | self.activeTimer = 0.0 |
| 75 | end |
update methodDefinition
Idle:update(float dt)Arguments
| float | dt | in ms |
| boolean | true | if loading was successful else false |
| 80 | function ChainsawSoundStateIdle:update(dt) |
| 81 | ChainsawSoundStateIdle:superClass().update(self, dt) |
| 82 | |
| 83 | if not g_soundManager:getIsSamplePlaying(self.owner.samples.start) and not g_soundManager:getIsSamplePlaying(self.owner.samples.idle) then |
| 84 | g_soundManager:playSample(self.owner.samples.idle) |
| 85 | end |
| 86 | |
| 87 | if self.owner.isCutting then |
| 88 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.CUT) |
| 89 | elseif self.owner.activatePressed then |
| 90 | self.activeTimer = self.activeTimer + dt |
| 91 | |
| 92 | if self.activeTimer > self.owner.quicktapThreshold then |
| 93 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.ACTIVE) |
| 94 | end |
| 95 | else |
| 96 | if self.activeTimer > 0.0 and self.activeTimer < self.owner.quicktapThreshold then |
| 97 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.QUICKTAP) |
| 98 | end |
| 99 | end |
| 100 | end |
Creating instance of sound state.Definition
Active:new()Return Values
| boolean | true | if adding was successful else false |
| 112 | function ChainsawSoundStateActive:new(id, owner, stateMachine, custom_mt) |
| 113 | local self = SimpleState:new(id, owner, stateMachine, ChainsawSoundStateActive_mt) |
| 114 | return self |
| 115 | end |
Activate methodDefinition
Active:activate()Return Values
| table | category | the category object |
| 119 | function ChainsawSoundStateActive:activate(parms) |
| 120 | ChainsawSoundStateActive:superClass().activate(self, parms) |
| 121 | local shouldInitiateStart = false |
| 122 | shouldInitiateStart = not (parms ~= nil and parms.alreadyActive) |
| 123 | if shouldInitiateStart then |
| 124 | g_soundManager:playSample(self.owner.samples.activeStart) |
| 125 | end |
| 126 | end |
Deactivate methodDefinition
Active:deactivate()Return Values
| table | specTypes | a list of spec types |
| 130 | function ChainsawSoundStateActive:deactivate() |
| 131 | ChainsawSoundStateActive:superClass().deactivate(self) |
| 132 | g_soundManager:stopSample(self.owner.samples.activeStart) |
| 133 | g_soundManager:stopSample(self.owner.samples.activeLoop) |
| 134 | end |
update methodDefinition
Active:update(float dt)Arguments
| float | dt | in ms |
| table | specType | the corresponding spectype |
| 139 | function ChainsawSoundStateActive:update(dt) |
| 140 | ChainsawSoundStateActive:superClass().update(self, dt) |
| 141 | if self.owner.isCutting then |
| 142 | local parms = {} |
| 143 | parms.alreadyActive = not g_soundManager:getIsSamplePlaying(self.owner.samples.activeStart) |
| 144 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.CUT, parms) |
| 145 | elseif self.owner.activatePressed then |
| 146 | if not g_soundManager:getIsSamplePlaying(self.owner.samples.activeStart) and not g_soundManager:getIsSamplePlaying(self.owner.samples.activeLoop) then |
| 147 | g_soundManager:playSample(self.owner.samples.activeLoop) |
| 148 | end |
| 149 | else |
| 150 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.IDLE) |
| 151 | g_soundManager:playSample(self.owner.samples.activeStop) |
| 152 | end |
| 153 | end |
Creating instance of sound state.Definition
Cut:new()Return Values
| boolean | wasSuccessfull | true if added else false |
| 165 | function ChainsawSoundStateCut:new(id, owner, stateMachine, custom_mt) |
| 166 | local self = SimpleState:new(id, owner, stateMachine, ChainsawSoundStateCut_mt) |
| 167 | return self |
| 168 | end |
Activate methodDefinition
Cut:activate()Return Values
| table | items | a list of all store items |
| 172 | function ChainsawSoundStateCut:activate(parms) |
| 173 | ChainsawSoundStateCut:superClass().activate(self, parms) |
| 174 | local shouldInitiateStart = false |
| 175 | shouldInitiateStart = not (parms ~= nil and parms.alreadyActive) |
| 176 | if shouldInitiateStart then |
| 177 | g_soundManager:playSample(self.owner.samples.cutStart) |
| 178 | end |
| 179 | end |
Deactivate methodDefinition
Cut:deactivate()Return Values
| table | storeItem | the storeitem oject |
| 183 | function ChainsawSoundStateCut:deactivate() |
| 184 | ChainsawSoundStateCut:superClass().deactivate(self) |
| 185 | g_soundManager:stopSample(self.owner.samples.cutStart) |
| 186 | g_soundManager:stopSample(self.owner.samples.cutLoop) |
| 187 | end |
update methodDefinition
Cut:update(float dt)Arguments
| float | dt | in ms |
| table | storeItem | the storeitem object |
| 192 | function ChainsawSoundStateCut:update(dt) |
| 193 | ChainsawSoundStateCut:superClass().update(self, dt) |
| 194 | if not self.owner.isCutting then |
| 195 | if self.owner.activatePressed then |
| 196 | local parms = {} |
| 197 | parms.alreadyActive = not g_soundManager:getIsSamplePlaying(self.owner.samples.cutStart) or g_soundManager:getIsSamplePlaying(self.owner.samples.cutLoop) |
| 198 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.ACTIVE, parms) |
| 199 | g_soundManager:playSample(self.owner.samples.cutStop) |
| 200 | else |
| 201 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.IDLE) |
| 202 | g_soundManager:playSample(self.owner.samples.cutStop) |
| 203 | end |
| 204 | else |
| 205 | if not g_soundManager:getIsSamplePlaying(self.owner.samples.cutStart) and not g_soundManager:getIsSamplePlaying(self.owner.samples.cutLoop) then |
| 206 | g_soundManager:playSample(self.owner.samples.cutLoop) |
| 207 | end |
| 208 | end |
| 209 | end |
Creating instance of sound state.Definition
Quicktap:new()Return Values
| table | storeItem | the storeitem object |
| 221 | function ChainsawSoundStateQuicktap:new(id, owner, stateMachine, custom_mt) |
| 222 | local self = SimpleState:new(id, owner, stateMachine, ChainsawSoundStateQuicktap_mt) |
| 223 | return self |
| 224 | end |
Activate methodDefinition
Quicktap:activate()Return Values
| table | storeItem | the storeitem object |
| 228 | function ChainsawSoundStateQuicktap:activate(parms) |
| 229 | ChainsawSoundStateQuicktap:superClass().activate(self, parms) |
| 230 | |
| 231 | if self.owner.samplesQuicktapCount > 0 then |
| 232 | local idx = math.floor(math.random(1, self.owner.samplesQuicktapCount)) |
| 233 | local sample = self.owner.samplesQuicktap[idx] |
| 234 | g_soundManager:playSample(sample) |
| 235 | end |
| 236 | self.stateMachine:changeState(Chainsaw.SOUND_STATES.IDLE) |
| 237 | end |
Class for handtools
Register handtool typeDefinition
registerHandTool(string typeName, table classObject)Arguments
| string | typeName | name of new type |
| table | classObject | class object |
| table | sample | sample object |
Creating handtool objectDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| bool | True | if the definition and parameters are valid |
| bool | True | if an external XML file is loaded in place of the given parameter, caller must delete the handle afterwards! |
| int | XML | file handle, either the one passed in as an argument or an alternate external sound file definition which must be released by the caller |
| string | Sound | definition parent element key which may have changed according to an alternate external sound file definition |
| int | Link | node target for spatial sound samples |
| table | instance | Instance of object |
| 30 | function HandTool:new(isServer, isClient, customMt) |
| 31 | local mt = customMt |
| 32 | if mt == nil then |
| 33 | mt = HandTool_mt |
| 34 | end |
| 35 | |
| 36 | local self = Object:new(isServer, isClient, mt) |
| 37 | self.static = true |
| 38 | self.player = nil |
| 39 | self.owner = nil |
| 40 | self.price = 0 |
| 41 | self.age = 0 |
| 42 | self.activatePressed = false |
| 43 | return self |
| 44 | end |
Load chainsaw from xml fileDefinition
load(string xmlFilename, table player)Arguments
| string | xmlFilename | xml file name |
| table | player | player |
| boolean | success | success |
| 51 | function HandTool:load(xmlFilename, player) |
| 52 | self.configFileName = xmlFilename |
| 53 | |
| 54 | self.customEnvironment, self.baseDirectory = Utils.getModNameAndBaseDirectory(xmlFilename) |
| 55 | |
| 56 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 57 | if xmlFile == 0 then |
| 58 | return false |
| 59 | end |
| 60 | local i3dFilename = getXMLString(xmlFile, "handTool.filename") |
| 61 | self.position = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.position#value"), "0 0 0"), 3) |
| 62 | self.rotation = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.rotation#value"), "0 0 0"), 3) |
| 63 | |
| 64 | if i3dFilename == nil then |
| 65 | delete(xmlFile) |
| 66 | return false |
| 67 | end |
| 68 | self.i3dFilename = Utils.getFilename(i3dFilename, self.baseDirectory) |
| 69 | |
| 70 | local node = g_i3DManager:loadSharedI3DFile(self.i3dFilename) |
| 71 | self.rootNode = getChildAt(node, 0) |
| 72 | self.player = player |
| 73 | |
| 74 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 75 | if self.price == 0 or self.price == nil then |
| 76 | self.price = StoreItemUtil.getDefaultPrice(storeItem) |
| 77 | end |
| 78 | |
| 79 | if g_currentMission ~= nil and storeItem.canBeSold then |
| 80 | g_currentMission.environment:addDayChangeListener(self) |
| 81 | end |
| 82 | |
| 83 | self.targets = {} |
| 84 | IKUtil.loadIKChainTargets(xmlFile, "handTool.targets", self.rootNode, self.targets, nil) |
| 85 | |
| 86 | link(player.toolsRootNode, self.rootNode) |
| 87 | setTranslation(self.rootNode, self.position[1], self.position[2], self.position[3]) |
| 88 | setRotation(self.rootNode, self.rotation[1], self.rotation[2], self.rotation[3]) |
| 89 | |
| 90 | delete(node) |
| 91 | delete(xmlFile) |
| 92 | setVisibility(self.rootNode, false) |
| 93 | |
| 94 | self.isActive = false |
| 95 | |
| 96 | return true |
| 97 | end |
Deleting handtoolDefinition
delete()Code
| 104 | function HandTool:delete() |
| 105 | self:removeActionEvents() |
| 106 | |
| 107 | if g_currentMission ~= nil then |
| 108 | g_currentMission.environment:removeDayChangeListener(self) |
| 109 | end |
| 110 | if self.player.isOwner then |
| 111 | local farmId = self.player.farmId |
| 112 | local farm = g_farmManager:getFarmById(farmId) |
| 113 | if farm ~= nil then -- only remove from farm if it still exists (not the case when leaving a game) |
| 114 | farm:removeHandTool(self) |
| 115 | end |
| 116 | end |
| 117 | if self.rootNode ~= nil and self.rootNode ~= 0 then |
| 118 | g_i3DManager:releaseSharedI3DFile(self.i3dFilename, nil, true) |
| 119 | delete(self.rootNode) |
| 120 | end |
| 121 | HandTool:superClass().delete(self) |
| 122 | end |
On activateDefinition
onActivate(boolean allowInput)Arguments
| boolean | allowInput | allow input |
| 133 | function HandTool:onActivate(allowInput) |
| 134 | setVisibility(self.rootNode, true) |
| 135 | self.isActive = true |
| 136 | self:raiseActive() |
| 137 | |
| 138 | if self.player.isOwner then |
| 139 | self:registerActionEvents() |
| 140 | end |
| 141 | end |
On deactivateDefinition
onDeactivate(boolean allowInput)Arguments
| boolean | allowInput | allow input |
| 146 | function HandTool:onDeactivate(allowInput) |
| 147 | setVisibility(self.rootNode, false) |
| 148 | self.isActive = false |
| 149 | self:removeActionEvents() |
| 150 | end |
Get daily up keepDefinition
getDailyUpkeep()Return Values
| table | sample | sample object |
| float | dailyUpkeep | daily up keep |
| 163 | function HandTool:getDailyUpkeep() |
| 164 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 165 | local multiplier = 1 |
| 166 | |
| 167 | if storeItem.lifetime ~= nil and storeItem.lifetime ~= 0 then |
| 168 | local ageMultiplier = math.min(self.age / storeItem.lifetime, 1) |
| 169 | multiplier = EconomyManager.MAX_DAILYUPKEEP_MULTIPLIER * ageMultiplier |
| 170 | end |
| 171 | |
| 172 | return StoreItemUtil.getDailyUpkeep(storeItem, nil) * multiplier |
| 173 | end |
Get sell priceDefinition
getSellPrice()Return Values
| boolean | isPlaying | true if sample is playing else false |
| float | sellPrice | sell price |
| 178 | function HandTool:getSellPrice() |
| 179 | local priceMultiplier = 0.5 |
| 180 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 181 | local maxVehicleAge = storeItem.lifetime |
| 182 | |
| 183 | if maxVehicleAge ~= nil and maxVehicleAge ~= 0 then |
| 184 | priceMultiplier = priceMultiplier * math.exp(-3.5 * math.min(self.age/maxVehicleAge, 1)) |
| 185 | end |
| 186 | |
| 187 | return math.floor(self.price * math.max(priceMultiplier, 0.05)) |
| 188 | end |
Called if day changedDefinition
dayChanged()Return Values
| boolean | isIndoor | true if indoor mode is active else false |
| 192 | function HandTool:dayChanged() |
| 193 | self.age = self.age + 1 |
| 194 | end |
registerActionEvents()Return Values
| boolean | isIndoor | true if inside building mode is active else false |
| 198 | function HandTool:registerActionEvents() |
| 199 | end |
removeActionEvents()Return Values
| float | factor | the modifier factor |
| 203 | function HandTool:removeActionEvents() |
| 204 | g_inputBinding:removeActionEventsByTarget(self) |
| 205 | end |
Class for high pressure washer lance
new()Return Values
| table | instance | instance of object |
| integer | group | audio group |
| 16 | function HighPressureWasherLance:new(isServer, isClient, customMt) |
| 17 | local self = HighPressureWasherLance:superClass().new(self, isServer, isClient, customMt or HighPressureWasherLance_mt) |
| 18 | |
| 19 | self.foundVehicle = nil |
| 20 | self.doWashing = false |
| 21 | self.washDistance = 10.0 |
| 22 | self.washMultiplier = 1.0 |
| 23 | self.pricePerSecond = 10 |
| 24 | self.isHPWLance = true |
| 25 | |
| 26 | return self |
| 27 | end |
load()Code
| 31 | function HighPressureWasherLance:load(xmlFilename, player) |
| 32 | if not HighPressureWasherLance:superClass().load(self, xmlFilename, player) then |
| 33 | return false |
| 34 | end |
| 35 | |
| 36 | -- Lance model |
| 37 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 38 | self.lanceNode = I3DUtil.indexToObject(self.rootNode, getXMLString(xmlFile, "handTool.highPressureWasherLance.lance#node")) |
| 39 | |
| 40 | local lancePosition = {} |
| 41 | local lanceRotation = {} |
| 42 | if self.player == g_currentMission.player then |
| 43 | lancePosition = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.highPressureWasherLance.lance.firstPerson#position"), "0 0 0"), 3) |
| 44 | lanceRotation = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.highPressureWasherLance.lance.firstPerson#rotation"), "0 0 0"), 3) |
| 45 | else |
| 46 | lancePosition = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.highPressureWasherLance.lance.thirdPerson#position"), "0 0 0"), 3) |
| 47 | lanceRotation = StringUtil.getRadiansFromString(Utils.getNoNil(getXMLString(xmlFile, "handTool.highPressureWasherLance.lance.thirdPerson#rotation"), "0 0 0"), 3) |
| 48 | end |
| 49 | self.lanceRaycastNode = I3DUtil.indexToObject(self.rootNode, getXMLString(xmlFile, "handTool.highPressureWasherLance.lance#raycastNode")) |
| 50 | self.washDistance = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.highPressureWasherLance.lance#washDistance"), 10) |
| 51 | self.washMultiplier = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.highPressureWasherLance.lance#washMultiplier"), 1) |
| 52 | self.pricePerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "handTool.highPressureWasherLance.lance#pricePerMinute"), 10) / 1000 |
| 53 | |
| 54 | setTranslation(self.rootNode, unpack(lancePosition)) |
| 55 | setRotation(self.rootNode, unpack(lanceRotation)) |
| 56 | |
| 57 | self.effects = g_effectManager:loadEffect(xmlFile, "handTool.highPressureWasherLance.effects", self.rootNode, self) |
| 58 | g_effectManager:setFillType(self.effects, FillType.WATER) |
| 59 | |
| 60 | -- Sounds |
| 61 | self.washingSample = g_soundManager:loadSampleFromXML(xmlFile, "handTool.highPressureWasherLance.sounds", "washing", self.baseDirectory, self.rootNode, 0, AudioGroup.VEHICLE, nil, self) |
| 62 | |
| 63 | return true |
| 64 | end |
DeletingDefinition
delete()Return Values
| table | instance | instance of object |
| integer | group | audio group |
| 68 | function HighPressureWasherLance:delete() |
| 69 | g_effectManager:deleteEffects(self.effects) |
| 70 | g_soundManager:deleteSample(self.washingSample) |
| 71 | HighPressureWasherLance:superClass().delete(self) |
| 72 | end |
onDeactivate()Code
| 76 | function HighPressureWasherLance:onDeactivate() |
| 77 | self:setIsWashing(false, true, true) |
| 78 | HighPressureWasherLance:superClass().onDeactivate(self) |
| 79 | end |
update()Return Values
| table | new | Landscaping instance |
| 83 | function HighPressureWasherLance:update(dt, allowInput) |
| 84 | HighPressureWasherLance:superClass().update(self, dt) |
| 85 | |
| 86 | if allowInput then |
| 87 | self:setIsWashing(self.activatePressed, false, false) |
| 88 | end |
| 89 | |
| 90 | if self.isServer then |
| 91 | if self.doWashing then |
| 92 | self.foundVehicle = nil |
| 93 | self:cleanVehicle(self.player.cameraNode, dt) |
| 94 | if self.lanceRaycastNode ~= nil then |
| 95 | self:cleanVehicle(self.lanceRaycastNode, dt) |
| 96 | end |
| 97 | if self.foundVehicle ~= nil then |
| 98 | local farmId = self.foundVehicle:getOwnerFarmId() |
| 99 | local price = self.pricePerSecond * (dt / 1000) |
| 100 | local stats = g_farmManager:getFarmById(self.player.farmId).stats |
| 101 | stats:updateStats("expenses", price) |
| 102 | g_currentMission:addMoney(-price, farmId, "vehicleRunningCost") |
| 103 | end |
| 104 | end |
| 105 | end |
| 106 | self.activatePressed = false |
| 107 | self:raiseActive() |
| 108 | end |
Set is washingDefinition
setIsWashing(boolean doWashing, boolean force, boolean noEventSend)Arguments
| boolean | doWashing | do washing |
| boolean | force | force |
| boolean | noEventSend | no event send |
| table | New | LandscapingSculptEvent instance |
| 115 | function HighPressureWasherLance:setIsWashing(doWashing, force, noEventSend) |
| 116 | HPWLanceStateEvent.sendEvent(self.player, doWashing, noEventSend) |
| 117 | if self.doWashing ~= doWashing then |
| 118 | if doWashing then |
| 119 | g_effectManager:setFillType(self.effects, FillType.WATER) |
| 120 | g_effectManager:startEffects(self.effects) |
| 121 | g_soundManager:playSample(self.washingSample) |
| 122 | else |
| 123 | if force then |
| 124 | g_effectManager:resetEffects(self.effects) |
| 125 | else |
| 126 | g_effectManager:stopEffects(self.effects) |
| 127 | end |
| 128 | g_soundManager:stopSample(self.washingSample) |
| 129 | end |
| 130 | self.doWashing = doWashing |
| 131 | end |
| 132 | end |
Clean vehicleDefinition
cleanVehicle(integer node, float dt)Arguments
| integer | node | node id |
| float | dt | time since last call in ms |
| table | instance | instance of object |
| integer | group | audio group |
| 138 | function HighPressureWasherLance:cleanVehicle(node, dt) |
| 139 | local x, y, z = getWorldTranslation(node) |
| 140 | local dx, dy, dz = localDirectionToWorld(node, 0, 0, -1) |
| 141 | local lastFoundVehicle = self.foundVehicle |
| 142 | raycastAll(x, y, z, dx, dy, dz, "washRaycastCallback", self.washDistance, self, 32+64+128+256+4096+8194) |
| 143 | |
| 144 | if self.foundVehicle ~= nil and lastFoundVehicle ~= self.foundVehicle then |
| 145 | self.foundVehicle:addDirtAmount(-self.washMultiplier * dt / self.foundVehicle:getWashDuration()) |
| 146 | end |
| 147 | end |
Wash raycast callbackDefinition
washRaycastCallback(integer hitActorId, float x, float y, float z, float distance, float nx, float ny, float nz, integer subShapeIndex, integer hitShapeId)Arguments
| integer | hitActorId | id of hit object actor |
| float | x | x raycast position |
| float | y | y raycast position |
| float | z | z raycast position |
| float | distance | distance to raycast position |
| float | nx | x component of hit surface normal (unused) |
| float | ny | y component of hit surface normal (unused) |
| float | nz | z component of hit surface normal (unused) |
| integer | subShapeIndex | sub shape index of hit object |
| integer | hitShapeId | id of hit object shape |
| 161 | function HighPressureWasherLance:washRaycastCallback(hitActorId, x, y, z, distance, nx, ny, nz, subShapeIndex, hitShapeId) |
| 162 | local vehicle = g_currentMission.nodeToObject[hitActorId] |
| 163 | if hitActorId ~= hitShapeId then |
| 164 | -- object is a compoundChild. Try to find the compound |
| 165 | local parentId = hitShapeId |
| 166 | while parentId ~= 0 do |
| 167 | if g_currentMission.nodeToObject[parentId] ~= nil then |
| 168 | -- found valid compound |
| 169 | vehicle = g_currentMission.nodeToObject[parentId] |
| 170 | break |
| 171 | end |
| 172 | parentId = getParent(parentId) |
| 173 | end |
| 174 | end |
| 175 | |
| 176 | if vehicle ~= nil and vehicle.getAllowsWashingByType ~= nil and vehicle:getAllowsWashingByType(Washable.WASHTYPE_HIGH_PRESSURE_WASHER) then |
| 177 | self.foundVehicle = vehicle |
| 178 | return false |
| 179 | end |
| 180 | return true |
| 181 | end |
Get is active for inputDefinition
getIsActiveForInput()Return Values
| boolean | isActivateable | is activateable |
| boolean | isActiveForInput | is active for input |
| 187 | function HighPressureWasherLance:getIsActiveForInput() |
| 188 | if self.player == g_currentMission.player and not g_gui:getIsGuiVisible() then |
| 189 | return true |
| 190 | end |
| 191 | return false |
| 192 | end |
Event for conveyor belt angle
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function AIConveyorBeltSetAngleEvent:emptyNew() |
| 16 | local self = Event:new(AIConveyorBeltSetAngleEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table vehicle, integer currentAngle)Arguments
| table | vehicle | vehicle |
| integer | currentAngle | current angle |
| 24 | function AIConveyorBeltSetAngleEvent:new(vehicle, currentAngle) |
| 25 | local self = AIConveyorBeltSetAngleEvent:emptyNew() |
| 26 | self.currentAngle = currentAngle; |
| 27 | self.vehicle = vehicle; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function AIConveyorBeltSetAngleEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId); |
| 37 | self.currentAngle = streamReadInt8(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function AIConveyorBeltSetAngleEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle); |
| 47 | streamWriteInt8(streamId, self.currentAngle); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function AIConveyorBeltSetAngleEvent:run(connection) |
| 54 | self.vehicle:setAIConveyorBeltAngle(self.currentAngle, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(AIConveyorBeltSetAngleEvent:new(self.vehicle, self.currentAngle), nil, connection, self.vehicle); |
| 57 | end; |
| 58 | end; |
Event for ai block
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function AIVehicleIsBlockedEvent:emptyNew() |
| 16 | local self = Event:new(AIVehicleIsBlockedEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, boolean isBlocked)Arguments
| table | object | object |
| boolean | isBlocked | is blocked |
| 24 | function AIVehicleIsBlockedEvent:new(object, isBlocked) |
| 25 | local self = AIVehicleIsBlockedEvent:emptyNew() |
| 26 | self.object = object |
| 27 | self.isBlocked = isBlocked |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function AIVehicleIsBlockedEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId) |
| 37 | self.isBlocked = streamReadBool(streamId) |
| 38 | self:run(connection) |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function AIVehicleIsBlockedEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 47 | streamWriteBool(streamId, self.isBlocked) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function AIVehicleIsBlockedEvent:run(connection) |
| 54 | if self.object ~= nil then |
| 55 | if self.isBlocked then |
| 56 | self.object:aiBlock() |
| 57 | else |
| 58 | self.object:aiContinue() |
| 59 | end |
| 60 | end |
| 61 | end |
Event for ai start
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function AIVehicleSetStartedEvent:emptyNew() |
| 16 | local self = Event:new(AIVehicleSetStartedEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, integer reason, boolean isStarted, integer helper)Arguments
| table | object | object |
| integer | reason | reason |
| boolean | isStarted | is started |
| integer | helper | helper id |
| 26 | function AIVehicleSetStartedEvent:new(object, reason, isStarted, helper, startedFarmId) |
| 27 | local self = AIVehicleSetStartedEvent:emptyNew() |
| 28 | self.object = object |
| 29 | self.isStarted = isStarted |
| 30 | self.reason = reason |
| 31 | self.startedFarmId = startedFarmId |
| 32 | if helper ~= nil then |
| 33 | self.helperIndex = helper.index |
| 34 | end |
| 35 | return self |
| 36 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 42 | function AIVehicleSetStartedEvent:readStream(streamId, connection) |
| 43 | self.object = NetworkUtil.readNodeObject(streamId) |
| 44 | self.isStarted = streamReadBool(streamId) |
| 45 | |
| 46 | if not self.isStarted then |
| 47 | self.reason = streamReadUIntN(streamId, AIVehicle.NUM_BITS_REASONS) |
| 48 | else |
| 49 | self.helperIndex = streamReadUInt8(streamId) |
| 50 | end |
| 51 | |
| 52 | self.startedFarmId = streamReadUIntN(streamId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 53 | |
| 54 | self:run(connection) |
| 55 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 61 | function AIVehicleSetStartedEvent:writeStream(streamId, connection) |
| 62 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 63 | streamWriteBool(streamId, self.isStarted) |
| 64 | |
| 65 | if not self.isStarted then |
| 66 | streamWriteUIntN(streamId, self.reason, AIVehicle.NUM_BITS_REASONS) |
| 67 | else |
| 68 | streamWriteUInt8(streamId, self.helperIndex) |
| 69 | end |
| 70 | |
| 71 | streamWriteUIntN(streamId, self.startedFarmId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 72 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 77 | function AIVehicleSetStartedEvent:run(connection) |
| 78 | if self.isStarted then |
| 79 | self.object:startAIVehicle(self.helperIndex, true, self.startedFarmId) |
| 80 | else |
| 81 | self.object:stopAIVehicle(self.reason, true) |
| 82 | end |
| 83 | if not connection:getIsServer() then |
| 84 | for _, v in pairs(g_server.clientConnections) do |
| 85 | if v ~= connection and not v:getIsLocal() then |
| 86 | v:sendEvent(AIVehicleSetStartedEvent:new(self.object, self.reason, self.isStarted, g_helperManager:getHelperByIndex(self.helperIndex), self.startedFarmId)) |
| 87 | end |
| 88 | end |
| 89 | end |
| 90 | end |
Event for animation start
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function AnimatedVehicleStartEvent:emptyNew() |
| 16 | local self = Event:new(AnimatedVehicleStartEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, string name, float speed, float animTime)Arguments
| table | object | object |
| string | name | name of animation |
| float | speed | speed of animation |
| float | animTime | time of animation |
| 26 | function AnimatedVehicleStartEvent:new(object, name, speed, animTime) |
| 27 | local self = AnimatedVehicleStartEvent:emptyNew() |
| 28 | self.name = name; |
| 29 | self.speed = speed; |
| 30 | self.animTime = animTime; |
| 31 | self.object = object; |
| 32 | return self; |
| 33 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 39 | function AnimatedVehicleStartEvent:readStream(streamId, connection) |
| 40 | self.object = NetworkUtil.readNodeObject(streamId); |
| 41 | self.name = streamReadString(streamId); |
| 42 | self.speed = streamReadFloat32(streamId); |
| 43 | self.animTime = streamReadFloat32(streamId); |
| 44 | |
| 45 | self.object:playAnimation(self.name, self.speed, self.animTime, true); |
| 46 | if not connection:getIsServer() then |
| 47 | g_server:broadcastEvent(AnimatedVehicleStartEvent:new(self.object, self.name, self.speed, self.animTime), nil, connection, self.object); |
| 48 | end; |
| 49 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 55 | function AnimatedVehicleStartEvent:writeStream(streamId, connection) |
| 56 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 57 | streamWriteString(streamId, self.name); |
| 58 | streamWriteFloat32(streamId, self.speed); |
| 59 | streamWriteFloat32(streamId, self.animTime); |
| 60 | end; |
Event for animation stop
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function AnimatedVehicleStopEvent:emptyNew() |
| 16 | local self = Event:new(AnimatedVehicleStopEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, string name)Arguments
| table | object | object |
| string | name | name |
| 24 | function AnimatedVehicleStopEvent:new(object, name) |
| 25 | local self = AnimatedVehicleStopEvent:emptyNew() |
| 26 | self.name = name; |
| 27 | self.object = object; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function AnimatedVehicleStopEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.name = streamReadString(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function AnimatedVehicleStopEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteString(streamId, self.name); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function AnimatedVehicleStopEvent:run(connection) |
| 54 | AnimatedVehicle.stopAnimation(self.object, self.name, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(AnimatedVehicleStopEvent:new(self.object, self.name), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Event for bale loader state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function BaleLoaderStateEvent:emptyNew() |
| 16 | local self = Event:new(BaleLoaderStateEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer stateId, integer nearestBaleServerId)Arguments
| table | object | object |
| integer | stateId | stateId |
| integer | nearestBaleServerId | nearestBaleServerId |
| 25 | function BaleLoaderStateEvent:new(object, stateId, nearestBaleServerId) |
| 26 | local self = BaleLoaderStateEvent:emptyNew() |
| 27 | self.object = object; |
| 28 | self.stateId = stateId; |
| 29 | assert(nearestBaleServerId ~= nil or self.stateId ~= BaleLoader.CHANGE_GRAB_BALE); |
| 30 | self.nearestBaleServerId = nearestBaleServerId; |
| 31 | return self; |
| 32 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 38 | function BaleLoaderStateEvent:readStream(streamId, connection) |
| 39 | self.object = NetworkUtil.readNodeObject(streamId); |
| 40 | |
| 41 | self.stateId = streamReadInt8(streamId); |
| 42 | if self.stateId == BaleLoader.CHANGE_GRAB_BALE then |
| 43 | self.nearestBaleServerId = NetworkUtil.readNodeObjectId(streamId); |
| 44 | end; |
| 45 | self:run(connection); |
| 46 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 52 | function BaleLoaderStateEvent:writeStream(streamId, connection) |
| 53 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 54 | streamWriteInt8(streamId, self.stateId); |
| 55 | if self.stateId == BaleLoader.CHANGE_GRAB_BALE then |
| 56 | NetworkUtil.writeNodeObjectId(streamId, self.nearestBaleServerId); |
| 57 | end; |
| 58 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 63 | function BaleLoaderStateEvent:run(connection) |
| 64 | self.object:doStateChange(self.stateId, self.nearestBaleServerId); |
| 65 | end; |
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function BalerCreateBaleEvent:emptyNew() |
| 15 | local self = Event:new(BalerCreateBaleEvent_mt); |
| 16 | return self; |
| 17 | end; |
Create new instance of eventDefinition
new(table object, integer baleFillType, float baleTime)Arguments
| table | object | object |
| integer | baleFillType | bale fill type |
| float | baleTime | bale time |
| 24 | function BalerCreateBaleEvent:new(object, baleFillType, baleTime) |
| 25 | local self = BalerCreateBaleEvent:emptyNew() |
| 26 | self.baleFillType = baleFillType; |
| 27 | self.baleTime = baleTime; |
| 28 | self.object = object; |
| 29 | return self; |
| 30 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 36 | function BalerCreateBaleEvent:readStream(streamId, connection) |
| 37 | self.object = NetworkUtil.readNodeObject(streamId); |
| 38 | self.baleTime = streamReadFloat32(streamId); |
| 39 | self.baleFillType = streamReadUIntN(streamId, FillTypeManager.SEND_NUM_BITS); |
| 40 | self:run(connection); |
| 41 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 47 | function BalerCreateBaleEvent:writeStream(streamId, connection) |
| 48 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 49 | streamWriteFloat32(streamId, self.baleTime); |
| 50 | streamWriteUIntN(streamId, self.baleFillType, FillTypeManager.SEND_NUM_BITS); |
| 51 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 56 | function BalerCreateBaleEvent:run(connection) |
| 57 | self.object:createBale(self.baleFillType); |
| 58 | self.object:setBaleTime(table.getn(self.object.spec_baler.bales), self.baleTime); |
| 59 | end; |
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function BalerSetBaleTimeEvent:emptyNew() |
| 15 | local self = Event:new(BalerSetBaleTimeEvent_mt); |
| 16 | return self; |
| 17 | end; |
Create new instance of eventDefinition
BalerSetBaleTimeEvent:new(table object, integer bale, float baleTime)Arguments
| table | object | object |
| integer | bale | bale id |
| float | baleTime | bale time |
| 24 | function BalerSetBaleTimeEvent:new(object, bale, baleTime) |
| 25 | local self = BalerSetBaleTimeEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.bale = bale; |
| 28 | self.baleTime = baleTime; |
| 29 | return self; |
| 30 | end; |
Called on client side on joinDefinition
BalerSetBaleTimeEvent:readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 36 | function BalerSetBaleTimeEvent:readStream(streamId, connection) |
| 37 | self.object = NetworkUtil.readNodeObject(streamId); |
| 38 | self.bale = streamReadInt32(streamId); |
| 39 | self.baleTime = streamReadFloat32(streamId); |
| 40 | self:run(connection); |
| 41 | end; |
Called on server side on joinDefinition
BalerSetBaleTimeEvent:writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 47 | function BalerSetBaleTimeEvent:writeStream(streamId, connection) |
| 48 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 49 | streamWriteInt32(streamId, self.bale); |
| 50 | streamWriteFloat32(streamId, self.baleTime); |
| 51 | end; |
Run action on receiving sideDefinition
BalerSetBaleTimeEvent:run(integer connection)Arguments
| integer | connection | connection |
| 56 | function BalerSetBaleTimeEvent:run(connection) |
| 57 | self.object:setBaleTime(self.bale, self.baleTime); |
| 58 | end; |
Event for baler is unloading state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function BalerSetIsUnloadingBaleEvent:emptyNew() |
| 16 | local self = Event:new(BalerSetIsUnloadingBaleEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean isUnloadingBale)Arguments
| table | object | object |
| boolean | isUnloadingBale | is unloading bale |
| 24 | function BalerSetIsUnloadingBaleEvent:new(object, isUnloadingBale) |
| 25 | local self = BalerSetIsUnloadingBaleEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.isUnloadingBale = isUnloadingBale; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function BalerSetIsUnloadingBaleEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.isUnloadingBale = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function BalerSetIsUnloadingBaleEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.isUnloadingBale); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function BalerSetIsUnloadingBaleEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.object); |
| 56 | end; |
| 57 | self.object:setIsUnloadingBale(self.isUnloadingBale, true); |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, boolean isUnloadingBale, boolean noEventSend)Arguments
| table | object | object |
| boolean | isUnloadingBale | isUnloadingBale |
| boolean | noEventSend | no event send |
| 65 | function BalerSetIsUnloadingBaleEvent.sendEvent(object, isUnloadingBale, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(BalerSetIsUnloadingBaleEvent:new(object, isUnloadingBale), nil, nil, object); |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(BalerSetIsUnloadingBaleEvent:new(object, isUnloadingBale)); |
| 71 | end; |
| 72 | end; |
| 73 | end; |
Event for bale wrapper state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function BaleWrapperStateEvent:emptyNew() |
| 16 | local self = Event:new(BaleWrapperStateEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer stateId, integer nearestBaleServerId)Arguments
| table | object | object |
| integer | stateId | state id |
| integer | nearestBaleServerId | server id of nearest bale |
| 25 | function BaleWrapperStateEvent:new(object, stateId, nearestBaleServerId) |
| 26 | local self = BaleWrapperStateEvent:emptyNew() |
| 27 | self.object = object; |
| 28 | self.stateId = stateId; |
| 29 | assert(nearestBaleServerId ~= nil or self.stateId ~= BaleWrapper.CHANGE_GRAB_BALE); |
| 30 | self.nearestBaleServerId = nearestBaleServerId; |
| 31 | return self; |
| 32 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 38 | function BaleWrapperStateEvent:readStream(streamId, connection) |
| 39 | self.object = NetworkUtil.readNodeObject(streamId); |
| 40 | self.stateId = streamReadInt8(streamId); |
| 41 | if self.stateId == BaleWrapper.CHANGE_GRAB_BALE then |
| 42 | self.nearestBaleServerId = NetworkUtil.readNodeObjectId(streamId); |
| 43 | end; |
| 44 | self:run(connection); |
| 45 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 51 | function BaleWrapperStateEvent:writeStream(streamId, connection) |
| 52 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 53 | streamWriteInt8(streamId, self.stateId); |
| 54 | if self.stateId == BaleWrapper.CHANGE_GRAB_BALE then |
| 55 | NetworkUtil.writeNodeObjectId(streamId, self.nearestBaleServerId); |
| 56 | end; |
| 57 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 62 | function BaleWrapperStateEvent:run(connection) |
| 63 | self.object:doStateChange(self.stateId, self.nearestBaleServerId); |
| 64 | end; |
Event for bunker silo close
Create instance of Event classDefinition
emptyNew()Return Values
| table | instance | instance of event |
| table | self | instance of class event |
| 13 | function BunkerSiloCloseEvent:emptyNew() |
| 14 | local self = Event:new(BunkerSiloCloseEvent_mt); |
| 15 | return self; |
| 16 | end; |
Create new instance of eventDefinition
new(table bunkerSilo)Arguments
| table | bunkerSilo | bunkerSilo |
| table | self | instance of class event |
| table | instance | instance of event |
| 22 | function BunkerSiloCloseEvent:new(bunkerSilo) |
| 23 | local self = BunkerSiloCloseEvent:emptyNew() |
| 24 | self.bunkerSilo = bunkerSilo; |
| 25 | return self; |
| 26 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | instance | instance of event |
| 32 | function BunkerSiloCloseEvent:readStream(streamId, connection) |
| 33 | if not connection:getIsServer() then |
| 34 | self.bunkerSilo = NetworkUtil.readNodeObject(streamId); |
| 35 | end; |
| 36 | self:run(connection); |
| 37 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | self | instance of class event |
| 43 | function BunkerSiloCloseEvent:writeStream(streamId, connection) |
| 44 | if connection:getIsServer() then |
| 45 | NetworkUtil.writeNodeObject(streamId, self.bunkerSilo); |
| 46 | end; |
| 47 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| table | instance | instance of event |
| 52 | function BunkerSiloCloseEvent:run(connection) |
| 53 | if not connection:getIsServer() then |
| 54 | self.bunkerSilo:setState(BunkerSilo.STATE_CLOSED); |
| 55 | end; |
| 56 | end; |
Event for bunker silo open
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| table | self | instance of class event |
| 13 | function BunkerSiloOpenEvent:emptyNew() |
| 14 | local self = Event:new(BunkerSiloOpenEvent_mt); |
| 15 | return self; |
| 16 | end; |
Create new instance of eventDefinition
new(table bunkerSilo, float x, float y, float z)Arguments
| table | bunkerSilo | bunkerSilo |
| float | x | x opening position |
| float | y | y opening position |
| float | z | z opening position |
| table | instance | instance of event |
| table | instance | instance of event |
| 25 | function BunkerSiloOpenEvent:new(bunkerSilo, x,y,z) |
| 26 | local self = BunkerSiloOpenEvent:emptyNew() |
| 27 | self.bunkerSilo = bunkerSilo; |
| 28 | self.x = x; |
| 29 | self.y = y; |
| 30 | self.z = z; |
| 31 | return self; |
| 32 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | self | instance of class event |
| 38 | function BunkerSiloOpenEvent:readStream(streamId, connection) |
| 39 | if not connection:getIsServer() then |
| 40 | self.bunkerSilo = NetworkUtil.readNodeObject(streamId); |
| 41 | self.x = streamReadFloat32(streamId); |
| 42 | self.y = streamReadFloat32(streamId); |
| 43 | self.z = streamReadFloat32(streamId); |
| 44 | end; |
| 45 | self:run(connection); |
| 46 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | instance | instance of event |
| 52 | function BunkerSiloOpenEvent:writeStream(streamId, connection) |
| 53 | if connection:getIsServer() then |
| 54 | NetworkUtil.writeNodeObject(streamId, self.bunkerSilo); |
| 55 | streamWriteFloat32(streamId, self.x); |
| 56 | streamWriteFloat32(streamId, self.y); |
| 57 | streamWriteFloat32(streamId, self.z); |
| 58 | end; |
| 59 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| table | motorInstance | motor instance |
| 64 | function BunkerSiloOpenEvent:run(connection) |
| 65 | if not connection:getIsServer() then |
| 66 | self.bunkerSilo:openSilo(self.x, self.y, self.z); |
| 67 | end; |
| 68 | end; |
Event for cutting
Create instance of Event classDefinition
emptyNew()Return Values
| boolean | true | if storeitem is configurable, else false |
| table | self | instance of class event |
| 14 | function ChainsawCutEvent:emptyNew() |
| 15 | local self = Event:new(ChainsawCutEvent_mt); |
| 16 | return self; |
| 17 | end; |
Create new instance of eventDefinition
new(integer splitShapeId, float x, float y, float z, float nx, float ny, float nz, float yx, float yy, float yz, float cutSizeY, float cutSizeZ)Arguments
| integer | splitShapeId | id of split shape |
| float | x | x |
| float | y | y |
| float | z | z |
| float | nx | nx |
| float | ny | ny |
| float | nz | nz |
| float | yx | yx |
| float | yy | yy |
| float | yz | yz |
| float | cutSizeY | y cut size |
| float | cutSizeZ | z cut size |
| boolean | true | if storeitem is leaseable, else false |
| table | instance | instance of event |
| 34 | function ChainsawCutEvent:new(splitShapeId, x,y,z, nx,ny,nz, yx,yy,yz, cutSizeY, cutSizeZ, farmId) |
| 35 | local self = ChainsawCutEvent:emptyNew() |
| 36 | self.splitShapeId = splitShapeId; |
| 37 | self.x,self.y,self.z = x,y,z; |
| 38 | self.nx,self.ny,self.nz = nx,ny,nz; |
| 39 | self.yx,self.yy,self.yz = yx,yy,yz; |
| 40 | self.cutSizeY,self.cutSizeZ = cutSizeY,cutSizeZ; |
| 41 | self.farmId = farmId |
| 42 | return self; |
| 43 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| integer | configId | the default config id |
| 49 | function ChainsawCutEvent:readStream(streamId, connection) |
| 50 | if not connection:getIsServer() then |
| 51 | local splitShapeId = readSplitShapeIdFromStream(streamId); |
| 52 | local x = streamReadFloat32(streamId); |
| 53 | local y = streamReadFloat32(streamId); |
| 54 | local z = streamReadFloat32(streamId); |
| 55 | local nx = streamReadFloat32(streamId); |
| 56 | local ny = streamReadFloat32(streamId); |
| 57 | local nz = streamReadFloat32(streamId); |
| 58 | local yx = streamReadFloat32(streamId); |
| 59 | local yy = streamReadFloat32(streamId); |
| 60 | local yz = streamReadFloat32(streamId); |
| 61 | local cutSizeY = streamReadFloat32(streamId); |
| 62 | local cutSizeZ = streamReadFloat32(streamId); |
| 63 | local farmId = streamReadUIntN(streamId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 64 | |
| 65 | if splitShapeId ~= 0 then |
| 66 | ChainsawUtil.cutSplitShape(splitShapeId, x,y,z, nx,ny,nz, yx,yy,yz, cutSizeY, cutSizeZ, farmId); |
| 67 | end |
| 68 | end |
| 69 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| integer | the | default price |
| 75 | function ChainsawCutEvent:writeStream(streamId, connection) |
| 76 | if connection:getIsServer() then |
| 77 | writeSplitShapeIdToStream(streamId, self.splitShapeId); |
| 78 | streamWriteFloat32(streamId, self.x); |
| 79 | streamWriteFloat32(streamId, self.y); |
| 80 | streamWriteFloat32(streamId, self.z); |
| 81 | streamWriteFloat32(streamId, self.nx); |
| 82 | streamWriteFloat32(streamId, self.ny); |
| 83 | streamWriteFloat32(streamId, self.nz); |
| 84 | streamWriteFloat32(streamId, self.yx); |
| 85 | streamWriteFloat32(streamId, self.yy); |
| 86 | streamWriteFloat32(streamId, self.yz); |
| 87 | streamWriteFloat32(streamId, self.cutSizeY); |
| 88 | streamWriteFloat32(streamId, self.cutSizeZ); |
| 89 | streamWriteFloat32(streamId, self.farmId) |
| 90 | end |
| 91 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| integer | the | daily upkeep |
| 96 | function ChainsawCutEvent:run(connection) |
| 97 | print("Error: ChainsawCutEvent is not allowed to be executed on a local client"); |
| 98 | end; |
Event for delimb
Create instance of Event classDefinition
emptyNew()Return Values
| integer | cost | of the storeitem |
| table | self | instance of class event |
| 14 | function ChainsawDelimbEvent:emptyNew() |
| 15 | local self = Event:new(ChainsawDelimbEvent_mt); |
| 16 | return self; |
| 17 | end; |
Create new instance of eventDefinition
new(table player, float x, float y, float z, float nx, float ny, float nz, float yx, float yy, float yz, boolean onDelimb)Arguments
| table | player | player |
| float | x | x |
| float | y | y |
| float | z | z |
| float | nx | nx |
| float | ny | ny |
| float | nz | nz |
| float | yx | yx |
| float | yy | yy |
| float | yz | yz |
| boolean | onDelimb | on delimb |
| table | config | object |
| table | instance | instance of event |
| 33 | function ChainsawDelimbEvent:new(player, x,y,z, nx,ny,nz, yx,yy,yz, onDelimb) |
| 34 | local self = ChainsawDelimbEvent:emptyNew() |
| 35 | self.player = player; |
| 36 | self.x, self.y, self.z = x, y, z; |
| 37 | self.nx, self.ny, self.nz = nx, ny, nz; |
| 38 | self.yx, self.yy, self.yz = yx, yy, yz; |
| 39 | self.onDelimb = onDelimb; |
| 40 | return self; |
| 41 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | functions | list of storeitem functions |
| 47 | function ChainsawDelimbEvent:readStream(streamId, connection) |
| 48 | if not connection:getIsServer() then -- server side |
| 49 | self.player = NetworkUtil.readNodeObject(streamId); |
| 50 | self.x = streamReadFloat32(streamId); |
| 51 | self.y = streamReadFloat32(streamId); |
| 52 | self.z = streamReadFloat32(streamId); |
| 53 | self.nx = streamReadFloat32(streamId); |
| 54 | self.ny = streamReadFloat32(streamId); |
| 55 | self.nz = streamReadFloat32(streamId); |
| 56 | self.yx = streamReadFloat32(streamId); |
| 57 | self.yy = streamReadFloat32(streamId); |
| 58 | self.yz = streamReadFloat32(streamId); |
| 59 | self.onDelimb = false; |
| 60 | if self.player ~= nil then |
| 61 | local chainsaw = self.player.currentTool; |
| 62 | if chainsaw ~= nil then |
| 63 | local ret = findAndRemoveSplitShapeAttachments(self.x,self.y,self.z, self.nx,self.ny,self.nz, self.yx,self.yy,self.yz, 0.7, chainsaw.cutSizeY, chainsaw.cutSizeZ); |
| 64 | if ret then |
| 65 | self.onDelimb = true; |
| 66 | connection:sendEvent(self); |
| 67 | end; |
| 68 | end; |
| 69 | end; |
| 70 | else -- client side |
| 71 | self.player = NetworkUtil.readNodeObject(streamId); |
| 72 | self.onDelimb = streamReadBool(streamId); |
| 73 | if self.player ~= nil and self.player.currentTool ~= nil then |
| 74 | if self.player.currentTool.setOnDelimb ~= nil then |
| 75 | self.player.currentTool:setOnDelimb(self.onDelimb); |
| 76 | end; |
| 77 | end; |
| 78 | end |
| 79 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | specs | list of storeitem specs |
| 85 | function ChainsawDelimbEvent:writeStream(streamId, connection) |
| 86 | if connection:getIsServer() then -- client |
| 87 | NetworkUtil.writeNodeObject(streamId, self.player); |
| 88 | streamWriteFloat32(streamId, self.x); |
| 89 | streamWriteFloat32(streamId, self.y); |
| 90 | streamWriteFloat32(streamId, self.z); |
| 91 | streamWriteFloat32(streamId, self.nx); |
| 92 | streamWriteFloat32(streamId, self.ny); |
| 93 | streamWriteFloat32(streamId, self.nz); |
| 94 | streamWriteFloat32(streamId, self.yx); |
| 95 | streamWriteFloat32(streamId, self.yy); |
| 96 | streamWriteFloat32(streamId, self.yz); |
| 97 | else |
| 98 | NetworkUtil.writeNodeObject(streamId, self.player); |
| 99 | streamWriteBool(streamId, self.onDelimb); |
| 100 | end |
| 101 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| integer | brandIndex | the brandindex |
| 106 | function ChainsawDelimbEvent:run(connection) |
| 107 | print("Error: ChainsawDelimbEvent is not allowed to be executed on a local client"); |
| 108 | end; |
Event for chainsaw state
Create instance of Event classDefinition
emptyNew()Return Values
| table | instance | instance of object |
| table | self | instance of class event |
| 14 | function ChainsawStateEvent:emptyNew() |
| 15 | local self = Event:new(ChainsawStateEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table player, boolean isCutting, boolean isHorizontalCut)Arguments
| table | player | player |
| boolean | isCutting | is cutting |
| boolean | isHorizontalCut | is horizontal cutting |
| boolean | true | if loading was successful else false |
| table | instance | instance of event |
| 25 | function ChainsawStateEvent:new(player, isCutting, isHorizontalCut, hasBeencut) |
| 26 | local self = ChainsawStateEvent:emptyNew() |
| 27 | self.player = player |
| 28 | self.isCutting = isCutting |
| 29 | self.isHorizontalCut = isHorizontalCut |
| 30 | self.hasBeenCut = hasBeencut |
| 31 | return self |
| 32 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | instance | instance of object |
| 38 | function ChainsawStateEvent:readStream(streamId, connection) |
| 39 | self.player = NetworkUtil.readNodeObject(streamId) |
| 40 | self.isCutting = streamReadBool(streamId) |
| 41 | self.isHorizontalCut = streamReadBool(streamId) |
| 42 | self.hasBeenCut = streamReadBool(streamId) |
| 43 | self:run(connection) |
| 44 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| boolean | true | if loading was successful else false |
| 50 | function ChainsawStateEvent:writeStream(streamId, connection) |
| 51 | NetworkUtil.writeNodeObject(streamId, self.player) |
| 52 | streamWriteBool(streamId, self.isCutting) |
| 53 | streamWriteBool(streamId, self.isHorizontalCut) |
| 54 | streamWriteBool(streamId, self.hasBeenCut) |
| 55 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| boolean | true | if loading was successful else false |
| 60 | function ChainsawStateEvent:run(connection) |
| 61 | if not connection:getIsServer() then |
| 62 | g_server:broadcastEvent(self, false, connection, self.player) |
| 63 | end |
| 64 | |
| 65 | local currentTool = self.player.baseInformation.currentHandtool |
| 66 | if currentTool ~= nil and currentTool.setCutting ~= nil then |
| 67 | currentTool:setCutting(self.isCutting, self.isHorizontalCut, self.hasBeenCut, true) |
| 68 | end |
| 69 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table player, boolean isCutting, boolean isHorizontalCut, boolean noEventSend)Arguments
| table | player | player |
| boolean | isCutting | is cutting |
| boolean | isHorizontalCut | is horizontal cutting |
| boolean | noEventSend | no event send |
| table | sample | sample object |
| 77 | function ChainsawStateEvent.sendEvent(player, isCutting, isHorizontalCut, hasBeenCut, noEventSend) |
| 78 | local currentTool = player.baseInformation.currentHandtool |
| 79 | if currentTool ~= nil and currentTool.setCutting ~= nil and (currentTool.isCutting ~= isCutting or currentTool.hasBeenCut ~= hasBeenCut) then |
| 80 | if noEventSend == nil or noEventSend == false then |
| 81 | if g_server ~= nil then |
| 82 | g_server:broadcastEvent(ChainsawStateEvent:new(player, isCutting, isHorizontalCut, hasBeenCut), nil, nil, player) |
| 83 | else |
| 84 | g_client:getServerConnection():sendEvent(ChainsawStateEvent:new(player, isCutting, isHorizontalCut, hasBeenCut)) |
| 85 | end |
| 86 | end |
| 87 | end |
| 88 | end |
Event for straw enable state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function CombineStrawEnableEvent:emptyNew() |
| 16 | local self = Event:new(CombineStrawEnableEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table vehicle, boolean isSwathActive)Arguments
| table | vehicle | vehicle |
| boolean | isSwathActive | is straw enabled |
| 24 | function CombineStrawEnableEvent:new(vehicle, isSwathActive) |
| 25 | local self = CombineStrawEnableEvent:emptyNew() |
| 26 | self.vehicle = vehicle; |
| 27 | self.isSwathActive = isSwathActive; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function CombineStrawEnableEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId); |
| 37 | self.isSwathActive = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function CombineStrawEnableEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle); |
| 47 | streamWriteBool(streamId, self.isSwathActive); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function CombineStrawEnableEvent:run(connection) |
| 54 | self.vehicle:setIsSwathActive(self.isSwathActive, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(CombineStrawEnableEvent:new(self.vehicle, self.isSwathActive), nil, connection, self.vehicle); |
| 57 | end; |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isSwathActive, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isSwathActive | is straw enabled |
| boolean | noEventSend | no event send |
| 65 | function CombineStrawEnableEvent.sendEvent(vehicle, isSwathActive, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(CombineStrawEnableEvent:new(vehicle, isSwathActive), nil, nil, vehicle); |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(CombineStrawEnableEvent:new(vehicle, isSwathActive)); |
| 71 | end; |
| 72 | end; |
| 73 | end; |
Event for straw enable state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function CylinderedEasyControlChangeEvent:emptyNew() |
| 16 | local self = Event:new(CylinderedEasyControlChangeEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table vehicle, boolean isEasyControlActive)Arguments
| table | vehicle | vehicle |
| boolean | isEasyControlActive | is easy control enabled |
| 24 | function CylinderedEasyControlChangeEvent:new(vehicle, isEasyControlActive) |
| 25 | local self = CylinderedEasyControlChangeEvent:emptyNew() |
| 26 | self.vehicle = vehicle |
| 27 | self.isEasyControlActive = isEasyControlActive |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function CylinderedEasyControlChangeEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 37 | self.isEasyControlActive = streamReadBool(streamId) |
| 38 | self:run(connection) |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function CylinderedEasyControlChangeEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 47 | streamWriteBool(streamId, self.isEasyControlActive) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function CylinderedEasyControlChangeEvent:run(connection) |
| 54 | if self.vehicle ~= nil then |
| 55 | self.vehicle:setIsEasyControlActive(self.isEasyControlActive, true) |
| 56 | end |
| 57 | if not connection:getIsServer() then |
| 58 | g_server:broadcastEvent(CylinderedEasyControlChangeEvent:new(self.vehicle, self.isEasyControlActive), nil, connection, self.vehicle) |
| 59 | end |
| 60 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isEasyControlActive, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isEasyControlActive | is easy control enabled |
| boolean | noEventSend | no event send |
| 67 | function CylinderedEasyControlChangeEvent.sendEvent(vehicle, isEasyControlActive, noEventSend) |
| 68 | if noEventSend == nil or noEventSend == false then |
| 69 | if g_server ~= nil then |
| 70 | g_server:broadcastEvent(CylinderedEasyControlChangeEvent:new(vehicle, isEasyControlActive), nil, nil, vehicle) |
| 71 | elseif g_client ~= nil then |
| 72 | g_client:getServerConnection():sendEvent(CylinderedEasyControlChangeEvent:new(vehicle, isEasyControlActive)) |
| 73 | end |
| 74 | end |
| 75 | end |
Event for toggle lower all
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function DrivableToggleLowerAllEvent:emptyNew() |
| 16 | local self = Event:new(DrivableToggleLowerAllEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table vehicle)Arguments
| table | vehicle | vehicle |
| 23 | function DrivableToggleLowerAllEvent:new(vehicle) |
| 24 | local self = DrivableToggleLowerAllEvent:emptyNew() |
| 25 | self.vehicle = vehicle; |
| 26 | return self; |
| 27 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 33 | function DrivableToggleLowerAllEvent:readStream(streamId, connection) |
| 34 | self.vehicle = NetworkUtil.readNodeObject(streamId); |
| 35 | self:run(connection); |
| 36 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 42 | function DrivableToggleLowerAllEvent:writeStream(streamId, connection) |
| 43 | NetworkUtil.writeNodeObject(streamId, self.vehicle); |
| 44 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 49 | function DrivableToggleLowerAllEvent:run(connection) |
| 50 | self.vehicle:toggleLowerAllImplements(true); |
| 51 | if not connection:getIsServer() then |
| 52 | g_server:broadcastEvent(DrivableToggleLowerAllEvent:new(self.vehicle), nil, connection, self.object); |
| 53 | end; |
| 54 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | noEventSend | no event send |
| 60 | function DrivableToggleLowerAllEvent.sendEvent(vehicle, noEventSend) |
| 61 | if noEventSend == nil or noEventSend == false then |
| 62 | if g_server ~= nil then |
| 63 | g_server:broadcastEvent(DrivableToggleLowerAllEvent:new(vehicle), nil, nil, vehicle); |
| 64 | else |
| 65 | g_client:getServerConnection():sendEvent(DrivableToggleLowerAllEvent:new(vehicle)); |
| 66 | end; |
| 67 | end; |
| 68 | end; |
Event for turned on state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function FillUnitUnloadEvent:emptyNew() |
| 16 | local self = Event:new(FillUnitUnloadEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object)Arguments
| table | object | object |
| 23 | function FillUnitUnloadEvent:new(object) |
| 24 | local self = FillUnitUnloadEvent:emptyNew() |
| 25 | self.object = object |
| 26 | return self |
| 27 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 38 | function FillUnitUnloadEvent:readStream(streamId, connection) |
| 39 | if not connection:getIsServer() then |
| 40 | self.object = NetworkUtil.readNodeObject(streamId) |
| 41 | end |
| 42 | self:run(connection) |
| 43 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 49 | function FillUnitUnloadEvent:writeStream(streamId, connection) |
| 50 | if connection:getIsServer() then |
| 51 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 52 | end |
| 53 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 58 | function FillUnitUnloadEvent:run(connection) |
| 59 | if not connection:getIsServer() then |
| 60 | if self.object ~= nil then |
| 61 | local success = self.object:unloadFillUnits(true) |
| 62 | if not success then |
| 63 | connection:sendEvent(FillUnitUnloadEvent:newServerToClient()) |
| 64 | end |
| 65 | end |
| 66 | else |
| 67 | g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("fillUnit_unload_nospace")) |
| 68 | end |
| 69 | end |
Event for set folding direction
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function FoldableSetFoldDirectionEvent:emptyNew() |
| 16 | local self = Event:new(FoldableSetFoldDirectionEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer direction, boolean moveToMiddle)Arguments
| table | object | object |
| integer | direction | direction |
| boolean | moveToMiddle | move to middle |
| 25 | function FoldableSetFoldDirectionEvent:new(object, direction, moveToMiddle) |
| 26 | local self = FoldableSetFoldDirectionEvent:emptyNew() |
| 27 | self.object = object; |
| 28 | self.direction = MathUtil.sign(direction); |
| 29 | self.moveToMiddle = moveToMiddle; |
| 30 | return self; |
| 31 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 37 | function FoldableSetFoldDirectionEvent:readStream(streamId, connection) |
| 38 | self.object = NetworkUtil.readNodeObject(streamId); |
| 39 | self.direction = streamReadUIntN(streamId, 2)-1; |
| 40 | self.moveToMiddle = streamReadBool(streamId); |
| 41 | self:run(connection); |
| 42 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 48 | function FoldableSetFoldDirectionEvent:writeStream(streamId, connection) |
| 49 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 50 | streamWriteUIntN(streamId, self.direction+1, 2); |
| 51 | streamWriteBool(streamId, self.moveToMiddle); |
| 52 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 57 | function FoldableSetFoldDirectionEvent:run(connection) |
| 58 | if self.object ~= nil then |
| 59 | self.object:setFoldState(self.direction, self.moveToMiddle, true); |
| 60 | end; |
| 61 | if not connection:getIsServer() then |
| 62 | g_server:broadcastEvent(FoldableSetFoldDirectionEvent:new(self.object, self.direction, self.moveToMiddle), nil, connection, self.object); |
| 63 | end; |
| 64 | end; |
Event for greenhouse tank filling state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function GreenhouseSetIsWaterTankFillingEvent:emptyNew() |
| 16 | local self = Event:new(GreenhouseSetIsWaterTankFillingEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean isFilling, table trailer)Arguments
| table | object | object |
| boolean | isFilling | is filling |
| table | trailer | trailer |
| table | instance | instance of event |
| 26 | function GreenhouseSetIsWaterTankFillingEvent:new(object, isFilling, trailer) |
| 27 | local self = GreenhouseSetIsWaterTankFillingEvent:emptyNew() |
| 28 | self.object = object; |
| 29 | self.isFilling = isFilling; |
| 30 | self.trailer = trailer; |
| 31 | return self; |
| 32 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 38 | function GreenhouseSetIsWaterTankFillingEvent:readStream(streamId, connection) |
| 39 | self.object = NetworkUtil.readNodeObject(streamId); |
| 40 | self.isFilling = streamReadBool(streamId); |
| 41 | if self.isFilling and not connection:getIsServer() then |
| 42 | self.trailer = NetworkUtil.readNodeObject(streamId); |
| 43 | end; |
| 44 | self:run(connection); |
| 45 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 51 | function GreenhouseSetIsWaterTankFillingEvent:writeStream(streamId, connection) |
| 52 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 53 | streamWriteBool(streamId, self.isFilling); |
| 54 | if self.isFilling and connection:getIsServer() then |
| 55 | NetworkUtil.writeNodeObject(streamId, self.trailer); |
| 56 | end; |
| 57 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 62 | function GreenhouseSetIsWaterTankFillingEvent:run(connection) |
| 63 | if not connection:getIsServer() then |
| 64 | g_server:broadcastEvent(self, false, connection, self.object); |
| 65 | end; |
| 66 | self.object:setIsWaterTankFilling(self.isFilling, self.trailer, true); |
| 67 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, boolean isFilling, table trailer, boolean noEventSend)Arguments
| table | object | object |
| boolean | isFilling | is filling |
| table | trailer | trailer |
| boolean | noEventSend | no event send |
| 75 | function GreenhouseSetIsWaterTankFillingEvent.sendEvent(object, isFilling, trailer, noEventSend) |
| 76 | if isFilling ~= object.isWaterTankFilling then |
| 77 | if noEventSend == nil or noEventSend == false then |
| 78 | if g_server ~= nil then |
| 79 | g_server:broadcastEvent(GreenhouseSetIsWaterTankFillingEvent:new(object, isFilling, trailer), nil, nil, object); |
| 80 | else |
| 81 | assert(not isFilling or (trailer ~= nil)); |
| 82 | g_client:getServerConnection():sendEvent(GreenhouseSetIsWaterTankFillingEvent:new(object, isFilling, trailer)); |
| 83 | end; |
| 84 | end; |
| 85 | end; |
| 86 | end; |
Event for honking
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function HonkEvent:emptyNew() |
| 16 | local self = Event:new(HonkEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean isPlaying)Arguments
| table | object | object |
| boolean | isPlaying | honk is playing |
| 24 | function HonkEvent:new(object, isPlaying) |
| 25 | local self = HonkEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.isPlaying = isPlaying; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function HonkEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.isPlaying = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function HonkEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.isPlaying); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function HonkEvent:run(connection) |
| 54 | self.object:playHonk(self.isPlaying, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(HonkEvent:new(self.object, self.isPlaying), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isPlaying, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isPlaying | honk is playing |
| boolean | noEventSend | no event send |
| 65 | function HonkEvent.sendEvent(vehicle, isPlaying, noEventSend) |
| 66 | if vehicle.spec_honk ~= nil and vehicle.spec_honk.isPlaying ~= isPlaying then |
| 67 | if noEventSend == nil or noEventSend == false then |
| 68 | if g_server ~= nil then |
| 69 | g_server:broadcastEvent(HonkEvent:new(vehicle, isPlaying), nil, nil, vehicle); |
| 70 | else |
| 71 | g_client:getServerConnection():sendEvent(HonkEvent:new(vehicle, isPlaying)); |
| 72 | end; |
| 73 | end; |
| 74 | end; |
| 75 | end; |
Event for hpw state
Create instance of Event classDefinition
emptyNew()Return Values
| table | instance | instance of object |
| integer | group | audio group |
| table | self | instance of class event |
| 15 | function HPWLanceStateEvent:emptyNew() |
| 16 | local self = Event:new(HPWLanceStateEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, boolean doWashing)Arguments
| table | object | object |
| boolean | doWashing | do washing |
| table | instance | instance of event |
| 25 | function HPWLanceStateEvent:new(player, doWashing) |
| 26 | local self = HPWLanceStateEvent:emptyNew() |
| 27 | self.player = player |
| 28 | self.doWashing = doWashing |
| 29 | return self |
| 30 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| table | instance | instance of basket trigger object |
| 36 | function HPWLanceStateEvent:readStream(streamId, connection) |
| 37 | self.player = NetworkUtil.readNodeObject(streamId) |
| 38 | self.doWashing = streamReadBool(streamId) |
| 39 | self:run(connection) |
| 40 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| boolean | success | success |
| 46 | function HPWLanceStateEvent:writeStream(streamId, connection) |
| 47 | NetworkUtil.writeNodeObject(streamId, self.player) |
| 48 | streamWriteBool(streamId, self.doWashing) |
| 49 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| table | self | returns the instance |
| 54 | function HPWLanceStateEvent:run(connection) |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(self, false, connection, self.player) |
| 57 | end |
| 58 | local currentTool = self.player.baseInformation.currentHandtool |
| 59 | if currentTool ~= nil and currentTool.setIsWashing ~= nil then |
| 60 | currentTool:setIsWashing(self.doWashing, false, true) |
| 61 | end |
| 62 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, boolean doWashing, boolean noEventSend)Arguments
| table | object | object |
| boolean | doWashing | do washing |
| boolean | noEventSend | no event send |
| bool | true | if level has changed |
| 69 | function HPWLanceStateEvent.sendEvent(player, doWashing, noEventSend) |
| 70 | local currentTool = player.baseInformation.currentHandtool |
| 71 | if currentTool ~= nil and currentTool.setIsWashing ~= nil and doWashing ~= currentTool.doWashing then |
| 72 | if noEventSend == nil or noEventSend == false then |
| 73 | if g_server ~= nil then |
| 74 | g_server:broadcastEvent(HPWLanceStateEvent:new(player, doWashing), nil, nil, player) |
| 75 | else |
| 76 | g_client:getServerConnection():sendEvent(HPWLanceStateEvent:new(player, doWashing)) |
| 77 | end |
| 78 | end |
| 79 | end |
| 80 | end |
Event for hpw turn on state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function HPWPlaceableTurnOnEvent:emptyNew() |
| 16 | local self = Event:new(HPWPlaceableTurnOnEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, boolean isTurnedOn, table player)Arguments
| table | object | object |
| boolean | isTurnedOn | is turned on |
| table | player | player |
| table | instance | instance of event |
| 26 | function HPWPlaceableTurnOnEvent:new(object, isTurnedOn, player) |
| 27 | local self = HPWPlaceableTurnOnEvent:emptyNew() |
| 28 | self.object = object |
| 29 | self.isTurnedOn = isTurnedOn |
| 30 | self.player = player |
| 31 | |
| 32 | return self |
| 33 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 39 | function HPWPlaceableTurnOnEvent:readStream(streamId, connection) |
| 40 | self.object = NetworkUtil.readNodeObject(streamId) |
| 41 | self.isTurnedOn = streamReadBool(streamId) |
| 42 | if self.isTurnedOn then |
| 43 | self.player = NetworkUtil.readNodeObject(streamId) |
| 44 | end |
| 45 | self:run(connection) |
| 46 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 52 | function HPWPlaceableTurnOnEvent:writeStream(streamId, connection) |
| 53 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 54 | streamWriteBool(streamId, self.isTurnedOn) |
| 55 | if self.isTurnedOn then |
| 56 | NetworkUtil.writeNodeObject(streamId, self.player) |
| 57 | end |
| 58 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 63 | function HPWPlaceableTurnOnEvent:run(connection) |
| 64 | if not connection:getIsServer() then |
| 65 | g_server:broadcastEvent(self, false, connection, self.object) |
| 66 | end |
| 67 | self.object:setIsTurnedOn(self.isTurnedOn, self.player, true) |
| 68 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, boolean isTurnedOn, table player, boolean noEventSend)Arguments
| table | object | object |
| boolean | isTurnedOn | is turned on |
| table | player | player |
| boolean | noEventSend | no event send |
| 76 | function HPWPlaceableTurnOnEvent.sendEvent(object, isTurnedOn, player, noEventSend) |
| 77 | if isTurnedOn ~= object.isTurnedOn then |
| 78 | if noEventSend == nil or noEventSend == false then |
| 79 | if g_server ~= nil then |
| 80 | g_server:broadcastEvent(HPWPlaceableTurnOnEvent:new(object, isTurnedOn, player), nil, nil, object) |
| 81 | else |
| 82 | g_client:getServerConnection():sendEvent(HPWPlaceableTurnOnEvent:new(object, isTurnedOn, player)) |
| 83 | end |
| 84 | end |
| 85 | end |
| 86 | end |
Event for honking
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function JumpEvent:emptyNew() |
| 16 | local self = Event:new(JumpEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, boolean isPlaying)Arguments
| table | object | object |
| boolean | isPlaying | honk is playing |
| 24 | function JumpEvent:new(object) |
| 25 | local self = JumpEvent:emptyNew() |
| 26 | self.object = object |
| 27 | return self |
| 28 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 34 | function JumpEvent:readStream(streamId, connection) |
| 35 | if not connection:getIsServer() then |
| 36 | self.object = NetworkUtil.readNodeObject(streamId) |
| 37 | self:run(connection) |
| 38 | end |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function JumpEvent:writeStream(streamId, connection) |
| 46 | if connection:getIsServer() then |
| 47 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 48 | end |
| 49 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 54 | function JumpEvent:run(connection) |
| 55 | self.object:jump(true) |
| 56 | end |
Event for honking
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function MixerWagonBaleNotAcceptedEvent:emptyNew() |
| 16 | local self = Event:new(MixerWagonBaleNotAcceptedEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table vehicle)Arguments
| table | vehicle | vehicle |
| 23 | function MixerWagonBaleNotAcceptedEvent:new(vehicle) |
| 24 | local self = MixerWagonBaleNotAcceptedEvent:emptyNew() |
| 25 | self.vehicle = vehicle |
| 26 | return self |
| 27 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 33 | function MixerWagonBaleNotAcceptedEvent:readStream(streamId, connection) |
| 34 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 35 | g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_CRITICAL, g_i18n:getText("warning_baleNotSupported")) |
| 36 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 42 | function MixerWagonBaleNotAcceptedEvent:writeStream(streamId, connection) |
| 43 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 44 | end |
Event for mower toggle drop
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function MowerToggleWindrowDropEvent:emptyNew() |
| 16 | local self = Event:new(MowerToggleWindrowDropEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean useMowerWindrowDropAreas)Arguments
| table | object | object |
| boolean | useMowerWindrowDropAreas | use mower windrow drop areas |
| 24 | function MowerToggleWindrowDropEvent:new(object, useMowerWindrowDropAreas) |
| 25 | local self = MowerToggleWindrowDropEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.useMowerWindrowDropAreas = useMowerWindrowDropAreas; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function MowerToggleWindrowDropEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.useMowerWindrowDropAreas = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function MowerToggleWindrowDropEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.useMowerWindrowDropAreas); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function MowerToggleWindrowDropEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.object); |
| 56 | end; |
| 57 | if self.object ~= nil then |
| 58 | self.object:setUseMowerWindrowDropAreas(self.useMowerWindrowDropAreas, true); |
| 59 | end; |
| 60 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean useMowerWindrowDropAreas, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | useMowerWindrowDropAreas | use mower windrow drop areas |
| boolean | noEventSend | no event send |
| 67 | function MowerToggleWindrowDropEvent.sendEvent(vehicle, useMowerWindrowDropAreas, noEventSend) |
| 68 | if useMowerWindrowDropAreas ~= vehicle.useMowerWindrowDropAreas then |
| 69 | if noEventSend == nil or noEventSend == false then |
| 70 | if g_server ~= nil then |
| 71 | g_server:broadcastEvent(MowerToggleWindrowDropEvent:new(vehicle, useMowerWindrowDropAreas), nil, nil, vehicle); |
| 72 | else |
| 73 | g_client:getServerConnection():sendEvent(MowerToggleWindrowDropEvent:new(vehicle, useMowerWindrowDropAreas)); |
| 74 | end; |
| 75 | end; |
| 76 | end; |
| 77 | end; |
Event for lower and lift pickup
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function PickupSetStateEvent:emptyNew() |
| 16 | local self = Event:new(PickupSetStateEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean isPickupLowered)Arguments
| table | object | object |
| boolean | isPickupLowered | is pickup lowered |
| 24 | function PickupSetStateEvent:new(object, isPickupLowered) |
| 25 | local self = PickupSetStateEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.isPickupLowered = isPickupLowered; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function PickupSetStateEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.isPickupLowered = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function PickupSetStateEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.isPickupLowered); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function PickupSetStateEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.object); |
| 56 | end; |
| 57 | if self.object ~= nil then |
| 58 | self.object:setPickupState(self.isPickupLowered, true); |
| 59 | end; |
| 60 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isPickupLowered, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isPickupLowered | is pickup lowered |
| boolean | noEventSend | no event send |
| 67 | function PickupSetStateEvent.sendEvent(vehicle, isPickupLowered, noEventSend) |
| 68 | if isPickupLowered ~= vehicle.spec_pickup.isLowered then |
| 69 | if noEventSend == nil or noEventSend == false then |
| 70 | if g_server ~= nil then |
| 71 | g_server:broadcastEvent(PickupSetStateEvent:new(vehicle, isPickupLowered), nil, nil, vehicle); |
| 72 | else |
| 73 | g_client:getServerConnection():sendEvent(PickupSetStateEvent:new(vehicle, isPickupLowered)); |
| 74 | end; |
| 75 | end; |
| 76 | end; |
| 77 | end; |
Event for limit to field state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function PlantLimitToFieldEvent:emptyNew() |
| 16 | local self = Event:new(PlantLimitToFieldEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean plantLimitToField)Arguments
| table | object | object |
| boolean | plantLimitToField | plant is limited to field |
| 24 | function PlantLimitToFieldEvent:new(object, plantLimitToField) |
| 25 | local self = PlantLimitToFieldEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.plantLimitToField = plantLimitToField; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function PlantLimitToFieldEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.plantLimitToField = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function PlantLimitToFieldEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.plantLimitToField); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function PlantLimitToFieldEvent:run(connection) |
| 54 | self.object:setPlantLimitToField(self.plantLimitToField, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(PlantLimitToFieldEvent:new(self.object, self.plantLimitToField), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isPickupLowered, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isPickupLowered | is pickup lowered |
| boolean | noEventSend | no event send |
| 65 | function PlantLimitToFieldEvent.sendEvent(vehicle, plantLimitToField, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(PlantLimitToFieldEvent:new(vehicle, plantLimitToField), nil, nil, vehicle); |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(PlantLimitToFieldEvent:new(vehicle, plantLimitToField)); |
| 71 | end; |
| 72 | end; |
| 73 | end; |
Create an instanceDefinition
sendEvent(table player, integer farmId, bool noEventSend)Arguments
| table | player | player instance |
| integer | farmId | farm identification |
| bool | noEventSend | if false will send the event |
| 88 | function PlayerPermissionsEvent.sendEvent(userId, permissions, isFarmManager, noEventSend) |
| 89 | if noEventSend == nil or noEventSend == false then |
| 90 | local event = PlayerPermissionsEvent:new(userId, permissions, isFarmManager) |
| 91 | |
| 92 | if g_server ~= nil then |
| 93 | local farm = g_farmManager:getFarmByUserId(userId) |
| 94 | local player = farm.userIdToPlayer[userId] |
| 95 | |
| 96 | g_server:broadcastEvent(event, nil, nil, player) |
| 97 | else |
| 98 | g_client:getServerConnection():sendEvent(event) |
| 99 | end |
| 100 | end |
| 101 | end |
Event for limit to field state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function PlowLimitToFieldEvent:emptyNew() |
| 16 | local self = Event:new(PlowLimitToFieldEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean plowLimitToField)Arguments
| table | object | object |
| boolean | plowLimitToField | plow is limited to field |
| 24 | function PlowLimitToFieldEvent:new(object, plowLimitToField) |
| 25 | local self = PlowLimitToFieldEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.plowLimitToField = plowLimitToField; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function PlowLimitToFieldEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.plowLimitToField = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function PlowLimitToFieldEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.plowLimitToField); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function PlowLimitToFieldEvent:run(connection) |
| 54 | self.object:setPlowLimitToField(self.plowLimitToField, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(PlowLimitToFieldEvent:new(self.object, self.plowLimitToField), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Event for plow rotation
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function PlowRotationEvent:emptyNew() |
| 16 | local self = Event:new(PlowRotationEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean rotationMax)Arguments
| table | object | object |
| boolean | rotationMax | rotation max |
| 24 | function PlowRotationEvent:new(object, rotationMax) |
| 25 | local self = PlowRotationEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.rotationMax = rotationMax; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function PlowRotationEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.rotationMax = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function PlowRotationEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.rotationMax); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function PlowRotationEvent:run(connection) |
| 54 | self.object:setRotationMax(self.rotationMax, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(PlowRotationEvent:new(self.object, self.rotationMax), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Event for toggle box creation
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function ReceivingHopperSetCreateBoxesEvent:emptyNew() |
| 16 | local self = Event:new(ReceivingHopperSetCreateBoxesEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean state)Arguments
| table | object | object |
| boolean | state | state |
| 24 | function ReceivingHopperSetCreateBoxesEvent:new(object, state) |
| 25 | local self = ReceivingHopperSetCreateBoxesEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.state = state; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function ReceivingHopperSetCreateBoxesEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.state = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function ReceivingHopperSetCreateBoxesEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.state); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function ReceivingHopperSetCreateBoxesEvent:run(connection) |
| 54 | self.object:setCreateBoxes(self.state, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(ReceivingHopperSetCreateBoxesEvent:new(self.object, self.state), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean state, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | state | state |
| boolean | noEventSend | no event send |
| 65 | function ReceivingHopperSetCreateBoxesEvent.sendEvent(vehicle, state, noEventSend) |
| 66 | if state ~= vehicle.state then |
| 67 | if noEventSend == nil or noEventSend == false then |
| 68 | if g_server ~= nil then |
| 69 | g_server:broadcastEvent(ReceivingHopperSetCreateBoxesEvent:new(vehicle, state), nil, nil, vehicle); |
| 70 | else |
| 71 | g_client:getServerConnection():sendEvent(ReceivingHopperSetCreateBoxesEvent:new(vehicle, state)); |
| 72 | end; |
| 73 | end; |
| 74 | end; |
| 75 | end; |
Event for reverse driving state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function ReverseDrivingSetStateEvent:emptyNew() |
| 16 | local self = Event:new(ReverseDrivingSetStateEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table vehicle, boolean isReverseDriving)Arguments
| table | vehicle | vehicle |
| boolean | isReverseDriving | is reverse driving |
| 24 | function ReverseDrivingSetStateEvent:new(vehicle, isReverseDriving) |
| 25 | local self = ReverseDrivingSetStateEvent:emptyNew() |
| 26 | self.vehicle = vehicle; |
| 27 | self.isReverseDriving = isReverseDriving; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function ReverseDrivingSetStateEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId); |
| 37 | self.isReverseDriving = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function ReverseDrivingSetStateEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle); |
| 47 | streamWriteBool(streamId, self.isReverseDriving); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function ReverseDrivingSetStateEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.vehicle); |
| 56 | end; |
| 57 | if self.vehicle ~= nil then |
| 58 | self.vehicle:setIsReverseDriving(self.isReverseDriving, true); |
| 59 | end; |
| 60 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isReverseDriving, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isReverseDriving | is reverse driving |
| boolean | noEventSend | no event send |
| 67 | function ReverseDrivingSetStateEvent.sendEvent(vehicle, isReverseDriving, noEventSend) |
| 68 | if isReverseDriving ~= vehicle.isReverseDriving then |
| 69 | if noEventSend == nil or noEventSend == false then |
| 70 | if g_server ~= nil then |
| 71 | g_server:broadcastEvent(ReverseDrivingSetStateEvent:new(vehicle, isReverseDriving), nil, nil, vehicle); |
| 72 | else |
| 73 | g_client:getServerConnection():sendEvent(ReverseDrivingSetStateEvent:new(vehicle, isReverseDriving)); |
| 74 | end; |
| 75 | end; |
| 76 | end; |
| 77 | end; |
Event for ridge marker state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function RidgeMarkerSetStateEvent:emptyNew() |
| 16 | local self = Event:new(RidgeMarkerSetStateEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table vehicle, boolean state)Arguments
| table | vehicle | vehicle |
| boolean | state | state |
| 24 | function RidgeMarkerSetStateEvent:new(vehicle, state) |
| 25 | local self = RidgeMarkerSetStateEvent:emptyNew() |
| 26 | self.vehicle = vehicle |
| 27 | self.state = state |
| 28 | assert(state >= 0 and state < RidgeMarker.MAX_NUM_RIDGEMARKERS) |
| 29 | return self |
| 30 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 36 | function RidgeMarkerSetStateEvent:readStream(streamId, connection) |
| 37 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 38 | self.state = streamReadUIntN(streamId, RidgeMarker.SEND_NUM_BITS) |
| 39 | self:run(connection) |
| 40 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 46 | function RidgeMarkerSetStateEvent:writeStream(streamId, connection) |
| 47 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 48 | streamWriteUIntN(streamId, self.state, RidgeMarker.SEND_NUM_BITS) |
| 49 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 54 | function RidgeMarkerSetStateEvent:run(connection) |
| 55 | self.vehicle:setRidgeMarkerState(self.state, true) |
| 56 | if not connection:getIsServer() then |
| 57 | g_server:broadcastEvent(RidgeMarkerSetStateEvent:new(self.vehicle, self.state), nil, connection, self.vehicle) |
| 58 | end |
| 59 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, integer state, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| integer | state | discharge state |
| boolean | noEventSend | no event send |
| 66 | function RidgeMarkerSetStateEvent.sendEvent(vehicle, state, noEventSend) |
| 67 | if noEventSend == nil or noEventSend == false then |
| 68 | if g_server ~= nil then |
| 69 | g_server:broadcastEvent(RidgeMarkerSetStateEvent:new(vehicle, state), nil, nil, self) |
| 70 | else |
| 71 | g_client:getServerConnection():sendEvent(RidgeMarkerSetStateEvent:new(vehicle, state)) |
| 72 | end |
| 73 | end |
| 74 | end |
Event for cover state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetCoverStateEvent:emptyNew() |
| 16 | return Event:new(SetCoverStateEvent_mt) |
| 17 | end |
Create new instance of eventDefinition
new(table vehicle, integer state)Arguments
| table | vehicle | vehicle |
| integer | state | cover state |
| 23 | function SetCoverStateEvent:new(vehicle, state) |
| 24 | local self = SetCoverStateEvent:emptyNew() |
| 25 | |
| 26 | self.vehicle = vehicle |
| 27 | self.state = state |
| 28 | |
| 29 | return self |
| 30 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 36 | function SetCoverStateEvent:readStream(streamId, connection) |
| 37 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 38 | self.state = streamReadUIntN(streamId, Cover.SEND_NUM_BITS) |
| 39 | self:run(connection) |
| 40 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 46 | function SetCoverStateEvent:writeStream(streamId, connection) |
| 47 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 48 | streamWriteUIntN(streamId, self.state, Cover.SEND_NUM_BITS) |
| 49 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 54 | function SetCoverStateEvent:run(connection) |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(self, false, connection, self.vehicle) |
| 57 | end |
| 58 | if self.vehicle ~= nil then |
| 59 | self.vehicle:setCoverState(self.state, true) |
| 60 | end |
| 61 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, integer state, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| integer | state | cover state |
| boolean | noEventSend | no event send |
| 68 | function SetCoverStateEvent.sendEvent(vehicle, state, noEventSend) |
| 69 | if vehicle.spec_cover.state ~= state then |
| 70 | if noEventSend == nil or noEventSend == false then |
| 71 | if g_server ~= nil then |
| 72 | g_server:broadcastEvent(SetCoverStateEvent:new(vehicle, state), nil, nil, vehicle) |
| 73 | else |
| 74 | g_client:getServerConnection():sendEvent(SetCoverStateEvent:new(vehicle, state)) |
| 75 | end |
| 76 | end |
| 77 | end |
| 78 | end |
Event for steering mode
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetCrabSteeringEvent:emptyNew() |
| 16 | local self = Event:new(SetCrabSteeringEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer state)Arguments
| table | object | object |
| integer | state | state |
| 24 | function SetCrabSteeringEvent:new(vehicle, state) |
| 25 | local self = SetCrabSteeringEvent:emptyNew() |
| 26 | self.vehicle = vehicle; |
| 27 | self.state = state; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetCrabSteeringEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId); |
| 37 | self.state = streamReadUIntN(streamId, CrabSteering.STEERING_SEND_NUM_BITS); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetCrabSteeringEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle); |
| 47 | streamWriteUIntN(streamId, self.state, CrabSteering.STEERING_SEND_NUM_BITS); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetCrabSteeringEvent:run(connection) |
| 54 | self.vehicle:setCrabSteering(self.state, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(SetCrabSteeringEvent:new(self.vehicle, self.state), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, integer state, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| integer | state | state |
| boolean | noEventSend | no event send |
| 65 | function SetCrabSteeringEvent.sendEvent(vehicle, state, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(SetCrabSteeringEvent:new(vehicle, state), nil, nil, vehicle); |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(SetCrabSteeringEvent:new(vehicle, state)); |
| 71 | end; |
| 72 | end; |
| 73 | end; |
Event for cruise control speed
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetCruiseControlSpeedEvent:emptyNew() |
| 16 | local self = Event:new(SetCruiseControlSpeedEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table vehicle, float speed)Arguments
| table | vehicle | vehicle |
| float | speed | speed |
| 24 | function SetCruiseControlSpeedEvent:new(vehicle, speed) |
| 25 | local self = SetCruiseControlSpeedEvent:emptyNew() |
| 26 | self.speed = speed; |
| 27 | self.vehicle = vehicle; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetCruiseControlSpeedEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId); |
| 37 | self.speed = streamReadUInt8(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetCruiseControlSpeedEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle); |
| 47 | streamWriteUInt8(streamId, self.speed); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetCruiseControlSpeedEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.vehicle); |
| 56 | end; |
| 57 | self.vehicle:setCruiseControlMaxSpeed(self.speed); |
| 58 | end; |
Event for cruise control state event
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetCruiseControlStateEvent:emptyNew() |
| 16 | local self = Event:new(SetCruiseControlStateEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table vehicle, integer state)Arguments
| table | vehicle | vehicle |
| integer | state | state |
| 24 | function SetCruiseControlStateEvent:new(vehicle, state) |
| 25 | local self = SetCruiseControlStateEvent:emptyNew() |
| 26 | self.state = state; |
| 27 | self.vehicle = vehicle; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetCruiseControlStateEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId); |
| 37 | self.state = streamReadUIntN(streamId, 2); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetCruiseControlStateEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle); |
| 47 | streamWriteUIntN(streamId, self.state, 2); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetCruiseControlStateEvent:run(connection) |
| 54 | self.vehicle:setCruiseControlState(self.state, true); |
| 55 | end; |
Event for dicharge state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetDischargeStateEvent:emptyNew() |
| 16 | local self = Event:new(SetDischargeStateEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table vehicle, integer state)Arguments
| table | vehicle | vehicle |
| integer | state | discharge state |
| 24 | function SetDischargeStateEvent:new(vehicle, state) |
| 25 | local self = SetDischargeStateEvent:emptyNew() |
| 26 | self.vehicle = vehicle |
| 27 | self.state = state |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetDischargeStateEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 37 | self.state = streamReadUIntN(streamId, 2) |
| 38 | self:run(connection) |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetDischargeStateEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 47 | streamWriteUIntN(streamId, self.state, 2) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetDischargeStateEvent:run(connection) |
| 54 | self.vehicle:setDischargeState(self.state, true) |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(SetDischargeStateEvent:new(self.vehicle, self.state), nil, connection, self.vehicle) |
| 57 | end |
| 58 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, integer state, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| integer | state | discharge state |
| boolean | noEventSend | no event send |
| 65 | function SetDischargeStateEvent.sendEvent(vehicle, state, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(SetDischargeStateEvent:new(vehicle, state), nil, nil, vehicle) |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(SetDischargeStateEvent:new(vehicle, state)) |
| 71 | end |
| 72 | end |
| 73 | end |
Event for toggle filling
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetFillUnitIsFillingEvent:emptyNew() |
| 16 | local self = Event:new(SetFillUnitIsFillingEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table vehicle, boolean isFilling)Arguments
| table | vehicle | vehicle |
| boolean | isFilling | is filling state |
| 24 | function SetFillUnitIsFillingEvent:new(vehicle, isFilling) |
| 25 | local self = SetFillUnitIsFillingEvent:emptyNew() |
| 26 | self.vehicle = vehicle |
| 27 | self.isFilling = isFilling |
| 28 | return self |
| 29 | end |
Called on client sideDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetFillUnitIsFillingEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 37 | self.isFilling = streamReadBool(streamId) |
| 38 | self:run(connection) |
| 39 | end |
Called on server sideDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetFillUnitIsFillingEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 47 | streamWriteBool(streamId, self.isFilling) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetFillUnitIsFillingEvent:run(connection) |
| 54 | self.vehicle:setFillUnitIsFilling(self.isFilling, true) |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(SetFillUnitIsFillingEvent:new(self.vehicle, self.isFilling), nil, connection, self.vehicle) |
| 57 | end |
| 58 | end |
Event for motor turned on state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetMotorTurnedOnEvent:emptyNew() |
| 16 | local self = Event:new(SetMotorTurnedOnEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean turnedOn)Arguments
| table | object | object |
| boolean | turnedOn | is turned on |
| 24 | function SetMotorTurnedOnEvent:new(object, turnedOn) |
| 25 | local self = SetMotorTurnedOnEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.turnedOn = turnedOn; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetMotorTurnedOnEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.turnedOn = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetMotorTurnedOnEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.turnedOn); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetMotorTurnedOnEvent:run(connection) |
| 54 | if self.turnedOn then |
| 55 | self.object:startMotor(true); |
| 56 | else |
| 57 | self.object:stopMotor(true); |
| 58 | end; |
| 59 | if not connection:getIsServer() then |
| 60 | g_server:broadcastEvent(SetMotorTurnedOnEvent:new(self.object, self.turnedOn), nil, connection, self.object); |
| 61 | end; |
| 62 | end; |
Event for pipe state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetPipeStateEvent:emptyNew() |
| 16 | local self = Event:new(SetPipeStateEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer pipeState)Arguments
| table | object | object |
| integer | pipeState | pipe state |
| 24 | function SetPipeStateEvent:new(object, pipeState) |
| 25 | local self = SetPipeStateEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.pipeState = pipeState; |
| 28 | assert(self.pipeState >= 0 and self.pipeState < 8); |
| 29 | return self; |
| 30 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 36 | function SetPipeStateEvent:readStream(streamId, connection) |
| 37 | self.object = NetworkUtil.readNodeObject(streamId); |
| 38 | self.pipeState = streamReadUIntN(streamId, 3); |
| 39 | self:run(connection); |
| 40 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 46 | function SetPipeStateEvent:writeStream(streamId, connection) |
| 47 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 48 | streamWriteUIntN(streamId, self.pipeState, 3); |
| 49 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 54 | function SetPipeStateEvent:run(connection) |
| 55 | self.object:setPipeState(self.pipeState, true); |
| 56 | if not connection:getIsServer() then |
| 57 | g_server:broadcastEvent(SetPipeStateEvent:new(self.object, self.pipeState), nil, connection, self.object); |
| 58 | end; |
| 59 | end; |
Set seed index event
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetSeedIndexEvent:emptyNew() |
| 16 | local self = Event:new(SetSeedIndexEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, integer seedIndex)Arguments
| table | object | object |
| integer | seedIndex | index of seed |
| 24 | function SetSeedIndexEvent:new(object, seedIndex) |
| 25 | local self = SetSeedIndexEvent:emptyNew() |
| 26 | self.object = object |
| 27 | self.seedIndex = seedIndex |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetSeedIndexEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId) |
| 37 | self.seedIndex = streamReadUInt8(streamId) |
| 38 | self:run(connection) |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetSeedIndexEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 47 | streamWriteUInt8(streamId, self.seedIndex) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetSeedIndexEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.object) |
| 56 | end |
| 57 | self.object:setSeedIndex(self.seedIndex, true) |
| 58 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, integer seedIndex, boolean noEventSend)Arguments
| table | object | object |
| integer | seedIndex | index of seed |
| boolean | noEventSend | no event send |
| 65 | function SetSeedIndexEvent.sendEvent(object, seedIndex, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(SetSeedIndexEvent:new(object, seedIndex), nil, nil, object) |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(SetSeedIndexEvent:new(object, seedIndex)) |
| 71 | end |
| 72 | end |
| 73 | end |
Event for turned on state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetTurnedOnEvent:emptyNew() |
| 16 | local self = Event:new(SetTurnedOnEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean isTurnedOn)Arguments
| table | object | object |
| boolean | isTurnedOn | is turned on state |
| 24 | function SetTurnedOnEvent:new(object, isTurnedOn) |
| 25 | local self = SetTurnedOnEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.isTurnedOn = isTurnedOn; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetTurnedOnEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.isTurnedOn = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetTurnedOnEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.isTurnedOn); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetTurnedOnEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.object); |
| 56 | end; |
| 57 | if self.object ~= nil then |
| 58 | self.object:setIsTurnedOn(self.isTurnedOn, true); |
| 59 | end; |
| 60 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, boolean isTurnedOn, boolean noEventSend)Arguments
| table | object | object |
| boolean | isTurnedOn | is turned on state |
| boolean | noEventSend | no event send |
| 67 | function SetTurnedOnEvent.sendEvent(vehicle, isTurnedOn, noEventSend) |
| 68 | if noEventSend == nil or noEventSend == false then |
| 69 | if g_server ~= nil then |
| 70 | g_server:broadcastEvent(SetTurnedOnEvent:new(vehicle, isTurnedOn), nil, nil, vehicle); |
| 71 | else |
| 72 | g_client:getServerConnection():sendEvent(SetTurnedOnEvent:new(vehicle, isTurnedOn)); |
| 73 | end; |
| 74 | end; |
| 75 | end; |
Event for work modes
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function SetWorkModeEvent:emptyNew() |
| 16 | local self = Event:new(SetWorkModeEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, integer state)Arguments
| table | object | object |
| integer | state | state |
| 24 | function SetWorkModeEvent:new(vehicle, state) |
| 25 | local self = SetWorkModeEvent:emptyNew() |
| 26 | self.vehicle = vehicle |
| 27 | self.state = state |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function SetWorkModeEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 37 | self.state = streamReadUIntN(streamId, WorkMode.WORKMODE_SEND_NUM_BITS) |
| 38 | self:run(connection) |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function SetWorkModeEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 47 | streamWriteUIntN(streamId, self.state, WorkMode.WORKMODE_SEND_NUM_BITS) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function SetWorkModeEvent:run(connection) |
| 54 | self.vehicle:setWorkMode(self.state, true) |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(SetWorkModeEvent:new(self.vehicle, self.state), nil, connection, self.object) |
| 57 | end |
| 58 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, integer state, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| integer | state | state |
| boolean | noEventSend | no event send |
| 65 | function SetWorkModeEvent.sendEvent(vehicle, state, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(SetWorkModeEvent:new(vehicle, state), nil, nil, vehicle) |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(SetWorkModeEvent:new(vehicle, state)) |
| 71 | end |
| 72 | end |
| 73 | end |
Event for tension belts state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function TensionBeltsEvent:emptyNew() |
| 16 | local self = Event:new(TensionBeltsEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, boolean isActive, integer beltId)Arguments
| table | object | object |
| boolean | isActive | belt is active |
| integer | beltId | id of belt |
| 25 | function TensionBeltsEvent:new(object, isActive, beltId) |
| 26 | local self = TensionBeltsEvent:emptyNew() |
| 27 | self.object = object |
| 28 | self.isActive = isActive |
| 29 | self.beltId = beltId |
| 30 | return self |
| 31 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 37 | function TensionBeltsEvent:readStream(streamId, connection) |
| 38 | self.object = NetworkUtil.readNodeObject(streamId); |
| 39 | if not streamReadBool(streamId) then |
| 40 | self.beltId = streamReadUIntN(streamId, TensionBelts.NUM_SEND_BITS)+1 |
| 41 | end |
| 42 | self.isActive = streamReadBool(streamId) |
| 43 | self:run(connection) |
| 44 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 50 | function TensionBeltsEvent:writeStream(streamId, connection) |
| 51 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 52 | streamWriteBool(streamId, self.beltId == nil) |
| 53 | if self.beltId ~= nil then |
| 54 | streamWriteUIntN(streamId, self.beltId-1, TensionBelts.NUM_SEND_BITS) |
| 55 | end |
| 56 | streamWriteBool(streamId, self.isActive) |
| 57 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 62 | function TensionBeltsEvent:run(connection) |
| 63 | if not connection:getIsServer() then |
| 64 | g_server:broadcastEvent(self, false, connection, self.object) |
| 65 | end |
| 66 | self.object:setTensionBeltsActive(self.isActive, self.beltId, true) |
| 67 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isActive, integer beltId, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isActive | belt is active |
| integer | beltId | id of belt |
| boolean | noEventSend | no event send |
| 75 | function TensionBeltsEvent.sendEvent(vehicle, isActive, beltId, noEventSend) |
| 76 | if noEventSend == nil or noEventSend == false then |
| 77 | if g_server ~= nil then |
| 78 | g_server:broadcastEvent(TensionBeltsEvent:new(vehicle, isActive, beltId), nil, nil, vehicle) |
| 79 | else |
| 80 | g_client:getServerConnection():sendEvent(TensionBeltsEvent:new(vehicle, isActive, beltId)) |
| 81 | end |
| 82 | end |
| 83 | end |
Event for tension belts state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function TensionBeltsRefreshEvent:emptyNew() |
| 16 | local self = Event:new(TensionBeltsRefreshEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, boolean isActive, integer beltId)Arguments
| table | object | object |
| boolean | isActive | belt is active |
| integer | beltId | id of belt |
| 25 | function TensionBeltsRefreshEvent:new(object) |
| 26 | local self = TensionBeltsRefreshEvent:emptyNew() |
| 27 | self.object = object |
| 28 | return self |
| 29 | end |
Called on client sideDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function TensionBeltsRefreshEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self:run(connection) |
| 38 | end |
Called on server sideDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 44 | function TensionBeltsRefreshEvent:writeStream(streamId, connection) |
| 45 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 46 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 51 | function TensionBeltsRefreshEvent:run(connection) |
| 52 | if not connection:getIsServer() then |
| 53 | g_server:broadcastEvent(self, false, connection, self.object) |
| 54 | end |
| 55 | self.object:refreshTensionBelts() |
| 56 | end |
Event for toggle trailer tipping
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function TrailerToggleTipEvent:emptyNew() |
| 16 | local self = Event:new(TrailerToggleTipEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean isStart, table tipTrigger, integer tipSideIndex)Arguments
| table | object | object |
| boolean | isStart | is start |
| table | tipTrigger | tip trigger |
| integer | tipSideIndex | index of tip side |
| 26 | function TrailerToggleTipEvent:new(object, isStart, tipSideIndex) |
| 27 | local self = TrailerToggleTipEvent:emptyNew() |
| 28 | self.isStart = isStart; |
| 29 | self.tipSideIndex = Utils.getNoNil(tipSideIndex, 1); |
| 30 | assert(self.tipSideIndex <= 15); |
| 31 | self.object = object; |
| 32 | return self; |
| 33 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 39 | function TrailerToggleTipEvent:readStream(streamId, connection) |
| 40 | self.object = NetworkUtil.readNodeObject(streamId); |
| 41 | self.isStart = streamReadBool(streamId); |
| 42 | if self.isStart then |
| 43 | self.tipSideIndex = streamReadUIntN(streamId, 4); |
| 44 | end; |
| 45 | self:run(connection); |
| 46 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 52 | function TrailerToggleTipEvent:writeStream(streamId, connection) |
| 53 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 54 | streamWriteBool(streamId, self.isStart); |
| 55 | if self.isStart then |
| 56 | streamWriteUIntN(streamId, self.tipSideIndex, 4); |
| 57 | end; |
| 58 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 63 | function TrailerToggleTipEvent:run(connection) |
| 64 | if not connection:getIsServer() then |
| 65 | g_server:broadcastEvent(self, false, connection, self.object); |
| 66 | end; |
| 67 | |
| 68 | if self.isStart then |
| 69 | self.object:startTipping(self.tipSideIndex, true); |
| 70 | else |
| 71 | self.object:stopTipping(true); |
| 72 | end; |
| 73 | end; |
Event for loading of pallet on tree planter
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function TreePlanterLoadPalletEvent:emptyNew() |
| 16 | local self = Event:new(TreePlanterLoadPalletEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer palletObjectId)Arguments
| table | object | object |
| integer | palletObjectId | object id of pallet |
| 24 | function TreePlanterLoadPalletEvent:new(object, palletObjectId) |
| 25 | local self = TreePlanterLoadPalletEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.palletObjectId = palletObjectId; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function TreePlanterLoadPalletEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.palletObjectId = NetworkUtil.readNodeObjectId(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function TreePlanterLoadPalletEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | NetworkUtil.writeNodeObjectId(streamId, self.palletObjectId); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function TreePlanterLoadPalletEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.object); |
| 56 | end; |
| 57 | self.object:loadPallet(self.palletObjectId, true); |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, integer palletObjectId, boolean noEventSend)Arguments
| table | object | object |
| integer | palletObjectId | object id of pallet |
| boolean | noEventSend | no event send |
| 65 | function TreePlanterLoadPalletEvent.sendEvent(object, palletObjectId, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(TreePlanterLoadPalletEvent:new(object, palletObjectId), nil, nil, object); |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(TreePlanterLoadPalletEvent:new(object, palletObjectId)); |
| 71 | end; |
| 72 | end; |
| 73 | end; |
Event for attaching
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleAttachEvent:emptyNew() |
| 15 | local self = Event:new(VehicleAttachEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table vehicle, table implement, integer inputJointIndex, integer jointIndex, boolean startLowered)Arguments
| table | vehicle | vehicle |
| table | implement | implement |
| integer | inputJointIndex | index of input attacher joint |
| integer | jointIndex | index of attacher joint |
| boolean | startLowered | start in lowered state |
| table | instance | instance of event |
| 27 | function VehicleAttachEvent:new(vehicle, implement, inputJointIndex, jointIndex, startLowered) |
| 28 | local self = VehicleAttachEvent:emptyNew() |
| 29 | self.jointIndex = jointIndex |
| 30 | self.inputJointIndex = inputJointIndex |
| 31 | self.vehicle = vehicle |
| 32 | self.implement = implement |
| 33 | self.startLowered = startLowered |
| 34 | assert(self.jointIndex >= 0 and self.jointIndex < 127) |
| 35 | return self |
| 36 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 42 | function VehicleAttachEvent:readStream(streamId, connection) |
| 43 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 44 | self.implement = NetworkUtil.readNodeObject(streamId) |
| 45 | self.jointIndex = streamReadUIntN(streamId, 7) |
| 46 | self.inputJointIndex = streamReadUIntN(streamId, 7) |
| 47 | self.startLowered = streamReadBool(streamId) |
| 48 | self:run(connection) |
| 49 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 55 | function VehicleAttachEvent:writeStream(streamId, connection) |
| 56 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 57 | NetworkUtil.writeNodeObject(streamId, self.implement) |
| 58 | streamWriteUIntN(streamId, self.jointIndex, 7) |
| 59 | streamWriteUIntN(streamId, self.inputJointIndex, 7) |
| 60 | streamWriteBool(streamId, self.startLowered) |
| 61 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 66 | function VehicleAttachEvent:run(connection) |
| 67 | self.vehicle:attachImplement(self.implement, self.inputJointIndex, self.jointIndex, true, nil, self.startLowered) |
| 68 | if not connection:getIsServer() then |
| 69 | g_server:broadcastEvent(self, nil, connection, self.object) |
| 70 | end |
| 71 | end |
Event for enter request
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleBrokenEvent:emptyNew() |
| 15 | local self = Event:new(VehicleBrokenEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table object, table playerStyle)Arguments
| table | object | object |
| table | playerStyle | info |
| table | instance | instance of event |
| 24 | function VehicleBrokenEvent:new(object) |
| 25 | local self = VehicleBrokenEvent:emptyNew() |
| 26 | self.object = object |
| 27 | return self |
| 28 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 34 | function VehicleBrokenEvent:readStream(streamId, connection) |
| 35 | self.object = NetworkUtil.readNodeObject(streamId) |
| 36 | self:run(connection) |
| 37 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 43 | function VehicleBrokenEvent:writeStream(streamId, connection) |
| 44 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 45 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 50 | function VehicleBrokenEvent:run(connection) |
| 51 | if self.object ~= nil then |
| 52 | self.object:setBroken() |
| 53 | end |
| 54 | end |
Event for bundle attaching
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleBundleAttachEvent:emptyNew() |
| 15 | local self = Event:new(VehicleBundleAttachEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table bundles)Arguments
| table | bundles | bundles |
| table | instance | instance of event |
| 23 | function VehicleBundleAttachEvent:new(bundles) |
| 24 | local self = VehicleBundleAttachEvent:emptyNew() |
| 25 | self.bundles = bundles |
| 26 | return self |
| 27 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 33 | function VehicleBundleAttachEvent:readStream(streamId, connection) |
| 34 | local numBundles = streamReadUInt8(streamId) |
| 35 | for i=1, numBundles do |
| 36 | local v1 = NetworkUtil.readNodeObjectId(streamId) |
| 37 | local v2 = NetworkUtil.readNodeObjectId(streamId) |
| 38 | local inputJointIndex = streamReadUIntN(streamId, 7) |
| 39 | local jointIndex = streamReadUIntN(streamId, 7) |
| 40 | table.insert(g_currentMission.vehiclesToAttach, {v1id=v1, v2id=v2, inputJointIndex=inputJointIndex, jointIndex=jointIndex}) |
| 41 | end |
| 42 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 48 | function VehicleBundleAttachEvent:writeStream(streamId, connection) |
| 49 | streamWriteUInt8(streamId, #self.bundles) |
| 50 | for i=1, #self.bundles do |
| 51 | local bundle = self.bundles[i] |
| 52 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(bundle.v1)) |
| 53 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(bundle.v2)) |
| 54 | streamWriteUIntN(streamId, bundle.input, 7) |
| 55 | streamWriteUIntN(streamId, bundle.attacher, 7) |
| 56 | end |
| 57 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 62 | function VehicleBundleAttachEvent:run(connection) |
| 63 | self.vehicle:attachImplement(self.implement, self.inputJointIndex, self.jointIndex, true, nil, self.startLowered) |
| 64 | if not connection:getIsServer() then |
| 65 | g_server:broadcastEvent(self, nil, connection, self.object) |
| 66 | end |
| 67 | end |
Event for detaching
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleDetachEvent:emptyNew() |
| 15 | local self = Event:new(VehicleDetachEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table vehicle, table implement)Arguments
| table | vehicle | vehicle |
| table | implement | implement |
| table | instance | instance of event |
| 24 | function VehicleDetachEvent:new(vehicle, implement) |
| 25 | local self = VehicleDetachEvent:emptyNew() |
| 26 | self.implement = implement |
| 27 | self.vehicle = vehicle |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function VehicleDetachEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 37 | self.implement = NetworkUtil.readNodeObject(streamId) |
| 38 | if connection:getIsServer() then |
| 39 | self.vehicle:detachImplementByObject(self.implement, true) |
| 40 | else |
| 41 | self.vehicle:detachImplementByObject(self.implement) |
| 42 | end |
| 43 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 49 | function VehicleDetachEvent:writeStream(streamId, connection) |
| 50 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 51 | NetworkUtil.writeNodeObject(streamId, self.implement) |
| 52 | end |
Event for enter request
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleEnterRequestEvent:emptyNew() |
| 15 | local self = Event:new(VehicleEnterRequestEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table object, table playerStyle)Arguments
| table | object | object |
| table | playerStyle | info |
| table | instance | instance of event |
| 24 | function VehicleEnterRequestEvent:new(object, playerStyle, farmId) |
| 25 | local self = VehicleEnterRequestEvent:emptyNew() |
| 26 | self.object = object |
| 27 | self.objectId = NetworkUtil.getObjectId(self.object) |
| 28 | self.farmId = farmId |
| 29 | self.playerStyle = playerStyle |
| 30 | return self |
| 31 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 37 | function VehicleEnterRequestEvent:readStream(streamId, connection) |
| 38 | self.objectId = NetworkUtil.readNodeObjectId(streamId) |
| 39 | self.farmId = streamReadUIntN(streamId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 40 | if self.playerStyle == nil then |
| 41 | self.playerStyle = PlayerStyle:new() |
| 42 | end |
| 43 | self.playerStyle:readStream(streamId, connection) |
| 44 | self.object = NetworkUtil.getObject(self.objectId) |
| 45 | self:run(connection) |
| 46 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 52 | function VehicleEnterRequestEvent:writeStream(streamId, connection) |
| 53 | NetworkUtil.writeNodeObjectId(streamId, self.objectId) |
| 54 | streamWriteUIntN(streamId, self.farmId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 55 | self.playerStyle:writeStream(streamId, connection) |
| 56 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 61 | function VehicleEnterRequestEvent:run(connection) |
| 62 | --if self.object.isControlled == false then |
| 63 | local enterableSpec = self.object.spec_enterable |
| 64 | if self.object ~= nil and enterableSpec ~= nil and enterableSpec.isControlled == false then |
| 65 | self.object:setOwner(connection) |
| 66 | self.object.controllerFarmId = self.farmId |
| 67 | g_server:broadcastEvent(VehicleEnterResponseEvent:new(self.objectId, false, self.playerStyle, self.farmId), true, connection, self.object) |
| 68 | connection:sendEvent(VehicleEnterResponseEvent:new(self.objectId, true, self.playerStyle, self.farmId)) |
| 69 | end |
| 70 | end |
Event for enter response
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleEnterResponseEvent:emptyNew() |
| 15 | local self = Event:new(VehicleEnterResponseEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table id, boolean isOwner, table playerStyle)Arguments
| table | id | id |
| boolean | isOwner | is owner |
| table | playerStyle |
| table | instance | instance of event |
| 25 | function VehicleEnterResponseEvent:new(id, isOwner, playerStyle, farmId) |
| 26 | local self = VehicleEnterResponseEvent:emptyNew() |
| 27 | self.id = id |
| 28 | self.isOwner = isOwner |
| 29 | self.playerStyle = playerStyle |
| 30 | self.farmId = farmId |
| 31 | return self |
| 32 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 38 | function VehicleEnterResponseEvent:readStream(streamId, connection) |
| 39 | self.id = NetworkUtil.readNodeObjectId(streamId) |
| 40 | self.isOwner = streamReadBool(streamId) |
| 41 | if self.playerStyle == nil then |
| 42 | self.playerStyle = PlayerStyle:new() |
| 43 | end |
| 44 | self.playerStyle:readStream(streamId, connection) |
| 45 | self.farmId = streamReadUIntN(streamId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 46 | self:run(connection) |
| 47 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 53 | function VehicleEnterResponseEvent:writeStream(streamId, connection) |
| 54 | NetworkUtil.writeNodeObjectId(streamId, self.id) |
| 55 | streamWriteBool(streamId, self.isOwner) |
| 56 | self.playerStyle:writeStream(streamId, connection) |
| 57 | streamWriteUIntN(streamId, self.farmId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 58 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 63 | function VehicleEnterResponseEvent:run(connection) |
| 64 | local object = NetworkUtil.getObject(self.id) |
| 65 | if self.isOwner then |
| 66 | g_currentMission:onEnterVehicle(object, self.playerStyle, self.farmId) |
| 67 | else |
| 68 | local enterableSpec = object.spec_enterable |
| 69 | if enterableSpec ~= nil then |
| 70 | if not enterableSpec.isEntered then |
| 71 | object:enterVehicle(false, self.playerStyle, self.farmId) |
| 72 | end |
| 73 | end |
| 74 | end |
| 75 | end |
Event for leaving
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleLeaveEvent:emptyNew() |
| 15 | local self = Event:new(VehicleLeaveEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table object)Arguments
| table | object | object |
| table | instance | instance of event |
| 23 | function VehicleLeaveEvent:new(object) |
| 24 | local self = VehicleLeaveEvent:emptyNew() |
| 25 | self.object = object |
| 26 | return self |
| 27 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 33 | function VehicleLeaveEvent:readStream(streamId, connection) |
| 34 | self.object = NetworkUtil.readNodeObject(streamId) |
| 35 | self:run(connection) |
| 36 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 42 | function VehicleLeaveEvent:writeStream(streamId, connection) |
| 43 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 44 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 49 | function VehicleLeaveEvent:run(connection) |
| 50 | if not connection:getIsServer() then |
| 51 | if self.object.owner ~= nil then |
| 52 | self.object:setOwner(nil) |
| 53 | self.object.controllerFarmId = nil |
| 54 | end |
| 55 | g_server:broadcastEvent(VehicleLeaveEvent:new(self.object), nil, connection, self.object) |
| 56 | end |
| 57 | self.object:leaveVehicle() |
| 58 | end |
Event for lowering implement
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 14 | function VehicleLowerImplementEvent:emptyNew() |
| 15 | local self = Event:new(VehicleLowerImplementEvent_mt) |
| 16 | return self |
| 17 | end |
Create new instance of eventDefinition
new(table vehicle, integer jointIndex, boolean moveDown)Arguments
| table | vehicle | vehicle |
| integer | jointIndex | index of joint |
| boolean | moveDown | move down |
| table | instance | instance of event |
| 25 | function VehicleLowerImplementEvent:new(vehicle, jointIndex, moveDown) |
| 26 | local self = VehicleLowerImplementEvent:emptyNew() |
| 27 | self.jointIndex = jointIndex |
| 28 | self.vehicle = vehicle |
| 29 | self.moveDown = moveDown |
| 30 | return self |
| 31 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 37 | function VehicleLowerImplementEvent:readStream(streamId, connection) |
| 38 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 39 | self.jointIndex = streamReadInt8(streamId) |
| 40 | self.moveDown = streamReadBool(streamId) |
| 41 | self:run(connection) |
| 42 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 48 | function VehicleLowerImplementEvent:writeStream(streamId, connection) |
| 49 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 50 | streamWriteInt8(streamId, self.jointIndex) |
| 51 | streamWriteBool(streamId, self.moveDown) |
| 52 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 57 | function VehicleLowerImplementEvent:run(connection) |
| 58 | self.vehicle:setJointMoveDown(self.jointIndex, self.moveDown, true) |
| 59 | if not connection:getIsServer() then |
| 60 | g_server:broadcastEvent(VehicleLowerImplementEvent:new(self.vehicle, self.jointIndex, self.moveDown), nil, connection, self.object) |
| 61 | end |
| 62 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, integer jointIndex, boolean moveDown, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| integer | jointIndex | index of joint |
| boolean | moveDown | move down |
| boolean | noEventSend | no event send |
| 70 | function VehicleLowerImplementEvent.sendEvent(vehicle, jointIndex, moveDown, noEventSend) |
| 71 | if noEventSend == nil or noEventSend == false then |
| 72 | if g_server ~= nil then |
| 73 | g_server:broadcastEvent(VehicleLowerImplementEvent:new(vehicle, jointIndex, moveDown), nil, nil, vehicle) |
| 74 | else |
| 75 | g_client:getServerConnection():sendEvent(VehicleLowerImplementEvent:new(vehicle, jointIndex, moveDown)) |
| 76 | end |
| 77 | end |
| 78 | end |
Event for beacon light state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function VehicleSetBeaconLightEvent:emptyNew() |
| 16 | local self = Event:new(VehicleSetBeaconLightEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean active)Arguments
| table | object | object |
| boolean | active | active |
| 24 | function VehicleSetBeaconLightEvent:new(object, active) |
| 25 | local self = VehicleSetBeaconLightEvent:emptyNew() |
| 26 | self.active = active; |
| 27 | self.object = object; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function VehicleSetBeaconLightEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.active = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function VehicleSetBeaconLightEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.active); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function VehicleSetBeaconLightEvent:run(connection) |
| 54 | self.object:setBeaconLightsVisibility(self.active, true, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(VehicleSetBeaconLightEvent:new(self.object, self.active), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Event for light state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function VehicleSetLightEvent:emptyNew() |
| 16 | local self = Event:new(VehicleSetLightEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer lightsTypesMask)Arguments
| table | object | object |
| integer | lightsTypesMask | light types mask |
| 24 | function VehicleSetLightEvent:new(object, lightsTypesMask) |
| 25 | local self = VehicleSetLightEvent:emptyNew() |
| 26 | self.lightsTypesMask = lightsTypesMask; |
| 27 | self.object = object; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function VehicleSetLightEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.lightsTypesMask = streamReadInt32(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function VehicleSetLightEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteInt32(streamId, self.lightsTypesMask); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function VehicleSetLightEvent:run(connection) |
| 54 | self.object:setLightsTypesMask(self.lightsTypesMask, true, true); |
| 55 | if not connection:getIsServer() then |
| 56 | g_server:broadcastEvent(VehicleSetLightEvent:new(self.object, self.lightsTypesMask), nil, connection, self.object); |
| 57 | end; |
| 58 | end; |
Event for turn light state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function VehicleSetTurnLightEvent:emptyNew() |
| 16 | local self = Event:new(VehicleSetTurnLightEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, integer state)Arguments
| table | object | object |
| integer | state | state |
| 24 | function VehicleSetTurnLightEvent:new(object, state) |
| 25 | local self = VehicleSetTurnLightEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.state = state; |
| 28 | assert(state >= 0 and state <= Lights.TURNLIGHT_HAZARD); |
| 29 | return self; |
| 30 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 36 | function VehicleSetTurnLightEvent:readStream(streamId, connection) |
| 37 | self.object = NetworkUtil.readNodeObject(streamId); |
| 38 | self.state = streamReadUIntN(streamId, Lights.turnLightSendNumBits); |
| 39 | self:run(connection); |
| 40 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 46 | function VehicleSetTurnLightEvent:writeStream(streamId, connection) |
| 47 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 48 | streamWriteUIntN(streamId, self.state, Lights.turnLightSendNumBits); |
| 49 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 54 | function VehicleSetTurnLightEvent:run(connection) |
| 55 | self.object:setTurnLightState(self.state, true, true); |
| 56 | if not connection:getIsServer() then |
| 57 | g_server:broadcastEvent(VehicleSetTurnLightEvent:new(self.object, self.state), nil, connection, self.object); |
| 58 | end; |
| 59 | end; |
Event for water trailer filling
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function WaterTrailerSetIsFillingEvent:emptyNew() |
| 16 | local self = Event:new(WaterTrailerSetIsFillingEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table vehicle, boolean isFilling)Arguments
| table | vehicle | vehicle |
| boolean | isFilling | is filling |
| 24 | function WaterTrailerSetIsFillingEvent:new(vehicle, isFilling) |
| 25 | local self = WaterTrailerSetIsFillingEvent:emptyNew() |
| 26 | self.vehicle = vehicle |
| 27 | self.isFilling = isFilling |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function WaterTrailerSetIsFillingEvent:readStream(streamId, connection) |
| 36 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 37 | self.isFilling = streamReadBool(streamId) |
| 38 | self:run(connection) |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function WaterTrailerSetIsFillingEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 47 | streamWriteBool(streamId, self.isFilling) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function WaterTrailerSetIsFillingEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(self, false, connection, self.vehicle) |
| 56 | end |
| 57 | self.vehicle:setIsWaterTrailerFilling(self.isFilling, true) |
| 58 | end |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table vehicle, boolean isFilling, boolean noEventSend)Arguments
| table | vehicle | vehicle |
| boolean | isFilling | is filling |
| boolean | noEventSend | no event send |
| 65 | function WaterTrailerSetIsFillingEvent.sendEvent(vehicle, isFilling, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(WaterTrailerSetIsFillingEvent:new(vehicle, isFilling), nil, nil, vehicle) |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(WaterTrailerSetIsFillingEvent:new(vehicle, isFilling)) |
| 71 | end |
| 72 | end |
| 73 | end |
Event for repairing
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function WearableRepairEvent:emptyNew() |
| 16 | local self = Event:new(WearableRepairEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table vehicle)Arguments
| table | vehicle | vehicle |
| 23 | function WearableRepairEvent:new(vehicle, atSellingPoint) |
| 24 | local self = WearableRepairEvent:emptyNew() |
| 25 | self.vehicle = vehicle |
| 26 | self.atSellingPoint = atSellingPoint |
| 27 | return self |
| 28 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 34 | function WearableRepairEvent:readStream(streamId, connection) |
| 35 | self.vehicle = NetworkUtil.readNodeObject(streamId) |
| 36 | self.atSellingPoint = streamReadBool(streamId) |
| 37 | self:run(connection) |
| 38 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 44 | function WearableRepairEvent:writeStream(streamId, connection) |
| 45 | NetworkUtil.writeNodeObject(streamId, self.vehicle) |
| 46 | streamWriteBool(streamId, self.atSellingPoint) |
| 47 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 52 | function WearableRepairEvent:run(connection) |
| 53 | if not connection:getIsServer() then |
| 54 | if self.vehicle.repairVehicle ~= nil then |
| 55 | self.vehicle:repairVehicle(self.atSellingPoint) |
| 56 | |
| 57 | g_server:broadcastEvent(self) -- broadcast for UI updates |
| 58 | g_messageCenter:publish(MessageType.VEHICLE_REPAIRED, {self.vehicle, self.atSellingPoint}) |
| 59 | end |
| 60 | else |
| 61 | g_messageCenter:publish(MessageType.VEHICLE_REPAIRED, {self.vehicle, self.atSellingPoint}) |
| 62 | end |
| 63 | end |
Event for cut tree
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function WoodHarvesterCutTreeEvent:emptyNew() |
| 16 | local self = Event:new(WoodHarvesterCutTreeEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, float length)Arguments
| table | object | object |
| float | length | length |
| 24 | function WoodHarvesterCutTreeEvent:new(object, length) |
| 25 | local self = WoodHarvesterCutTreeEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.length = length; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function WoodHarvesterCutTreeEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.length = streamReadFloat32(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function WoodHarvesterCutTreeEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteFloat32(streamId, self.length); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function WoodHarvesterCutTreeEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(WoodHarvesterCutTreeEvent:new(self.object, self.length), nil, connection, self.object); |
| 56 | end; |
| 57 | self.object:cutTree(self.length, true); |
| 58 | end; |
Broadcast event from server to all clients, if called on client call function on server and broadcast it to all clientsDefinition
sendEvent(table object, float length, boolean noEventSend)Arguments
| table | object | object |
| float | length | length |
| boolean | noEventSend | no event send |
| 65 | function WoodHarvesterCutTreeEvent.sendEvent(object, length, noEventSend) |
| 66 | if noEventSend == nil or noEventSend == false then |
| 67 | if g_server ~= nil then |
| 68 | g_server:broadcastEvent(WoodHarvesterCutTreeEvent:new(object, length), nil, nil, object); |
| 69 | else |
| 70 | g_client:getServerConnection():sendEvent(WoodHarvesterCutTreeEvent:new(object, length)); |
| 71 | end; |
| 72 | end; |
| 73 | end; |
Event for on cut tree
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function WoodHarvesterOnCutTreeEvent:emptyNew() |
| 16 | local self = Event:new(WoodHarvesterOnCutTreeEvent_mt) |
| 17 | return self |
| 18 | end |
Create new instance of eventDefinition
new(table object, float radius)Arguments
| table | object | object |
| float | radius | radius |
| 24 | function WoodHarvesterOnCutTreeEvent:new(object, radius) |
| 25 | local self = WoodHarvesterOnCutTreeEvent:emptyNew() |
| 26 | self.object = object |
| 27 | self.radius = radius |
| 28 | return self |
| 29 | end |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function WoodHarvesterOnCutTreeEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId) |
| 37 | self.radius = streamReadFloat32(streamId) |
| 38 | self:run(connection) |
| 39 | end |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function WoodHarvesterOnCutTreeEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object) |
| 47 | streamWriteFloat32(streamId, self.radius) |
| 48 | end |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function WoodHarvesterOnCutTreeEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(WoodHarvesterOnCutTreeEvent:new(self.object, self.radius), nil, connection, self.object) |
| 56 | end |
| 57 | SpecializationUtil.raiseEvent(self.object, "onCutTree", self.radius) |
| 58 | end |
Event for delimb tree state
Create instance of Event classDefinition
emptyNew()Return Values
| table | self | instance of class event |
| 15 | function WoodHarvesterOnDelimbTreeEvent:emptyNew() |
| 16 | local self = Event:new(WoodHarvesterOnDelimbTreeEvent_mt); |
| 17 | return self; |
| 18 | end; |
Create new instance of eventDefinition
new(table object, boolean state)Arguments
| table | object | object |
| boolean | state | state |
| 24 | function WoodHarvesterOnDelimbTreeEvent:new(object, state) |
| 25 | local self = WoodHarvesterOnDelimbTreeEvent:emptyNew() |
| 26 | self.object = object; |
| 27 | self.state = state; |
| 28 | return self; |
| 29 | end; |
Called on client side on joinDefinition
readStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 35 | function WoodHarvesterOnDelimbTreeEvent:readStream(streamId, connection) |
| 36 | self.object = NetworkUtil.readNodeObject(streamId); |
| 37 | self.state = streamReadBool(streamId); |
| 38 | self:run(connection); |
| 39 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 45 | function WoodHarvesterOnDelimbTreeEvent:writeStream(streamId, connection) |
| 46 | NetworkUtil.writeNodeObject(streamId, self.object); |
| 47 | streamWriteBool(streamId, self.state); |
| 48 | end; |
Run action on receiving sideDefinition
run(integer connection)Arguments
| integer | connection | connection |
| 53 | function WoodHarvesterOnDelimbTreeEvent:run(connection) |
| 54 | if not connection:getIsServer() then |
| 55 | g_server:broadcastEvent(WoodHarvesterOnDelimbTreeEvent:new(self.object, self.state), nil, connection, self.object); |
| 56 | end; |
| 57 | self.object:onDelimbTree(self.state); |
| 58 | end; |
Class for animated map objectsParent
AnimatedObject
Creating animated objectDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| boolean | success | success |
| 17 | function AnimatedMapObject:onCreate(id) |
| 18 | local object = AnimatedMapObject:new(g_server ~= nil, g_client ~= nil) |
| 19 | if object:load(id) then |
| 20 | g_currentMission:addOnCreateLoadedObject(object) |
| 21 | g_currentMission:addOnCreateLoadedObjectToSave(object) |
| 22 | object:register(true) |
| 23 | else |
| 24 | object:delete() |
| 25 | end |
| 26 | end |
Creating new instance of animated object classDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| boolean | isActive | speed rotating part is active |
| table | self | new instance of object |
| 34 | function AnimatedMapObject:new(isServer, isClient, customMt) |
| 35 | local self = AnimatedObject:new(isServer, isClient, customMt or AnimatedObject_mt) |
| 36 | return self |
| 37 | end |
Load animated object attributes from objectDefinition
load(integer nodeId)Arguments
| integer | nodeId | id of object to load from |
| integer | direction | direction |
| boolean | success | success |
| 49 | function AnimatedMapObject:load(nodeId) |
| 50 | local xmlFilename = getUserAttribute(nodeId, "xmlFilename") |
| 51 | |
| 52 | if xmlFilename == nil then |
| 53 | print("Error: Missing 'xmlFilename' user attribute for AnimatedMapObject node '"..getName(nodeId).."'!") |
| 54 | return false |
| 55 | end |
| 56 | |
| 57 | local baseDir = g_currentMission.loadingMapBaseDirectory |
| 58 | if baseDir == "" then |
| 59 | baseDir = Utils.getNoNil(self.baseDirectory, baseDir) |
| 60 | end |
| 61 | xmlFilename = Utils.getFilename(xmlFilename, baseDir) |
| 62 | |
| 63 | local index = getUserAttribute(nodeId, "index") |
| 64 | if index == nil then |
| 65 | print("Error: Missing 'index' user attribute for AnimatedMapObject node '"..getName(nodeId).."'!") |
| 66 | return false |
| 67 | end |
| 68 | |
| 69 | local xmlFile = loadXMLFile("AnimatedObject", xmlFilename) |
| 70 | if xmlFile == 0 then |
| 71 | return false |
| 72 | end |
| 73 | |
| 74 | -- Find the index in the XML |
| 75 | local key |
| 76 | local i = 0 |
| 77 | while true do |
| 78 | local objectKey = string.format("animatedObjects.animatedObject(%d)", i) |
| 79 | if not hasXMLProperty(xmlFile, objectKey) then |
| 80 | break |
| 81 | end |
| 82 | |
| 83 | local configIndex = getXMLString(xmlFile, objectKey.."#index") |
| 84 | if configIndex == index then |
| 85 | key = objectKey |
| 86 | break |
| 87 | end |
| 88 | i = i + 1 |
| 89 | end |
| 90 | |
| 91 | if key == nil then |
| 92 | print("Error: index '"..index.."' not found in AnimatedObject xml '"..xmlFilename.."'!") |
| 93 | return false |
| 94 | end |
| 95 | |
| 96 | local result = AnimatedMapObject:superClass().load(self, nodeId, xmlFilename, index) |
| 97 | |
| 98 | delete(xmlFile) |
| 99 | |
| 100 | return result |
| 101 | end |
Class for animated objects
Creating animated objectDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| 19 | function AnimatedObject:onCreate(id) |
| 20 | local object = AnimatedObject:new(g_server ~= nil, g_client ~= nil) |
| 21 | if object:load(id) then |
| 22 | g_currentMission:addOnCreateLoadedObject(object) |
| 23 | g_currentMission:addOnCreateLoadedObjectToSave(object) |
| 24 | object:register(true) |
| 25 | else |
| 26 | object:delete() |
| 27 | end |
| 28 | end |
Creating new instance of animated object classDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| table | self | new instance of object |
| 36 | function AnimatedObject:new(isServer, isClient, customMt) |
| 37 | local self = Object:new(isServer, isClient, customMt or AnimatedObject_mt) |
| 38 | self.nodeId = 0 |
| 39 | self.isMoving = false |
| 40 | self.wasPressed = false |
| 41 | self.baseDirectory = nil |
| 42 | self.customEnvironment = g_currentMission.loadingMapModName |
| 43 | |
| 44 | -- input controls fields: |
| 45 | self.controls = {} |
| 46 | self.controls.active = false |
| 47 | self.controls.posAction = nil |
| 48 | self.controls.negAction = nil |
| 49 | self.controls.posText = nil |
| 50 | self.controls.negText = nil |
| 51 | self.controls.posActionEventId = nil |
| 52 | self.controls.negActionEventId = nil |
| 53 | |
| 54 | return self |
| 55 | end |
Load animated object from object with given configuration fileDefinition
load(integer nodeId, xmlFilename string, index integer)Arguments
| integer | nodeId | id of object |
| xmlFilename | string | Path of the xml configuration |
| index | integer | Configuration index within the xml file |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| boolean | success | success |
| 63 | function AnimatedObject:load(nodeId, xmlFile, key) |
| 64 | self.nodeId = nodeId |
| 65 | |
| 66 | self.samples = {} |
| 67 | |
| 68 | local success = true |
| 69 | self.saveId = getXMLString(xmlFile, key.."#saveId") |
| 70 | if self.saveId == nil then |
| 71 | self.saveId = "AnimatedObject_"..getName(nodeId) |
| 72 | end |
| 73 | |
| 74 | local animKey = key .. ".animation" |
| 75 | |
| 76 | self.animation = {} |
| 77 | self.animation.parts = {} |
| 78 | self.animation.duration = Utils.getNoNil(getXMLFloat(xmlFile, animKey.."#duration"), 3) * 1000 |
| 79 | if self.animation.duration == 0 then |
| 80 | self.animation.duration = 1000 |
| 81 | end |
| 82 | self.animation.time = 0 |
| 83 | self.animation.direction = 0 |
| 84 | |
| 85 | self.animation.targetTime = 0 |
| 86 | self.animation.lastTime = 0 |
| 87 | self.animation.timeIsDirty = false |
| 88 | |
| 89 | self.interpolationDuration = 50+30 |
| 90 | self.interpolationAlpha = 0 |
| 91 | |
| 92 | |
| 93 | local i = 0 |
| 94 | while true do |
| 95 | local partKey = string.format("%s.part(%d)", animKey, i) |
| 96 | if not hasXMLProperty(xmlFile, partKey) then |
| 97 | break |
| 98 | end |
| 99 | |
| 100 | local node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, partKey.."#node")) |
| 101 | if node ~= nil then |
| 102 | local part = {} |
| 103 | part.node = node |
| 104 | part.animCurve = AnimCurve:new(linearInterpolatorN) |
| 105 | local hasFrames = false |
| 106 | local j = 0 |
| 107 | while true do |
| 108 | local frameKey = string.format("%s.keyFrame(%d)", partKey, j) |
| 109 | if not hasXMLProperty(xmlFile, frameKey) then |
| 110 | break |
| 111 | end |
| 112 | |
| 113 | local keyTime = getXMLFloat(xmlFile, frameKey.."#time") |
| 114 | local keyframe = {self:loadFrameValues(xmlFile, frameKey, node)} |
| 115 | keyframe.time = keyTime |
| 116 | part.animCurve:addKeyframe(keyframe) |
| 117 | hasFrames = true |
| 118 | |
| 119 | j = j + 1 |
| 120 | end |
| 121 | |
| 122 | if hasFrames then |
| 123 | table.insert(self.animation.parts, part) |
| 124 | end |
| 125 | end |
| 126 | i = i + 1 |
| 127 | end |
| 128 | |
| 129 | local initialTime = Utils.getNoNil(getXMLFloat(xmlFile, animKey.."#initialTime"), 0)*1000 |
| 130 | self:setAnimTime(initialTime / self.animation.duration, true) |
| 131 | |
| 132 | local startTime = getXMLFloat(xmlFile, key..".openingHours#startTime") |
| 133 | local endTime = getXMLFloat(xmlFile, key..".openingHours#endTime") |
| 134 | if startTime ~= nil and endTime ~= nil then |
| 135 | local disableIfClosed = Utils.getNoNil(getXMLBool(xmlFile, key..".openingHours#disableIfClosed"), false) |
| 136 | local closedText = getXMLString(xmlFile, key..".openingHours#closedText") |
| 137 | if closedText ~= nil then |
| 138 | if g_i18n:hasText(closedText, self.customEnvironment) then |
| 139 | closedText = g_i18n:getText(closedText, self.customEnvironment) |
| 140 | end |
| 141 | end |
| 142 | self.openingHours = {startTime=startTime, endTime=endTime, disableIfClosed=disableIfClosed, closedText=closedText} |
| 143 | g_currentMission.environment:addHourChangeListener(self) |
| 144 | end |
| 145 | |
| 146 | self.isEnabled = true |
| 147 | |
| 148 | |
| 149 | local triggerId = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, key..".controls#triggerNode")) |
| 150 | if triggerId ~= nil then |
| 151 | self.controls.triggerId = triggerId |
| 152 | |
| 153 | addTrigger(self.controls.triggerId, "triggerCallback", self) |
| 154 | for i=0, getNumOfChildren(self.controls.triggerId)-1 do |
| 155 | addTrigger(getChildAt(self.controls.triggerId, i), "triggerCallback", self) |
| 156 | end |
| 157 | |
| 158 | local posAction = getXMLString(xmlFile, key..".controls#posAction") |
| 159 | if posAction ~= nil then |
| 160 | if InputAction[posAction] then |
| 161 | self.controls.posAction = posAction |
| 162 | |
| 163 | local posText = getXMLString(xmlFile, key..".controls#posText") |
| 164 | if posText ~= nil then |
| 165 | if g_i18n:hasText(posText, self.customEnvironment) then |
| 166 | posText = g_i18n:getText(posText, self.customEnvironment) |
| 167 | end |
| 168 | self.controls.posActionText = posText |
| 169 | end |
| 170 | |
| 171 | local negText = getXMLString(xmlFile, key..".controls#negText") |
| 172 | if negText ~= nil then |
| 173 | if g_i18n:hasText(negText, self.customEnvironment) then |
| 174 | negText = g_i18n:getText(negText, self.customEnvironment) |
| 175 | end |
| 176 | self.controls.negActionText = negText |
| 177 | end |
| 178 | |
| 179 | local negAction = getXMLString(xmlFile, key..".controls#negAction") |
| 180 | if negAction ~= nil then |
| 181 | if InputAction[negAction] then |
| 182 | self.controls.negAction = negAction |
| 183 | else |
| 184 | print("Warning: Negative direction action '"..negAction.."' not defined!") |
| 185 | end |
| 186 | end |
| 187 | else |
| 188 | print("Warning: Positive direction action '"..posAction.."' not defined!") |
| 189 | end |
| 190 | end |
| 191 | end |
| 192 | |
| 193 | if g_client ~= nil then |
| 194 | local soundsKey = key .. ".sounds" |
| 195 | self.sampleMoving = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "moving", self.baseDirectory, self.nodeId, 1, AudioGroup.ENVIRONMENT, nil, nil) |
| 196 | self.samplePosEnd = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "posEnd", self.baseDirectory, self.nodeId, 1, AudioGroup.ENVIRONMENT, nil, nil) |
| 197 | self.sampleNegEnd = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "negEnd", self.baseDirectory, self.nodeId, 1, AudioGroup.ENVIRONMENT, nil, nil) |
| 198 | end |
| 199 | |
| 200 | self.animatedObjectDirtyFlag = self:getNextDirtyFlag() |
| 201 | |
| 202 | return success |
| 203 | end |
Load frame values from xmlDefinition
loadFrameValues(integer fileId, string key, integer node)Arguments
| integer | fileId | xml file id |
| string | key | key |
| integer | node | node id |
| float | dirtMultiplier | current dirt multiplier |
| float | x | x translation |
| float | y | y translation |
| float | z | z translation |
| float | rx | x rotation |
| float | ry | y rotation |
| float | rz | z rotation |
| float | sx | x scale |
| float | sy | y scale |
| float | sz | z scale |
| integer | visibility | visibility |
| 220 | function AnimatedObject:loadFrameValues(xmlFile, key, node) |
| 221 | local rx,ry,rz = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotation")) |
| 222 | local x,y,z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#translation")) |
| 223 | local sx,sy,sz = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#scale")) |
| 224 | local isVisible = Utils.getNoNil(getXMLBool(xmlFile, key.."#visibility"), true) |
| 225 | |
| 226 | local drx,dry,drz = getRotation(node) |
| 227 | rx = Utils.getNoNilRad(rx, drx) |
| 228 | ry = Utils.getNoNilRad(ry, dry) |
| 229 | rz = Utils.getNoNilRad(rz, drz) |
| 230 | local dx,dy,dz = getTranslation(node) |
| 231 | x = Utils.getNoNil(x, dx) |
| 232 | y = Utils.getNoNil(y, dy) |
| 233 | z = Utils.getNoNil(z, dz) |
| 234 | local dsx,dsy,dsz = getScale(node) |
| 235 | sx = Utils.getNoNil(sx, dsx) |
| 236 | sy = Utils.getNoNil(sy, dsy) |
| 237 | sz = Utils.getNoNil(sz, dsz) |
| 238 | |
| 239 | local visibility = 1 |
| 240 | if not isVisible then |
| 241 | visibility = 0 |
| 242 | end |
| 243 | |
| 244 | return x, y, z, rx, ry, rz, sx, sy, sz, visibility |
| 245 | end |
Delete animated objectDefinition
delete()Return Values
| float | wearMultiplier | current wear multiplier |
| 249 | function AnimatedObject:delete() |
| 250 | if self.controls.triggerId ~= nil then |
| 251 | removeTrigger(self.controls.triggerId) |
| 252 | for i=0, getNumOfChildren(self.controls.triggerId)-1 do |
| 253 | removeTrigger(getChildAt(self.controls.triggerId, i)) |
| 254 | end |
| 255 | self.controls.triggerId = nil |
| 256 | end |
| 257 | |
| 258 | if self.sampleMoving ~= nil then |
| 259 | g_soundManager:deleteSample(self.sampleMoving) |
| 260 | self.sampleMoving = nil |
| 261 | end |
| 262 | if self.samplePosEnd ~= nil then |
| 263 | g_soundManager:deleteSample(self.samplePosEnd) |
| 264 | self.samplePosEnd = nil |
| 265 | end |
| 266 | if self.sampleNegEnd ~= nil then |
| 267 | g_soundManager:deleteSample(self.sampleNegEnd) |
| 268 | self.sampleNegEnd = nil |
| 269 | end |
| 270 | |
| 271 | g_currentMission.environment:removeHourChangeListener(self) |
| 272 | |
| 273 | AnimatedObject:superClass().delete(self) |
| 274 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| table | objectsInTensionBeltRange | object in belt range |
| integer | numObjectsIntensionBeltRange | number of objects in belt range |
| 280 | function AnimatedObject:readStream(streamId, connection) |
| 281 | AnimatedObject:superClass().readStream(self, streamId, connection) |
| 282 | if connection:getIsServer() then |
| 283 | local animTime = streamReadFloat32(streamId) |
| 284 | self:setAnimTime(animTime, true) |
| 285 | end |
| 286 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 292 | function AnimatedObject:writeStream(streamId, connection) |
| 293 | AnimatedObject:superClass().writeStream(self, streamId, connection) |
| 294 | if not connection:getIsServer() then |
| 295 | streamWriteFloat32(streamId, self.animation.time) |
| 296 | end |
| 297 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| table | objectIdsToUnmount | table with object ids to unmount |
| integer | numObjects | number of objects to unmount |
| 304 | function AnimatedObject:readUpdateStream(streamId, timestamp, connection) |
| 305 | AnimatedObject:superClass().readUpdateStream(self, streamId, timestamp, connection) |
| 306 | if connection:getIsServer() then |
| 307 | local isDirty = streamReadBool(streamId) |
| 308 | if isDirty then |
| 309 | local animTime = streamReadFloat32(streamId) |
| 310 | --self:setAnimTime(animTime) |
| 311 | |
| 312 | self.animation.targetTime = animTime |
| 313 | self.animation.lastTime = self.animation.time |
| 314 | |
| 315 | local deltaTime = g_client.tickDuration |
| 316 | if self.lastPhysicsNetworkTime ~= nil then |
| 317 | deltaTime = math.min(g_packetPhysicsNetworkTime - self.lastPhysicsNetworkTime, 3*g_client.tickDuration) |
| 318 | end |
| 319 | self.lastPhysicsNetworkTime = g_packetPhysicsNetworkTime |
| 320 | |
| 321 | -- interpTimeLeft is the time we would've continued interpolating. This should always be about g_clientInterpDelay. |
| 322 | -- If we extrapolated, reset to the full delay |
| 323 | local interpTimeLeft = g_clientInterpDelay |
| 324 | if self.animation.timeIsDirty and self.interpolationAlpha < 1 then |
| 325 | interpTimeLeft = (1-self.interpolationAlpha) * self.interpolationDuration |
| 326 | interpTimeLeft = interpTimeLeft * 0.95 + g_clientInterpDelay * 0.05 -- slighly blend towards the expected delay |
| 327 | interpTimeLeft = math.min(interpTimeLeft, 3*g_clientInterpDelay) -- clamp to some reasonable maximum |
| 328 | end |
| 329 | self.interpolationDuration = interpTimeLeft + deltaTime |
| 330 | self.interpolationAlpha = 0 |
| 331 | |
| 332 | self.animation.timeIsDirty = true |
| 333 | end |
| 334 | end |
| 335 | end |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| 342 | function AnimatedObject:writeUpdateStream(streamId, connection, dirtyMask) |
| 343 | AnimatedObject:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
| 344 | if not connection:getIsServer() then |
| 345 | if streamWriteBool(streamId, bitAND(dirtyMask, self.animatedObjectDirtyFlag) ~= 0) then |
| 346 | streamWriteFloat32(streamId, self.animation.time) |
| 347 | end |
| 348 | end |
| 349 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | inRange | player is in range |
| table | belt | nearest belt |
| boolean | success | success |
| 356 | function AnimatedObject:loadFromXMLFile(xmlFile, key) |
| 357 | local animTime = getXMLFloat(xmlFile, key .. "#time") |
| 358 | if animTime ~= nil then |
| 359 | self.animation.direction = Utils.getNoNil(getXMLInt(xmlFile, key.."#direction"), 0) |
| 360 | self:setAnimTime(animTime, true) |
| 361 | end |
| 362 | |
| 363 | AnimatedObject.hourChanged(self) |
| 364 | |
| 365 | return true |
| 366 | end |
Register action events when the player is in range.Definition
registerActionEventsWhenInRange()
Remove action events.Definition
Call as early as possible to avoid input conflicts.
removeActionEvents()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
Event function for animation toggle (e.g. open/close door with one input).Definition
onAnimationInputToggle()Return Values
| float | dirtMultiplier | current dirt multiplier |
Event function for continuous animation (e.g. keep button pressed to raise/lower something).Definition
onAnimationInputContinuous()Return Values
| float | dirtMultiplier | current wear multiplier |
Called on updateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| boolean | isActive | speed rotating part is active |
| 483 | function AnimatedObject:update(dt) |
| 484 | AnimatedObject:superClass().update(self, dt) |
| 485 | |
| 486 | -- Update availability of the input actions |
| 487 | local deactivateInput = false |
| 488 | if self.playerInRange then |
| 489 | if self.isEnabled then |
| 490 | if not self.controls.active then |
| 491 | self:registerActionEventsWhenInRange() |
| 492 | end |
| 493 | self:updateActionEventTexts() |
| 494 | else |
| 495 | deactivateInput = true |
| 496 | if self.openingHours ~= nil and self.openingHours.closedText ~= nil then |
| 497 | g_currentMission:addExtraPrintText(self.openingHours.closedText) |
| 498 | end |
| 499 | end |
| 500 | else |
| 501 | deactivateInput = true |
| 502 | end |
| 503 | |
| 504 | if deactivateInput and self.controls.active then |
| 505 | self:removeActionEvents() |
| 506 | end |
| 507 | |
| 508 | local finishedAnimation = false |
| 509 | |
| 510 | -- former updateTick() |
| 511 | if self.isServer then |
| 512 | if self.animation.direction ~= 0 then |
| 513 | local newAnimTime = MathUtil.clamp(self.animation.time + (self.animation.direction*dt)/self.animation.duration, 0, 1) |
| 514 | |
| 515 | self:setAnimTime(newAnimTime) |
| 516 | if newAnimTime == 0 or newAnimTime == 1 then |
| 517 | self.animation.direction = 0 |
| 518 | finishedAnimation = true |
| 519 | end |
| 520 | end |
| 521 | |
| 522 | if self.animation.time ~= self.animation.timeSend then |
| 523 | self.animation.timeSend = self.animation.time |
| 524 | self:raiseDirtyFlags(self.animatedObjectDirtyFlag) |
| 525 | end |
| 526 | end |
| 527 | |
| 528 | if not self.isServer and self.animation.timeIsDirty then |
| 529 | local maxIntrpAlpha = 1.2 |
| 530 | local interpolationAlpha = self.interpolationAlpha + g_physicsDtUnclamped / self.interpolationDuration |
| 531 | |
| 532 | if interpolationAlpha >= maxIntrpAlpha then |
| 533 | interpolationAlpha = maxIntrpAlpha |
| 534 | self.animation.timeIsDirty = false |
| 535 | end |
| 536 | |
| 537 | self.interpolationAlpha = interpolationAlpha |
| 538 | local animTime = self.animation.lastTime + (interpolationAlpha * (self.animation.targetTime - self.animation.lastTime)) |
| 539 | self:setAnimTime(animTime) |
| 540 | end |
| 541 | |
| 542 | if self.sampleMoving ~= nil then |
| 543 | if self.isMoving and self.animation.direction ~= 0 then |
| 544 | if not self.sampleMoving.isPlaying then |
| 545 | g_soundManager:playSample(self.sampleMoving) |
| 546 | self.sampleMoving.isPlaying = true |
| 547 | end |
| 548 | else |
| 549 | if self.sampleMoving.isPlaying then |
| 550 | g_soundManager:stopSample(self.sampleMoving) |
| 551 | self.sampleMoving.isPlaying = false |
| 552 | end |
| 553 | end |
| 554 | end |
| 555 | |
| 556 | if finishedAnimation and self.animation.direction == 0 then |
| 557 | if self.samplePosEnd ~= nil and self.animation.time == 1 then |
| 558 | g_soundManager:playSample(self.samplePosEnd) |
| 559 | elseif self.sampleNegEnd ~= nil and self.animation.time == 0 then |
| 560 | g_soundManager:playSample(self.sampleNegEnd) |
| 561 | end |
| 562 | end |
| 563 | |
| 564 | self.isMoving = false |
| 565 | |
| 566 | if self.animation.direction ~= 0 then |
| 567 | self:raiseActive() |
| 568 | end |
| 569 | end |
Called on update tickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| boolean | checkSpeedlimit | check speed limit |
| 574 | function AnimatedObject:updateTick(dt) |
| 575 | AnimatedObject:superClass().updateTick(self, dt) |
| 576 | end |
Set animation timeDefinition
setAnimTime(float t)Arguments
| float | t | time |
| float | speedLimit | speed limit |
| 581 | function AnimatedObject:setAnimTime(t, omitSound) |
| 582 | t = MathUtil.clamp(t, 0, 1) |
| 583 | |
| 584 | for _, part in pairs(self.animation.parts) do |
| 585 | local v = part.animCurve:get(t) |
| 586 | self:setFrameValues(part.node, v) |
| 587 | end |
| 588 | |
| 589 | self.animation.time = t |
| 590 | self.isMoving = true |
| 591 | end |
Set frame valuesDefinition
setFrameValues(integer node, table v)Arguments
| integer | node | node id |
| table | v | values |
| table | object | object of sapling pallet |
| 597 | function AnimatedObject:setFrameValues(node, v) |
| 598 | setTranslation(node, v[1], v[2], v[3]) |
| 599 | setRotation(node, v[4], v[5], v[6]) |
| 600 | setScale(node, v[7], v[8], v[9]) |
| 601 | setVisibility(node, v[10] == 1) |
| 602 | end |
Called on hour changeDefinition
hourChanged()Return Values
| table | self | new instance |
| 606 | function AnimatedObject:hourChanged() |
| 607 | if self.isServer then |
| 608 | local currentHour = g_currentMission.environment.currentHour |
| 609 | if self.openingHours ~= nil then |
| 610 | if currentHour >= self.openingHours.startTime and currentHour < self.openingHours.endTime then |
| 611 | if not self.openingHours.isOpen then |
| 612 | if self.isServer then |
| 613 | self.animation.direction = 1 |
| 614 | end |
| 615 | self.openingHours.isOpen = true |
| 616 | end |
| 617 | if self.openingHours.disableIfClosed then |
| 618 | self.isEnabled = true |
| 619 | end |
| 620 | else |
| 621 | if self.openingHours.isOpen then |
| 622 | if self.isServer then |
| 623 | self.animation.direction = -1 |
| 624 | end |
| 625 | self.openingHours.isOpen = false |
| 626 | end |
| 627 | if self.openingHours.disableIfClosed then |
| 628 | self.isEnabled = false |
| 629 | end |
| 630 | end |
| 631 | end |
| 632 | end |
| 633 | end |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of object that calls callback |
| boolean | onEnter | called on enter |
| boolean | onLeave | called on leave |
| boolean | onStay | called on stay |
| boolean | isActivateable | is activateable |
| 642 | function AnimatedObject:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 643 | if g_currentMission.missionInfo:isa(FSCareerMissionInfo) then |
| 644 | if onEnter or onLeave then |
| 645 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
| 646 | if onEnter then |
| 647 | self.playerInRange = g_currentMission.player |
| 648 | else |
| 649 | self.playerInRange = nil |
| 650 | end |
| 651 | self:raiseActive() |
| 652 | end |
| 653 | end |
| 654 | end |
| 655 | end |
Class for bales
Creating bale objectDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | customMt |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| table | instance | Instance of object |
| 18 | function Bale:new(isServer, isClient, customMt) |
| 19 | local mt = customMt; |
| 20 | if mt == nil then |
| 21 | mt = Bale_mt; |
| 22 | end; |
| 23 | |
| 24 | local self = MountableObject:new(isServer, isClient, mt); |
| 25 | |
| 26 | self.forcedClipDistance = 150; |
| 27 | registerObjectClassName(self, "Bale"); |
| 28 | |
| 29 | self.fillType = FillType.STRAW; |
| 30 | self.fillLevel = 0; |
| 31 | self.wrappingState = 0; |
| 32 | self.baleValueScale = 1; |
| 33 | self.canBeSold = true |
| 34 | |
| 35 | g_currentMission:addLimitedObject(FSBaseMission.LIMITED_OBJECT_TYPE_BALE, self); |
| 36 | |
| 37 | return self; |
| 38 | end; |
Deleting bale objectDefinition
delete()Return Values
| boolean | isTurnedOn | vehicle is turned on |
| 42 | function Bale:delete() |
| 43 | if self.i3dFilename ~= nil then |
| 44 | g_i3DManager:releaseSharedI3DFile(self.i3dFilename, nil, true); |
| 45 | end |
| 46 | g_currentMission:removeLimitedObject(FSBaseMission.LIMITED_OBJECT_TYPE_BALE, self); |
| 47 | unregisterObjectClassName(self); |
| 48 | g_currentMission:removeItemToSave(self); |
| 49 | Bale:superClass().delete(self); |
| 50 | end; |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| boolean | canBeTurnedOn | vehicle can be turned on |
| 57 | function Bale:readUpdateStream(streamId, timestamp, connection) |
| 58 | if connection:getIsServer() then |
| 59 | if self.supportsWrapping then |
| 60 | self:setWrappingState(streamReadUInt8(streamId)/255); |
| 61 | end |
| 62 | end |
| 63 | Bale:superClass().readUpdateStream(self, streamId, timestamp, connection); |
| 64 | end; |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| boolean | allow | allow turn on |
| 71 | function Bale:writeUpdateStream(streamId, connection, dirtyMask) |
| 72 | if not connection:getIsServer() then |
| 73 | if self.supportsWrapping then |
| 74 | streamWriteUInt8(streamId, MathUtil.clamp(self.wrappingState*255, 0, 255)); |
| 75 | end |
| 76 | end |
| 77 | Bale:superClass().writeUpdateStream(self, streamId, connection, dirtyMask); |
| 78 | end; |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| string | warningText | turn on not allowed warning text |
| 84 | function Bale:readStream(streamId, connection) |
| 85 | local i3dFilename = NetworkUtil.convertFromNetworkFilename(streamReadString(streamId)); |
| 86 | if self.nodeId == 0 then |
| 87 | self:createNode(i3dFilename); |
| 88 | end; |
| 89 | self.fillLevel = streamReadFloat32(streamId); |
| 90 | Bale:superClass().readStream(self, streamId, connection); |
| 91 | g_currentMission:addItemToSave(self); |
| 92 | |
| 93 | self.baleValueScale = streamReadFloat32(streamId); |
| 94 | if self.supportsWrapping then |
| 95 | self:setWrappingState(streamReadUInt8(streamId)/255); |
| 96 | setShaderParameter(self.meshNode, "wrappingState", self.wrappingState, 0, 0, 0, false); |
| 97 | |
| 98 | local r = streamReadFloat32(streamId); |
| 99 | local g = streamReadFloat32(streamId); |
| 100 | local b = streamReadFloat32(streamId); |
| 101 | local a = streamReadFloat32(streamId); |
| 102 | self:setColor(r, g, b, a); |
| 103 | end; |
| 104 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| boolean | isActive | work area is active |
| 110 | function Bale:writeStream(streamId, connection) |
| 111 | streamWriteString(streamId, NetworkUtil.convertToNetworkFilename(self.i3dFilename)); |
| 112 | streamWriteFloat32(streamId, self.fillLevel); |
| 113 | Bale:superClass().writeStream(self, streamId, connection); |
| 114 | |
| 115 | streamWriteFloat32(streamId, self.baleValueScale); |
| 116 | |
| 117 | if self.supportsWrapping then |
| 118 | streamWriteUInt8(streamId, MathUtil.clamp(self.wrappingState*255, 0, 255)); |
| 119 | |
| 120 | streamWriteFloat32(streamId, self.wrappingColor[1]); |
| 121 | streamWriteFloat32(streamId, self.wrappingColor[2]); |
| 122 | streamWriteFloat32(streamId, self.wrappingColor[3]); |
| 123 | streamWriteFloat32(streamId, self.wrappingColor[4]); |
| 124 | end |
| 125 | end; |
Mount bale to objectDefinition
mount(table object, integer node, float x, float y, float z, float rx, float ry, float rz)Arguments
| table | object | target object |
| integer | node | target node id |
| float | x | x position |
| float | y | z position |
| float | z | z position |
| float | rx | rx rotation |
| float | ry | ry rotation |
| float | rz | rz rotation |
| boolean | isReady | is ready for ai work |
| 137 | function Bale:mount(object, node, x,y,z, rx,ry,rz) |
| 138 | g_currentMission:removeItemToSave(self); |
| 139 | Bale:superClass().mount(self, object, node, x,y,z, rx,ry,rz); |
| 140 | end; |
Unmount baleDefinition
unmount()Return Values
| boolean | isOperating | is operating |
| 144 | function Bale:unmount() |
| 145 | if Bale:superClass().unmount(self) then |
| 146 | g_currentMission:addItemToSave(self); |
| 147 | return true; |
| 148 | end |
| 149 | return false; |
| 150 | end; |
Set node idDefinition
setNodeId(integer nodeId)Arguments
| integer | nodeId | node Id |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| 155 | function Bale:setNodeId(nodeId) |
| 156 | Bale:superClass().setNodeId(self, nodeId); |
| 157 | |
| 158 | local isRoundbale = Utils.getNoNil(getUserAttribute(nodeId, "isRoundbale"), false); |
| 159 | local defaultFillLevel = 2100*2; |
| 160 | if isRoundbale then |
| 161 | defaultFillLevel = 2000*2; |
| 162 | end |
| 163 | if getUserAttribute(nodeId, "baleValue") ~= nil then |
| 164 | print("Warning: bale 'baleValue' is not supported anymore. Use 'baleValueScale' instead and adjust the creating vehicles."); |
| 165 | end |
| 166 | local meshIndex = Utils.getNoNil(getUserAttribute(nodeId, "baleMeshIndex"), "1|0"); |
| 167 | self.meshNode = I3DUtil.indexToObject(nodeId, meshIndex); |
| 168 | |
| 169 | self.meshNodes = {self.meshNode} |
| 170 | |
| 171 | self.supportsWrapping = Utils.getNoNil(getUserAttribute(nodeId, "supportsWrapping"), false); |
| 172 | if self.supportsWrapping then |
| 173 | self.wrappingColor = {1,1,1,1}; |
| 174 | end |
| 175 | |
| 176 | self.fillLevel = defaultFillLevel |
| 177 | self.baleValueScale = Utils.getNoNil(tonumber(getUserAttribute(nodeId, "baleValueScale")), 1); |
| 178 | |
| 179 | self.fillType = FillType.STRAW; |
| 180 | local fillTypeStr = getUserAttribute(nodeId, "fillType"); |
| 181 | if fillTypeStr ~= nil then |
| 182 | local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeStr) |
| 183 | if fillTypeIndex ~= nil then |
| 184 | self.fillType = fillTypeIndex; |
| 185 | end |
| 186 | elseif Utils.getNoNil(getUserAttribute(nodeId, "isHaybale"), false) then |
| 187 | self.fillType = FillType.DRYGRASS_WINDROW; |
| 188 | end |
| 189 | |
| 190 | local baleWidth = tonumber(getUserAttribute(nodeId, "baleWidth")); |
| 191 | local baleHeight = tonumber(getUserAttribute(nodeId, "baleHeight")); |
| 192 | local baleLength = tonumber(getUserAttribute(nodeId, "baleLength")); |
| 193 | local baleDiameter = tonumber(getUserAttribute(nodeId, "baleDiameter")); |
| 194 | if baleDiameter ~= nil and baleWidth ~= nil then |
| 195 | self.baleDiameter = baleDiameter; |
| 196 | self.baleWidth = baleWidth; |
| 197 | elseif baleHeight ~= nil and baleWidth ~= nil and baleLength ~= nil then |
| 198 | self.baleHeight = baleHeight; |
| 199 | self.baleWidth = baleWidth; |
| 200 | self.baleLength = baleLength; |
| 201 | else |
| 202 | local isRoundbale = Utils.getNoNil(getUserAttribute(nodeId, "isRoundbale"), false); |
| 203 | if isRoundbale then |
| 204 | self.baleDiameter = 1.8; |
| 205 | self.baleWidth = 1.2; |
| 206 | else |
| 207 | self.baleHeight = 0.8; |
| 208 | self.baleWidth = 1.2; |
| 209 | self.baleLength = 2.4; |
| 210 | end |
| 211 | end |
| 212 | end; |
Load node from i3d fileDefinition
createNode(string i3dFilename)Arguments
| string | i3dFilename | i3d file name |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| 217 | function Bale:createNode(i3dFilename) |
| 218 | self.i3dFilename = i3dFilename; |
| 219 | self.customEnvironment, self.baseDirectory = Utils.getModNameAndBaseDirectory(i3dFilename); |
| 220 | local baleRoot = g_i3DManager:loadSharedI3DFile(i3dFilename); |
| 221 | |
| 222 | local baleId = getChildAt(baleRoot, 0); |
| 223 | link(getRootNode(), baleId); |
| 224 | delete(baleRoot); |
| 225 | |
| 226 | self:setNodeId(baleId); |
| 227 | end; |
Load baleDefinition
load(string i3dFilename, float x, float y, float z, float rx, float ry, float rz, integer fillLevel)Arguments
| string | i3dFilename | i3d file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| integer | fillLevel | fill level |
| boolean | success | success |
| 239 | function Bale:load(i3dFilename, x,y,z, rx,ry,rz, fillLevel) |
| 240 | self.i3dFilename = i3dFilename; |
| 241 | self.customEnvironment, self.baseDirectory = Utils.getModNameAndBaseDirectory(i3dFilename); |
| 242 | self:createNode(i3dFilename); |
| 243 | setTranslation(self.nodeId, x, y, z); |
| 244 | setRotation(self.nodeId, rx, ry, rz); |
| 245 | if fillLevel ~= nil then |
| 246 | self.fillLevel = fillLevel; |
| 247 | end |
| 248 | g_currentMission:addItemToSave(self); |
| 249 | end; |
Setting node id and i3d file nameDefinition
loadFromMemory(integer nodeId, string i3dFilename)Arguments
| integer | nodeId | node id |
| string | i3dFilename | i3d file name |
| boolean | isActive | work area is active |
| 255 | function Bale:loadFromMemory(nodeId, i3dFilename) |
| 256 | self.i3dFilename = i3dFilename; |
| 257 | self.customEnvironment, self.baseDirectory = Utils.getModNameAndBaseDirectory(i3dFilename); |
| 258 | self:setNodeId(nodeId); |
| 259 | end; |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | checkSpeedlimit | check speed limit |
| boolean | success | success |
| 267 | function Bale:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 268 | |
| 269 | local x,y,z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#position")); |
| 270 | local xRot,yRot,zRot = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotation")); |
| 271 | if x == nil or y == nil or z == nil or xRot == nil or yRot == nil or zRot == nil then |
| 272 | return false; |
| 273 | end; |
| 274 | xRot = math.rad(xRot) |
| 275 | yRot = math.rad(yRot) |
| 276 | zRot = math.rad(zRot) |
| 277 | |
| 278 | local filename = getXMLString(xmlFile, key.."#filename"); |
| 279 | if filename == nil then |
| 280 | return false; |
| 281 | end; |
| 282 | filename = NetworkUtil.convertFromNetworkFilename(filename); |
| 283 | local rootNode = g_i3DManager:loadSharedI3DFile(filename); |
| 284 | if rootNode == 0 then |
| 285 | return false; |
| 286 | end; |
| 287 | |
| 288 | local ret = false; |
| 289 | local node = getChildAt(rootNode, 0); |
| 290 | if node ~= nil and node ~= 0 then |
| 291 | setTranslation(node, x,y,z); |
| 292 | setRotation(node, xRot,yRot,zRot); |
| 293 | link(getRootNode(), node); |
| 294 | ret = true; |
| 295 | end; |
| 296 | delete(rootNode); |
| 297 | if not ret then |
| 298 | return false; |
| 299 | end; |
| 300 | |
| 301 | self:setOwnerFarmId(Utils.getNoNil(getXMLInt(xmlFile, key .. "#farmId"), AccessHandler.EVERYONE), true) |
| 302 | |
| 303 | self:loadFromMemory(node, filename); |
| 304 | |
| 305 | |
| 306 | local fillLevel = getXMLFloat(xmlFile, key.."#fillLevel"); |
| 307 | if fillLevel ~= nil then |
| 308 | self.fillLevel = fillLevel; |
| 309 | end; |
| 310 | |
| 311 | local attributes = {} |
| 312 | self:loadExtraAttributesFromXMLFile(attributes, xmlFile, key, resetVehicles) |
| 313 | self:applyExtraAttributes(attributes) |
| 314 | |
| 315 | return true; |
| 316 | end; |
Get price value of baleDefinition
getValue()Return Values
| float | dirtMultiplier | current dirt multiplier |
| 357 | function Bale:getValue() |
| 358 | local pricePerLiter = g_currentMission.economyManager:getPricePerLiter(self.fillType); |
| 359 | return self.fillLevel * pricePerLiter * self.baleValueScale; |
| 360 | end; |
Get fill type of baleDefinition
getFillType()Return Values
| float | dirtMultiplier | current wear multiplier |
| integer | fillType | current fill type id |
| 365 | function Bale:getFillType() |
| 366 | return self.fillType; |
| 367 | end; |
Get fill level of baleDefinition
getFillLevel()Return Values
| boolean | success | success |
| integer | fillLevel | current fill level |
| 372 | function Bale:getFillLevel() |
| 373 | return self.fillLevel; |
| 374 | end; |
Set fill level of baleDefinition
setFillLevel(integer fillLevel)Arguments
| integer | fillLevel | fill level |
| float | speedLimit | speed limit |
| 379 | function Bale:setFillLevel(fillLevel) |
| 380 | self.fillLevel = fillLevel; |
| 381 | end; |
Set if bale can be soldDefinition
setCanBeSold(boolean canBeSold)Arguments
| boolean | canBeSold | bale can be sold |
| float | totalMass | total mass |
| 386 | function Bale:setCanBeSold(canBeSold) |
| 387 | self.canBeSold = canBeSold |
| 388 | end |
Returns if bale can be soldDefinition
getCanBeSold()Return Values
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| boolean | canBeSold | bale can be sold |
| 393 | function Bale:getCanBeSold() |
| 394 | return self.canBeSold |
| 395 | end |
Set wrapping state of baleDefinition
setWrappingState(boolean wrappingState)Arguments
| boolean | wrappingState | new wrapping state |
| boolean | allow | allow turn on |
| 400 | function Bale:setWrappingState(wrappingState) |
| 401 | if self.supportsWrapping then |
| 402 | if self.wrappingState ~= wrappingState then |
| 403 | self:raiseDirtyFlags(self.physicsObjectDirtyFlag); |
| 404 | end |
| 405 | self.wrappingState = wrappingState; |
| 406 | setShaderParameter(self.meshNode, "wrappingState", self.wrappingState, 0, 0, 0, false); |
| 407 | end |
| 408 | end |
Set color of baleDefinition
setColor(float r, float g, float b, float a)Arguments
| float | r | red channel value |
| float | g | green channel value |
| float | b | blue channel value |
| float | a | alpha channel value |
| float | dirtMultiplier | current dirt multiplier |
| 416 | function Bale:setColor(r, g, b, a) |
| 417 | r, g, b, a = Utils.getNoNil(r, 1), Utils.getNoNil(g, 1), Utils.getNoNil(b, 1), Utils.getNoNil(a, 1); |
| 418 | self.wrappingColor = {r, g, b, a}; |
| 419 | |
| 420 | if getHasShaderParameter(self.meshNode, "colorScale") then |
| 421 | setShaderParameter(self.meshNode, "colorScale", r, g, b, a, false); |
| 422 | end; |
| 423 | end; |
Set wrapping state of baleDefinition
getMeshNodes()Return Values
| float | dirtMultiplier | current wear multiplier |
| table | meshNodes | mesh nodes |
| 428 | function Bale:getMeshNodes() |
| 429 | return self.meshNodes |
| 430 | end |
Class for basketballsParent
PhysicsObject
Creating basketballDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| boolean | success | success |
| 15 | function Basketball:onCreate(id) |
| 16 | local basketball = Basketball:new(g_server ~= nil, g_client ~= nil) |
| 17 | local x, y, z = getWorldTranslation(id) |
| 18 | local rx, ry, rz = getWorldRotation(id) |
| 19 | local filename = Utils.getNoNil(getUserAttribute(id, "filename"), "$data/objects/basketball/basketball.i3d") |
| 20 | filename = Utils.getFilename(filename, g_currentMission.loadingMapBaseDirectory); |
| 21 | |
| 22 | if basketball:load(filename, x, y, z, rx, ry, rz) then |
| 23 | g_currentMission:addOnCreateLoadedObject(basketball) |
| 24 | basketball:register(true) |
| 25 | else |
| 26 | basketball:delete() |
| 27 | end |
| 28 | end |
Creating basketball objectDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | customMt |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| table | instance | Instance of object |
| 36 | function Basketball:new(isServer, isClient, customMt) |
| 37 | local mt = customMt |
| 38 | if mt == nil then |
| 39 | mt = Basketball_mt |
| 40 | end |
| 41 | |
| 42 | local self = PhysicsObject:new(isServer, isClient, mt) |
| 43 | |
| 44 | self.forcedClipDistance = 150 |
| 45 | registerObjectClassName(self, "Basketball") |
| 46 | |
| 47 | return self |
| 48 | end |
Deleting basketball objectDefinition
delete()Return Values
| boolean | isAllowed | is allowed |
| 52 | function Basketball:delete() |
| 53 | if self.i3dFilename ~= nil then |
| 54 | g_i3DManager:releaseSharedI3DFile(self.i3dFilename, nil, true) |
| 55 | end |
| 56 | unregisterObjectClassName(self) |
| 57 | Basketball:superClass().delete(self) |
| 58 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| string | attributes | attributes |
| 64 | function Basketball:readStream(streamId, connection) |
| 65 | if connection:getIsServer() then |
| 66 | local i3dFilename = NetworkUtil.convertFromNetworkFilename(streamReadString(streamId)) |
| 67 | if self.nodeId == 0 then |
| 68 | self:createNode(i3dFilename) |
| 69 | end |
| 70 | Basketball:superClass().readStream(self, streamId, connection) |
| 71 | end |
| 72 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| boolean | success | success |
| 78 | function Basketball:writeStream(streamId, connection) |
| 79 | if not connection:getIsServer() then |
| 80 | streamWriteString(streamId, NetworkUtil.convertToNetworkFilename(self.i3dFilename)) |
| 81 | Basketball:superClass().writeStream(self, streamId, connection) |
| 82 | end |
| 83 | end |
Load node from i3d fileDefinition
createNode(string i3dFilename)Arguments
| string | i3dFilename | i3d file name |
| float | sellPrice | sell price |
| 88 | function Basketball:createNode(i3dFilename) |
| 89 | self.i3dFilename = i3dFilename |
| 90 | self.customEnvironment, self.baseDirectory = Utils.getModNameAndBaseDirectory(i3dFilename) |
| 91 | local basketballRoot = g_i3DManager:loadSharedI3DFile(i3dFilename) |
| 92 | |
| 93 | local basketballId = getChildAt(basketballRoot, 0) |
| 94 | link(getRootNode(), basketballId) |
| 95 | delete(basketballRoot) |
| 96 | |
| 97 | self:setNodeId(basketballId) |
| 98 | end |
Load BasketballDefinition
load(string i3dFilename, float x, float y, float z, float rx, float ry, float rz)Arguments
| string | i3dFilename | i3d file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | isOnField | is on field |
| 109 | function Basketball:load(i3dFilename, x,y,z, rx,ry,rz) |
| 110 | self.i3dFilename = i3dFilename |
| 111 | self.customEnvironment, self.baseDirectory = Utils.getModNameAndBaseDirectory(i3dFilename) |
| 112 | self:createNode(i3dFilename) |
| 113 | setTranslation(self.nodeId, x, y, z) |
| 114 | setRotation(self.nodeId, rx, ry, rz) |
| 115 | |
| 116 | return true |
| 117 | end; |
Class for bga
Creating bga objectDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| integer | parentComponent | id of parent component node |
| 15 | function Bga:onCreate(id) |
| 16 | g_logManager:error("BGA onCreate is deprecated!") |
| 17 | end |
Creating bga objectDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | customMt |
| float | lastSpeed | last speed |
| table | instance | Instance of object |
| 25 | function Bga:new(isServer, isClient, customMt) |
| 26 | local self = Object:new(isServer, isClient, customMt or Bga_mt) |
| 27 | |
| 28 | self.nodeId = 0 |
| 29 | |
| 30 | self.digestateSilo = {} |
| 31 | |
| 32 | self.bunker = {} |
| 33 | self.bunker.updateTimer = 0 |
| 34 | self.bunker.money = 0 |
| 35 | |
| 36 | self.bgaDirtyFlag = self:getNextDirtyFlag() |
| 37 | |
| 38 | return self |
| 39 | end |
Load bgaDefinition
load(integer nodeId)Arguments
| integer | nodeId | node id |
| table | owner | owner |
| boolean | success | success |
| 45 | function Bga:load(id, xmlFile, key) |
| 46 | |
| 47 | self.nodeId = id |
| 48 | |
| 49 | local siloKey = key..".digestateSilo" |
| 50 | |
| 51 | local loadingNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, siloKey..".loadingStation#node")) |
| 52 | if loadingNode ~= nil then |
| 53 | local loadingStation = LoadingStation:new(self.isServer, self.isClient) |
| 54 | if loadingStation:load(loadingNode, xmlFile, siloKey..".loadingStation") then |
| 55 | self.digestateSilo.loadingStation = loadingStation |
| 56 | end |
| 57 | end |
| 58 | |
| 59 | if self.digestateSilo.loadingStation == nil then |
| 60 | g_logManager:warning("Could not load loading station for '%s.loadingStation'!", siloKey) |
| 61 | return false |
| 62 | end |
| 63 | |
| 64 | local storages = {} |
| 65 | local i = 0 |
| 66 | while true do |
| 67 | local storageKey = string.format("%s.storages.storage(%d)", siloKey, i) |
| 68 | if not hasXMLProperty(xmlFile, storageKey) then |
| 69 | break |
| 70 | end |
| 71 | |
| 72 | local storageNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, storageKey.."#node")) |
| 73 | if storageNode ~= nil then |
| 74 | local storage = Storage:new(self.isServer, self.isClient) |
| 75 | if storage:load(storageNode, xmlFile, storageKey) then |
| 76 | table.insert(storages, storage) |
| 77 | storage:setOwnerFarmId(self:getOwnerFarmId(), true) |
| 78 | else |
| 79 | g_logManager:warning("Could not load storage for '%s'!", storageKey) |
| 80 | end |
| 81 | else |
| 82 | g_logManager:warning("Missing 'node' for storage '%s'!", storageKey) |
| 83 | end |
| 84 | |
| 85 | i = i + 1 |
| 86 | end |
| 87 | |
| 88 | if #storages == 0 then |
| 89 | g_logManager:warning("Missing digestate silo storages for bga!") |
| 90 | return false |
| 91 | end |
| 92 | |
| 93 | self.digestateSilo.storages = storages |
| 94 | |
| 95 | |
| 96 | local bunkerKey = "placeable.bga.bunker" |
| 97 | local unloadingNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, bunkerKey..".unloadingStation#node")) |
| 98 | if unloadingNode ~= nil then |
| 99 | local unloadingStation = BgaSellStation:new(self.isServer, self.isClient, self) |
| 100 | if unloadingStation:load(unloadingNode, xmlFile, bunkerKey..".unloadingStation") then |
| 101 | self.bunker.unloadingStation = unloadingStation |
| 102 | unloadingStation:addTargetStorage(self) |
| 103 | end |
| 104 | end |
| 105 | |
| 106 | if self.bunker.unloadingStation == nil then |
| 107 | g_logManager:warning("Could not load unloading station for '%s.unloadingStation'!", bunkerKey) |
| 108 | return false |
| 109 | end |
| 110 | |
| 111 | self.bunker.slots = {} |
| 112 | self.bunker.fillTypeToSlot = {} |
| 113 | local i = 0 |
| 114 | while true do |
| 115 | local slotKey = string.format("%s.slot(%d)", bunkerKey, i) |
| 116 | if not hasXMLProperty(xmlFile, slotKey) then |
| 117 | break |
| 118 | end |
| 119 | |
| 120 | local slot = {} |
| 121 | slot.capacity = getXMLInt(xmlFile, slotKey.."#capacity") or 40000 |
| 122 | slot.litersPerSecond = getXMLFloat(xmlFile, slotKey.."#litersPerSecond") or 1 |
| 123 | slot.fillTypes = {} |
| 124 | slot.fillLevel = 0 |
| 125 | |
| 126 | slot.display = DigitalDisplay:new() |
| 127 | if not slot.display:load(self.nodeId, xmlFile, slotKey..".display") then |
| 128 | slot.display = nil |
| 129 | else |
| 130 | slot.display:setValue(0) |
| 131 | end |
| 132 | |
| 133 | |
| 134 | local found = false |
| 135 | local j = 0 |
| 136 | while true do |
| 137 | local fillTypeKey = string.format("%s.fillType(%d)", slotKey, j) |
| 138 | if not hasXMLProperty(xmlFile, fillTypeKey) then |
| 139 | break |
| 140 | end |
| 141 | |
| 142 | local fillTypeCategories = getXMLString(xmlFile, fillTypeKey .. "#fillTypeCategories") |
| 143 | local fillTypeNames = getXMLString(xmlFile, fillTypeKey .. "#fillTypes") |
| 144 | local fillTypes |
| 145 | |
| 146 | if fillTypeCategories ~= nil and fillTypeNames == nil then |
| 147 | fillTypes = g_fillTypeManager:getFillTypesByCategoryNames(fillTypeCategories, "Warning: '"..tostring(key).. "' has invalid fillTypeCategory '%s'.") |
| 148 | elseif fillTypeCategories == nil and fillTypeNames ~= nil then |
| 149 | fillTypes = g_fillTypeManager:getFillTypesByNames(fillTypeNames, "Warning: '"..tostring(key).. "' has invalid fillType '%s'.") |
| 150 | else |
| 151 | g_logManager:warning("Missing fillTypeCategories or fillTypes attribute for bga slot '%s'!", slotKey) |
| 152 | end |
| 153 | |
| 154 | for _,fillTypeIndex in pairs(fillTypes) do |
| 155 | if self.bunker.fillTypeToSlot[fillTypeIndex] == nil then |
| 156 | self.bunker.fillTypeToSlot[fillTypeIndex] = slot |
| 157 | |
| 158 | if slot.fillTypes[fillTypeIndex] == nil then |
| 159 | found = true |
| 160 | slot.fillTypes[fillTypeIndex] = {} |
| 161 | slot.fillTypes[fillTypeIndex].fillLevel = 0 |
| 162 | slot.fillTypes[fillTypeIndex].scale = getXMLFloat(xmlFile, fillTypeKey.."#scale") or 1 |
| 163 | slot.fillTypes[fillTypeIndex].pricePerLiter = getXMLFloat(xmlFile, fillTypeKey.."#pricePerLiter") or 1 |
| 164 | else |
| 165 | g_logManager:warning("'%s' already used for '%s'!", g_fillTypeManager:getFillTypeNameByIndex(fillTypeIndex), fillTypeKey) |
| 166 | end |
| 167 | else |
| 168 | g_logManager:warning("'%s' already used by another slot for '%s'!", g_fillTypeManager:getFillTypeNameByIndex(fillTypeIndex), slotKey) |
| 169 | end |
| 170 | end |
| 171 | |
| 172 | j = j + 1 |
| 173 | end |
| 174 | |
| 175 | if found then |
| 176 | table.insert(self.bunker.slots, slot) |
| 177 | else |
| 178 | g_logManager:warning("No fillTypes defined for slot '%s'!", slotKey) |
| 179 | end |
| 180 | |
| 181 | i = i + 1 |
| 182 | end |
| 183 | |
| 184 | self.samples = {} |
| 185 | if self.isClient then |
| 186 | self.samples.work = g_soundManager:loadSampleFromXML(xmlFile, "placeable.bga.sounds", "work", self.baseDirectory, self.nodeId, 0, AudioGroup.ENVIRONMENT, nil, nil) |
| 187 | end |
| 188 | |
| 189 | g_currentMission.environment:addDayChangeListener(self) |
| 190 | |
| 191 | return true |
| 192 | end |
Deleting bga objectDefinition
delete()Return Values
| integer |
| 212 | function Bga:delete() |
| 213 | if self.digestateSilo.loadingStation ~= nil then |
| 214 | self.digestateSilo.loadingStation:delete() |
| 215 | end |
| 216 | |
| 217 | for _, storage in ipairs(self.digestateSilo.storages) do |
| 218 | g_currentMission:removeStorage(storage) |
| 219 | storage:delete() |
| 220 | end |
| 221 | |
| 222 | if self.bunker.unloadingStation ~= nil then |
| 223 | g_currentMission:removeUnloadingStation(self.bunker.unloadingStation) |
| 224 | self.bunker.unloadingStation:delete() |
| 225 | end |
| 226 | |
| 227 | if self.isClient then |
| 228 | for _,sample in pairs(self.samples) do |
| 229 | g_soundManager:deleteSample(sample) |
| 230 | end |
| 231 | end |
| 232 | |
| 233 | g_currentMission.environment:removeDayChangeListener(self) |
| 234 | g_messageCenter:unsubscribeAll(self) |
| 235 | |
| 236 | Bga:superClass().delete(self) |
| 237 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| boolean | isFromVehicle | is from vehicle |
| 289 | function Bga:readStream(streamId, connection) |
| 290 | if connection:getIsServer() then |
| 291 | local loadingStationId = NetworkUtil.readNodeObjectId(streamId) |
| 292 | self.digestateSilo.loadingStation:readStream(streamId, connection) |
| 293 | g_client:finishRegisterObject(self.digestateSilo.loadingStation, loadingStationId) |
| 294 | |
| 295 | for _, storage in ipairs(self.digestateSilo.storages) do |
| 296 | local storageId = NetworkUtil.readNodeObjectId(streamId) |
| 297 | storage:readStream(streamId, connection) |
| 298 | g_client:finishRegisterObject(storage, storageId) |
| 299 | end |
| 300 | |
| 301 | local unloadingStationId = NetworkUtil.readNodeObjectId(streamId) |
| 302 | self.bunker.unloadingStation:readStream(streamId, connection) |
| 303 | g_client:finishRegisterObject(self.bunker.unloadingStation, unloadingStationId) |
| 304 | |
| 305 | for _, slot in ipairs(self.bunker.slots) do |
| 306 | slot.fillLevel = streamReadInt32(streamId) |
| 307 | if slot.display ~= nil then |
| 308 | slot.display:setValue(slot.fillLevel) |
| 309 | end |
| 310 | end |
| 311 | end |
| 312 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| boolean | isOperating | is operating |
| 318 | function Bga:writeStream(streamId, connection) |
| 319 | if not connection:getIsServer() then |
| 320 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.digestateSilo.loadingStation)) |
| 321 | self.digestateSilo.loadingStation:writeStream(streamId, connection) |
| 322 | g_server:registerObjectInStream(connection, self.digestateSilo.loadingStation) |
| 323 | |
| 324 | for _, storage in ipairs(self.digestateSilo.storages) do |
| 325 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(storage)) |
| 326 | storage:writeStream(streamId, connection) |
| 327 | g_server:registerObjectInStream(connection, storage) |
| 328 | end |
| 329 | |
| 330 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.bunker.unloadingStation)) |
| 331 | self.bunker.unloadingStation:writeStream(streamId, connection) |
| 332 | g_server:registerObjectInStream(connection, self.bunker.unloadingStation) |
| 333 | |
| 334 | for _, slot in ipairs(self.bunker.slots) do |
| 335 | streamWriteInt32(streamId, slot.fillLevel) |
| 336 | end |
| 337 | end |
| 338 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| float | totalMass | total mass |
| 345 | function Bga:readUpdateStream(streamId, timestamp, connection) |
| 346 | if connection:getIsServer() then |
| 347 | local fillLevel = 0 |
| 348 | for _, slot in ipairs(self.bunker.slots) do |
| 349 | slot.fillLevel = streamReadInt32(streamId) |
| 350 | fillLevel = fillLevel + slot.fillLevel |
| 351 | if slot.display ~= nil then |
| 352 | slot.display:setValue(slot.fillLevel) |
| 353 | end |
| 354 | end |
| 355 | |
| 356 | if self.isClient then |
| 357 | if fillLevel > 0 then |
| 358 | if not g_soundManager:getIsSamplePlaying(self.samples.work) then |
| 359 | g_soundManager:playSample(self.samples.work) |
| 360 | end |
| 361 | else |
| 362 | if g_soundManager:getIsSamplePlaying(self.samples.work) then |
| 363 | g_soundManager:stopSample(self.samples.work) |
| 364 | end |
| 365 | end |
| 366 | end |
| 367 | end |
| 368 | end |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| boolean | success | success |
| 375 | function Bga:writeUpdateStream(streamId, connection, dirtyMask) |
| 376 | if not connection:getIsServer() then |
| 377 | for _, slot in ipairs(self.bunker.slots) do |
| 378 | streamWriteInt32(streamId, slot.fillLevel) |
| 379 | end |
| 380 | end |
| 381 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| boolean | success | success |
| 389 | function Bga:loadFromXMLFile(xmlFile, key) |
| 390 | self.bunker.money = getXMLFloat(xmlFile, key.."#money") or 0 |
| 391 | |
| 392 | if not self.digestateSilo.loadingStation:loadFromXMLFile(xmlFile, key..".digestateSilo.loadingStation") then |
| 393 | return false |
| 394 | end |
| 395 | |
| 396 | local i = 0 |
| 397 | while true do |
| 398 | local storageKey = string.format("%s.digestateSilo.storage(%d)", key, i) |
| 399 | if not hasXMLProperty(xmlFile, storageKey) then |
| 400 | break |
| 401 | end |
| 402 | |
| 403 | local index = getXMLInt(xmlFile, storageKey.."#index") |
| 404 | if index ~= nil then |
| 405 | if self.digestateSilo.storages[index] ~= nil then |
| 406 | if not self.digestateSilo.storages[index]:loadFromXMLFile(xmlFile, storageKey) then |
| 407 | return false |
| 408 | end |
| 409 | else |
| 410 | g_logManager:warning("Could not load digestateSilo storage. Given 'index' '%d' is not defined!", index) |
| 411 | end |
| 412 | end |
| 413 | |
| 414 | i = i + 1 |
| 415 | end |
| 416 | |
| 417 | local i = 0 |
| 418 | while true do |
| 419 | local slotKey = string.format("%s.slot(%d)", key, i) |
| 420 | if not hasXMLProperty(xmlFile, slotKey) then |
| 421 | break |
| 422 | end |
| 423 | |
| 424 | local index = getXMLInt(xmlFile, slotKey.."#index") |
| 425 | if index ~= nil then |
| 426 | local slot = self.bunker.slots[index] |
| 427 | if slot ~= nil then |
| 428 | local j = 0 |
| 429 | while true do |
| 430 | local fillTypeKey = string.format("%s.fillType(%d)", slotKey, j) |
| 431 | if not hasXMLProperty(xmlFile, fillTypeKey) then |
| 432 | break |
| 433 | end |
| 434 | |
| 435 | local fillTypeName = getXMLString(xmlFile, fillTypeKey.."#fillType") |
| 436 | local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeName) |
| 437 | if fillTypeIndex ~= nil and slot.fillTypes[fillTypeIndex] ~= nil then |
| 438 | slot.fillTypes[fillTypeIndex].fillLevel = getXMLFloat(xmlFile, fillTypeKey.."#fillLevel") or 0 |
| 439 | slot.fillLevel = slot.fillLevel + slot.fillTypes[fillTypeIndex].fillLevel |
| 440 | end |
| 441 | |
| 442 | j = j + 1 |
| 443 | end |
| 444 | end |
| 445 | |
| 446 | if slot.fillLevel > 0 then |
| 447 | self.bunker.isFilled = true |
| 448 | self:raiseActive() |
| 449 | |
| 450 | if slot.display ~= nil then |
| 451 | slot.display:setValue(slot.fillLevel) |
| 452 | end |
| 453 | end |
| 454 | end |
| 455 | |
| 456 | i = i + 1 |
| 457 | end |
| 458 | |
| 459 | return true |
| 460 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| boolean | success | success |
| 489 | function Bga:update(dt) |
| 490 | if self.bunker.isFilled then |
| 491 | self:raiseActive() |
| 492 | end |
| 493 | |
| 494 | if self.isClient then |
| 495 | if self.bunker.isFilled then |
| 496 | if not g_soundManager:getIsSamplePlaying(self.samples.work) then |
| 497 | g_soundManager:playSample(self.samples.work) |
| 498 | end |
| 499 | else |
| 500 | if g_soundManager:getIsSamplePlaying(self.samples.work) then |
| 501 | g_soundManager:stopSample(self.samples.work) |
| 502 | end |
| 503 | end |
| 504 | end |
| 505 | end |
UpdateTickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| string | Schema | overlay name which was modified if necessary |
| 510 | function Bga:updateTick(dt) |
| 511 | if self.isServer and self.bunker.isFilled then |
| 512 | self.bunker.updateTimer = self.bunker.updateTimer + dt |
| 513 | |
| 514 | if self.bunker.updateTimer > 1000 then |
| 515 | self.bunker.isFilled = false |
| 516 | |
| 517 | for _, slot in ipairs(self.bunker.slots) do |
| 518 | slot.fillLevel = 0 |
| 519 | |
| 520 | for _, data in pairs(slot.fillTypes) do |
| 521 | if data.fillLevel > 0 then |
| 522 | local deltaLiters = math.min(slot.litersPerSecond*g_currentMission.missionInfo.timeScale, data.fillLevel) |
| 523 | data.fillLevel = data.fillLevel - deltaLiters |
| 524 | |
| 525 | local converted = deltaLiters * data.scale |
| 526 | if converted > 0 then |
| 527 | self:addDigestate( converted) |
| 528 | end |
| 529 | |
| 530 | if deltaLiters > 0 then |
| 531 | self:raiseDirtyFlags(self.bgaDirtyFlag) |
| 532 | end |
| 533 | end |
| 534 | |
| 535 | slot.fillLevel = slot.fillLevel + data.fillLevel |
| 536 | end |
| 537 | |
| 538 | if slot.fillLevel > 0 then |
| 539 | self.bunker.isFilled = true |
| 540 | end |
| 541 | |
| 542 | if slot.display ~= nil then |
| 543 | slot.display:setValue(slot.fillLevel) |
| 544 | end |
| 545 | end |
| 546 | |
| 547 | self.bunker.updateTimer = 0 |
| 548 | end |
| 549 | end |
| 550 | end |
Class for bunker silo
Creating bunker silo objectDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| float | limit | limit |
| boolean | doCheckSpeedLimit | do check speed limit |
| 23 | function BunkerSilo:onCreate(id) |
| 24 | g_logManager:error("BunkerSilo.onCreate is deprecated!") |
| 25 | end |
Creating bunker silo objectDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | customMt |
| table | instance | Instance of object |
| 33 | function BunkerSilo:new(isServer, isClient, customMt) |
| 34 | |
| 35 | local self = Object:new(isServer, isClient, customMt or BunkerSilo_mt) |
| 36 | |
| 37 | self.nodeId = 0 |
| 38 | self.interactionTriggerNode = nil |
| 39 | |
| 40 | self.bunkerSiloArea = {} |
| 41 | self.bunkerSiloArea.offsetFront = 0 |
| 42 | self.bunkerSiloArea.offsetBack = 0 |
| 43 | |
| 44 | self.acceptedFillTypes = {} |
| 45 | |
| 46 | self.inputFillType = FillType.CHAFF |
| 47 | self.outputFillType = FillType.SILAGE |
| 48 | self.fermentingFillType = FillType.TARP |
| 49 | |
| 50 | self.isOpenedAtFront = false |
| 51 | self.isOpenedAtBack = false |
| 52 | self.distanceToCompactedFillLevel = 100 |
| 53 | |
| 54 | self.fermentingTime = 0 |
| 55 | self.fermentingDuration = 6*60*60*1000 -- 6hours (ingame) |
| 56 | self.fermentingPercent = 0 |
| 57 | |
| 58 | self.fillLevel = 0 |
| 59 | self.compactedFillLevel = 0 |
| 60 | self.compactedPercent = 0 |
| 61 | self.emptyThreshold = 100 |
| 62 | |
| 63 | self.playerInRange = false |
| 64 | self.vehiclesInRange = {} |
| 65 | self.numVehiclesInRange = 0 |
| 66 | |
| 67 | self.siloIsFullWarningTimer = 0 |
| 68 | self.siloIsFullWarningDuration = 2000 |
| 69 | |
| 70 | self.updateTimer = 0 |
| 71 | |
| 72 | self.activatable = BunkerSiloActivatable:new(self) |
| 73 | |
| 74 | self.state = BunkerSilo.STATE_FILL |
| 75 | |
| 76 | self.bunkerSiloDirtyFlag = self:getNextDirtyFlag() |
| 77 | |
| 78 | return self |
| 79 | end |
Load bunker siloDefinition
load(integer nodeId)Arguments
| integer | nodeId | node id |
| float | dailyUpkeep | daily up keep |
| boolean | success | success |
| 85 | function BunkerSilo:load(id, xmlFile, key) |
| 86 | |
| 87 | self.nodeId = id |
| 88 | |
| 89 | self.bunkerSiloArea.start = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".area#startNode")) |
| 90 | self.bunkerSiloArea.width = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".area#widthNode")) |
| 91 | self.bunkerSiloArea.height = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".area#heightNode")) |
| 92 | |
| 93 | self.bunkerSiloArea.sx, self.bunkerSiloArea.sy, self.bunkerSiloArea.sz = getWorldTranslation(self.bunkerSiloArea.start) |
| 94 | self.bunkerSiloArea.wx, self.bunkerSiloArea.wy, self.bunkerSiloArea.wz = getWorldTranslation(self.bunkerSiloArea.width) |
| 95 | self.bunkerSiloArea.hx, self.bunkerSiloArea.hy, self.bunkerSiloArea.hz = getWorldTranslation(self.bunkerSiloArea.height) |
| 96 | |
| 97 | self.bunkerSiloArea.dhx = self.bunkerSiloArea.hx - self.bunkerSiloArea.sx |
| 98 | self.bunkerSiloArea.dhy = self.bunkerSiloArea.hy - self.bunkerSiloArea.sy |
| 99 | self.bunkerSiloArea.dhz = self.bunkerSiloArea.hz - self.bunkerSiloArea.sz |
| 100 | self.bunkerSiloArea.dhx_norm, self.bunkerSiloArea.dhy_norm, self.bunkerSiloArea.dhz_norm = MathUtil.vector3Normalize(self.bunkerSiloArea.dhx, self.bunkerSiloArea.dhy, self.bunkerSiloArea.dhz) |
| 101 | |
| 102 | self.bunkerSiloArea.dwx = self.bunkerSiloArea.wx - self.bunkerSiloArea.sx |
| 103 | self.bunkerSiloArea.dwy = self.bunkerSiloArea.wy - self.bunkerSiloArea.sy |
| 104 | self.bunkerSiloArea.dwz = self.bunkerSiloArea.wz - self.bunkerSiloArea.sz |
| 105 | self.bunkerSiloArea.dwx_norm, self.bunkerSiloArea.dwy_norm, self.bunkerSiloArea.dwz_norm = MathUtil.vector3Normalize(self.bunkerSiloArea.dwx, self.bunkerSiloArea.dwy, self.bunkerSiloArea.dwz) |
| 106 | |
| 107 | self.interactionTriggerNode = I3DUtil.indexToObject(id, getXMLString(xmlFile, key..".interactionTrigger#node")) |
| 108 | if self.interactionTriggerNode ~= nil then |
| 109 | addTrigger(self.interactionTriggerNode, "interactionTriggerCallback", self) |
| 110 | end |
| 111 | |
| 112 | self.acceptedFillTypes = {} |
| 113 | local data = StringUtil.splitString(" ", getXMLString(xmlFile, key.."#acceptedFillTypes") or "chaff grass_windrow dryGrass_windrow") |
| 114 | for i=1, table.getn(data) do |
| 115 | local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(data[i]) |
| 116 | if fillTypeIndex ~= nil then |
| 117 | self.acceptedFillTypes[fillTypeIndex] = true |
| 118 | else |
| 119 | g_logManager:warning("'%s' is an invalid fillType for bunkerSilo '%s'!", tostring(data[i]), key.."#acceptedFillTypes") |
| 120 | end |
| 121 | end |
| 122 | |
| 123 | local inputFillTypeName = getXMLString(xmlFile, key.."#inputFillType") or "chaff" |
| 124 | local inputFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(inputFillTypeName) |
| 125 | if inputFillTypeIndex ~= nil then |
| 126 | self.inputFillType = inputFillTypeIndex |
| 127 | else |
| 128 | g_logManager:warning("'%s' is an invalid input fillType for bunkerSilo '%s'!", tostring(inputFillTypeName), key.."#inputFillType") |
| 129 | end |
| 130 | |
| 131 | local outputFillTypeName = getXMLString(xmlFile, key.."#outputFillType") or "silage" |
| 132 | local outputFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(outputFillTypeName) |
| 133 | if outputFillTypeIndex ~= nil then |
| 134 | self.outputFillType = outputFillTypeIndex |
| 135 | else |
| 136 | g_logManager:warning("'%s' is an invalid output fillType for bunkerSilo '%s'!", tostring(outputFillTypeName), key.."#outputFillType") |
| 137 | end |
| 138 | |
| 139 | if self.isServer then |
| 140 | g_densityMapHeightManager:setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType) |
| 141 | end |
| 142 | |
| 143 | self.distanceToCompactedFillLevel = getXMLFloat(xmlFile, key.."#distanceToCompactedFillLevel") or self.distanceToCompactedFillLevel |
| 144 | self.fermentingDuration = (getXMLFloat(xmlFile, key.."#fermentingHours") or 6) * 1000 * 60 * 60 |
| 145 | self.openingLength = getXMLFloat(xmlFile, key.."#openingLength") or 5 |
| 146 | |
| 147 | self.fillLevel = 0 |
| 148 | |
| 149 | -- adjust timings to difficulty |
| 150 | local difficultyMultiplier = g_currentMission.missionInfo.difficulty |
| 151 | self.fermentingDuration = self.fermentingDuration*difficultyMultiplier |
| 152 | self.distanceToCompactedFillLevel = self.distanceToCompactedFillLevel/difficultyMultiplier |
| 153 | |
| 154 | -- just for tutorial 12 (feeder) |
| 155 | self.isTutorialSilo = Utils.getNoNil(getXMLBool(xmlFile, key.."#isTutorialSilo"), false ) |
| 156 | |
| 157 | self:setState(BunkerSilo.STATE_FILL) |
| 158 | |
| 159 | return true |
| 160 | end |
Deleting bunker silo objectDefinition
delete()Return Values
| string | xml | xml |
| 164 | function BunkerSilo:delete() |
| 165 | g_currentMission:removeOnCreateLoadedObjectToSave(self) |
| 166 | if self.interactionTriggerNode ~= nil then |
| 167 | removeTrigger(self.interactionTriggerNode) |
| 168 | end |
| 169 | |
| 170 | g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea) |
| 171 | g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea) |
| 172 | |
| 173 | g_currentMission:removeActivatableObject(self.activatable) |
| 174 | BunkerSilo:superClass().delete(self) |
| 175 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| table | self | instance of class event |
| 181 | function BunkerSilo:readStream(streamId, connection) |
| 182 | BunkerSilo:superClass().readStream(self, streamId, connection) |
| 183 | if connection:getIsServer() then |
| 184 | local state = streamReadUIntN(streamId, 3) |
| 185 | self:setState(state) |
| 186 | self.isOpenedAtFront = streamReadBool(streamId) |
| 187 | self.isOpenedAtBack = streamReadBool(streamId) |
| 188 | self.fillLevel = streamReadFloat32(streamId) |
| 189 | self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5) |
| 190 | self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5) |
| 191 | end |
| 192 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| table | instance | instance of event |
| 198 | function BunkerSilo:writeStream(streamId, connection) |
| 199 | BunkerSilo:superClass().writeStream(self, streamId, connection) |
| 200 | if not connection:getIsServer() then |
| 201 | streamWriteUIntN(streamId, self.state, 3) |
| 202 | streamWriteBool(streamId, self.isOpenedAtFront) |
| 203 | streamWriteBool(streamId, self.isOpenedAtBack) |
| 204 | streamWriteFloat32(streamId, self.fillLevel) |
| 205 | streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8) |
| 206 | streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8) |
| 207 | end |
| 208 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| table | self | instance of class event |
| 215 | function BunkerSilo:readUpdateStream(streamId, timestamp, connection) |
| 216 | BunkerSilo:superClass().readUpdateStream(self, streamId, timestamp, connection) |
| 217 | if connection:getIsServer() then |
| 218 | if streamReadBool(streamId) then |
| 219 | local state = streamReadUIntN(streamId, 3) |
| 220 | if state ~= self.state then |
| 221 | self:setState(state, true) |
| 222 | end |
| 223 | |
| 224 | self.fillLevel = streamReadFloat32(streamId) |
| 225 | self.isOpenedAtFront = streamReadBool(streamId) |
| 226 | self.isOpenedAtBack = streamReadBool(streamId) |
| 227 | |
| 228 | if self.state == BunkerSilo.STATE_FILL then |
| 229 | self.compactedPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5) |
| 230 | elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then |
| 231 | self.fermentingPercent = math.floor( (streamReadUIntN(streamId, 8) / 2.55) + 0.5) |
| 232 | end |
| 233 | end |
| 234 | end |
| 235 | end |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| table | instance | instance of event |
| 242 | function BunkerSilo:writeUpdateStream(streamId, connection, dirtyMask) |
| 243 | BunkerSilo:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
| 244 | if not connection:getIsServer() then |
| 245 | if streamWriteBool(streamId, bitAND(dirtyMask, self.bunkerSiloDirtyFlag) ~= 0) then |
| 246 | streamWriteUIntN(streamId, self.state, 3) |
| 247 | |
| 248 | streamWriteFloat32(streamId, self.fillLevel) |
| 249 | streamWriteBool(streamId, self.isOpenedAtFront) |
| 250 | streamWriteBool(streamId, self.isOpenedAtBack) |
| 251 | |
| 252 | if self.state == BunkerSilo.STATE_FILL then |
| 253 | streamWriteUIntN(streamId, 2.55*self.compactedPercent, 8) |
| 254 | elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then |
| 255 | streamWriteUIntN(streamId, 2.55*self.fermentingPercent, 8) |
| 256 | end |
| 257 | end |
| 258 | end |
| 259 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| table | self | instance of class event |
| boolean | success | success |
| 267 | function BunkerSilo:loadFromXMLFile(xmlFile, key) |
| 268 | |
| 269 | local state = getXMLInt(xmlFile, key.."#state") |
| 270 | if state ~= nil then |
| 271 | if state >= 0 and state < BunkerSilo.NUM_STATES then |
| 272 | self:setState(state) |
| 273 | end |
| 274 | end |
| 275 | |
| 276 | local fillLevel = getXMLFloat(xmlFile, key.."#fillLevel") |
| 277 | if fillLevel ~= nil then |
| 278 | self.fillLevel = fillLevel |
| 279 | end |
| 280 | local compactedFillLevel = getXMLFloat(xmlFile, key.."#compactedFillLevel") |
| 281 | if compactedFillLevel ~= nil then |
| 282 | self.compactedFillLevel = MathUtil.clamp(compactedFillLevel, 0, self.fillLevel) |
| 283 | end |
| 284 | self.compactedPercent = MathUtil.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel) |
| 285 | |
| 286 | local fermentingTime = getXMLFloat(xmlFile, key.."#fermentingTime") |
| 287 | if fermentingTime ~= nil then |
| 288 | self.fermentingTime = MathUtil.clamp(fermentingTime, 0, self.fermentingDuration) |
| 289 | self.fermentingPercent = MathUtil.getFlooredPercent(self.fermentingTime, self.fermentingDuration) |
| 290 | end |
| 291 | |
| 292 | self.isOpenedAtFront = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtFront"), false) |
| 293 | self.isOpenedAtBack = Utils.getNoNil(getXMLBool(xmlFile, key.."#openedAtBack"), false) |
| 294 | |
| 295 | if self.isOpenedAtFront then |
| 296 | self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.outputFillType) |
| 297 | else |
| 298 | self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType) |
| 299 | end |
| 300 | if self.isOpenedAtBack then |
| 301 | self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.outputFillType) |
| 302 | else |
| 303 | self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType) |
| 304 | end |
| 305 | |
| 306 | if self.fillLevel > 0 and self.state == BunkerSilo.STATE_DRAIN then |
| 307 | local area = self.bunkerSiloArea |
| 308 | local offWx = area.wx - area.sx |
| 309 | local offWz = area.wz - area.sz |
| 310 | local offW = math.sqrt(offWx*offWx + offWz*offWz) |
| 311 | |
| 312 | local offHx = area.hx - area.sx |
| 313 | local offHz = area.hz - area.sz |
| 314 | local offH = math.sqrt(offHx*offHx + offHz*offHz) |
| 315 | |
| 316 | if offW > 0.001 and offH > 0.001 then |
| 317 | -- offset by 0.9m in each direction (and max 45%) |
| 318 | local offWScale = math.min(0.45, 0.9 / offW) |
| 319 | offWx = offWx * offWScale |
| 320 | offWz = offWz * offWScale |
| 321 | |
| 322 | local offHScale = math.min(0.45, 0.9 / offH) |
| 323 | offHx = offHx * offHScale |
| 324 | offHz = offHz * offHScale |
| 325 | |
| 326 | local innerFillLevel1 = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx+offWx+offHx,area.sz+offWz+offHz, area.wx-offWx+offHx,area.wz-offWz+offHz, area.hx+offWx-offHx,area.hz+offWz-offHz) |
| 327 | local innerFillLevel2 = DensityMapHeightUtil.getFillLevelAtArea(self.outputFillType, area.sx+offWx+offHx,area.sz+offWz+offHz, area.wx-offWx+offHx,area.wz-offWz+offHz, area.hx+offWx-offHx,area.hz+offWz-offHz) |
| 328 | local innerFillLevel = innerFillLevel1 + innerFillLevel2 |
| 329 | if innerFillLevel < self.emptyThreshold*0.5 then |
| 330 | DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.fermentingFillType) |
| 331 | DensityMapHeightUtil.removeFromGroundByArea(area.sx,area.sz, area.wx,area.wz, area.hx,area.hz, self.outputFillType) |
| 332 | self:setState(BunkerSilo.STATE_FILL, false) |
| 333 | end |
| 334 | end |
| 335 | elseif self.state == BunkerSilo.STATE_FILL then |
| 336 | local area = self.bunkerSiloArea |
| 337 | local fermentingFillLevel, fermentingPixels, totalFermentingPixels = DensityMapHeightUtil.getFillLevelAtArea(self.fermentingFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
| 338 | -- Set to fermented state if more than 50% of the area is filled with fermenting fill type |
| 339 | if fermentingFillLevel > self.emptyThreshold and fermentingPixels > 0.5 * totalFermentingPixels then |
| 340 | local inputFillLevel, inputPixels, totalInputPixels = DensityMapHeightUtil.getFillLevelAtArea(self.inputFillType, area.sx,area.sz, area.wx,area.wz, area.hx,area.hz) |
| 341 | -- Only change if less than 10% is filled with input type (chaff) (ie. the silo is not being filled) |
| 342 | if inputPixels < 0.1*totalInputPixels then |
| 343 | self:setState(BunkerSilo.STATE_FERMENTED, false) |
| 344 | end |
| 345 | end |
| 346 | end |
| 347 | |
| 348 | return true |
| 349 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| table | instance | instance of event |
| 363 | function BunkerSilo:update(dt) |
| 364 | |
| 365 | if self:getCanInteract(true) then |
| 366 | local fillTypeIndex = self.inputFillType |
| 367 | if self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN then |
| 368 | fillTypeIndex = self.outputFillType |
| 369 | end |
| 370 | local fillTypeName = "" |
| 371 | local fillType = g_fillTypeManager:getFillTypeByIndex(fillTypeIndex) |
| 372 | if fillType ~= nil then |
| 373 | fillTypeName = fillType.title |
| 374 | end |
| 375 | if self.state == BunkerSilo.STATE_FILL then |
| 376 | g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel)) |
| 377 | g_currentMission:addExtraPrintText(g_i18n:getText("info_compacting")..string.format(" %d%%", self.compactedPercent)) |
| 378 | elseif self.state == BunkerSilo.STATE_CLOSED or self.state == BunkerSilo.STATE_FERMENTED then |
| 379 | g_currentMission:addExtraPrintText(g_i18n:getText("info_fermenting")..string.format(" %s: %d%%", fillTypeName, self.fermentingPercent)) |
| 380 | elseif self.state == BunkerSilo.STATE_DRAIN then |
| 381 | g_currentMission:addExtraPrintText(g_i18n:getText("info_fillLevel")..string.format(" %s: %d", fillTypeName, self.fillLevel)) |
| 382 | end |
| 383 | end |
| 384 | |
| 385 | if self.state == BunkerSilo.STATE_CLOSED then |
| 386 | if self.isServer then |
| 387 | self.fermentingTime = math.min(self.fermentingDuration, self.fermentingTime + dt*g_currentMission.missionInfo.timeScale) |
| 388 | local fermentingPercent = MathUtil.getFlooredPercent(self.fermentingTime, self.fermentingDuration) |
| 389 | if fermentingPercent ~= self.fermentingPercent then |
| 390 | self.fermentingPercent = fermentingPercent |
| 391 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
| 392 | end |
| 393 | if self.fermentingTime >= self.fermentingDuration then |
| 394 | self:setState(BunkerSilo.STATE_FERMENTED, true) |
| 395 | end |
| 396 | end |
| 397 | end |
| 398 | |
| 399 | if self.isServer then |
| 400 | if self.state == BunkerSilo.STATE_FILL then |
| 401 | for vehicle,state in pairs(self.vehiclesInRange) do |
| 402 | if state then |
| 403 | if vehicle:getIsActive() then |
| 404 | local distance = vehicle.lastMovedDistance |
| 405 | if distance > 0 then |
| 406 | local mass = vehicle:getTotalMass(false) |
| 407 | |
| 408 | local refNode = vehicle.components[1].node |
| 409 | local scale = (mass / BunkerSilo.COMPACTING_BASE_MASS) |
| 410 | |
| 411 | local bunkerSiloCompacter |
| 412 | if vehicle.getBunkerSiloCompacter ~= nil then |
| 413 | bunkerSiloCompacter = vehicle:getBunkerSiloCompacter() |
| 414 | end |
| 415 | if bunkerSiloCompacter ~= nil then |
| 416 | if bunkerSiloCompacter.refNode ~= nil then |
| 417 | refNode = bunkerSiloCompacter.refNode |
| 418 | end |
| 419 | if bunkerSiloCompacter.scale ~= nil then |
| 420 | scale = (mass / BunkerSilo.COMPACTING_BASE_MASS) * bunkerSiloCompacter.scale |
| 421 | end |
| 422 | end |
| 423 | |
| 424 | local deltaCompact = distance*scale*self.distanceToCompactedFillLevel |
| 425 | |
| 426 | local wheels = vehicle:getWheels() |
| 427 | local numWheels = #wheels |
| 428 | if numWheels > 0 then |
| 429 | local wheelsOnSilo = 0 |
| 430 | local wheelsInAir = 0 |
| 431 | for _, wheel in ipairs(wheels) do |
| 432 | if wheel.contact == Wheels.WHEEL_GROUND_HEIGHT_CONTACT then |
| 433 | wheelsOnSilo = wheelsOnSilo + 1 |
| 434 | elseif wheel.contact == Wheels.WHEEL_NO_CONTACT then |
| 435 | wheelsInAir = wheelsInAir + 1 |
| 436 | end |
| 437 | end |
| 438 | if wheelsOnSilo > 0 then |
| 439 | deltaCompact = deltaCompact * ((wheelsOnSilo + wheelsInAir) / numWheels) |
| 440 | else |
| 441 | deltaCompact = 0 |
| 442 | end |
| 443 | end |
| 444 | |
| 445 | if deltaCompact > 0 then |
| 446 | local compactedFillLevel = math.min(self.compactedFillLevel + deltaCompact, self.fillLevel) |
| 447 | if compactedFillLevel ~= self.compactedFillLevel then |
| 448 | self.compactedFillLevel = compactedFillLevel |
| 449 | self.compactedPercent = MathUtil.getFlooredPercent(math.min(self.compactedFillLevel, self.fillLevel), self.fillLevel) |
| 450 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
| 451 | end |
| 452 | end |
| 453 | end |
| 454 | end |
| 455 | end |
| 456 | end |
| 457 | end |
| 458 | end |
| 459 | |
| 460 | -- for chaff tutorial: always take the highest fill level of all bunker silos |
| 461 | if g_currentMission ~= nil and g_currentMission.bunkerScore ~= nil then |
| 462 | if g_currentMission.bunkerScore < self.fillLevel then |
| 463 | g_currentMission.bunkerScore = self.fillLevel |
| 464 | end |
| 465 | end |
| 466 | |
| 467 | self:raiseActive() |
| 468 | end |
UpdateTickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| table | instance | Instance of object |
| 473 | function BunkerSilo:updateTick(dt) |
| 474 | if self.isServer then |
| 475 | self.updateTimer = self.updateTimer - dt |
| 476 | if self.updateTimer <= 0 then |
| 477 | self.updateTimer = 200 + math.random()*100 -- update every 200 to 300ms |
| 478 | |
| 479 | local oldFillLevel = self.fillLevel |
| 480 | self:updateFillLevel() |
| 481 | if oldFillLevel ~= self.fillLevel then |
| 482 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
| 483 | end |
| 484 | end |
| 485 | end |
| 486 | if not self.adjustedOpeningLength then |
| 487 | self.adjustedOpeningLength = true |
| 488 | self.openingLength = math.max(self.openingLength, DensityMapHeightUtil.getDefaultMaxRadius(self.outputFillType)+1) |
| 489 | end |
| 490 | end |
Set stateDefinition
setState(boolean state, boolean showNotification)Arguments
| boolean | state | new state |
| boolean | showNotification | show notification |
| boolean | success | success |
| 524 | function BunkerSilo:setState(state, showNotification) |
| 525 | |
| 526 | if state ~= self.state then |
| 527 | |
| 528 | if state == BunkerSilo.STATE_FILL then |
| 529 | |
| 530 | self.fermentingTime = 0 |
| 531 | self.fermentingPercent = 0 |
| 532 | self.compactedFillLevel = 0 |
| 533 | self.isOpenedAtFront = false |
| 534 | self.isOpenedAtBack = false |
| 535 | self.bunkerSiloArea.offsetFront = 0 |
| 536 | self.bunkerSiloArea.offsetBack = 0 |
| 537 | |
| 538 | if showNotification then |
| 539 | g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloIsEmpty")) |
| 540 | end |
| 541 | |
| 542 | if self.isServer then |
| 543 | g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea) |
| 544 | g_densityMapHeightManager:setConvertingFillTypeAreas(self.bunkerSiloArea, self.acceptedFillTypes, self.inputFillType) |
| 545 | end |
| 546 | |
| 547 | elseif state == BunkerSilo.STATE_CLOSED then |
| 548 | |
| 549 | if self.isServer then |
| 550 | -- change fillType |
| 551 | local area = self.bunkerSiloArea |
| 552 | local offsetFront = self:getBunkerAreaOffset(true, 0, self.inputFillType) |
| 553 | local offsetBack = self:getBunkerAreaOffset(false, 0, self.inputFillType) |
| 554 | |
| 555 | local x0 = area.sx + (offsetFront * area.dhx_norm) |
| 556 | local z0 = area.sz + (offsetFront * area.dhz_norm) |
| 557 | local x1 = x0 + area.dwx |
| 558 | local z1 = z0 + area.dwz |
| 559 | local x2 = area.sx + area.dhx - (offsetBack * area.dhx_norm) |
| 560 | local z2 = area.sz + area.dhz - (offsetBack * area.dhz_norm) |
| 561 | |
| 562 | local changed = DensityMapHeightUtil.changeFillTypeAtArea(x0,z0, x1,z1, x2,z2, self.inputFillType, self.fermentingFillType) |
| 563 | |
| 564 | g_densityMapHeightManager:removeFixedFillTypesArea(self.bunkerSiloArea) |
| 565 | g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea) |
| 566 | end |
| 567 | |
| 568 | if showNotification then |
| 569 | g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloCovered")) |
| 570 | end |
| 571 | |
| 572 | elseif state == BunkerSilo.STATE_FERMENTED then |
| 573 | |
| 574 | if showNotification then |
| 575 | g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloDoneFermenting")) |
| 576 | end |
| 577 | |
| 578 | elseif state == BunkerSilo.STATE_DRAIN then |
| 579 | |
| 580 | self.bunkerSiloArea.offsetFront = 0 |
| 581 | self.bunkerSiloArea.offsetBack = 0 |
| 582 | |
| 583 | if showNotification then |
| 584 | g_currentMission:addIngameNotification(FSBaseMission.INGAME_NOTIFICATION_INFO, g_i18n:getText("ingameNotification_bunkerSiloOpened")) |
| 585 | end |
| 586 | |
| 587 | if self.isServer then |
| 588 | g_densityMapHeightManager:removeConvertingFillTypeAreas(self.bunkerSiloArea) |
| 589 | local fillTypes = {} |
| 590 | fillTypes[self.outputFillType] = true |
| 591 | g_densityMapHeightManager:setFixedFillTypesArea(self.bunkerSiloArea, fillTypes) |
| 592 | end |
| 593 | |
| 594 | end |
| 595 | |
| 596 | self.state = state |
| 597 | if self.isServer then |
| 598 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
| 599 | end |
| 600 | end |
| 601 | end |
Open siloDefinition
openSilo(float px, float py, float pz)Arguments
| float | px | x player position |
| float | py | y player position |
| float | pz | z player position |
| float | newX | new x position |
| float | newY | new y position |
| float | newZ | new z position |
| 608 | function BunkerSilo:openSilo(px,py,pz) |
| 609 | self:setState(BunkerSilo.STATE_DRAIN, true) |
| 610 | |
| 611 | self.bunkerSiloArea.offsetFront = self:getBunkerAreaOffset(true, 0, self.fermentingFillType) |
| 612 | self.bunkerSiloArea.offsetBack = self:getBunkerAreaOffset(false, 0, self.fermentingFillType) |
| 613 | |
| 614 | -- check which side is closer to player |
| 615 | local openAtFront = self:getIsCloserToFront(px,py,pz) |
| 616 | if openAtFront and not self.isOpenedAtFront then |
| 617 | self:switchFillTypeAtOffset(true, self.bunkerSiloArea.offsetFront, self.openingLength) |
| 618 | self.isOpenedAtFront = true |
| 619 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
| 620 | elseif not self.isOpenedAtBack then |
| 621 | self:switchFillTypeAtOffset(false, self.bunkerSiloArea.offsetBack, self.openingLength) |
| 622 | self.isOpenedAtBack = true |
| 623 | self:raiseDirtyFlags(self.bunkerSiloDirtyFlag) |
| 624 | end |
| 625 | end |
Get bunker area offsetDefinition
getBunkerAreaOffset(boolean updateAtFront, float offset, integer fillType)Arguments
| boolean | updateAtFront | update at front |
| float | offset | offset |
| integer | fillType | fill type |
| float | offset | offset |
| 633 | function BunkerSilo:getBunkerAreaOffset(updateAtFront, offset, fillType) |
| 634 | local area = self.bunkerSiloArea |
| 635 | |
| 636 | local hx, hz = area.dhx_norm, area.dhz_norm |
| 637 | local hl = MathUtil.vector3Length(area.dhx, area.dhy, area.dhz) |
| 638 | |
| 639 | while offset <= (hl - 1) do |
| 640 | local pos = offset |
| 641 | if not updateAtFront then |
| 642 | pos = hl - offset - 1 |
| 643 | end |
| 644 | local d1x,d1z = pos*hx, pos*hz |
| 645 | local d2x,d2z = (pos+1)*hx, (pos+1)*hz |
| 646 | |
| 647 | local a0x, a0z = area.sx + d1x, area.sz + d1z |
| 648 | local a1x, a1z = area.wx + d1x, area.wz + d1z |
| 649 | local a2x, a2z = area.sx + d2x, area.sz + d2z |
| 650 | |
| 651 | local fillLevel = DensityMapHeightUtil.getFillLevelAtArea(fillType, a0x,a0z, a1x,a1z, a2x,a2z) |
| 652 | if fillLevel > 0 then |
| 653 | return offset |
| 654 | end |
| 655 | offset = offset + 1 |
| 656 | end |
| 657 | |
| 658 | return math.max(hl - 1, 0) |
| 659 | end |
Switch fill type at offsetDefinition
switchFillTypeAtOffset(boolean switchAtFront, float offset, float length)Arguments
| boolean | switchAtFront | switch at front |
| float | offset | offset |
| float | length | length |
| 666 | function BunkerSilo:switchFillTypeAtOffset(switchAtFront, offset, length) |
| 667 | |
| 668 | local fillType = self.fermentingFillType |
| 669 | local newFillType = self.outputFillType |
| 670 | |
| 671 | local a0x, a0z = nil, nil |
| 672 | local a1x, a1z = nil, nil |
| 673 | local a2x, a2z = nil, nil |
| 674 | |
| 675 | local area = self.bunkerSiloArea |
| 676 | |
| 677 | if switchAtFront then |
| 678 | a0x, a0z = area.sx + (offset * area.dhx_norm), area.sz + (offset * area.dhz_norm) |
| 679 | a1x, a1z = a0x + area.dwx, a0z + area.dwz |
| 680 | a2x, a2z = area.sx + ((offset + length) * area.dhx_norm), area.sz + ((offset + length) * area.dhz_norm) |
| 681 | else |
| 682 | a0x, a0z = area.hx - (offset * area.dhx_norm), area.hz - (offset * area.dhz_norm) |
| 683 | a1x, a1z = a0x + area.dwx, a0z + area.dwz |
| 684 | a2x, a2z = area.hx - ((offset + length) * area.dhx_norm), area.hz - ((offset + length) * area.dhz_norm) |
| 685 | end |
| 686 | |
| 687 | DensityMapHeightUtil.changeFillTypeAtArea(a0x,a0z, a1x,a1z, a2x,a2z, fillType, newFillType) |
| 688 | |
| 689 | end |
Get is closer to frontDefinition
getIsCloserToFront(float ix, float iy, float iz)Arguments
| float | ix | x position |
| float | iy | y position |
| float | iz | z position |
| boolean | hasCollision | has collision |
| float | collisionDistance | distance to collision |
| float | normalX | normal x |
| float | normalY | normal y |
| float | normalZ | normal z |
| float | normalDotDir | normal dot direction |
| boolean | isCloserToFront | is closer to front |
| 697 | function BunkerSilo:getIsCloserToFront(ix,iy,iz) |
| 698 | local area = self.bunkerSiloArea |
| 699 | |
| 700 | local x = area.sx + (0.5*area.dwx) + (area.offsetFront * area.dhx_norm) |
| 701 | local y = area.sy + (0.5*area.dwy) + (area.offsetFront * area.dhy_norm) |
| 702 | local z = area.sz + (0.5*area.dwz) + (area.offsetFront * area.dhz_norm) |
| 703 | local distFront = MathUtil.vector3Length(x-ix, y-iy, z-iz) |
| 704 | |
| 705 | local x = area.sx + (0.5*area.dwx) + area.dhx - (area.offsetBack * area.dhx_norm) |
| 706 | local y = area.sy + (0.5*area.dwy) + area.dhy - (area.offsetBack * area.dhy_norm) |
| 707 | local z = area.sz + (0.5*area.dwz) + area.dhz - (area.offsetBack * area.dhz_norm) |
| 708 | local distBack = MathUtil.vector3Length(x-ix, y-iy, z-iz) |
| 709 | |
| 710 | return distFront < distBack |
| 711 | end |
Get can interact with siloDefinition
getCanInteract(boolean showInformationOnly)Arguments
| boolean | showInformationOnly | show information only |
| boolean | canInteract | can interact |
| 717 | function BunkerSilo:getCanInteract(showInformationOnly) |
| 718 | if showInformationOnly then |
| 719 | if (g_currentMission.controlPlayer and self.playerInRange) then |
| 720 | return true |
| 721 | end |
| 722 | if not g_currentMission.controlPlayer then |
| 723 | for vehicle in pairs(self.vehiclesInRange) do |
| 724 | if vehicle:getIsActiveForInput(true) then |
| 725 | return true |
| 726 | end |
| 727 | end |
| 728 | end |
| 729 | else |
| 730 | if (g_currentMission.controlPlayer and self.playerInRange) then |
| 731 | --if next(self.vehiclesInRange) == nil then |
| 732 | return true |
| 733 | --end |
| 734 | end |
| 735 | end |
| 736 | return false |
| 737 | end |
Get can close siloDefinition
getCanCloseSilo()Return Values
| boolean | canClose | can close silo |
| 742 | function BunkerSilo:getCanCloseSilo() |
| 743 | return self.state == BunkerSilo.STATE_FILL and self.fillLevel > 0 and self.compactedPercent >= 100 |
| 744 | end |
Get can open siloDefinition
getCanOpenSilo()Return Values
| boolean | canOpen | can open silo |
| 749 | function BunkerSilo:getCanOpenSilo() |
| 750 | if not (self.state == BunkerSilo.STATE_FERMENTED or self.state == BunkerSilo.STATE_DRAIN) then |
| 751 | return false |
| 752 | end |
| 753 | local ix,iy,iz = self:getInteractionPosition() |
| 754 | if ix ~= nil then |
| 755 | local closerToFront = self:getIsCloserToFront(ix,iy,iz) |
| 756 | if closerToFront and not self.isOpenedAtFront then |
| 757 | return true |
| 758 | end |
| 759 | if not closerToFront and not self.isOpenedAtBack then |
| 760 | return true |
| 761 | end |
| 762 | end |
| 763 | return false |
| 764 | end |
Get interact positionDefinition
getInteractionPosition()Return Values
| float | x | x world position |
| float | y | y world position |
| float | z | z world position |
| 778 | function BunkerSilo:getInteractionPosition() |
| 779 | if g_currentMission.controlPlayer and self.playerInRange then |
| 780 | return getWorldTranslation(g_currentMission.player.rootNode) |
| 781 | else |
| 782 | if self.vehiclesInRange[g_currentMission.currentVehicle] ~= nil then |
| 783 | return getWorldTranslation(self.vehiclesInRange[g_currentMission.currentVehicle].components[1].node) |
| 784 | end |
| 785 | end |
| 786 | return nil |
| 787 | end |
interactionTriggerCallbackDefinition
interactionTriggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay, integer otherId)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| integer | otherId | id of other actor |
| 797 | function BunkerSilo:interactionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId) |
| 798 | if onEnter or onLeave then |
| 799 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
| 800 | if onEnter then |
| 801 | self.playerInRange = true |
| 802 | g_currentMission:removeActivatableObject(self.activatable) -- make sure it is not added twice |
| 803 | g_currentMission:addActivatableObject(self.activatable) |
| 804 | else |
| 805 | self.playerInRange = false |
| 806 | if self.numVehiclesInRange == 0 then |
| 807 | g_currentMission:removeActivatableObject(self.activatable) |
| 808 | end |
| 809 | end |
| 810 | else |
| 811 | --local vehicle = g_currentMission.nodeToObject[otherId] |
| 812 | local vehicle = g_currentMission.nodeToObject[otherShapeId] |
| 813 | if vehicle ~= nil then |
| 814 | if onEnter then |
| 815 | if self.vehiclesInRange[vehicle] == nil then |
| 816 | self.vehiclesInRange[vehicle] = true |
| 817 | self.numVehiclesInRange = self.numVehiclesInRange + 1 |
| 818 | |
| 819 | g_currentMission:removeActivatableObject(self.activatable) -- make sure it is not added twice |
| 820 | g_currentMission:addActivatableObject(self.activatable) |
| 821 | |
| 822 | -- add callback if shovel |
| 823 | if vehicle.setChangedFillLevelCallback ~= nil then |
| 824 | vehicle:setChangedFillLevelCallback(BunkerSilo.onChangedFillLevelCallback, self) |
| 825 | end |
| 826 | end |
| 827 | else |
| 828 | if self.vehiclesInRange[vehicle] then |
| 829 | self.vehiclesInRange[vehicle] = nil |
| 830 | self.numVehiclesInRange = self.numVehiclesInRange - 1 |
| 831 | |
| 832 | if self.numVehiclesInRange == 0 and not self.playerInRange then |
| 833 | g_currentMission:removeActivatableObject(self.activatable) |
| 834 | end |
| 835 | |
| 836 | -- remove callback if shovel |
| 837 | if vehicle.setChangedFillLevelCallback ~= nil then |
| 838 | vehicle:setChangedFillLevelCallback(nil) |
| 839 | end |
| 840 | end |
| 841 | end |
| 842 | end |
| 843 | end |
| 844 | end |
| 845 | end |
Called if fill level changedDefinition
onChangedFillLevelCallback(table vehicle, integer fillDelta, integer fillType)Arguments
| table | vehicle | vehicle |
| integer | fillDelta | fill delta |
| integer | fillType | fill type |
| table | self | instance of class event |
| 852 | function BunkerSilo.onChangedFillLevelCallback(self, vehicle, fillDelta, fillType) |
| 853 | if fillDelta >= 0 then |
| 854 | return |
| 855 | end |
| 856 | |
| 857 | local area = self.bunkerSiloArea |
| 858 | |
| 859 | local x,y,z = getWorldTranslation(vehicle.components[1].node) |
| 860 | local closerToFront = self:getIsCloserToFront(x,y,z) |
| 861 | |
| 862 | if vehicle.shovel ~= nil and vehicle.shovel.pickUp ~= nil and vehicle.shovel.pickUp.node ~= nil then |
| 863 | x,y,z = localToWorld(vehicle.shovel.pickUp.node, 0, 0, vehicle.shovel.pickUp.length) |
| 864 | end |
| 865 | |
| 866 | local length = self.openingLength |
| 867 | |
| 868 | if closerToFront then |
| 869 | if self.isOpenedAtFront then |
| 870 | local p1 = MathUtil.getProjectOnLineParameter(x,z, area.sx,area.sz, area.dhx_norm,area.dhz_norm) |
| 871 | if p1 > area.offsetFront - length then |
| 872 | local offset = self:getBunkerAreaOffset(true, area.offsetFront, self.fermentingFillType) |
| 873 | local targetOffset = math.max(p1, offset) + length |
| 874 | |
| 875 | self:switchFillTypeAtOffset(true, area.offsetFront, targetOffset - area.offsetFront) |
| 876 | area.offsetFront = targetOffset |
| 877 | end |
| 878 | end |
| 879 | else |
| 880 | if self.isOpenedAtBack then |
| 881 | local p1 = MathUtil.getProjectOnLineParameter(x,z, area.hx,area.hz, -area.dhx_norm,-area.dhz_norm) |
| 882 | if p1 > area.offsetBack - length then |
| 883 | local offset = self:getBunkerAreaOffset(true, area.offsetBack, self.fermentingFillType) |
| 884 | local targetOffset = math.max(p1, offset) + length |
| 885 | |
| 886 | self:switchFillTypeAtOffset(false, area.offsetBack, targetOffset - area.offsetBack) |
| 887 | area.offsetBack = targetOffset |
| 888 | end |
| 889 | end |
| 890 | end |
| 891 | end |
Class for field definitions
Create field definition objectDefinition
new()Return Values
| table | instance | Instance of object |
| 25 | function Farmland:new(customMt) |
| 26 | local self = setmetatable({}, customMt or Farmland_mt) |
| 27 | |
| 28 | self.isOwned = false |
| 29 | self.xWorldPos = 0 |
| 30 | self.zWorldPos = 0 |
| 31 | |
| 32 | return self |
| 33 | end |
Load farmland data from xmlDefinition
load(integer xmlFile, string key)Arguments
| integer | xmlFile | handle of xml file |
| string | key | current xml element key |
| boolean | true | if loading was successful else false |
| 40 | function Farmland:load(xmlFile, key) |
| 41 | self.id = getXMLInt(xmlFile, key.."#id") |
| 42 | |
| 43 | if self.id == nil or self.id == 0 then |
| 44 | print("Error: Invalid farmland id '"..tostring(self.id).."'!") |
| 45 | return false |
| 46 | end |
| 47 | |
| 48 | self.name = Utils.getNoNil(getXMLString(xmlFile, key.."#name"), "") |
| 49 | self.areaInHa = Utils.getNoNil(getXMLFloat(xmlFile, key.."#areaInHa"), 2.5) |
| 50 | |
| 51 | self.fixedPrice = getXMLFloat(xmlFile, key.."#price") |
| 52 | if self.fixedPrice == nil then |
| 53 | self.priceFactor = Utils.getNoNil(getXMLFloat(xmlFile, key.."#priceScale"), 1) |
| 54 | end |
| 55 | self.price = self.fixedPrice or 1 |
| 56 | |
| 57 | self:updatePrice() |
| 58 | |
| 59 | local npc = g_npcManager:getNPCByIndex(getXMLInt(xmlFile, key.."#npcIndex")) |
| 60 | self.npcIndex = g_npcManager:getRandomIndex() |
| 61 | if npc ~= nil then |
| 62 | self.npcIndex = npc.index |
| 63 | end |
| 64 | |
| 65 | -- Names are used with custom NPC sets |
| 66 | local npc = g_npcManager:getNPCByName(getXMLString(xmlFile, key.."#npcName")) |
| 67 | if npc ~= nil then |
| 68 | self.npcIndex = npc.index |
| 69 | end |
| 70 | |
| 71 | self.isOwned = false |
| 72 | self.defaultFarmProperty = Utils.getNoNil(getXMLBool(xmlFile, key.."#defaultFarmProperty"), false) |
| 73 | |
| 74 | return true |
| 75 | end |
Delete field definition objectDefinition
delete()Code
| 79 | function Farmland:delete() |
| 80 | end |
Set farmland area indicator world positionDefinition
setFarmlandIndicatorPosition(float xWorldPos, float zWorldPos)Arguments
| float | xWorldPos | farmland indicator x world position |
| float | zWorldPos | farmland size in ha |
| 86 | function Farmland:setFarmlandIndicatorPosition(xWorldPos, zWorldPos) |
| 87 | self.xWorldPos, self.zWorldPos = xWorldPos, zWorldPos |
| 88 | end |
Set farmland areaDefinition
setArea(float areaInHa)Arguments
| float | areaInHa | farmland size in ha |
| 93 | function Farmland:setArea(areaInHa) |
| 94 | self.areaInHa = areaInHa |
| 95 | if self.fixedPrice == nil then |
| 96 | self:updatePrice() |
| 97 | end |
| 98 | end |
Class for field definitions
Create ai field definition objectDefinition
new()Return Values
| table | instance | Instance of object |
| 19 | function Field:new(customMt) |
| 20 | local self = {} |
| 21 | setmetatable(self, customMt or Field_mt) |
| 22 | |
| 23 | self.fieldId = 0 |
| 24 | self.posX = 0 |
| 25 | self.posZ = 0 |
| 26 | self.rootNode = nil |
| 27 | self.name = nil |
| 28 | self.mapHotspot = nil |
| 29 | self.fieldMissionAllowed = true |
| 30 | self.fieldGrassMission = false |
| 31 | self.fieldAngle = 0.0 |
| 32 | self.fieldDimensions = nil |
| 33 | self.fieldArea = 1.0 |
| 34 | self.getFieldStatusPartitions = {} |
| 35 | self.setFieldStatusPartitions = {} |
| 36 | self.maxFieldStatusPartitions = {} |
| 37 | self.isAIActive = true |
| 38 | self.fruitType = nil -- current fruit in the field, as seen by FJM |
| 39 | self.lastCheckedTime = nil |
| 40 | |
| 41 | self.currentMission = nil |
| 42 | |
| 43 | return self |
| 44 | end |
Load Field data from nodeDefinition
load(integer id)Arguments
| integer | id | ai field node id |
| boolean | true | if loading was successful else false |
| 50 | function Field:load(id) |
| 51 | self.rootNode = id |
| 52 | self.name = Utils.getNoNil(getUserAttribute(id, "name"), "") |
| 53 | |
| 54 | self.fieldMissionAllowed = Utils.getNoNil(getUserAttribute(id, "fieldMissionAllowed"), true) |
| 55 | self.fieldGrassMission = Utils.getNoNil(getUserAttribute(id, "fieldGrassMission"), false) |
| 56 | |
| 57 | local fieldDimensions = I3DUtil.indexToObject(id, getUserAttribute(id, "fieldDimensionIndex")) |
| 58 | if fieldDimensions == nil then |
| 59 | print("Warning: No fieldDimensionIndex defined for Field '"..getName(id).."'!") |
| 60 | return false |
| 61 | end |
| 62 | local angleRad = math.rad(Utils.getNoNil(tonumber(getUserAttribute(id, "fieldAngle")), 0)) |
| 63 | |
| 64 | self.fieldAngle = FSDensityMapUtil.convertToDensityMapAngle(angleRad, g_currentMission.terrainDetailAngleMaxValue) |
| 65 | self.fieldDimensions = fieldDimensions |
| 66 | |
| 67 | FieldUtil.updateFieldPartitions(self, self.getFieldStatusPartitions, 900) |
| 68 | FieldUtil.updateFieldPartitions(self, self.setFieldStatusPartitions, 400) |
| 69 | FieldUtil.updateFieldPartitions(self, self.maxFieldStatusPartitions, 10000000) |
| 70 | |
| 71 | self.posX, self.posZ = FieldUtil.getCenterOfField(self) |
| 72 | |
| 73 | self.nameIndicator = I3DUtil.indexToObject(id, getUserAttribute(id, "nameIndicatorIndex")) -- this is where the field number appears on the ingamemap |
| 74 | if self.nameIndicator ~= nil then |
| 75 | local x, _, z = getWorldTranslation(self.nameIndicator) |
| 76 | self.posX, self.posZ = x, z |
| 77 | end |
| 78 | |
| 79 | self.farmland = nil |
| 80 | |
| 81 | return true |
| 82 | end |
Delete field definition objectDefinition
delete()Code
| 86 | function Field:delete() |
| 87 | if self.mapHotspot == nil then |
| 88 | g_currentMission:removeMapHotspot(self.mapHotspot) |
| 89 | self.mapHotspot:delete() |
| 90 | self.mapHotspot = nil |
| 91 | end |
| 92 | end |
Class for help icons
Creating help iconsDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| float | maxClutchTorque | max clutch torque |
| 17 | function HelpIcons:onCreate(id) |
| 18 | local helpIcons = HelpIcons:new(id); |
| 19 | g_currentMission:addNonUpdateable(helpIcons); |
| 20 | g_currentMission.helpIconsBase = helpIcons; |
| 21 | end; |
Creating help iconsDefinition
new(integer name)Arguments
| integer | name | node id |
| float | rotInertia | rotation inertia |
| table | instance | Instance of object |
| 27 | function HelpIcons:new(name) |
| 28 | local self = {}; |
| 29 | setmetatable(self, HelpIcons_mt); |
| 30 | |
| 31 | self.me = name; |
| 32 | local num = getNumOfChildren(self.me); |
| 33 | |
| 34 | self.helpIcons = {}; |
| 35 | for i = 0, num - 1 do |
| 36 | local helpIconTriggerId = getChildAt(self.me, i); |
| 37 | local helpIconId = getChildAt(helpIconTriggerId, 0); |
| 38 | local helpIconCustomNumber = Utils.getNoNil(getUserAttribute(helpIconTriggerId, "customNumber"), 0); |
| 39 | addTrigger(helpIconTriggerId, "triggerCallback", self); |
| 40 | local helpIcon = {helpIconTriggerId = helpIconTriggerId, helpIconId = helpIconId, helpIconCustomNumber = helpIconCustomNumber}; |
| 41 | table.insert(self.helpIcons, helpIcon); |
| 42 | end; |
| 43 | self.visible = true; |
| 44 | |
| 45 | return self; |
| 46 | end; |
Deleting help iconsDefinition
delete()Return Values
| float | dampingRate | damping rate [t m^2 s^-1] |
| 50 | function HelpIcons:delete() |
| 51 | for _, helpIcon in pairs(self.helpIcons) do |
| 52 | removeTrigger(helpIcon.helpIconTriggerId); |
| 53 | end; |
| 54 | end; |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| float | dampingRate | damping rate [t m^2 s^-1] |
| 71 | function HelpIcons:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 72 | if onEnter then -- and g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode and g_currentMission.controlPlayer then |
| 73 | -- only trigger if the player or a vehicle controlled by the player enters |
| 74 | if (g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode and g_currentMission.controlPlayer) or (g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.nodeToObject[otherId]) then |
| 75 | local missionInfo = g_currentMission.missionInfo |
| 76 | |
| 77 | for i, helpIcon in ipairs(self.helpIcons) do -- order is important for savegame |
| 78 | if helpIcon.helpIconTriggerId == triggerId then |
| 79 | if getVisibility(helpIcon.helpIconId) then |
| 80 | setVisibility(helpIcon.helpIconId, false); |
| 81 | setCollisionMask(helpIcon.helpIconTriggerId, 0); |
| 82 | |
| 83 | -- update help icon string |
| 84 | missionInfo.foundHelpIcons = ""; |
| 85 | for _, helpIcon in ipairs(self.helpIcons) do |
| 86 | if getVisibility(helpIcon.helpIconId) then |
| 87 | missionInfo.foundHelpIcons = missionInfo.foundHelpIcons .. "0"; |
| 88 | else |
| 89 | missionInfo.foundHelpIcons = missionInfo.foundHelpIcons .. "1"; |
| 90 | end; |
| 91 | end; |
| 92 | |
| 93 | local messageNumber = helpIcon.helpIconCustomNumber; |
| 94 | if messageNumber == 0 then |
| 95 | messageNumber = i; |
| 96 | end; |
| 97 | g_currentMission.inGameMessage:showMessage(g_i18n:getText("helpIcon_title" .. messageNumber), g_i18n:getText("helpIcon_text" .. messageNumber), 0); |
| 98 | end; |
| 99 | end; |
| 100 | end; |
| 101 | |
| 102 | end; |
| 103 | end; |
| 104 | end; |
Show help iconsDefinition
showHelpIcons(boolean visible, boolean clearIconStates)Arguments
| boolean | visible | visible |
| boolean | clearIconStates | clear icon states |
| float | dampingRate | damping rate [t m^2 s^-1] |
| 110 | function HelpIcons:showHelpIcons(visible, clearIconStates) |
| 111 | self.visible = visible; |
| 112 | |
| 113 | local oldStates = g_currentMission.missionInfo.foundHelpIcons; |
| 114 | |
| 115 | for i, helpIcon in ipairs(self.helpIcons) do |
| 116 | local isVisible = visible |
| 117 | if clearIconStates == nil or not clearIconStates then |
| 118 | isVisible = isVisible and string.sub(oldStates, i, i) == "0"; |
| 119 | end; |
| 120 | |
| 121 | setVisibility(helpIcon.helpIconId, isVisible); |
| 122 | if isVisible then |
| 123 | setCollisionMask(helpIcon.helpIconTriggerId, 3145728); |
| 124 | else |
| 125 | setCollisionMask(helpIcon.helpIconTriggerId, 0); |
| 126 | end; |
| 127 | end; |
| 128 | |
| 129 | end; |
Delete help iconDefinition
deleteHelpIcon(integer i)Arguments
| integer | i | id of help icon |
| float | maxMotorTorque | max motor torque |
| 134 | function HelpIcons:deleteHelpIcon(i) |
| 135 | if self.helpIcons[i] ~= nil then |
| 136 | setVisibility(self.helpIcons[i].helpIconId, false); |
| 137 | setCollisionMask(self.helpIcons[i].helpIconTriggerId, 0); |
| 138 | end; |
| 139 | end; |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| float | brakeForce | brake force |
| 98 | function LoadingStation:readStream(streamId, connection) |
| 99 | LoadingStation:superClass().readStream(self, streamId, connection) |
| 100 | if connection:getIsServer() then |
| 101 | for _, loadTrigger in ipairs(self.loadTriggers) do |
| 102 | local loadTriggerId = NetworkUtil.readNodeObjectId(streamId) |
| 103 | loadTrigger:readStream(streamId, connection) |
| 104 | g_client:finishRegisterObject(loadTrigger, loadTriggerId) |
| 105 | end |
| 106 | end |
| 107 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| float | minRpm | min rpm |
| 113 | function LoadingStation:writeStream(streamId, connection) |
| 114 | LoadingStation:superClass().writeStream(self, streamId, connection) |
| 115 | if not connection:getIsServer() then |
| 116 | for _, loadTrigger in ipairs(self.loadTriggers) do |
| 117 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(loadTrigger)) |
| 118 | loadTrigger:writeStream(streamId, connection) |
| 119 | g_server:registerObjectInStream(connection, loadTrigger) |
| 120 | end |
| 121 | end |
| 122 | end |
Get whether the given farm is allowed to fill objects from this loading station.Definition
getIsFillAllowedToFarm()Return Values
| float | maxRpm | max rpm |
Class for NightIllumination objects which are used for building windows that are illuminated at night
Creating NightIllumination objectDefinition
onCreate(integer id)Arguments
| integer | id | ID of the node |
| float | minRequiredRpm | min required rpm |
| float | minRequiredRpm | max required rpm |
| 19 | function NightIllumination:onCreate(id) |
| 20 | g_currentMission:addNonUpdateable(NightIllumination:new(id)) |
| 21 | end |
Creating NightIllumination objectDefinition
new(integer name)Arguments
| integer | name | ID of the node |
| table | instance | Instance of object |
| 27 | function NightIllumination:new(id) |
| 28 | local self = {} |
| 29 | setmetatable(self, NightIllumination_mt) |
| 30 | |
| 31 | g_currentMission.environment:addWeatherChangeListener(self) |
| 32 | |
| 33 | self.id = id |
| 34 | self.windowsId = 0 |
| 35 | self.lightsId = 0 |
| 36 | |
| 37 | if getNumOfChildren(id) > 0 then |
| 38 | self.windowsId = getChildAt(id, 0) |
| 39 | end |
| 40 | if getNumOfChildren(id) > 1 then |
| 41 | self.lightsId = getChildAt(id, 1) |
| 42 | end |
| 43 | |
| 44 | self.lightIntensity = Utils.getNoNil(getUserAttribute(self.id, "lightIntensity"), 1.0) |
| 45 | |
| 46 | -- set windows to dark (using dashboardLightsShader) |
| 47 | if self.windowsId ~= 0 then |
| 48 | setShaderParameter(self.windowsId, "lightControl", 0, 0, 0, 0, false) |
| 49 | end |
| 50 | |
| 51 | -- make lights invisible |
| 52 | if self.lightsId ~= 0 then |
| 53 | setVisibility(self.lightsId, false) |
| 54 | end |
| 55 | |
| 56 | return self |
| 57 | end |
Remove Object from WeatherChangeListenersDefinition
delete()Return Values
| float | lastMotorRpm | last motor rpm |
| 61 | function NightIllumination:delete() |
| 62 | if g_currentMission.environment ~= nil then |
| 63 | g_currentMission.environment:removeWeatherChangeListener(self) |
| 64 | end |
| 65 | end |
Change illumination of night objectsDefinition
weatherChanged()Return Values
| float | lastMotorRpm | last motor rpm |
| 69 | function NightIllumination:weatherChanged() |
| 70 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
| 71 | local isLightNeeded = not (g_currentMission.environment.isSunOn and not g_currentMission.environment.weather:getIsRaining()) |
| 72 | |
| 73 | if self.windowsId ~= 0 then |
| 74 | if isLightNeeded then |
| 75 | setShaderParameter(self.windowsId, "lightControl", self.lightIntensity, 0, 0, 0, false) |
| 76 | else |
| 77 | setShaderParameter(self.windowsId, "lightControl", 0, 0, 0, 0, false) |
| 78 | end |
| 79 | end |
| 80 | |
| 81 | if self.lightsId ~= 0 then |
| 82 | setVisibility(self.lightsId, isLightNeeded) |
| 83 | end |
| 84 | end |
| 85 | end |
Class for nightlight objects which are blending in on night
Creating nightlight objectDefinition
onCreate(integer id)Arguments
| integer | id | ID of the node |
| float | appliedTorque | torque [kN] |
| 15 | function Nightlight2:onCreate(id) |
| 16 | g_currentMission:addNonUpdateable(Nightlight2:new(id)) |
| 17 | end |
Creating nightlight objectDefinition
new(integer name)Arguments
| integer | name | ID of the node |
| float | externalTorque | external torque [kN] |
| table | instance | Instance of object |
| 23 | function Nightlight2:new(id) |
| 24 | local self = {} |
| 25 | setmetatable(self, Nightlight2_mt) |
| 26 | |
| 27 | self.id = id |
| 28 | self.switchCollision = Utils.getNoNil(getUserAttribute(id, "switchCollision"), false) |
| 29 | |
| 30 | if self.switchCollision then |
| 31 | self.collisionMask = getCollisionMask(id) |
| 32 | end |
| 33 | |
| 34 | self:setVisibility(false) |
| 35 | |
| 36 | g_currentMission.environment:addWeatherChangeListener(self) |
| 37 | |
| 38 | return self |
| 39 | end |
Remove Object from WeatherChangeListenersDefinition
delete()Return Values
| float | torque | external torque [kN] |
| 43 | function Nightlight2:delete() |
| 44 | if g_currentMission.environment ~= nil then |
| 45 | g_currentMission.environment:removeWeatherChangeListener(self) |
| 46 | end |
| 47 | end |
Change visibility of night objectDefinition
weatherChanged()Return Values
| float | equalizedMotorRpm | equalized motor rpm |
| 59 | function Nightlight2:weatherChanged() |
| 60 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
| 61 | self:setVisibility(not (g_currentMission.environment.isSunOn and not g_currentMission.environment.weather:getIsRaining())) |
| 62 | end |
| 63 | end |
NightlightFlickers are flickering lights that are only active at night or during bad weather
Creating nightlightflickerDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| float | ptoMotorRpmRatio | pto motor rpm ratio |
| 15 | function NightlightFlicker:onCreate(id) |
| 16 | g_currentMission:addUpdateable(NightlightFlicker:new(id)); |
| 17 | end; |
Creating nightlightflickerDefinition
new(integer name)Arguments
| integer | name | node id |
| float | nonClampedMotorRpm | non clamped motor rpm |
| table | instance | Instance of object |
| 23 | function NightlightFlicker:new(id) |
| 24 | local self = {}; |
| 25 | setmetatable(self, NightlightFlicker_mt); |
| 26 | |
| 27 | self.id = id; |
| 28 | self.isVisible = false; |
| 29 | self.isFlickerActive = false; |
| 30 | self.nextFlicker = 0; |
| 31 | self.flickerDuration = 100; |
| 32 | setVisibility(self.id, self.isVisible); |
| 33 | |
| 34 | g_currentMission.environment:addWeatherChangeListener(self); |
| 35 | |
| 36 | return self; |
| 37 | end; |
Update flickeringDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| float | nonClampedMotorRpm | non clamped motor rpm |
| 45 | function NightlightFlicker:update(dt) |
| 46 | if self.isVisible then |
| 47 | |
| 48 | self.nextFlicker = self.nextFlicker - dt; |
| 49 | if self.nextFlicker <= 0 then |
| 50 | self.isFlickerActive = true; |
| 51 | setVisibility(self.id, false); |
| 52 | self.nextFlicker = math.floor(math.random() * 1500 + self.flickerDuration + 10); -- set next flicker at least 10ms after this one |
| 53 | end; |
| 54 | |
| 55 | if self.isFlickerActive then |
| 56 | self.flickerDuration = self.flickerDuration - dt; |
| 57 | if self.flickerDuration <= 0 then |
| 58 | self.isFlickerActive = false; |
| 59 | self.flickerDuration = math.floor(math.random() * 200); |
| 60 | setVisibility(self.id, true); |
| 61 | end; |
| 62 | end; |
| 63 | |
| 64 | end; |
| 65 | end; |
Change visibility of night objectDefinition
weatherChanged()Return Values
| float | clutchRpm | clutch rpm |
| 69 | function NightlightFlicker:weatherChanged() |
| 70 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
| 71 | self.isVisible = not (g_currentMission.environment.isSunOn and not g_currentMission.environment.weather:getIsRaining()); |
| 72 | setVisibility(self.id, self.isVisible); |
| 73 | end; |
| 74 | end; |
Class for physics objects
Creating physics objectDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | customMt |
| table | torqueCurve | torque curve |
| table | instance | Instance of object |
| 17 | function PhysicsObject:new(isServer, isClient, customMt) |
| 18 | |
| 19 | local self = Object:new(isServer, isClient, customMt or PhysicsObject_mt) |
| 20 | |
| 21 | self.nodeId = 0 |
| 22 | self.networkTimeInterpolator = InterpolationTime:new(1.2) |
| 23 | self.forcedClipDistance = 60 |
| 24 | |
| 25 | self.physicsObjectDirtyFlag = self:getNextDirtyFlag() |
| 26 | |
| 27 | return self |
| 28 | end |
Deleting physics objectDefinition
delete()Return Values
| float | torque | torque |
| 32 | function PhysicsObject:delete() |
| 33 | removeWakeUpReport(self.nodeId) |
| 34 | g_currentMission:removeNodeObject(self.nodeId) |
| 35 | delete(self.nodeId) |
| 36 | self.nodeId = 0 |
| 37 | PhysicsObject:superClass().delete(self) |
| 38 | end |
Get allows auto deleteDefinition
getAllowsAutoDelete()Return Values
| float | torque | torque |
| boolean | allowsAutoDelete | allows auto delete |
| 43 | function PhysicsObject:getAllowsAutoDelete() |
| 44 | return true |
| 45 | end |
Load on createDefinition
loadOnCreate(integer nodeId)Arguments
| integer | nodeId | node id |
| float | maxForwardSpeed | maximum forward speed |
| 50 | function PhysicsObject:loadOnCreate(nodeId) |
| 51 | self:setNodeId(nodeId) |
| 52 | if not self.isServer then |
| 53 | self:onGhostRemove() |
| 54 | end |
| 55 | end |
Set node idDefinition
setNodeId(integer nodeId)Arguments
| integer | nodeId | node Id |
| float | maxBackwardSpeed | maximum backward speed |
| 60 | function PhysicsObject:setNodeId(nodeId) |
| 61 | self.nodeId = nodeId |
| 62 | setRigidBodyType(self.nodeId, self:getDefaultRigidBodyType()) |
| 63 | addToPhysics(self.nodeId) |
| 64 | |
| 65 | local x, y, z = getTranslation(self.nodeId) |
| 66 | local xRot, yRot, zRot = getRotation(self.nodeId) |
| 67 | self.sendPosX, self.sendPosY, self.sendPosZ = x, y, z |
| 68 | self.sendRotX, self.sendRotY, self.sendRotZ = xRot, yRot, zRot |
| 69 | |
| 70 | if not self.isServer then |
| 71 | local quatX, quatY, quatZ, quatW = mathEulerToQuaternion(xRot, yRot, zRot) |
| 72 | self.positionInterpolator = InterpolatorPosition:new(x, y, z) |
| 73 | self.quaternionInterpolator = InterpolatorQuaternion:new(quatX, quatY, quatZ, quatW) |
| 74 | end |
| 75 | |
| 76 | self:addChildsToNodeObject(self.nodeId) |
| 77 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| float | physicalMaxForwardSpeed | physical maximum forward speed |
| 83 | function PhysicsObject:readStream(streamId, connection) |
| 84 | assert(self.nodeId ~= 0) |
| 85 | if connection:getIsServer() then |
| 86 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams |
| 87 | local paramsY = g_currentMission.vehicleYPosCompressionParams |
| 88 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 89 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
| 90 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 91 | local xRot = NetworkUtil.readCompressedAngle(streamId) |
| 92 | local yRot = NetworkUtil.readCompressedAngle(streamId) |
| 93 | local zRot = NetworkUtil.readCompressedAngle(streamId) |
| 94 | |
| 95 | local quatX, quatY, quatZ, quatW = mathEulerToQuaternion(xRot,yRot,zRot) |
| 96 | self:setWorldPositionQuaternion(x, y, z, quatX, quatY, quatZ, quatW, true) |
| 97 | |
| 98 | self.networkTimeInterpolator:reset() |
| 99 | end |
| 100 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| float | physicalMaxBackwardSpeed | physical maximum backward speed |
| 106 | function PhysicsObject:writeStream(streamId, connection) |
| 107 | if not connection:getIsServer() then |
| 108 | local x,y,z = getTranslation(self.nodeId) |
| 109 | local xRot,yRot,zRot = getRotation(self.nodeId) |
| 110 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams |
| 111 | local paramsY = g_currentMission.vehicleYPosCompressionParams |
| 112 | NetworkUtil.writeCompressedWorldPosition(streamId, x, paramsXZ) |
| 113 | NetworkUtil.writeCompressedWorldPosition(streamId, y, paramsY) |
| 114 | NetworkUtil.writeCompressedWorldPosition(streamId, z, paramsXZ) |
| 115 | NetworkUtil.writeCompressedAngle(streamId, xRot) |
| 116 | NetworkUtil.writeCompressedAngle(streamId, yRot) |
| 117 | NetworkUtil.writeCompressedAngle(streamId, zRot) |
| 118 | end |
| 119 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| float | physicalMaxSpeed | physical maximum speed |
| 126 | function PhysicsObject:readUpdateStream(streamId, timestamp, connection) |
| 127 | if connection:getIsServer() then |
| 128 | if streamReadBool(streamId) then |
| 129 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams |
| 130 | local paramsY = g_currentMission.vehicleYPosCompressionParams |
| 131 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 132 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
| 133 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 134 | local xRot = NetworkUtil.readCompressedAngle(streamId) |
| 135 | local yRot = NetworkUtil.readCompressedAngle(streamId) |
| 136 | local zRot = NetworkUtil.readCompressedAngle(streamId) |
| 137 | |
| 138 | local quatX, quatY, quatZ, quatW = mathEulerToQuaternion(xRot,yRot,zRot) |
| 139 | self.positionInterpolator:setTargetPosition(x, y, z) |
| 140 | self.quaternionInterpolator:setTargetQuaternion(quatX, quatY, quatZ, quatW) |
| 141 | self.networkTimeInterpolator:startNewPhaseNetwork() |
| 142 | end |
| 143 | end |
| 144 | end |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| float | bestGearRatio | best gear ratio |
| 151 | function PhysicsObject:writeUpdateStream(streamId, connection, dirtyMask) |
| 152 | if not connection:getIsServer() then |
| 153 | if streamWriteBool(streamId, bitAND(dirtyMask, self.physicsObjectDirtyFlag) ~= 0) then |
| 154 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams |
| 155 | local paramsY = g_currentMission.vehicleYPosCompressionParams |
| 156 | NetworkUtil.writeCompressedWorldPosition(streamId, self.sendPosX, paramsXZ) |
| 157 | NetworkUtil.writeCompressedWorldPosition(streamId, self.sendPosY, paramsY) |
| 158 | NetworkUtil.writeCompressedWorldPosition(streamId, self.sendPosZ, paramsXZ) |
| 159 | |
| 160 | NetworkUtil.writeCompressedAngle(streamId, self.sendRotX) |
| 161 | NetworkUtil.writeCompressedAngle(streamId, self.sendRotY) |
| 162 | NetworkUtil.writeCompressedAngle(streamId, self.sendRotZ) |
| 163 | end |
| 164 | end |
| 165 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| float | bestGear | best gear |
| float | gearRatio | gear ratio |
| 170 | function PhysicsObject:update(dt) |
| 171 | if not self.isServer then |
| 172 | self.networkTimeInterpolator:update(dt) |
| 173 | local interpolationAlpha = self.networkTimeInterpolator:getAlpha() |
| 174 | local posX, posY, posZ = self.positionInterpolator:getInterpolatedValues(interpolationAlpha) |
| 175 | local quatX, quatY, quatZ, quatW = self.quaternionInterpolator:getInterpolatedValues(interpolationAlpha) |
| 176 | self:setWorldPositionQuaternion(posX, posY, posZ, quatX, quatY, quatZ, quatW, false) |
| 177 | |
| 178 | if self.networkTimeInterpolator:isInterpolating() then |
| 179 | self:raiseActive() |
| 180 | end |
| 181 | else |
| 182 | if not getIsSleeping(self.nodeId) then |
| 183 | self:raiseActive() |
| 184 | end |
| 185 | end |
| 186 | end |
Update moveDefinition
updateMove()Return Values
| boolean | hasMoved | has moved |
| 192 | function PhysicsObject:updateMove() |
| 193 | local x, y, z = getTranslation(self.nodeId) |
| 194 | local xRot, yRot, zRot = getRotation(self.nodeId) |
| 195 | local hasMoved = math.abs(self.sendPosX-x)>0.005 or math.abs(self.sendPosY-y)>0.005 or math.abs(self.sendPosZ-z)>0.005 or |
| 196 | math.abs(self.sendRotX-xRot)>0.02 or math.abs(self.sendRotY-yRot)>0.02 or math.abs(self.sendRotZ-zRot)>0.02 |
| 197 | |
| 198 | if hasMoved then |
| 199 | self:raiseDirtyFlags(self.physicsObjectDirtyFlag) |
| 200 | self.sendPosX, self.sendPosY, self.sendPosZ = x, y ,z |
| 201 | self.sendRotX, self.sendRotY, self.sendRotZ = xRot, yRot, zRot |
| 202 | end |
| 203 | |
| 204 | return hasMoved |
| 205 | end |
updateTickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| float | adjustedAcceleratorPedal | the adjusted accelerator pedal for the current gear situation (e.g. 0 while switching gears) |
| 210 | function PhysicsObject:updateTick(dt) |
| 211 | if self.isServer then |
| 212 | self:updateMove() |
| 213 | end |
| 214 | end |
Test scopeDefinition
testScope(float x, float y, float z, float coeff)Arguments
| float | x | x position |
| float | y | y position |
| float | z | z position |
| float | coeff | coeff |
| float | minGearRatio | minimum gear ratio |
| float | maxGearRatio | maximum gear ratio |
| boolean | inScope | in scope |
| 223 | function PhysicsObject:testScope(x,y,z, coeff) |
| 224 | local x1, y1, z1 = getWorldTranslation(self.nodeId) |
| 225 | local dist = (x1-x)*(x1-x) + (y1-y)*(y1-y) + (z1-z)*(z1-z) |
| 226 | local clipDist = math.min(getClipDistance(self.nodeId)*coeff, self.forcedClipDistance) |
| 227 | if dist < clipDist*clipDist then |
| 228 | return true |
| 229 | else |
| 230 | return false |
| 231 | end |
| 232 | end |
Get update priorityDefinition
getUpdatePriority(float skipCount, float x, float y, float z, float coeff, table connection)Arguments
| float | skipCount | skip count |
| float | x | x position |
| float | y | y position |
| float | z | z position |
| float | coeff | coeff |
| table | connection | connection |
| float | priority | priority |
| 243 | function PhysicsObject:getUpdatePriority(skipCount, x, y, z, coeff, connection) |
| 244 | local x1, y1, z1 = getWorldTranslation(self.nodeId) |
| 245 | local dist = math.sqrt((x1-x)*(x1-x) + (y1-y)*(y1-y) + (z1-z)*(z1-z)) |
| 246 | local clipDist = math.min(getClipDistance(self.nodeId)*coeff, self.forcedClipDistance) |
| 247 | return (1-dist/clipDist)* 0.8 + 0.5*skipCount * 0.2 |
| 248 | end |
On ghost removeDefinition
onGhostRemove()Return Values
| integer | maxRpm | current max rpm |
| 252 | function PhysicsObject:onGhostRemove() |
| 253 | setVisibility(self.nodeId, false) |
| 254 | removeFromPhysics(self.nodeId) |
| 255 | end |
On ghost addDefinition
onGhostAdd()Return Values
| boolean | continue | continue |
| 259 | function PhysicsObject:onGhostAdd() |
| 260 | setVisibility(self.nodeId, true) |
| 261 | addToPhysics(self.nodeId) |
| 262 | end |
Set poseDefinition
setWorldPositionQuaternion(float x, float y, float z, float xRot, float yRot, float zRot, float w_rot)Arguments
| float | x | x position |
| float | y | z position |
| float | z | z position |
| float | xRot | x rotation |
| float | yRot | y rotation |
| float | zRot | z rotation |
| float | w_rot | w rotation |
| table | instance | instance of object |
| 273 | function PhysicsObject:setWorldPositionQuaternion(x, y, z, quatX, quatY, quatZ, quatW, changeInterp) |
| 274 | if not self.isServer then |
| 275 | if changeInterp then |
| 276 | self.positionInterpolator:setPosition(x, y, z) |
| 277 | self.quaternionInterpolator:setQuaternion(quatX, quatY, quatZ, quatW) |
| 278 | end |
| 279 | end |
| 280 | |
| 281 | setTranslation(self.nodeId, x, y, z) |
| 282 | setQuaternion(self.nodeId, quatX, quatY, quatZ, quatW) |
| 283 | end |
Get default rigid body typeDefinition
getDefaultRigidBodyType()Return Values
| boolean | true | if loading was successful else false |
| string | rigidBodyType | rigid body type |
| 288 | function PhysicsObject:getDefaultRigidBodyType() |
| 289 | if self.isServer then |
| 290 | return "Dynamic" |
| 291 | else |
| 292 | return "Kinematic" |
| 293 | end |
| 294 | end |
Add childs to node objectDefinition
addChildsToNodeObject(integer nodeId)Arguments
| integer | nodeId | id of node |
| boolean | success | true if added else false |
| 299 | function PhysicsObject:addChildsToNodeObject(nodeId) |
| 300 | for i=0,getNumOfChildren(nodeId)-1 do |
| 301 | self:addChildsToNodeObject(getChildAt(nodeId, i)) |
| 302 | end |
| 303 | local rigidBodyType = getRigidBodyType(nodeId) |
| 304 | if rigidBodyType ~= "NoRigidBody" then |
| 305 | g_currentMission:addNodeObject(nodeId, self) |
| 306 | |
| 307 | if self.isServer then |
| 308 | addWakeUpReport(nodeId, "onPhysicObjectWakeUpCallback", self) |
| 309 | end |
| 310 | end |
| 311 | end |
Rotators rotate around their y axis
Creating rotatorDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| integer | i | index of tire type |
| 15 | function Rotator:onCreate(id) |
| 16 | g_currentMission:addUpdateable(Rotator:new(id)) |
| 17 | end |
Creating rotatorDefinition
new(integer name)Arguments
| integer | name | node id |
| float | diffRotSpeed | rot speed [rad/sec] |
| table | instance | Instance of object |
| 23 | function Rotator:new(name) |
| 24 | local self = {} |
| 25 | setmetatable(self, Rotator_mt) |
| 26 | |
| 27 | self.axisTable = {0, 0, 0} |
| 28 | self.me = name |
| 29 | self.speed = Utils.getNoNil(getUserAttribute(name, "speed"), 0.0012) |
| 30 | local axis = Utils.getNoNil(getUserAttribute(name, "axis"), 3) |
| 31 | self.axisTable[axis] = 1 |
| 32 | |
| 33 | return self |
| 34 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| float | tireFriction | tire friction |
| 42 | function Rotator:update(dt) |
| 43 | rotate(self.me, self.axisTable[1] * self.speed * dt, self.axisTable[2] * self.speed * dt, self.axisTable[3] * self.speed * dt) |
| 44 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| integer | groundType | ground type |
| boolean | success | success |
| 227 | function SellingStation:loadFromXMLFile(xmlFile, key) |
| 228 | local i=0 |
| 229 | while true do |
| 230 | local statsKey = string.format(key..".stats(%d)", i) |
| 231 | if not hasXMLProperty(xmlFile, statsKey) then |
| 232 | break |
| 233 | end |
| 234 | local fillTypeStr = getXMLString(xmlFile, statsKey.."#fillType") |
| 235 | local fillType = g_fillTypeManager:getFillTypeIndexByName(fillTypeStr) |
| 236 | if fillType ~= nil and self.acceptedFillTypes[fillType] then |
| 237 | self.totalReceived[fillType] = Utils.getNoNil(getXMLFloat(xmlFile, statsKey.."#received"), 0) |
| 238 | self.totalPaid[fillType] = Utils.getNoNil(getXMLFloat(xmlFile, statsKey.."#paid"), 0) |
| 239 | self.pricingDynamics[fillType]:loadFromXMLFile(xmlFile, statsKey) |
| 240 | end |
| 241 | i = i + 1 |
| 242 | end |
| 243 | |
| 244 | return true |
| 245 | end |
Initialize pricing dynamicsDefinition
initPricingDynamics()Return Values
| table | instance | instance of object |
This pre-simulates a particle system so it doesn't start at zero when the game begins
Creating SimParticleSystemDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| 15 | function SimParticleSystem:onCreate(id) |
| 16 | g_currentMission:addNonUpdateable(SimParticleSystem:new(id)); |
| 17 | end; |
Creating SimParticleSystemDefinition
new(integer name)Arguments
| integer | name | node id |
| table | instance | Instance of object |
| 23 | function SimParticleSystem:new(name) |
| 24 | local self = {}; |
| 25 | setmetatable(self, SimParticleSystem_mt); |
| 26 | self.id = name; |
| 27 | |
| 28 | local particleSystem = nil; |
| 29 | |
| 30 | if getHasClassId(self.id, ClassIds.SHAPE) then |
| 31 | local geometry = getGeometry(self.id); |
| 32 | if geometry ~= 0 then |
| 33 | if getHasClassId(geometry, ClassIds.PRECIPITATION) then |
| 34 | particleSystem = geometry; |
| 35 | end; |
| 36 | end; |
| 37 | end; |
| 38 | |
| 39 | if particleSystem ~= nil then |
| 40 | local lifespan = getParticleSystemLifespan(particleSystem); |
| 41 | addParticleSystemSimulationTime(particleSystem, lifespan); |
| 42 | end; |
| 43 | |
| 44 | return self; |
| 45 | end; |
Creating new instance of storage classDefinition
new(boolean isServer, boolean isClient)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | self | new instance of object |
| 23 | function Storage:new(isServer, isClient, customMt) |
| 24 | local self = Object:new(isServer, isClient, customMt or Storage_mt) |
| 25 | |
| 26 | self.unloadingStations = {} |
| 27 | self.loadingStations = {} |
| 28 | |
| 29 | self.rootNode = 0 |
| 30 | |
| 31 | return self |
| 32 | end |
Load storage attributes from objectDefinition
load(integer id)Arguments
| integer | id | id of object |
| boolean | success | success |
| 38 | function Storage:load(id, xmlFile, key) |
| 39 | self.rootNode = id |
| 40 | |
| 41 | self.capacityPerFillType = getXMLFloat(xmlFile, key .. "#capacityPerFillType") or 100000 |
| 42 | self.costsPerFillLevelAndDay = getXMLFloat(xmlFile, key .. "#costsPerFillLevelAndDay") or 0 |
| 43 | |
| 44 | self.fillTypes = {} |
| 45 | self.fillLevels = {} |
| 46 | self.sortedFillTypes = {} |
| 47 | |
| 48 | local fillTypeCategories = getXMLString(xmlFile, key .. "#fillTypeCategories") |
| 49 | local fillTypeNames = getXMLString(xmlFile, key .. "#fillTypes") |
| 50 | local fillTypes |
| 51 | |
| 52 | if fillTypeCategories ~= nil and fillTypeNames == nil then |
| 53 | fillTypes = g_fillTypeManager:getFillTypesByCategoryNames(fillTypeCategories, "Warning: '"..tostring(key).. "' has invalid fillTypeCategory '%s'.") |
| 54 | elseif fillTypeCategories == nil and fillTypeNames ~= nil then |
| 55 | fillTypes = g_fillTypeManager:getFillTypesByNames(fillTypeNames, "Warning: '"..tostring(key).. "' has invalid fillType '%s'.") |
| 56 | else |
| 57 | print("Warning: '"..tostring(key).. "' a 'Storage' entry needs either the 'fillTypeCategories' or 'fillTypes' attribute.") |
| 58 | return false |
| 59 | end |
| 60 | |
| 61 | for _,fillType in pairs(fillTypes) do |
| 62 | self.fillTypes[fillType] = true |
| 63 | end |
| 64 | |
| 65 | for fillType,_ in pairs(self.fillTypes) do |
| 66 | table.insert(self.sortedFillTypes, fillType) |
| 67 | self.fillLevels[fillType] = 0 |
| 68 | end |
| 69 | table.sort(self.sortedFillTypes) |
| 70 | |
| 71 | self.storageDirtyFlag = self:getNextDirtyFlag() |
| 72 | |
| 73 | g_messageCenter:subscribe(MessageType.FARM_DELETED, self.farmDestroyed, self) |
| 74 | |
| 75 | return true |
| 76 | end |
Deleting storageDefinition
delete()Code
| 80 | function Storage:delete() |
| 81 | if self.rootNode ~= 0 and entityExists(self.rootNode) then |
| 82 | delete(self.rootNode) |
| 83 | end |
| 84 | |
| 85 | g_currentMission.environment:removeHourChangeListener(self) |
| 86 | g_messageCenter:unsubscribeAll(self) |
| 87 | |
| 88 | Storage:superClass().delete(self) |
| 89 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 95 | function Storage:readStream(streamId, connection) |
| 96 | Storage:superClass().readStream(self, streamId, connection) |
| 97 | |
| 98 | for _, fillType in ipairs(self.sortedFillTypes) do |
| 99 | local fillLevel = 0 |
| 100 | if streamReadBool(streamId) then |
| 101 | fillLevel = streamReadFloat32(streamId) |
| 102 | end |
| 103 | self:setFillLevel(fillLevel, fillType) |
| 104 | end |
| 105 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 111 | function Storage:writeStream(streamId, connection) |
| 112 | Storage:superClass().writeStream(self, streamId, connection) |
| 113 | |
| 114 | for _, fillType in ipairs(self.sortedFillTypes) do |
| 115 | local fillLevel = self.fillLevels[fillType] |
| 116 | if streamWriteBool(streamId, fillLevel > 0) then |
| 117 | streamWriteFloat32(streamId, fillLevel) |
| 118 | end |
| 119 | end |
| 120 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| 127 | function Storage:readUpdateStream(streamId, timestamp, connection) |
| 128 | Storage:superClass().readUpdateStream(self, streamId, timestamp, connection) |
| 129 | if connection:getIsServer() then |
| 130 | if streamReadBool(streamId) then |
| 131 | for _, fillType in ipairs(self.sortedFillTypes) do |
| 132 | local fillLevel = 0 |
| 133 | if streamReadBool(streamId) then |
| 134 | fillLevel = streamReadFloat32(streamId) |
| 135 | end |
| 136 | self:setFillLevel(fillLevel, fillType) |
| 137 | end |
| 138 | end |
| 139 | end |
| 140 | end |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| 147 | function Storage:writeUpdateStream(streamId, connection, dirtyMask) |
| 148 | Storage:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
| 149 | if not connection:getIsServer() then |
| 150 | if streamWriteBool(streamId, bitAND(dirtyMask, self.storageDirtyFlag) ~= 0) then |
| 151 | for _, fillType in ipairs(self.sortedFillTypes) do |
| 152 | local fillLevel = self.fillLevels[fillType] |
| 153 | if streamWriteBool(streamId, fillLevel > 0) then |
| 154 | streamWriteFloat32(streamId, fillLevel) |
| 155 | end |
| 156 | end |
| 157 | end |
| 158 | end |
| 159 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | success | success |
| 166 | function Storage:loadFromXMLFile(xmlFile, key) |
| 167 | self:setOwnerFarmId(Utils.getNoNil(getXMLInt(xmlFile, key .. "#farmId"), AccessHandler.EVERYONE), true) |
| 168 | |
| 169 | local i = 0 |
| 170 | while true do |
| 171 | local siloKey = string.format(key .. ".node(%d)", i) |
| 172 | if not hasXMLProperty(xmlFile, siloKey) then |
| 173 | break |
| 174 | end |
| 175 | |
| 176 | local fillTypeStr = getXMLString(xmlFile, siloKey.."#fillType") |
| 177 | local fillLevel = math.max(Utils.getNoNil(getXMLFloat(xmlFile, siloKey.."#fillLevel"), 0), 0) |
| 178 | local fillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(fillTypeStr) |
| 179 | |
| 180 | if fillTypeIndex ~= nil then |
| 181 | if self.fillLevels[fillTypeIndex] ~= nil then |
| 182 | self:setFillLevel(fillLevel, fillTypeIndex, nil) |
| 183 | else |
| 184 | print("Warning: Filltype '"..fillTypeStr.."' not supported by Storage "..getName(self.rootNode)) |
| 185 | end |
| 186 | else |
| 187 | print("Error: Invalid filltype '"..fillTypeStr.."'") |
| 188 | end |
| 189 | |
| 190 | i = i + 1 |
| 191 | end |
| 192 | |
| 193 | return true |
| 194 | end |
Returns if storage allows fill typeDefinition
getIsFillTypeSupported(integer fillType)Arguments
| integer | fillType | fill type |
| boolean | allow | allow fill type |
| 224 | function Storage:getIsFillTypeSupported(fillType) |
| 225 | return self.fillTypes[fillType] == true |
| 226 | end |
Returns fill levelDefinition
getFillLevel(integer fillType)Arguments
| integer | fillType | fill type |
| float | fillLevel | fill level |
| 232 | function Storage:getFillLevel(fillType) |
| 233 | return self.fillLevels[fillType] or 0 |
| 234 | end |
Set fill level of storageDefinition
setFillLevel(float fillLevel, integer fillType)Arguments
| float | fillLevel | new fill level |
| integer | fillType | fill type |
| 244 | function Storage:setFillLevel(fillLevel, fillType) |
| 245 | fillLevel = MathUtil.clamp(fillLevel, 0, self.capacityPerFillType) |
| 246 | if self.fillLevels[fillType] ~= nil and fillLevel ~= self.fillLevels[fillType] then |
| 247 | self.fillLevels[fillType] = fillLevel |
| 248 | |
| 249 | if self.isServer then |
| 250 | self:raiseDirtyFlags(self.storageDirtyFlag) |
| 251 | end |
| 252 | end |
| 253 | end |
Returns free capacityDefinition
getFreeCapacity(integer fillType)Arguments
| integer | fillType | fill type |
| float | freeCapacity | free capacity |
| 259 | function Storage:getFreeCapacity(fillType) |
| 260 | if self.fillLevels[fillType] ~= nil then |
| 261 | return math.max(self.capacityPerFillType - self.fillLevels[fillType], 0) |
| 262 | end |
| 263 | return 0 |
| 264 | end |
Called on hour changeDefinition
hourChanged()Code
| 272 | function Storage:hourChanged() |
| 273 | if self.isServer then |
| 274 | local difficultyMultiplier = g_currentMission.missionInfo.buyPriceMultiplier |
| 275 | local fillLevelFactor = difficultyMultiplier * self.costsPerFillLevelAndDay / 24 |
| 276 | |
| 277 | local costs = 0 |
| 278 | for _, fillLevel in pairs(self.fillLevels) do |
| 279 | costs = costs + fillLevel * fillLevelFactor |
| 280 | end |
| 281 | |
| 282 | g_currentMission:addMoney(-costs, self:getOwnerFarmId(), "propertyMaintenance") |
| 283 | g_currentMission:addMoneyChange(-costs, self:getOwnerFarmId(), EconomyManager.MONEY_TYPE_PROPERTY_MAINTENANCE) |
| 284 | end |
| 285 | end |
Class for objects which are visible when the sun is out
Creating sun admirer objectDefinition
onCreate(integer id)Arguments
| integer | id | ID of the node |
| 15 | function SunAdmirer:onCreate(id) |
| 16 | g_currentMission:addNonUpdateable(SunAdmirer:new(id)) |
| 17 | end |
Creating nightlight objectDefinition
new(integer name)Arguments
| integer | name | ID of the node |
| table | instance | Instance of object |
| 23 | function SunAdmirer:new(id) |
| 24 | local self = {} |
| 25 | setmetatable(self, SunAdmirer_mt) |
| 26 | |
| 27 | self.id = id |
| 28 | self.switchCollision = Utils.getNoNil(getUserAttribute(id, "switchCollision"), false) |
| 29 | |
| 30 | if self.switchCollision then |
| 31 | self.collisionMask = getCollisionMask(id) |
| 32 | end |
| 33 | |
| 34 | self:setVisibility(true) |
| 35 | |
| 36 | g_currentMission.environment:addWeatherChangeListener(self) |
| 37 | |
| 38 | return self |
| 39 | end |
Remove Object from WeatherChangeListenersDefinition
delete()Code
| 43 | function SunAdmirer:delete() |
| 44 | if g_currentMission.environment ~= nil then |
| 45 | g_currentMission.environment:removeWeatherChangeListener(self) |
| 46 | end |
| 47 | end |
Change visibility of sun objectDefinition
weatherChanged()Code
| 59 | function SunAdmirer:weatherChanged() |
| 60 | if g_currentMission ~= nil and g_currentMission.environment ~= nil then |
| 61 | self:setVisibility(g_currentMission.environment.isSunOn and not g_currentMission.environment.weather:getIsRaining()) |
| 62 | end |
| 63 | end |
Tour icons are part of the (optional) guided tour at the career game's start
Creating tour iconsDefinition
onCreate(integer id)Arguments
| integer | id | node id |
| 15 | function TourIcons:onCreate(id) |
| 16 | local tourIcons = TourIcons:new(id) |
| 17 | table.insert(g_currentMission.updateables, tourIcons) |
| 18 | g_currentMission.tourIconsBase = tourIcons |
| 19 | end |
Creating tour iconsDefinition
new(integer id)Arguments
| integer | id | node id |
| table | instance | Instance of object |
| 25 | function TourIcons:new(name) |
| 26 | local self = {} |
| 27 | setmetatable(self, TourIcons_mt) |
| 28 | |
| 29 | self.me = name |
| 30 | local num = getNumOfChildren(self.me) |
| 31 | |
| 32 | self.tourIcons = {} |
| 33 | for i = 0, num - 1 do |
| 34 | local tourIconTriggerId = getChildAt(self.me, i) |
| 35 | local tourIconId = getChildAt(tourIconTriggerId, 0) |
| 36 | addTrigger(tourIconTriggerId, "triggerCallback", self) |
| 37 | setVisibility(tourIconId, false) |
| 38 | local tourIcon = {tourIconTriggerId = tourIconTriggerId, tourIconId = tourIconId} |
| 39 | table.insert(self.tourIcons, tourIcon) |
| 40 | end |
| 41 | |
| 42 | self.visible = false |
| 43 | self.mapHotspot = nil |
| 44 | self.currentTourIconNumber = 1 |
| 45 | self.alpha = 0.25 |
| 46 | self.alphaDirection = 1 |
| 47 | self.startTourDialog = false |
| 48 | self.startTourDialogDelay = 0 |
| 49 | self.permanentMessageDelay = 0 |
| 50 | self.isPaused = false |
| 51 | self.pauseTime = 0 |
| 52 | self.soldStuffAtGrainElevator = false |
| 53 | |
| 54 | _, self.permanentTextSize = getNormalizedScreenValues(0, 28) |
| 55 | |
| 56 | return self |
| 57 | end |
Deleting tour iconsDefinition
delete()Code
| 61 | function TourIcons:delete() |
| 62 | for _, tourIcon in pairs(self.tourIcons) do |
| 63 | removeTrigger(tourIcon.tourIconTriggerId) |
| 64 | end |
| 65 | end |
Show tour yes/no dialogDefinition
showTourDialog()Code
| 69 | function TourIcons:showTourDialog() |
| 70 | g_gui:showYesNoDialog({text=g_i18n:getText("tour_text_start"), title="", callback=self.reactToDialog, target=self}) |
| 71 | end |
React to tour dialogDefinition
reactToDialog(boolean yes)Arguments
| boolean | yes | answer to dialog |
| 76 | function TourIcons:reactToDialog(yes) |
| 77 | if yes then |
| 78 | self.visible = true |
| 79 | self:activateNextIcon() |
| 80 | -- hide all non-tour question marks |
| 81 | if g_currentMission.helpIconsBase ~= nil then |
| 82 | g_currentMission.helpIconsBase:showHelpIcons(false, true) |
| 83 | end |
| 84 | else |
| 85 | self.visible = false |
| 86 | g_currentMission.hud:showInGameMessage("", g_i18n:getText("tour_text_abort"), -1) |
| 87 | end |
| 88 | |
| 89 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 94 | function TourIcons:update(dt) |
| 95 | if not g_currentMission.missionInfo.isValid and g_server ~= nil and self.initDone == nil and g_currentMission:getIsTourSupported() then |
| 96 | self.initDone = true |
| 97 | |
| 98 | -- prepare fields |
| 99 | local field = g_fieldManager:getFieldByIndex(24) |
| 100 | local fruitDesc = g_fruitTypeManager:getFruitTypeByIndex(FruitType.WHEAT) |
| 101 | for i = 1,table.getn(field.maxFieldStatusPartitions) do |
| 102 | g_fieldManager:setFieldPartitionStatus(field, field.maxFieldStatusPartitions, i, fruitDesc.index, FieldManager.FIELDSTATE_GROWING, fruitDesc.maxHarvestingGrowthState, 3, true, g_currentMission.plowCounterMaxValue, 0, g_currentMission.limeCounterMaxValue) |
| 103 | end |
| 104 | |
| 105 | local field = g_fieldManager:getFieldByIndex(25) |
| 106 | local fruitDesc = g_fruitTypeManager:getFruitTypeByIndex(FruitType.CANOLA) |
| 107 | for i = 1,table.getn(field.maxFieldStatusPartitions) do |
| 108 | g_fieldManager:setFieldPartitionStatus(field, field.maxFieldStatusPartitions, i, fruitDesc.index, FieldManager.FIELDSTATE_HARVESTED, 0, 0, false, g_currentMission.plowCounterMaxValue, 0, g_currentMission.limeCounterMaxValue) |
| 109 | end |
| 110 | |
| 111 | local field = g_fieldManager:getFieldByIndex(26) |
| 112 | for i = 1,table.getn(field.maxFieldStatusPartitions) do |
| 113 | g_fieldManager:setFieldPartitionStatus(field, field.maxFieldStatusPartitions, i, nil, FieldManager.FIELDSTATE_CULTIVATED, 0, 0, false, g_currentMission.plowCounterMaxValue, 0, g_currentMission.limeCounterMaxValue) |
| 114 | end |
| 115 | |
| 116 | local field = g_fieldManager:getFieldByIndex(19) |
| 117 | for i = 1,table.getn(field.maxFieldStatusPartitions) do |
| 118 | g_fieldManager:setFieldPartitionStatus(field, field.maxFieldStatusPartitions, i, nil, FieldManager.FIELDSTATE_CULTIVATED, 0, 0, false, g_currentMission.plowCounterMaxValue, 0, g_currentMission.limeCounterMaxValue) |
| 119 | end |
| 120 | end |
| 121 | |
| 122 | if self.startTourDialog then |
| 123 | self.startTourDialogDelay = self.startTourDialogDelay - dt |
| 124 | local showDialog = true |
| 125 | if g_currentMission.cameraFlightManager ~= nil then |
| 126 | showDialog = g_currentMission.cameraFlightManager.careerStartFlightPlayed |
| 127 | end |
| 128 | if showDialog then |
| 129 | if self.startTourDialogDelay < 0 then |
| 130 | self.startTourDialog = false |
| 131 | self:showTourDialog() |
| 132 | end |
| 133 | end |
| 134 | end |
| 135 | |
| 136 | if self.isPaused then |
| 137 | if self.pauseTime > 0 then |
| 138 | self.pauseTime = self.pauseTime - dt |
| 139 | else |
| 140 | self.pauseTime = 0 |
| 141 | self.isPaused = false |
| 142 | self:activateNextIcon() |
| 143 | end |
| 144 | end |
| 145 | |
| 146 | if self.visible and not self.isPaused then |
| 147 | -- show current permanent message on screen if no ingame message or any other GUI screen is displayed |
| 148 | if not g_currentMission.hud:isInGameMessageVisible() and not g_gui:getIsGuiVisible() then |
| 149 | |
| 150 | if false then --g_i18n:hasText("tour_permanentText" .. self.currentTourIconNumber - 1) then |
| 151 | |
| 152 | if self.permanentMessageDelay > 0 then |
| 153 | self.permanentMessageDelay = self.permanentMessageDelay - dt |
| 154 | self.alpha = 0.25 |
| 155 | self.alphaDirection = 1 |
| 156 | else |
| 157 | |
| 158 | setTextColor(1, 1, 1, self.alpha) |
| 159 | setTextBold(true) |
| 160 | setTextAlignment(RenderText.ALIGN_CENTER) |
| 161 | setTextWrapWidth(0.35) |
| 162 | if GS_IS_CONSOLE_VERSION then |
| 163 | setTextWrapWidth(0.295) |
| 164 | end |
| 165 | renderText(0.5, 0.93, self.permanentTextSize, g_i18n:getText("tour_permanentText" .. self.currentTourIconNumber - 1)) |
| 166 | setTextWrapWidth(0) |
| 167 | setTextAlignment(RenderText.ALIGN_LEFT) |
| 168 | setTextBold(false) |
| 169 | setTextColor(1, 1, 1, 1) |
| 170 | |
| 171 | self.alpha = self.alpha + self.alphaDirection * (dt / 600) |
| 172 | if self.alpha > 1 or self.alpha < 0.25 then |
| 173 | self.alphaDirection = self.alphaDirection * -1 |
| 174 | self.alpha = MathUtil.clamp(self.alpha, 0.25, 1) |
| 175 | end |
| 176 | end |
| 177 | |
| 178 | end |
| 179 | end |
| 180 | |
| 181 | -- handle special cases |
| 182 | |
| 183 | --# harvesting |
| 184 | |
| 185 | -- icon #3 activates as soon as the player enters the tour's combine harvester |
| 186 | if self.currentTourIconNumber <= 3 then |
| 187 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourCombine"] then |
| 188 | self.currentTourIconNumber = 3 |
| 189 | -- self:activateNextIcon() |
| 190 | self.pauseTime = 1000 |
| 191 | self.isPaused = true |
| 192 | end |
| 193 | end |
| 194 | |
| 195 | -- wait for player to attach the cutter |
| 196 | if self.currentTourIconNumber == 4 then |
| 197 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourCombine"] then |
| 198 | if g_currentMission.tourVehicles["tourCombine"].spec_combine.numAttachedCutters > 0 then |
| 199 | -- self:activateNextIcon() |
| 200 | self.pauseTime = 1000 |
| 201 | self.isPaused = true |
| 202 | end |
| 203 | end |
| 204 | end |
| 205 | |
| 206 | -- wait for player to activate the cutter |
| 207 | if self.currentTourIconNumber == 5 then |
| 208 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourCombine"] then |
| 209 | if g_currentMission.controlledVehicle:getIsTurnedOn() then |
| 210 | -- self:activateNextIcon() |
| 211 | self.pauseTime = 1000 |
| 212 | self.isPaused = true |
| 213 | end |
| 214 | end |
| 215 | end |
| 216 | |
| 217 | -- wait for player to activate the helper |
| 218 | if self.currentTourIconNumber == 7 then |
| 219 | if g_currentMission.tourVehicles["tourCombine"]:getIsTurnedOn() and g_currentMission.tourVehicles["tourCombine"]:getIsAIActive() then |
| 220 | -- self:activateNextIcon() |
| 221 | self.pauseTime = 1000 |
| 222 | self.isPaused = true |
| 223 | end |
| 224 | end |
| 225 | |
| 226 | --# cultivating |
| 227 | |
| 228 | -- wait for player to enter tractor1 and attach cultivator |
| 229 | if self.currentTourIconNumber == 9 then |
| 230 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourTractor1"] then |
| 231 | if g_currentMission.tourVehicles["tourCultivator"]:getRootVehicle() == g_currentMission.tourVehicles["tourTractor1"] |
| 232 | and g_currentMission.tourVehicles["tourWeight1"]:getRootVehicle() == g_currentMission.tourVehicles["tourTractor1"] then |
| 233 | -- self:activateNextIcon() |
| 234 | self.pauseTime = 1000 |
| 235 | self.isPaused = true |
| 236 | end |
| 237 | end |
| 238 | end |
| 239 | |
| 240 | if self.currentTourIconNumber == 11 then |
| 241 | -- self:activateNextIcon() |
| 242 | self.pauseTime = 1000 |
| 243 | self.isPaused = true |
| 244 | end |
| 245 | |
| 246 | --# sowing |
| 247 | |
| 248 | -- wait for player to enter tractor2 and attach sowingMachine |
| 249 | if self.currentTourIconNumber == 13 then |
| 250 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourTractor2"] then |
| 251 | if g_currentMission.tourVehicles["tourSowingMachine"]:getRootVehicle() == g_currentMission.tourVehicles["tourTractor2"] |
| 252 | and g_currentMission.tourVehicles["tourWeight2"]:getRootVehicle() == g_currentMission.tourVehicles["tourTractor2"] then |
| 253 | -- self:activateNextIcon() |
| 254 | self.pauseTime = 1000 |
| 255 | self.isPaused = true |
| 256 | end |
| 257 | end |
| 258 | end |
| 259 | |
| 260 | |
| 261 | --# overloading / tipping |
| 262 | |
| 263 | -- wait for player to enter tractor3 and attach trailer |
| 264 | if self.currentTourIconNumber == 16 then |
| 265 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourTractor3"] then |
| 266 | if g_currentMission.tourVehicles["tourTrailer"]:getRootVehicle() == g_currentMission.tourVehicles["tourTractor3"] then |
| 267 | -- self:activateNextIcon() |
| 268 | self.pauseTime = 1000 |
| 269 | self.isPaused = true |
| 270 | end |
| 271 | end |
| 272 | end |
| 273 | |
| 274 | if self.currentTourIconNumber == 17 then |
| 275 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourTractor3"] then |
| 276 | if g_currentMission.tourVehicles["tourTrailer"]:getRootVehicle() == g_currentMission.tourVehicles["tourTractor3"] then |
| 277 | |
| 278 | local tractor = g_currentMission.tourVehicles["tourTractor3"] |
| 279 | local trailer = g_currentMission.tourVehicles["tourTrailer"] |
| 280 | local combine = g_currentMission.tourVehicles["tourCombine"] |
| 281 | |
| 282 | local x,y,z = localToWorld(combine.components[1].node, 6,0,0) |
| 283 | self.mapHotspot:setWorldPosition(x, z) |
| 284 | |
| 285 | local xv,yv,zv = getWorldTranslation(tractor.components[1].node) |
| 286 | local dist = MathUtil.vector3Length(x-xv, y-yv, z-zv) |
| 287 | if dist < 10 then |
| 288 | g_currentMission:setMapTargetHotspot(nil) |
| 289 | else |
| 290 | g_currentMission:setMapTargetHotspot(self.mapHotspot) |
| 291 | end |
| 292 | |
| 293 | local fillUnitIndex = 1 |
| 294 | local dischargeNode = combine:getCurrentDischargeNode() |
| 295 | if trailer:getFillUnitFillLevel(fillUnitIndex) > 0 and dischargeNode ~= nil and dischargeNode.dischargeObject == trailer then |
| 296 | -- self:activateNextIcon() |
| 297 | self.pauseTime = 1000 |
| 298 | self.isPaused = true |
| 299 | end |
| 300 | end |
| 301 | else |
| 302 | -- If the harvester is full or stopped, and a player goes into the harvester to open the pipe, the trailer vehicle |
| 303 | -- is not controlled so above code does not trigger. This cascades and breaks the tour. |
| 304 | if g_currentMission.tourVehicles["tourTrailer"]:getFillUnitFillLevel(1) > 400 then |
| 305 | self.pauseTime = 1000 |
| 306 | self.isPaused = true |
| 307 | end |
| 308 | end |
| 309 | end |
| 310 | |
| 311 | if self.currentTourIconNumber == 19 then |
| 312 | -- self:activateNextIcon() |
| 313 | self.pauseTime = 3000 |
| 314 | self.isPaused = true |
| 315 | end |
| 316 | |
| 317 | if self.currentTourIconNumber == 20 then |
| 318 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle == g_currentMission.tourVehicles["tourTractor3"] then |
| 319 | if g_currentMission.tourVehicles["tourTrailer"]:getRootVehicle() == g_currentMission.tourVehicles["tourTractor3"] then |
| 320 | local trailer = g_currentMission.tourVehicles["tourTrailer"] |
| 321 | |
| 322 | local unloadingStation |
| 323 | for _, station in pairs(g_currentMission.unloadingStations) do |
| 324 | if station.owningPlaceable ~= nil and station.owningPlaceable.mapBoundId == "sellingStationRestaurant" then |
| 325 | unloadingStation = station |
| 326 | break |
| 327 | end |
| 328 | end |
| 329 | |
| 330 | local dischargeNode = trailer:getCurrentDischargeNode() |
| 331 | |
| 332 | if dischargeNode ~= nil and dischargeNode.currentDischargeObject ~= nil and dischargeNode.currentDischargeObject:getTarget() == unloadingStation then |
| 333 | -- self:activateNextIcon() |
| 334 | self.pauseTime = 3000 |
| 335 | self.isPaused = true |
| 336 | end |
| 337 | end |
| 338 | end |
| 339 | end |
| 340 | |
| 341 | --# shop |
| 342 | if self.currentTourIconNumber == 22 then |
| 343 | -- self:activateNextIcon() |
| 344 | self.pauseTime = 1000 |
| 345 | self.isPaused = true |
| 346 | end |
| 347 | |
| 348 | end |
| 349 | end |
Make tour icon visableDefinition
makeIconVisible(integer tourIconId)Arguments
| integer | tourIconId | id of tour icon |
| 354 | function TourIcons:makeIconVisible(tourIconId) |
| 355 | -- make next icon visible |
| 356 | setVisibility(tourIconId, true) |
| 357 | local x, _, z = getWorldTranslation(tourIconId) |
| 358 | |
| 359 | if self.mapHotspot == nil then |
| 360 | self.mapHotspot = MapHotspot:new("guide", MapHotspot.CATEGORY_TOUR) |
| 361 | self.mapHotspot:setImage(nil, getNormalizedUVs(MapHotspot.UV.HIGHLIGHT_MARKER), {0.2705, 0.6514, 0.0802, 1}) |
| 362 | self.mapHotspot:setPersistent(true) |
| 363 | self.mapHotspot:setRenderLast(true) |
| 364 | self.mapHotspot:setBlinking(true) |
| 365 | |
| 366 | g_currentMission:addMapHotspot(self.mapHotspot) |
| 367 | end |
| 368 | |
| 369 | self.mapHotspot:setWorldPosition(x, z) |
| 370 | |
| 371 | |
| 372 | -- Find 'hidden' icon used internally only |
| 373 | local x,y,z = getWorldTranslation(tourIconId) |
| 374 | local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x,y,z) |
| 375 | if y > h then |
| 376 | g_currentMission:setMapTargetHotspot(self.mapHotspot) |
| 377 | self.mapHotspot.enabled = true |
| 378 | else |
| 379 | g_currentMission:setMapTargetHotspot(nil) |
| 380 | self.mapHotspot.enabled = false |
| 381 | end |
| 382 | |
| 383 | g_currentMission.hud.ingameMap:toggleSize(IngameMap.STATE_MINIMAP, true) |
| 384 | end |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 393 | function TourIcons:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 394 | if onEnter then |
| 395 | if self.tourIcons[self.currentTourIconNumber] ~= nil then |
| 396 | if self.tourIcons[self.currentTourIconNumber].tourIconTriggerId == triggerId and getVisibility(self.tourIcons[self.currentTourIconNumber].tourIconId) then |
| 397 | self:activateNextIcon() |
| 398 | end |
| 399 | end |
| 400 | end |
| 401 | end |
Activate next iconDefinition
activateNextIcon()Code
| 405 | function TourIcons:activateNextIcon() |
| 406 | |
| 407 | -- make all previous icons invisible (also handles cases where player managed to skip icons) |
| 408 | for i = 1, self.currentTourIconNumber do |
| 409 | local tourIcon = self.tourIcons[i] |
| 410 | if getVisibility(tourIcon.tourIconId) then |
| 411 | setVisibility(tourIcon.tourIconId, false) |
| 412 | setCollisionMask(tourIcon.tourIconTriggerId, 0) |
| 413 | end |
| 414 | end |
| 415 | |
| 416 | if self.tourIcons[self.currentTourIconNumber + 1] ~= nil then |
| 417 | self:makeIconVisible(self.tourIcons[self.currentTourIconNumber + 1].tourIconId) |
| 418 | else |
| 419 | -- end of tour! |
| 420 | if self.mapHotspot ~= nil then |
| 421 | g_currentMission:removeMapHotspot(self.mapHotspot) |
| 422 | self.mapHotspot:delete() |
| 423 | self.mapHotspot = nil |
| 424 | end |
| 425 | -- re-display non-tour help icons |
| 426 | if g_gameSettings:getValue("showHelpIcons") then |
| 427 | if g_currentMission.helpIconsBase ~= nil then |
| 428 | g_currentMission.helpIconsBase:showHelpIcons(true, true) |
| 429 | end |
| 430 | end |
| 431 | |
| 432 | self.visible = false |
| 433 | end |
| 434 | |
| 435 | local title = g_i18n:getText("ui_tour") |
| 436 | local text = "" |
| 437 | local controls = {} |
| 438 | |
| 439 | if self.currentTourIconNumber == 1 then |
| 440 | |
| 441 | text = g_i18n:getText("tour_text_part01_lookAndWalk") |
| 442 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.TOGGLE_MAP_SIZE, nil, g_i18n:getText("action_toggleMapView"))) |
| 443 | |
| 444 | local useGamepadButtons = g_inputBinding:getInputHelpMode() == GS_INPUT_HELP_MODE_GAMEPAD |
| 445 | if useGamepadButtons then |
| 446 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.AXIS_MOVE_FORWARD_PLAYER, InputAction.AXIS_MOVE_SIDE_PLAYER, g_i18n:getText("action_movePlayer"))) |
| 447 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.AXIS_LOOK_UPDOWN_PLAYER, InputAction.AXIS_LOOK_LEFTRIGHT_PLAYER, g_i18n:getText("action_lookPlayer"))) |
| 448 | else |
| 449 | -- special case on PC |
| 450 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.AXIS_MOVE_FORWARD_PLAYER, InputAction.AXIS_MOVE_SIDE_PLAYER, g_i18n:getText("action_movePlayer"))) |
| 451 | |
| 452 | -- to show a mouse icon, instead of 'arrow keys' on keyboard |
| 453 | -- local lookAroundSymbol = g_inputDisplayManager.controllerSymbols["mouse_MOUSE_BUTTON_NONE"] -- TODO: fix direct access, too error prone |
| 454 | -- if lookAroundSymbol ~= nil then |
| 455 | -- table.insert(controls, {overlays = {lookAroundSymbol.overlay}, text = g_i18n:getText("action_lookPlayer")} ) |
| 456 | -- end |
| 457 | end |
| 458 | |
| 459 | elseif self.currentTourIconNumber == 2 then -- # harvesting |
| 460 | |
| 461 | text = g_i18n:getText("tour_text_part01_enterCombine") |
| 462 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ENTER, nil, g_i18n:getText("input_ENTER"))) |
| 463 | |
| 464 | elseif self.currentTourIconNumber == 3 then |
| 465 | |
| 466 | text = g_i18n:getText("tour_text_part01_attachHeader") |
| 467 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ATTACH, nil, g_i18n:getText("input_ATTACH"))) |
| 468 | |
| 469 | elseif self.currentTourIconNumber == 4 then |
| 470 | |
| 471 | text = g_i18n:getText("tour_text_part01_turnOnCombine") |
| 472 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.SWITCH_IMPLEMENT, nil, g_i18n:getText("action_switchImplement"))) |
| 473 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.IMPLEMENT_EXTRA2, nil, g_i18n:getText("action_unfold"))) |
| 474 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.IMPLEMENT_EXTRA, nil, g_i18n:getText("action_turnOn"))) |
| 475 | |
| 476 | elseif self.currentTourIconNumber == 5 then |
| 477 | |
| 478 | text = g_i18n:getText("tour_text_part01_startHarvesting") |
| 479 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.AXIS_ACCELERATE_VEHICLE, InputAction.AXIS_BRAKE_VEHICLE, g_i18n:getText("action_accelerate"))) |
| 480 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.AXIS_MOVE_SIDE_VEHICLE, nil, g_i18n:getText("action_steer"))) |
| 481 | |
| 482 | elseif self.currentTourIconNumber == 6 then |
| 483 | |
| 484 | text = g_i18n:getText("tour_text_part01_startHelperHarvesting") |
| 485 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.TOGGLE_AI, nil, g_i18n:getText("input_TOGGLE_AI"))) |
| 486 | |
| 487 | elseif self.currentTourIconNumber == 7 then |
| 488 | |
| 489 | text = g_i18n:getText("tour_text_part01_finished") |
| 490 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ENTER, nil, g_i18n:getText("action_exitVehicle"))) |
| 491 | |
| 492 | elseif self.currentTourIconNumber == 8 then -- # cultivating |
| 493 | |
| 494 | text = g_i18n:getText("tour_text_part02_enterTractor01") |
| 495 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ENTER, nil, g_i18n:getText("input_ENTER"))) |
| 496 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ATTACH, nil, g_i18n:getText("input_ATTACH"))) |
| 497 | |
| 498 | elseif self.currentTourIconNumber == 9 then |
| 499 | |
| 500 | text = g_i18n:getText("tour_text_part02_startCultivating") |
| 501 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.LOWER_IMPLEMENT, nil, g_i18n:getText("input_LOWER_IMPLEMENT"))) |
| 502 | |
| 503 | elseif self.currentTourIconNumber == 10 then |
| 504 | |
| 505 | text = g_i18n:getText("tour_text_part02_enoughCultivating") |
| 506 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.TOGGLE_AI, nil, g_i18n:getText("input_TOGGLE_AI"))) |
| 507 | |
| 508 | elseif self.currentTourIconNumber == 11 then |
| 509 | |
| 510 | text = g_i18n:getText("tour_text_part02_finished") |
| 511 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.SWITCH_VEHICLE, nil, g_i18n:getText("input_SWITCH_VEHICLE"))) |
| 512 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.SWITCH_VEHICLE_BACK, nil, g_i18n:getText("input_SWITCH_VEHICLE_BACK"))) |
| 513 | |
| 514 | elseif self.currentTourIconNumber == 12 then -- # sowing |
| 515 | |
| 516 | text = g_i18n:getText("tour_text_part03_enterTractor01") |
| 517 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ENTER, nil, g_i18n:getText("input_ENTER"))) |
| 518 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ATTACH, nil, g_i18n:getText("input_ATTACH"))) |
| 519 | |
| 520 | elseif self.currentTourIconNumber == 13 then |
| 521 | |
| 522 | text = g_i18n:getText("tour_text_part03_startSowing") |
| 523 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.IMPLEMENT_EXTRA3, nil, g_i18n:getText("action_chooseSeed"))) |
| 524 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.LOWER_IMPLEMENT, nil, g_i18n:getText("input_LOWER_IMPLEMENT"))) |
| 525 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.IMPLEMENT_EXTRA, nil, g_i18n:getText("action_turnOn"))) |
| 526 | |
| 527 | elseif self.currentTourIconNumber == 14 then |
| 528 | |
| 529 | text = g_i18n:getText("tour_text_part03_finished") |
| 530 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.SWITCH_VEHICLE, nil, g_i18n:getText("input_SWITCH_VEHICLE"))) |
| 531 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.SWITCH_VEHICLE_BACK, nil, g_i18n:getText("input_SWITCH_VEHICLE_BACK"))) |
| 532 | |
| 533 | elseif self.currentTourIconNumber == 15 then -- # trailer / tipping and selling |
| 534 | |
| 535 | text = g_i18n:getText("tour_text_part04_enterTractor01") |
| 536 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ENTER, nil, g_i18n:getText("input_ENTER"))) |
| 537 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.ATTACH, nil, g_i18n:getText("input_ATTACH"))) |
| 538 | |
| 539 | elseif self.currentTourIconNumber == 16 then |
| 540 | |
| 541 | text = g_i18n:getText("tour_text_part04_alignToHarvester") |
| 542 | |
| 543 | elseif self.currentTourIconNumber == 17 then |
| 544 | |
| 545 | text = g_i18n:getText("tour_text_part04_unloadWheat") |
| 546 | |
| 547 | elseif self.currentTourIconNumber == 18 then |
| 548 | |
| 549 | text = g_i18n:getText("tour_text_part04_pricesInfo") |
| 550 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.MENU, nil, g_i18n:getText("input_MENU"))) |
| 551 | |
| 552 | elseif self.currentTourIconNumber == 19 then |
| 553 | |
| 554 | text = g_i18n:getText("tour_text_part04_sellWheat") |
| 555 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.TOGGLE_TIPSTATE, nil, g_i18n:getText("input_TOGGLE_TIPSTATE"))) |
| 556 | |
| 557 | elseif self.currentTourIconNumber == 20 then |
| 558 | |
| 559 | text = g_i18n:getText("tour_text_part04_doneSelling") |
| 560 | |
| 561 | elseif self.currentTourIconNumber == 21 then -- # shop |
| 562 | |
| 563 | text = g_i18n:getText("tour_text_part05_visitShop") |
| 564 | table.insert(controls, g_inputDisplayManager:getControllerSymbolOverlays(InputAction.TOGGLE_STORE, nil, g_i18n:getText("input_TOGGLE_STORE"))) |
| 565 | |
| 566 | elseif self.currentTourIconNumber == 22 then |
| 567 | |
| 568 | text = g_i18n:getText("tour_text_end") |
| 569 | |
| 570 | end |
| 571 | |
| 572 | if g_currentMission.controlledVehicle ~= nil and g_currentMission.controlledVehicle.setCruiseControlState ~= nil then |
| 573 | g_currentMission.controlledVehicle:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF) |
| 574 | end |
| 575 | |
| 576 | g_currentMission.hud:showInGameMessage(title, text, -1, controls) |
| 577 | |
| 578 | |
| 579 | self.currentTourIconNumber = self.currentTourIconNumber + 1 |
| 580 | |
| 581 | self.permanentMessageDelay = 250 |
| 582 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 99 | function UnloadingStation:readStream(streamId, connection) |
| 100 | UnloadingStation:superClass().readStream(self, streamId, connection) |
| 101 | if connection:getIsServer() then |
| 102 | for _, unloadTrigger in ipairs(self.unloadTriggers) do |
| 103 | local unloadTriggerId = NetworkUtil.readNodeObjectId(streamId) |
| 104 | unloadTrigger:readStream(streamId, connection) |
| 105 | g_client:finishRegisterObject(unloadTrigger, unloadTriggerId) |
| 106 | end |
| 107 | end |
| 108 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 114 | function UnloadingStation:writeStream(streamId, connection) |
| 115 | UnloadingStation:superClass().writeStream(self, streamId, connection) |
| 116 | if not connection:getIsServer() then |
| 117 | for _, unloadTrigger in ipairs(self.unloadTriggers) do |
| 118 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(unloadTrigger)) |
| 119 | unloadTrigger:writeStream(streamId, connection) |
| 120 | g_server:registerObjectInStream(connection, unloadTrigger) |
| 121 | end |
| 122 | end |
| 123 | end |
Class for vehicle selling point
Creating vehicle selling pointDefinition
new(integer id)Arguments
| integer | id | node id |
| table | instance | Instance of object |
| 16 | function VehicleSellingPoint:new(id) |
| 17 | local self = {} |
| 18 | setmetatable(self, VehicleSellingPoint_mt) |
| 19 | |
| 20 | self.id = id |
| 21 | |
| 22 | self.vehicleInRange = {} |
| 23 | self.currentVehicle = nil |
| 24 | self.currentVehicleId = 0 |
| 25 | |
| 26 | self.activateText = g_i18n:getText("action_configSellSpecificVehicle") |
| 27 | |
| 28 | self.isEnabled = true |
| 29 | self.objectActivated = false |
| 30 | |
| 31 | return self |
| 32 | end |
Deleting vehicle selling pointDefinition
delete()Code
| 50 | function VehicleSellingPoint:delete() |
| 51 | g_messageCenter:unsubscribeAll(self) |
| 52 | |
| 53 | if self.playerTrigger ~= nil then |
| 54 | removeTrigger(self.playerTrigger) |
| 55 | self.playerTrigger = nil |
| 56 | end |
| 57 | if self.sellTriggerNode ~= nil then |
| 58 | removeTrigger(self.sellTriggerNode) |
| 59 | self.sellTriggerNode = nil |
| 60 | end |
| 61 | |
| 62 | self.sellIcon = nil |
| 63 | end |
Get is activateableDefinition
getIsActivatable()Return Values
| boolean | isActivateable | is activateable |
| 68 | function VehicleSellingPoint:getIsActivatable() |
| 69 | return self.isEnabled and g_currentMission.controlPlayer and (self:getOwnerFarmId() == 0 or g_currentMission:getFarmId() == self:getOwnerFarmId()) |
| 70 | end |
On activate objectDefinition
onActivateObject()Code
| 78 | function VehicleSellingPoint:onActivateObject() |
| 79 | self:determineCurrentVehicle() |
| 80 | g_gui:showDirectSellDialog({vehicle=self.currentVehicle, owner=self, ownWorkshop=self.ownWorkshop}) |
| 81 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 86 | function VehicleSellingPoint:update(dt) |
| 87 | end |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 96 | function VehicleSellingPoint:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 97 | if self.isEnabled and (not g_isPresentationVersion or g_isPresentationVersionShopEnabled) and g_currentMission.missionInfo:isa(FSCareerMissionInfo) then |
| 98 | if onEnter or onLeave then |
| 99 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
| 100 | if onEnter then |
| 101 | if not self.objectActivated then |
| 102 | g_currentMission:addActivatableObject(self) |
| 103 | self.objectActivated = true |
| 104 | end |
| 105 | else |
| 106 | if self.objectActivated then |
| 107 | g_currentMission:removeActivatableObject(self) |
| 108 | self.objectActivated = false |
| 109 | end |
| 110 | end |
| 111 | end |
| 112 | end |
| 113 | end |
| 114 | end |
Sell area trigger callbackDefinition
sellAreaTriggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 123 | function VehicleSellingPoint:sellAreaTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId) |
| 124 | if otherShapeId ~= nil and (onEnter or onLeave) then |
| 125 | if onEnter then |
| 126 | self.vehicleInRange[otherShapeId] = true |
| 127 | elseif onLeave then |
| 128 | self.vehicleInRange[otherShapeId] = nil |
| 129 | end |
| 130 | |
| 131 | self:determineCurrentVehicle() |
| 132 | end |
| 133 | end |
Determine current vehicleDefinition
determineCurrentVehicle()Code
| 137 | function VehicleSellingPoint:determineCurrentVehicle() |
| 138 | self.currentVehicle = nil |
| 139 | for vehicleId, inRange in pairs(self.vehicleInRange) do |
| 140 | if inRange ~= nil then |
| 141 | self.currentVehicle = g_currentMission.nodeToObject[vehicleId] |
| 142 | |
| 143 | if self.currentVehicle ~= nil then |
| 144 | if not SpecializationUtil.hasSpecialization(Rideable, self.currentVehicle.specializations) or self.currentVehicle:getOwnerFarmId() ~= self:getOwnerFarmId() then |
| 145 | break |
| 146 | end |
| 147 | end |
| 148 | end |
| 149 | -- invalid vehicle (not existing or left), remove from list |
| 150 | self.vehicleInRange[vehicleId] = nil |
| 151 | end |
| 152 | end |
Turn the icon on or off depending on the current game and the players farmDefinition
updateIconVisibility()
Event function for activation input.Definition
onActivate()
Class for placeable beehivesParent
Placeable
Creating placeable beehiveDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 18 | function BeehivePlaceable:new(isServer, isClient, customMt) |
| 19 | local mt = customMt |
| 20 | if mt == nil then |
| 21 | mt = BeehivePlaceable_mt |
| 22 | end |
| 23 | |
| 24 | local self = Placeable:new(isServer, isClient, mt) |
| 25 | |
| 26 | registerObjectClassName(self, "BeehivePlaceable") |
| 27 | |
| 28 | return self |
| 29 | end |
Deleting placeable beehiveDefinition
delete()Code
| 33 | function BeehivePlaceable:delete() |
| 34 | unregisterObjectClassName(self) |
| 35 | g_currentMission.environment:removeHourChangeListener(self) |
| 36 | if self.particleSystem ~= nil then |
| 37 | ParticleUtil.deleteParticleSystem(self.particleSystem) |
| 38 | end |
| 39 | BeehivePlaceable:superClass().delete(self) |
| 40 | end |
Deleting placeable beehive finalDefinition
deleteFinal()Code
| 44 | function BeehivePlaceable:deleteFinal() |
| 45 | BeehivePlaceable:superClass().deleteFinal(self) |
| 46 | end |
Load beehiveDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 59 | function BeehivePlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 60 | if not BeehivePlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 61 | return false |
| 62 | end |
| 63 | |
| 64 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 65 | |
| 66 | self.particleSystem = {} |
| 67 | ParticleUtil.loadParticleSystem(xmlFile, self.particleSystem, "placeable.particleSystem", self.nodeId, true, nil, self.baseDirectory) |
| 68 | |
| 69 | delete(xmlFile) |
| 70 | |
| 71 | return true |
| 72 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 76 | function BeehivePlaceable:finalizePlacement() |
| 77 | BeehivePlaceable:superClass().finalizePlacement(self) |
| 78 | end |
Called if hour changedDefinition
hourChanged()Code
| 82 | function BeehivePlaceable:hourChanged() |
| 83 | if self.isServer then |
| 84 | g_currentMission:addMoney(self.incomePerHour, self:getOwnerFarmId(), "propertyIncome") |
| 85 | g_currentMission:addMoneyChange(self.incomePerHour, self:getOwnerFarmId(), EconomyManager.MONEY_TYPE_PROPERTY_INCOME) |
| 86 | end |
| 87 | end |
Class for bgaParent
Placeable
Creating placeable siloDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 24 | function BgaPlaceable:new(isServer, isClient, customMt) |
| 25 | local self = Placeable:new(isServer, isClient, customMt or BgaPlaceable_mt) |
| 26 | |
| 27 | registerObjectClassName(self, "BgaPlaceable") |
| 28 | |
| 29 | return self |
| 30 | end |
Deleting placeable siloDefinition
delete()Code
| 34 | function BgaPlaceable:delete() |
| 35 | self.bga:delete() |
| 36 | |
| 37 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 38 | bunkerSilo:delete() |
| 39 | end |
| 40 | |
| 41 | g_farmlandManager:removeStateChangeListener(self) |
| 42 | |
| 43 | unregisterObjectClassName(self) |
| 44 | BgaPlaceable:superClass().delete(self) |
| 45 | end |
Load siloDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 58 | function BgaPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 59 | if not BgaPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 60 | return false |
| 61 | end |
| 62 | |
| 63 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 64 | |
| 65 | self.bga = Bga:new(self.isServer, self.isClient) |
| 66 | if not self.bga:load(self.nodeId, xmlFile, "placeable.bga") then |
| 67 | self.bga:delete() |
| 68 | delete(xmlFile) |
| 69 | return false |
| 70 | end |
| 71 | |
| 72 | self.bunkerSilos = {} |
| 73 | |
| 74 | local i = 0 |
| 75 | while true do |
| 76 | local bunkerKey = string.format("placeable.bunkerSilos.bunkerSilo(%d)", i) |
| 77 | if not hasXMLProperty(xmlFile, bunkerKey) then |
| 78 | break |
| 79 | end |
| 80 | |
| 81 | local bunkerSilo = BunkerSilo:new(self.isServer, self.isClient) |
| 82 | if bunkerSilo:load(self.nodeId, xmlFile, bunkerKey) then |
| 83 | table.insert(self.bunkerSilos, bunkerSilo) |
| 84 | else |
| 85 | bunkerSilo:delete() |
| 86 | end |
| 87 | |
| 88 | i = i + 1 |
| 89 | end |
| 90 | |
| 91 | delete(xmlFile) |
| 92 | |
| 93 | return true |
| 94 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 98 | function BgaPlaceable:finalizePlacement() |
| 99 | BgaPlaceable:superClass().finalizePlacement(self) |
| 100 | |
| 101 | self.bga:register(true) |
| 102 | |
| 103 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 104 | bunkerSilo:register(true) |
| 105 | end |
| 106 | |
| 107 | if self.isServer then |
| 108 | self:updateOwnership(true) |
| 109 | end |
| 110 | |
| 111 | g_farmlandManager:addStateChangeListener(self) |
| 112 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 118 | function BgaPlaceable:readStream(streamId, connection) |
| 119 | BgaPlaceable:superClass().readStream(self, streamId, connection) |
| 120 | if connection:getIsServer() then |
| 121 | local bgaId = NetworkUtil.readNodeObjectId(streamId) |
| 122 | self.bga:readStream(streamId, connection) |
| 123 | g_client:finishRegisterObject(self.bga, bgaId) |
| 124 | |
| 125 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 126 | local bunkerSiloId = NetworkUtil.readNodeObjectId(streamId) |
| 127 | bunkerSilo:readStream(streamId, connection) |
| 128 | g_client:finishRegisterObject(bunkerSilo, bunkerSiloId) |
| 129 | end |
| 130 | end |
| 131 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 137 | function BgaPlaceable:writeStream(streamId, connection) |
| 138 | BgaPlaceable:superClass().writeStream(self, streamId, connection) |
| 139 | if not connection:getIsServer() then |
| 140 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.bga)) |
| 141 | self.bga:writeStream(streamId, connection) |
| 142 | g_server:registerObjectInStream(connection, self.bga) |
| 143 | |
| 144 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 145 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(bunkerSilo)) |
| 146 | bunkerSilo:writeStream(streamId, connection) |
| 147 | g_server:registerObjectInStream(connection, bunkerSilo) |
| 148 | end |
| 149 | end |
| 150 | end |
Collect pick objectsDefinition
collectPickObjects(integer node)Arguments
| integer | node | node id |
| 167 | function BgaPlaceable:collectPickObjects(node) |
| 168 | local foundNode = false |
| 169 | for _, unloadTrigger in ipairs(self.bga.bunker.unloadingStation.unloadTriggers) do |
| 170 | if node == unloadTrigger.exactFillRootNode then |
| 171 | foundNode = true |
| 172 | break |
| 173 | end |
| 174 | end |
| 175 | |
| 176 | if not foundNode then |
| 177 | for _, loadTrigger in ipairs(self.bga.digestateSilo.loadingStation.loadTriggers) do |
| 178 | if node == loadTrigger.triggerNode then |
| 179 | foundNode = true |
| 180 | break |
| 181 | end |
| 182 | end |
| 183 | end |
| 184 | |
| 185 | if not foundNode then |
| 186 | BgaPlaceable:superClass().collectPickObjects(self, node) |
| 187 | end |
| 188 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 196 | function BgaPlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 197 | if not BgaPlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 198 | return false |
| 199 | end |
| 200 | |
| 201 | self.bga:loadFromXMLFile(xmlFile, key..".bga", resetVehicles) |
| 202 | |
| 203 | local i = 0 |
| 204 | while true do |
| 205 | local bunkerKey = string.format("%s.bunkerSilo(%d)", key, i) |
| 206 | if not hasXMLProperty(xmlFile, bunkerKey) then |
| 207 | break |
| 208 | end |
| 209 | |
| 210 | local index = getXMLInt(xmlFile, bunkerKey.."#index") |
| 211 | |
| 212 | if index ~= nil then |
| 213 | if self.bunkerSilos[index] ~= nil then |
| 214 | if not self.bunkerSilos[index]:loadFromXMLFile(xmlFile, bunkerKey) then |
| 215 | return false |
| 216 | end |
| 217 | else |
| 218 | g_logManager:warning("Could not load bunkersilo. Given 'index' '%d' is not defined!", index) |
| 219 | end |
| 220 | end |
| 221 | |
| 222 | i = i + 1 |
| 223 | end |
| 224 | |
| 225 | return true |
| 226 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 233 | function BgaPlaceable:saveToXMLFile(xmlFile, key, usedModNames) |
| 234 | BgaPlaceable:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 235 | |
| 236 | self.bga:saveToXMLFile(xmlFile, key..".bga", usedModNames) |
| 237 | |
| 238 | for k, bunkerSilo in ipairs(self.bunkerSilos) do |
| 239 | local bunkerKey = string.format("%s.bunkerSilo(%d)", key, k-1) |
| 240 | setXMLInt(xmlFile, bunkerKey .. "#index", k) |
| 241 | bunkerSilo:saveToXMLFile(xmlFile, bunkerKey, usedModNames) |
| 242 | end |
| 243 | end |
Creating placeable siloDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 26 | function BunkerSiloPlaceable:new(isServer, isClient, customMt) |
| 27 | local self = Placeable:new(isServer, isClient, customMt or BgaPlaceable_mt) |
| 28 | |
| 29 | registerObjectClassName(self, "BunkerSiloPlaceable") |
| 30 | |
| 31 | return self |
| 32 | end |
Deleting placeable siloDefinition
delete()Code
| 36 | function BunkerSiloPlaceable:delete() |
| 37 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 38 | bunkerSilo:delete() |
| 39 | end |
| 40 | |
| 41 | unregisterObjectClassName(self) |
| 42 | BunkerSiloPlaceable:superClass().delete(self) |
| 43 | end |
Load siloDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 56 | function BunkerSiloPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 57 | if not BunkerSiloPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 58 | return false |
| 59 | end |
| 60 | |
| 61 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 62 | |
| 63 | self.bunkerSilos = {} |
| 64 | |
| 65 | local i = 0 |
| 66 | while true do |
| 67 | local bunkerKey = string.format("placeable.bunkerSilos.bunkerSilo(%d)", i) |
| 68 | if not hasXMLProperty(xmlFile, bunkerKey) then |
| 69 | break |
| 70 | end |
| 71 | |
| 72 | local bunkerSilo = BunkerSilo:new(self.isServer, self.isClient) |
| 73 | if bunkerSilo:load(self.nodeId, xmlFile, bunkerKey) then |
| 74 | table.insert(self.bunkerSilos, bunkerSilo) |
| 75 | else |
| 76 | bunkerSilo:delete() |
| 77 | end |
| 78 | |
| 79 | i = i + 1 |
| 80 | end |
| 81 | |
| 82 | delete(xmlFile) |
| 83 | |
| 84 | return true |
| 85 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 89 | function BunkerSiloPlaceable:finalizePlacement() |
| 90 | BunkerSiloPlaceable:superClass().finalizePlacement(self) |
| 91 | |
| 92 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 93 | bunkerSilo:register(true) |
| 94 | end |
| 95 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 109 | function BunkerSiloPlaceable:readStream(streamId, connection) |
| 110 | BunkerSiloPlaceable:superClass().readStream(self, streamId, connection) |
| 111 | if connection:getIsServer() then |
| 112 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 113 | local bunkerSiloId = NetworkUtil.readNodeObjectId(streamId) |
| 114 | bunkerSilo:readStream(streamId, connection) |
| 115 | g_client:finishRegisterObject(bunkerSilo, bunkerSiloId) |
| 116 | end |
| 117 | end |
| 118 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 124 | function BunkerSiloPlaceable:writeStream(streamId, connection) |
| 125 | BunkerSiloPlaceable:superClass().writeStream(self, streamId, connection) |
| 126 | if not connection:getIsServer() then |
| 127 | for _, bunkerSilo in ipairs(self.bunkerSilos) do |
| 128 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(bunkerSilo)) |
| 129 | bunkerSilo:writeStream(streamId, connection) |
| 130 | g_server:registerObjectInStream(connection, bunkerSilo) |
| 131 | end |
| 132 | end |
| 133 | end |
Collect pick objectsDefinition
collectPickObjects(integer node)Arguments
| integer | node | node id |
| 138 | function BunkerSiloPlaceable:collectPickObjects(node) |
| 139 | -- local foundNode = false |
| 140 | -- for _, unloadTrigger in ipairs(self.bga.bunker.unloadingStation.unloadTriggers) do |
| 141 | -- if node == unloadTrigger.exactFillRootNode then |
| 142 | -- foundNode = true |
| 143 | -- break |
| 144 | -- end |
| 145 | -- end |
| 146 | |
| 147 | if not foundNode then |
| 148 | BunkerSiloPlaceable:superClass().collectPickObjects(self, node) |
| 149 | end |
| 150 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 158 | function BunkerSiloPlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 159 | if not BunkerSiloPlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 160 | return false |
| 161 | end |
| 162 | |
| 163 | local i = 0 |
| 164 | while true do |
| 165 | local bunkerKey = string.format("%s.bunkerSilo(%d)", key, i) |
| 166 | if not hasXMLProperty(xmlFile, bunkerKey) then |
| 167 | break |
| 168 | end |
| 169 | |
| 170 | local index = getXMLInt(xmlFile, bunkerKey.."#index") |
| 171 | |
| 172 | if index ~= nil then |
| 173 | if self.bunkerSilos[index] ~= nil then |
| 174 | if not self.bunkerSilos[index]:loadFromXMLFile(xmlFile, bunkerKey) then |
| 175 | return false |
| 176 | end |
| 177 | else |
| 178 | g_logManager:warning("Could not load bunkersilo. Given 'index' '%d' is not defined!", index) |
| 179 | end |
| 180 | end |
| 181 | |
| 182 | i = i + 1 |
| 183 | end |
| 184 | |
| 185 | return true |
| 186 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 193 | function BunkerSiloPlaceable:saveToXMLFile(xmlFile, key, usedModNames) |
| 194 | BunkerSiloPlaceable:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 195 | |
| 196 | for k, bunkerSilo in ipairs(self.bunkerSilos) do |
| 197 | local bunkerKey = string.format("%s.bunkerSilo(%d)", key, k-1) |
| 198 | setXMLInt(xmlFile, bunkerKey .. "#index", k) |
| 199 | bunkerSilo:saveToXMLFile(xmlFile, bunkerKey, usedModNames) |
| 200 | end |
| 201 | end |
Creating placeable siloDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 26 | function BuyingStationPlaceable:new(isServer, isClient, customMt) |
| 27 | local self = Placeable:new(isServer, isClient, customMt or SiloPlaceable_mt) |
| 28 | |
| 29 | registerObjectClassName(self, "BuyingStationPlaceable") |
| 30 | |
| 31 | return self |
| 32 | end |
Deleting placeable siloDefinition
delete()Code
| 36 | function BuyingStationPlaceable:delete() |
| 37 | if self.buyingStation ~= nil then |
| 38 | self.buyingStation:delete() |
| 39 | end |
| 40 | |
| 41 | unregisterObjectClassName(self) |
| 42 | BuyingStationPlaceable:superClass().delete(self) |
| 43 | end |
Load siloDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 56 | function BuyingStationPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 57 | if not BuyingStationPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 58 | return false |
| 59 | end |
| 60 | |
| 61 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 62 | |
| 63 | self.buyingStation = BuyingStation:new(self.isServer, self.isClient) |
| 64 | self.buyingStation:load(self.nodeId, xmlFile, "placeable.buyingStation") |
| 65 | self.buyingStation.owningPlaceable = self |
| 66 | |
| 67 | delete(xmlFile) |
| 68 | |
| 69 | return true |
| 70 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 74 | function BuyingStationPlaceable:finalizePlacement() |
| 75 | BuyingStationPlaceable:superClass().finalizePlacement(self) |
| 76 | |
| 77 | self.buyingStation:register(true) |
| 78 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 84 | function BuyingStationPlaceable:readStream(streamId, connection) |
| 85 | BuyingStationPlaceable:superClass().readStream(self, streamId, connection) |
| 86 | if connection:getIsServer() then |
| 87 | local buyingStationId = NetworkUtil.readNodeObjectId(streamId) |
| 88 | self.buyingStation:readStream(streamId, connection) |
| 89 | g_client:finishRegisterObject(self.buyingStation, buyingStationId) |
| 90 | end |
| 91 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 97 | function BuyingStationPlaceable:writeStream(streamId, connection) |
| 98 | BuyingStationPlaceable:superClass().writeStream(self, streamId, connection) |
| 99 | if not connection:getIsServer() then |
| 100 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.buyingStation)) |
| 101 | self.buyingStation:writeStream(streamId, connection) |
| 102 | g_server:registerObjectInStream(connection, self.buyingStation) |
| 103 | end |
| 104 | end |
Collect pick objectsDefinition
collectPickObjects(integer node)Arguments
| integer | node | node id |
| 109 | function BuyingStationPlaceable:collectPickObjects(node) |
| 110 | local foundNode = false |
| 111 | if not foundNode then |
| 112 | for _, loadTrigger in ipairs(self.buyingStation.loadTriggers) do |
| 113 | if node == loadTrigger.triggerNode then |
| 114 | foundNode = true |
| 115 | break |
| 116 | end |
| 117 | end |
| 118 | end |
| 119 | |
| 120 | if not foundNode then |
| 121 | BuyingStationPlaceable:superClass().collectPickObjects(self, node) |
| 122 | end |
| 123 | end |
Creating instance and initializing member variablesDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom meta table |
| table | instance | Instance of object |
| 14 | function Doghouse:new(isServer, isClient, customMt) |
| 15 | local self = Placeable:new(isServer, isClient, customMt or Doghouse_mt) |
| 16 | |
| 17 | registerObjectClassName(self, "Doghouse") |
| 18 | -- dog |
| 19 | self.dog = Dog:new() |
| 20 | |
| 21 | -- trigger |
| 22 | self.triggerNode = nil |
| 23 | self.isActivatable = false |
| 24 | self.activateText = g_i18n:getText("action_doghouseFillbowl") |
| 25 | |
| 26 | -- network |
| 27 | self.forcedClipDistance = 80 |
| 28 | |
| 29 | if not g_currentMission.gameStarted then |
| 30 | g_currentMission:registerObjectToCallOnMissionStart(self) |
| 31 | end |
| 32 | |
| 33 | return self |
| 34 | end |
Delete instanceDefinition
delete()Code
| 38 | function Doghouse:delete() |
| 39 | self.dog:delete() |
| 40 | self.dog = nil |
| 41 | unregisterObjectClassName(self) |
| 42 | if self.triggerNode ~= nil then |
| 43 | removeTrigger(self.triggerNode) |
| 44 | end |
| 45 | Doghouse:superClass().delete(self) |
| 46 | end |
Returns true if we can place a buildingDefinition
getCanBePlacedAt()Return Values
| 51 | function Doghouse:getCanBePlacedAt(x, y, z, distance) |
| 52 | local canBePlaced = Doghouse:superClass().getCanBePlacedAt(self, x, y, z, distance) |
| 53 | return canBePlaced and not self:isDoghouseRegistered() |
| 54 | end |
Returns true if we can place a buildingDefinition
canBuy()Return Values
| 59 | function Doghouse:canBuy() |
| 60 | local canBuy = AnimalHusbandry:superClass().canBuy(self) |
| 61 | return canBuy and not self:isDoghouseRegistered(), g_i18n:getText("warning_onlyOneOfThisItemAllowedPerFarm") |
| 62 | end |
Finalize placementDefinition
finalizePlacement()Return Values
| bool | returns | true if placement successful |
| 67 | function Doghouse:finalizePlacement() |
| 68 | Doghouse:superClass().finalizePlacement(self) |
| 69 | |
| 70 | -- dog specific information |
| 71 | local xmlFile = loadXMLFile("TempXML", self.configFileName) |
| 72 | |
| 73 | if xmlFile == 0 then |
| 74 | return false |
| 75 | end |
| 76 | |
| 77 | self.spawnNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.dog#spawnNode")) |
| 78 | local posX, posY, posZ = getWorldTranslation(self.spawnNode) |
| 79 | local dogXMLConfig = getXMLString(xmlFile, "placeable.dog#xmlFilename") |
| 80 | self.dog:init(self, dogXMLConfig, posX, posY, posZ, self.isServer, self.isClient) |
| 81 | self:registerDoghouseToMission() |
| 82 | |
| 83 | self.namePlateNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.nameplate#node")) |
| 84 | |
| 85 | -- player interaction trigger |
| 86 | self.triggerNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.playerInteractionTrigger#node")) |
| 87 | if self.triggerNode ~= nil then |
| 88 | addTrigger(self.triggerNode, "playerInteractionTriggerCallback", self) |
| 89 | end |
| 90 | |
| 91 | self.foodNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.bowl#foodNode")) |
| 92 | delete(xmlFile) |
| 93 | if self.foodNode ~= nil then |
| 94 | setVisibility(self.foodNode, false) |
| 95 | return true |
| 96 | end |
| 97 | return false |
| 98 | end |
Called on sell of the placeableDefinition
onSell()Code
| 102 | function Doghouse:onSell() |
| 103 | Doghouse:superClass().onSell(self) |
| 104 | if self.isServer then |
| 105 | self:unregisterDoghouseToMission() |
| 106 | end |
| 107 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 113 | function Doghouse:readStream(streamId, connection) |
| 114 | Doghouse:superClass().readStream(self, streamId, connection) |
| 115 | if connection:getIsServer() then |
| 116 | self.dog:setName(Utils.getNoNil(streamReadString(streamId), "")) |
| 117 | end |
| 118 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 124 | function Doghouse:writeStream(streamId, connection) |
| 125 | Doghouse:superClass().writeStream(self, streamId, connection) |
| 126 | if not connection:getIsServer() then |
| 127 | streamWriteString(streamId, self.dog.name) |
| 128 | end |
| 129 | end |
Write update network streamDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | network stream identification |
| table | connection | connection information |
| integer | dirtyMask |
| 136 | function Doghouse:writeUpdateStream(streamId, connection, dirtyMask) |
| 137 | Doghouse:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
| 138 | self.dog:writeUpdateStream(streamId, connection, dirtyMask) |
| 139 | end |
Read update network streamDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | network stream identification |
| integer | timestamp | |
| table | connection | connection information |
| 146 | function Doghouse:readUpdateStream(streamId, timestamp, connection) |
| 147 | Doghouse:superClass().readUpdateStream(self, streamId, timestamp, connection) |
| 148 | self.dog:readUpdateStream(streamId, timestamp, connection) |
| 149 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 157 | function Doghouse:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 158 | local result = Doghouse:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) |
| 159 | |
| 160 | if result then |
| 161 | self.dog:setName(getXMLString(xmlFile, key.."#animalName")) |
| 162 | return result |
| 163 | end |
| 164 | return result |
| 165 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 172 | function Doghouse:saveToXMLFile(xmlFile, key, usedModNames) |
| 173 | Doghouse:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 174 | |
| 175 | setXMLString(xmlFile, key.."#animalName", HTMLUtil.encodeToHTML(self.dog.name)) |
| 176 | end |
Test scopeDefinition
testScope(float x, float y, float z, float coeff)Arguments
| float | x | x position |
| float | y | y position |
| float | z | z position |
| float | coeff | coeff |
| boolean | inScope | in scope |
| 185 | function Doghouse:testScope(x,y,z, coeff) |
| 186 | local distance, clipDistance = getCompanionClosestDistance(self.dog.dogInstance, x, y, z) |
| 187 | local clipDist = math.min(clipDistance * coeff, self.forcedClipDistance) |
| 188 | |
| 189 | if distance < clipDist then |
| 190 | return true |
| 191 | else |
| 192 | return false |
| 193 | end |
| 194 | end |
Get update priorityDefinition
getUpdatePriority(float skipCount, float x, float y, float z, float coeff, table connection)Arguments
| float | skipCount | skip count |
| float | x | x position |
| float | y | y position |
| float | z | z position |
| float | coeff | coeff |
| table | connection | connection |
| float | priority | priority |
| 205 | function Doghouse:getUpdatePriority(skipCount, x, y, z, coeff, connection) |
| 206 | local distance, clipDistance = getCompanionClosestDistance(self.dog.dogInstance, x, y, z) |
| 207 | local clipDist = math.min(clipDistance * coeff, self.forcedClipDistance) |
| 208 | local result = (1.0 - distance / clipDist) * 0.8 + 0.5 * skipCount * 0.2 |
| 209 | |
| 210 | return result |
| 211 | end |
On ghost removeDefinition
onGhostRemove()Code
| 215 | function Doghouse:onGhostRemove() |
| 216 | setVisibility(self.nodeId, false) |
| 217 | removeFromPhysics(self.nodeId) |
| 218 | self.dog:setVisibility(false) |
| 219 | end |
On ghost addDefinition
onGhostAdd()Code
| 223 | function Doghouse:onGhostAdd() |
| 224 | setVisibility(self.nodeId, true) |
| 225 | addToPhysics(self.nodeId) |
| 226 | self.dog:setVisibility(true) |
| 227 | end |
Update network tickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| 232 | function Doghouse:updateTick(dt) |
| 233 | Doghouse:superClass().updateTick(self, dt) |
| 234 | |
| 235 | self.dog:updateTick(dt) |
| 236 | end |
drawDogName()Code
| 252 | function Doghouse:drawDogName() |
| 253 | -- @Todo: adust text width |
| 254 | setTextColor(0.843, 0.745, 0.705, 1.0) |
| 255 | setTextAlignment(RenderText.ALIGN_CENTER) |
| 256 | local x,y,z = getWorldTranslation(self.namePlateNode) |
| 257 | local rx,ry,rz = getWorldRotation(self.namePlateNode) |
| 258 | renderText3D(x,y,z, rx,ry,rz, 0.04, self.dog.name) |
| 259 | setTextAlignment(RenderText.ALIGN_LEFT) |
| 260 | end |
Callback when interaction trigger is activatedDefinition
playerInteractionTriggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 269 | function Doghouse:playerInteractionTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 270 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode and g_currentMission.player.farmId == self:getOwnerFarmId() then |
| 271 | if onEnter then |
| 272 | self.isActivatable = true |
| 273 | elseif onLeave then |
| 274 | self.isActivatable = false |
| 275 | end |
| 276 | end |
| 277 | self:raiseActive() |
| 278 | end |
Callback on bowl activatedDefinition
onActivateObject()Code
| 282 | function Doghouse:onActivateObject() |
| 283 | self:raiseActive() |
| 284 | end |
Checks if doghouse can be activated and bowl is emptyDefinition
getIsActivatable()Return Values
| bool | return | true if doghouse can be activated to fill bowl |
| 289 | function Doghouse:getIsActivatable() |
| 290 | return self.isActivatable |
| 291 | end |
Draw HUD iconDefinition
drawActivate()Code
| 295 | function Doghouse:drawActivate() |
| 296 | g_currentMission:showFillDogBowlContext() |
| 297 | end |
Teleport animal back to doghouseDefinition
onPlayerLeave()Code
| 301 | function Doghouse:onPlayerLeave() |
| 302 | self:raiseActive() |
| 303 | end |
Returns true if a doghouse is registeredDefinition
isDoghouseRegistered()Return Values
| 308 | function Doghouse:isDoghouseRegistered() |
| 309 | local dogHouse = g_currentMission:getDoghouse(self:getOwnerFarmId()) |
| 310 | return dogHouse ~= nil |
| 311 | end |
Registers the doghouse to the mission game.Definition
registerDoghouseToMission()Return Values
| bool | true | if registration went well |
| 316 | function Doghouse:registerDoghouseToMission() |
| 317 | if not self:isDoghouseRegistered() and self.dog ~= nil then |
| 318 | g_currentMission.doghouses[self.dog] = self |
| 319 | return true |
| 320 | end |
| 321 | return false |
| 322 | end |
Registers the doghouse to the mission game.Definition
unregisterDoghouseToMission()Return Values
| bool | true | if registration went well |
| 327 | function Doghouse:unregisterDoghouseToMission() |
| 328 | if self.dog ~= nil then |
| 329 | g_currentMission.doghouses[self.dog] = nil |
| 330 | return true |
| 331 | end |
| 332 | return false |
| 333 | end |
Class for placeable farmhouseParent
Placeable
Creating placeable farmhouseDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 25 | function FarmhousePlaceable:new(isServer, isClient, customMt) |
| 26 | local self = Placeable:new(isServer, isClient, customMt or FarmhousePlaceable_mt) |
| 27 | |
| 28 | registerObjectClassName(self, "FarmhousePlaceable") |
| 29 | |
| 30 | return self |
| 31 | end |
Deleting placeable farmhouseDefinition
delete()Code
| 35 | function FarmhousePlaceable:delete() |
| 36 | unregisterObjectClassName(self) |
| 37 | |
| 38 | if self.sleepingTrigger ~= nil then |
| 39 | removeTrigger(self.sleepingTrigger) |
| 40 | end |
| 41 | |
| 42 | g_currentMission:removeActivatableObject(self) |
| 43 | self.objectActivated = false |
| 44 | |
| 45 | FarmhousePlaceable:superClass().delete(self) |
| 46 | end |
Load farmhouseDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 59 | function FarmhousePlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 60 | if not FarmhousePlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 61 | return false |
| 62 | end |
| 63 | |
| 64 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 65 | |
| 66 | self.spawnNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.farmhouse#spawnNode")) |
| 67 | |
| 68 | self.sleepingTrigger = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.farmhouse.sleeping#triggerNode")) |
| 69 | if self.sleepingTrigger ~= nil then |
| 70 | addTrigger(self.sleepingTrigger, "sleepingTriggerCallback", self) |
| 71 | end |
| 72 | self.sleepingCamera = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.farmhouse.sleeping#cameraNode")) |
| 73 | |
| 74 | self.activateText = g_i18n:getText("ui_inGameSleep") |
| 75 | |
| 76 | self.isEnabled = true |
| 77 | self.objectActivated = false |
| 78 | |
| 79 | |
| 80 | delete(xmlFile) |
| 81 | |
| 82 | return true |
| 83 | end |
Get is activateableDefinition
getIsActivatable()Return Values
| boolean | isActivateable | is activateable |
| 134 | function FarmhousePlaceable:getIsActivatable() |
| 135 | return g_currentMission:getFarmId() == self:getOwnerFarmId() |
| 136 | end |
On activate objectDefinition
onActivateObject()Code
| 144 | function FarmhousePlaceable:onActivateObject() |
| 145 | g_sleepManager:showDialog() |
| 146 | end |
Class for placeable greenhouse
Creating placeable greenhouseDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 20 | function GreenhousePlaceable:new(isServer, isClient, customMt) |
| 21 | local self = Placeable:new(isServer, isClient, customMt or GreenhousePlaceable_mt) |
| 22 | |
| 23 | registerObjectClassName(self, "GreenhousePlaceable") |
| 24 | |
| 25 | self.waterTankCapacity = 1000 |
| 26 | self.waterTankFillLevel = 1000 |
| 27 | self.sentWaterTankFillLevel = self.waterTankFillLevel |
| 28 | self.waterTankUsagePerHour = 0 |
| 29 | self.waterTankFillLitersPerSecond = 200 |
| 30 | self.waterTrailers = {} |
| 31 | self.isFruitAlive = true |
| 32 | self.displayFruit = false |
| 33 | |
| 34 | self.manureFillLevel = 0 |
| 35 | self.manureUsagePerHour = 0 |
| 36 | self.manureCapacity = 200 |
| 37 | self.manurePlaneMinY = 0 |
| 38 | self.manurePlaneMaxY = 1 |
| 39 | self.sentManureFillLevel = self.manureFillLevel |
| 40 | |
| 41 | self.vehiclesInRange = {} |
| 42 | self.playerInRange = false |
| 43 | |
| 44 | self.greenhousePlaceableDirtyFlag = self:getNextDirtyFlag() |
| 45 | |
| 46 | self.waterTrailerActivatable = GreenhousePlaceableWaterTankActivatable:new(self) |
| 47 | |
| 48 | return self |
| 49 | end |
Deleting placeable greenhouseDefinition
delete()Code
| 53 | function GreenhousePlaceable:delete() |
| 54 | if self.shovelTarget ~= nil then |
| 55 | self.shovelTarget:delete() |
| 56 | end |
| 57 | |
| 58 | g_currentMission:removeActivatableObject(self.waterTrailerActivatable) |
| 59 | unregisterObjectClassName(self) |
| 60 | g_currentMission.environment:removeHourChangeListener(self) |
| 61 | |
| 62 | if self.waterTankTriggerNode ~= nil then |
| 63 | removeTrigger(self.waterTankTriggerNode) |
| 64 | end |
| 65 | |
| 66 | if g_client ~= nil then |
| 67 | for _,sample in pairs(self.samples) do |
| 68 | g_soundManager:deleteSample(sample) |
| 69 | end |
| 70 | end |
| 71 | |
| 72 | GreenhousePlaceable:superClass().delete(self) |
| 73 | end |
Deleting placeable greenhouse finalDefinition
deleteFinal()Code
| 77 | function GreenhousePlaceable:deleteFinal() |
| 78 | GreenhousePlaceable:superClass().deleteFinal(self) |
| 79 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 85 | function GreenhousePlaceable:readStream(streamId, connection) |
| 86 | GreenhousePlaceable:superClass().readStream(self, streamId, connection) |
| 87 | if connection:getIsServer() then |
| 88 | local waterTankFillLevel = streamReadUInt8(streamId)/255*self.waterTankCapacity |
| 89 | self:setWaterTankFillLevel(waterTankFillLevel) |
| 90 | local manureFillLevel = streamReadUInt8(streamId)/255*self.manureCapacity |
| 91 | self:setManureFillLevel(manureFillLevel) |
| 92 | end |
| 93 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 99 | function GreenhousePlaceable:writeStream(streamId, connection) |
| 100 | GreenhousePlaceable:superClass().writeStream(self, streamId, connection) |
| 101 | if not connection:getIsServer() then |
| 102 | streamWriteUInt8(streamId, math.floor(self.waterTankFillLevel/self.waterTankCapacity * 255)) |
| 103 | streamWriteUInt8(streamId, math.floor(self.manureFillLevel/self.manureCapacity * 255)) |
| 104 | end |
| 105 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| 112 | function GreenhousePlaceable:readUpdateStream(streamId, timestamp, connection) |
| 113 | GreenhousePlaceable:superClass().readUpdateStream(self, streamId, timestamp, connection) |
| 114 | if connection:getIsServer() then |
| 115 | local waterTankFillLevel = streamReadUInt8(streamId)/255*self.waterTankCapacity |
| 116 | self:setWaterTankFillLevel(waterTankFillLevel) |
| 117 | local manureFillLevel = streamReadUInt8(streamId)/255*self.manureCapacity |
| 118 | self:setManureFillLevel(manureFillLevel) |
| 119 | end |
| 120 | end |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| 127 | function GreenhousePlaceable:writeUpdateStream(streamId, connection, dirtyMask) |
| 128 | GreenhousePlaceable:superClass().writeUpdateStream(self, streamId, connection, dirtyMask) |
| 129 | if not connection:getIsServer() then |
| 130 | streamWriteUInt8(streamId, math.floor(self.waterTankFillLevel/self.waterTankCapacity * 255)) |
| 131 | streamWriteUInt8(streamId, math.floor(self.manureFillLevel/self.manureCapacity * 255)) |
| 132 | end |
| 133 | end |
Create nodeDefinition
createNode(string i3dFilename)Arguments
| string | i3dFilename | i3d file name |
| boolean | success | success |
| 139 | function GreenhousePlaceable:createNode(i3dFilename) |
| 140 | if not GreenhousePlaceable:superClass().createNode(self, i3dFilename) then |
| 141 | return false |
| 142 | end |
| 143 | |
| 144 | return true |
| 145 | end |
Load greenhouseDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 158 | function GreenhousePlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 159 | if not GreenhousePlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 160 | return false |
| 161 | end |
| 162 | |
| 163 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 164 | |
| 165 | local fruitAlive = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.fruit#alive")) |
| 166 | local fruitDead = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.fruit#dead")) |
| 167 | if fruitAlive ~= nil then |
| 168 | self.fruitAlive = fruitAlive |
| 169 | end |
| 170 | if fruitDead ~= nil then |
| 171 | self.fruitDead = fruitDead |
| 172 | end |
| 173 | |
| 174 | self.waterTankTriggerNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.waterTank#triggerNode")) |
| 175 | self.waterTankCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.waterTank#capacity"), self.waterTankCapacity) |
| 176 | self.waterTankUsagePerHour = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.waterTank#usagePerHour"), self.waterTankUsagePerHour) |
| 177 | self.waterTankFillLitersPerSecond = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.waterTank#fillLitersPerSecond"), self.waterTankFillLitersPerSecond) |
| 178 | |
| 179 | self.samples = {} |
| 180 | if g_client ~= nil then |
| 181 | self.samples.refuel = g_soundManager:loadSampleFromXML(xmlFile, "placeable.waterTank.sounds", "refuel", self.baseDirectory, self.nodeId, 0, AudioGroup.VEHICLE, nil, nil) |
| 182 | end |
| 183 | |
| 184 | self.manurePlane = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.manurePlane#node")) |
| 185 | self.shovelTargetNode = I3DUtil.indexToObject(self.nodeId, Utils.getNoNil(getXMLString(xmlFile, "placeable.manurePlane#shovelTargetIndex"), getXMLString(xmlFile, "placeable.manurePlane#collisionNode"))) |
| 186 | local minY, maxY = StringUtil.getVectorFromString(getXMLString(xmlFile, "placeable.manurePlane#minMaxY")) |
| 187 | if minY ~= nil and maxY ~= nil then |
| 188 | self.manurePlaneMinY = minY |
| 189 | self.manurePlaneMaxY = maxY |
| 190 | end |
| 191 | self.manureCapacity = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.manurePlane#capacity"), self.manureCapacity) |
| 192 | self.manureUsagePerHour = Utils.getNoNil(getXMLFloat(xmlFile, "placeable.manurePlane#usagePerHour"), self.manureUsagePerHour) |
| 193 | |
| 194 | delete(xmlFile) |
| 195 | |
| 196 | self:setWaterTankFillLevel(0) |
| 197 | self.sentWaterTankFillLevel = self.waterTankFillLevel |
| 198 | |
| 199 | self:setManureFillLevel(0) |
| 200 | self.sentManureFillLevel = self.manureFillLevel |
| 201 | |
| 202 | return true |
| 203 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 207 | function GreenhousePlaceable:finalizePlacement() |
| 208 | GreenhousePlaceable:superClass().finalizePlacement(self) |
| 209 | g_currentMission.environment:addHourChangeListener(self) |
| 210 | if self.waterTankTriggerNode ~= nil then |
| 211 | addTrigger(self.waterTankTriggerNode, "onWaterTankTrigger", self) |
| 212 | end |
| 213 | |
| 214 | if self.shovelTargetNode ~= nil then |
| 215 | if self.shovelTargetNode ~= nil then |
| 216 | self.shovelTarget = ShovelTarget:new(self) |
| 217 | if not self.shovelTarget:load(self.shovelTargetNode) then |
| 218 | self.shovelTarget:delete() |
| 219 | self.shovelTarget = nil |
| 220 | end |
| 221 | end |
| 222 | end |
| 223 | end |
Initialize poseDefinition
initPose(float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| float | x | x world position |
| float | y | y world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| 234 | function GreenhousePlaceable:initPose(x,y,z, rx,ry,rz, initRandom) |
| 235 | GreenhousePlaceable:superClass().initPose(self, x,y,z, rx,ry,rz, initRandom) |
| 236 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 244 | function GreenhousePlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 245 | |
| 246 | if not GreenhousePlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 247 | return false |
| 248 | end |
| 249 | |
| 250 | local waterTankFillLevel = getXMLFloat(xmlFile, key.."#waterTankFillLevel") |
| 251 | if waterTankFillLevel ~= nil then |
| 252 | self:setWaterTankFillLevel(waterTankFillLevel) |
| 253 | end |
| 254 | local manureFillLevel = getXMLFloat(xmlFile, key.."#manureFillLevel") |
| 255 | if manureFillLevel ~= nil then |
| 256 | self:setManureFillLevel(manureFillLevel) |
| 257 | end |
| 258 | |
| 259 | return true |
| 260 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 267 | function GreenhousePlaceable:saveToXMLFile(xmlFile, key, usedModNames) |
| 268 | GreenhousePlaceable:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 269 | |
| 270 | setXMLFloat(xmlFile, key.."#waterTankFillLevel", self.waterTankFillLevel) |
| 271 | setXMLFloat(xmlFile, key.."#manureFillLevel", self.manureFillLevel) |
| 272 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 277 | function GreenhousePlaceable:update(dt) |
| 278 | GreenhousePlaceable:superClass().update(self, dt) |
| 279 | if self:getShowInfo() then |
| 280 | g_currentMission:addExtraPrintText(g_i18n:getText("info_waterFillLevel").." "..math.floor(self.waterTankFillLevel).." ("..math.floor(100*self.waterTankFillLevel/self.waterTankCapacity).."%)") |
| 281 | g_currentMission:addExtraPrintText(g_i18n:getText("info_manureFillLevel").." "..math.floor(self.manureFillLevel).." ("..math.floor(100*self.manureFillLevel/self.manureCapacity).."%)") |
| 282 | end |
| 283 | end |
updateTickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| 288 | function GreenhousePlaceable:updateTick(dt) |
| 289 | if self.isServer then |
| 290 | |
| 291 | if self.waterTankFillLevel ~= self.sentWaterTankFillLevel then |
| 292 | self:raiseDirtyFlags(self.greenhousePlaceableDirtyFlag) |
| 293 | |
| 294 | self.sentWaterTankFillLevel = self.waterTankFillLevel |
| 295 | end |
| 296 | if self.manureFillLevel ~= self.sentManureFillLevel then |
| 297 | self:raiseDirtyFlags(self.greenhousePlaceableDirtyFlag) |
| 298 | |
| 299 | self.sentManureFillLevel = self.manureFillLevel |
| 300 | end |
| 301 | |
| 302 | if self.isWaterTankFilling then |
| 303 | local disableFilling = false |
| 304 | local waterFillLevel = self.waterTankFillTrailer:getFillLevel(FillType.WATER) |
| 305 | if waterFillLevel > 0 then |
| 306 | local oldFillLevel = self.waterTankFillLevel |
| 307 | |
| 308 | local delta = self.waterTankFillLitersPerSecond*dt*0.001 |
| 309 | delta = math.min(delta, waterFillLevel) |
| 310 | |
| 311 | self:setWaterTankFillLevel(self.waterTankFillLevel + delta) |
| 312 | |
| 313 | local delta = self.waterTankFillLevel - oldFillLevel |
| 314 | if delta <= 0 then |
| 315 | disableFilling = true |
| 316 | end |
| 317 | self.waterTankFillTrailer:setFillLevel(waterFillLevel - delta, FillType.WATER, true) |
| 318 | else |
| 319 | disableFilling = true |
| 320 | end |
| 321 | if disableFilling then |
| 322 | self:setIsWaterTankFilling(false) |
| 323 | end |
| 324 | end |
| 325 | |
| 326 | end |
| 327 | end |
Returns true if info should showDefinition
getShowInfo()Return Values
| boolean | showInfo | show info |
| 332 | function GreenhousePlaceable:getShowInfo() |
| 333 | if (g_currentMission.controlPlayer and self.playerInRange) then |
| 334 | return true |
| 335 | end |
| 336 | if not g_currentMission.controlPlayer then |
| 337 | for vehicle in pairs(self.vehiclesInRange) do |
| 338 | if vehicle:getIsActiveForInput() then |
| 339 | return true |
| 340 | end |
| 341 | end |
| 342 | end |
| 343 | return false |
| 344 | end |
Called if hour changedDefinition
hourChanged()Code
| 348 | function GreenhousePlaceable:hourChanged() |
| 349 | if self.isServer then |
| 350 | self:setWaterTankFillLevel(self.waterTankFillLevel - self.waterTankUsagePerHour) |
| 351 | self:setManureFillLevel(self.manureFillLevel - self.manureUsagePerHour) |
| 352 | |
| 353 | if self.isFruitAlive then |
| 354 | local income = self.incomePerHour |
| 355 | if self.manureFillLevel > 0 then |
| 356 | income = income * 2 |
| 357 | end |
| 358 | g_currentMission:addMoney(income, self:getOwnerFarmId(), "propertyIncome") |
| 359 | g_currentMission:addMoneyChange(income, self:getOwnerFarmId(), EconomyManager.MONEY_TYPE_PROPERTY_INCOME) |
| 360 | end |
| 361 | end |
| 362 | end |
Set water tank fill levelDefinition
setWaterTankFillLevel(float fillLevel)Arguments
| float | fillLevel | new fill level |
| 367 | function GreenhousePlaceable:setWaterTankFillLevel(fillLevel) |
| 368 | self.waterTankFillLevel = MathUtil.clamp(fillLevel, 0, self.waterTankCapacity) |
| 369 | |
| 370 | self.isFruitAlive = self.waterTankFillLevel > 0.0001 |
| 371 | |
| 372 | if self.waterTankFillLevel > 0 then |
| 373 | self.displayFruit = true |
| 374 | end |
| 375 | |
| 376 | if self.fruitAlive ~= nil then |
| 377 | setVisibility(self.fruitAlive, self.isFruitAlive and self.displayFruit) |
| 378 | end |
| 379 | if self.fruitDead ~= nil then |
| 380 | setVisibility(self.fruitDead, not self.isFruitAlive and self.displayFruit) |
| 381 | end |
| 382 | end |
Add fill level from tool to triggerDefinition
addFillLevelFromTool_OLD(table object, float fillDelta, integer fillType, integer toolType)Arguments
| table | object | object |
| float | fillDelta | delta to fill |
| integer | fillType | fill type index |
| integer | toolType | tool type index |
| float | fillDelta | real fill delta |
| 391 | function GreenhousePlaceable:addFillLevelFromTool_OLD(object, fillDelta, fillType, toolType) |
| 392 | fillDelta = math.min(fillDelta, self.manureCapacity-self.manureFillLevel) |
| 393 | |
| 394 | if fillDelta > 0 then |
| 395 | self:setManureFillLevel(self.manureFillLevel+fillDelta) |
| 396 | end |
| 397 | |
| 398 | return fillDelta |
| 399 | end |
Returns if shovel fill type is allowedDefinition
getAllowFillTypeFromTool()Return Values
| float | isAllowed | is allowed |
| 404 | function GreenhousePlaceable:getAllowFillTypeFromTool(fillType) |
| 405 | return fillType == FillType.MANURE |
| 406 | end |
Set manure fill levelDefinition
setManureFillLevel(float fillLevel)Arguments
| float | fillLevel | new fill level |
| 411 | function GreenhousePlaceable:setManureFillLevel(fillLevel) |
| 412 | self.manureFillLevel = MathUtil.clamp(fillLevel, 0, self.manureCapacity) |
| 413 | |
| 414 | if self.manurePlane ~= nil then |
| 415 | setVisibility(self.manurePlane, self.manureFillLevel > 0.0001) |
| 416 | local x,y,z = getTranslation(self.manurePlane) |
| 417 | y = self.manurePlaneMinY + self.manureFillLevel/self.manureCapacity * (self.manurePlaneMaxY - self.manurePlaneMinY) |
| 418 | |
| 419 | setTranslation(self.manurePlane, x,y,z) |
| 420 | end |
| 421 | end |
Set is water tank fillingDefinition
setIsWaterTankFilling(boolean isWaterTankFilling, table trailer, boolean noEventSend)Arguments
| boolean | isWaterTankFilling | is water tank filling |
| table | trailer | trailer |
| boolean | noEventSend | no event send |
| 428 | function GreenhousePlaceable:setIsWaterTankFilling(isWaterTankFilling, trailer, noEventSend) |
| 429 | GreenhouseSetIsWaterTankFillingEvent.sendEvent(self, isWaterTankFilling, trailer, noEventSend) |
| 430 | if self.isWaterTankFilling ~= isWaterTankFilling then |
| 431 | self.isWaterTankFilling = isWaterTankFilling |
| 432 | self.waterTankFillTrailer = trailer |
| 433 | end |
| 434 | if g_client ~= nil then |
| 435 | if isWaterTankFilling then |
| 436 | g_soundManager:playSample(self.samples.refuel) |
| 437 | else |
| 438 | g_soundManager:stopSample(self.samples.refuel) |
| 439 | end |
| 440 | end |
| 441 | end |
Add water trailer in rangeDefinition
addWaterTrailer(table trailer)Arguments
| table | trailer | trailer |
| 446 | function GreenhousePlaceable:addWaterTrailer(trailer) |
| 447 | if table.getn(self.waterTrailers) == 0 then |
| 448 | g_currentMission:addActivatableObject(self.waterTrailerActivatable) |
| 449 | end |
| 450 | table.insert(self.waterTrailers, trailer) |
| 451 | end |
Remove water trailer in rangeDefinition
removeWaterTrailer(table trailer)Arguments
| table | trailer | trailer |
| 456 | function GreenhousePlaceable:removeWaterTrailer(trailer) |
| 457 | for i=1, table.getn(self.waterTrailers) do |
| 458 | if self.waterTrailers[i] == trailer then |
| 459 | table.remove(self.waterTrailers, i) |
| 460 | break |
| 461 | end |
| 462 | end |
| 463 | if table.getn(self.waterTrailers) == 0 then |
| 464 | if self.isServer then |
| 465 | self:setIsWaterTankFilling(false) |
| 466 | end |
| 467 | g_currentMission:removeActivatableObject(self.waterTrailerActivatable) |
| 468 | end |
| 469 | end |
Water tank triggerDefinition
onWaterTankTrigger(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay, integer otherShapeId)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| integer | otherShapeId | id of other actor |
| 479 | function GreenhousePlaceable:onWaterTankTrigger(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
| 480 | if onEnter or onLeave then |
| 481 | |
| 482 | if g_currentMission.player ~= nil and otherActorId == g_currentMission.player.rootNode then |
| 483 | if onEnter then |
| 484 | self.playerInRange = true |
| 485 | else |
| 486 | self.playerInRange = false |
| 487 | end |
| 488 | else |
| 489 | local vehicle = g_currentMission.nodeToObject[otherActorId] |
| 490 | if vehicle ~= nil then |
| 491 | if onEnter then |
| 492 | self.vehiclesInRange[vehicle] = true |
| 493 | else |
| 494 | self.vehiclesInRange[vehicle] = nil |
| 495 | end |
| 496 | end |
| 497 | |
| 498 | |
| 499 | local trailer = g_currentMission.objectToTrailer[otherShapeId] |
| 500 | if trailer ~= nil and trailer:allowFillType(FillType.WATER, false) then |
| 501 | if onEnter then |
| 502 | self:addWaterTrailer(trailer) |
| 503 | else -- onLeave |
| 504 | self:removeWaterTrailer(trailer) |
| 505 | end |
| 506 | end |
| 507 | end |
| 508 | end |
| 509 | end |
Class for placeable heating plantsParent
Placeable
Creating placeable high pressure washerDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 18 | function HeatingPlantPlaceable:new(isServer, isClient, customMt) |
| 19 | local mt = customMt; |
| 20 | if mt == nil then |
| 21 | mt = HeatingPlantPlaceable_mt; |
| 22 | end; |
| 23 | |
| 24 | local self = Placeable:new(isServer, isClient, mt); |
| 25 | |
| 26 | registerObjectClassName(self, "HeatingPlantPlaceable"); |
| 27 | |
| 28 | self.heatingPlantDirtyFlag = self:getNextDirtyFlag(); |
| 29 | |
| 30 | return self; |
| 31 | end; |
Deleting heating plantDefinition
delete()Code
| 35 | function HeatingPlantPlaceable:delete() |
| 36 | if self.tipTrigger ~= nil then |
| 37 | self.tipTrigger:removeUpdateEventListener(self) |
| 38 | self.tipTrigger:delete() |
| 39 | end |
| 40 | |
| 41 | if self.exhaustEffect ~= nil then |
| 42 | g_i3DManager:releaseSharedI3DFile(self.exhaustEffect.filename, self.baseDirectory, true); |
| 43 | end |
| 44 | |
| 45 | g_animationManager:deleteAnimations(self.animationNodes) |
| 46 | |
| 47 | unregisterObjectClassName(self); |
| 48 | HeatingPlantPlaceable:superClass().delete(self); |
| 49 | end; |
Load heating plantDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 62 | function HeatingPlantPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 63 | if not HeatingPlantPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 64 | return false; |
| 65 | end; |
| 66 | |
| 67 | local xmlFile = loadXMLFile("TempXML", xmlFilename); |
| 68 | |
| 69 | self.tipTrigger = TipTrigger:new(self.isServer, self.isClient) |
| 70 | if self.tipTrigger:load(I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.heatingPlant#tipTrigger"))) then |
| 71 | self.tipTrigger:register(true) |
| 72 | self.tipTrigger:addUpdateEventListener(self) |
| 73 | else |
| 74 | self.tipTrigger:delete() |
| 75 | self.tipTrigger = nil |
| 76 | end |
| 77 | |
| 78 | if self.isClient then |
| 79 | self.animationNodes = g_animationManager:loadAnimations(xmlFile, "placeable.animationNodes", self.nodeId, self, nil) |
| 80 | |
| 81 | local filename = getXMLString(xmlFile, "placeable.exhaust#filename"); |
| 82 | local node = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.exhaust#index")); |
| 83 | if filename ~= nil then |
| 84 | local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false); |
| 85 | if i3dNode ~= 0 then |
| 86 | self.exhaustEffect = {} |
| 87 | self.exhaustEffect.node = getChildAt(i3dNode, 0); |
| 88 | self.exhaustEffect.filename = filename |
| 89 | link(Utils.getNoNil(node, self.nodeId), self.exhaustEffect.node); |
| 90 | setVisibility(self.exhaustEffect.node, false); |
| 91 | setShaderParameter(self.exhaustEffect.node, "param", 0, 0, 0, 0.4, false); |
| 92 | delete(i3dNode); |
| 93 | end |
| 94 | end |
| 95 | end |
| 96 | |
| 97 | self.workingTimeDuration = 120*1000 |
| 98 | self.workingTime = 0 |
| 99 | |
| 100 | delete(xmlFile); |
| 101 | |
| 102 | return true; |
| 103 | end; |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 109 | function HeatingPlantPlaceable:readStream(streamId, connection) |
| 110 | HeatingPlantPlaceable:superClass().readStream(self, streamId, connection); |
| 111 | if connection:getIsServer() then |
| 112 | local tipTriggerId = NetworkUtil.readNodeObjectId(streamId); |
| 113 | self.tipTrigger:readStream(streamId, connection); |
| 114 | g_client:finishRegisterObject(self.tipTrigger, tipTriggerId); |
| 115 | end |
| 116 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 122 | function HeatingPlantPlaceable:writeStream(streamId, connection) |
| 123 | HeatingPlantPlaceable:superClass().writeStream(self, streamId, connection); |
| 124 | if not connection:getIsServer() then |
| 125 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.tipTrigger)); |
| 126 | self.tipTrigger:writeStream(streamId, connection); |
| 127 | g_server:registerObjectInStream(connection, self.tipTrigger); |
| 128 | end |
| 129 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| 136 | function HeatingPlantPlaceable:readUpdateStream(streamId, timestamp, connection) |
| 137 | HeatingPlantPlaceable:superClass().readUpdateStream(self, streamId, timestamp, connection); |
| 138 | if connection:getIsServer() then |
| 139 | local workingTimeBiggerZero = streamReadBool(streamId) |
| 140 | if workingTimeBiggerZero then |
| 141 | self.workingTime = self.workingTimeDuration |
| 142 | if self.exhaustEffect ~= nil then |
| 143 | setVisibility(self.exhaustEffect.node, true); |
| 144 | end |
| 145 | end |
| 146 | end; |
| 147 | end; |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| 154 | function HeatingPlantPlaceable:writeUpdateStream(streamId, connection, dirtyMask) |
| 155 | HeatingPlantPlaceable:superClass().writeUpdateStream(self, streamId, connection, dirtyMask); |
| 156 | if not connection:getIsServer() then |
| 157 | streamWriteBool(streamId, self.workingTime > 0) |
| 158 | end; |
| 159 | end; |
Collect pick objectsDefinition
collectPickObjects(integer node)Arguments
| integer | node | node id |
| 164 | function HeatingPlantPlaceable:collectPickObjects(node) |
| 165 | if self.tipTrigger == nil or self.tipTrigger.shovelTarget == nil or self.tipTrigger.shovelTarget.nodeId ~= node then |
| 166 | HeatingPlantPlaceable:superClass().collectPickObjects(self, node) |
| 167 | end |
| 168 | end; |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 173 | function HeatingPlantPlaceable:update(dt) |
| 174 | HeatingPlantPlaceable:superClass().update(self, dt); |
| 175 | |
| 176 | if self.isClient then |
| 177 | if self.workingTime > 0 then |
| 178 | self.workingTime = self.workingTime - dt |
| 179 | if self.workingTime <= 0 then |
| 180 | if self.exhaustEffect ~= nil then |
| 181 | setVisibility(self.exhaustEffect.node, false); |
| 182 | end |
| 183 | g_animationManager:stopAnimations(self.animationNodes) |
| 184 | end |
| 185 | end |
| 186 | end |
| 187 | end |
Update tickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| 192 | function HeatingPlantPlaceable:updateTick(dt) |
| 193 | HeatingPlantPlaceable:superClass().updateTick(self, dt); |
| 194 | end |
Called if someone filled in somethingDefinition
onUpdateEvent(table trigger, float fillDelta, integer fillType, table trailer, table tipTriggerTarget)Arguments
| table | trigger | trigger |
| float | fillDelta | delta filled in |
| integer | fillType | fill type filled in |
| table | trailer | object of trailer |
| table | tipTriggerTarget | tip trigger target |
| 203 | function HeatingPlantPlaceable:onUpdateEvent(trigger, fillDelta, fillType, trailer, tipTriggerTarget) |
| 204 | if self.exhaustEffect ~= nil then |
| 205 | setVisibility(self.exhaustEffect.node, true); |
| 206 | end |
| 207 | if self.isServer then |
| 208 | self:raiseDirtyFlags(self.heatingPlantDirtyFlag); |
| 209 | end |
| 210 | self.workingTime = self.workingTimeDuration |
| 211 | |
| 212 | g_animationManager:startAnimations(self.animationNodes) |
| 213 | end |
Creating placeable siloDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 26 | function SellingStationPlaceable:new(isServer, isClient, customMt) |
| 27 | local self = Placeable:new(isServer, isClient, customMt or SellStationPlaceable_mt) |
| 28 | |
| 29 | registerObjectClassName(self, "SellingStationPlaceable") |
| 30 | |
| 31 | return self |
| 32 | end |
Deleting placeable siloDefinition
delete()Code
| 36 | function SellingStationPlaceable:delete() |
| 37 | if self.sellingStation ~= nil then |
| 38 | g_currentMission:removeUnloadingStation(self.sellingStation) |
| 39 | self.sellingStation:delete() |
| 40 | end |
| 41 | |
| 42 | unregisterObjectClassName(self) |
| 43 | SellingStationPlaceable:superClass().delete(self) |
| 44 | end |
Load siloDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 57 | function SellingStationPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 58 | if not SellingStationPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 59 | return false |
| 60 | end |
| 61 | |
| 62 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 63 | |
| 64 | self.sellingStation = SellingStation:new(self.isServer, self.isClient) |
| 65 | self.sellingStation:load(self.nodeId, xmlFile, "placeable.sellingStation") |
| 66 | self.sellingStation.owningPlaceable = self |
| 67 | |
| 68 | delete(xmlFile) |
| 69 | |
| 70 | return true |
| 71 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 75 | function SellingStationPlaceable:finalizePlacement() |
| 76 | SellingStationPlaceable:superClass().finalizePlacement(self) |
| 77 | self.sellingStation:register(true) |
| 78 | g_currentMission:addUnloadingStation(self.sellingStation) |
| 79 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 85 | function SellingStationPlaceable:readStream(streamId, connection) |
| 86 | SellingStationPlaceable:superClass().readStream(self, streamId, connection) |
| 87 | if connection:getIsServer() then |
| 88 | local sellingStationId = NetworkUtil.readNodeObjectId(streamId) |
| 89 | self.sellingStation:readStream(streamId, connection) |
| 90 | g_client:finishRegisterObject(self.sellingStation, sellingStationId) |
| 91 | end |
| 92 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 98 | function SellingStationPlaceable:writeStream(streamId, connection) |
| 99 | SellingStationPlaceable:superClass().writeStream(self, streamId, connection) |
| 100 | if not connection:getIsServer() then |
| 101 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.sellingStation)) |
| 102 | self.sellingStation:writeStream(streamId, connection) |
| 103 | g_server:registerObjectInStream(connection, self.sellingStation) |
| 104 | end |
| 105 | end |
Collect pick objectsDefinition
collectPickObjects(integer node)Arguments
| integer | node | node id |
| 110 | function SellingStationPlaceable:collectPickObjects(node) |
| 111 | local foundNode = false |
| 112 | for _, unloadTrigger in ipairs(self.sellingStation.unloadTriggers) do |
| 113 | if node == unloadTrigger.exactFillRootNode then |
| 114 | foundNode = true |
| 115 | break |
| 116 | end |
| 117 | end |
| 118 | |
| 119 | if not foundNode then |
| 120 | SellingStationPlaceable:superClass().collectPickObjects(self, node) |
| 121 | end |
| 122 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 130 | function SellingStationPlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 131 | if not SellingStationPlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 132 | return false |
| 133 | end |
| 134 | |
| 135 | if not self.sellingStation:loadFromXMLFile(xmlFile, key..".sellingStation") then |
| 136 | return false |
| 137 | end |
| 138 | |
| 139 | return true |
| 140 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 147 | function SellingStationPlaceable:saveToXMLFile(xmlFile, key, usedModNames) |
| 148 | SellingStationPlaceable:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 149 | |
| 150 | self.sellingStation:saveToXMLFile(xmlFile, key..".sellingStation", usedModNames) |
| 151 | end |
Class for placeable siloParent
Placeable
Creating placeable siloDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 18 | function SiloPlaceable:new(isServer, isClient, customMt) |
| 19 | local self = Placeable:new(isServer, isClient, customMt or SiloPlaceable_mt) |
| 20 | |
| 21 | registerObjectClassName(self, "SiloPlaceable") |
| 22 | |
| 23 | return self |
| 24 | end |
Deleting placeable siloDefinition
delete()Code
| 28 | function SiloPlaceable:delete() |
| 29 | if self.unloadingStation ~= nil then |
| 30 | self.unloadingStation:delete() |
| 31 | end |
| 32 | |
| 33 | if self.loadingStation ~= nil then |
| 34 | self.loadingStation:delete() |
| 35 | end |
| 36 | |
| 37 | for _, storage in ipairs(self.storages) do |
| 38 | g_currentMission:removeStorage(storage) |
| 39 | storage:delete() |
| 40 | end |
| 41 | |
| 42 | unregisterObjectClassName(self) |
| 43 | SiloPlaceable:superClass().delete(self) |
| 44 | end |
Load siloDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 57 | function SiloPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 58 | if not SiloPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 59 | return false |
| 60 | end |
| 61 | |
| 62 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 63 | |
| 64 | self.storagePerFarm = Utils.getNoNil(getXMLBool(xmlFile, "placeable.storages#perFarm"), false) |
| 65 | self.foreignSilo = Utils.getNoNil(getXMLBool(xmlFile, "placeable.storages#foreignSilo"), self.storagePerFarm) -- Shows as foreign silo in the menu |
| 66 | |
| 67 | self.unloadingStation = UnloadingStation:new(self.isServer, self.isClient) |
| 68 | self.unloadingStation:load(self.nodeId, xmlFile, "placeable.unloadingStation") |
| 69 | self.unloadingStation.owningPlaceable = self |
| 70 | self.unloadingStation.hasStoragePerFarm = self.storagePerFarm |
| 71 | |
| 72 | self.loadingStation = LoadingStation:new(self.isServer, self.isClient) |
| 73 | self.loadingStation:load(self.nodeId, xmlFile, "placeable.loadingStation") |
| 74 | self.loadingStation.owningPlaceable = self |
| 75 | self.loadingStation.hasStoragePerFarm = self.storagePerFarm |
| 76 | |
| 77 | local numStorageSets = self.storagePerFarm and FarmManager.MAX_NUM_FARMS or 1 |
| 78 | if not g_currentMission.missionDynamicInfo.isMultiplayer then |
| 79 | numStorageSets = 1 |
| 80 | end |
| 81 | |
| 82 | self.storages = {} |
| 83 | local i = 0 |
| 84 | while true do |
| 85 | local storageKey = string.format("placeable.storages.storage(%d)", i) |
| 86 | if not hasXMLProperty(xmlFile, storageKey) then |
| 87 | break |
| 88 | end |
| 89 | |
| 90 | local storageNode = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, storageKey.."#node")) |
| 91 | if storageNode ~= nil then |
| 92 | for i = 1, numStorageSets do |
| 93 | local storage = Storage:new(self.isServer, self.isClient) |
| 94 | if storage:load(storageNode, xmlFile, storageKey) then |
| 95 | storage.ownerFarmId = i |
| 96 | storage.foreignSilo = self.foreignSilo -- Pass along for usage by prices menu |
| 97 | table.insert(self.storages, storage) |
| 98 | end |
| 99 | end |
| 100 | else |
| 101 | g_logManager:xmlWarning(xmlFilename, "Missing 'node' for storage '%s'!", storageKey) |
| 102 | end |
| 103 | |
| 104 | i = i + 1 |
| 105 | end |
| 106 | |
| 107 | delete(xmlFile) |
| 108 | |
| 109 | return true |
| 110 | end |
Called if placeable is placedDefinition
finalizePlacement()Code
| 114 | function SiloPlaceable:finalizePlacement() |
| 115 | SiloPlaceable:superClass().finalizePlacement(self) |
| 116 | |
| 117 | self.unloadingStation:register(true) |
| 118 | g_currentMission:addUnloadingStation(self.unloadingStation) |
| 119 | |
| 120 | self.loadingStation:register(true) |
| 121 | g_currentMission:addLoadingStation(self.loadingStation) |
| 122 | |
| 123 | for _, storage in ipairs(self.storages) do |
| 124 | if not self.storagePerFarm then |
| 125 | storage:setOwnerFarmId(self:getOwnerFarmId(), true) |
| 126 | end |
| 127 | |
| 128 | g_currentMission:addStorage(storage) |
| 129 | |
| 130 | storage:register(true) |
| 131 | |
| 132 | g_currentMission:addStorageToUnloadingStations(storage, {self.unloadingStation}, false) |
| 133 | g_currentMission:addStorageToLoadingStations(storage, {self.loadingStation}, false) |
| 134 | end |
| 135 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 161 | function SiloPlaceable:readStream(streamId, connection) |
| 162 | SiloPlaceable:superClass().readStream(self, streamId, connection) |
| 163 | if connection:getIsServer() then |
| 164 | local unloadingStationId = NetworkUtil.readNodeObjectId(streamId) |
| 165 | self.unloadingStation:readStream(streamId, connection) |
| 166 | g_client:finishRegisterObject(self.unloadingStation, unloadingStationId) |
| 167 | |
| 168 | local loadingStationId = NetworkUtil.readNodeObjectId(streamId) |
| 169 | self.loadingStation:readStream(streamId, connection) |
| 170 | g_client:finishRegisterObject(self.loadingStation, loadingStationId) |
| 171 | |
| 172 | for _, storage in ipairs(self.storages) do |
| 173 | local storageId = NetworkUtil.readNodeObjectId(streamId) |
| 174 | storage:readStream(streamId, connection) |
| 175 | g_client:finishRegisterObject(storage, storageId) |
| 176 | end |
| 177 | end |
| 178 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 184 | function SiloPlaceable:writeStream(streamId, connection) |
| 185 | SiloPlaceable:superClass().writeStream(self, streamId, connection) |
| 186 | if not connection:getIsServer() then |
| 187 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.unloadingStation)) |
| 188 | self.unloadingStation:writeStream(streamId, connection) |
| 189 | g_server:registerObjectInStream(connection, self.unloadingStation) |
| 190 | |
| 191 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(self.loadingStation)) |
| 192 | self.loadingStation:writeStream(streamId, connection) |
| 193 | g_server:registerObjectInStream(connection, self.loadingStation) |
| 194 | |
| 195 | for _, storage in ipairs(self.storages) do |
| 196 | NetworkUtil.writeNodeObjectId(streamId, NetworkUtil.getObjectId(storage)) |
| 197 | storage:writeStream(streamId, connection) |
| 198 | g_server:registerObjectInStream(connection, storage) |
| 199 | end |
| 200 | end |
| 201 | end |
Collect pick objectsDefinition
collectPickObjects(integer node)Arguments
| integer | node | node id |
| 218 | function SiloPlaceable:collectPickObjects(node) |
| 219 | local foundNode = false |
| 220 | for _, unloadTrigger in ipairs(self.unloadingStation.unloadTriggers) do |
| 221 | if node == unloadTrigger.exactFillRootNode then |
| 222 | foundNode = true |
| 223 | break |
| 224 | end |
| 225 | end |
| 226 | |
| 227 | if not foundNode then |
| 228 | for _, loadTrigger in ipairs(self.loadingStation.loadTriggers) do |
| 229 | if node == loadTrigger.triggerNode then |
| 230 | foundNode = true |
| 231 | break |
| 232 | end |
| 233 | end |
| 234 | end |
| 235 | |
| 236 | if not foundNode then |
| 237 | SiloPlaceable:superClass().collectPickObjects(self, node) |
| 238 | end |
| 239 | end |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 247 | function SiloPlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 248 | if not SiloPlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 249 | return false |
| 250 | end |
| 251 | |
| 252 | if not self.unloadingStation:loadFromXMLFile(xmlFile, key..".unloadingStation") then |
| 253 | return false |
| 254 | end |
| 255 | |
| 256 | local i = 0 |
| 257 | while true do |
| 258 | local storageKey = string.format("%s.storage(%d)", key, i) |
| 259 | if not hasXMLProperty(xmlFile, storageKey) then |
| 260 | break |
| 261 | end |
| 262 | |
| 263 | local index = getXMLInt(xmlFile, storageKey.."#index") |
| 264 | |
| 265 | if index ~= nil then |
| 266 | if self.storages[index] ~= nil then |
| 267 | if not self.storages[index]:loadFromXMLFile(xmlFile, storageKey) then |
| 268 | return false |
| 269 | end |
| 270 | else |
| 271 | g_logManager:warning("Could not load storage. Given 'index' '%d' is not defined!", index) |
| 272 | end |
| 273 | end |
| 274 | |
| 275 | i = i + 1 |
| 276 | end |
| 277 | |
| 278 | return true |
| 279 | end |
Get save attributes and nodesDefinition
saveToXMLFile(string nodeIdent)Arguments
| string | nodeIdent | node ident |
| string | attributes | attributes |
| string | nodes | nodes |
| 286 | function SiloPlaceable:saveToXMLFile(xmlFile, key, usedModNames) |
| 287 | SiloPlaceable:superClass().saveToXMLFile(self, xmlFile, key, usedModNames) |
| 288 | |
| 289 | self.unloadingStation:saveToXMLFile(xmlFile, key..".unloadingStation", usedModNames) |
| 290 | |
| 291 | for k, storage in ipairs(self.storages) do |
| 292 | local storageKey = string.format("%s.storage(%d)", key, k-1) |
| 293 | setXMLInt(xmlFile, storageKey .. "#index", k) |
| 294 | storage:saveToXMLFile(xmlFile, storageKey, usedModNames) |
| 295 | end |
| 296 | end |
Class for placeable solar collectorsParent
Placeable
Creating placeable solar collectorDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 18 | function SolarCollectorPlaceable:new(isServer, isClient, customMt) |
| 19 | local mt = customMt; |
| 20 | if mt == nil then |
| 21 | mt = SolarCollectorPlaceable_mt; |
| 22 | end; |
| 23 | |
| 24 | local self = Placeable:new(isServer, isClient, mt); |
| 25 | |
| 26 | registerObjectClassName(self, "SolarCollectorPlaceable"); |
| 27 | |
| 28 | self.headNode = 0; |
| 29 | self.incomePerHour = 0; |
| 30 | self.headRotationRandom = 0; |
| 31 | return self; |
| 32 | end; |
Deleting solar collectorDefinition
delete()Code
| 36 | function SolarCollectorPlaceable:delete() |
| 37 | unregisterObjectClassName(self); |
| 38 | g_currentMission.environment:removeHourChangeListener(self); |
| 39 | SolarCollectorPlaceable:superClass().delete(self); |
| 40 | end; |
Deleting placeable solar collector finalDefinition
deleteFinal()Code
| 44 | function SolarCollectorPlaceable:deleteFinal() |
| 45 | SolarCollectorPlaceable:superClass().deleteFinal(self); |
| 46 | end; |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 52 | function SolarCollectorPlaceable:readStream(streamId, connection) |
| 53 | if connection:getIsServer() then |
| 54 | self.headRotationRandom=NetworkUtil.readCompressedAngle(streamId); |
| 55 | end; |
| 56 | SolarCollectorPlaceable:superClass().readStream(self, streamId, connection); |
| 57 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 63 | function SolarCollectorPlaceable:writeStream(streamId, connection) |
| 64 | if not connection:getIsServer() then |
| 65 | NetworkUtil.writeCompressedAngle(streamId, self.headRotationRandom); |
| 66 | end; |
| 67 | SolarCollectorPlaceable:superClass().writeStream(self, streamId, connection); |
| 68 | end; |
Create nodeDefinition
createNode(string i3dFilename)Arguments
| string | i3dFilename | i3d file name |
| boolean | success | success |
| 82 | function SolarCollectorPlaceable:createNode(i3dFilename) |
| 83 | if not SolarCollectorPlaceable:superClass().createNode(self, i3dFilename) then |
| 84 | return false; |
| 85 | end; |
| 86 | |
| 87 | if getNumOfChildren(self.nodeId) < 1 then |
| 88 | delete(self.nodeId); |
| 89 | self.nodeId = 0; |
| 90 | return false; |
| 91 | end; |
| 92 | self.headNode = getChildAt(self.nodeId, 0); |
| 93 | |
| 94 | return true; |
| 95 | end; |
Load solar collectorDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 108 | function SolarCollectorPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 109 | if not SolarCollectorPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 110 | return false; |
| 111 | end; |
| 112 | |
| 113 | return true; |
| 114 | end; |
Called if placeable is placedDefinition
finalizePlacement()Code
| 118 | function SolarCollectorPlaceable:finalizePlacement() |
| 119 | SolarCollectorPlaceable:superClass().finalizePlacement(self); |
| 120 | g_currentMission.environment:addHourChangeListener(self); |
| 121 | end |
Initialize poseDefinition
initPose(float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| float | x | x world position |
| float | y | y world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| 132 | function SolarCollectorPlaceable:initPose(x,y,z, rx,ry,rz, initRandom) |
| 133 | SolarCollectorPlaceable:superClass().initPose(self, x,y,z, rx,ry,rz, initRandom); |
| 134 | |
| 135 | local rotVariation = 0.1; |
| 136 | self.headRotationRandom = math.rad(75-90) + math.random() * 2*rotVariation - rotVariation; -- 75-90 = rotate to light (75 y rot) x axis |
| 137 | |
| 138 | self:updateHeadRotation(); |
| 139 | end; |
Updates the rotation of the headDefinition
updateHeadRotation()Code
| 143 | function SolarCollectorPlaceable:updateHeadRotation() |
| 144 | |
| 145 | local headRotation = math.rad(75-90); |
| 146 | if g_currentMission.environment.sunLightId ~= nil then |
| 147 | local dx,_,dz = localDirectionToWorld(g_currentMission.environment.sunLightId, 0,0,1); |
| 148 | headRotation = math.atan2(dx, dz); |
| 149 | end |
| 150 | headRotation = headRotation + self.headRotationRandom; |
| 151 | |
| 152 | local dx,_,dz = worldDirectionToLocal(self.nodeId, math.sin(headRotation),0,math.cos(headRotation)); |
| 153 | setDirection(self.headNode, dx,0,dz, 0,1,0); |
| 154 | end; |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 162 | function SolarCollectorPlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 163 | local headRotationRandom = getXMLFloat(xmlFile, key.."#headRotationRandom"); |
| 164 | if headRotationRandom == nil then |
| 165 | return false; |
| 166 | end; |
| 167 | |
| 168 | self.headRotationRandom = headRotationRandom; |
| 169 | |
| 170 | if not SolarCollectorPlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 171 | return false; |
| 172 | end; |
| 173 | |
| 174 | return true; |
| 175 | end; |
Called if hour changedDefinition
hourChanged()Code
| 188 | function SolarCollectorPlaceable:hourChanged() |
| 189 | if self.isServer then |
| 190 | g_currentMission:addMoney(self.incomePerHour, self:getOwnerFarmId(), "propertyIncome"); |
| 191 | g_currentMission:addMoneyChange(self.incomePerHour, self:getOwnerFarmId(), EconomyManager.MONEY_TYPE_PROPERTY_INCOME); |
| 192 | end |
| 193 | self:updateHeadRotation(); |
| 194 | end |
Class for placeable wind turbinesParent
Placeable
Creating placeable wind turbineDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 18 | function WindTurbinePlaceable:new(isServer, isClient, customMt) |
| 19 | local mt = customMt; |
| 20 | if mt == nil then |
| 21 | mt = WindTurbinePlaceable_mt; |
| 22 | end; |
| 23 | |
| 24 | local self = Placeable:new(isServer, isClient, mt); |
| 25 | |
| 26 | registerObjectClassName(self, "WindTurbinePlaceable"); |
| 27 | |
| 28 | self.rotationNode = 0; |
| 29 | self.headNode = 0; |
| 30 | self.incomePerHour = 0; |
| 31 | return self; |
| 32 | end; |
Deleting placeable wind turbineDefinition
delete()Code
| 36 | function WindTurbinePlaceable:delete() |
| 37 | unregisterObjectClassName(self); |
| 38 | g_currentMission.environment:removeHourChangeListener(self); |
| 39 | WindTurbinePlaceable:superClass().delete(self); |
| 40 | end; |
Deleting placeable wind turbineDefinition
deleteFinal()Code
| 44 | function WindTurbinePlaceable:deleteFinal() |
| 45 | WindTurbinePlaceable:superClass().deleteFinal(self); |
| 46 | end; |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 52 | function WindTurbinePlaceable:readStream(streamId, connection) |
| 53 | if connection:getIsServer() then |
| 54 | self.headRotation=NetworkUtil.readCompressedAngle(streamId); |
| 55 | end; |
| 56 | WindTurbinePlaceable:superClass().readStream(self, streamId, connection); |
| 57 | end; |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 63 | function WindTurbinePlaceable:writeStream(streamId, connection) |
| 64 | if not connection:getIsServer() then |
| 65 | NetworkUtil.writeCompressedAngle(streamId, self.headRotation); |
| 66 | end; |
| 67 | WindTurbinePlaceable:superClass().writeStream(self, streamId, connection); |
| 68 | end; |
Create nodeDefinition
createNode(string i3dFilename)Arguments
| string | i3dFilename | i3d file name |
| boolean | success | success |
| 82 | function WindTurbinePlaceable:createNode(i3dFilename) |
| 83 | if not WindTurbinePlaceable:superClass().createNode(self, i3dFilename) then |
| 84 | return false; |
| 85 | end; |
| 86 | |
| 87 | if getNumOfChildren(self.nodeId) < 1 then |
| 88 | delete(self.nodeId); |
| 89 | self.nodeId = 0; |
| 90 | return false; |
| 91 | end; |
| 92 | self.headNode = getChildAt(self.nodeId, 0); |
| 93 | if getNumOfChildren(self.headNode) < 1 then |
| 94 | delete(self.nodeId); |
| 95 | self.nodeId = 0; |
| 96 | return false; |
| 97 | end; |
| 98 | self.rotationNode = getChildAt(self.headNode, 0); |
| 99 | |
| 100 | return true; |
| 101 | end; |
Load wind turbineDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 114 | function WindTurbinePlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 115 | if not WindTurbinePlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 116 | return false; |
| 117 | end; |
| 118 | |
| 119 | return true; |
| 120 | end; |
Called if placeable is placedDefinition
finalizePlacement()Code
| 124 | function WindTurbinePlaceable:finalizePlacement() |
| 125 | WindTurbinePlaceable:superClass().finalizePlacement(self); |
| 126 | g_currentMission.environment:addHourChangeListener(self); |
| 127 | end |
Initialize poseDefinition
initPose(float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| float | x | x world position |
| float | y | y world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| 138 | function WindTurbinePlaceable:initPose(x,y,z, rx,ry,rz, initRandom) |
| 139 | WindTurbinePlaceable:superClass().initPose(self, x,y,z, rx,ry,rz, initRandom); |
| 140 | |
| 141 | if initRandom == nil or initRandom == true then |
| 142 | local rotVariation = 0.2; |
| 143 | self.headRotation = 0.7 + math.random() * 2*rotVariation - rotVariation; |
| 144 | end; |
| 145 | rotate(self.rotationNode, 0,0,math.random()*math.pi*2); |
| 146 | self:updateHeadRotation(); |
| 147 | end; |
Updating head rotationDefinition
updateHeadRotation()Code
| 151 | function WindTurbinePlaceable:updateHeadRotation() |
| 152 | local dx,_,dz = worldDirectionToLocal(self.nodeId, math.sin(self.headRotation),0,math.cos(self.headRotation)); |
| 153 | setDirection(self.headNode, dx,0,dz, 0,1,0); |
| 154 | end; |
Loading from attributes and nodesDefinition
loadFromXMLFile(integer xmlFile, string key, boolean resetVehicles)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | resetVehicles | reset vehicles |
| boolean | success | success |
| 162 | function WindTurbinePlaceable:loadFromXMLFile(xmlFile, key, resetVehicles) |
| 163 | local headRotation = getXMLFloat(xmlFile, key.."#headRotation"); |
| 164 | if headRotation == nil then |
| 165 | return false; |
| 166 | end; |
| 167 | |
| 168 | self.headRotation = headRotation; |
| 169 | |
| 170 | if not WindTurbinePlaceable:superClass().loadFromXMLFile(self, xmlFile, key, resetVehicles) then |
| 171 | return false; |
| 172 | end; |
| 173 | |
| 174 | return true; |
| 175 | end; |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 186 | function WindTurbinePlaceable:update(dt) |
| 187 | if self.rotationNode ~= 0 then |
| 188 | rotate(self.rotationNode, 0,0,-0.0025*dt); |
| 189 | end; |
| 190 | end; |
Called if hour changedDefinition
hourChanged()Code
| 194 | function WindTurbinePlaceable:hourChanged() |
| 195 | if self.isServer then |
| 196 | g_currentMission:addMoney(self.incomePerHour, self:getOwnerFarmId(), "propertyIncome"); |
| 197 | g_currentMission:addMoneyChange(self.incomePerHour, self:getOwnerFarmId(), EconomyManager.MONEY_TYPE_PROPERTY_INCOME); |
| 198 | end |
| 199 | end |
Creating woodsell stationDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 26 | function WoodSellStationPlaceable:new(isServer, isClient, customMt) |
| 27 | local self = Placeable:new(isServer, isClient, customMt or WoodSellStationPlaceable_mt) |
| 28 | |
| 29 | registerObjectClassName(self, "WoodSellStationPlaceable") |
| 30 | |
| 31 | self.woodInTrigger = {} |
| 32 | |
| 33 | return self |
| 34 | end |
Deleting triggerDefinition
delete()Code
| 38 | function WoodSellStationPlaceable:delete() |
| 39 | if self.mapHotspot ~= nil then |
| 40 | g_currentMission:removeMapHotspot(self.mapHotspot) |
| 41 | self.mapHotspot:delete() |
| 42 | end |
| 43 | |
| 44 | if self.woodSellTrigger ~= nil then |
| 45 | removeTrigger(self.woodSellTrigger) |
| 46 | self.woodSellTrigger = nil |
| 47 | end |
| 48 | |
| 49 | if self.sellTrigger ~= nil then |
| 50 | g_currentMission:removeActivatableObject(self) |
| 51 | removeTrigger(self.sellTrigger) |
| 52 | self.sellTrigger = nil |
| 53 | end |
| 54 | |
| 55 | unregisterObjectClassName(self) |
| 56 | WoodSellStationPlaceable:superClass().delete(self) |
| 57 | end |
Load woodsell stationDefinition
load(string xmlFilename, float x, float y, float z, float rx, float ry, float rz, boolean initRandom)Arguments
| string | xmlFilename | xml file name |
| float | x | x world position |
| float | y | z world position |
| float | z | z world position |
| float | rx | rx world rotation |
| float | ry | ry world rotation |
| float | rz | rz world rotation |
| boolean | initRandom | initialize random |
| boolean | success | success |
| 70 | function WoodSellStationPlaceable:load(xmlFilename, x,y,z, rx,ry,rz, initRandom) |
| 71 | if not WoodSellStationPlaceable:superClass().load(self, xmlFilename, x,y,z, rx,ry,rz, initRandom) then |
| 72 | return false |
| 73 | end |
| 74 | |
| 75 | local xmlFile = loadXMLFile("TempXML", xmlFilename) |
| 76 | |
| 77 | self.appearsOnPDA = Utils.getNoNil(getXMLBool(xmlFile, "placeable.woodSellStation#appearsOnPDA"), false) |
| 78 | local rawName = Utils.getNoNil(getXMLString(xmlFile, "placeable.woodSellStation#stationName"), "WoodSellStation") |
| 79 | self.stationName = g_i18n:convertText(rawName) -- returns input if it cannot be resolved |
| 80 | |
| 81 | local woodSellTrigger = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.woodSellStation#triggerNode")) |
| 82 | if woodSellTrigger == nil then |
| 83 | g_logManager:xmlWarning(xmlFilename, "Missing wood trigger node in 'placeable.woodSellStation#triggerNode'!") |
| 84 | delete(xmlFile) |
| 85 | return false |
| 86 | end |
| 87 | |
| 88 | local colMask = getCollisionMask(woodSellTrigger) |
| 89 | if bitAND(SplitTypeManager.COLLISIONMASK_TRIGGER, colMask) == 0 then |
| 90 | g_logManager:xmlWarning(xmlFilename, "Invalid collision mask for wood trigger 'placeable.woodSellStation#triggerNode'. Bit 24 needs to be set!") |
| 91 | delete(xmlFile) |
| 92 | return false |
| 93 | end |
| 94 | |
| 95 | addTrigger(woodSellTrigger, "woodTriggerCallback", self) |
| 96 | self.woodSellTrigger = woodSellTrigger |
| 97 | |
| 98 | |
| 99 | local sellTrigger = I3DUtil.indexToObject(self.nodeId, getXMLString(xmlFile, "placeable.woodSellStation#sellTrigger")) |
| 100 | if sellTrigger == nil then |
| 101 | g_logManager:xmlWarning(xmlFilename, "Missing sell trigger in 'placeable.woodSellStation#sellTrigger'!") |
| 102 | delete(xmlFile) |
| 103 | return false |
| 104 | end |
| 105 | |
| 106 | colMask = getCollisionMask(sellTrigger) |
| 107 | if bitAND(Player.COLLISIONMASK_TRIGGER, colMask) == 0 then |
| 108 | g_logManager:xmlWarning(xmlFilename, "Invalid collision mask for sell trigger 'placeable.woodSellStation#triggerNode'. Bit 20 needs to be set!") |
| 109 | delete(xmlFile) |
| 110 | return false |
| 111 | end |
| 112 | |
| 113 | if self.appearsOnPDA then |
| 114 | local mapPosition = self.woodSellTrigger |
| 115 | local mapPositionIndex = getUserAttribute(self.woodSellTrigger, "mapPositionIndex") |
| 116 | if mapPositionIndex ~= nil then |
| 117 | mapPosition = I3DUtil.indexToObject(self.woodSellTrigger, mapPositionIndex) |
| 118 | if mapPosition == nil then |
| 119 | mapPosition = self.woodSellTrigger |
| 120 | end |
| 121 | end |
| 122 | local filenames = nil |
| 123 | local imageUVs = getNormalizedUVs(MapHotspot.UV.SELLING_POINT) |
| 124 | local x, _, z = getWorldTranslation(mapPosition) |
| 125 | |
| 126 | local hotspotTextOffset = Utils.getNoNil(getXMLString(xmlFile, "placeable.woodSellStation#hotspotTextOffset"), "0px 0px") |
| 127 | |
| 128 | self.mapHotspot = MapHotspot:new("woodSellStation", MapHotspot.CATEGORY_TRIGGER) |
| 129 | self.mapHotspot:setText(self.stationName) |
| 130 | self.mapHotspot:setBorderedImage(nil, imageUVs, {0.9559, 0.5647, 0.0423, 1}) |
| 131 | self.mapHotspot:setWorldPosition(x, z) |
| 132 | self.mapHotspot:setRawTextOffset(hotspotTextOffset) |
| 133 | |
| 134 | g_currentMission:addMapHotspot(self.mapHotspot) |
| 135 | end |
| 136 | |
| 137 | addTrigger(sellTrigger, "woodSellTriggerCallback", self) |
| 138 | self.sellTrigger = sellTrigger |
| 139 | |
| 140 | self.activateText = g_i18n:getText("action_sellWood") |
| 141 | self.objectActivated = false |
| 142 | |
| 143 | self.updateEventListeners = {} |
| 144 | |
| 145 | delete(xmlFile) |
| 146 | |
| 147 | return true |
| 148 | end |
Add listener to update listenersDefinition
addUpdateEventListener()Code
| 205 | function WoodSellStationPlaceable:addUpdateEventListener(listener) |
| 206 | if listener ~= nil then |
| 207 | self.updateEventListeners[listener] = listener |
| 208 | end |
| 209 | end |
Remove listener from update listenersDefinition
removeUpdateEventListener()Code
| 213 | function WoodSellStationPlaceable:removeUpdateEventListener(listener) |
| 214 | if listener ~= nil then |
| 215 | self.updateEventListeners[listener] = nil |
| 216 | end |
| 217 | end |
Returns true if wood can be soldDefinition
getIsActivatable()Return Values
| boolean | isActivateable | is activateable |
| 275 | function WoodSellStationPlaceable:getIsActivatable() |
| 276 | return g_currentMission.controlPlayer |
| 277 | end |
Called on activate objectDefinition
onActivateObject()Code
| 285 | function WoodSellStationPlaceable:onActivateObject() |
| 286 | g_currentMission:addActivatableObject(self) |
| 287 | self.objectActivated = true |
| 288 | self:sellWood(g_currentMission:getFarmId()) |
| 289 | end |
Class for basket triggers
On create basket triggerDefinition
onCreate(integer id)Arguments
| integer | id | id of trigger node |
| 17 | function BasketTrigger:onCreate(id) |
| 18 | local trigger = BasketTrigger:new(); |
| 19 | if trigger:load(id) then |
| 20 | g_currentMission:addNonUpdateable(trigger); |
| 21 | else |
| 22 | trigger:delete(); |
| 23 | end |
| 24 | end; |
Creating basket trigger objectDefinition
new(table mt)Arguments
| table | mt | custom metatable (optional) |
| table | instance | instance of basket trigger object |
| 30 | function BasketTrigger:new(mt) |
| 31 | local self = {}; |
| 32 | if mt == nil then |
| 33 | mt = BasketTrigger_mt; |
| 34 | end |
| 35 | setmetatable(self, mt); |
| 36 | |
| 37 | self.triggerId = 0; |
| 38 | self.nodeId = 0; |
| 39 | |
| 40 | return self; |
| 41 | end; |
Load basket triggerDefinition
load(integer nodeId)Arguments
| integer | nodeId | id of node |
| boolean | success | success |
| 47 | function BasketTrigger:load(nodeId) |
| 48 | self.nodeId = nodeId; |
| 49 | |
| 50 | self.triggerId = I3DUtil.indexToObject(nodeId, getUserAttribute(nodeId, "triggerIndex")); |
| 51 | if self.triggerId == nil then |
| 52 | self.triggerId = nodeId; |
| 53 | end |
| 54 | addTrigger(self.triggerId, "triggerCallback", self); |
| 55 | |
| 56 | self.triggerObjects = {}; |
| 57 | |
| 58 | self.isEnabled = true; |
| 59 | |
| 60 | return true; |
| 61 | end; |
Delete basket triggerDefinition
delete()Code
| 65 | function BasketTrigger:delete() |
| 66 | removeTrigger(self.triggerId); |
| 67 | end; |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 76 | function BasketTrigger:triggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
| 77 | if self.isEnabled then |
| 78 | |
| 79 | if onEnter then |
| 80 | local object = g_currentMission:getNodeObject(otherActorId); |
| 81 | if object.thrownFromPosition ~= nil then |
| 82 | self.triggerObjects[otherActorId] = true; |
| 83 | end |
| 84 | |
| 85 | elseif onLeave then |
| 86 | if self.triggerObjects[otherActorId] then |
| 87 | self.triggerObjects[otherActorId] = false; |
| 88 | |
| 89 | local object = g_currentMission:getNodeObject(otherActorId); |
| 90 | local x,y,z = worldToLocal(self.triggerId, object.thrownFromPosition[1],object.thrownFromPosition[2],object.thrownFromPosition[3]); |
| 91 | local dist = MathUtil.vector3Length(x,y,z); |
| 92 | end |
| 93 | end; |
| 94 | end; |
| 95 | end; |
Creates a new instance of the classDefinition
new(table customMt)Arguments
| table | customMt | meta table |
| table | self | returns the instance |
| 23 | function FillPlane:new(customMt) |
| 24 | if customMt == nil then |
| 25 | customMt = FillPlane_mt |
| 26 | end |
| 27 | local self = {} |
| 28 | setmetatable(self, customMt) |
| 29 | |
| 30 | self:initDataStructures() |
| 31 | |
| 32 | return self |
| 33 | end |
DestructorDefinition
delete()Code
| 37 | function FillPlane:delete() |
| 38 | end |
Init class membersDefinition
initDataStructures()Code
| 42 | function FillPlane:initDataStructures() |
| 43 | self.node = nil |
| 44 | self.maxCapacity = 0 |
| 45 | self.moveMinY = 0 |
| 46 | self.moveMaxY = 0 |
| 47 | self.loaded = false |
| 48 | self.colorChange = false |
| 49 | end |
Loads fill planeDefinition
load(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| 56 | function FillPlane:load(rootNode, xmlFile, xmlNode) |
| 57 | local fillPlaneNodeStr = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "node", getXMLString, rootNode) |
| 58 | |
| 59 | if fillPlaneNodeStr ~= nil then |
| 60 | local fillPlaneNode = I3DUtil.indexToObject(rootNode, fillPlaneNodeStr) |
| 61 | |
| 62 | if fillPlaneNode ~= nil then |
| 63 | self.node = fillPlaneNode |
| 64 | self.moveMinY = Utils.getNoNil(XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "minY", getXMLFloat, rootNode), 0) |
| 65 | self.moveMaxY = Utils.getNoNil(XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "maxY", getXMLFloat, rootNode), 0) |
| 66 | self.colorChange = Utils.getNoNil(XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "colorChange", getXMLBool, rootNode), false) |
| 67 | assert(self.moveMinY <= self.moveMaxY) |
| 68 | self.loaded = self.node ~= nil |
| 69 | local x, _, z = getTranslation(self.node) |
| 70 | setTranslation(self.node, x, self.moveMinY, z) |
| 71 | end |
| 72 | end |
| 73 | end |
Changes fill levels visualsDefinition
setState(table instance)Arguments
| table | instance | target to check fillLevel |
| bool | true | if level has changed |
| 79 | function FillPlane:setState(state) |
| 80 | if self.loaded then |
| 81 | local delta = self.moveMaxY - self.moveMinY |
| 82 | local y = math.min(self.moveMinY + delta * state, self.moveMaxY) |
| 83 | local x, oldY, z = getTranslation(self.node) |
| 84 | setTranslation(self.node, x, y, z) |
| 85 | |
| 86 | return oldY ~= y |
| 87 | end |
| 88 | |
| 89 | return false |
| 90 | end |
Sets fill plane color shaderDefinition
setColorScale(float[] a)Arguments
| float[] | a | float array for r, g, b |
| 95 | function FillPlane:setColorScale(colorScale) |
| 96 | if self.loaded then |
| 97 | setShaderParameter(self.node, "colorScale", colorScale[1], colorScale[2], colorScale[3], 0, false) |
| 98 | end |
| 99 | end |
Class for fill triggers
On create fill triggerDefinition
onCreate(integer id)Arguments
| integer | id | id of trigger node |
| 13 | function FillTrigger:onCreate(id) |
| 14 | g_currentMission:addNonUpdateable(FillTrigger:new(id)) |
| 15 | end |
Create fill trigger objectDefinition
new(integer id, table sourceObject, integer fillUnitIndex, table customMt)Arguments
| integer | id | id of trigger node |
| table | sourceObject | sourceObject |
| integer | fillUnitIndex | fillUnitIndex |
| table | customMt | custom metatable (optional) |
| table | instance | instance of gas station trigger |
| 24 | function FillTrigger:new(id, sourceObject, fillUnitIndex, fillLitersPerSecond, defaultFillType, customMt) |
| 25 | local self = {} |
| 26 | setmetatable(self, customMt or FillTrigger_mt) |
| 27 | |
| 28 | self.customEnvironment = g_currentMission.loadingMapModName |
| 29 | |
| 30 | self.triggerId = id |
| 31 | addTrigger(id, "fillTriggerCallback", self) |
| 32 | |
| 33 | -- place sound at the same position as the trigger |
| 34 | self.soundNode = createTransformGroup("fillTriggerSoundNode") |
| 35 | link(getParent(id), self.soundNode) |
| 36 | setTranslation(self.soundNode, getTranslation(id)) |
| 37 | |
| 38 | self.sourceObject = sourceObject |
| 39 | self.vehiclesTriggerCount = {} |
| 40 | self.fillUnitIndex = fillUnitIndex |
| 41 | self.fillLitersPerSecond = fillLitersPerSecond |
| 42 | self.appearsOnPDA = Utils.getNoNil(getUserAttribute(id, "appearsOnPDA"), true) |
| 43 | self.isEnabled = true |
| 44 | |
| 45 | self.fillTypeIndex = FillType.DIESEL |
| 46 | |
| 47 | if self.appearsOnPDA and sourceObject == nil then |
| 48 | local mapPosition = id |
| 49 | local mapPositionIndex = getUserAttribute(id, "mapPositionIndex") |
| 50 | if mapPositionIndex ~= nil then |
| 51 | mapPosition = I3DUtil.indexToObject(id, mapPositionIndex) |
| 52 | if mapPosition == nil then |
| 53 | mapPosition = id |
| 54 | end |
| 55 | end |
| 56 | |
| 57 | local x, _, z = getWorldTranslation(mapPosition) |
| 58 | |
| 59 | local fullViewName = Utils.getNoNil(getUserAttribute(id, "stationName"), "map_fuelStation") |
| 60 | if g_i18n:hasText(fullViewName, self.customEnvironment) then |
| 61 | fullViewName = g_i18n:getText(fullViewName, self.customEnvironment) |
| 62 | end |
| 63 | |
| 64 | self.mapHotspot = MapHotspot:new("fuelStation", MapHotspot.CATEGORY_DEFAULT) |
| 65 | self.mapHotspot:setText(fullViewName) |
| 66 | self.mapHotspot:setWorldPosition(x, z) |
| 67 | self.mapHotspot:setBorderedImage(nil, getNormalizedUVs(MapHotspot.UV.GAS_STATION)) |
| 68 | |
| 69 | g_currentMission:addMapHotspot(self.mapHotspot) |
| 70 | end |
| 71 | |
| 72 | self.moneyChangeId = getMoneyTypeId() |
| 73 | |
| 74 | return self |
| 75 | end |
Delete fill triggerDefinition
delete()Code
| 79 | function FillTrigger:delete() |
| 80 | -- remove the gas stations from all vehicles that are triggered by this trigger |
| 81 | for vehicle,count in pairs(self.vehiclesTriggerCount) do |
| 82 | if count > 0 then |
| 83 | if vehicle.removeFillUnitTrigger ~= nil then |
| 84 | vehicle:removeFillUnitTrigger(self) |
| 85 | end |
| 86 | end |
| 87 | end |
| 88 | |
| 89 | if self.mapHotspot ~= nil then |
| 90 | g_currentMission:removeMapHotspot(self.mapHotspot) |
| 91 | self.mapHotspot:delete() |
| 92 | end |
| 93 | |
| 94 | g_soundManager:deleteSample(self.sample) |
| 95 | |
| 96 | removeTrigger(self.triggerId) |
| 97 | end |
Called if vehicle gets out of triggerDefinition
onVehicleDeleted(table vehicle)Arguments
| table | vehicle | vehicle |
| 102 | function FillTrigger:onVehicleDeleted(vehicle) |
| 103 | self.vehiclesTriggerCount[vehicle] = nil |
| 104 | g_currentMission:showMoneyChange(self.moneyChangeId, g_i18n:getText("finance_purchaseFuel")) |
| 105 | end |
Fill vehicleDefinition
fillVehicle(table vehicle, float delta)Arguments
| table | vehicle | vehicle to fill |
| float | delta | delta |
| float | delta | real delta |
| 112 | function FillTrigger:fillVehicle(vehicle, delta, dt) |
| 113 | if self.fillLitersPerSecond ~= nil then |
| 114 | delta = math.max(delta, self.fillLitersPerSecond*0.001*dt) |
| 115 | end |
| 116 | |
| 117 | local farmId = vehicle:getActiveFarm() |
| 118 | |
| 119 | if self.sourceObject ~= nil then |
| 120 | local sourceFuelFillLevel = self.sourceObject:getFillUnitFillLevel(self.fillUnitIndex) |
| 121 | if sourceFuelFillLevel > 0 and g_currentMission.accessHandler:canFarmAccess(farmId, self.sourceObject) then |
| 122 | delta = math.min(delta, sourceFuelFillLevel) |
| 123 | if delta <= 0 then |
| 124 | return 0 |
| 125 | end |
| 126 | else |
| 127 | return 0 |
| 128 | end |
| 129 | end |
| 130 | |
| 131 | local fillType = self:getCurrentFillType() |
| 132 | |
| 133 | local fillUnitIndex = vehicle:getFirstValidFillUnitToFill(fillType) |
| 134 | if fillUnitIndex == nil then |
| 135 | return 0 |
| 136 | end |
| 137 | |
| 138 | delta = vehicle:addFillUnitFillLevel(farmId, fillUnitIndex, delta, fillType, ToolType.TRIGGER, nil) |
| 139 | |
| 140 | if delta > 0 then |
| 141 | if self.sourceObject ~= nil then |
| 142 | self.sourceObject:addFillUnitFillLevel(farmId, self.fillUnitIndex, -delta, fillType, ToolType.TRIGGER, nil) |
| 143 | else |
| 144 | local price = delta * g_currentMission.economyManager:getPricePerLiter(fillType) |
| 145 | g_farmManager:getFarmById(farmId).stats:updateStats("expenses", price) |
| 146 | |
| 147 | local userId = g_currentMission:getServerUserId() |
| 148 | local user = g_currentMission:getUserByConnection(vehicle:getOwner()) |
| 149 | if user ~= nil then |
| 150 | userId = user.userId |
| 151 | end |
| 152 | |
| 153 | g_currentMission:addMoney(-price, farmId, "purchaseFuel") |
| 154 | g_currentMission:addMoneyChange(-price, farmId, self.moneyChangeId) |
| 155 | end |
| 156 | end |
| 157 | |
| 158 | return delta |
| 159 | end |
Returns true if is activateableDefinition
getIsActivatable(table vehicle)Arguments
| table | vehicle | vehicle |
| boolean | isActivateable | is activateable |
| 165 | function FillTrigger:getIsActivatable(vehicle) |
| 166 | if self.sourceObject ~= nil then |
| 167 | if self.sourceObject:getFillUnitFillLevel(self.fillUnitIndex) > 0 and g_currentMission.accessHandler:canFarmAccess(vehicle:getActiveFarm(), self.sourceObject) then |
| 168 | return true |
| 169 | end |
| 170 | end |
| 171 | |
| 172 | return false |
| 173 | end |
Trigger callbackDefinition
fillTriggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 182 | function FillTrigger:fillTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 183 | if self.isEnabled and (onEnter or onLeave) then |
| 184 | local vehicle = g_currentMission:getNodeObject(otherId) |
| 185 | if vehicle ~= nil and vehicle.addFillUnitTrigger ~= nil and vehicle.removeFillUnitTrigger ~= nil and vehicle ~= self and vehicle ~= self.sourceObject then |
| 186 | local count = Utils.getNoNil(self.vehiclesTriggerCount[vehicle], 0) |
| 187 | if onEnter then |
| 188 | local fillType = self:getCurrentFillType() |
| 189 | local fillUnitIndex = vehicle:getFirstValidFillUnitToFill(fillType) |
| 190 | if fillUnitIndex ~= nil then |
| 191 | self.vehiclesTriggerCount[vehicle] = count + 1 |
| 192 | |
| 193 | if count == 0 then |
| 194 | vehicle:addFillUnitTrigger(self, fillType, fillUnitIndex) |
| 195 | end |
| 196 | end |
| 197 | else |
| 198 | self.vehiclesTriggerCount[vehicle] = count - 1 |
| 199 | if count <= 1 then |
| 200 | self.vehiclesTriggerCount[vehicle] = nil |
| 201 | vehicle:removeFillUnitTrigger(self) |
| 202 | g_currentMission:showMoneyChange(self.moneyChangeId, g_i18n:getText("finance_purchaseFuel")) |
| 203 | end |
| 204 | end |
| 205 | end |
| 206 | end |
| 207 | end |
Class for InsideBuildingTriggers
On create InsideBuildingTriggerDefinition
onCreate(integer id)Arguments
| integer | id | id of trigger node |
| 15 | function InsideBuildingTrigger.onCreate(_, id) |
| 16 | local trigger = InsideBuildingTrigger:new() |
| 17 | if trigger:load(id) then |
| 18 | g_currentMission:addNonUpdateable(trigger) |
| 19 | else |
| 20 | trigger:delete() |
| 21 | end |
| 22 | end |
Creating InsideBuildingTrigger objectDefinition
new(table customMt)Arguments
| table | customMt | custom metatable (optional) |
| table | instance | instance of basket trigger object |
| 28 | function InsideBuildingTrigger:new(customMt) |
| 29 | local self = {} |
| 30 | setmetatable(self, customMt or InsideBuildingTrigger_mt) |
| 31 | |
| 32 | self.triggerId = 0 |
| 33 | self.nodeId = 0 |
| 34 | |
| 35 | return self |
| 36 | end |
Load InsideBuildingTriggerDefinition
load(integer nodeId)Arguments
| integer | nodeId | id of node |
| boolean | success | success |
| 42 | function InsideBuildingTrigger:load(nodeId) |
| 43 | self.nodeId = nodeId |
| 44 | |
| 45 | self.triggerId = I3DUtil.indexToObject(nodeId, getUserAttribute(nodeId, "triggerIndex")) |
| 46 | if self.triggerId == nil then |
| 47 | self.triggerId = nodeId |
| 48 | end |
| 49 | addTrigger(self.triggerId, "insideBuildingTriggerCallback", self) |
| 50 | |
| 51 | self.isEnabled = true |
| 52 | |
| 53 | return true |
| 54 | end |
Delete InsideBuildingTriggerDefinition
delete()Code
| 58 | function InsideBuildingTrigger:delete() |
| 59 | removeTrigger(self.triggerId) |
| 60 | end |
Trigger callbackDefinition
insideBuildingTriggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 69 | function InsideBuildingTrigger:insideBuildingTriggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
| 70 | -- log(g_currentMission.player.rootNode, triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
| 71 | if g_currentMission.player ~= nil and g_currentMission.player.rootNode == otherActorId then |
| 72 | if self.isEnabled then |
| 73 | if onEnter then |
| 74 | g_currentMission:setIsInsideBuilding(true) |
| 75 | elseif onLeave then |
| 76 | g_currentMission:setIsInsideBuilding(false) |
| 77 | end |
| 78 | end |
| 79 | end |
| 80 | end |
Get the farm id for given object. If none can be found, SPECTATOR is used.Definition
farmIdForFillableObject()
Class for loan triggers
On create loan triggerDefinition
onCreate(integer id)Arguments
| integer | id | id of trigger node |
| 15 | function LoanTrigger:onCreate(id) |
| 16 | g_currentMission:addNonUpdateable(LoanTrigger:new(id)) |
| 17 | end |
Create loan trigger objectDefinition
new(integer name)Arguments
| integer | name | id of trigger node |
| table | instance | instance |
| 23 | function LoanTrigger:new(name) |
| 24 | local self = {} |
| 25 | setmetatable(self, LoanTrigger_mt) |
| 26 | |
| 27 | if g_currentMission:getIsClient() then |
| 28 | self.triggerId = name |
| 29 | addTrigger(name, "triggerCallback", self) |
| 30 | end |
| 31 | |
| 32 | self.loanSymbol = getChildAt(name, 0) |
| 33 | |
| 34 | self.activateText = g_i18n:getText("action_checkFinances") |
| 35 | |
| 36 | self.isEnabled = true |
| 37 | self.objectActivated = false |
| 38 | |
| 39 | g_messageCenter:subscribe(MessageType.PLAYER_FARM_CHANGED, self.playerFarmChanged, self) |
| 40 | |
| 41 | self:updateIconVisibility() |
| 42 | |
| 43 | return self |
| 44 | end |
Delete loan triggerDefinition
delete()Code
| 48 | function LoanTrigger:delete() |
| 49 | g_messageCenter:unsubscribeAll(self) |
| 50 | |
| 51 | if self.triggerId ~= nil then |
| 52 | removeTrigger(self.triggerId) |
| 53 | end |
| 54 | self.loanSymbol = nil |
| 55 | g_currentMission:removeActivatableObject(self) |
| 56 | end |
Returns true if is activateableDefinition
getIsActivatable()Return Values
| boolean | isActivateable | is activateable |
| 61 | function LoanTrigger:getIsActivatable() |
| 62 | return self.isEnabled and g_currentMission.controlPlayer and g_currentMission:getFarmId() ~= FarmManager.SPECTATOR_FARM_ID |
| 63 | end |
Called on activate objectDefinition
onActivateObject()Code
| 71 | function LoanTrigger:onActivateObject() |
| 72 | g_gui:showGui("InGameMenu") |
| 73 | g_messageCenter:publish(MessageType.GUI_INGAME_OPEN_FINANCES_SCREEN) |
| 74 | |
| 75 | g_currentMission:addActivatableObject(self) |
| 76 | self.objectActivated = true |
| 77 | end |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 86 | function LoanTrigger:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 87 | if self.isEnabled and (not g_isPresentationVersion or g_isPresentationVersionShopEnabled) and g_currentMission.missionInfo:isa(FSCareerMissionInfo) then |
| 88 | if onEnter or onLeave then |
| 89 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
| 90 | if onEnter then |
| 91 | if not self.objectActivated then |
| 92 | g_currentMission:addActivatableObject(self) |
| 93 | self.objectActivated = true |
| 94 | end |
| 95 | else |
| 96 | if self.objectActivated then |
| 97 | g_currentMission:removeActivatableObject(self) |
| 98 | self.objectActivated = false |
| 99 | end |
| 100 | end |
| 101 | end |
| 102 | end |
| 103 | end |
| 104 | end |
Turn the icon on or off depending on the current game and the players farmDefinition
updateIconVisibility()
Class for RainDropFactorTriggers
On create RainDropFactorTriggerDefinition
onCreate(integer id)Arguments
| integer | id | id of trigger node |
| 15 | function RainDropFactorTrigger:onCreate(id) |
| 16 | local trigger = RainDropFactorTrigger:new(); |
| 17 | if trigger:load(id) then |
| 18 | g_currentMission:addNonUpdateable(trigger); |
| 19 | else |
| 20 | trigger:delete(); |
| 21 | end |
| 22 | end; |
Creating RainDropFactorTrigger objectDefinition
new(table mt)Arguments
| table | mt | custom metatable (optional) |
| table | instance | instance of basket trigger object |
| 28 | function RainDropFactorTrigger:new(mt) |
| 29 | local self = {}; |
| 30 | if mt == nil then |
| 31 | mt = RainDropFactorTrigger_mt; |
| 32 | end |
| 33 | setmetatable(self, mt); |
| 34 | |
| 35 | self.triggerId = 0; |
| 36 | self.nodeId = 0; |
| 37 | |
| 38 | return self; |
| 39 | end; |
Load RainDropFactorTriggerDefinition
load(integer nodeId)Arguments
| integer | nodeId | id of node |
| boolean | success | success |
| 45 | function RainDropFactorTrigger:load(nodeId) |
| 46 | self.nodeId = nodeId; |
| 47 | |
| 48 | self.triggerId = I3DUtil.indexToObject(nodeId, getUserAttribute(nodeId, "triggerIndex")); |
| 49 | if self.triggerId == nil then |
| 50 | self.triggerId = nodeId; |
| 51 | end |
| 52 | addTrigger(self.triggerId, "triggerCallback", self); |
| 53 | |
| 54 | self.triggerObjects = {}; |
| 55 | |
| 56 | self.isEnabled = true; |
| 57 | |
| 58 | return true; |
| 59 | end; |
Delete RainDropFactorTriggerDefinition
delete()Code
| 63 | function RainDropFactorTrigger:delete() |
| 64 | removeTrigger(self.triggerId); |
| 65 | end; |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 74 | function RainDropFactorTrigger:triggerCallback(triggerId, otherActorId, onEnter, onLeave, onStay, otherShapeId) |
| 75 | if self.isEnabled then |
| 76 | if onEnter then |
| 77 | if g_currentMission.environment ~= nil then |
| 78 | -- g_currentMission.environment.globalRainDropFactor = 0.0; |
| 79 | end |
| 80 | elseif onLeave then |
| 81 | if g_currentMission.environment ~= nil then |
| 82 | -- g_currentMission.environment.globalRainDropFactor = 1.0; |
| 83 | end |
| 84 | end; |
| 85 | end; |
| 86 | end; |
Class for shop triggers to open shop gui
On create shop triggerDefinition
onCreate(integer id)Arguments
| integer | id | trigger node id |
| 15 | function ShopTrigger:onCreate(id) |
| 16 | g_currentMission:addNonUpdateable(ShopTrigger:new(id)) |
| 17 | end |
Creating shop trigger objectDefinition
new(integer name)Arguments
| integer | name | trigger node id |
| table | instance | instance of object |
| 23 | function ShopTrigger:new(name) |
| 24 | local self = {} |
| 25 | setmetatable(self, ShopTrigger_mt) |
| 26 | |
| 27 | if g_currentMission:getIsClient() then |
| 28 | self.triggerId = name |
| 29 | addTrigger(name, "triggerCallback", self) |
| 30 | end |
| 31 | |
| 32 | self.shopSymbol = getChildAt(name, 0) |
| 33 | self.shopPlayerSpawn = getChildAt(name, 1) |
| 34 | |
| 35 | self.objectActivated = false |
| 36 | self.isEnabled = true |
| 37 | |
| 38 | g_messageCenter:subscribe(MessageType.PLAYER_FARM_CHANGED, self.playerFarmChanged, self) |
| 39 | |
| 40 | self:updateIconVisibility() |
| 41 | |
| 42 | self.activateText = g_i18n:getText("action_activateShop") |
| 43 | |
| 44 | return self |
| 45 | end |
Deleting shop triggerDefinition
delete()Code
| 49 | function ShopTrigger:delete() |
| 50 | g_messageCenter:unsubscribeAll(self) |
| 51 | if self.triggerId ~= nil then |
| 52 | removeTrigger(self.triggerId) |
| 53 | end |
| 54 | self.shopSymbol = nil |
| 55 | g_currentMission:removeActivatableObject(self) |
| 56 | end |
Returns true if shop can be openedDefinition
getIsActivatable()Return Values
| boolean | isActivateable | is activateable |
| 61 | function ShopTrigger:getIsActivatable() |
| 62 | return self.isEnabled and g_currentMission.controlPlayer and g_currentMission:getFarmId() ~= FarmManager.SPECTATOR_FARM_ID |
| 63 | end |
Called on activate objectDefinition
onActivateObject()Code
| 71 | function ShopTrigger:onActivateObject() |
| 72 | g_currentMission:addActivatableObject(self) |
| 73 | self.objectActivated = true |
| 74 | |
| 75 | local inGameMenu = g_gui:getScreenInstanceByClass(InGameMenu) |
| 76 | inGameMenu:setMode(InGameMenu.MODE_SHOP) |
| 77 | g_gui:changeScreen(nil, InGameMenu) |
| 78 | |
| 79 | local x,y,z = getWorldTranslation(self.shopPlayerSpawn) |
| 80 | local dx, _, dz = localDirectionToWorld(self.shopPlayerSpawn, 0, 0, -1) |
| 81 | g_currentMission.player:moveToAbsolute(x,y,z) |
| 82 | g_currentMission.player.rotY = MathUtil.getYRotationFromDirection(dx, dz) |
| 83 | end |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 92 | function ShopTrigger:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 93 | if self.isEnabled and (not g_isPresentationVersion or g_isPresentationVersionShopEnabled) and g_currentMission.missionInfo:isa(FSCareerMissionInfo) then |
| 94 | if onEnter or onLeave then |
| 95 | if g_currentMission.player ~= nil and otherId == g_currentMission.player.rootNode then |
| 96 | if onEnter then |
| 97 | if not self.objectActivated then |
| 98 | g_currentMission:addActivatableObject(self) |
| 99 | self.objectActivated = true |
| 100 | end |
| 101 | else |
| 102 | if self.objectActivated then |
| 103 | g_currentMission:removeActivatableObject(self) |
| 104 | self.objectActivated = false |
| 105 | end |
| 106 | end |
| 107 | end |
| 108 | end |
| 109 | end |
| 110 | end |
Turn the icon on or off depending on the current game and the players farmDefinition
updateIconVisibility()
Class for transport mission triggers
On create mission triggerDefinition
onCreate(integer id)Arguments
| integer | id | trigger node id |
| 19 | function TransportMissionTrigger:onCreate(id) |
| 20 | g_currentMission:addNonUpdateable(TransportMissionTrigger:new(id)) |
| 21 | end |
Creating mission trigger objectDefinition
new(integer name)Arguments
| integer | name | trigger node id |
| table | instance | instance of object |
| 27 | function TransportMissionTrigger:new(id) |
| 28 | local self = {} |
| 29 | setmetatable(self, TransportMissionTrigger_mt) |
| 30 | |
| 31 | self.triggerId = id |
| 32 | self.index = getUserAttribute(self.triggerId, "index") |
| 33 | |
| 34 | addTrigger(id, "triggerCallback", self) |
| 35 | |
| 36 | self.isEnabled = true |
| 37 | |
| 38 | g_missionManager:addTransportMissionTrigger(self) |
| 39 | |
| 40 | -- Hide until needed |
| 41 | self:setMission(nil) |
| 42 | |
| 43 | return self |
| 44 | end |
Deleting shop triggerDefinition
delete()Code
| 48 | function TransportMissionTrigger:delete() |
| 49 | removeTrigger(self.triggerId) |
| 50 | |
| 51 | g_missionManager:removeTransportMissionTrigger(self) |
| 52 | end |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 72 | function TransportMissionTrigger:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 73 | if self.isEnabled and self.mission ~= nil then |
| 74 | if onEnter then |
| 75 | self.mission:objectEnteredTrigger(self, otherId) |
| 76 | elseif onLeave then |
| 77 | self.mission:objectLeftTrigger(self, otherId) |
| 78 | end |
| 79 | end |
| 80 | end |
Creates a new instance of the classDefinition
new(bool isServer, bool isClient, table customMt)Arguments
| bool | isServer | true if we are server |
| bool | isClient | true if we are client |
| table | customMt | meta table |
| table | self | returns the instance |
| 25 | function UnloadFeedingTrough:new(isServer, isClient, customMt) |
| 26 | local self = UnloadTrigger:new(isServer, isClient, customMt or UnloadFeedingTrough_mt) |
| 27 | |
| 28 | self.animalPlaces = {} |
| 29 | |
| 30 | return self |
| 31 | end |
Loads elements of the classDefinition
load(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| bool | return | true if successful |
| 39 | function UnloadFeedingTrough:load(rootNode, xmlFile, xmlNode, target) |
| 40 | local returnValue = UnloadFeedingTrough:superClass().load(self, rootNode, xmlFile, xmlNode, target) |
| 41 | |
| 42 | if returnValue then |
| 43 | self:loadAnimalPlaces(rootNode, xmlFile, xmlNode) |
| 44 | end |
| 45 | return returnValue |
| 46 | end |
Loads animal placesDefinition
loadAnimalPlaces(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| 53 | function UnloadFeedingTrough:loadAnimalPlaces(rootNode, xmlFile, xmlNode) |
| 54 | -- print(string.format("-- [UnloadFeedingTrough:loadAnimalPlaces] rootNode(%s) xmlFile(%s) xmlNode(%s)", tostring(rootNode), tostring(xmlFile), tostring(xmlNode))) |
| 55 | local animalPlacesNode = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "animalPlacesNode", getXMLString, rootNode) |
| 56 | if animalPlacesNode ~= nil then |
| 57 | local animalPlaces = I3DUtil.indexToObject(rootNode, animalPlacesNode) |
| 58 | -- print(string.format("-- [UnloadFeedingTrough:loadAnimalPlaces] animalPlacesNode(%s) animalPlaces(%s)", tostring(animalPlacesNode), tostring(animalPlaces))) |
| 59 | if animalPlaces ~= nil and self.target~= nil and self.target.husbandryId ~= nil then |
| 60 | for i = 1, getNumOfChildren(animalPlaces) do |
| 61 | local animalPlaceId = getChildAt(animalPlaces, i - 1) |
| 62 | local animalPlace = addFeedingPlace(self.target.husbandryId, animalPlaceId, 0.0) |
| 63 | table.insert(self.animalPlaces, animalPlace) |
| 64 | end |
| 65 | end |
| 66 | end |
| 67 | end |
Overriding Unload Trigger method with empty method. The animal husbandry module is taking care of setting up the filltypesDefinition
loadFillTypes(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| 74 | function UnloadFeedingTrough:loadFillTypes(rootNode, xmlFile, xmlNode) |
| 75 | end |
Setup fillTypes from animal food groupsDefinition
initFillTypesFromFoodGroups(table foodGroups)Arguments
| table | foodGroups |
| 81 | function UnloadFeedingTrough:initFillTypesFromFoodGroups(foodGroups) |
| 82 | self.fillTypes = {} |
| 83 | for _, foodGroup in pairs(foodGroups) do |
| 84 | for _, fillTypeIndex in pairs(foodGroup.fillTypes) do |
| 85 | self.fillTypes[fillTypeIndex] = true |
| 86 | end |
| 87 | end |
| 88 | end |
Changes fill levels from a toolDefinition
addFillUnitFillLevel(float deltaFillLevel, integer fillType, table fillInfo, integer toolType)Arguments
| float | deltaFillLevel | |
| integer | fillType | |
| table | fillInfo | |
| integer | toolType |
| float | returns | the change delta |
| 101 | function UnloadFeedingTrough:addFillUnitFillLevel(farmId, fillUnitIndex, fillLevelDelta, fillTypeIndex, toolType, fillPositionData) |
| 102 | local foodMixture = g_animalFoodManager:getFoodMixtureByFillType(fillTypeIndex) |
| 103 | local delta = 0 |
| 104 | if foodMixture ~= nil then |
| 105 | for _, ingredient in ipairs(foodMixture.ingredients) do |
| 106 | local ingredientFillType = ingredient.fillTypes[1] |
| 107 | local ingredientFillLevel = fillLevelDelta * ingredient.weight |
| 108 | delta = delta + UnloadFeedingTrough:superClass().addFillUnitFillLevel(self, farmId, fillUnitIndex, ingredientFillLevel, ingredientFillType, toolType, fillPositionData) |
| 109 | end |
| 110 | else |
| 111 | delta = UnloadFeedingTrough:superClass().addFillUnitFillLevel(self, farmId, fillUnitIndex, fillLevelDelta, fillTypeIndex, toolType, fillPositionData) |
| 112 | end |
| 113 | |
| 114 | self:updateAnimalPlaces(delta) |
| 115 | return delta |
| 116 | end |
Changes animal places visualsDefinition
updateAnimalPlaces()Code
| 120 | function UnloadFeedingTrough:updateAnimalPlaces(delta) |
| 121 | if delta ~= nil and delta > 0 then |
| 122 | for _, animalPlace in pairs(self.animalPlaces) do |
| 123 | updateFeedingPlace(self.target.husbandryId, animalPlace, delta) |
| 124 | end |
| 125 | end |
| 126 | end |
Creates a new instance of the classDefinition
new(bool isServer, bool isClient, table customMt)Arguments
| bool | isServer | true if we are server |
| bool | isClient | true if we are client |
| table | customMt | meta table |
| table | self | returns the instance |
| 32 | function UnloadTrigger:new(isServer, isClient, customMt) |
| 33 | local self = Object:new(isServer, isClient, customMt or UnloadTrigger_mt) |
| 34 | |
| 35 | self.baleTriggerNode = nil |
| 36 | self.balesInTrigger = {} |
| 37 | self.fillTypes = {} |
| 38 | self.avoidFillTypes = {} |
| 39 | self.acceptedToolTypes = {} |
| 40 | |
| 41 | return self |
| 42 | end |
Loads elements of the classDefinition
load(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| bool | return | true if successful |
| 50 | function UnloadTrigger:load(rootNode, xmlFile, xmlNode, target) |
| 51 | self:loadBaleTrigger(rootNode, xmlFile, xmlNode) |
| 52 | |
| 53 | local exactFillRootNode = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "exactFillRootNode", getXMLString, rootNode) |
| 54 | self.exactFillRootNode = I3DUtil.indexToObject(rootNode, exactFillRootNode) |
| 55 | |
| 56 | if self.baleTriggerNode == nil and self.exactFillRootNode == nil then |
| 57 | g_logManager:warning("Missing exactFillRootNode or baleTrigger for unloadTrigger") |
| 58 | return false |
| 59 | end |
| 60 | |
| 61 | if self.exactFillRootNode ~= nil then |
| 62 | local colMask = getCollisionMask(self.exactFillRootNode) |
| 63 | if bitAND(FillUnit.EXACTFILLROOTNODE_MASK, colMask) == 0 then |
| 64 | g_logManager:warning("Invalid exactFillRootNode collision mask for unloadTrigger. Bit 30 needs to be set!") |
| 65 | return false |
| 66 | end |
| 67 | |
| 68 | g_currentMission:addNodeObject(self.exactFillRootNode, self) |
| 69 | end |
| 70 | |
| 71 | if target ~= nil then |
| 72 | self:setTarget(target) |
| 73 | end |
| 74 | |
| 75 | self:loadFillTypes(rootNode, xmlFile, xmlNode) |
| 76 | self:loadAcceptedToolType(rootNode, xmlFile, xmlNode) |
| 77 | self:loadAvoidFillTypes(rootNode, xmlFile, xmlNode) |
| 78 | self.isEnabled = true |
| 79 | |
| 80 | return true |
| 81 | end |
Loads accepted tool typeDefinition
loadAcceptedToolType(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| 88 | function UnloadTrigger:loadAcceptedToolType(rootNode, xmlFile, xmlNode) |
| 89 | local acceptedToolTypeNames = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "acceptedToolTypes", getXMLString, rootNode) |
| 90 | local acceptedToolTypes = StringUtil.getVectorFromString(acceptedToolTypeNames) |
| 91 | |
| 92 | if acceptedToolTypes ~= nil then |
| 93 | for _,acceptedToolType in pairs(acceptedToolTypes) do |
| 94 | local toolTypeInt = g_toolTypeManager:getToolTypeIndexByName(acceptedToolType) |
| 95 | self.acceptedToolTypes[toolTypeInt] = true |
| 96 | end |
| 97 | else |
| 98 | self.acceptedToolTypes = nil |
| 99 | end |
| 100 | end |
Loads avoid fill TypesDefinition
loadAvoidFillTypes(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| 107 | function UnloadTrigger:loadAvoidFillTypes(rootNode, xmlFile, xmlNode) |
| 108 | local avoidFillTypeCategories = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "avoidFillTypeCategories", getXMLString, rootNode) |
| 109 | local avoidFillTypeNames = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "avoidFillTypes", getXMLString, rootNode) |
| 110 | local avoidFillTypes = nil |
| 111 | |
| 112 | if avoidFillTypeCategories ~= nil and avoidFillTypeNames == nil then |
| 113 | avoidFillTypes = g_fillTypeManager:getFillTypesByCategoryNames(avoidFillTypeCategories, "Warning: UnloadTrigger has invalid avoidFillTypeCategory '%s'.") |
| 114 | elseif avoidFillTypeCategories == nil and avoidFillTypeNames ~= nil then |
| 115 | avoidFillTypes = g_fillTypeManager:getFillTypesByNames(avoidFillTypeNames, "Warning: UnloadTrigger has invalid avoidFillType '%s'.") |
| 116 | end |
| 117 | if avoidFillTypes ~= nil then |
| 118 | for _,fillType in pairs(avoidFillTypes) do |
| 119 | self.avoidFillTypes[fillType] = true |
| 120 | end |
| 121 | else |
| 122 | self.avoidFillTypes = nil |
| 123 | end |
| 124 | end |
Loads fill TypesDefinition
loadFillTypes(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| 131 | function UnloadTrigger:loadFillTypes(rootNode, xmlFile, xmlNode) |
| 132 | local fillTypeCategories = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "fillTypeCategories", getXMLString, rootNode) |
| 133 | local fillTypeNames = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "fillTypes", getXMLString, rootNode) |
| 134 | local fillTypes = nil |
| 135 | |
| 136 | if fillTypeCategories ~= nil and fillTypeNames == nil then |
| 137 | fillTypes = g_fillTypeManager:getFillTypesByCategoryNames(fillTypeCategories, "Warning: UnloadTrigger has invalid fillTypeCategory '%s'.") |
| 138 | elseif fillTypeNames ~= nil then |
| 139 | fillTypes = g_fillTypeManager:getFillTypesByNames(fillTypeNames, "Warning: UnloadTrigger has invalid fillType '%s'.") |
| 140 | end |
| 141 | if fillTypes ~= nil then |
| 142 | for _, fillType in pairs(fillTypes) do |
| 143 | self.fillTypes[fillType] = true |
| 144 | end |
| 145 | else |
| 146 | self.fillTypes = nil |
| 147 | end |
| 148 | end |
Loads bale triggerDefinition
loadBaleTrigger(table rootNode, string xmlFile, string xmlNode)Arguments
| table | rootNode | of the object |
| string | xmlFile | file to read |
| string | xmlNode | xmlNode to read from |
| 155 | function UnloadTrigger:loadBaleTrigger(rootNode, xmlFile, xmlNode) |
| 156 | local baleTriggerNode = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "baleTriggerIndex", getXMLString, rootNode) |
| 157 | if baleTriggerNode ~= nil then |
| 158 | g_logManager:warning("'baleTriggerIndex' is not supported anymore for unloadTrigger! Please use 'baleTriggerNode' instead!") |
| 159 | end |
| 160 | |
| 161 | baleTriggerNode = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "baleTriggerNode", getXMLString, rootNode) |
| 162 | |
| 163 | if baleTriggerNode ~= nil then |
| 164 | self.baleTriggerNode = I3DUtil.indexToObject(rootNode, baleTriggerNode) |
| 165 | if self.baleTriggerNode ~= nil then |
| 166 | addTrigger(self.baleTriggerNode, "baleTriggerCallback", self) |
| 167 | end |
| 168 | end |
| 169 | |
| 170 | local baleDeleteLitersPerSecond = XMLUtil.getValueFromXMLFileOrUserAttribute(xmlFile, xmlNode, "baleDeleteLitersPerSecond", getXMLInt, rootNode) |
| 171 | if baleDeleteLitersPerSecond ~= nil then |
| 172 | self.baleDeleteLitersPerMS = baleDeleteLitersPerSecond * 0.0001 |
| 173 | end |
| 174 | end |
Delete instanceDefinition
delete()Code
| 178 | function UnloadTrigger:delete() |
| 179 | if self.baleTriggerNode ~= nil and self.baleTriggerNode ~= 0 then |
| 180 | removeTrigger(self.baleTriggerNode) |
| 181 | self.baleTriggerNode = 0 |
| 182 | end |
| 183 | |
| 184 | UnloadTrigger:superClass().delete(self) |
| 185 | end |
Connects object using the trigger to the triggerDefinition
setTarget(table object)Arguments
| table | object | target on which the unload trigger is attached |
| 190 | function UnloadTrigger:setTarget(object) |
| 191 | assert(object.getIsFillTypeAllowed ~= nil) |
| 192 | assert(object.getIsToolTypeAllowed ~= nil) |
| 193 | assert(object.addFillLevelFromTool ~= nil) |
| 194 | assert(object.getFreeCapacity ~= nil) |
| 195 | |
| 196 | self.target = object |
| 197 | end |
Update methodDefinition
update(float dt)Arguments
| float | dt | delta time |
| 206 | function UnloadTrigger:update(dt) |
| 207 | UnloadTrigger:superClass().update(self, dt) |
| 208 | |
| 209 | self:updateBales(dt) |
| 210 | end |
Update bale mechanicsDefinition
updateBales(float dt)Arguments
| float | dt | delta time |
| 215 | function UnloadTrigger:updateBales(dt) |
| 216 | for index, bale in ipairs(self.balesInTrigger) do |
| 217 | if bale ~= nil and bale.nodeId ~= 0 then |
| 218 | if bale.dynamicMountJointIndex == nil then |
| 219 | local fillType = bale:getFillType() |
| 220 | local fillLevel = bale:getFillLevel() |
| 221 | local fillInfo = nil |
| 222 | |
| 223 | local delta = bale:getFillLevel() |
| 224 | if self.baleDeleteLitersPerMS ~= nil then |
| 225 | delta = self.baleDeleteLitersPerMS * dt |
| 226 | end |
| 227 | |
| 228 | if delta > 0 then |
| 229 | self.target:addFillLevelFromTool(bale:getOwnerFarmId(), delta, fillType, fillInfo, ToolType.BALE) |
| 230 | bale:setFillLevel(fillLevel - delta) |
| 231 | local newFillLevel = bale:getFillLevel() |
| 232 | if newFillLevel < 0.01 then |
| 233 | bale:delete() |
| 234 | table.remove(self.balesInTrigger, index) |
| 235 | break |
| 236 | end |
| 237 | end |
| 238 | end |
| 239 | else |
| 240 | table.remove(self.balesInTrigger, index) |
| 241 | end |
| 242 | end |
| 243 | |
| 244 | if #self.balesInTrigger > 0 then |
| 245 | self:raiseActive() |
| 246 | end |
| 247 | end |
Returns default value '1'Definition
getFillUnitIndexFromNode(integer node)Arguments
| integer | node | scenegraph node |
| 252 | function UnloadTrigger:getFillUnitIndexFromNode(node) |
| 253 | return 1 |
| 254 | end |
Returns exactFillRootNodeDefinition
getFillUnitExactFillRootNode(integer fillUnitIndex)Arguments
| integer | fillUnitIndex | index of fillunit |
| 259 | function UnloadTrigger:getFillUnitExactFillRootNode(fillUnitIndex) |
| 260 | return self.exactFillRootNode |
| 261 | end |
Increase fill levelDefinition
addFillUnitFillLevel(integer fillUnitIndex, float fillLevelDelta, integer fillTypeIndex, table toolType, table fillPositionData)Arguments
| integer | fillUnitIndex | |
| float | fillLevelDelta | |
| integer | fillTypeIndex | |
| table | toolType | |
| table | fillPositionData |
| 271 | function UnloadTrigger:addFillUnitFillLevel(farmId, fillUnitIndex, fillLevelDelta, fillTypeIndex, toolType, fillPositionData) |
| 272 | local applied = self.target:addFillLevelFromTool(farmId, fillLevelDelta, fillTypeIndex, fillPositionData, toolType) |
| 273 | return applied |
| 274 | end |
Checks if fill type is allowedDefinition
getFillUnitAllowsFillType(integer fillUnitIndex, integer fillType)Arguments
| integer | fillUnitIndex | |
| integer | fillType |
| bool | true | if allowed |
| 290 | function UnloadTrigger:getFillUnitAllowsFillType(fillUnitIndex, fillType) |
| 291 | return self:getIsFillTypeAllowed(fillType) |
| 292 | end |
Checks if fillType is allowedDefinition
getIsFillTypeAllowed(integer fillType)Arguments
| integer | fillType |
| boolean | isAllowed | true if fillType is supported else false |
| 298 | function UnloadTrigger:getIsFillTypeAllowed(fillType) |
| 299 | return self:getIsFillTypeSupported(fillType) and self:getFillUnitFreeCapacity(1, fillType) > 0 |
| 300 | end |
Checks if fillType is supportedDefinition
getIsFillTypeSupported(integer fillType)Arguments
| integer | fillType |
| boolean | isSupported | true if fillType is supported else false |
| 306 | function UnloadTrigger:getIsFillTypeSupported(fillType) |
| 307 | local accepted = true |
| 308 | |
| 309 | if self.target ~= nil then |
| 310 | if not self.target:getIsFillTypeAllowed(fillType) then |
| 311 | accepted = false |
| 312 | end |
| 313 | else |
| 314 | if self.fillTypes ~= nil then |
| 315 | if not self.fillTypes[fillType] then |
| 316 | accepted = false |
| 317 | end |
| 318 | end |
| 319 | end |
| 320 | |
| 321 | if self.avoidFillTypes ~= nil then |
| 322 | if self.avoidFillTypes[fillType] then |
| 323 | accepted = false |
| 324 | end |
| 325 | end |
| 326 | |
| 327 | return accepted |
| 328 | end |
Returns the free capacityDefinition
getFillUnitFreeCapacity(integer fillUnitIndex, integer fillTypeIndex)Arguments
| integer | fillUnitIndex | fill unit index |
| integer | fillTypeIndex | fill type index |
| float | freeCapacity | free capacity |
| 343 | function UnloadTrigger:getFillUnitFreeCapacity(fillUnitIndex, fillTypeIndex, farmId) |
| 344 | if self.target.getFreeCapacity ~= nil then |
| 345 | return self.target:getFreeCapacity(fillTypeIndex, farmId) |
| 346 | end |
| 347 | return 0 |
| 348 | end |
Checks if toolType is allowedDefinition
getIsToolTypeAllowed(integer toolType)Arguments
| integer | toolType |
| boolean | isAllowed | true if toolType is allowed else false |
| 354 | function UnloadTrigger:getIsToolTypeAllowed(toolType) |
| 355 | local accepted = true |
| 356 | |
| 357 | if self.acceptedToolTypes ~= nil then |
| 358 | if self.acceptedToolTypes[toolType] ~= true then |
| 359 | accepted = false |
| 360 | end |
| 361 | end |
| 362 | |
| 363 | if accepted then |
| 364 | return self.target:getIsToolTypeAllowed(toolType) |
| 365 | else |
| 366 | return false |
| 367 | end |
| 368 | end |
Callback method for the bale triggerDefinition
baleTriggerCallback(integer triggerId, integer otherId, bool onEnter, bool onLeave, bool onStay, integer otherShapeId)Arguments
| integer | triggerId | |
| integer | otherId | |
| bool | onEnter | |
| bool | onLeave | |
| bool | onStay | |
| integer | otherShapeId |
| 378 | function UnloadTrigger:baleTriggerCallback(triggerId, otherId, onEnter, onLeave, onStay, otherShapeId) |
| 379 | if self.isEnabled then |
| 380 | local object = g_currentMission:getNodeObject(otherId) |
| 381 | if object ~= nil and object:isa(Bale) then |
| 382 | if onEnter then |
| 383 | local fillType = object:getFillType() |
| 384 | |
| 385 | if self.target:getIsFillTypeAllowed(fillType) and self.target:getIsToolTypeAllowed(ToolType.BALE) then |
| 386 | if self.target:getFreeCapacity(fillType) > 0 then |
| 387 | self:raiseActive() |
| 388 | table.insert(self.balesInTrigger, object) |
| 389 | end |
| 390 | end |
| 391 | |
| 392 | if self.fillTypes ~= nil and self.fillTypes[fillType] ~= true then |
| 393 | return |
| 394 | end |
| 395 | if self.avoidFillTypes ~= nil and self.avoidFillTypes[fillType] == true then |
| 396 | return |
| 397 | end |
| 398 | if self.acceptedToolTypes ~= nil and self.acceptedToolTypes[ToolType.BALE] ~= true then |
| 399 | return |
| 400 | end |
| 401 | |
| 402 | if self.target:getIsFillTypeAllowed(fillType) and self.target:getIsToolTypeAllowed(ToolType.BALE) then |
| 403 | self:raiseActive() |
| 404 | table.insert(self.balesInTrigger, object) |
| 405 | end |
| 406 | elseif onLeave then |
| 407 | for index,bale in ipairs(self.balesInTrigger) do |
| 408 | if bale == object then |
| 409 | table.remove(self.balesInTrigger, index) |
| 410 | break |
| 411 | end |
| 412 | end |
| 413 | end |
| 414 | end |
| 415 | end |
| 416 | end |
Class for weigh stations
On create weigh stationDefinition
onCreate(integer id)Arguments
| integer | id | id of weigh station node |
| 13 | function WeighStation:onCreate(id) |
| 14 | g_currentMission:addNonUpdateable(WeighStation:new(id)) |
| 15 | end |
Create new weigh station objectDefinition
new(integer trigger)Arguments
| integer | trigger | id id of trigger node |
| table | instance | instance of object |
| 21 | function WeighStation:new(triggerId) |
| 22 | local self = {} |
| 23 | setmetatable(self, WeighStation_mt) |
| 24 | |
| 25 | local nodeId = triggerId |
| 26 | self.triggerId = triggerId |
| 27 | addTrigger(triggerId, "triggerCallback", self) |
| 28 | |
| 29 | self.isEnabled = true |
| 30 | self.triggerVehicles = {} |
| 31 | |
| 32 | local weightDisplayIndex = getUserAttribute(nodeId, "weightDisplayIndex") |
| 33 | if weightDisplayIndex ~= nil then |
| 34 | self.displayNumbers = I3DUtil.indexToObject(nodeId, weightDisplayIndex) |
| 35 | end |
| 36 | |
| 37 | return self |
| 38 | end |
Delete weigh stationDefinition
delete()Code
| 42 | function WeighStation:delete() |
| 43 | if self.triggerId ~= nil then |
| 44 | removeTrigger(self.triggerId) |
| 45 | self.triggerId = nil |
| 46 | end |
| 47 | end |
Write new mass into the displayDefinition
updateDisplayNumbers(float mass)Arguments
| float | mass | mass |
| 52 | function WeighStation:updateDisplayNumbers(mass) |
| 53 | if self.displayNumbers ~= nil then |
| 54 | I3DUtil.setNumberShaderByValue(self.displayNumbers, math.floor(mass), 0) |
| 55 | end |
| 56 | end |
Get mass of vehicles in trigger and update displayDefinition
updateWeight()Code
| 60 | function WeighStation:updateWeight() |
| 61 | local mass = 0 |
| 62 | for vehicle, _ in pairs(self.triggerVehicles) do |
| 63 | mass = mass + vehicle:getTotalMass() |
| 64 | end |
| 65 | self:updateDisplayNumbers(mass*1000) |
| 66 | end |
Trigger callbackDefinition
triggerCallback(integer triggerId, integer otherId, boolean onEnter, boolean onLeave, boolean onStay)Arguments
| integer | triggerId | id of trigger |
| integer | otherId | id of actor |
| boolean | onEnter | on enter |
| boolean | onLeave | on leave |
| boolean | onStay | on stay |
| 75 | function WeighStation:triggerCallback(triggerId, otherId, onEnter, onLeave, onStay) |
| 76 | if self.isEnabled and (onEnter or onLeave) then |
| 77 | local vehicle = g_currentMission.nodeToObject[otherId] |
| 78 | if onEnter then |
| 79 | if vehicle ~= nil then |
| 80 | if self.triggerVehicles[vehicle] == nil then |
| 81 | self.triggerVehicles[vehicle] = 0 |
| 82 | end |
| 83 | self.triggerVehicles[vehicle] = self.triggerVehicles[vehicle] + 1 |
| 84 | end |
| 85 | else |
| 86 | if vehicle ~= nil then |
| 87 | self.triggerVehicles[vehicle] = self.triggerVehicles[vehicle] - 1 |
| 88 | if self.triggerVehicles[vehicle] == 0 then |
| 89 | self.triggerVehicles[vehicle] = nil |
| 90 | end |
| 91 | end |
| 92 | end |
| 93 | |
| 94 | self:updateWeight() |
| 95 | end |
| 96 | end |
Vehicle configuration util class
Add bought configurationDefinition
addBoughtConfiguration(string name, integer id)Arguments
| string | name | of bought configuration type |
| integer | id | id of bought configuration |
| 23 | function ConfigurationUtil.addBoughtConfiguration(object, name, id) |
| 24 | if g_configurationManager:getConfigurationIndexByName(name) ~= nil then |
| 25 | if object.boughtConfigurations[name] == nil then |
| 26 | object.boughtConfigurations[name] = {} |
| 27 | end |
| 28 | object.boughtConfigurations[name][id] = true |
| 29 | end |
| 30 | end |
Returns true if configuration has been boughtDefinition
hasBoughtConfiguration(string name, integer id)Arguments
| string | name | of bought configuration type |
| integer | id | id of bought configuration |
| boolean | configurationHasBeenBought | configuration has been bought |
| 37 | function ConfigurationUtil.hasBoughtConfiguration(object, name, id) |
| 38 | if object.boughtConfigurations[name] ~= nil and object.boughtConfigurations[name][id] then |
| 39 | return true |
| 40 | end |
| 41 | return false |
| 42 | end |
Set configuration valueDefinition
setConfiguration(string name, integer id)Arguments
| string | name | name of configuration type |
| integer | id | id of configuration value |
| 48 | function ConfigurationUtil.setConfiguration(object, name, id) |
| 49 | object.configurations[name] = id |
| 50 | end |
Returns color of config idDefinition
getColorByConfigId(string configName, integer configId)Arguments
| string | configName | name if config |
| integer | configId | id of config to get color |
| table | color | color (r, g, b) |
| 58 | function ConfigurationUtil.getColorByConfigId(object, configName, configId) |
| 59 | local configId = object.configurations[configName] |
| 60 | if configId ~= nil then |
| 61 | local item = g_storeManager:getItemByXMLFilename(object.configFileName) |
| 62 | local config = item.configurations[configName][configId] |
| 63 | if config ~= nil then |
| 64 | return config.color |
| 65 | end |
| 66 | end |
| 67 | |
| 68 | return nil |
| 69 | end |
Returns material of config idDefinition
getMaterialByConfigId(string configName, integer configId)Arguments
| string | configName | name if config |
| integer | configId | id of config to get color |
| integer | material | material |
| 76 | function ConfigurationUtil.getMaterialByConfigId(object, configName, configId) |
| 77 | local configId = object.configurations[configName] |
| 78 | if configId ~= nil then |
| 79 | local item = g_storeManager:getItemByXMLFilename(object.configFileName) |
| 80 | local config = item.configurations[configName][configId] |
| 81 | if config ~= nil then |
| 82 | return config.material |
| 83 | end |
| 84 | end |
| 85 | |
| 86 | return nil |
| 87 | end |
Apply design configDefinition
applyDesign(integer xmlFile, integer configDesignId)Arguments
| integer | xmlFile | id of xml object |
| integer | configDesignId | id of design to apply |
| 93 | function ConfigurationUtil.applyDesign(object, xmlFile, configDesignId) |
| 94 | local designKey = string.format("vehicle.designConfigurations.designConfiguration(%d)", configDesignId-1) |
| 95 | if not hasXMLProperty(xmlFile, designKey) then |
| 96 | print("Warning: Invalid design configuration " .. configDesignId); |
| 97 | return |
| 98 | end |
| 99 | local i = 0 |
| 100 | while true do |
| 101 | local materialKey = string.format(designKey..".material(%d)", i) |
| 102 | if not hasXMLProperty(xmlFile, materialKey) then |
| 103 | break |
| 104 | end |
| 105 | local baseMaterialNode = I3DUtil.indexToObject(object.components, getXMLString(xmlFile, materialKey.."#node"), object.i3dMappings); |
| 106 | local refMaterialNode = I3DUtil.indexToObject(object.components, getXMLString(xmlFile, materialKey.."#refNode"), object.i3dMappings); |
| 107 | if baseMaterialNode ~= nil and refMaterialNode ~= nil then |
| 108 | local oldMaterial = getMaterial(baseMaterialNode, 0) |
| 109 | local newMaterial = getMaterial(refMaterialNode, 0) |
| 110 | for _, component in pairs(object.components) do |
| 111 | ConfigurationUtil.replaceMaterialRec(object, component.node, oldMaterial, newMaterial); |
| 112 | end; |
| 113 | end |
| 114 | |
| 115 | local materialName = getXMLString(xmlFile, materialKey .. "#name") |
| 116 | if materialName ~= nil then |
| 117 | local shaderParameterName = getXMLString(xmlFile, materialKey .. "#shaderParameter") |
| 118 | if shaderParameterName ~= nil then |
| 119 | local colorStr = getXMLString(xmlFile, materialKey.."#color") |
| 120 | if colorStr ~= nil then |
| 121 | local color = g_brandColorManager:getBrandColorByName(colorStr) |
| 122 | if color == nil then |
| 123 | color = ConfigurationUtil.getColorFromString(colorStr) |
| 124 | end |
| 125 | if color ~= nil then |
| 126 | local materialId = getXMLInt(xmlFile, materialKey.."#materialId") |
| 127 | if object.setBaseMaterialColor ~= nil then |
| 128 | object:setBaseMaterialColor(materialName, shaderParameterName, color, materialId) |
| 129 | end |
| 130 | end |
| 131 | end |
| 132 | end |
| 133 | end |
| 134 | |
| 135 | i = i + 1 |
| 136 | end |
| 137 | ObjectChangeUtil.updateObjectChanges(xmlFile, "vehicle.designConfigurations.designConfiguration", configDesignId, object.components, object); |
| 138 | end |
Replace material of nodeDefinition
replaceMaterialRec(integer node, integer oldMaterial, integer newMaterial)Arguments
| integer | node | id of node |
| integer | oldMaterial | id of old material |
| integer | newMaterial | id of new material |
| 145 | function ConfigurationUtil.replaceMaterialRec(object, node, oldMaterial, newMaterial) |
| 146 | if getHasClassId(node, ClassIds.SHAPE) then |
| 147 | local nodeMaterial = getMaterial(node, 0); |
| 148 | if nodeMaterial == oldMaterial then |
| 149 | setMaterial(node, newMaterial, 0) |
| 150 | end |
| 151 | end; |
| 152 | |
| 153 | local numChildren = getNumOfChildren(node); |
| 154 | if numChildren > 0 then |
| 155 | for i=0, numChildren-1 do |
| 156 | ConfigurationUtil.replaceMaterialRec(object, getChildAt(node, i), oldMaterial, newMaterial) |
| 157 | end; |
| 158 | end; |
| 159 | end |
Sets color of vehicleDefinition
setColor(integer xmlFile, string configName, integer configColorId)Arguments
| integer | xmlFile | id of xml object |
| string | configName | name of config |
| integer | configColorId | id of config color to use |
| 166 | function ConfigurationUtil.setColor(object, xmlFile, configName, configColorId) |
| 167 | local color = ConfigurationUtil.getColorByConfigId(object, configName, configColorId) |
| 168 | if color ~= nil then |
| 169 | local r,g,b,a = unpack(color); |
| 170 | local i = 0; |
| 171 | while true do |
| 172 | local colorKey = string.format("vehicle.%sConfigurations.colorNode(%d)", configName, i) |
| 173 | if not hasXMLProperty(xmlFile, colorKey) then |
| 174 | break; |
| 175 | end |
| 176 | |
| 177 | local node = I3DUtil.indexToObject(object.components, getXMLString(xmlFile, colorKey .. "#node"), object.i3dMappings); |
| 178 | if node ~= nil then |
| 179 | if getHasClassId(node, ClassIds.SHAPE) then |
| 180 | if Utils.getNoNil(getXMLBool(xmlFile, colorKey .. "#recursive"), false) then |
| 181 | I3DUtil.setShaderParameterRec(node, "colorScale", r, g, b, a); |
| 182 | else |
| 183 | setShaderParameter(node, "colorScale", r, g, b, a, false); |
| 184 | end |
| 185 | else |
| 186 | print("Warning: Could not set vehicle color to '"..getName(node).."' because node is not a shape!") |
| 187 | end |
| 188 | end |
| 189 | i = i + 1; |
| 190 | end |
| 191 | end |
| 192 | end |
Get value of configurationDefinition
getConfigurationValue(integer xmlFile, string key, string subKey, string param, function xmlFunc, any_type defaultValue, string fallbackConfigKey, string fallbackOldgKey)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| string | subKey | sub key |
| string | param | parameter |
| function | xmlFunc | xml function |
| any_type | defaultValue | default value |
| string | fallbackConfigKey | fallback config key |
| string | fallbackOldgKey | fallback old key |
| any_type | value | value of config |
| 205 | function ConfigurationUtil.getConfigurationValue(xmlFile, key, subKey, param, xmlFunc, defaultValue, fallbackConfigKey, fallbackOldKey) |
| 206 | if type(subKey) == "table" then |
| 207 | printCallstack(); |
| 208 | end |
| 209 | local value = nil; |
| 210 | if key ~= nil then |
| 211 | value = xmlFunc(xmlFile, key..subKey..param); |
| 212 | end; |
| 213 | |
| 214 | if value == nil and fallbackConfigKey ~= nil then |
| 215 | value = xmlFunc(xmlFile, fallbackConfigKey..subKey..param); -- Check for default configuration (xml index 0) |
| 216 | end; |
| 217 | if value == nil and fallbackOldKey ~= nil then |
| 218 | value = xmlFunc(xmlFile, fallbackOldKey..subKey..param); -- Fallback to old xml setup |
| 219 | end; |
| 220 | return Utils.getNoNil(value, defaultValue); -- using default value |
| 221 | end; |
Get xml configuration keyDefinition
getXMLConfigurationKey(integer xmlFile, integer index, string key, string defaultKey, string configurationKey)Arguments
| integer | xmlFile | id of xml object |
| integer | index | index |
| string | key | key |
| string | defaultKey | default key |
| string | configurationKey | configuration key |
| string | configKey | key of configuration |
| integer | configIndex | index of configuration |
| 232 | function ConfigurationUtil.getXMLConfigurationKey(xmlFile, index, key, defaultKey, configurationKey) |
| 233 | local configIndex = Utils.getNoNil(index, 1); |
| 234 | local configKey = string.format(key.."(%d)", configIndex-1); |
| 235 | if index ~= nil and not hasXMLProperty(xmlFile, configKey) then |
| 236 | print("Warning: Invalid "..configurationKey.." index '"..tostring(index).."' in '"..key.."'. Using default "..configurationKey.." settings instead!"); |
| 237 | end; |
| 238 | |
| 239 | if not hasXMLProperty(xmlFile, configKey) then |
| 240 | configKey = key.."(0)"; |
| 241 | end; |
| 242 | if not hasXMLProperty(xmlFile, configKey) then |
| 243 | configKey = defaultKey; |
| 244 | end; |
| 245 | |
| 246 | return configKey, configIndex; |
| 247 | end; |
Get config color single item loadDefinition
getConfigColorSingleItemLoad(integer xmlFile, string baseXMLName, string baseDir, string customEnvironment, boolean isMod, table configItem)Arguments
| integer | xmlFile | id of xml object |
| string | baseXMLName | base xml name |
| string | baseDir | base directory |
| string | customEnvironment | custom environment |
| boolean | isMod | is mod |
| table | configItem | config item |
| 257 | function ConfigurationUtil.getConfigColorSingleItemLoad(xmlFile, baseXMLName, baseDir, customEnvironment, isMod, configItem) |
| 258 | |
| 259 | local colorStr = Utils.getNoNil(getXMLString(xmlFile, baseXMLName.."#color"), "1 1 1 1") |
| 260 | local color = g_brandColorManager:getBrandColorByName(colorStr) |
| 261 | if color == nil then |
| 262 | color = ConfigurationUtil.getColorFromString(colorStr) |
| 263 | end |
| 264 | |
| 265 | configItem.color = color |
| 266 | configItem.material = getXMLInt(xmlFile, baseXMLName.."#material") |
| 267 | |
| 268 | configItem.name = XMLUtil.getXMLI18NValue(xmlFile, baseXMLName.."#name", getXMLString, "", "", customEnvironment, false) |
| 269 | end |
Get config color post loadDefinition
getConfigColorPostLoad(integer xmlFile, string baseKey, string baseDir, string customEnvironment, boolean isMod, table configurationItems)Arguments
| integer | xmlFile | id of xml object |
| string | baseKey | base key |
| string | baseDir | base directory |
| string | customEnvironment | custom environment |
| boolean | isMod | is mod |
| table | configurationItems | config items |
| 279 | function ConfigurationUtil.getConfigColorPostLoad(xmlFile, baseKey, baseDir, customEnvironment, isMod, configurationItems, storeItem) |
| 280 | local defaultColorIndex = getXMLInt(xmlFile, baseKey.."#defaultColorIndex") |
| 281 | |
| 282 | if Utils.getNoNil(getXMLBool(xmlFile, baseKey.."#useDefaultColors"), false) then |
| 283 | local price = Utils.getNoNil(getXMLInt(xmlFile, baseKey.."#price"), 1000); |
| 284 | |
| 285 | for i, color in pairs(g_vehicleColors) do |
| 286 | local configItem = StoreItemUtil.addConfigurationItem(configurationItems, "", "", price, 0, false) |
| 287 | if color.r ~= nil and color.g ~= nil and color.b ~= nil then |
| 288 | configItem.color = {color.r, color.g, color.b, 1} |
| 289 | elseif color.brandColor ~= nil then |
| 290 | configItem.color = g_brandColorManager:getBrandColorByName(color.brandColor) |
| 291 | |
| 292 | if configItem.color == nil then |
| 293 | configItem.color = {1, 1, 1, 1} |
| 294 | g_logManager:warning("Unable to find brandColor '%s' in g_vehicleColors", color.brandColor) |
| 295 | end |
| 296 | end |
| 297 | |
| 298 | configItem.name = g_i18n:convertText(color.name) |
| 299 | |
| 300 | if i == defaultColorIndex then |
| 301 | configItem.isDefault = true |
| 302 | configItem.price = 0 |
| 303 | end |
| 304 | end |
| 305 | end; |
| 306 | |
| 307 | if defaultColorIndex == nil then |
| 308 | local defaultIsDefined = false |
| 309 | for _, item in ipairs(configurationItems) do |
| 310 | if item.isDefault ~= nil and item.isDefault then |
| 311 | defaultIsDefined = true |
| 312 | end |
| 313 | end |
| 314 | |
| 315 | if not defaultIsDefined then |
| 316 | if #configurationItems > 0 then |
| 317 | configurationItems[1].isDefault = true |
| 318 | configurationItems[1].price = 0 |
| 319 | end |
| 320 | end |
| 321 | end |
| 322 | end; |
Get store additional config dataDefinition
getStoreAddtionalConfigData(integer xmlFile, string baseXMLName, string baseDir, string customEnvironment, boolean isMod, table configItem)Arguments
| integer | xmlFile | id of xml object |
| string | baseXMLName | base xml name |
| string | baseDir | base directory |
| string | customEnvironment | custom environment |
| boolean | isMod | is mod |
| table | configItem | config item |
| 338 | function ConfigurationUtil.getStoreAddtionalConfigData(xmlFile, baseXMLName, baseDir, customEnvironment, isMod, configItem) |
| 339 | configItem.vehicleType = getXMLString(xmlFile, baseXMLName.."#vehicleType") |
| 340 | end |
Get color from stringDefinition
getColorFromString(string colorString)Arguments
| string | colorString | color rgba string or brand color identifier |
| table | color | color (r, g, b) |
| 346 | function ConfigurationUtil.getColorFromString(colorString) |
| 347 | if colorString ~= nil then |
| 348 | local colorVector = g_brandColorManager:getBrandColorByName(colorString) or {StringUtil.getVectorFromString(colorString)} |
| 349 | |
| 350 | if colorVector == nil or #colorVector < 3 or #colorVector > 4 then |
| 351 | print("Error: Invalid color string '" .. colorString .. "'") |
| 352 | return nil |
| 353 | end |
| 354 | return colorVector; |
| 355 | end |
| 356 | return nil; |
| 357 | end |
DebugUtil
Draws a debug node and optional a text at world position of given nodeDefinition
drawDebugNode(integer id, string text)Arguments
| integer | id | node id |
| string | text | text |
Draw debug circleDefinition
drawDebugCircle(float x, float y, float z, float radius, integer steps)Arguments
| float | x | world x position |
| float | y | world y position |
| float | z | world z position |
| float | radius | radius |
| integer | steps | steps |
Draw debug cube at world positionDefinition
drawDebugCubeAtWorldPos(float x, float y, float z, float dirX, float dirY, float dirZ, float upX, float upY, float upZ, float sizeX, float sizeY, float sizeZ, float r, float g, float b)Arguments
| float | x | world x center position |
| float | y | world y center position |
| float | z | world z center position |
| float | dirX | x direction |
| float | dirY | y direction |
| float | dirZ | z direction |
| float | upX | x up of vector |
| float | upY | y up of vector |
| float | upZ | z up of vector |
| float | sizeX | x size |
| float | sizeY | y size |
| float | sizeZ | z size |
| float | r | red value |
| float | g | green value |
| float | b | blue value |
Draw debug cube at given nodeDefinition
drawDebugCube(integer id, float sizeX, float sizeY, float sizeZ, float r, float g, float b)Arguments
| integer | id | node id |
| float | sizeX | x size |
| float | sizeY | y size |
| float | sizeZ | z size |
| float | r | red value |
| float | g | green value |
| float | b | blue value |
Draw debug cubeDefinition
drawSimpleDebugCube(float x, float y, float z, float width, float red, float green, float blue)Arguments
| float | x | world x center position |
| float | y | world y center position |
| float | z | world z center position |
| float | width | |
| float | red | |
| float | green | |
| float | blue |
Draw debug reference axis fom NodeDefinition
drawDebugReferenceAxisFromNode(node to)Arguments
| node | to | draw a reference axis from |
Draw debug reference axisDefinition
drawDebugReferenceAxis(float x, float y, float z, float up, float up, float up, float direction, float direction, float direction)Arguments
| float | x | world x center position |
| float | y | world y center position |
| float | z | world z center position |
| float | up | x |
| float | up | y |
| float | up | z |
| float | direction | x |
| float | direction | y |
| float | direction | z |
Draw debug parallelogramDefinition
drawDebugParallelogram(float x, float z, float widthX, float widthZ, float heightX, float heightZ, float heightOffset, float r, float g, float b, float a)Arguments
| float | x | world x center position |
| float | z | world z center position |
| float | widthX | widthX |
| float | widthZ | widthZ |
| float | heightX | heightX |
| float | heightZ | heightZ |
| float | heightOffset | heightOffset |
| float | r | red |
| float | g | green |
| float | b | blue |
| float | a | alpha |
Print a table recursivelyDefinition
printTableRecursively(table inputTable, string inputIndent, integer depth, integer maxDepth)Arguments
| table | inputTable | table to print |
| string | inputIndent | input indent |
| integer | depth | current depth |
| integer | maxDepth | max depth |
Print the script call location of a function call which uses this function.Definition
printCallingFunctionLocation()
FSDensityMapUtil
Cut fruit areaDefinition
cutFruitArea(integer fruitId, float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, boolean destroySpray, boolean destroySeedingWidth, boolean useMinForageState)Arguments
| integer | fruitId | fruit id |
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| boolean | destroySpray | destroy spray |
| boolean | destroySeedingWidth | destroy seeding width |
| boolean | useMinForageState | use min forange state |
| integer | harvestPixelsSum | harvest of pixels sum |
| integer | harvestNumPixels | harvest number of pixels |
| float | sprayFactor | spray factor |
| float | plowFactor | plow factor |
| float | limeFactor | lime factor |
| float | weedFactor | weed factor |
| integer | growthState | growth state |
| float | maxArea | max area |
Get fruit areaDefinition
getFruitArea(integer fruitId, float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, boolean allowPreparing, boolean useMinForageState)Arguments
| integer | fruitId | fruit id |
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| boolean | allowPreparing | allow preparing |
| boolean | useMinForageState | use min forage state |
| integer | ret | ret |
| integer | total | total |
Update roller areaDefinition
updateRollerArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| integer | changedValue | changed value |
Update cultivator areaDefinition
updateCultivatorArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, boolean createField, boolean commonForced, float angle, integer blockedSprayType)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| boolean | createField | createField |
| boolean | commonForced | common createField |
| float | angle | angle |
| integer | blockedSprayType | blockedSprayType |
| integer | changedArea | changed area pixels |
| integer | totalArea | total area |
Update plow areaDefinition
updatePlowArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, boolean forced, boolean commonForced, float angle)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| boolean | forced | forced |
| boolean | commonForced | common forced |
| float | angle | angle |
| integer | changedArea | changed area pixels |
| integer | totalArea | total area pixles |
Update destroy common areaDefinition
updateDestroyCommonArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, boolean limitToField)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| boolean | limitToField | limit to field |
Update spray areaDefinition
updateSprayArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, integer sprayTypeIndex)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| integer | sprayTypeIndex | spray type index |
| integer | numPixels | number of pixels |
| integer | totalNumPixels | total number of pixels |
Update herbicide areaDefinition
updateHerbicideArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| integer | numPixels | number of pixels |
| integer | totalNumPixels | total number of pixels |
Update weeder areaDefinition
updateWeederArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| integer | numPixels | number of pixels |
Remove weed areaDefinition
removeWeedArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, integer maxGrowthState)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| integer | maxGrowthState | max growth state of the weed |
Set weed areaDefinition
setWeedArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, integer value)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| integer | value | state of the weed |
Reset spray areaDefinition
resetSprayArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, boolean force)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| boolean | force | force |
Update sowing areaDefinition
updateSowingArea(integer fruitId, float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, float angle, integer growthState)Arguments
| integer | fruitId | fruit id |
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| float | angle | angle |
| integer | growthState | fruit growth state value |
| integer | changedArea | changed area pixels |
| integer | totalArea | total area pixels |
Update direct sowing areaDefinition
updateDirectSowingArea(integer fruitId, float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, float angle, integer growthState)Arguments
| integer | fruitId | fruit id |
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| float | angle | angle |
| integer | growthState | fruit growth state value |
| integer | changedArea | changed area pixels |
| integer | totalArea | total area pixels |
Update fruit preparer areaDefinition
updateFruitPreparerArea(integer fruitId, float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, float startDropWorldX, float startDropWorldZ, float widthDropWorldX, float widthDropWorldZ, float heightDropWorldX, float heightDropWorldZ)Arguments
| integer | fruitId | fruit id |
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
| float | startDropWorldX | start drop world X |
| float | startDropWorldZ | start drop world Z |
| float | widthDropWorldX | width drop world X |
| float | widthDropWorldZ | width drop world Z |
| float | heightDropWorldX | height drop world X |
| float | heightDropWorldZ | height drop world Z |
| integer | numChangedPixels | number of changed pixels |
Erase tire track on given parallelogramDefinition
eraseTireTrack(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ)Arguments
| float | startWorldX | start world X |
| float | startWorldZ | start world Z |
| float | widthWorldX | width world X |
| float | widthWorldZ | width world Z |
| float | heightWorldX | height world X |
| float | heightWorldZ | height world Z |
Returns tire track color from given density bitsDefinition
getTireTrackColorFromDensityBits(integer densityBits)Arguments
| integer | densityBits | density bits |
| table | color | tire track color |
Creates an instance of SimpleStateMachine; consumer is responsible for deletion of instance.Definition
create()Code
| 15 | function FSMUtil.create() |
| 16 | local fsm = SimpleStateMachine:new() |
| 17 | return fsm |
| 18 | end |
Class handling global settings. Global instance is g_gameSettings
Create a new instanceDefinition
new(table customMt, table messageCenter)Arguments
| table | customMt | Sub-class meta table |
| table | messageCenter | MessageCenter reference for settings change notifications |
Returns the setting value with the given nameDefinition
Settings in the game:
-maxNumMirrors
-lightsProfile
-realBeaconLights
-motorStopTimerDuration
-uiScale
-fovY
-isTrainTabbable
-radioVehicleOnly
-radioIsActive
-useColorblindMode
-easyArmControl
-useMiles
-showTriggerMarker
-resetCamera
-useWorldCamera
-invertYLook
-showHelpIcons
-showHelpMenu
-radioVolume
-vehicleVolume
-environmentVolume
-cameraSensitivity
-vehicleArmSensitivity
-ingameMapState
-ingameMapFilter
-moneyUnit
-masterVolume
-musicVolume
getValue(string name)Arguments
| string | name | name of the setting |
| mixed | value | Value of the setting. The type depends on the setting |
Changed the setting value with the given nameDefinition
setValue(string name, mixed value, boolean doSave)Arguments
| string | name | name of the setting |
| mixed | value | value to set the setting to |
| boolean | doSave | If true, the settings are saved persistently, otherwise it will only be saved when another call triggers it |
| boolean | successful | Returns true, if the setting was changed |
Returns a html encoded stringDefinition
encodeToHTML(string str, boolean inCData)Arguments
| string | str | input string |
| boolean | inCData | true if text is in cdata element |
| string | encodedString | the encoded string |
Returns a html decoded stringDefinition
decodeFromHTML(string str)Arguments
| string | str | input string |
| string | decodedString | the decoded string |
Returns a copy of the given table. It's not a deep copy!Definition
copyTable(table sourceTable)Arguments
| table | sourceTable | the source table |
| table | copy | the copied table |
Returns a full copy of the given table, including all child tablesDefinition
copyTableRecursively(table sourceTable)Arguments
| table | sourceTable | the source table |
| table | copy | the copied table |
Adds an element to the list if element is not part of the listDefinition
addElementToList(table list, table element)Arguments
| table | list | a list |
| table | element | an element |
Removes an element from a given listDefinition
removeElementFromList(table list, table element)Arguments
| table | list | a list |
| table | element | an element |
Checks if list contains an elementDefinition
hasListElement(table list, table element)Arguments
| table | list | a list |
| table | element | an element |
| boolean | isElementOfList | true if element is part of the list, else false |
Gets the index of the first occurrenceDefinition
findListElementFirstIndex(table list, table element, object defaultReturn)Arguments
| table | list | a list |
| table | element | an element |
| object | defaultReturn | a default return value |
| integer | index | index of first occurrence |
Checks if two lists are equalDefinition
areListsEqual(table list1, table list2, boolean orderIndependent)Arguments
| table | list1 | list one |
| table | list2 | list two |
| boolean | orderIndependent | true the order of the elements has to be the same |
| boolean | areEqual | true if lists are equal, else false |
Gets a random element from given listDefinition
getRandomElement(table list)Arguments
| table | list | list to get element from |
| any | value | value of the random element |
Converts a list to a setDefinition
listToSet(table list)Arguments
| table | list | a list |
| table | set | the converted set |
Converts a set to a listDefinition
setToList(table set)Arguments
| table | set | a set |
| table | list | the converted list |
Converts a set to a hashDefinition
setToHash(table set)Arguments
| table | set | a set |
| table | hash | the converted hash |
Checks if two sets are equalDefinition
areSetsEqual(table set1, table set2)Arguments
| table | set1 | set one |
| table | set2 | set two |
| boolean | areEqual | true if sets are equal, else false |
Checks if set1 is a subset of set2Definition
isSubset(table set1, table set2)Arguments
| table | set1 | set one |
| table | set2 | set two |
| boolean | isSubset | true if set1 is a subset of set2 |
Checks if set1 is a real subset of set2Definition
isRealSubset(table set1, table set2)Arguments
| table | set1 | set one |
| table | set2 | set two |
| boolean | isSubset | true if set1 is a real subset of set2 |
Checks if set1 and set2 have an intersectionDefinition
hasSetIntersection(table set1, table set2)Arguments
| table | set1 | set one |
| table | set2 | set two |
| boolean | hasIntersection | true if set1 and set2 have an intersection |
Gets the intersection of two setsDefinition
getSetIntersection(table set1, table set2)Arguments
| table | set1 | set one |
| table | set2 | set two |
| table | intersection | the intersection of both sets |
Gets the substraction of two setsDefinition
getSetSubtraction(table set1, table set2)Arguments
| table | set1 | set one |
| table | set2 | set two |
| table | substraction | the substraction of both set |
Gets the union of two setsDefinition
getSetUnion(table set1, table set2)Arguments
| table | set1 | set one |
| table | set2 | set two |
| table | union | the union of both sets |
Filters a list. Returns a copy (see functional programming)Definition
filter(table list, function closure)Arguments
| table | list | list to filter |
| function | closure | filter |
| table | filtered | list (non-deep copy) |
Filters a list. Returns a copy (see functional programming). Indexed version.Definition
ifilter(table list, function closure)Arguments
| table | list | indexed list to filter |
| function | closure | filter |
| table | filtered | list (non-deep copy) |
Returns the sign of the given valueDefinition
sign(float x)Arguments
| float | x | a value |
| integer | sign | the sign of the value |
Returns of the given value is nanDefinition
isNan(float value)Arguments
| float | value | a value |
| boolean | true | if value is nan else false |
Returns the rounded valueDefinition
round(float value, float precision)Arguments
| float | value | a value |
| float | precision | a precision |
| number | rounted | value |
Returns the radian value of a given angle in degressDefinition
degToRad(float degValue)Arguments
| float | degValue | angle in degrees |
| float | value | radian angle |
Returns interpolated value between two given valuesDefinition
lerp(float v1, float v2, float alpha)Arguments
| float | v1 | start value |
| float | v2 | end value |
| float | alpha | alpha |
| float | value | interpolated value |
Returns interpolated value between two vectorsDefinition
lerp3(float x1, float y1, float z1, float x2, float y2, float z2, float alpha)Arguments
| float | x1 | start x value |
| float | y1 | start y value |
| float | z1 | start z value |
| float | x2 | end x value |
| float | y2 | end y value |
| float | z2 | end z value |
| float | alpha | alpha |
| float | value | interpolated value |
Returns alpha based on current valueDefinition
inverseLerp(float v1, float v2, float cv)Arguments
| float | v1 | start value |
| float | v2 | end value |
| float | cv | current value |
| float | alpha | alpha |
Returns value between given min and maxDefinition
clamp(float value, float minVal, float maxVal)Arguments
| float | value | to clamp |
| float | minVal | min value |
| float | maxVal | max value |
| float | value | value |
Returns true if the value is out of the given rangeDefinition
getIsOutOfBounds(float value, float limit1, float limit2)Arguments
| float | value | value |
| float | limit1 | limit 1 |
| float | limit2 | limit 2 |
| float | isOutOfBounds | is out of bounds |
Returns the floored percentDefinition
getFlooredPercent(float value, float maxValue)Arguments
| float | value | a value |
| float | maxValue | max value |
| float | value | the floored percent value |
Returns the floored clamped valueDefinition
getFlooredBounded(float value, float minValue, float maxValue)Arguments
| float | value | a value |
| float | minValue | min value |
| float | maxValue | max value |
| float | value | the floored clamped value |
Returns a valid limit in the range of -pi to piDefinition
getValidLimit(float limit)Arguments
| float | limit | a radian angle |
| float | angle | the resized angle in the range -pi to pi |
Returns the difference between two rad anglesDefinition
getAngleDifference(float alpha, float beta)Arguments
| float | alpha | a radian angle |
| float | beta | a radian angle |
| float | angle | the radian difference in range -pi to pi |
Returns length of 2d vectorDefinition
vector2Length(float x, float y)Arguments
| float | x | x |
| float | y | y |
| float | length | length |
Returns squared length of 2d vectorDefinition
vector2LengthSq(float x, float y)Arguments
| float | x | x |
| float | y | y |
| float | length | square length |
Returns normalized vectorDefinition
vector2Normalize(float x, float y)Arguments
| float | x | x |
| float | y | y |
| float | x | normalized x |
| float | y | normalized y |
Returns length of 3d vectorDefinition
vector3Length(float x, float y, float z)Arguments
| float | x | x |
| float | y | y |
| float | z | z |
| float | length | length |
Returns squared length of 3d vectorDefinition
vector3LengthSq(float x, float y, float z)Arguments
| float | x | x |
| float | y | y |
| float | z | z |
| float | length | square length |
Returns normalized vectorDefinition
vector3Normalize(float x, float y, float z)Arguments
| float | x | x |
| float | y | y |
| float | z | z |
| float | x | normalized x |
| float | y | normalized y |
| float | z | normalized z |
Returns a scaled vectorDefinition
vector3SetLength(float x, float y, float z, float length)Arguments
| float | x | x |
| float | y | y |
| float | z | z |
| float | length | scale length |
| float | x | scaled x |
| float | y | scaled y |
| float | z | scaled z |
Returns a clamped vector based on min and max vector lengthDefinition
vector3Clamp(float x, float y, float z, float minVal, float maxVal)Arguments
| float | x | x |
| float | y | y |
| float | z | z |
| float | minVal | min length |
| float | maxVal | max length |
| float | x | clamped x |
| float | y | clamped y |
| float | z | clamped z |
Returns a linear interpolated vector based on given alphaDefinition
vector3Lerp(float x1, float y1, float z1, float x2, float y2, float z2, float alpha)Arguments
| float | x1 | x1 |
| float | y1 | y1 |
| float | z1 | z1 |
| float | x2 | x2 |
| float | y2 | y2 |
| float | z2 | z2 |
| float | alpha | alpha value |
| float | x | interpolated x |
| float | y | interpolated y |
| float | z | interpolated z |
Returns a linear interpolated vector based on given alphaDefinition
vector3ArrayLerp(table v1, table v2, float alpha)Arguments
| table | v1 | vector1 |
| table | v2 | vector2 |
| float | alpha | alpha value |
| float | x | interpolated x |
| float | y | interpolated y |
| float | z | interpolated z |
Transform a vector by matrix multiplication, the matrix is given row by rowDefinition
vector3Transformation(float x, float y, float z, float m11, float m12, float m13, float m21, float m22, float m23, float m31, float m32, float m33)Arguments
| float | x | x |
| float | y | y |
| float | z | z |
| float | m11 | m11 |
| float | m12 | m12 |
| float | m13 | m13 |
| float | m21 | m21 |
| float | m22 | m22 |
| float | m23 | m23 |
| float | m31 | m31 |
| float | m32 | m32 |
| float | m33 | m33 |
| float | x | transformed x |
| float | y | transformed y |
| float | z | transformed z |
Returns the dot product of 2 vectorsDefinition
dotProduct(float ax, float ay, float az, float bx, float by, float bz)Arguments
| float | ax | ax |
| float | ay | ay |
| float | az | az |
| float | bx | bx |
| float | by | by |
| float | bz | bz |
| dot | the | dot product |
Returns the cross product of 2 vectorsDefinition
crossProduct(float ax, float ay, float az, float bx, float by, float bz)Arguments
| float | ax | ax |
| float | ay | ay |
| float | az | az |
| float | bx | bx |
| float | by | by |
| float | bz | bz |
| float | x | x |
| float | y | y |
| float | z | z |
Returns the angle to rotate from the z axis around the y axis (if x==0, the angle is 0 or 180°). This is unlike the default specification, where the rotation is 0 at the x axisDefinition
getYRotationFromDirection(float dx, float dz)Arguments
| float | dx | dx |
| float | dz | dz |
| float | y | rotation |
Returns the x and z direction based on the given y rotationDefinition
getDirectionFromYRotation(float rotY)Arguments
| float | rotY | y rotation |
| float | x | x direction |
| float | z | z direction |
Returns an 2d vector limited by min and max rotationDefinition
getRotationLimitedVector2(float x, float y, float minRot, float maxRot)Arguments
| float | x | x direction |
| float | y | y direction |
| float | minRot | min rot |
| float | maxRot | max rot |
| float | x | limited x direction |
| float | z | limited z direction |
Returns the projected point on a given lineDefinition
projectOnLine(float px, float pz, float lineX, float lineX, float normlineDirX, float normlineDirZ)Arguments
| float | px | x position |
| float | pz | z position |
| float | lineX | x position |
| float | lineX | x position |
| float | normlineDirX | normalized x direction |
| float | normlineDirZ | normalized z direction |
| float | x | x position |
| float | x | z position |
Returns the dot product on a given lineDefinition
getProjectOnLineParameter(float px, float pz, float lineX, float lineZ, float normlineDirX, float normlineDirZ)Arguments
| float | px | x position |
| float | pz | z position |
| float | lineX | x position |
| float | lineZ | x position |
| float | normlineDirX | normalized x direction |
| float | normlineDirZ | normalized z direction |
| float | dot | product |
Returns the multiplication of two quaternionsDefinition
quaternionMult(float x, float y, float z, float w, float x1, float y1, float z1, float w1)Arguments
| float | x | x1 |
| float | y | y1 |
| float | z | z1 |
| float | w | w1 |
| float | x1 | x2 |
| float | y1 | y2 |
| float | z1 | z2 |
| float | w1 | w2 |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Returns the normalized quaternionDefinition
quaternionNormalized(float x, float y, float z, float w)Arguments
| float | x | x |
| float | y | y |
| float | z | z |
| float | w | w |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Returns the linear interpolated quaternionDefinition
slerpQuaternion(float x1, float y1, float z1, float w1, float x2, float y2, float z2, float w2, float t)Arguments
| float | x1 | x1 |
| float | y1 | y1 |
| float | z1 | z1 |
| float | w1 | w1 |
| float | x2 | x2 |
| float | y2 | y2 |
| float | z2 | z2 |
| float | w2 | w2 |
| float | t | interpolation value |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Returns normalized rotation for the shortest path from current rotation to target rotationDefinition
normalizeRotationForShortestPath(float targetRotation, float curRotation)Arguments
| float | targetRotation | the target rotation |
| float | curRotation | the current rotation |
| float | rotation | the final rotation |
Returns the normalized shortest path from current quaternion to target quaternionDefinition
nlerpQuaternionShortestPath(float x1, float y1, float z1, float w1, float x2, float y2, float z2, float w2, float t)Arguments
| float | x1 | x1 |
| float | y1 | y1 |
| float | z1 | z1 |
| float | w1 | w1 |
| float | x2 | x2 |
| float | y2 | y2 |
| float | z2 | z2 |
| float | w2 | w2 |
| float | t | alpha |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Returns the shortest path from current quaternion to target quaternionDefinition
slerpQuaternionShortestPath(float x1, float y1, float z1, float w1, float x2, float y2, float z2, float w2, float t)Arguments
| float | x1 | x1 |
| float | y1 | y1 |
| float | z1 | z1 |
| float | w1 | w1 |
| float | x2 | x2 |
| float | y2 | y2 |
| float | z2 | z2 |
| float | w2 | w2 |
| float | t | alpha |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Returns the shortest path from current quaternion to target quaternion (mad = multiply and add)Definition
quaternionMadShortestPath(float x, float y, float z, float w, float x1, float y1, float z1, float w1, float t)Arguments
| float | x | x1 |
| float | y | y1 |
| float | z | z1 |
| float | w | w1 |
| float | x1 | x2 |
| float | y1 | y2 |
| float | z1 | z2 |
| float | w1 | w2 |
| float | t | alpha |
| float | x | x part of quaternion |
| float | y | y part of quaternion |
| float | z | z part of quaternion |
| float | w | w part of quaternion |
Returns the distance to a rectangleDefinition
getDistanceToRectangle2D(float posX, float posZ, float sx, float sz, float dx, float dz, float length, float widthHalf)Arguments
| float | posX | x position |
| float | posZ | z position |
| float | sx | x position of rectangle |
| float | sz | z position of rectangle |
| float | dx | x direction of rectangle |
| float | dz | z direction of rectangle |
| float | length | length of rectangle |
| float | widthHalf | half width of rectangle |
| float | distance | distance to rectangle |
Returns the distance to a line segmentDefinition
getSignedDistanceToLineSegment2D(float x, float z, float sx, float sz, float dx, float dz, float length)Arguments
| float | x | x position |
| float | z | z position |
| float | sx | x position of rectangle |
| float | sz | z position of rectangle |
| float | dx | x direction of rectangle |
| float | dz | z direction of rectangle |
| float | length | length of rectangle |
| float | distance | distance to line segment |
Returns the line-line intersection pointDefinition
getLineLineIntersection2D(float x1, float z1, float dirX1, float dirZ1, float x2, float z2, float dirX2, float dirZ2)Arguments
| float | x1 | x1 position |
| float | z1 | z1 position |
| float | dirX1 | line1 x direction |
| float | dirZ1 | line1 z direction |
| float | x2 | x2 position |
| float | z2 | z2 position |
| float | dirX2 | line2 x direction |
| float | dirZ2 | line2 z direction |
| boolean | hasIntersection | true if both lines intersect |
| float | t1 | x position |
| float | t2 | z position |
Returns if rectangle and line have intersectionDefinition
hasRectangleLineIntersection2D(float x1, float z1, float dirX1, float dirZ1, float dirX2, float dirZ2, float x3, float z3, float dirX3, float dirZ3)Arguments
| float | x1 | x1 position |
| float | z1 | z1 position |
| float | dirX1 | rectangle x1 direction |
| float | dirZ1 | rectangle z1 direction |
| float | dirX2 | rectangle x2 direction |
| float | dirZ2 | rectangle z2 direction |
| float | x3 | line x position |
| float | z3 | line z position |
| float | dirX3 | line x direction |
| float | dirZ3 | line z direction |
| boolean | hasIntersection | true if the line segment is completly in the rectangle OR if it intersects with one of the four rectangle sides |
Returns circle-circle intersection pointsDefinition
getCircleCircleIntersection(float x1, float y1, float r1, float x2, float y2, float r2)Arguments
| float | x1 | x1 position |
| float | y1 | y1 position |
| float | r1 | radius 1 |
| float | x2 | x2 position |
| float | y2 | y2 position |
| float | r2 | radius 2 |
| float | pos1X | x pos of intersection 1, else nil |
| float | pos1Y | y pos of intersection 1, else nil |
| float | pos2X | x pos of intersection 2, else nil |
| float | pos2Z | z pos of intersection 2, else nil |
Returns if 2 spheres have an intersectionDefinition
hasSphereSphereIntersection(float x1, float y1, float z1, float r1, float x2, float y2, float z2, float r2)Arguments
| float | x1 | x1 position |
| float | y1 | y1 position |
| float | z1 | z1 position |
| float | r1 | radius 1 |
| float | x2 | x2 position |
| float | y2 | y2 position |
| float | z2 | z2 position |
| float | r2 | radius 2 |
| boolean | hasIntersection | true if spheres have an intersection else false |
Converts a area to hectarsDefinition
areaToHa(float area, float pixelToSqm)Arguments
| float | area | area |
| float | pixelToSqm | pixel density |
| float | area | area in hectars |
Converts an inch distance to metersDefinition
inchToM(inchValue the)Arguments
| inchValue | the | inch value to convert |
Converts an meter distance to inchDefinition
mToInch(float meterValue)Arguments
| float | meterValue | the meter value to convert |
Returns the brightness of a color [0..1]Definition
getBrightnessFromColor(float r, float g, float b)Arguments
| float | r | red value |
| float | g | green value |
| float | b | blue value |
| float | brightness | brightness |
ObjectChangeUtil
Load object change from xmlDefinition
loadObjectChangeFromXML(integer xmlFile, string key, table objects, integer rootNode, table parent)Arguments
| integer | xmlFile | file id of xml file |
| string | key | key |
| table | objects | table to insert loaded objects |
| integer | rootNode | id of root node |
| table | parent | parent |
| 14 | function ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, key, objects, rootNode, parent) |
| 15 | local i = 0 |
| 16 | while true do |
| 17 | local nodeKey = string.format(key .. ".objectChange(%d)", i) |
| 18 | if not hasXMLProperty(xmlFile, nodeKey) then |
| 19 | break |
| 20 | end |
| 21 | local node = I3DUtil.indexToObject(rootNode, getXMLString(xmlFile, nodeKey .. "#node"), parent.i3dMappings) |
| 22 | if node ~= nil then |
| 23 | local object = {} |
| 24 | object.node = node |
| 25 | ObjectChangeUtil.loadValuesFromXML(xmlFile, nodeKey, node, object, parent) |
| 26 | table.insert(objects, object) |
| 27 | end |
| 28 | i = i + 1 |
| 29 | end |
| 30 | end |
Load object values from xmlDefinition
loadValuesFromXML(integer xmlFile, string key, integer node, table object, table parent)Arguments
| integer | xmlFile | file id of xml file |
| string | key | key |
| integer | node | node id to load from |
| table | object | table to insert loaded data |
| table | parent | parent |
| 39 | function ObjectChangeUtil.loadValuesFromXML(xmlFile, key, node, object, parent) |
| 40 | object.visibilityActive = getXMLBool(xmlFile, key.."#visibilityActive") |
| 41 | object.visibilityInactive = getXMLBool(xmlFile, key.."#visibilityInactive") |
| 42 | object.translationActive = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#translationActive"), 3) |
| 43 | object.translationInactive = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#translationInactive"), 3) |
| 44 | object.rotationActive = StringUtil.getRadiansFromString(getXMLString(xmlFile, key.."#rotationActive"), 3) |
| 45 | object.rotationInactive = StringUtil.getRadiansFromString(getXMLString(xmlFile, key.."#rotationInactive"), 3) |
| 46 | object.scaleActive = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#scaleActive"), 3) |
| 47 | object.scaleInactive = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#scaleInactive"), 3) |
| 48 | |
| 49 | XMLUtil.checkDeprecatedXMLElements(xmlFile, "", key.."#collisionActive", key.."#compoundChildActive or #rigidBodyTypeActive") --FS17 to FS19 |
| 50 | XMLUtil.checkDeprecatedXMLElements(xmlFile, "", key.."#collisionInactive", key.."#compoundChildInactive or #rigidBodyTypeInactive") --FS17 to FS19 |
| 51 | |
| 52 | object.massActive = nil |
| 53 | object.massInactive = nil |
| 54 | local massActive = getXMLFloat(xmlFile, key.."#massActive") |
| 55 | if massActive ~= nil then |
| 56 | object.massActive = massActive / 1000 |
| 57 | end |
| 58 | local massInactive = getXMLFloat(xmlFile, key.."#massInactive") |
| 59 | if massInactive ~= nil then |
| 60 | object.massInactive = massInactive / 1000 |
| 61 | end |
| 62 | object.centerOfMassActive = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#centerOfMassActive"), 3) |
| 63 | object.centerOfMassInactive = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#centerOfMassInactive"), 3) |
| 64 | object.compoundChildActive = getXMLBool(xmlFile, key.."#compoundChildActive") |
| 65 | object.compoundChildInactive = getXMLBool(xmlFile, key.."#compoundChildInactive") |
| 66 | object.rigidBodyTypeActive = getXMLString(xmlFile, key.."#rigidBodyTypeActive") |
| 67 | object.rigidBodyTypeInactive = getXMLString(xmlFile, key.."#rigidBodyTypeInactive") |
| 68 | |
| 69 | if object.rigidBodyTypeActive ~= nil then |
| 70 | local t = object.rigidBodyTypeActive |
| 71 | if t ~= "Static" and t ~= "Dynamic" and t ~= "Kinematic" and t ~= "NoRigidBody" then |
| 72 | g_logManager:warning("Invalid rigidBodyTypeActive '%s' for object change node '%s'. Use 'Static', 'Dynamic', 'Kinematic' or 'NoRigidBody'!", t, key) |
| 73 | object.rigidBodyTypeActive = nil |
| 74 | end |
| 75 | end |
| 76 | if object.rigidBodyTypeInactive ~= nil then |
| 77 | local t = object.rigidBodyTypeInactive |
| 78 | if t ~= "Static" and t ~= "Dynamic" and t ~= "Kinematic" and t ~= "NoRigidBody" then |
| 79 | g_logManager:warning("Invalid rigidBodyTypeInactive '%s' for object change node '%s'. Use 'Static', 'Dynamic', 'Kinematic' or 'NoRigidBody'!", t, key) |
| 80 | object.rigidBodyTypeInactive = nil |
| 81 | end |
| 82 | end |
| 83 | |
| 84 | if parent ~= nil and parent.loadObjectChangeValuesFromXML ~= nil then |
| 85 | parent:loadObjectChangeValuesFromXML(xmlFile, key, node, object) |
| 86 | end |
| 87 | end |
Set object changesDefinition
setObjectChanges(table object, boolean isActive, table target, function updateFunc)Arguments
| table | object | objects to change |
| boolean | isActive | is active |
| table | target | target for updateFunc |
| function | updateFunc | function to update |
| 95 | function ObjectChangeUtil.setObjectChanges(objects, isActive, target, updateFunc) |
| 96 | for _, object in pairs(objects) do |
| 97 | ObjectChangeUtil.setObjectChange(object, isActive, target, updateFunc) |
| 98 | end |
| 99 | end |
Set object changeDefinition
setObjectChange(table object, boolean isActive, table target, function updateFunc)Arguments
| table | object | objects to change |
| boolean | isActive | is active |
| table | target | target for updateFunc |
| function | updateFunc | function to update |
| 107 | function ObjectChangeUtil.setObjectChange(object, isActive, target, updateFunc) |
| 108 | if isActive then |
| 109 | if object.visibilityActive ~= nil then |
| 110 | setVisibility(object.node, object.visibilityActive) |
| 111 | end |
| 112 | if object.translationActive ~= nil then |
| 113 | setTranslation(object.node, object.translationActive[1], object.translationActive[2], object.translationActive[3]) |
| 114 | end |
| 115 | if object.rotationActive ~= nil then |
| 116 | setRotation(object.node, object.rotationActive[1], object.rotationActive[2], object.rotationActive[3]) |
| 117 | end |
| 118 | if object.scaleActive ~= nil then |
| 119 | setScale(object.node, object.scaleActive[1], object.scaleActive[2], object.scaleActive[3]) |
| 120 | end |
| 121 | if object.massActive ~= nil then |
| 122 | setMass(object.node, object.massActive) |
| 123 | |
| 124 | if target ~= nil and target.components ~= nil then |
| 125 | for _, component in ipairs(target.components) do |
| 126 | if component.node == object.node then |
| 127 | component.defaultMass = object.massActive |
| 128 | target:setMassDirty() |
| 129 | end |
| 130 | end |
| 131 | end |
| 132 | end |
| 133 | if object.centerOfMassActive ~= nil then |
| 134 | setCenterOfMass(object.node, unpack(object.centerOfMassActive)) |
| 135 | end |
| 136 | if object.compoundChildActive ~= nil then |
| 137 | setIsCompoundChild(object.node, object.compoundChildActive) |
| 138 | end |
| 139 | if object.rigidBodyTypeActive ~= nil then |
| 140 | setRigidBodyType(object.node, object.rigidBodyTypeActive) |
| 141 | end |
| 142 | else |
| 143 | if object.visibilityInactive ~= nil then |
| 144 | setVisibility(object.node, object.visibilityInactive) |
| 145 | end |
| 146 | if object.translationInactive ~= nil then |
| 147 | setTranslation(object.node, object.translationInactive[1], object.translationInactive[2], object.translationInactive[3]) |
| 148 | end |
| 149 | if object.rotationInactive ~= nil then |
| 150 | setRotation(object.node, object.rotationInactive[1], object.rotationInactive[2], object.rotationInactive[3]) |
| 151 | end |
| 152 | if object.scaleInactive ~= nil then |
| 153 | setScale(object.node, object.scaleInactive[1], object.scaleInactive[2], object.scaleInactive[3]) |
| 154 | end |
| 155 | if object.massInactive ~= nil then |
| 156 | setMass(object.node, object.massInactive) |
| 157 | |
| 158 | if target ~= nil and target.components ~= nil then |
| 159 | for _, component in ipairs(target.components) do |
| 160 | if component.node == object.node then |
| 161 | component.defaultMass = object.massInactive |
| 162 | target:setMassDirty() |
| 163 | end |
| 164 | end |
| 165 | end |
| 166 | end |
| 167 | if object.centerOfMassInactive ~= nil then |
| 168 | setCenterOfMass(object.node, unpack(object.centerOfMassInactive)) |
| 169 | end |
| 170 | if object.compoundChildInactive ~= nil then |
| 171 | setIsCompoundChild(object.node, object.compoundChildInactive) |
| 172 | end |
| 173 | if object.rigidBodyTypeInactive ~= nil then |
| 174 | setRigidBodyType(object.node, object.rigidBodyTypeInactive) |
| 175 | end |
| 176 | end |
| 177 | if target ~= nil then |
| 178 | if target.setObjectChangeValues ~= nil then |
| 179 | target:setObjectChangeValues(object, isActive) |
| 180 | end |
| 181 | if updateFunc ~= nil then |
| 182 | updateFunc(target, object.node) |
| 183 | end |
| 184 | end |
| 185 | end |
Update object changesDefinition
updateObjectChanges(integer xmlFile, string key, integer configKey, integer rootNode, table parent)Arguments
| integer | xmlFile | file id of xml file |
| string | key | key |
| integer | configKey | id of used config |
| integer | rootNode | id of root node |
| table | parent | parent |
| 194 | function ObjectChangeUtil.updateObjectChanges(xmlFile, key, configKey, rootNode, parent) |
| 195 | local i = 0 |
| 196 | local activeI = (configKey - 1) |
| 197 | while true do |
| 198 | local objectChangeKey = string.format(key.."(%d)", i) |
| 199 | if not hasXMLProperty(xmlFile, objectChangeKey) then |
| 200 | break |
| 201 | end |
| 202 | if i ~= activeI then |
| 203 | local objects = {} |
| 204 | ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, objectChangeKey, objects, rootNode, parent) |
| 205 | ObjectChangeUtil.setObjectChanges(objects, false, parent) |
| 206 | end |
| 207 | i = i + 1 |
| 208 | end |
| 209 | |
| 210 | -- Set the active config last so that it can overwrite settings of inactive configurations |
| 211 | if i > activeI then |
| 212 | local objectChangeKey = string.format(key.."(%d)", activeI) |
| 213 | local objects = {} |
| 214 | ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, objectChangeKey, objects, rootNode, parent) |
| 215 | ObjectChangeUtil.setObjectChanges(objects, true, parent) |
| 216 | end |
| 217 | end |
ParticleUtil
Load particle systemDefinition
loadParticleSystem(integer xmlId, table particleSystem, string baseString, table linkNodes, boolean defaultEmittingState, string defaultPsFile, string baseDir, integer defaultLinkNode)Arguments
| integer | xmlId | id of xml file |
| table | particleSystem | table to add particle system data |
| string | baseString | base string to load data from xml |
| table | linkNodes | link nodes |
| boolean | defaultEmittingState | default emitting state |
| string | defaultPsFile | path to default ps file |
| string | baseDir | base directory |
| integer | defaultLinkNode | id of default link node |
Delete particle systemDefinition
deleteParticleSystem(table particleSystem)Arguments
| table | particleSystem | particle system |
Delete particle systemsDefinition
deleteParticleSystems(table particleSystems)Arguments
| table | particleSystems | particle systems |
Set emitting state of particle systemDefinition
setEmittingState(table particleSystem, boolean state, boolean resetStartTimer, boolean resetStopTimer)Arguments
| table | particleSystem | particle system |
| boolean | state | emitting state |
| boolean | resetStartTimer | reset start timer |
| boolean | resetStopTimer | reset stop timer |
Returns average speed of particle systemDefinition
getParticleSystemAverageSpeed(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | averageSpeed | average speed |
Setting time scale of particle systemDefinition
setParticleSystemTimeScale(table particleSystem, float scale)Arguments
| table | particleSystem | particle system |
| float | scale | time scale |
Setting emit count scale of particle systemDefinition
setEmitCountScale(table particleSystem, float scale)Arguments
| table | particleSystem | particle system |
| float | scale | emit count scale |
Setting particle system lifespanDefinition
setParticleLifespan(table particleSystem, float lifespan)Arguments
| table | particleSystem | particle system |
| float | lifespan | lifespan |
Sets start and stop time of particle systemDefinition
setParticleStartStopTime(table particleSystem, float startTime, float stopTime)Arguments
| table | particleSystem | particle system |
| float | startTime | start time |
| float | stopTime | stop time |
Returns speed of particle systemDefinition
getParticleSystemSpeed(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | speed | speed |
Sets speed of particle systemDefinition
setParticleSystemSpeed(table particleSystem, float speed)Arguments
| table | particleSystem | particle system |
| float | speed | speed |
Returns speed random of particle systemDefinition
getParticleSystemSpeedRandom(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | speedRandom | speed random |
Sets speed random of particle systemDefinition
setParticleSystemSpeedRandom(table particleSystem, float speedRandom)Arguments
| table | particleSystem | particle system |
| float | speedRandom | speed random |
Returns normal speed of particle systemDefinition
getParticleSystemNormalSpeed(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | normalSpeed | normal speed |
Sets normal speed of particle systemDefinition
setParticleSystemNormalSpeed(table particleSystem, float normalSpeed)Arguments
| table | particleSystem | particle system |
| float | normalSpeed | normal speed |
Returns tangent speed of particle systemDefinition
getParticleSystemTangentSpeed(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | tangentSpeed | tangent speed |
Sets tangent speed of particle systemDefinition
setParticleSystemTangentSpeed(table particleSystem, float tangentSpeed)Arguments
| table | particleSystem | particle system |
| float | tangentSpeed | tangent speed |
Returns X sprite scale of particle systemDefinition
getParticleSystemSpriteScaleX(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | spriteScaleX | X sprite scale |
Sets X sprite scale of particle systemDefinition
setParticleSystemSpriteScaleX(table particleSystem, float spriteScaleX)Arguments
| table | particleSystem | particle system |
| float | spriteScaleX | X sprite scale |
Returns Y sprite scale of particle systemDefinition
getParticleSystemSpriteScaleY(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | spriteScaleY | Y sprite scale |
Sets Y sprite scale of particle systemDefinition
setParticleSystemSpriteScaleY(table particleSystem, float spriteScaleY)Arguments
| table | particleSystem | particle system |
| float | spriteScaleY | Y sprite scale |
Returns X sprite scale gain of particle systemDefinition
getParticleSystemSpriteScaleXGain(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | spriteScaleXGain | X sprite scale gain |
Sets X sprite scale gain of particle systemDefinition
setParticleSystemSpriteScaleXGain(table particleSystem, float spriteScaleXGain)Arguments
| table | particleSystem | particle system |
| float | spriteScaleXGain | X sprite scale gain |
Returns Y sprite scale gain of particle systemDefinition
getParticleSystemSpriteScaleYGain(table particleSystem)Arguments
| table | particleSystem | particle system |
| float | spriteScaleYGain | Y sprite scale gain |
Sets Y sprite scale gain of particle systemDefinition
setParticleSystemSpriteScaleYGain(table particleSystem, float spriteScaleYGain)Arguments
| table | particleSystem | particle system |
| float | spriteScaleYGain | Y sprite scale gain |
Resets number of emitted particlesDefinition
resetNumOfEmittedParticles(table particleSystem)Arguments
| table | particleSystem | particle system |
Check if a placeable object at a given position lies within a restricted zone or below the water line.Definition
isInsideRestrictedZone(places Array, placable Placeable, x Testing, y Testing, Z Testing)Arguments
| places | Array | of restricted zones (see PlacementUtil.createRestrictedZone() for data definition) |
| placable | Placeable | instance |
| x | Testing | X position in world space |
| y | Testing | Y position in world space |
| Z | Testing | Z position in world space |
Check if a placeable object at a given position lies within store or loading spaces.Definition
isInsidePlacementPlaces(places Array, placable Placeable, x Testing, y Testing, Z Testing)Arguments
| places | Array | of places (see PlacementUtil.createPlace() for data definition) |
| placable | Placeable | instance |
| x | Testing | X position in world space |
| y | Testing | Y position in world space |
| Z | Testing | Z position in world space |
Test if a placeable's placement test volume at a given position and orientation would collide with another object.Definition
The test is performed with a placeable's "placementTestSizeX" and "placementTestSizeZ" attribute values. The given
coordinates must represent a point either on an object or on the terrain to generate a correct result.
hasObjectOverlap(Placeable Placeable, x Testing, y Testing, Z Testing, rotY Testing)Arguments
| Placeable | Placeable | instance |
| x | Testing | X position in world space |
| y | Testing | Y position in world space |
| Z | Testing | Z position in world space |
| rotY | Testing | rotation in radians |
| True | if | there is a collision, false otherwise |
Test if a placeable at given position and rotation would overlap with any player positionDefinition
hasOverlapWithPoint()
Load a placeable object from an XML definition file.Definition
loadPlaceableFromXML(xmlFilename Path, x New, y New, Z New, rx New, ry New, rz New, moveMode True)Arguments
| xmlFilename | Path | to placeable XML definition file |
| x | New | object X position in world space |
| y | New | object Y position in world space |
| Z | New | object Z position in world space |
| rx | New | object X rotation in radians |
| ry | New | object Y rotation in radians |
| rz | New | object Z rotation in radians |
| moveMode | True | if the placeable can be moved around for placement, false otherwise |
| Placeable | object | instance or nil if no object could be created |
| True | if | no placeable object could be created at the requested position because there was no valid space, false otherwise |
Load a placeable object from a validated XML definition.Definition
loadPlaceable(placeableType Type, xmlFilename Path, x New, y New, Z New, rx New, ry New, rz New, moveMode True)Arguments
| placeableType | Type | name of the requested placeable object |
| xmlFilename | Path | to placeable XML definition file |
| x | New | object X position in world space |
| y | New | object Y position in world space |
| Z | New | object Z position in world space |
| rx | New | object X rotation in radians |
| ry | New | object Y rotation in radians |
| rz | New | object Z rotation in radians |
| moveMode | True | if the placeable can be moved around for placement, false if the placeable is to be created on the spot |
| Placeable | object | instance or nil if no object could be created |
| True | if | no placeable object could be created at the requested position because there was no valid space, false otherwise |
Get a placeable area based on start, width and height nodes.Definition
The area is returned as an array of 9 numbers, i.e. 1 world space point and 2 world space vectors. The point denotes
the area origin and the vectors define the orientation and length of the area sides.
return {worldStartX, worldStartY, worldStartZ, side1X, side1Y, side1Z, side2X, side2Y, side2Z}
getPlaceableAreaByNodes()
Returns vector from string separated by a whitespaceDefinition
getVectorFromString(string input)Arguments
| string | input | input |
| any_type | unpackedValues | returns unpacked values found in string |
Returns vector N from string separated by a whitespaceDefinition
getVectorNFromString(string input, integer num)Arguments
| string | input | input |
| integer | num | number of values |
| table | values | values |
Returns radian vector N from string separated by a whitespaceDefinition
getRadiansFromString(string input, integer num)Arguments
| string | input | input |
| integer | num | number of values |
| table | values | radian values |
Returns an array of elements from the string, split at separator, and passed through lambdaDefinition
parseList(string str, string seprator, function lambda)Arguments
| string | str | input string |
| string | seprator | list separator |
| function | lambda | function to apply to each list element, e.g. tonumber |
Returns text elements splitted by splitPatternDefinition
splitString(string splitPattern, string text)Arguments
| string | splitPattern | splitting pattern |
| string | text | a text |
| table | result | text elements |
Returns if a string starts with given sequenceDefinition
startsWith(string str, string find)Arguments
| string | str | a string |
| string | find | the start pattern |
| boolean | startsWidth | true if given string starts with pattern else false |
Returns if a string ends with given sequenceDefinition
endsWith(string str, string find)Arguments
| string | str | a string |
| string | find | the start pattern |
| boolean | startsWidth | true if given string ends with pattern else false |
Returns a trimed string (no whitespaces at start and end)Definition
trim(string str)Arguments
| string | str | a string |
| string | trimedString | trimed text |
Utils
Returns second parameter if the first is nilDefinition
getNoNil(any_type value, any_type setTo)Arguments
| any_type | value | value |
| any_type | setTo | set to value |
| any_type | value | not nil value |
XMLUtil
Class for various xml operations
Searches for an XML attribute in two placesDefinition
getXMLStringWithDefault(DomXMLFile xmlFile, string key, string defkey, string overridekey, string attr)Arguments
| DomXMLFile | xmlFile | the XML file to search |
| string | key | the main key to look under |
| string | defkey | the secondary key to look under, if the main key fails (can be nil) |
| string | overridekey | the override key, which can override the main key (can be nil) |
| string | attr | the attribute to find |
Searches for an XML int attribute in two placesDefinition
getXMLIntWithDefault(DomXMLFile xmlFile, string key, string defkey, string overridekey, string attr)Arguments
| DomXMLFile | xmlFile | the XML file to search |
| string | key | the main key to look under |
| string | defkey | the secondary key to look under, if the main key fails (can be nil) |
| string | overridekey | the override key, which can override the main key (can be nil) |
| string | attr | the attribute to find |
Searches for an XML float attribute in two placesDefinition
getXMLFloatWithDefault(DomXMLFile xmlFile, string key, string defkey, string overridekey, string attr)Arguments
| DomXMLFile | xmlFile | the XML file to search |
| string | key | the main key to look under |
| string | defkey | the secondary key to look under, if the main key fails (can be nil) |
| string | overridekey | the override key, which can override the main key (can be nil) |
| string | attr | the attribute to find |
Tries to retrieve xml value with given xmlFunc, returns nil if xml-value equals "-" otherwise xml-value if exists otherwise fallbackValueDefinition
getXMLOverwrittenValue(DomXMLFile xmlFile, string key, string subKey, args param, function xmlFunc, fallbackValue fallbackValue, valueFunc additional, ... variable)Arguments
| DomXMLFile | xmlFile | the XML file to search |
| string | key | the main key |
| string | subKey | the secondary key to append to the main key |
| args | param | parameters passed to the xmlFunc |
| function | xmlFunc | the xml function to be used for accessing the key |
| fallbackValue | fallbackValue | to be returned if key does not exist |
| valueFunc | additional | function to be applied to value retrieved from xml file |
| ... | variable | number of arguments additionally passed to valueFunc |
Loads the map specific data from the map xml. Can either be inlined in the map xml directly (<xmlKey>...</xmlKey>) or can be referenced with an external file (<xmlKey filename="..." />)Definition
loadDataFromMapXML(DomXMLFile mapXMLFile, string xmlKey, object loadTarget, function loadFunc, vararg vararg)Arguments
| DomXMLFile | mapXMLFile | the map XML file |
| string | xmlKey | the xml key specifying the data to be loaded |
| object | loadTarget | the object to call the load function on |
| function | loadFunc | the load function to be called with the data |
| vararg | vararg | any data that should be passed to the load function |
Util class for various ai vehicle functions
Drive vehicle to given pointDefinition
driveToPoint(table self, float dt, float acceleration, boolean allowedToDrive, boolean moveForwards, float tX, float tY, float maxSpeed, boolean doNotSteer)Arguments
| table | self | object of vehicle to move |
| float | dt | time since last call in ms |
| float | acceleration | acceleration |
| boolean | allowedToDrive | allowed to drive |
| boolean | moveForwards | move forwards |
| float | tX | local space x position |
| float | tY | local space y position |
| float | maxSpeed | speed limit |
| boolean | doNotSteer | do not steer |
| 28 | function AIVehicleUtil.driveToPoint(self, dt, acceleration, allowedToDrive, moveForwards, tX, tZ, maxSpeed, doNotSteer) |
| 29 | |
| 30 | if self.firstTimeRun then |
| 31 | |
| 32 | if allowedToDrive then |
| 33 | |
| 34 | local tX_2 = tX * 0.5 |
| 35 | local tZ_2 = tZ * 0.5 |
| 36 | |
| 37 | local d1X, d1Z = tZ_2, -tX_2 |
| 38 | if tX > 0 then |
| 39 | d1X, d1Z = -tZ_2, tX_2 |
| 40 | end |
| 41 | |
| 42 | local hit,_,f2 = MathUtil.getLineLineIntersection2D(tX_2,tZ_2, d1X,d1Z, 0,0, tX, 0) |
| 43 | |
| 44 | if doNotSteer == nil or not doNotSteer then |
| 45 | local rotTime = 0 |
| 46 | if hit and math.abs(f2) < 100000 then |
| 47 | local radius = tX * f2 |
| 48 | rotTime = self.wheelSteeringDuration * ( math.atan(1/radius) / math.atan(1/self.maxTurningRadius) ) |
| 49 | end |
| 50 | |
| 51 | local targetRotTime |
| 52 | if rotTime >= 0 then |
| 53 | targetRotTime = math.min(rotTime, self.maxRotTime) |
| 54 | else |
| 55 | targetRotTime = math.max(rotTime, self.minRotTime) |
| 56 | end |
| 57 | |
| 58 | if targetRotTime > self.rotatedTime then |
| 59 | self.rotatedTime = math.min(self.rotatedTime + dt*self:getAISteeringSpeed(), targetRotTime) |
| 60 | else |
| 61 | self.rotatedTime = math.max(self.rotatedTime - dt*self:getAISteeringSpeed(), targetRotTime) |
| 62 | end |
| 63 | |
| 64 | -- adjust maxSpeed |
| 65 | local steerDiff = targetRotTime - self.rotatedTime |
| 66 | local fac = math.abs(steerDiff) / math.max(self.maxRotTime, -self.minRotTime) |
| 67 | local speedReduction = 1.0 - math.pow(fac, 0.25) |
| 68 | |
| 69 | -- if the speed is decreased to less than 1.5km/h we do not accelrate anymore |
| 70 | if maxSpeed * speedReduction < 1.5 then |
| 71 | acceleration = 0 |
| 72 | speedReduction = 1.5 / maxSpeed |
| 73 | end |
| 74 | |
| 75 | maxSpeed = maxSpeed * speedReduction |
| 76 | end |
| 77 | end |
| 78 | |
| 79 | self:getMotor():setSpeedLimit(math.min(maxSpeed, self:getCruiseControlSpeed())) |
| 80 | if self:getCruiseControlState() ~= Drivable.CRUISECONTROL_STATE_ACTIVE then |
| 81 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE) |
| 82 | end |
| 83 | |
| 84 | if not allowedToDrive then |
| 85 | acceleration = 0 |
| 86 | end |
| 87 | if not moveForwards then |
| 88 | acceleration = -acceleration |
| 89 | end |
| 90 | |
| 91 | WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal*self.movingDirection, acceleration, not allowedToDrive, true) |
| 92 | |
| 93 | end |
| 94 | |
| 95 | end |
Drive in given directionDefinition
driveInDirection(table self, float dt, float steeringAngleLimit, float acceleration, float slowAcceleration, float slowAngleLimit, boolean allowedToDrive, boolean moveForwards, float lx, float lz, float maxSpeed, float slowDownFactor)Arguments
| table | self | object of vehicle |
| float | dt | time since last call in ms |
| float | steeringAngleLimit | limit for steering angle |
| float | acceleration | acceleration |
| float | slowAcceleration | slow acceleration |
| float | slowAngleLimit | limit of slow angle |
| boolean | allowedToDrive | allow to drive |
| boolean | moveForwards | move forwards |
| float | lx | x direction |
| float | lz | z direction |
| float | maxSpeed | max speed |
| float | slowDownFactor | slow down factor |
| 111 | function AIVehicleUtil.driveInDirection(self, dt, steeringAngleLimit, acceleration, slowAcceleration, slowAngleLimit, allowedToDrive, moveForwards, lx, lz, maxSpeed, slowDownFactor) |
| 112 | |
| 113 | local angle = 0 |
| 114 | if lx ~= nil and lz ~= nil then |
| 115 | local dot = lz |
| 116 | angle = math.deg(math.acos(dot)) |
| 117 | if angle < 0 then |
| 118 | angle = angle+180 |
| 119 | end |
| 120 | |
| 121 | local turnLeft = lx > 0.00001 |
| 122 | if not moveForwards then |
| 123 | turnLeft = not turnLeft |
| 124 | end |
| 125 | |
| 126 | local targetRotTime |
| 127 | if turnLeft then |
| 128 | --rotate to the left |
| 129 | targetRotTime = self.maxRotTime*math.min(angle/steeringAngleLimit, 1) |
| 130 | else |
| 131 | --rotate to the right |
| 132 | targetRotTime = self.minRotTime*math.min(angle/steeringAngleLimit, 1) |
| 133 | end |
| 134 | |
| 135 | if targetRotTime > self.rotatedTime then |
| 136 | self.rotatedTime = math.min(self.rotatedTime + dt*self:getAISteeringSpeed(), targetRotTime) |
| 137 | else |
| 138 | self.rotatedTime = math.max(self.rotatedTime - dt*self:getAISteeringSpeed(), targetRotTime) |
| 139 | end |
| 140 | end |
| 141 | |
| 142 | |
| 143 | if self.firstTimeRun then |
| 144 | local acc = acceleration |
| 145 | if maxSpeed ~= nil and maxSpeed ~= 0 then |
| 146 | if math.abs(angle) >= slowAngleLimit then |
| 147 | maxSpeed = maxSpeed * slowDownFactor |
| 148 | end |
| 149 | self.motor:setSpeedLimit(maxSpeed) |
| 150 | |
| 151 | if self.cruiseControl.state ~= Drivable.CRUISECONTROL_STATE_ACTIVE then |
| 152 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_ACTIVE) |
| 153 | end |
| 154 | else |
| 155 | if math.abs(angle) >= slowAngleLimit then |
| 156 | acc = slowAcceleration |
| 157 | end |
| 158 | end |
| 159 | if not allowedToDrive then |
| 160 | acc = 0 |
| 161 | end |
| 162 | if not moveForwards then |
| 163 | acc = -acc |
| 164 | end |
| 165 | WheelsUtil.updateWheelsPhysics(self, dt, self.lastSpeedReal*self.movingDirection, acc, not allowedToDrive, true) |
| 166 | end |
| 167 | end |
Returns drive directionDefinition
getDriveDirection(integer refNode, float x, float y, float z)Arguments
| integer | refNode | id of ref node |
| float | x | world x |
| float | y | world y |
| float | z | world z |
| float | lx | x direction |
| float | lz | z direction |
| 177 | function AIVehicleUtil.getDriveDirection(refNode, x, y, z) |
| 178 | local lx, _, lz = worldToLocal(refNode, x, y, z) |
| 179 | |
| 180 | local length = MathUtil.vector2Length(lx, lz) |
| 181 | if length > 0.00001 then |
| 182 | length = 1/length |
| 183 | lx = lx*length |
| 184 | lz = lz*length |
| 185 | end |
| 186 | return lx, lz |
| 187 | end |
Returns average drive direction between 2 given vectorsDefinition
getAverageDriveDirection(integer refNode, float x, float y, float z, float x2, float y2, float z2)Arguments
| integer | refNode | id of ref node |
| float | x | world x 1 |
| float | y | world y 1 |
| float | z | world z 1 |
| float | x2 | world x 2 |
| float | y2 | world y 2 |
| float | z2 | world z 2 |
| float | lx | average x direction |
| float | lz | average z direction |
| 200 | function AIVehicleUtil.getAverageDriveDirection(refNode, x, y, z, x2, y2, z2) |
| 201 | local lx, _, lz = worldToLocal(refNode, (x+x2)*0.5, (y+y2)*0.5, (z+z2)*0.5) |
| 202 | |
| 203 | local length = MathUtil.vector2Length(lx, lz) |
| 204 | if length > 0.00001 then |
| 205 | lx = lx/length |
| 206 | lz = lz/length |
| 207 | end |
| 208 | return lx, lz, length |
| 209 | end |
Returns if trailer or trailer low is attachedDefinition
getAttachedImplementsAllowTurnBackward(table vehicle)Arguments
| table | vehicle | vehicle to check |
| boolean | isAttached | is attached |
| 215 | function AIVehicleUtil.getAttachedImplementsAllowTurnBackward(vehicle) |
| 216 | if vehicle.getAIAllowTurnBackward ~= nil then |
| 217 | if not vehicle:getAIAllowTurnBackward() then |
| 218 | return false |
| 219 | end |
| 220 | end |
| 221 | |
| 222 | if vehicle.getAttachedImplements ~= nil then |
| 223 | for _, implement in pairs(vehicle:getAttachedImplements()) do |
| 224 | local object = implement.object |
| 225 | if object ~= nil then |
| 226 | if object.getAIAllowTurnBackward ~= nil then |
| 227 | if not object:getAIAllowTurnBackward() then |
| 228 | return false |
| 229 | end |
| 230 | end |
| 231 | |
| 232 | if not AIVehicleUtil.getAttachedImplementsAllowTurnBackward(object) then |
| 233 | return false |
| 234 | end |
| 235 | end |
| 236 | end |
| 237 | end |
| 238 | |
| 239 | return true |
| 240 | end |
Returns reverser direction node of attached ai toolDefinition
getAIToolReverserDirectionNode(table vehicle)Arguments
| table | vehicle | vehicle to check |
| integer | aiToolReverserDirectionNode | reverser direction node of ai tool |
| 270 | function AIVehicleUtil.getAIToolReverserDirectionNode(vehicle) |
| 271 | for _, implement in pairs(vehicle:getAttachedImplements()) do |
| 272 | if implement.object ~= nil then |
| 273 | local reverserNode = implement.object:getAIToolReverserDirectionNode() |
| 274 | |
| 275 | local attachedReverserNode = AIVehicleUtil.getAIToolReverserDirectionNode(implement.object) |
| 276 | reverserNode = reverserNode or attachedReverserNode |
| 277 | |
| 278 | return reverserNode |
| 279 | end |
| 280 | end |
| 281 | end |
Returns max tool turn radiusDefinition
getMaxToolRadius(table implement)Arguments
| table | implement | implement to check |
| float | maxTurnRadius | max turn radius |
| 287 | function AIVehicleUtil.getMaxToolRadius(implement) |
| 288 | local radius = 0 |
| 289 | |
| 290 | local _, rotationNode, wheels = implement.object:getAITurnRadiusLimitation() |
| 291 | |
| 292 | -- collect the max manual defined turn radius of all vehicles, not only valid ai implements |
| 293 | local rootVehicle = implement.object:getRootVehicle() |
| 294 | local retRadius = AIVehicleUtil.getAttachedImplementsMaxTurnRadius(rootVehicle) |
| 295 | |
| 296 | if retRadius ~= -1 then |
| 297 | radius = retRadius |
| 298 | end |
| 299 | |
| 300 | if rotationNode then |
| 301 | local activeInputAttacherJoint = implement.object:getActiveInputAttacherJoint() |
| 302 | local refNode = rotationNode |
| 303 | |
| 304 | -- If the refNode is any attacher joint, we always use the currently used attacher joint |
| 305 | for _, inputAttacherJoint in pairs(implement.object:getInputAttacherJoints()) do |
| 306 | if refNode == inputAttacherJoint.node then |
| 307 | refNode = activeInputAttacherJoint.node |
| 308 | break |
| 309 | end |
| 310 | end |
| 311 | |
| 312 | local rx,_,rz = localToLocal(refNode, implement.object.components[1].node, 0,0,0) |
| 313 | |
| 314 | for _, wheel in pairs(wheels) do |
| 315 | local nx,_,nz = localToLocal(wheel.repr, implement.object.components[1].node, 0,0,0) |
| 316 | |
| 317 | local x,z = nx-rx, nz-rz |
| 318 | local cx,cz = 0,0 |
| 319 | |
| 320 | -- get max rotation |
| 321 | local rotMax |
| 322 | if refNode == activeInputAttacherJoint.node then |
| 323 | local attacherVehicle = implement.object:getAttacherVehicle() |
| 324 | local jointDesc = attacherVehicle:getAttacherJointDescFromObject(implement.object) |
| 325 | rotMax = math.max(jointDesc.upperRotLimit[2], jointDesc.lowerRotLimit[2]) * activeInputAttacherJoint.lowerRotLimitScale[2] |
| 326 | else |
| 327 | for _,compJoint in pairs(implement.object.componentJoints) do |
| 328 | if refNode == compJoint.jointNode then |
| 329 | rotMax = compJoint.rotLimit[2] |
| 330 | break |
| 331 | end |
| 332 | end |
| 333 | end |
| 334 | |
| 335 | -- calc turning radius |
| 336 | local x1 = x*math.cos(rotMax) - z*math.sin(rotMax) |
| 337 | local z1 = x*math.sin(rotMax) + z*math.cos(rotMax) |
| 338 | |
| 339 | local dx = -z1 |
| 340 | local dz = x1 |
| 341 | if wheel.steeringAxleScale ~= 0 and wheel.steeringAxleRotMax ~= 0 then |
| 342 | local tmpx, tmpz = dx, dz |
| 343 | dx = tmpx*math.cos(wheel.steeringAxleRotMax) - tmpz*math.sin(wheel.steeringAxleRotMax) |
| 344 | dz = tmpx*math.sin(wheel.steeringAxleRotMax) + tmpz*math.cos(wheel.steeringAxleRotMax) |
| 345 | end |
| 346 | |
| 347 | local hit,f1,_ = MathUtil.getLineLineIntersection2D(cx,cz, 1,0, x1,z1, dx,dz) |
| 348 | if hit then |
| 349 | radius = math.max(radius, math.abs(f1)) |
| 350 | end |
| 351 | end |
| 352 | end |
| 353 | |
| 354 | return radius |
| 355 | end |
Update invertation of ai left and right markers on vehicleDefinition
updateInvertLeftRightMarkers(table rootAttacherVehicle, table vehicle)Arguments
| table | rootAttacherVehicle | root attacher vehicle |
| table | vehicle | vehicle |
| 361 | function AIVehicleUtil.updateInvertLeftRightMarkers(rootAttacherVehicle, vehicle) |
| 362 | if vehicle.getAIMarkers ~= nil then |
| 363 | local leftMarker, rightMarker, _ = vehicle:getAIMarkers() |
| 364 | if leftMarker ~= nil and rightMarker ~= nil then |
| 365 | local lX, _, _ = localToLocal(leftMarker, rootAttacherVehicle:getAIVehicleDirectionNode(), 0,0,0) |
| 366 | local rX, _, _ = localToLocal(rightMarker, rootAttacherVehicle:getAIVehicleDirectionNode(), 0,0,0) |
| 367 | |
| 368 | if rX > lX then |
| 369 | vehicle:setAIMarkersInverted() |
| 370 | end |
| 371 | end |
| 372 | end |
| 373 | end |
Checks fruits on left and right side of vehicle to decide the turn directionDefinition
getValidityOfTurnDirections(table vehicle, float checkFrontDistance, table turnData)Arguments
| table | vehicle | vehicle to check |
| float | checkFrontDistance | distance to check in front of vehicle |
| table | turnData | properties for turning |
| float | leftAreaPercentage | left area percentage |
| float | rightAreaPercentage | right area percentage |
| 382 | function AIVehicleUtil.getValidityOfTurnDirections(vehicle, turnData) |
| 383 | -- let's check the area at/around the marker which is farest behind of vehicle |
| 384 | local directionNode = vehicle:getAIVehicleDirectionNode() |
| 385 | local attachedAIImplements = vehicle:getAttachedAIImplements() |
| 386 | local checkFrontDistance = 5 |
| 387 | |
| 388 | local leftAreaPercentage = 0 |
| 389 | local rightAreaPercentage = 0 |
| 390 | |
| 391 | local minZ = math.huge |
| 392 | local maxZ = -math.huge |
| 393 | for _,implement in pairs(attachedAIImplements) do |
| 394 | local leftMarker, rightMarker, backMarker = implement.object:getAIMarkers() |
| 395 | |
| 396 | local _,_,zl = localToLocal(leftMarker, directionNode, 0,0,0) |
| 397 | local _,_,zr = localToLocal(rightMarker, directionNode, 0,0,0) |
| 398 | local _,_,zb = localToLocal(backMarker, directionNode, 0,0,0) |
| 399 | |
| 400 | minZ = math.min(minZ, zl, zr, zb) |
| 401 | maxZ = math.max(maxZ, zl, zr, zb) |
| 402 | end |
| 403 | |
| 404 | local sideDistance |
| 405 | if turnData == nil then |
| 406 | local minAreaWidth = math.huge |
| 407 | for _,implement in pairs(attachedAIImplements) do |
| 408 | local leftMarker, rightMarker, _ = implement.object:getAIMarkers() |
| 409 | |
| 410 | local lx, _, _ = localToLocal(leftMarker, directionNode, 0,0,0) |
| 411 | local rx, _, _ = localToLocal(rightMarker, directionNode, 0,0,0) |
| 412 | minAreaWidth = math.min(minAreaWidth, math.abs(lx-rx)) |
| 413 | end |
| 414 | sideDistance = minAreaWidth |
| 415 | else |
| 416 | sideDistance = math.abs(turnData.sideOffsetRight - turnData.sideOffsetLeft) |
| 417 | end |
| 418 | |
| 419 | local dx, dz = vehicle.aiDriveDirection[1], vehicle.aiDriveDirection[2] |
| 420 | local sx, sz = -dz, dx |
| 421 | |
| 422 | for _,implement in pairs(attachedAIImplements) do |
| 423 | local leftMarker, rightMarker, _ = implement.object:getAIMarkers() |
| 424 | |
| 425 | local lx, ly, lz = localToLocal(leftMarker, directionNode, 0,0,0) |
| 426 | local rx, ry, rz = localToLocal(rightMarker, directionNode, 0,0,0) |
| 427 | |
| 428 | local width = math.abs(lx-rx) |
| 429 | local length = checkFrontDistance + (maxZ - minZ) + math.max(sideDistance*1.3 + 2, checkFrontDistance) -- 1.3~tan(53) allows detecting back along a field side with angle 53 (and 2m extra compensates for some variances, or higher angles with small tools) |
| 430 | |
| 431 | lx, _, lz = localToWorld(directionNode, lx,ly,maxZ + checkFrontDistance) |
| 432 | rx, _, rz = localToWorld(directionNode, rx,ry,maxZ + checkFrontDistance) |
| 433 | |
| 434 | local lSX = lx |
| 435 | local lSZ = lz |
| 436 | local lWX = lSX - sx * width |
| 437 | local lWZ = lSZ - sz * width |
| 438 | local lHX = lSX - dx * length |
| 439 | local lHZ = lSZ - dz * length |
| 440 | |
| 441 | local rSX = rx |
| 442 | local rSZ = rz |
| 443 | local rWX = rSX + sx * width |
| 444 | local rWZ = rSZ + sz * width |
| 445 | local rHX = rSX - dx * length |
| 446 | local rHZ = rSZ - dz * length |
| 447 | |
| 448 | local lArea, lTotal = AIVehicleUtil.getAIAreaOfVehicle(implement.object, lSX,lSZ, lWX,lWZ, lHX,lHZ, false) |
| 449 | local rArea, rTotal = AIVehicleUtil.getAIAreaOfVehicle(implement.object, rSX,rSZ, rWX,rWZ, rHX,rHZ, false) |
| 450 | |
| 451 | if lTotal > 0 then |
| 452 | leftAreaPercentage = leftAreaPercentage + (lArea / lTotal) |
| 453 | end |
| 454 | if rTotal > 0 then |
| 455 | rightAreaPercentage = rightAreaPercentage + (rArea / rTotal) |
| 456 | end |
| 457 | |
| 458 | -- just visual debuging |
| 459 | if VehicleDebug.state == VehicleDebug.DEBUG_AI then |
| 460 | local lSY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, lSX,0,lSZ)+2 |
| 461 | local lWY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, lWX,0,lWZ)+2 |
| 462 | local lHY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, lHX,0,lHZ)+2 |
| 463 | local rSY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rSX,0,rSZ)+2 |
| 464 | local rWY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rWX,0,rWZ)+2 |
| 465 | local rHY = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, rHX,0,rHZ)+2 |
| 466 | |
| 467 | vehicle:addAIDebugLine({lSX,lSY,lSZ}, {lWX,lWY,lWZ}, {0.5, 0.5, 0.5}) |
| 468 | vehicle:addAIDebugLine({lSX,lSY,lSZ}, {lHX,lHY,lHZ}, {0.5, 0.5, 0.5}) |
| 469 | vehicle:addAIDebugLine({rSX,rSY,rSZ}, {rWX,rWY,rWZ}, {0.5, 0.5, 0.5}) |
| 470 | vehicle:addAIDebugLine({rSX,rSY,rSZ}, {rHX,rHY,rHZ}, {0.5, 0.5, 0.5}) |
| 471 | end |
| 472 | end |
| 473 | |
| 474 | leftAreaPercentage = leftAreaPercentage / table.getn(attachedAIImplements) |
| 475 | rightAreaPercentage = rightAreaPercentage / table.getn(attachedAIImplements) |
| 476 | |
| 477 | return leftAreaPercentage, rightAreaPercentage |
| 478 | end |
Returns if valid ground to work on is found for ai vehicleDefinition
checkImplementListForValidGround(table vehicle, float lookAheadDist, float lookAheadSize)Arguments
| table | vehicle | vehicle to check |
| float | lookAheadDist | look a head distance |
| float | lookAheadSize | look a head size |
Returns amount of fruit to work for ai vehicle is in given areaDefinition
getAIAreaOfVehicle(table vehicle, float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ)Arguments
| table | vehicle | vehicle |
| float | startWorldX | start world x |
| float | startWorldZ | start world z |
| float | widthWorldX | width world x |
| float | widthWorldZ | width world z |
| float | heightWorldX | height world x |
| float | heightWorldZ | height world z |
| float | area | area found |
| float | totalArea | total area checked |
| 528 | function AIVehicleUtil.getAIAreaOfVehicle(vehicle, startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, ignoreProhibitedValues) |
| 529 | local terrainDetailRequiredValueRanges = vehicle:getAITerrainDetailRequiredRange() |
| 530 | local terrainDetailProhibitValueRanges = vehicle:getAITerrainDetailProhibitedRange() |
| 531 | |
| 532 | local fruitRequirements = vehicle:getAIFruitRequirements() |
| 533 | local useDensityHeightMap, useWindrowFruitType = vehicle:getAIFruitExtraRequirements() |
| 534 | |
| 535 | local fruitProhibitions = vehicle:getAIFruitProhibitions() |
| 536 | |
| 537 | if ignoreProhibitedValues then |
| 538 | terrainDetailProhibitValueRanges = {} |
| 539 | fruitProhibitions = {} |
| 540 | end |
| 541 | |
| 542 | if not useDensityHeightMap then |
| 543 | return AIVehicleUtil.getAIFruitArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, terrainDetailRequiredValueRanges, terrainDetailProhibitValueRanges, fruitRequirements, fruitProhibitions, useWindrowFruitType) |
| 544 | else |
| 545 | return AIVehicleUtil.getAIDensityHeightArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, terrainDetailRequiredValueRanges, terrainDetailProhibitValueRanges, fruitRequirements, fruitProhibitions, useWindrowFruitType) |
| 546 | end |
| 547 | end |
Returns amount of fruit to work is in given areaDefinition
getAIFruitArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, table terrainDetailRequiredValueRanges, table terrainDetailProhibitValueRanges, table fruitRequirements, table fruitProhibitions, boolean useWindrowed)Arguments
| float | startWorldX | start world x |
| float | startWorldZ | start world z |
| float | widthWorldX | width world x |
| float | widthWorldZ | width world z |
| float | heightWorldX | height world x |
| float | heightWorldZ | height world z |
| table | terrainDetailRequiredValueRanges | terrain detail required value ranges |
| table | terrainDetailProhibitValueRanges | terrain detail prohibit value ranges |
| table | fruitRequirements | required fruit type |
| table | fruitProhibitions | prohibited fruit type |
| boolean | useWindrowed | use windrow |
| float | area | area found |
| float | totalArea | total area checked |
| 564 | function AIVehicleUtil.getAIFruitArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, terrainDetailRequiredValueRanges, terrainDetailProhibitValueRanges, fruitRequirements, fruitProhibitions, useWindrowed) |
| 565 | local query = g_currentMission.fieldCropsQuery |
| 566 | |
| 567 | for _, fruitRequirement in pairs(fruitRequirements) do |
| 568 | if fruitRequirement.fruitType ~= FruitType.UNKNOWN then |
| 569 | local ids = g_currentMission.fruits[fruitRequirement.fruitType] |
| 570 | if ids ~= nil and ids.id ~= 0 then |
| 571 | if useWindrowed then |
| 572 | return 0, 1 |
| 573 | end |
| 574 | |
| 575 | local desc = g_fruitTypeManager:getFruitTypeByIndex(fruitRequirement.fruitType) |
| 576 | query:addRequiredCropType(ids.id, fruitRequirement.minGrowthState+1, fruitRequirement.maxGrowthState+1, desc.startStateChannel, desc.numStateChannels, g_currentMission.terrainDetailTypeFirstChannel, g_currentMission.terrainDetailTypeNumChannels) |
| 577 | end |
| 578 | end |
| 579 | end |
| 580 | |
| 581 | for _, fruitProhibition in pairs(fruitProhibitions) do |
| 582 | if fruitProhibition.fruitType ~= FruitType.UNKNOWN then |
| 583 | local ids = g_currentMission.fruits[fruitProhibition.fruitType] |
| 584 | if ids ~= nil and ids.id ~= 0 then |
| 585 | local desc = g_fruitTypeManager:getFruitTypeByIndex(fruitProhibition.fruitType) |
| 586 | query:addProhibitedCropType(ids.id, fruitProhibition.minGrowthState+1, fruitProhibition.maxGrowthState+1, desc.startStateChannel, desc.numStateChannels, g_currentMission.terrainDetailTypeFirstChannel, g_currentMission.terrainDetailTypeNumChannels) |
| 587 | end |
| 588 | end |
| 589 | end |
| 590 | |
| 591 | for _,valueRange in pairs(terrainDetailRequiredValueRanges) do |
| 592 | query:addRequiredGroundValue(valueRange[1], valueRange[2], valueRange[3], valueRange[4]) |
| 593 | end |
| 594 | |
| 595 | for _,valueRange in pairs(terrainDetailProhibitValueRanges) do |
| 596 | query:addProhibitedGroundValue(valueRange[1], valueRange[2], valueRange[3], valueRange[4]) |
| 597 | end |
| 598 | |
| 599 | local x,z, widthX,widthZ, heightX,heightZ = MathUtil.getXZWidthAndHeight(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ) |
| 600 | return query:getParallelogram(x,z, widthX,widthZ, heightX,heightZ, true) |
| 601 | end |
Returns amount of density height to work is in given areaDefinition
getAIDensityHeightArea(float startWorldX, float startWorldZ, float widthWorldX, float widthWorldZ, float heightWorldX, float heightWorldZ, table terrainDetailRequiredValueRanges, table terrainDetailProhibitValueRanges, integer requiredfruittype, integer requiredMinGrowthState, integer requiredMaxGrowthState, integer prohibitedFruitType, integer prohibitedMinGrowthState, integer prohibitedMaxGrowthState, boolean useWindrowed)Arguments
| float | startWorldX | start world x |
| float | startWorldZ | start world z |
| float | widthWorldX | width world x |
| float | widthWorldZ | width world z |
| float | heightWorldX | height world x |
| float | heightWorldZ | height world z |
| table | terrainDetailRequiredValueRanges | terrain detail required value ranges |
| table | terrainDetailProhibitValueRanges | terrain detail prohibit value ranges |
| integer | requiredfruittype | required fruit type |
| integer | requiredMinGrowthState | required min growth state |
| integer | requiredMaxGrowthState | required max growth state |
| integer | prohibitedFruitType | prohibited fruit type |
| integer | prohibitedMinGrowthState | prohibited min growth state |
| integer | prohibitedMaxGrowthState | prohibited max growth state |
| boolean | useWindrowed | use windrow |
| float | area | area found |
| float | totalArea | total area checked |
| 622 | function AIVehicleUtil.getAIDensityHeightArea(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, terrainDetailRequiredValueRanges, terrainDetailProhibitValueRanges, fruitRequirements, fruitProhibitions, useWindrowed) |
| 623 | -- first check if we are on a field |
| 624 | |
| 625 | local data = g_currentMission.densityMapModifiers.getAIDensityHeightArea |
| 626 | local modifier = data.modifier |
| 627 | local filter = data.filter |
| 628 | |
| 629 | modifier:setParallelogramWorldCoords(startWorldX, startWorldZ, widthWorldX, widthWorldZ, heightWorldX, heightWorldZ, "ppp") |
| 630 | filter:setValueCompareParams("greater", 0) |
| 631 | |
| 632 | local _, detailArea, _ = modifier:executeGet(filter) |
| 633 | if detailArea == 0 then |
| 634 | return 0, 0 |
| 635 | end |
| 636 | |
| 637 | local retArea, retTotalArea = 0, 0 |
| 638 | for _, fruitRequirement in pairs(fruitRequirements) do |
| 639 | if fruitRequirement.fruitType ~= FruitType.UNKNOWN then |
| 640 | local fillType |
| 641 | if useWindrowed then |
| 642 | fillType = g_fruitTypeManager:getWindrowFillTypeIndexByFruitTypeIndex(fruitRequirement.fruitType) |
| 643 | else |
| 644 | fillType = g_fruitTypeManager:getFruitTypeIndexByFillTypeIndex(fruitRequirement.fruitType) |
| 645 | end |
| 646 | local _, area, totalArea = DensityMapHeightUtil.getFillLevelAtArea(fillType, startWorldX,startWorldZ, widthWorldX,widthWorldZ, heightWorldX,heightWorldZ) |
| 647 | retArea, retTotalArea = retArea+area, totalArea |
| 648 | end |
| 649 | end |
| 650 | |
| 651 | return retArea, retTotalArea |
| 652 | end |
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 17 | function ConfigurationManager:new(customMt) |
| 18 | local self = AbstractManager:new(customMt or ConfigurationManager_mt) |
| 19 | |
| 20 | self:initDataStructures() |
| 21 | |
| 22 | return self |
| 23 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 27 | function ConfigurationManager:initDataStructures() |
| 28 | self.configurations = {} |
| 29 | self.intToConfigurationName = {} |
| 30 | self.configurationNameToInt = {} |
| 31 | end |
Register new configuration typeDefinition
addConfigurationType(string name, string title, function preLoadFunc, function singleItemLoadFunc, function postLoadFunc, integer selectorType)Arguments
| string | name | name of config |
| string | title | title displayed in shop |
| function | preLoadFunc | called before loading of configuration |
| function | singleItemLoadFunc | called on single item load |
| function | postLoadFunc | called after loading of configuration |
| integer | selectorType | selector type [ConfigurationManager.SELECTOR_MULTIOPTION ConfigurationManager.SELECTOR_COLOR] |
Returns number of configuration typesDefinition
getNumOfConfigurationTypes()Return Values
| integer | numOfConfigurationTypes | number of configuration types |
| 73 | function ConfigurationManager:getNumOfConfigurationTypes() |
| 74 | return table.getn(self.intToConfigurationName) |
| 75 | end |
Returns a table of the available configuration typesDefinition
getConfigurationTypes()Return Values
| integer | numOfConfigurationTypes | number of configuration types |
| 80 | function ConfigurationManager:getConfigurationTypes() |
| 81 | return self.intToConfigurationName |
| 82 | end |
Returns configuration name by given indexDefinition
getConfigurationNameByIndex(integer index)Arguments
| integer | index | index of config |
| string | name | name of config |
| 88 | function ConfigurationManager:getConfigurationNameByIndex(index) |
| 89 | return self.intToConfigurationName[index] |
| 90 | end |
Returns configuration index by given nameDefinition
getConfigurationIndexByName(string name)Arguments
| string | name | name of config |
| integer | index | index of config |
| 96 | function ConfigurationManager:getConfigurationIndexByName(name) |
| 97 | return self.configurationNameToInt[name] |
| 98 | end |
Returns configuration desc by nameDefinition
getConfigurationDescByName(string name)Arguments
| string | name | name of config |
| table | configuration | configuration |
| 104 | function ConfigurationManager:getConfigurationDescByName(name) |
| 105 | return self.configurations[name] |
| 106 | end |
Returns configuration attribute by given name and attributeDefinition
getConfigurationAttribute(string configurationName, string attribute)Arguments
| string | configurationName | name of config |
| string | attribute | name of attribute |
| any_type | value | value of attribute |
| 113 | function ConfigurationManager:getConfigurationAttribute(configurationName, attribute) |
| 114 | local config = self:getConfigurationDescByName(configurationName) |
| 115 | return config[attribute] |
| 116 | end |
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 17 | function SpecializationManager:new(customMt) |
| 18 | local self = AbstractManager:new(customMt or SpecializationManager_mt) |
| 19 | |
| 20 | return self |
| 21 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 25 | function SpecializationManager:initDataStructures() |
| 26 | self.specializations = {} |
| 27 | end |
Load data on map loadDefinition
loadMapData()Return Values
| boolean | true | if loading was successful else false |
| 32 | function SpecializationManager:loadMapData() |
| 33 | SpecializationManager:superClass().loadMapData(self) |
| 34 | |
| 35 | local xmlFile = loadXMLFile("SpecializationsXML", "dataS/specializations.xml") |
| 36 | local i=0 |
| 37 | while true do |
| 38 | local baseName = string.format("specializations.specialization(%d)", i) |
| 39 | |
| 40 | local typeName = getXMLString(xmlFile, baseName.. "#name") |
| 41 | if typeName == nil then |
| 42 | break |
| 43 | end |
| 44 | local className = getXMLString(xmlFile, baseName.. "#className") |
| 45 | local filename = getXMLString(xmlFile, baseName.. "#filename") |
| 46 | |
| 47 | g_deferredLoadingManager:addSubtask(function() |
| 48 | self:addSpecialization(typeName, className, filename, "") |
| 49 | end) |
| 50 | i = i+1 |
| 51 | end |
| 52 | delete(xmlFile) |
| 53 | |
| 54 | g_deferredLoadingManager:addSubtask(function() |
| 55 | print(" Loaded specializations") |
| 56 | end) |
| 57 | |
| 58 | return true |
| 59 | end |
Adds a new vehicleTypeDefinition
addSpecialization(string name, string className, string filename, string customEnvironment)Arguments
| string | name | specialization name |
| string | className | classname |
| string | filename | filename |
| string | customEnvironment | a custom environment |
| boolean | success | true if added else false |
Register interaction flagDefinition
registerInteractionFlag(string name)Arguments
| string | name | name of flag |
| 47 | function Vehicle.registerInteractionFlag(name) |
| 48 | local key = "INTERACTION_FLAG_"..string.upper(name) |
| 49 | if Vehicle[key] == nil then |
| 50 | Vehicle.NUM_INTERACTION_FLAGS = Vehicle.NUM_INTERACTION_FLAGS + 1 |
| 51 | Vehicle[key] = Vehicle.NUM_INTERACTION_FLAGS |
| 52 | end |
| 53 | end |
Get xml states attributesDefinition
saveStatsToXMLFile()Return Values
| string | attributes | attributes |
| 862 | function Vehicle:saveStatsToXMLFile(xmlFile, key) |
| 863 | local isTabbable = self.getIsTabbable == nil or self:getIsTabbable() |
| 864 | if self.isDeleted or not self.isVehicleSaved or not isTabbable then |
| 865 | return false |
| 866 | end |
| 867 | local name = "Unknown" |
| 868 | local categoryName = "unknown" |
| 869 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 870 | if storeItem ~= nil then |
| 871 | if storeItem.name ~= nil then |
| 872 | name = tostring(storeItem.name) |
| 873 | end |
| 874 | if storeItem.categoryName ~= nil and storeItem.categoryName ~= "" then |
| 875 | categoryName = tostring(storeItem.categoryName) |
| 876 | end |
| 877 | end |
| 878 | |
| 879 | setXMLString(xmlFile, key.."#name", HTMLUtil.encodeToHTML(name)) |
| 880 | setXMLString(xmlFile, key.."#category", HTMLUtil.encodeToHTML(categoryName)) |
| 881 | setXMLString(xmlFile, key.."#type", HTMLUtil.encodeToHTML(tostring(self.typeName))) |
| 882 | |
| 883 | if self.components[1] ~= nil and self.components[1].node ~= 0 then |
| 884 | local x,y,z = getWorldTranslation(self.components[1].node) |
| 885 | setXMLFloat(xmlFile, key.."#x", x) |
| 886 | setXMLFloat(xmlFile, key.."#y", y) |
| 887 | setXMLFloat(xmlFile, key.."#z", z) |
| 888 | end |
| 889 | |
| 890 | for id, spec in pairs(self.specializations) do |
| 891 | local name = self.specializationNames[id] |
| 892 | |
| 893 | if spec.saveStatsToXMLFile ~= nil then |
| 894 | spec.saveStatsToXMLFile(self, xmlFile, key) |
| 895 | end |
| 896 | end |
| 897 | |
| 898 | return true |
| 899 | end |
Called on client side on joinDefinition
readStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 905 | function Vehicle:readStream(streamId, connection) |
| 906 | Vehicle:superClass().readStream(self, streamId) |
| 907 | local configFile = NetworkUtil.convertFromNetworkFilename(streamReadString(streamId)) |
| 908 | local typeName = streamReadString(streamId) |
| 909 | |
| 910 | local configurations = {} |
| 911 | local numConfigs = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
| 912 | for i=1, numConfigs do |
| 913 | local configNameId = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
| 914 | local configId = streamReadUInt16(streamId) |
| 915 | |
| 916 | local configName = g_configurationManager:getConfigurationNameByIndex(configNameId+1) |
| 917 | if configName ~= nil then |
| 918 | configurations[configName] = configId+1 |
| 919 | end |
| 920 | end |
| 921 | |
| 922 | local boughtConfigurations = {} |
| 923 | local numConfigs = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
| 924 | for i=1, numConfigs do |
| 925 | local configNameId = streamReadUIntN(streamId, ConfigurationUtil.SEND_NUM_BITS) |
| 926 | local configName = g_configurationManager:getConfigurationNameByIndex(configNameId+1) |
| 927 | boughtConfigurations[configName] = {} |
| 928 | local numBoughtConfigIds = streamReadUInt16(streamId) |
| 929 | for j=1, numBoughtConfigIds do |
| 930 | local boughtConfigId = streamReadUInt16(streamId) |
| 931 | boughtConfigurations[configName][boughtConfigId + 1] = true |
| 932 | end |
| 933 | end |
| 934 | |
| 935 | if self.configFileName == nil then |
| 936 | local vehicleData = {} |
| 937 | vehicleData.filename = configFile |
| 938 | vehicleData.isAbsolute = false |
| 939 | vehicleData.typeName = typeName |
| 940 | vehicleData.posX = 0 |
| 941 | vehicleData.posY = nil |
| 942 | vehicleData.posZ = 0 |
| 943 | vehicleData.yOffset = 0 |
| 944 | vehicleData.rotX = 0 |
| 945 | vehicleData.rotY = 0 |
| 946 | vehicleData.rotZ = 0 |
| 947 | vehicleData.isVehicleSaved = true |
| 948 | vehicleData.price = 0 |
| 949 | vehicleData.propertyState = Vehicle.PROPERTY_STATE_NONE |
| 950 | -- assign parent class Object's ownerFarmId field here to ensure ownership synchronization in MP: |
| 951 | vehicleData.ownerFarmId = self.ownerFarmId |
| 952 | vehicleData.isLeased = 0 |
| 953 | vehicleData.configurations = configurations |
| 954 | vehicleData.boughtConfigurations = boughtConfigurations |
| 955 | self:load(vehicleData) |
| 956 | end |
| 957 | |
| 958 | -- remove from physics to set static components correctly |
| 959 | self:removeFromPhysics() |
| 960 | |
| 961 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
| 962 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
| 963 | for i=1, table.getn(self.components) do |
| 964 | local component = self.components[i] |
| 965 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 966 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
| 967 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 968 | local x_rot = NetworkUtil.readCompressedAngle(streamId) |
| 969 | local y_rot = NetworkUtil.readCompressedAngle(streamId) |
| 970 | local z_rot = NetworkUtil.readCompressedAngle(streamId) |
| 971 | |
| 972 | local qx,qy,qz,qw = mathEulerToQuaternion(x_rot,y_rot,z_rot) |
| 973 | self:setWorldPositionQuaternion(x,y,z, qx,qy,qz,qw, i, true) |
| 974 | |
| 975 | component.networkInterpolators.position:setPosition(x,y,z) |
| 976 | component.networkInterpolators.quaternion:setQuaternion(qx,qy,qz,qw) |
| 977 | end |
| 978 | self.networkTimeInterpolator:reset() |
| 979 | |
| 980 | -- add to physics again |
| 981 | self:addToPhysics() |
| 982 | |
| 983 | self.serverMass = streamReadFloat32(streamId) |
| 984 | self.age = streamReadUInt16(streamId) |
| 985 | self:setOperatingTime(streamReadFloat32(streamId), true) |
| 986 | self.price = streamReadInt32(streamId) |
| 987 | self.propertyState = streamReadUIntN(streamId, 2) |
| 988 | |
| 989 | SpecializationUtil.raiseEvent(self, "onReadStream", streamId, connection) |
| 990 | end |
Called on server side on joinDefinition
writeStream(integer streamId, table connection)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| 996 | function Vehicle:writeStream(streamId, connection) |
| 997 | Vehicle:superClass().writeStream(self, streamId) |
| 998 | streamWriteString(streamId, NetworkUtil.convertToNetworkFilename(self.configFileName)) |
| 999 | streamWriteString(streamId, self.typeName) |
| 1000 | |
| 1001 | local numConfigs = 0 |
| 1002 | for _,_ in pairs(self.configurations) do |
| 1003 | numConfigs = numConfigs + 1 |
| 1004 | end |
| 1005 | |
| 1006 | streamWriteUIntN(streamId, numConfigs, ConfigurationUtil.SEND_NUM_BITS) |
| 1007 | for configName, configId in pairs(self.configurations) do |
| 1008 | local configNameId = g_configurationManager:getConfigurationIndexByName(configName) |
| 1009 | streamWriteUIntN(streamId, configNameId-1, ConfigurationUtil.SEND_NUM_BITS) |
| 1010 | streamWriteUInt16(streamId, configId-1) |
| 1011 | end |
| 1012 | |
| 1013 | local numBoughtConfigs = 0 |
| 1014 | for _,_ in pairs(self.boughtConfigurations) do |
| 1015 | numBoughtConfigs = numBoughtConfigs + 1 |
| 1016 | end |
| 1017 | |
| 1018 | streamWriteUIntN(streamId, numBoughtConfigs, ConfigurationUtil.SEND_NUM_BITS) |
| 1019 | for configName, configIds in pairs(self.boughtConfigurations) do |
| 1020 | local numBoughtConfigIds = 0 |
| 1021 | for _,_ in pairs(configIds) do |
| 1022 | numBoughtConfigIds = numBoughtConfigIds + 1 |
| 1023 | end |
| 1024 | local configNameId = g_configurationManager:getConfigurationIndexByName(configName) |
| 1025 | streamWriteUIntN(streamId, configNameId-1, ConfigurationUtil.SEND_NUM_BITS) |
| 1026 | streamWriteUInt16(streamId, numBoughtConfigIds) |
| 1027 | for id, _ in pairs(configIds) do |
| 1028 | streamWriteUInt16(streamId, id-1) |
| 1029 | end |
| 1030 | end |
| 1031 | |
| 1032 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
| 1033 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
| 1034 | for i=1, table.getn(self.components) do |
| 1035 | local component = self.components[i] |
| 1036 | local x,y,z = getWorldTranslation(component.node) |
| 1037 | local x_rot,y_rot,z_rot = getWorldRotation(component.node) |
| 1038 | NetworkUtil.writeCompressedWorldPosition(streamId, x, paramsXZ) |
| 1039 | NetworkUtil.writeCompressedWorldPosition(streamId, y, paramsY) |
| 1040 | NetworkUtil.writeCompressedWorldPosition(streamId, z, paramsXZ) |
| 1041 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
| 1042 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
| 1043 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
| 1044 | end |
| 1045 | |
| 1046 | streamWriteFloat32(streamId, self.serverMass) |
| 1047 | streamWriteUInt16(streamId, self.age) |
| 1048 | streamWriteFloat32(streamId, self.operatingTime) |
| 1049 | streamWriteInt32(streamId, self.price) |
| 1050 | streamWriteUIntN(streamId, self.propertyState, 2) |
| 1051 | |
| 1052 | SpecializationUtil.raiseEvent(self, "onWriteStream", streamId, connection) |
| 1053 | end |
Called on client side on updateDefinition
readUpdateStream(integer streamId, integer timestamp, table connection)Arguments
| integer | streamId | stream ID |
| integer | timestamp | timestamp |
| table | connection | connection |
| 1060 | function Vehicle:readUpdateStream(streamId, timestamp, connection) |
| 1061 | if connection.isServer then |
| 1062 | local hasUpdate = streamReadBool(streamId) |
| 1063 | if hasUpdate then |
| 1064 | self.networkTimeInterpolator:startNewPhaseNetwork() |
| 1065 | |
| 1066 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
| 1067 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
| 1068 | for i=1, table.getn(self.components) do |
| 1069 | local component = self.components[i] |
| 1070 | if not component.isStatic then |
| 1071 | local x = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 1072 | local y = NetworkUtil.readCompressedWorldPosition(streamId, paramsY) |
| 1073 | local z = NetworkUtil.readCompressedWorldPosition(streamId, paramsXZ) |
| 1074 | local x_rot = NetworkUtil.readCompressedAngle(streamId) |
| 1075 | local y_rot = NetworkUtil.readCompressedAngle(streamId) |
| 1076 | local z_rot = NetworkUtil.readCompressedAngle(streamId) |
| 1077 | local qx,qy,qz,qw = mathEulerToQuaternion(x_rot,y_rot,z_rot) |
| 1078 | |
| 1079 | component.networkInterpolators.position:setTargetPosition(x,y,z) |
| 1080 | component.networkInterpolators.quaternion:setTargetQuaternion(qx,qy,qz,qw) |
| 1081 | end |
| 1082 | end |
| 1083 | SpecializationUtil.raiseEvent(self, "onReadPositionUpdateStream", streamId, connection) |
| 1084 | end |
| 1085 | end |
| 1086 | |
| 1087 | if Vehicle.debugNetworkUpdate then |
| 1088 | print("-------------------------------------------------------------") |
| 1089 | print(self.configFileName) |
| 1090 | for _, spec in ipairs(self.eventListeners["readUpdateStream"]) do |
| 1091 | local className = ClassUtil.getClassName(spec) |
| 1092 | local startBits = streamGetReadOffset(streamId) |
| 1093 | spec["readUpdateStream"](self, streamId, timestamp, connection) |
| 1094 | print(" "..tostring(className).." read " .. streamGetReadOffset(streamId)-startBits .. " bits") |
| 1095 | end |
| 1096 | else |
| 1097 | SpecializationUtil.raiseEvent(self, "onReadUpdateStream", streamId, timestamp, connection) |
| 1098 | end |
| 1099 | end |
Called on server side on updateDefinition
writeUpdateStream(integer streamId, table connection, integer dirtyMask)Arguments
| integer | streamId | stream ID |
| table | connection | connection |
| integer | dirtyMask | dirty mask |
| 1106 | function Vehicle:writeUpdateStream(streamId, connection, dirtyMask) |
| 1107 | if not connection.isServer then |
| 1108 | if streamWriteBool(streamId, bitAND(dirtyMask, self.vehicleDirtyFlag) ~= 0) then |
| 1109 | |
| 1110 | local paramsXZ = self.highPrecisionPositionSynchronization and g_currentMission.vehicleXZPosHighPrecisionCompressionParams or g_currentMission.vehicleXZPosCompressionParams |
| 1111 | local paramsY = self.highPrecisionPositionSynchronization and g_currentMission.vehicleYPosHighPrecisionCompressionParams or g_currentMission.vehicleYPosCompressionParams |
| 1112 | for i=1, table.getn(self.components) do |
| 1113 | local component = self.components[i] |
| 1114 | if not component.isStatic then |
| 1115 | local x,y,z = getWorldTranslation(component.node) |
| 1116 | local x_rot,y_rot,z_rot = getWorldRotation(component.node) |
| 1117 | NetworkUtil.writeCompressedWorldPosition(streamId, x, paramsXZ) |
| 1118 | NetworkUtil.writeCompressedWorldPosition(streamId, y, paramsY) |
| 1119 | NetworkUtil.writeCompressedWorldPosition(streamId, z, paramsXZ) |
| 1120 | NetworkUtil.writeCompressedAngle(streamId, x_rot) |
| 1121 | NetworkUtil.writeCompressedAngle(streamId, y_rot) |
| 1122 | NetworkUtil.writeCompressedAngle(streamId, z_rot) |
| 1123 | end |
| 1124 | end |
| 1125 | SpecializationUtil.raiseEvent(self, "onWritePositionUpdateStream", streamId, connection, dirtyMask) |
| 1126 | end |
| 1127 | end |
| 1128 | |
| 1129 | if Vehicle.debugNetworkUpdate then |
| 1130 | print("-------------------------------------------------------------") |
| 1131 | print(self.configFileName) |
| 1132 | for _, spec in ipairs(self.eventListeners["writeUpdateStream"]) do |
| 1133 | local className = ClassUtil.getClassName(spec) |
| 1134 | local startBits = streamGetWriteOffset(streamId) |
| 1135 | spec["writeUpdateStream"](self, streamId, connection, dirtyMask) |
| 1136 | print(" "..tostring(className).." Wrote " .. streamGetWriteOffset(streamId)-startBits .. " bits") |
| 1137 | end |
| 1138 | else |
| 1139 | SpecializationUtil.raiseEvent(self, "onWriteUpdateStream", streamId, connection, dirtyMask) |
| 1140 | end |
| 1141 | end |
updateTickDefinition
updateTick(float dt)Arguments
| float | dt | time since last call in ms |
| 1281 | function Vehicle:updateTick(dt) |
| 1282 | |
| 1283 | local isActive = self:getIsActive() |
| 1284 | local isActiveForInput = self:getIsActiveForInput() |
| 1285 | local isSelected = self:getIsSelected() |
| 1286 | |
| 1287 | self.wasTooFast = false |
| 1288 | if self.isServer then |
| 1289 | if self.synchronizePosition then |
| 1290 | local hasOwner = self:getOwner() ~= nil |
| 1291 | for i=1, table.getn(self.components) do |
| 1292 | local component = self.components[i] |
| 1293 | if not component.isStatic then |
| 1294 | local x,y,z = getWorldTranslation(component.node) |
| 1295 | local x_rot,y_rot,z_rot=getWorldRotation(component.node) |
| 1296 | local sentTranslation = component.sentTranslation |
| 1297 | local sentRotation = component.sentRotation |
| 1298 | if hasOwner or |
| 1299 | math.abs(x-sentTranslation[1]) > 0.005 or |
| 1300 | math.abs(y-sentTranslation[2]) > 0.005 or |
| 1301 | math.abs(z-sentTranslation[3]) > 0.005 or |
| 1302 | math.abs(x_rot-sentRotation[1]) > 0.1 or |
| 1303 | math.abs(y_rot-sentRotation[2]) > 0.1 or |
| 1304 | math.abs(z_rot-sentRotation[3]) > 0.1 |
| 1305 | then |
| 1306 | self:raiseDirtyFlags(self.vehicleDirtyFlag) |
| 1307 | sentTranslation[1] = x |
| 1308 | sentTranslation[2] = y |
| 1309 | sentTranslation[3] = z |
| 1310 | sentRotation[1] = x_rot |
| 1311 | sentRotation[2] = y_rot |
| 1312 | sentRotation[3] = z_rot |
| 1313 | |
| 1314 | self.lastMoveTime = g_currentMission.time |
| 1315 | end |
| 1316 | end |
| 1317 | end |
| 1318 | end |
| 1319 | |
| 1320 | -- is the vehicle sunken in the water? |
| 1321 | self.showTailwaterDepthWarning = false |
| 1322 | if not self.isBroken and not g_gui:getIsGuiVisible() then |
| 1323 | local tailwaterDepth = self:getTailwaterDepth() |
| 1324 | if tailwaterDepth > self.thresholdTailwaterDepthWarning then |
| 1325 | self.showTailwaterDepthWarning = true |
| 1326 | if tailwaterDepth > self.thresholdTailwaterDepth then |
| 1327 | self:setBroken() |
| 1328 | end |
| 1329 | end |
| 1330 | end |
| 1331 | |
| 1332 | local rootAttacherVehicle = self:getRootVehicle() |
| 1333 | if rootAttacherVehicle ~= nil and rootAttacherVehicle ~= self then |
| 1334 | rootAttacherVehicle.showTailwaterDepthWarning = rootAttacherVehicle.showTailwaterDepthWarning or self.showTailwaterDepthWarning |
| 1335 | end |
| 1336 | end |
| 1337 | |
| 1338 | if self:getIsOperating() then |
| 1339 | self:setOperatingTime(self.operatingTime + dt) |
| 1340 | end |
| 1341 | |
| 1342 | SpecializationUtil.raiseEvent(self, "onUpdateTick", dt, isActiveForInput, isSelected) |
| 1343 | SpecializationUtil.raiseEvent(self, "onPostUpdateTick", dt, isActiveForInput, isSelected) |
| 1344 | end |
Draw UI infoDefinition
drawUIInfo()Code
| 1371 | function Vehicle:drawUIInfo() |
| 1372 | if g_showVehicleDistance then |
| 1373 | local dist = calcDistanceFrom(self.rootNode, getCamera()) |
| 1374 | if dist <= 350 then |
| 1375 | Utils.renderTextAtWorldPosition(x,y+1,z, string.format("%.0f", dist), getCorrectTextSize(0.02), 0) |
| 1376 | end |
| 1377 | end |
| 1378 | end |
Add component nodes to listDefinition
addNodeObjectMapping(table list)Arguments
| table | list | list |
| 1396 | function Vehicle:addNodeObjectMapping(list) |
| 1397 | for _,v in pairs(self.components) do |
| 1398 | list[v.node] = self |
| 1399 | end |
| 1400 | end |
Remove component nodes from listDefinition
removeNodeObjectMapping(table list)Arguments
| table | list | list |
| 1405 | function Vehicle:removeNodeObjectMapping(list) |
| 1406 | for _,v in pairs(self.components) do |
| 1407 | list[v.node] = nil |
| 1408 | end |
| 1409 | end |
Add vehicle to physicsDefinition
addToPhysics()Return Values
| boolean | success | success |
| 1414 | function Vehicle:addToPhysics() |
| 1415 | |
| 1416 | if not self.isAddedToPhysics then |
| 1417 | local lastMotorizedNode = nil |
| 1418 | for _, component in pairs(self.components) do |
| 1419 | addToPhysics(component.node) |
| 1420 | if component.motorized then |
| 1421 | if lastMotorizedNode ~= nil then |
| 1422 | if self.isServer then |
| 1423 | addVehicleLink(lastMotorizedNode, component.node) |
| 1424 | end |
| 1425 | end |
| 1426 | lastMotorizedNode = component.node |
| 1427 | end |
| 1428 | end |
| 1429 | |
| 1430 | self.isAddedToPhysics = true |
| 1431 | |
| 1432 | if self.isServer then |
| 1433 | for _, jointDesc in pairs(self.componentJoints) do |
| 1434 | self:createComponentJoint(self.components[jointDesc.componentIndices[1]], self.components[jointDesc.componentIndices[2]], jointDesc) |
| 1435 | end |
| 1436 | |
| 1437 | -- if rootnode is sleeping all other components are sleeping as well |
| 1438 | addWakeUpReport(self.rootNode, "onVehicleWakeUpCallback", self) |
| 1439 | end |
| 1440 | |
| 1441 | for _, collisionPair in pairs(self.collisionPairs) do |
| 1442 | setPairCollision(collisionPair.component1.node, collisionPair.component2.node, collisionPair.enabled) |
| 1443 | end |
| 1444 | |
| 1445 | self:setMassDirty() |
| 1446 | end |
| 1447 | |
| 1448 | return true |
| 1449 | end |
Remove vehicle from physicsDefinition
removeFromPhysics()Code
| 1453 | function Vehicle:removeFromPhysics() |
| 1454 | for _, component in pairs(self.components) do |
| 1455 | removeFromPhysics(component.node) |
| 1456 | end |
| 1457 | -- invalidate wheel shapes and component joints (removing the components removes the wheels and joints too) |
| 1458 | if self.isServer then |
| 1459 | for _, jointDesc in pairs(self.componentJoints) do |
| 1460 | jointDesc.jointIndex = 0 |
| 1461 | end |
| 1462 | removeWakeUpReport(self.rootNode) |
| 1463 | end |
| 1464 | self.isAddedToPhysics = false |
| 1465 | |
| 1466 | return true |
| 1467 | end |
Set relative position of vehicleDefinition
setRelativePosition(float positionX, float offsetY, float positionZ, float yRot)Arguments
| float | positionX | x position |
| float | offsetY | y offset |
| float | positionZ | z position |
| float | yRot | y rotation |
| 1475 | function Vehicle:setRelativePosition(positionX, offsetY, positionZ, yRot) |
| 1476 | -- position the vehicle |
| 1477 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, positionX, 300, positionZ) |
| 1478 | |
| 1479 | self:setAbsolutePosition(positionX, terrainHeight+offsetY, positionZ, 0, yRot, 0) |
| 1480 | end |
Set world position and rotation of componentDefinition
setWorldPosition(float x, float y, float z, float xRot, float yRot, float zRot, integer i, boolean changeInterp)Arguments
| float | x | x position |
| float | y | y position |
| float | z | z position |
| float | xRot | x rotation |
| float | yRot | y rotation |
| float | zRot | z rotation |
| integer | i | index if component |
| boolean | changeInterp | change interpolation |
| 1524 | function Vehicle:setWorldPosition(x,y,z, xRot,yRot,zRot, i, changeInterp) |
| 1525 | local component = self.components[i] |
| 1526 | setWorldTranslation(component.node, x,y,z) |
| 1527 | setWorldRotation(component.node, xRot,yRot,zRot) |
| 1528 | if changeInterp then |
| 1529 | local qx, qy, qz, qw = mathEulerToQuaternion(xRot,yRot,zRot) |
| 1530 | component.networkInterpolators.quaternion:setQuaternion(qx, qy, qz, qw) |
| 1531 | component.networkInterpolators.position:setPosition(x,y,z) |
| 1532 | end |
| 1533 | end |
Set world position and quaternion rotation of componentDefinition
setWorldPositionQuaternion(float x, float y, float z, float qx, float qy, float qz, float qw, integer i, boolean changeInterp)Arguments
| float | x | x position |
| float | y | y position |
| float | z | z position |
| float | qx | x rotation |
| float | qy | y rotation |
| float | qz | z rotation |
| float | qw | w rotation |
| integer | i | index if component |
| boolean | changeInterp | change interpolation |
| 1546 | function Vehicle:setWorldPositionQuaternion(x,y,z, qx,qy,qz,qw, i, changeInterp) |
| 1547 | local component = self.components[i] |
| 1548 | setWorldTranslation(component.node, x,y,z) |
| 1549 | setWorldQuaternion(component.node, qx,qy,qz,qw) |
| 1550 | if changeInterp then |
| 1551 | component.networkInterpolators.quaternion:setQuaternion(qx, qy, qz, qw) |
| 1552 | component.networkInterpolators.position:setPosition(x,y,z) |
| 1553 | end |
| 1554 | end |
Returns priceDefinition
getPrice(float price)Arguments
| float | price | price |
| 1578 | function Vehicle:getPrice() |
| 1579 | return self.price |
| 1580 | end |
Get sell priceDefinition
getSellPrice()Return Values
| float | sellPrice | sell price |
| 1585 | function Vehicle:getSellPrice() |
| 1586 | local priceMultiplier = 0.75 |
| 1587 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 1588 | local maxVehicleAge = storeItem.lifetime |
| 1589 | |
| 1590 | if maxVehicleAge ~= nil and maxVehicleAge ~= 0 then |
| 1591 | local ageMultiplier = 0.5 * math.min(self.age/maxVehicleAge, 1) |
| 1592 | local operatingTime = self.operatingTime / (1000*60*60) |
| 1593 | local operatingTimeMultiplier = 0.5 * math.min(operatingTime / (maxVehicleAge*EconomyManager.LIFETIME_OPERATINGTIME_RATIO), 1) |
| 1594 | priceMultiplier = priceMultiplier * math.exp(-3.5 * (ageMultiplier+operatingTimeMultiplier)) |
| 1595 | end |
| 1596 | |
| 1597 | return math.max(math.floor(self:getPrice() * math.max(priceMultiplier, 0.05)) - self:getRepairPrice(true), 0) |
| 1598 | end |
Returns true if vehicle is on a fieldDefinition
getIsOnField()Return Values
| boolean | isOnField | is on field |
| 1603 | function Vehicle:getIsOnField() |
| 1604 | local densityBits = 0 |
| 1605 | for _,component in pairs(self.components) do |
| 1606 | local wx, wy, wz = getWorldTranslation(component.node) |
| 1607 | |
| 1608 | local h = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz) |
| 1609 | if h-1 > wy then -- 1m threshold since ground tools are working slightly under the ground |
| 1610 | break |
| 1611 | end |
| 1612 | |
| 1613 | local bits = getDensityAtWorldPos(g_currentMission.terrainDetailId, wx, wy, wz) |
| 1614 | densityBits = bitOR(densityBits, bits) |
| 1615 | if densityBits ~= 0 then |
| 1616 | return true |
| 1617 | end |
| 1618 | end |
| 1619 | |
| 1620 | return false |
| 1621 | end |
Get parent component of nodeDefinition
getParentComponent(integer node)Arguments
| integer | node | id of node |
| integer | parentComponent | id of parent component node |
| 1627 | function Vehicle:getParentComponent(node) |
| 1628 | while node ~= 0 do |
| 1629 | if self:getIsVehicleNode(node) then |
| 1630 | return node |
| 1631 | end |
| 1632 | node = getParent(node) |
| 1633 | end |
| 1634 | return 0 |
| 1635 | end |
Returns last speed in kphDefinition
getLastSpeed(boolean useAttacherVehicleSpeed)Arguments
| boolean | useAttacherVehicleSpeed | use speed of attacher vehicle |
| float | lastSpeed | last speed |
| 1641 | function Vehicle:getLastSpeed(useAttacherVehicleSpeed) |
| 1642 | if useAttacherVehicleSpeed then |
| 1643 | if self.attacherVehicle ~= nil then |
| 1644 | return self.attacherVehicle:getLastSpeed(true) |
| 1645 | end |
| 1646 | end |
| 1647 | |
| 1648 | return self.lastSpeed * 3600 |
| 1649 | end |
Get owner of vehicleDefinition
getOwner()Return Values
| table | owner | owner |
| 1659 | function Vehicle:getOwner() |
| 1660 | if self.owner ~= nil then |
| 1661 | return self.owner |
| 1662 | end |
| 1663 | |
| 1664 | return nil |
| 1665 | end |
Get the active farm. Preferable the one of the controlling player. Otherwise the owner.Definition
getActiveFarm()Return Values
| integer |
Returns true if node is from vehicleDefinition
getIsVehicleNode(integer nodeId)Arguments
| integer | nodeId | node id |
| boolean | isFromVehicle | is from vehicle |
| 1677 | function Vehicle:getIsVehicleNode(nodeId) |
| 1678 | return self.vehicleNodes[nodeId] ~= nil |
| 1679 | end |
Returns true if is operatingDefinition
getIsOperating()Return Values
| boolean | isOperating | is operating |
| 1684 | function Vehicle:getIsOperating() |
| 1685 | return false |
| 1686 | end |
Returns total mass of vehicle (optional including attached vehicles)Definition
getTotalMass(boolean onlyGivenVehicle)Arguments
| boolean | onlyGivenVehicle | use only the given vehicle, if false or nil it includes all attachables |
| float | totalMass | total mass |
| 2070 | function Vehicle:getTotalMass(onlyGivenVehicle) |
| 2071 | if self.isServer then |
| 2072 | local mass = 0 |
| 2073 | |
| 2074 | for _, component in ipairs(self.components) do |
| 2075 | mass = mass + component.mass |
| 2076 | end |
| 2077 | |
| 2078 | return mass |
| 2079 | end |
| 2080 | |
| 2081 | return 0 |
| 2082 | end |
Get fill level informationDefinition
getFillLevelInformation(table fillLevelInformations)Arguments
| table | fillLevelInformations | fill level informations |
| 2087 | function Vehicle:getFillLevelInformation(fillLevelInformations) |
| 2088 | end |
Called on activateDefinition
activate()Code
| 2092 | function Vehicle:activate() |
| 2093 | SpecializationUtil.raiseEvent(self, "onActivate") |
| 2094 | end |
Called on deactivateDefinition
deactivate()Code
| 2098 | function Vehicle:deactivate() |
| 2099 | if self:getDeactivateOnLeave() then |
| 2100 | SpecializationUtil.raiseEvent(self, "onDeactivate") |
| 2101 | end |
| 2102 | end |
Set component joint frameDefinition
setComponentJointFrame(integer jointDesc, integer anchorActor)Arguments
| integer | jointDesc | joint desc index |
| integer | anchorActor | anchor actor |
| 2109 | function Vehicle:setComponentJointFrame(jointDesc, anchorActor) |
| 2110 | if anchorActor == 0 then |
| 2111 | local localPoses = jointDesc.jointLocalPoses[1] |
| 2112 | localPoses.trans[1], localPoses.trans[2], localPoses.trans[3] = localToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0) |
| 2113 | localPoses.rot[1], localPoses.rot[2], localPoses.rot[3] = localRotationToLocal(jointDesc.jointNode, self.components[jointDesc.componentIndices[1]].node, 0, 0, 0) |
| 2114 | else |
| 2115 | local localPoses = jointDesc.jointLocalPoses[2] |
| 2116 | localPoses.trans[1], localPoses.trans[2], localPoses.trans[3] = localToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0) |
| 2117 | localPoses.rot[1], localPoses.rot[2], localPoses.rot[3] = localRotationToLocal(jointDesc.jointNodeActor1, self.components[jointDesc.componentIndices[2]].node, 0, 0, 0) |
| 2118 | end |
| 2119 | |
| 2120 | local jointNode = jointDesc.jointNode |
| 2121 | if anchorActor == 1 then |
| 2122 | jointNode = jointDesc.jointNodeActor1 |
| 2123 | end |
| 2124 | |
| 2125 | if jointDesc.jointIndex ~= 0 then |
| 2126 | setJointFrame(jointDesc.jointIndex, anchorActor, jointNode) |
| 2127 | end |
| 2128 | end |
Set component joint rot limitDefinition
setComponentJointRotLimit(integer componentJoint, integer axis, float minLimit, float maxLimit)Arguments
| integer | componentJoint | index of component joint |
| integer | axis | axis |
| float | minLimit | min limit |
| float | maxLimit | max limit |
| 2137 | function Vehicle:setComponentJointRotLimit(componentJoint, axis, minLimit, maxLimit) |
| 2138 | if self.isServer then |
| 2139 | componentJoint.rotLimit[axis] = maxLimit |
| 2140 | componentJoint.rotMinLimit[axis] = minLimit |
| 2141 | |
| 2142 | if componentJoint.jointIndex ~= 0 then |
| 2143 | if minLimit <= maxLimit then |
| 2144 | setJointRotationLimit(componentJoint.jointIndex, axis-1, true, minLimit, maxLimit) |
| 2145 | else |
| 2146 | setJointRotationLimit(componentJoint.jointIndex, axis-1, false, 0, 0) |
| 2147 | end |
| 2148 | end |
| 2149 | end |
| 2150 | end |
Set component joint trans limitDefinition
setComponentJointTransLimit(integer componentJoint, integer axis, float minLimit, float maxLimit)Arguments
| integer | componentJoint | index of component joint |
| integer | axis | axis |
| float | minLimit | min limit |
| float | maxLimit | max limit |
| 2158 | function Vehicle:setComponentJointTransLimit(componentJoint, axis, minLimit, maxLimit) |
| 2159 | if self.isServer then |
| 2160 | componentJoint.transLimit[axis] = maxLimit |
| 2161 | componentJoint.transMinLimit[axis] = minLimit |
| 2162 | |
| 2163 | if componentJoint.jointIndex ~= 0 then |
| 2164 | if minLimit <= maxLimit then |
| 2165 | setJointTranslationLimit(componentJoint.jointIndex, axis-1, true, minLimit, maxLimit) |
| 2166 | else |
| 2167 | setJointTranslationLimit(componentJoint.jointIndex, axis-1, false, 0, 0) |
| 2168 | end |
| 2169 | end |
| 2170 | end |
| 2171 | end |
Load component from xmlDefinition
loadComponentFromXML(table component, integer xmlFile, string key, table rootPosition, integer i)Arguments
| table | component | component |
| integer | xmlFile | id of xml object |
| string | key | key |
| table | rootPosition | root position (x, y, z) |
| integer | i | component index |
| boolean | success | success |
| 2181 | function Vehicle:loadComponentFromXML(component, xmlFile, key, rootPosition, i) |
| 2182 | if not self.isServer then |
| 2183 | if getRigidBodyType(component.node) == "Dynamic" then |
| 2184 | setRigidBodyType(component.node, "Kinematic") |
| 2185 | end |
| 2186 | end |
| 2187 | link(getRootNode(), component.node) |
| 2188 | if i == 1 then |
| 2189 | rootPosition[1], rootPosition[2], rootPosition[3] = getTranslation(component.node) |
| 2190 | if rootPosition[2] ~= 0 then |
| 2191 | g_logManager:xmlWarning(self.configFileName, "Y-Translation of component 1 (node 0>) has to be 0. Current value is: %.5f", rootPosition[2]) |
| 2192 | end |
| 2193 | end |
| 2194 | |
| 2195 | if getRigidBodyType(component.node) == "Static" then |
| 2196 | component.isStatic = true |
| 2197 | elseif getRigidBodyType(component.node) == "Kinematic" then |
| 2198 | component.isKinematic = true |
| 2199 | elseif getRigidBodyType(component.node) == "Dynamic" then |
| 2200 | component.isDynamic = true |
| 2201 | end |
| 2202 | |
| 2203 | -- the position of the first component is the zero |
| 2204 | translate(component.node, -rootPosition[1], -rootPosition[2], -rootPosition[3]) |
| 2205 | local x,y,z = getTranslation(component.node) |
| 2206 | local rx,ry,rz = getRotation(component.node) |
| 2207 | component.originalTranslation = {x,y,z} |
| 2208 | component.originalRotation = {rx,ry,rz} |
| 2209 | |
| 2210 | component.sentTranslation = {x,y,z} |
| 2211 | component.sentRotation = {rx,ry,rz} |
| 2212 | |
| 2213 | component.defaultMass = nil |
| 2214 | component.mass = nil |
| 2215 | |
| 2216 | local mass = getXMLFloat(xmlFile, key.."#mass") |
| 2217 | if mass ~= nil then |
| 2218 | if mass < 10 then |
| 2219 | g_logManager:xmlDevWarning(self.configFileName, "Mass is lower than 10kg for '%s'. Mass unit is kilogramms. Is this correct?", key) |
| 2220 | end |
| 2221 | if component.isDynamic then |
| 2222 | setMass(component.node, mass/1000) |
| 2223 | end |
| 2224 | |
| 2225 | component.defaultMass = mass/1000 |
| 2226 | component.mass = component.defaultMass |
| 2227 | component.lastMass = component.mass |
| 2228 | else |
| 2229 | g_logManager:xmlWarning(self.configFileName, "Missing 'mass' for '%s'. Using default mass 500kg instead!", key) |
| 2230 | component.defaultMass = 0.5 |
| 2231 | component.mass = 0.5 |
| 2232 | component.lastMass = component.mass |
| 2233 | end |
| 2234 | |
| 2235 | local comStr = getXMLString(xmlFile, key .. "#centerOfMass"); |
| 2236 | if comStr ~= nil then |
| 2237 | local com = StringUtil.getVectorNFromString(comStr, 3) |
| 2238 | if com ~= nil then |
| 2239 | setCenterOfMass(component.node, com[1], com[2], com[3]) |
| 2240 | else |
| 2241 | g_logManager:xmlWarning(self.configFileName, "Invalid center of mass given for '%s'. Ignoring this definition", key) |
| 2242 | end |
| 2243 | end |
| 2244 | local count = getXMLInt(xmlFile, key .. "#solverIterationCount") |
| 2245 | if count ~= nil then |
| 2246 | setSolverIterationCount(component.node, count) |
| 2247 | component.solverIterationCount = count |
| 2248 | end |
| 2249 | component.motorized = getXMLBool(xmlFile, key .. "#motorized") -- Note: motorized is nil if not set in the xml, and can be set by the wheels |
| 2250 | self.vehicleNodes[component.node] = {component=component} |
| 2251 | local clipDistance = getClipDistance(component.node) |
| 2252 | if clipDistance >= 1000000 and getVisibility(component.node) then |
| 2253 | local defaultClipdistance = 300 |
| 2254 | g_logManager:xmlWarning(self.configFileName, "No clipdistance is set to component node '%s' (%s>). Set default clipdistance '%d'", getName(component.node), i-1, defaultClipdistance) |
| 2255 | setClipDistance(component.node, defaultClipdistance) |
| 2256 | end |
| 2257 | |
| 2258 | component.collideWithAttachables = Utils.getNoNil(getXMLBool(xmlFile, key.."#collideWithAttachables"), false) |
| 2259 | |
| 2260 | if getRigidBodyType(component.node) ~= "NoRigidBody" then |
| 2261 | if getLinearDamping(component.node) > 0.01 then |
| 2262 | g_logManager:xmlDevWarning(self.configFileName, "Non-zero linear damping (%.4f) for component node '%s' (%s>). Is this correct?", getLinearDamping(component.node), getName(component.node), i-1) |
| 2263 | elseif getAngularDamping(component.node) > 0.05 then |
| 2264 | g_logManager:xmlDevWarning(self.configFileName, "Large angular damping (%.4f) for component node '%s' (%s>). Is this correct?", getAngularDamping(component.node), getName(component.node), i-1) |
| 2265 | elseif getAngularDamping(component.node) < 0.0001 then |
| 2266 | g_logManager:xmlDevWarning(self.configFileName, "Zero damping for component node '%s' (%s>). Is this correct?", getName(component.node), i-1) |
| 2267 | end |
| 2268 | end |
| 2269 | |
| 2270 | local name = getName(component.node) |
| 2271 | if not StringUtil.endsWith(name, "component"..i) then |
| 2272 | g_logManager:xmlDevWarning(self.configFileName, "Name of component '%d' ('%s') does not correpond with the component naming convention! (vehicleName_componentName_component%d)", i, name, i) |
| 2273 | end |
| 2274 | |
| 2275 | return true |
| 2276 | end |
Load component joints from xmlDefinition
loadComponentJointFromXML(table jointDesc, integer xmlFile, string key, integer componentJointI, integer jointNode, integer index1, integer index2)Arguments
| table | jointDesc | joint desc |
| integer | xmlFile | id of xml object |
| string | key | key |
| integer | componentJointI | component joint index |
| integer | jointNode | id of joint node |
| integer | index1 | index of component 1 |
| integer | index2 | index of component 2 |
| boolean | success | success |
| 2288 | function Vehicle:loadComponentJointFromXML(jointDesc, xmlFile, key, componentJointI, jointNode, index1, index2) |
| 2289 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, key .. "#indexActor1", key .. "#nodeActor1") --FS17 to FS19 |
| 2290 | |
| 2291 | jointDesc.componentIndices = {index1, index2} |
| 2292 | jointDesc.jointNode = jointNode |
| 2293 | jointDesc.jointNodeActor1 = Utils.getNoNil(I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#nodeActor1"), self.i3dMappings), jointNode) |
| 2294 | if self.isServer then |
| 2295 | if self.components[index1] == nil or self.components[index2] == nil then |
| 2296 | g_logManager:xmlWarning(self.configFileName, "Invalid component indices (component1: %d, component2: %d) for component joint %d. Indices start with 1!", index1, index2, componentJointI) |
| 2297 | return false |
| 2298 | end |
| 2299 | |
| 2300 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimit")) |
| 2301 | local rotLimits = { math.rad(Utils.getNoNil(x, 0)), math.rad(Utils.getNoNil(y, 0)), math.rad(Utils.getNoNil(z, 0)) } |
| 2302 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimit")) |
| 2303 | local transLimits = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2304 | jointDesc.rotLimit = rotLimits |
| 2305 | jointDesc.transLimit = transLimits |
| 2306 | |
| 2307 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotMinLimit")) |
| 2308 | local rotMinLimits = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } |
| 2309 | |
| 2310 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transMinLimit")) |
| 2311 | local transMinLimits = { x,y,z } |
| 2312 | |
| 2313 | for i=1,3 do |
| 2314 | if rotMinLimits[i] == nil then |
| 2315 | if rotLimits[i] >= 0 then |
| 2316 | rotMinLimits[i] = -rotLimits[i] |
| 2317 | else |
| 2318 | rotMinLimits[i] = rotLimits[i]+1 |
| 2319 | end |
| 2320 | end |
| 2321 | if transMinLimits[i] == nil then |
| 2322 | if transLimits[i] >= 0 then |
| 2323 | transMinLimits[i] = -transLimits[i] |
| 2324 | else |
| 2325 | transMinLimits[i] = transLimits[i]+1 |
| 2326 | end |
| 2327 | end |
| 2328 | end |
| 2329 | |
| 2330 | jointDesc.jointLocalPoses = {} |
| 2331 | local trans = {localToLocal(jointDesc.jointNode, self.components[index1].node, 0, 0, 0)} |
| 2332 | local rot = {localRotationToLocal(jointDesc.jointNode, self.components[index1].node, 0, 0, 0)} |
| 2333 | jointDesc.jointLocalPoses[1] = {trans=trans, rot=rot} |
| 2334 | |
| 2335 | local trans = {localToLocal(jointDesc.jointNodeActor1, self.components[index2].node, 0, 0, 0)} |
| 2336 | local rot = {localRotationToLocal(jointDesc.jointNodeActor1, self.components[index2].node, 0, 0, 0)} |
| 2337 | jointDesc.jointLocalPoses[2] = {trans=trans, rot=rot} |
| 2338 | |
| 2339 | jointDesc.rotMinLimit = rotMinLimits |
| 2340 | jointDesc.transMinLimit = transMinLimits |
| 2341 | |
| 2342 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimitSpring")) |
| 2343 | local rotLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2344 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimitDamping")) |
| 2345 | local rotLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
| 2346 | jointDesc.rotLimitSpring = rotLimitSpring |
| 2347 | jointDesc.rotLimitDamping = rotLimitDamping |
| 2348 | |
| 2349 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotLimitForceLimit")) |
| 2350 | local rotLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) } |
| 2351 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimitForceLimit")) |
| 2352 | local transLimitForceLimit = { Utils.getNoNil(x, -1), Utils.getNoNil(y, -1), Utils.getNoNil(z, -1) } |
| 2353 | jointDesc.rotLimitForceLimit = rotLimitForceLimit |
| 2354 | jointDesc.transLimitForceLimit = transLimitForceLimit |
| 2355 | |
| 2356 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimitSpring")) |
| 2357 | local transLimitSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2358 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transLimitDamping")) |
| 2359 | local transLimitDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
| 2360 | jointDesc.transLimitSpring = transLimitSpring |
| 2361 | jointDesc.transLimitDamping = transLimitDamping |
| 2362 | |
| 2363 | jointDesc.zRotationXOffset = 0 |
| 2364 | local zRotationNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#zRotationNode"), self.i3dMappings) |
| 2365 | if zRotationNode ~= nil then |
| 2366 | jointDesc.zRotationXOffset,_,_ = localToLocal(zRotationNode, jointNode, 0,0,0) |
| 2367 | end |
| 2368 | |
| 2369 | jointDesc.isBreakable = Utils.getNoNil(getXMLBool(xmlFile, key.."#breakable"), false) |
| 2370 | if jointDesc.isBreakable then |
| 2371 | jointDesc.breakForce = Utils.getNoNil(getXMLFloat(xmlFile, key.."#breakForce"), 10) |
| 2372 | jointDesc.breakTorque = Utils.getNoNil(getXMLFloat(xmlFile, key.."#breakTorque"), 10) |
| 2373 | end |
| 2374 | jointDesc.enableCollision = Utils.getNoNil(getXMLBool(xmlFile, key.."#enableCollision"), false) |
| 2375 | |
| 2376 | -- Rotational drive |
| 2377 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#maxRotDriveForce")) |
| 2378 | local maxRotDriveForce = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2379 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveVelocity")) |
| 2380 | local rotDriveVelocity = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } -- convert from deg/s to rad/s |
| 2381 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveRotation")) |
| 2382 | local rotDriveRotation = { Utils.getNoNilRad(x, nil), Utils.getNoNilRad(y, nil), Utils.getNoNilRad(z, nil) } -- convert from deg to rad |
| 2383 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveSpring")) |
| 2384 | local rotDriveSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2385 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#rotDriveDamping")) |
| 2386 | local rotDriveDamping = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2387 | |
| 2388 | jointDesc.rotDriveVelocity = rotDriveVelocity |
| 2389 | jointDesc.rotDriveRotation = rotDriveRotation |
| 2390 | jointDesc.rotDriveSpring = rotDriveSpring |
| 2391 | jointDesc.rotDriveDamping = rotDriveDamping |
| 2392 | jointDesc.maxRotDriveForce = maxRotDriveForce |
| 2393 | |
| 2394 | -- Translational drive |
| 2395 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDriveVelocity")) |
| 2396 | local transDriveVelocity = { x,y,z } |
| 2397 | |
| 2398 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDrivePosition")) |
| 2399 | local transDrivePosition = { x,y,z } |
| 2400 | |
| 2401 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDriveSpring")) |
| 2402 | local transDriveSpring = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2403 | |
| 2404 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#transDriveDamping")) |
| 2405 | local transDriveDamping = { Utils.getNoNil(x, 1), Utils.getNoNil(y, 1), Utils.getNoNil(z, 1) } |
| 2406 | |
| 2407 | local x, y, z = StringUtil.getVectorFromString(getXMLString(xmlFile, key.."#maxTransDriveForce")) |
| 2408 | local maxTransDriveForce = { Utils.getNoNil(x, 0), Utils.getNoNil(y, 0), Utils.getNoNil(z, 0) } |
| 2409 | |
| 2410 | jointDesc.transDriveVelocity = transDriveVelocity |
| 2411 | jointDesc.transDrivePosition = transDrivePosition |
| 2412 | jointDesc.transDriveSpring = transDriveSpring |
| 2413 | jointDesc.transDriveDamping = transDriveDamping |
| 2414 | jointDesc.maxTransDriveForce = maxTransDriveForce |
| 2415 | |
| 2416 | jointDesc.jointIndex = 0 |
| 2417 | end |
| 2418 | |
| 2419 | return true |
| 2420 | end |
Create component joint between two componentsDefinition
createComponentJoint(table component1, table component2, table jointDesc)Arguments
| table | component1 | component 1 |
| table | component2 | component 2 |
| table | jointDesc | joint desc |
| boolean | success | success |
| 2428 | function Vehicle:createComponentJoint(component1, component2, jointDesc) |
| 2429 | if component1 == nil or component2 == nil or jointDesc == nil then |
| 2430 | g_logManager:xmlWarning(self.configFileName, "Could not create component joint. No component1, component2 or jointDesc given!") |
| 2431 | return false |
| 2432 | end |
| 2433 | |
| 2434 | local constr = JointConstructor:new() |
| 2435 | constr:setActors(component1.node, component2.node) |
| 2436 | |
| 2437 | local localPoses1 = jointDesc.jointLocalPoses[1] |
| 2438 | local localPoses2 = jointDesc.jointLocalPoses[2] |
| 2439 | constr:setJointLocalPositions(localPoses1.trans[1], localPoses1.trans[2], localPoses1.trans[3], localPoses2.trans[1], localPoses2.trans[2], localPoses2.trans[3]) |
| 2440 | constr:setJointLocalRotations(localPoses1.rot[1], localPoses1.rot[2], localPoses1.rot[3], localPoses2.rot[1], localPoses2.rot[2], localPoses2.rot[3]) |
| 2441 | --constr:setJointTransforms(jointDesc.jointNode, jointDesc.jointNodeActor1) |
| 2442 | |
| 2443 | constr:setRotationLimitSpring(jointDesc.rotLimitSpring[1], jointDesc.rotLimitDamping[1], jointDesc.rotLimitSpring[2], jointDesc.rotLimitDamping[2], jointDesc.rotLimitSpring[3], jointDesc.rotLimitDamping[3]) |
| 2444 | constr:setTranslationLimitSpring(jointDesc.transLimitSpring[1], jointDesc.transLimitDamping[1], jointDesc.transLimitSpring[2], jointDesc.transLimitDamping[2], jointDesc.transLimitSpring[3], jointDesc.transLimitDamping[3]) |
| 2445 | constr:setZRotationXOffset(jointDesc.zRotationXOffset) |
| 2446 | for i=1, 3 do |
| 2447 | if jointDesc.rotLimit[i] >= jointDesc.rotMinLimit[i] then |
| 2448 | constr:setRotationLimit(i-1, jointDesc.rotMinLimit[i], jointDesc.rotLimit[i]) |
| 2449 | end |
| 2450 | |
| 2451 | if jointDesc.transLimit[i] >= jointDesc.transMinLimit[i] then |
| 2452 | constr:setTranslationLimit(i-1, true, jointDesc.transMinLimit[i], jointDesc.transLimit[i]) |
| 2453 | else |
| 2454 | constr:setTranslationLimit(i-1, false, 0, 0) |
| 2455 | end |
| 2456 | end |
| 2457 | |
| 2458 | constr:setRotationLimitForceLimit(jointDesc.rotLimitForceLimit[1], jointDesc.rotLimitForceLimit[2], jointDesc.rotLimitForceLimit[3]) |
| 2459 | constr:setTranslationLimitForceLimit(jointDesc.transLimitForceLimit[1], jointDesc.transLimitForceLimit[2], jointDesc.transLimitForceLimit[3]) |
| 2460 | |
| 2461 | if jointDesc.isBreakable then |
| 2462 | constr:setBreakable(jointDesc.breakForce, jointDesc.breakTorque) |
| 2463 | end |
| 2464 | constr:setEnableCollision(jointDesc.enableCollision) |
| 2465 | |
| 2466 | for i=1,3 do |
| 2467 | if jointDesc.maxRotDriveForce[i] > 0.0001 and (jointDesc.rotDriveVelocity[i] ~= nil or jointDesc.rotDriveRotation[i] ~= nil) then |
| 2468 | local pos = Utils.getNoNil(jointDesc.rotDriveRotation[i], 0) |
| 2469 | local vel = Utils.getNoNil(jointDesc.rotDriveVelocity[i], 0) |
| 2470 | constr:setAngularDrive(i-1, jointDesc.rotDriveRotation[i] ~= nil, jointDesc.rotDriveVelocity[i] ~= nil, jointDesc.rotDriveSpring[i], jointDesc.rotDriveDamping[i], jointDesc.maxRotDriveForce[i], pos, vel) |
| 2471 | end |
| 2472 | if jointDesc.maxTransDriveForce[i] > 0.0001 and (jointDesc.transDriveVelocity[i] ~= nil or jointDesc.transDrivePosition[i] ~= nil) then |
| 2473 | local pos = Utils.getNoNil(jointDesc.transDrivePosition[i], 0) |
| 2474 | local vel = Utils.getNoNil(jointDesc.transDriveVelocity[i], 0) |
| 2475 | constr:setLinearDrive(i-1, jointDesc.transDrivePosition[i] ~= nil, jointDesc.transDriveVelocity[i] ~= nil, jointDesc.transDriveSpring[i], jointDesc.transDriveDamping[i], jointDesc.maxTransDriveForce[i], pos, vel) |
| 2476 | end |
| 2477 | end |
| 2478 | |
| 2479 | jointDesc.jointIndex = constr:finalize() |
| 2480 | |
| 2481 | return true |
| 2482 | end |
Add a mod prefix to a schema overlay name to match name loading in HUD which avoids name collisions.Definition
First checks if there is a matching default schema overlay name and only adds the prefix if there is none.
prefixSchemaOverlayName(string baseName, string prefix)Arguments
| string | baseName | Base schema overlay name |
| string | prefix | Name prefix to add if the baseName parameter is not one of the default schema overlays |
| string | Schema | overlay name which was modified if necessary |
Load schema overlay data from xml fileDefinition
The HUD draws the schema from this information and handles all required visual components.
loadSchemaOverlay(integer xmlFile)Arguments
| integer | xmlFile | id of xml object |
| 2502 | function Vehicle:loadSchemaOverlay(xmlFile) |
| 2503 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#file") --FS17 to FS19 |
| 2504 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#width") --FS17 to FS19 |
| 2505 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#height") --FS17 to FS19 |
| 2506 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#invisibleBorderRight", "vehicle.base.schemaOverlay#invisibleBorderRight") --FS17 to FS19 |
| 2507 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#invisibleBorderLeft", "vehicle.base.schemaOverlay#invisibleBorderLeft") --FS17 to FS19 |
| 2508 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#attacherJointPosition", "vehicle.base.schemaOverlay#attacherJointPosition") --FS17 to FS19 |
| 2509 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#basePosition", "vehicle.base.schemaOverlay#basePosition") --FS17 to FS19 |
| 2510 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#fileSelected") --FS17 to FS19 |
| 2511 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#fileTurnedOn") --FS17 to FS19 |
| 2512 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.schemaOverlay#fileSelectedTurnedOn") --FS17 to FS19 |
| 2513 | |
| 2514 | if hasXMLProperty(xmlFile, "vehicle.base.schemaOverlay") then |
| 2515 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, "vehicle.schemaOverlay.attacherJoint", "vehicle.attacherJoints.attacherJoint.schema") -- FS17 |
| 2516 | |
| 2517 | local x, y = StringUtil.getVectorFromString(getXMLString(xmlFile, "vehicle.base.schemaOverlay#attacherJointPosition")) |
| 2518 | local baseX, baseY = StringUtil.getVectorFromString(getXMLString(xmlFile, "vehicle.base.schemaOverlay#basePosition")) |
| 2519 | |
| 2520 | if baseX == nil then |
| 2521 | baseX = x |
| 2522 | end |
| 2523 | |
| 2524 | if baseY == nil then |
| 2525 | baseY = y |
| 2526 | end |
| 2527 | |
| 2528 | local schemaNameDefault = getXMLString(xmlFile, "vehicle.base.schemaOverlay.default#name") or "" |
| 2529 | local schemaNameTurnedOn = getXMLString(xmlFile, "vehicle.base.schemaOverlay.turnedOn#name") or "" |
| 2530 | local schemaNameSelected = getXMLString(xmlFile, "vehicle.base.schemaOverlay.selected#name") or "" |
| 2531 | local schemaNameSelectedTurnedOn = getXMLString(xmlFile, "vehicle.base.schemaOverlay.turnedOnSelected#name") or "" |
| 2532 | |
| 2533 | local modPrefix = self.customEnvironment or "" |
| 2534 | schemaNameDefault = Vehicle.prefixSchemaOverlayName(schemaNameDefault, modPrefix) |
| 2535 | schemaNameTurnedOn = Vehicle.prefixSchemaOverlayName(schemaNameTurnedOn, modPrefix) |
| 2536 | schemaNameSelected = Vehicle.prefixSchemaOverlayName(schemaNameSelected, modPrefix) |
| 2537 | schemaNameSelectedTurnedOn = Vehicle.prefixSchemaOverlayName(schemaNameSelectedTurnedOn, modPrefix) |
| 2538 | |
| 2539 | self.schemaOverlay = VehicleSchemaOverlayData.new( |
| 2540 | baseX, baseY, |
| 2541 | schemaNameDefault, |
| 2542 | schemaNameTurnedOn, |
| 2543 | schemaNameSelected, |
| 2544 | schemaNameSelectedTurnedOn, |
| 2545 | getXMLFloat(xmlFile, "vehicle.base.schemaOverlay#invisibleBorderRight"), |
| 2546 | getXMLFloat(xmlFile, "vehicle.base.schemaOverlay#invisibleBorderLeft")) |
| 2547 | end |
| 2548 | end |
Called if day changedDefinition
dayChanged()Code
| 2556 | function Vehicle:dayChanged() |
| 2557 | self.age = self.age + 1 |
| 2558 | end |
Get speed limitDefinition
getSpeedLimit(boolean onlyIfWorking)Arguments
| boolean | onlyIfWorking | only if working |
| float | limit | limit |
| boolean | doCheckSpeedLimit | do check speed limit |
| 2641 | function Vehicle:getSpeedLimit(onlyIfWorking) |
| 2642 | local limit = math.huge |
| 2643 | local doCheckSpeedLimit = self:doCheckSpeedLimit() |
| 2644 | if onlyIfWorking == nil or (onlyIfWorking and doCheckSpeedLimit) then |
| 2645 | limit = self.speedLimit |
| 2646 | |
| 2647 | local damage = self:getVehicleDamage() |
| 2648 | if damage > 0 then |
| 2649 | limit = limit * (1 - damage * Vehicle.DAMAGED_SPEEDLIMIT_REDUCTION) |
| 2650 | end |
| 2651 | end |
| 2652 | |
| 2653 | local attachedImplements |
| 2654 | if self.getAttachedImplements ~= nil then |
| 2655 | attachedImplements = self:getAttachedImplements() |
| 2656 | end |
| 2657 | if attachedImplements ~= nil then |
| 2658 | for _, implement in pairs(attachedImplements) do |
| 2659 | if implement.object ~= nil then |
| 2660 | local speed, implementDoCheckSpeedLimit = implement.object:getSpeedLimit(onlyIfWorking) |
| 2661 | if onlyIfWorking == nil or (onlyIfWorking and implementDoCheckSpeedLimit) then |
| 2662 | limit = math.min(limit, speed) |
| 2663 | end |
| 2664 | doCheckSpeedLimit = doCheckSpeedLimit or implementDoCheckSpeedLimit |
| 2665 | end |
| 2666 | end |
| 2667 | end |
| 2668 | return limit, doCheckSpeedLimit |
| 2669 | end |
Get daily up keepDefinition
getDailyUpkeep()Return Values
| float | dailyUpkeep | daily up keep |
| 2682 | function Vehicle:getDailyUpkeep() |
| 2683 | local storeItem = g_storeManager:getItemByXMLFilename(self.configFileName) |
| 2684 | |
| 2685 | local multiplier = 1 |
| 2686 | if storeItem.lifetime ~= nil and storeItem.lifetime ~= 0 then |
| 2687 | local ageMultiplier = 0.3 * math.min(self.age/storeItem.lifetime, 1) |
| 2688 | local operatingTime = self.operatingTime / (1000*60*60) |
| 2689 | local operatingTimeMultiplier = 0.7 * math.min(operatingTime / (storeItem.lifetime*EconomyManager.LIFETIME_OPERATINGTIME_RATIO), 1) |
| 2690 | multiplier = 1 + EconomyManager.MAX_DAILYUPKEEP_MULTIPLIER * (ageMultiplier+operatingTimeMultiplier) |
| 2691 | end |
| 2692 | |
| 2693 | return StoreItemUtil.getDailyUpkeep(storeItem, self.configurations) * multiplier |
| 2694 | end |
Get reload xmlDefinition
getReloadXML(table vehicle)Arguments
| table | vehicle | vehicle |
| string | xml | xml |
| 2767 | function Vehicle.getReloadXML(vehicle) |
| 2768 | |
| 2769 | local vehicleXMLFile = createXMLFile("vehicleXMLFile", "", "vehicles") |
| 2770 | if vehicleXMLFile ~= nil then |
| 2771 | local key = string.format("vehicles.vehicle(%d)", 0) |
| 2772 | |
| 2773 | setXMLInt(vehicleXMLFile, key.."#id", 1) |
| 2774 | setXMLString(vehicleXMLFile, key.."#filename", HTMLUtil.encodeToHTML(NetworkUtil.convertToNetworkFilename(vehicle.configFileName))) |
| 2775 | |
| 2776 | vehicle:saveToXMLFile(vehicleXMLFile, key, {}) |
| 2777 | |
| 2778 | return vehicleXMLFile |
| 2779 | end |
| 2780 | |
| 2781 | return nil |
| 2782 | end |
Creating vehicle cameraDefinition
new(boolean isServer, boolean isClient, table customMt)Arguments
| boolean | isServer | is server |
| boolean | isClient | is client |
| table | customMt | custom metatable |
| table | instance | Instance of object |
| 23 | function VehicleCamera:new(vehicle, customMt) |
| 24 | |
| 25 | local instance = {} |
| 26 | if customMt ~= nil then |
| 27 | setmetatable(instance, customMt) |
| 28 | else |
| 29 | setmetatable(instance, VehicleCamera_mt) |
| 30 | end |
| 31 | |
| 32 | |
| 33 | instance.vehicle = vehicle |
| 34 | instance.isActivated = false |
| 35 | |
| 36 | instance.limitRotXDelta = 0 |
| 37 | |
| 38 | instance.raycastDistance = 0 |
| 39 | instance.normalX = 0 |
| 40 | instance.normalY = 0 |
| 41 | instance.normalZ = 0 |
| 42 | |
| 43 | instance.raycastNodes = {} |
| 44 | instance.disableCollisionTime = -1 |
| 45 | |
| 46 | instance.lookAtPosition = {0,0,0} |
| 47 | instance.lookAtLastTargetPosition = {0,0,0} |
| 48 | instance.position = {0,0,0} |
| 49 | instance.lastTargetPosition = {0,0,0} |
| 50 | |
| 51 | instance.lastInputValues = {} |
| 52 | instance.lastInputValues.upDown = 0 |
| 53 | instance.lastInputValues.leftRight = 0 |
| 54 | |
| 55 | instance.isCollisionEnabled = not g_modIsLoaded["disableVehicleCameraCollision"] |
| 56 | |
| 57 | return instance |
| 58 | end |
Load vehicle camera from xml fileDefinition
loadFromXML(integer xmlFile, string key)Arguments
| integer | xmlFile | id of xml object |
| string | key | key |
| boolean | success | success |
| 65 | function VehicleCamera:loadFromXML(xmlFile, key) |
| 66 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.vehicle.configFileName, key .. "#index", "#node") -- FS17 to FS19 |
| 67 | |
| 68 | local camIndexStr = getXMLString(xmlFile, key .. "#node") |
| 69 | self.cameraNode = I3DUtil.indexToObject(self.vehicle.components, camIndexStr, self.vehicle.i3dMappings) |
| 70 | if self.cameraNode == nil or not getHasClassId(self.cameraNode, ClassIds.CAMERA) then |
| 71 | g_logManager:xmlWarning(self.vehicle.configFileName, "Invalid camera node for camera '%s'. Must be a camera type!", key) |
| 72 | return false |
| 73 | end |
| 74 | |
| 75 | self.fovY = calculateFovY(self.cameraNode) |
| 76 | setFovY(self.cameraNode, self.fovY) |
| 77 | |
| 78 | self.isRotatable = Utils.getNoNil(getXMLBool(xmlFile, key .. "#rotatable"), false) |
| 79 | self.limit = Utils.getNoNil(getXMLBool(xmlFile, key .. "#limit"), false) |
| 80 | if self.limit then |
| 81 | self.rotMinX = getXMLFloat(xmlFile, key .. "#rotMinX") |
| 82 | self.rotMaxX = getXMLFloat(xmlFile, key .. "#rotMaxX") |
| 83 | |
| 84 | self.transMin = getXMLFloat(xmlFile, key .. "#transMin") |
| 85 | self.transMax = getXMLFloat(xmlFile, key .. "#transMax") |
| 86 | if self.rotMinX == nil or self.rotMaxX == nil or self.transMin == nil or self.transMax == nil then |
| 87 | g_logManager:xmlWarning(self.vehicle.configFileName, "Missing 'rotMinX', 'rotMaxX', 'transMin' or 'transMax' for camera '%s'", key) |
| 88 | return false |
| 89 | end |
| 90 | end |
| 91 | |
| 92 | self.isInside = Utils.getNoNil(getXMLBool(xmlFile, key .. "#isInside"), false) |
| 93 | if self.isInside then |
| 94 | self.defaultLowPassGain = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#defaultLowPassGain"), 0.5) |
| 95 | self.defaultVolume = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#defaultVolume"), 0.9) |
| 96 | else |
| 97 | self.defaultLowPassGain = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#defaultLowPassGain"), 1.0) |
| 98 | self.defaultVolume = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#defaultVolume"), 1.0) |
| 99 | end |
| 100 | self.allowHeadTracking = Utils.getNoNil(getXMLBool(xmlFile, key .. "#allowHeadTracking"), self.isInside) |
| 101 | |
| 102 | local shadowBoxIndexStr = getXMLString(xmlFile, key .. "#shadowFocusBox") |
| 103 | self.shadowFocusBoxNode = I3DUtil.indexToObject(self.vehicle.components, shadowBoxIndexStr, self.vehicle.i3dMappings) |
| 104 | if self.shadowFocusBoxNode ~= nil and not getHasClassId(self.shadowFocusBoxNode, ClassIds.SHAPE) then |
| 105 | g_logManager:xmlWarning(self.vehicle.configFileName, "Invalid camera shadow focus box '%s'. Must be a shape and cpu mesh", getName(shadowFocusBoxNode)) |
| 106 | self.shadowFocusBoxNode = nil; |
| 107 | end |
| 108 | |
| 109 | if self.isInside and self.shadowFocusBoxNode == nil then |
| 110 | g_logManager:xmlDevWarning(self.vehicle.configFileName, "Missing shadow focus box for indoor camera '%s'", key) |
| 111 | end |
| 112 | |
| 113 | self.useOutdoorSounds = Utils.getNoNil(getXMLBool(xmlFile, key .. "#useOutdoorSounds"), not self.isInside) |
| 114 | |
| 115 | if self.isRotatable then |
| 116 | self.rotateNode = I3DUtil.indexToObject(self.vehicle.components, getXMLString(xmlFile, key .. "#rotateNode"), self.vehicle.i3dMappings) |
| 117 | self.hasExtraRotationNode = self.rotateNode ~= nil |
| 118 | end |
| 119 | |
| 120 | local rotation = StringUtil.getRadiansFromString(getXMLString(xmlFile, key.."#rotation"), 3) |
| 121 | if rotation ~= nil then |
| 122 | local rotationNode = self.cameraNode |
| 123 | if self.rotateNode ~= nil then |
| 124 | rotationNode = self.rotateNode |
| 125 | end |
| 126 | setRotation(rotationNode, unpack(rotation)) |
| 127 | end |
| 128 | local translation = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#translation"), 3) |
| 129 | if translation ~= nil then |
| 130 | setTranslation(self.cameraNode, unpack(translation)) |
| 131 | end |
| 132 | |
| 133 | self.allowTranslation = (self.rotateNode ~= nil and self.rotateNode ~= self.cameraNode) |
| 134 | |
| 135 | self.useMirror = Utils.getNoNil(getXMLBool(xmlFile, key .. "#useMirror"), false) |
| 136 | self.useWorldXZRotation = getXMLBool(xmlFile, key .. "#useWorldXZRotation") -- overrides the ingame setting |
| 137 | self.resetCameraOnVehicleSwitch = getXMLBool(xmlFile, key .. "#resetCameraOnVehicleSwitch") -- overrides the ingame setting |
| 138 | |
| 139 | self.positionSmoothingParameter = 0 |
| 140 | self.lookAtSmoothingParameter = 0 |
| 141 | local useDefaultPositionSmoothing = Utils.getNoNil(getXMLBool(xmlFile, key .. "#useDefaultPositionSmoothing"), true) |
| 142 | if useDefaultPositionSmoothing then |
| 143 | if self.isInside then |
| 144 | self.positionSmoothingParameter = 0.128 -- 0.095 |
| 145 | self.lookAtSmoothingParameter = 0.176 -- 0.12 |
| 146 | else |
| 147 | self.positionSmoothingParameter = 0.016 |
| 148 | self.lookAtSmoothingParameter = 0.022 |
| 149 | end |
| 150 | end |
| 151 | self.positionSmoothingParameter = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#positionSmoothingParameter"), self.positionSmoothingParameter) |
| 152 | self.lookAtSmoothingParameter = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#lookAtSmoothingParameter"), self.lookAtSmoothingParameter) |
| 153 | |
| 154 | local useHeadTracking = g_gameSettings:getValue("isHeadTrackingEnabled") and isHeadTrackingAvailable() and self.allowHeadTracking |
| 155 | if useHeadTracking then |
| 156 | self.positionSmoothingParameter = 0 |
| 157 | self.lookAtSmoothingParameter = 0 |
| 158 | end |
| 159 | |
| 160 | self.cameraPositionNode = self.cameraNode |
| 161 | if self.positionSmoothingParameter > 0 then |
| 162 | -- create a node which indicates the target position of the camera |
| 163 | self.cameraPositionNode = createTransformGroup("cameraPositionNode") |
| 164 | local camIndex = getChildIndex(self.cameraNode) |
| 165 | link(getParent(self.cameraNode), self.cameraPositionNode, camIndex) |
| 166 | local x,y,z = getTranslation(self.cameraNode) |
| 167 | local rx,ry,rz = getRotation(self.cameraNode) |
| 168 | setTranslation(self.cameraPositionNode, x,y,z) |
| 169 | setRotation(self.cameraPositionNode, rx,ry,rz) |
| 170 | |
| 171 | unlink(self.cameraNode) |
| 172 | end |
| 173 | self.rotYSteeringRotSpeed = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, key .. "#rotYSteeringRotSpeed"), 0)) |
| 174 | |
| 175 | if self.rotateNode == nil or self.rotateNode == self.cameraNode then |
| 176 | self.rotateNode = self.cameraPositionNode |
| 177 | end |
| 178 | |
| 179 | if useHeadTracking then |
| 180 | local dx,dy,dz = localDirectionToLocal(self.cameraPositionNode, getParent(self.cameraPositionNode), 0,0,1) |
| 181 | local tx,ty,tz = localToLocal(self.cameraPositionNode, getParent(self.cameraPositionNode), 0,0,0) |
| 182 | self.headTrackingNode = createTransformGroup("headTrackingNode") |
| 183 | link(getParent(self.cameraPositionNode), self.headTrackingNode) |
| 184 | setTranslation(self.headTrackingNode, tx,ty,tz) |
| 185 | if math.abs(dx)+math.abs(dz) > 0.0001 then |
| 186 | setDirection(self.headTrackingNode, dx, dy, dz, 0,1,0) |
| 187 | else |
| 188 | setRotation(self.headTrackingNode, 0,0,0) |
| 189 | end |
| 190 | end |
| 191 | |
| 192 | self.origRotX, self.origRotY, self.origRotZ = getRotation(self.rotateNode) |
| 193 | self.rotX = self.origRotX |
| 194 | self.rotY = self.origRotY |
| 195 | self.rotZ = self.origRotZ |
| 196 | |
| 197 | self.origTransX, self.origTransY, self.origTransZ = getTranslation(self.cameraPositionNode) |
| 198 | self.transX = self.origTransX |
| 199 | self.transY = self.origTransY |
| 200 | self.transZ = self.origTransZ |
| 201 | |
| 202 | local transLength = MathUtil.vector3Length(self.origTransX, self.origTransY, self.origTransZ) + 0.00001 -- prevent devision by zero |
| 203 | self.zoom = transLength |
| 204 | self.zoomTarget = transLength |
| 205 | self.zoomLimitedTarget = -1 |
| 206 | |
| 207 | local trans1OverLength = 1.0/transLength |
| 208 | self.transDirX = trans1OverLength*self.origTransX |
| 209 | self.transDirY = trans1OverLength*self.origTransY |
| 210 | self.transDirZ = trans1OverLength*self.origTransZ |
| 211 | if self.allowTranslation then |
| 212 | if transLength <= 0.01 then |
| 213 | g_logManager:xmlWarning(self.vehicle.configFileName, "Invalid camera translation for camera '%s'. Distance needs to be bigger than 0.01", key) |
| 214 | end |
| 215 | end |
| 216 | |
| 217 | table.insert(self.raycastNodes, self.rotateNode) |
| 218 | local i=0 |
| 219 | while true do |
| 220 | local raycastKey = key..string.format(".raycastNode(%d)", i) |
| 221 | if not hasXMLProperty(xmlFile, raycastKey) then |
| 222 | break |
| 223 | end |
| 224 | |
| 225 | XMLUtil.checkDeprecatedXMLElements(xmlFile, self.vehicle.configFileName, raycastKey .. "#index", raycastKey .. "#node") --FS17 to FS19 |
| 226 | |
| 227 | local node = I3DUtil.indexToObject(self.vehicle.components, getXMLString(xmlFile, raycastKey .. "#node"), self.vehicle.i3dMappings) |
| 228 | if node ~= nil then |
| 229 | table.insert(self.raycastNodes, node) |
| 230 | end |
| 231 | |
| 232 | i=i+1 |
| 233 | end |
| 234 | |
| 235 | local sx,sy,sz = getScale(self.cameraNode) |
| 236 | if sx ~= 1 or sy ~= 1 or sz ~= 1 then |
| 237 | g_logManager:xmlWarning(self.vehicle.configFileName, "Vehicle camera with scale found for camera '%s'. Resetting to scale 1", key) |
| 238 | setScale(self.cameraNode, 1,1,1) |
| 239 | end |
| 240 | |
| 241 | self.headTrackingPositionOffset = {0, 0, 0} |
| 242 | self.headTrackingRotationOffset = {0, 0, 0} |
| 243 | |
| 244 | return true |
| 245 | end |
Deleting vehicle cameraDefinition
delete()Code
| 249 | function VehicleCamera:delete() |
| 250 | if self.cameraNode ~= nil and self.positionSmoothingParameter > 0 then |
| 251 | delete(self.cameraNode) |
| 252 | self.cameraNode = nil |
| 253 | end |
| 254 | setShadowFocusBox(0) |
| 255 | end |
Zoom camera smoothlyDefinition
zoomSmoothly(float offset)Arguments
| float | offset | offset |
| 260 | function VehicleCamera:zoomSmoothly(offset) |
| 261 | --self.zoomTarget = self.zoomTarget + offset |
| 262 | --if self.limit then |
| 263 | -- self.zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget)) |
| 264 | --end |
| 265 | --self.zoomTarget = math.max(self.zoomTarget, 0.01) |
| 266 | |
| 267 | local zoomTarget = self.zoomTarget |
| 268 | if self.transMin ~= nil and self.transMax ~= nil and self.transMin ~= self.transMax then |
| 269 | zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget + offset)) |
| 270 | end |
| 271 | self.zoomTarget = zoomTarget |
| 272 | |
| 273 | end |
Raycast callbackDefinition
raycastCallback(integer transformId, float x, float y, float z, float distance, float nx, float ny, float nz)Arguments
| integer | transformId | id raycasted object |
| float | x | x raycast position |
| float | y | y raycast position |
| float | z | z raycast position |
| float | distance | distance to raycast position |
| float | nx | normal x |
| float | ny | normal y |
| float | nz | normal z |
| 285 | function VehicleCamera:raycastCallback(transformId, x, y, z, distance, nx, ny, nz) |
| 286 | self.raycastDistance = distance |
| 287 | self.normalX = nx |
| 288 | self.normalY = ny |
| 289 | self.normalZ = nz |
| 290 | self.raycastTransformId = transformId |
| 291 | end |
UpdateDefinition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 296 | function VehicleCamera:update(dt) |
| 297 | local target = self.zoomTarget |
| 298 | if self.zoomLimitedTarget >= 0 then |
| 299 | target = math.min(self.zoomLimitedTarget, self.zoomTarget) |
| 300 | end |
| 301 | self.zoom = target + ( math.pow(0.99579, dt) * (self.zoom - target) ) |
| 302 | |
| 303 | -- |
| 304 | if self.lastInputValues.upDown ~= 0 then |
| 305 | local value = self.lastInputValues.upDown * g_gameSettings:getValue(GameSettings.SETTING.CAMERA_SENSITIVITY) |
| 306 | self.lastInputValues.upDown = 0 |
| 307 | |
| 308 | if self.isRotatable then |
| 309 | if self.isActivated and not g_gui:getIsGuiVisible() then |
| 310 | if self.limitRotXDelta > 0.001 then |
| 311 | self.rotX = math.min(self.rotX - value, self.rotX) |
| 312 | elseif self.limitRotXDelta < -0.001 then |
| 313 | self.rotX = math.max(self.rotX - value, self.rotX) |
| 314 | else |
| 315 | self.rotX = self.rotX - value |
| 316 | end |
| 317 | |
| 318 | if self.limit then |
| 319 | self.rotX = math.min(self.rotMaxX, math.max(self.rotMinX, self.rotX)) |
| 320 | end |
| 321 | end |
| 322 | end |
| 323 | end |
| 324 | |
| 325 | if self.lastInputValues.leftRight ~= 0 then |
| 326 | local value = self.lastInputValues.leftRight * g_gameSettings:getValue(GameSettings.SETTING.CAMERA_SENSITIVITY) |
| 327 | self.lastInputValues.leftRight = 0 |
| 328 | |
| 329 | if self.isRotatable then |
| 330 | if self.isActivated and not g_gui:getIsGuiVisible() then |
| 331 | self.rotY = self.rotY - value |
| 332 | end |
| 333 | end |
| 334 | end |
| 335 | |
| 336 | -- |
| 337 | if g_gameSettings:getValue("isHeadTrackingEnabled") and isHeadTrackingAvailable() and self.allowHeadTracking and self.headTrackingNode ~= nil then |
| 338 | local tx,ty,tz = getHeadTrackingTranslation() |
| 339 | local pitch,yaw,roll = getHeadTrackingRotation() |
| 340 | if pitch ~= nil then |
| 341 | local camParent = getParent(self.cameraNode) |
| 342 | local ctx,cty,ctz; |
| 343 | local crx,cry,crz; |
| 344 | if camParent ~= 0 then |
| 345 | ctx, cty, ctz = localToLocal(self.headTrackingNode, camParent, tx, ty, tz); |
| 346 | crx, cry, crz = localRotationToLocal(self.headTrackingNode, camParent, pitch,yaw,roll); |
| 347 | else |
| 348 | ctx, cty, ctz = localToWorld(self.headTrackingNode, tx, ty, tz); |
| 349 | crx, cry, crz = localRotationToWorld(self.headTrackingNode, pitch,yaw,roll); |
| 350 | end |
| 351 | |
| 352 | setRotation(self.cameraNode, crx, cry, crz) |
| 353 | setTranslation(self.cameraNode, ctx, cty, ctz) |
| 354 | end |
| 355 | else |
| 356 | |
| 357 | self:updateRotateNodeRotation() |
| 358 | |
| 359 | |
| 360 | if self.limit then |
| 361 | -- adjust rotation to avoid clipping with terrain |
| 362 | if self.isRotatable and ((self.useWorldXZRotation == nil and g_gameSettings:getValue("useWorldCamera")) or self.useWorldXZRotation) then |
| 363 | |
| 364 | local numIterations = 4 |
| 365 | for i=1, numIterations do |
| 366 | local transX, transY, transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom |
| 367 | local x,y,z = localToWorld(getParent(self.cameraPositionNode), transX, transY, transZ) |
| 368 | |
| 369 | local terrainHeight = DensityMapHeightUtil.getHeightAtWorldPos(x,0,z) |
| 370 | |
| 371 | local minHeight = terrainHeight + 0.9 |
| 372 | if y < minHeight then |
| 373 | local h = math.sin(self.rotX)*self.zoom |
| 374 | local h2 = h-(minHeight-y) |
| 375 | self.rotX = math.asin(MathUtil.clamp(h2/self.zoom, -1, 1)) |
| 376 | self:updateRotateNodeRotation() |
| 377 | else |
| 378 | break |
| 379 | end |
| 380 | end |
| 381 | end |
| 382 | |
| 383 | -- adjust zoom to avoid collision with objects |
| 384 | if self.allowTranslation then |
| 385 | |
| 386 | self.limitRotXDelta = 0 |
| 387 | local hasCollision, collisionDistance, nx,ny,nz, normalDotDir = self:getCollisionDistance() |
| 388 | if hasCollision then |
| 389 | local distOffset = 0.1 |
| 390 | if normalDotDir ~= nil then |
| 391 | local absNormalDotDir = math.abs(normalDotDir) |
| 392 | distOffset = MathUtil.lerp(1.2, 0.1, absNormalDotDir*absNormalDotDir*(3-2*absNormalDotDir)) |
| 393 | end |
| 394 | collisionDistance = math.max(collisionDistance-distOffset, 0.01) |
| 395 | self.disableCollisionTime = g_currentMission.time+400 |
| 396 | self.zoomLimitedTarget = collisionDistance |
| 397 | if collisionDistance < self.zoom then |
| 398 | self.zoom = collisionDistance |
| 399 | end |
| 400 | if self.isRotatable and nx ~= nil and collisionDistance < self.transMin then |
| 401 | local _,lny,_ = worldDirectionToLocal(self.rotateNode, nx,ny,nz) |
| 402 | if lny > 0.5 then |
| 403 | self.limitRotXDelta = 1 |
| 404 | elseif lny < -0.5 then |
| 405 | self.limitRotXDelta = -1 |
| 406 | end |
| 407 | end |
| 408 | else |
| 409 | if self.disableCollisionTime <= g_currentMission.time then |
| 410 | self.zoomLimitedTarget = -1 |
| 411 | end |
| 412 | end |
| 413 | end |
| 414 | |
| 415 | end |
| 416 | self.transX, self.transY, self.transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom |
| 417 | setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ) |
| 418 | |
| 419 | if self.positionSmoothingParameter > 0 then |
| 420 | |
| 421 | local interpDt = g_physicsDt |
| 422 | if g_server == nil then |
| 423 | -- on clients, we interpolate the vehicles with dt, thus we need to use the same for camera interpolation |
| 424 | interpDt = dt |
| 425 | end |
| 426 | if interpDt > 0 then |
| 427 | local xlook,ylook,zlook = getWorldTranslation(self.rotateNode) |
| 428 | local lookAtPos = self.lookAtPosition |
| 429 | local lookAtLastPos = self.lookAtLastTargetPosition |
| 430 | lookAtPos[1],lookAtPos[2],lookAtPos[3] = self:getSmoothed(self.lookAtSmoothingParameter, lookAtPos[1],lookAtPos[2],lookAtPos[3], xlook,ylook,zlook, lookAtLastPos[1],lookAtLastPos[2],lookAtLastPos[3], interpDt) |
| 431 | lookAtLastPos[1],lookAtLastPos[2],lookAtLastPos[3] = xlook,ylook,zlook |
| 432 | |
| 433 | local x,y,z = getWorldTranslation(self.cameraPositionNode) |
| 434 | local pos = self.position |
| 435 | local lastPos = self.lastTargetPosition |
| 436 | pos[1],pos[2],pos[3] = self:getSmoothed(self.positionSmoothingParameter, pos[1],pos[2],pos[3], x,y,z, lastPos[1],lastPos[2],lastPos[3], interpDt) |
| 437 | lastPos[1],lastPos[2],lastPos[3] = x,y,z |
| 438 | |
| 439 | self:setSeparateCameraPose() |
| 440 | end |
| 441 | end |
| 442 | |
| 443 | end |
| 444 | |
| 445 | end |
Get smooth camera positionDefinition
getSmoothed(float alpha, float curX, float curY, float curZ, float targetX, float targetY, float targetZ, float lastTargetX, float lastTargetY, float lastTargetZ, float dt)Arguments
| float | alpha | alpha |
| float | curX | current x position |
| float | curY | current y position |
| float | curZ | current z position |
| float | targetX | target x position |
| float | targetY | target y position |
| float | targetZ | target z position |
| float | lastTargetX | last target x position |
| float | lastTargetY | last target y position |
| float | lastTargetZ | last target z position |
| float | dt | time since last call in ms |
| float | newX | new x position |
| float | newY | new y position |
| float | newZ | new z position |
Called on activateDefinition
onActivate()Code
| 495 | function VehicleCamera:onActivate() |
| 496 | |
| 497 | self.isActivated = true |
| 498 | if (self.resetCameraOnVehicleSwitch == nil and g_gameSettings:getValue("resetCamera")) or self.resetCameraOnVehicleSwitch then |
| 499 | self:resetCamera() |
| 500 | end |
| 501 | setCamera(self.cameraNode) |
| 502 | if self.shadowFocusBoxNode then |
| 503 | setShadowFocusBox(self.shadowFocusBoxNode) |
| 504 | end |
| 505 | |
| 506 | if self.positionSmoothingParameter > 0 then |
| 507 | local xlook,ylook,zlook = getWorldTranslation(self.rotateNode) |
| 508 | self.lookAtPosition[1] = xlook |
| 509 | self.lookAtPosition[2] = ylook |
| 510 | self.lookAtPosition[3] = zlook |
| 511 | self.lookAtLastTargetPosition[1] = xlook |
| 512 | self.lookAtLastTargetPosition[2] = ylook |
| 513 | self.lookAtLastTargetPosition[3] = zlook |
| 514 | local x,y,z = getWorldTranslation(self.cameraPositionNode) |
| 515 | self.position[1] = x |
| 516 | self.position[2] = y |
| 517 | self.position[3] = z |
| 518 | self.lastTargetPosition[1] = x |
| 519 | self.lastTargetPosition[2] = y |
| 520 | self.lastTargetPosition[3] = z |
| 521 | |
| 522 | |
| 523 | local rx,ry,rz = getWorldRotation(self.rotateNode) |
| 524 | |
| 525 | setRotation(self.cameraNode, rx,ry,rz) |
| 526 | setTranslation(self.cameraNode, x,y,z) |
| 527 | end |
| 528 | |
| 529 | self.lastInputValues = {} |
| 530 | self.lastInputValues.upDown = 0 |
| 531 | self.lastInputValues.leftRight = 0 |
| 532 | |
| 533 | -- activate action event callbacks |
| 534 | local _, actionEventId1 = g_inputBinding:registerActionEvent(InputAction.AXIS_LOOK_UPDOWN_VEHICLE, self, VehicleCamera.actionEventLookUpDown, false, false, true, true, nil) |
| 535 | local _, actionEventId2 = g_inputBinding:registerActionEvent(InputAction.AXIS_LOOK_LEFTRIGHT_VEHICLE, self, VehicleCamera.actionEventLookLeftRight, false, false, true, true, nil) |
| 536 | g_inputBinding:setActionEventTextVisibility(actionEventId1, false) |
| 537 | g_inputBinding:setActionEventTextVisibility(actionEventId2, false) |
| 538 | end |
Called on deactivateDefinition
onDeactivate()Code
| 542 | function VehicleCamera:onDeactivate() |
| 543 | self.isActivated = false |
| 544 | setShadowFocusBox(0) |
| 545 | |
| 546 | -- remove action event callbacks |
| 547 | g_inputBinding:removeActionEventsByTarget(self) |
| 548 | end |
Reset camera to original poseDefinition
resetCamera()Code
| 571 | function VehicleCamera:resetCamera() |
| 572 | self.rotX = self.origRotX |
| 573 | self.rotY = self.origRotY |
| 574 | self.rotZ = self.origRotZ |
| 575 | |
| 576 | self.transX = self.origTransX |
| 577 | self.transY = self.origTransY |
| 578 | self.transZ = self.origTransZ |
| 579 | |
| 580 | local transLength = MathUtil.vector3Length(self.origTransX, self.origTransY, self.origTransZ) |
| 581 | self.zoom = transLength |
| 582 | self.zoomTarget = transLength |
| 583 | self.zoomLimitedTarget = -1 |
| 584 | |
| 585 | self:updateRotateNodeRotation() |
| 586 | setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ) |
| 587 | |
| 588 | if self.positionSmoothingParameter > 0 then |
| 589 | local xlook,ylook,zlook = getWorldTranslation(self.rotateNode) |
| 590 | self.lookAtPosition[1] = xlook |
| 591 | self.lookAtPosition[2] = ylook |
| 592 | self.lookAtPosition[3] = zlook |
| 593 | local x,y,z = getWorldTranslation(self.cameraPositionNode) |
| 594 | self.position[1] = x |
| 595 | self.position[2] = y |
| 596 | self.position[3] = z |
| 597 | |
| 598 | self:setSeparateCameraPose() |
| 599 | end |
| 600 | end |
Update rotation node rotationDefinition
updateRotateNodeRotation()Code
| 604 | function VehicleCamera:updateRotateNodeRotation() |
| 605 | local rotY = self.rotY |
| 606 | if self.rotYSteeringRotSpeed ~= nil and self.rotYSteeringRotSpeed ~= 0 and self.vehicle.spec_articulatedAxis ~= nil and self.vehicle.spec_articulatedAxis.interpolatedRotatedTime ~= nil then |
| 607 | rotY = rotY + self.vehicle.spec_articulatedAxis.interpolatedRotatedTime*self.rotYSteeringRotSpeed |
| 608 | end |
| 609 | |
| 610 | if (self.useWorldXZRotation == nil and g_gameSettings:getValue("useWorldCamera")) or self.useWorldXZRotation then |
| 611 | local upx,upy,upz = 0,1,0 |
| 612 | |
| 613 | local dx,_,dz = localDirectionToWorld(getParent(self.rotateNode), 0,0,1) |
| 614 | local invLen = 1/math.sqrt(dx*dx + dz*dz) |
| 615 | dx = dx*invLen |
| 616 | dz = dz*invLen |
| 617 | |
| 618 | |
| 619 | |
| 620 | local newDx = math.cos(self.rotX) * (math.cos(rotY)*dx + math.sin(rotY)*dz) |
| 621 | local newDy = -math.sin(self.rotX) |
| 622 | local newDz = math.cos(self.rotX) * (-math.sin(rotY)*dx + math.cos(rotY)*dz) |
| 623 | |
| 624 | |
| 625 | newDx,newDy,newDz = worldDirectionToLocal(getParent(self.rotateNode), newDx,newDy,newDz) |
| 626 | upx,upy,upz = worldDirectionToLocal(getParent(self.rotateNode), upx,upy,upz) |
| 627 | |
| 628 | -- worst case check |
| 629 | if math.abs(MathUtil.dotProduct(newDx,newDy,newDz, upx,upy,upz)) > ( 0.99 * MathUtil.vector3Length(newDx,newDy,newDz) * MathUtil.vector3Length(upx,upy,upz) ) then |
| 630 | setRotation(self.rotateNode, self.rotX, rotY, self.rotZ) |
| 631 | else |
| 632 | setDirection(self.rotateNode, newDx,newDy,newDz, upx,upy,upz) |
| 633 | end |
| 634 | else |
| 635 | setRotation(self.rotateNode, self.rotX, rotY, self.rotZ) |
| 636 | end |
| 637 | end |
Set separate camera poseDefinition
setSeparateCameraPose()Code
| 641 | function VehicleCamera:setSeparateCameraPose() |
| 642 | if self.rotateNode ~= self.cameraPositionNode then |
| 643 | local dx = self.position[1] - self.lookAtPosition[1] |
| 644 | local dy = self.position[2] - self.lookAtPosition[2] |
| 645 | local dz = self.position[3] - self.lookAtPosition[3] |
| 646 | |
| 647 | local upx,upy,upz = 0,1,0 |
| 648 | if math.abs(dx) < 0.001 and math.abs(dz) < 0.001 then |
| 649 | upx = 0.1 |
| 650 | end |
| 651 | |
| 652 | setDirection(self.cameraNode, dx,dy,dz, upx,upy,upz) |
| 653 | else |
| 654 | local rx,ry,rz = getWorldRotation(self.rotateNode) |
| 655 | setRotation(self.cameraNode, rx,ry,rz) |
| 656 | end |
| 657 | setTranslation(self.cameraNode, self.position[1],self.position[2],self.position[3]) |
| 658 | end |
Get distance to collisionDefinition
getCollisionDistance()Return Values
| boolean | hasCollision | has collision |
| float | collisionDistance | distance to collision |
| float | normalX | normal x |
| float | normalY | normal y |
| float | normalZ | normal z |
| float | normalDotDir | normal dot direction |
| 668 | function VehicleCamera:getCollisionDistance() |
| 669 | if not self.isCollisionEnabled then |
| 670 | return false, nil, nil, nil, nil, nil |
| 671 | end |
| 672 | |
| 673 | local raycastMask = 32+64+128+256+4096 |
| 674 | |
| 675 | local targetCamX, targetCamY, targetCamZ = localToWorld(self.rotateNode, self.transDirX*self.zoomTarget, self.transDirY*self.zoomTarget, self.transDirZ*self.zoomTarget) |
| 676 | |
| 677 | local hasCollision = false |
| 678 | local collisionDistance = -1 |
| 679 | local normalX,normalY,normalZ |
| 680 | local normalDotDir |
| 681 | for _, raycastNode in ipairs(self.raycastNodes) do |
| 682 | |
| 683 | hasCollision = false |
| 684 | |
| 685 | local nodeX, nodeY, nodeZ = getWorldTranslation(raycastNode) |
| 686 | local dirX, dirY, dirZ = targetCamX-nodeX, targetCamY-nodeY, targetCamZ-nodeZ |
| 687 | local dirLength = MathUtil.vector3Length(dirX, dirY, dirZ) |
| 688 | dirX = dirX / dirLength |
| 689 | dirY = dirY / dirLength |
| 690 | dirZ = dirZ / dirLength |
| 691 | |
| 692 | local startX = nodeX |
| 693 | local startY = nodeY |
| 694 | local startZ = nodeZ |
| 695 | local currentDistance = 0 |
| 696 | local minDistance = self.transMin |
| 697 | |
| 698 | while true do |
| 699 | if (dirLength-currentDistance) <= 0 then |
| 700 | break |
| 701 | end |
| 702 | self.raycastDistance = 0 |
| 703 | raycastClosest(startX, startY, startZ, dirX, dirY, dirZ, "raycastCallback", dirLength-currentDistance, self, raycastMask, true) |
| 704 | |
| 705 | if self.raycastDistance ~= 0 then |
| 706 | currentDistance = currentDistance + self.raycastDistance+0.001 |
| 707 | local ndotd = MathUtil.dotProduct(self.normalX, self.normalY, self.normalZ, dirX, dirY, dirZ) |
| 708 | |
| 709 | local isAttachedVehicle = false |
| 710 | local object = g_currentMission:getNodeObject(self.raycastTransformId) |
| 711 | if object ~= nil then |
| 712 | if object ~= self.vehicle then |
| 713 | local attached1 = object.getIsAttachedTo ~= nil and object:getIsAttachedTo(self.vehicle) |
| 714 | local attached2 = self.vehicle.getIsAttachedTo ~= nil and self.vehicle:getIsAttachedTo(object) |
| 715 | isAttachedVehicle = attached1 or attached2 |
| 716 | |
| 717 | local mountObject = object.dynamicMountObject |
| 718 | if mountObject ~= nil and (mountObject == self.vehicle or mountObject:getRootVehicle() == self.vehicle) then |
| 719 | isAttachedVehicle = true |
| 720 | end |
| 721 | end |
| 722 | end |
| 723 | |
| 724 | if isAttachedVehicle or object == self.vehicle then --isAttachedNode or isDynamicallyMounted then |
| 725 | if ndotd > 0 then |
| 726 | minDistance = math.max(minDistance, currentDistance) |
| 727 | end |
| 728 | else |
| 729 | hasCollision = true |
| 730 | -- we take the distance from the rotate node |
| 731 | if raycastNode == self.rotateNode then |
| 732 | normalX,normalY,normalZ = self.normalX, self.normalY, self.normalZ |
| 733 | collisionDistance = math.max(self.transMin, currentDistance) |
| 734 | normalDotDir = ndotd |
| 735 | end |
| 736 | break |
| 737 | end |
| 738 | startX = nodeX+dirX*currentDistance |
| 739 | startY = nodeY+dirY*currentDistance |
| 740 | startZ = nodeZ+dirZ*currentDistance |
| 741 | else |
| 742 | break |
| 743 | end |
| 744 | end |
| 745 | if not hasCollision then |
| 746 | break |
| 747 | end |
| 748 | end |
| 749 | |
| 750 | return hasCollision, collisionDistance, normalX,normalY,normalZ, normalDotDir |
| 751 | end |
Class for vehicle motors
Creating new motorDefinition
new(integer minRpm, integer maxRpm, float maxForwardSpeed, float maxBackwardSpeed, table torqueCurve, float brakeForce, float forwardGearRatios, float backwardGearRatios, float minForwardGearRatio, float maxForwardGearRatio, float minBackwardGearRatio, float maxBackwardGearRatio, integer ptoMotorRpmRatio)Arguments
| integer | minRpm | min rpm |
| integer | maxRpm | max rpm |
| float | maxForwardSpeed | max forward speed |
| float | maxBackwardSpeed | max backward speed |
| table | torqueCurve | torque curve (AnimCurve) |
| float | brakeForce | brake force |
| float | forwardGearRatios | list of gear ratios to use when driving forwards (in decreasing order) |
| float | backwardGearRatios | list of gear ratios to use when driving backwards (in decreasing order) |
| float | minForwardGearRatio | min forward gear ratio |
| float | maxForwardGearRatio | max forward gear ratio |
| float | minBackwardGearRatio | min backward gear ratio |
| float | maxBackwardGearRatio | max backward gear ratio |
| integer | ptoMotorRpmRatio | pto motor rpm ratio |
| table | motorInstance | motor instance |
| 36 | function VehicleMotor:new(vehicle, minRpm, maxRpm, maxForwardSpeed, maxBackwardSpeed, torqueCurve, brakeForce, forwardGearRatios, backwardGearRatios, minForwardGearRatio, maxForwardGearRatio, minBackwardGearRatio, maxBackwardGearRatio, ptoMotorRpmRatio, minSpeed) |
| 37 | |
| 38 | local self = {} |
| 39 | setmetatable(self, VehicleMotor_mt) |
| 40 | |
| 41 | self.vehicle = vehicle |
| 42 | self.minRpm = minRpm |
| 43 | self.maxRpm = maxRpm |
| 44 | self.minSpeed = minSpeed |
| 45 | self.maxForwardSpeed = maxForwardSpeed -- speed in m/s |
| 46 | self.maxBackwardSpeed = maxBackwardSpeed |
| 47 | |
| 48 | self.maxClutchTorque = 5 -- amount of torque that can be transferred from motor to clutch/wheels [t m s^-2] |
| 49 | |
| 50 | self.torqueCurve = torqueCurve |
| 51 | self.brakeForce = brakeForce |
| 52 | |
| 53 | self.gear = 0 |
| 54 | self.minGearRatio = 0 |
| 55 | self.maxGearRatio = 0 |
| 56 | |
| 57 | self.forwardGearRatios = forwardGearRatios |
| 58 | self.backwardGearRatios = backwardGearRatios |
| 59 | self.minForwardGearRatio = minForwardGearRatio |
| 60 | self.maxForwardGearRatio = maxForwardGearRatio |
| 61 | self.minBackwardGearRatio = minBackwardGearRatio |
| 62 | self.maxBackwardGearRatio = maxBackwardGearRatio |
| 63 | |
| 64 | self.manualTargetGear = nil |
| 65 | self.targetGear = 0 |
| 66 | self.previousGear = 0 |
| 67 | self.gearChangeTimer = -1 |
| 68 | self.gearChangeTime = 250 |
| 69 | self.autoGearChangeTimer = -1 |
| 70 | self.autoGearChangeTime = 1000 |
| 71 | |
| 72 | self.lastRealMotorRpm = 0 |
| 73 | self.lastMotorRpm = 0 |
| 74 | |
| 75 | self.rpmLimit = math.huge |
| 76 | self.speedLimit = math.huge -- Speed limit in km/h |
| 77 | self.speedLimitAcc = math.huge |
| 78 | |
| 79 | self.accelerationLimit = 2 -- m s^-2 |
| 80 | |
| 81 | self.equalizedMotorRpm = 0 |
| 82 | |
| 83 | self.requiredMotorPower = 0 |
| 84 | |
| 85 | if self.maxForwardSpeed == nil then |
| 86 | self.maxForwardSpeed = self:calculatePhysicalMaximumForwardSpeed() |
| 87 | end |
| 88 | if self.maxBackwardSpeed == nil then |
| 89 | self.maxBackwardSpeed = self:calculatePhysicalMaximumBackwardSpeed() |
| 90 | end |
| 91 | |
| 92 | self.peakMotorTorque = self.torqueCurve:getMaximum() |
| 93 | |
| 94 | -- Calculate peak power. Assume we have a linear interpolation on the torque values |
| 95 | -- For each segment, find the maximum power (D[torque(x, i) * x] == 0) and take the maximum segment |
| 96 | -- D[ ((x-x0) / (x1-x0) (y1-y0) + y0) x] == 0 |
| 97 | -- -> (x1 y0 - x0 y1) / (2 (y0 - y1)) if y0 != y1 |
| 98 | self.peakMotorPower = 0 |
| 99 | self.peakMotorPowerRotSpeed = 0 |
| 100 | local numKeyFrames = #self.torqueCurve.keyframes |
| 101 | if numKeyFrames >= 2 then |
| 102 | for i=2,numKeyFrames do |
| 103 | local v0 = self.torqueCurve.keyframes[i-1] |
| 104 | local v1 = self.torqueCurve.keyframes[i] |
| 105 | local torque0 = self.torqueCurve:getFromKeyframes(v0, v0, i-1, i-1, 0) |
| 106 | local torque1 = self.torqueCurve:getFromKeyframes(v1, v1, i, i, 0) |
| 107 | local rpm, torque |
| 108 | if math.abs(torque0 - torque1) > 0.0001 then |
| 109 | rpm = (v1.time * torque0 - v0.time * torque1) / (2.0 * (torque0 - torque1)) |
| 110 | rpm = math.min(math.max(rpm, v0.time), v1.time) |
| 111 | torque = self.torqueCurve:getFromKeyframes(v0, v1, i-1, i, (v1.time - rpm) / (v1.time - v0.time)) |
| 112 | else |
| 113 | rpm = v0.time |
| 114 | torque = torque0 |
| 115 | end |
| 116 | local power = torque * rpm |
| 117 | if power > self.peakMotorPower then |
| 118 | self.peakMotorPower = power |
| 119 | self.peakMotorPowerRotSpeed = rpm |
| 120 | end |
| 121 | end |
| 122 | -- Convert from rpm to rad/s |
| 123 | self.peakMotorPower = self.peakMotorPower * math.pi/30 |
| 124 | self.peakMotorPowerRotSpeed = self.peakMotorPowerRotSpeed * math.pi/30 |
| 125 | else |
| 126 | local v = self.torqueCurve.keyframes[1] |
| 127 | local rotSpeed = v.time*math.pi/30 |
| 128 | local torque = self.torqueCurve:getFromKeyframes(v, v, i, i, 0) |
| 129 | self.peakMotorPower = rotSpeed*torque |
| 130 | self.peakMotorPowerRotSpeed = rotSpeed |
| 131 | end |
| 132 | |
| 133 | self.ptoMotorRpmRatio = ptoMotorRpmRatio |
| 134 | |
| 135 | self.rotInertia = 0.001 -- Rotational inertia of the motor, mostly defined by the flywheel [t m^2] |
| 136 | self.dampingRateFullThrottle = 0.00015 -- Damping rate of the motor if the acceleration pedal is 1 [t m^2 s^-1] |
| 137 | self.dampingRateZeroThrottleClutchEngaged = 0.003 -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is engaged [t m^2 s^-1] |
| 138 | self.dampingRateZeroThrottleClutchDisengaged = 0.001 -- Damping rate of the motor if the acceleration pedal is 0 and the clutch is disengaged [t m^2 s^-1] |
| 139 | |
| 140 | |
| 141 | |
| 142 | -- Motor properties as read from the physics engine |
| 143 | self.gearRatio = 0 |
| 144 | self.motorRotSpeed = 0 -- motor rotation speed [rad/s] |
| 145 | self.motorRotAcceleration = 0 -- motor rotation acceleration [rad/s^2] |
| 146 | self.motorRotAccelerationSmoothed = 0 -- motor rotation acceleration smoothed [rad/s^2] |
| 147 | |
| 148 | self.motorAvailableTorque = 0 -- torque that was available to the physics simulation [kN == t m/s^2] |
| 149 | self.motorAppliedTorque = 0 -- torque that was applied (<= available), can be smaller when acceleration/speed is limited [kN == t m/s^2] |
| 150 | self.motorExternalTorque = 0 -- torque that was removed from the motor and was not applied to the wheels (e.g. PTO) [kN == t m/s^2] |
| 151 | |
| 152 | self.differentialRotSpeed = 0 -- rotation speed of the main differential [rad/s] |
| 153 | self.differentialRotAcceleration = 0 -- rotation accleration of the main differential [rad/s^2] |
| 154 | self.differentialRotAccelerationSmoothed = 0 -- smoothed rotation accleration of the main differential [rad/s^2] |
| 155 | |
| 156 | return self |
| 157 | end |
Set low brake forceDefinition
setLowBrakeForce(float lowBrakeForceScale, float lowBrakeForceSpeedLimit)Arguments
| float | lowBrakeForceScale | low brake force scale |
| float | lowBrakeForceSpeedLimit | low brake force speed limit |
| 163 | function VehicleMotor:setLowBrakeForce(lowBrakeForceScale, lowBrakeForceSpeedLimit) |
| 164 | self.lowBrakeForceScale = lowBrakeForceScale |
| 165 | self.lowBrakeForceSpeedLimit = lowBrakeForceSpeedLimit |
| 166 | end |
Returns max clutch torqueDefinition
getMaxClutchTorque()Return Values
| float | maxClutchTorque | max clutch torque |
| 171 | function VehicleMotor:getMaxClutchTorque() |
| 172 | return self.maxClutchTorque |
| 173 | end |
Returns rotation inertiaDefinition
getRotInertia()Return Values
| float | rotInertia | rotation inertia |
| 178 | function VehicleMotor:getRotInertia() |
| 179 | return self.rotInertia |
| 180 | end |
Sets rotation inertiaDefinition
setRotInertia(float rotInertia)Arguments
| float | rotInertia | rotation inertia |
| 185 | function VehicleMotor:setRotInertia(rotInertia) |
| 186 | self.rotInertia = rotInertia |
| 187 | end |
Returns the damping rate of the motor if the acceleration pedal is 1Definition
getDampingRateFullThrottle()Return Values
| float | dampingRate | damping rate [t m^2 s^-1] |
| 192 | function VehicleMotor:getDampingRateFullThrottle() |
| 193 | return self.dampingRateFullThrottle |
| 194 | end |
Returns the damping rate of the motor if the acceleration pedal is 0 and the clutch is engagedDefinition
getDampingRateZeroThrottleClutchEngaged()Return Values
| float | dampingRate | damping rate [t m^2 s^-1] |
| 199 | function VehicleMotor:getDampingRateZeroThrottleClutchEngaged() |
| 200 | return self.dampingRateZeroThrottleClutchEngaged |
| 201 | end |
Returns the damping rate of the motor if the acceleration pedal is 0 and the clutch is disengagedDefinition
getDampingRateZeroThrottleClutchDisengaged()Return Values
| float | dampingRate | damping rate [t m^2 s^-1] |
| 206 | function VehicleMotor:getDampingRateZeroThrottleClutchDisengaged() |
| 207 | return self.dampingRateZeroThrottleClutchDisengaged |
| 208 | end |
Sets the damping rate of the motor if the acceleration pedal is 1Definition
setDampingRateFullThrottle(float dampingRate)Arguments
| float | dampingRate | new damping rate [t m^2 s^-1] |
| 213 | function VehicleMotor:setDampingRateFullThrottle(dampingRate) |
| 214 | self.dampingRateFullThrottle = dampingRate |
| 215 | end |
Sets the damping rate of the motor if the acceleration pedal is 0 and the clutch is engagedDefinition
setDampingRateZeroThrottleClutchEngaged(float dampingRate)Arguments
| float | dampingRate | new damping rate [t m^2 s^-1] |
| 220 | function VehicleMotor:setDampingRateZeroThrottleClutchEngaged(dampingRate) |
| 221 | self.dampingRateZeroThrottleClutchEngaged = dampingRate |
| 222 | end |
Sets the damping rate of the motor if the acceleration pedal is 0 and the clutch is disengagedDefinition
setDampingRateZeroThrottleClutchDisengaged(float dampingRate)Arguments
| float | dampingRate | new damping rate [t m^2 s^-1] |
| 227 | function VehicleMotor:setDampingRateZeroThrottleClutchDisengaged(dampingRate) |
| 228 | self.dampingRateZeroThrottleClutchDisengaged = dampingRate |
| 229 | end |
Sets the time it takes change gearsDefinition
setGearChangeTime(float gearChangeTime)Arguments
| float | gearChangeTime | gear change time [ms] |
| 234 | function VehicleMotor:setGearChangeTime(gearChangeTime) |
| 235 | self.gearChangeTime = gearChangeTime |
| 236 | self.gearChangeTimer = math.min(self.gearChangeTimer, gearChangeTime) |
| 237 | end |
Sets the time that needs to pass since the last gear change until an automatic gear change is allowedDefinition
setAutoGearChangeTime(float autoGearChangeTime)Arguments
| float | autoGearChangeTime | automatic gear change time [ms] |
| 242 | function VehicleMotor:setAutoGearChangeTime(autoGearChangeTime) |
| 243 | self.autoGearChangeTime = autoGearChangeTime |
| 244 | self.autoGearChangeTimer = math.min(self.autoGearChangeTimer, autoGearChangeTime) |
| 245 | end |
Returns max torqueDefinition
getPeakTorque()Return Values
| float | maxMotorTorque | max motor torque |
| 250 | function VehicleMotor:getPeakTorque() |
| 251 | return self.peakMotorTorque |
| 252 | end |
Returns brake forceDefinition
getBrakeForce()Return Values
| float | brakeForce | brake force |
| 257 | function VehicleMotor:getBrakeForce() |
| 258 | return self.brakeForce |
| 259 | end |
Returns min rpmDefinition
getMinRpm()Return Values
| float | minRpm | min rpm |
| 264 | function VehicleMotor:getMinRpm() |
| 265 | return self.minRpm |
| 266 | end |
Returns max rpmDefinition
getMaxRpm()Return Values
| float | maxRpm | max rpm |
| 271 | function VehicleMotor:getMaxRpm() |
| 272 | return self.maxRpm |
| 273 | end |
Returns the currently required motor rpm range (e.g. defined by the activated pto)Definition
getRequiredMotorRpmRange()Return Values
| float | minRequiredRpm | min required rpm |
| float | minRequiredRpm | max required rpm |
| 279 | function VehicleMotor:getRequiredMotorRpmRange() |
| 280 | local motorPtoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm) |
| 281 | if motorPtoRpm ~= 0 then |
| 282 | return motorPtoRpm, motorPtoRpm |
| 283 | end |
| 284 | return self.minRpm, self.maxRpm |
| 285 | end |
Returns last motor rpm dampedDefinition
getLastMotorRpm()Return Values
| float | lastMotorRpm | last motor rpm |
| 290 | function VehicleMotor:getLastMotorRpm() |
| 291 | return self.lastMotorRpm |
| 292 | end |
Returns last motor rpm realDefinition
getLastRealMotorRpm()Return Values
| float | lastMotorRpm | last motor rpm |
| 297 | function VehicleMotor:getLastRealMotorRpm() |
| 298 | return self.lastRealMotorRpm |
| 299 | end |
Sets last motor rpmDefinition
setLastRpm(float lastRpm)Arguments
| float | lastRpm | new last motor rpm |
| 304 | function VehicleMotor:setLastRpm(lastRpm) |
| 305 | self.lastRealMotorRpm = lastRpm |
| 306 | |
| 307 | self.lastMotorRpm = self.lastMotorRpm * 0.95 + self.lastRealMotorRpm * 0.05 |
| 308 | end |
Returns the last applied torque to the motorDefinition
getMotorAppliedTorque()Return Values
| float | appliedTorque | torque [kN] |
| 313 | function VehicleMotor:getMotorAppliedTorque() |
| 314 | return self.motorAppliedTorque |
| 315 | end |
Returns the last applied external torque (torque used by external power consumers like the PTO)Definition
getMotorExternalTorque()Return Values
| float | externalTorque | external torque [kN] |
| 320 | function VehicleMotor:getMotorExternalTorque() |
| 321 | return self.motorExternalTorque |
| 322 | end |
Returns the last total available motor torqueDefinition
getMotorAvailableTorque()Return Values
| float | torque | external torque [kN] |
| 327 | function VehicleMotor:getMotorAvailableTorque() |
| 328 | return self.motorAvailableTorque |
| 329 | end |
Returns equalized motor rpmDefinition
getEqualizedMotorRpm()Return Values
| float | equalizedMotorRpm | equalized motor rpm |
| 334 | function VehicleMotor:getEqualizedMotorRpm() |
| 335 | return self.equalizedMotorRpm |
| 336 | end |
Sets equalized motor rpmDefinition
setEqualizedMotorRpm(float equalizedMotorRpm)Arguments
| float | equalizedMotorRpm | equalized motor rpm |
| 341 | function VehicleMotor:setEqualizedMotorRpm(rpm) |
| 342 | self.equalizedMotorRpm = rpm |
| 343 | self:setLastRpm(rpm) |
| 344 | end |
Returns pto motor rpm ratioDefinition
getPtoMotorRpmRatio()Return Values
| float | ptoMotorRpmRatio | pto motor rpm ratio |
| 349 | function VehicleMotor:getPtoMotorRpmRatio() |
| 350 | return self.ptoMotorRpmRatio |
| 351 | end |
Returns non clamped motor rpmDefinition
getNonClampedMotorRpm()Return Values
| float | nonClampedMotorRpm | non clamped motor rpm |
| 356 | function VehicleMotor:getNonClampedMotorRpm() |
| 357 | return self.motorRotSpeed * 30 / math.pi |
| 358 | end |
Returns non clamped motor rpmDefinition
getMotorRotSpeed()Return Values
| float | nonClampedMotorRpm | non clamped motor rpm |
| 363 | function VehicleMotor:getMotorRotSpeed() |
| 364 | return self.motorRotSpeed |
| 365 | end |
Returns clutch rpmDefinition
getClutchRotSpeed()Return Values
| float | clutchRpm | clutch rpm |
| 371 | function VehicleMotor:getClutchRotSpeed() |
| 372 | return self.differentialRotSpeed * self.gearRatio |
| 373 | end |
Returns torque curveDefinition
getTorqueCurve()Return Values
| table | torqueCurve | torque curve |
| 378 | function VehicleMotor:getTorqueCurve() |
| 379 | return self.torqueCurve |
| 380 | end |
Returns torque of the motor at the current rpm with the given accelerator pedalDefinition
getTorque(float acceleration)Arguments
| float | acceleration | acceleration |
| float | torque | torque |
| 386 | function VehicleMotor:getTorque(acceleration) |
| 387 | -- Note: the torque curve is undefined outside the min/max rpm range. Clamping makes the curve flat at the outside range |
| 388 | local torque = self:getTorqueCurveValue(MathUtil.clamp(self.motorRotSpeed * 30/math.pi, self.minRpm, self.maxRpm)) |
| 389 | torque = torque * math.abs(acceleration) |
| 390 | return torque |
| 391 | end |
Returns torque of the motor at the given rpmDefinition
getTorqueCurveValue(float rpm)Arguments
| float | rpm | rpm |
| float | torque | torque |
| 397 | function VehicleMotor:getTorqueCurveValue(rpm) |
| 398 | local damage = 1 - (self.vehicle:getVehicleDamage() * VehicleMotor.DAMAGE_TORQUE_REDUCTION) |
| 399 | return self:getTorqueCurve():get(rpm) * damage |
| 400 | end |
Returns maximum forward speedDefinition
getMaximumForwardSpeed()Return Values
| float | maxForwardSpeed | maximum forward speed |
| 416 | function VehicleMotor:getMaximumForwardSpeed() |
| 417 | return self.maxForwardSpeed |
| 418 | end |
Returns maximum backward speedDefinition
getMaximumBackwardSpeed()Return Values
| float | maxBackwardSpeed | maximum backward speed |
| 423 | function VehicleMotor:getMaximumBackwardSpeed() |
| 424 | return self.maxBackwardSpeed |
| 425 | end |
Returns physical maximum forward speedDefinition
calculatePhysicalMaximumForwardSpeed()Return Values
| float | physicalMaxForwardSpeed | physical maximum forward speed |
| 430 | function VehicleMotor:calculatePhysicalMaximumForwardSpeed() |
| 431 | return VehicleMotor.calculatePhysicalMaximumSpeed(self.minForwardGearRatio, self.forwardGearRatios, self.maxRpm) |
| 432 | end |
Returns physical maximum backward speedDefinition
calculatePhysicalMaximumBackwardSpeed()Return Values
| float | physicalMaxBackwardSpeed | physical maximum backward speed |
| 437 | function VehicleMotor:calculatePhysicalMaximumBackwardSpeed() |
| 438 | return VehicleMotor.calculatePhysicalMaximumSpeed(self.minBackwardGearRatio, self.backwardGearRatios, self.maxRpm) |
| 439 | end |
Returns physical maximum speedDefinition
calculatePhysicalMaximumSpeed(float minGearRatio, table gearRatios, integer maxRpm)Arguments
| float | minGearRatio | min gear ratio |
| table | gearRatios | gear ratios |
| integer | maxRpm | max rpm |
| float | physicalMaxSpeed | physical maximum speed |
| 447 | function VehicleMotor.calculatePhysicalMaximumSpeed(minGearRatio, gearRatios, maxRpm) |
| 448 | local minRatio |
| 449 | if minGearRatio ~= nil then |
| 450 | minRatio = minGearRatio |
| 451 | else |
| 452 | minRatio = math.huge |
| 453 | for _, ratio in pairs(gearRatios) do |
| 454 | minRatio = math.min(minRatio, ratio) |
| 455 | end |
| 456 | end |
| 457 | return maxRpm * math.pi / (30 * minRatio) |
| 458 | end |
Update the state of the motor (sync with physics simulation)Definition
update(float dt)Arguments
| float | dt | time since last call in ms |
| 463 | function VehicleMotor:update(dt) |
| 464 | local vehicle = self.vehicle |
| 465 | if next(vehicle.spec_motorized.differentials) ~= nil and vehicle.spec_motorized.motorizedNode ~= nil then |
| 466 | -- Only update the physics values if a physics simulation was performed |
| 467 | if g_physicsDtNonInterpolated > 0.0 then |
| 468 | local lastMotorRotSpeed = self.motorRotSpeed; |
| 469 | local lastDiffRotSpeed = self.differentialRotSpeed |
| 470 | self.motorRotSpeed, self.differentialRotSpeed, self.gearRatio = getMotorRotationSpeed(vehicle.spec_motorized.motorizedNode) |
| 471 | |
| 472 | self.motorAvailableTorque, self.motorAppliedTorque, self.motorExternalTorque = getMotorTorque(vehicle.spec_motorized.motorizedNode) |
| 473 | |
| 474 | |
| 475 | local motorRotAcceleration = (self.motorRotSpeed - lastMotorRotSpeed) / (g_physicsDtNonInterpolated*0.001) |
| 476 | self.motorRotAcceleration = motorRotAcceleration |
| 477 | self.motorRotAccelerationSmoothed = 0.8 * self.motorRotAccelerationSmoothed + 0.2 * motorRotAcceleration |
| 478 | |
| 479 | local diffRotAcc = (self.differentialRotSpeed - lastDiffRotSpeed) / (g_physicsDtNonInterpolated*0.001) |
| 480 | self.differentialRotAcceleration = diffRotAcc |
| 481 | self.differentialRotAccelerationSmoothed = 0.8 * self.differentialRotAccelerationSmoothed + 0.2 * diffRotAcc |
| 482 | |
| 483 | --print(string.format("update rpms: %.2f %.2f acc: %.2f", self.motorRotSpeed*30/math.pi, self.differentialRotSpeed*self.gearRatio*30/math.pi, motorRotAcceleration)) |
| 484 | end |
| 485 | |
| 486 | self.requiredMotorPower = math.huge |
| 487 | |
| 488 | else |
| 489 | local _, gearRatio = self:getMinMaxGearRatio() |
| 490 | self.differentialRotSpeed = WheelsUtil.computeDifferentialRotSpeedNonMotor(vehicle) |
| 491 | self.motorRotSpeed = math.max(self.differentialRotSpeed * gearRatio, 0) |
| 492 | self.gearRatio = gearRatio |
| 493 | end |
| 494 | |
| 495 | -- the clamped motor rpm always is higher-equal than the required rpm by the pto |
| 496 | --local ptoRpm = math.min(PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio, self.maxRpm) |
| 497 | -- smoothing for raise/fall of ptoRpm |
| 498 | if self.lastPtoRpm == nil then |
| 499 | self.lastPtoRpm = self.minRpm |
| 500 | end |
| 501 | local ptoRpm = PowerConsumer.getMaxPtoRpm(self.vehicle)*self.ptoMotorRpmRatio |
| 502 | if ptoRpm > self.lastPtoRpm then |
| 503 | self.lastPtoRpm = math.min(ptoRpm, self.lastPtoRpm + self.maxRpm*dt/2000) |
| 504 | elseif ptoRpm < self.lastPtoRpm then |
| 505 | self.lastPtoRpm = math.max(self.minRpm, self.lastPtoRpm - self.maxRpm*dt/1000) |
| 506 | end |
| 507 | local ptoRpm = math.min(self.lastPtoRpm, self.maxRpm) |
| 508 | |
| 509 | local clampedMotorRpm = math.max(self.motorRotSpeed*30/math.pi, ptoRpm, self.minRpm) |
| 510 | |
| 511 | self:setLastRpm(clampedMotorRpm) |
| 512 | |
| 513 | self.equalizedMotorRpm = clampedMotorRpm |
| 514 | end |
Returns best gear ratioDefinition
getBestGearRatio(float wheelSpeedRpm, float minRatio, float maxRatio, float accSafeMotorRpm, float requiredMotorPower, float requiredMotorRpm)Arguments
| float | wheelSpeedRpm | wheel speed rpm |
| float | minRatio | min ratio |
| float | maxRatio | max ratio |
| float | accSafeMotorRpm | acc save motor rpm |
| float | requiredMotorPower | the required motor power [kW] (can be bigger than what the motor can actually achieve) |
| float | requiredMotorRpm | fixed motor rpm to be used (if not 0) |
| float | bestGearRatio | best gear ratio |
| 526 | function VehicleMotor:getBestGearRatio(wheelSpeedRpm, minRatio, maxRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
| 527 | |
| 528 | if requiredMotorRpm ~= 0 then |
| 529 | local gearRatio = math.max(requiredMotorRpm-accSafeMotorRpm, requiredMotorRpm*0.8) / math.max(wheelSpeedRpm, 0.001) |
| 530 | gearRatio = MathUtil.clamp(gearRatio, minRatio, maxRatio) |
| 531 | return gearRatio |
| 532 | end |
| 533 | |
| 534 | -- Use a minimum wheel rpm to avoid that gearRatio is ignored |
| 535 | wheelSpeedRpm = math.max(wheelSpeedRpm, 0.0001) |
| 536 | |
| 537 | local bestMotorPower = 0 |
| 538 | local bestGearRatio = minRatio |
| 539 | --local bestRPM = 0 |
| 540 | -- TODO make this more efficient |
| 541 | for gearRatio = minRatio, maxRatio, 0.5 do |
| 542 | local motorRpm = wheelSpeedRpm * gearRatio |
| 543 | if motorRpm > self.maxRpm - accSafeMotorRpm then |
| 544 | break |
| 545 | end |
| 546 | local motorPower = self:getTorqueCurveValue(math.max(motorRpm, self.minRpm)) * motorRpm *math.pi/30 |
| 547 | if motorPower > bestMotorPower then |
| 548 | bestMotorPower = motorPower |
| 549 | bestGearRatio = gearRatio |
| 550 | --bestRPM = motorRpm |
| 551 | end |
| 552 | |
| 553 | if motorPower >= requiredMotorPower then |
| 554 | break |
| 555 | end |
| 556 | end |
| 557 | --print(string.format("Selected best gear: %f, %.2fkW rpm %.2f wheel %.2f", bestGearRatio, bestMotorPower, bestRPM, wheelSpeedRpm,)) |
| 558 | |
| 559 | return bestGearRatio |
| 560 | end |
Returns best gearDefinition
getBestGear(float acceleration, float wheelSpeedRpm, float accSafeMotorRpm, float requiredMotorPower, float requiredMotorRpm)Arguments
| float | acceleration | acceleration |
| float | wheelSpeedRpm | wheel speed rpm |
| float | accSafeMotorRpm | acc save motor rpm |
| float | requiredMotorPower | required wheel torque |
| float | requiredMotorRpm | required motor rpm |
| float | bestGear | best gear |
| float | gearRatio | gear ratio |
| 571 | function VehicleMotor:getBestGear(acceleration, wheelSpeedRpm, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
| 572 | if math.abs(acceleration) < 0.001 then |
| 573 | acceleration = 1 |
| 574 | if wheelSpeedRpm < 0 then |
| 575 | acceleration = -1 |
| 576 | end |
| 577 | end |
| 578 | if acceleration > 0 then |
| 579 | if self.minForwardGearRatio ~= nil then |
| 580 | local wheelSpeedRpm = math.max(wheelSpeedRpm, 0) |
| 581 | local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minForwardGearRatio, self.maxForwardGearRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
| 582 | return 1, bestGearRatio |
| 583 | else |
| 584 | return 1, self.forwardGearRatios[1] |
| 585 | end |
| 586 | else |
| 587 | if self.minBackwardGearRatio ~= nil then |
| 588 | local wheelSpeedRpm = math.max(-wheelSpeedRpm, 0) |
| 589 | local bestGearRatio = self:getBestGearRatio(wheelSpeedRpm, self.minBackwardGearRatio, self.maxBackwardGearRatio, accSafeMotorRpm, requiredMotorPower, requiredMotorRpm) |
| 590 | return -1, -bestGearRatio |
| 591 | else |
| 592 | return -1, -self.backwardGearRatios[1] |
| 593 | end |
| 594 | end |
| 595 | end |
Update gearDefinition
updateGear(float acceleratorPedal)Arguments
| float | acceleratorPedal | acceleratorPedal |
| float | adjustedAcceleratorPedal | the adjusted accelerator pedal for the current gear situation (e.g. 0 while switching gears) |
| 818 | function VehicleMotor:updateGear(acceleratorPedal, dt) |
| 819 | local adjAcceleratorPedal = acceleratorPedal |
| 820 | if self.gearChangeTimer >= 0 then |
| 821 | self.gearChangeTimer = self.gearChangeTimer - dt; |
| 822 | if self.gearChangeTimer < 0 then |
| 823 | if self.targetGear > 0 then |
| 824 | -- give the logic another chance to choose a better gear at the time when the gear change should happen |
| 825 | self.gear = self:findGearChangeTargetGear(self.targetGear, self.previousGear, self.forwardGearRatios, 1, acceleratorPedal, dt) |
| 826 | self.minGearRatio = self.forwardGearRatios[self.gear] |
| 827 | else |
| 828 | self.gear = -self:findGearChangeTargetGear(-self.targetGear, -self.previousGear, self.backwardGearRatios, -1, acceleratorPedal, dt) |
| 829 | self.minGearRatio = -self.backwardGearRatios[-self.gear] |
| 830 | end |
| 831 | self.maxGearRatio = self.minGearRatio |
| 832 | end |
| 833 | adjAcceleratorPedal = 0 |
| 834 | else |
| 835 | local gearSign = 0 |
| 836 | if acceleratorPedal > 0 then |
| 837 | if self.minForwardGearRatio ~= nil then |
| 838 | self.minGearRatio = self.minForwardGearRatio |
| 839 | self.maxGearRatio = self.maxForwardGearRatio |
| 840 | else |
| 841 | gearSign = 1 |
| 842 | end |
| 843 | elseif acceleratorPedal < 0 then |
| 844 | if self.minBackwardGearRatio ~= nil then |
| 845 | self.minGearRatio = -self.minBackwardGearRatio |
| 846 | self.maxGearRatio = -self.maxBackwardGearRatio |
| 847 | else |
| 848 | gearSign = -1 |
| 849 | end |
| 850 | else |
| 851 | if self.maxGearRatio > 0 then |
| 852 | if self.minForwardGearRatio == nil then |
| 853 | gearSign = 1 |
| 854 | end |
| 855 | elseif self.maxGearRatio < 0 then |
| 856 | if self.minBackwardGearRatio == nil then |
| 857 | gearSign = -1 |
| 858 | end |
| 859 | end |
| 860 | end |
| 861 | |
| 862 | self.autoGearChangeTimer = self.autoGearChangeTimer - dt |
| 863 | local newGear = self.gear |
| 864 | if g_manualGearShift then |
| 865 | if self.manualTargetGear ~= nil then |
| 866 | newGear = self.manualTargetGear |
| 867 | self.manualTargetGear = nil |
| 868 | end |
| 869 | else |
| 870 | if gearSign > 0 then |
| 871 | if self.gear <= 0 then |
| 872 | newGear = 1 |
| 873 | else |
| 874 | if self.autoGearChangeTimer <= 0 then |
| 875 | newGear = self:findGearChangeTargetGearPrediction(self.gear, self.forwardGearRatios, 1, self.autoGearChangeTimer, acceleratorPedal, dt) |
| 876 | end |
| 877 | newGear = math.min(math.max(newGear, 1), #self.forwardGearRatios) |
| 878 | end |
| 879 | elseif gearSign < 0 then |
| 880 | if self.gear >= 0 then |
| 881 | newGear = -1 |
| 882 | else |
| 883 | if self.autoGearChangeTimer <= 0 then |
| 884 | newGear = -self:findGearChangeTargetGearPrediction(-self.gear, self.backwardGearRatios, -1, self.autoGearChangeTimer, acceleratorPedal, dt) |
| 885 | end |
| 886 | newGear = math.max(math.min(newGear, -1), -#self.backwardGearRatios) |
| 887 | end |
| 888 | end |
| 889 | end |
| 890 | if newGear ~= self.gear then |
| 891 | self.targetGear = newGear |
| 892 | self.previousGear = self.gear |
| 893 | self.gear = 0 |
| 894 | self.minGearRatio = 0 |
| 895 | self.maxGearRatio = 0 |
| 896 | self.autoGearChangeTimer = self.autoGearChangeTime |
| 897 | self.gearChangeTimer = self.gearChangeTime |
| 898 | adjAcceleratorPedal = 0 |
| 899 | end |
| 900 | end |
| 901 | return adjAcceleratorPedal |
| 902 | end |
Returns currently selected minimum and maximum gear ratioDefinition
Gear ratios for driving backwards are negative. Min/max always refers to the absolute value
For regular gear box transmission, the minimum and maximum gear ratios are identical
getMinMaxGearRatio()Return Values
| float | minGearRatio | minimum gear ratio |
| float | maxGearRatio | maximum gear ratio |
| 910 | function VehicleMotor:getMinMaxGearRatio() |
| 911 | return self.minGearRatio, self.maxGearRatio |
| 912 | end |
Returns current max rpmDefinition
getCurMaxRpm()Return Values
| integer | maxRpm | current max rpm |
| 921 | function VehicleMotor:getCurMaxRpm() |
| 922 | local maxRpm = self.maxRpm |
| 923 | |
| 924 | local gearRatio = self:getGearRatio() |
| 925 | if gearRatio ~= 0 then |
| 926 | --local speedLimit = self.speedLimit * 0.277778 |
| 927 | local speedLimit = math.min(self.speedLimit, math.max(self.speedLimitAcc, self.vehicle.lastSpeedReal*3600)) * 0.277778 |
| 928 | if gearRatio > 0 then |
| 929 | speedLimit = math.min(speedLimit, self.maxForwardSpeed) |
| 930 | else |
| 931 | speedLimit = math.min(speedLimit, self.maxBackwardSpeed) |
| 932 | end |
| 933 | |
| 934 | maxRpm = math.min(maxRpm, speedLimit * 30 / math.pi * math.abs(gearRatio)) |
| 935 | end |
| 936 | |
| 937 | maxRpm = math.min(maxRpm, self.rpmLimit) |
| 938 | return maxRpm |
| 939 | end |
Sets speed limitDefinition
setSpeedLimit(float limit)Arguments
| float | limit | new limit |
| 944 | function VehicleMotor:setSpeedLimit(limit) |
| 945 | self.speedLimit = math.max(limit, self.minSpeed) |
| 946 | end |
Sets rpm limitDefinition
setRpmLimit(float limit)Arguments
| float | limit | new limit |
| 963 | function VehicleMotor:setRpmLimit(rpmLimit) |
| 964 | self.rpmLimit = rpmLimit |
| 965 | end |
Placement callback
Create instance of classDefinition
new()Code
| 11 | function VehiclePlacementCallback:new() |
| 12 | local instance = {} |
| 13 | setmetatable(instance, VehiclePlacementCallback_mt) |
| 14 | |
| 15 | return instance |
| 16 | end |
Raycast callbackDefinition
callback(integer transformId, float x, float y, float z, float distance)Arguments
| integer | transformId | id raycasted object |
| float | x | x raycast position |
| float | y | y raycast position |
| float | z | z raycast position |
| float | distance | distance to raycast position |
| boolean | continue | continue |
| 26 | function VehiclePlacementCallback:callback(transformName, x, y, z, distance) |
| 27 | self.raycastHitName = transformName |
| 28 | self.x = x |
| 29 | self.y = y |
| 30 | self.z = z |
| 31 | self.distance = distance |
| 32 | |
| 33 | return true |
| 34 | end |
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 17 | function VehicleTypeManager:new(customMt) |
| 18 | local self = AbstractManager:new(customMt or VehicleTypeManager_mt) |
| 19 | |
| 20 | return self |
| 21 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 25 | function VehicleTypeManager:initDataStructures() |
| 26 | self.vehicleTypes = {} |
| 27 | end |
Load data on map loadDefinition
loadMapData()Return Values
| boolean | true | if loading was successful else false |
| 32 | function VehicleTypeManager:loadMapData() |
| 33 | VehicleTypeManager:superClass().loadMapData(self) |
| 34 | |
| 35 | local xmlFile = loadXMLFile("VehicleTypesXML", "dataS/vehicleTypes.xml") |
| 36 | |
| 37 | local i = 0 |
| 38 | while true do |
| 39 | local key = string.format("vehicleTypes.type(%d)", i) |
| 40 | if not hasXMLProperty(xmlFile, key) then |
| 41 | break |
| 42 | end |
| 43 | |
| 44 | g_deferredLoadingManager:addSubtask(function() |
| 45 | self:loadVehicleTypeFromXML(xmlFile, key, nil, nil, nil) |
| 46 | end) |
| 47 | |
| 48 | i = i + 1 |
| 49 | end |
| 50 | |
| 51 | g_deferredLoadingManager:addSubtask(function() |
| 52 | delete(xmlFile) |
| 53 | end) |
| 54 | |
| 55 | g_deferredLoadingManager:addSubtask(function() |
| 56 | print(" Loaded vehicle types") |
| 57 | end) |
| 58 | |
| 59 | return true |
| 60 | end |
Adds a new vehicleTypeDefinition
addVehicleType(string typeName, string className, string filename, table specializationNames, string customEnvironment)Arguments
| string | typeName | vehicle type name |
| string | className | classname |
| string | filename | filename |
| table | specializationNames | list of specializations |
| string | customEnvironment | a custom environment |
| boolean | success | true if added else false |
Register new tire typeDefinition
registerTireType(string name, table frictionCoeffs, table frictionCoeffsWer)Arguments
| string | name | name of new tire type |
| table | frictionCoeffs | friction coeffs |
| table | frictionCoeffsWer | friction coeffs wet |
| 30 | function WheelsUtil.registerTireType(name, frictionCoeffs, frictionCoeffsWet) |
| 31 | local tireType = WheelsUtil.getTireType(name) |
| 32 | if tireType ~= nil then |
| 33 | print("Warning: Adding duplicate tire type '"..name.."'") |
| 34 | return |
| 35 | end |
| 36 | |
| 37 | local function getNoNilCoeffs(frictionCoeffs) |
| 38 | local localCoeffs = {} |
| 39 | if frictionCoeffs[1] == nil then |
| 40 | localCoeffs[1] = 1.15 |
| 41 | for i=2,WheelsUtil.NUM_GROUNDS do |
| 42 | if frictionCoeffs[i] ~= nil then |
| 43 | localCoeffs[1] = frictionCoeffs[i] |
| 44 | break |
| 45 | end |
| 46 | end |
| 47 | else |
| 48 | localCoeffs[1] = frictionCoeffs[1] |
| 49 | end |
| 50 | for i=2,WheelsUtil.NUM_GROUNDS do |
| 51 | localCoeffs[i] = frictionCoeffs[i] or frictionCoeffs[i-1] |
| 52 | end |
| 53 | return localCoeffs |
| 54 | end |
| 55 | |
| 56 | local tireType = {} |
| 57 | tireType.name = name |
| 58 | tireType.frictionCoeffs = getNoNilCoeffs(frictionCoeffs) |
| 59 | tireType.frictionCoeffsWet = getNoNilCoeffs(frictionCoeffsWet or frictionCoeffs) |
| 60 | table.insert(WheelsUtil.tireTypes, tireType) |
| 61 | end |
Returns tire type indexDefinition
getTireType(string name)Arguments
| string | name | name of tire type |
| integer | i | index of tire type |
| 67 | function WheelsUtil.getTireType(name) |
| 68 | for i, t in pairs(WheelsUtil.tireTypes) do |
| 69 | if t.name == name then |
| 70 | return i |
| 71 | end |
| 72 | end |
| 73 | return nil |
| 74 | end |
Updates wheel physicsDefinition
updateWheelsPhysics(float dt, float currentSpeed, float acceleration, boolean doHandbrake, boolean stopAndGoBraking)Arguments
| float | dt | time since last call in ms |
| float | currentSpeed | signed current speed (m/ms) |
| float | acceleration | target acceleration [-1,1] |
| boolean | doHandbrake | do handbrake |
| boolean | stopAndGoBraking | if false, the acceleration needs to be 0 before a change of direction is allowed |
| 194 | function WheelsUtil.updateWheelsPhysics(self, dt, currentSpeed, acceleration, doHandbrake, stopAndGoBraking) |
| 195 | --print("function WheelsUtil.updateWheelsPhysics("..tostring(self)..", "..tostring(dt)..", "..tostring(currentSpeed)..", "..tostring(acceleration)..", "..tostring(doHandbrake)..", "..tostring(stopAndGoBraking)) |
| 196 | |
| 197 | local acceleratorPedal = 0 |
| 198 | local brakePedal = 0 |
| 199 | |
| 200 | local reverserDirection = 1 |
| 201 | if self.spec_drivable ~= nil then |
| 202 | reverserDirection = self.spec_drivable.reverserDirection |
| 203 | acceleration = acceleration * reverserDirection |
| 204 | end |
| 205 | |
| 206 | local motor = self.spec_motorized.motor |
| 207 | |
| 208 | local absCurrentSpeed = math.abs(currentSpeed) |
| 209 | local accSign = MathUtil.sign(acceleration) |
| 210 | |
| 211 | self.nextMovingDirection = Utils.getNoNil(self.nextMovingDirection, 0) |
| 212 | |
| 213 | local automaticBrake = false |
| 214 | |
| 215 | if math.abs(acceleration) < 0.001 then |
| 216 | automaticBrake = true |
| 217 | |
| 218 | -- Non-stop&go only allows change of direction if the vehicle speed is smaller than 1km/h or the direction has already changed (e.g. because the brakes are not hard enough) |
| 219 | if stopAndGoBraking or currentSpeed * self.nextMovingDirection < 0.0003 then |
| 220 | self.nextMovingDirection = 0 |
| 221 | end |
| 222 | else |
| 223 | -- Disable the known moving direction if the vehicle is driving more than 5km/h (0.0014 * 3600 = 5.04km/h) in the opposite direction |
| 224 | if self.nextMovingDirection * currentSpeed < -0.0014 then |
| 225 | self.nextMovingDirection = 0 |
| 226 | end |
| 227 | |
| 228 | -- Continue accelerating if we want to go in the same direction |
| 229 | -- or if the vehicle is only moving slowly in the wrong direction (0.0003 * 3600 = 1.08 km/h) and we are allowed to change direction |
| 230 | if accSign == self.nextMovingDirection or (currentSpeed * accSign > -0.0003 and (stopAndGoBraking or self.nextMovingDirection == 0)) then |
| 231 | acceleratorPedal = acceleration |
| 232 | brakePedal = 0 |
| 233 | self.nextMovingDirection = accSign |
| 234 | else |
| 235 | acceleratorPedal = 0 |
| 236 | brakePedal = math.abs(acceleration) |
| 237 | if stopAndGoBraking then |
| 238 | self.nextMovingDirection = accSign |
| 239 | end |
| 240 | end |
| 241 | end |
| 242 | |
| 243 | if automaticBrake then |
| 244 | acceleratorPedal = 0 |
| 245 | end |
| 246 | |
| 247 | acceleratorPedal = motor:updateGear(acceleratorPedal, dt) |
| 248 | |
| 249 | if motor.gear == 0 and motor.targetGear ~= 0 then |
| 250 | -- brake automatically if the vehicle is rolling backwards while shifting |
| 251 | if currentSpeed * MathUtil.sign(motor.targetGear) < 0 then |
| 252 | automaticBrake = true |
| 253 | end |
| 254 | end |
| 255 | |
| 256 | if automaticBrake then |
| 257 | local isSlow = absCurrentSpeed < motor.lowBrakeForceSpeedLimit |
| 258 | local isArticulatedSteering = self.spec_articulatedAxis ~= nil and self.spec_articulatedAxis.componentJoint ~= nil and math.abs(self.rotatedTime) > 0.01 |
| 259 | |
| 260 | if (isSlow or doHandbrake) and not isArticulatedSteering then |
| 261 | brakePedal = 1 |
| 262 | else |
| 263 | -- interpolate between lowBrakeForce and 1 if speed is below 3.6 km/h |
| 264 | local factor = math.min(absCurrentSpeed / 0.001, 1) |
| 265 | brakePedal = MathUtil.lerp(1, motor.lowBrakeForceScale, factor) |
| 266 | end |
| 267 | end |
| 268 | |
| 269 | -- ToDo: move to Lights ?! |
| 270 | if self.spec_lights ~= nil then |
| 271 | if self.setBrakeLightsVisibility ~= nil then |
| 272 | self:setBrakeLightsVisibility(not automaticBrake and math.abs(brakePedal) > 0) |
| 273 | end |
| 274 | |
| 275 | if self.setReverseLightsVisibility ~= nil then |
| 276 | self:setReverseLightsVisibility((currentSpeed < -0.0006 or acceleratorPedal < 0) and reverserDirection == 1) |
| 277 | end |
| 278 | end |
| 279 | |
| 280 | acceleratorPedal, brakePedal = WheelsUtil.getSmoothedAcceleratorAndBrakePedals(self, acceleratorPedal, brakePedal, dt) |
| 281 | |
| 282 | --active braking if over the speed limit |
| 283 | local overSpeedLimit = self:getLastSpeed() - motor:getSpeedLimit() |
| 284 | if overSpeedLimit > 0 then |
| 285 | brakePedal = math.max(math.min(overSpeedLimit/5, 1), brakePedal) |
| 286 | acceleratorPedal = math.min(0, acceleratorPedal) |
| 287 | end |
| 288 | |
| 289 | if next(self.spec_motorized.differentials) ~= nil and self.spec_motorized.motorizedNode ~= nil then |
| 290 | |
| 291 | local absAcceleratorPedal = math.abs(acceleratorPedal) |
| 292 | local minGearRatio, maxGearRatio = motor:getMinMaxGearRatio() |
| 293 | |
| 294 | local maxSpeed; |
| 295 | if maxGearRatio >= 0 then |
| 296 | maxSpeed = motor:getMaximumForwardSpeed() |
| 297 | else |
| 298 | maxSpeed = motor:getMaximumBackwardSpeed() |
| 299 | end |
| 300 | |
| 301 | local acceleratorPedalControlsSpeed = false |
| 302 | if acceleratorPedalControlsSpeed then |
| 303 | maxSpeed = maxSpeed * absAcceleratorPedal |
| 304 | if absAcceleratorPedal > 0.001 then |
| 305 | absAcceleratorPedal = 1 |
| 306 | end |
| 307 | end |
| 308 | maxSpeed = math.min(maxSpeed, motor:getSpeedLimit() / 3.6) |
| 309 | local maxAcceleration = motor:getAccelerationLimit() |
| 310 | local minMotorRpm, maxMotorRpm = motor:getRequiredMotorRpmRange() |
| 311 | |
| 312 | local neededPtoTorque = PowerConsumer.getTotalConsumedPtoTorque(self) / motor:getPtoMotorRpmRatio(); |
| 313 | |
| 314 | --print(string.format("set vehicle props: accPed=%.1f speed=%.1f gearRatio=[%.1f %.1f] rpm=[%.1f %.1f]", absAcceleratorPedal, maxSpeed, minGearRatio, maxGearRatio, minMotorRpm, maxMotorRpm)) |
| 315 | controlVehicle(self.spec_motorized.motorizedNode, absAcceleratorPedal, maxSpeed, maxAcceleration, minMotorRpm*math.pi/30, maxMotorRpm*math.pi/30, minGearRatio, maxGearRatio, motor:getMaxClutchTorque(), neededPtoTorque) |
| 316 | end |
| 317 | |
| 318 | self:brake(brakePedal) |
| 319 | end |
Update wheel physicsDefinition
updateWheelPhysics(table wheel, boolean doHandbrake, float brakePedal, float dt)Arguments
| table | wheel | wheel |
| boolean | doHandbrake | doHandbrake |
| float | brakePedal | brake pedal |
| float | dt | dt |
| 328 | function WheelsUtil.updateWheelPhysics(self, wheel, brakePedal, dt) |
| 329 | WheelsUtil.updateWheelSteeringAngle(self, wheel, dt) |
| 330 | |
| 331 | if self.isServer and self.isAddedToPhysics then |
| 332 | local brakeForce = self:getBrakeForce() * brakePedal |
| 333 | setWheelShapeProps(wheel.node, wheel.wheelShape, wheel.torque, brakeForce*wheel.brakeFactor, wheel.steeringAngle, wheel.rotationDamping) |
| 334 | end |
| 335 | end |
Update check if wheel has ground contactDefinition
updateWheelHasGroundContact(table wheel)Arguments
| table | wheel | wheel |
| 340 | function WheelsUtil.updateWheelHasGroundContact(wheel) |
| 341 | |
| 342 | local x,_,_ = getWheelShapeContactPoint(wheel.node, wheel.wheelShape) |
| 343 | wheel.hasGroundContact = x~=nil |
| 344 | --return wheel.hasGroundContact |
| 345 | end |
Update wheel steering angleDefinition
updateWheelSteeringAngle(table wheel, float dt)Arguments
| table | wheel | wheel |
| float | dt | time since last call in ms |
| 351 | function WheelsUtil.updateWheelSteeringAngle(self, wheel, dt) |
| 352 | |
| 353 | local steeringAngle = wheel.steeringAngle |
| 354 | local rotatedTime = self.rotatedTime |
| 355 | |
| 356 | if wheel.steeringAxleScale ~= nil and wheel.steeringAxleScale ~= 0 then |
| 357 | local steeringAxleAngle = 0 |
| 358 | if self.spec_attachable ~= nil then |
| 359 | steeringAxleAngle = self.spec_attachable.steeringAxleAngle |
| 360 | end |
| 361 | steeringAngle = MathUtil.clamp(steeringAxleAngle * wheel.steeringAxleScale, wheel.steeringAxleRotMin, wheel.steeringAxleRotMax) |
| 362 | elseif wheel.versatileYRot and self:getIsVersatileYRotActive(wheel) then |
| 363 | if self.isServer then |
| 364 | if wheel.forceVersatility or wheel.hasGroundContact then |
| 365 | steeringAngle = Utils.getVersatileRotation(wheel.repr, wheel.node, dt, wheel.positionX, wheel.positionY, wheel.positionZ, wheel.steeringAngle, wheel.rotMin, wheel.rotMax) |
| 366 | end |
| 367 | end |
| 368 | elseif wheel.rotSpeed ~= nil and wheel.rotMax ~= nil and wheel.rotMin ~= nil then |
| 369 | if rotatedTime > 0 or wheel.rotSpeedNeg == nil then |
| 370 | steeringAngle = rotatedTime * wheel.rotSpeed |
| 371 | else |
| 372 | steeringAngle = rotatedTime * wheel.rotSpeedNeg |
| 373 | end |
| 374 | if steeringAngle > wheel.rotMax then |
| 375 | steeringAngle = wheel.rotMax |
| 376 | elseif steeringAngle < wheel.rotMin then |
| 377 | steeringAngle = wheel.rotMin |
| 378 | end |
| 379 | if self.updateSteeringAngle ~= nil then |
| 380 | steeringAngle = self:updateSteeringAngle(wheel, dt, steeringAngle) |
| 381 | end |
| 382 | end |
| 383 | wheel.steeringAngle = steeringAngle |
| 384 | end |
Compute differential rot speed from properties of vehicle other than the motor, e.g. rot speed of wheels or linear speed of vehicleDefinition
computeDifferentialRotSpeedNonMotor()Return Values
| float | diffRotSpeed | rot speed [rad/sec] |
| 389 | function WheelsUtil.computeDifferentialRotSpeedNonMotor(self) |
| 390 | if self.isServer and self.spec_wheels ~= nil and #self.spec_wheels.wheels ~= 0 then |
| 391 | local wheelSpeed = 0 |
| 392 | local numWheels = 0 |
| 393 | for _, wheel in pairs(self.spec_wheels.wheels) do |
| 394 | local axleSpeed = getWheelShapeAxleSpeed(wheel.node, wheel.wheelShape) -- rad/sec |
| 395 | if wheel.hasGroundContact then |
| 396 | wheelSpeed = wheelSpeed + axleSpeed * wheel.radius |
| 397 | numWheels = numWheels+1 |
| 398 | end |
| 399 | end |
| 400 | |
| 401 | if numWheels > 0 then |
| 402 | return wheelSpeed/numWheels |
| 403 | end |
| 404 | return 0 |
| 405 | else |
| 406 | -- v = w*r => w = v/r |
| 407 | -- differentials have embeded gear so that r can be considered 1 |
| 408 | return self.lastSpeedReal*1000 |
| 409 | end |
| 410 | end |
Update wheel graphicsDefinition
updateVisualWheel(table wheel, float x, float y, float z, float x, float suspensionLength)Arguments
| table | wheel | wheel |
| float | x | x position |
| float | y | y position |
| float | z | z position |
| float | x | x drive rotation |
| float | suspensionLength | length of suspension |
| 472 | function WheelsUtil.updateVisualWheel(self, wheel, x, y, z, xDrive, suspensionLength) |
| 473 | local changed = false |
| 474 | |
| 475 | local steeringAngle = wheel.steeringAngle |
| 476 | if not wheel.showSteeringAngle then |
| 477 | steeringAngle = 0 |
| 478 | end |
| 479 | |
| 480 | local _, oldY, _ = getRotation(wheel.repr) |
| 481 | local dirX, dirY, dirZ = localDirectionToLocal(wheel.repr, getParent(wheel.repr), 0, -1, 0) |
| 482 | if math.abs(steeringAngle-oldY) > WheelsUtil.STEERING_ANGLE_THRESHOLD then |
| 483 | setRotation(wheel.repr, 0, steeringAngle, 0) |
| 484 | changed = true |
| 485 | end |
| 486 | |
| 487 | local oldX, _, _ = getRotation(wheel.driveNode) |
| 488 | if math.abs(xDrive-oldX) > WheelsUtil.STEERING_ANGLE_THRESHOLD then |
| 489 | setRotation(wheel.driveNode, xDrive, 0, 0) |
| 490 | changed = true |
| 491 | end |
| 492 | |
| 493 | if wheel.wheelTire ~= nil then |
| 494 | local x, y, z, _ = getShaderParameter(wheel.wheelTire, "morphPosition") |
| 495 | local deformation = MathUtil.clamp((wheel.deltaY+0.04-suspensionLength)*0.7, 0, wheel.maxDeformation) |
| 496 | if math.abs(deformation - wheel.deformation) > 0.01 then |
| 497 | wheel.deformation = deformation |
| 498 | setShaderParameter(wheel.wheelTire, "morphPosition", x, y, z, deformation, false) |
| 499 | |
| 500 | if wheel.additionalWheels ~= nil then |
| 501 | for _, additionalWheel in pairs(wheel.additionalWheels) do |
| 502 | local x, y, z, _ = getShaderParameter(additionalWheel.wheelTire, "morphPosition") |
| 503 | setShaderParameter(additionalWheel.wheelTire, "morphPosition", x, y, z, deformation, false) |
| 504 | end |
| 505 | end |
| 506 | changed = true |
| 507 | end |
| 508 | |
| 509 | suspensionLength = suspensionLength+deformation |
| 510 | end |
| 511 | |
| 512 | suspensionLength = suspensionLength - wheel.deltaY |
| 513 | |
| 514 | if math.abs(wheel.lastMovement-suspensionLength) > WheelsUtil.SUSPENSION_THRESHOLD then |
| 515 | local transRatio = wheel.transRatio |
| 516 | local movement = suspensionLength * transRatio |
| 517 | setTranslation(wheel.repr, wheel.startPositionX + dirX*movement, wheel.startPositionY + dirY*movement, wheel.startPositionZ + dirZ*movement) |
| 518 | changed = true |
| 519 | if transRatio < 1 then |
| 520 | movement = suspensionLength*(1-transRatio) |
| 521 | setTranslation(wheel.driveNode, wheel.driveNodeStartPosX + dirX*movement, wheel.driveNodeStartPosY + dirY*movement, wheel.driveNodeStartPosZ + dirZ*movement) |
| 522 | end |
| 523 | |
| 524 | wheel.lastMovement = suspensionLength |
| 525 | end |
| 526 | |
| 527 | if wheel.steeringNode ~= nil then |
| 528 | local refAngle = wheel.steeringNodeMaxRot |
| 529 | local refTrans = wheel.steeringNodeMaxTransX |
| 530 | local refRot = wheel.steeringNodeMaxRotY |
| 531 | if steeringAngle < 0 then |
| 532 | refAngle = wheel.steeringNodeMinRot |
| 533 | refTrans = wheel.steeringNodeMinTransX |
| 534 | refRot = wheel.steeringNodeMinRotY |
| 535 | end |
| 536 | local steering = 0 |
| 537 | if refAngle ~= 0 then |
| 538 | steering = steeringAngle / refAngle |
| 539 | end |
| 540 | |
| 541 | if wheel.steeringNodeMinTransX ~= nil then |
| 542 | local x,y,z = getTranslation(wheel.steeringNode) |
| 543 | x = refTrans * steering |
| 544 | setTranslation(wheel.steeringNode, x, y, z) |
| 545 | end |
| 546 | if wheel.steeringNodeMinRotY ~= nil then |
| 547 | local rotX,rotY,rotZ = getRotation(wheel.steeringNode) |
| 548 | rotY = refRot * steering |
| 549 | setRotation(wheel.steeringNode, rotX, rotY, rotZ) |
| 550 | end |
| 551 | end |
| 552 | |
| 553 | if wheel.fenderNode ~= nil then |
| 554 | local angleDif = 0 |
| 555 | if steeringAngle > wheel.fenderRotMax then |
| 556 | angleDif = wheel.fenderRotMax - steeringAngle |
| 557 | elseif steeringAngle < wheel.fenderRotMin then |
| 558 | angleDif = wheel.fenderRotMin - steeringAngle |
| 559 | end |
| 560 | setRotation(wheel.fenderNode, 0, angleDif, 0) |
| 561 | end |
| 562 | |
| 563 | return changed |
| 564 | end |
Returns tire frictionDefinition
getTireFriction(integer tireType, integer groundType, float wetScale)Arguments
| integer | tireType | tire type index |
| integer | groundType | ground type index |
| float | wetScale | wet scale |
| float | tireFriction | tire friction |
| 572 | function WheelsUtil.getTireFriction(tireType, groundType, wetScale) |
| 573 | if wetScale == nil then |
| 574 | wetScale = 0 |
| 575 | end |
| 576 | local coeff = WheelsUtil.tireTypes[tireType].frictionCoeffs[groundType] |
| 577 | local coeffWet = WheelsUtil.tireTypes[tireType].frictionCoeffsWet[groundType] |
| 578 | return coeff + (coeffWet-coeff)*wetScale |
| 579 | end |
Get ground typeDefinition
getGroundType(boolean isField, boolean isRoad, float depth)Arguments
| boolean | isField | is on field |
| boolean | isRoad | is on road |
| float | depth | depth of terrain |
| integer | groundType | ground type |
| 587 | function WheelsUtil.getGroundType(isField, isRoad, depth) |
| 588 | -- terrain softness: |
| 589 | -- [ 0, 0.1]: road |
| 590 | -- [0.1, 0.8]: hard terrain |
| 591 | -- [0.8, 1 ]: soft terrain |
| 592 | if isField then |
| 593 | return WheelsUtil.GROUND_FIELD |
| 594 | elseif isRoad or depth < 0.1 then |
| 595 | return WheelsUtil.GROUND_ROAD |
| 596 | else |
| 597 | if depth > 0.8 then |
| 598 | return WheelsUtil.GROUND_SOFT_TERRAIN |
| 599 | else |
| 600 | return WheelsUtil.GROUND_HARD_TERRAIN |
| 601 | end |
| 602 | end |
| 603 | end |
Creating managerDefinition
new()Return Values
| table | instance | instance of object |
| 19 | function WorkAreaTypeManager:new(customMt) |
| 20 | local self = AbstractManager:new(customMt or WorkAreaTypeManager_mt) |
| 21 | |
| 22 | return self |
| 23 | end |
Initialize data structuresDefinition
initDataStructures()Code
| 27 | function WorkAreaTypeManager:initDataStructures() |
| 28 | self.workAreaTypes = {} |
| 29 | self.workAreaTypeNameToInt = {} |
| 30 | self.workAreaTypeNameToDesc = {} |
| 31 | WorkAreaType = self.workAreaTypeNameToInt |
| 32 | end |
Specialization for AI converyor belts
Checks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
| table | specializations | specializations |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| 19 | function AIConveyorBelt.prerequisitesPresent(specializations) |
| 20 | return SpecializationUtil.hasSpecialization(AIVehicle, specializations) and SpecializationUtil.hasSpecialization(Motorized, specializations) |
| 21 | end |
Called on loadingDefinition
onLoad(table savegame)Arguments
| table | savegame | savegame |
| 47 | function AIConveyorBelt:onLoad(savegame) |
| 48 | local spec = self.spec_aiConveyorBelt |
| 49 | |
| 50 | spec.isAllowed = hasXMLProperty(self.xmlFile, "vehicle.ai.conveyorBelt") |
| 51 | |
| 52 | spec.minAngle = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.ai.conveyorBelt#minAngle"), 5) |
| 53 | spec.maxAngle = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.ai.conveyorBelt#maxAngle"), 45) |
| 54 | spec.stepSize = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.ai.conveyorBelt#stepSize"), 5) |
| 55 | spec.currentAngle = spec.maxAngle |
| 56 | |
| 57 | spec.minTargetWorldYRot = 0 |
| 58 | spec.maxTargetWorldYRot = 0 |
| 59 | spec.currentDirection = 0 |
| 60 | spec.currentSpeed = 0 |
| 61 | |
| 62 | spec.speed = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.ai.conveyorBelt#speed"), 1) |
| 63 | spec.direction = Utils.getNoNil(getXMLFloat(self.xmlFile, "vehicle.ai.conveyorBelt#direction"), -1) |
| 64 | end |
Called on client side on joinDefinition
onReadStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 82 | function AIConveyorBelt:onReadStream(streamId, connection) |
| 83 | self:setAIConveyorBeltAngle(streamReadInt8(streamId), true) |
| 84 | end |
Called on server side on joinDefinition
onWriteStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 90 | function AIConveyorBelt:onWriteStream(streamId, connection) |
| 91 | streamWriteInt8(streamId, self.spec_aiConveyorBelt.currentAngle) |
| 92 | end |
Called on updateDefinition
onUpdate(float dt, boolean isActiveForInput, boolean isSelected)Arguments
| float | dt | time since last call in ms |
| boolean | isActiveForInput | true if vehicle is active for input |
| boolean | isSelected | true if vehicle is selected |
| 99 | function AIConveyorBelt:onUpdate(dt, isActiveForInput, isSelected) |
| 100 | local spec = self.spec_aiConveyorBelt |
| 101 | if self.isServer then |
| 102 | if self:getIsAIActive() then |
| 103 | spec.currentDirection, spec.currentSpeed = self:getDirectionAndSpeedToTargetAngle(spec.currentDirection, spec.minTargetWorldYRot, spec.maxTargetWorldYRot) |
| 104 | |
| 105 | self:getMotor():setSpeedLimit(math.abs(spec.currentSpeed * spec.speed)) |
| 106 | WheelsUtil.updateWheelsPhysics(self, dt, spec.currentSpeed * spec.speed * spec.direction, spec.currentDirection * spec.direction, false, true) |
| 107 | end |
| 108 | end |
| 109 | end |
Called on updateTickDefinition
onUpdateTick(float dt, boolean isActiveForInput, boolean isSelected)Arguments
| float | dt | time since last call in ms |
| boolean | isActiveForInput | true if vehicle is active for input |
| boolean | isSelected | true if vehicle is selected |
| 116 | function AIConveyorBelt:onUpdateTick(dt, isActiveForInput, isSelected) |
| 117 | local spec = self.spec_aiConveyorBelt |
| 118 | if self.isClient then |
| 119 | local actionEvent = spec.actionEvents[InputAction.IMPLEMENT_EXTRA3] |
| 120 | if actionEvent ~= nil then |
| 121 | g_inputBinding:setActionEventActive(actionEvent.actionEventId, isActiveForInput) |
| 122 | if isActiveForInput then |
| 123 | g_inputBinding:setActionEventText(actionEvent.actionEventId, string.format(g_i18n:getText("action_conveyorBeltChangeAngle"), string.format("%.0f", spec.currentAngle))) |
| 124 | end |
| 125 | end |
| 126 | end |
| 127 | end |
Specialization for AI Implements
Checks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
| table | specializations | specializations |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| 17 | function AIImplement.prerequisitesPresent(specializations) |
| 18 | return true |
| 19 | end |
Called on loadingDefinition
onLoad(table savegame)Arguments
| table | savegame | savegame |
| 97 | function AIImplement:onLoad(savegame) |
| 98 | local spec = self.spec_aiImplement |
| 99 | |
| 100 | local baseName = "vehicle.ai" |
| 101 | |
| 102 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".areaMarkers#leftIndex", baseName .. ".areaMarkers#leftNode") -- FS17 to FS 19 |
| 103 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".areaMarkers#rightIndex", baseName .. ".areaMarkers#rightNode") -- FS17 to FS 19 |
| 104 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".areaMarkers#backIndex", baseName .. ".areaMarkers#backNode") -- FS17 to FS 19 |
| 105 | |
| 106 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".sizeMarkers#leftIndex", baseName .. ".sizeMarkers#leftNode") -- FS17 to FS 19 |
| 107 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".sizeMarkers#rightIndex", baseName .. ".sizeMarkers#rightNode") -- FS17 to FS 19 |
| 108 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".sizeMarkers#backIndex", baseName .. ".sizeMarkers#backNode") -- FS17 to FS 19 |
| 109 | |
| 110 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".trafficCollisionTrigger#index", baseName .. ".collisionTrigger#node") -- FS17 to FS 19 |
| 111 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".trafficCollisionTrigger#node", baseName .. ".collisionTrigger#node") -- FS17 to FS 19 |
| 112 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".collisionTrigger#index", baseName .. ".collisionTrigger#node") -- FS17 to FS 19 |
| 113 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.aiLookAheadSize#value", baseName .. ".lookAheadSize#value") -- FS17 to FS 19 |
| 114 | |
| 115 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".toolReverserDirectionNode#index", baseName .. ".toolReverserDirectionNode#node") -- FS17 to FS 19 |
| 116 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".turningRadiusLimiation", baseName .. ".turningRadiusLimitation") -- FS17 to FS 19 |
| 117 | |
| 118 | XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. ".forceTurnNoBackward#value", baseName .. ".allowTurnBackward#value (inverted)") -- FS17 to FS 19 |
| 119 | |
| 120 | |
| 121 | spec.minTurningRadius = getXMLFloat(self.xmlFile, baseName .. ".minTurningRadius#value") |
| 122 | |
| 123 | spec.leftMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".areaMarkers#leftNode"), self.i3dMappings) |
| 124 | spec.rightMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".areaMarkers#rightNode"), self.i3dMappings) |
| 125 | spec.backMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".areaMarkers#backNode"), self.i3dMappings) |
| 126 | spec.aiMarkersInverted = false |
| 127 | |
| 128 | spec.sizeLeftMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".sizeMarkers#leftNode"), self.i3dMappings) |
| 129 | spec.sizeRightMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".sizeMarkers#rightNode"), self.i3dMappings) |
| 130 | spec.sizeBackMarker = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".sizeMarkers#backNode"), self.i3dMappings) |
| 131 | |
| 132 | spec.collisionTrigger = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".collisionTrigger#node"), self.i3dMappings) |
| 133 | if spec.collisionTrigger ~= nil then |
| 134 | local rigidBodyType = getRigidBodyType(spec.collisionTrigger) |
| 135 | if rigidBodyType ~= "Kinematic" then |
| 136 | g_logManager:xmlWarning(self.configFileName, "'aiCollisionTrigger' is not a kinematic body type") |
| 137 | end |
| 138 | end |
| 139 | |
| 140 | spec.needsLowering = Utils.getNoNil(getXMLBool(self.xmlFile, baseName .. ".needsLowering#value"), true) |
| 141 | spec.lowerIfAnyIsLowerd = Utils.getNoNil(getXMLBool(self.xmlFile, baseName .. ".needsLowering#lowerIfAnyIsLowerd"), false) |
| 142 | |
| 143 | spec.allowTurnBackward = Utils.getNoNil(getXMLBool(self.xmlFile, baseName .. ".allowTurnBackward#value"), true) |
| 144 | |
| 145 | spec.toolReverserDirectionNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".toolReverserDirectionNode#node"), self.i3dMappings) |
| 146 | |
| 147 | spec.turningRadiusLimitation = {} |
| 148 | spec.turningRadiusLimitation.rotationJoint = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, baseName .. ".turningRadiusLimitation#rotationJointNode"), self.i3dMappings) |
| 149 | if spec.turningRadiusLimitation.rotationJoint ~= nil then |
| 150 | spec.turningRadiusLimitation.wheelIndices = {StringUtil.getVectorFromString(getXMLString(self.xmlFile, baseName .. ".turningRadiusLimitation#wheelIndices"))} |
| 151 | end |
| 152 | |
| 153 | spec.turningRadiusLimitation.radius = getXMLFloat(self.xmlFile, baseName .. ".turningRadiusLimitation#radius") |
| 154 | |
| 155 | spec.lookAheadSize = Utils.getNoNil(getXMLFloat(self.xmlFile, baseName .. ".lookAheadSize#value"), 2) |
| 156 | spec.useAttributesOfAttachedImplement = Utils.getNoNil(getXMLBool(self.xmlFile, baseName .. ".useAttributesOfAttachedImplement#value"), false) |
| 157 | |
| 158 | spec.hasNoFullCoverageArea = Utils.getNoNil(getXMLString(self.xmlFile, baseName .. ".hasNoFullCoverageArea#value"), false) |
| 159 | |
| 160 | spec.terrainDetailRequiredValueRanges = {} |
| 161 | spec.terrainDetailProhibitedValueRanges = {} |
| 162 | |
| 163 | spec.requiredFruitTypes = {} |
| 164 | spec.prohibitedFruitTypes = {} |
| 165 | |
| 166 | spec.isLineStarted = false |
| 167 | end |
Specialization for AI vehicles
Checks if all prerequisite specializations are loadedDefinition
prerequisitesPresent(table specializations)Arguments
| table | specializations | specializations |
| boolean | hasPrerequisite | true if all prerequisite specializations are loaded |
| 62 | function AIVehicle.prerequisitesPresent(specializations) |
| 63 | return SpecializationUtil.hasSpecialization(Drivable, specializations) |
| 64 | end |
Called on loadingDefinition
onLoad(table savegame)Arguments
| table | savegame | savegame |
| 152 | function AIVehicle:onLoad(savegame) |
| 153 | local spec = self.spec_aiVehicle |
| 154 | |
| 155 | spec.aiSteeringSpeed = Utils.getNoNil(getXMLFloat(spec.xmlFile, "vehicle.ai.steeringSpeed"), 1)*0.001 |
| 156 | |
| 157 | spec.isActive = false |
| 158 | |
| 159 | spec.aiImplementList = {} |
| 160 | spec.aiImplementDataDirtyFlag = true |
| 161 | |
| 162 | spec.taskList = {} |
| 163 | |
| 164 | spec.driveStrategies = {} |
| 165 | |
| 166 | spec.didNotMoveTimeout = Utils.getNoNil( getXMLFloat(spec.xmlFile, "vehicle.ai.didNotMoveTimeout#value"), 5000) |
| 167 | if getXMLBool(spec.xmlFile, "vehicle.ai.didNotMoveTimeout#deactivated") then |
| 168 | spec.didNotMoveTimeout = math.huge |
| 169 | end |
| 170 | |
| 171 | spec.didNotMoveTimer = spec.didNotMoveTimeout |
| 172 | |
| 173 | spec.debugTexts = {} |
| 174 | spec.debugLines = {} |
| 175 | |
| 176 | spec.aiTrafficCollision = nil |
| 177 | spec.aiTrafficCollisionRemoveDelay = 0 |
| 178 | spec.aiTrafficCollisionTranslation = {0, 0, 20} |
| 179 | spec.aiTrafficCollisionScale = {40, 30, 40} |
| 180 | |
| 181 | spec.pricePerMS = Utils.getNoNil(getXMLFloat(spec.xmlFile, "vehicle.ai.pricePerHour"), 2000)/60/60/1000 |
| 182 | |
| 183 | spec.steeringNode = I3DUtil.indexToObject(self.components, getXMLString(self.xmlFile, "vehicle.ai.steeringNode#node"), self.i3dMappings) |
| 184 | end |
Called on client side on joinDefinition
onReadStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 190 | function AIVehicle:onReadStream(streamId, connection) |
| 191 | if streamReadBool(streamId) then |
| 192 | local helperIndex = streamReadUInt8(streamId) |
| 193 | local farmId = streamReadUIntN(streamId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 194 | self:startAIVehicle(helperIndex, true, farmId) |
| 195 | end |
| 196 | end |
Called on server side on joinDefinition
onWriteStream(integer streamId, integer connection)Arguments
| integer | streamId | streamId |
| integer | connection | connection |
| 202 | function AIVehicle:onWriteStream(streamId, connection) |
| 203 | local spec = self.spec_aiVehicle |
| 204 | if streamWriteBool(streamId, self:getIsAIActive()) then |
| 205 | streamWriteUInt8(streamId, spec.currentHelper.index) |
| 206 | streamWriteUIntN(streamId, spec.startedFarmId, FarmManager.FARM_ID_SEND_NUM_BITS) |
| 207 | end |
| 208 | end |
Called on updateDefinition
onUpdate(float dt, boolean isActiveForInput, boolean isSelected)Arguments
| float | dt | time since last call in ms |
| boolean | isActiveForInput | true if vehicle is active for input |
| boolean | isSelected | true if vehicle is selected |
| 215 | function AIVehicle:onUpdate(dt, isActiveForInput, isSelected) |
| 216 | local spec = self.spec_aiVehicle |
| 217 | |
| 218 | if VehicleDebug.state == VehicleDebug.DEBUG_AI and isActiveForInput then |
| 219 | if #spec.debugTexts > 0 then |
| 220 | for i, text in pairs(spec.debugTexts) do |
| 221 | renderText(0.7, 0.92-(0.02*i), 0.02, text) |
| 222 | end |
| 223 | end |
| 224 | if #spec.debugLines > 0 then |
| 225 | for _, l in pairs(spec.debugLines) do |
| 226 | drawDebugLine(l.s[1],l.s[2],l.s[3], l.c[1],l.c[2],l.c[3], l.e[1],l.e[2],l.e[3], l.c[1],l.c[2],l.c[3]) |
| 227 | end |
| 228 | end |
| 229 | end |
| 230 | |
| 231 | if spec.aiImplementDataDirtyFlag then |
| 232 | spec.aiImplementDataDirtyFlag = false |
| 233 | self:updateAIImplementData() |
| 234 | end |
| 235 | |
| 236 | if spec.isServer then |
| 237 | if self:getIsAIActive() then |
| 238 | if spec.driveStrategies ~= nil and #spec.driveStrategies > 0 then |
| 239 | for i=1,#spec.driveStrategies do |
| 240 | local driveStrategy = spec.driveStrategies[i] |
| 241 | driveStrategy:update(dt) |
| 242 | end |
| 243 | end |
| 244 | end |
| 245 | end |
| 246 | |
| 247 | -- as long as the ai is turned on we raise active (e.g. if the vehicle stops due a collision the vehicle collision may sleep and stop raising active) |
| 248 | if self:getIsAIActive() then |
| 249 | self:raiseActive() |
| 250 | end |
| 251 | end |
Called on update tickDefinition
onUpdateTick(float dt, boolean isActiveForInput, boolean isSelected)Arguments
| float | dt | time since last call in ms |
| boolean | isActiveForInput | true if vehicle is active for input |
| boolean | isSelected | true if vehicle is selected |
| 258 | function AIVehicle:onUpdateTick(dt, isActiveForInput, isSelected) |
| 259 | local spec = self.spec_aiVehicle |
| 260 | |
| 261 | self:clearAIDebugTexts() |
| 262 | self:clearAIDebugLines() |
| 263 | |
| 264 | if self.isServer then |
| 265 | if self:getIsAIActive() then |
| 266 | local difficultyMultiplier = g_currentMission.missionInfo.buyPriceMultiplier; |
| 267 | local price = -dt * difficultyMultiplier * spec.pricePerMS |
| 268 | |
| 269 | -- If field was not owned, it is a mission. Increase the price for balancing. |
| 270 | if self.getLastTouchedFarmlandFarmId ~= nil and self:getLastTouchedFarmlandFarmId() == 0 then |
| 271 | price = price * MissionManager.AI_PRICE_MULTIPLIER |
| 272 | end |
| 273 | |
| 274 | g_currentMission:addMoney(price, spec.startedFarmId, "wagePayment"); |
| 275 | g_currentMission:addMoneyChange(price, spec.startedFarmId, FSBaseMission.MONEY_TYPE_AI) |
| 276 | |
| 277 | if spec.driveStrategies ~= nil and #spec.driveStrategies > 0 then |
| 278 | local vX,vY,vZ = getWorldTranslation(self:getAIVehicleSteeringNode()) |
| 279 | |
| 280 | local tX, tZ, moveForwards, maxSpeedStra, maxSpeed, distanceToStop |
| 281 | for i=1,#spec.driveStrategies do |
| 282 | local driveStrategy = spec.driveStrategies[i] |
| 283 | tX, tZ, moveForwards, maxSpeedStra, distanceToStop = driveStrategy:getDriveData(dt, vX,vY,vZ) |
| 284 | maxSpeed = math.min(maxSpeedStra or math.huge, maxSpeed or math.huge) |
| 285 | if tX ~= nil or not self:getIsAIActive() then |
| 286 | break |
| 287 | end |
| 288 | end |
| 289 | |
| 290 | if tX == nil then |
| 291 | if self:getIsAIActive() then -- check if AI is still active, because it might have been kicked by a strategy |
| 292 | self:stopAIVehicle(AIVehicle.STOP_REASON_REGULAR) |
| 293 | end |
| 294 | end |
| 295 | |
| 296 | if not self:getIsAIActive() then |
| 297 | return |
| 298 | end |
| 299 | |
| 300 | local acceleration = 1.0 |
| 301 | |
| 302 | local minimumSpeed = 5 |
| 303 | local lookAheadDistance = 5 |
| 304 | |
| 305 | local distSpeed = math.max(minimumSpeed, maxSpeed * math.min(1, distanceToStop/lookAheadDistance)) |
| 306 | local speedLimit, _ = self:getSpeedLimit() |
| 307 | maxSpeed = math.min(maxSpeed, distSpeed, speedLimit) |
| 308 | maxSpeed = math.min(maxSpeed, self:getCruiseControlMaxSpeed()) |
| 309 | |
| 310 | local isAllowedToDrive = maxSpeed ~= 0 |
| 311 | |
| 312 | local pX, _, pZ = worldToLocal(self:getAIVehicleSteeringNode(), tX,vY,tZ) |
| 313 | if not moveForwards and self.spec_articulatedAxis ~= nil then |
| 314 | if self.spec_articulatedAxis.aiRevereserNode ~= nil then |
| 315 | pX, _, pZ = worldToLocal(self.spec_articulatedAxis.aiRevereserNode, tX,vY,tZ) |
| 316 | end |
| 317 | end |
| 318 | |
| 319 | AIVehicleUtil.driveToPoint(self, dt, acceleration, isAllowedToDrive, moveForwards, pX, pZ, maxSpeed) |
| 320 | |
| 321 | -- worst case check: did not move but should have moved |
| 322 | if isAllowedToDrive and self:getLastSpeed() < 0.5 then |
| 323 | spec.didNotMoveTimer = spec.didNotMoveTimer - dt |
| 324 | else |
| 325 | spec.didNotMoveTimer = spec.didNotMoveTimeout |
| 326 | end |
| 327 | |
| 328 | if spec.didNotMoveTimer < 0 then |
| 329 | self:stopAIVehicle(AIVehicle.STOP_REASON_BLOCKED_BY_OBJECT) |
| 330 | end |
| 331 | end |
| 332 | |
| 333 | if #spec.taskList > 0 then |
| 334 | for i, task in pairs(spec.taskList) do |
| 335 | self:addAIDebugText(string.format("AI TASK: %d - %s", i, task.getFunc)) |
| 336 | if task.getObject ~= nil then |
| 337 | if task.getObject[task.getFunc](task.getObject, unpack(task.getParams)) then |
| 338 | task.setObject[task.setFunc](task.setObject, unpack(task.setParams)) |
| 339 | spec.taskList[i] = nil |
| 340 | end |
| 341 | else |
| 342 | task.setObject[task.setFunc](task.setObject, unpack(task.setParams)) |
| 343 | spec.taskList[i] = nil |
| 344 | end |
| 345 | end |
| 346 | end |
| 347 | |
| 348 | self:raiseAIEvent("onAIActive", "onAIImplementActive") |
| 349 | else |
| 350 | if spec.aiTrafficCollisionRemoveDelay > 0 then |
| 351 | spec.aiTrafficCollisionRemoveDelay = spec.aiTrafficCollisionRemoveDelay - dt |
| 352 | if spec.aiTrafficCollisionRemoveDelay <= 0 then |
| 353 | if spec.aiTrafficCollision ~= nil then |
| 354 | if entityExists(spec.aiTrafficCollision) then |
| 355 | delete(spec.aiTrafficCollision) |
| 356 | end |
| 357 | end |
| 358 | spec.aiTrafficCollisionRemoveDelay = 0 |
| 359 | end |
| 360 | end |
| 361 | end |
| 362 | end |
| 363 | |
| 364 | if self.isClient then |
| 365 | local actionEvent = spec.actionEvents[InputAction.TOGGLE_AI] |
| 366 | if actionEvent ~= nil then |
| 367 | local showAction = false |
| 368 | |
| 369 | if self:getIsActiveForInput(true, true) then |
| 370 | -- if ai is active we always display the dismiss helper action |
| 371 | showAction = self:getCanStartAIVehicle() or self:getIsAIActive() |
| 372 | |
| 373 | if showAction then |
| 374 | if self:getIsAIActive() then |
| 375 | g_inputBinding:setActionEventText(actionEvent.actionEventId, g_i18n:getText("action_dismissEmployee")) |
| 376 | else |
| 377 | g_inputBinding:setActionEventText(actionEvent.actionEventId, g_i18n:getText("action_hireEmployee")) |
| 378 | end |
| 379 | end |
| 380 | end |
| 381 | |
| 382 | g_inputBinding:setActionEventActive(actionEvent.actionEventId, showAction) |
| 383 | end |
| 384 | end |
| 385 | end |
Returns true if ai can startDefinition
getCanStartAIVehicle()Return Values
| boolean | canStart | can start ai |
| 390 | function AIVehicle:getCanStartAIVehicle() |
| 391 | local spec = self.spec_aiVehicle |
| 392 | |
| 393 | if self:getAIVehicleDirectionNode() == nil then |
| 394 | return false |
| 395 | end |
| 396 | |
| 397 | if g_currentMission.disableAIVehicle then |
| 398 | return false |
| 399 | end |
| 400 | |
| 401 | if AIVehicle.numHirablesHired >= g_currentMission.maxNumHirables then |
| 402 | return false |
| 403 | end |
| 404 | |
| 405 | if #spec.aiImplementList == 0 then |
| 406 | return false |
| 407 | end |
| 408 | |
| 409 | return true |
| 410 | end |
Returns true if ai can contiueDefinition
getCanAIVehicleContinueWork()Return Values
| boolean | canContiue | can contiue ai |
| 415 | function AIVehicle:getCanAIVehicleContinueWork() |
| 416 | for _, implement in ipairs(self:getAttachedAIImplements()) do |
| 417 | if not implement.object:getCanAIImplementContinueWork() then |
| 418 | return false |
| 419 | end |
| 420 | end |
| 421 | |
| 422 | if SpecializationUtil.hasSpecialization(AIImplement, self.specializations) then |
| 423 | if not self:getCanAIImplementContinueWork() then |
| 424 | return false |
| 425 | end |
| 426 | end |
| 427 | |
| 428 | return true |
| 429 | end |
Starts ai vehicleDefinition
startAIVehicle(integer helperIndex, boolean noEventSend)Arguments
| integer | helperIndex | index of hired helper |
| boolean | noEventSend | no event send |
| 463 | function AIVehicle:startAIVehicle(helperIndex, noEventSend, startedFarmId) |
| 464 | local spec = self.spec_aiVehicle |
| 465 | |
| 466 | if not self:getIsAIActive() then |
| 467 | if helperIndex ~= nil then |
| 468 | spec.currentHelper = g_helperManager:getHelperByIndex(helperIndex) |
| 469 | else |
| 470 | spec.currentHelper = g_helperManager:getRandomHelper() |
| 471 | end |
| 472 | |
| 473 | g_helperManager:useHelper(spec.currentHelper) |
| 474 | |
| 475 | spec.startedFarmId = startedFarmId |
| 476 | |
| 477 | g_farmManager:getFarmById(startedFarmId).stats:updateStats("workersHired", 1) |
| 478 | |
| 479 | if noEventSend == nil or noEventSend == false then |
| 480 | local event = AIVehicleSetStartedEvent:new(self, nil, true, spec.currentHelper, startedFarmId) |
| 481 | if g_server ~= nil then |
| 482 | g_server:broadcastEvent(event, nil, nil, self) |
| 483 | else |
| 484 | g_client:getServerConnection():sendEvent(event) |
| 485 | end |
| 486 | end |
| 487 | |
| 488 | AIVehicle.numHirablesHired = AIVehicle.numHirablesHired + 1 |
| 489 | |
| 490 | if self.setRandomVehicleCharacter ~= nil then |
| 491 | self:setRandomVehicleCharacter() |
| 492 | end |
| 493 | |
| 494 | local hotspotX, _, hotspotZ = getWorldTranslation(spec.rootNode) |
| 495 | |
| 496 | local _, textSize = getNormalizedScreenValues(0, 9) |
| 497 | local _, textOffsetY = getNormalizedScreenValues(0, 18) |
| 498 | local width, height = getNormalizedScreenValues(24, 24) |
| 499 | spec.mapAIHotspot = MapHotspot:new("helper", MapHotspot.CATEGORY_AI) |
| 500 | spec.mapAIHotspot:setSize(width, height) |
| 501 | spec.mapAIHotspot:setLinkedNode(spec.components[1].node) |
| 502 | spec.mapAIHotspot:setText(spec.currentHelper.name) |
| 503 | spec.mapAIHotspot:setImage(nil, getNormalizedUVs(MapHotspot.UV.HELPER), {0.052, 0.1248, 0.672, 1}) |
| 504 | spec.mapAIHotspot:setBackgroundImage(nil, getNormalizedUVs(MapHotspot.UV.HELPER)) |
| 505 | spec.mapAIHotspot:setIconScale(0.7) |
| 506 | spec.mapAIHotspot:setTextOptions(textSize, nil, textOffsetY, {1, 1, 1, 1}, Overlay.ALIGN_VERTICAL_MIDDLE) |
| 507 | g_currentMission:addMapHotspot(spec.mapAIHotspot) |
| 508 | |
| 509 | spec.isActive = true |
| 510 | |
| 511 | if spec.isServer then |
| 512 | self:updateAIImplementData() |
| 513 | self:updateAIDriveStrategies() |
| 514 | end |
| 515 | |
| 516 | self:raiseAIEvent("onAIStart", "onAIImplementStart") |
| 517 | self:requestActionEventUpdate() |
| 518 | |
| 519 | local collisionRoot = g_i3DManager:loadSharedI3DFile(AIVehicle.TRAFFIC_COLLISION_BOX_FILENAME, self.baseDirectory, false, true, false) |
| 520 | if collisionRoot ~= nil and collisionRoot ~= 0 then |
| 521 | local collision = getChildAt(collisionRoot, 0) |
| 522 | |
| 523 | link(self.components[1].node, collision) |
| 524 | |
| 525 | setTranslation(collision, unpack(spec.aiTrafficCollisionTranslation)) |
| 526 | setScale(collision, unpack(spec.aiTrafficCollisionScale)) |
| 527 | spec.aiTrafficCollision = collision |
| 528 | |
| 529 | delete(collisionRoot) |
| 530 | end |
| 531 | end |
| 532 | end |
Stops ai vehicleDefinition
stopAIVehicle(integer reason, boolean noEventSend)Arguments
| integer | reason | reason |
| boolean | noEventSend | no event send |
| 538 | function AIVehicle:stopAIVehicle(reason, noEventSend) |
| 539 | local spec = self.spec_aiVehicle |
| 540 | |
| 541 | if self:getIsAIActive() then |
| 542 | if noEventSend == nil or noEventSend == false then |
| 543 | local event = AIVehicleSetStartedEvent:new(self, reason, false, nil, spec.startedFarmId) |
| 544 | |
| 545 | if g_server ~= nil then |
| 546 | g_server:broadcastEvent(event, nil, nil, self) |
| 547 | else |
| 548 | g_client:getServerConnection():sendEvent(event) |
| 549 | end |
| 550 | end |
| 551 | |
| 552 | if self.isClient then |
| 553 | if g_currentMission.player ~= nil then |
| 554 | if g_currentMission.player.farmId == spec.startedFarmId then |
| 555 | if reason ~= nil and reason ~= AIVehicle.STOP_REASON_USER then |
| 556 | local notificationType = FSBaseMission.INGAME_NOTIFICATION_CRITICAL |
| 557 | if reason == AIVehicle.STOP_REASON_REGULAR then |
| 558 | notificationType = FSBaseMission.INGAME_NOTIFICATION_OK |
| 559 | end |
| 560 | |
| 561 | g_currentMission:addIngameNotification(notificationType, string.format(g_i18n:getText(AIVehicle.REASON_TEXT_MAPPING[reason]), spec.currentHelper.name)) |
| 562 | end |
| 563 | end |
| 564 | end |
| 565 | end |
| 566 | |
| 567 | g_helperManager:releaseHelper(spec.currentHelper) |
| 568 | spec.currentHelper = nil |
| 569 | |
| 570 | g_farmManager:updateFarmStats(spec.startedFarmId, "workersHired", -1) |
| 571 | |
| 572 | AIVehicle.numHirablesHired = math.max(AIVehicle.numHirablesHired - 1, 0) |
| 573 | |
| 574 | if self.restoreVehicleCharacter ~= nil then |
| 575 | self:restoreVehicleCharacter() |
| 576 | end |
| 577 | |
| 578 | if spec.mapAIHotspot ~= nil then |
| 579 | g_currentMission:removeMapHotspot(spec.mapAIHotspot) |
| 580 | spec.mapAIHotspot:delete() |
| 581 | spec.mapAIHotspot = nil |
| 582 | end |
| 583 | |
| 584 | self:setCruiseControlState(Drivable.CRUISECONTROL_STATE_OFF, true) |
| 585 | if spec.isServer then |
| 586 | WheelsUtil.updateWheelsPhysics(self, 0, spec.lastSpeedReal*spec.movingDirection, 0, true, true) |
| 587 | |
| 588 | if spec.driveStrategies ~= nil and #spec.driveStrategies > 0 then |
| 589 | for i=#spec.driveStrategies,1,-1 do |
| 590 | spec.driveStrategies[i]:delete() |
| 591 | table.remove(spec.driveStrategies, i) |
| 592 | end |
| 593 | spec.driveStrategies = {} |
| 594 | end |
| 595 | end |
| 596 | |
| 597 | spec.isActive = false |
| 598 | spec.isTurning = false |
| 599 | |
| 600 | -- move the collision far under the ground and remove it 200ms delayed (avoids problems with the traffic doesn't get the trigger onLeave callback of the collision box) |
| 601 | setTranslation(spec.aiTrafficCollision, 0, -1000, 0) |
| 602 | spec.aiTrafficCollisionRemoveDelay = 200 |
| 603 | |
| 604 | self:raiseAIEvent("onAIEnd", "onAIImplementEnd") |
| 605 | self:requestActionEventUpdate() |
| 606 | end |
| 607 | end |
Get direction shape angleDefinition
getDirectionSnapAngle()Return Values
| float | direction | shape angle |
| 635 | function AIVehicle:getDirectionSnapAngle() |
| 636 | return 0 |
| 637 | end |
Fills aiImplementList with vehicles to use by aiDefinition
updateAIImplementData()Code
| 641 | function AIVehicle:updateAIImplementData() |
| 642 | local spec = self.spec_aiVehicle |
| 643 | |
| 644 | spec.aiImplementList = {} |
| 645 | self:addVehicleToAIImplementList(spec.aiImplementList) |
| 646 | end |
Set drive strategies depending on the vehicleDefinition
updateAIDriveStrategies()Code
| 658 | function AIVehicle:updateAIDriveStrategies() |
| 659 | local spec = self.spec_aiVehicle |
| 660 | |
| 661 | if #spec.aiImplementList > 0 then |
| 662 | if spec.driveStrategies ~= nil and #spec.driveStrategies > 0 then |
| 663 | for i=#spec.driveStrategies,1,-1 do |
| 664 | spec.driveStrategies[i]:delete() |
| 665 | table.remove(spec.driveStrategies, i) |
| 666 | end |
| 667 | spec.driveStrategies = {} |
| 668 | end |
| 669 | |
| 670 | local foundCombine = false |
| 671 | local foundBaler = false |
| 672 | for _,implement in pairs(spec.aiImplementList) do |
| 673 | if SpecializationUtil.hasSpecialization(Combine, implement.object.specializations) then |
| 674 | foundCombine = true |
| 675 | end |
| 676 | if SpecializationUtil.hasSpecialization(Baler, implement.object.specializations) then |
| 677 | foundBaler = true |
| 678 | end |
| 679 | end |
| 680 | |
| 681 | foundCombine = foundCombine or SpecializationUtil.hasSpecialization(Combine, spec.specializations) |
| 682 | if foundCombine then |
| 683 | local driveStrategyCombine = AIDriveStrategyCombine:new() |
| 684 | driveStrategyCombine:setAIVehicle(self) |
| 685 |