spwn 0.0.6

A language for Geometry Dash triggers
Documentation
#[no_std, cache_output]
constants = import "constants.spwn"

extract constants.obj_props
extract constants.comparisons
extract constants.easing_types
extract import "control_flow.spwn"
extract import "util.spwn"
extract import "general_triggers.spwn"

impl @group {

    _range_: #[desc("Implementation of the range operator (`..`) for groups") example("
for group in 1g..10g {
    -> group.move(10, 0, 0.5)
}
    ")] create_range_macro(@group),
    move: #[desc("Implementation of the move trigger") example("10g.move(100, 0, 0.5, easing = EASE_IN_OUT)")]
    (
        self,
        #[desc("Units to move on the X axis (10 units per grid square)")] x: @number,
        #[desc("Units to move on the Y axis (10 units per grid square)")] y: @number,
        #[desc("Duration of movement")] duration: @number = 0,
        easing: @easing_type = NONE,
        easing_rate: @number = 2
    ) {
        $.add( trigger{
            OBJ_ID: 901,
            TARGET: self,
            MOVE_X: x * 3,
            MOVE_Y: y * 3,
            DURATION: duration,
            EASING: easing.id,
            EASING_RATE: easing_rate,
        })

        wait(duration)
    },

    precise_move: #[desc("Combines a move trigger with a follow trigger to allow for more precise decimal movement (up to 2 decimal places)")
    example("10g.precise_move(50.45,-15.23,0.5, easing = EASE_IN_OUT)")]
    (
        self,
        #[desc("Units to move on the X axis (10 units per grid square)")] x: @number,
        #[desc("Units to move on the Y axis (10 units per grid square)")] y: @number,
        #[desc("Duration of movement")] duration: @number = 0,
        easing: @easing_type = NONE,
        easing_rate: @number = 2,
        #[desc("Saves groups and objects if the group only contains one object")] single: @bool = false,
    ) {
        if single {
            self.follow(self,x_mod=-1+x,y_mod=-1+y,duration = duration)
            self.move(1,1,duration=duration,easing=easing,easing_rate=easing_rate)
        } else {
            target = ?g
            $.add(obj {
                OBJ_ID: 1765,
                X: 0,
                Y: 80*30,
                GROUPS: target,
            }, true)
            $.add( alpha_trigger(target,0).with(X,0).with(Y,75*30), true )
            self.follow(target,x_mod=x,y_mod=y,duration = duration)
            target.move(1,1,duration=duration,easing=easing,easing_rate=easing_rate)
        }
    },

    lock_to_player: #[desc("Lock group to player position") example("10g.lock_to_player(lock_x = true, duration = 20)")]
    (
        self,
        #[desc("Lock to player X")] lock_x: @bool = true,
        #[desc("Lock to player Y")] lock_y: @bool = true,
        #[desc("Duration of lock")] duration: @number = 999,
    ) {
        $.add( trigger{
            OBJ_ID: 901,
            TARGET: self,
            DURATION: duration,
            LOCK_TO_PLAYER_X: lock_x,
            LOCK_TO_PLAYER_Y: lock_y,
        })
    },

    stop: #[desc("Implementation of the stop trigger") example("
move = !{
    10g.move(1000, 0, 10)
}
move!
wait(2)
move.start_group.stop()
    ")]
    (self){
        $.add( trigger{
            OBJ_ID: 1616,
            TARGET: self,
        })
    },

    alpha: #[desc("Implementation of the alpha trigger") example("1g.alpha(0)")]
    (self, opacity: @number = 1, duration: @number = 0){
        $.add( trigger {
            OBJ_ID: 1007,
            TARGET: self,
            OPACITY: opacity,
            DURATION: duration,
        })
        wait(duration)
    },

    toggle_on:
    #[desc("Toggles the group on") example("10g.toggle_on()")]
    (self){
        $.add( trigger{
            OBJ_ID: 1049,
            TARGET: self,
            ACTIVATE_GROUP: true,
        })
    },

    toggle_off: #[desc("Toggles the group off") example("10g.toggle_off()")]
    (self){
        $.add( trigger{
            OBJ_ID: 1049,
            TARGET: self,
            ACTIVATE_GROUP: false,
        })
    },

    rotate: #[desc("Implementation of the rotate trigger") example("
