From 62529a352ce1b36a4f478c2612b808ed322f8790 Mon Sep 17 00:00:00 2001 From: gnembon Date: Sun, 22 Dec 2019 02:26:40 -0400 Subject: [PATCH] camera update --- .../java/carpet/script/CarpetExpression.java | 15 +- src/main/java/carpet/script/Expression.java | 16 +- .../java/carpet/script/value/BlockValue.java | 11 +- .../java/carpet/script/value/EntityValue.java | 33 ++ .../resources/assets/carpet/scripts/camera.sc | 357 ++++++++++++------ 5 files changed, 312 insertions(+), 120 deletions(-) diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index c5cbe225..95fe343c 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -106,7 +106,6 @@ import java.util.function.BiFunction; import java.util.function.Predicate; import java.util.stream.Collectors; -import static carpet.script.value.NBTSerializableValue.nameFromRegistryId; import static java.lang.Math.abs; import static java.lang.Math.max; import static java.lang.Math.min; @@ -2865,6 +2864,7 @@ public class CarpetExpression CarpetContext cc = (CarpetContext)c; BlockState targetBlock = null; BlockValue.VectorLocator pointLocator; + boolean interactable = true; String name; try { @@ -2872,8 +2872,14 @@ public class CarpetExpression name = nameValue instanceof NullValue ? "" : nameValue.getString(); pointLocator = BlockValue.locateVec(cc, lv, 1, true); if (lv.size()>pointLocator.offset) - targetBlock = BlockValue.fromParams(cc, lv, pointLocator.offset, true).block.getBlockState(); - + { + BlockValue.LocatorResult blockLocator = BlockValue.fromParams(cc, lv, pointLocator.offset, true, true); + if (blockLocator.block != null) targetBlock = blockLocator.block.getBlockState(); + if (lv.size() > blockLocator.offset) + { + interactable = lv.get(blockLocator.offset).evalValue(c, Context.BOOLEAN).getBoolean(); + } + } } catch (IndexOutOfBoundsException e) { @@ -2901,6 +2907,7 @@ public class CarpetExpression armorstand.setNoGravity(true); armorstand.setInvisible(true); armorstand.setInvulnerable(true); + armorstand.getDataTracker().set(ArmorStandEntity.ARMOR_STAND_FLAGS, (byte)(interactable?8 : 16|8)); cc.s.getWorld().spawnEntity(armorstand); EntityValue result = new EntityValue(armorstand); return (_c, _t) -> result; @@ -3023,7 +3030,7 @@ public class CarpetExpression this.expr.addLazyFunction("current_dimension", 0, (c, t, lv) -> { ServerCommandSource s = ((CarpetContext)c).s; - Value retval = new StringValue(nameFromRegistryId(Registry.DIMENSION.getId(s.getWorld().dimension.getType()))); + Value retval = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.DIMENSION.getId(s.getWorld().dimension.getType()))); return (cc, tt) -> retval; }); diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index facb3504..4677800f 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -1198,16 +1198,14 @@ public class Expression implements Cloneable { Value v1 = lv1.evalValue(c, Context.BOOLEAN); if (!v1.getBoolean()) return (cc, tt) -> v1; - Value v2 = lv2.evalValue(c, Context.BOOLEAN); - return v2.getBoolean() ? ((cc, tt) -> v2) : LazyValue.FALSE; + return lv2; }); addLazyBinaryOperator("||", precedence.get("or||"), false, (c, t, lv1, lv2) -> { Value v1 = lv1.evalValue(c, Context.BOOLEAN); if (v1.getBoolean()) return (cc, tt) -> v1; - Value v2 = lv2.evalValue(c, Context.BOOLEAN); - return v2.getBoolean() ? ((cc, tt) -> v2) : LazyValue.FALSE; + return lv2; }); addBinaryOperator("~", precedence.get("attribute~:"), true, Value::in); @@ -1928,7 +1926,7 @@ public class Expression implements Cloneable LazyValue expr = lv.get(1); //scoping LazyValue _val = c.getVariable("_"); - LazyValue _iter = c.getVariable("_i"); + LazyValue _ite = c.getVariable("_i"); int successCount = 0; for (int i=0; iterator.hasNext(); i++) { @@ -1959,7 +1957,7 @@ public class Expression implements Cloneable //revering scope ((AbstractListValue) rval).fatality(); c.setVariable("_", _val); - c.setVariable("_i", _iter); + c.setVariable("_i", _ite); long promiseWontChange = successCount; return (cc, tt) -> new NumericValue(promiseWontChange); }); @@ -1988,15 +1986,18 @@ public class Expression implements Cloneable //scoping LazyValue _val = c.getVariable("_"); LazyValue _acc = c.getVariable("_a"); + LazyValue _ite = c.getVariable("_i"); - while (iterator.hasNext()) + for (int i=0; iterator.hasNext(); i++) { Value next = iterator.next(); String var = next.boundVariable; next.bindTo("_"); Value promiseWontChangeYou = acc; + int seriously = i; c.setVariable("_a", (cc, tt) -> promiseWontChangeYou.bindTo("_a")); c.setVariable("_", (cc, tt) -> next); + c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i")); try { acc = expr.evalValue(c, t); @@ -2016,6 +2017,7 @@ public class Expression implements Cloneable ((AbstractListValue) rval).fatality(); c.setVariable("_a", _acc); c.setVariable("_", _val); + c.setVariable("_i", _ite); Value hopeItsEnoughPromise = acc; return (cc, tt) -> hopeItsEnoughPromise; diff --git a/src/main/java/carpet/script/value/BlockValue.java b/src/main/java/carpet/script/value/BlockValue.java index 9a460338..da9704d0 100644 --- a/src/main/java/carpet/script/value/BlockValue.java +++ b/src/main/java/carpet/script/value/BlockValue.java @@ -130,15 +130,24 @@ public class BlockValue extends Value public static LocatorResult fromParams(CarpetContext c, List params, int offset) { - return fromParams(c, params,offset, false); + return fromParams(c, params,offset, false, false); } public static LocatorResult fromParams(CarpetContext c, List params, int offset, boolean acceptString) + { + return fromParams(c, params,offset, acceptString, false); + } + + public static LocatorResult fromParams(CarpetContext c, List params, int offset, boolean acceptString, boolean optional) { try { Value v1 = params.get(0 + offset).evalValue(c); //add conditional from string name + if (optional && v1 instanceof NullValue) + { + return new LocatorResult(null, 1+offset); + } if (acceptString && v1 instanceof StringValue) { return new LocatorResult(fromString(v1.getString()), 1+offset); diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 61760579..f1e44d99 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -772,6 +772,39 @@ public class EntityValue extends Value else e.noClip = v.getBoolean(); }); + put("effect", (e, v) -> + { + if (!(e instanceof LivingEntity)) return; + LivingEntity le = (LivingEntity)e; + if (v == null) + le.clearPotionEffects(); + else if (v instanceof ListValue) + { + List lv = ((ListValue) v).getItems(); + if (lv.size() >= 2 && lv.size() <= 5) + { + String effectName = lv.get(0).getString(); + if (effectName.startsWith("minecraft:")) + effectName = "minecraft:"+effectName; + StatusEffect effect = Registry.STATUS_EFFECT.get(new Identifier(effectName)); + if (effect == null) + throw new InternalExpressionException("Wrong effect name: "+effectName); + int duration = (int)NumericValue.asNumber(lv.get(1)).getLong(); + int amplifier = 0; + if (lv.size() > 2) + amplifier = (int)NumericValue.asNumber(lv.get(2)).getLong(); + boolean showParticles = true; + if (lv.size() > 3) + showParticles = lv.get(3).getBoolean(); + boolean showIcon = true; + if (lv.size() > 4) + showIcon = lv.get(4).getBoolean(); + le.addPotionEffect(new StatusEffectInstance(effect, duration, amplifier, showParticles, showIcon)); + return; + } + } + throw new InternalExpressionException("'effect' needs either no arguments (clear) or effect name, duration, and optional amplifier, show particles and show icon"); + }); // gamemode // spectate diff --git a/src/main/resources/assets/carpet/scripts/camera.sc b/src/main/resources/assets/carpet/scripts/camera.sc index 4c61f8e9..100f9b98 100644 --- a/src/main/resources/assets/carpet/scripts/camera.sc +++ b/src/main/resources/assets/carpet/scripts/camera.sc @@ -35,31 +35,66 @@ __command() -> '' ); +// camera position for the player sticks out of their eyes +__camera_position(player) -> (player~'location' + l(0, player~'eye_height',0,0,0)); + +__update() -> +( + undef('global_path_precalculated'); + global_needs_updating = true; +); + start() -> ( p = player(); - global_points = l(l(p ~ 'location', 0,'sharp')); - undef('global_path_precalculated'); - str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z') + global_points = l(l(__camera_position(p), 0,'sharp')); + __update(); + show(); + str('Started path at %.1f %.1f %.1f', p~'x', p~'y', p~'z'); ); add(delay) -> ( p = player(); - mode = 'sharp'; + mode = 'smooth'; //mode is currently unused, run_path does always sharp, gauss interpolator is always smooth // but this option could be used could be used at some point by more robust interpolators - __add_path_segment(p ~ 'location', delay, mode); - str('Added point %d: %.1f %.1f %.1f', length(global_points), p~'x', p~'y', p~'z') + __add_path_segment(__camera_position(p), round(60*delay), mode); + __print_path_size(); ); + +prepend(delay) -> +( + p = player(); + mode = 'smooth'; + __prepend_path_segment(__camera_position(p), round(60*delay), mode); + __print_path_size(); +); + __add_path_segment(vector, duration, mode) -> ( - undef('global_path_precalculated'); if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point')); if(!global_points, exit('Cannot add point to path that didn\'t started yet!')); l(v, end_time, m) = global_points:(-1); vector:(-2) = __adjusted_rot(v:(-2) , vector:(-2) ); - global_points += l(vector, end_time+duration, mode) + global_points += l(vector, end_time+duration, mode); + __update(); ); + +__prepend_path_segment(vector, duration, mode) -> +( + if ( (l('sharp','smooth') ~ mode) == null, exit('use smooth or sharp point')); + if(!global_points, exit('Cannot add point to path that didn\'t started yet!')); + l(v, start_time, m) = global_points:(-1); + vector:(-2) = __adjusted_rot(v:(-2), vector:(-2)); + new_points = l(l(vector, 0, mode)); + for (global_points, + _:1 += duration; + new_points += _; + ); + global_points = new_points; + __update(); +); + // adjusts current rotation so we don\'t spin around like crazy __adjusted_rot(previous_rot, current_rot) -> ( @@ -71,79 +106,80 @@ __adjusted_rot(previous_rot, current_rot) -> __print_path_size() -> ( - __assert_valid_for_motion(); - print("Path length: %d points, 0.2f secs at 60fps", length(global_points), global_points:(-1):1/60); + if (!__assert_valid_for_motion(), + print(str('%d points, %.1f secs', length(global_points), global_points:(-1):1/60)); + ); + ''; ); repeat(times, last_section_duration) -> ( - __assert_valid_for_motion(); - undef('global_path_precalculated'); + if (err = __assert_valid_for_motion(), exit(err)); positions = map(global_points, _:0); modes = map(global_points, _:(-1)); durations = map(global_points, global_points:(_i+1):1 - _:1 ); - durations:(-1) = last_section_duration; + durations:(-1) = round(60*last_section_duration); loop(times, loop( length(positions), __add_path_segment(positions:_, durations:_, modes:_) ) ); + __update(); __print_path_size(); ''; ); -speed(percentage) -> +stretch(percentage) -> ( - __assert_valid_for_motion(); + if (err = __assert_valid_for_motion(), exit(err)); undef('global_path_precalculated'); if (percentage < 25 || percentage > 400, - exit('path speed can only be speed, or slowed down 4 times. Recall command for larger changes') + exit('path speed can only be speed, or slowed down 4 times. Re-call command for larger changes') ); ratio = percentage/100; previous_path_length = global_points:(-1):1; for(global_points, _:1 = _:1*ratio ); - undef('global_path_precalculated'); - str('path %s from %d to %d ticks (%.2f seconds with 60fps)', + __update(); + str('path %s from %.2f to %.2f seconds', if(ratio<1,'shortened','extended'), - previous_path_length, - global_points:(-1):1, + previous_path_length/60, global_points:(-1):1/60 ) ); + select_interpolation(method) -> ( - undef('global_path_precalculated'); __prepare_path_if_needed() -> __prepare_path_if_needed_generic(); // each supported method needs to specify its __find_position_for_point to trace the path // accepting segment number, and position in the segment // or optionally __prepare_path_if_needed, if path is inefficient to compute point by point - if ( - method == 'linear', - ( - __find_position_for_point(s, p) -> __find_position_for_linear(s, p) - ), + global_interpolator = if ( + method == 'linear', '__interpolator_linear', + method == 'cr', '__interpolator_cr', + method == 'gauss', _(s, p) -> __interpolator_gauss(s, p, 0), method ~ '^gauss_', - ( - type = method - 'gauss_'; - global_interpol_option = if(type=='auto',0,number(type)); - __find_position_for_point(s, p) -> __find_position_for_gauss(s, p) - ), - - exit('Choose one of the following methods: linear, gauss:auto, gauss:') + ( + type = method - 'gauss_'; + variance = number(type); + _(s, p, outer(variance)) -> __interpolator_gauss(s, p, variance); + ), + exit('Choose one of the following methods: linear, gauss, gauss_, cr') ); + __update(); 'Ok' ); -select_interpolation('gauss_auto'); +select_interpolation('cr'); + __assert_valid_for_motion() -> ( - if(!global_points, exit('Path not defined yet')); - if(length(global_points)<2, exit('Path not complete - add more points')); + if(!global_points, return('Path not defined yet')); + if(length(global_points)<2, return('Path not complete - add more points')); null ); __get_path_at(segment, start, index) -> ( v = global_path_precalculated:(start+index); if(v == null, - v = __find_position_for_point(segment, index); + v = call(global_interpolator, segment, index); global_path_precalculated:(start+index) = v ); v @@ -153,16 +189,11 @@ __invalidate_points_cache() -> global_path_precalculated = map(range(global_poin global_showing_path = false; global_playing_path = false; -stop() -> +hide() -> ( - if (!global_playing_path && !global_showing_path, return('You can only stop playing or showing the path')); if (global_showing_path, global_showing_path = false; - print('stopped showing path'); - ); - if (global_playing_path, - global_playing_path = false; - print('stopped playing path'); + print('Stopped showing path'); ); '' ); @@ -170,97 +201,198 @@ stop() -> clear() -> ( global_points = l(); - undef('global_path_precalculated'); + __update(); ); global_needs_updating = false; global_selected_point = null; +global_markers = null; + +__distsq(vec1, vec2) -> reduce(vec1 - vec2, _a + _*_, 0); + +__closest_point_to_center(center, points) -> +( + dd = __distsq(points:0, center); + for(points, + d = __distsq(_, center); + if( d +( + if (!global_points, return()); + if (!global_showing_path, return()); + p = player(); + selected_point = __closest_point_to_center( + p~'pos'+l(0,p~'eye_height',0), + map(global_points, slice(_:0, 0, 3)) + ); + global_selected_point = if (global_selected_point == selected_point, null, selected_point); + global_needs_updating = true; + // no need to _update since path is still valid + +); + +__on_player_attacks_entity(p, e) -> +( + if (e~'type' == 'armor_stand' && query(e, 'has_tag', '__scarpet_marker_camera') && global_markers, + for (global_markers, + if(_==e, + global_selected_point = if (global_selected_point == _i, null, _i); + global_needs_updating = true; + return(); + ) + ); + ); +); + +global_show_thread = null; show() -> ( p = player(); - __assert_valid_for_motion(); __print_path_size(); + if (global_showing_path, return ()); global_showing_path = true; global_needs_updating= false; - def __create_markers()-> + __create_markers() -> ( - map(global_points, - m = create_marker(null, _:0, 'observer'); - if (global_selected_point && _i == global_selected_point, - modify(m,'effect','glowing',72000); + map(global_points || l(), + is_selected = global_selected_point != null && _i == global_selected_point; + caption = if (_i == 0, 'start', str('%.1fs', global_points:_i:1/60); ); + if (is_selected && _i > 0, + caption += str(' (%.1f current segment)', (global_points:_i:1 - global_points:(_i-1):1)/60); + ); + m = create_marker(caption, _:0, 'observer'); + if (is_selected, + modify(m,'effect','glowing',72000, 0, false, false); ); m ); ); + __show_path_tick() -> + ( + if (__assert_valid_for_motion(), return()); + __prepare_path_if_needed(); + loop(100, + segment = floor(rand(length(global_points)-1)); + particle_type = if ((segment+1) == global_selected_point, 'dust 0.1 0.9 0.1 1','dust 0.6 0.6 0.6 1'); + start = global_points:segment:1; + end = global_points:(segment+1):1; + index = floor(rand(end-start)); + l(x, y, z) = slice(__get_path_at(segment, start, index), 0, 3); + particle(particle_type, x, y, z, 1, 0, 0) + ); + null + ); + task( _(outer(p)) -> ( - markers = __create_markers(); + global_markers = __create_markers(); loop(7200, if(!global_showing_path, break()); if (global_needs_updating, global_needs_updating = false; - for(markers, modify(_,'remove'); - markers = __create_markers(); + for(global_markers, modify(_,'remove')); + global_markers = __create_markers(); ); - _show_path_tick('dust 0.9 0.1 0.1 1', 100); + __show_path_tick(); sleep(100); ); - for(markers, modify(_,'remove'); + for(global_markers, modify(_,'remove')); + global_markers = null; + global_showing_path = false; + )); + ''; +); + +move() -> +( + if (global_selected_point == null, return()); + if (!global_showing_path, return()); + global_points:global_selected_point:0 = __camera_position(player()); + __update(); +); + +duration(amount) -> +( + if (!global_selected_point, return()); // skip nulls, and 0 (first one) + if (!global_showing_path, return()); + duration = number(amount); + new_ticks = round(duration * 60); + if (new_ticks < 10, return()); + previous_ticks = global_points:global_selected_point:1-global_points:(global_selected_point-1):1; + delta = new_ticks - previous_ticks; + // adjust duration of all points after that. + for (range(global_selected_point, length(global_points)), + global_points:_:1 += delta; + ); + __update(); +); + +delete_point() -> +( + if (global_selected_point == null, return()); // skip nulls, and 0 (first one) + if (!global_showing_path, return()); + if (length(global_points) < 2, clear(); return()); + if (global_selected_point == 0, global_points:1:1 = 0); + global_points = filter(global_points, _i != global_selected_point); + __update(); +); + +trim_path() -> +( + if (global_selected_point == null, return()); // skip nulls, and 0 (first one) + if (!global_showing_path, return()); + global_points = slice(global_points, 0, global_selected_point); + __update(); +); + +play() -> +( + p = player(); + if (err = __assert_valid_for_motion(), exit(err)); + __prepare_path_if_needed(); + task( _(outer(fps), outer(p)) -> ( + sleep(1000); + global_playing_path = true; + mspt = 1000 / 60; + start_time = time(); + point = 0; + player_offset = l(0,p~'eye_height',0,0,0); + try ( + loop( length(global_points)-1, segment = _; + start = global_points:segment:1; + end = global_points:(segment+1):1; + loop(end-start, + if (p~'sneaking', global_playing_path = false); + if (!global_playing_path, throw()); + v = __get_path_at(segment, start, _)-player_offset; + modify(p, 'location', v); + point += 1; + end_time = time(); + took = end_time - start_time; + if (took < mspt, sleep(mspt-took)); + start_time = time() + ) + ); + ); + sleep(1000); + global_playing_path = false; print('Done!'); )); ''; ); -play(fps) -> -( - p = player(); - __assert_valid_for_motion(); - __prepare_path_if_needed(); - global_playing_path = true; - task( _(outer(fps), outer(p)) -> ( - sleep(1000); - mspt = 1000 / fps; - start_time = time(); - point = 0; - loop( length(global_points)-1, segment = _; - if (p~'sneaking', global_playing_path = false); - if (!global_playing_path, break()); - start = global_points:segment:1; - end = global_points:(segment+1):1; - loop(end-start, - v = __get_path_at(segment, start, _); - modify(p, 'location', v); - point += 1; - end_time = time(); - took = end_time - start_time; - if (took < mspt, sleep(mspt-took)); - start_time = time() - ) - ); - sleep(1000); - print('Done!'); - )); - ''; -); -_show_path_tick(particle_type, total) -> -( - __assert_valid_for_motion(); - __prepare_path_if_needed(); - loop(total, - segment = floor(rand(length(global_points)-1)); - start = global_points:segment:1; - end = global_points:(segment+1):1; - index = floor(rand(end-start)); - l(x, y, z) = slice(__get_path_at(segment, start, index), 0, 3); - particle(particle_type, x, y, z, 1, 0, 0) - ); - null -); __prepare_path_if_needed_generic() -> ( if(!global_path_precalculated, __invalidate_points_cache()) ); -__find_position_for_linear(segment, point) -> +__interpolator_linear(segment, point) -> ( l(va, start, mode_a) = global_points:segment; l(vb, end, mode_b) = global_points:(segment+1); @@ -271,16 +403,14 @@ __find_position_for_linear(segment, point) -> //(1/sqrt(2*pi*d*d))*euler^(-((x-miu)^2)/(2*d*d)) // but we will be normalizing anyways, so who cares __norm_prob(x, miu, d) -> euler^(-((x-miu)^2)/(2*d*d)); -__find_position_for_gauss(from_index, point) -> +__interpolator_gauss(from_index, point, deviation) -> ( - dev = global_interpol_option; components = l(); path_point = global_points:from_index:1; - try( for(range(from_index+1, length(global_points)), l(v,ptime,mode) = global_points:_; - dev = if (global_interpol_option > 0, global_interpol_option, + dev = if (deviation > 0, deviation, devs = l(); if (_+1 < length(global_points), devs += global_points:(_+1):1-ptime); if (_-1 >= 0, devs += ptime-global_points:(_-1):1); @@ -295,7 +425,7 @@ __find_position_for_gauss(from_index, point) -> try( for(range(from_index, -1, -1), l(v,ptime,mode) = global_points:_; - dev = if (global_interpol_option > 0, global_interpol_option, + dev = if (deviation > 0, deviation, devs = l(); if (_+1 < length(global_points), devs += global_points:(_+1):1-ptime); if (_-1 >= 0, devs += ptime-global_points:(_-1):1); @@ -309,4 +439,15 @@ __find_position_for_gauss(from_index, point) -> ); total = rtotal+ltotal; reduce(components, _a+_:0*(_:1/total), l(0,0,0,0,0)) -) \ No newline at end of file +); + +__interpolator_cr(from_index, point) -> +( + total = global_points:(from_index+1):1 - global_points:from_index:1; + p__1 = global_points:(if(from_index == 0, 0, from_index-1)):0; + p_0 = global_points:from_index:0; + p_1 = global_points:(from_index+1):0; + p_2 = global_points:(if(from_index == (length(global_points)-2), -1, from_index+2)):0; + r = point/total; // ratio within segment + (r*((2-r)*r-1) * p__1 + (r*r*(3*r-5)+2) * p_0 + r*((4 - 3*r)*r + 1) * p_1 + (r-1)*r*r * p_2) / 2 +); \ No newline at end of file