CURVEBALL - credits, sources and exactly what differs from the original ====================================================================== THE ORIGINAL ------------ Curveball (also written "Curve Ball") is a Flash-era 3D pong game: a rectangular tunnel in perspective, your paddle at the near end, the opponent's at the far end, and the ball travelling into the screen. Moving the paddle as you strike it puts spin on the ball, and the ball bends through the air for the rest of its flight. Publisher: PARAGON DIGITAL MEDIA (pdmedia.net). That credit is not taken from a games portal. It is printed inside the game's own file: the sponsored build carries a DefineText reading "Paragon Digital Media" under the caption "brought to you by", a 100x100 "pdm" logo bitmap, and the string http://www.pdmedia.net/ in its button actions. Portal pages that attribute the game elsewhere (for instance to "Liquid Media") are contradicted by the bytecode - that name appears in neither build. No individual programmer is named anywhere in either build, and neither build contains a date, a copyright string, or an SWF Metadata tag (SWF version 5 predates it). The year of publication is therefore recorded here as UNKNOWN rather than guessed at. This project is an independent reimplementation. It is not affiliated with, endorsed by, or connected to Paragon Digital Media. No original code, artwork, font or sound from the game is redistributed here; everything in this bundle was written or drawn for it. WHERE THE NUMBERS CAME FROM --------------------------- The Internet Archive was offline for the whole of the research session, so the original was fetched from four live mirrors instead and the copies were hashed against each other: build A (unsponsored), 59,360 bytes, sha256 4c837f4960ac661a40b0b7cad4323f5410df1905693fa3ecb810f9740c325977 https://www.albinoblacksheep.com/swf/games/curveball(www.albinoblacksheep.com).swf https://www.3dponggame.com/curveball.swf (byte-identical) http://www.andyslife.org/games/curveball.swf (byte-identical) build B (sponsored), 67,765 bytes, sha256 6c83ea8277630ebf5c97d72cd901907e82759400469afa4f02d221d936ea0927 https://classic.dryang.org/flash/files/curveball.swf Both are uncompressed SWF version 5, a 350x250 stage at 30 fps, 115 frames. Every executable block was disassembled from AVM1 bytecode and the two builds diffed: the twelve gameplay blocks (both paddles' load and enterFrame handlers, the ball's load, enterFrame and mouseDown handlers, the depth ring, the world init, the per-level init and the end-of-point decision) are BYTE-IDENTICAL. Build B differs only by the sponsor splash. Build A is the one replicated here. RECOVERED FROM THE BYTECODE (not reconstructed) ----------------------------------------------- world left 25, right 326, top 25, bottom 226, depth 75, centre (175.5, 125.5) projection s(z) = (90 - degrees(atan(z / 31.066017))) / 90 ; screen = centre + (p - centre)*s - an arctangent falloff, NOT a perspective matrix. 31.066017 is 75/(1+sqrt2), and it is confirmed independently by the tunnel artwork: the nine drawn rectangles sit at exactly z = k*75/8 under this formula, to within 0.13 px. ball 30 px across (radius 15); curveDecay 1.004; a wall divides that axis' curve by ((curveDecay-1)*50+1) and mirrors that axis' speed, with no change of magnitude spin curve = -paddleSpeed / curveAmount at the player's end, +enemySpeed / curveAmount at the opponent's; velocity gains the curve every frame, before the decay serve mySpeed.z = level speed, mySpeed.x and .y stay ZERO - every sideways component in the whole game is accumulated curve. A curve smaller than 0.01 is floored to +/-0.01, signed toward the middle. paddles both 60x40; the player's eases 1/1.5 of the way to the pointer each frame; the opponent's is a first-order lag chasing the ball's CURRENT position with time constant skillFactor, and returns to the middle at 1/15 while the ball comes at you collision AVM1 hitTest, i.e. bounding boxes in screen space, using the previous frame's boxes sweet spot a 14x10 box at the paddle's centre pays the Accuracy Bonus difficulty levelSkillFactor [17,14,11,9,7,5,3.5,2.75,2,1] levelSpeed [2,2.33,2.66,3,3.33,3.66,4,4.33,4.66,6] levelCurve [25,22.5,20,17.5,15,12.5,10,10,10,10] scoring hit 100 (-10), curve 50 (-5), super curve 150 (-15), accuracy 100 (-10), each floored at 0 and all four reset on a level change and on a player miss time bonus 3000, falling 25 every 11 frames the ball is moving (44.0 s), banked on a level win lives opponent 3 per level, player 5 for the entire game and never replenished timeline level card 46 frames, dead ball 19 frames, all at 30 fps palette stage #000000; nine tunnel strokes #00ff66 #00ea5e #00d555 #00bf4d #00aa44 #00953c #008033 #006a2b #005522; depth ring #77fffc with alpha 100-z; player paddle #0000ff and opponent #ff0000, each with four white pads at (+/-14.1, +/-9.1) and a white 12x8 centre pad; ball a radial gradient white -> #3fff11, red #ff0000 when dead; score / level / bonus readouts cyan #00ffff strings "curveball", "start game", "high scores", "main menu", "score:", "level:", "bonus:", "Game Over", "Curve Bonus!", "Super Curve Bonus!", "Accuracy Bonus!", "Name:", "submit", "You Got a High Score!" (that last one recovered by decoding glyph indices against a code-table-less DefineFont) RECONSTRUCTED (not recoverable from the file) --------------------------------------------- * Clip-event dispatch order. The ball reads one paddle's data fresh and the other's one frame stale, and which is which depends on AVM1's onClipEvent(enterFrame) ordering, which the file does not record. This port dispatches highest depth first (player paddle, ball, opponent, depth ring), so your paddle's motion in the frame of contact is what sets the spin. Both orders are implemented and the test suite runs the engine against its independent oracle under both. * The year, and any individual author. Not present in either build. * The five sound effects. The originals are ADPCM DefineSound tags; they are not redistributed. Equivalents are synthesised with WebAudio. * The typeface. The original embeds Bank Gothic BT Medium, which is not free to redistribute; the page falls back through a condensed system stack. * High scores. The original posted them to highscore.php / checkscore.php / enterscore.php on the publisher's server. Those endpoints are long dead, so the table lives in this browser only. WHAT DIFFERS ON PURPOSE ----------------------- 1. Levels past 10. The original indexes its three ten-entry level arrays with level-1 and has no cap, so reaching level 11 sets the ball speed to nothing and the game soft-locks. This port clamps the index at 10 and carries on at level-10 difficulty. 2. Real 3D. The original is a 2D display list with a scalar applied per clip. Here the geometry is authored in 3D world space and the ORIGINAL's own projection formula runs in a WebGL2 vertex shader, so the tunnel, the rails, the paddles and the depth ring are genuine 3D objects. The ball is a shaded sphere rather than a gradient-filled circle. 3. A fading ball trail, off with the "trail" button. Not in the original; it makes the curve legible on a big screen. 4. Pause (P or Escape), a sound toggle, an attract-mode demo behind the menu, and a local high-score table with a clear button. None of these existed. 5. Layout, typography and the responsive canvas are this port's. The playfield is the original's 350x250 stage letterboxed, so no gameplay geometry is stretched. WHAT DOES NOT DIFFER, EVEN THOUGH IT LOOKS LIKE A BUG ----------------------------------------------------- At level 9 the opponent's lag constant is 2 and at level 10 it is 1. A constant of 1 makes the assignment "position -= (position - ball)/1" put the opponent exactly on the ball every frame, and since the collision test compares the previous frame's boxes, its box and the ball's coincide. The opponent at level 10 provably cannot miss, and level 9 is very nearly the same. The original had no win screen either. This is reproduced rather than fixed; the test suite contains both the algebraic proof and a 288,000-frame randomised search that fails to falsify it. TOOLS AND SOURCES ----------------- SWF tag table, AVM1 disassembly, shape/gradient extraction, DefineText glyph decoding and the palette dump were done with purpose-written Python for this project. Wikipedia and the games portals were read only to locate the file; no fact above is sourced to them.