Perbaiki ukuran Solo Man, skala pose otomatis, server lokal tanpa cache
This commit is contained in:
parent
459fef9f36
commit
dc3bdbe57d
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 930 KiB After Width: | Height: | Size: 933 KiB |
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 264 KiB After Width: | Height: | Size: 802 KiB |
10
files.json
10
files.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"files": 216,
|
||||
"bytes": 30355057,
|
||||
"bytes": 30910020,
|
||||
"list": [
|
||||
{
|
||||
"file": "index.html",
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
},
|
||||
{
|
||||
"file": "game.js",
|
||||
"bytes": 223237
|
||||
"bytes": 224243
|
||||
},
|
||||
{
|
||||
"file": "menu.js",
|
||||
|
|
@ -444,7 +444,7 @@
|
|||
},
|
||||
{
|
||||
"file": "assets/gufron/run/sprite-sheet-alpha.webp",
|
||||
"bytes": 952108
|
||||
"bytes": 955308
|
||||
},
|
||||
{
|
||||
"file": "assets/gufron/ui/cutin-mode.webp",
|
||||
|
|
@ -664,11 +664,11 @@
|
|||
},
|
||||
{
|
||||
"file": "assets/soloman/manifest.js",
|
||||
"bytes": 13766
|
||||
"bytes": 13857
|
||||
},
|
||||
{
|
||||
"file": "assets/soloman/run/sprite-sheet-alpha.webp",
|
||||
"bytes": 270610
|
||||
"bytes": 821276
|
||||
},
|
||||
{
|
||||
"file": "assets/soloman/ui/cutin-mode.webp",
|
||||
|
|
|
|||
18
game.js
18
game.js
|
|
@ -308,6 +308,20 @@
|
|||
// Down-down+Space is an uppercut launcher, down-forward+Space a lunging punch. They reuse the fighter's own attack poses.
|
||||
const inputLog = [];
|
||||
const STATE_SCALE = { jump: 1.14, crouch: 1.1 };
|
||||
// Every pose is drawn so its height matches the fighter's idle height as closely as the pose allows (walk, run, jump and
|
||||
// crouch were generated smaller than idle); computed once from the measured atlas bounds.
|
||||
const stateKCache = new WeakMap();
|
||||
function stateK(mt, st) {
|
||||
if (!['walk', 'run', 'crouch', 'jump'].includes(st)) return 1;
|
||||
if (!mt?.states) return STATE_SCALE[st] || 1;
|
||||
let c = stateKCache.get(mt); if (!c) stateKCache.set(mt, c = {});
|
||||
if (c[st] === undefined) {
|
||||
const h = n => { const f = mt.states[n]?.frames; return f?.length ? f.reduce((a, x) => a + (x.bounds.bottom - x.bounds.top), 0) / f.length : 0; };
|
||||
const idle = h('idle'), cur = h(st);
|
||||
c[st] = idle && cur ? Math.min(st === 'run' ? 1.2 : 1.25, Math.max(1, idle / cur * ({ walk: .97, run: .97, crouch: .85, jump: .95 })[st])) : 1;
|
||||
}
|
||||
return c[st];
|
||||
}
|
||||
function logInput(key) { if (!'sad'.includes(key)) return; inputLog.push({ k: key, t: time }); if (inputLog.length > 6) inputLog.shift(); }
|
||||
function commandFor() {
|
||||
const fwd = hero.facing > 0 ? 'd' : 'a', n = inputLog.length, last = inputLog[n - 1], prev = inputLog[n - 2];
|
||||
|
|
@ -1962,7 +1976,7 @@
|
|||
const pose = heroPose(), index = pose.frame;
|
||||
const r = list[index], cw = manifest.cell?.width || manifest.cell?.w || r.w, ch = manifest.cell?.height || manifest.cell?.h || r.h;
|
||||
const anchorX = manifest.cell?.anchor_x ?? cw / 2, anchorY = manifest.cell?.anchor_y ?? ch - (manifest.cell?.safe_margin_y || 0);
|
||||
const spriteScale = pose.scale * (STATE_SCALE[hero.state] || 1), skin = window.Meta?.skinFilter?.(selectedCharacter);
|
||||
const spriteScale = pose.scale * stateK(metrics, hero.state), skin = window.Meta?.skinFilter?.(selectedCharacter);
|
||||
if (hero.shield > 0) { ctx.save(); ctx.strokeStyle = '#9fe8ff'; ctx.globalAlpha = Math.min(1, hero.shield) * .8; ctx.lineWidth = 4; ctx.beginPath(); ctx.ellipse(hero.x, hero.y - 90, 70, 105, 0, 0, Math.PI * 2); ctx.stroke(); ctx.restore(); }
|
||||
ctx.save(); if (skin) ctx.filter = skin; ctx.translate(Math.round(hero.x), Math.round(hero.y)); ctx.scale(hero.facing * spriteScale, spriteScale); ctx.imageSmoothingEnabled = false;
|
||||
if (pose.pivot) { ctx.translate(pose.worldPivot.x, pose.worldPivot.y); ctx.rotate(pose.rotation); ctx.translate(-pose.pivot.x, -pose.pivot.y); }
|
||||
|
|
@ -1973,7 +1987,7 @@
|
|||
shadow(dummy.x,CONFIG.groundY,dummy.state==='down'?65:opponentCharacter==='mira'?61:opponentCharacter==='haldor'?55:43,.20);
|
||||
if(!m||!img)return;
|
||||
const r=m.frame_layout.rows[pose.state][pose.frame];
|
||||
ctx.save();ctx.translate(Math.round(dummy.x),Math.round(dummy.y));{const k=STATE_SCALE[pose.state]||1;ctx.scale((dummy.facing||-1)*k,k);}ctx.imageSmoothingEnabled=false;
|
||||
ctx.save();ctx.translate(Math.round(dummy.x),Math.round(dummy.y));{const om=CF[opponentCharacter]?(cfFormOn(opponentCharacter,'enemy')?CF[opponentCharacter].mode?.form?.metrics:CF[opponentCharacter].metrics):null,k=stateK(om,pose.state);ctx.scale((dummy.facing||-1)*k,k);}ctx.imageSmoothingEnabled=false;
|
||||
ctx.drawImage(img,r.x,r.y,r.w,r.h,-m.cell.width/2,-(m.cell.height-m.cell.safe_margin_y),r.w,r.h);ctx.restore();
|
||||
if(dummy.action && !dummy.action.fired && dummy.action.type!=='ultimate') {
|
||||
ctx.save();ctx.strokeStyle='#f0b15e';ctx.lineWidth=2;ctx.globalAlpha=.7;
|
||||
|
|
|
|||
|
|
@ -61,11 +61,13 @@ def main():
|
|||
ap.add_argument("--target", type=float, default=0.5)
|
||||
ap.add_argument("--rel", default="1")
|
||||
ap.add_argument("--key", default="ff00ff")
|
||||
ap.add_argument("--pitch", type=float, default=None, help="pakai pitch ini (pitch pose idle) untuk semua baris; pitch tiap baris kadang salah ukur")
|
||||
args = ap.parse_args()
|
||||
|
||||
key = np.array([int(args.key[i:i + 2], 16) for i in (0, 2, 4)])
|
||||
im = Image.open(args.src).convert("RGB")
|
||||
px, py = estimate_pixel_grid_runlen(im.convert("RGBA"))
|
||||
if args.pitch: px = py = args.pitch
|
||||
base = args.target / ((px + py) / 2)
|
||||
rel = [float(v) for v in args.rel.split(",")]
|
||||
rel = rel * args.poses if len(rel) == 1 else rel
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Server lokal untuk pengembangan: python3 serve.py [port]. Mengirim Cache-Control: no-store supaya browser tidak memakai berkas lama."""
|
||||
import http.server, sys
|
||||
|
||||
class Handler(http.server.SimpleHTTPRequestHandler):
|
||||
def end_headers(self):
|
||||
self.send_header('Cache-Control', 'no-store, max-age=0')
|
||||
super().end_headers()
|
||||
|
||||
port = int(sys.argv[1]) if len(sys.argv) > 1 else 8141
|
||||
http.server.ThreadingHTTPServer(('', port), Handler).serve_forever()
|
||||
Loading…
Reference in New Issue