@vimp-games/snakes 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,6 +14,14 @@ crystal the more you grow and the more you score.
14
14
  | `W` | boost — nearly double speed, paid for in crystals, which drop behind you |
15
15
  | `R` | respawn after a crash (the OK button of the result screen presses it for you) |
16
16
 
17
+ Mouse and touch are the second way to play, and the only one a phone has:
18
+ **press and hold** — the snake turns towards the point under the pointer, no
19
+ faster than the keys would turn it; **double tap and keep holding** — boost,
20
+ for as long as the finger stays down. Releasing hands the snake back to
21
+ whatever heading it had, and taking hold of `A`/`D` cancels the pointer
22
+ target. The channel is muted while the chat, the stat table or a vote is
23
+ open, so typing a message never steers the snake.
24
+
17
25
  Three ways it ends and only one of them is your fault:
18
26
 
19
27
  - your head touches the boundary of the disc — you crash;
@@ -21,8 +29,15 @@ Three ways it ends and only one of them is your fault:
21
29
  were carrying scatters over the map where you died;
22
30
  - your head touches **your own** tail — nothing happens, it passes over.
23
31
 
24
- The stat table (`Tab`) is a single leaderboard ranked by crystals carried right
25
- now. Everyone is a player; there are no teams.
32
+ Three numbers follow you, and none of them is reset by a crash: **eaten**
33
+ (crystals swallowed), **kills** (snakes that ran into you) and **score**, which
34
+ is the crystals you ate plus the entire score of everyone you killed. Killing
35
+ the leader is worth the double: you take their whole score, and their crystals
36
+ scatter on the map on top of it. The HUD shows all three; what you are carrying
37
+ right now is told by the size of your snake.
38
+
39
+ The stat table (`Tab`) is a single leaderboard ranked by score, ties broken by
40
+ crystals eaten. Everyone is a player; there are no teams.
26
41
 
27
42
  ## Run it
28
43
 
@@ -38,6 +53,37 @@ has been built once Vite fails to resolve the import and `npm run dev` dies on
38
53
  startup. The same holds before the master is started against this package,
39
54
  even in dev mode — build the core and then `npm run build` at least once.
40
55
 
56
+ ### In the lobby, against another player
57
+
58
+ `npm run dev` is a single tab against bots. To play this game through the
59
+ lobby master (two tabs, a real room), link both halves and start the master
60
+ from the engine checkout:
61
+
62
+ ```bash
63
+ # once, in this package and in ~/Sites/my/vimp-tanks
64
+ npm link
65
+
66
+ # once, in the engine checkout — BOTH games in ONE command, or the second
67
+ # link overwrites the first
68
+ cd ~/Sites/my/vimp
69
+ npm link @vimp-games/tanks @vimp-games/snakes
70
+
71
+ npm run dev # master on https://localhost:3002
72
+ ```
73
+
74
+ The master builds its game catalog from `node_modules/@vimp-games/*` when
75
+ `GAMES_MATRIX` is unset, sorted by id — so `snakes` comes first and is the
76
+ lobby's **active** game, which is what makes the `Create server` button
77
+ clickable (the lobby can only host the active game). To pin a different one,
78
+ start the master with the catalog spelled out:
79
+
80
+ ```bash
81
+ GAMES_MATRIX='[{"id":"tanks","package":"@vimp-games/tanks"}]' npm run dev
82
+ ```
83
+
84
+ This package must be built (`npm run core:build && npm run build`) before the
85
+ master starts: the catalog reads `dist/manifest.json`, not the sources.
86
+
41
87
  ## Commands
42
88
 
