Merge branch 'develop'

This commit is contained in:
2026-01-31 19:25:16 -06:00
33 changed files with 16558 additions and 192 deletions

Binary file not shown.

BIN
critterfolio/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

56
critterfolio/icon.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 269 KiB

70
critterfolio/index.html Normal file
View File

@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Critterfolio</title>
<link rel="icon" href="icon.svg">
<style>
body {
margin: 0;
padding: 0;
background-color: #f9f9f9;
color: #333;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
text-align: center;
padding: 2rem;
max-width: 400px;
width: 90%;
}
.logo {
width: 200px;
height: auto;
margin-bottom: 1.5rem;
}
h1 {
font-size: 3rem;
margin-bottom: 2rem;
font-weight: 700;
}
h2 {
font-size: 2rem;
margin-bottom: 2rem;
font-weight: 500;
}
.download-button {
display: inline-block;
background-color: #007AFF;
color: white;
padding: 12px 24px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
transition: background-color 0.2s ease;
}
.download-button:hover {
background-color: #005ecb;
}
</style>
</head>
<body>
<div class="container">
<h1>CritterFolio</h1>
<img src="icon.svg" alt="Critterfolio Logo" class="logo">
<h3>A simple application for managing all of your critters!</h3>
<h2>Downloads</h1>
<a href="critterfolio.apk" class="download-button">Download for Android</a>
</div>
</body>
</html>

172
css/main.css Normal file
View File

@@ -0,0 +1,172 @@
:root {
--color-background: #0f0f0f;
--color-foreground: #dd0000;
--frame-spacing: 15px;
--frame-border-radius: 10px;
--main-view-width: 80vw;
}
* {
margin: 0;
padding: 0;
font-family: monospace;
}
html {
background-color: var(--color-background);
color: var(--color-foreground);
}
#page-heading {
font-size: 30pt;
text-align: center;
font-style: italic;
letter-spacing: 0.10em;
}
@media (max-width: 900px) {
#page-heading {
font-size: 20pt;
}
}
#main {
margin-top: 20px;
margin-bottom: 100px;
width: var(--main-view-width);
display: flex;
flex-wrap: wrap;
align-items: stretch;
padding: 20px;
gap: var(--frame-spacing);
margin-inline: auto;
}
#main > * {
flex: 1 1 auto;
}
#tui-status-bar {
width: 100vw;
border-top: 2px solid var(--color-foreground);
position: fixed;
left: 0;
bottom: 0;
background-color: var(--color-background);
color: var(--color-foreground);
padding: 20px;
max-height: 30px;
display: flex;
}
#tui-status-bar p {
margin-left: 10px;
margin-right: 10px;
}
#tui-status-bar p::before {
content: ':';
margin-right: 3px;
}
/* TUI FRAME */
.tui-frame {
width: auto;
height: auto;
padding: 20px;
border: 2px solid var(--color-foreground);
border-radius: var(--frame-border-radius);
position: relative;
display: flex;
}
.tui-frame-header {
position: absolute;
top: 0;
left: 20px;
transform: translateY(-50%);
background-color: var(--color-background);
padding: 0 10px;
text-transform: uppercase;
letter-spacing: 0.15em;
}
.tui-frame-content * {
font-size: 14pt;
/* display: block; */
}
.tui-frame-content p {
margin-bottom: 30px;
}
.tui-frame-content h2 {
font-size: 19pt;
font-style: italic;
letter-spacing: 0.15em;
text-transform: uppercase;
margin-top: 20px;
}
.tui-frame-content h2::before {
content: '// ';
}
.tui-frame-content li {
display: flex;
align-items: center;
margin-top: 5px;
align-items: baseline;
}
.tui-frame-content li::before {
content: "*";
display: inline;
margin-right: 5px;
}
.tui-frame-single-image {
display: block;
margin: auto;
width: 80%;
}
.tui-frame-section {
display: flex;
flex-direction: column;
flex-wrap: wrap;
margin-bottom: 10px;
gap: 10px;
}
.tui-frame-section * {
margin-right: 10px;
}
/* CONTROLS */
button {
color: var(--color-foreground);
background-color: transparent;
border: 2px solid var(--color-foreground);
padding: 5px;
text-decoration: underline;
}
button:hover {
transform: scale(1.05, 1.05);
}
a {
text-decoration: underline;
color: var(--color-foreground);
font-style: italic;
display: inline !important;
}
input {
color: var(--color-foreground);
background-color: transparent;
border: 2px solid var(--color-foreground);
padding: 5px;
text-decoration: underline;
}

9
dev/.htaccess-backup Normal file
View File

@@ -0,0 +1,9 @@
RewriteEngine On
# Allow direct access to existing files and folders
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Redirect all other requests to index.html
RewriteRule ^.*$ /dev/interactive-angular-map/index.html [L]

View File

