Add garden
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 13s

This commit is contained in:
thepaperpilot 2024-06-03 22:20:36 -05:00
parent 1a093b2c2b
commit 6bdaf0294e
32 changed files with 214 additions and 217 deletions

1
.gitignore vendored
View file

@ -2,4 +2,5 @@ node_modules
site/.vitepress/dist
site/.vitepress/cache
site/garden/
site/public/garden/
garden-output/

3
.gitmodules vendored
View file

@ -13,9 +13,6 @@
[submodule "the_ascension_tree"]
path = site/public/the_ascension_tree
url = https://github.com/thepaperpilot/the_ascension_tree
[submodule "guide-to-incrementals"]
path = site/guide-to-incrementals
url = https://code.incremental.social/thepaperpilot/Guide-to-Incrementals
[submodule "dream"]
path = site/public/dream
url = https://github.com/thepaperpilot/Dream-Hero

1
CNAME
View file

@ -1 +0,0 @@
www.thepaperpilot.org

2
Garden

@ -1 +1 @@
Subproject commit 87c5ec9f00068af2767b9d42760e78314ca666e3
Subproject commit 6fc782a95e686cd4426c2d1ec4f6917e3026dc47

View file

@ -19,14 +19,22 @@ function toSlug(string) {
}
(async () => {
const blockRefs = {};
const blockRefs = {};
const blockLinks = {};
const indices = [];
await walk("./garden-output/logseq-pages", (dir, file, resolve) => {
const filePath = path.resolve(dir, file);
const data = fs.readFileSync(filePath).toString();
for (const match of data.matchAll(/- (.*)\n\s*id:: (.*)/gm)) {
const slug = path.basename(file, ".md").replaceAll('___', '/').replaceAll(/%3F/gi, '').replace('what-is-content-', 'what-is-content');
for (const match of data.matchAll(/(.*)\n\s*id:: (.*)/gm)) {
const text = match[1];
const id = match[2];
blockRefs[id] = `[${text}](/garden/${path.basename(file, ".md")}/index.md#${id})`;
const link = `/garden/${slug}/index.md#${id}`;
blockLinks[id] = link;
blockRefs[id] = `[${text}](${link})`;
}
if (data.match(/index: "true"/g)) {
indices.push(slug);
}
resolve();
});
@ -46,15 +54,15 @@ function toSlug(string) {
}
const name = path.basename(file, ".md").replaceAll('___', '/');
const slug = toSlug(name);
const slug = toSlug(name).replaceAll(/%3F/gi, '');
const link = `/garden/${slug}/index.md`;
pageLinks[name] = link;
pageLinks[name.replaceAll(/%3F/gi, '?')] = link;
for (match of data.matchAll(/alias:: (.*)/g)) {
for (const match of data.matchAll(/alias:: (.*)/g)) {
match[1].split(", ").forEach(page => (pageLinks[page] = link));
}
for (match of data.matchAll(/tags:: (.*)/g)) {
for (const match of data.matchAll(/tags:: (.*)/g)) {
match[1].split(", ").forEach(page => {
const pageSlug = toSlug(page);
taggedBy[pageSlug] = [...(taggedBy[pageSlug] ?? []), name];
@ -62,9 +70,11 @@ function toSlug(string) {
});
}
for (match of data.matchAll(/\[\[([^\[\]]*)\]\]/g)) {
const pageSlug = toSlug(match[1]);
referencedBy[pageSlug] = [...(referencedBy[pageSlug] ?? []), name];
if (!indices.includes(slug)) {
for (const match of data.matchAll(/\[\[([^\[\]]*)\]\]/g)) {
const pageSlug = toSlug(match[1]);
referencedBy[pageSlug] = [...(referencedBy[pageSlug] ?? []), name];
}
}
resolve();
@ -97,6 +107,18 @@ function toSlug(string) {
data = data.replaceAll(
/\[\[([^\[\]]*)\]\]/g,
(_, page) => `[${page}](${pageLinks[page]})`);
// Fix internal asset links
data = data.replaceAll(
/\(\/logseq-assets\/([^\)]*)\)/g,
'(/garden/$1)');
// Fix logseq block links
data = data.replaceAll(
/logseq:\/\/graph\/Garden\?block-id=([^\)]*)/g,
(_, block) => `${blockLinks[block]})`);
// Fix logseq page links
data = data.replaceAll(
/logseq:\/\/graph\/Garden\?page=([^\)]*)/g,
(_, page) => `${pageLinks[page.replaceAll('%20', ' ')]})`);
// Add tags and references
const title = path.basename(file, ".md");
if (title in tagged) {
@ -116,6 +138,7 @@ function toSlug(string) {
`---\n\n> Referenced by: ${referencedBy[title].map(tag => `[${tag}](${pageLinks[tag]})`).join(", ")}\n\n`);
}
// Add title to the top
data = data.replaceAll('___', '/');
data = data.replaceAll(
/---\n\n/gm,
`---\n# ${data.match(/title: "(.+)"/)[1]}\n\n`);
@ -127,18 +150,38 @@ function toSlug(string) {
});
fs.mkdirSync("./site/garden");
fs.mkdirSync("./site/public/garden");
// Move everything from ./garden-output/logseq-pages into ./site/garden
await walk("./garden-output/logseq-pages", (dir, file, resolve) => {
const folder = path.resolve("./site/garden", path.basename(file, ".md"));
const folder = path.resolve("./site/garden", ...path.basename(file, ".md").split('___'));
fs.mkdirSync(folder);
fs.copyFileSync(path.resolve(dir, file), path.resolve(folder, "index.md"));
resolve();
});
// Move everything from ./garden-output/logseq-assets into ./site/public
// Move everything from ./garden-output/logseq-assets into ./site/public/garden
await walk("./garden-output/logseq-assets", (dir, file, resolve) => {
fs.copyFileSync(path.resolve(dir, file), path.resolve("./site/public", path.basename(file)));
fs.copyFileSync(path.resolve(dir, file), path.resolve("./site/public/garden", ...path.basename(file).split('___')));
resolve();
});
// Copy the guide-to-incrementals pages to the old locations so links don't break
fs.mkdirSync('./site/guide-to-incrementals');
fs.copyFileSync('./site/garden/guide-to-incrementals/index.md', './site/guide-to-incrementals/index.md');
fs.mkdirSync('./site/guide-to-incrementals/design');
fs.mkdirSync('./site/guide-to-incrementals/design/criticism');
fs.copyFileSync('./site/garden/guide-to-incrementals/navigating-criticism/index.md', './site/guide-to-incrementals/design/criticism/index.md');
fs.mkdirSync('./site/guide-to-incrementals/ludology');
fs.mkdirSync('./site/guide-to-incrementals/ludology/appeal-developers');
fs.copyFileSync('./site/garden/guide-to-incrementals/appeal-to-developers/index.md', './site/guide-to-incrementals/ludology/appeal-developers/index.md');
fs.mkdirSync('./site/guide-to-incrementals/ludology/appeal-gamers');
fs.copyFileSync('./site/garden/guide-to-incrementals/appeal-to-players/index.md', './site/guide-to-incrementals/ludology/appeal-gamers/index.md');
fs.mkdirSync('./site/guide-to-incrementals/ludology/content');
// For what is content, also remove the - at the end
fs.cpSync('./site/garden/guide-to-incrementals/what-is-content-', './site/garden/guide-to-incrementals/what-is-content', { recursive: true });
fs.copyFileSync('./site/garden/guide-to-incrementals/what-is-content-/index.md', './site/guide-to-incrementals/ludology/content/index.md');
fs.rmSync('./site/garden/guide-to-incrementals/what-is-content-', { recursive: true });
fs.mkdirSync('./site/guide-to-incrementals/ludology/definition');
fs.copyFileSync('./site/garden/guide-to-incrementals/defining-the-genre/index.md', './site/guide-to-incrementals/ludology/definition/index.md');
})();

View file

@ -5,7 +5,7 @@
"scripts": {
"serve": "vitepress serve site",
"dev": "vitepress dev site",
"build": "rm -rf ./site/garden && rm -rf ./garden-output && yarn run logseq-export && node build_garden.js && vitepress build site",
"build": "rm -rf ./site/guide-to-incrementals && rm -rf ./site/public/garden && rm -rf ./site/garden && rm -rf ./garden-output && yarn run logseq-export && node build_garden.js && vitepress build site",
"logseq-export": "run-script-os",
"logseq-export:win32": "logseq-export/logseq-export.exe --logseqFolder ./Garden --outputFolder ./garden-output",
"logseq-export:linux": "chmod +x logseq-export/logseq-export && logseq-export/logseq-export --logseqFolder ./Garden --outputFolder ./garden-output"

View file

@ -4,7 +4,7 @@ import { defineConfig } from "vitepress";
module.exports = {
lang: "en-US",
title: 'The Paper Pilot',
description: 'The Paper Pilot portfolio site',
description: 'The Paper Pilot Personal Website',
vite: {
plugins: [
SearchPlugin({
@ -16,11 +16,13 @@ module.exports = {
})
]
},
sitemap: {
hostname: 'https://thepaperpilot.org'
},
head: [
['link', { rel: 'preconnect', href: 'https://fonts.googleapis.com' }],
['link', { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Pacifico&family=Roboto+Mono:ital,wght@0,400;0,600;1,400&display=swap' }],
['link', { rel: 'manifest', href: '/site.webmanifest' }],
['script', { defer: true, 'data-domain': 'thepaperpilot.org', src: 'https://plausible.io/js/plausible.js' }],
['meta', { name: 'og:description', content: 'The Paper Pilot portfolio site' }]
],
lastUpdated: true,
@ -28,69 +30,161 @@ module.exports = {
themeConfig: {
outline: 'deep',
nav: [
{ text: "Guide to Incrementals", link: "/guide-to-incrementals/", activeMatch: "^/guide-to-incrementals" },
{ text: "Projects", link: "/projects/", activeMatch: "^/projects" },
{ text: "Profectus", link: "https://moddingtree.com" }
{ text: "Profectus", link: "https://moddingtree.com" },
{ text: "Incremental Social", link: "https://incremental.social" }
],
socialLinks: [
{ icon: "github", link: "https://github.com/thepaperpilot" },
{ icon: "linkedin", link: "https://www.linkedin.com/pub/anthony-lawn/a9/a98/2" },
{ icon: "discord", link: "https://discord.gg/yJ4fjnjU54" },
{ icon: { svg: `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-mastodon" viewBox="0 0 16 16">
<path d="M11.19 12.195c2.016-.24 3.77-1.475 3.99-2.603.348-1.778.32-4.339.32-4.339 0-3.47-2.286-4.488-2.286-4.488C12.062.238 10.083.017 8.027 0h-.05C5.92.017 3.942.238 2.79.765c0 0-2.285 1.017-2.285 4.488l-.002.662c-.004.64-.007 1.35.011 2.091.083 3.394.626 6.74 3.78 7.57 1.454.383 2.703.463 3.709.408 1.823-.1 2.847-.647 2.847-.647l-.06-1.317s-1.303.41-2.767.36c-1.45-.05-2.98-.156-3.215-1.928a3.614 3.614 0 0 1-.033-.496s1.424.346 3.228.428c1.103.05 2.137-.064 3.188-.189zm1.613-2.47H11.13v-4.08c0-.859-.364-1.295-1.091-1.295-.804 0-1.207.517-1.207 1.541v2.233H7.168V5.89c0-1.024-.403-1.541-1.207-1.541-.727 0-1.091.436-1.091 1.296v4.079H3.197V5.522c0-.859.22-1.541.66-2.046.456-.505 1.052-.764 1.793-.764.856 0 1.504.328 1.933.983L8 4.39l.417-.695c.429-.655 1.077-.983 1.934-.983.74 0 1.336.259 1.791.764.442.505.661 1.187.661 2.046v4.203z\"/>
</svg>` }, link: "https://mastodon.gamedev.place/@thepaperpilot" }
{ icon: { svg: `<svg xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" version="1.1" viewBox="0 0 27.9 32">
<g xmlns="http://www.w3.org/2000/svg" transform="translate(-.095 .005)" fill="#040404">
<path d="m27.1 31.2v-30.5h-2.19v-0.732h3.04v32h-3.04v-0.732z"/>
<path d="m8.23 10.4v1.54h0.044c0.385-0.564 0.893-1.03 1.49-1.37 0.58-0.323 1.25-0.485 1.99-0.485 0.72 0 1.38 0.14 1.97 0.42 0.595 0.279 1.05 0.771 1.36 1.48 0.338-0.5 0.796-0.941 1.38-1.32 0.58-0.383 1.27-0.574 2.06-0.574 0.602 0 1.16 0.074 1.67 0.22 0.514 0.148 0.954 0.383 1.32 0.707 0.366 0.323 0.653 0.746 0.859 1.27 0.205 0.522 0.308 1.15 0.308 1.89v7.63h-3.13v-6.46c0-0.383-0.015-0.743-0.044-1.08-0.0209-0.307-0.103-0.607-0.242-0.882-0.133-0.251-0.336-0.458-0.584-0.596-0.257-0.146-0.606-0.22-1.05-0.22-0.44 0-0.796 0.085-1.07 0.253-0.272 0.17-0.485 0.39-0.639 0.662-0.159 0.287-0.264 0.602-0.308 0.927-0.052 0.347-0.078 0.697-0.078 1.05v6.35h-3.13v-6.4c0-0.338-7e-3 -0.673-0.021-1-0.0114-0.314-0.0749-0.623-0.188-0.916-0.108-0.277-0.3-0.512-0.55-0.673-0.258-0.168-0.636-0.253-1.14-0.253-0.198 0.0083-0.394 0.042-0.584 0.1-0.258 0.0745-0.498 0.202-0.705 0.374-0.228 0.184-0.422 0.449-0.584 0.794-0.161 0.346-0.242 0.798-0.242 1.36v6.62h-3.13v-11.4z"/>
<path d="m0.936 0.732v30.5h2.19v0.732h-3.04v-32h3.03v0.732z"/>
</g>
</svg>` }, link: "https://matrix.to/#/@thepaperpilot:incremental.social" },
{ icon: { svg: `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.1"
viewBox="0 0 334.73599 334.736"
id="svg40"
sodipodi:docname="logo_condensed.svg"
inkscape:version="1.3.1 (5ab75fa947, 2023-11-03)"
width="334.73599"
height="334.73599"
inkscape:export-filename="logo_condensed_forgejo.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs44" />
<sodipodi:namedview
id="namedview42"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
showgrid="false"
inkscape:zoom="2.8284271"
inkscape:cx="87.327687"
inkscape:cy="132.40574"
inkscape:window-width="2560"
inkscape:window-height="1369"
inkscape:window-x="-8"
inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="g4"
inkscape:showpageshadow="0"
inkscape:deskcolor="#d1d1d1"
showguides="true">
<sodipodi:guide
position="264.97068,-61.5051"
orientation="1,0"
id="guide1"
inkscape:locked="false" />
<sodipodi:guide
position="93.001002,77.044749"
orientation="1,0"
id="guide2"
inkscape:locked="false" />
<sodipodi:guide
position="109.56249,95.005809"
orientation="1,0"
id="guide3"
inkscape:locked="false" />
<sodipodi:guide
position="163.99999,77.505809"
orientation="1,0"
id="guide4"
inkscape:locked="false" />
<sodipodi:guide
position="179,1.5058096"
orientation="1,0"
id="guide5"
inkscape:locked="false" />
</sodipodi:namedview>
<g
id="g1"
transform="translate(-1,84.932692)">
<path
fill="#6b438b"
d="m 271.24983,105.65061 q -0.36,4.08 -0.31,8.47 0.05,4.56 -4.49,8.19 -9.11,7.28 -17.84,-0.35 c -5.3,-4.65 -3.99,-10.34 -4.42,-16.25 -11.33,-3.3 -21.65,-12.470005 -22.8,-24.590005 q -0.53,-5.59 -0.95,-11.21 -0.04,-0.52 -0.53,-0.64 c -4.87,-1.23 -7.99,-2.08 -10.29,-6.94 -3.9,-8.23 0.79,-15.35 8.92,-18.12 a 0.39,0.4 77.9 0 0 0.27,-0.41 c -0.74,-8.02 -1.4,-15.08 1.56,-22.79 3.11,-8.09 9.57,-14.2599996 17.53,-17.6199996 q 4.11,-1.73 10.44,-2.07 8.68,-0.46000001 17.4,-0.26 16.85,0.37 26.28,13.5899996 c 6.43,9.02 5.96,18.48 5.03,29.16 q -0.05,0.51 0.45,0.64 c 13.7,3.61 12.56,22.82 -1.42,24.78 a 0.71,0.71 0 0 0 -0.61,0.65 c -0.38,5.39 -0.17,11.34 -2.1,16.46 q -5.7,15.160005 -22.12,19.310005 z"
id="path28"
style="display:inline" />
<path
fill="#eceff4"
d="m 142.39,41.700605 -31,11.57 a 0.62,0.61 79.8 0 1 -0.83,-0.58 l 0.01,-19.31 q 0,-0.55 0.51,-0.74 23.03,-8.68 46.02,-17.25 c 2.57,-0.96 4.78,-0.76 7.42,-0.75 a 0.49,0.49 0 0 1 0.49,0.49 V 147.45061 a 0.79,0.79 0 0 1 -0.8,0.79 l -19.99,-0.01 q -1.17,0 -1.17,-1.16 V 42.160605 a 0.49,0.49 0 0 0 -0.66,-0.46 z"
id="path30"
style="display:inline" />
<path
fill="#eceff4"
d="m 59.21,74.480605 h 33.95 a 0.84,0.84 0 0 1 0.84,0.84 l -0.01,18.96 a 0.84,0.84 0 0 1 -0.84,0.84 l -33.88,0.03 a 0.84,0.84 0 0 0 -0.84,0.84 l -0.08,38.580005 a 0.84,0.84 0 0 1 -0.84,0.84 l -20.11,-0.01 a 0.84,0.84 0 0 1 -0.84,-0.84 L 36.55,95.980605 a 0.84,0.84 0 0 0 -0.84,-0.84 H 1.84 a 0.84,0.84 0 0 1 -0.84,-0.84 l 0.02,-19.02 a 0.84,0.84 0 0 1 0.84,-0.84 l 33.86,0.02 a 0.84,0.84 0 0 0 0.84,-0.84 v -35.79 a 0.84,0.84 0 0 1 0.84,-0.84 l 20.13,-0.02 a 0.84,0.84 0 0 1 0.84,0.84 v 35.83 a 0.84,0.84 0 0 0 0.84,0.84 z"
id="path32"
style="display:inline" />
<path
fill="#533566"
d="m 244.18983,105.71061 c 0.43,5.91 -0.88,11.6 4.42,16.25 q 8.73,7.63 17.84,0.35 4.54,-3.63 4.49,-8.19 -0.05,-4.39 0.31,-8.47 24.6,4 42.34,20.72 l -0.18,37.49 -110.93,0.01 -0.09,-37.61 q 17.53,-16.43 41.8,-20.55 z"
id="path34"
style="display:inline" />
<path
fill="#6b438b"
d="m 202.38983,126.26061 0.09,37.61 -21.38,-0.49 a 1.13,1.13 0 0 1 -1.04,-1.49 q 7.21,-21.29 22.33,-35.63 z"
id="path36"
style="display:inline" />
<path
fill="#6b438b"
d="m 313.58983,126.37061 q 12.71,12.03 19.9,29.52 1.79,4.36 2.23,6.86 0.11,0.61 -0.51,0.62 l -21.8,0.49 z"
id="path38"
style="display:inline" />
<g
transform="matrix(0.77226665,0,0,0.77226665,154.89692,5.2164554)"
id="g4"
style="display:none">
<path
d="M 58,168 V 70 a 50,50 0 0 1 50,-50 h 20"
class="orange"
id="path1"
style="fill:none;stroke:#ff6600;stroke-width:25" />
<path
d="m 58,168 v -30 a 50,50 0 0 1 50,-50 h 20"
class="red"
id="path2-3"
style="fill:none;stroke:#d40000;stroke-width:25" />
<circle
cx="142"
cy="20"
r="18"
class="orange"
id="circle2"
style="fill:none;stroke:#ff6600;stroke-width:15" />
<circle
cx="142"
cy="88"
r="18"
class="red"
id="circle3"
style="fill:none;stroke:#d40000;stroke-width:15" />
<circle
cx="58"
cy="180"
r="18"
class="red"
id="circle4"
style="fill:none;stroke:#d40000;stroke-width:15" />
</g>
</g>
</svg>
` }, link: "https://incremental.social/u/thepaperpilot" }
],
sidebar: {
"guide-to-incrementals": [
{
text: "Ludology",
collapsible: true,
items: [
{ text: "Defining the Genre", link: "/guide-to-incrementals/ludology/definition" },
{ text: "Appeal to Players", link: "/guide-to-incrementals/ludology/appeal-gamers" },
{ text: "Appeal to Developers", link: "/guide-to-incrementals/ludology/appeal-developers" },
{ text: "What is Content?", link: "/guide-to-incrementals/ludology/content" }
]
},
{
text: "Development",
collapsible: true,
items: [
{ text: "Navigating Criticism", link: "/guide-to-incrementals/design/criticism"}
]
}
],
"projects": [
{
text: "Games",
items: [
{ text: "Planar Pioneers", link: "https://www.thepaperpilot.org/planar" },
{ text: "Advent Incremental", link: "https://www.thepaperpilot.org/advent" },
{ text: "Game Dev Tree", link: "https://www.thepaperpilot.org/gamedevtree/" },
{ text: "Dice Armor", link: "/projects/dice/" },
{ text: "Capture the Citadel", link: "/projects/citadel/"},
{ text: "More on Itch", link: "https://thepaperpilot.itch.io/" }
]
},
{
text: "Non-Games",
items: [
{ text: "Profectus", link: "https://moddingtree.com" },
{ text: "Incremental Social", link: "https://incremental.social" },
{ text: "V-ecs", link: "/projects/vecs/" },
{ text: "OptiSpeech", link: "/projects/optispeech/" },
{ text: "Babble Buds", link: "/projects/babble/" }
]
}
],
"garden": [
{
text: "Garden",
items: [
{ text: "The Small Web", link: "https://www.thepaperpilot.org/garden/the-small-web" }
]
}
]
}
sidebar: [
{ text: "My Projects", link: "https://www.thepaperpilot.org/garden/my-projects" },
{ text: "Guide to Incrementals", link: "https://www.thepaperpilot.org/garden/guide-to-incrementals" },
{ text: "The Small Web", link: "https://www.thepaperpilot.org/garden/the-small-web" }
]
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 872 KiB

View file

@ -1,30 +0,0 @@
---
title: Babble Buds
---
# Babble Buds
[Babble Buds Homepage](http://babblebuds.xyz/)
Source Code:
- [Babble Buds](https://github.com/thepaperpilot/Babble-Buds)
- [Babble Movie Maker](https://github.com/thepaperpilot/BabbleMovieMaker)
- [babble.js](https://github.com/thepaperpilot/babble.js)
- [babble.cs](https://github.com/thepaperpilot/babble.cs)
Babble buds is a free, open-source virtual puppet show software. It is heavily based on the non-public software called "Puppet Pals", used in URealms Live. The software is written in javascript using React, a rendering library called PIXI.js, and electron.
Users can create puppets with different faces for different emotions, and then use the puppet on a stage where you and other users can each make your respective puppets move, change emotions, and "babble" at each other. The stage has a green screen feature and can be popped out, which gives the users tons of possibilities in terms of using the program for a role-playing live stream, faux video chatting with friends, game development, or whatever else you want!
Users can connect to the public server and create private rooms so that they and their friends can see each other's puppets and use the software however they please. For the security conscious, you can also use the server's source code to self-host your private server.
![Babble Buds Screenshot](./screenshot.png)
## Engine
The engine originally made to make the Babble Buds program was separated into a separate engine called `babble.js`, so that projects created in Babble Buds can be used in other projects. For example, a game can create puppets in Babble Buds and then use them for cutscenes or player agency inside of the game. Additionally, it has been ported to C# (called `babble.cs`) for use with Unity, for the same kinds of purposes. You can check out [Tower Offense](https://www.thepaperpilot.itch.io/tower-offense) for a pixi.js game using Babble Buds puppets for the cutscenes, or [Dice Armor](../dice/) for a unity game using Babble Buds puppets for the cutscenes.
## Babble Movie Maker
Babble Movie Maker is a cutscene editor for Babble Buds puppets. You open a babble buds project in it, and you can add actors to a stage and have them move and change expressions, etc., on a timeline. You can then use the cutscene in a game using `babble.js` or `babble.cs`, or export the cutscene into a video file. There is even support for defining custom commands with custom fields, so that if you've expanded upon the default actions provided in `babble.js` or `babble.cs`, you can still use Movie Maker to create your cutscenes.
![Babble MM Screenshot](./babblemmscreenshot.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

View file

@ -1,10 +0,0 @@
---
title: Capture the Citadel
---
# Capture the Citadel
A 3D VR re-envisioning of a Slay the Spire-style game by Anthony Lawn and Grant Barbee for their VR class in college's final project.
For more details, visit [Grant's page on the game](https://grantcbarbee.github.io/conquer-the-citadel.html).
![Screenshot](./screenshot.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 962 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 KiB

View file

@ -1,46 +0,0 @@
---
title: Dice Armor
---
# Dice Armor
[Download Here](https://drive.google.com/open?id=18rwqEIdMChdGtB-9LdI4wiqeM5C5ViOL)
Dice Armor is a game that started development as a semester-long project by a team of nine: a producer, a creative director, a narrative writer, an artist, two programmers, and 3 game designers. The information here is about my contributions as the lead programmer over the semester because I can show off stuff like the editor scripts I wrote. I was doing everything from interface coding, editor scripts, integrating Babble Buds, and of course, everything related to the gameplay itself. To date I'm still the lead programmer for the game; for more up-to-date information on the current state of the game please visit the official site.
The build available here was created for showing off at the end of the semester, and as such has some buttons present to make the game easier to skip parts of the game to see all the content: You start with all the dice in the game already in the shop, there's a button to give yourself free money to buy these dice with, and in the duel, there are buttons to force a win or a loss, which can be used to skip the tutorial (not recommended for first-time players).
![Tutorial](./da2.png)
Dice Armor is a dice dueling game. Players can use abilities, flip dice, and attack each other to win in a dice game that puts chance into the hands of the players. This is what the dueling scene looks like, with a tutorial cutscene happening on top to guide the player through the basics. Also, all the dice are constructed dynamically, using quaternion math to figure out the placement of each component relative to the face it is going on. The die in the middle has one of the player' and opponents' portraits on each of its sides.
![Editors](./editors.png)
For many of the objects I've created, I've made scriptable objects so that game designers can add and modify them easily. Additionally, I would create custom inspectors for the objects to help make them as easy to understand and edit as possible. The opponent's artificial intelligence is made up of many strategies, in a prioritized list. When it is the opponents' turn they go through each strategy and check if they can be run, and if so then the opponent performs the strategy and starts back over at the top of the list of strategies. The + sign under the list of strategies opens an organized dropdown of all the various strategies.
![Simulator](./simulator.jpg)
In addition to custom inspector code, I've created new tools for the editor for our game designers to use. This is a duel simulator that will take two opponents and simulate an arbitrary number of duels between them, and output the results and summarize them for you, much much quicker than manually going through the duels, even with an absurdly high timeScale. This will become incredibly useful in making balance changes and testing new dice against existing sets. This is a screenshot of it in edit mode, but in play mode it removes the "Dueling Managers" field and will use whatever the current duel balance settings are, allowing for the GDs to test freely in play mode without worrying about undoing all their changes afterward.
![Cutscene](./da1.png)
I created the Babble Buds puppet editor and ported the rendering library I wrote for it to C# so it could be used in Unity. Dice Armor has a full campaign using cutscenes made using the Babble Buds cutscene editor, taking advantage of its support for custom commands and fields to control things like talking, giving the player dice and money, starting duels, and controlling player progression through the story.
![Action Wheel](./da6.png)
When a cutscene ends, its final command is to either start a duel or set the next cutscene in the story. In the latter case, there is an additional field for what to call the next cutscene, and what location it takes place. The cutscene is then added to the player's save file, and when they visit the city locations are greyed out until they have at least one action to do there. Each location has a dynamically populated action wheel with a custom range of acceptable angles.
![Shop](./da7.png)
The dice shop is dynamically populated by a list of dice available to the player, which can be changed during cutscenes, and is checked against the dice owned by the player to generate sold-out indicators. On the left, the player can choose to filter the options down to a single dice effect, which also updates the "Buy All" button to buy only all the dice in the current filter.
![Inventory](./da8.png)
The inventory works most the same as the shop, but for equipping dice. It also allows you to drag individual dice or entire sets to the equipped dice glyph. While dragging it will highlight all the slots the new dice will be equipped into.
![Dice Rolling](./da3.png)
The dice rolling uses the physics engine and detects once the dice have stopped moving, then determines which side is face up based on which of the normals is closest to straight up. It flags the die as cocked if that smallest angle is above a threshold. The dice sink into the table when not rolling to not interfere with any dice that are rolling.
![Missile Storm](./da9.png)
During certain events like winning the game or having the face of a die broken, the players' portraits will flash an emotion for a second. After winning, a random living die from the winning player is chosen to play their "finisher move", a flashy and dramatic effect to end the game. Shown is the arcane mechana's finisher, "Missile Storm".

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

View file

@ -1,7 +0,0 @@
---
title: Projects
lastUpdated: false
---
# Games and Tools
Check out the various games and tools I've made or worked on in the sidebar!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View file

@ -1,26 +0,0 @@
---
title: OptiSpeech
---
# OptiSpeech
The Optispeech project involves designing and testing a real-time tongue model that can be viewed in a transparent head while a subject talks — for the purposes of treating speech errors and teaching foreign language sounds. This work has been conducted in partnership with Vulintus and with support from the National Institutes of Health (NIH). The UT Dallas Speech Production Lab is currently updating the program to use updated versions of Unity and adding support for more features and hardware.
![System Architecture](./system-architecture-600.jpg)
<iframe width="560" height="315" src="https://www.youtube.com/embed/9uHqIRs7ZjM" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="display: block; margin: auto;"></iframe>
This video shows a talker with WAVE sensors placed on the tongue hitting a virtual target sphere located at the alveolar ridge. When an alveolar consonant is hit (e.g., /s/, /n/, /d/) the sphere changes color from red to green.
<iframe width="560" height="315" src="https://www.youtube.com/embed/Oz42mKvlzqI" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen style="display: block; margin: auto;"></iframe>
This video shows an American talker learning a novel sound not found in English. When the post-alveolar consonant is hit, the target sphere changes color from red to green. Here, the NDI WAVE system serves as input.
The program is being updated by a team in the UT Dallas Speech Production Lab led by Anthony Lawn, so the program uses a more modern version of Unity, has an easier-to-use interface, can more easily support new features, and can connect to additional EMA systems, namely the Carstens AG501.
![New Interface](./new-interface.png)
In addition, the program now includes documentation and unit tests to improve program stability and maintainability going forward.
![Documentation](./documentation.png)
![Unit Tests](./unittests.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

View file

@ -1,18 +0,0 @@
---
title: V-ecs
---
# V-ecs
![V-ecs Screenshot](./screenshot.png)
V-ecs (pronounced "Vex") is a Vulkan-based engine I made for making highly moddable games and tools in Lua centered around the ECS design pattern and a work-stealing job system.
The engine works with "worlds", which are collections of systems and renderers. The engine comes with several worlds using systems and renderers I made, including a voxel world, an incremental game, and some test scenes. All of these include systems to render the fps as well as show a debug console by typing the grave key (\`). The default world is a title screen that detects any worlds in the "worlds" folder and displays a button for each of them.
![Debug Menu](./debug.png)
The original plans were to eventually put it on the steam workshop so people could more easily share their creations amongst each other, but I never became happy enough with the performance of the engine - the parallelization of the lua code involved a lot of overhead that severely limited performance.
Instead, I made a couple of worlds by myself - an infinite procedurally generated voxel world, a simple incremental game, and a more complex incremental game I call "Sands of Time".
![Sands of Time](./sandsoftime.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 558 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB