554 | function SpeedMeterDisplay:animateDamageGaugeToggle(makeActive) |
555 | local startAlpha = self.damageGaugeBackgroundElement:getAlpha() |
556 | local endAlpha = makeActive and 1 or 0 |
557 | |
558 | if self.fadeDamageGaugeAnimation:getFinished() then |
559 | local sequence = TweenSequence.new(self) |
560 | local fade = Tween:new(self.fadeDamageGauge, startAlpha, endAlpha, HUDDisplayElement.MOVE_ANIMATION_DURATION) |
561 | sequence:addTween(fade) |
562 | |
563 | sequence:start() |
564 | self.fadeDamageGaugeAnimation = sequence |
565 | else -- if still animating, stop that and just set the visibility values immediately |
566 | self.fadeDamageGaugeAnimation:stop() |
567 | self:fadeDamageGauge(endAlpha) |
568 | end |
569 | end |
514 | function SpeedMeterDisplay:animateFuelGaugeToggle(makeActive) |
515 | local startAlpha = self.fuelGaugeBackgroundElement:getAlpha() |
516 | local endAlpha = makeActive and 1 or 0 |
517 | |
518 | if self.fadeFuelGaugeAnimation:getFinished() then |
519 | local sequence = TweenSequence.new(self) |
520 | local fade = Tween:new(self.fadeFuelGauge, startAlpha, endAlpha, HUDDisplayElement.MOVE_ANIMATION_DURATION) |
521 | sequence:addTween(fade) |
522 | |
523 | sequence:start() |
524 | self.fadeFuelGaugeAnimation = sequence |
525 | else -- if still animating, stop that and just set the visibility values immediately |
526 | self.fadeFuelGaugeAnimation:stop() |
527 | self:fadeFuelGauge(endAlpha) |
528 | end |
529 | end |
96 | function SpeedMeterDisplay:createComponents(hudAtlasPath) |
97 | local baseX, baseY = self:getBasePosition() |
98 | self:storeScaledValues(baseX, baseY) |
99 | |
100 | -- create components in order of drawing: |
101 | self.speedGaugeSegmentElements, |
102 | self.speedGaugeSegmentPartElements = self:createSpeedGaugeElements(hudAtlasPath, baseX, baseY) |
103 | |
104 | self.damageGaugeSegmentPartElements = self:createDamageGaugeElements(hudAtlasPath, baseX, baseY) |
105 | |
106 | self.fuelGaugeSegmentPartElements = self:createFuelGaugeElements(hudAtlasPath, baseX, baseY) |
107 | |
108 | self.gaugeBackgroundElement = self:createGaugeBackground(hudAtlasPath, baseX, baseY) |
109 | self.damageGaugeBackgroundElement = self:createSideGaugeBackground(hudAtlasPath, baseX, baseY, false) -- left side |
110 | self.fuelGaugeBackgroundElement = self:createSideGaugeBackground(hudAtlasPath, baseX, baseY, true) -- right side |
111 | self.damageGaugeIconElement, self.fuelGaugeIconElement = self:createGaugeIconElements(hudAtlasPath, baseX, baseY) |
112 | |
113 | self.speedIndicatorElement = self:createSpeedGaugeIndicator(hudAtlasPath, baseX, baseY) |
114 | self.damageIndicatorElement = self:createDamageGaugeIndicator(hudAtlasPath, baseX, baseY) |
115 | self.fuelIndicatorElement = self:createFuelGaugeIndicator(hudAtlasPath, baseX, baseY) |
116 | |
117 | self.operatingTimeElement = self:createOperatingTimeElement(hudAtlasPath, baseX, baseY) |
118 | self.operatingTimeElement:setVisible(false) |
119 | self.cruiseControlElement = self:createCruiseControlElement(hudAtlasPath, baseX, baseY) |
120 | self:createHorizontalSeparator(hudAtlasPath, baseX, baseY) |
121 | end |
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 |
795 | function SpeedMeterDisplay:createGaugeFillElements(hudAtlasPath, baseX, baseY, gaugeStartAngle, gaugeEndAngle, |
796 | fillSegmentAngle, radius, segmentSize, segmentPivot, segmentUVs, segmentColor) |
797 | local fullSegmentElements = {} |
798 | local radiusX, radiusY = getNormalizedScreenValues(unpack(radius)) |
799 | local width, height = getNormalizedScreenValues(unpack(segmentSize)) |
800 | local pivotX, pivotY = self:normalizeUVPivot(segmentPivot, segmentSize, segmentUVs) |
801 | |
802 | local numFillSegmentsInGauge = round(math.abs(gaugeEndAngle - gaugeStartAngle) / fillSegmentAngle) |
803 | |
804 | for i = 1, numFillSegmentsInGauge, 1 do |
805 | local rotation = gaugeStartAngle - fillSegmentAngle * (i - 1) |
806 | |
807 | local posX = self.gaugeCenterX + math.cos(rotation) * radiusX |
808 | local posY = self.gaugeCenterY + math.sin(rotation) * radiusY |
809 | |
810 | local segmentOverlay = Overlay:new(hudAtlasPath, posX, posY, width, height) |
811 | segmentOverlay:setUVs(getNormalizedUVs(segmentUVs)) |
812 | segmentOverlay:setColor(unpack(segmentColor)) |
813 | segmentOverlay:setRotation(rotation - HALF_PI, pivotX, pivotY) -- overlay rotation origin at 12 o'clock, need to compensate |
814 | |
815 | local segmentElement = HUDElement:new(segmentOverlay) |
816 | segmentElement:setVisible(false) -- will be updated based on current gauge level |
817 | segmentElement:setRotationPivot(pivotX, pivotY) |
818 | table.insert(fullSegmentElements, segmentElement) |
819 | self:addChild(segmentElement) |
820 | end |
821 | |
822 | return fullSegmentElements |
823 | end |
701 | function SpeedMeterDisplay:createGaugeIconElements(hudAtlasPath, baseX, baseY) |
702 | local posX, posY = getNormalizedScreenValues(unpack(SpeedMeterDisplay.POSITION.DAMAGE_LEVEL_ICON)) |
703 | local width, height = getNormalizedScreenValues(unpack(SpeedMeterDisplay.SIZE.DAMAGE_LEVEL_ICON)) |
704 | local iconOverlay = Overlay:new(hudAtlasPath, baseX + posX, baseY + posY, width, height) |
705 | iconOverlay:setUVs(getNormalizedUVs(SpeedMeterDisplay.UV.DAMAGE_LEVEL_ICON)) |
706 | |
707 | local damageGaugeIconElement = HUDElement:new(iconOverlay) |
708 | self:addChild(damageGaugeIconElement) |
709 | |
710 | posX, posY = getNormalizedScreenValues(unpack(SpeedMeterDisplay.POSITION.FUEL_LEVEL_ICON)) |
711 | width, height = getNormalizedScreenValues(unpack(SpeedMeterDisplay.SIZE.FUEL_LEVEL_ICON)) |
712 | iconOverlay = Overlay:new(hudAtlasPath, baseX + posX, baseY + posY, width, height) |
713 | iconOverlay:setUVs(getNormalizedUVs(SpeedMeterDisplay.UV.FUEL_LEVEL_ICON)) |
714 | |
715 | local fuelGaugeIconElement = HUDElement:new(iconOverlay) |
716 | self:addChild(fuelGaugeIconElement) |
717 | |
718 | return damageGaugeIconElement, fuelGaugeIconElement |
719 | end |
834 | function SpeedMeterDisplay:createGaugePartialElements(hudAtlasPath, baseX, baseY, fullSegmentSize, segmentPivot, segmentColor, gaugeSegmentUVs) |
835 | local width, height = getNormalizedScreenValues(unpack(fullSegmentSize)) |
836 | |
837 | local partialSegmentElements = {} |
838 | for i = 1, #gaugeSegmentUVs do |
839 | local segmentOverlay = Overlay:new(hudAtlasPath, 0, 0, width, height) |
840 | segmentOverlay:setUVs(getNormalizedUVs(gaugeSegmentUVs[i])) -- UVs are ordered by size |
841 | segmentOverlay:setColor(unpack(segmentColor)) |
842 | |
843 | local segmentElement = HUDElement:new(segmentOverlay) |
844 | segmentElement:setVisible(false) -- will be updated based on current speed level |
845 | local pivotX, pivotY = self:normalizeUVPivot(segmentPivot, fullSegmentSize, gaugeSegmentUVs[i]) |
846 | segmentElement:setRotationPivot(pivotX, pivotY) |
847 | |
848 | table.insert(partialSegmentElements, segmentElement) |
849 | self:addChild(segmentElement) |
850 | end |
851 | |
852 | return partialSegmentElements |
853 | end |
723 | function SpeedMeterDisplay:createHorizontalSeparator(hudAtlasPath, baseX, baseY) |
724 | local posX, posY = getNormalizedScreenValues(unpack(SpeedMeterDisplay.POSITION.SEPARATOR)) |
725 | local width, height = getNormalizedScreenValues(unpack(SpeedMeterDisplay.SIZE.SEPARATOR)) |
726 | local separatorOverlay = Overlay:new(hudAtlasPath, baseX + posX, baseY + posY, width, height) |
727 | separatorOverlay:setUVs(getNormalizedUVs(SpeedMeterDisplay.UV.SEPARATOR)) |
728 | separatorOverlay:setAlignment(Overlay.ALIGN_VERTICAL_BOTTOM, Overlay.ALIGN_HORIZONTAL_CENTER) |
729 | separatorOverlay:setColor(unpack(SpeedMeterDisplay.COLOR.SEPARATOR)) |
730 | |
731 | self:addChild(HUDElement:new(separatorOverlay)) |
732 | end |
769 | function SpeedMeterDisplay:createIndicator(hudAtlasPath, size, uvs, color, pivot) |
770 | local width, height = getNormalizedScreenValues(unpack(size)) |
771 | local indicatorOverlay = Overlay:new(hudAtlasPath, 0, 0, width, height) |
772 | indicatorOverlay:setUVs(getNormalizedUVs(uvs)) |
773 | indicatorOverlay:setColor(unpack(color)) |
774 | local indicatorElement = HUDElement:new(indicatorOverlay) |
775 | local pivotX, pivotY = self:normalizeUVPivot(pivot, size, uvs) |
776 | indicatorElement:setRotationPivot(pivotX, pivotY) |
777 | |
778 | self:addChild(indicatorElement) |
779 | return indicatorElement |
780 | end |
685 | function SpeedMeterDisplay:createSideGaugeBackground(hudAtlasPath, baseX, baseY, isRight) |
686 | local width, height = getNormalizedScreenValues(unpack(SpeedMeterDisplay.SIZE.SIDE_GAUGE_BACKGROUND)) |
687 | local position = isRight and SpeedMeterDisplay.POSITION.GAUGE_BACKGROUND_RIGHT or SpeedMeterDisplay.POSITION.GAUGE_BACKGROUND_LEFT |
688 | local offX, offY = getNormalizedScreenValues(unpack(position)) |
689 | local sideGaugeOverlay = Overlay:new(hudAtlasPath, baseX + offX, baseY + offY, width, height) |
690 | sideGaugeOverlay:setUVs(getNormalizedUVs(SpeedMeterDisplay.UV.SIDE_GAUGE_BACKGROUND)) |
691 | sideGaugeOverlay:setInvertX(isRight) |
692 | |
693 | local element = HUDElement:new(sideGaugeOverlay) |
694 | element:setVisible(false) -- will be made visible when required |
695 | self:addChild(element) |
696 | return element |
697 | end |
876 | function SpeedMeterDisplay:createSpeedGaugeElements(hudAtlasPath, baseX, baseY) |
877 | local fullSegmentElements = self:createGaugeFillElements( |
878 | hudAtlasPath, baseX, baseY, |
879 | SpeedMeterDisplay.ANGLE.SPEED_GAUGE_MIN, |
880 | SpeedMeterDisplay.ANGLE.SPEED_GAUGE_MAX, |
881 | SpeedMeterDisplay.ANGLE.SPEED_GAUGE_SEGMENT_FULL, |
882 | SpeedMeterDisplay.SIZE.SPEED_GAUGE_RADIUS, |
883 | SpeedMeterDisplay.SIZE.SPEED_GAUGE_SEGMENT, |
884 | SpeedMeterDisplay.PIVOT.SPEED_GAUGE_SEGMENT_FULL, |
885 | SpeedMeterDisplay.UV.SPEED_GAUGE_SEGMENT_FULL, |
886 | SpeedMeterDisplay.COLOR.SPEED_GAUGE) |
887 | |
888 | local partialSegmentElements = self:createGaugePartialElements( |
889 | hudAtlasPath, baseX, baseY, |
890 | SpeedMeterDisplay.SIZE.SPEED_GAUGE_SEGMENT, |
891 | SpeedMeterDisplay.PIVOT.SPEED_GAUGE_SEGMENT_FULL, |
892 | SpeedMeterDisplay.COLOR.SPEED_GAUGE, |
893 | SpeedMeterDisplay.UV.GAUGE_SEGMENT) |
894 | |
895 | return fullSegmentElements, partialSegmentElements |
896 | end |
449 | function SpeedMeterDisplay:drawCruiseControlText() |
450 | if self.cruiseControlElement:getVisible() then |
451 | setTextAlignment(RenderText.ALIGN_LEFT) |
452 | setTextColor(unpack(self.cruiseControlColor)) |
453 | setTextBold(true) |
454 | |
455 | local speedText = string.format(g_i18n:getText("ui_cruiseControlSpeed"), g_i18n:getSpeed(self.cruiseControlSpeed)) |
456 | local baseX, baseY = self.cruiseControlElement:getPosition() |
457 | local posX = baseX + self.cruiseControlElement:getWidth() + self.cruiseControlTextOffsetX |
458 | local posY = baseY + self.cruiseControlTextOffsetY |
459 | |
460 | renderText(posX, posY, self.cruiseControlTextSize, speedText) |
461 | end |
462 | end |
466 | function SpeedMeterDisplay:drawSpeedText() |
467 | -- speed switches at 0.5 -> 0.5 - 1.5 = 1km/h; 1.5 - 2.5 = 2 km/h |
468 | -- gives a much more smoother display if the cruise control is set |
469 | local speed = math.floor(self.speedKmh) |
470 | if math.abs(self.speedKmh-speed) > 0.5 then |
471 | speed = speed + 1 |
472 | end |
473 | |
474 | local speedI18N = string.format("%1d", g_i18n:getSpeed(speed)) |
475 | local speedUnit = utf8ToUpper(g_i18n:getSpeedMeasuringUnit()) |
476 | |
477 | local posX, posY = self.gaugeBackgroundElement:getPosition() |
478 | posX = posX + self.gaugeBackgroundElement:getWidth() * 0.5 |
479 | |
480 | setTextColor(unpack(SpeedMeterDisplay.COLOR.SPEED_TEXT)) |
481 | setTextBold(false) |
482 | setTextAlignment(RenderText.ALIGN_CENTER) |
483 | |
484 | renderText(posX, posY + self.speedTextOffsetY, self.speedTextSize, speedI18N) |
485 | |
486 | setTextColor(unpack(SpeedMeterDisplay.COLOR.SPEED_UNIT)) |
487 | |
488 | renderText(posX, posY + self.speedUnitTextOffsetY, self.speedUnitTextSize, speedUnit) |
489 | end |
533 | function SpeedMeterDisplay:fadeDamageGauge(alpha) |
534 | self.damageIndicatorElement:setAlpha(alpha) |
535 | self.damageGaugeBackgroundElement:setAlpha(alpha) |
536 | self.damageGaugeIconElement:setAlpha(alpha) |
537 | local baseAlpha = SpeedMeterDisplay.COLOR.DAMAGE_GAUGE[4] |
538 | for _, element in pairs(self.damageGaugeSegmentPartElements) do |
539 | if element:getVisible() then |
540 | element:setAlpha(alpha * baseAlpha) |
541 | end |
542 | end |
543 | |
544 | local visible = alpha > 0 |
545 | if visible ~= self.damageGaugeBackgroundElement:getVisible() then |
546 | self.damageIndicatorElement:setVisible(visible) |
547 | self.damageGaugeBackgroundElement:setVisible(visible) |
548 | self.damageGaugeIconElement:setVisible(visible) |
549 | end |
550 | end |
493 | function SpeedMeterDisplay:fadeFuelGauge(alpha) |
494 | self.fuelIndicatorElement:setAlpha(alpha) |
495 | self.fuelGaugeBackgroundElement:setAlpha(alpha) |
496 | self.fuelGaugeIconElement:setAlpha(alpha) |
497 | local baseAlpha = SpeedMeterDisplay.COLOR.FUEL_GAUGE[4] |
498 | for _, element in pairs(self.fuelGaugeSegmentPartElements) do |
499 | if element:getVisible() then |
500 | element:setAlpha(alpha * baseAlpha) |
501 | end |
502 | end |
503 | |
504 | local visible = alpha > 0 |
505 | if visible ~= self.fuelGaugeBackgroundElement:getVisible() then |
506 | self.fuelIndicatorElement:setVisible(visible) |
507 | self.fuelGaugeBackgroundElement:setVisible(visible) |
508 | self.fuelGaugeIconElement:setVisible(visible) |
509 | end |
510 | end |
640 | function SpeedMeterDisplay.getBackgroundPosition(scale) |
641 | -- set position so that gauge background is properly aligned |
642 | local gaugeWidth, gaugeHeight = getNormalizedScreenValues(unpack(SpeedMeterDisplay.SIZE.GAUGE_BACKGROUND)) |
643 | local bgWidth, bgHeight = getNormalizedScreenValues(unpack(SpeedMeterDisplay.SIZE.SHADOW_BACKGROUND)) |
644 | local selfOffX, selfOffY = getNormalizedScreenValues(unpack(SpeedMeterDisplay.POSITION.SPEED_METER)) |
645 | |
646 | local offX = (selfOffX + (bgWidth - gaugeWidth) * 0.5) * scale |
647 | local offY = (selfOffY + (bgHeight - gaugeHeight) * 0.5) * scale |
648 | |
649 | return 1 - g_safeFrameOffsetX - gaugeWidth * scale - offX, g_safeFrameOffsetY - offY |
650 | end |
19 | function SpeedMeterDisplay.new(hudAtlasPath) |
20 | local backgroundOverlay = SpeedMeterDisplay.createBackground(hudAtlasPath) |
21 | local self = SpeedMeterDisplay:superClass().new(SpeedMeterDisplay_mt, backgroundOverlay, nil) |
22 | |
23 | self.uiScale = 1.0 -- UI scale, only change after initialization and only using setScale() |
24 | |
25 | self.vehicle = nil -- currently controlled vehicle reference |
26 | self.isVehicleDrawSafe = false -- safety flag for drawing, must always run one update after setting a vehicle before drawing |
27 | |
28 | self.speedIndicatorElement = nil -- speed meter needle indicator element |
29 | self.speedGaugeSegmentElements = nil -- large gauge elements covering the area between two gauge notches |
30 | self.speedGaugeSegmentPartElements = nil -- small gauge elements for detailed display |
31 | self.speedIndicatorRadiusX = 0 |
32 | self.speedIndicatorRadiusY = 0 |
33 | self.speedTextOffsetY = 0 |
34 | self.speedUnitTextOffsetY = 0 |
35 | self.speedTextSize = 0 |
36 | self.speedUnitTextSize = 0 |
37 | self.speedKmh = 0 -- current vehicle velocity in km/h |
38 | |
39 | self.damageGaugeBackgroundElement = nil |
40 | self.damageIndicatorElement = nil |
41 | self.damageGaugeSegmentPartElements = nil |
42 | self.damageGaugeIconElement = nil |
43 | self.damageIndicatorRadiusX = 0 |
44 | self.damageIndicatorRadiusY = 0 |
45 | self.damageGaugeRadiusX = 0 |
46 | self.damageGaugeRadiusY = 0 |
47 | self.damageGaugeActive = false |
48 | |
49 | self.fuelGaugeBackgroundElement = nil |
50 | self.fuelIndicatorElement = nil |
51 | self.fuelGaugeSegmentPartElements = nil |
52 | self.fuelGaugeIconElement = nil |
53 | self.fuelIndicatorRadiusX = 0 |
54 | self.fuelIndicatorRadiusY = 0 |
55 | self.fuelGaugeRadiusX = 0 |
56 | self.fuelGaugeRadiusY = 0 |
57 | self.fuelGaugeActive = false |
58 | |
59 | self.cruiseControlElement = nil |
60 | self.cruiseControlSpeed = 0 |
61 | self.cruiseControlColor = nil |
62 | self.cruiseControlTextOffsetX = 0 |
63 | self.cruiseControlTextOffsetY = 0 |
64 | |
65 | self.operatingTimeElement = nil |
66 | self.operatingTimeText = "" |
67 | self.operatingTimeTextSize = 1 |
68 | self.operatingTimeTextOffsetX = 0 |
69 | self.operatingTimeTextOffsetY = 0 |
70 | self.operatingTimeTextDrawPositionX = 0 |
71 | self.operatingTimeTextDrawPositionY = 0 |
72 | |
73 | self.fadeFuelGaugeAnimation = TweenSequence.NO_SEQUENCE |
74 | self.fadeDamageGaugeAnimation = TweenSequence.NO_SEQUENCE |
75 | |
76 | self:createComponents(hudAtlasPath) |
77 | |
78 | return self |
79 | end |
579 | function SpeedMeterDisplay:setScale(uiScale) |
580 | SpeedMeterDisplay:superClass().setScale(self, uiScale, uiScale) |
581 | |
582 | self.uiScale = uiScale |
583 | |
584 | local currentVisibility = self:getVisible() |
585 | self:setVisible(true, false) |
586 | -- update anchored position based on scaled values: |
587 | local posX, posY = SpeedMeterDisplay.getBackgroundPosition(uiScale) |
588 | self:setPosition(posX, posY) |
589 | |
590 | self:storeOriginalPosition() |
591 | self:setVisible(currentVisibility, false) |
592 | |
593 | local baseX, baseY = self.gaugeBackgroundElement:getPosition() |
594 | self:storeScaledValues(baseX, baseY) |
595 | end |
126 | function SpeedMeterDisplay:setVehicle(vehicle) |
127 | self.vehicle = vehicle |
128 | |
129 | local hasVehicle = vehicle ~= nil |
130 | self.cruiseControlElement:setVisible(hasVehicle) |
131 | |
132 | local isMotorized = hasVehicle and vehicle.spec_motorized ~= nil |
133 | local needFuelGauge = true |
134 | -- enable/disable the fuel gauge based on the fuel tank capacity, i.e. if it consumes any fuel |
135 | if hasVehicle and isMotorized then |
136 | local _, capacity = SpeedMeterDisplay.getVehicleFuelLevelAndCapacity(vehicle) |
137 | needFuelGauge = capacity ~= nil |
138 | end |
139 | |
140 | self.fuelGaugeActive = needFuelGauge |
141 | self:animateFuelGaugeToggle(needFuelGauge) |
142 | |
143 | local needDamageGauge = hasVehicle and vehicle.getWearTotalAmount ~= nil and vehicle:getWearTotalAmount() ~= nil |
144 | self.damageGaugeActive = needDamageGauge |
145 | self:animateDamageGaugeToggle(needDamageGauge) |
146 | |
147 | local hasOperatingTime = hasVehicle and vehicle.operatingTime ~= nil |
148 | self.operatingTimeElement:setVisible(hasOperatingTime) |
149 | |
150 | self.isVehicleDrawSafe = false -- use a safety flag here because setVehicle() can be called inbetween update and draw |
151 | end |
614 | function SpeedMeterDisplay:storeScaledValues(baseX, baseY) |
615 | self:storeGaugeCenterPosition(baseX, baseY) |
616 | |
617 | self.cruiseControlTextSize = self:scalePixelToScreenHeight(SpeedMeterDisplay.TEXT_SIZE.CRUISE_CONTROL) |
618 | self.cruiseControlTextOffsetX, self.cruiseControlTextOffsetY = self:scalePixelToScreenVector(SpeedMeterDisplay.POSITION.CRUISE_CONTROL_TEXT) |
619 | |
620 | self.operatingTimeTextSize = self:scalePixelToScreenHeight(SpeedMeterDisplay.TEXT_SIZE.OPERATING_TIME) |
621 | self.operatingTimeTextOffsetX, self.operatingTimeTextOffsetY = self:scalePixelToScreenVector(SpeedMeterDisplay.POSITION.OPERATING_TIME_TEXT) |
622 | |
623 | self.speedTextOffsetY = self:scalePixelToScreenHeight(SpeedMeterDisplay.POSITION.SPEED_TEXT[2]) |
624 | self.speedUnitTextOffsetY = self:scalePixelToScreenHeight(SpeedMeterDisplay.POSITION.SPEED_UNIT[2]) |
625 | self.speedTextSize = self:scalePixelToScreenHeight(SpeedMeterDisplay.TEXT_SIZE.SPEED) |
626 | self.speedUnitTextSize = self:scalePixelToScreenHeight(SpeedMeterDisplay.TEXT_SIZE.SPEED_UNIT) |
627 | self.speedIndicatorRadiusX, self.speedIndicatorRadiusY = self:scalePixelToScreenVector(SpeedMeterDisplay.SIZE.GAUGE_INDICATOR_LARGE_RADIUS) |
628 | self.speedGaugeRadiusX, self.speedGaugeRadiusY = self:scalePixelToScreenVector(SpeedMeterDisplay.SIZE.SPEED_GAUGE_RADIUS) |
629 | |
630 | self.damageIndicatorRadiusX, self.damageIndicatorRadiusY = self:scalePixelToScreenVector(SpeedMeterDisplay.SIZE.GAUGE_INDICATOR_SMALL_RADIUS) |
631 | self.damageGaugeRadiusX, self.damageGaugeRadiusY = self:scalePixelToScreenVector(SpeedMeterDisplay.SIZE.SIDE_GAUGE_RADIUS) |
632 | |
633 | self.fuelIndicatorRadiusX, self.fuelIndicatorRadiusY = self:scalePixelToScreenVector(SpeedMeterDisplay.SIZE.GAUGE_INDICATOR_SMALL_RADIUS) |
634 | self.fuelGaugeRadiusX, self.fuelGaugeRadiusY = self:scalePixelToScreenVector(SpeedMeterDisplay.SIZE.SIDE_GAUGE_RADIUS) |
635 | end |
329 | function SpeedMeterDisplay:updateDamageGauge(dt) |
330 | if not self.fadeDamageGaugeAnimation:getFinished() then |
331 | self.fadeDamageGaugeAnimation:update(dt) |
332 | end |
333 | |
334 | if self.damageGaugeActive then |
335 | local steps = #self.damageGaugeSegmentPartElements |
336 | local gaugeValue = 1 - self.vehicle:getWearTotalAmount() -- operation is safe, checked when setting vehicle |
337 | gaugeValue = round(gaugeValue * steps) / steps |
338 | |
339 | local indicatorRotation = MathUtil.lerp(SpeedMeterDisplay.ANGLE.DAMAGE_GAUGE_MIN, SpeedMeterDisplay.ANGLE.DAMAGE_GAUGE_MAX, gaugeValue) |
340 | |
341 | self:updateGaugeIndicator(self.damageIndicatorElement, self.damageIndicatorRadiusX, self.damageIndicatorRadiusY, indicatorRotation) |
342 | |
343 | local neededColor = SpeedMeterDisplay.COLOR.DAMAGE_GAUGE |
344 | if gaugeValue < 0.2 then |
345 | neededColor = SpeedMeterDisplay.COLOR.DAMAGE_GAUGE_LOW |
346 | end |
347 | |
348 | -- Prevent updating the overlays every frame |
349 | if self.lastDamageGaugeColor ~= neededColor then |
350 | self:setGaugePartialElementsColor(self.damageGaugeSegmentPartElements, neededColor) |
351 | self.lastDamageGaugeColor = neededColor |
352 | end |
353 | |
354 | self:updateGaugePartialSegments( |
355 | self.damageGaugeSegmentPartElements, |
356 | indicatorRotation, 1, |
357 | self.damageGaugeRadiusX, self.damageGaugeRadiusY, |
358 | SpeedMeterDisplay.ANGLE.DAMAGE_GAUGE_MIN, |
359 | SpeedMeterDisplay.ANGLE.SIDE_GAUGE_SEGMENT_FULL, |
360 | SpeedMeterDisplay.ANGLE.SIDE_GAUGE_SEGMENT_SMALLEST, |
361 | true) |
362 | end |
363 | end |
377 | function SpeedMeterDisplay:updateFuelGauge(dt) |
378 | if not self.fadeFuelGaugeAnimation:getFinished() then |
379 | self.fadeFuelGaugeAnimation:update(dt) |
380 | end |
381 | |
382 | if self.fuelGaugeActive then |
383 | local level, capacity = SpeedMeterDisplay.getVehicleFuelLevelAndCapacity(self.vehicle) |
384 | |
385 | local gaugeValue = 0 |
386 | if capacity ~= nil and capacity > 0 then |
387 | local steps = #self.fuelGaugeSegmentPartElements |
388 | gaugeValue = level / capacity |
389 | gaugeValue = round(gaugeValue * steps) / steps |
390 | end |
391 | |
392 | local indicatorRotation = MathUtil.lerp(SpeedMeterDisplay.ANGLE.FUEL_GAUGE_MIN, SpeedMeterDisplay.ANGLE.FUEL_GAUGE_MAX, gaugeValue) |
393 | |
394 | self:updateGaugeIndicator(self.fuelIndicatorElement, self.fuelIndicatorRadiusX, self.fuelIndicatorRadiusY, |
395 | indicatorRotation) |
396 | |
397 | self:updateGaugePartialSegments( |
398 | self.fuelGaugeSegmentPartElements, |
399 | indicatorRotation, -1, |
400 | self.fuelGaugeRadiusX, self.fuelGaugeRadiusY, |
401 | SpeedMeterDisplay.ANGLE.FUEL_GAUGE_MIN, |
402 | SpeedMeterDisplay.ANGLE.SIDE_GAUGE_SEGMENT_FULL, |
403 | SpeedMeterDisplay.ANGLE.SIDE_GAUGE_SEGMENT_SMALLEST, |
404 | true) |
405 | end |
406 | end |
224 | function SpeedMeterDisplay:updateGaugeIndicator(indicatorElement, radiusX, radiusY, rotation) |
225 | local pivotX, pivotY = indicatorElement:getRotationPivot() |
226 | |
227 | local cosRot = math.cos(rotation) |
228 | local sinRot = math.sin(rotation) |
229 | local posX = self.gaugeCenterX + cosRot * radiusX - pivotX |
230 | local posY = self.gaugeCenterY + sinRot * radiusY - pivotY |
231 | |
232 | indicatorElement:setPosition(posX, posY) |
233 | indicatorElement:setRotation(rotation - HALF_PI) |
234 | end |
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 |
266 | function SpeedMeterDisplay:updateGaugePartialSegments(partialSegments, indicatorRotation, rotationDirection, gaugeRadiusX, gaugeRadiusY, gaugeMinAngle, fullSegmentAngle, detailSegmentAngle, isPartialOnly) |
267 | -- set size and position of remainder segment from last full segment to current indicator position |
268 | local angle = math.abs(indicatorRotation - gaugeMinAngle) |
269 | local fullParts, rem = math.modf(angle / fullSegmentAngle) |
270 | |
271 | -- special float precision issue handling for gauges using no fill segments: |
272 | if isPartialOnly and fullParts > 0 then |
273 | fullParts = 0 |
274 | rem = 1 |
275 | end |
276 | |
277 | local largestPartSegmentSize = round(rem * #partialSegments) |
278 | |
279 | local partialRotation = gaugeMinAngle - fullParts * fullSegmentAngle * rotationDirection |
280 | if rotationDirection < 0 then |
281 | partialRotation = partialRotation + largestPartSegmentSize * detailSegmentAngle |
282 | end |
283 | |
284 | local cosRot = math.cos(partialRotation) |
285 | local sinRot = math.sin(partialRotation) |
286 | local posX = self.gaugeCenterX + cosRot * gaugeRadiusX |
287 | local posY = self.gaugeCenterY + sinRot * gaugeRadiusY |
288 | |
289 | for i, element in ipairs(partialSegments) do |
290 | if i == largestPartSegmentSize then |
291 | element:setRotation(partialRotation - HALF_PI) -- overlay rotation origin at 12 o'clock, need to compensate |
292 | element:setPosition(posX, posY) |
293 | element:setVisible(true) |
294 | else |
295 | element:setVisible(false) |
296 | end |
297 | end |
298 | end |
179 | function SpeedMeterDisplay:updateOperatingTime(dt) |
180 | if self.operatingTimeElement:getVisible() then |
181 | local minutes = self.vehicle.operatingTime / (1000 * 60) |
182 | local hours = math.floor(minutes / 60) |
183 | minutes = math.floor((minutes - hours * 60) / 6) |
184 | |
185 | self.operatingTimeText = string.format(g_i18n:getText("shop_operatingTime"), hours, minutes) |
186 | local textWidth = getTextWidth(self.operatingTimeTextSize, self.operatingTimeText) |
187 | local operatingTimeWidth = self.operatingTimeElement:getWidth() + self.operatingTimeTextOffsetX + textWidth |
188 | |
189 | local posX, _ = self:getPosition() |
190 | local _, posY = self.operatingTimeElement:getPosition() |
191 | |
192 | posX = posX + (self:getWidth() - operatingTimeWidth) * 0.5 |
193 | |
194 | self.operatingTimeTextDrawPositionX = posX + self.operatingTimeElement:getWidth() + self.operatingTimeTextOffsetX |
195 | self.operatingTimeTextDrawPositionY = posY + self.operatingTimeTextOffsetY |
196 | |
197 | self.operatingTimeElement:setPosition(posX, nil) |
198 | self.operatingTimeIsSafe = true |
199 | end |
200 | end |
302 | function SpeedMeterDisplay:updateSpeedGauge(dt) |
303 | local kmh = math.max(0, self.vehicle:getLastSpeed() * self.vehicle.spec_motorized.speedDisplayScale) |
304 | if kmh < 0.5 then |
305 | kmh = 0 |
306 | end |
307 | |
308 | self.speedKmh = kmh -- used again for drawing the speed text |
309 | |
310 | local gaugeValue = MathUtil.clamp(kmh / (self.vehicle.spec_drivable.cruiseControl.maxSpeed * 1.1), 0, 1) |
311 | local indicatorRotation = MathUtil.lerp(SpeedMeterDisplay.ANGLE.SPEED_GAUGE_MIN, SpeedMeterDisplay.ANGLE.SPEED_GAUGE_MAX, gaugeValue) |
312 | |
313 | self:updateGaugeIndicator(self.speedIndicatorElement, self.speedIndicatorRadiusX, self.speedIndicatorRadiusY, |
314 | indicatorRotation) |
315 | |
316 | self:updateGaugeFillSegments(self.speedGaugeSegmentElements, gaugeValue) |
317 | |
318 | self:updateGaugePartialSegments( |
319 | self.speedGaugeSegmentPartElements, |
320 | indicatorRotation, 1, |
321 | self.speedGaugeRadiusX, self.speedGaugeRadiusY, |
322 | SpeedMeterDisplay.ANGLE.SPEED_GAUGE_MIN, |
323 | SpeedMeterDisplay.ANGLE.SPEED_GAUGE_SEGMENT_FULL, |
324 | SpeedMeterDisplay.ANGLE.SPEED_GAUGE_SEGMENT_SMALLEST) |
325 | end |