@@ -0,0 +1,146 @@
/*! coi-serviceworker v0.1.7 - Guido Zuidhof and contributors, licensed under MIT */
let coepCredentialless = false;
if (typeof window === 'undefined') {
self.addEventListener("install", () => self.skipWaiting());
self.addEventListener("activate", (event) => event.waitUntil(self.clients.claim()));
self.addEventListener("message", (ev) => {
if (!ev.data) {
return;
} else if (ev.data.type === "deregister") {
self.registration
.unregister()
.then(() => {
return self.clients.matchAll();
})
.then(clients => {
clients.forEach((client) => client.navigate(client.url));
});
} else if (ev.data.type === "coepCredentialless") {
coepCredentialless = ev.data.value;
}
});
self.addEventListener("fetch", function (event) {
const r = event.request;
if (r.cache === "only-if-cached" && r.mode !== "same-origin") {
return;
}
const request = (coepCredentialless && r.mode === "no-cors")
? new Request(r, {
credentials: "omit",
})
: r;
event.respondWith(
fetch(request)
.then((response) => {
if (response.status === 0) {
return response;
}
const newHeaders = new Headers(response.headers);
newHeaders.set("Cross-Origin-Embedder-Policy",
coepCredentialless ? "credentialless" : "require-corp"
);
if (!coepCredentialless) {
newHeaders.set("Cross-Origin-Resource-Policy", "cross-origin");
}
newHeaders.set("Cross-Origin-Opener-Policy", "same-origin");
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: newHeaders,
});
})
.catch((e) => console.error(e))
);
});
} else {
(() => {
const reloadedBySelf = window.sessionStorage.getItem("coiReloadedBySelf");
window.sessionStorage.removeItem("coiReloadedBySelf");
const coepDegrading = (reloadedBySelf == "coepdegrade");
// You can customize the behavior of this script through a global `coi` variable.
const coi = {
shouldRegister: () => !reloadedBySelf,
shouldDeregister: () => false,
coepCredentialless: () => true,
coepDegrade: () => true,
doReload: () => window.location.reload(),
quiet: false,
...window.coi
};
const n = navigator;
const controlling = n.serviceWorker && n.serviceWorker.controller;
// Record the failure if the page is served by serviceWorker.
if (controlling && !window.crossOriginIsolated) {
window.sessionStorage.setItem("coiCoepHasFailed", "true");
}
const coepHasFailed = window.sessionStorage.getItem("coiCoepHasFailed");
if (controlling) {
// Reload only on the first failure.
const reloadToDegrade = coi.coepDegrade() && !(
coepDegrading || window.crossOriginIsolated
);
n.serviceWorker.controller.postMessage({
type: "coepCredentialless",
value: (reloadToDegrade || coepHasFailed && coi.coepDegrade())
? false
: coi.coepCredentialless(),
});
if (reloadToDegrade) {
!coi.quiet && console.log("Reloading page to degrade COEP.");
window.sessionStorage.setItem("coiReloadedBySelf", "coepdegrade");
coi.doReload("coepdegrade");
}
if (coi.shouldDeregister()) {
n.serviceWorker.controller.postMessage({ type: "deregister" });
}
}
// If we're already coi: do nothing. Perhaps it's due to this script doing its job, or COOP/COEP are
// already set from the origin server. Also if the browser has no notion of crossOriginIsolated, just give up here.
if (window.crossOriginIsolated !== false || !coi.shouldRegister()) return;
if (!window.isSecureContext) {
!coi.quiet && console.log("COOP/COEP Service Worker not registered, a secure context is required.");
return;
}
// In some environments (e.g. Firefox private mode) this won't be available
if (!n.serviceWorker) {
!coi.quiet && console.error("COOP/COEP Service Worker not registered, perhaps due to private mode.");
return;
}
n.serviceWorker.register(window.document.currentScript.src).then(
(registration) => {
!coi.quiet && console.log("COOP/COEP Service Worker registered", registration.scope);
registration.addEventListener("updatefound", () => {
!coi.quiet && console.log("Reloading page to make use of updated COOP/COEP Service Worker.");
window.sessionStorage.setItem("coiReloadedBySelf", "updatefound");
coi.doReload();
});
// If the registration is active, but it's not controlling the page
if (registration.active && !n.serviceWorker.controller) {
!coi.quiet && console.log("Reloading page to make use of COOP/COEP Service Worker.");
window.sessionStorage.setItem("coiReloadedBySelf", "notcontrolling");
coi.doReload();
}
},
(err) => {
!coi.quiet && console.error("COOP/COEP Service Worker failed to register:", err);
}
);
})();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,213 @@
/**************************************************************************/
/* audio.worklet.js */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
class RingBuffer {
constructor(p_buffer, p_state, p_threads) {
this.buffer = p_buffer;
this.avail = p_state;
this.threads = p_threads;
this.rpos = 0;
this.wpos = 0;
}
data_left() {
return this.threads ? Atomics.load(this.avail, 0) : this.avail;
}
space_left() {
return this.buffer.length - this.data_left();
}
read(output) {
const size = this.buffer.length;
let from = 0;
let to_write = output.length;
if (this.rpos + to_write > size) {
const high = size - this.rpos;
output.set(this.buffer.subarray(this.rpos, size));
from = high;
to_write -= high;
this.rpos = 0;
}
if (to_write) {
output.set(this.buffer.subarray(this.rpos, this.rpos + to_write), from);
}
this.rpos += to_write;
if (this.threads) {
Atomics.add(this.avail, 0, -output.length);
Atomics.notify(this.avail, 0);
} else {
this.avail -= output.length;
}
}
write(p_buffer) {
const to_write = p_buffer.length;
const mw = this.buffer.length - this.wpos;
if (mw >= to_write) {
this.buffer.set(p_buffer, this.wpos);
this.wpos += to_write;
if (mw === to_write) {
this.wpos = 0;
}
} else {
const high = p_buffer.subarray(0, mw);
const low = p_buffer.subarray(mw);
this.buffer.set(high, this.wpos);
this.buffer.set(low);
this.wpos = low.length;
}
if (this.threads) {
Atomics.add(this.avail, 0, to_write);
Atomics.notify(this.avail, 0);
} else {
this.avail += to_write;
}
}
}
class GodotProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.threads = false;
this.running = true;
this.lock = null;
this.notifier = null;
this.output = null;
this.output_buffer = new Float32Array();
this.input = null;
this.input_buffer = new Float32Array();
this.port.onmessage = (event) => {
const cmd = event.data['cmd'];
const data = event.data['data'];
this.parse_message(cmd, data);
};
}
process_notify() {
if (this.notifier) {
Atomics.add(this.notifier, 0, 1);
Atomics.notify(this.notifier, 0);
}
}
parse_message(p_cmd, p_data) {
if (p_cmd === 'start' && p_data) {
const state = p_data[0];
let idx = 0;
this.threads = true;
this.lock = state.subarray(idx, ++idx);
this.notifier = state.subarray(idx, ++idx);
const avail_in = state.subarray(idx, ++idx);
const avail_out = state.subarray(idx, ++idx);
this.input = new RingBuffer(p_data[1], avail_in, true);
this.output = new RingBuffer(p_data[2], avail_out, true);
} else if (p_cmd === 'stop') {
this.running = false;
this.output = null;
this.input = null;
this.lock = null;
this.notifier = null;
} else if (p_cmd === 'start_nothreads') {
this.output = new RingBuffer(p_data[0], p_data[0].length, false);
} else if (p_cmd === 'chunk') {
this.output.write(p_data);
}
}
static array_has_data(arr) {
return arr.length && arr[0].length && arr[0][0].length;
}
process(inputs, outputs, parameters) {
if (!this.running) {
return false; // Stop processing.
}
if (this.output === null) {
return true; // Not ready yet, keep processing.
}
const process_input = GodotProcessor.array_has_data(inputs);
if (process_input) {
const input = inputs[0];
const chunk = input[0].length * input.length;
if (this.input_buffer.length !== chunk) {
this.input_buffer = new Float32Array(chunk);
}
if (!this.threads) {
GodotProcessor.write_input(this.input_buffer, input);
this.port.postMessage({ 'cmd': 'input', 'data': this.input_buffer });
} else if (this.input.space_left() >= chunk) {
GodotProcessor.write_input(this.input_buffer, input);
this.input.write(this.input_buffer);
} else {
this.port.postMessage('Input buffer is full! Skipping input frame.');
}
}
const process_output = GodotProcessor.array_has_data(outputs);
if (process_output) {
const output = outputs[0];
const chunk = output[0].length * output.length;
if (this.output_buffer.length !== chunk) {
this.output_buffer = new Float32Array(chunk);
}
if (this.output.data_left() >= chunk) {
this.output.read(this.output_buffer);
GodotProcessor.write_output(output, this.output_buffer);
if (!this.threads) {
this.port.postMessage({ 'cmd': 'read', 'data': chunk });
}
} else {
this.port.postMessage('Output buffer has not enough frames! Skipping output frame.');
}
}
this.process_notify();
return true;
}
static write_output(dest, source) {
const channels = dest.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < dest[ch].length; sample++) {
dest[ch][sample] = source[sample * channels + ch];
}
}
}
static write_input(dest, source) {
const channels = source.length;
for (let ch = 0; ch < channels; ch++) {
for (let sample = 0; sample < source[ch].length; sample++) {
dest[sample * channels + ch] = source[ch][sample];
}
}
}
}
registerProcessor('godot-processor', GodotProcessor);

