#[no_std, cache_output] extract import "control_flow.spwn" extract import "constants.spwn".comparisons DEFAULT_BITS = 10 type @counter impl @counter { new: #[desc("Creates a new counter") example(" @counter::new() // creates a new counter with a starting value of 0 @counter::new(10) // creates a new counter with a starting value of 10 @counter::new(5i) // creates a new counter thaat uses item ID 5 @counter::new(true) // creates a new counter with a starting value of true (1) ")] ( #[desc("Source (can be a number, item ID or boolean)")] source: @number | @item | @bool = 0, #[desc( "Defines the maximum stable size of the counter. If the counter goes outside of the range from 0 to 2^size, it's behavior will be undefined. Smaller sizes are more group effective." )] bits: @number = DEFAULT_BITS ){ if source.type == @number { id = ?i if source != 0 { id.add(source) } -> return @counter::{ item: id, bits } } else if source.type == @item { -> return @counter::{ item: source, bits } } else if source.type == @bool { if bits != DEFAULT_BITS && bits != 1 { throw "A boolean counter can only have size = 1" } id = ?i if source { id.add(1) } -> return @counter::{ item: id, bits: 1, } } } } impl @counter { display: #[desc("Creates a item display object that displays the value of the counter") example(" points = counter(0) points.display(75, 75) ")] ( self, #[desc("X pos of display in units (1 grid square = 30 units)")] x: @number, #[desc("Y pos of display in units")] y: @number ) { extract import "constants.spwn".obj_props $.add(obj { OBJ_ID: 1615, X: x, Y: y, ITEM: self.item, COLOR: 1c, GROUPS: 999g }); }, add_to: #[desc("Adds the counter's value to a counter (or all counters in a list), and resets the counter to 0 in the process") example(" a = counter(100) b = counter(0) wait(1) a.add_to(b) // a is now 0, b is now 100 ")] ( self, #[desc("Counter(s) to add to")] items: [@counter | @item] | @counter | @item, #[desc("Multiplier for the value added")] factor: @number = 1, #[desc("Macro to be called for each decrease of the counter. Takes one argument representing the number the counter is being decreased by")] for_each: @macro = (n){} ) { for i in self.bits..0 { x = 2^i -> if self >= x { self -= x if items.type == @array { for t in items { t.add(x * factor) } } else { items.add(x * factor) } for_each(x) } } }, add_to_multifactor: #[desc("Like normal add_to, but each counter has its own factor")] ( self, #[desc("Counter(s) to add to")] items: [[@counter | @number]], ) { for i in self.bits..0 { x = 2^i -> if self >= x { self -= x for t in items { t[0].add(x * t[1]) } } } }, subtract_from: #[desc("Subtracts the counter's value from another counter (or all counters in a list), and resets the counter to 0 in the process") example(" a = counter(100) b = counter(70) wait(1) b.subtract_from(a) // a is now 30, b is now 0 ")] ( self, #[desc("Counter(s) to subtract from")] items: [@counter | @item] | @counter | @item, #[desc("Multiplier for the value subtracted")] factor: @number = 1, #[desc("Macro to be called for each decrease of the counter. Takes one argument representing the number the counter is being decreased by")] for_each: @macro = (n){} ) { for i in self.bits..0 { x = 2^i -> if self >= x { self -= x if items.type == @array { for t in items { t.add(-x * factor) } } else { items.add(-x * factor) } for_each(x) } } }, multiply: #[desc("Multiplies the value of the counter by some factor (does not consume the factor)") example(" c = counter(5) wait(1) c.multiply(10) // c is now 50 ")] ( self, #[desc("Factor to multiply by, either another counter (very expensive) or a normal number")] factor: @counter | @number, ) { if factor.type == @number { temp = @counter::new(0, bits = self.bits) self.add_to([temp.item], factor) temp.add_to([self.item]) } else if factor.type == @counter { wait() result = @counter::new(0, bits = self.bits) for i in self.bits..0 { x = 2^i -> if self >= x { self -= x factor.copy_to(result, factor = x) } } result.add_to(self) } }, divide: #[desc("Devides the value of the counter by some divisor") example(" c = counter(7) r = counter(0) wait(1) c.divide(2, remainder = r) // c is now 3, r is now 1 ")] ( self, #[desc("Divisor to divide by, either another counter (very expensive) or a normal number")] divisor: @counter | @number, #[desc("Counter or item to set to the remainder value")] remainder: @counter | @item = @counter::new(), ) { if divisor.type == @number { result = @counter::new(0, bits = self.bits) for i in self.bits..0 { num = 2^i -> if self >= divisor * num { self.add(-divisor * num) result.add(num) } } self.add_to(remainder) result.add_to(self) } else if divisor.type == @counter { wait() result = @counter::new(0, bits = self.bits) broken = @counter::new(false) if divisor.bits > 8 { throw "Divisor max size (8 bits) exceeded" } -> if broken == 1 { broken -= 1 } for i in self.bits..0 { -> if broken == 0 { num = 2^i self.item.if_is(LARGER_THAN, 0, !{ divisor.copy_to(self, factor = -num) result.add(num) }) -> if self == 0 { broken += 1 } -> if self < 0 { divisor.copy_to(self, factor = num) result.add(-num) } } } -> if broken == 0 { -> if self < 0 { divisor.copy_to(self) result.add(-1) broken += 1 } } self.add_to(remainder) result.add_to(self) } }, //will consume both numbers compare: #[desc("Returns 0 if both counters are equal, 1 if the other is smaller, and -1 if the other is greater.") example(" c1 = counter(10) c2 = counter(15) cmp = c1.compare(c2) // -1 // c1 is now -5, c2 is now 0 ")](self, other: @counter, factor: @number = 1) { comp = @counter::new(0, bits = $.max(self.bits, other.bits)) self.copy_to(comp) other.copy_to(comp, factor = -1) comp.item.if_is(EQUAL_TO, 0, !{ -> return 0 }) comp.item.if_is(LARGER_THAN, 0, !{ comp.reset() -> return 1 }) comp.item.if_is(SMALLER_THAN, 0, !{ comp.reset_negative() -> return -1 }) }, reset: #[desc("Resets counter to 0.") example(" c = counter(100) wait(1) c.reset() // c is now 0 ")] ( self, #[desc("Macro to be called for each decrease of the counter. Takes one argument representing the number the counter is being decreased by")] for_each: @macro = (n){} ){ for i in self.bits..0 { x = 2^i -> if self >= x { self -= x for_each(x) } } }, reset_negative: #[desc("Resets a negative counter to 0") example(" c = counter(-100) wait(1) c.reset_negative() // c is now 0 ")] ( self, #[desc("Macro to be called for each decrease of the counter. Takes one argument representing the number the counter is being decreased by")] for_each: @macro = (n){} ){ for i in self.bits..0 { x = 2^i -> if self <= -x { self += x for_each(x) } } }, copy_to: #[desc("Copies the value of the counter to another counter (or to all counters in a list), without consuming the original") example(" c1 = counter(100) c2 = counter(0) wait(1) c1.copy_to(c2) // both counters are now 100 ")] ( self, #[desc("Counter(s) to copy to")] items: [@counter | @item] | @counter | @item, #[desc("Factor of to multiply the copy by")] factor: @number = 1 ) { temp_storage = @counter::new(0, bits = self.bits) for i in self.bits..0 { x = 2^i -> if self >= x { self -= x temp_storage += x if items.type == @array { for t in items { t.add(x * factor) } } else { items.add(x * factor) } } } temp_storage.add_to(self) }, clone: #[desc("Copies the counter and returns the copy") example(" c1 = counter(100) c2 = c1.clone() // c1 and c2 are now 100 ")] ( self ) { new_counter = @counter::new(0, bits = self.bits) self.copy_to([new_counter]) return new_counter }, _plus_: #[desc("Implementation of the plus (`+`) operator") example(" c = counter(10) c2 = c1 + 10 // c2 is 20 ")] (self, other: @number | @counter) { if other.type == @number { new_counter = self.clone() new_counter.add(other) -> return new_counter } else if other.type == @counter { new_counter = self.clone() other.copy_to([new_counter.item], factor = 1) -> return new_counter } }, _minus_: #[desc("Implementation of the minus (`-`) operator") example(" c = counter(10) c2 = c1 - 3 // c2 is 7 ")] (self, other: @number | @counter) { if other.type == @number { new_counter = self.clone() new_counter.add(-other) return new_counter } else if other.type == @counter { new_counter = self.clone() other.copy_to([new_counter.item], factor = -1) return new_counter } }, _times_: #[desc("Implementation of the times (`*`) operator") example(" c = counter(10) c2 = c1 * 10 // c2 is 100 ")] (self, num: @number | @counter) { if other.type == @number { new = @counter::new(0, self.bits) self.copy_to(new, factor = other) return new } else if other.type == @counter { new = self.clone() new.multiply(other) return new } }, _divided_by_: #[desc("Implementation of the divided by (`/`) operator") example(" c = counter(100) c2 = c1 / 10 // c2 is 10 ")] (self, num: @number | @counter) { clone = self.clone() clone.divide(num) -> return clone }, _mod_: #[desc("Implementation of the modulus (`%`) operator") example(" c = counter(42) c2 = c1 % 10 // c2 is 2 ")] (self, num: @number | @counter) { clone = self.clone() out = @counter::new() clone.divide(num, remainder = out) -> return out }, _modulate_: #[desc("Implementation of the modulate (`%=`) operator") example(" c = counter(42) c %= 10 // c is 2 ")] (self, num: @number | @counter) { out = @counter::new() self.divide(num, remainder = out) self.reset() out.add_to([self]) }, _more_than_: #[desc("Implementation of the more than (`>`) operator") example(" c = counter(42) more = c > 10 // more is now true ")] (self, other: @number | @counter) { if other.type == @number { self.item.if_is(LARGER_THAN, other, !{ -> return true }) self.item.if_is(SMALLER_THAN, other + 1, !{ -> return false }) } else if other.type == @counter { cmp = self.compare(other) return cmp == 1 } }, _less_than_: #[desc("Implementation of the less than (`<`) operator") example(" c = counter(42) less = c < 42 // less is now false ")] (self, other: @number | @counter) { if other.type == @number { self.item.if_is(SMALLER_THAN, other, !{ -> return true }) self.item.if_is(LARGER_THAN, other - 1, !{ -> return false }) } else if other.type == @counter { cmp = self.compare(other) -> return cmp == -1 } }, _more_or_equal_: #[desc("Implementation of the more than or equal (`>=`) operator") example(" c = counter(42) more_or_eq = c >= 10 // more_or_eq is now true ")] (self, other: @number | @counter) { if other.type == @number { self.item.if_is(LARGER_THAN, other - 1, !{ -> return true }) self.item.if_is(SMALLER_THAN, other, !{ -> return false }) } else if other.type == @counter { cmp = self.compare(other) -> return cmp == 1 || cmp == 0 } }, _less_or_equal_: #[desc("Implementation of the less than or equal (`<=`) operator") example(" c = counter(42) less_or_eq = c <= 42 // less_or_eq is now true ")] (self, other: @number | @counter) { if other.type == @number { self.item.if_is(SMALLER_THAN, other + 1, !{ -> return true }) self.item.if_is(LARGER_THAN, other, !{ -> return false }) } else if other.type == @counter { cmp = self.compare(other) -> return cmp == -1 || cmp == 0 } }, _equal_: #[desc("Implementation of the equals (`==`) operator") example(" c = counter(42) eq = c == 42 // eq is now true ")] (self, other: @number | @counter) { if other.type == @number { self.item.if_is(EQUAL_TO, other, !{ -> return true }) ret_false = !{ -> return false } self.item.if_is(LARGER_THAN, other, ret_false) self.item.if_is(SMALLER_THAN, other, ret_false) } else if other.type == @counter { cmp = self.compare(other) -> return cmp == 0 } }, _not_equal_: #[desc("Implementation of the not equal (`!=`) operator") example(" c = counter(42) not_eq = c != 42 // not_eq is now false ")](self, other: @number | @counter) { -> return !(self == other) }, add: #[desc("Implementation of the pickup trigger") example(" c = counter(10) c.add(10) // c is now 20 ")] (self, #[desc("Amount to add")] num: @number) { self.item.add(num) }, _add_: #[desc("Implementation of the add (`+=`) operator") example(" c = counter(10) c += 10 // c is now 20 ")](self, num: @number | @counter) { if num.type == @number { self.add(num) } else if num.type == @counter { num.copy_to(self) //holy shit why was this not like this all along } }, _increment_: #[desc("Implementation of the increment (`n++`) operator. Does not return any value.") example(" c = counter(10) c++ // c is now 11 ")](self) { self.add(1) }, _decrement_: #[desc("Implementation of the decrement (`n--`) operator. Does not return any value.") example(" c = counter(10) c-- // c is now 9 ")](self) { self.add(-1) }, _subtract_: #[desc("Implementation of the subtract (`-=`) operator") example(" c = counter(20) c -= 5 // c is now 15 ")](self, num: @number | @counter) { if num.type == @number { self.add(-num) } else if num.type == @counter { num.copy_to(self, factor = -1) } }, _multiply_: #[desc("Implementation of the multiply (`*=`) operator") example(" c = counter(5) c *= 6 // c is now 30 ")](self, num: @number | @counter) { if num.type == @number { self.multiply(num) } else if num.type == @counter { self.multiply(num) } }, _divide_: #[desc("Implementation of the divide (`/=`) operator") example(" c = counter(30) c /= 6 // c is now 5 ")](self, num: @number | @counter) { if num.type == @number { self.divide(num) } else if num.type == @counter { self.divide(num) } }, _assign_: #[desc("Implementation of the assign (`=`) operator") example(" c = counter(23) c = 42 // c is now 42 ")](self, num: @number | @counter) { self.reset() if num.type == @number { if num > 0 { self.add(num) } } else if num.type == @counter { num.copy_to(self) } }, _swap_: #[desc("Implementation of the swap (`<=>`) operator") example(" c = counter(23) c2 = counter(42) wait(1) c <=> c2 // c is now 42, c2 is now 23 ")] (self, num: @counter) { swap_tmp = @counter::new(); self.add_to(swap_tmp) num.add_to(self) swap_tmp.add_to(num) }, to_const: #[desc("Converts the counter into a normal number (very context-splitting, be careful)") example(" c = counter(3) wait(1) 10g.move(c.to_const(0..10) * 10, 0, 1) // group ID 10 moves 3 blocks ")] ( self, #[desc("Array or range of possible output values")] range: [@number] | @range ) { for val in range { -> self.item.if_is(EQUAL_TO, val, !{ -> return val }) } }, to_const_enclosed: #[desc("Converts the counter into a normal number that you can use within a macro") example(" c = counter(3) wait(1) c.to_const_enclosed(0..10, (c_const) { 10g.move(c_const * 10, 0, 1) }) // group ID 10 moves 3 blocks ")] ( self, #[desc("Array or range of possible output values")] range: [@number] | @range, #[desc("Closure where you can use the const value, should take the value as the first argument")] closure: @macro, ) { for val in range { -> self.item.if_is(EQUAL_TO, val, !{ closure(val) }) } }, _as_: #[desc("Implementation of the as (`as`) operator") example(" c = counter(1) b = c as @bool // b is now true ")] (self, _type: @type_indicator) { if _type == @bool { -> return self != 0 } else { throw "Cannot convert counter to " + _type as @string + " (counter can convert to a number using the counter.to_const macro)" } }, reaches: #[desc("Returns an event for when the counter reaches a certain value") example(" c = counter(2) on(c.reaches(10), !{ BG.pulse(0, 255, 0, fade_out = 0.5) // will pulse each time the counter becomes 10 }) ")] (self, #[desc("Value to reach")] value: @number) { return self.item.count(value) } }