43
89
  ```bash
@@ -75,9 +121,9 @@ Only `dist/` is published (`files: ["dist"]`).
75
121
  round only ends through a reported kill) and what makes respawning possible at
76
122
  all (the engine has no per-player respawn inside a round — its only spawn
77
123
  primitive is private to `_startRound`). The consequence is that the engine also
78
- never writes `score` or `deaths`, which is why `src/host/StatBridge.js` writes
79
- them instead, off the core's `custom` events. See the note atop
80
- `src/config/game.js`.
124
+ never writes `score` or `deaths`, which is why `src/host/StatBridge.js` keeps
125
+ the whole scoring model instead — eaten, kills and score, per game id off the
126
+ core's `custom` events. See the note atop `src/config/game.js`.
81
127
 
82
128
  **There is no physics.** A snake is a polyline; the arena edge, other bodies
83
129
  and crystals are distance tests. The engine's Rapier world is stepped and stays
@@ -91,6 +137,13 @@ never the core — so the disc comes out of the map grid by one formula both
91
137
  halves apply (`core/src/arena.rs`, `src/client/parts/Arena.js`,
92
138
  `src/data/maps/arena.js`).
93
139
 
140
+ **The arena grows with the crowd.** The area per snake is the difficulty curve,
141
+ so the grid is a function of how many are in the room: `buildArena(count)` in
142
+ `src/data/maps/arena.js` owns the law, `src/host/ArenaScaler.js` owns when it
143
+ applies, and the core's `population` custom event is what triggers it. The
144
+ resize is a `coreAdapter.createMap` plus a MAP_DATA re-send, not an engine map
145
+ change: the round, the panel and the stat table survive it untouched.
146
+
94
147
  ## Headless verification
95
148
 
96
149
  From the **engine** checkout, with this package linked:
@@ -103,12 +156,17 @@ npm run sim -- --game <path to vimp-snakes> --scenario <path>/scenarios/movement
103
156
  | --- | --- |
104
157
  | `movement.json` | cruising and turning, with prediction drift checked against tight thresholds |
105
158
  | `crash-and-respawn.json` | driving into the boundary, staying dead, the respawn key |
106
- | `growth.json` | two players, three bots, crystals, the boost, `/spawn` |
107
-
108
- All three are expected to pass with `--determinism`. Two invariants skip by
109
- design: `roundLifecycle` (this game has no round end) and, in two of the three
159
+ | `growth.json` | two players, three bots, crystals, the boost, `/bot` |
160
+ | `pointer.json` | steering to a point with the mouse/finger, the keyboard taking over mid-run, the double-tap boost |
161
+ | `bots.json` | `/bot <count>` as a SET: six bots, then two, then a refused count, then none |
162
+
163
+ All five are expected to pass with `--determinism`.
164
+ The sim runs the **built** plugin (`dist/`), so `npm run build` before it or
165
+ you are testing the previous version. Two invariants skip by
166
+ design: `roundLifecycle` (this game has no round end) and, in two of the four
110
167
  scenarios, `predictionDrift` (a crash and a respawn are legitimate one-off
111
- spikes — `movement.json` is the one that watches for drift that *grows*).
168
+ spikes — `movement.json` and `pointer.json` are the two that watch for drift
169
+ that *grows*).
112
170
 
113
171
  ## Sounds without ffmpeg
114
172
 
@@ -0,0 +1,8 @@
1
+ import{Container as D,Graphics as T,Sprite as Z,Rectangle as ee}from"pixi.js";const te=3,ne='#panel{gap:16px;font:600 20px/1 system-ui,sans-serif;color:#e6ecf5;text-shadow:0 1px 2px rgb(0 0 0 / 60%)}#panel-eaten:before{content:"◆ ";color:#7ef9ff}#panel-kills:before{content:"☠ ";color:#ff7a8a}#panel-score:before{content:"★ ";color:#ffd76b}#panel-crystals,#panel-dead,#panel-mode,#panel-time{display:none!important}#stat .stat-head span:nth-child(1),#stat table td:nth-child(1){width:34%}#stat .stat-head span:nth-child(2),#stat table td:nth-child(2){width:12%;text-align:center}#stat .stat-head span:nth-child(3),#stat table td:nth-child(3),#stat .stat-head span:nth-child(4),#stat table td:nth-child(4),#stat .stat-head span:nth-child(5),#stat table td:nth-child(5),#stat .stat-head span:nth-child(6),#stat table td:nth-child(6){width:13.5%;text-align:right}#snakes-gameover{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center;background:#060a149e;font:400 16px/1.5 system-ui,sans-serif;color:#e6ecf5}#snakes-gameover[hidden]{display:none}.snakes-gameover-card{min-width:260px;padding:28px 32px;border:1px solid #39507f;border-radius:12px;background:#141c30;box-shadow:0 18px 48px #00000073;text-align:center}.snakes-gameover-card h2{margin:0 0 4px;font-size:22px;font-weight:600;letter-spacing:.02em}.snakes-gameover-score{margin:0 0 6px;opacity:.75}.snakes-gameover-total{margin:0 0 22px;font-size:15px;letter-spacing:.02em;opacity:.6}.snakes-gameover-total span{font-weight:700;color:#ffd76b}.snakes-gameover-score span{font-size:40px;font-weight:700;color:#7ef9ff;vertical-align:-2px}.snakes-gameover-card button{min-width:120px;padding:10px 20px;border:0;border-radius:8px;background:#3ab7ff;color:#06182a;font:inherit;font-weight:600;cursor:pointer}.snakes-gameover-card button:hover,.snakes-gameover-card button:focus-visible{background:#7ed0ff}',re={background:856608,floor:1317936,edge:3756159,edgeWidth:10,rings:4,ringColor:1911111},v={innerScale:.62,innerDarken:.55,smoothing:4,eye:16251903,pupil:1054760,boostGlow:16771466},se=.5;class oe extends D{constructor(e){super(),this.zIndex=0,!(Array.isArray(e)||e.type==="dynamic")&&this._draw(e)}_draw({map:e,step:t}){const n=e.length,a=(e[0]?.length??0)*t,i=n*t,c=Math.min(a,i)/2,_=a/2,l=i/2,{background:d,floor:f,edge:p,edgeWidth:b,rings:m,ringColor:Q}=re,w=new T;w.rect(-a,-i,a*3,i*3),w.fill(d),w.circle(_,l,c),w.fill(f);for(let P=1;P<m;P+=1)w.circle(_,l,c*P/m),w.stroke({color:Q,width:2,alpha:se});w.circle(_,l,c),w.stroke({color:p,width:b,alignment:1}),this.addChild(w)}update(){}destroy(){super.destroy({children:!0})}}const F=[16731469,16752412,16765503,10214972,3065014,3848191,5531903,11623679,16735432,16250871,8250065,16753828],G=[8321535,10146047,13154047,12386267,16771466,16757702],Y=[{value:1,radius:8},{value:3,radius:13},{value:8,radius:20}],V=16,k=V*2,A={ANGLE:k,RADIUS:k+1,CRYSTALS:k+2,COLOR:k+3,BOOST:k+4};function ie(r,e){const t=Math.round((r>>16&255)*e),n=Math.round((r>>8&255)*e),o=Math.round((r&255)*e);return t<<16|n<<8|o}function ce(r,e){const t=r.length-1;if(t<1)return r;const n=[r[0]];for(let o=0;o<t;o+=1){const a=r[Math.max(o-1,0)],i=r[o],c=r[o+1],_=r[Math.min(o+2,t)];for(let l=1;l<=e;l+=1){const d=l/e,f=d*d,p=f*d;n.push([.5*(2*i[0]+(-a[0]+c[0])*d+(2*a[0]-5*i[0]+4*c[0]-_[0])*f+(-a[0]+3*i[0]-3*c[0]+_[0])*p),.5*(2*i[1]+(-a[1]+c[1])*d+(2*a[1]-5*i[1]+4*c[1]-_[1])*f+(-a[1]+3*i[1]-3*c[1]+_[1])*p)])}}return n}class ae extends D{constructor(e,t,n={},o={}){super(),this.zIndex=3,this._sound=n.soundManager,this._localPlayer=n.localPlayer,this._id=o.id??null,this._body=new T,this._head=new T,this.addChild(this._body,this._head),this._headAt=[0,0],this._crystals=null,this.update(e)}update(e){const t=e[A.RADIUS]||1,n=e[A.ANGLE]||0,o=e[A.CRYSTALS]||0,a=!!e[A.BOOST],i=F[(e[A.COLOR]||0)%F.length],c=[];for(let _=0;_<V;_+=1)c.push([e[_*2]||0,e[_*2+1]||0]);this._drawBody(c,t,i,a),this._drawHead(c[0],n,t,i),this._crystals!==null&&o>this._crystals&&this._play("pickup",c[0]),this._crystals=o,this._headAt=c[0]}_drawBody(e,t,n,o){const a=ce(e,v.smoothing),i=this._body;i.clear(),i.moveTo(a[0][0],a[0][1]);for(let c=1;c<a.length;c+=1)i.lineTo(a[c][0],a[c][1]);o&&i.stroke({color:v.boostGlow,width:t*2.6,alpha:.22,cap:"round",join:"round"}),i.stroke({color:n,width:t*2,cap:"round",join:"round"}),i.stroke({color:ie(n,v.innerDarken),width:t*2*v.innerScale,alpha:.45,cap:"round",join:"round"})}_drawHead([e,t],n,o,a){const i=this._head;i.clear(),i.circle(e,t,o),i.fill(a);const c=Math.cos(n),_=Math.sin(n),l=o*.5,d=o*.42,f=o*.32;for(const p of[-1,1]){const b=e+c*d-_*l*p,m=t+_*d+c*l*p;i.circle(b,m,f),i.fill(v.eye),i.circle(b+c*f*.35,m+_*f*.35,f*.5),i.fill(v.pupil)}}_isLocal(){return this._localPlayer?this._localPlayer.is(this._id):(this._warned||(this._warned=!0,console.error("[snakes] the `localPlayer` service is missing: engine too old, game cues stay silent")),!1)}_play(e,[t,n]){!this._sound||!this._isLocal()||this._sound.registerSound(e,{position:{x:t,y:n}})}destroy(){this._play("death",this._headAt),super.destroy({children:!0})}}const R={X:0,Y:1,TIER:2,COLOR:3},_e=32;class le extends D{constructor(e,t){super(),this.zIndex=2,this._sprite=new Z(t.crystalGem),this._sprite.anchor.set(.5),this.addChild(this._sprite),this.update(e)}update(e){this.x=e[R.X]||0,this.y=e[R.Y]||0;const t=Y[e[R.TIER]||0]??Y[0];this._sprite.scale.set(t.radius/_e),this._sprite.tint=G[(e[R.COLOR]||0)%G.length],this._sprite.rotation=(this.x+this.y)%360*(Math.PI/180)}destroy(){super.destroy({children:!0})}}const de={Arena:oe,Snake:ae,Crystal:le};function fe(r,e){const{radius:t,facets:n}=r,o=t*2,a=new T,i=(d,f,p=0)=>{const b=(f+p)/n*Math.PI*2-Math.PI/2;return[t+Math.cos(b)*d,t+Math.sin(b)*d]},c=[];for(let d=0;d<n;d+=1)c.push(i(t*.94,d));a.poly(c.flat()),a.fill({color:16777215,alpha:.85});const _=[];for(let d=0;d<n;d+=1)_.push(i(t*.5,d,.5));a.poly(_.flat()),a.fill({color:16777215,alpha:1});const l=e.generateTexture({target:a,frame:new ee(0,0,o,o)});return a.destroy(!0),l}const he={crystalGem:fe},x="snakes-gameover",pe=82,$="d",B="s";function ue(r,e){const t=new KeyboardEvent(r,{keyCode:e,bubbles:!0});t.keyCode!==e&&(Object.defineProperty(t,"keyCode",{value:e}),Object.defineProperty(t,"which",{value:e})),window.dispatchEvent(t)}function ge(r){const e={};for(const t of r??[]){const n=String(t).indexOf(":");n>0&&(e[t.slice(0,n)]=Number(t.slice(n+1)))}return e}class be{constructor(){this._root=null,this._crystals=null,this._total=null,this._visible=!1,this._lastScore=0}mount(){if(this._root||typeof document>"u")return;const e=document.createElement("div");e.id=x,e.hidden=!0,e.innerHTML=`
2
+ <div class="${x}-card">
3
+ <h2>You crashed</h2>
4
+ <p class="${x}-score"><span>0</span> crystals</p>
5
+ <p class="${x}-total">Total score: <span>0</span></p>
6
+ <button type="button">OK</button>
7
+ </div>
8
+ `,this._crystals=e.querySelector(`.${x}-score span`),this._total=e.querySelector(`.${x}-total span`),e.querySelector("button").addEventListener("click",()=>this._respawn()),document.body.appendChild(e),this._root=e}onPanel(e){if(!this._root)return;const t=ge(e);if(B in t&&(this._lastScore=t[B]),!($ in t))return;const n=t[$];n>0?this._show(n-1):this._hide()}reset(){this._hide(),this._lastScore=0}_show(e){this._visible||(this._visible=!0,this._crystals.textContent=String(e),this._total.textContent=String(this._lastScore),this._root.hidden=!1)}_hide(){this._visible&&(this._visible=!1,this._root.hidden=!0)}_respawn(){this._hide();for(const e of["keydown","keyup"])ue(e,pe)}}const we="modulepreload",ye=function(r){return"/"+r},U={},me=function(e,t,n){let o=Promise.resolve();if(t&&t.length>0){let _=function(l){return Promise.all(l.map(d=>Promise.resolve(d).then(f=>({status:"fulfilled",value:f}),f=>({status:"rejected",reason:f}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),c=i?.nonce||i?.getAttribute("nonce");o=_(t.map(l=>{if(l=ye(l),l in U)return;U[l]=!0;const d=l.endsWith(".css"),f=d?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${f}`))return;const p=document.createElement("link");if(p.rel=d?"stylesheet":we,d||(p.as="script"),p.crossOrigin="",p.href=l,c&&p.setAttribute("nonce",c),document.head.appendChild(p),d)return new Promise((b,m)=>{p.addEventListener("load",b),p.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${l}`)))})}))}function a(i){const c=new Event("vite:preloadError",{cancelable:!0});if(c.payload=i,window.dispatchEvent(c),!c.defaultPrevented)throw i}return o.then(i=>{for(const c of i||[])c.status==="rejected"&&a(c.reason);return e().catch(a)})},ve=r=>(r??"").endsWith(".js"),xe=r=>import(r),Se=()=>me(()=>Promise.resolve().then(()=>Te),[]),I=new be,Ie={id:"snakes",engineApi:te,async createClientCore(r,{wasmUrl:e}={}){if(ve(e)){const a=await xe(e);return{core:new a.ClientCore(r),memory:null}}const{default:t,ClientCore:n}=await Se(),o=await t({module_or_path:e});return{core:new n(r),memory:o.memory}},parts:de,bakers:he,styles:ne,hooks:{onAuth(r,e){r.set_model(e.model),I.mount(),I.reset()},onPanel(r,e){r.sync_panel(JSON.stringify(e)),I.onPanel(e)},onLocalAction(){return null}}};class N{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,q.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_clientcore_free(e,0)}apply_aim(e,t,n,o){s.clientcore_apply_aim(this.__wbg_ptr,e,t,n,o)}apply_input(e,t,n){const o=u(e,s.__wbindgen_malloc,s.__wbindgen_realloc),a=h,i=u(t,s.__wbindgen_malloc,s.__wbindgen_realloc),c=h;s.clientcore_apply_input(this.__wbg_ptr,o,a,i,c,n)}debug_json(){let e,t;try{const n=s.clientcore_debug_json(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}decode_frame(e){let t,n;try{const o=z(e,s.__wbindgen_malloc),a=h,i=s.clientcore_decode_frame(this.__wbg_ptr,o,a);return t=i[0],n=i[1],g(i[0],i[1])}finally{s.__wbindgen_free(t,n,1)}}hot_ptr(){return s.clientcore_hot_ptr(this.__wbg_ptr)>>>0}hot_values(){const e=s.clientcore_hot_values(this.__wbg_ptr);var t=j(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}my_game_id(){return s.clientcore_my_game_id(this.__wbg_ptr)}constructor(e){const t=u(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=h,o=s.clientcore_new(t,n);if(o[2])throw y(o[1]);return this.__wbg_ptr=o[0],q.register(this,this.__wbg_ptr,this),this}offset(){return s.clientcore_offset(this.__wbg_ptr)}push_frame(e,t){const n=z(e,s.__wbindgen_malloc),o=h;return s.clientcore_push_frame(this.__wbg_ptr,n,o,t)!==0}reset(){s.clientcore_reset(this.__wbg_ptr)}resync(){s.clientcore_resync(this.__wbg_ptr)}sample(e){return s.clientcore_sample(this.__wbg_ptr,e)>>>0}set_active(e){s.clientcore_set_active(this.__wbg_ptr,e)}set_map(e){const t=u(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=h,o=s.clientcore_set_map(this.__wbg_ptr,t,n);if(o[1])throw y(o[0])}set_model(e){const t=u(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=h;s.clientcore_set_model(this.__wbg_ptr,t,n)}sync_panel(e){const t=u(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=h;s.clientcore_sync_panel(this.__wbg_ptr,t,n)}take_divergence(){let e,t;try{const n=s.clientcore_take_divergence(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}take_frames(){let e,t;try{const n=s.clientcore_take_frames(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(N.prototype[Symbol.dispose]=N.prototype.free);class W{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,K.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_gamecore_free(e,0)}alive_players(){const e=s.gamecore_alive_players(this.__wbg_ptr);var t=j(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}apply_aim(e,t,n,o,a){s.gamecore_apply_aim(this.__wbg_ptr,e,t,n,o,a)}apply_input(e,t,n,o){const a=u(n,s.__wbindgen_malloc,s.__wbindgen_realloc),i=h,c=u(o,s.__wbindgen_malloc,s.__wbindgen_realloc),_=h;s.gamecore_apply_input(this.__wbg_ptr,e,t,a,i,c,_)}body_has_events(){return s.gamecore_body_has_events(this.__wbg_ptr)!==0}clear(){s.gamecore_clear(this.__wbg_ptr)}debug_json(){let e,t;try{const n=s.gamecore_debug_json(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}deserialize_state(e){const t=z(e,s.__wbindgen_malloc),n=h,o=s.gamecore_deserialize_state(this.__wbg_ptr,t,n);if(o[1])throw y(o[0])}frame_bytes(){const e=s.gamecore_frame_bytes(this.__wbg_ptr);var t=H(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}frame_ptr(){return s.gamecore_frame_ptr(this.__wbg_ptr)>>>0}is_alive(e){return s.gamecore_is_alive(this.__wbg_ptr,e)!==0}last_input_seq(e){return s.gamecore_last_input_seq(this.__wbg_ptr,e)>>>0}load_map(e){const t=u(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=h,o=s.gamecore_load_map(this.__wbg_ptr,t,n);if(o[1])throw y(o[0])}map_info(){let e,t;try{const n=s.gamecore_map_info(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}constructor(e){const t=u(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=h,o=s.gamecore_new(t,n);if(o[2])throw y(o[1]);return this.__wbg_ptr=o[0],K.register(this,this.__wbg_ptr,this),this}pack_body(){const e=s.gamecore_pack_body(this.__wbg_ptr);if(e[1])throw y(e[0])}pack_frame(e,t,n,o,a,i,c,_){var l=Ae(c)?0:u(c,s.__wbindgen_malloc,s.__wbindgen_realloc),d=h;return s.gamecore_pack_frame(this.__wbg_ptr,e,t,n,o,a,i,l,d,_)>>>0}players_data(){let e,t;try{const n=s.gamecore_players_data(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}position_of(e){const t=s.gamecore_position_of(this.__wbg_ptr,e);var n=j(t[0],t[1]).slice();return s.__wbindgen_free(t[0],t[1]*4,4),n}remove_actor(e){s.gamecore_remove_actor(this.__wbg_ptr,e)}remove_players_and_shots(){let e,t;try{const n=s.gamecore_remove_players_and_shots(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}remove_scripted_actor(e){s.gamecore_remove_scripted_actor(this.__wbg_ptr,e)}reset_actor(e,t,n,o,a){s.gamecore_reset_actor(this.__wbg_ptr,e,t,n,o,a)}reset_all_vitals(){s.gamecore_reset_all_vitals(this.__wbg_ptr)}serialize_state(){const e=s.gamecore_serialize_state(this.__wbg_ptr);if(e[3])throw y(e[2]);var t=H(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}spawn_actor(e,t,n,o,a,i){const c=u(t,s.__wbindgen_malloc,s.__wbindgen_realloc),_=h,l=s.gamecore_spawn_actor(this.__wbg_ptr,e,c,_,n,o,a,i);if(l[1])throw y(l[0])}spawn_scripted_actor(e,t,n,o,a,i){const c=u(t,s.__wbindgen_malloc,s.__wbindgen_realloc),_=h,l=s.gamecore_spawn_scripted_actor(this.__wbg_ptr,e,c,_,n,o,a,i);if(l[1])throw y(l[0])}step(e){s.gamecore_step(this.__wbg_ptr,e)}take_events(){let e,t;try{const n=s.gamecore_take_events(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(W.prototype[Symbol.dispose]=W.prototype.free);function X(){return{__proto__:null,"./vimp_snakes_core_bg.js":{__proto__:null,__wbg_Error_408e67f47ca7b58b:function(e,t){return Error(g(e,t))},__wbg___wbindgen_throw_bb96b2010945f0bc:function(e,t){throw new Error(g(e,t))},__wbindgen_init_externref_table:function(){const e=s.__wbindgen_externrefs,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)}}}}const q=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(r=>s.__wbg_clientcore_free(r,1)),K=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(r=>s.__wbg_gamecore_free(r,1));function j(r,e){return r=r>>>0,ke().subarray(r/4,r/4+e)}function H(r,e){return r=r>>>0,S().subarray(r/1,r/1+e)}let E=null;function ke(){return(E===null||E.byteLength===0)&&(E=new Float32Array(s.memory.buffer)),E}function g(r,e){return Oe(r>>>0,e)}let O=null;function S(){return(O===null||O.byteLength===0)&&(O=new Uint8Array(s.memory.buffer)),O}function Ae(r){return r==null}function z(r,e){const t=e(r.length*1,1)>>>0;return S().set(r,t/1),h=r.length,t}function u(r,e,t){if(t===void 0){const c=C.encode(r),_=e(c.length,1)>>>0;return S().subarray(_,_+c.length).set(c),h=c.length,_}let n=r.length,o=e(n,1)>>>0;const a=S();let i=0;for(;i<n;i++){const c=r.charCodeAt(i);if(c>127)break;a[o+i]=c}if(i!==n){i!==0&&(r=r.slice(i)),o=t(o,n,n=i+r.length*3,1)>>>0;const c=S().subarray(o+i,o+n),_=C.encodeInto(r,c);i+=_.written,o=t(o,n,i,1)>>>0}return h=i,o}function y(r){const e=s.__wbindgen_externrefs.get(r);return s.__externref_table_dealloc(r),e}let L=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});L.decode();const Ee=2146435072;let M=0;function Oe(r,e){return M+=e,M>=Ee&&(L=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),L.decode(),M=e),L.decode(S().subarray(r,r+e))}const C=new TextEncoder;"encodeInto"in C||(C.encodeInto=function(r,e){const t=C.encode(r);return e.set(t),{read:r.length,written:t.length}});let h=0,s;function J(r,e){return s=r.exports,E=null,O=null,s.__wbindgen_start(),s}async function Ce(r,e){if(typeof Response=="function"&&r instanceof Response){if(!r.ok)throw new Error(`failed to fetch Wasm: ${r.status} ${r.statusText} fetching '${r.url}'`);if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(r,e)}catch(o){if(t(r.type)&&r.headers.get("Content-Type")!=="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",o);else throw o}const n=await r.arrayBuffer();return await WebAssembly.instantiate(n,e)}else{const n=await WebAssembly.instantiate(r,e);return n instanceof WebAssembly.Instance?{instance:n,module:r}:n}function t(n){switch(n){case"basic":case"cors":case"default":return!0}return!1}}function Re(r){if(s!==void 0)return s;r!==void 0&&(Object.getPrototypeOf(r)===Object.prototype?{module:r}=r:console.warn("using deprecated parameters for `initSync()`; pass a single object instead"));const e=X();r instanceof WebAssembly.Module||(r=new WebAssembly.Module(r));const t=new WebAssembly.Instance(r,e);return J(t)}async function Le(r){if(s!==void 0)return s;r!==void 0&&(Object.getPrototypeOf(r)===Object.prototype?{module_or_path:r}=r:console.warn("using deprecated parameters for the initialization function; pass a single object instead")),r===void 0&&(r=new URL("/assets/vimp_snakes_core_bg-DoTMN3X5.wasm",import.meta.url));const e=X();(typeof r=="string"||typeof Request=="function"&&r instanceof Request||typeof URL=="function"&&r instanceof URL)&&(r=fetch(r));const{instance:t,module:n}=await Ce(await r,e);return J(t)}const Te=Object.freeze(Object.defineProperty({__proto__:null,ClientCore:N,GameCore:W,default:Le,initSync:Re},Symbol.toStringTag,{value:"Module"}));export{Ie as default};
@@ -8,6 +8,11 @@
8
8
  export class ClientCore {
9
9
  free(): void;
10
10
  [Symbol.dispose](): void;
11
+ /**
12
+ * Ввод указателем: мировая точка + биты состояния — в историю
13
+ * предикта.
14
+ */
15
+ apply_aim(x: number, y: number, flags: number, local_now: number): void;
11
16
  /**
12
17
  * Ввод игрока: action ('down'/'up') + имя клавиши — в историю
13
18
  * предикта.
@@ -125,6 +130,11 @@ export class GameCore {
125
130
  * (аналог Game.getAlivePlayers для меты).
126
131
  */
127
132
  alive_players(): Float32Array;
133
+ /**
134
+ * Ввод указателем: seq + мировая точка + биты состояния
135
+ * (wire-формат 'seq:aim:x:y:flags' разбирает JS-оболочка).
136
+ */
137
+ apply_aim(game_id: number, seq: number, x: number, y: number, flags: number): void;
128
138
  /**
129
139
  * Ввод игрока: seq + action ('down'/'up') + имя клавиши
130
140
  * (wire-формат 'seq:action:name' разбирает JS-оболочка).
@@ -15,6 +15,17 @@ class ClientCore {
15
15
  const ptr = this.__destroy_into_raw();
16
16
  wasm.__wbg_clientcore_free(ptr, 0);
17
17
  }
18
+ /**
19
+ * Ввод указателем: мировая точка + биты состояния — в историю
20
+ * предикта.
21
+ * @param {number} x
22
+ * @param {number} y
23
+ * @param {number} flags
24
+ * @param {number} local_now
25
+ */
26
+ apply_aim(x, y, flags, local_now) {
27
+ wasm.clientcore_apply_aim(this.__wbg_ptr, x, y, flags, local_now);
28
+ }
18
29
  /**
19
30
  * Ввод игрока: action ('down'/'up') + имя клавиши — в историю
20
31
  * предикта.
@@ -275,6 +286,18 @@ class GameCore {
275
286
  wasm.__wbindgen_free(ret[0], ret[1] * 4, 4);
276
287
  return v1;
277
288
  }
289
+ /**
290
+ * Ввод указателем: seq + мировая точка + биты состояния
291
+ * (wire-формат 'seq:aim:x:y:flags' разбирает JS-оболочка).
292
+ * @param {number} game_id
293
+ * @param {number} seq
294
+ * @param {number} x
295
+ * @param {number} y
296
+ * @param {number} flags
297
+ */
298
+ apply_aim(game_id, seq, x, y, flags) {
299
+ wasm.gamecore_apply_aim(this.__wbg_ptr, game_id, seq, x, y, flags);
300
+ }
278
301
  /**
279
302
  * Ввод игрока: seq + action ('down'/'up') + имя клавиши
280
303
  * (wire-формат 'seq:action:name' разбирает JS-оболочка).
@@ -3,6 +3,7 @@
3
3
  export const memory: WebAssembly.Memory;
4
4
  export const __wbg_clientcore_free: (a: number, b: number) => void;
5
5
  export const __wbg_gamecore_free: (a: number, b: number) => void;
6
+ export const clientcore_apply_aim: (a: number, b: number, c: number, d: number, e: number) => void;
6
7
  export const clientcore_apply_input: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
7
8
  export const clientcore_debug_json: (a: number) => [number, number];
8
9
  export const clientcore_decode_frame: (a: number, b: number, c: number) => [number, number];
@@ -22,6 +23,7 @@ export const clientcore_sync_panel: (a: number, b: number, c: number) => void;
22
23
  export const clientcore_take_divergence: (a: number) => [number, number];
23
24
  export const clientcore_take_frames: (a: number) => [number, number];
24
25
  export const gamecore_alive_players: (a: number) => [number, number];
26
+ export const gamecore_apply_aim: (a: number, b: number, c: number, d: number, e: number, f: number) => void;
25
27
  export const gamecore_apply_input: (a: number, b: number, c: number, d: number, e: number, f: number, g: number) => void;
26
28
  export const gamecore_body_has_events: (a: number) => number;
27
29
  export const gamecore_clear: (a: number) => void;
@@ -0,0 +1 @@
1
+ const q=Array.from({length:16},(n,e)=>[{name:`p${e}x`,ty:"f32",interp:"lerp"},{name:`p${e}y`,ty:"f32",interp:"lerp"}]).flat(),$={s1:{id:1,kind:"indexed8",class:"hot",fields:[...q,{name:"angle",ty:"f32",interp:"lerpAngle"},{name:"radius",ty:"f32",interp:"lerp"},{name:"crystals",ty:"u16"},{name:"color",ty:"u8"},{name:"boost",ty:"u8"}]},cr:{id:2,kind:"indexed32",class:"event",fields:[{name:"x",ty:"f32"},{name:"y",ty:"f32"},{name:"tier",ty:"u8"},{name:"color",ty:"u8"}]},c1:{id:3,kind:"indexedNoNull8",class:"hot",fields:[{name:"x",ty:"f32",interp:"lerp"},{name:"y",ty:"f32",interp:"lerp"},{name:"angle",ty:"f32",interp:"lerpAngle"}]}},H=[{value:1,radius:8},{value:3,radius:13},{value:8,radius:20}],M={s1:{baseSpeed:260,boostFactor:1.9,turnSpeed:3.4,baseRadius:14,radiusGain:1.6,baseLength:150,lengthPerCrystal:9,pointSpacing:6,boostDrainPerSecond:6,boostMinCrystals:2,world:{maxCrystals:60,spawnInterval:.35,tierWeights:[70,25,5],tiers:H,dropRatio:.8,edgeMargin:60,startCrystals:0}}},K={},B=128,I=1,X=20,N=8,C=4,x=64,Z=Math.PI*(3-Math.sqrt(5)),Q=.72;function A(n){const e=Math.ceil(Math.max(n,0)/C)*C,t=Math.max(e,N);return Math.round(X*Math.sqrt(t/N))}function J(n){const e=n*B/2,t=e;return Array.from({length:x},(r,a)=>{const o=e*Q*Math.sqrt((a+.5)/x),i=a*Z,_=t+Math.cos(i)*o,c=t+Math.sin(i)*o;return[Math.round(_),Math.round(c),Math.round(((i*180/Math.PI+180)%360+360)%360)]})}function V(n=0){const e=A(n);return{setId:"c1",scale:1,step:B,physicsStatic:[],physicsDynamic:[],layers:{1:[I]},map:Array.from({length:e},()=>Array.from({length:e},()=>I)),respawns:{players:J(e)}}}const ee=V(0),te={arena:ee},re={title:"Vimp Snakes",parts:{models:M,weapons:K,friendlyFire:!1},snapshot:$,soundCues:{roundStart:null,victory:null,defeat:null,frag:null,death:null},initialVote:"teamChange",maps:te,currentMap:"arena",mapScale:1,mapSetId:"c1",mapsInVote:1,timers:{timeStep:1e3/120,networkSendRate:4,roundTime:36e5,mapTime:36e5,roomTimeMin:1e4,roomTimeMax:36e5,voteTime:1e4,timeBlockedVote:3e4,teamChangeGracePeriod:1e4,roundRestartDelay:5e3,mapChangeDelay:2e3,rttPingInterval:3e3,idleCheckInterval:3e4},playerState:{defaultState:{best:0,eaten:0}},spectatorTeam:"spectators",teams:{players:1,spectators:2},scripted:{namePrefix:"Snake",defaultModel:"s1"},playerKeys:{left:{key:1},right:{key:2},boost:{key:4},respawn:{key:8,type:1}},panel:{fields:{crystals:{key:"c",value:0},eaten:{key:"e",value:0},kills:{key:"k",value:0},score:{key:"s",value:0},dead:{key:"d",value:0}},activeKey:"wa"},stat:{name:{key:0,bodyMethod:"=",headSync:!0,headMethod:"#"},status:{key:1,bodyMethod:"=",bodyValue:"",headValue:""},eaten:{key:2,bodyMethod:"=",bodyValue:0,headMethod:"+",headValue:0},kills:{key:3,bodyMethod:"=",bodyValue:0,headMethod:"+",headValue:0},score:{key:4,bodyMethod:"=",bodyValue:0,headMethod:"+",headValue:0},latency:{key:5,bodyMethod:"="}},roomDefaults:{maxPlayers:32},roomForm:[{name:"maxPlayers",control:"text",label:"Max players",numeric:!0},{name:"map",control:"select",label:"Map",source:"maps"}]},ne={elems:{authId:"auth",fieldsId:"auth-fields",errorId:"auth-error",enterId:"auth-enter",titleId:"auth-title",informsId:"auth-informs"},texts:{title:"Vimp Snakes",sections:[{heading:"How to play",lines:[{keys:"A, D",text:"turn"},{keys:"W",text:"boost (burns crystals)"},{keys:"R",text:"respawn after a crash"},{separator:!0},{keys:"",text:"eat crystals to grow"},{keys:"",text:"the edge and other snakes kill you"},{keys:"",text:"your own tail does not"},{separator:!0},{keys:"C",text:"chat"},{keys:"M",text:"vote"},{keys:"Tab",text:"stats",last:!0}]}]},params:[{name:"model",value:"s1",options:{control:"select",label:"Snake",options:Object.keys(M),validator:"isValidModel",storage:"model"}}],validators:{isValidModel:n=>n in M}},se={pickup:{file:"shot",priority:80,volume:.35},death:{file:"death",priority:150,volume:.5}},ae={codecList:["webm","mp3"],sounds:se},ie={parts:{gameSets:{s1:["Snake"],cr:["Crystal"],c1:["Arena"]},entitiesOnCanvas:{Arena:"vimp",Snake:"vimp",Crystal:"vimp"},bakedAssets:{vimp:[{name:"crystalGem",component:"Crystal",params:{radius:32,facets:6}}]},componentDependencies:{soundManager:["Snake"],localPlayer:["Snake"]},sounds:ae},initIdList:["vimp","panel","chat"],modules:{canvasManager:{canvases:{vimp:{width:960,height:600,aspectRatio:"16:10",baseScale:"1:1",dynamicCamera:!0,shakeCamera:!1}}},controls:{keySetList:[{78:"nextPlayer",80:"prevPlayer"},{65:"left",68:"right",87:"boost",82:"respawn"}],pointer:{keySets:[1],doubleTapMs:300,doubleTapPx:40,sendIntervalMs:50}},chat:{params:{messages:{s:["Team {0} is full. Your current team: {1}","Your team: {0}","Your new team: {0}","Your new status: spectator","{0} crashed into {1}","{0} joined the game","{0} left the game"],v:["A vote has been created","Voting has started","Your vote has been accepted","Voting is temporarily unavailable","Vote passed","Vote failed"],m:["Current map: {0}","Next map: {0}"],c:["Command not found","Your rank: {0}"],n:["Invalid name","{0} changed name to {1}"],g:["Bot snakes in the arena: {0}","Invalid bot count"]}}},panel:{keys:{e:"eaten",k:"kills",s:"score",c:"crystals",d:"dead",wa:"mode",t:"time"},fields:[{name:"eaten",elem:"panel-eaten",type:"value"},{name:"kills",elem:"panel-kills",type:"value"},{name:"score",elem:"panel-score",type:"value"},{name:"crystals",elem:"panel-crystals",type:"value"},{name:"dead",elem:"panel-dead",type:"value"},{name:"mode",elem:"panel-mode",type:"weapon"},{name:"time",elem:"panel-time",type:"time"}]},stat:{params:{columns:["snake","status","eaten","kills","score","ping"],heads:{1:"players"},bodies:{1:"players",2:"spectators"},sortList:{players:[[4,!0],[2,!0]]}}},vote:{params:{templates:{teamChange:["Play or watch?","teams",!0],mapChangeBySystem:["Choose the next map"],mapChangeByUser:["{0} suggested the map: {1}",["Yes","No"]]},menu:[["teamChange",["Play / watch","teams"]]]}}},gameInform:{list:["{0} WINS!","SLITHER!","GAME OVER!"]}};class oe{constructor({participants:e,coreAdapter:t,socketManager:r},a){this._participants=e,this._coreAdapter=t,this._socketManager=r,this._scriptedManager=a,this._population=null,this._size=null,this._mapData=null,this._delivered=new Map}onCoreEvent(e){!e||e.type!=="population"||this._apply(Number(e.count)||0)}_apply(e){const t=this._sizeFor(e);if(t!==this._size){this._size=t,this._population=e;const r=V(e);this._coreAdapter.createMap(r),this._scriptedManager?.createMap(r),this._mapData=r,this._delivered.clear()}this._broadcast()}_sizeFor(e){if(this._size===null)return A(e);const t=A(e);return t>this._size||e<=this._population-C?t:this._size}_broadcast(){if(!this._mapData)return;const e=this._participants.getHumans();for(const t of e)!t.isReady||this._delivered.get(t.gameId)===this._size||(this._delivered.set(t.gameId,this._size),this._socketManager.sendMap(t.socketId,this._mapData));if(this._delivered.size>e.length){const t=new Set(e.map(r=>r.gameId));for(const r of this._delivered.keys())t.has(r)||this._delivered.delete(r)}}get mapData(){return this._mapData??null}}class _e{constructor({participants:e,coreAdapter:t,panel:r,stat:a,scripted:o}){this._participants=e,this._coreAdapter=t,this._panel=r,this._stat=a,this._model=o.defaultModel,this._respawns=null}createMap(e){this._respawns=e.respawns}getCountsPerTeam(){const e={};for(const t of this._participants.getScripted())e[t.team]=(e[t.team]??0)+1;return e}createScripted(e,t=null){if(!this._respawns)return 0;const r=this._participants.getPlayableTeams();let a=0;for(let o=0;o<e&&!this._participants.isFull;o+=1){const i=t??this._pickTeam(r);if(!i||!this._hasRoom(i))break;const _=this._participants.createScripted({team:i,model:this._model}),c=this._participants.get(_);this._stat.addUser(_,c.teamId,{name:c.name,status:"",latency:"BOT"}),this._panel.addUser(_),a+=1}return a}_hasRoom(e){const t=this._respawns[e];return!!(t&&this._participants.getTeamSize(e)<t.length)}_pickTeam(e){return[...e].sort((t,r)=>this._participants.getTeamSize(t)-this._participants.getTeamSize(r)).find(t=>this._hasRoom(t))??null}removeScripted(e=null){const t=this._participants.getScripted();(e?t.filter(a=>a.team===e):[...t]).forEach(a=>this._remove(a.gameId))}removeOneForHuman(e){for(const t of this._participants.getScripted())if(t.team===e)return this._remove(t.gameId),!0;return!1}_remove(e){const t=this._participants.get(e);!t||!t.isScripted||(this._stat.removeUser(e,t.teamId),this._panel.removeUser(e),this._coreAdapter.removePlayer(e),this._participants.remove(e))}}class ce{constructor({participants:e,stat:t}){this._participants=e,this._stat=t,this._totals=new Map}onCoreEvent(e,t={}){if(!e||typeof e!="object")return;const{vimp:r,panel:a}=t,o=String(e.id);switch(e.type){case"crystals":this._onCrystals(o,Number(e.gained)||0,a);break;case"death":this._onDeath(o,e,r,a);break}}reset(e){this._totals.delete(e)}_onCrystals(e,t,r){const a=this._record(e);!a||t<=0||(a.eaten+=t,a.score+=t,this._publish(e,a,r))}_onDeath(e,t,r,a){const o=this._record(e);if(!o)return;const i=t.killer===null||t.killer===void 0?null:String(t.killer);if(i!==null&&i!==e){const _=this._record(i);_&&(_.kills+=1,_.score+=o.score,this._publish(i,_,a))}this._publish(e,o,a),this._recordBest(e,o,r)}_record(e){const t=this._participants.get(e);if(!t)return null;const r=this._totals.get(e);if(r&&r.participant===t)return r;const a={participant:t,eaten:0,kills:0,score:0,flushedEaten:0};return this._totals.set(e,a),a}_publish(e,t,r){const{eaten:a,kills:o,score:i}=t;this._stat.updateUser(e,t.participant.teamId,{eaten:a,kills:o,score:i}),r&&(r.updateUser(e,"eaten",a,"set"),r.updateUser(e,"kills",o,"set"),r.updateUser(e,"score",i,"set"))}_recordBest(e,t,r){if(!r?.getPlayerState)return;const a=r.getPlayerState(e);if(!a)return;const o=t.eaten-t.flushedEaten;t.flushedEaten=t.eaten;const i=Math.max(Number(a.best)||0,t.score),_=(Number(a.eaten)||0)+o;r.setPlayerState(e,{...a,best:i,eaten:_})}}let W=null,L=null;function le(n){const e=new _e(n);return W=new ce(n),L=new oe(n,e),{scripted:e}}function pe(){return W}function de(){return L}const ue={name:"/bot",handler(n,e,t){const r=Number(t[0]);if(t[0]===void 0||!Number.isFinite(r)||r<0){n.chat.pushSystemByUser(e,"BOT_COUNT_INVALID");return}const{maxPlayers:a}=n.participants,o=Math.trunc(r),i=a>0?Math.min(o,a):o;n.scripted.removeScripted();const _=i>0?n.scripted.createScripted(i):0;n.chat.pushSystem("BOTS_SET",[_]),_>0&&n.roundManager.initiateNewRound()}},me={name:"/name",handler(n,e,t){n.roundManager.changeName(e,t.join(" "))}},he={name:"/nr",handler(n,e){n.isDevMode?n.roundManager.initiateNewRound():n.chat.pushSystemByUser(e,"COMMANDS_NOT_FOUND")}},fe={name:"/rank",handler(n,e){n.chat.pushSystemByUser(e,"RANK",[n.playerDataSync.getRank(e)])}},ge={BOTS_SET:"g:0",BOT_COUNT_INVALID:"g:1"},ye="modulepreload",be=function(n){return"/"+n},O={},we=function(e,t,r){let a=Promise.resolve();if(t&&t.length>0){let c=function(p){return Promise.all(p.map(h=>Promise.resolve(h).then(g=>({status:"fulfilled",value:g}),g=>({status:"rejected",reason:g}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),_=i?.nonce||i?.getAttribute("nonce");a=c(t.map(p=>{if(p=be(p),p in O)return;O[p]=!0;const h=p.endsWith(".css"),g=h?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${p}"]${g}`))return;const f=document.createElement("link");if(f.rel=h?"stylesheet":ye,h||(f.as="script"),f.crossOrigin="",f.href=p,_&&f.setAttribute("nonce",_),document.head.appendChild(f),h)return new Promise((G,Y)=>{f.addEventListener("load",G),f.addEventListener("error",()=>Y(new Error(`Unable to preload CSS for ${p}`)))})}))}function o(i){const _=new Event("vite:preloadError",{cancelable:!0});if(_.payload=i,window.dispatchEvent(_),!_.defaultPrevented)throw i}return a.then(i=>{for(const _ of i||[])_.status==="rejected"&&o(_.reason);return e().catch(o)})},ve=n=>(n??"").endsWith(".js"),Se=n=>import(n),ke=()=>we(()=>Promise.resolve().then(()=>Ie),[]),Ne={id:"snakes",engineApi:3,async createCore(n,{wasmUrl:e}={}){if(ve(e)){const a=await Se(e);return new a.GameCore(n)}const{default:t,GameCore:r}=await ke();return await t({module_or_path:e}),new r(n)},gameConfig:re,authSchema:ne,chatCommands:[ue,me,he,fe],systemMessages:ge,createModules:le,buildClientGameConfig:()=>ie,onCoreEvent(n,e={}){pe()?.onCoreEvent(n,e),de()?.onCoreEvent(n)}};class E{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,D.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_clientcore_free(e,0)}apply_aim(e,t,r,a){s.clientcore_apply_aim(this.__wbg_ptr,e,t,r,a)}apply_input(e,t,r){const a=d(e,s.__wbindgen_malloc,s.__wbindgen_realloc),o=l,i=d(t,s.__wbindgen_malloc,s.__wbindgen_realloc),_=l;s.clientcore_apply_input(this.__wbg_ptr,a,o,i,_,r)}debug_json(){let e,t;try{const r=s.clientcore_debug_json(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}decode_frame(e){let t,r;try{const a=R(e,s.__wbindgen_malloc),o=l,i=s.clientcore_decode_frame(this.__wbg_ptr,a,o);return t=i[0],r=i[1],u(i[0],i[1])}finally{s.__wbindgen_free(t,r,1)}}hot_ptr(){return s.clientcore_hot_ptr(this.__wbg_ptr)>>>0}hot_values(){const e=s.clientcore_hot_values(this.__wbg_ptr);var t=P(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}my_game_id(){return s.clientcore_my_game_id(this.__wbg_ptr)}constructor(e){const t=d(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=l,a=s.clientcore_new(t,r);if(a[2])throw m(a[1]);return this.__wbg_ptr=a[0],D.register(this,this.__wbg_ptr,this),this}offset(){return s.clientcore_offset(this.__wbg_ptr)}push_frame(e,t){const r=R(e,s.__wbindgen_malloc),a=l;return s.clientcore_push_frame(this.__wbg_ptr,r,a,t)!==0}reset(){s.clientcore_reset(this.__wbg_ptr)}resync(){s.clientcore_resync(this.__wbg_ptr)}sample(e){return s.clientcore_sample(this.__wbg_ptr,e)>>>0}set_active(e){s.clientcore_set_active(this.__wbg_ptr,e)}set_map(e){const t=d(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=l,a=s.clientcore_set_map(this.__wbg_ptr,t,r);if(a[1])throw m(a[0])}set_model(e){const t=d(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=l;s.clientcore_set_model(this.__wbg_ptr,t,r)}sync_panel(e){const t=d(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=l;s.clientcore_sync_panel(this.__wbg_ptr,t,r)}take_divergence(){let e,t;try{const r=s.clientcore_take_divergence(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}take_frames(){let e,t;try{const r=s.clientcore_take_frames(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(E.prototype[Symbol.dispose]=E.prototype.free);class T{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,z.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_gamecore_free(e,0)}alive_players(){const e=s.gamecore_alive_players(this.__wbg_ptr);var t=P(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}apply_aim(e,t,r,a,o){s.gamecore_apply_aim(this.__wbg_ptr,e,t,r,a,o)}apply_input(e,t,r,a){const o=d(r,s.__wbindgen_malloc,s.__wbindgen_realloc),i=l,_=d(a,s.__wbindgen_malloc,s.__wbindgen_realloc),c=l;s.gamecore_apply_input(this.__wbg_ptr,e,t,o,i,_,c)}body_has_events(){return s.gamecore_body_has_events(this.__wbg_ptr)!==0}clear(){s.gamecore_clear(this.__wbg_ptr)}debug_json(){let e,t;try{const r=s.gamecore_debug_json(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}deserialize_state(e){const t=R(e,s.__wbindgen_malloc),r=l,a=s.gamecore_deserialize_state(this.__wbg_ptr,t,r);if(a[1])throw m(a[0])}frame_bytes(){const e=s.gamecore_frame_bytes(this.__wbg_ptr);var t=F(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}frame_ptr(){return s.gamecore_frame_ptr(this.__wbg_ptr)>>>0}is_alive(e){return s.gamecore_is_alive(this.__wbg_ptr,e)!==0}last_input_seq(e){return s.gamecore_last_input_seq(this.__wbg_ptr,e)>>>0}load_map(e){const t=d(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=l,a=s.gamecore_load_map(this.__wbg_ptr,t,r);if(a[1])throw m(a[0])}map_info(){let e,t;try{const r=s.gamecore_map_info(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}constructor(e){const t=d(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=l,a=s.gamecore_new(t,r);if(a[2])throw m(a[1]);return this.__wbg_ptr=a[0],z.register(this,this.__wbg_ptr,this),this}pack_body(){const e=s.gamecore_pack_body(this.__wbg_ptr);if(e[1])throw m(e[0])}pack_frame(e,t,r,a,o,i,_,c){var p=Ce(_)?0:d(_,s.__wbindgen_malloc,s.__wbindgen_realloc),h=l;return s.gamecore_pack_frame(this.__wbg_ptr,e,t,r,a,o,i,p,h,c)>>>0}players_data(){let e,t;try{const r=s.gamecore_players_data(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}position_of(e){const t=s.gamecore_position_of(this.__wbg_ptr,e);var r=P(t[0],t[1]).slice();return s.__wbindgen_free(t[0],t[1]*4,4),r}remove_actor(e){s.gamecore_remove_actor(this.__wbg_ptr,e)}remove_players_and_shots(){let e,t;try{const r=s.gamecore_remove_players_and_shots(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}remove_scripted_actor(e){s.gamecore_remove_scripted_actor(this.__wbg_ptr,e)}reset_actor(e,t,r,a,o){s.gamecore_reset_actor(this.__wbg_ptr,e,t,r,a,o)}reset_all_vitals(){s.gamecore_reset_all_vitals(this.__wbg_ptr)}serialize_state(){const e=s.gamecore_serialize_state(this.__wbg_ptr);if(e[3])throw m(e[2]);var t=F(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}spawn_actor(e,t,r,a,o,i){const _=d(t,s.__wbindgen_malloc,s.__wbindgen_realloc),c=l,p=s.gamecore_spawn_actor(this.__wbg_ptr,e,_,c,r,a,o,i);if(p[1])throw m(p[0])}spawn_scripted_actor(e,t,r,a,o,i){const _=d(t,s.__wbindgen_malloc,s.__wbindgen_realloc),c=l,p=s.gamecore_spawn_scripted_actor(this.__wbg_ptr,e,_,c,r,a,o,i);if(p[1])throw m(p[0])}step(e){s.gamecore_step(this.__wbg_ptr,e)}take_events(){let e,t;try{const r=s.gamecore_take_events(this.__wbg_ptr);return e=r[0],t=r[1],u(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(T.prototype[Symbol.dispose]=T.prototype.free);function U(){return{__proto__:null,"./vimp_snakes_core_bg.js":{__proto__:null,__wbg_Error_408e67f47ca7b58b:function(e,t){return Error(u(e,t))},__wbg___wbindgen_throw_bb96b2010945f0bc:function(e,t){throw new Error(u(e,t))},__wbindgen_init_externref_table:function(){const e=s.__wbindgen_externrefs,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)}}}}const D=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_clientcore_free(n,1)),z=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_gamecore_free(n,1));function P(n,e){return n=n>>>0,Me().subarray(n/4,n/4+e)}function F(n,e){return n=n>>>0,y().subarray(n/1,n/1+e)}let b=null;function Me(){return(b===null||b.byteLength===0)&&(b=new Float32Array(s.memory.buffer)),b}function u(n,e){return Ee(n>>>0,e)}let w=null;function y(){return(w===null||w.byteLength===0)&&(w=new Uint8Array(s.memory.buffer)),w}function Ce(n){return n==null}function R(n,e){const t=e(n.length*1,1)>>>0;return y().set(n,t/1),l=n.length,t}function d(n,e,t){if(t===void 0){const _=v.encode(n),c=e(_.length,1)>>>0;return y().subarray(c,c+_.length).set(_),l=_.length,c}let r=n.length,a=e(r,1)>>>0;const o=y();let i=0;for(;i<r;i++){const _=n.charCodeAt(i);if(_>127)break;o[a+i]=_}if(i!==r){i!==0&&(n=n.slice(i)),a=t(a,r,r=i+n.length*3,1)>>>0;const _=y().subarray(a+i,a+r),c=v.encodeInto(n,_);i+=c.written,a=t(a,r,i,1)>>>0}return l=i,a}function m(n){const e=s.__wbindgen_externrefs.get(n);return s.__externref_table_dealloc(n),e}let S=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});S.decode();const Ae=2146435072;let k=0;function Ee(n,e){return k+=e,k>=Ae&&(S=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),S.decode(),k=e),S.decode(y().subarray(n,n+e))}const v=new TextEncoder;"encodeInto"in v||(v.encodeInto=function(n,e){const t=v.encode(n);return e.set(t),{read:n.length,written:t.length}});let l=0,s;function j(n,e){return s=n.exports,b=null,w=null,s.__wbindgen_start(),s}async function Te(n,e){if(typeof Response=="function"&&n instanceof Response){if(!n.ok)throw new Error(`failed to fetch Wasm: ${n.status} ${n.statusText} fetching '${n.url}'`);if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(n,e)}catch(a){if(t(n.type)&&n.headers.get("Content-Type")!=="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",a);else throw a}const r=await n.arrayBuffer();return await WebAssembly.instantiate(r,e)}else{const r=await WebAssembly.instantiate(n,e);return r instanceof WebAssembly.Instance?{instance:r,module:n}:r}function t(r){switch(r){case"basic":case"cors":case"default":return!0}return!1}}function Pe(n){if(s!==void 0)return s;n!==void 0&&(Object.getPrototypeOf(n)===Object.prototype?{module:n}=n:console.warn("using deprecated parameters for `initSync()`; pass a single object instead"));const e=U();n instanceof WebAssembly.Module||(n=new WebAssembly.Module(n));const t=new WebAssembly.Instance(n,e);return j(t)}async function Re(n){if(s!==void 0)return s;n!==void 0&&(Object.getPrototypeOf(n)===Object.prototype?{module_or_path:n}=n:console.warn("using deprecated parameters for the initialization function; pass a single object instead")),n===void 0&&(n=new URL("/assets/vimp_snakes_core_bg-DoTMN3X5.wasm",import.meta.url));const e=U();(typeof n=="string"||typeof Request=="function"&&n instanceof Request||typeof URL=="function"&&n instanceof URL)&&(n=fetch(n));const{instance:t,module:r}=await Te(await n,e);return j(t)}const Ie=Object.freeze(Object.defineProperty({__proto__:null,ClientCore:E,GameCore:T,default:Re,initSync:Pe},Symbol.toStringTag,{value:"Module"}));export{Ne as default};
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "id": "snakes",
3
3
  "engineApi": 3,
4
- "version": "57358497931fc0f0",
5
- "title": "Vimp Snakes",
4
+ "version": "0b750983623f8685",
5
+ "title": "Snakes",
6
6
  "entries": {
7
- "client": "/games/snakes/client-DZrdJlPY.js",
8
- "host": "/games/snakes/host-ClLuGvxl.js",
9
- "wasm": "/games/snakes/assets/vimp_snakes_core_bg-BqmtH1zs.wasm",
7
+ "client": "/games/snakes/client-DymCuUVE.js",
8
+ "host": "/games/snakes/host-Bu_7eMBr.js",
9
+ "wasm": "/games/snakes/assets/vimp_snakes_core_bg-DoTMN3X5.wasm",
10
10
  "wasmNode": "./core-node/vimp_snakes_core.js"
11
11
  },
12
12
  "assetsBase": "/games/snakes/",
13
13
  "maps": {
14
- "version": "49eedae6135dc5fb",
14
+ "version": "06b6ec4320792b57",
15
15
  "list": [
16
16
  "arena"
17
17
  ]
18
18
  },
19
19
  "roomDefaults": {
20
- "maxPlayers": 8,
20
+ "maxPlayers": 32,
21
21
  "map": "arena"
22
22
  },
23
23
  "roomForm": [
@@ -26,7 +26,7 @@
26
26
  "control": "text",
27
27
  "label": "Max players",
28
28
  "numeric": true,
29
- "regExp": "[1-8]"
29
+ "regExp": "(?:[1-9]|1[0-9]|2[0-9]|3[0-2])"
30
30
  },
31
31
  {
32
32
  "name": "map",
@@ -1 +1 @@
1
- {"setId":"c1","scale":1,"step":128,"physicsStatic":[],"physicsDynamic":[],"layers":{"1":[1]},"map":[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]],"respawns":{"players":[[1920,1280,180],[1871,1525,203],[1733,1733,225],[1525,1871,248],[1280,1920,270],[1035,1871,293],[827,1733,315],[689,1525,338],[640,1280,0],[689,1035,23],[827,827,45],[1035,689,68],[1280,640,90],[1525,689,113],[1733,827,135],[1871,1035,158]]}}
1
+ {"setId":"c1","scale":1,"step":128,"physicsStatic":[],"physicsDynamic":[],"layers":{"1":[1]},"map":[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]],"respawns":{"players":[[1361,1280,180],[1176,1375,318],[1296,1099,95],[1411,1451,233],[1039,1237,10],[1508,1135,148],[1204,1564,285],[1135,1000,63],[1595,1395,200],[952,1415,338],[1438,942,115],[1397,1653,253],[928,1076,30],[1693,1189,168],[1028,1639,305],[1222,830,83],[1638,1582,220],[798,1300,358],[1631,930,135],[1257,1788,273],[946,880,50],[1809,1351,188],[831,1592,325],[1403,735,103],[1563,1775,240],[726,1103,18],[1818,1031,155],[1047,1837,293],[1072,701,70],[1834,1571,208],[665,1442,345],[1630,736,123],[1391,1927,260],[753,872,38],[1954,1224,175],[814,1784,313],[1283,584,90],[1754,1802,228],[568,1214,5],[1857,842,143],[1149,2001,280],[885,652,58],[2004,1479,195],[604,1627,333],[1547,559,110],[1573,2000,248],[570,944,25],[2039,1046,163],[874,1972,300],[1110,488,78],[1947,1754,215],[460,1382,353],[1821,644,130],[1311,2122,268],[683,674,45],[2137,1324,183],[612,1832,320],[1401,415,98],[1780,2006,235],[414,1081,13],[2060,838,150],[1001,2139,288],[902,451,65],[2125,1639,203]]}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vimp-games/snakes",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Vimp Snakes — a VIMP game plugin",
5
5
  "author": "lgick",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "globals": "^16.2.0",
42
42
  "happy-dom": "^20.10.6",
43
43
  "pixi.js": "^8.14.0",
44
- "vimp-engine": "^0.10.2",
44
+ "vimp-engine": "^0.11.0",
45
45
  "vite": "^7.1.11",
46
46
  "vitest": "^4.1.9"
47
47
  }
@@ -1,7 +0,0 @@
1
- import{Container as F,Graphics as T,Sprite as Z,Rectangle as ee}from"pixi.js";const te=3,ne='#panel{gap:16px;font:600 20px/1 system-ui,sans-serif;color:#e6ecf5;text-shadow:0 1px 2px rgb(0 0 0 / 60%)}#panel-crystals:before{content:"◆ ";color:#7ef9ff}#panel-length:before{content:"↔ ";opacity:.7}#panel-time{opacity:.8}#panel-dead{display:none}#snakes-gameover{position:fixed;inset:0;z-index:50;display:flex;align-items:center;justify-content:center;background:#060a149e;font:400 16px/1.5 system-ui,sans-serif;color:#e6ecf5}#snakes-gameover[hidden]{display:none}.snakes-gameover-card{min-width:260px;padding:28px 32px;border:1px solid #39507f;border-radius:12px;background:#141c30;box-shadow:0 18px 48px #00000073;text-align:center}.snakes-gameover-card h2{margin:0 0 4px;font-size:22px;font-weight:600;letter-spacing:.02em}.snakes-gameover-score{margin:0 0 22px;opacity:.75}.snakes-gameover-score span{font-size:40px;font-weight:700;color:#7ef9ff;vertical-align:-2px}.snakes-gameover-card button{min-width:120px;padding:10px 20px;border:0;border-radius:8px;background:#3ab7ff;color:#06182a;font:inherit;font-weight:600;cursor:pointer}.snakes-gameover-card button:hover,.snakes-gameover-card button:focus-visible{background:#7ed0ff}',re={background:856608,floor:1317936,edge:3756159,edgeWidth:10,rings:4,ringColor:1911111},v={innerScale:.62,innerDarken:.55,smoothing:4,eye:16251903,pupil:1054760,boostGlow:16771466},se=.5;class oe extends F{constructor(e){super(),this.zIndex=0,!(Array.isArray(e)||e.type==="dynamic")&&this._draw(e)}_draw({map:e,step:t}){const n=e.length,_=(e[0]?.length??0)*t,i=n*t,c=Math.min(_,i)/2,a=_/2,l=i/2,{background:f,floor:d,edge:u,edgeWidth:b,rings:m,ringColor:Q}=re,w=new T;w.rect(-_,-i,_*3,i*3),w.fill(f),w.circle(a,l,c),w.fill(d);for(let I=1;I<m;I+=1)w.circle(a,l,c*I/m),w.stroke({color:Q,width:2,alpha:se});w.circle(a,l,c),w.stroke({color:u,width:b,alignment:1}),this.addChild(w)}update(){}destroy(){super.destroy({children:!0})}}const D=[16731469,16752412,16765503,10214972,3065014,3848191,5531903,11623679,16735432,16250871,8250065,16753828],Y=[8321535,10146047,13154047,12386267,16771466,16757702],B=[{value:1,radius:8},{value:3,radius:13},{value:8,radius:20}],V=16,A=V*2,S={ANGLE:A,RADIUS:A+1,CRYSTALS:A+2,COLOR:A+3,BOOST:A+4};function ie(r,e){const t=Math.round((r>>16&255)*e),n=Math.round((r>>8&255)*e),o=Math.round((r&255)*e);return t<<16|n<<8|o}function ce(r,e){const t=r.length-1;if(t<1)return r;const n=[r[0]];for(let o=0;o<t;o+=1){const _=r[Math.max(o-1,0)],i=r[o],c=r[o+1],a=r[Math.min(o+2,t)];for(let l=1;l<=e;l+=1){const f=l/e,d=f*f,u=d*f;n.push([.5*(2*i[0]+(-_[0]+c[0])*f+(2*_[0]-5*i[0]+4*c[0]-a[0])*d+(-_[0]+3*i[0]-3*c[0]+a[0])*u),.5*(2*i[1]+(-_[1]+c[1])*f+(2*_[1]-5*i[1]+4*c[1]-a[1])*d+(-_[1]+3*i[1]-3*c[1]+a[1])*u)])}}return n}class _e extends F{constructor(e,t,n={}){super(),this.zIndex=3,this._sound=n.soundManager,this._body=new T,this._head=new T,this.addChild(this._body,this._head),this._headAt=[0,0],this._crystals=null,this.update(e)}update(e){const t=e[S.RADIUS]||1,n=e[S.ANGLE]||0,o=e[S.CRYSTALS]||0,_=!!e[S.BOOST],i=D[(e[S.COLOR]||0)%D.length],c=[];for(let a=0;a<V;a+=1)c.push([e[a*2]||0,e[a*2+1]||0]);this._drawBody(c,t,i,_),this._drawHead(c[0],n,t,i),this._crystals!==null&&o>this._crystals&&this._play("pickup",c[0]),this._crystals=o,this._headAt=c[0]}_drawBody(e,t,n,o){const _=ce(e,v.smoothing),i=this._body;i.clear(),i.moveTo(_[0][0],_[0][1]);for(let c=1;c<_.length;c+=1)i.lineTo(_[c][0],_[c][1]);o&&i.stroke({color:v.boostGlow,width:t*2.6,alpha:.22,cap:"round",join:"round"}),i.stroke({color:n,width:t*2,cap:"round",join:"round"}),i.stroke({color:ie(n,v.innerDarken),width:t*2*v.innerScale,alpha:.45,cap:"round",join:"round"})}_drawHead([e,t],n,o,_){const i=this._head;i.clear(),i.circle(e,t,o),i.fill(_);const c=Math.cos(n),a=Math.sin(n),l=o*.5,f=o*.42,d=o*.32;for(const u of[-1,1]){const b=e+c*f-a*l*u,m=t+a*f+c*l*u;i.circle(b,m,d),i.fill(v.eye),i.circle(b+c*d*.35,m+a*d*.35,d*.5),i.fill(v.pupil)}}_play(e,[t,n]){this._sound?.registerSound(e,{position:{x:t,y:n}})}destroy(){this._play("death",this._headAt),super.destroy({children:!0})}}const O={X:0,Y:1,TIER:2,COLOR:3},ae=32;class le extends F{constructor(e,t){super(),this.zIndex=2,this._sprite=new Z(t.crystalGem),this._sprite.anchor.set(.5),this.addChild(this._sprite),this.update(e)}update(e){this.x=e[O.X]||0,this.y=e[O.Y]||0;const t=B[e[O.TIER]||0]??B[0];this._sprite.scale.set(t.radius/ae),this._sprite.tint=Y[(e[O.COLOR]||0)%Y.length],this._sprite.rotation=(this.x+this.y)%360*(Math.PI/180)}destroy(){super.destroy({children:!0})}}const fe={Arena:oe,Snake:_e,Crystal:le};function de(r,e){const{radius:t,facets:n}=r,o=t*2,_=new T,i=(f,d,u=0)=>{const b=(d+u)/n*Math.PI*2-Math.PI/2;return[t+Math.cos(b)*f,t+Math.sin(b)*f]},c=[];for(let f=0;f<n;f+=1)c.push(i(t*.94,f));_.poly(c.flat()),_.fill({color:16777215,alpha:.85});const a=[];for(let f=0;f<n;f+=1)a.push(i(t*.5,f,.5));_.poly(a.flat()),_.fill({color:16777215,alpha:1});const l=e.generateTexture({target:_,frame:new ee(0,0,o,o)});return _.destroy(!0),l}const pe={crystalGem:de},R="snakes-gameover",ue=82,G="d",U="c";function he(r,e){const t=new KeyboardEvent(r,{keyCode:e,bubbles:!0});t.keyCode!==e&&(Object.defineProperty(t,"keyCode",{value:e}),Object.defineProperty(t,"which",{value:e})),window.dispatchEvent(t)}function ge(r){const e={};for(const t of r??[]){const n=String(t).indexOf(":");n>0&&(e[t.slice(0,n)]=Number(t.slice(n+1)))}return e}class be{constructor(){this._root=null,this._score=null,this._visible=!1,this._lastCrystals=0}mount(){if(this._root||typeof document>"u")return;const e=document.createElement("div");e.id=R,e.hidden=!0,e.innerHTML=`
2
- <div class="${R}-card">
3
- <h2>You crashed</h2>
4
- <p class="${R}-score"><span>0</span> crystals</p>
5
- <button type="button">OK</button>
6
- </div>
7
- `,this._score=e.querySelector(`.${R}-score span`),e.querySelector("button").addEventListener("click",()=>this._respawn()),document.body.appendChild(e),this._root=e}onPanel(e){if(!this._root)return;const t=ge(e);if(U in t&&(this._lastCrystals=t[U]),!(G in t))return;const n=t[G];n>0?this._show(n-1):this._hide()}reset(){this._hide(),this._lastCrystals=0}_show(e){this._visible||(this._visible=!0,this._score.textContent=String(e),this._root.hidden=!1)}_hide(){this._visible&&(this._visible=!1,this._root.hidden=!0)}_respawn(){this._hide();for(const e of["keydown","keyup"])he(e,ue)}}const we="modulepreload",ye=function(r){return"/"+r},$={},me=function(e,t,n){let o=Promise.resolve();if(t&&t.length>0){let a=function(l){return Promise.all(l.map(f=>Promise.resolve(f).then(d=>({status:"fulfilled",value:d}),d=>({status:"rejected",reason:d}))))};document.getElementsByTagName("link");const i=document.querySelector("meta[property=csp-nonce]"),c=i?.nonce||i?.getAttribute("nonce");o=a(t.map(l=>{if(l=ye(l),l in $)return;$[l]=!0;const f=l.endsWith(".css"),d=f?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${l}"]${d}`))return;const u=document.createElement("link");if(u.rel=f?"stylesheet":we,f||(u.as="script"),u.crossOrigin="",u.href=l,c&&u.setAttribute("nonce",c),document.head.appendChild(u),f)return new Promise((b,m)=>{u.addEventListener("load",b),u.addEventListener("error",()=>m(new Error(`Unable to preload CSS for ${l}`)))})}))}function _(i){const c=new Event("vite:preloadError",{cancelable:!0});if(c.payload=i,window.dispatchEvent(c),!c.defaultPrevented)throw i}return o.then(i=>{for(const c of i||[])c.status==="rejected"&&_(c.reason);return e().catch(_)})},ve=r=>(r??"").endsWith(".js"),xe=r=>import(r),Ae=()=>me(()=>Promise.resolve().then(()=>Te),[]),M=new be,Me={id:"snakes",engineApi:te,async createClientCore(r,{wasmUrl:e}={}){if(ve(e)){const _=await xe(e);return{core:new _.ClientCore(r),memory:null}}const{default:t,ClientCore:n}=await Ae(),o=await t({module_or_path:e});return{core:new n(r),memory:o.memory}},parts:fe,bakers:pe,styles:ne,hooks:{onAuth(r,e){r.set_model(e.model),M.mount(),M.reset()},onPanel(r,e){r.sync_panel(JSON.stringify(e)),M.onPanel(e)},onLocalAction(){return null}}};class W{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,q.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_clientcore_free(e,0)}apply_input(e,t,n){const o=h(e,s.__wbindgen_malloc,s.__wbindgen_realloc),_=p,i=h(t,s.__wbindgen_malloc,s.__wbindgen_realloc),c=p;s.clientcore_apply_input(this.__wbg_ptr,o,_,i,c,n)}debug_json(){let e,t;try{const n=s.clientcore_debug_json(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}decode_frame(e){let t,n;try{const o=z(e,s.__wbindgen_malloc),_=p,i=s.clientcore_decode_frame(this.__wbg_ptr,o,_);return t=i[0],n=i[1],g(i[0],i[1])}finally{s.__wbindgen_free(t,n,1)}}hot_ptr(){return s.clientcore_hot_ptr(this.__wbg_ptr)>>>0}hot_values(){const e=s.clientcore_hot_values(this.__wbg_ptr);var t=j(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}my_game_id(){return s.clientcore_my_game_id(this.__wbg_ptr)}constructor(e){const t=h(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=p,o=s.clientcore_new(t,n);if(o[2])throw y(o[1]);return this.__wbg_ptr=o[0],q.register(this,this.__wbg_ptr,this),this}offset(){return s.clientcore_offset(this.__wbg_ptr)}push_frame(e,t){const n=z(e,s.__wbindgen_malloc),o=p;return s.clientcore_push_frame(this.__wbg_ptr,n,o,t)!==0}reset(){s.clientcore_reset(this.__wbg_ptr)}resync(){s.clientcore_resync(this.__wbg_ptr)}sample(e){return s.clientcore_sample(this.__wbg_ptr,e)>>>0}set_active(e){s.clientcore_set_active(this.__wbg_ptr,e)}set_map(e){const t=h(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=p,o=s.clientcore_set_map(this.__wbg_ptr,t,n);if(o[1])throw y(o[0])}set_model(e){const t=h(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=p;s.clientcore_set_model(this.__wbg_ptr,t,n)}sync_panel(e){const t=h(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=p;s.clientcore_sync_panel(this.__wbg_ptr,t,n)}take_divergence(){let e,t;try{const n=s.clientcore_take_divergence(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}take_frames(){let e,t;try{const n=s.clientcore_take_frames(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(W.prototype[Symbol.dispose]=W.prototype.free);class N{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,K.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_gamecore_free(e,0)}alive_players(){const e=s.gamecore_alive_players(this.__wbg_ptr);var t=j(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}apply_input(e,t,n,o){const _=h(n,s.__wbindgen_malloc,s.__wbindgen_realloc),i=p,c=h(o,s.__wbindgen_malloc,s.__wbindgen_realloc),a=p;s.gamecore_apply_input(this.__wbg_ptr,e,t,_,i,c,a)}body_has_events(){return s.gamecore_body_has_events(this.__wbg_ptr)!==0}clear(){s.gamecore_clear(this.__wbg_ptr)}debug_json(){let e,t;try{const n=s.gamecore_debug_json(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}deserialize_state(e){const t=z(e,s.__wbindgen_malloc),n=p,o=s.gamecore_deserialize_state(this.__wbg_ptr,t,n);if(o[1])throw y(o[0])}frame_bytes(){const e=s.gamecore_frame_bytes(this.__wbg_ptr);var t=H(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}frame_ptr(){return s.gamecore_frame_ptr(this.__wbg_ptr)>>>0}is_alive(e){return s.gamecore_is_alive(this.__wbg_ptr,e)!==0}last_input_seq(e){return s.gamecore_last_input_seq(this.__wbg_ptr,e)>>>0}load_map(e){const t=h(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=p,o=s.gamecore_load_map(this.__wbg_ptr,t,n);if(o[1])throw y(o[0])}map_info(){let e,t;try{const n=s.gamecore_map_info(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}constructor(e){const t=h(e,s.__wbindgen_malloc,s.__wbindgen_realloc),n=p,o=s.gamecore_new(t,n);if(o[2])throw y(o[1]);return this.__wbg_ptr=o[0],K.register(this,this.__wbg_ptr,this),this}pack_body(){const e=s.gamecore_pack_body(this.__wbg_ptr);if(e[1])throw y(e[0])}pack_frame(e,t,n,o,_,i,c,a){var l=ke(c)?0:h(c,s.__wbindgen_malloc,s.__wbindgen_realloc),f=p;return s.gamecore_pack_frame(this.__wbg_ptr,e,t,n,o,_,i,l,f,a)>>>0}players_data(){let e,t;try{const n=s.gamecore_players_data(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}position_of(e){const t=s.gamecore_position_of(this.__wbg_ptr,e);var n=j(t[0],t[1]).slice();return s.__wbindgen_free(t[0],t[1]*4,4),n}remove_actor(e){s.gamecore_remove_actor(this.__wbg_ptr,e)}remove_players_and_shots(){let e,t;try{const n=s.gamecore_remove_players_and_shots(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}remove_scripted_actor(e){s.gamecore_remove_scripted_actor(this.__wbg_ptr,e)}reset_actor(e,t,n,o,_){s.gamecore_reset_actor(this.__wbg_ptr,e,t,n,o,_)}reset_all_vitals(){s.gamecore_reset_all_vitals(this.__wbg_ptr)}serialize_state(){const e=s.gamecore_serialize_state(this.__wbg_ptr);if(e[3])throw y(e[2]);var t=H(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}spawn_actor(e,t,n,o,_,i){const c=h(t,s.__wbindgen_malloc,s.__wbindgen_realloc),a=p,l=s.gamecore_spawn_actor(this.__wbg_ptr,e,c,a,n,o,_,i);if(l[1])throw y(l[0])}spawn_scripted_actor(e,t,n,o,_,i){const c=h(t,s.__wbindgen_malloc,s.__wbindgen_realloc),a=p,l=s.gamecore_spawn_scripted_actor(this.__wbg_ptr,e,c,a,n,o,_,i);if(l[1])throw y(l[0])}step(e){s.gamecore_step(this.__wbg_ptr,e)}take_events(){let e,t;try{const n=s.gamecore_take_events(this.__wbg_ptr);return e=n[0],t=n[1],g(n[0],n[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(N.prototype[Symbol.dispose]=N.prototype.free);function X(){return{__proto__:null,"./vimp_snakes_core_bg.js":{__proto__:null,__wbg_Error_408e67f47ca7b58b:function(e,t){return Error(g(e,t))},__wbg___wbindgen_throw_bb96b2010945f0bc:function(e,t){throw new Error(g(e,t))},__wbindgen_init_externref_table:function(){const e=s.__wbindgen_externrefs,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)}}}}const q=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(r=>s.__wbg_clientcore_free(r,1)),K=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(r=>s.__wbg_gamecore_free(r,1));function j(r,e){return r=r>>>0,Se().subarray(r/4,r/4+e)}function H(r,e){return r=r>>>0,x().subarray(r/1,r/1+e)}let k=null;function Se(){return(k===null||k.byteLength===0)&&(k=new Float32Array(s.memory.buffer)),k}function g(r,e){return Ce(r>>>0,e)}let E=null;function x(){return(E===null||E.byteLength===0)&&(E=new Uint8Array(s.memory.buffer)),E}function ke(r){return r==null}function z(r,e){const t=e(r.length*1,1)>>>0;return x().set(r,t/1),p=r.length,t}function h(r,e,t){if(t===void 0){const c=C.encode(r),a=e(c.length,1)>>>0;return x().subarray(a,a+c.length).set(c),p=c.length,a}let n=r.length,o=e(n,1)>>>0;const _=x();let i=0;for(;i<n;i++){const c=r.charCodeAt(i);if(c>127)break;_[o+i]=c}if(i!==n){i!==0&&(r=r.slice(i)),o=t(o,n,n=i+r.length*3,1)>>>0;const c=x().subarray(o+i,o+n),a=C.encodeInto(r,c);i+=a.written,o=t(o,n,i,1)>>>0}return p=i,o}function y(r){const e=s.__wbindgen_externrefs.get(r);return s.__externref_table_dealloc(r),e}let L=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});L.decode();const Ee=2146435072;let P=0;function Ce(r,e){return P+=e,P>=Ee&&(L=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),L.decode(),P=e),L.decode(x().subarray(r,r+e))}const C=new TextEncoder;"encodeInto"in C||(C.encodeInto=function(r,e){const t=C.encode(r);return e.set(t),{read:r.length,written:t.length}});let p=0,s;function J(r,e){return s=r.exports,k=null,E=null,s.__wbindgen_start(),s}async function Oe(r,e){if(typeof Response=="function"&&r instanceof Response){if(!r.ok)throw new Error(`failed to fetch Wasm: ${r.status} ${r.statusText} fetching '${r.url}'`);if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(r,e)}catch(o){if(t(r.type)&&r.headers.get("Content-Type")!=="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",o);else throw o}const n=await r.arrayBuffer();return await WebAssembly.instantiate(n,e)}else{const n=await WebAssembly.instantiate(r,e);return n instanceof WebAssembly.Instance?{instance:n,module:r}:n}function t(n){switch(n){case"basic":case"cors":case"default":return!0}return!1}}function Re(r){if(s!==void 0)return s;r!==void 0&&(Object.getPrototypeOf(r)===Object.prototype?{module:r}=r:console.warn("using deprecated parameters for `initSync()`; pass a single object instead"));const e=X();r instanceof WebAssembly.Module||(r=new WebAssembly.Module(r));const t=new WebAssembly.Instance(r,e);return J(t)}async function Le(r){if(s!==void 0)return s;r!==void 0&&(Object.getPrototypeOf(r)===Object.prototype?{module_or_path:r}=r:console.warn("using deprecated parameters for the initialization function; pass a single object instead")),r===void 0&&(r=new URL("/assets/vimp_snakes_core_bg-BqmtH1zs.wasm",import.meta.url));const e=X();(typeof r=="string"||typeof Request=="function"&&r instanceof Request||typeof URL=="function"&&r instanceof URL)&&(r=fetch(r));const{instance:t,module:n}=await Oe(await r,e);return J(t)}const Te=Object.freeze(Object.defineProperty({__proto__:null,ClientCore:W,GameCore:N,default:Le,initSync:Re},Symbol.toStringTag,{value:"Module"}));export{Me as default};
@@ -1 +0,0 @@
1
- const G=Array.from({length:16},(n,e)=>[{name:`p${e}x`,ty:"f32",interp:"lerp"},{name:`p${e}y`,ty:"f32",interp:"lerp"}]).flat(),Y={s1:{id:1,kind:"indexed8",class:"hot",fields:[...G,{name:"angle",ty:"f32",interp:"lerpAngle"},{name:"radius",ty:"f32",interp:"lerp"},{name:"crystals",ty:"u16"},{name:"color",ty:"u8"},{name:"boost",ty:"u8"}]},cr:{id:2,kind:"indexed32",class:"event",fields:[{name:"x",ty:"f32"},{name:"y",ty:"f32"},{name:"tier",ty:"u8"},{name:"color",ty:"u8"}]},c1:{id:3,kind:"indexedNoNull8",class:"hot",fields:[{name:"x",ty:"f32",interp:"lerp"},{name:"y",ty:"f32",interp:"lerp"},{name:"angle",ty:"f32",interp:"lerpAngle"}]}},$=[{value:1,radius:8},{value:3,radius:13},{value:8,radius:20}],C={s1:{baseSpeed:260,boostFactor:1.9,turnSpeed:3.4,baseRadius:14,radiusGain:1.6,baseLength:150,lengthPerCrystal:9,pointSpacing:6,boostDrainPerSecond:6,boostMinCrystals:2,world:{maxCrystals:60,spawnInterval:.35,tierWeights:[70,25,5],tiers:$,dropRatio:.8,edgeMargin:60,startCrystals:0}}},q={},M=20,j=128,P=1,H=M*j/2,R=H,x=16,N=640,K=Array.from({length:x},(n,e)=>{const t=e/x*Math.PI*2,r=R+Math.cos(t)*N,a=R+Math.sin(t)*N;return[Math.round(r),Math.round(a),Math.round((t*180/Math.PI+180)%360)]}),X={setId:"c1",scale:1,step:j,physicsStatic:[],physicsDynamic:[],layers:{1:[P]},map:Array.from({length:M},()=>Array.from({length:M},()=>P)),respawns:{players:K}},Z={arena:X},Q={title:"Vimp Snakes",parts:{models:C,weapons:q,friendlyFire:!1},snapshot:Y,soundCues:{roundStart:null,victory:null,defeat:null,frag:null,death:null},initialVote:"teamChange",maps:Z,currentMap:"arena",mapScale:1,mapSetId:"c1",mapsInVote:1,timers:{timeStep:1e3/120,networkSendRate:4,roundTime:36e5,mapTime:36e5,roomTimeMin:1e4,roomTimeMax:36e5,voteTime:1e4,timeBlockedVote:3e4,teamChangeGracePeriod:1e4,roundRestartDelay:5e3,mapChangeDelay:2e3,rttPingInterval:3e3,idleCheckInterval:3e4},playerState:{defaultState:{best:0,eaten:0}},spectatorTeam:"spectators",teams:{players:1,spectators:2},scripted:{namePrefix:"Snake",defaultModel:"s1"},playerKeys:{left:{key:1},right:{key:2},boost:{key:4},respawn:{key:8,type:1}},panel:{fields:{crystals:{key:"c",value:0},length:{key:"l",value:0},dead:{key:"d",value:0}},activeKey:"wa"},stat:{name:{key:0,bodyMethod:"=",headSync:!0,headMethod:"#"},status:{key:1,bodyMethod:"=",bodyValue:"",headValue:""},score:{key:2,bodyMethod:"=",bodyValue:0,headMethod:"+",headValue:0},deaths:{key:3,bodyMethod:"=",bodyValue:0,headMethod:"+",headValue:0},latency:{key:4,bodyMethod:"="}},roomDefaults:{maxPlayers:8},roomForm:[{name:"maxPlayers",control:"text",label:"Max players",numeric:!0},{name:"map",control:"select",label:"Map",source:"maps"}]},J={elems:{authId:"auth",fieldsId:"auth-fields",errorId:"auth-error",enterId:"auth-enter",titleId:"auth-title",informsId:"auth-informs"},texts:{title:"Vimp Snakes",sections:[{heading:"How to play",lines:[{keys:"A, D",text:"turn"},{keys:"W",text:"boost (burns crystals)"},{keys:"R",text:"respawn after a crash"},{separator:!0},{keys:"",text:"eat crystals to grow"},{keys:"",text:"the edge and other snakes kill you"},{keys:"",text:"your own tail does not"},{separator:!0},{keys:"C",text:"chat"},{keys:"M",text:"vote"},{keys:"Tab",text:"stats",last:!0}]}]},params:[{name:"model",value:"s1",options:{control:"select",label:"Snake",options:Object.keys(C),validator:"isValidModel",storage:"model"}}],validators:{isValidModel:n=>n in C}},ee={pickup:{file:"shot",priority:80,volume:.35},death:{file:"death",priority:150,volume:.5}},te={codecList:["webm","mp3"],sounds:ee},re={parts:{gameSets:{s1:["Snake"],cr:["Crystal"],c1:["Arena"]},entitiesOnCanvas:{Arena:"vimp",Snake:"vimp",Crystal:"vimp"},bakedAssets:{vimp:[{name:"crystalGem",component:"Crystal",params:{radius:32,facets:6}}]},componentDependencies:{soundManager:["Snake"]},sounds:te},initIdList:["vimp","panel","chat"],modules:{canvasManager:{canvases:{vimp:{width:960,height:600,aspectRatio:"16:10",baseScale:"1:1",dynamicCamera:!0,shakeCamera:!1}}},controls:{keySetList:[{78:"nextPlayer",80:"prevPlayer"},{65:"left",68:"right",87:"boost",82:"respawn"}]},chat:{params:{messages:{s:["Team {0} is full. Your current team: {1}","Your team: {0}","Your new team: {0}","Your new status: spectator","{0} crashed into {1}","{0} joined the game","{0} left the game"],v:["A vote has been created","Voting has started","Your vote has been accepted","Voting is temporarily unavailable","Vote passed","Vote failed"],m:["Current map: {0}","Next map: {0}"],c:["Command not found","Your rank: {0}"],n:["Invalid name","{0} changed name to {1}"],g:["{0} bot snake(s) spawned"]}}},panel:{keys:{c:"crystals",l:"length",d:"dead",wa:"mode",t:"time"},fields:[{name:"crystals",elem:"panel-crystals",type:"value"},{name:"length",elem:"panel-length",type:"value"},{name:"mode",elem:"panel-mode",type:"weapon"},{name:"time",elem:"panel-time",type:"time"},{name:"dead",elem:"panel-dead",type:"value"}]},stat:{params:{columns:["snake","status","crystals","crashes","ping"],heads:{1:"players"},bodies:{1:"players",2:"spectators"},sortList:{players:[[2,!0],[3,!1]]}}},vote:{params:{templates:{teamChange:["Play or watch?","teams",!0],mapChangeBySystem:["Choose the next map"],mapChangeByUser:["{0} suggested the map: {1}",["Yes","No"]]},menu:[["teamChange",["Play / watch","teams"]],["mapChange",["Suggest map","maps"]]]}}},gameInform:{list:["{0} WINS!","SLITHER!","GAME OVER!"]}};class ne{constructor({participants:e,coreAdapter:t,panel:r,stat:a,scripted:c}){this._participants=e,this._coreAdapter=t,this._panel=r,this._stat=a,this._model=c.defaultModel,this._respawns=null}createMap(e){this._respawns=e.respawns}getCountsPerTeam(){const e={};for(const t of this._participants.getScripted())e[t.team]=(e[t.team]??0)+1;return e}createScripted(e,t=null){if(!this._respawns)return 0;const r=this._participants.getPlayableTeams();let a=0;for(let c=0;c<e&&!this._participants.isFull;c+=1){const o=t??this._pickTeam(r);if(!o||!this._hasRoom(o))break;const i=this._participants.createScripted({team:o,model:this._model}),l=this._participants.get(i);this._stat.addUser(i,l.teamId,{name:l.name,status:"",latency:"BOT"}),this._panel.addUser(i),a+=1}return a}_hasRoom(e){const t=this._respawns[e];return!!(t&&this._participants.getTeamSize(e)<t.length)}_pickTeam(e){return[...e].sort((t,r)=>this._participants.getTeamSize(t)-this._participants.getTeamSize(r)).find(t=>this._hasRoom(t))??null}removeScripted(e=null){const t=this._participants.getScripted();(e?t.filter(a=>a.team===e):[...t]).forEach(a=>this._remove(a.gameId))}removeOneForHuman(e){for(const t of this._participants.getScripted())if(t.team===e)return this._remove(t.gameId),!0;return!1}_remove(e){const t=this._participants.get(e);!t||!t.isScripted||(this._stat.removeUser(e,t.teamId),this._panel.removeUser(e),this._coreAdapter.removePlayer(e),this._participants.remove(e))}}class se{constructor({participants:e,stat:t}){this._participants=e,this._stat=t}onCoreEvent(e,t){if(!e||typeof e!="object")return;const r=Number(e.id);switch(e.type){case"crystals":this._write(r,{score:Number(e.total)||0});break;case"death":this._write(r,{score:0,deaths:Number(e.crashes)||0}),this._recordBest(r,Number(e.crystals)||0,t);break;case"respawn":this._write(r,{score:0});break}}_write(e,t){const r=this._participants.get(e);r&&this._stat.updateUser(e,r.teamId,t)}_recordBest(e,t,r){if(!r?.getPlayerState)return;const a=r.getPlayerState(e);if(!a)return;const c=Math.max(Number(a.best)||0,t),o=(Number(a.eaten)||0)+t;r.setPlayerState(e,{...a,best:c,eaten:o})}}let B=null;function ae(n){return B=new se(n),{scripted:new ne(n)}}function oe(){return B}const ie={name:"/spawn",handler(n,e,t){const{maxPlayers:r}=n.participants,a=Math.max(1,Math.trunc(Number(t[0]))||1),c=r>0?Math.min(a,r):a,o=n.scripted.createScripted(c);n.chat.pushSystem("BOTS_SPAWNED",[o]),o>0&&n.roundManager.initiateNewRound()}},ce={BOTS_SPAWNED:"g:0"},_e="modulepreload",le=function(n){return"/"+n},W={},de=function(e,t,r){let a=Promise.resolve();if(t&&t.length>0){let l=function(d){return Promise.all(d.map(f=>Promise.resolve(f).then(h=>({status:"fulfilled",value:h}),h=>({status:"rejected",reason:h}))))};document.getElementsByTagName("link");const o=document.querySelector("meta[property=csp-nonce]"),i=o?.nonce||o?.getAttribute("nonce");a=l(t.map(d=>{if(d=le(d),d in W)return;W[d]=!0;const f=d.endsWith(".css"),h=f?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${d}"]${h}`))return;const g=document.createElement("link");if(g.rel=f?"stylesheet":_e,f||(g.as="script"),g.crossOrigin="",g.href=d,i&&g.setAttribute("nonce",i),document.head.appendChild(g),f)return new Promise((D,U)=>{g.addEventListener("load",D),g.addEventListener("error",()=>U(new Error(`Unable to preload CSS for ${d}`)))})}))}function c(o){const i=new Event("vite:preloadError",{cancelable:!0});if(i.payload=o,window.dispatchEvent(i),!i.defaultPrevented)throw o}return a.then(o=>{for(const i of o||[])i.status==="rejected"&&c(i.reason);return e().catch(c)})},pe=n=>(n??"").endsWith(".js"),me=n=>import(n),ue=()=>de(()=>Promise.resolve().then(()=>Se),[]),ke={id:"snakes",engineApi:3,async createCore(n,{wasmUrl:e}={}){if(pe(e)){const a=await me(e);return new a.GameCore(n)}const{default:t,GameCore:r}=await ue();return await t({module_or_path:e}),new r(n)},gameConfig:Q,authSchema:J,chatCommands:[ie],systemMessages:ce,createModules:ae,buildClientGameConfig:()=>re,onCoreEvent(n,{vimp:e}={}){oe()?.onCoreEvent(n,e)}};class A{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,O.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_clientcore_free(e,0)}apply_input(e,t,r){const a=p(e,s.__wbindgen_malloc,s.__wbindgen_realloc),c=_,o=p(t,s.__wbindgen_malloc,s.__wbindgen_realloc),i=_;s.clientcore_apply_input(this.__wbg_ptr,a,c,o,i,r)}debug_json(){let e,t;try{const r=s.clientcore_debug_json(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}decode_frame(e){let t,r;try{const a=I(e,s.__wbindgen_malloc),c=_,o=s.clientcore_decode_frame(this.__wbg_ptr,a,c);return t=o[0],r=o[1],m(o[0],o[1])}finally{s.__wbindgen_free(t,r,1)}}hot_ptr(){return s.clientcore_hot_ptr(this.__wbg_ptr)>>>0}hot_values(){const e=s.clientcore_hot_values(this.__wbg_ptr);var t=T(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}my_game_id(){return s.clientcore_my_game_id(this.__wbg_ptr)}constructor(e){const t=p(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=_,a=s.clientcore_new(t,r);if(a[2])throw u(a[1]);return this.__wbg_ptr=a[0],O.register(this,this.__wbg_ptr,this),this}offset(){return s.clientcore_offset(this.__wbg_ptr)}push_frame(e,t){const r=I(e,s.__wbindgen_malloc),a=_;return s.clientcore_push_frame(this.__wbg_ptr,r,a,t)!==0}reset(){s.clientcore_reset(this.__wbg_ptr)}resync(){s.clientcore_resync(this.__wbg_ptr)}sample(e){return s.clientcore_sample(this.__wbg_ptr,e)>>>0}set_active(e){s.clientcore_set_active(this.__wbg_ptr,e)}set_map(e){const t=p(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=_,a=s.clientcore_set_map(this.__wbg_ptr,t,r);if(a[1])throw u(a[0])}set_model(e){const t=p(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=_;s.clientcore_set_model(this.__wbg_ptr,t,r)}sync_panel(e){const t=p(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=_;s.clientcore_sync_panel(this.__wbg_ptr,t,r)}take_divergence(){let e,t;try{const r=s.clientcore_take_divergence(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}take_frames(){let e,t;try{const r=s.clientcore_take_frames(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(A.prototype[Symbol.dispose]=A.prototype.free);class E{__destroy_into_raw(){const e=this.__wbg_ptr;return this.__wbg_ptr=0,F.unregister(this),e}free(){const e=this.__destroy_into_raw();s.__wbg_gamecore_free(e,0)}alive_players(){const e=s.gamecore_alive_players(this.__wbg_ptr);var t=T(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*4,4),t}apply_input(e,t,r,a){const c=p(r,s.__wbindgen_malloc,s.__wbindgen_realloc),o=_,i=p(a,s.__wbindgen_malloc,s.__wbindgen_realloc),l=_;s.gamecore_apply_input(this.__wbg_ptr,e,t,c,o,i,l)}body_has_events(){return s.gamecore_body_has_events(this.__wbg_ptr)!==0}clear(){s.gamecore_clear(this.__wbg_ptr)}debug_json(){let e,t;try{const r=s.gamecore_debug_json(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}deserialize_state(e){const t=I(e,s.__wbindgen_malloc),r=_,a=s.gamecore_deserialize_state(this.__wbg_ptr,t,r);if(a[1])throw u(a[0])}frame_bytes(){const e=s.gamecore_frame_bytes(this.__wbg_ptr);var t=V(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}frame_ptr(){return s.gamecore_frame_ptr(this.__wbg_ptr)>>>0}is_alive(e){return s.gamecore_is_alive(this.__wbg_ptr,e)!==0}last_input_seq(e){return s.gamecore_last_input_seq(this.__wbg_ptr,e)>>>0}load_map(e){const t=p(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=_,a=s.gamecore_load_map(this.__wbg_ptr,t,r);if(a[1])throw u(a[0])}map_info(){let e,t;try{const r=s.gamecore_map_info(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}constructor(e){const t=p(e,s.__wbindgen_malloc,s.__wbindgen_realloc),r=_,a=s.gamecore_new(t,r);if(a[2])throw u(a[1]);return this.__wbg_ptr=a[0],F.register(this,this.__wbg_ptr,this),this}pack_body(){const e=s.gamecore_pack_body(this.__wbg_ptr);if(e[1])throw u(e[0])}pack_frame(e,t,r,a,c,o,i,l){var d=ge(i)?0:p(i,s.__wbindgen_malloc,s.__wbindgen_realloc),f=_;return s.gamecore_pack_frame(this.__wbg_ptr,e,t,r,a,c,o,d,f,l)>>>0}players_data(){let e,t;try{const r=s.gamecore_players_data(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}position_of(e){const t=s.gamecore_position_of(this.__wbg_ptr,e);var r=T(t[0],t[1]).slice();return s.__wbindgen_free(t[0],t[1]*4,4),r}remove_actor(e){s.gamecore_remove_actor(this.__wbg_ptr,e)}remove_players_and_shots(){let e,t;try{const r=s.gamecore_remove_players_and_shots(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}remove_scripted_actor(e){s.gamecore_remove_scripted_actor(this.__wbg_ptr,e)}reset_actor(e,t,r,a,c){s.gamecore_reset_actor(this.__wbg_ptr,e,t,r,a,c)}reset_all_vitals(){s.gamecore_reset_all_vitals(this.__wbg_ptr)}serialize_state(){const e=s.gamecore_serialize_state(this.__wbg_ptr);if(e[3])throw u(e[2]);var t=V(e[0],e[1]).slice();return s.__wbindgen_free(e[0],e[1]*1,1),t}spawn_actor(e,t,r,a,c,o){const i=p(t,s.__wbindgen_malloc,s.__wbindgen_realloc),l=_,d=s.gamecore_spawn_actor(this.__wbg_ptr,e,i,l,r,a,c,o);if(d[1])throw u(d[0])}spawn_scripted_actor(e,t,r,a,c,o){const i=p(t,s.__wbindgen_malloc,s.__wbindgen_realloc),l=_,d=s.gamecore_spawn_scripted_actor(this.__wbg_ptr,e,i,l,r,a,c,o);if(d[1])throw u(d[0])}step(e){s.gamecore_step(this.__wbg_ptr,e)}take_events(){let e,t;try{const r=s.gamecore_take_events(this.__wbg_ptr);return e=r[0],t=r[1],m(r[0],r[1])}finally{s.__wbindgen_free(e,t,1)}}}Symbol.dispose&&(E.prototype[Symbol.dispose]=E.prototype.free);function L(){return{__proto__:null,"./vimp_snakes_core_bg.js":{__proto__:null,__wbg_Error_408e67f47ca7b58b:function(e,t){return Error(m(e,t))},__wbg___wbindgen_throw_bb96b2010945f0bc:function(e,t){throw new Error(m(e,t))},__wbindgen_init_externref_table:function(){const e=s.__wbindgen_externrefs,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,!0),e.set(t+3,!1)}}}}const O=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_clientcore_free(n,1)),F=typeof FinalizationRegistry>"u"?{register:()=>{},unregister:()=>{}}:new FinalizationRegistry(n=>s.__wbg_gamecore_free(n,1));function T(n,e){return n=n>>>0,fe().subarray(n/4,n/4+e)}function V(n,e){return n=n>>>0,y().subarray(n/1,n/1+e)}let b=null;function fe(){return(b===null||b.byteLength===0)&&(b=new Float32Array(s.memory.buffer)),b}function m(n,e){return ye(n>>>0,e)}let w=null;function y(){return(w===null||w.byteLength===0)&&(w=new Uint8Array(s.memory.buffer)),w}function ge(n){return n==null}function I(n,e){const t=e(n.length*1,1)>>>0;return y().set(n,t/1),_=n.length,t}function p(n,e,t){if(t===void 0){const i=v.encode(n),l=e(i.length,1)>>>0;return y().subarray(l,l+i.length).set(i),_=i.length,l}let r=n.length,a=e(r,1)>>>0;const c=y();let o=0;for(;o<r;o++){const i=n.charCodeAt(o);if(i>127)break;c[a+o]=i}if(o!==r){o!==0&&(n=n.slice(o)),a=t(a,r,r=o+n.length*3,1)>>>0;const i=y().subarray(a+o,a+r),l=v.encodeInto(n,i);o+=l.written,a=t(a,r,o,1)>>>0}return _=o,a}function u(n){const e=s.__wbindgen_externrefs.get(n);return s.__externref_table_dealloc(n),e}let S=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0});S.decode();const he=2146435072;let k=0;function ye(n,e){return k+=e,k>=he&&(S=new TextDecoder("utf-8",{ignoreBOM:!0,fatal:!0}),S.decode(),k=e),S.decode(y().subarray(n,n+e))}const v=new TextEncoder;"encodeInto"in v||(v.encodeInto=function(n,e){const t=v.encode(n);return e.set(t),{read:n.length,written:t.length}});let _=0,s;function z(n,e){return s=n.exports,b=null,w=null,s.__wbindgen_start(),s}async function be(n,e){if(typeof Response=="function"&&n instanceof Response){if(!n.ok)throw new Error(`failed to fetch Wasm: ${n.status} ${n.statusText} fetching '${n.url}'`);if(typeof WebAssembly.instantiateStreaming=="function")try{return await WebAssembly.instantiateStreaming(n,e)}catch(a){if(t(n.type)&&n.headers.get("Content-Type")!=="application/wasm")console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",a);else throw a}const r=await n.arrayBuffer();return await WebAssembly.instantiate(r,e)}else{const r=await WebAssembly.instantiate(n,e);return r instanceof WebAssembly.Instance?{instance:r,module:n}:r}function t(r){switch(r){case"basic":case"cors":case"default":return!0}return!1}}function we(n){if(s!==void 0)return s;n!==void 0&&(Object.getPrototypeOf(n)===Object.prototype?{module:n}=n:console.warn("using deprecated parameters for `initSync()`; pass a single object instead"));const e=L();n instanceof WebAssembly.Module||(n=new WebAssembly.Module(n));const t=new WebAssembly.Instance(n,e);return z(t)}async function ve(n){if(s!==void 0)return s;n!==void 0&&(Object.getPrototypeOf(n)===Object.prototype?{module_or_path:n}=n:console.warn("using deprecated parameters for the initialization function; pass a single object instead")),n===void 0&&(n=new URL("/assets/vimp_snakes_core_bg-BqmtH1zs.wasm",import.meta.url));const e=L();(typeof n=="string"||typeof Request=="function"&&n instanceof Request||typeof URL=="function"&&n instanceof URL)&&(n=fetch(n));const{instance:t,module:r}=await be(await n,e);return z(t)}const Se=Object.freeze(Object.defineProperty({__proto__:null,ClientCore:A,GameCore:E,default:ve,initSync:we},Symbol.toStringTag,{value:"Module"}));export{ke as default};