diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 5fc1fcd..f62d6e3 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -22,8 +22,6 @@ jobs: - uses: actions/checkout@v3 - name: Install dependencies run: dart pub get - - name: Lint Code - run: dart analyze lib/ - name: Run tests run: dart run coverage:test_with_coverage - name: Upload coverage to Codecov @@ -31,3 +29,5 @@ jobs: with: token: ${{secrets.CODECOV_TOKEN}} file: coverage/lcov.info + - name: Lint Code + run: dart analyze lib/ diff --git a/example/files/load.dart b/example/files/load.dart index 46c2085..951dcec 100644 --- a/example/files/load.dart +++ b/example/files/load.dart @@ -10,7 +10,8 @@ class LoadFile extends Widget { Data.merge(Entity.All(), nbt: { "uuid": UUID(1, 2, 3, 4), 'name': TextComponent('name'), - }) + }), + Bossbar("test").get(BossbarOption.value) + 1 ]); } } diff --git a/example/score_test.dart b/example/score_test.dart new file mode 100644 index 0000000..4ad603e --- /dev/null +++ b/example/score_test.dart @@ -0,0 +1,15 @@ +import 'package:objd/core.dart'; + +void main(List args) { + var s1 = Score(Entity.All(), "s1"); + var s2 = Score.Self('s2'); + var t = Bossbar("test").get(BossbarOption.value) + 1; + + print(t); + // print(s1 + (s2 + s1)); + + final (_, ops) = t.copy(); + print(ops); + //print(getCommands(For.of(ops)).join('\n')); + print(getCommands(t).join('\n')); //TODO: Caveat s1 + s2 != s2 + s1 +} diff --git a/folder name/data/minecraft/tags/functions/load.json b/folder name/data/minecraft/tags/functions/load.json new file mode 100644 index 0000000..83044f1 --- /dev/null +++ b/folder name/data/minecraft/tags/functions/load.json @@ -0,0 +1 @@ +{"values":["mypack:load"]} \ No newline at end of file diff --git a/folder name/data/minecraft/tags/functions/tick.json b/folder name/data/minecraft/tags/functions/tick.json new file mode 100644 index 0000000..05aec44 --- /dev/null +++ b/folder name/data/minecraft/tags/functions/tick.json @@ -0,0 +1 @@ +{"values":["mypack:main"]} \ No newline at end of file diff --git a/folder name/data/mypack/functions/load.mcfunction b/folder name/data/mypack/functions/load.mcfunction new file mode 100644 index 0000000..b0cd501 --- /dev/null +++ b/folder name/data/mypack/functions/load.mcfunction @@ -0,0 +1,3 @@ +scoreboard objectives add objd_temp dummy +data merge entity @a {uuid:[I;1,2,3,4],name:{"text":"name"}} +scoreboard players add #at6AbnE8 objd_temp 1 diff --git a/folder name/data/mypack/functions/main.mcfunction b/folder name/data/mypack/functions/main.mcfunction new file mode 100644 index 0000000..e69de29 diff --git a/folder name/pack.mcmeta b/folder name/pack.mcmeta new file mode 100644 index 0000000..e728709 --- /dev/null +++ b/folder name/pack.mcmeta @@ -0,0 +1 @@ +{"pack":{"pack_format":8,"description":"This is a datapack generated with objd by Stevertus"}} \ No newline at end of file diff --git a/lib/src/basic/score.dart b/lib/src/basic/score.dart deleted file mode 100644 index 8009b1e..0000000 --- a/lib/src/basic/score.dart +++ /dev/null @@ -1,511 +0,0 @@ -import 'package:objd/src/basic/file.dart'; -import 'package:objd/src/basic/group.dart'; -import 'package:objd/src/basic/rest_action.dart'; -import 'package:objd/src/basic/widget.dart'; -import 'package:objd/src/wrappers/data.dart'; -import 'package:objd/src/basic/command.dart'; -import 'package:objd/src/basic/for_list.dart'; -import 'package:objd/src/basic/types/entity.dart'; -import 'package:objd/src/basic/types/condition.dart'; -import 'package:objd/src/basic/scoreboard.dart'; -import 'package:objd/src/build/build.dart'; -import 'package:objd/src/wrappers/if.dart'; - -import 'builder.dart'; - -class Score extends RestActionAble { - List _commands = []; - - /// Get commands - List get commands => _commands; - final Entity entity; - String score; - String type; - String _strGen = ''; - - /// The [Score] class is the basis for setting values, calculating with scores and checking the values. - ///It implements one base class with no functionality and several methods to do actions: - /// - ///|constructor| | - ///|--|--| - ///|Entity| the entity within the scoreboard | - ///|String| the name of the objective | - ///|addNew| bool whether it should add the scoreboard itself if it does not exist(default = true)| - /// - ///```dart - /// Score(Entity.Selected(),'score',addNew: true) - ///``` - - Score( - this.entity, - this.score, { - bool addNew = true, - List? commands, - this.type = 'dummy', - }) { - if (commands != null) _commands = commands; - if (addNew) { - _commands.add( - Scoreboard(score, type: type), - ); - } - if (Scoreboard.prefix != null && !score.contains(Scoreboard.prefix!)) { - score = Scoreboard.prefix! + score; - } - } - - /// same as Score() but with a predefined entity(Entity.Selected(),) - /// ```dart - /// Score.fromSelected('objective').set(3) - /// ⇒ scoreboard players set @s objective 3 - /// ``` - Score.fromSelected( - this.score, { - bool addNew = true, - this.type = 'dummy', - }) : entity = Entity.Self() { - if (addNew) { - _commands.add( - Scoreboard(score, type: type), - ); - } - if (Scoreboard.prefix != null && !score.contains(Scoreboard.prefix!)) { - score = Scoreboard.prefix! + score; - } - } - Score.str(this._strGen, - {this.score = '', String match = '0', this.type = 'dummy'}) - : entity = Entity.Self() { - _match = match; - } - - /// Do you need constant values with scores? objD got you covered with `Score.con`: - /// - /// |Score.con| | - /// |--|--| - /// |int| a constant number | - /// |addNew|bool whether it should add objd_consts itself if it does not exist(default = true)| - - /// This will automatically create a scoreboard called `objd_consts` and set the value to the fake entity `#[value]` - /// - /// **Example:** - /// ```dart - /// Score.con(5) - /// ⇒ scoreboard players set #5 objd_consts 5 - /// ``` - Score.con( - int number, { - bool addNew = true, - this.type = 'dummy', - }) : score = 'objd_consts', - entity = Entity.PlayerName( - '#$number', - ) { - if (Scoreboard.prefix != null && !score.contains(Scoreboard.prefix!)) { - score = Scoreboard.prefix! + score; - } - - if (addNew) { - _commands.add( - Scoreboard(score, type: type), - ); - } - _commands.add( - set(number), - ); - } - - String _getESStr({Entity? entity, String? score}) { - entity ??= this.entity; - score ??= this.score; - return '$entity $score'; - } - - Score addCommandRet(Widget command) { - var commands = List.from(_commands); - commands.add(command); - return Score(entity, score, addNew: false, commands: commands, type: type); - } - - /// add - Score operator +(dynamic other) { - if (other is int) return add(other); - if (other is Score) return addScore(other); - throw ('Please use either a Score or an Int in the operator +'); - } - - /// subtract - Score operator -(dynamic other) { - if (other is int) return subtract(other); - if (other is Score) return subtractScore(other); - throw ('Please use either a Score or an Int in the operator -'); - } - - /// modulo by - Score operator %(dynamic other) { - if (other is int) { - return modulo( - Score.con(other), - ); - } - if (other is Score) return modulo(other); - throw ('Please use either a Score or an Int in the operator %'); - } - - /// divide by - Score operator /(dynamic other) { - if (other is int) { - return divideByScore( - Score.con(other), - ); - } - if (other is Score) return divideByScore(other); - throw ('Please use either a Score or an Int in the operator /'); - } - - /// multiply by - Score operator *(dynamic other) { - if (other is int) { - return multiplyByScore( - Score.con(other), - ); - } - if (other is Score) return multiplyByScore(other); - throw ('Please use either a Score or an Int in the operator /'); - } - - /// greater than - Score operator >(dynamic other) { - if (other is int) { - return matchesRange( - Range.from(other + 1), - ); - } - if (other is Score) return isBigger(other); - throw ('Please use either a Score or an Int in the operator >'); - } - - /// less than - Score operator <(dynamic other) { - if (other is int) { - return matchesRange( - Range.to(other + -1), - ); - } - if (other is Score) return isSmaller(other); - throw ('Please use either a Score or an Int in the operator >'); - } - - /// bigger or equal - Score operator >=(dynamic other) { - if (other is int) { - return matchesRange( - Range.from(other), - ); - } - if (other is Score) return isBiggerOrEqual(other); - throw ('Please use either a Score or an Int in the operator >='); - } - - /// less or equal - Score operator <=(dynamic other) { - if (other is int) { - return matchesRange( - Range.to(other), - ); - } - if (other is Score) return isSmallerOrEqual(other); - throw ('Please use either a Score or an Int in the operator <='); - } - - /// matches - Score operator &(dynamic other) { - if (other is int) return matches(other); - if (other is Range) return matchesRange(other); - if (other is Score) return isEqual(other); - throw ('Please use either a Score, Range or an Int in the operator &'); - } - - /// assign value(int, Score, Data or Condition) - Widget setTo(dynamic other) { - if (other is int) return set(other); - if (other is Score) return setEqual(other); - if (other is Data) return setToData(other); - if (other is Condition) return setToCondition(other); - if (other is Command) return setToResult(other); - if (other is File) return setToFunction(other); - if (other is Widget) return setToWidget(other); - throw ('Please use either a Score, Data, Condition, Command or an Int in the operator >>'); - } - - Widget operator >>(dynamic other) => setTo(other); - Widget operator <<(dynamic other) => setTo(other); - - /// sets the score to a given value of int - Score set(int val) { - return addCommandRet( - Command( - 'scoreboard players set ${_getESStr()} $val', - ), - ); - } - - /// resets the value of a score - Score reset() { - return addCommandRet( - Command( - 'scoreboard players reset ${_getESStr()}', - ), - ); - } - - /// adds a value to the score - Score add([int val = 1]) { - return addCommandRet( - Command( - 'scoreboard players add ${_getESStr()} $val', - ), - ); - } - - /// subtracts a value from the score - Score subtract([int val = 1]) { - return addCommandRet( - Command( - 'scoreboard players remove ${_getESStr()} $val', - ), - ); - } - - /// gets the value of the score to work with it further - Score get() { - return addCommandRet( - Command( - 'scoreboard players get ${_getESStr()}', - ), - ); - } - - // all operations - /// sets this score equal to another - Score setEqual(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${isEqual(score).getString()}', - ), - ); - } - - /// swaps two scores - Score swapWith(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${_getESStr()} >< ${_getESStr(entity: score.entity, score: score.score)}', - ), - ); - } - - /// compares two scores and sets the smallest to this one - Score setToSmallest(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${isSmaller(score).getString()}', - ), - ); - } - - /// compares two scores and sets the biggest to this one - Score setToBiggest(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${isBigger(score).getString()}', - ), - ); - } - - /// adds another score to this one - Score addScore(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${_getESStr()} += ${_getESStr(entity: score.entity, score: score.score)}', - ), - ); - } - - /// subtracts another score from this one - Score subtractScore(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${_getESStr()} -= ${_getESStr(entity: score.entity, score: score.score)}', - ), - ); - } - - /// sets this score to the result of the multiplication - Score multiplyByScore(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${_getESStr()} *= ${_getESStr(entity: score.entity, score: score.score)}', - ), - ); - } - - /// sets this score to the result of the division - Score divideByScore(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${_getESStr()} /= ${_getESStr(entity: score.entity, score: score.score)}', - ), - ); - } - - /// sets this score to the remainder of the division - Score modulo(Score score) { - return addCommandRet( - Command( - 'scoreboard players operation ${_getESStr()} %= ${_getESStr(entity: score.entity, score: score.score)}', - ), - ); - } - - /// sets the score to an nbt value - Score setToData(Data data) { - if (!data.isGetting) { - throw ('Please set a score to Data.get and not Data.${data.subcommand}'); - } - return addCommandRet( - Builder( - (c) => Command( - 'execute store result score ${_getESStr()} run data get ${data.getTarget(c)} ${data.path} ${data.scale ?? 1}', - ), - ), - ); - } - - /// set to functions return value(or number of commands) - Group setToFunction(File file) => setToWidget(file.run(create: true)); - - /// sets the score to the success of the given Command - Score setToResult(Command commmand, {bool useSuccess = false}) { - return addCommandRet( - Command( - 'execute store ${useSuccess ? 'success' : 'result'} score ${_getESStr()} run $commmand', - ), - ); - } - - /// sets the score to the result of the given Widget - /// JUST one Command should be the input - Group setToWidget(Widget widget, {bool useSuccess = false}) { - return Group( - prefix: - 'execute store ${useSuccess ? 'success' : 'result'} score ${_getESStr()} run', - children: [widget], - ); - } - - /// sets the score to the success of the given condition result - Score setToCondition(Condition cond, {bool useSuccess = false}) { - return addCommandRet( - Command( - 'execute store ${useSuccess ? 'success' : 'result'} score ${_getESStr()} ${Condition.getPrefixes(cond.getList())[0]}', - ), - ); - } - - /// finds the smallest value in a list of scores - Widget findSmallest(List scores, {int? min}) { - return For( - to: scores.length - 1, - create: (int i) { - var ret = setToSmallest(scores[i]); - if (min != null) { - return If( - scores[i].matchesRange( - Range.from(min), - ), - then: [ret], - ); - } - return ret; - }); - } - - /// finds the biggest value in a list of scores - Widget findBiggest(List scores, {int? max}) { - return For( - to: scores.length - 1, - create: (int i) { - var ret = setToBiggest(scores[i]); - if (max != null) { - return If( - scores[i].matchesRange( - Range.to(max), - ), - then: [ret], - ); - } - return ret; - }); - } - - /// tests - - Score isEqual(Score score) { - return Score.str( - '${_getESStr()} = ${_getESStr(entity: score.entity, score: score.score)}', - ); - } - - Score isSmaller(Score score) { - return Score.str( - '${_getESStr()} < ${_getESStr(entity: score.entity, score: score.score)}', - ); - } - - Score isSmallerOrEqual(Score score) { - return Score.str( - '${_getESStr()} <= ${_getESStr(entity: score.entity, score: score.score)}', - ); - } - - Score isBiggerOrEqual(Score score) { - return Score.str( - '${_getESStr()} >= ${_getESStr(entity: score.entity, score: score.score)}', - ); - } - - Score isBigger(Score score) { - return Score.str( - '${_getESStr()} > ${_getESStr(entity: score.entity, score: score.score)}', - ); - } - - String _match = '0'; - String getMatch() => _match; - Score matches(int value) { - _match = value.toString(); - return Score.str('${_getESStr()} matches $_match', - score: score, match: _match); - } - - Score matchesRange(Range range) { - _match = range.toString(); - return Score.str('${_getESStr()} matches $_match', - score: score, match: _match); - } - - @override - Widget generate(Context context) { - return For.of(_commands); - } - - @override - Map toMap() { - return {'Score': For.of(_commands).toMap()}; - } - - String getString() { - return _strGen; - } -} diff --git a/lib/src/basic/score/score.dart b/lib/src/basic/score/score.dart new file mode 100644 index 0000000..e9b0612 --- /dev/null +++ b/lib/src/basic/score/score.dart @@ -0,0 +1,731 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +library score; + +import 'package:objd/src/basic/command.dart'; +import 'package:objd/src/basic/file.dart'; +import 'package:objd/src/basic/for_list.dart'; +import 'package:objd/src/basic/score/score_builder.dart'; +import 'package:objd/src/basic/score/score_condition.dart'; +import 'package:objd/src/basic/score/score_export.dart'; +import 'package:objd/src/basic/scoreboard.dart'; +import 'package:objd/src/basic/types/condition.dart'; +import 'package:objd/src/basic/types/entity.dart'; +import 'package:objd/src/basic/widget.dart'; +import 'package:objd/src/build/context.dart'; +import 'package:objd/src/wrappers/comment.dart'; +import 'package:objd/src/wrappers/data.dart'; +import 'package:objd/src/wrappers/execute.dart'; +import 'package:objd/src/wrappers/if.dart'; + +abstract mixin class ScoreStoreable implements Widget { + // block, bossbar, entity, score, storage + Widget get_assignable_right(Context context) => generate(context); + + ScoreOperation toScore({Score? out}) => StoreScoreOperation( + out ?? Score.tmp(), + this, + ); + + BinaryScoreOperation operator +(dynamic other) => toScore() + other; + BinaryScoreOperation operator -(dynamic other) => toScore() - other; + BinaryScoreOperation operator %(dynamic other) => toScore() % other; + BinaryScoreOperation operator /(dynamic other) => toScore() / other; + BinaryScoreOperation operator *(dynamic other) => toScore() * other; + // TODO: Not finished, refactor of If needed + // ScoreCondition operator >(dynamic other) => toScore() > other; + // ScoreCondition operator >=(dynamic other) => toScore() >= other; + // ScoreCondition operator <=(dynamic other) => toScore() <= other; + // ScoreCondition operator <(dynamic other) => toScore() < other; + // ScoreCondition operator &(dynamic other) => toScore() & other; +} + +abstract mixin class ScoreAssignable { + // block, bossbar, entity, score, storage + String get_assignable_left(); + + ScoreStoreable toStorable(); + + Widget setTo(dynamic other); + Widget operator >>(dynamic other) => setTo(other); + Widget operator <<(dynamic other) => setTo(other); +} + +sealed class ScoreOperation extends Widget implements ScoreStoreable { + /// add + @override + @override + BinaryScoreOperation operator +(dynamic other) => switch (other) { + int val => add(val), + ScoreOperation s => addScore(s), + ScoreStoreable s => addScore(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator +') + }; + + /// subtract + @override + BinaryScoreOperation operator -(dynamic other) => switch (other) { + int val => subtract(val), + ScoreOperation s => subtractScore(s), + ScoreStoreable s => subtractScore(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator -') + }; + + /// modulo by + @override + BinaryScoreOperation operator %(dynamic other) => switch (other) { + int val => modulo(Score.con(val)), + ScoreOperation s => modulo(s), + ScoreStoreable s => modulo(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator %') + }; + + /// divide by + @override + BinaryScoreOperation operator /(dynamic other) => switch (other) { + int val => divideByScore(Score.con(val)), + ScoreOperation s => divideByScore(s), + ScoreStoreable s => divideByScore(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator /') + }; + + /// multiply by + @override + BinaryScoreOperation operator *(dynamic other) => switch (other) { + int val => multiplyByScore(Score.con(val)), + ScoreOperation s => multiplyByScore(s), + ScoreStoreable s => multiplyByScore(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator *') + }; + + /// adds a value to the score + BinaryScoreOperation add([int val = 1]) => BinaryScoreOperation( + this, + val >= 0 ? ScoreOperator.Addition : ScoreOperator.Subtraction, + Score.con(val.abs()), + ); + + /// subtracts a value from the score + BinaryScoreOperation subtract([int val = 1]) => BinaryScoreOperation( + this, + val >= 0 ? ScoreOperator.Subtraction : ScoreOperator.Addition, + Score.con(val.abs()), + ); + + /// gets the value of the score to work with it further + // TODO + Widget get() => For.of([this, Command('scoreboard players get $this')]); + + // binary operations + + /// adds another score to this one + BinaryScoreOperation addScore(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Addition, score); + + /// subtracts another score from this one + BinaryScoreOperation subtractScore(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Subtraction, score); + + /// sets this score to the result of the multiplication + BinaryScoreOperation multiplyByScore(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Multiplication, score); + + /// sets this score to the result of the division + BinaryScoreOperation divideByScore(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Division, score); + + /// sets this score to the remainder of the division + BinaryScoreOperation modulo(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Modulo, score); + + //TODO: Move Condition Operators here + + void build() { + print('test'); + } + + (Score, List) copy({ + Score? out, + ScoreBuilder? builder, + bool compact = false, + }) { + out ??= Score.tmp(); + + builder ??= ScoreBuilder(); + + return (out, [out, ...builder.compile(this, out: out)]); + } + + @override + ScoreOperation toScore({Score? out}) => + out == null ? this : BinaryScoreOperation.assign(out, this); + + //TODO: Multistep + @override + Widget get_assignable_right(Context context) => generate(context); +} + +sealed class ElementaryScoreOperation extends ScoreOperation {} + +final class IncrementScoreOperation extends ElementaryScoreOperation { + final Score score; + final int increment; + + IncrementScoreOperation(this.score, this.increment); + + @override + generate(Context context) => Command( + increment > 0 + ? 'scoreboard players add $score $increment' + : 'scoreboard players remove $score ${-increment}', + ); + + @override + String toString() => '$score += $increment'; +} + +final class SetScoreOperation extends ElementaryScoreOperation { + final Score score; + final int value; + + SetScoreOperation(this.score, this.value); + + @override + generate(Context context) => Command('scoreboard players set $score $value'); + + @override + String toString() => 'set $score = $value'; +} + +final class ResetScoreOperation extends ElementaryScoreOperation { + final Score score; + + ResetScoreOperation(this.score); + + @override + generate(Context context) => For.of([ + score, + Command('scoreboard players reset $score'), + ]); + + @override + String toString() => 'reset $score'; +} + +final class StoreScoreOperation extends ElementaryScoreOperation { + final ScoreAssignable left; + final Widget right; + final bool useSuccess; + + StoreScoreOperation(this.left, this.right, {this.useSuccess = false}); + + @override + Widget generate(Context context) => For.of([ + if (left is Score) left as Score, + Execute.internal_store_command( + left.get_assignable_left(), + right is ScoreStoreable + ? (right as ScoreStoreable).get_assignable_right(context) + : right.generate(context), + useSuccess, + ) + ]); + + @override + String toString() => [ + ' | store ${left.get_assignable_left()}', + '<<', + ' | $right', + ].join('\n'); + + @override + (Score, List) copy({ + Score? out, + ScoreBuilder? builder, + bool compact = false, + }) { + final (copyScore, ops) = switch ((left, compact)) { + (Score s, true) => s.copy(out: out, builder: builder, compact: compact), + _ => super.copy(out: out, builder: builder, compact: compact) + }; + return (copyScore, [StoreScoreOperation(copyScore, right), ...ops]); + } +} + +final class StoreConditionScoreOperation extends ElementaryScoreOperation { + final ScoreAssignable left; + final Condition right; + final bool useSuccess; + + StoreConditionScoreOperation(this.left, this.right, + {this.useSuccess = false}); + + @override + Widget generate(Context context) => For.of([ + if (left is Score) left as Score, + Command( + 'execute store ${useSuccess ? 'success' : 'result'} ${left.get_assignable_left()} ${Condition.getPrefixes(right.getList())[0]}', + ) + ]); + + @override + String toString() => [ + ' | store ${left.get_assignable_left()}', + '<<', + ' | $right', + ].join('\n'); + + @override + (Score, List) copy({ + Score? out, + ScoreBuilder? builder, + bool compact = false, + }) { + final (copyScore, ops) = switch ((left, compact)) { + (Score s, true) => s.copy(out: out, builder: builder, compact: compact), + _ => super.copy(out: out, builder: builder, compact: compact) + }; + return ( + copyScore, + [StoreConditionScoreOperation(copyScore, right), ...ops] + ); + } +} + +final class ElementaryBinaryScoreOperation extends ElementaryScoreOperation { + final Score left; + final Score right; + + final ScoreOperator operation; + + ElementaryBinaryScoreOperation( + this.left, + this.operation, + this.right, + ); + + ElementaryBinaryScoreOperation.assign(this.left, this.right) + : operation = ScoreOperator.Assign; + + @override + Widget generate(Context context) => + Command('scoreboard players operation $left ${operation.op} $right'); + + @override + String toString() => '$left ${operation.op} $right'; +} + +// final class AssignScoreOperation extends ElementaryScoreOperation { +// final Score left; +// final ScoreAssignable right; + +// AssignScoreOperation(this.left, this.right); + +// @override +// Widget generate(Context context) => Command( +// 'scoreboard players operation $left = ${right.get_assignable_right()}', +// ); + +// @override +// String toString() => [ +// ' | $left', +// '<<', +// ' | ${right.get_assignable_right()}', +// ].join('\n'); +// } + +class BinaryScoreOperation extends ScoreOperation { + final ScoreOperation left; + final ScoreOperation right; + + final ScoreOperator operation; + + BinaryScoreOperation(this.left, this.operation, this.right); + + BinaryScoreOperation.assign(this.left, this.right) + : operation = ScoreOperator.Assign; + + @override + Widget generate(Context context) { + final ScoreBuilder builder = ScoreBuilder(); + + final (rScore, rActions) = right.copy(compact: true, builder: builder); + + print(left); + + if (left is Score) { + return For.of([ + left, + ...rActions, + ...builder.compileBinary(left, operation, rScore), + ]); + } + + if (left case StoreScoreOperation(right: ScoreAssignable store)) { + final (lScore, lActions) = left.copy(compact: true, builder: builder); + return For.of([ + ...lActions, + ...rActions, + ...builder.compileBinary(lScore, operation, rScore), + store << lScore + ]); + } + + final (lScore, lActions) = left.copy(builder: builder); + + return For.of([ + ...lActions, + ...rActions, + ...builder.compileBinary(lScore, operation, rScore), + ]); + } + + @override + String toString() { + final lStr = left.toString().splitMapJoin('\n', onNonMatch: (s) => ' | $s'); + final rStr = + right.toString().splitMapJoin('\n', onNonMatch: (s) => ' | $s'); + + return [lStr, operation.op, rStr].join('\n'); + } +} + +enum ScoreOperator { + Addition("+="), + Subtraction("-="), + Multiplication("*="), + Division("/="), + Modulo("%="), + Assign("="), + Min("<"), + Max(">"), + Swap("><"); + + final String op; + const ScoreOperator(this.op); +} + +class ConstScore extends Score { + final int value; + + ConstScore( + this.value, { + bool addNew = true, + String type = 'dummy', + }) : super( + Entity.PlayerName( + '#$value', + ), + 'objd_consts', + addNew: addNew, + type: type, + ); +} + +class Score extends ElementaryScoreOperation + implements ScoreAssignable, ScoreStoreable { + final Entity entity; + String score; + final String type; + final bool addNew; + + /// The [Score] class is the basis for setting values, calculating with scores and checking the values. + ///It implements one base class with no functionality and several methods to do actions: + /// + ///|constructor| | + ///|--|--| + ///|Entity| the entity within the scoreboard | + ///|String| the name of the objective | + ///|addNew| bool whether it should add the scoreboard itself if it does not exist(default = true)| + /// + ///```dart + /// Score(Entity.Selected(),'score',addNew: true) + ///``` + + Score( + this.entity, + this.score, { + this.addNew = true, + this.type = 'dummy', + }) { + if (Scoreboard.prefix != null && !score.contains(Scoreboard.prefix!)) { + score = Scoreboard.prefix! + score; + } + } + + /// same as Score() but with a predefined entity(Entity.Selected(),) + /// ```dart + /// Score.fromSelected('objective').set(3) + /// ⇒ scoreboard players set @s objective 3 + /// ``` + factory Score.fromSelected( + String score, { + bool addNew = true, + String type = 'dummy', + }) => + Score(Entity.Self(), score, addNew: addNew, type: type); + + /// same as Score() but with a predefined entity(Entity.Selected(),) + /// ```dart + /// Score.fromSelected('objective').set(3) + /// ⇒ scoreboard players set @s objective 3 + /// ``` + factory Score.Self( + String score, { + bool addNew = true, + String type = 'dummy', + }) => + Score(Entity.Self(), score, addNew: addNew, type: type); + + /// Do you need constant values with scores? objD got you covered with `Score.con`: + /// + /// |Score.con| | + /// |--|--| + /// |int| a constant number | + /// |addNew|bool whether it should add objd_consts itself if it does not exist(default = true)| + + /// This will automatically create a scoreboard called `objd_consts` and set the value to the fake entity `#[value]` + /// + /// **Example:** + /// ```dart + /// Score.con(5) + /// ⇒ scoreboard players set #5 objd_consts 5 + /// ``` + static ConstScore con( + int number, { + bool addNew = true, + String type = 'dummy', + }) => + ConstScore(number, addNew: addNew, type: type); + + factory Score.PlayerName( + String name, + String score, { + bool addNew = true, + String type = 'dummy', + }) => + Score( + Entity.PlayerName(name), + score, + addNew: addNew, + type: type, + ); + + factory Score.tmp({ + int len = 8, + String score = "objd_temp", + String alphabet = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_', + }) => + Score( + Entity.PlayerName( + '#${Scoreboard.generateNewTempPlayerName(len: len, alphabet: alphabet)}', + ), + score, + ); + + @override + String toString({Entity? entity, String? score}) { + entity ??= this.entity; + score ??= this.score; + return '$entity $score'; + } + + @override + (Score, List) copy({ + Score? out, + ScoreBuilder? builder, + bool compact = false, + }) { + if (compact && out == null) return (this, []); + if (out != null) { + return (out, [out, ElementaryBinaryScoreOperation.assign(out, this)]); + } + return super.copy(out: out, builder: builder, compact: compact); + } + + /// sets the score to an nbt value + StoreScoreOperation setToData(DataGet data, {bool useSuccess = false}) => + StoreScoreOperation(this, data, useSuccess: useSuccess); + + /// set to functions return value(or number of commands) + StoreScoreOperation setToFunction(File file, {bool useSuccess = false}) => + setToWidget(file.run(create: true)); + + /// sets the score to the success of the given Command + StoreScoreOperation setToResult(Command commmand, + {bool useSuccess = false}) => + setToWidget(commmand, useSuccess: useSuccess); + + /// sets the score to the result of the given Widget + /// JUST one Command should be the input + StoreScoreOperation setToWidget(Widget widget, {bool useSuccess = false}) => + StoreScoreOperation(this, widget, useSuccess: useSuccess); + + /// sets the score to the success of the given condition result + StoreConditionScoreOperation setToCondition(Condition cond, + {bool useSuccess = false}) => + StoreConditionScoreOperation(this, cond, useSuccess: useSuccess); + + /// assign value(int, Score, Data or Condition) + @override + ScoreOperation setTo(dynamic other, {bool useSuccess = false}) => + switch (other) { + int val => set(val), + ScoreOperation s => setEqual(s), + DataGet s => setToData(s, useSuccess: useSuccess), + ScoreStoreable s => + StoreScoreOperation(this, s, useSuccess: useSuccess), + Condition c => setToCondition(c, useSuccess: useSuccess), + Widget w => setToWidget(w, useSuccess: useSuccess), + _ => + throw ('Please use either a Score, Data, Condition, Command or an Int in the operator >>, got $other'), + }; + + @override + ScoreOperation operator >>(dynamic other) => setTo(other); + @override + ScoreOperation operator <<(dynamic other) => setTo(other); + + /// sets the score to a given value of int + BinaryScoreOperation set(int val) { + // TODO: Remove, only necessary if used outside score + // if (this is! ScoreAssignable) { + // throw Exception('$val can not be assigned to left Hand Side $this'); + // } + + return BinaryScoreOperation(this, ScoreOperator.Assign, Score.con(val)); + } + + ResetScoreOperation reset() => ResetScoreOperation(this); + + /// sets this score equal to another + BinaryScoreOperation setEqual(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Assign, score); + + /// swaps two scores + BinaryScoreOperation swapWith(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Swap, score); + + /// compares two scores and sets the smallest to this one + BinaryScoreOperation setToSmallest(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Min, score); + + /// compares two scores and sets the biggest to this one + BinaryScoreOperation setToBiggest(ScoreOperation score) => + BinaryScoreOperation(this, ScoreOperator.Max, score); + + /// tests + + /// greater than + //@override + ScoreCondition operator >(dynamic other) => switch (other) { + int val => matchesRange(Range.from(val + 1)), + Score s => isBigger(s), + // ScoreStoreable s => isBigger(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator >') + }; + + /// less than + //@override + ScoreCondition operator <(dynamic other) => switch (other) { + int val => matchesRange(Range.to(val - 1)), + Score s => isSmaller(s), + //ScoreStoreable s => isSmaller(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator <') + }; + + /// bigger or equal + //@override + ScoreCondition operator >=(dynamic other) => switch (other) { + int val => matchesRange(Range.from(val)), + Score s => isBiggerOrEqual(s), + //ScoreStoreable s => isBiggerOrEqual(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator >=') + }; + + /// less or equal + //@override + ScoreCondition operator <=(dynamic other) => switch (other) { + int val => matchesRange(Range.to(val)), + Score s => isSmallerOrEqual(s), + //ScoreStoreable s => isSmallerOrEqual(s.toScore()), + _ => throw ('Please use either a Score or an Int in the operator <=') + }; + + /// matches + //@override + ScoreCondition operator &(dynamic other) => switch (other) { + int val => matches(val), + Range r => matchesRange(r), + Score s => isEqual(s), + //ScoreStoreable s => isEqual(s.toScore()), + _ => + throw ('Please use either a Score, Range or an Int in the operator &') + }; + + BinaryScoreCondition isEqual(Score score) => + BinaryScoreCondition(this, ConditionalOperator.Equal, score); + BinaryScoreCondition isSmaller(Score score) => + BinaryScoreCondition(this, ConditionalOperator.Less, score); + BinaryScoreCondition isSmallerOrEqual(Score score) => + BinaryScoreCondition(this, ConditionalOperator.LessEqual, score); + BinaryScoreCondition isBiggerOrEqual(Score score) => + BinaryScoreCondition(this, ConditionalOperator.BiggerEqual, score); + BinaryScoreCondition isBigger(Score score) => + BinaryScoreCondition(this, ConditionalOperator.Bigger, score); + + MatchesScoreCondition matches(int value) => + MatchesScoreCondition(this, Range.exact(value)); + MatchesScoreCondition matchesRange(Range range) => + MatchesScoreCondition(this, range); + + /// finds the smallest value in a list of scores + Widget findSmallest(List scores, {int? min}) { + return For( + to: scores.length - 1, + create: (int i) { + var ret = setToSmallest(scores[i]); + if (min != null) { + return If( + scores[i].matchesRange( + Range.from(min), + ), + then: [ret], + ); + } + return ret; + }); + } + + /// finds the biggest value in a list of scores + Widget findBiggest(List scores, {int? max}) { + return For( + to: scores.length - 1, + create: (int i) { + var ret = setToBiggest(scores[i]); + if (max != null) { + return If( + scores[i].matchesRange( + Range.to(max), + ), + then: [ret], + ); + } + return ret; + }); + } + + @override + Widget generate(Context context) { + return addNew ? Scoreboard(score, type: type) : Comment.Null(); + } + + @override + String get_assignable_left() => 'score $this'; + + @override + Widget get_assignable_right(Context context) => get(); + + @override + ScoreStoreable toStorable() => this; +} diff --git a/lib/src/basic/score/score_builder.dart b/lib/src/basic/score/score_builder.dart new file mode 100644 index 0000000..445b395 --- /dev/null +++ b/lib/src/basic/score/score_builder.dart @@ -0,0 +1,102 @@ +import 'package:objd/src/basic/score/score.dart'; + +class ScoreBuilder { + List compile(ScoreOperation input, {Score? out}) { + return switch (input) { + BinaryScoreOperation( + left: final left, + operation: final operation, + right: final right + ) => + compileBinary(left, operation, right, out: out), + IncrementScoreOperation(score: final score) => [ + input, + if (out != null) ElementaryBinaryScoreOperation.assign(out, score), + ], + SetScoreOperation(score: final score) => [ + input, + if (out != null) ElementaryBinaryScoreOperation.assign(out, score), + ], + ResetScoreOperation(score: final score) => [ + input, + if (out != null) ElementaryBinaryScoreOperation.assign(out, score), + ], + StoreConditionScoreOperation(left: final left) => [ + input, + if (out != null) StoreScoreOperation(out, left.toStorable()) + ], + StoreScoreOperation(left: final left) => [ + input, + if (out != null) StoreScoreOperation(out, left.toStorable()) + ], + ElementaryBinaryScoreOperation(left: final left) => [ + input, + if (out != null) ElementaryBinaryScoreOperation.assign(out, left) + ], + Score score => [ + score, + if (out != null) ElementaryBinaryScoreOperation.assign(out, score), + ], + }; + } + + List compileBinary( + ScoreOperation left, ScoreOperator operator, ScoreOperation right, + {Score? out}) { + if (operator == ScoreOperator.Assign) { + assert(left is ScoreAssignable, "Tried to assign $right to $left"); + final (tmpRight, prevactions) = right.copy(compact: true); + + if (left is Score) { + if (right is ConstScore) { + return [left, SetScoreOperation(left, right.value)]; + } + + return [ + left, + ...prevactions, + ElementaryBinaryScoreOperation.assign(left, tmpRight) + ]; + } + return [ + ...prevactions, + StoreScoreOperation(left as ScoreAssignable, tmpRight) + ]; + } + + var leftActions = compile(left, out: out); + if (out == null) { + if (left is Score) { + out ??= left; + } else { + (out, leftActions) = left.copy(); + } + } + + if (right is ConstScore) { + switch (operator) { + case ScoreOperator.Assign: + return [...leftActions, SetScoreOperation(out, right.value)]; + + case ScoreOperator.Addition: + return [...leftActions, IncrementScoreOperation(out, right.value)]; + case ScoreOperator.Subtraction: + return [...leftActions, IncrementScoreOperation(out, -right.value)]; + default: + } + } + + final (tmpRight, rightActions) = right.copy(compact: true); + + // other operators + return [ + ...leftActions, + ...rightActions, + ElementaryBinaryScoreOperation( + out, + operator, + tmpRight, + ) + ]; + } +} diff --git a/lib/src/basic/score/score_condition.dart b/lib/src/basic/score/score_condition.dart new file mode 100644 index 0000000..cce3576 --- /dev/null +++ b/lib/src/basic/score/score_condition.dart @@ -0,0 +1,63 @@ +// ignore_for_file: public_member_api_docs, sort_constructors_first +import 'package:objd/src/basic/score/score_export.dart'; +import 'package:objd/src/basic/types/condition.dart'; +import 'package:objd/src/basic/types/entity.dart'; + +sealed class ScoreCondition extends Condition { + final bool invert; + //TODO: refactor + // ignore: use_super_parameters + ScoreCondition._(String generated, {this.invert = false}) + // ignore: deprecated_member_use_from_same_package + : super.raw(generated, invert: invert); +} + +class MatchesScoreCondition extends ScoreCondition { + // TODO: Implement for general ScoreOperations, refactor of If necessary + final Score score; + final Range range; + + MatchesScoreCondition(this.score, this.range, {super.invert = false}) + : super._('score $score matches $range'); + + String getMatch() => range.toString(); + + @override + String toString() { + final lStr = + score.toString().splitMapJoin('\n', onNonMatch: (s) => ' | $s'); + final rStr = range.toString(); + + return [lStr, 'matches', rStr].join('\n'); + } +} + +class BinaryScoreCondition extends ScoreCondition { + // TODO: Implement for general ScoreOperations, refactor of If necessary + final Score left; + final ConditionalOperator operation; + final Score right; + + BinaryScoreCondition(this.left, this.operation, this.right) + : super._('score $left ${operation.op} $right'); + + @override + String toString() { + final lStr = left.toString().splitMapJoin('\n', onNonMatch: (s) => ' | $s'); + final rStr = + right.toString().splitMapJoin('\n', onNonMatch: (s) => ' | $s'); + + return [lStr, operation.op, rStr].join('\n'); + } +} + +enum ConditionalOperator { + Bigger(">"), + BiggerEqual(">="), + Equal("="), + LessEqual("<="), + Less("<"); + + final String op; + const ConditionalOperator(this.op); +} diff --git a/lib/src/basic/score/score_export.dart b/lib/src/basic/score/score_export.dart new file mode 100644 index 0000000..9377c17 --- /dev/null +++ b/lib/src/basic/score/score_export.dart @@ -0,0 +1,4 @@ +library score; + +export 'score.dart'; +export 'score_condition.dart'; diff --git a/lib/src/basic/scoreboard.dart b/lib/src/basic/scoreboard.dart index 0b7fa5a..2efad54 100644 --- a/lib/src/basic/scoreboard.dart +++ b/lib/src/basic/scoreboard.dart @@ -1,6 +1,8 @@ +import 'dart:math'; + +import 'package:objd/src/basic/score/score.dart'; import 'package:objd/src/basic/types/entity.dart'; import 'package:objd/src/basic/rest_action.dart'; -import 'package:objd/src/basic/score.dart'; import 'package:objd/src/basic/widget.dart'; import 'package:objd/src/basic/command.dart'; import 'package:objd/src/basic/text_components.dart'; @@ -8,11 +10,45 @@ import 'package:objd/src/basic/extend.dart'; import 'package:objd/src/build/build.dart'; import 'package:objd/src/wrappers/comment.dart'; +const _SCORE_PLAYERNAME_ALPHABET = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'; + class Scoreboard extends RestActionAble { /// Often you find yourself giving all scoreboards a prefix especially for your project. This can get very repetitive and annoying, so objD has this prefix built in. static String? prefix; + static Set _tempScores = {}; + static List? _override_playernames; + + static void overrideTempPlayerNames(List names) { + _override_playernames = names; + _tempScores = {}; + } + + static String generateNewTempPlayerName({ + int len = 8, + String alphabet = _SCORE_PLAYERNAME_ALPHABET, + }) { + var r = Random(); + String name = ''; + if (_override_playernames != null) { + assert(_override_playernames!.length > _tempScores.length, + "No more overrides left"); + name = _override_playernames![_tempScores.length]; + } else { + do { + name = List.generate( + len, + (index) => _SCORE_PLAYERNAME_ALPHABET[ + r.nextInt(_SCORE_PLAYERNAME_ALPHABET.length)], + ).join(); + } while (_tempScores.contains(name)); + } + _tempScores.add(name); + return name; + } + final String subcommand; String name; late String type; diff --git a/lib/src/basic/text_components.dart b/lib/src/basic/text_components.dart index a9676b3..45e22c2 100644 --- a/lib/src/basic/text_components.dart +++ b/lib/src/basic/text_components.dart @@ -2,12 +2,12 @@ import 'package:gson/values.dart'; import 'package:objd/src/basic/command.dart'; +import 'package:objd/src/basic/score/score.dart'; import 'package:objd/src/basic/types/entity.dart'; import 'dart:convert'; import 'package:objd/src/basic/types/item.dart'; import 'package:objd/src/basic/types/location.dart'; -import 'package:objd/src/basic/score.dart'; class TextComponent extends GsonValue { late Map value; diff --git a/lib/src/basic/types/condition.dart b/lib/src/basic/types/condition.dart index 26e54d1..7f702b8 100644 --- a/lib/src/basic/types/condition.dart +++ b/lib/src/basic/types/condition.dart @@ -1,10 +1,10 @@ +import 'package:objd/src/basic/score/score_export.dart'; import 'package:objd/src/basic/types/area.dart'; import 'package:objd/src/basic/types/biomes.dart'; import 'package:objd/src/basic/types/block.dart'; import 'package:objd/src/basic/types/entity.dart'; import 'package:objd/src/basic/types/location.dart'; import 'package:objd/src/basic/predicate.dart'; -import 'package:objd/src/basic/score.dart'; import 'package:objd/src/basic/tag.dart'; import 'package:objd/src/wrappers/data.dart'; import 'package:objd/src/wrappers/execute.dart'; @@ -15,6 +15,15 @@ class Condition { _ConditionType? _type; _ConditionUtil? _generated; + // ignore: library_private_types_in_public_api + + @Deprecated("Will be removed with Conditon refactor, not for public use") + Condition.raw(String generated, {bool invert = false}) + : _generated = _ConditionUtil( + generated, + invert: invert, + ); + /// The Condition class defines conditions for the if widget and more. It can also combines conditions and generates an argument list. /// A condition can accept many values and this makes the Condition very complex. /// @@ -156,10 +165,10 @@ class Condition { } if (cond is Score) { - if (cond.getString().isEmpty) { + if (cond.toString().isEmpty) { throw ('Please insert a score condition method into a condition!'); } - _generated = _ConditionUtil('score ${cond.getString()}', invert: invert); + _generated = _ConditionUtil('score ${cond.toString()}', invert: invert); return; } diff --git a/lib/src/basic/types/entity.dart b/lib/src/basic/types/entity.dart index b04a544..11dbda4 100644 --- a/lib/src/basic/types/entity.dart +++ b/lib/src/basic/types/entity.dart @@ -23,7 +23,7 @@ class Entity extends GsonValue implements EntityClass { Team? team, String? strNbt, Map? nbt, - List? scores, + List? scores, Range? level, Gamemode? gamemode, Area? area, @@ -65,7 +65,7 @@ class Entity extends GsonValue implements EntityClass { String? strNbt, Map? nbt, int? limit, - List? scores, + List? scores, Range? level, Gamemode? gamemode, Area? area, @@ -105,7 +105,7 @@ class Entity extends GsonValue implements EntityClass { String? strNbt, Map? nbt, int? limit, - List? scores, + List? scores, Range? level, Gamemode? gamemode, Area? area, @@ -144,7 +144,7 @@ class Entity extends GsonValue implements EntityClass { Team? team, String? strNbt, Map? nbt, - List? scores, + List? scores, Range? level, Gamemode? gamemode, Area? area, @@ -183,7 +183,7 @@ class Entity extends GsonValue implements EntityClass { String? strNbt, Map? nbt, Map? data, - List? scores, + List? scores, Range? level, Gamemode? gamemode, Area? area, @@ -227,7 +227,7 @@ class Entity extends GsonValue implements EntityClass { Team? team, String? strNbt, Map? nbt, - List? scores, + List? scores, Range? level, Gamemode? gamemode, Area? area, @@ -296,7 +296,7 @@ class Entity extends GsonValue implements EntityClass { int? limit, List? tags, Team? team, - List? scores, + List? scores, Map? nbt, String? strNbt, EntityType? type, @@ -337,7 +337,7 @@ class Entity extends GsonValue implements EntityClass { int? limit, List? tags, Team? team, - List? scores, + List? scores, Map? nbt, String? strNbt, EntityType? type, @@ -376,7 +376,7 @@ class Entity extends GsonValue implements EntityClass { int? limit, List? tags, Team? team, - List? scores, + List? scores, Map? nbt, String? strNbt, EntityType? type, @@ -456,7 +456,7 @@ class Entity extends GsonValue implements EntityClass { if (scores != null) { var ret = []; for (var score in scores) { - if (score.getMatch().isEmpty) { + if (score is! MatchesScoreCondition) { throw ('Please insert a match method in the scores value for an entity!'); } ret.add('${score.score}=${score.getMatch()}'); @@ -481,7 +481,7 @@ class Entity extends GsonValue implements EntityClass { int? limit, List? tags, Team? team, - List? scores, + List? scores, Map? nbt, String? strNbt, EntityType? type, @@ -544,7 +544,7 @@ class Entity extends GsonValue implements EntityClass { int? limit, List? tags, Team? team, - List? scores, + List? scores, Map? nbt, String? strNbt, EntityType? type, diff --git a/lib/src/basic/types/param.dart b/lib/src/basic/types/param.dart new file mode 100644 index 0000000..dc5c09f --- /dev/null +++ b/lib/src/basic/types/param.dart @@ -0,0 +1 @@ +class Param {} diff --git a/lib/src/basic/types/selector.dart b/lib/src/basic/types/selector.dart index 0c86191..0b81437 100644 --- a/lib/src/basic/types/selector.dart +++ b/lib/src/basic/types/selector.dart @@ -1,6 +1,6 @@ import 'package:gson/gson.dart'; import 'package:objd/src/basic/parsable.dart'; -import 'package:objd/src/basic/score.dart'; +import 'package:objd/src/basic/score/score_export.dart'; import 'package:objd/src/basic/types/area.dart'; import 'package:objd/src/basic/types/entity.dart'; import 'package:objd/src/basic/types/rotation.dart'; @@ -12,7 +12,7 @@ class Selector { int? limit; List? tags; Team? team; - List? scores; + List? scores; Map? nbt; String? strNbt; EntityType? type; @@ -151,13 +151,12 @@ class Selector { /// Copy a selector Selector.clone(Selector s) { - List? scores; + List? scores; if (s.scores != null) { scores = []; - for (var score in s.scores!) { + for (var cond in s.scores!) { scores.add( - Score(score.entity, score.score, - addNew: false, commands: score.commands), + MatchesScoreCondition(cond.score, cond.range), ); } } diff --git a/lib/src/basic/widgets.dart b/lib/src/basic/widgets.dart index b43d0b5..03f88d2 100644 --- a/lib/src/basic/widgets.dart +++ b/lib/src/basic/widgets.dart @@ -5,7 +5,7 @@ export 'command_builder.dart'; export 'pack.dart'; export 'group.dart'; export 'scoreboard.dart'; -export 'score.dart'; +export 'score/score_export.dart'; export 'tag.dart'; export 'text_components.dart'; export 'file.dart'; diff --git a/lib/src/utils/log.dart b/lib/src/utils/log.dart index b811bbc..4f6726a 100644 --- a/lib/src/utils/log.dart +++ b/lib/src/utils/log.dart @@ -1,6 +1,6 @@ +import 'package:objd/src/basic/score/score.dart'; import 'package:objd/src/basic/types/entity.dart'; import 'package:objd/src/basic/rest_action.dart'; -import 'package:objd/src/basic/score.dart'; import 'package:objd/src/basic/text_components.dart'; import 'package:objd/src/basic/widget.dart'; import 'package:objd/src/build/context.dart'; diff --git a/lib/src/utils/random_score.dart b/lib/src/utils/random_score.dart index 34435cc..e6bc848 100644 --- a/lib/src/utils/random_score.dart +++ b/lib/src/utils/random_score.dart @@ -36,13 +36,14 @@ class RandomScore extends RestActionAble { children: [ Comment('Random UUID Generator from ${context.file}'), AreaEffectCloud(Location.here(), tags: ['objd_random']), - s[entity].setToWidget( - Data.get( - Entity(tags: ['objd_random'], limit: 1).sort(Sort.nearest), - path: context.version > 15 ? 'UUID[0]' : 'UUIDMost', - scale: (context.version > 15 ? 1000 : 1) * 0.0000000001, - ), - ), + // TODO: Add back in + // s[entity].setToWidget( + // Data.get( + // Entity(tags: ['objd_random'], limit: 1).sort(Sort.nearest), + // path: context.version > 15 ? 'UUID[0]' : 'UUIDMost', + // scale: (context.version > 15 ? 1000 : 1) * 0.0000000001, + // ), + // ), s[entity].modulo( s['#max'], ), diff --git a/lib/src/utils/storage.dart b/lib/src/utils/storage.dart index 3b178f9..782e270 100644 --- a/lib/src/utils/storage.dart +++ b/lib/src/utils/storage.dart @@ -1,14 +1,14 @@ import 'package:objd/core.dart'; //// The Storage Widget gives you easy tools to store and receive nbt data globally. -class Storage extends Widget { +class Storage extends Widget with ScoreAssignable, ScoreStoreable { _StorageType? _type; final String name; final bool autoNamespace; Map? nbt; String? key; DataModify? _modify; - Data? data; + DataGet? data; Score? score; double? scale; String? datatype; @@ -23,6 +23,9 @@ class Storage extends Widget { Storage( this.name, { this.autoNamespace = true, + this.key, + this.datatype, + this.scale, }); /// Here you can set one key to a specific value. @@ -76,10 +79,8 @@ class Storage extends Widget { this.name, { this.autoNamespace = true, required this.key, - required Data data, + required DataGet data, }) : assert(key != null), - assert(data.subcommand == 'get', - 'You have to insert a Data.get into copyData!'), // ignore: prefer_initializing_formals data = data, _type = _StorageType.data; @@ -136,7 +137,7 @@ class Storage extends Widget { ///Copies Nbt Data from a **Data.get** Widget. Storage copyData( String key, { - required Data data, + required DataGet data, }) => Storage.copyData( name, @@ -195,6 +196,42 @@ class Storage extends Widget { @override Widget generate(Context context) => toData(); + + @override + Widget setTo(dynamic other, {BossbarOption option = BossbarOption.value}) { + if (other is ScoreStoreable) { + return StoreScoreOperation(this, other); + } + + return set(name, other); + } + + @override + String get_assignable_left() { + assert( + key != null, + "You must to provide a key to assign to storage", + ); + return 'storage $name $key ${datatype ?? "byte"} ${scale ?? 1}'; + } + + @override + Data get_assignable_right(Context context) { + assert( + _type == _StorageType.get, + "Only Storage.get is allowed", + ); + return toData(); + } + + @override + ScoreStoreable toStorable() { + assert( + key != null, + "You must to provide a key to retrieve data from storage", + ); + return get(key!); + } } enum _StorageType { diff --git a/lib/src/wrappers/bossbar.dart b/lib/src/wrappers/bossbar.dart index 01188eb..368334b 100644 --- a/lib/src/wrappers/bossbar.dart +++ b/lib/src/wrappers/bossbar.dart @@ -4,7 +4,7 @@ import 'package:objd/src/basic/widgets.dart'; import 'package:objd/src/build/context.dart'; import 'package:objd/src/wrappers/execute.dart'; -class Bossbar extends RestActionAble { +class Bossbar extends RestActionAble with ScoreAssignable, ScoreStoreable { final BossbarType type; final String id; @@ -16,6 +16,14 @@ class Bossbar extends RestActionAble { Bossbar(this.id, {this.name, this.type = BossbarType.add}) { name ??= id; } + Bossbar._( + this.id, { + this.name, + this.type = BossbarType.add, + //this.nameTexts, + this.option, + //this.modifiers = const {}, + }) : modifiers = const {}; Bossbar remove() => copyWith(type: BossbarType.remove); @@ -55,7 +63,7 @@ class Bossbar extends RestActionAble { } @override - Widget? generate(Context context) { + Widget generate(Context context) { switch (type) { case BossbarType.add: return Command('bossbar add $id {"text":"$name"}'); @@ -74,8 +82,6 @@ class Bossbar extends RestActionAble { }); return For.of(widgets); } - default: - return null; } } @@ -110,14 +116,43 @@ class Bossbar extends RestActionAble { Bossbar copyWith({ BossbarType? type, + BossbarOption? option, String? id, }) { - return Bossbar( + return Bossbar._( id ?? this.id, name: name, type: type ?? this.type, + option: option, + ); + } + + @override + Widget setTo(dynamic other, {BossbarOption option = BossbarOption.value}) { + print(other); + if (other is int) return set(value: other); + if (other is ScoreStoreable) { + return StoreScoreOperation(copyWith(option: option), other); + } + + throw UnimplementedError("setTo is not implemented for $other"); + } + + @override + String get_assignable_left() => + 'bossbar $id ${(option ?? BossbarOption.value).name}'; + + @override + Widget get_assignable_right(Context context) { + assert( + type == BossbarType.get, + "You can only assign Bossbar.get to something else", ); + return generate(context); } + + @override + ScoreStoreable toStorable() => get(BossbarOption.value); } enum BossbarOption { max, players, value, visible } diff --git a/lib/src/wrappers/data.dart b/lib/src/wrappers/data.dart index 855855c..72db5ce 100644 --- a/lib/src/wrappers/data.dart +++ b/lib/src/wrappers/data.dart @@ -19,7 +19,6 @@ class Data extends RestActionAble { String get type => _type; String get typeValue => _typeValue; - bool get isGetting => subcommand == 'get'; /// The Data Widgets allows you to edit nbt data of Entities or Blocks. /// ```dart @@ -32,7 +31,8 @@ class Data extends RestActionAble { /// ) /// ⇒ data merge entity @s {'Invisible':1,'NoGravity':1} /// ``` - Data(this.target, {this.nbt = const {}, String type = 'merge'}) + Data(this.target, + {this.nbt = const {}, String type = 'merge', this.path = '', this.scale}) : _subcommand = type { handleTarget(target); } @@ -40,14 +40,17 @@ class Data extends RestActionAble { : _subcommand = 'merge' { handleTarget(target); } - Data.get(this.target, {required this.path, this.scale}) - : _subcommand = 'get' { - handleTarget(target); - } Data.remove(this.target, {required this.path}) : _subcommand = 'remove' { handleTarget(target); } + static DataGet get(dynamic target, {required String path, num? scale}) => + DataGet( + target, + path: path, + scale: scale, + ); + /// You can also convert a score directly to a nbt field with Data.fromScore: /// /// |Data.fromScore| | @@ -122,15 +125,7 @@ class Data extends RestActionAble { case 'merge': return Command('data merge ${getTarget(context)} ${_getNbt()}'); case 'get': - final cmd = ['data get', getTarget(context), path]; - - if (scale != null) { - cmd.add( - scale! < 0.000001 ? scale!.toStringAsFixed(10) : scale.toString(), - ); - } - - return Command(cmd.join(' ')); + throw Deprecated("Moved to DataGet"); case 'remove': return Command('data remove ${getTarget(context)} $path'); case 'modify': @@ -149,6 +144,23 @@ class Data extends RestActionAble { } } +class DataGet extends Data with ScoreStoreable { + DataGet(super.target, {required super.path, super.scale}); + + @override + Command generate(Context context) { + final cmd = ['data get', getTarget(context), path]; + + if (scale != null) { + cmd.add( + scale! < 0.000001 ? scale!.toStringAsFixed(10) : scale.toString(), + ); + } + + return Command(cmd.join(' ')); + } +} + /// There are five sub operations again: set, merge, prepend, append and insert. class DataModify { String type; diff --git a/lib/src/wrappers/execute.dart b/lib/src/wrappers/execute.dart index ee6e6c3..27035eb 100644 --- a/lib/src/wrappers/execute.dart +++ b/lib/src/wrappers/execute.dart @@ -405,7 +405,7 @@ class Execute extends RestActionAble { /// just for internal use static Group internal_store_command(String type, Widget w, bool useSuccess) => Group( - prefix: 'execute store ${useSuccess ? 'success ' : 'result '}$type run', + prefix: 'execute store ${useSuccess ? 'success' : 'result'} $type run', groupMin: 1000, children: [w], ); diff --git a/test/data_test.dart b/test/data_test.dart index 468b064..4cc6678 100644 --- a/test/data_test.dart +++ b/test/data_test.dart @@ -10,6 +10,14 @@ void main() { Data.get(Location.here(), path: "test"), "data get block ~ ~ ~ test", ); + commands( + 'assign to score', + Score(Entity.All(), "test") << Data.get(Location.here(), path: "test"), + [ + 'scoreboard objectives add test dummy', + "execute store result score @a test run data get block ~ ~ ~ test" + ], + ); command( 'simple merge', diff --git a/test/score_test.dart b/test/score_test.dart index 5dca23e..f9d7ca2 100644 --- a/test/score_test.dart +++ b/test/score_test.dart @@ -7,6 +7,14 @@ void main() { final s1 = Score(Entity.All(), "test"); group('Score', () { + test('temp', () { + // mock temp name generation + Scoreboard.overrideTempPlayerNames(["mock1"]); + expect( + Score.tmp().toString(), + "#mock1 objd_temp", + ); + }); test('prefixing', () { Scoreboard.prefix = 'mock_'; final s = Score.fromSelected('test'); @@ -124,17 +132,23 @@ void main() { ]); commands('setTo Data', s1 << Data.get(Entity.Self(), path: "test"), [ "scoreboard objectives add test dummy", - "execute store result score @a test run data get entity @s test 1" + "execute store result score @a test run data get entity @s test" ]); commands( 'setTo File', s1 << File("test"), - ["execute store result score @a test run function :test"], + [ + "scoreboard objectives add test dummy", + "execute store result score @a test run function :test" + ], ); commands( 'setTo Widget', s1 << Say("mock"), - ["execute store result score @a test run say mock"], + [ + "scoreboard objectives add test dummy", + "execute store result score @a test run say mock" + ], ); commands( 'setTo Condition', @@ -146,12 +160,22 @@ void main() { ); }); + group("Score Bossbar", () { + Scoreboard.overrideTempPlayerNames(["mock1"]); + commands('add', Bossbar("test").get(BossbarOption.value) + 1, [ + "scoreboard objectives add objd_temp dummy", + "execute store result score #mock1 objd_temp run bossbar get test value", + "scoreboard players add #mock1 objd_temp 1", + "execute store result bossbar test value run scoreboard players get #mock1 objd_temp" + ]); + }); + group('Score Conditions', () { - commands( - "empty", - s1.isBigger(Score(Entity.Self(), "new")), - [], - ); + // commands( + // "empty", + // s1.isBigger(Score(Entity.Self(), "new")), + // [], + // ); command( "simple if", If(