249
dev/blight/index.html Normal file
View File

@@ -0,0 +1,249 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no">
<title>Let There Be Blight</title>
<style>
body {
touch-action: none;
margin: 0;
border: 0 none;
padding: 0;
text-align: center;
background-color: black;
}
#canvas {
display: block;
margin: 0;
color: white;
}
#canvas:focus {
outline: none;
}
.godot {
font-family: 'Noto Sans', 'Droid Sans', Arial, sans-serif;
color: #e0e0e0;
background-color: #3b3943;
background-image: linear-gradient(to bottom, #403e48, #35333c);
border: 1px solid #45434e;
box-shadow: 0 0 1px 1px #2f2d35;
}
/* Status display */
#status {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
display: flex;
justify-content: center;
align-items: center;
/* don't consume click events - make children visible explicitly */
visibility: hidden;
}
#status-progress {
width: 366px;
height: 7px;
background-color: #38363A;
border: 1px solid #444246;
padding: 1px;
box-shadow: 0 0 2px 1px #1B1C22;
border-radius: 2px;
visibility: visible;
}
@media only screen and (orientation:portrait) {
#status-progress {
width: 61.8%;
}
}
#status-progress-inner {
height: 100%;
width: 0;
box-sizing: border-box;
transition: width 0.5s linear;
background-color: #202020;
border: 1px solid #222223;
box-shadow: 0 0 1px 1px #27282E;
border-radius: 3px;
}
#status-indeterminate {
height: 42px;
visibility: visible;
position: relative;
}
#status-indeterminate > div {
width: 4.5px;
height: 0;
border-style: solid;
border-width: 9px 3px 0 3px;
border-color: #2b2b2b transparent transparent transparent;
transform-origin: center 21px;
position: absolute;
}
#status-indeterminate > div:nth-child(1) { transform: rotate( 22.5deg); }
#status-indeterminate > div:nth-child(2) { transform: rotate( 67.5deg); }
#status-indeterminate > div:nth-child(3) { transform: rotate(112.5deg); }
#status-indeterminate > div:nth-child(4) { transform: rotate(157.5deg); }
#status-indeterminate > div:nth-child(5) { transform: rotate(202.5deg); }
#status-indeterminate > div:nth-child(6) { transform: rotate(247.5deg); }
#status-indeterminate > div:nth-child(7) { transform: rotate(292.5deg); }
#status-indeterminate > div:nth-child(8) { transform: rotate(337.5deg); }
#status-notice {
margin: 0 100px;
line-height: 1.3;
visibility: visible;
padding: 4px 6px;
visibility: visible;
}
</style>
<link id='-gd-engine-icon' rel='icon' type='image/png' href='index.icon.png' />
<link rel='apple-touch-icon' href='index.apple-touch-icon.png'/>
</head>
<body>
<script src="coi-serviceworker.js"></script>
<canvas id="canvas">
HTML5 canvas appears to be unsupported in the current browser.<br >
Please try updating or use a different browser.
</canvas>
<div id="status">
<div id="status-progress" style="display: none;" oncontextmenu="event.preventDefault();">
<div id ="status-progress-inner"></div>
</div>
<div id="status-indeterminate" style="display: none;" oncontextmenu="event.preventDefault();">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
<div id="status-notice" class="godot" style="display: none;"></div>
</div>
<script src="index.js"></script>
<script>
const GODOT_CONFIG = {"args":[],"canvasResizePolicy":2,"executable":"index","experimentalVK":false,"fileSizes":{"index.pck":20558368,"index.wasm":49219187},"focusCanvas":true,"gdextensionLibs":[]};
const engine = new Engine(GODOT_CONFIG);
(function () {
const INDETERMINATE_STATUS_STEP_MS = 100;
const statusProgress = document.getElementById('status-progress');
const statusProgressInner = document.getElementById('status-progress-inner');
const statusIndeterminate = document.getElementById('status-indeterminate');
const statusNotice = document.getElementById('status-notice');
let initializing = true;
let statusMode = 'hidden';
let animationCallbacks = [];
function animate(time) {
animationCallbacks.forEach((callback) => callback(time));
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
function animateStatusIndeterminate(ms) {
const i = Math.floor((ms / INDETERMINATE_STATUS_STEP_MS) % 8);
if (statusIndeterminate.children[i].style.borderTopColor === '') {
Array.prototype.slice.call(statusIndeterminate.children).forEach((child) => {
child.style.borderTopColor = '';
});
statusIndeterminate.children[i].style.borderTopColor = '#dfdfdf';
}
}
function setStatusMode(mode) {
if (statusMode === mode || !initializing) {
return;
}
[statusProgress, statusIndeterminate, statusNotice].forEach((elem) => {
elem.style.display = 'none';
});
animationCallbacks = animationCallbacks.filter(function (value) {
return (value !== animateStatusIndeterminate);
});
switch (mode) {
case 'progress':
statusProgress.style.display = 'block';
break;
case 'indeterminate':
statusIndeterminate.style.display = 'block';
animationCallbacks.push(animateStatusIndeterminate);
break;
case 'notice':
statusNotice.style.display = 'block';
break;
case 'hidden':
break;
default:
throw new Error('Invalid status mode');
}
statusMode = mode;
}
function setStatusNotice(text) {
while (statusNotice.lastChild) {
statusNotice.removeChild(statusNotice.lastChild);
}
const lines = text.split('\n');
lines.forEach((line) => {
statusNotice.appendChild(document.createTextNode(line));
statusNotice.appendChild(document.createElement('br'));
});
}
function displayFailureNotice(err) {
const msg = err.message || err;
console.error(msg);
setStatusNotice(msg);
setStatusMode('notice');
initializing = false;
}
const missing = Engine.getMissingFeatures();
if (missing.length !== 0) {
const missingMsg = 'Error\nThe following features required to run Godot projects on the Web are missing:\n';
displayFailureNotice(missingMsg + missing.join('\n'));
} else {
setStatusMode('indeterminate');
engine.startGame({
'onProgress': function (current, total) {
if (total > 0) {
statusProgressInner.style.width = `${(current / total) * 100}%`;
setStatusMode('progress');
if (current === total) {
// wait for progress bar animation
setTimeout(() => {
setStatusMode('indeterminate');
}, 500);
}
} else {
setStatusMode('indeterminate');
}
},
}).then(() => {
setStatusMode('hidden');
initializing = false;
}, displayFailureNotice);
}
}());
</script>
</body>
</html>

BIN
dev/blight/index.icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

14477
dev/blight/index.js Normal file

File diff suppressed because one or more lines are too long

BIN
dev/blight/index.pck Normal file

Binary file not shown.

BIN
dev/blight/index.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
dev/blight/index.wasm Normal file

Binary file not shown.

161
dev/blight/index.worker.js Normal file
View File

@@ -0,0 +1,161 @@
/**
* @license
* Copyright 2015 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/
// Pthread Web Worker startup routine:
// This is the entry point file that is loaded first by each Web Worker
// that executes pthreads on the Emscripten application.
'use strict';
var Module = {};
// Thread-local guard variable for one-time init of the JS state
var initializedJS = false;
function assert(condition, text) {
if (!condition) abort('Assertion failed: ' + text);
}
function threadPrintErr() {
var text = Array.prototype.slice.call(arguments).join(' ');
console.error(text);
}
function threadAlert() {
var text = Array.prototype.slice.call(arguments).join(' ');
postMessage({cmd: 'alert', text: text, threadId: Module['_pthread_self']()});
}
// We don't need out() for now, but may need to add it if we want to use it
// here. Or, if this code all moves into the main JS, that problem will go
// away. (For now, adding it here increases code size for no benefit.)
var out = () => { throw 'out() is not defined in worker.js.'; }
var err = threadPrintErr;
self.alert = threadAlert;
Module['instantiateWasm'] = (info, receiveInstance) => {
// Instantiate from the module posted from the main thread.
// We can just use sync instantiation in the worker.
var module = Module['wasmModule'];
// We don't need the module anymore; new threads will be spawned from the main thread.
Module['wasmModule'] = null;
var instance = new WebAssembly.Instance(module, info);
// TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193,
// the above line no longer optimizes out down to the following line.
// When the regression is fixed, we can remove this if/else.
return receiveInstance(instance);
}
// Turn unhandled rejected promises into errors so that the main thread will be
// notified about them.
self.onunhandledrejection = (e) => {
throw e.reason ?? e;
};
function handleMessage(e) {
try {
if (e.data.cmd === 'load') { // Preload command that is called once per worker to parse and load the Emscripten code.
// Until we initialize the runtime, queue up any further incoming messages.
let messageQueue = [];
self.onmessage = (e) => messageQueue.push(e);
// And add a callback for when the runtime is initialized.
self.startWorker = (instance) => {
Module = instance;
// Notify the main thread that this thread has loaded.
postMessage({ 'cmd': 'loaded' });
// Process any messages that were queued before the thread was ready.
for (let msg of messageQueue) {
handleMessage(msg);
}
// Restore the real message handler.
self.onmessage = handleMessage;
};
// Module and memory were sent from main thread
Module['wasmModule'] = e.data.wasmModule;
// Use `const` here to ensure that the variable is scoped only to
// that iteration, allowing safe reference from a closure.
for (const handler of e.data.handlers) {
Module[handler] = function() {
postMessage({ cmd: 'callHandler', handler, args: [...arguments] });
}
}
Module['wasmMemory'] = e.data.wasmMemory;
Module['buffer'] = Module['wasmMemory'].buffer;
Module['workerID'] = e.data.workerID;
Module['ENVIRONMENT_IS_PTHREAD'] = true;
if (typeof e.data.urlOrBlob == 'string') {
importScripts(e.data.urlOrBlob);
} else {
var objectUrl = URL.createObjectURL(e.data.urlOrBlob);
importScripts(objectUrl);
URL.revokeObjectURL(objectUrl);
}
Godot(Module);
} else if (e.data.cmd === 'run') {
// Pass the thread address to wasm to store it for fast access.
Module['__emscripten_thread_init'](e.data.pthread_ptr, /*isMainBrowserThread=*/0, /*isMainRuntimeThread=*/0, /*canBlock=*/1);
// Await mailbox notifications with `Atomics.waitAsync` so we can start
// using the fast `Atomics.notify` notification path.
Module['__emscripten_thread_mailbox_await'](e.data.pthread_ptr);
assert(e.data.pthread_ptr);
// Also call inside JS module to set up the stack frame for this pthread in JS module scope
Module['establishStackSpace']();
Module['PThread'].receiveObjectTransfer(e.data);
Module['PThread'].threadInitTLS();
if (!initializedJS) {
initializedJS = true;
}
try {
Module['invokeEntryPoint'](e.data.start_routine, e.data.arg);
} catch(ex) {
if (ex != 'unwind') {
// The pthread "crashed". Do not call `_emscripten_thread_exit` (which
// would make this thread joinable). Instead, re-throw the exception
// and let the top level handler propagate it back to the main thread.
throw ex;
}
}
} else if (e.data.cmd === 'cancel') { // Main thread is asking for a pthread_cancel() on this thread.
if (Module['_pthread_self']()) {
Module['__emscripten_thread_exit'](-1);
}
} else if (e.data.target === 'setimmediate') {
// no-op
} else if (e.data.cmd === 'checkMailbox') {
if (initializedJS) {
Module['checkMailbox']();
}
} else if (e.data.cmd) {
// The received message looks like something that should be handled by this message
// handler, (since there is a e.data.cmd field present), but is not one of the
// recognized commands:
err('worker.js received unknown command ' + e.data.cmd);
err(e.data);
}
} catch(ex) {
err('worker.js onmessage() captured an uncaught exception: ' + ex);
if (ex && ex.stack) err(ex.stack);
if (Module['__emscripten_thread_crashed']) {
Module['__emscripten_thread_crashed']();
}
throw ex;
}
};
self.onmessage = handleMessage;

