1- import { AimAssistTargetMode , ButtonState , EffectTypes , EntityDamageCause , EntityHealthComponent , EntityInventoryComponent , EquipmentSlot , InputButton , InputPermissionCategory , ItemLockMode , ItemStack , MolangVariableMap , Player , RGBA , StartupEvent , system , Vector3 , world } from "@minecraft/server" ;
1+ import { ButtonState , EntityDamageCause , EntityHealthComponent , EntityInventoryComponent , EquipmentSlot , InputButton , InputPermissionCategory , ItemLockMode , ItemStack , MolangVariableMap , Player , StartupEvent , system , Vector3 , world } from "@minecraft/server" ;
22import { MinecraftEffectTypes } from "@minecraft/vanilla-data" ;
3- import { directionVector , drawLine , entityCenter , entityGetSlot , entityHasSlotTag , spawnLine } from "../../utility" ;
3+ import { directionVector , drawLine , EFFECT_INFINITE , entityCenter , entityHasSlotTag , playSoundFrom } from "../../utility" ;
44import { V3 } from "../../math/vectorUtils" ;
55import { deg2Rad , PI , remap , TAU } from "../../math/general" ;
6- import { VECTOR2_ZERO , VECTOR3_BACK , VECTOR3_ZERO } from "@minecraft/math" ;
6+ import { VECTOR3_ZERO } from "@minecraft/math" ;
77
8- let light : ItemStack
9- const maxAirJumps : number = 10
10- const lightTrailThickness : number = 0.15
11- const lightTrailLengthMax : number = 10.0
12- const lightTrailLengthMin : number = 1.5
13- const fCooldownTime = 80
14- const flyTurnThreshold = Math . cos ( deg2Rad ( 60 ) ) ;
158enum States {
169 NORMAL ,
1710 FLYING ,
1811 MOON_JUMP ,
1912 DASH
2013}
2114
22- class LightVisual {
15+ let light : ItemStack
16+ const maxAirJumps : number = 10
17+ const lightTrailThickness : number = 0.15
18+ const lightSpinTrailThickness : number = 0.075
19+ const fCooldownTime = 80
20+ const flyTurnThreshold = Math . cos ( deg2Rad ( 60 ) ) ;
21+ const flyTurnCooldownTime : number = 10
22+ const airborneAirtime = 3
23+
24+ const moonJumpForce = 0.75
25+ const moonJumpSize1 = 0.5
26+ const moonJumpSize2 = 1.3 / 2
27+ const moonJumpSize3 = 1.5 / 2
28+ const moonJumpPos1 = - 0.0
29+ const moonJumpPos2 = - 0.2
30+ const moonJumpPos3 = - 0.4
31+
32+ const dashCooldownTime = 5
33+ const dashForce = 1.0
34+ const dashLift = 0.1
35+ const dashSlowFallTime = 5
36+
37+ const flightSpeedMin = 0.1
38+ const flightSpeedMax = 1.0
39+ const flightStarSize = 2.0
40+ const flightStarFrames = 5
41+ const spinTrailTurnsPerSecond = 1.5
42+ const spinTrailRadius = 0.5
43+ const flySoundDuration = 7 //8.58
44+
45+ class LightFlightVisual {
2346 lastSpin1 : Vector3 | undefined
2447 lastSpin2 : Vector3 | undefined
2548 lastTrail : Vector3 | undefined
2649 angle : number = 0.0
2750}
28- const flyTurnCooldownTime : number = 10
2951const flyTurnCooldown : Map < string , number > = new Map ( )
3052const flySoundCooldown : Map < string , number > = new Map ( )
3153const airTime : Map < string , number > = new Map ( )
3254const ignoreFall : Map < string , boolean > = new Map ( )
33- const airJumps : Map < string , number > = new Map ( )
55+ const dashCooldown : Map < string , number > = new Map ( )
3456const flyingAngles : Map < string , Vector3 > = new Map ( )
35- const visuals : Map < string , LightVisual > = new Map ( )
57+ const lightFlightVisuals : Map < string , LightFlightVisual > = new Map ( )
3658
3759// Defining setters for ability cooldowns
3860Object . defineProperties ( Player . prototype , {
@@ -60,6 +82,41 @@ Object.defineProperties(Player.prototype, {
6082 get ( ) { return this . getDynamicProperty ( "whynot:light_state" ) ?? States . NORMAL } ,
6183 set ( state : number ) { this . setDynamicProperty ( "whynot:light_state" , state ) } ,
6284 } ,
85+ flyTurnCooldown : {
86+ get ( ) { return flyTurnCooldown . get ( this . id ) ?? 0 } ,
87+ set ( v : number ) { flyTurnCooldown . set ( this . id , v ) }
88+ } ,
89+ center : {
90+ get ( ) { return entityCenter ( this ) ?? this . location }
91+ } ,
92+ flySoundCooldown : {
93+ get ( ) { return flySoundCooldown . get ( this . id ) ?? 0 } ,
94+ set ( v : number ) { flySoundCooldown . set ( this . id , v ) }
95+ } ,
96+ airTime : {
97+ get ( ) { return airTime . get ( this . id ) ?? 0 } ,
98+ set ( v : number ) { airTime . set ( this . id , v ) }
99+ } ,
100+ ignoreFall : {
101+ get ( ) { return ignoreFall . get ( this . id ) ?? false } ,
102+ set ( v : boolean ) { ignoreFall . set ( this . id , v ) }
103+ } ,
104+ airJumps : {
105+ get ( ) { return this . getDynamicProperty ( "whynot:air_jumps" ) ?? 10 } ,
106+ set ( v : number ) { this . setDynamicProperty ( "whynot:air_jumps" , v ) } ,
107+ } ,
108+ flyingAngles : {
109+ get ( ) { return flyingAngles . get ( this . id ) ?? VECTOR3_ZERO } ,
110+ set ( v : Vector3 ) { flyingAngles . set ( this . id , v ) }
111+ } ,
112+ lightFlightVisuals : {
113+ get ( ) { return lightFlightVisuals . get ( this . id ) ?? new LightFlightVisual ( ) } ,
114+ set ( v : LightFlightVisual ) { lightFlightVisuals . set ( this . id , v ) }
115+ } ,
116+ dashCooldown : {
117+ get ( ) { return dashCooldown . get ( this . id ) ?? 0 } ,
118+ set ( v : number ) { dashCooldown . set ( this . id , v ) }
119+ }
63120} ) ;
64121
65122// Eat fruit component
@@ -87,43 +144,47 @@ function changeState(player: Player, state: States) {
87144 switch ( player . state ) {
88145 case States . FLYING :
89146 player . triggerEvent ( "whynot:add_static_player" ) ;
90- flyingAngles . set ( player . id , player . getViewDirection ( ) ) ;
91- player . addEffect ( MinecraftEffectTypes . Invisibility , 20000000 , { showParticles : false } )
92-
93- player . playSound ( "light_dash ")
94- player . playSound ( "light_beam_start" )
95- visuals . set ( player . id , new LightVisual ( ) )
96- flySoundCooldown . set ( player . id , 0 )
97- flyTurnCooldown . set ( player . id , 0 )
147+ player . addEffect ( MinecraftEffectTypes . Invisibility , EFFECT_INFINITE , { showParticles : false } )
148+
149+ playSoundFrom ( player , "light_dash" )
150+ playSoundFrom ( player , "light_beam_start ")
151+ player . flyingAngles = player . getViewDirection ( )
152+ player . lightFlightVisuals = new LightFlightVisual ( )
153+ player . flySoundCooldown = 0
154+ player . flyTurnCooldown = 0
98155 player . inputPermissions . setPermissionCategory ( InputPermissionCategory . Movement , false )
99156 break ;
100157
101158 case States . MOON_JUMP :
102- ignoreFall . set ( player . id , true )
103- player . applyImpulse ( V3 . make ( 0 , - player . getVelocity ( ) . y + 0.75 , 0 ) )
104- let molangSize1 = new MolangVariableMap ( ) ; molangSize1 . setFloat ( "size" , 1.0 / 2.0 )
105- let molangSize2 = new MolangVariableMap ( ) ; molangSize2 . setFloat ( "size" , 1.3 / 2.0 )
106- let molangSize3 = new MolangVariableMap ( ) ; molangSize3 . setFloat ( "size" , 1.5 / 2.0 )
107- player . spawnParticle ( "whynot:moon_jump" , V3 . add ( player . location , V3 . make ( 0 , 0 , 0 ) ) , molangSize1 )
108- player . spawnParticle ( "whynot:moon_jump" , V3 . add ( player . location , V3 . make ( 0 , - 0.2 , 0 ) ) , molangSize2 )
109- player . spawnParticle ( "whynot:moon_jump" , V3 . add ( player . location , V3 . make ( 0 , - 0.4 , 0 ) ) , molangSize3 )
110- player . playSound ( "moon_jump" )
111- airJumps . set ( player . id , ( airJumps . get ( player . id ) ?? 0 ) - 1 )
159+ player . airJumps --
160+ player . ignoreFall = true
161+ player . applyImpulse ( V3 . make ( 0 , - player . getVelocity ( ) . y + moonJumpForce , 0 ) )
162+ playSoundFrom ( player , "moon_jump" )
163+
164+ // Particles
165+ let molangSize1 = new MolangVariableMap ( ) ; molangSize1 . setFloat ( "size" , moonJumpSize1 )
166+ let molangSize2 = new MolangVariableMap ( ) ; molangSize2 . setFloat ( "size" , moonJumpSize2 )
167+ let molangSize3 = new MolangVariableMap ( ) ; molangSize3 . setFloat ( "size" , moonJumpSize3 )
168+ const pos = player . location
169+ player . spawnParticle ( "whynot:moon_jump" , V3 . make ( pos . x , pos . y + moonJumpPos1 , pos . z ) , molangSize1 )
170+ player . spawnParticle ( "whynot:moon_jump" , V3 . make ( pos . x , pos . y + moonJumpPos2 , pos . z ) , molangSize2 )
171+ player . spawnParticle ( "whynot:moon_jump" , V3 . make ( pos . x , pos . y + moonJumpPos3 , pos . z ) , molangSize3 )
112172
113173 changeState ( player , States . NORMAL )
114174 break ;
115175
116176 case States . DASH :
117177 const dir = player . getViewDirection ( )
118- let vel = V3 . normalize ( V3 . make ( dir . x , 0 , dir . z ) )
178+ const vel = V3 . normalize ( V3 . make ( dir . x , 0 , dir . z ) )
119179 const dirMap = new MolangVariableMap ( ) ;
120180 dirMap . setVector3 ( "direction" , V3 . scale ( vel , - 1 ) )
121- player . spawnParticle ( "whynot:dash" , entityCenter ( player ) ?? player . location , dirMap )
122- player . addEffect ( MinecraftEffectTypes . SlowFalling , 5 , { showParticles : false } )
123- player . playSound ( "dash" )
181+ player . spawnParticle ( "whynot:dash" , player . center , dirMap )
182+ player . addEffect ( MinecraftEffectTypes . SlowFalling , dashSlowFallTime , { showParticles : false } )
183+ playSoundFrom ( player , "dash" )
124184
185+ player . dashCooldown = dashCooldownTime
125186 player . clearVelocity ( )
126- player . applyImpulse ( V3 . add ( V3 . scale ( vel , 1.0 ) , V3 . make ( 0 , 0.1 , 0 ) ) )
187+ player . applyImpulse ( V3 . make ( vel . x * dashForce , dashLift , vel . z * dashForce ) )
127188
128189 changeState ( player , States . NORMAL )
129190 break ;
@@ -148,26 +209,29 @@ export function main() {
148209 world . getAllPlayers ( ) . forEach ( player => changeState ( player , States . NORMAL ) )
149210 world . beforeEvents . entityHurt . subscribe ( ev => {
150211 if ( ev . damageSource . cause != EntityDamageCause . fall ) return
151- if ( ! ev . hurtEntity . isValid || ev . hurtEntity . typeId != "minecraft:player" ) return
152- if ( ! ignoreFall . get ( ev . hurtEntity . id ) ) return
212+
213+ const player = ev . hurtEntity as Player
214+ if ( ! player . isValid || player . typeId != "minecraft:player" ) return
215+ if ( ! player . ignoreFall ) return
153216
154217 ev . cancel = true
155218 } )
156219 world . afterEvents . playerButtonInput . subscribe ( ( { button, newButtonState, player} ) => {
157220 const pressed = newButtonState == ButtonState . Pressed
158221 if ( ! entityHasSlotTag ( player , EquipmentSlot . Mainhand , "whynot:light" ) ) return ;
159222
160- if ( button == InputButton . Sneak && newButtonState == ButtonState . Pressed && player . state == States . NORMAL ) {
223+ if ( button == InputButton . Sneak && newButtonState == ButtonState . Pressed &&
224+ player . state == States . NORMAL && player . dashCooldown <= 0 ) {
161225 changeState ( player , States . DASH )
162226 }
163227 if ( button != InputButton . Jump ) return
164- if ( ( ( airTime . get ( player . id ) ?? 0 ) > 2 ) && pressed && player . state == States . NORMAL ) {
228+ if ( player . airTime >= airborneAirtime && pressed && player . state == States . NORMAL ) {
165229 // Activate fly
166230 if ( player . fCooldown <= 0 && player . isSneaking ) {
167231 changeState ( player , States . FLYING )
168232 }
169233 // Moon jump
170- else if ( ( airJumps . get ( player . id ) ?? 0 ) > 0 ) {
234+ else if ( player . airJumps > 0 ) {
171235 changeState ( player , States . MOON_JUMP )
172236 }
173237 }
@@ -189,55 +253,54 @@ function mainTick() {
189253 // Set Airtime
190254 if ( player . isOnGround ) {
191255 if ( player . state != States . FLYING ) {
192- airJumps . set ( player . id , maxAirJumps )
193- ignoreFall . set ( player . id , false )
194-
256+ player . airJumps = maxAirJumps
257+ player . ignoreFall = false
195258 }
196- airTime . set ( player . id , 0 )
259+ player . airTime = 0
197260 }
198- else airTime . set ( player . id , ( airTime . get ( player . id ) ?? 0 ) + 1 )
261+ else player . airTime ++
199262
200263 if ( player . state == States . FLYING ) {
201- ignoreFall . set ( player . id , true )
264+ player . ignoreFall = true
202265 player . fCooldown = fCooldownTime
203266
204267 // get move dir
205268 const viewDir = player . getViewDirection ( ) ;
206- const addVel = V3 . normalize ( flyingAngles . get ( player . id ) ?? viewDir ) ;
269+ const addVel = V3 . normalize ( player . flyingAngles ) ;
207270
208271 // Scale speed from health
209272 const health = player . getComponent ( EntityHealthComponent . componentId ) as EntityHealthComponent ;
210- const speed = remap ( health . currentValue , health . effectiveMin , health . effectiveMax , 0.1 , 1.0 ) ;
273+ const speed = remap ( health . currentValue , health . effectiveMin , health . effectiveMax , flightSpeedMin , flightSpeedMax ) ;
211274 const vel = V3 . scale ( addVel , speed )
212275 player . clearVelocity ( ) ;
213276 player . applyImpulse ( vel ) ;
214277
215278 // Particles
216279 const starMap = new MolangVariableMap ( )
217- const center = entityCenter ( player ) ?? player . location
218- starMap . setFloat ( "size" , 2.0 )
219- starMap . setFloat ( "frame" , system . currentTick % 5 )
280+ const center = player . center
281+ starMap . setFloat ( "size" , flightStarSize )
282+ starMap . setFloat ( "frame" , system . currentTick % flightStarFrames )
220283 player . spawnParticle (
221284 "whynot:light_star" ,
222285 center ,
223286 starMap
224287 )
225288
226289 // Visuals
227- const playerVisuals = visuals . get ( player . id )
290+ const playerVisuals = player . lightFlightVisuals
228291 if ( playerVisuals ) {
229292 const matrix = V3 . getBasisMatrix ( addVel )
230- playerVisuals . angle += ( 1 / 20.0 ) * TAU * 1.5 * speed
293+ playerVisuals . angle += ( 1 / 20.0 ) * TAU * spinTrailTurnsPerSecond * speed
231294
232295 let angle = playerVisuals . angle
233- const spin1 = V3 . make ( Math . cos ( angle ) * 0.5 , Math . sin ( angle ) * 0.5 , 0 )
234- const spin2 = V3 . make ( Math . cos ( angle + PI ) * 0.5 , Math . sin ( angle + PI ) * 0.5 , 0 )
296+ const spin1 = V3 . make ( Math . cos ( angle ) * spinTrailRadius , Math . sin ( angle ) * spinTrailRadius , 0 )
297+ const spin2 = V3 . make ( Math . cos ( angle + PI ) * spinTrailRadius , Math . sin ( angle + PI ) * spinTrailRadius , 0 )
235298
236299 const add1 = V3 . add ( V3 . multiplyVectorByMatrix ( spin1 , matrix ) , center )
237300 const add2 = V3 . add ( V3 . multiplyVectorByMatrix ( spin2 , matrix ) , center )
238301 if ( playerVisuals . lastSpin1 && playerVisuals . lastSpin2 ) {
239- drawLine ( "whynot:light_trail" , player . dimension , add1 , playerVisuals . lastSpin1 , 0.075 )
240- drawLine ( "whynot:light_trail" , player . dimension , add2 , playerVisuals . lastSpin2 , 0.075 )
302+ drawLine ( "whynot:light_trail" , player . dimension , add1 , playerVisuals . lastSpin1 , lightSpinTrailThickness )
303+ drawLine ( "whynot:light_trail" , player . dimension , add2 , playerVisuals . lastSpin2 , lightSpinTrailThickness )
241304 }
242305 if ( playerVisuals . lastTrail )
243306 drawLine ( "whynot:light_trail" , player . dimension , center , playerVisuals . lastTrail , lightTrailThickness )
@@ -247,29 +310,26 @@ function mainTick() {
247310 }
248311
249312 // Get block in path and bounce if needed
250- const blockHit = player . dimension . getBlockFromRay ( entityCenter ( player ) ?? player . location , vel , { maxDistance : 4 , includeLiquidBlocks :false , includePassableBlocks : false } )
313+ const blockHit = player . dimension . getBlockFromRay ( player . location , addVel , { maxDistance : 4 , includeLiquidBlocks :false , includePassableBlocks : false } )
251314 if ( blockHit && blockHit . block . isSolid ) {
252315 const bounced = V3 . reflect ( vel , directionVector ( blockHit . face ) ) ;
253- flyingAngles . set ( player . id , bounced ) ;
254- flyTurnCooldown . set ( player . id , flyTurnCooldownTime )
255- player . playSound ( "light_dash" )
316+ player . flyingAngles = bounced
317+ player . flyTurnCooldown = flyTurnCooldownTime
318+ playSoundFrom ( player , "light_dash" )
256319 }
257320
258321 // If viewing angle change is significant change direction and did not bounce
259- else if ( V3 . dot ( viewDir , addVel ) < flyTurnThreshold && ( flyTurnCooldown . get ( player . id ) ?? 0 ) <= 0 ) {
260- flyingAngles . set ( player . id , viewDir ) ;
261- player . playSound ( "light_dash" )
322+ else if ( V3 . dot ( viewDir , addVel ) < flyTurnThreshold && player . flyTurnCooldown <= 0 ) {
323+ player . flyingAngles = viewDir ;
324+ playSoundFrom ( player , "light_dash" )
262325 }
263326
264327 // Reduce fly turn cooldown
265- let currFlyTurnCooldown = flyTurnCooldown . get ( player . id ) ?? 0 ;
266- if ( currFlyTurnCooldown > 0 ) flyTurnCooldown . set ( player . id , currFlyTurnCooldown - 1 ) ;
267-
268- let currSoundCooldon = flySoundCooldown . get ( player . id ) ?? 0 ;
269- if ( currSoundCooldon > 0 ) flySoundCooldown . set ( player . id , currSoundCooldon - 1 ) ;
328+ if ( player . flyTurnCooldown > 0 ) player . flyTurnCooldown -- ;
329+ if ( player . flySoundCooldown > 0 ) player . flySoundCooldown -- ;
270330 else {
271- player . playSound ( "light_beam_loop" , { volume : 10 } )
272- flySoundCooldown . set ( player . id , 7 ) //8.58
331+ playSoundFrom ( player , "light_beam_loop" )
332+ flySoundCooldown . set ( player . id , flySoundDuration )
273333 }
274334 }
275335
@@ -279,6 +339,7 @@ function mainTick() {
279339 if ( player . cCooldown > 0 ) player . cCooldown -- ;
280340 if ( player . vCooldown > 0 ) player . vCooldown -- ;
281341 if ( player . fCooldown > 0 ) player . fCooldown -- ;
342+ if ( player . dashCooldown > 0 ) player . dashCooldown -- ;
282343
283344 // If player is holding light display cooldowns
284345 if ( ! entityHasSlotTag ( player , EquipmentSlot . Mainhand , "whynot:light" ) ) {
0 commit comments