Compare commits

..

8 Commits

Author SHA1 Message Date
ed27d2c62d Merge branch 'main' into lib-cutscene 2026-01-25 12:07:33 +01:00
Rares Bozga
dcc7f81705 Various bugfixes 2026-01-25 13:06:21 +02:00
Rares Bozga
7771b9041b Fix oversight in storyboard 2026-01-16 23:27:37 +02:00
dd126636c6 Merge pull request 'Completed cutscene library' (#5) from lib-cutscene into main
Reviewed-on: #5
2026-01-11 14:02:46 +01:00
Rares Bozga
06445b71fb Completed cutscene library 2026-01-11 15:02:17 +02:00
44ffc2b8d0 Merge pull request 'Added atomic handling for storyboard operations and fixed various bugs' (#4) from feature-storyboard-atomic into main
Reviewed-on: #4
2026-01-09 12:25:07 +01:00
Rares Bozga
01a4e7414c Various bugfixes 2026-01-09 13:24:07 +02:00
Rares Bozga
37268060a2 Initial variant of atomicity 2026-01-09 12:00:54 +02:00
13 changed files with 428 additions and 25 deletions

View File

@@ -20,6 +20,13 @@ ball_create:
- define size <[ball_collision].bounding_box.get[1].sub[<[ball_collision].location>].x.abs.add[0.01]> - define size <[ball_collision].bounding_box.get[1].sub[<[ball_collision].location>].x.abs.add[0.01]>
- spawn zombie[silent=true;has_ai=false;gravity=false;visible=false] <[location].with_pitch[0].with_yaw[0].below[<[size].add[1]>]> save:ball_display - spawn zombie[silent=true;has_ai=false;gravity=false;visible=false] <[location].with_pitch[0].with_yaw[0].below[<[size].add[1]>]> save:ball_display
- define ball_display <entry[ball_display].spawned_entity> - define ball_display <entry[ball_display].spawned_entity>
- if <[ball_display].is_baby>:
- age <[ball_display]> adult lock
- if <[ball_display].is_inside_vehicle>:
# CHICKEN JOCKEEEEEEEEEEEEEEEEEEEEEEEEEY
- define chicken <[ball_display].vehicle>
- adjust <[chicken]> passengers:<list[]>
- remove <[chicken]>
- adjust <[ball_display]> equipment:<map[].with[helmet].as[<[display_item]>]> - adjust <[ball_display]> equipment:<map[].with[helmet].as[<[display_item]>]>
- flag <[ball_collision]> ball:<[id]> - flag <[ball_collision]> ball:<[id]>
- flag <[ball_collision]> ball_display:<[ball_display]> - flag <[ball_collision]> ball_display:<[ball_display]>

309
scripts/lib/cutscene.dsc Normal file
View File

@@ -0,0 +1,309 @@
#
# Cutscene - library for creating cool cutscenes similar to the Replay Mod
#
# Playing cutscenes can also be atomic, meaning that if the player leaves mid
# cutscene, the cutscene may interrupt the queue that called the task in order
# to prevent the script from continuing.
# Determines the set of points passing through the given points spaced by the given distance.
# Effectively, the points representing the lines going from p0 to p1, then p1 to p2, etc...
# The points have to be given escaped! So <[myinput].escaped>!
cutscene_concatenate_points_between:
debug: false
type: procedure
definitions: points|distance
script:
- define points <[points].unescaped>
- define result <list[]>
- repeat <[points].size.sub[1]> as:index:
- define result <[result].include[<[points].get[<[index]>].points_between[<[points].get[<[index].add[1]>]>].distance[<[distance]>]>]>
- determine <[result]>
# Determines the Catmull-Rom spline passing through the given points, taking the distance
# between the resulting equidistant points and the resolution of the computation into account.
# Note that the efficiency of this algorithm is not amazing necessarily, so you should cache
# the results. Higher resolutions also mean the algorithm will take longer to finish.
# The points have to be given escaped! So <[myinput].escaped>!
cutscene_catmull_rom_equidistant_spline:
debug: false
type: procedure
definitions: points|distance|resolution
script:
- define points <[points].unescaped>
- define distance <[distance].if_null[0.25]>
- define resolution <[resolution].if_null[100]>
- if <[points].size> < 2:
- determine <[points]>
- if <[points].size> == 2:
- determine <[points].get[1].points_between[<[points].get[2]>].distance[<[distance]>]>
- define spline_points <list[]>
- define cumulative_distances <list[<element[0]>]>
- repeat <[points].size.sub[1]> from:0 as:segment_index:
- if <[segment_index]> == 0:
- define p0 <[points].get[1].sub[<[points].get[2].sub[<[points].get[1]>]>]>
- define p1 <[points].get[1]>
- define p2 <[points].get[2]>
- define p3 <tern[<[points].size.is_more_than[2]>].pass[<[points].get[3]>].fail[<[points].get[2]>]>
- else if <[segment_index]> == <[points].size.sub[2]>:
- define p0 <[points].get[<[segment_index]>]>
- define p1 <[points].get[<[segment_index].add[1]>]>
- define p2 <[points].get[<[segment_index].add[2]>]>
- define p3 <[points].get[<[segment_index].add[2]>].add[<[points].get[<[segment_index].add[2]>].sub[<[points].get[<[segment_index].add[1]>]>]>]>
- else:
- define p0 <[points].get[<[segment_index]>]>
- define p1 <[points].get[<[segment_index].add[1]>]>
- define p2 <[points].get[<[segment_index].add[2]>]>
- define p3 <[points].get[<[segment_index].add[3]>]>
- define t 0
- while <[t]> < 1:
- define t2 <[t].mul[<[t]>]>
- define t3 <[t2].mul[<[t]>]>
- define px <element[0.5].mul[<[p1].x.mul[2].add[<[p2].x.sub[<[p0].x>].mul[<[t]>]>].add[<[p0].x.mul[2].sub[<[p1].x.mul[5]>].add[<[p2].x.mul[4]>].sub[<[p3].x>].mul[<[t2]>]>].add[<[p0].x.mul[-1].add[<[p1].x.mul[3]>].sub[<[p2].x.mul[3]>].add[<[p3].x>].mul[<[t3]>]>]>]>
- define py <element[0.5].mul[<[p1].y.mul[2].add[<[p2].y.sub[<[p0].y>].mul[<[t]>]>].add[<[p0].y.mul[2].sub[<[p1].y.mul[5]>].add[<[p2].y.mul[4]>].sub[<[p3].y>].mul[<[t2]>]>].add[<[p0].y.mul[-1].add[<[p1].y.mul[3]>].sub[<[p2].y.mul[3]>].add[<[p3].y>].mul[<[t3]>]>]>]>
- define pz <element[0.5].mul[<[p1].z.mul[2].add[<[p2].z.sub[<[p0].z>].mul[<[t]>]>].add[<[p0].z.mul[2].sub[<[p1].z.mul[5]>].add[<[p2].z.mul[4]>].sub[<[p3].z>].mul[<[t2]>]>].add[<[p0].z.mul[-1].add[<[p1].z.mul[3]>].sub[<[p2].z.mul[3]>].add[<[p3].z>].mul[<[t3]>]>]>]>
- define point <location[<[px]>,<[py]>,<[pz]>,<[points].get[1].world.name>]>
- if <[spline_points].size> > 0:
- define last_point <[spline_points].get[-1]>
- define cumulative_distance <[last_point].distance[<[point]>]>
- define cumulative_distances <[cumulative_distances].include[<[cumulative_distances].get[-1].add[<[cumulative_distance]>]>]>
- define spline_points <[spline_points].include[<[point]>]>
- define t <[t].add[<element[1].div[<[resolution]>]>]>
- define equidistant_points <list[<[spline_points].get[1]>]>
- define total_length <[cumulative_distances].get[-1]>
- define current_distance 0
- define current_index 1
- while <[current_distance]> < <[total_length]>:
- define target_distance <[current_distance].add[<[distance]>]>
- if <[target_distance]> > <[total_length]>:
- while stop
- while <[current_index]> < <[cumulative_distances].size> && <[cumulative_distances].get[<[current_index].add[1]>]> < <[target_distance]>:
- define current_index <[current_index].add[1]>
- define t1 <[cumulative_distances].get[<[current_index]>]>
- define t2 <[cumulative_distances].get[<[current_index].add[1]>]>
- define alpha <[target_distance].sub[<[t1]>].div[<[t2].sub[<[t1]>]>]>
- define p1 <[spline_points].get[<[current_index]>]>
- define p2 <[spline_points].get[<[current_index].add[1]>]>
- define x <[p1].x.add[<[alpha].mul[<[p2].x.sub[<[p1].x>]>]>]>
- define y <[p1].y.add[<[alpha].mul[<[p2].y.sub[<[p1].y>]>]>]>
- define z <[p1].z.add[<[alpha].mul[<[p2].z.sub[<[p1].z>]>]>]>
- define point <location[<[x]>,<[y]>,<[z]>,<[points].get[1].world.name>]>
- define equidistant_points <[equidistant_points].include[<[point]>]>
- define current_distance <[target_distance]>
- define last_control_point <[points].get[-1]>
- define last_generated <[equidistant_points].get[-1]>
- if <[last_generated].distance[<[last_control_point]>]> >= <[distance].div[2]>:
- define equidistant_points <[equidistant_points].include[<[last_control_point]>]>
- determine <[equidistant_points]>
# Previews the given path for the given player, where the source definition are the points that
# the resulting path must pass through, for the specified duration_tag. Note for the duration_tag
# that a <duration[]> is required, and it will not work if using simple <element[]>, like '5s'.
cutscene_preview_path:
debug: false
type: task
definitions: player|source|result|duration_tag
script:
- define duration_tag <[duration_tag].if_null[<duration[5s]>]>
- define now <util.current_time_millis>
- while <util.current_time_millis.sub[<[now]>]> < <[duration_tag].in_milliseconds>:
- if !<[player].is_online>:
- stop
- playeffect at:<[source]> targets:<[player]> effect:CLOUD offset:0,0,0 quantity:1 velocity:0,0,0 visibility:1000
- playeffect at:<[result]> targets:<[player]> effect:ENCHANTED_HIT offset:0,0,0 quantity:1 velocity:0,0,0 visibility:1000
- wait 5t
# Simple linear interpolation.
# Given as parameter to cutscene_follow_path_by_interpolation.
cutscene_interpolate_linear:
debug: false
type: procedure
definitions: step
script:
- determine 0.5
# Ease in interpolation.
# Given as parameter to cutscene_follow_path_by_interpolation.
cutscene_interpolate_ease_in:
debug: false
type: procedure
definitions: step
script:
- determine <[step].mul[<util.pi.div[2]>].sin>
# Ease out interpolation.
# Given as parameter to cutscene_follow_path_by_interpolation.
cutscene_interpolate_ease_out:
debug: false
type: procedure
definitions: step
script:
- determine <[step].mul[<util.pi.div[2]>].add[<util.pi.div[2]>].sin>
# Ease in-out interpolation.
# Given as parameter to cutscene_follow_path_by_interpolation.
cutscene_interpolate_ease_in_out:
debug: false
type: procedure
definitions: step
script:
- determine <[step].mul[<util.pi>].sin>
# Makes the player follow the given path at the given speed, using the provided interpolation.
# Note that the speed is an arbitrary construct, but higher numbers will make the player follow
# the path faster.
cutscene_follow_path_by_interpolation:
debug: false
type: task
definitions: player|path|look_at|speed|interpolation_procedure
script:
- if <[path].size> == 0:
- stop
- define look_at_is_list <tern[<[look_at].get[1].if_null[null].equals[null]>].pass[false].fail[true]>
- teleport <[player]> <[path].get[1]>
- define time_unit <[path].size.div[<[speed].mul[20]>]>
- define should_await_time 0
- define index 1
- while <[index]> < <[path].size>:
- define point <[path].get[<[index]>]>
- define step <[index].sub[1].div[<[path].size>]>
- define await_time <element[1].sub[<proc[<[interpolation_procedure]>].context[<[step]>]>].mul[<[time_unit]>]>
- define should_await_time <[should_await_time].add[<[await_time]>]>
- if <[should_await_time]> < 1:
- define index <[index].add[1]>
- while next
- wait <[should_await_time].round_down>t
- define should_await_time <[should_await_time].sub[<[should_await_time].round_down>]>
- define direction <[look_at].sub[<[point]>].normalize>
- define quaternion_pitch <[direction].quaternion_between_vectors[0,1,0].represented_angle.sub[<util.pi.div[2]>].to_degrees>
- define result_point <[point].with_pitch[<[quaternion_pitch]>].with_yaw[<[point].direction[<[look_at]>].yaw>]>
- if !<[player].is_online>:
- determine false
- teleport <[player]> <[result_point].add[0,-1,0]> offthread_repeat:8
- define index <[index].add[1]>
- if !<[player].is_online>:
- determine false
- teleport <[player]> <[path].get[-1]>
- determine true
# Creates and saves a cutscene.
# Definitions explanation:
# - name: The name of the cutscene. Must be unique, case sensitive.
# - points: The source points to generate the path from.
# - look_at: The location that the player should look at throughout the entire path follow
# - compute_spline: Whether to use straight lines for the path, or to compute a smooth spline.
# - interpolation_procedure: A procedure to interpolate the speed of the path follower.
#
# If the cutscene already exists, it will not create it and silently fail.
# Determines true if the cutscene was created, or false if it already exists.
cutscene_create_and_save:
debug: false
type: task
definitions: name|points|look_at|speed|compute_spline|interpolation_procedure
script:
- if <server.flag[cutscene].if_null[<map[]>].contains[<[name]>]>:
- determine false
- if !<[compute_spline]>:
- define result <proc[cutscene_concatenate_points_between].context[<[points].escaped>|0.33]>
- else:
- define result <proc[cutscene_catmull_rom_equidistant_spline].context[<[points].escaped>|0.33|50]>
- definemap cutscene:
name: <[name]>
source: <[points]>
result: <[result]>
look_at: <[look_at]>
speed: <[speed]>
interpolation_procedure: <[interpolation_procedure]>
- flag server cutscene:<server.flag[cutscene].if_null[<map[]>].with[<[name]>].as[<[cutscene]>]>
- determine true
# Destroys an existing cutscnee.
# If the cutscene doesn't exist, it will fail silently.
# Determines true if the cutscene was destroyed, or false if it did not yet exist.
cutscene_destroy:
debug: false
type: task
definitions: name
script:
- if !<server.flag[cutscene].if_null[<map[]>].contains[<[name]>]>:
- determine false
- flag server cutscene:<server.flag[cutscene].if_null[<map[]>].exclude[<[name]>]>
- determine false
# Previews the given path for the given player, using a saved cutscene as the source.
# Note for the duration_tag that a <duration[]> is required, and it will not work if
# using simple <element[]>, like '5s'.
cutscene_preview_path_by_name:
debug: false
type: task
definitions: name|player|duration_tag
script:
- if !<server.flag[cutscene].if_null[<map[]>].contains[<[name]>]>:
- debug error "Cannot preview path by name! <[name]> @ <[player]>"
- stop
- define cutscene <server.flag[cutscene].get[<[name]>]>
- run cutscene_preview_path def.player:<[player]> def.source:<[cutscene].get[source]> def.result:<[cutscene].get[result]> def.duration_tag:<[duration_tag]>
# Plays the given cutscene by name.
# If the player disconnects, the cutscene will stop playing, but the queue that
# called this task will continue. Consider cutscene_play_atomic in case you would
# like to stop the queue that called this task.
# Determines true if the cutscene completed, false otherwise.
# Should be ~waited for.
cutscene_play:
debug: false
type: task
definitions: name|player
script:
- run cutscene_stop def.player:<[player]>
- if !<server.flag[cutscene].if_null[<map[]>].contains[<[name]>]>:
- determine false
- define cutscene <server.flag[cutscene].get[<[name]>]>
- flag <[player]> cutscene:<map[].with[gamemode].as[<player.gamemode>].with[location].as[<player.location>]>
- adjust <[player]> gamemode:spectator
- run cutscene_follow_path_by_interpolation def.player:<[player]> def.path:<[cutscene].get[result]> def.look_at:<[cutscene].get[look_at]> def.speed:<[cutscene].get[speed]> def.interpolation_procedure:<[cutscene].get[interpolation_procedure]> save:result
- define queue <entry[result].created_queue>
- flag <[player]> cutscene_queue:<[queue]>
- waituntil <[queue].state.equals[running]> max:5t
- waituntil <[queue].state.equals[running].not>
- define ok <[queue].determination.get[1]>
- run cutscene_stop def.player:<player>
- determine <[ok]>
# Plays the cutscene atomically.
# See cutscene_play for an explanation.
# Should be ~waited for.
cutscene_play_atomic:
debug: false
type: task
definitions: name|player|queue
script:
- ~run cutscene_play def.name:<[name]> def.player:<[player]> save:result
- define result <entry[result].created_queue.determination.get[1]>
- if !<[result].if_null[false]>:
- debug log "Cancelled atomic cutscene: <[player].name> @ <[name]>"
- queue stop <[queue]>
# Stops the currently playing cutscene from playing.
# If no cutscene is playing, it silently fails.
cutscene_stop:
debug: false
type: task
definitions: player
script:
- if !<[player].has_flag[cutscene]>:
- stop
- define save <[player].flag[cutscene]>
- if <[player].flag[cutscene_queue]> != null:
- queue stop <[player].flag[cutscene_queue]>
- adjust <[player]> gamemode:<[save].get[gamemode]>
- teleport <[player]> <[save].get[location]>
- flag <[player]> cutscene:!
## Internal only!
cutscene_world:
debug: false
type: world
events:
on player joins:
- run cutscene_stop def.player:<player>

View File

@@ -35,7 +35,7 @@ textbox_write:
- queue stop <[queue]> - queue stop <[queue]>
- run textbox_flush def.player:<[player]> - run textbox_flush def.player:<[player]>
- stop - stop
- define lines <[line3s].split[$$nl].parse_tag[<[parse_value].trim>]> - define lines <[line3s].split[$$nl].parse_tag[<[parse_value].trim.replace[<&0>].with[<&6>]>]>
- flag <[player]> textbox_state:writing - flag <[player]> textbox_state:writing
- flag <[player]> textbox_input:<[lines]> - flag <[player]> textbox_input:<[lines]>
- flag <[player]> textbox_lines:<[lines].size> - flag <[player]> textbox_lines:<[lines].size>
@@ -57,7 +57,7 @@ textbox_write:
- stop - stop
- if !<[player].is_online>: - if !<[player].is_online>:
- stop - stop
- bossbar update textbox_<[player].uuid>_<[loop_index]> title:<black><bold><[line].substring[1,<[value]>]> - bossbar update textbox_<[player].uuid>_<[loop_index]> title:<&6><&l><[line].substring[1,<[value]>]>
- if <[value].sub[1].mod[3]> == 0: - if <[value].sub[1].mod[3]> == 0:
- playsound sound:textbox.text <[player]> custom pitch:<util.random.decimal[0.98].to[1]> volume:<proc[settings_get].context[<[player]>|sound_textbox_volume].div[100].if_null[1]> - playsound sound:textbox.text <[player]> custom pitch:<util.random.decimal[0.98].to[1]> volume:<proc[settings_get].context[<[player]>|sound_textbox_volume].div[100].if_null[1]>
- wait <[write_speed]>t - wait <[write_speed]>t
@@ -100,7 +100,7 @@ textbox_skip:
- bossbar create textbox_<[player].uuid>_<[value]> players:<[player]> title:<empty> - bossbar create textbox_<[player].uuid>_<[value]> players:<[player]> title:<empty>
- define lines <[player].flag[textbox_input]> - define lines <[player].flag[textbox_input]>
- foreach <[lines]> as:line: - foreach <[lines]> as:line:
- bossbar update textbox_<[player].uuid>_<[loop_index]> title:<black><bold><[line]> - bossbar update textbox_<[player].uuid>_<[loop_index]> title:<&6><&l><[line]>
- flag <[player]> textbox_state:continue - flag <[player]> textbox_state:continue
# Clears textbox and flushes all flag memory values # Clears textbox and flushes all flag memory values
@@ -208,12 +208,12 @@ textbox_handle_click:
- ratelimit <player> 5t - ratelimit <player> 5t
- else if <[state]> == continue: - else if <[state]> == continue:
- determine cancelled passively - determine cancelled passively
- playsound sound:textbox.close <player> custom volume:<proc[settings_get].context[<[player]>|sound_textbox_volume].div[100].if_null[1]> - playsound sound:textbox.close <player> custom volume:<proc[settings_get].context[<player>|sound_textbox_volume].div[100].if_null[1]>
- ~run textbox_flush def.player:<player> - ~run textbox_flush def.player:<player>
- ratelimit <player> 10t - ratelimit <player> 10t
- else if <[state]> == choice: - else if <[state]> == choice:
- determine cancelled passively - determine cancelled passively
- playsound sound:input.ok <player> custom volume:<proc[settings_get].context[<[player]>|sound_textbox_volume].div[100].if_null[1]> - playsound sound:input.ok <player> custom volume:<proc[settings_get].context[<player>|sound_textbox_volume].div[100].if_null[1]>
- flag <player> textbox_choice_select:<player.flag[textbox_choices].get[current]> - flag <player> textbox_choice_select:<player.flag[textbox_choices].get[current]>
- ~run textbox_flush def.player:<player> - ~run textbox_flush def.player:<player>
- ratelimit <player> 10t - ratelimit <player> 10t
@@ -250,7 +250,13 @@ textbox_choice:
- teleport <[player]> <[player].location.below.above.with_y[<[player].location.below.above.y.round_down>]> - teleport <[player]> <[player].location.below.above.with_y[<[player].location.below.above.y.round_down>]>
- else: - else:
- stop - stop
- waituntil <[player].flag[textbox_state].if_null[null]> == null - waituntil <[player].flag[textbox_state].if_null[null]> == null || !<[player].is_online>
- if !<[player].is_online>:
- if <[queue].if_null[null]> != null:
- debug log "[Textbox] Choice; cancelled queue <[queue].numeric_id><&at><[queue].script.name> for <[player].name>; offline"
- queue stop <[queue]>
- run textbox_flush def.player:<[player]>
- stop
- ~run textbox_flush def.player:<[player]> - ~run textbox_flush def.player:<[player]>
- flag <[player]> textbox_state:choice - flag <[player]> textbox_state:choice
- flag <[player]> textbox_choices:<map[].with[data].as[<[choices]>].with[current].as[left]> - flag <[player]> textbox_choices:<map[].with[data].as[<[choices]>].with[current].as[left]>
@@ -284,35 +290,35 @@ textbox_internal_choice_select:
- bossbar create textbox_<[player].uuid>_bottom players:<[player]> title:<empty> - bossbar create textbox_<[player].uuid>_bottom players:<[player]> title:<empty>
- define choices <[player].flag[textbox_choices].get[data]> - define choices <[player].flag[textbox_choices].get[data]>
# #
- define left <&0><&l><[choices].get[left].get[text].if_null[null]> - define left <&6><&l><[choices].get[left].get[text].if_null[null]>
- define right <&0><&l><[choices].get[right].get[text].if_null[null]> - define right <&6><&l><[choices].get[right].get[text].if_null[null]>
- define top <&0><&l><[choices].get[top].get[text].if_null[null]> - define top <&6><&l><[choices].get[top].get[text].if_null[null]>
- define bottom <&0><&l><[choices].get[bottom].get[text].if_null[null]> - define bottom <&6><&l><[choices].get[bottom].get[text].if_null[null]>
# #
- if <[choice_dir]> == left: - if <[choice_dir]> == left:
- if <[left]> == <&0><&l>null: - if <[left]> == <&6><&l>null:
- stop - stop
- define left "<&4>❤ <&0><&l><[left]>" - define left "<&4>❤ <&6><&l><[left]>"
- else if <[choice_dir]> == right: - else if <[choice_dir]> == right:
- if <[right]> == <&0><&l>null: - if <[right]> == <&6><&l>null:
- stop - stop
- define right "<&4>❤ <&0><&l><[right]>" - define right "<&4>❤ <&6><&l><[right]>"
- else if <[choice_dir]> == top: - else if <[choice_dir]> == top:
- if <[top]> == <&0><&l>null: - if <[top]> == <&6><&l>null:
- stop - stop
- define top "<&4>❤ <&0><&l><[top]>" - define top "<&4>❤ <&6><&l><[top]>"
- else: - else:
- if <[bottom]> == <&0><&l>null: - if <[bottom]> == <&6><&l>null:
- stop - stop
- define bottom "<&4>❤ <&0><&l><[bottom]>" - define bottom "<&4>❤ <&6><&l><[bottom]>"
# #
- define mid_padding <element[<&sp>].repeat[<element[30].sub[<[left].strip_color.length>].sub[<[right].strip_color.length>]>]> - define mid_padding <element[<&sp>].repeat[<element[30].sub[<[left].strip_color.length>].sub[<[right].strip_color.length>]>]>
# #
- if <[top]> != <&0><&l>null: - if <[top]> != <&6><&l>null:
- bossbar update textbox_<[player].uuid>_top title:<[top]> - bossbar update textbox_<[player].uuid>_top title:<[top]>
- if <[left]> != <&0><&l>null || <[right]> != <&0><&l>null: - if <[left]> != <&6><&l>null || <[right]> != <&6><&l>null:
- bossbar update textbox_<[player].uuid>_mid title:<[left]><[mid_padding]><[right]> - bossbar update textbox_<[player].uuid>_mid title:<[left]><[mid_padding]><[right]>
- if <[bottom]> != <&0><&l>null: - if <[bottom]> != <&6><&l>null:
- bossbar update textbox_<[player].uuid>_bottom title:<[bottom]> - bossbar update textbox_<[player].uuid>_bottom title:<[bottom]>
- flag <[player]> textbox_choices:<map[].with[data].as[<[choices]>].with[current].as[<[choice_dir]>]> - flag <[player]> textbox_choices:<map[].with[data].as[<[choices]>].with[current].as[<[choice_dir]>]>
- playsound BLOCK_NOTE_BLOCK_BIT <[player]> pitch:2 volume:<proc[settings_get].context[<[player]>|sound_textbox_volume].div[100].if_null[1]> - playsound BLOCK_NOTE_BLOCK_BIT <[player]> pitch:2 volume:<proc[settings_get].context[<[player]>|sound_textbox_volume].div[100].if_null[1]>

View File

@@ -66,6 +66,10 @@ liteprofilesutils_world:
- narrate <[joinleavedata].get[join].parsed> targets:<[setting_enabled_players]> - narrate <[joinleavedata].get[join].parsed> targets:<[setting_enabled_players]>
## prevent /profile remove ## prevent /profile remove
on command: on command:
- if <context.source_type> != player:
- stop
- if <player.is_op>:
- stop
- if <context.command.to_lowercase> == profile || <context.command.to_lowercase> == account || <context.command.to_lowercase> == pf: - if <context.command.to_lowercase> == profile || <context.command.to_lowercase> == account || <context.command.to_lowercase> == pf:
- if <context.args.get[1].to_lowercase.if_null[null]> == remove: - if <context.args.get[1].to_lowercase.if_null[null]> == remove:
- determine cancelled passively - determine cancelled passively

View File

@@ -22,7 +22,7 @@ compatibility_check_world:
after player joins: after player joins:
- if <proc[settings_get].context[<player>|general_ignore_version_compatibility_check]>: - if <proc[settings_get].context[<player>|general_ignore_version_compatibility_check]>:
- stop - stop
- define player_version <player.viaversion_version.split[-].get[1]> - define player_version <player.viaversion_version.split[-].get[2]>
- define server_version <server.version.split[(].get[2].split[:].get[2].split[)].get[1].trim> - define server_version <server.version.split[(].get[2].split[:].get[2].split[)].get[1].trim>
- if <proc[compatibility_check_compare_versions].context[<[player_version]>|<[server_version]>]> == -1: - if <proc[compatibility_check_compare_versions].context[<[player_version]>|<[server_version]>]> == -1:
- wait 5s - wait 5s

View File

@@ -2,9 +2,11 @@ ch1_1_preassign:
debug: false debug: false
type: world type: world
events: events:
on player joins: after player joins:
- wait 1s
- if <proc[storyboard_player_state_get].context[<player>|preassign]> == null: - if <proc[storyboard_player_state_get].context[<player>|preassign]> == null:
- run storyboard_npc_memalloc "def:<player>|marie|player|<location[-4,2,-15,world]>|Marie Ayashibayomi|true|<script[storyboard_skin_dump].data_key[marie].get[a]>" - if !<proc[storyboard_npc_exists].context[<player>|marie]>:
- run storyboard_npc_memalloc "def:<player>|marie|player|<location[-4,2,-15,world]>|Marie Ayashibayomi|true|<script[storyboard_skin_dump].data_key[marie].get[a]>"
- run storyboard_npc_set_assignment def.player:<player> def.name:marie def.assignment:ch1_1_marie_assign - run storyboard_npc_set_assignment def.player:<player> def.name:marie def.assignment:ch1_1_marie_assign
- run storyboard_player_state_set def.player:<player> def.key:preassign def.value:true - run storyboard_player_state_set def.player:<player> def.key:preassign def.value:true
@@ -34,6 +36,7 @@ ch1_1_marie_interact:
1: 1:
click trigger: click trigger:
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- if <player.flag[textbox_state].if_null[null]> != null: - if <player.flag[textbox_state].if_null[null]> != null:
- stop - stop
- engage player duration:999999s - engage player duration:999999s
@@ -97,9 +100,11 @@ ch1_1_marie_interact:
- run storyboard_npc_state_set def.player:<player> def.name:marie def.key:opinion def.value:-1 - run storyboard_npc_state_set def.player:<player> def.name:marie def.key:opinion def.value:-1
- zap 2 - zap 2
- disengage player - disengage player
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>
2: 2:
click trigger: click trigger:
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- if <player.flag[textbox_state].if_null[null]> != null: - if <player.flag[textbox_state].if_null[null]> != null:
- stop - stop
- engage player duration:999999s - engage player duration:999999s
@@ -128,6 +133,7 @@ ch1_1_marie_interact:
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:* Mkay." def.avatar_unicode:<script[storyboard_avatar_dump].data_key[marie].get[upset]> - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:* Mkay." def.avatar_unicode:<script[storyboard_avatar_dump].data_key[marie].get[upset]>
- wait 1s - wait 1s
- disengage player - disengage player
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>
- stop - stop
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:* Hm. Are you now?" def.avatar_unicode:<script[storyboard_avatar_dump].data_key[marie].get[upset]> - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:* Hm. Are you now?" def.avatar_unicode:<script[storyboard_avatar_dump].data_key[marie].get[upset]>
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:* You know, that was kind of$$nlmean of you." def.avatar_unicode:<script[storyboard_avatar_dump].data_key[marie].get[upset]> - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:* You know, that was kind of$$nlmean of you." def.avatar_unicode:<script[storyboard_avatar_dump].data_key[marie].get[upset]>
@@ -235,3 +241,4 @@ ch1_1_marie_interact:
- inventory update - inventory update
- zap 3 - zap 3
- disengage player - disengage player
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>

View File

@@ -14,6 +14,7 @@ dialogue_npc_kobayashi:
1: 1:
click trigger: click trigger:
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- if <player.flag[textbox_state].if_null[null]> != null: - if <player.flag[textbox_state].if_null[null]> != null:
- stop - stop
- engage player - engage player
@@ -45,3 +46,4 @@ dialogue_npc_kobayashi:
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:Indeed, luv." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:Indeed, luv."
- wait 1s - wait 1s
- disengage player - disengage player
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>

View File

@@ -14,6 +14,7 @@ dialogue_npc_patchouli:
1: 1:
click trigger: click trigger:
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- if <player.flag[textbox_state].if_null[null]> != null: - if <player.flag[textbox_state].if_null[null]> != null:
- stop - stop
- engage player - engage player
@@ -23,3 +24,4 @@ dialogue_npc_patchouli:
- wait 1s - wait 1s
- disengage player - disengage player
- ratelimit <player> 10t - ratelimit <player> 10t
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>

View File

@@ -14,6 +14,7 @@ dialogue_npc_ryuko:
1: 1:
click trigger: click trigger:
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- if <player.flag[textbox_state].if_null[null]> != null: - if <player.flag[textbox_state].if_null[null]> != null:
- stop - stop
- engage player - engage player
@@ -29,3 +30,4 @@ dialogue_npc_ryuko:
- wait 1s - wait 1s
- disengage player - disengage player
- ratelimit <player> 10t - ratelimit <player> 10t
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>

View File

@@ -15,11 +15,13 @@ intro_interact_posters_task:
debug: false debug: false
type: task type: task
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:Book-related posters are on the wall." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:Book-related posters are on the wall."
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:<&o><&dq>Screw that. I'm running away.<&dq>" - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:<&o><&dq>Screw that. I'm running away.<&dq>"
- ~run textbox_write def.player:<player> def.queue:<queue> def.line3s:<&o><&dq>Where?<&dq> - ~run textbox_write def.player:<player> def.queue:<queue> def.line3s:<&o><&dq>Where?<&dq>
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:<&o><&dq>Dunno. Do you want to come?<&dq>" - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:<&o><&dq>Dunno. Do you want to come?<&dq>"
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:<&o><&dq>Yes,<&dq> I said without thinking." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:<&o><&dq>Yes,<&dq> I said without thinking."
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>
# Cirno # Cirno
intro_interact_cirno: intro_interact_cirno:
@@ -38,8 +40,10 @@ intro_interact_cirno_task:
debug: false debug: false
type: task type: task
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:A quality, soft plushie of a beloved$$nlcharacter." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:A quality, soft plushie of a beloved$$nlcharacter."
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:It seems familiar, as if there are a$$nlsubstantial amount of images circulating$$nlaround with this character." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:It seems familiar, as if there are a$$nlsubstantial amount of images circulating$$nlaround with this character."
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>
# Laptop # Laptop
intro_interact_laptop: intro_interact_laptop:
@@ -58,6 +62,7 @@ intro_interact_laptop_task:
debug: false debug: false
type: task type: task
script: script:
- run storyboard_player_begin_atomic_sequence def.queue:<queue> def.player:<player>
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:It looks like a game is booted up.$$nlYou can see a city, and it looks like..." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:It looks like a game is booted up.$$nlYou can see a city, and it looks like..."
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:. . ." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:. . ."
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:It's <bold>you!$$nl. . .$$nlWell, it's your character." - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:It's <bold>you!$$nl. . .$$nlWell, it's your character."
@@ -73,6 +78,11 @@ intro_interact_laptop_task_name_callback:
script: script:
- define __player <[player]> - define __player <[player]>
- define name <[input].substring[1,24]> - define name <[input].substring[1,24]>
- if <server.flag[character_rpnames].contains[<[name]>]>:
- narrate targets:<[player]> "<&c>That RP name is already taken. Try a different one!"
- wait 1s
- run anvil_input def.player:<player> "def.prompt:Character Name" def.callback:intro_interact_laptop_task_name_callback
- stop
- execute as_player player:<[player]> "rpname <[name]>" - execute as_player player:<[player]> "rpname <[name]>"
- ~run textbox_write def.player:<player> def.queue:<queue> def.line3s:<[name]> - ~run textbox_write def.player:<player> def.queue:<queue> def.line3s:<[name]>
- ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:How nice!" - ~run textbox_write def.player:<player> def.queue:<queue> "def.line3s:How nice!"
@@ -197,3 +207,4 @@ intro_interact_laptop_task_role_callback:
- adjust <player> show_to_players - adjust <player> show_to_players
- flag <player> intro:done - flag <player> intro:done
- execute as_player player:<player> spawn - execute as_player player:<player> spawn
- run storyboard_player_end_atomic_sequence def.queue:<queue> def.player:<player>

View File

@@ -41,6 +41,7 @@ storyboard_npc_memalloc:
- define npcs <[player].flag[storyboard_state].get[npcs].if_null[<map[]>]> - define npcs <[player].flag[storyboard_state].get[npcs].if_null[<map[]>]>
- create <[type]> <[npc_id]> <[at]> registry:<[registry]> save:npc - create <[type]> <[npc_id]> <[at]> registry:<[registry]> save:npc
- define npc <entry[npc].created_npc> - define npc <entry[npc].created_npc>
- playeffect at:<[npc].location.above[1]> offset:0.35,1,0.35 effect:SOUL_FIRE_FLAME quantity:20
- define npc_state <map[]> - define npc_state <map[]>
- define assignment null - define assignment null
- if <[npcs].contains[<[name]>]>: - if <[npcs].contains[<[name]>]>:
@@ -137,6 +138,14 @@ storyboard_npc_memdestroy:
- define npc <proc[storyboard_npc_by_name].context[<[player]>|<[name]>]> - define npc <proc[storyboard_npc_by_name].context[<[player]>|<[name]>]>
- remove <[npc]> - remove <[npc]>
# Checks if an NPC exists for the given player.
storyboard_npc_exists:
debug: false
type: procedure
definitions: player|name
script:
- determine <proc[storyboard_npc_by_name].context[<[player]>|<[name]>].if_null[null].equals[null].not>
# Flags the NPC by name, mapping the given key to the given value. # Flags the NPC by name, mapping the given key to the given value.
storyboard_npc_state_set: storyboard_npc_state_set:
debug: false debug: false
@@ -215,6 +224,7 @@ storyboard_npc_internal_auto_memory_management:
type: world type: world
events: events:
after player joins: after player joins:
- wait 1s
- define npcs <player.flag[storyboard_state].get[npcs].if_null[<map[]>]> - define npcs <player.flag[storyboard_state].get[npcs].if_null[<map[]>]>
- foreach <[npcs]> key:name as:data: - foreach <[npcs]> key:name as:data:
- define allocated <[data].get[allocated].if_null[null]> - define allocated <[data].get[allocated].if_null[null]>

View File

@@ -60,6 +60,37 @@ storyboard_player_unfreeze:
- adjust <player> walk_speed:<player.flag[storyboard_freeze_speed]> - adjust <player> walk_speed:<player.flag[storyboard_freeze_speed]>
- flag <player> storyboard_freeze_speed:! - flag <player> storyboard_freeze_speed:!
# Marks the script after this task as an atomic sequence, meaning that
# the script either fully completes or is restored back to this point.
# This task does not ensure the atomicity, instead it will block any
# futher executions of the given script (deduced from the queue) until
# storyboard_player_end_atomic_sequence is called, or until the player
# logs off (which basically negates the atomicity). The developer must
# ensure that the script enclosed by the begin and end tasks is atomic.
storyboard_player_begin_atomic_sequence:
debug: false
type: task
definitions: queue|player
script:
- if <[player].flag[storyboard_atomic].if_null[<map[]>].contains[<[queue].script.name>]>:
- queue <[queue]> stop
- stop
- flag <[player]> storyboard_atomic:<[player].flag[storyboard_atomic].if_null[<map[]>].with[<[queue].script.name>].as[<util.time_now>]>
# Ends the atomic sequence above this task. See the sibling task
# storyboard_player_begin_atomic_sequence for more details.
storyboard_player_end_atomic_sequence:
debug: false
type: task
definitions: queue|player
script:
- if !<[player].flag[storyboard_atomic].if_null[<map[]>].contains[<[queue].script.name>]>:
- debug error "Tried ending storyboard atomic sequence <[queue].script.name> before storyboard_player_begin_atomic_sequence was called! (Player UUID <[player].uuid>)"
- stop
- waituntil !<player.has_flag[textbox_state]>
- wait 5t
- flag <[player]> storyboard_atomic:<[player].flag[storyboard_atomic].if_null[<map[]>].exclude[<[queue].script.name>]>
## Internal only! ## Internal only!
storyboard_player_freeze_check: storyboard_player_freeze_check:
debug: false debug: false
@@ -68,3 +99,12 @@ storyboard_player_freeze_check:
on player joins: on player joins:
- if <player.has_flag[storyboard_freeze_speed]>: - if <player.has_flag[storyboard_freeze_speed]>:
- run storyboard_player_unfreeze def.player:<player> - run storyboard_player_unfreeze def.player:<player>
storyboard_player_atomic_sequence_disconnect_handler:
debug: false
type: world
events:
on player quit:
- flag <player> storyboard_atomic:!
on player joins:
- flag <player> storyboard_atomic:!

View File

@@ -4,7 +4,10 @@ tabcomplete_config:
groups: groups:
default: default:
commands: commands:
# please keep these here...
- plugins - plugins
- denizenclickable
# anything else
- sit - sit
- lay - lay
- crawl - crawl