Complete typed tactics command and log views
ober
a753b398b1f2a6d8bec26910d91250ed241c99db
--- a/templates/original-tactics-parts/BattleCommandView.kt.ss +++ b/templates/original-tactics-parts/BattleCommandView.kt.ss @@ -30,6 +30,8 @@ (type JSONArray) (type JSONObject) (type Number) + (type List) + (type ProjectedRangePoint) (type Int32) (type Float32) @@ -122,6 +124,22 @@ (extern (bcvStringReplace (text : String) (old : String) (replacement : String)) : String (kotlin-member-call replace)) + (extern (bcvStringCompare (text : String) (other : String)) : Int32 + (kotlin-member-call compareTo)) + (extern (bcvStringLowercase (text : String)) : String + (kotlin-member-call lowercase)) + (extern (bcvStringStartsWith (text : String) (prefix : String)) : Bool + (kotlin-member-call startsWith)) + (extern (bcvStringEndsWith (text : String) (suffix : String)) : Bool + (kotlin-member-call endsWith)) + (extern (bcvStringTrim (text : String)) : String + (kotlin-member-call trim)) + (extern (bcvStringSubstring (text : String) (start : Int32)) : String + (kotlin-member-call substring)) + (extern (bcvObjectHas (object : JSONObject) (key : String)) : Bool + (kotlin-member-call has)) + (extern (bcvObjectLength (object : JSONObject)) : Int32 + (kotlin-member-call length)) (extern (bcvJsonKeys (json : JSONObject)) : (Iterator String) (kotlin-member-call keys)) (extern (bcvIteratorHasNext (items : (Iterator String))) : Bool @@ -137,6 +155,27 @@ (kotlin-call maneuverAbbreviation)) (extern (bcvShipDisplayLabel (ship : (Nullable JSONObject))) : String (kotlin-call shipDisplayLabel)) + (extern (bcvArrayPutString (array : JSONArray) (value : String)) : JSONArray + (kotlin-member-call put)) + (extern (bcvProjectedRangeForecast + (alpha : (Nullable JSONObject)) (beta : (Nullable JSONObject)) + (alphaManeuvers : (Nullable Any)) (betaManeuvers : (Nullable Any)) + (currentImpulse : Int32) (maxMoves : Int32) + (labelLimit : Int32)) : (List ProjectedRangePoint) + (kotlin-call projectedRangeForecast)) + (extern (bcvHexRangeFromPositions + (alpha : (Nullable JSONObject)) + (beta : (Nullable JSONObject))) : (Nullable Int32) + (kotlin-call hexRangeFromPositions)) + (extern (bcvListSize (items : (List ProjectedRangePoint))) : Int32 + (kotlin-member-get size)) + (extern (bcvListGet (items : (List ProjectedRangePoint)) + (index : Int32)) : ProjectedRangePoint + (kotlin-member-call get)) + (extern (bcvPointImpulse (point : ProjectedRangePoint)) : Int32 + (kotlin-member-get impulse)) + (extern (bcvPointRange (point : ProjectedRangePoint)) : Int32 + (kotlin-member-get range)) (def (bcvConfigurePaint (paint : Paint) (color : Int32) (style : Style) @@ -154,6 +193,9 @@ (bcvPaintFakeBoldSet paint bold) paint)) (def (bcvMaxInt (a : Int32) (b : Int32)) : Int32 (if (> a b) a b)) + (def (bcvMinInt (a : Int32) (b : Int32)) : Int32 (if (< a b) a b)) + (def (bcvClampInt (value : Int32) (low : Int32) (high : Int32)) : Int32 + (if (< value low) low (if (> value high) high value))) (def (bcvMaxFloat (a : Float32) (b : Float32)) : Float32 (if (> a b) a b)) (def (bcvMinFloat (a : Float32) (b : Float32)) : Float32 (if (< a b) a b)) (def (bcvClampFloat (value : Float32) (low : Float32) (high : Float32)) : Float32 @@ -411,18 +453,35 @@ " A ") alpha) " B ") - beta))) + beta)) + (forecastText + (rangeForecastLine battle orderJson currentImpulse))) (begin (bcvCanvasDrawRoundRect canvas rect (float32 8.0) (float32 8.0) panelPaint) (bcvCanvasDrawRoundRect canvas rect (float32 8.0) (float32 8.0) borderPaint) - (bcvPaintTextAlignSet valuePaint (bcvPaintAlignCenter)) - (bcvCanvasDrawText canvas - (fitText ordersText valuePaint - (- (bcvRectWidth rect) (float32 18.0))) - (bcvRectCenterX rect) - (+ (bcvRectTop rect) (float32 24.0)) valuePaint)))))))) + (if (string-blank? forecastText) + (begin + (bcvPaintTextAlignSet valuePaint (bcvPaintAlignCenter)) + (bcvCanvasDrawText canvas + (fitText ordersText valuePaint + (- (bcvRectWidth rect) (float32 18.0))) + (bcvRectCenterX rect) + (+ (bcvRectTop rect) (float32 24.0)) valuePaint)) + (begin + (bcvPaintTextAlignSet valuePaint (bcvPaintAlignLeft)) + (bcvCanvasDrawText canvas + (fitText ordersText valuePaint + (- (bcvRectWidth rect) (float32 18.0))) + (+ (bcvRectLeft rect) (float32 9.0)) + (+ (bcvRectTop rect) (float32 16.0)) valuePaint) + (bcvPaintTextAlignSet smallPaint (bcvPaintAlignLeft)) + (bcvCanvasDrawText canvas + (fitText forecastText smallPaint + (- (bcvRectWidth rect) (float32 18.0))) + (+ (bcvRectLeft rect) (float32 9.0)) + (+ (bcvRectTop rect) (float32 32.0)) smallPaint)))))))))) (def (drawShipCard (canvas : Canvas) (rect : RectF) (side : String) (ship : (Nullable JSONObject)) @@ -487,15 +546,16 @@ (- (bcvRectWidth rect) (float32 18.0)) "HULL" internalNow (maxInternals ship internalNow))) (let ((shieldNow (shieldTotal ship #f)) - (shieldMax (shieldTotal ship #t)) - (paPanels (counterValue (bcvNullableObjectAny ship "boxes") "52")) + (shieldMax (bcvMaxInt (shieldTotal ship #t) (shieldTotal ship #f))) + (paPanels (paPanelCount ship)) + (paCharge (paPanelCharge ship)) (paMax (paPanelMax ship))) (if (and (<= shieldMax (int32 0)) (or (> paPanels (int32 0)) (> paMax (int32 1)))) (drawMetricBar canvas (+ (bcvRectLeft rect) (float32 9.0)) (+ (bcvRectTop rect) (float32 116.0)) - (- (bcvRectWidth rect) (float32 18.0)) "PA PANELS" - paPanels paMax) + (- (bcvRectWidth rect) (float32 18.0)) "PA CHG" + paCharge paMax) (drawMetricBar canvas (+ (bcvRectLeft rect) (float32 9.0)) (+ (bcvRectTop rect) (float32 116.0)) (- (bcvRectWidth rect) (float32 18.0)) "SHIELDS" @@ -534,12 +594,20 @@ (bcvNullableObjectAny ship "direct-fire-cooldown"))) (launch (counterTotal - (bcvNullableObjectAny ship "seeking-launch-limits")))) + (bcvNullableObjectAny ship "seeking-launch-limits"))) + (families (weaponFamilyLine statusActor)) + (primary (primaryWeaponLine statusActor))) (bcvCanvasDrawText canvas (fitText - (string-append - (string-append "COOL " (bcvIntText cool)) - (string-append " LAUNCH " (bcvIntText launch))) + (if (not (string-blank? families)) + (string-append families + (string-append " COOL " (bcvIntText cool))) + (if (not (string-blank? primary)) + (string-append primary + (string-append " COOL " (bcvIntText cool))) + (string-append + (string-append "COOL " (bcvIntText cool)) + (string-append " LAUNCH " (bcvIntText launch))))) smallPaint (- (bcvRectWidth rect) (float32 18.0))) (+ (bcvRectLeft rect) (float32 9.0)) (bcvMinFloat (- (bcvRectBottom rect) (float32 10.0)) @@ -588,10 +656,8 @@ (bcvCanvasDrawText canvas "TACTICAL LOG" (+ (bcvRectLeft rect) (float32 9.0)) (+ (bcvRectTop rect) (float32 18.0)) smallPaint) - (let ((start - (bcvMaxInt (int32 0) - (- (json-array-length events) (int32 3))))) - (if (= start (json-array-length events)) + (let ((lines (recentEventLines (int32 3)))) + (if (= (json-array-length lines) (int32 0)) (begin (bcvPaintTextAlignSet labelPaint (bcvPaintAlignLeft)) (bcvCanvasDrawText canvas "No combat events yet" @@ -599,26 +665,61 @@ (+ (bcvRectTop rect) (float32 42.0)) labelPaint)) (begin (for/fold ((y (+ (bcvRectTop rect) (float32 40.0)))) - ((index (in-range start (json-array-length events)))) - (let ((event (json-array-opt-json-object events index))) - (if (or (nullable-null? event) - (> y (- (bcvRectBottom rect) (float32 6.0)))) - y + ((index (in-range (int32 0) (json-array-length lines)))) + (if (> y (- (bcvRectBottom rect) (float32 6.0))) y + (begin + (if (= index (- (json-array-length lines) (int32 1))) + (bcvCanvasDrawCircle canvas + (+ (bcvRectLeft rect) (float32 10.0)) + (- y (float32 5.0)) (float32 4.0) accentPaint) + (begin)) + (bcvPaintTextAlignSet labelPaint (bcvPaintAlignLeft)) + (bcvCanvasDrawText canvas + (fitText (json-array-opt-string lines index) + labelPaint (- (bcvRectWidth rect) (float32 28.0))) + (+ (bcvRectLeft rect) (float32 20.0)) y labelPaint) + (+ y (float32 19.0))))) + (begin)))) + (bcvPaintColorSet smallPaint + (bcvColorRgb (int32 165) (int32 177) (int32 195))))) + (def (recentEventLines (limit : Int32)) : JSONArray + (modifiers private) + (let ((backward (new JSONArray)) (out (new JSONArray))) + (begin + (recentEventLinesAt backward (- (json-array-length events) (int32 1)) limit) + (reverseEventLines backward out (- (json-array-length backward) (int32 1))) + out))) + (def (recentEventLinesAt (out : JSONArray) (index : Int32) + (limit : Int32)) : Unit + (modifiers private) + (if (or (< index (int32 0)) (>= (json-array-length out) limit)) (begin) + (let ((event (json-array-opt-json-object events index))) + (if (nullable-null? event) + (recentEventLinesAt out (- index (int32 1)) limit) + (let ((kind (json-object-opt-string-default + (nullable-get event) "type" ""))) + (begin + (if (or (string-blank? kind) (equal? kind "impulse-activity")) + (begin) (begin - (bcvPaintTextAlignSet labelPaint (bcvPaintAlignLeft)) - (bcvCanvasDrawText canvas - (fitText (summarizeEvent (nullable-get event)) - labelPaint (- (bcvRectWidth rect) (float32 28.0))) - (+ (bcvRectLeft rect) (float32 20.0)) y labelPaint) - (+ y (float32 19.0)))))) - (begin)))))) + (bcvArrayPutString out (summarizeEvent (nullable-get event))) + (begin))) + (recentEventLinesAt out (- index (int32 1)) limit))))))) + (def (reverseEventLines (source : JSONArray) (out : JSONArray) + (index : Int32)) : Unit + (modifiers private) + (if (< index (int32 0)) (begin) + (begin + (bcvArrayPutString out (json-array-opt-string source index)) + (reverseEventLines source out (- index (int32 1)))))) (def (summarizeEvent (event : JSONObject)) : String (modifiers private) (let ((turn (json-object-opt-int32-default event "turn" (int32 -1))) (impulse (json-object-opt-int32-default event "impulse" (int32 -1))) (actor (json-object-opt-string-default event "actor" "")) - (kind (json-object-opt-string-default event "type" "event"))) + (kind (json-object-opt-string-default event "type" "event")) + (payload (json-object-opt-json-object event "payload"))) (let ((prefix (if (and (>= turn (int32 0)) (>= impulse (int32 0))) (string-append @@ -631,9 +732,266 @@ (if (string-blank? actor) "" (string-append " " (string-uppercase actor))))) - (string-append - (string-append (string-append prefix actorText) " ") - (bcvStringReplace kind "-" " "))))) + (let ((detail (eventDetail kind payload))) + (string-append (string-append (string-append prefix actorText) " ") detail))))) + + (def (eventDetail (kind : String) + (payload : (Nullable JSONObject))) : String + (modifiers private) + (if (equal? kind "fire-weapons") (fireSummary payload) + (if (equal? kind "seeking-launch") (launchSummary payload) + (if (equal? kind "seeking-impact") (impactSummary payload) + (if (or (equal? kind "movement") (equal? kind "turn") + (equal? kind "sideslip") (equal? kind "high-energy-turn")) + (movementSummary payload) + (if (or (equal? kind "turn-blocked") + (equal? kind "maneuver-blocked") + (equal? kind "fire-control-blocked")) + (blockedSummary payload) + (if (or (equal? kind "ew-control") (equal? kind "ew-support") + (equal? kind "ew-support-rejected") + (equal? kind "ew-support-lost") + (equal? kind "lock-on-control") + (equal? kind "shield-control") + (equal? kind "energy-allocation") + (equal? kind "wild-swac")) + (controlSummary payload) + (if (or (equal? kind "web-created") + (equal? kind "web-reduced") + (equal? kind "web-caster") + (equal? kind "web-snare") + (equal? kind "web-breaker")) + (webSummary payload) + (if (equal? kind "turn-completed") "turn complete" + (if (equal? kind "orders-submitted") "orders committed" + (if (nullable-null? payload) kind + (let ((reason + (json-object-opt-string-default + (nullable-get payload) "reason" kind))) + (if (string-blank? reason) kind reason)))))))))))) + + ) + (def (fireSummary (payload : (Nullable JSONObject))) : String + (modifiers private) + (if (nullable-null? payload) "weapons fire" + (let ((summaries (json-object-opt-json-array + (nullable-get payload) "fire-target-summaries"))) + (let ((details (fireSummaryAt summaries (int32 0) "" (int32 0)))) + (if (not (string-blank? (BattlePlotText-text details))) + (BattlePlotText-text details) + (let ((alpha (bcvNullableObjectInt + (json-object-opt-json-object + (nullable-get payload) "alpha-volley") + "damage" (int32 0))) + (beta (bcvNullableObjectInt + (json-object-opt-json-object + (nullable-get payload) "beta-volley") + "damage" (int32 0))) + (range (let ((before (json-object-opt-any + (nullable-get payload) "range-before"))) + (if (nullable-null? before) + (let ((current (json-object-opt-any + (nullable-get payload) "range"))) + (if (nullable-null? current) "-" + (bcvAnyToString (nullable-get current)))) + (bcvAnyToString (nullable-get before)))))) + (string-append "weapons fire R" + (string-append range + (string-append " A:" (string-append (bcvIntText alpha) + (string-append " B:" (bcvIntText beta)))))))))))) + (def (fireSummaryAt (summaries : (Nullable JSONArray)) (index : Int32) + (out : String) (count : Int32)) : BattlePlotText + (modifiers private) + (if (or (nullable-null? summaries) + (>= index (json-array-length (nullable-get summaries))) + (>= count (int32 2))) (make-BattlePlotText out count) + (let ((summary (json-array-opt-json-object (nullable-get summaries) index))) + (if (nullable-null? summary) + (fireSummaryAt summaries (+ index (int32 1)) out count) + (let ((damage (bcvNumberValue (json-object-opt-any + (nullable-get summary) "damage"))) + (internals (bcvNumberValue (json-object-opt-any + (nullable-get summary) "internals")))) + (if (and (<= damage (int32 0)) (<= internals (int32 0))) + (fireSummaryAt summaries (+ index (int32 1)) out count) + (let ((face (shieldFaceLabel + (json-object-opt-any (nullable-get summary) + "shield-facing")))) + (let ((faceText (if (string-blank? face) "" + (string-append " " + (string-append face + (string-append " " + (string-append (bcvIntText + (bcvNumberValue (json-object-opt-any + (nullable-get summary) "shield-before"))) + (string-append ">" (bcvIntText + (bcvNumberValue (json-object-opt-any + (nullable-get summary) "shield-after")))))))))) + (internalText (if (> internals (int32 0)) + (string-append " int " (bcvIntText internals)) ""))) + (let ((part + (string-append + (sideLabel (json-object-opt-string-default + (nullable-get summary) "attacker" "")) + (string-append ">" + (string-append (sideLabel + (json-object-opt-string-default + (nullable-get summary) "target" "")) + (string-append " dmg " + (string-append (bcvIntText damage) + (string-append faceText internalText)))))))) + (fireSummaryAt summaries (+ index (int32 1)) + (if (= count (int32 0)) part + (string-append out (string-append " " part))) + (+ count (int32 1)))))))))))) + (def (launchSummary (payload : (Nullable JSONObject))) : String + (modifiers private) + (if (nullable-null? payload) "seeker launch" + (let ((seeker (json-object-opt-json-object + (nullable-get payload) "seeker"))) + (if (nullable-null? seeker) "seeker launch" + (let ((idValue (let ((id (json-object-opt-any + (nullable-get seeker) "id"))) + (if (nullable-null? id) + (json-object-opt-any + (nullable-get seeker) "seeking-id") id)))) + (let ((idText (if (nullable-null? idValue) "?" + (bcvAnyToString (nullable-get idValue))))) + (string-append "launch " + (string-append (json-object-opt-string-default + (nullable-get seeker) "kind" "seeker") + (string-append " #" + (string-append idText + (string-append " to " + (json-object-opt-string-default + (nullable-get seeker) "target" "?")))))))))))) + (def (impactSummary (payload : (Nullable JSONObject))) : String + (modifiers private) + (if (nullable-null? payload) "seeker impact" + (let ((kind (json-object-opt-string-default (nullable-get payload) "kind" + (json-object-opt-string-default + (nullable-get payload) "seeker-kind" "seeker"))) + (target (json-object-opt-string-default + (nullable-get payload) "target" "?")) + (damage (json-object-opt-any (nullable-get payload) "damage")) + (face (shieldFaceLabel (json-object-opt-any + (nullable-get payload) "shield-facing")))) + (let ((damageText (if (nullable-null? damage) "-" + (bcvAnyToString (nullable-get damage)))) + (faceText (if (string-blank? face) "" + (string-append " " + (string-append face + (string-append " " + (string-append (bcvIntText (bcvNumberValue + (json-object-opt-any (nullable-get payload) "shield-before"))) + (string-append ">" (bcvIntText (bcvNumberValue + (json-object-opt-any (nullable-get payload) "shield-after"))))))))))) + (string-append kind + (string-append " impact " + (string-append damageText + (string-append " on " + (string-append target faceText)))))))) + ) + (def (movementSummary (payload : (Nullable JSONObject))) : String + (modifiers private) + (if (nullable-null? payload) "maneuver" + (let ((after (json-object-opt-json-object + (nullable-get payload) "position-after"))) + (if (nullable-null? after) "maneuver" + (string-append "move q" + (string-append (bcvIntText (json-object-opt-int32-default + (nullable-get after) "q" (int32 0))) + (string-append " r" (string-append + (bcvIntText (json-object-opt-int32-default + (nullable-get after) "r" (int32 0))) + (string-append " f" (bcvIntText + (json-object-opt-int32-default + (nullable-get after) "facing" (int32 0))))))))))) + ) + (def (blockedSummary (payload : (Nullable JSONObject))) : String + (modifiers private) + (if (nullable-null? payload) "blocked" + (bcvStringTrim + (string-append + (string-uppercase (json-object-opt-string-default + (nullable-get payload) "maneuver" "")) + (string-append " " (bcvStringReplace + (json-object-opt-string-default + (nullable-get payload) "reason" "blocked") "-" " ")))))) + (def (controlSummary (payload : (Nullable JSONObject))) : String + (modifiers private) + (let ((text (payloadFieldSummary payload #t))) + (if (string-blank? text) "control" text))) + (def (webSummary (payload : (Nullable JSONObject))) : String + (modifiers private) + (let ((text (payloadFieldSummary payload #f))) + (if (string-blank? text) "web action" text))) + (def (payloadFieldSummary (payload : (Nullable JSONObject)) + (control : Bool)) : String + (modifiers private) + (payloadFieldSummaryAt payload control (int32 0) "" (int32 0))) + (def (payloadFieldSummaryAt (payload : (Nullable JSONObject)) (control : Bool) + (index : Int32) (out : String) + (count : Int32)) : String + (modifiers private) + (if (or (nullable-null? payload) (>= count (int32 3)) + (>= index (if control (int32 10) (int32 7)))) out + (let ((key (payloadKeyAt control index))) + (let ((value (json-object-opt-any (nullable-get payload) key))) + (if (or (not (bcvObjectHas (nullable-get payload) key)) + (nullable-null? value)) + (payloadFieldSummaryAt payload control (+ index (int32 1)) out count) + (payloadFieldSummaryAt payload control (+ index (int32 1)) + (let ((part (string-append (fieldLabel key) + (string-append " " (compactPayloadValue value))))) + (if (= count (int32 0)) part + (string-append out (string-append " " part)))) + (+ count (int32 1))))))) + ) + (def (payloadKeyAt (control : Bool) (index : Int32)) : String + (modifiers private) + (if control + (if (= index (int32 0)) "mode" + (if (= index (int32 1)) "reason" + (if (= index (int32 2)) "ecm" + (if (= index (int32 3)) "eccm" + (if (= index (int32 4)) "ew-shift" + (if (= index (int32 5)) "power" + (if (= index (int32 6)) "reserve-power" + (if (= index (int32 7)) "fire-control-active?" + (if (= index (int32 8)) "shield" "target"))))))))) + (if (= index (int32 0)) "strength" + (if (= index (int32 1)) "web-strength" + (if (= index (int32 2)) "web-hexes" + (if (= index (int32 3)) "hexes" + (if (= index (int32 4)) "damage" + (if (= index (int32 5)) "range" "reason")))))))) + (def (fieldLabel (key : String)) : String + (modifiers private) + (string-uppercase + (bcvStringReplace (bcvStringReplace + (bcvStringReplace (if (bcvStringEndsWith key "?") + (string-take key (- (string-length-int32 key) (int32 1))) key) + "-active" "") "shield-facing" "shield") "-" " "))) + (def (compactPayloadValue (value : (Nullable Any))) : String + (modifiers private) + (if (nullable-null? value) "" + (let ((object (bcvAsJsonObject (nullable-get value)))) + (if (not (nullable-null? object)) + (let ((q (json-object-opt-any (nullable-get object) "q")) + (r (json-object-opt-any (nullable-get object) "r")) + (facing (json-object-opt-any (nullable-get object) "facing"))) + (if (and (not (nullable-null? q)) (not (nullable-null? r))) + (string-append "q" (string-append (bcvAnyToString (nullable-get q)) + (string-append "/r" (string-append (bcvAnyToString (nullable-get r)) + (string-append "/f" (if (nullable-null? facing) "-" + (bcvAnyToString (nullable-get facing)))))))) + (string-append "{" (string-append + (bcvIntText (bcvObjectLength (nullable-get object))) "}")))) + (let ((array (bcvAsJsonArray (nullable-get value)))) + (if (nullable-null? array) + (bcvStringReplace (bcvAnyToString (nullable-get value)) "-" " ") + (bcvIntText (json-array-length (nullable-get array))))))))) (def (maneuverPlotText (raw : (Nullable Any)) (currentImpulse : Int32) @@ -652,14 +1010,13 @@ (if (string-blank? maneuver) out (make-BattlePlotText - (string-append - (BattlePlotText-text out) - (string-append - (if (= (BattlePlotText-count out) (int32 0)) "" "/") - (string-append - (string-append "I" (bcvIntText impulse)) - (string-append " " - (bcvManeuverAbbreviation maneuver))))) + (let ((part (string-append + (string-append "I" (bcvIntText impulse)) + (string-append " " + (bcvManeuverAbbreviation maneuver))))) + (if (= (BattlePlotText-count out) (int32 0)) part + (string-append (BattlePlotText-text out) + (string-append "/" part)))) (+ (BattlePlotText-count out) (int32 1))))))))) (if (= (BattlePlotText-count state) (int32 0)) "CLEAR" @@ -703,6 +1060,58 @@ (json-array-opt-string (nullable-get pair) (int32 1)))))))) + (def (rangeForecastLine (battle : JSONObject) (orders : JSONObject) + (currentImpulse : Int32)) : String + (modifiers private) + (let ((forecast (bcvProjectedRangeForecast + (json-object-opt-json-object battle "alpha-position") + (json-object-opt-json-object battle "beta-position") + (json-object-opt-any orders "alpha-maneuvers") + (json-object-opt-any orders "beta-maneuvers") + currentImpulse (int32 8) (int32 5)))) + (if (= (bcvListSize forecast) (int32 0)) "" + (let ((currentRange (bcvHexRangeFromPositions + (json-object-opt-json-object battle "alpha-position") + (json-object-opt-json-object battle "beta-position"))) + (finalRange (bcvPointRange + (bcvListGet forecast + (- (bcvListSize forecast) (int32 1)))))) + (let ((trend (if (nullable-null? currentRange) "FORECAST" + (if (< finalRange (nullable-get currentRange)) "CLOSING" + (if (> finalRange (nullable-get currentRange)) "OPENING" + "RANGE"))))) + (string-append trend + (string-append " " (forecastPoints forecast (int32 0) "")))))))) + (def (forecastPoints (forecast : (List ProjectedRangePoint)) + (index : Int32) (out : String)) : String + (modifiers private) + (if (or (>= index (bcvListSize forecast)) (>= index (int32 4))) out + (let ((point (bcvListGet forecast index))) + (forecastPoints forecast (+ index (int32 1)) + (let ((part (string-append "I" (string-append + (bcvIntText (bcvPointImpulse point)) + (string-append " R" (bcvIntText (bcvPointRange point))))))) + (if (= index (int32 0)) part + (string-append out (string-append " " part)))))))) + (def (maneuverEntries (raw : (Nullable Any))) : JSONArray + (modifiers private) + (let ((out (new JSONArray))) + (begin + (for/fold ((ignored (int32 0))) ((impulse (in-range (int32 1) (int32 33)))) + (if (nullable-null? raw) ignored + (let ((maneuver (maneuverAt (nullable-get raw) impulse))) + (begin + (if (string-blank? maneuver) (begin) + (begin + (bcvArrayPutString out + (string-append (bcvIntText impulse) + (string-append ":" maneuver))) + (begin))) + ignored)))) + out))) + (def (maneuverAbbrev (maneuver : String)) : String + (modifiers private) (bcvManeuverAbbreviation maneuver)) + (def (maxInternals (ship : (Nullable JSONObject)) (current : Int32)) : Int32 (modifiers private) @@ -725,8 +1134,30 @@ (def (paPanelMax (ship : (Nullable JSONObject))) : Int32 (modifiers private) (bcvMaxInt (int32 1) - (+ (counterValue (bcvNullableObjectAny ship "boxes") "52") + (+ (paPanelBoxCount ship) (counterValue (bcvNullableObjectAny ship "destroyed-boxes") "52")))) + (def (paPanelCount (ship : (Nullable JSONObject))) : Int32 + (modifiers private) + (if (nullable-null? ship) (int32 0) + (if (bcvObjectHas (nullable-get ship) "pa-panels") + (bcvMaxInt (int32 0) + (bcvNumberValue (json-object-opt-any (nullable-get ship) "pa-panels"))) + (bcvMaxInt (int32 0) (- (paPanelBoxCount ship) (paPanelCharge ship)))))) + (def (paPanelBoxCount (ship : (Nullable JSONObject))) : Int32 + (modifiers private) + (let ((boxes (bcvNumberValue (bcvNullableObjectAny ship "pa-panel-boxes"))) + (maximum (bcvNumberValue (bcvNullableObjectAny ship "pa-panel-max")))) + (if (> boxes (int32 0)) boxes + (if (> maximum (int32 0)) maximum + (counterValue (bcvNullableObjectAny ship "boxes") "52"))))) + (def (paPanelCharge (ship : (Nullable JSONObject))) : Int32 + (modifiers private) + (bcvClampInt + (bcvNumberValue (bcvNullableObjectAny ship "pa-panel-charge")) + (int32 0) + (bcvMaxInt (int32 1) + (bcvMaxInt (paPanelBoxCount ship) + (bcvNumberValue (bcvNullableObjectAny ship "pa-panel-max")))))) (def (counterTotal (counter : (Nullable Any))) : Int32 (modifiers private) (if (nullable-null? counter) @@ -735,7 +1166,7 @@ (if (nullable-null? obj) (let ((array (bcvAsJsonArray (nullable-get counter)))) (if (nullable-null? array) - (bcvNumberValue counter) + (int32 0) (counterArrayTotal (nullable-get array) (int32 0)))) (counterObjectTotal (nullable-get obj)))))) (def (counterObjectTotal (obj : JSONObject)) : Int32 @@ -827,6 +1258,187 @@ (bcvMaxInt (int32 0) (json-object-opt-int32-default (nullable-get weapon) "fireable-in-arc" (int32 0)))))))))) + (def (primaryWeaponLine (actor : (Nullable JSONObject))) : String + (modifiers private) + (let ((weapons (if (nullable-null? actor) (nullable-none JSONArray) + (json-object-opt-json-array + (nullable-get actor) "weapon-status")))) + (if (nullable-null? weapons) "" + (let ((index (bestPrimaryWeaponIndex (nullable-get weapons) + (int32 0) (int32 -1)))) + (if (< index (int32 0)) "" + (let ((weapon (json-array-opt-json-object + (nullable-get weapons) index))) + (if (nullable-null? weapon) "" + (let ((arc (compactArray + (json-object-opt-json-array + (nullable-get weapon) "arc-codes") (int32 2)))) + (string-append + (shortWeapon (json-object-opt-string-default + (nullable-get weapon) "kind" "-")) + (string-append " " + (string-append (bcvIntText + (json-object-opt-int32-default + (nullable-get weapon) "fireable-in-arc" (int32 0))) + (string-append "/" + (string-append (bcvIntText + (json-object-opt-int32-default + (nullable-get weapon) "armed" (int32 0))) + (if (string-blank? arc) "" + (string-append " " arc))))))))))))))) + (def (weaponFamilyLine (actor : (Nullable JSONObject))) : String + (modifiers private) + (let ((weapons (if (nullable-null? actor) (nullable-none JSONArray) + (json-object-opt-json-array + (nullable-get actor) "weapon-status")))) + (if (nullable-null? weapons) "" + (weaponFamilyLineAt (nullable-get weapons) (int32 0) + (int32 -1) (int32 -1) (int32 -1) "")))) + (def (weaponFamilyLineAt (weapons : JSONArray) (slot : Int32) + (first : Int32) (second : Int32) (third : Int32) + (out : String)) : String + (modifiers private) + (if (>= slot (int32 3)) out + (let ((best (bestWeaponIndex weapons (int32 0) (int32 -1) + first second third))) + (if (< best (int32 0)) out + (let ((weapon (json-array-opt-json-object weapons best))) + (if (nullable-null? weapon) out + (let ((cooldown (bcvMaxInt (int32 0) + (json-object-opt-int32-default + (nullable-get weapon) "cooldown" (int32 0)))) + (fireable (bcvMaxInt (int32 0) + (json-object-opt-int32-default + (nullable-get weapon) "fireable-in-arc" + (json-object-opt-int32-default + (nullable-get weapon) "fireable" (int32 0))))) + (armed (bcvMaxInt (int32 0) + (json-object-opt-int32-default + (nullable-get weapon) "armed" (int32 0)))) + (installed (bcvMaxInt (int32 0) + (json-object-opt-int32-default + (nullable-get weapon) "installed" (int32 0))))) + (let ((line (string-append + (shortWeapon (json-object-opt-string-default + (nullable-get weapon) "kind" "-")) + (string-append " " + (string-append (bcvIntText fireable) + (string-append "/" + (string-append (bcvIntText armed) + (string-append "/" + (string-append (bcvIntText installed) + (if (> cooldown (int32 0)) + (string-append " cd" (bcvIntText cooldown)) "")))))))))) + (weaponFamilyLineAt weapons (+ slot (int32 1)) + (if (= slot (int32 0)) best first) + (if (= slot (int32 1)) best second) + (if (= slot (int32 2)) best third) + (if (= slot (int32 0)) line + (string-append out (string-append " " line))))))))))) + ) + (def (bestPrimaryWeaponIndex (weapons : JSONArray) (index : Int32) + (best : Int32)) : Int32 + (modifiers private) + (if (>= index (json-array-length weapons)) best + (let ((weapon (json-array-opt-json-object weapons index))) + (if (nullable-null? weapon) + (bestPrimaryWeaponIndex weapons (+ index (int32 1)) best) + (if (< best (int32 0)) + (bestPrimaryWeaponIndex weapons (+ index (int32 1)) index) + (let ((bestWeapon (json-array-opt-json-object weapons best))) + (if (or (nullable-null? bestWeapon) + (> (weaponScore (nullable-get weapon)) + (weaponScore (nullable-get bestWeapon)))) + (bestPrimaryWeaponIndex weapons (+ index (int32 1)) index) + (bestPrimaryWeaponIndex weapons (+ index (int32 1)) best)))))))) + (def (bestWeaponIndex (weapons : JSONArray) (index : Int32) + (best : Int32) (skipA : Int32) + (skipB : Int32) (skipC : Int32)) : Int32 + (modifiers private) + (if (>= index (json-array-length weapons)) best + (let ((weapon (json-array-opt-json-object weapons index))) + (if (or (= index skipA) (= index skipB) (= index skipC)) + (bestWeaponIndex weapons (+ index (int32 1)) best skipA skipB skipC) + (if (nullable-null? weapon) + (bestWeaponIndex weapons (+ index (int32 1)) best skipA skipB skipC) + (if (<= (+ (json-object-opt-int32-default (nullable-get weapon) "installed" (int32 0)) + (json-object-opt-int32-default (nullable-get weapon) "armed" (int32 0)) + (json-object-opt-int32-default (nullable-get weapon) "fireable" (int32 0)) + (json-object-opt-int32-default (nullable-get weapon) "cooldown" (int32 0))) + (int32 0)) + (bestWeaponIndex weapons (+ index (int32 1)) best skipA skipB skipC) + (if (< best (int32 0)) + (bestWeaponIndex weapons (+ index (int32 1)) index skipA skipB skipC) + (let ((bestWeapon (json-array-opt-json-object weapons best))) + (if (or (nullable-null? bestWeapon) + (> (weaponScore (nullable-get weapon)) + (weaponScore (nullable-get bestWeapon))) + (and (= (weaponScore (nullable-get weapon)) + (weaponScore (nullable-get bestWeapon))) + (< (bcvStringCompare + (json-object-opt-string-default + (nullable-get weapon) "kind" "") + (json-object-opt-string-default + (nullable-get bestWeapon) "kind" "")) (int32 0)))) + (bestWeaponIndex weapons (+ index (int32 1)) index skipA skipB skipC) + (bestWeaponIndex weapons (+ index (int32 1)) best skipA skipB skipC))))))))) + ) + (def (weaponScore (weapon : JSONObject)) : Int32 + (modifiers private) + (+ (* (json-object-opt-int32-default weapon "fireable-in-arc" (int32 0)) (int32 1000)) + (* (json-object-opt-int32-default weapon "fireable" (int32 0)) (int32 100)) + (* (json-object-opt-int32-default weapon "armed" (int32 0)) (int32 10)) + (json-object-opt-int32-default weapon "installed" (int32 0)))) + (def (shortWeapon (kind : String)) : String + (modifiers private) + (string-uppercase + (bcvStringReplace + (bcvStringReplace + (bcvStringReplace + (bcvStringReplace + (bcvStringReplace kind "phaser-" "PH-") "photon" "PHOT") + "disruptor" "DISR") "plasma-" "PL-") "drone-" "DR-"))) + (def (compactArray (array : (Nullable JSONArray)) (limit : Int32)) : String + (modifiers private) + (if (or (nullable-null? array) + (= (json-array-length (nullable-get array)) (int32 0))) "" + (compactArrayAt (nullable-get array) limit (int32 0) ""))) + (def (compactArrayAt (array : JSONArray) (limit : Int32) + (index : Int32) (out : String)) : String + (modifiers private) + (if (or (>= index (json-array-length array)) (>= index limit)) + (if (> (json-array-length array) limit) + (string-append out (string-append "/+" + (bcvIntText (- (json-array-length array) limit)))) out) + (let ((value (json-array-opt-any array index))) + (compactArrayAt array limit (+ index (int32 1)) + (let ((part (if (nullable-null? value) "null" + (bcvAnyToString (nullable-get value))))) + (if (= index (int32 0)) part + (string-append out (string-append "/" part)))))))) + (def (numberValue (value : (Nullable Any))) : Int32 + (modifiers private) (bcvNumberValue value)) + (def (sideLabel (value : String)) : String + (modifiers private) + (let ((lower (bcvStringLowercase value))) + (if (equal? lower "alpha") "A" + (if (equal? lower "beta") "B" + (string-take (string-uppercase value) + (bcvMinInt (int32 6) (string-length-int32 value))))))) + (def (shieldFaceLabel (value : (Nullable Any))) : String + (modifiers private) + (if (nullable-null? value) "" + (let ((text (bcvAnyToString (nullable-get value)))) + (if (or (equal? text "#f") (equal? text "false")) "" + (if (equal? text "all") "ALL" + (let ((plain (if (bcvStringStartsWith text "shield-") + (bcvStringSubstring text (int32 7)) text))) + (let ((number (string->int32-or-null plain))) + (if (nullable-null? number) (string-uppercase text) + (string-append "SH" (bcvIntText (nullable-get number)))))))))) + ) + (def (forEachCounterPair (counter : (Nullable Any))) : Unit + (modifiers private) (begin)) (def (barColor (ratio : Float32)) : Int32 (modifiers private) (if (> ratio (float32 0.66)) @@ -845,8 +1457,8 @@ (def (fitTextEnd (text : String) (paint : Paint) (maxWidth : Float32) (end : Int32)) : String (modifiers private) - (if (<= end (int32 1)) - (string-append (string-take text (int32 1)) "...") + (if (<= end (int32 4)) + (string-take text end) (let ((candidate (string-append (string-take text end) "..."))) (if (<= (bcvPaintMeasureText paint candidate) maxWidth) --- a/templates/original-tactics-parts/CombatLogView.kt.ss +++ b/templates/original-tactics-parts/CombatLogView.kt.ss @@ -27,6 +27,7 @@ (type JSONArray) (type JSONObject) (type Any) + (type Number) (type Int32) (type Float32) (extern (clvPaintAntiAliasFlag) : Int32 @@ -39,6 +40,8 @@ (kotlin-value Paint Align LEFT)) (extern (clvPaintAlignCenter) : Align (kotlin-value Paint Align CENTER)) + (extern (clvPaintAlignRight) : Align + (kotlin-value Paint Align RIGHT)) (extern (clvColorWhite) : Int32 (kotlin-value Color WHITE)) (extern (clvColorRgb (red : Int32) (green : Int32) (blue : Int32)) : Int32 @@ -55,6 +58,8 @@ (kotlin-member-set isFakeBoldText)) (extern (clvPaintTextAlignSet (paint : Paint) (value : Align)) : Unit (kotlin-member-set textAlign)) + (extern (clvPaintMeasureText (paint : Paint) (text : String)) : Float32 + (kotlin-member-call measureText)) (extern (clvViewInvalidate (view : CombatLogView)) : Unit (kotlin-member-call invalidate)) (extern (clvSuperOnDraw (canvas : Canvas)) : Unit @@ -87,12 +92,57 @@ (y : Float32) (paint : Paint)) : Unit (kotlin-member-call drawText)) + (extern (clvRectLeft (rect : RectF)) : Float32 (kotlin-member-get left)) + (extern (clvRectTop (rect : RectF)) : Float32 (kotlin-member-get top)) + (extern (clvRectRight (rect : RectF)) : Float32 (kotlin-member-get right)) + (extern (clvRectBottom (rect : RectF)) : Float32 (kotlin-member-get bottom)) + (extern (clvRectWidth (rect : RectF)) : Float32 (kotlin-member-call width)) (extern (clvJsonOptInt (object : JSONObject) (key : String) (fallback : Int32)) : Int32 (kotlin-member-call optInt)) (extern (clvJsonArrayLength (array : JSONArray)) : Int32 (kotlin-member-call length)) + (extern (clvArrayPutObject (array : JSONArray) (value : JSONObject)) : JSONArray + (kotlin-member-call put)) + (extern (clvAsObject (value : Any)) : (Nullable JSONObject) + (kotlin-safe-cast JSONObject)) + (extern (clvAsArray (value : Any)) : (Nullable JSONArray) + (kotlin-safe-cast JSONArray)) + (extern (clvAsNumber (value : Any)) : (Nullable Number) + (kotlin-safe-cast Number)) + (extern (clvAsString (value : Any)) : (Nullable String) + (kotlin-safe-cast String)) + (extern (clvNumberInt (value : Number)) : Int32 (kotlin-member-call toInt)) + (extern (clvAnyString (value : Any)) : String (kotlin-member-call toString)) + (extern (clvStringContains (text : String) (part : String)) : Bool + (kotlin-member-call contains)) + (extern (clvStringLowercase (text : String)) : String + (kotlin-member-call lowercase)) + (extern (clvStringEndsWith (text : String) (suffix : String)) : Bool + (kotlin-member-call endsWith)) + (extern (clvStringStartsWith (text : String) (prefix : String)) : Bool + (kotlin-member-call startsWith)) + (extern (clvStringSubstring (text : String) (start : Int32)) : String + (kotlin-member-call substring)) + (extern (clvStringTrim (text : String)) : String + (kotlin-member-call trim)) + (extern (clvStringReplace (text : String) (old : String) + (replacement : String)) : String + (kotlin-member-call replace)) + (extern (clvObjectHas (object : JSONObject) (key : String)) : Bool + (kotlin-member-call has)) + (extern (clvObjectLength (object : JSONObject)) : Int32 + (kotlin-member-call length)) + (extern (clvJsonKeys (object : JSONObject)) : (Iterator String) + (kotlin-member-call keys)) + (extern (clvIteratorHasNext (items : (Iterator String))) : Bool + (kotlin-member-call hasNext)) + (extern (clvIteratorNext (items : (Iterator String))) : String + (kotlin-member-call next)) + (extern (clvShortShipLabel (ship : (Nullable JSONObject)) + (limit : Int32) (fallback : String)) : String + (kotlin-call shortShipDisplayLabel)) (def (clvConfigurePaint (paint : Paint) (color : Int32) (style : Style)) : Paint @@ -114,6 +164,14 @@ (clvDisplayMetricsDensity (clvResourcesDisplayMetrics (clvViewResources view))))) (if (< density (float32 1.0)) (float32 1.0) density))) + (def (clvMaxInt (a : Int32) (b : Int32)) : Int32 (if (> a b) a b)) + (def (clvClampInt (value : Int32) (low : Int32) (high : Int32)) : Int32 + (if (< value low) low (if (> value high) high value))) + (def (clvNullableInt (object : (Nullable JSONObject)) + (key : String) (fallback : Int32)) : Int32 + (if (nullable-null? object) fallback + (json-object-opt-int32-default (nullable-get object) key fallback))) + (record LogStringState ((text : String) (count : Int32))) (class CombatLogView ((context : Context)) (extends View context) (var game : (Nullable JSONObject) (nullable-none JSONObject) @@ -208,54 +266,628 @@ (def (drawContent (canvas : Canvas)) : Unit (modifiers private) (begin - (clvCanvasDrawRoundRect - canvas - (new RectF - (float32 0.0) - (float32 0.0) - drawWidth - drawHeight) - (float32 10.0) - (float32 10.0) - backgroundPaint) + (clvCanvasDrawRoundRect canvas + (new RectF (float32 0.0) (float32 0.0) drawWidth drawHeight) + (float32 10.0) (float32 10.0) backgroundPaint) (if (nullable-null? game) - (begin - (clvPaintTextAlignSet titlePaint (clvPaintAlignCenter)) - (clvCanvasDrawText - canvas - "COMBAT LOG" - (/ drawWidth (float32 2.0)) - (- (/ drawHeight (float32 2.0)) (float32 4.0)) - titlePaint) - (clvPaintTextAlignSet dimPaint (clvPaintAlignCenter)) - (clvCanvasDrawText - canvas - "No active engagement" - (/ drawWidth (float32 2.0)) - (+ (/ drawHeight (float32 2.0)) (float32 22.0)) - dimPaint)) - (let ((current (nullable-get game))) + (begin + (clvPaintTextAlignSet titlePaint (clvPaintAlignCenter)) + (clvCanvasDrawText canvas "COMBAT LOG" (/ drawWidth (float32 2.0)) + (- (/ drawHeight (float32 2.0)) (float32 4.0)) titlePaint) + (clvPaintTextAlignSet dimPaint (clvPaintAlignCenter)) + (clvCanvasDrawText canvas "No active engagement" + (/ drawWidth (float32 2.0)) + (+ (/ drawHeight (float32 2.0)) (float32 22.0)) dimPaint)) + (let ((current (nullable-get game))) + (let ((rows (recentEvents (int32 12)))) + (begin + (drawHeader canvas current) + (drawShipStrip canvas + (json-object-opt-json-object current "battle-state")) + (if (= (json-array-length rows) (int32 0)) + (begin + (clvPaintTextAlignSet dimPaint (clvPaintAlignCenter)) + (clvCanvasDrawText canvas "No combat events yet" + (/ drawWidth (float32 2.0)) (float32 112.0) dimPaint)) + (drawEventRows canvas rows (int32 0) (float32 84.0))))))))) + (def (drawHeader (canvas : Canvas) (current : JSONObject)) : Unit + (modifiers private) + (let ((status (if (nullable-null? dashboard) (nullable-none JSONObject) + (json-object-opt-json-object (nullable-get dashboard) "status"))) + (battle (json-object-opt-json-object current "battle-state")) + (activity (if (nullable-null? dashboard) (nullable-none JSONObject)