@@ -34,6 +34,7 @@ if SERVER then
3434 util .AddNetworkString (" FPGA_Upload" )
3535 util .AddNetworkString (" FPGA_Download" )
3636 util .AddNetworkString (" FPGA_OpenEditor" )
37+ util .AddNetworkString (" wire_fpga_editor_status" )
3738
3839 -- Reset
3940 function TOOL :Reload (trace )
@@ -204,6 +205,30 @@ if SERVER then
204205 end
205206 end )
206207
208+ local wire_fpga_event = {}
209+
210+ concommand .Add (" wire_fpga_event" , function (ply , command , args )
211+ local handler = wire_fpga_event [args [1 ]]
212+ if not handler then return end
213+ return handler (ply , args )
214+ end )
215+
216+ -- actual editor open/close handlers
217+
218+ function wire_fpga_event .editor_open (ply , args )
219+ net .Start (" wire_fpga_editor_status" )
220+ net .WriteEntity (ply )
221+ net .WriteBit (true )
222+ net .Broadcast ()
223+ end
224+
225+ function wire_fpga_event .editor_close (ply , args )
226+ net .Start (" wire_fpga_editor_status" )
227+ net .WriteEntity (ply )
228+ net .WriteBit (false )
229+ net .Broadcast ()
230+ end
231+
207232end
208233
209234
@@ -367,4 +392,241 @@ if CLIENT then
367392 end
368393
369394 end
395+
396+ local busy_players = WireLib .RegisterPlayerTable ()
397+ net .Receive (" wire_fpga_editor_status" , function (len )
398+ local ply = net .ReadEntity ()
399+ local status = net .ReadBit () ~= 0
400+ if not IsValid (ply ) or ply == LocalPlayer () then return end
401+
402+ busy_players [ply ] = status or nil
403+ end )
404+
405+ local min = math.min
406+ local surface_DrawPoly = surface .DrawPoly
407+ local surface_SetDrawColor = surface .SetDrawColor
408+
409+ local nodeColor = Color (100 ,100 ,100 ,255 )
410+ local lineColor = Color (70 , 160 , 255 , 255 )
411+
412+ local size = 100
413+ local padding = 100
414+
415+ local node1x , node1y = - padding * 1.5 , padding
416+ local node2x , node2y = padding * 1.5 , - padding
417+
418+ local anim = {
419+ tStart = 0 ,
420+ tEnd = 0 ,
421+ dir = 1 ,
422+ phase = 1 ,
423+ speed = 1 ,
424+ holdTime = 2.5 ,
425+ holdTimer = 0
426+ }
427+ local reversed = false
428+
429+ local node1 = { offset = 0 , vel = 0 }
430+ local node2 = { offset = 0 , vel = 0 }
431+
432+ local springStrength = 35
433+ local damping = 4
434+ local impulsePower = 100
435+
436+ local function UpdateSpring (node , ft )
437+ local force = - (springStrength * node .offset + damping * node .vel )
438+ node .vel = node .vel + force * ft
439+ node .offset = node .offset + node .vel * ft
440+ end
441+
442+ local curveSegments = 20
443+ local baseCurve = {}
444+ local thickness = 10
445+ local half = thickness * 0.5
446+
447+ local function BuildBaseCurve (dir )
448+
449+ baseCurve = {}
450+
451+ local startX , startY , endX , endY
452+ local cx1 , cy1 , cx2 , cy2
453+
454+ if dir == 1 then
455+ startX = node1x + size * 0.5
456+ startY = node1y
457+ endX = node2x - size * 0.5
458+ endY = node2y
459+
460+ cx1 , cy1 = 0 , startY
461+ cx2 , cy2 = 0 , endY
462+
463+ reversed = false
464+ else
465+ startX = node2x + size * 0.5
466+ startY = node2y
467+ endX = node1x - size * 0.5
468+ endY = node1y
469+
470+ cx1 , cy1 = startX + size * 2 , startY + size * 2
471+ cx2 , cy2 = endX - size * 2 , endY - size * 2
472+
473+ reversed = true
474+ end
475+
476+ local prevX , prevY
477+
478+ for i = 0 , curveSegments do
479+ local t = i / curveSegments
480+
481+ local it = 1 - t
482+ local it2 = it * it
483+ local it3 = it2 * it
484+ local t2 = t * t
485+ local t3 = t2 * t
486+
487+ local x =
488+ it3 * startX +
489+ 3 * it2 * t * cx1 +
490+ 3 * it * t2 * cx2 +
491+ t3 * endX
492+
493+ local y =
494+ it3 * startY +
495+ 3 * it2 * t * cy1 +
496+ 3 * it * t2 * cy2 +
497+ t3 * endY
498+
499+ if prevX then
500+ baseCurve [# baseCurve + 1 ] = {
501+ x1 = prevX ,
502+ y1 = prevY ,
503+ x2 = x ,
504+ y2 = y ,
505+ t1 = (i - 1 )/ curveSegments ,
506+ t2 = i / curveSegments
507+ }
508+ end
509+
510+ prevX , prevY = x , y
511+ end
512+ end
513+
514+ BuildBaseCurve (anim .dir )
515+
516+ local function DrawCachedCurve (tStart , tEnd )
517+
518+ surface_SetDrawColor (lineColor )
519+
520+ local startIndex = math.floor (tStart * curveSegments )
521+ local endIndex = math.floor (tEnd * curveSegments )
522+
523+ for i = startIndex + 1 , endIndex do
524+ local seg = baseCurve [i ]
525+ if seg then
526+
527+ local t1 = seg .t1
528+ local t2 = seg .t2
529+
530+ if reversed then
531+ t1 = 1 - t1
532+ t2 = 1 - t2
533+ end
534+
535+ local offset1 = node1 .offset * (1 - t1 ) + node2 .offset * t1
536+ local offset2 = node1 .offset * (1 - t2 ) + node2 .offset * t2
537+
538+ local y1 = seg .y1 + offset1
539+ local y2 = seg .y2 + offset2
540+
541+ local dx = seg .x2 - seg .x1
542+ local dy = y2 - y1
543+
544+ local nx = dy
545+ local ny = - dx
546+
547+ local len = (nx * nx + ny * ny ) ^ 0.5
548+ if len > 0 then
549+ nx = nx / len * half
550+ ny = ny / len * half
551+ end
552+
553+ surface_DrawPoly ({
554+ { x = seg .x1 - nx , y = y1 - ny },
555+ { x = seg .x1 + nx , y = y1 + ny },
556+ { x = seg .x2 + nx , y = y2 + ny },
557+ { x = seg .x2 - nx , y = y2 - ny }
558+ })
559+ end
560+ end
561+ end
562+
563+ hook .Add (" PostPlayerDraw" ," wire_fpga_editor_status" ,function (ply )
564+
565+ if not busy_players [ply ] then return end
566+
567+ local pos = ply :GetPos () + ply :GetUp () * (ply :OBBMaxs ().z + 10 )
568+
569+ local angle = (pos - EyePos ()):GetNormalized ():Angle ()
570+ angle = Angle (0 , angle .y , 0 )
571+ angle :RotateAroundAxis (angle :Up (), - 90 )
572+ angle :RotateAroundAxis (angle :Forward (), 90 )
573+
574+ local ft = FrameTime ()
575+
576+ UpdateSpring (node1 , ft )
577+ UpdateSpring (node2 , ft )
578+
579+ if anim .phase == 1 then
580+ anim .tEnd = min (1 , anim .tEnd + anim .speed * ft )
581+ if anim .tEnd >= 1 then
582+ anim .phase = 2
583+ anim .holdTimer = 0
584+
585+ if anim .dir == 1 then
586+ node2 .vel = node2 .vel - impulsePower
587+ else
588+ node1 .vel = node1 .vel - impulsePower
589+ end
590+ end
591+
592+ elseif anim .phase == 2 then
593+ anim .holdTimer = anim .holdTimer + ft
594+ if anim .holdTimer >= anim .holdTime then
595+ anim .phase = 3
596+ end
597+
598+ elseif anim .phase == 3 then
599+ anim .tStart = min (1 , anim .tStart + anim .speed * ft )
600+ if anim .tStart >= 1 then
601+ anim .tStart = 0
602+ anim .tEnd = 0
603+ anim .phase = 1
604+ anim .dir = - anim .dir
605+
606+ BuildBaseCurve (anim .dir )
607+ end
608+ end
609+
610+ cam .Start3D2D (pos , angle , 0.05 )
611+
612+ draw .RoundedBox (12 ,
613+ node1x - size * 0.5 ,
614+ node1y - size * 0.5 + node1 .offset ,
615+ size , size ,
616+ nodeColor
617+ )
618+
619+ draw .RoundedBox (12 ,
620+ node2x - size * 0.5 ,
621+ node2y - size * 0.5 + node2 .offset ,
622+ size , size ,
623+ nodeColor
624+ )
625+
626+ DrawCachedCurve (anim .tStart , anim .tEnd )
627+
628+ cam .End3D2D ()
629+
630+ end )
631+
370632end
0 commit comments