Skip to content

Commit

Permalink
beta update 21
Browse files Browse the repository at this point in the history
  • Loading branch information
morgan3d committed Jan 13, 2020
1 parent aa17494 commit 87a0714
Show file tree
Hide file tree
Showing 89 changed files with 3,784 additions and 947 deletions.
9 changes: 5 additions & 4 deletions console/BetterJSON.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
- Numbers with bare leading or trailing decimal
- NaN, Infinity, -Infinity
- Hexadecimal numbers
- Strict Unix newlines within strings
- Optional unquoted object keys using [A-Za-z_0-9]+ characters only
See also ../tools/betterjson.py for the Python version
Just use BetterJSON.parse and BetterJSON.stringify in place of
JSON.parse and JSON.stringify. They have the same API.
Expand Down Expand Up @@ -74,15 +75,15 @@ const NaNSymbol = '\uE001', InfinitySymbol = '\uE002', NegInfinitySymbol = '\uE0
return {
/** Returns the new string and a map */
protectQuotedStrings: function protectQuotedStrings(src) {
let numProtected = 0, protectionMap = [];
const protectionMap = [];

// Hide escaped quotes that would confuse the following regexp
src = src.replace('\\"', doubleQuoteProtection);
src = src.replace(/\\\\"/g, doubleQuoteProtection);

// Protect strings
src = src.replace(/"((?:[^"\\]|\\.)*)"/g, function (match, str) {
protectionMap.push(str);
return '"' + String.fromCharCode(numProtected++ + protectionBlockStart) + '"';
return '"' + String.fromCharCode(protectionMap.length - 1 + protectionBlockStart) + '"';
});

return [src, protectionMap];
Expand Down
33 changes: 33 additions & 0 deletions console/launcher/_Message.pyxl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
_Message
════════════════════════════════════════════════════════════════════════
let onDone

const fadeInFrames = 42
const holdFrames = 110
const fadeOutFrames = 42

enter(callback)
────────────────────────────────────────────────────────────────────────
onDone = callback