View File

@@ -0,0 +1,9 @@
RewriteEngine On
# Allow direct access to existing files and folders
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [L]
# Redirect all other requests to index.html
RewriteRule ^.*$ /dev/interactive-angular-map/index.html [L]

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en" data-beasties-container>
<head>
<meta charset="utf-8">
<title>Interactive Map</title>
<base href="/dev/interactive-angular-map/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<style>body,html{margin:0;padding:0}</style><link rel="stylesheet" href="styles-U3BIK65T.css" media="print" onload="this.media='all'"><noscript><link rel="stylesheet" href="styles-U3BIK65T.css"></noscript></head>
<body>
<app-root></app-root>
<script src="polyfills-FFHMD2TL.js" type="module"></script><script src="main-4Z6S77FU.js" type="module"></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
body,html{margin:0;padding:0}

156
dev/stars/index.html Normal file
View File

@@ -0,0 +1,156 @@
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<title></title>
<style>
body{
background-color: #1f1e27;
margin: 0;
width: 100%;
height: 100%;
}
#myCanvas {
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
#valuesForm {
z-index: 2;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 300px;
text-align: center;
}
#valuesForm input, #valuesForm button {
display: block;
margin-left: auto;
margin-right: auto;
margin-top: 50px;
width: 50%;
background-color: rgba(0, 0, 0, 0);
color: #71627a;
text-align: center;
outline: #71627a 2px solid;
border-radius: 2px;
padding: 5px;
font-size: 1.5em;
overflow: hidden;
transition: outline-offset 0.5s;
border: none;
font-family: monospace;
}
#valuesForm button:focus, #valuesForm button:hover, #valuesForm input:focus, #valuesForm input:hover {
transition: outline-offset 0.5s;
outline-offset: 10px;
}
#valuesForm h1 {
color: #71627a;
}
</style>
</head>
<body>
<form id="valuesForm">
<h1>STARS</h1>
<input type="text" id="amtInput" placeholder="Amount">
<input type="text" id="maxSizeInput" placeholder="Max Size">
<button type="submit">Redraw</button>
</form>
<canvas id="myCanvas"></canvas>
<script>
const form = document.getElementById("valuesForm");
form.addEventListener("submit", function(event)
{
event.preventDefault();
amount = document.getElementById("amtInput").value;
maxSize = document.getElementById("maxSizeInput").value;
Draw();
});
const c = document.getElementById("myCanvas");
const ctx = c.getContext("2d");
var amount = 2000;
var maxSize = 5;
var boundsX;
var boundsY;
function Setup()
{
c.width = window.innerWidth;
c.height = window.innerHeight;
boundsX = c.width;
boundsY = c.height;
}
function Draw()
{
Setup();
for (i=0; i<amount; i++)
{
size = rand(maxSize);
MakeRect(rand(boundsX), rand(boundsY), size, size);
}
}
function MakeRect(posX, posY, sizeX, sizeY)
{
ctx.beginPath();
ctx.fillStyle = "#71627a";
ctx.fillRect(posX, posY, sizeX, sizeY);
ctx.stroke();
}
function rand(max)
{
return Math.floor(Math.random() * max);
}
function resize()
{
var canvas = document.getElementById('myCanvas');
var canvasRatio = canvas.height / canvas.width;
var windowRatio = window.innerHeight / window.innerWidth;
var width;
var height;
if (windowRatio < canvasRatio) {
height = window.innerHeight;
width = height / canvasRatio;
} else {
width = window.innerWidth;
height = width * canvasRatio;
}
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
Draw();
}
window.addEventListener('resize', resize, false);
Draw();
</script>
</body>
</html>

