universaltech:start

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
universaltech:start [2026/06/12 03:54] – created jumpstartadminuniversaltech:start [2026/06/22 06:57] (current) jumpstartadmin
Line 1: Line 1:
-====== @@TITLE@@ ====== +~~NOCACHE~~
 <html> <html>
-<div class="js-technique-wrap">+<!DOCTYPE html> 
 +<html lang="en"> 
 +<head> 
 +<meta charset="UTF-8"> 
 +<style> 
 +body.mode_show #dokuwiki__site { display: none !important; } 
 +* { box-sizing: border-box; margin: 0; padding: 0; } 
 +.navbar.navbar-default { z-index: 10000 !important; } 
 +body:not(.mode_show) #ut-page { display: none !important; }
  
-  <!-- ═══════════════════════════════════════ +#ut-page { 
-       INFOSTRIP +  position: fixed; 
-       Edit the chips below to match this technique. +  top: 0; 
-       Difficultyjs-chip-beginner / js-chip-intermediate / js-chip-advanced / js-chip-expert +  left0; right: 0; bottom: 0; 
-       Tier      js-chip-tier-s / js-chip-tier-a / js-chip-tier-b / js-chip-tier-c +  z-index: 9000; 
-  ════════════════════════════════════════ --> +  background#0A0816; 
-  <aside class="js-infostrip">+  display: flex; 
 +  overflow: hidden; 
 +  font-family: 'Forevs', sans-serif; 
 +  color: #EDE8F5; 
 +}
  
