diff --git a/assets/_master.py b/assets/_master.py index a582e32..fb27b86 100644 --- a/assets/_master.py +++ b/assets/_master.py @@ -68,21 +68,89 @@ STRIDE_NOTE = ("In particular frames 2 and 3 must stay what they are in the refe "SHOWN TWICE with only the legs different — everything above the hips identical " "between them, the legs clearly swapped, and the two frames NOT identical copies. ") + +# ---- THE WOUND LADDER. A match opponent is scored ON ITS BODY: every point won takes a stage off +# it, so a variant that only exists whole would jump from untouched to dead with nothing between. +# Each stage is a restyle of THE STAGE ABOVE IT — that chaining is what finally made damage +# accumulate, because the missing arm then rides in the PICTURE instead of having to win an argument +# with it. The wound rows have no separate stance frame: the shuffle doubles as one, which is how +# drawZombie already reads them. +WOUND = ( + "The reference image is a {n}-FRAME SPRITE SHEET of one zombie tennis player. REDRAW THE WHOLE " + "SHEET with ONE THING CHANGED: {change} " + "EVERYTHING ELSE STAYS EXACTLY AS IT IS IN THE REFERENCE — the same character, the same clothing " + "and its exact shades, the same skin, the same racket, the same {n} poses in the same order, the " + "same leg positions, the same facing, the same build. There is NO TENNIS BALL in any frame. " + "Output exactly {n} figures side by side in ONE horizontal row, all on the same ground line. ") +ARM_OFF = ( + "the arm WITHOUT the racket is COMPLETELY GONE — torn off at the shoulder, leaving a ragged " + "stump gushing blood down that side and the clothing slashed open across the ribs. COUNT THE " + "ARMS: there must be EXACTLY ONE ARM in every single frame, the one holding the racket. Nothing " + "hangs from the other shoulder. A frame with two arms in it is wrong — this is the whole change " + "being asked for, so do not copy the reference's second arm across. Where the reference uses " + "that arm, the stump is thrown out in its place instead. ") +HEADLESS = ( + "the HEAD IS COMPLETELY GONE — no head, no face, no hair anywhere above the shoulders, only a " + "ragged gushing neck stump, so every figure is A CLEAR HEAD SHORTER than in the reference. It is " + "ALREADY missing one arm in the reference and it STAYS missing — it still has only one arm. ") +# ...and because the missing arm IS the tossing arm, a wounded server has to toss the way one-armed +# players really do. The wound changing how it plays is the point, not a detail. +ONE_ARM_SERVE = ( + "Because it has only one arm it can no longer throw the ball up with a free hand: in the TOSS " + "frame the stump is flung upward instead and the racket hand flicks up in front of the face, as " + "a one-armed player tosses off the racket face. ") + def variant(key, who): bpath = os.path.join(HERE, '_regen_batches.json') B = json.load(open(bpath, encoding='utf-8'), object_pairs_hook=collections.OrderedDict) + seed = lambda src, dst: shutil.copy(os.path.join(HERE, src + '.png'), os.path.join(HERE, dst + '.png')) + made = [] + + def batch(name, sprites, refs, subject, stride=None, changed=None, newart=False): + for src, dst in zip(refs, sprites): # placeholders: the density bar needs a baseline to beat + seed(src, dst) + spec = dict(sprites=sprites, ref_only=list(refs), check='loose', anchor=0, subject=subject) + spec['newart' if newart else 'state'] = True + if stride: spec['stride'] = stride + if changed: + spec['changed'] = changed + # pose-varied sheets move redness and shorts BRIGHTNESS on their own (a body face down + # shows a fraction of the shirt it shows standing); the shorts HUE is the measure that + # survives a pose change, so it stays the tight one. See the note in _regen.kit. + spec['palette_ref'] = refs[0] + spec['palette_tol'] = [26, 14, 99, 5] + B[name] = spec + made.append(name) + print(f'{name:16s} <- {refs[0]}… {", ".join(sprites)}') + + K = 'z_' + key + # --- the whole body, restyled off the master for part, frames in SHEETS.items(): - names = ['z_' + key + f[3:] for f in frames] # z_r1 -> z_br1, z_rtoss -> z_brtoss - for src, dst in zip(frames, names): # placeholders: the density bar needs a baseline - shutil.copy(os.path.join(HERE, src + '.png'), os.path.join(HERE, dst + '.png')) - spec = dict(sprites=names, ref_only=frames, newart=True, check='loose', anchor=0, - subject=RESTYLE.format(n=len(frames), who=who) - + (STRIDE_NOTE if part == 'play' else '')) - if part == 'play': spec['stride'] = [[1, 2]] # the gate covers it from the first roll - B[f'{key}_{part}'] = spec - print(f'{key}_{part:6s} -> {", ".join(names)}') + batch(f'{key}_{part}', [K + f[3:] for f in frames], frames, + RESTYLE.format(n=len(frames), who=who) + (STRIDE_NOTE if part == 'play' else ''), + stride=[[1, 2]] if part == 'play' else None, newart=True) + + # --- the two wound stages, each chained off the one above it + WALK = ['2', '3', '4'] # shuffle A, shuffle B, forehand — the wound rows' three frames + SERVE = ['bounce', 'toss', 'serve'] + FALL = ['fall1', 'fall2', 'fall3', 'rise'] + for st, change, extra, moved in ((1, ARM_OFF, ONE_ARM_SERVE, 0.10), (2, HEADLESS, '', 0.15)): + up = K + (f'w{st - 1}' if st > 1 else '') # the stage this one is drawn from + walk_src = [up + s for s in (['', 'b', 's'] if st > 1 else WALK)] + batch(f'{key}_w{st}', [f'{K}w{st}', f'{K}w{st}b', f'{K}w{st}s'], walk_src, + WOUND.format(n=3, change=change) + + 'In particular frames 1 and 2 are ONE DRAWING SHOWN TWICE with only the legs ' + 'different — identical above the hips, legs clearly swapped, and NOT identical copies. ', + stride=[[0, 1]], changed=moved) + batch(f'{key}_serve_w{st}', [f'{K}w{st}' + s for s in SERVE], [up + s for s in SERVE], + WOUND.format(n=3, change=change) + extra, changed=moved) + batch(f'{key}_fall_w{st}', [f'{K}w{st}' + s for s in FALL], [up + s for s in FALL], + WOUND.format(n=4, change=change), changed=moved) + json.dump(B, open(bpath, 'w', encoding='utf-8'), indent=1, ensure_ascii=False) - print(f'\nnow run: python3 assets/_regen.py {key}_play {key}_serve {key}_fall --tries=4') + print(f'\n{len(made)} batches, 31 frames. Run them IN THIS ORDER — each wound stage is drawn ' + f'from the stage above it, so an earlier one must be installed first:\n\n' + f' python3 assets/_regen.py {" ".join(made)} --tries=4\n') if __name__ == '__main__': if '--variant' in sys.argv: