diff --git a/proto/apl.proto b/proto/apl.proto index 24d2042c13..f008cda502 100644 --- a/proto/apl.proto +++ b/proto/apl.proto @@ -48,6 +48,7 @@ message APLValue { APLValueOr or = 3; APLValueNot not = 4; APLValueCompare cmp = 5; + APLValueMath math = 38; // Encounter values APLValueCurrentTime current_time = 7; @@ -175,6 +176,19 @@ message APLValueCompare { APLValue lhs = 2; APLValue rhs = 3; } +message APLValueMath { + enum MathOperator { + OpUnknown = 0; + OpAdd = 1; // Add + OpSub = 2; // Subtract + OpMul = 3; // Multiply + OpDiv = 4; // Divide + } + MathOperator op = 1; + + APLValue lhs = 2; + APLValue rhs = 3; +} message APLValueCurrentTime {} message APLValueCurrentTimePercent {} diff --git a/sim/core/apl_value.go b/sim/core/apl_value.go index ed51bd46de..68531e18a4 100644 --- a/sim/core/apl_value.go +++ b/sim/core/apl_value.go @@ -56,6 +56,8 @@ func (rot *APLRotation) newAPLValue(config *proto.APLValue) APLValue { return rot.newValueNot(config.GetNot()) case *proto.APLValue_Cmp: return rot.newValueCompare(config.GetCmp()) + case *proto.APLValue_Math: + return rot.newValueMath(config.GetMath()) // Encounter case *proto.APLValue_CurrentTime: diff --git a/sim/core/apl_values_operators.go b/sim/core/apl_values_operators.go index 7617bd95de..11ce6d94dd 100644 --- a/sim/core/apl_values_operators.go +++ b/sim/core/apl_values_operators.go @@ -311,6 +311,100 @@ func (value *APLValueCompare) GetBool(sim *Simulation) bool { return false } +type APLValueMath struct { + defaultAPLValueImpl + op proto.APLValueMath_MathOperator + lhs APLValue + rhs APLValue +} + +func (rot *APLRotation) newValueMath(config *proto.APLValueMath) APLValue { + lhs, rhs := rot.newAPLValue(config.Lhs), rot.newAPLValue(config.Rhs) + if lhs == nil || rhs == nil { + return nil + } + + if lhs.Type() == proto.APLValueType_ValueTypeBool || rhs.Type() == proto.APLValueType_ValueTypeBool { + rot.validationWarning("Bool types not allowed in Math Operations!") + return nil + } + + if lhs.Type() == proto.APLValueType_ValueTypeString || rhs.Type() == proto.APLValueType_ValueTypeString { + rot.validationWarning("String types not allowed in Math Operations!") + return nil + } + + return &APLValueMath{ + op: config.Op, + lhs: lhs, + rhs: rhs, + } +} +func (value *APLValueMath) Type() proto.APLValueType { + return value.lhs.Type() +} +func (value *APLValueMath) GetInt(sim *Simulation) int32 { + switch value.op { + case proto.APLValueMath_OpAdd: + return value.lhs.GetInt(sim) + value.rhs.GetInt(sim) + case proto.APLValueMath_OpSub: + return value.lhs.GetInt(sim) - value.rhs.GetInt(sim) + case proto.APLValueMath_OpMul: + return value.lhs.GetInt(sim) * value.rhs.GetInt(sim) + case proto.APLValueMath_OpDiv: + return value.lhs.GetInt(sim) / value.rhs.GetInt(sim) + } + return 0 +} +func (value *APLValueMath) GetFloat(sim *Simulation) float64 { + switch value.op { + case proto.APLValueMath_OpAdd: + return value.lhs.GetFloat(sim) + value.rhs.GetFloat(sim) + case proto.APLValueMath_OpSub: + return value.lhs.GetFloat(sim) - value.rhs.GetFloat(sim) + case proto.APLValueMath_OpMul: + return value.lhs.GetFloat(sim) * value.rhs.GetFloat(sim) + case proto.APLValueMath_OpDiv: + return value.lhs.GetFloat(sim) / value.rhs.GetFloat(sim) + } + return 0 +} +func (value *APLValueMath) GetDuration(sim *Simulation) time.Duration { + switch value.op { + case proto.APLValueMath_OpAdd: + return value.lhs.GetDuration(sim) + value.rhs.GetDuration(sim) + case proto.APLValueMath_OpSub: + return value.lhs.GetDuration(sim) - value.rhs.GetDuration(sim) + case proto.APLValueMath_OpMul: + left := value.lhs.GetDuration(sim) + right := value.rhs.GetDuration(sim) + + switch value.lhs.Type() { + case proto.APLValueType_ValueTypeInt: + left = time.Duration(value.lhs.GetInt(sim)) + case proto.APLValueType_ValueTypeFloat: + left = time.Duration(value.lhs.GetFloat(sim)) + } + + switch value.rhs.Type() { + case proto.APLValueType_ValueTypeInt: + right = time.Duration(value.rhs.GetInt(sim)) + case proto.APLValueType_ValueTypeFloat: + right = time.Duration(value.rhs.GetFloat(sim)) + } + return left * right + case proto.APLValueMath_OpDiv: + divider := value.rhs.GetDuration(sim) + if value.rhs.Type() == proto.APLValueType_ValueTypeFloat { + divider = time.Duration(value.rhs.GetFloat(sim)) + } else if value.rhs.Type() == proto.APLValueType_ValueTypeInt { + divider = time.Duration(value.rhs.GetInt(sim)) + } + return value.lhs.GetDuration(sim) / divider + } + return 0 +} + type APLValueAnd struct { defaultAPLValueImpl vals []APLValue diff --git a/ui/core/components/individual_sim_ui/apl_values.ts b/ui/core/components/individual_sim_ui/apl_values.ts index 64888b4e4f..0e9db5868d 100644 --- a/ui/core/components/individual_sim_ui/apl_values.ts +++ b/ui/core/components/individual_sim_ui/apl_values.ts @@ -5,6 +5,7 @@ import { APLValueNot, APLValueCompare, APLValueCompare_ComparisonOperator as ComparisonOperator, + APLValueMath_MathOperator as MathOperator, APLValueConst, APLValueCurrentTime, APLValueCurrentTimePercent, @@ -38,6 +39,7 @@ import { APLValueCurrentNonDeathRuneCount, APLValueSpellTravelTime, APLValueSpellChannelTime, + APLValueMath, } from '../../proto/apl.js'; import { EventID, TypedEvent } from '../../typed_event.js'; @@ -244,6 +246,24 @@ function comparisonOperatorFieldConfig(field: string): AplHelpers.APLPickerBuild }; } +function mathOperatorFieldConfig(field: string): AplHelpers.APLPickerBuilderFieldConfig { + return { + field: field, + newValue: () => MathOperator.OpAdd, + factory: (parent, player, config) => new TextDropdownPicker(parent, player, { + ...config, + defaultLabel: 'None', + equals: (a, b) => a == b, + values: [ + { value: MathOperator.OpAdd, label: '+' }, + { value: MathOperator.OpSub, label: '-' }, + { value: MathOperator.OpMul, label: '*' }, + { value: MathOperator.OpDiv, label: '/' }, + ], + }), + }; +} + export function valueFieldConfig(field: string, options?: Partial>): AplHelpers.APLPickerBuilderFieldConfig { return { field: field, @@ -323,6 +343,17 @@ const valueTypeFactories: Record, ValueTypeConfig valueFieldConfig('rhs'), ], }), + ['math']: inputBuilder({ + label: 'Math', + submenu: ['Logic'], + shortDescription: 'Do basic math on two values.', + newValue: APLValueMath.create, + fields: [ + valueFieldConfig('lhs'), + mathOperatorFieldConfig('op'), + valueFieldConfig('rhs'), + ], + }), ['and']: inputBuilder({ label: 'All of', submenu: ['Logic'],