-    <span class="js-infostrip-label">Difficulty</span+/* VIDEO BG */ 
-    <div class="js-chip js-chip-advanced"> +#ut-video-bg { 
-      <span class="js-chip-dot"></span+  position: absolute; 
-      Advanced+  inset: 0; 
 +  z-index: 0; 
 +
 +#ut-video-bg video { 
 +  width: 100%; height: 100%; 
 +  object-fit: cover; 
 +  opacity: 0.55; 
 +
 +#ut-video-bg::after { 
 +  content: ''; 
 +  position: absolute; 
 +  inset: 0; 
 +  background: linear-gradient(to right, rgba(10,8,22,0.95) 20%, rgba(10,8,22,0.55) 55%, rgba(10,8,22,0.1) 100%); 
 +
 + 
 +/* LEFT */ 
 +#ut-left { 
 +  position: relative; 
 +  z-index: 10; 
 +  flex: 1; 
 +  display: flex; 
 +  flex-direction: column; 
 +  justify-content: flex-end; 
 +  padding: 0 0 64px 64px; 
 +  pointer-events: none; 
 +
 + 
 +#ut-splash, #ut-content { 
 +  transition: opacity 0.35s, transform 0.35s; 
 +
 +#ut-splash.hidden, #ut-content.hidden { 
 +  opacity: 0; 
 +  transform: translateY(12px); 
 +  pointer-events: none; 
 +  position: absolute; 
 +
 +#ut-content { pointer-events: all; } 
 + 
 +.ut-eyebrow { 
 +  font-size: 11px; 
 +  letter-spacing: 0.2em; 
 +  text-transform: uppercase; 
 +  color: #6B4FBB; 
 +  margin-bottom: 12px; 
 +
 +#ut-splash-title { 
 +  font-size: 72px; 
 +  font-weight: 700; 
 +  line-height: 1; 
 +  letter-spacing: -1.5px; 
 +  margin-bottom: 18px; 
 +  color: #EDE8F5; 
 +
 +#ut-splash-title em { color: #6B4FBB; font-style: normal; } 
 +#ut-splash-desc { 
 +  font-size: 15px; 
 +  color: #9D85DB; 
 +  max-width: 400px; 
 +  line-height: 1.65; 
 +
 + 
 +/* content state */ 
 +#ut-breadcrumb { 
 +  font-size: 11px; 
 +  letter-spacing: 0.14em; 
 +  text-transform: uppercase; 
 +  color: #6B4FBB; 
 +  margin-bottom: 10px; 
 +
 +#ut-content-title { 
 +  font-size: 56px; 
 +  font-weight: 700; 
 +  letter-spacing: -1px; 
 +  line-height: 1; 
 +  margin-bottom: 12px; 
 +  color: #EDE8F5; 
 +
 +#ut-content-desc { 
 +  font-size: 14px; 
 +  color: #9D85DB; 
 +  max-width: 420px; 
 +  line-height: 1.65; 
 +  margin-bottom: 24px; 
 +
 +#ut-card-grid { 
 +  display: flex; 
 +  flex-wrap: wrap; 
 +  gap: 8px; 
 +  margin-bottom: 22px; 
 +  pointer-events: all; 
 +
 +.ut-guide-card { 
 +  background: rgba(18,15,41,0.7); 
 +  border: 1px solid rgba(108,79,187,0.3); 
 +  border-radius: 10px; 
 +  padding: 12px 16px; 
 +  cursor: pointer; 
 +  transition: all 0.15s; 
 +  text-decoration: none; 
 +  display: block; 
 +
 +.ut-guide-card:hover { 
 +  background: rgba(61,43,122,0.5); 
 +  border-color: rgba(184,173,219,0.45); 
 +  transform: translateY(-2px); 
 +
 +.ut-guide-card-name { font-size: 13px; font-weight: 600; color: #C8BFED; margin-bottom: 3px; } 
 +.ut-guide-card-meta { font-size: 11px; color: #6B4FBB; } 
 + 
 +#ut-open-btn { 
 +  display: inline-flex; 
 +  align-items: center; 
 +  gap: 8px; 
 +  background: rgba(108,79,187,0.28); 
 +  border: 1px solid rgba(108,79,187,0.5); 
 +  border-radius: 10px; 
 +  padding: 10px 20px; 
 +  color: #B8ADDB; 
 +  font-size: 13px; 
 +  font-family: 'Forevs', sans-serif; 
 +  cursor: pointer; 
 +  text-decoration: none; 
 +  transition: all 0.15s; 
 +  pointer-events: all; 
 +
 +#ut-open-btn:hover { 
 +  background: rgba(108,79,187,0.5); 
 +  color: #EDE8F5; 
 +  border-color: rgba(184,173,219,0.6); 
 +
 + 
 +/* RIGHT — osu carousel */ 
 +#ut-carousel-wrap { 
 +  position: relative; 
 +  z-index: 10; 
 +  width: 500px; 
 +  flex-shrink: 0; 
 +  overflow: hidden; 
 +  display: flex; 
 +  align-items: center; 
 +  user-select: none; 
 +
 + 
 +#ut-carousel { 
 +  position: absolute; 
 +  right: 0; 
 +  width: 100%; 
 +
 + 
 +/* tiles */ 
 +.ut-item { 
 +  position: absolute; 
 +  right: 0; 
 +  border-radius: 16px 0 0 16px; 
 +  overflow: hidden; 
 +  cursor: pointer; 
 +  border: 1px solid rgba(108,79,187,0.2); 
 +  border-right: none; 
 +  will-change: transform, top, height, width, opacity; 
 +  transition: 
 +    width 0.22s cubic-bezier(0.4,0,0.2,1), 
 +    height 0.22s cubic-bezier(0.4,0,0.2,1), 
 +    opacity 0.22s; 
 +
 + 
 +.ut-item.is-cat        { width: 420px; height: 80px; } 
 +.ut-item.is-sub        { width: 370px; height: 60px; } 
 +.ut-item.is-cat.selected { width: 480px; height: 96px; } 
 +.ut-item.is-sub.selected { width: 440px; height: 72px; } 
 + 
 +.ut-item-bg { 
 +  position: absolute; inset: 0; 
 +  opacity: 0.2; 
 +  transition: opacity 0.2s; 
 +
 +.ut-item:hover .ut-item-bg { opacity: 0.3; } 
 +.ut-item.selected .ut-item-bg { opacity: 0.38; } 
 + 
 +.ut-item-overlay { 
 +  position: absolute; inset: 0; 
 +  background: linear-gradient(90deg, rgba(10,8,22,0.85) 0%, rgba(10,8,22,0.25) 100%); 
 +
 + 
 +.ut-item-inner { 
 +  position: relative; 
 +  z-index: 2; 
 +  display: flex; 
 +  align-items: center; 
 +  gap: 16px; 
 +  padding: 0 22px; 
 +  width: 100%; 
 +  height: 100%; 
 +
 + 
 +.ut-item-icon { 
 +  font-size: 22px; 
 +  color: #9D85DB; 
 +  flex-shrink: 0; 
 +  transition: color 0.2s, font-size 0.2s; 
 +
 +.ut-item.selected .ut-item-icon  { color: #C8BFED; font-size: 24px; } 
 +.ut-item.is-sub .ut-item-icon    { font-size: 15px; color: #6B4FBB; } 
 +.ut-item.is-sub.selected .ut-item-icon { font-size: 17px; color: #9D85DB; } 
 + 
 +.ut-item-text { flex: 1; min-width: 0; } 
 +.ut-item-label { 
 +  font-size: 15px; 
 +  font-weight: 600; 
 +  color: #C8BFED; 
 +  white-space: nowrap; 
 +  overflow: hidden; 
 +  text-overflow: ellipsis; 
 +  transition: color 0.2s, font-size 0.2s; 
 +
 +.ut-item.selected .ut-item-label     { color: #EDE8F5; font-size: 17px; } 
 +.ut-item.is-sub .ut-item-label       { font-size: 13px; color: #9D85DB; font-weight: 500; } 
 +.ut-item.is-sub.selected .ut-item-label { color: #C8BFED; font-size: 14px; } 
 + 
 +.ut-item-sub-count { 
 +  font-size: 11px; 
 +  color: #6B4FBB; 
 +  margin-top: 3px; 
 +
 + 
 +.ut-item-accent-bar { 
 +  position: absolute; 
 +  left: 0; top: 0; bottom: 0; 
 +  width: 3px; 
 +  border-radius: 16px 0 0 16px; 
 +  opacity: 0; 
 +  transition: opacity 0.2s; 
 +
 +.ut-item.selected .ut-item-accent-bar { opacity: 1; } 
 + 
 +/* accent colors */ 
 +.ut-acc-movement .ut-item-bg  { background: linear-gradient(90deg,#3D2B7A,#6B4FBB);
 +.ut-acc-movement .ut-item-accent-bar { background: #9D85DB; } 
 +.ut-acc-combat .ut-item-bg    { background: linear-gradient(90deg,#7A2B4A,#BB4F7A);
 +.ut-acc-combat .ut-item-accent-bar   { background: #DB85AD; } 
 +.ut-acc-items .ut-item-bg     { background: linear-gradient(90deg,#1E5A7A,#2B8FBB);
 +.ut-acc-items .ut-item-accent-bar    { background: #85CADB; } 
 +.ut-acc-map .ut-item-bg       { background: linear-gradient(90deg,#1E7A4A,#2BBB6F);
 +.ut-acc-map .ut-item-accent-bar      { background: #85DBA8; } 
 +.ut-acc-mechanics .ut-item-bg { background: linear-gradient(90deg,#7A5A1E,#BB8F2B);
 +.ut-acc-mechanics .ut-item-accent-bar{ background: #DBC085; } 
 +.ut-acc-abilities .ut-item-bg { background: linear-gradient(90deg,#5A1E7A,#8F2BBB);
 +.ut-acc-abilities .ut-item-accent-bar{ background: #C085DB; } 
 + 
 +/* search */ 
 +#ut-search-wrap { 
 +  position: absolute; 
 +  top: 24px; right: 0; 
 +  width: 440px; 
 +  z-index: 20; 
 +
 +#ut-search { 
 +  width: 100%; 
 +  background: rgba(10,8,22,0.8); 
 +  border: 1px solid rgba(108,79,187,0.4); 
 +  border-right: none; 
 +  border-radius: 10px 0 0 10px; 
 +  padding: 10px 16px; 
 +  color: #EDE8F5; 
 +  font-size: 13px; 
 +  font-family: 'Forevs', sans-serif; 
 +  outline: none; 
 +  transition: border-color 0.15s; 
 +
 +#ut-search::placeholder { color: rgba(108,79,187,0.55);
 +#ut-search:focus { border-color: rgba(184,173,219,0.55);
 +</style> 
 +</head> 
 +<body> 
 +<div id="ut-page"> 
 + 
 +  <div id="ut-video-bg"> 
 +    <video autoplay muted loop playsinline id="ut-video"> 
 +      <source src="/wiki/lib/exe/fetch.php?media=0%3Awiki1.mp4" type="video/mp4"
 +    </video> 
 +  </div> 
 + 
 +  <div id="ut-left"> 
 +    <div id="ut-splash"> 
 +      <div class="ut-eyebrow">JumpStart Wiki</div> 
 +      <div id="ut-splash-title">Master the<br><em>Fundamentals</em></div
 +      <div id="ut-splash-desc">Mechanics and techniques that apply to every hero in Deadlock. Pick a category to explore.</div> 
 +    </div> 
 +    <div id="ut-content" class="hidden"> 
 +      <div id="ut-breadcrumb"></div> 
 +      <div id="ut-content-title"></div
 +      <div id="ut-content-desc"></div> 
 +      <div id="ut-card-grid"></div> 
 +      <a id="ut-open-btn" href="#"><i class="ti ti-external-link"></i><span id="ut-btn-label">Open wiki page</span></a>
     </div>     </div>
 +  </div>
  
-    <span class="js-infostrip-label">Tier</span+  <div id="ut-carousel-wrap"> 
-    <div class="js-chip js-chip-tier-s"> +    <div id="ut-search-wrap"> 
-      <span class="js-chip-dotstyle="background:#c2883a;"></span> +      <input type="text" id="ut-searchplaceholder="Search tech..." autocomplete="off" />
-      S-Tier+
     </div>     </div>
 +    <div id="ut-carousel"></div>
 +  </div>
  
-    <span class="js-infostrip-label">Works on</span+</div
-    <a href="/wiki/doku.php?id=heroes:all" class="js-hero-tag"+<script
-      <span class="js-hero-tag-icon"></span> +(function(){
-      All Heroes +
-    </a>+
  
-    <span class="js-infostrip-label">Category</span> +const CATS 
-    <div class="js-chip" style="border-left-color:#3a6e9e; color:#7ab4cf;"> +  { id:'movement', icon:'ti-run', label:'Movement', acc:'movement', 
-      <span class="js-chip-dot" style="background:#3a6e9e;"></span> +    desc:'Core ground movement — walking, sprinting, crouching, sliding, jumping, and stamina management.', 
-      Movement +    wikiId:'universaltech:movement', 
-    </div>+    subs:[ 
 +      {id:'walking',   label:'Walking',   desc:'Base movement speed and mechanics.', wikiId:'universaltech:movement:walking'}, 
 +      {id:'sprinting', label:'Sprinting', desc:'Sprint mechanics and speed thresholds.', wikiId:'universaltech:movement:sprinting'}, 
 +      {id:'crouching', label:'Crouching', desc:'Crouch movement and hitbox changes.', wikiId:'universaltech:movement:crouching'}, 
 +      {id:'sliding',   label:'Sliding',   desc:'Slide mechanics, speed, and timing.', wikiId:'universaltech:movement:sliding'}, 
 +      {id:'stamina',   label:'Stamina',   desc:'Stamina system, regeneration, and management.', wikiId:'universaltech:movement:stamina'}, 
 +      {id:'jumping',   label:'Jumping',   desc:'Jump mechanics, height, and timing.', wikiId:'universaltech:movement:jumping'}, 
 +    ]}, 
 +  { id:'dashing', icon:'ti-player-skip-forward', label:'Dashing', acc:'combat', 
 +    desc:'Dash techs — directional dashes, aerial dash combos, and advanced dash cancels.', 
 +    wikiId:'universaltech:dashing', 
 +    subs:[ 
 +      {id:'strafedash', label:'Strafe Dashing',         desc:'Dashing at angles for lateral speed.', wikiId:'universaltech:dashing:strafedash'}, 
 +      {id:'dashjump',   label:'Dash Jumping',           desc:'Combining a dash into a jump for distance.', wikiId:'universaltech:dashing:dashjump'}, 
 +      {id:'dashslide',  label:'Dash Sliding',           desc:'Cancelling a dash into a slide.', wikiId:'universaltech:dashing:dashslide'}, 
 +      {id:'iad',        label:'Instant Air Dash (I.A.D)',desc:'Minimal-height air dash for fast horizontal movement.', wikiId:'universaltech:dashing:iad'}, 
 +      {id:'wallstrafe', label:'Wall Strafe',            desc:'Dashing along walls for momentum.', wikiId:'universaltech:dashing:wallstrafe'}, 
 +    ]}, 
 +  { id:'mantling', icon:'ti-arrow-bar-up', label:'Mantling', acc:'map', 
 +    desc:'Mantle techs — advanced ledge grabs, super glides, and mantle-based launches.', 
 +    wikiId:'universaltech:mantling', 
 +    subs:[ 
 +      {id:'mantleslide',  label:'Mantle Slide',      desc:'Sliding immediately out of a mantle.', wikiId:'universaltech:mantling:mantleslide'}, 
 +      {id:'superglide',   label:'Super Glide',       desc:'Gaining extra airtime off a mantle.', wikiId:'universaltech:mantling:superglide'}, 
 +      {id:'glidewj',      label:'Glide Wall Jump',   desc:'Wall jumping during a glide for height.', wikiId:'universaltech:mantling:glidewj'}, 
 +      {id:'mantlestrafe', label:'Mantle Strafe',     desc:'Angling movement during mantle animation.', wikiId:'universaltech:mantling:mantlestrafe'}, 
 +      {id:'fakemantle',   label:'Fake Mantle',       desc:'Cancelling the mantle animation early.', wikiId:'universaltech:mantling:fakemantle'}, 
 +      {id:'mantlelaunch', label:'Mantle Launch',     desc:'Using mantle momentum to launch far.', wikiId:'universaltech:mantling:mantlelaunch'}, 
 +    ]}, 
 +  { id:'ziplines', icon:'ti-line-dashed', label:'Ziplines', acc:'items', 
 +    desc:'Zipline techs — momentum conservation, cancels, jumps, and launch techniques.', 
 +    wikiId:'universaltech:ziplines', 
 +    subs:[ 
 +      {id:'zmc',     label:'Zipline Momentum Conservation (Z.M.C)', desc:'Keeping speed when leaving a zipline.', wikiId:'universaltech:ziplines:zmc'}, 
 +      {id:'zipcancel',label:'Zip Cancel', desc:'Cancelling off a zipline at the right moment.', wikiId:'universaltech:ziplines:zipcancel'}, 
 +      {id:'zipjump', label:'Zip Jump',   desc:'Jumping from a zipline with extra height.', wikiId:'universaltech:ziplines:zipjump'}, 
 +      {id:'zipdash', label:'Zip Dash',   desc:'Dashing on or off a zipline.', wikiId:'universaltech:ziplines:zipdash'}, 
 +      {id:'ziplaunch',label:'Zip Launch',desc:'Using zipline exit for maximum launch speed.', wikiId:'universaltech:ziplines:ziplaunch'}, 
 +    ]}, 
 +  { id:'ropes', icon:'ti-minus-vertical', label:'Ropes', acc:'mechanics', 
 +    desc:'Rope techs — cancels, launches, and wall jumps off rope anchors.', 
 +    wikiId:'universaltech:ropes', 
 +    subs:[ 
 +      {id:'ropecancel', label:'Rope Cancel',    desc:'Cancelling rope grab for momentum burst.', wikiId:'universaltech:ropes:ropecancel'}, 
 +      {id:'ropelaunch', label:'Rope Launch',    desc:'Using rope to catapult in a direction.', wikiId:'universaltech:ropes:ropelaunch'}, 
 +      {id:'ropewj',     label:'Rope Wall Jump', desc:'Wall jumping off a rope attachment point.', wikiId:'universaltech:ropes:ropewj'}, 
 +    ]}, 
 +  { id:'vent', icon:'ti-wind', label:'Vent', acc:'abilities', 
 +    desc:'Vent interactions — boost techs and stun mechanics.', 
 +    wikiId:'universaltech:vent', 
 +    subs:[ 
 +      {id:'ventboost', label:'Vent Boost', desc:'Using vents for extra upward velocity.', wikiId:'universaltech:vent:ventboost'}, 
 +      {id:'ventstun',  label:'Vent Stun',  desc:'Vent stun timing and counterplay.', wikiId:'universaltech:vent:ventstun'}, 
 +    ]}, 
 +  { id:'melee', icon:'ti-sword', label:'Melee', acc:'combat', 
 +    desc:'Melee techs — light, heavy, parry, cancels, and advanced melee interactions.', 
 +    wikiId:'universaltech:melee', 
 +    subs:[ 
 +      {id:'lightmelee', label:'Light Melee',               desc:'Light melee timing and combo use.', wikiId:'universaltech:melee:lightmelee'}, 
 +      {id:'heavymelee', label:'Heavy Melee',               desc:'Heavy melee range, knockback, and setup.', wikiId:'universaltech:melee:heavymelee'}, 
 +      {id:'parry',      label:'Parry',                     desc:'Parry window, animation, and rewards.', wikiId:'universaltech:melee:parry'}, 
 +      {id:'hmc',        label:'Heavy Melee Cancel (H.M.C)',desc:'Cancelling heavy melee for free movement.', wikiId:'universaltech:melee:hmc'}, 
 +      {id:'hiddenmelee',label:'Hidden Melee',              desc:'Concealed melee animations and setups.', wikiId:'universaltech:melee:hiddenmelee'}, 
 +      {id:'feign',      label:'Feign',                     desc:'Feinting melee to bait parry responses.', wikiId:'universaltech:melee:feign'}, 
 +    ]}, 
 +  { id:'walljump', icon:'ti-arrow-bounce', label:'Wall Jumping', acc:'movement', 
 +    desc:'Wall jump techs — directional control, corner and edge boosts, surf, and fatigue management.', 
 +    wikiId:'universaltech:walljump', 
 +    subs:[ 
 +      {id:'wjd',       label:'Wall Jump Direction (W.J.D)', desc:'Controlling angle and direction off walls.', wikiId:'universaltech:walljump:wjd'}, 
 +      {id:'cornerboost',label:'Corner Boost',              desc:'Using corners for extra launch speed.', wikiId:'universaltech:walljump:cornerboost'}, 
 +      {id:'edgeboost',  label:'Edge Boost',                desc:'Boosting off ledge geometry.', wikiId:'universaltech:walljump:edgeboost'}, 
 +      {id:'refundwj',   label:'Refund Window',             desc:'Timing wall jumps to recover stamina.', wikiId:'universaltech:walljump:refundwj'}, 
 +      {id:'wjfatigue',  label:'Wall Jump Fatigue',         desc:'Managing wall jump degradation over time.', wikiId:'universaltech:walljump:wjfatigue'}, 
 +      {id:'wallsurf',   label:'Wall Surf',                 desc:'Riding wall surfaces for sustained speed.', wikiId:'universaltech:walljump:wallsurf'}, 
 +    ]}, 
 +  { id:'strafing', icon:'ti-arrows-left-right', label:'Strafing', acc:'movement', 
 +    desc:'Advanced strafe techs — air strafing, bunnyhopping, and slide bunnyhopping.', 
 +    wikiId:'universaltech:strafing', 
 +    subs:[ 
 +      {id:'airstrafe',   label:'Air Strafing',      desc:'Directional air control for speed and evasion.', wikiId:'universaltech:strafing:airstrafe'}, 
 +      {id:'bunnyhop',    label:'Bunnyhopping',       desc:'Chaining jumps to maintain and build speed.', wikiId:'universaltech:strafing:bunnyhop'}, 
 +      {id:'slidebhop',   label:'Slide Bunnyhopping', desc:'Combining slides and hops for max momentum.', wikiId:'universaltech:strafing:slidebhop'}, 
 +    ]}, 
 +  { id:'itemtech', icon:'ti-package', label:'Item Tech', acc:'items', 
 +    desc:'Universal item interactions and advanced tech with purchasable items.', 
 +    wikiId:'universaltech:itemtech', 
 +    subs:[ 
 +      {id:'majesticleap',  label:'Majestic Leap',    desc:'Launch techs and landing timing.', wikiId:'universaltech:itemtech:majesticleap'}, 
 +      {id:'warpstone',     label:'Warp Stone',        desc:'Positioning tricks and cancel windows.', wikiId:'universaltech:itemtech:warpstone'}, 
 +      {id:'rescuebeam',    label:'Rescue Beam',       desc:'Beam pull interactions and edge cases.', wikiId:'universaltech:itemtech:rescuebeam'}, 
 +      {id:'magiccarpet',   label:'Magic Carpet',      desc:'Hover positioning and advanced uses.', wikiId:'universaltech:itemtech:magiccarpet'}, 
 +      {id:'slowinghex',    label:'Slowing Hex',       desc:'Slow application timing and combos.', wikiId:'universaltech:itemtech:slowinghex'}, 
 +      {id:'staminamaster', label:'Stamina Mastery',   desc:'Maximising stamina regen and usage.', wikiId:'universaltech:itemtech:staminamaster'}, 
 +      {id:'capacitor',     label:'Capacitor',         desc:'Charge timing and burst application.', wikiId:'universaltech:itemtech:capacitor'}, 
 +    ]}, 
 +];
  
-  </aside>+const VIDS = [ 
 +  '/wiki/lib/exe/fetch.php?media=0%3Awiki1.mp4', 
 +  '/wiki/lib/exe/fetch.php?media=0%3Awiki2.mp4', 
 +  '/wiki/lib/exe/fetch.php?media=0%3Awiki3.mp4', 
 +  '/wiki/lib/exe/fetch.php?media=0%3Awiki4.mp4', 
 +]; 
 +let vi = 0; 
 +const video = document.getElementById('ut-video'); 
 +video.addEventListener('ended', () =
 +  vi = (vi + 1) % VIDS.length; 
 +  video.src = VIDS[vi]; 
 +  video.play(); 
 +});
  
-  <!-- ═══════════════════════════════════════ +// ── STATE ────────────────────────────────────────────── 
-       BODY +let activeCatId = null; 
-  ════════════════════════════════════════ --> +let activeSubId = null; 
-  <div class="js-technique-body">+let filter '';
  
-    <header class="js-page-header"> +// ── LAYOUT CONSTANTS ──────────────────────────────────── 
-      <span class="js-page-eyebrow">Movement · Techniques</span> +const CAT_H     = 80; 
-      <h1 class="js-page-title">Technique Name</h1> +const CAT_H_SEL 96; 
-      <p class="js-page-subtitle"> +const SUB_H     60; 
-        One or two sentences describing what this technique is and why it matters. +const SUB_H_SEL 72; 
-        Keep it to the point — the overview section below handles the detail. +const GAP       = 7;
-      </p> +
-    </header>+
  
-</html>+// scrollY = offset into virtual list (px). Center of viewport = scrollY. 
 +let scrollY   = 0; 
 +let velocity  = 0; 
 +let rafId     = null; 
 +let isDragging = false; 
 +let lastDragY  = 0; 
 +let lastDragT  = 0;
  
-===== Overview =====+// ── FLAT ITEM LIST ────────────────────────────────────── 
 +function buildItemList() { 
 +  const items []; 
 +  const q filter.toLowerCase(); 
 +  CATS.forEach(cat => { 
 +    const subMatch cat.subs.filter(s =
 +      !q || s.label.toLowerCase().includes(q) || cat.label.toLowerCase().includes(q) 
 +    ); 
 +    if (q && subMatch.length === 0 && !cat.label.toLowerCase().includes(q)) return; 
 +    items.push({ type:'cat', cat, sub:null }); 
 +    if (activeCatId === cat.id) { 
 +      (q ? subMatch : cat.subs).forEach(sub => items.push({ type:'sub', cat, sub })); 
 +    } 
 +  }); 
 +  return items; 
 +}
  
-Write your overview hereExplain what the technique doeswhen to use itand any important context.+// ── POSITIONS ─────────────────────────────────────────── 
 +function computePositions(items) { 
 +  let y = 0; 
 +  return items.map(item => { 
 +    const isSel = item.type === 'cat' 
 +      ? activeCatId === item.cat.id && activeSubId === null 
 +      : activeSubId === item.sub.id; 
 +    const h = item.type === 'cat' ? (isSel ? CAT_H_SEL : CAT_H) : (isSel ? SUB_H_SEL : SUB_H); 
 +    const pos = { yhisSel }; 
 +    y += h + GAP; 
 +    return pos; 
 +  }); 
 +}
  
-<html> +// ── RENDER ───────────────────────────────────────────── 
-    <div class="js-callout"> +function render() { 
-      <div class="js-callout-bar tip"></div> +  const carousel document.getElementById('ut-carousel'); 
-      <div class="js-callout-inner"> +  const wrapH    document.getElementById('ut-carousel-wrap').offsetHeight; 
-        <span class="js-callout-label">Tip</span> +  const items    buildItemList(); 
-        <p class="js-callout-text">Add a helpful tip hereDelete this block if not needed.</p> +  const pos      computePositions(items); 
-      </div> +  const totalH   pos.length ? pos[pos.length-1].y + pos[pos.length-1].h : 0;
-    </div> +
-</html>+
  
-===== How to Execute =====+  // clamp scroll: allow scrolling through the whole list 
 +  const minScroll -(wrapH / 2); 
 +  const maxScroll totalH - wrapH / 2; 
 +  scrollY Math.max(minScroll, Math.min(maxScroll, scrollY));
  
-<html> +  // track which uids exist 
-    <div class="js-steps">+  const existingEls {}; 
 +  carousel.querySelectorAll('.ut-item').forEach(el => { existingEls[el.dataset.uid] = el; }); 
 +  const newUids = new Set(); 
 +  items.forEach(item => newUids.add(uid(item))); 
 +  Object.keys(existingEls).forEach(u ={ if (!newUids.has(u)) existingEls[u].remove(); });
  
-      <div class="js-step"> +  // viewport center 
-        <div class="js-step-num">01</div> +  const vc wrapH 2;
-        <div class="js-step-content"> +
-          <div class="js-step-title">Step title here</div> +
-          <p class="js-step-desc">Describe this step in detail. What does the player do, when, and why.</p> +
-        </div> +
-      </div>+
  
-      <div class="js-step"+  items.forEach((item, i) => { 
-        <div class="js-step-num">02</div> +    const u uid(item); 
-        <div class="js-step-content"> +    const { y: iy, h, isSel } pos[i];
-          <div class="js-step-title">Step title here</div> +
-          <p class="js-step-desc">Describe this step in detail.</p> +
-        </div> +
-      </div>+
  
-      <div class="js-step"> +    // position relative to viewport center 
-        <div class="js-step-num">03</div> +    const screenY iy scrollY; 
-        <div class="js-step-content"> +    const itemCenter screenY + h 2; 
-          <div class="js-step-title">Step title here</div> +    const distFromCenter itemCenter vc; 
-          <p class="js-step-desc">Describe this step in detail.</p> +    const absD Math.abs(distFromCenter); 
-        </div> +    const maxD wrapH * 0.52;
-      </div>+
  
-    </div> +    // osu curve: items further away pushed right 
-</html>+    const pushFrac = Math.min(absD maxD, 1); 
 +    // ease it: more push at the edges 
 +    const push = Math.pow(pushFrac, 1.4) * (item.type === 'sub' ? 52 : 68);
  
-===== Input Notation =====+    // fade near edges 
 +    const fadeFrac Math.min(absD / (wrapH * 0.44), 1); 
 +    const opacity  Math.max(0, 1 - Math.pow(fadeFrac, 1.8));
  
-<html> +    if (opacity <= 0.01) { 
-    <table class="js-input-table"> +      if (existingEls[u]) existingEls[u].style.opacity '0'; 
-      <thead> +      return; 
-        <tr> +    }
-          <th>Action</th> +
-          <th>Input</th> +
-          <th>Timing</th> +
-          <th>Notes</th> +
-        </tr> +
-      </thead> +
-      <tbody> +
-        <tr> +
-          <td>Action name</td> +
-          <td><span class="js-key">Space</span></td> +
-          <td>On ground</td> +
-          <td>Notes here</td> +
-        </tr> +
-        <tr> +
-          <td>Action name</td> +
-          <td> +
-            <span class="js-key">Shift</span> +
-            <span class="js-input-plus">+</span> +
-            <span class="js-key">W</span> +
-          </td> +
-          <td>Hold throughout</td> +
-          <td>Notes here</td> +
-        </tr> +
-      </tbody> +
-    </table> +
-</html>+
  
-===== Stats =====+    let el existingEls[u]; 
 +    if (!el) { 
 +      el document.createElement('div'); 
 +      el.dataset.uid u; 
 +      el.innerHTML buildInner(item); 
 +      el.addEventListener('click', () => onItemClick(item)); 
 +      carousel.appendChild(el); 
 +    }
  
-<html> +    el.className 'ut-item' + 
-    <table class="js-stat-table"> +      (item.type === 'sub' ? ' is-sub' : ' is-cat') + 
-      <thead> +      ' ut-acc-' + item.cat.acc + 
-        <tr> +      (isSel ? ' selected' : '');
-          <th>Metric</th> +
-          <th>Base Value</th> +
-          <th>With Technique</th> +
-          <th>Notes</th> +
-        </tr> +
-      </thead> +
-      <tbody> +
-        <tr> +
-          <td>Stat label</td> +
-          <td>000</td> +
-          <td><span class="js-stat-val">000</span></td> +
-          <td><span class="js-stat-note">Notes</span></td> +
-        </tr> +
-        <tr> +
-          <td>Stat label</td> +
-          <td>000</td> +
-          <td><span class="js-stat-val">000</span></td> +
-          <td><span class="js-stat-note">Notes</span></td> +
-        </tr> +
-      </tbody> +
-    </table> +
-</html>+
  
-===== Video Examples =====+    el.style.top     screenY + 'px'; 
 +    el.style.height  h + 'px'; 
 +    el.style.transform `translateX(${push}px)`; 
 +    el.style.opacity opacity.toFixed(3); 
 +  });
  
-<html> +  carousel.style.height = wrapH + 'px'; 
-    <!-- Single video: use js-video-block alone. +}
-         Multiple videos: wrap in js-video-grid--> +
-    <div class="js-video-grid">+
  
-      <div class="js-video-block"> +function uid(item) { 
-        <div class="js-video-wrap"> +  return item.type === 'cat' ? item.cat.id item.cat.id + '__' + item.sub.id; 
-          <!-- Replace src with your fetch.php path, e.g.: +}
-               /wiki/lib/exe/fetch.php?media=techniques:techname_example1.mp4 --> +
-          <video autoplay muted loop playsinline> +
-            <source src="/wiki/lib/exe/fetch.php?media=techniques:example1.mp4" type="video/mp4"> +
-          </video> +
-        </div> +
-        <div class="js-video-caption">Caption for clip one</div> +
-      </div>+
  
-      <div class="js-video-block"> +function buildInner(item) { 
-        <div class="js-video-wrap"> +  if (item.type === 'cat') { 
-          <video autoplay muted loop playsinline+    return `<div class="ut-item-bg"></div
-            <source src="/wiki/lib/exe/fetch.php?media=techniques:example2.mp4type="video/mp4"> +<div class="ut-item-overlay"></div
-          </video+<div class="ut-item-accent-bar"></div
-        </div> +<div class="ut-item-inner"> 
-        <div class="js-video-caption">Caption for clip two</div> +  <i class="ti ${item.cat.icon} ut-item-icon" aria-hidden="true"></i> 
-      </div>+  <div class="ut-item-text"> 
 +    <div class="ut-item-label">${item.cat.label}</div> 
 +    <div class="ut-item-sub-count">${item.cat.subs.length} techniques</div> 
 +  </div> 
 +  <i class="ti ti-chevron-right" style="font-size:14px;color:#6B4FBB;flex-shrink:0aria-hidden="true"></i> 
 +</div>`; 
 +  } else { 
 +    return `<div class="ut-item-bg"></div
 +<div class="ut-item-overlay"></div
 +<div class="ut-item-accent-bar"></div> 
 +<div class="ut-item-inner"> 
 +  <i class="ti ti-circle-dot ut-item-icon" aria-hidden="true"></i> 
 +  <div class="ut-item-text"
 +    <div class="ut-item-label">${item.sub.label}</div> 
 +  </div> 
 +  <i class="ti ti-arrow-right" style="font-size:13px;color:#6B4FBB;flex-shrink:0" aria-hidden="true"></i> 
 +</div>`; 
 +  } 
 +}
  
-    </div>+// ── CLICK ────────────────────────────────────────────── 
 +function onItemClick(item) { 
 +  if (item.type === 'cat') { 
 +    if (activeCatId === item.cat.id && activeSubId === null) { 
 +      activeCatId = null; 
 +      showSplash(); 
 +    } else { 
 +      activeCatId = item.cat.id; 
 +      activeSubId = null; 
 +      showCat(item.cat); 
 +    } 
 +  } else { 
 +    activeCatId = item.cat.id; 
 +    activeSubId = item.sub.id; 
 +    showSub(item.cat, item.sub); 
 +  } 
 +  scrollToSelected(); 
 +  render(); 
 +}
  
-    <!-- Warning callout — edit or delete as needed --> +function scrollToSelected() { 
-    <div class="js-callout"> +  const items = buildItemList(); 
-      <div class="js-callout-bar warning"></div> +  const pos   = computePositions(items); 
-      <div class="js-callout-inner"> +  const wrapH = document.getElementById('ut-carousel-wrap').offsetHeight; 
-        <span class="js-callout-label">Warning</span> +  let selY = null, selH = CAT_H; 
-        <p class="js-callout-text">Note any conditions that break or limit this technique.</p> +  items.forEach((item, i) ={ 
-      </div> +    const isSel item.type === 'cat' 
-    </div> +      ? activeCatId === item.cat.id && activeSubId === null 
-</html>+      : activeSubId === item.sub?.id; 
 +    if (isSel) { selY pos[i].y; selH = pos[i].h; } 
 +  }); 
 +  if (selY !== null) { 
 +    animateScroll(selY + selH 2 - wrapH 2); 
 +  } 
 +}
  
-===== Related Techniques =====+// Spring scroll — sets velocity toward target naturally instead of teleporting 
 +function animateScroll(target) { 
 +  // Stop any current momentum and spring toward target 
 +  momentumRunning false; 
 +  velocity 0; 
 +  const SPRING 0.12;  // attraction strength per frame 
 +  const DAMP   0.72;  // damping to prevent overshoot 
 +  let springRunning true; 
 +  let sv 0;           // spring velocity separate from main velocity 
 +  function step() { 
 +    if (!springRunning) return; 
 +    const diff target - scrollY; 
 +    if (Math.abs(diff) < 0.5 && Math.abs(sv) < 0.5) { 
 +      scrollY target; 
 +      render(); 
 +      springRunning false; 
 +      return; 
 +    } 
 +    sv +diff * SPRING; 
 +    sv *= DAMP; 
 +    scrollY += sv; 
 +    render(); 
 +    requestAnimationFrame(step); 
 +  } 
 +  requestAnimationFrame(step); 
 +}
  
-<html> +// ── CONTENT AREA ─────────────────────────────────────── 
-    <div class="js-related">+const splash  document.getElementById('ut-splash'); 
 +const content = document.getElementById('ut-content');
  
-      <a href="/wiki/doku.php?id=techniques:related-technique-1" class="js-related-card"> +function showSplash() { 
-        <span class="js-related-card-name">Related Technique</span> +  splash.classList.remove('hidden'); 
-        <span class="js-related-card-desc">One-line description of why it's related</span> +  content.classList.add('hidden'); 
-      </a>+
 +function showCat(cat) { 
 +  splash.classList.add('hidden'); 
 +  content.classList.remove('hidden'); 
 +  document.getElementById('ut-breadcrumb').textContent    = 'Universal Tech'; 
 +  document.getElementById('ut-content-title').textContent = cat.label; 
 +  document.getElementById('ut-content-desc').textContent  = cat.desc; 
 +  document.getElementById('ut-card-grid').innerHTML = ''; 
 +  document.getElementById('ut-open-btn').href = '/wiki/doku.php?id=' + cat.wikiId; 
 +  document.getElementById('ut-btn-label').textContent 'Open ' + cat.label + ' overview'; 
 +
 +function showSub(cat, sub) { 
 +  splash.classList.add('hidden'); 
 +  content.classList.remove('hidden'); 
 +  document.getElementById('ut-breadcrumb').textContent    = 'Universal Tech › ' + cat.label; 
 +  document.getElementById('ut-content-title').textContent sub.label; 
 +  document.getElementById('ut-content-desc').textContent  = sub.desc; 
 +  document.getElementById('ut-card-grid').innerHTML ''; 
 +  document.getElementById('ut-open-btn').href = '/wiki/doku.php?id=' + sub.wikiId; 
 +  document.getElementById('ut-btn-label').textContent = 'Open ' + sub.label + ' page'; 
 +}
  
-      <a href="/wiki/doku.php?id=techniques:related-technique-2" class="js-related-card"> +// ── SCROLL ───────────────────────────────────────────── 
-        <span class="js-related-card-name">Related Technique</span> +const wrap document.getElementById('ut-carousel-wrap'); 
-        <span class="js-related-card-desc">One-line description of why it's related</span> +let momentumRunning false;
-      </a>+
  
-      <a href="/wiki/doku.php?id=techniques:related-technique-3" class="js-related-card"> +// Wheel sensitivitylow so small scrolls move little, fast scrolls build up 
-        <span class="js-related-card-name">Related Technique</span> +const WHEEL_ACCEL   0.18;   // fraction of deltaY added to velocity each event 
-        <span class="js-related-card-desc">One-line description of why it's related</span> +const FRICTION      0.82;   // velocity multiplied each frame (lower = stops sooner) 
-      </a>+const VEL_CAP       380;    // max px/frame velocity 
 +const VEL_STOP      = 0.6;    /below this velocity, stop the loop
  
-      <a href="/wiki/doku.php?id=techniques:related-technique-4" class="js-related-card"> +function startMomentum() { 
-        <span class="js-related-card-name">Related Technique</span> +  if (momentumRunning) return; 
-        <span class="js-related-card-desc">One-line description of why it's related</span> +  momentumRunning true; 
-      </a>+  function step() { 
 +    velocity *FRICTION; 
 +    if (Math.abs(velocity) VEL_STOP) { 
 +      velocity 0; 
 +      momentumRunning false; 
 +      return; 
 +    } 
 +    scrollY += velocity; 
 +    render(); 
 +    requestAnimationFrame(step); 
 +  } 
 +  requestAnimationFrame(step); 
 +}
  
-    </div>+// WHEEL — accumulate velocity proportional to scroll speed 
 +wrap.addEventListener('wheel', e =
 +  e.preventDefault(); 
 +  const scale = e.deltaMode === 1 ? 20 : e.deltaMode === 2 ? 300 : 1; 
 +  const delta = e.deltaY * scale; 
 +  // Add a fraction of delta to velocity — proportional feel 
 +  velocity += delta * WHEEL_ACCEL; 
 +  velocity = Math.max(-VEL_CAP, Math.min(VEL_CAP, velocity)); 
 +  startMomentum(); 
 +}, { passive: false });
  
-  </div><!-- end js-technique-body --> +// DRAG 
-</div><!-- end js-technique-wrap -->+wrap.addEventListener('mousedown', e =
 +  momentumRunning = false; 
 +  velocity = 0; 
 +  isDragging = true; lastDragY = e.clientY; lastDragT = performance.now(); 
 +  wrap.style.cursor = 'grabbing'; 
 +  e.preventDefault(); 
 +}); 
 +window.addEventListener('mousemove', e => { 
 +  if (!isDragging) return; 
 +  const now = performance.now(); 
 +  const dy  = e.clientY lastDragY; 
 +  const dt  = Math.max(now lastDragT, 1); 
 +  velocity  = (-dy / dt) * 8; 
 +  scrollY  -= dy; 
 +  lastDragY = e.clientY; lastDragT = now; 
 +  render(); 
 +}); 
 +window.addEventListener('mouseup', () =
 +  if (!isDragging) return; 
 +  isDragging = false; 
 +  wrap.style.cursor = 'grab'; 
 +  startMomentum(); 
 +}); 
 + 
 +// TOUCH 
 +wrap.addEventListener('touchstart', e =
 +  momentumRunning = false; velocity = 0; 
 +  isDragging = true; lastDragY = e.touches[0].clientY; lastDragT = performance.now(); 
 +}, { passive: true }); 
 +wrap.addEventListener('touchmove', e => { 
 +  if (!isDragging) return; 
 +  const now = performance.now(); 
 +  const dy  = e.touches[0].clientY lastDragY; 
 +  velocity  = (-dy / Math.max(now lastDragT, 1)) * 8; 
 +  scrollY  -= dy; 
 +  lastDragY = e.touches[0].clientY; lastDragT = now; 
 +  render(); 
 +}, { passive: true }); 
 +wrap.addEventListener('touchend', () => { isDragging = false; startMomentum(); }); 
 + 
 +// ── SEARCH ───────────────────────────────────────────── 
 +document.getElementById('ut-search').addEventListener('input', e => { 
 +  filter = e.target.value; 
 +  render(); 
 +}); 
 + 
 +// ── INIT ─────────────────────────────────────────────── 
 +window.addEventListener('resize', render); 
 +// start scroll so first item is near center 
 +scrollY = -(document.getElementById('ut-carousel-wrap').offsetHeight / 2 - CAT_H / 2); 
 +render(); 
 + 
 +})(); 
 +</script> 
 +</body> 
 +</html>
 </html> </html>
  • universaltech/start.1781236449.txt.gz
  • Last modified: 2026/06/12 03:54
  • by jumpstartadmin