frame
────────────────────────────────────────────────────────────────────────
setBackground(#0)

let α = modeFrames
let done = false

if modeFrames < fadeInFrames:
α = (modeFrames + 1) / fadeInFrames
else if modeFrames < fadeInFrames + holdFrames:
α = 1
else if modeFrames < fadeInFrames + holdFrames + fadeOutFrames:
α = 1 - (modeFrames + 1 - fadeInFrames - holdFrames) / fadeOutFrames
else:
done = true

drawSprite({sprite: pauseMessageSprite, pos: ½ screenSize, opacity: α})

if done or anyButtonPress():
onDone()
220 changes: 220 additions & 0 deletions console/launcher/_Play.pyxl
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
_Play
════════════════════════════════════════════════════════════════════════

const gameArray = [{
title: "Quadpaddle",
developer: "CasualEffects",
url: "quad://games/quadpaddle",
description: "Blast bricks as a team from all four sides, grabbing powerups and racking up points.",
version: 0.5,
cooperative: true,
competitive: false,
minPlayers: 1,
maxPlayers: 4,
achievements: false,
builtin: true,
highscores: false,
label: qplabel[0][0],
preview: qppreview
},

{
title: "Serpitron",
developer: "CasualEffects",
url: "quad://games/serpitron",
description: "Grow your serpent without crashing into obstacles or yourself. With rats, shotguns, blizzards, cities, & pyramids.",
version: 0.5,
cooperative: false,
competitive: true,
minPlayers: 1,
maxPlayers: 4,
achievements: false,
builtin: true,
highscores: false,
label: splabel[0][0],
preview: sppreview
},

{
title: "Ice Time",
developer: "CasualEffects",
url: "quad://games/icetime",
description: "2 vs. 2 ice hockey with teams, player stats., faceoffs, passing…and a Zamboni.",
version: 0.5,
cooperative: true,
competitive: true,
minPlayers: 1,
maxPlayers: 4,
achievements: false,
highscores: false,
builtin: true,
label: itlabel[0][0],
preview: itpreview
},

{
title: "SpeedStreet",
developer: "CasualEffects",
url: "quad://games/speedstreet",

version: 0.5,
description: "How fast can you go? Tear through the road course pulling tricks off jumps and fighting to be the leader of the pack.",
minPlayers: 1,
maxPlayers: 4,
cooperative: false,
competitive: true,
achievements: false,
highscores: false,
builtin: true,
label: sslabel[0][0],
preview: sspreview
}
]

const backgroundColor = #135
const promptColor = #4DF
const promptFont = mediumFont
const shadowColor = #0006
const selectedColor = #f
const unselectedColor = #9

// Pixel scrolling
let gameShift = 0
let gameIndex = 0

let modeFrameShift = 0

def drawIcon(icon, pos):
assert(icon != nil)
drawSprite({sprite: icon, pos: pos + xy(3 + icon.size.x / 2, 3 - icon.size.y / 2)})
return xy(icon.size.x + 2, icon.size.y + 2)


frame
────────────────────────────────────────────────────────────────────────

const introFrames = 20
if gameFrames < introFrames:
// Fade and slide in
const α = 1 - min(1, (gameFrames + 1) / (introFrames + 1))
setPostEffects({background:#0,
pos: xy(α * screenSize.x, 0),
color: rgba(0, 0, 0, α)})
else if gameFrames == introFrames:
resetPostEffects()

setBackground(backgroundColor)

drawCornerRect(xy(0,0), xy(90, 15), #2)
drawCornerRect(xy(155,0), xy(230, 15), #2)
drawText({font: mediumFont, text:joy.prompt["⍇"], pos: xy(70,12), color: promptColor, shadow: shadowColor})
drawText({font: mediumFont, text:"LIBRARY", pos: xy(99,12), color: selectedColor, shadow: shadowColor})
drawText({font: mediumFont, text:"DISCOVER", pos: xy(180,12), color: unselectedColor, shadow: shadowColor})
drawText({font: mediumFont, text:"URL", pos: xy(260,12), color: unselectedColor, shadow: shadowColor})
drawText({font: mediumFont, text:joy.prompt["⍈"], pos: xy(300,12), color: promptColor, shadow: shadowColor})

drawLine(xy(0, 16), xy(90, 16), #f)
drawLine(xy(90, 16), xy(90, 1), #f)
drawLine(xy(91, 0), xy(154, 0), #f)
drawLine(xy(155, 1), xy(155, 16), #f)
drawLine(xy(155, 16), xy(384, 16), #f)

drawText({font: mediumFont, text:joy.prompt["⍐"], pos: xy(1, 28), color: promptColor, shadow: shadowColor})
drawText({font: mediumFont, text:joy.prompt["⍗"], pos: xy(1, 220), color: promptColor, shadow: shadowColor})

const boxVerticalSpacing = 75

setClip(xy(12, 20), xy(64, 201))
for gameIndex - 2 <= g <= gameIndex + 2:
const game = arrayValue(gameArray, g, "loop")
const pos = xy(44, boxVerticalSpacing * g + 64/2 + 19 - gameShift - (gameIndex - 1) * boxVerticalSpacing)
drawSprite({sprite: game.label, pos: pos})
drawLine(pos + xy(1-64/2, 64/2), pos + xy(64/2-2, 64/2), shadowColor)
resetClip()

// Highlight selected game
drawCornerRect(xy(11, 93), xy(66, 66), nil, #f)
drawCornerRect(xy(10, 92), xy(68, 68), nil, #d)

local:
const game = gameArray[gameIndex]
const t = floor((modeFrames - modeFrameShift) / 3) mod 60
const x = 99
const pos = xy(x + 384/4, 224/4 + 66)
drawSprite({sprite: game.preview[t mod 6][floor(t / 6)], pos: pos})
drawLine(pos + xy(1-384/4, 224/4), pos + xy(384/4-2, 224/4), shadowColor)
drawLine(pos + xy(2-384/4, 224/4 + 1), pos + xy(384/4-3, 224/4 + 1), shadowColor / 2)
drawText({font:largeFont, text:game.title, pos:xy(x, 40), color:selectedColor, shadow:shadowColor, xAlign:"left"})
drawText({font:mediumFont, text:"by " + game.developer, pos:xy(x, 56), color:selectedColor, shadow:shadowColor, xAlign:"left"})
drawText({font:smallFont, text:game.url, pos:xy(x, 220), color:unselectedColor, xAlign:"left"})
drawText({font:smallFont, text:game.description, pos:xy(x, 193), color:selectedColor, xAlign:"left", wrapWidth: 280})

const sideX = 295
const wordX = sideX + 27
drawText({font:promptFont, text:joy.prompt["ⓠ"] + " PLAY", pos:xy(sideX + 7, 74), color:promptColor, shadow:shadowColor, xAlign:"left"})
if not game.builtin: drawText({font:promptFont, text:joy.prompt["ⓓ"] + " FORGET", pos:xy(sideX + 7, 88), color:promptColor, shadow:shadowColor, xAlign:"left"})

// Icons
let y = 112
if game.maxPlayers == 0 or game.minPlayers == 0 and game.maxPlayers == nil:
drawText({font:mediumFont, text:"Animation", pos:xy(wordX, y), color:selectedColor, xAlign:"left"}).y
drawIcon(icons.players[0], xy(sideX, y)).y
y += 15
else if game.minPlayers != nil and game.maxPlayers != nil:
let lo = clamp(min(game.minPlayers, game.maxPlayers), 1, 5)
let hi = clamp(max(game.minPlayers, game.maxPlayers), 1, 5)
if lo != hi:
drawIcon(icons.players[lo], xy(sideX, y))
drawLine(xy(sideX + 21, y - 4), xy(sideX + 28, y - 4), selectedColor)
drawIcon(icons.players[hi], xy(sideX + 29, y)).y
else:
drawIcon(icons.players[lo], xy(sideX, y))
drawText({font:mediumFont, text:"" + lo + "-Player", pos:xy(wordX, y), color:selectedColor, xAlign:"left"})
y += 15
else if game.minPlayers != nil or game.maxPlayers != nil:
// Only one specified
let lo = if game.minPlayers != nil then game.minPlayers else game.maxPlayers
drawIcon(icons.players[clamp(lo, 1, 5)], xy(sideX, y))
drawText({font:mediumFont, text:"" + lo + "-Player", pos:xy(wordX, y), color:selectedColor, xAlign:"left"})
y += 15

if game.cooperative:
drawIcon(icons.cooperative, xy(sideX, y)).y
y += drawText({font:mediumFont, text:"Cooperate", pos:xy(wordX, y), color:selectedColor, xAlign:"left"}).y

if game.competitive:
drawIcon(icons.competitive, xy(sideX, y)).y
y += drawText({font:mediumFont, text:"Compete", pos:xy(wordX, y), color:selectedColor, xAlign:"left"}).y

if game.achievements:
drawIcon(icons.achievements, xy(sideX, y)).y
y += drawText({font:mediumFont, text:"Achieve", pos:xy(wordX, y), color:selectedColor, xAlign:"left"}).y

if game.highscores:
drawIcon(icons.highscores, xy(sideX, y)).y
y += drawText({font:mediumFont, text:"Score", pos:xy(wordX, y), color:selectedColor, xAlign:"left"}).y

if joy.aa or joy.qq:
def callback():
launchGame(gameArray[gameIndex].url)
setMode(_SlideOut, callback) because "Player selected game"

// Animate scrolling
if joy.yy:
const Δy = 3 joy.yy
playAudioClip(boop, false, 300%)
def shift(): gameShift += Δy
addFrameHook(shift, nil, boxVerticalSpacing / 3)

// Change games
if gameShift >= boxVerticalSpacing:
gameShift -= boxVerticalSpacing
modeFrameShift = modeFrames - 1
++gameIndex
else if gameShift <= -boxVerticalSpacing:
gameShift += boxVerticalSpacing
--gameIndex
modeFrameShift = modeFrames - 1

gameIndex = loop(gameIndex, gameArray.length)

26 changes: 26 additions & 0 deletions console/launcher/_SlideOut.pyxl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
_SlideOut
════════════════════════════════════════════════════════════════════════

let onDone

enter(callback)
────────────────────────────────────────────────────────────────────────
onDone = callback


frame
────────────────────────────────────────────────────────────────────────

const introFrames = 20
if modeFrames < introFrames:
// Fade and slide in
const α = clamp((modeFrames + 1) / (introFrames + 1), 0, 1)
drawPreviousMode()
setPostEffects({background:#0,
pos: xy(α * screenSize.x, 0),
color: rgba(0, 0, 0, α²)})
else:
// Animation complete
resetPostEffects()
setMode(_Message, onDone)

8 changes: 6 additions & 2 deletions console/launcher/launcher.game.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
{
"title": "Launcher",
"developer": "Casual Effects",
"copyright": "©2019 Morgan McGuire",
"copyright": "©2020 Morgan McGuire",
"license": "LGPL 3.0",

"packages": [],
"scripts": [],
"modes": ["Play*"],
"modes": ["_Play*", "_SlideOut", "_Message"],
"assets": {
"largeFont": "quad://fonts/deja-15.font.json",
"mediumFont": "quad://fonts/deja-9.font.json",
"smallFont": "quad://fonts/scoreboard-7.font.json",
"icons": "icons.sprite.json",
"boop": "boop.sound.json",
"pauseMessageSprite": "quad://console/os/pause-message.sprite.json",

"sslabel": "sslabel.sprite.json",
"sspreview": "sspreview.sprite.json",

"itlabel": "itlabel.sprite.json",
"itpreview": "itpreview.sprite.json",

"qplabel": "qplabel.sprite.json",
"qppreview": "qppreview.sprite.json",

"splabel": "splabel.sprite.json",
"sppreview": "sppreview.sprite.json"
},
Expand Down
Loading

0 comments on commit 87a0714

Please sign in to comment.