center = 3g
10g.rotate(center, 360, 2, easing = EASE_IN_OUT)
    ")]
    (
        self,
        #[desc("Group of object to rotate around")] center: @group,
        #[desc("Rotation in degrees")] degrees: @number,
        #[desc("Duration of rotation")] duration: @number = 0,
        #[desc("Easing type")] easing: @easing_type = NONE,
        #[desc("Easing rate")] easing_rate: @number = 2,
        #[desc("Only rotate positions of the objects, not the textures")] lock_object_rotation: @bool = false
    ){
        $.add( trigger{
            OBJ_ID: 1346,
            TARGET: self,
            CENTER: center,
            ROTATE_DEGREES: degrees,
            DURATION: duration,
            EASING: easing.id,
            EASING_RATE: easing_rate,
            LOCK_OBJECT_ROTATION: lock_object_rotation
        })
        wait(duration)
    },

    follow:
    #[desc("Implementation of the follow trigger") example("10g.follow(11g)")]
    (
        self,
        #[desc("Group of object to follow")] other: @group,
        #[desc("Multiplier for the movement on the X-axis")] x_mod: @number = 1,
        #[desc("Multiplier for the movement on the Y-axis")] y_mod: @number = 1,
        #[desc("Duration of following")] duration: @number = 999
    ){
        $.add( trigger{
            OBJ_ID: 1347,
            X_MOD: x_mod,
            Y_MOD: y_mod,
            DURATION: duration,
            TARGET: self,
            FOLLOW: other,
        })
        //wouldnt make any sense to wait out the duration here
    },
    
    follow_lerp:
    #[desc("Keeps an object's position proportionally between 2 others")
    example("
// Since this function works using follow triggers, objects need to already be in the proper position,
// otherwise they'll be offset.
1g.follow_lerp(2g,3g,0.5) // Keeps group 1 in the middle of groups 2 and 3
1g.follow_lerp(2g,3g,0.25) // Keeps group 1 25% of the way between groups 2 and 3
1g.follow_lerp(2g,3g,-1) // Keeps group 1 as a reflection of group 3 by group 2
    ")]
    (
        self,
        #[desc("Group of object A to follow")] groupA: @group,
        #[desc("Group of object B to follow")] groupB: @group,
        #[desc("Group of object B to follow")] weight: @number = 0.5,
        #[desc("Duration of following")] duration: @number = 999,
    ){
        self.follow(groupA,x_mod = 1 - weight,y_mod = 1 - weight,duration = duration)
        self.follow(groupB,x_mod = weight,y_mod = weight,duration = duration)
    },

    follow_player_y:
    #[desc("Implementation of the follow player Y trigger") example("10g.follow_player_y(delay = 0.5)")]
    (
        self,
        #[desc("Interpolation factor (?)")] speed: @number = 1,
        #[desc("Delay of movement")]  delay: @number = 0,
        #[desc("Offset on the Y-axis")] offset: @number = 0,
        #[desc("Maximum speed")] max_speed: @number = 0,
        #[desc("Duration of following")] duration: @number = 999
    ){
        $.add( trigger{
            OBJ_ID: 1814,
            SPEED : speed,
            DELAY : delay,
            Y_OFFSET : offset,
            MAX_SPEED : max_speed,
            TARGET: self,
            DURATION: duration,
        })
        //wouldnt make any sense to wait out the duration here
    },

    move_to:
    #[desc("Implementation of the 'Move target' feature of the move trigger. Remember that both groups can only contain one object.") example("10g.move_to(20g)")]
    (
        self,
        #[desc("Group of the object to move to")] target: @group,
        #[desc("Duration of movement")] duration: @number = 0,
        #[desc("Will move to the object only on the X-axis")] x_only: @bool = false,
        #[desc("Will move to the object only on the y-axis")] y_only: @bool = false,
        #[desc("Easing type")] easing: @easing_type = NONE,
        #[desc("Easing rate")] easing_rate: @number = 2
    ) {
        $.add( trigger{
            OBJ_ID: 901,
            TARGET: self,
            USE_TARGET: true,

            TARGET_POS_AXES: (){
                if x_only && y_only {
                    -> return 0
                } else if x_only {
                    -> return 1
                } else if y_only {
                    -> return 2
                } else {
                    -> return 0
                }
            }(),
            TARGET_POS: target,

            DURATION: duration,
            EASING: easing.id,
            EASING_RATE: easing_rate,
        })
        wait(duration)
    },

    move_to_xy:
    #[desc("Moves group to a specific coordinate") example("
10g.move_to_xy(300, 300)
10g.move_to_xy(x = 300) // does not move on the y axis
10g.move_to_xy(y = 300) // does not move on the x axis
    ")]
    (
        self,
        #[desc("X position to move to in units (1 grid square is 30 units)")] x: @number | @NULL = null, 
        #[desc("Y position to move to in units (1 grid square is 30 units)")] y: @number | @NULL = null,
        #[desc("Duration of movement")] duration: @number = 0, 
        #[desc("Easing type")] easing: @easing_type = NONE, 
        #[desc("Easing rate")] easing_rate: @number = 2,
    ) {
        if x == null && y == null {
            throw "At least one coordinate must be specified"
        }
        target = ?g
        $.add(obj {
            OBJ_ID: 1765,
            X: x if x != null else 0,
            Y: y if y != null else 0,
            GROUPS: target,
        }, true)
        $.add( alpha_trigger(target,0).with(X,0).with(Y,75*30), true )
        self.move_to(target, duration, y == null, x == null, easing, easing_rate)
    },

    pulse:
    #[desc("Implementation of the pulse trigger for groups") example("10g.pulse(255, 0, 0, fade_out = 0.5)")]
    (
        self,
        #[desc("Red value of pulse color (or hue if HSV is enabled)")] r: @number,
        #[desc("Green value of pulse color (or saturation if HSV is enabled)")] g: @number,
        #[desc("Blue value of pulse color (or brightness/value if HSV is enabled)")] b: @number,
        #[desc("Fade-in duration")] fade_in: @number = 0,
        #[desc("Duration to hold the color")] hold: @number = 0,
        #[desc("Fade-out duration")] fade_out: @number = 0,
        #[desc("Weather to prioritize this pulse over simultaneous pulses")] exclusive: @bool = false,
        #[desc("Toggle HSV mode")] hsv: @bool = false,
        #[desc("HSV specific: saturation checked")] s_checked: @bool = false,
        #[desc("HSV specific: brightness checked")] b_checked: @bool = false
    ) {
        if hsv {
            $.add( trigger{
                OBJ_ID: 1006,
                COPIED_COLOR_HVS:
                    r as @string + "a" + g as @string + "a" + b as @string + "a"
                     + s_checked as @number as @string + "a" + b_checked as @number as @string,
                EXCLUSIVE: exclusive,
                FADE_IN: fade_in,
                HOLD: hold,
                FADE_OUT: fade_out,
                TARGET: self,
                PULSE_HSV: hsv,
                TARGET_TYPE: 1 //group
            })
        } else {
            $.add( trigger{
                OBJ_ID: 1006,
                TRIGGER_RED: r,
                TRIGGER_GREEN: g,
                TRIGGER_BLUE: b,
                EXCLUSIVE: exclusive,
                FADE_IN: fade_in,
                HOLD: hold,
                FADE_OUT: fade_out,
                TARGET: self,
                PULSE_HSV: hsv,
                TARGET_TYPE: 1 //group
            })
        }
        wait(fade_in + hold + fade_out)
    }


}