177
index.html Executable file → Normal file
View File

@@ -1,101 +1,126 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/hex-logo.animate.css">
<link rel="stylesheet" href="css/master.css">
<title>Hex Studios</title>
<link rel="stylesheet" href="css/main.css">
<script type="module" src="scripts/main.js"></script>
<title>// HEX STUDIOS //</title>
</head>
<body>
<div id="header">
<div id="hex-svg">
<svg width="400" height="400" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="hex-logo">
<g id="hex-logo-h">
<path id="rect850" d="M15.7479 34.5518H11.5634V62.4482H15.7479V34.5518Z" fill="black"/>
<path id="rect852" d="M22.722 50.5923V46.4078H11.5634V50.5923H22.722Z" fill="black"/>
<path id="rect854" d="M30.8119 34.5517H26.6274V62.4481H30.8119V34.5517Z" fill="black"/>
</g>
<g id="hex-logo-e">
<path id="rect856" d="M40.065 34.5518H35.8805V62.4482H40.065V34.5518Z" fill="black"/>
<path id="rect858" d="M51.2235 50.5923V46.4078H42.8546V50.5923H51.2235Z" fill="black"/>
<path id="rect860" d="M51.2235 38.7363V34.5518H35.8805V38.7363H51.2235Z" fill="black"/>
<path id="rect864" d="M51.2235 62.4482V58.2637H35.8805V62.4482H51.2235Z" fill="black"/>
</g>
<path id="hex-logo-hexagon" d="M62.8316 61.5916C62.6287 61.2394 61.3368 59.0007 59.9609 56.6166C57.5437 52.4282 55.71 49.2333 55.4309 48.7242L55.2963 48.4785L55.5171 48.0747C55.7907 47.5738 56.1847 46.8861 57.8597 43.9856C59.0777 41.8765 63.0011 35.0864 63.1156 34.8896L63.1651 34.8042L67.1282 34.7815C69.3078 34.7702 72.8948 34.7626 75.0992 34.7702L79.1072 34.7815L80.2735 36.8109C80.915 37.9271 82.5723 40.8034 83.9565 43.2027C86.4771 47.5721 86.9074 48.3293 86.9395 48.4529C86.9584 48.5258 86.7985 48.827 86.0349 50.1569C85.5828 50.9448 80.6853 59.4294 79.6327 61.2485L79.0862 62.1929L75.4324 62.2155C73.4228 62.2268 69.8486 62.2382 67.4897 62.2344L63.2009 62.2335L62.8316 61.5916ZM78.5288 61.0404C78.5666 60.9735 79.6892 59.0267 81.0255 56.714C82.3618 54.4013 83.9743 51.6106 84.6088 50.5123C85.2432 49.4141 85.7621 48.5008 85.7619 48.4829C85.7616 48.4602 80.94 40.0826 78.7059 36.2251L78.4897 35.8516L71.1537 35.829C67.119 35.8177 63.8084 35.8177 63.797 35.829C63.7857 35.8404 62.4908 38.0789 60.9197 40.8024C59.3486 43.5258 57.7105 46.3648 57.2794 47.1112C56.8484 47.8576 56.4957 48.4742 56.4957 48.4813C56.4957 48.4888 56.9287 49.2439 57.4579 50.1602C57.987 51.0764 59.5632 53.806 60.9605 56.2259C62.3578 58.6457 63.5717 60.7461 63.6581 60.8934L63.8153 61.1614L71.1387 61.1611L78.4621 61.1609L78.529 61.0396L78.5288 61.0404Z" fill="black" stroke="black" stroke-width="0.986869" stroke-linecap="square" stroke-linejoin="round"/>
<path id="hex-logo-greater" d="M64.63 40.7631L69.5655 47.7179L70.1206 48.5001L69.5655 49.2823L64.63 56.2371L64.0749 55.4549L69.0104 48.5001L64.0749 41.5454L64.63 40.7631Z" fill="black" stroke="black" stroke-width="0.725197"/>
<path id="hex-logo-lesser" d="M77.6163 40.7631L72.6808 47.7179L72.1257 48.5001L72.6808 49.2823L77.6163 56.2371L78.1715 55.4549L73.2359 48.5001L78.1715 41.5454L77.6163 40.7631Z" fill="black" stroke="black" stroke-width="0.725197"/>
<rect id="hex-logo-slash" x="50" y="50" width="1.5" height="13" fill="black"/>
</g>
</svg>
</div>
</div>
<div id="content">
<div id="fader"></div>
<h1 class="content-title">This Is HEX Studios</h1>
<hr class="content-break">
<div id="aboutme">
<div class="aboutme-box">
<div class="aboutme-box-left">
<img src="res/chris2.jpg" alt="">
</div>
<div class="aboutme-box-right">
<div id="main">
<h1 id="page-heading">// HEX STUDIOS //</h1>
<div class="tui-frame">
<h2 class="tui-frame-header">WELCOME</h2>
<div class="tui-frame-content">
<p>Hello! My name is Chris, aka Spudnut2000. Welcome to my personal portfolio site!</p>
<h2>About Me</h2>
<p>Hi! I'm Chris Bell. I am a programmer, musician, and nerd. My passion has been Game Development for over 5 years, using Unreal Engine, Unity, and Godot. I am currently a full-time Unity developer, but have knowledge in C/C++, C#, Java, HTML/CSS, and a few other languages. Most of my projects can be found on either my <a class="hexlink" href="https://github.com/spudnut2000">personal GitHub</a> or my <a class="hexlink" href="https://github.com/HexStudios">Hex Studios GitHub</a> pages.</p>
<p>I am a husband, programmer, musician, and all around nerd. I like to develop both software and games.</p>
<p>I am primarily a Software Engineer by trade with a Bachelor's in Sotware Engineering and ~5 years of professional experience, where the majority of my knowledge is in C#/.NET, XAML, and HTML/CSS. I am also proficient in many other languages like Odin, C/C++, Java, QML, and JavaScript.</p>
</div>
</div>
<div class="tui-frame">
<h2 class="tui-frame-header">IMAGE</h2>
<img class="tui-frame-single-image" src="res/chris2.jpg" />
</div>
<div class="tui-frame">
<h2 class="tui-frame-header">SITE SETTINGS</h2>
<div class="tui-frame-content">
<p>Change site colors, spacing, and borders</p>
<div class="tui-frame-section" id="settings-form">
<label for="foreground-setting">Foreground Color</label>
<input type="text" name="foreground-setting" id="foreground-setting">
<label for="background-setting">Background Color</label>
<input type="text" name="background-setting" id="background-setting">
<label for="frame-spacing-setting">Frame Spacing</label>
<input type="number" name="frame-spacing-setting" id="frame-spacing-setting" min="5" max="50" value="10">
<label for="frame-border-radius-setting">Frame Rounding</label>
<input type="number" name="frame-border-radius-setting" id="frame-border-radius-setting" min="0" max="50" value="10">
<button type="button" id="save-settings-button">SAVE</button>
<button type="button" id="reset-settings-button">RESET</button>
</div>
</div>
</div>
<h1 class="content-title">PROJECTS</h1>
<hr class="content-break">
<div id="projects">
<div class="projects-box">
<!-- Shots in the Dark -->
<div class="card"><div class="upper"><a href="https://github.com/hexstudios/shotsinthedarkremaster"><img src="res/shots.png"></a></div><div class="lower">
<h2>Shots in the Dark - Remastered</h2>
<p>Shots in the dark was a Brackeys Game Jam 2022.2 submission. This is my attempt to remake it from scratch and finally complete it.</p>
</div></div>
<!-- Project 7 -->
<div class="card"><div class="upper"><a href="projectvii.html"><img src="res/project7-card.png"></a></div><div class="lower">
<h2>Project VII</h2>
<p>Project VII is an ongoing story-driven game. Details are not yet disclosed.</p>
</div></div>
<div class="card"><div class="upper"></div><div class="lower">
</div></div>
</div>
</div>
<h1 class="content-title">CONTACT</h1>
<hr class="content-break">
</div>
<div id="contact">
<div class="contact-box">
<div class="tui-frame">
<h2 class="tui-frame-header">credentials</h2>
<div class="tui-frame-content">
<h2>Education</h2>
<ul>
<li>Email: <a class="hexlink" href="mailto:chrisbell@hexstudios.co">chrisbell@hexstudios.co</a></li>
<li>Bachelors, Software Engineering - 2026</li>
<li>Associates, Computer Science - 2022</li>
</ul>
<h2>Experience</h2>
<ul>
<li>Software Engineer (C#, .NET, Unity) - 4 years</li>
<li>Computer Technician (Repair, IT) - 2 years</li>
</ul>
</div>
</div>
<div id="fader-reverse"></div>
<div id="footer">
<p>&copy; Copyright 2022 HEX Studios&trade;</p>
<br>
<div class="footer-nav">
<div class="tui-frame">
<h2 class="tui-frame-header">LINKS</h2>
<div class="tui-frame-content">
<ul>
<li><a class="hexlink" href="#aboutme">About</a></li>
<li><a class="hexlink" href="#projects">Projects</a></li>
<li><a class="hexlink" href="#contact">Contact</a></li>
<li><a class="hexlink" href="https://github.com/HexStudios/hexstudios-co">Page Source</a></li>
<li><a href="https://www.linkedin.com/in/christopher-i-bell/">LinkedIn</a></li>
<li><a href="https://bellsworne.com">Bellsworne</a></li>
<li><a href="https://youtube.com/@hexstudios">Youtube</a></li>
</ul>
</div>
</div>
<div class="tui-frame">
<h2 class="tui-frame-header">projects</h2>
<div class="tui-frame-content">
<h2>SessionZero</h2>
<p>SessionZero is a data-driven TTRPG companion application for managing characters, sessions, and game data.</p>
<p>This is my passion project, and is currently a work in progress. It's core is developed using C# and .NET 8.</p>
<p>Visit <a href="https://sessionzero.app">sessionzero.app</a> to learn more, or <a href="https://git.bellsworne.tech/Bellsworne/sessionzero-cs">view the source code.</a></p>
<h2>Critterfolio</h2>
<p>A simple cross-platform full-stack application for managing your critters, for farms or pet owners. Made in C# with Avalonia UI.</p>
<p>This project was developed as my Software Engineering Capstone project, but the idea came from my wife who breeds goats and needed an easy way to track linage and other notes about her goats.</p>
<p>It still needs some work, but you can download it for Windows, Linux, or Android <a href="critterfolio/index.html">HERE</a>.</p>
</div>
</div>
<!-- <div class="tui-frame">
<h2 class="tui-frame-header">motto</h2>
<p>Say No To AI</p>
</div>
<div class="tui-frame">
<h2 class="tui-frame-header">motto</h2>
<p>Say No To AI</p>
</div> -->
</div>
<div id="tui-status-bar">
<p>COPYRIGHT 2026 <a href="https://bellsworne.com">BELLSWORNE LLC</a></p>
<p><a href="https://git.bellsworne.tech/chrisbell/hexstudios-co">Source Code</a></p>
</div>
</body>
</html>

134
old/index.html Executable file
View File

@@ -0,0 +1,134 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="css/hex-logo.animate.css">
<link rel="stylesheet" href="css/master.css">
<title>Hex Studios</title>
</head>
<body>
<div id="header">
<div id="hex-svg">
<svg width="400" height="400" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="hex-logo">
<g id="hex-logo-h">
<path id="rect850" d="M15.7479 34.5518H11.5634V62.4482H15.7479V34.5518Z" fill="black"/>
<path id="rect852" d="M22.722 50.5923V46.4078H11.5634V50.5923H22.722Z" fill="black"/>
<path id="rect854" d="M30.8119 34.5517H26.6274V62.4481H30.8119V34.5517Z" fill="black"/>
</g>
<g id="hex-logo-e">
<path id="rect856" d="M40.065 34.5518H35.8805V62.4482H40.065V34.5518Z" fill="black"/>
<path id="rect858" d="M51.2235 50.5923V46.4078H42.8546V50.5923H51.2235Z" fill="black"/>
<path id="rect860" d="M51.2235 38.7363V34.5518H35.8805V38.7363H51.2235Z" fill="black"/>
<path id="rect864" d="M51.2235 62.4482V58.2637H35.8805V62.4482H51.2235Z" fill="black"/>
</g>
<path id="hex-logo-hexagon" d="M62.8316 61.5916C62.6287 61.2394 61.3368 59.0007 59.9609 56.6166C57.5437 52.4282 55.71 49.2333 55.4309 48.7242L55.2963 48.4785L55.5171 48.0747C55.7907 47.5738 56.1847 46.8861 57.8597 43.9856C59.0777 41.8765 63.0011 35.0864 63.1156 34.8896L63.1651 34.8042L67.1282 34.7815C69.3078 34.7702 72.8948 34.7626 75.0992 34.7702L79.1072 34.7815L80.2735 36.8109C80.915 37.9271 82.5723 40.8034 83.9565 43.2027C86.4771 47.5721 86.9074 48.3293 86.9395 48.4529C86.9584 48.5258 86.7985 48.827 86.0349 50.1569C85.5828 50.9448 80.6853 59.4294 79.6327 61.2485L79.0862 62.1929L75.4324 62.2155C73.4228 62.2268 69.8486 62.2382 67.4897 62.2344L63.2009 62.2335L62.8316 61.5916ZM78.5288 61.0404C78.5666 60.9735 79.6892 59.0267 81.0255 56.714C82.3618 54.4013 83.9743 51.6106 84.6088 50.5123C85.2432 49.4141 85.7621 48.5008 85.7619 48.4829C85.7616 48.4602 80.94 40.0826 78.7059 36.2251L78.4897 35.8516L71.1537 35.829C67.119 35.8177 63.8084 35.8177 63.797 35.829C63.7857 35.8404 62.4908 38.0789 60.9197 40.8024C59.3486 43.5258 57.7105 46.3648 57.2794 47.1112C56.8484 47.8576 56.4957 48.4742 56.4957 48.4813C56.4957 48.4888 56.9287 49.2439 57.4579 50.1602C57.987 51.0764 59.5632 53.806 60.9605 56.2259C62.3578 58.6457 63.5717 60.7461 63.6581 60.8934L63.8153 61.1614L71.1387 61.1611L78.4621 61.1609L78.529 61.0396L78.5288 61.0404Z" fill="black" stroke="black" stroke-width="0.986869" stroke-linecap="square" stroke-linejoin="round"/>
<path id="hex-logo-greater" d="M64.63 40.7631L69.5655 47.7179L70.1206 48.5001L69.5655 49.2823L64.63 56.2371L64.0749 55.4549L69.0104 48.5001L64.0749 41.5454L64.63 40.7631Z" fill="black" stroke="black" stroke-width="0.725197"/>
<path id="hex-logo-lesser" d="M77.6163 40.7631L72.6808 47.7179L72.1257 48.5001L72.6808 49.2823L77.6163 56.2371L78.1715 55.4549L73.2359 48.5001L78.1715 41.5454L77.6163 40.7631Z" fill="black" stroke="black" stroke-width="0.725197"/>
<rect id="hex-logo-slash" x="50" y="50" width="1.5" height="13" fill="black"/>
</g>
</svg>
</div>
</div>
<div id="content">
<div id="fader"></div>
<h1 class="content-title">This Is HEX Studios</h1>
<hr class="content-break">
<div id="aboutme">
<div class="aboutme-box">
<div class="aboutme-box-left">
<img src="res/chris2.jpg" alt="">
</div>
<div class="aboutme-box-right">
<h2>About Me</h2>
<p>
Hi! I'm Chris Bell. I am a programmer, musician, and nerd. My passion has been Game Development for over 5 years,
using Unreal Engine, Unity, and Godot. I am currently a full-time Unity developer, but have knowledge in C/C++,
C#, Java, HTML/CSS, and a few other languages. Most of my projects can be found on my
<a class="hexlink" href="https://git.bellsworne.tech/chrisbell">Gitea</a> site.
</p>
<br>
<p>
HexStudios is mostly just my personal portfolio, and I currently operate a
company under the name <a class="hexlink" href="https://bellsworne.com">Bellsworne</a>.
Bellsworne has multiple sub-sections like <a class="hexlink" href="https://bellsworne.tech">Bellsworne Tech</a>,
where a lot of my more professional software projects live,
and <a class="hexlink" href="https://bellsworne.com/games">Bellsworne Games</a>
where my future games will be published.
</p>
</div>
</div>
</div>
<h1 class="content-title">PROJECTS</h1>
<hr class="content-break">
<div id="projects">
<div class="projects-box">
<!-- Stars -->
<div class="card"><div class="upper"><a href="dev/stars"><img src="res/stars.png"></a></div>
<div class="lower">
<h2>Stars</h2>
<p>A cool little visualization of randomly generated "stars", made in JS</p>
</div>
</div>
<!-- Let There Be Blight -->
<div class="card"><div class="upper"><a href="dev/blight"><img src="res/blight.png"></a></div>
<div class="lower">
<h2>Let There Be Blight</h2>
<p>A game made in 72 hours for MiniJam #150: Magic, made in Godot</p>
</div>
</div>
<!-- Interactive Map -->
<div class="card"><div class="upper"><a href="dev/interactive-angular-map"><img src="res/map.png"></a></div>
<div class="lower">
<h2>Interactive Map</h2>
<p>An interactive world map made in Angular</p>
</div>
</div>
<!-- ADEPT -->
<!-- <div class="card"><div class="upper"><a href="dev/interactive-angular-map"><img src="res/map.png"></a></div> -->
<!-- <div class="lower"> -->
<!-- <h2>Interactive Map</h2> -->
<!-- <p>An interactive world map made in Angular</p> -->
<!-- </div> -->
<!-- </div> -->
</div>
</div>
<h1 class="content-title">CONTACT</h1>
<hr class="content-break">
</div>
<div id="contact">
<div class="contact-box">
<ul>
<li>Email: <a class="hexlink" href="mailto:chrisbell@hexstudios.co">chrisbell@hexstudios.co</a></li>
</ul>
</div>
</div>
<div id="fader-reverse"></div>
<div id="footer">
<p>&copy; Copyright 2025 HEX Studios&trade;</p>
<br>
<div class="footer-nav">
<ul>
<li><a class="hexlink" href="#aboutme">About</a></li>
<li><a class="hexlink" href="#projects">Projects</a></li>
<li><a class="hexlink" href="#contact">Contact</a></li>
<li><a class="hexlink" href="https://git.bellsworne.tech/chrisbell/hexstudios-co.git">Page Source</a></li>
</ul>
</div>
</div>
</body>
</html>

BIN
res/blight.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

BIN
res/map.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
res/stars.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

96
scripts/main.js Normal file
View File

@@ -0,0 +1,96 @@
const r = document.querySelector(':root');
const fgInput = document.getElementById('foreground-setting');
const bgInput = document.getElementById('background-setting');
const spacingInput = document.getElementById('frame-spacing-setting');
const radiusInput = document.getElementById('frame-border-radius-setting');
const saveBtn = document.getElementById('save-settings-button');
const resetBtn = document.getElementById('reset-settings-button');
let lightMode = false;
const defaultSettings = {
foreground: "#ba1818",
background: "#0f0f0f",
spacing: "15px",
radius: "10px"
};
saveBtn.addEventListener('click', () => {
const currentSettings = {
foreground: fgInput.value,
background: bgInput.value,
spacing: spacingInput.value + 'px',
radius: radiusInput.value + 'px'
};
setForegroundColor(currentSettings.foreground);
setBackgroundColor(currentSettings.background);
setFrameSpacing(currentSettings.spacing);
setFrameBorderRadius(currentSettings.radius);
localStorage.setItem('user-settings', JSON.stringify(currentSettings));
});
resetBtn.addEventListener('click', () => {
localStorage.setItem('user-settings', JSON.stringify(defaultSettings));
loadStoredSettings();
syncSettings();
});
function setForegroundColor(color) {
r.style.setProperty('--color-foreground', color);
}
function setBackgroundColor(color) {
r.style.setProperty('--color-background', color);
}
function setFrameSpacing(amount) {
r.style.setProperty('--frame-spacing', amount);
}
function setFrameBorderRadius(amount) {
r.style.setProperty('--frame-border-radius', amount);
}
function syncSettings() {
const rootStyles = getComputedStyle(document.documentElement);
const varMap = {
'foreground-setting': '--color-foreground',
'background-setting': '--color-background',
'frame-spacing-setting': '--frame-spacing',
'frame-border-radius-setting': '--frame-border-radius'
};
for (const [id, variable] of Object.entries(varMap)) {
const element = document.getElementById(id);
if (element) {
let cssValue = rootStyles.getPropertyValue(variable).trim();
if (element.type === 'number') {
element.value = parseFloat(cssValue) || 0;
} else {
element.value = cssValue;
}
}
}
}
function loadStoredSettings() {
const saved = localStorage.getItem('user-settings');
if (saved) {
const settings = JSON.parse(saved);
setForegroundColor(settings.foreground);
setBackgroundColor(settings.background);
setFrameSpacing(settings.spacing);
setFrameBorderRadius(settings.radius);
}
}
loadStoredSettings();
syncSettings();

111
secret.html Normal file

File diff suppressed because one or more lines are too long