Major update - simpler file structure, better layouts, moar content

This commit is contained in:
Eric Woodward 2022-09-23 02:40:26 -04:00
commit 8c024d74cd
346 changed files with 19555 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
node_modules/
out/
IDEAS.md
TODO.md

2
.prettierignore Normal file
View File

@ -0,0 +1,2 @@
*.ejs
*.min.js

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Douglas Matoso
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.

19
README.md Normal file
View File

@ -0,0 +1,19 @@
# Mystic Site Builder (2021 Edition)
Micro static site generator in Node.js
Based on the ideas in this post: https://medium.com/douglas-matoso-english/build-static-site-generator-nodejs-8969ebe34b22
## Setup
```console
$ npm i
$ npm run build
$ npm run serve
```
Go to http://localhost:5000 to see the generated site.
## How to use
If you want to use NanoGen to generate your own site, just fork this repository and add your content to the `src` folder.

30
app.js Normal file
View File

@ -0,0 +1,30 @@
#!/usr/bin/env node
"use strict";
require("dotenv").config();
const path = require("path"),
build = require("./lib/build"),
serve = require("./lib/serve"),
watch = require("./lib/watch"),
{ log, readJsonIfExists } = require("./lib/utils"),
{ version } = require("./package.json"),
localConfig =
readJsonIfExists(path.resolve(process.cwd(), "site.config.json5")) ||
{},
siteOpts = require("./lib/loadConfig")(localConfig, "MULE"),
config = {
...siteOpts,
logFunction: log,
},
yargs = require("yargs")
.version(version)
.alias("v", "version")
.usage("Usage: $0 start|stop")
.demandCommand(1),
{ argv } = yargs;
if (argv._[0] === "build") return build(config);
else if (argv._[0] === "serve") return serve(config);
else if (argv._[0] === "watch") return watch(config);

370
lib/build.js Normal file
View File

@ -0,0 +1,370 @@
const { exists } = require("fs-extra/lib/fs");
module.exports = async (config) => {
const { promises: fs } = require("fs"),
fse = require("fs-extra"),
path = require("path"),
ejs = require("ejs"),
frontMatter = require("front-matter"),
glob = require("glob"),
hljs = require("highlight.js"),
md = require("markdown-it")({
highlight: (str, lang) => {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(str, { language: lang }).value;
} catch (__) {}
}
return ""; // use external default escaping
},
html: true,
linkify: true,
typographer: true,
xhtmlOut: true,
}),
emoji = require("markdown-it-emoji"),
// { readJsonIfExists } = require("./utils"),
{ build, isRebuild, logFunction: log = () => {} } = config || {},
{ outputPath, journalsPerPage = 5, srcPath } = build,
{ site } = config,
copyAssets = async (directory) => {
const assets = await fs.readdir(directory);
assets.forEach(async (asset) => {
// we no longer merge scripts and styles, thanks to http/2's parallel file handling
if (asset === "_root") {
fse.copy(path.join(srcPath, "assets", asset), outputPath);
} else {
fse.copy(
path.join(srcPath, "assets", asset),
path.join(outputPath, asset)
);
}
});
},
getReadTime = (text) => {
const WPM = 275,
fixedString = text.replace(/[^\w\s]+/g, ""),
count = fixedString.split(/\s+/).length;
if (count < WPM) return "less than 1 minute";
else return `${Math.ceil(count / WPM)} minutes`;
},
tagSorter = (a, b) => a.toLowerCase().localeCompare(b.toLowerCase()),
parseFile = (file, pagePath, siteData, isSupport) => {
const { dir, ext, name } = path.parse(file) || {},
hasExt = name.indexOf(".") > -1,
destPath = path.join(outputPath, dir),
filePath = path.join(pagePath, file),
// read page file
data = fse.readFileSync(filePath, "utf-8"),
// render page
{ attributes, body } = frontMatter(data),
{ content_type: contentType, tags: originalTags = [] } =
attributes,
// TODO: Look for tags in posts as well, link to them, and add them to tag pages
tags =
typeof originalTags === "string"
? originalTags.split(/\W+/)
: [].concat(originalTags),
innerTags = (
contentType === "journal"
? body.match(/\b#(\w+)/g) || []
: []
).map((val) => val.replace("#", "")),
allTags = [...tags, ...innerTags].sort(tagSorter),
updatedBody =
contentType === "journal"
? allTags.reduce(
(acc, tag) =>
acc.replace(
`#${tag}`,
`
<a href="/journal/tags/${tag}/index.html">
#<span class="p-category category">${tag}</span>
</a>`
),
body
)
: body;
return {
...config,
page: {
name,
...attributes,
body: updatedBody,
destPath,
filePath,
path: path.join(dir, hasExt ? name : `${name}.html`),
tags: [...tags, ...innerTags].sort(tagSorter),
ext,
},
site: {
...site,
pages: isSupport ? siteData : [],
},
};
},
parseContent = (page, siteData) => {
const {
body,
content_type: contentType,
filePath,
// tags,
} = page || {},
{ ext } = path.parse(filePath) || {},
{ pages, tags } = siteData || {};
let content = body,
readTime;
if (ext === ".md") {
if (contentType === "journal" && typeof body === "string") {
readTime = getReadTime(body);
}
content = md.render(body);
} else if (ext === ".ejs") {
content = ejs.render(
body,
{ page, site: { ...site, pages, tags } },
{ filename: filePath }
);
}
return { ...page, content, readTime };
},
renderFile = async (page, isSupport) => {
const {
content,
destPath,
layout,
path: pagePath,
pages,
siteTags,
tags,
} = page || {};
try {
const layoutFileName = `${srcPath}/layouts/${
layout || "default"
}.ejs`,
layoutData = await fs.readFile(layoutFileName, "utf-8"),
completePage = isSupport
? content
: ejs.render(layoutData, {
content,
page,
site: {
...site,
pages,
tags:
page.content_type === "journal"
? siteTags
: tags,
},
filename: layoutFileName,
});
if (!completePage) {
console.log("failed!", pagePath, content);
return;
}
// create destination directory
fse.mkdirsSync(destPath);
// save the html file
fse.writeFileSync(
path.join(outputPath, pagePath),
completePage
);
} catch (e) {
console.log("failed!", pagePath);
console.log("paths", destPath, outputPath);
console.error(e);
return;
}
};
md.use(emoji);
log(`${isRebuild ? "Reb" : "B"}uilding...`);
// clear destination folder
fse.emptyDirSync(outputPath);
// copy assets folder
await copyAssets(path.join(srcPath, "assets"));
const files = ["pages", "sitePosts"].reduce((acc, pageDir) => {
return [
...acc,
...glob
.sync("**/*.@(md|ejs|html)", {
cwd: path.join(srcPath, pageDir),
})
.map((file) =>
parseFile(file, path.join(srcPath, pageDir))
),
];
}, []),
sortByPubDate = (a, b) => {
if (a.date_pub && b.date_pub) {
let a_dt = new Date(a.date_pub).getTime(),
b_dt = new Date(b.date_pub).getTime();
if (a_dt < b_dt) {
return 1;
}
if (b_dt < a_dt) {
return -1;
}
return 0;
}
if (a.date_pub) return -1;
if (b.date_pub) return 1;
return 0;
},
pages = files.map(({ page }) => ({ ...page })).sort(sortByPubDate),
tagCloud = pages.reduce((acc, curr) => {
const { tags } = curr;
tags.forEach((tag) => {
if (acc[tag]) acc[tag]++;
else acc[tag] = 1;
});
return acc;
}, {}),
tags = Object.keys(tagCloud).sort(tagSorter),
yearCloud = pages
.filter(({ content_type = "" }) => content_type === "journal")
.reduce((acc, curr) => {
const { date_pub } = curr;
if (date_pub) {
const year = new Date(date_pub).getFullYear();
if (acc[year]) acc[year]++;
else acc[year] = 1;
}
return acc;
}, {}),
years = Object.keys(yearCloud).sort().reverse(),
pagesWithContent = pages.map((page) =>
parseContent(page, { pages, tags })
);
// add data for the whole site to each page as it's rendered
pagesWithContent.forEach((page) => {
renderFile({ ...page, pages: pagesWithContent, siteTags: tags });
});
/* Journal Stuff - Tags & Years */
// make page(s) for each tag
tags.forEach((tag) => {
// check counts
let postCount = tagCloud[tag],
pageCount = Math.ceil(postCount / journalsPerPage);
for (let i = 1; i <= pageCount; i++) {
const firstEntryIndex = journalsPerPage * (i - 1),
lastEntryIndex = journalsPerPage * i;
renderFile({
content: tag,
destPath: path.join(outputPath, "journal", "tags", tag),
entriesToList: pagesWithContent
.filter(
(p) =>
p && Array.isArray(p.tags) && p.tags.includes(tag)
)
.slice(firstEntryIndex, lastEntryIndex),
layout: "tag",
path: `journal/tags/${tag}/${
i === 1 ? "index.html" : `page${i}.html`
}`,
site: { ...site, pages: pagesWithContent, tags },
pageCount,
pageNum: i,
pages: pagesWithContent,
tag,
tags,
title: `Journal Entries Tagged with #${tag}`,
});
}
});
// make page(s) for each year
years.forEach((year) => {
// check counts
let postCount = yearCloud[year],
pageCount = Math.ceil(postCount / journalsPerPage);
for (let i = 1; i <= pageCount; i++) {
const firstEntryIndex = journalsPerPage * (i - 1),
lastEntryIndex = journalsPerPage * i;
// TODO: rethink the data passed in here - you're paging solution works (kinda), take it over the finish line!
renderFile({
content: year,
destPath: path.join(outputPath, "journal", year),
entriesToList: pagesWithContent
.filter(({ content_type = "", date_pub = "" }) => {
if (!date_pub || content_type !== "journal")
return false;
const p_dt = new Date(date_pub).getTime(),
y1_dt = new Date(
`${year}-01-01T00:00:00-0500`
).getTime(),
y2_dt = new Date(
`${year}-12-31T23:59:59-0500`
).getTime();
return p_dt >= y1_dt && p_dt <= y2_dt;
})
.slice(firstEntryIndex, lastEntryIndex),
layout: "journal-year",
path: `journal/${year}/${
i === 1 ? "index.html" : `page${i}.html`
}`,
site: { ...site, pages: pagesWithContent, tags },
pageCount,
pageNum: i,
pages: pagesWithContent,
tags,
title: `Journal Entries from ${year}`,
year,
});
}
});
/* Support pages - anything too weird / specific for markdown rendering */
// collect support pages
const support = ["support"].reduce((acc, pageDir) => {
return [
...acc,
...glob
.sync("**/*.@(md|ejs|html)", {
cwd: path.join(srcPath, pageDir),
})
.map((file) =>
parseFile(
file,
path.join(srcPath, pageDir),
pagesWithContent,
true
)
),
];
}, []);
// write each one out
support.forEach((fileData) => {
const { page } = fileData;
if (page?.ext === ".ejs") {
const pageAndContent = parseContent(page, {
pages: pagesWithContent,
tags,
});
return renderFile({ ...fileData, ...pageAndContent, tags }, true);
}
return renderFile(fileData, true);
});
};

19
lib/defaults.json5 Normal file
View File

@ -0,0 +1,19 @@
{
/*
*/
/** TACO Express Default Options **/
/*
The function used to log output (console.log, morgan, etc).
Should take one (or more) strings as arguments.
*/
logFunction: null,
build: {},
site: {},
serve: {
port: 5000,
},
}

43
lib/loadConfig.js Normal file
View File

@ -0,0 +1,43 @@
module.exports = (opts, envKey) => {
const
fs = require('fs'),
path = require('path'),
json5 = require('json5'),
{ convertCamelToUpperSnakeCase, readJsonIfExists } = require('./utils'),
{ cwd, env } = process,
def = readJsonIfExists(path.resolve(__dirname, 'defaults.json5')),
// gets value from ENV || options || defaults (in that order)
getVal = (envName) => {
const snakeEnvName = `${envKey}_${convertCamelToUpperSnakeCase(envName)}`;
if (env[snakeEnvName]) return env[snakeEnvName];
if (opts[envName]) return opts[envName];
return def[envName];
},
// gets array from ENV || options || defaults (in that order)
getArray = (envName, optName = '') => {
if (optName === '') {
optName = envName;
envName = convertCamelToUpperSnakeCase(envName);
}
envName = `${envKey}_${envName}`;
if (env[envName]) return env[envName].split(path.delimiter);
if (Array.isArray(opts[optName]) && opts[optName].length) return opts[optName];
return def[optName];
};
return {
...Object.keys(def).reduce((acc, curr) => {
if (Array.isArray(def[curr])) acc[curr] = getArray(curr);
else acc[curr] = getVal(curr);
return acc;
}, {}),
};
};

26
lib/serve.js Normal file
View File

@ -0,0 +1,26 @@
module.exports = async (config) => {
let isReady = false;
const
http = require('http'),
address = require('network-address'),
handler = require('serve-handler'),
build = require('./build'),
{ build: buildOpts, logFunction: log = () => {}, serve: serveOpts } = config || {},
{ outputPath, srcPath } = buildOpts || {},
{ port = 5000 } = serveOpts || {},
server = http.createServer((request, response) => {
// You pass two more arguments for config and middleware
// More details here: https://github.com/vercel/serve-handler#options
return handler(request, response, { public: outputPath });
});
await build(config);
server.listen(port, async () => {
log(`Running at http://${address()}:${port} / http://localhost:${port}`);
});
};

57
lib/utils.js Normal file
View File

@ -0,0 +1,57 @@
module.exports = (() => {
const
chalk = require('chalk'),
getTime = () => {
const
now = new Date(),
tzo = -now.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = (num) => {
const norm = Math.floor(Math.abs(num));
return `${norm < 10 ? '0' : ''}${norm}`;
};
return [
now.getFullYear(),
'-',
pad(now.getMonth() + 1),
'-',
pad(now.getDate()),
'T',
pad(now.getHours()),
':',
pad(now.getMinutes()),
':',
pad(now.getSeconds()),
dif,
pad(tzo / 60),
':',
pad(tzo % 60)
].join('');
};
return {
convertCamelToUpperSnakeCase:
str => str.replace(/[A-Z]/g, letter => `_${letter}`).toUpperCase(),
getTime,
log: (msg) => console.log(`${chalk.grey(`${getTime()}:`)} ${msg}`),
readJsonIfExists: (filePath) => {
const
fs = require('fs'),
json5 = require('json5');
try {
return json5.parse(fs.readFileSync(filePath, {encoding: 'utf8'}));
} catch (err) {
if (err.code === 'ENOENT') return {};
throw err;
}
},
};
})();

58
lib/watch.js Normal file
View File

@ -0,0 +1,58 @@
module.exports = async (config) => {
let isReady = false;
const
http = require('http'),
chokidar = require('chokidar'),
address = require('network-address'),
handler = require('serve-handler'),
build = require('./build'),
rebuild = (cfg) => {
isReady = false;
build({ ...cfg, isRebuild: true });
isReady = true;
},
{ build: buildOpts, logFunction: log = () => {}, serve: serveOpts } = config || {},
{ outputPath, srcPath } = buildOpts || {},
{ port = 5000 } = serveOpts || {},
watcher = chokidar.watch([srcPath, '*.json'], {
ignored: /(^|[\/\\])\../, // ignore dotfiles
persistent: true
})
.on('add', (path) => {
if (isReady) {
log(`File ${path} has been added`)
rebuild(config);
}
})
.on('change', (path) => {
if (isReady) {
log(`File ${path} has been changed`)
rebuild(config);
}
})
.on('ready', () => {
isReady = true;
})
.on('unlink', (path) => {
if (isReady) {
log(`File ${path} has been removed`)
rebuild(config);
}
}),
server = http.createServer((request, response) => {
// You pass two more arguments for config and middleware
// More details here: https://github.com/vercel/serve-handler#options
return handler(request, response, { public: outputPath });
});
await build(config);
server.listen(port, () => {
log(`Running at http://${address()}:${port} / http://localhost:${port}`);
});
};

2653
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

34
package.json Executable file
View File

@ -0,0 +1,34 @@
{
"name": "iew-site-builder",
"version": "0.9.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "node app.js build",
"build:prod": "cross-env NODE_ENV=production node ./lib/build",
"serve": "node app.js serve",
"watch": "node app.js watch"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"chalk": "^4.1.2",
"chokidar": "^3.4.3",
"dotenv": "^16.0.1",
"highlight.js": "^11.3.1",
"json5": "^2.1.3",
"markdown-it": "^13.0.1",
"markdown-it-emoji": "^2.0.0",
"network-address": "^1.1.2",
"serve-handler": "^6.1.3"
},
"devDependencies": {
"ejs": "^3.1.5",
"front-matter": "^4.0.2",
"fs-extra": "^10.0.0",
"glob": "^8.0.3",
"serve": "^13.0.2",
"yargs": "^17.3.1"
}
}

26
site.config.json5 Normal file
View File

@ -0,0 +1,26 @@
{
site: {
title: "It's Eric Woodward (dotcom)",
author: {
name: "Eric Woodward",
email: "redacted@nunyodam.com", // not used
photo: "/images/eric-8bit.gif",
site: "https://itsericwoodward.com",
},
base_uri: "",
robots: "index,follow",
language: "en-us",
copyright: "Copyright 2014-2022 Eric Woodward, licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.",
basePath: "",
uri: "https://www.itsericwoodward.com",
},
build: {
journalsPerPage: 5,
srcPath: "src",
outputPath: "out",
},
serve: {
port: 4997,
},
}

View File

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<browserconfig><msapplication><tile><square70x70logo src="/images/favicons/ms-icon-70x70.png"/><square150x150logo src="/images/favicons/ms-icon-150x150.png"/><square310x310logo src="/images/favicons/ms-icon-310x310.png"/><TileColor>#0d1852</TileColor></tile></msapplication></browserconfig>

Binary file not shown.

View File

@ -0,0 +1,348 @@
/*!
* jQLite JavaScript Library v1.1.1 (http://code.google.com/p/jqlite/)
* Copyright (c) 2010 Brett Fattori (bfattori@gmail.com)
* Licensed under the MIT license
* http://www.opensource.org/licenses/mit-license.php
*
* Many thanks to the jQuery team's efforts. Some code is
* Copyright (c) 2010, John Resig. See
* http://jquery.org/license
*
* @author Brett Fattori (bfattori@gmail.com)
* @author $Author: bfattori $
* @version $Revision: 145 $
*
* Created: 03/29/2010
* Modified: $Date: 2010-06-21 11:08:14 -0400 (Mon, 21 Jun 2010) $
*/
(function(){function B(){return+new Date}var D=function(a,b){if(a===""&&b)return b;var d=a.split(" "),c=d.shift(),e;if(c.charAt(0)=="#"){var g=i.getElementById(c.substring(1));e=g?[g]:[]}else{e=c.charAt(0)!=="."?c.split(".")[0]:"*";var h=c.split("."),j=null;if(e.indexOf("[")!=-1){j=e;e=e.substr(0,e.indexOf("["))}g=function(o){var n=arguments.callee,k;if(!(k=!n.needClass)){k=n.classes;if(o.className.length==0)k=false;else{for(var r=o.className.split(" "),l=k.length,p=0;p<k.length;p++)f.inArray(k[p],
r)!=-1&&l--;k=l==0}}if(k=k){if(!(k=!n.needAttribute)){n=n.attributes;k=true;for(r=0;r<n.length;r++){l=n[r].split("=");p=l[0].indexOf("!")!=-1||l[0].indexOf("*")!=-1?l[0].charAt(l[0].length-1)+"=":"=";if(p!="=")l[0]=l[0].substring(0,l[0].length-1);switch(p){case "=":k&=o.getAttribute(l[0])===l[1];break;case "!=":k&=o.getAttribute(l[0])!==l[1];break;case "*=":k&=o.getAttribute(l[0]).indexOf(l[1])!=-1;break;default:k=false}}k=k}k=k}if(k)return o};for(var u=[],s=0;s<b.length;s++)for(var C=b[s].getElementsByTagName(e),
v=0;v<C.length;v++)u.push(C[v]);h&&h.shift();e=[];g.classes=h;if(j!=null){var w=j.indexOf("[");s=j.lastIndexOf("]");w=j.substring(w+1,s).split("][")}g.attributes=j!=null?w:null;g.needClass=c.indexOf(".")!=-1&&h.length>0;g.needAttribute=j!=null;for(c=0;c<u.length;c++)g(u[c])&&e.push(u[c])}return D(d.join(" "),e)},Q=function(a,b){b=b||i;if(a.nodeType&&a.nodeType===E){a=i.body;if(a===null)return[i]}if(a.nodeType&&a.nodeType===m)return[a];if(a.jquery&&typeof a.jquery==="string")return a.toArray();if(b)b=
F(b);if(f.isArray(a))return a;else if(typeof a==="string"){for(var d=[],c=0;c<b.length;c++){var e=[b[c]];if(!f.forceSimpleSelectorEngine&&e[0].querySelectorAll){e=e[0].querySelectorAll(a);for(var g=0;g<e.length;g++)d.push(e.item(g))}else d=d.concat(D(a,e))}return d}else return null},G=false;setTimeout(function(){var a=i.body;if(a){var b=i.createElement("script"),d="i"+(new Date).getTime();b.type="text/javascript";try{b.appendChild(i.createTextNode("window."+d+"=1;"))}catch(c){}a.insertBefore(b,a.firstChild);
var e=true;if(window[d])delete window[d];else e=false;a.removeChild(b);G=e}else setTimeout(arguments.callee,33)},33);var H=function(a){var b=i.createElement("div");b.innerHTML=a;return{scripts:b.getElementsByTagName("script"),data:a}},I=function(a){a=a.replace(/-/g," ");a=a;var b=true;b=b||false;a=!a?"":a.toString().replace(/^\s*|\s*$/g,"");var d="";if(a.length<=0)a="";else{var c=false;d+=b?a.charAt(0):a.charAt(0).toUpperCase();for(b=1;b<a.length;b++){d+=c?a.charAt(b).toUpperCase():a.charAt(b).toLowerCase();
var e=a.charCodeAt(b);c=e==32||e==45||e==46;if(e==99||e==67)if(a.charCodeAt(b-1)==77||a.charCodeAt(b-1)==109)c=true}a=d}return a.replace(/ /g,"")},J={click:"MouseEvents",dblclick:"MouseEvents",mousedown:"MouseEvents",mouseup:"MouseEvents",mouseover:"MouseEvents",mousemove:"MouseEvents",mouseout:"MouseEvents",contextmenu:"MouseEvents",keypress:"KeyEvents",keydown:"KeyEvents",keyup:"KeyEvents",load:"HTMLEvents",unload:"HTMLEvents",abort:"HTMLEvents",error:"HTMLEvents",resize:"HTMLEvents",scroll:"HTMLEvents",
select:"HTMLEvents",change:"HTMLEvents",submit:"HTMLEvents",reset:"HTMLEvents",focus:"HTMLEvents",blur:"HTMLEvents",touchstart:"MouseEvents",touchend:"MouseEvents",touchmove:"MouseEvents"},K=function(a,b,d){if(f.isFunction(d)){if(typeof b==="string")b=b.toLowerCase();var c=J[b];if(b.indexOf("on")==0)b=b.substring(2);if(c){c=function(e){var g=arguments.callee,h=e.data||[];h.unshift(e);g=g.fn.apply(a,h);if(typeof g!="undefined"&&g===false){if(e.preventDefault&&e.stopPropagation){e.preventDefault();
e.stopPropagation()}else{e.returnValue=false;e.cancelBubble=true}return false}return true};c.fn=d;a.addEventListener?a.addEventListener(b,c,false):a.attachEvent("on"+b,c)}else{if(!a._handlers)a._handlers={};c=a._handlers[b]||[];c.push(d);a._handlers[b]=c}}},f=function(a,b){return(new x).init(a,b)},i=window.document,y=Object.prototype.hasOwnProperty,z=Object.prototype.toString,L=Array.prototype.push,R=Array.prototype.slice,m=1,E=9,A=[],M=false,N=false,q;f.forceSimpleSelectorEngine=false;f.each=function(a,
b){var d,c=0,e=a.length;if(e===undefined||f.isFunction(a))for(d in a){if(b.call(a[d],d,a[d])===false)break}else for(d=a[0];c<e&&b.call(d,c,d)!==false;d=a[++c]);return a};f.noop=function(){};f.isFunction=function(a){return z.call(a)==="[object Function]"};f.isArray=function(a){return z.call(a)==="[object Array]"};f.isPlainObject=function(a){if(!a||z.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!y.call(a,"constructor")&&!y.call(a.constructor.prototype,"isPrototypeOf"))return false;
var b;for(b in a);return b===undefined||y.call(a,b)};f.merge=function(a,b){var d=a.length,c=0;if(typeof b.length==="number")for(var e=b.length;c<e;c++)a[d++]=b[c];else for(;b[c]!==undefined;)a[d++]=b[c++];a.length=d;return a};f.param=function(a){var b="";a&&f.each(a,function(d,c){b+=(b.length!=0?"&":"")+c+"="+encodeURIComponent(d)});return b};f.evalScripts=function(a){for(var b=i.getElementsByTagName("head")[0]||i.documentElement,d=0;d<a.length;d++){var c=i.createElement("script");c.type="text/javascript";
if(G)c.appendChild(i.createTextNode(a[d].text));else c.text=a[d].text;b.insertBefore(c,b.firstChild);b.removeChild(c)}};f.ready=function(){for(M=true;A.length>0;)A.shift()()};var t="jQuery"+B(),S=0,O={};f.noData={embed:true,object:true,applet:true};f.cache={};f.data=function(a,b,d){if(!(a.nodeName&&jQuery.noData[a.nodeName.toLowerCase()])){a=a==window?O:a;var c=a[t];c||(c=a[t]=++S);if(b&&!jQuery.cache[c])jQuery.cache[c]={};if(d!==undefined)jQuery.cache[c][b]=d;return b?jQuery.cache[c][b]:c}};f.removeData=
function(a,b){a=a==window?O:a;var d=a[t];if(b){if(jQuery.cache[d]){delete jQuery.cache[d][b];b="";for(b in jQuery.cache[d])break;b||jQuery.removeData(a)}}else{try{delete a[t]}catch(c){a.removeAttribute&&a.removeAttribute(t)}delete jQuery.cache[d]}};f.ajax={status:-1,statusText:"",responseText:null,responseXML:null,send:function(a,b,d){if(f.isFunction(b)){d=b;b={}}if(a){var c=true,e=null,g=null;if(typeof b.async!=="undefined"){c=b.async;delete b.async}if(typeof b.username!=="undefined"){e=b.username;
delete b.username}if(typeof b.password!=="undefined"){g=b.password;delete b.password}b=f.param(b);if(b.length!=0)a+=(a.indexOf("?")==-1?"?":"&")+b;b=new XMLHttpRequest;b.open("GET",a,c,e,g);b.send();if(c){a=function(h){var j=arguments.callee;h.status==200?f.ajax.complete(h,j.cb):f.ajax.error(h,j.cb)};a.cb=d;d=function(){var h=arguments.callee;h.req.readyState!=4?setTimeout(h,250):h.xcb(h.req)};d.req=b;d.xcb=a;setTimeout(d,250)}}},complete:function(a,b){f.ajax.status=a.status;f.ajax.responseText=a.responseText;
f.ajax.responseXML=a.responseXML;f.isFunction(b)&&b(a.responseText,a.status)},error:function(a,b){f.ajax.status=a.status;f.ajax.statusText=a.statusText;f.isFunction(b)&&b(a.status,a.statusText)}};f.makeArray=function(a,b){var d=b||[];if(a!=null)a.length==null||typeof a==="string"||jQuery.isFunction(a)||typeof a!=="function"&&a.setInterval?L.call(d,a):f.merge(d,a);return d};f.inArray=function(a,b){for(var d=0;d<b.length;d++)if(b[d]===a)return d;return-1};f.trim=function(a){return a!=null?a.toString().replace(/^\s*|\s*$/g,
""):""};var x=function(){};x.prototype={selector:"",context:null,length:0,jquery:"jqlite-1.1.1",init:function(a,b){if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1}else if(typeof a==="function")this.ready(a);else{var d=[];if(a.jquery&&typeof a.jquery==="string")d=a.toArray();else if(f.isArray(a))d=a;else if(typeof a==="string"&&f.trim(a).indexOf("<")==0&&f.trim(a).indexOf(">")!=-1){d=f.trim(a).toLowerCase();d=d.indexOf("<option")==0?"SELECT":d.indexOf("<li")==0?"UL":d.indexOf("<tr")==
0?"TBODY":d.indexOf("<td")==0?"TR":"DIV";d=i.createElement(d);d.innerHTML=a;d=[d.removeChild(d.firstChild)]}else{if(a.indexOf(",")!=-1){d=a.split(",");for(var c=0;c<d.length;c++)d[c]=f.trim(d[c])}else d=[a];c=[];for(var e=0;e<d.length;e++)c=c.concat(Q(d[e],b));d=c}L.apply(this,d)}return this},each:function(a){return f.each(this,a)},size:function(){return this.length},toArray:function(){return R.call(this,0)},ready:function(a){if(M)a();else{A.push(a);return this}},data:function(a,b){if(typeof a===
"undefined"&&this.length)return jQuery.data(this[0]);else if(typeof a==="object")return this.each(function(){jQuery.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===undefined){if(data===undefined&&this.length)data=jQuery.data(this[0],a);return data===undefined&&d[1]?this.data(d[0]):data}else return this.each(function(){jQuery.data(this,a,b)})},removeData:function(a){return this.each(function(){jQuery.removeData(this,a)})},addClass:function(a){return this.each(function(){if(this.className.length!=
0){var b=this.className.split(" ");if(f.inArray(a,b)==-1){b.push(a);this.className=b.join(" ")}}else this.className=a})},removeClass:function(a){return this.each(function(){if(this.className.length!=0){var b=this.className.split(" "),d=f.inArray(a,b);if(d!=-1){b.splice(d,1);this.className=b.join(" ")}}})},hasClass:function(a){if(this[0].className.length==0)return false;return f.inArray(a,this[0].className.split(" "))!=-1},isElementName:function(a){return this[0].nodeName.toLowerCase()===a.toLowerCase()},
toggleClass:function(a){return this.each(function(){if(this.className.length==0)this.className=a;else{var b=this.className.split(" "),d=f.inArray(a,b);d!=-1?b.splice(d,1):b.push(a);this.className=b.join(" ")}})},hide:function(a){return this.each(function(){if(this.style&&this.style.display!=null)if(this.style.display.toString()!="none"){this._oldDisplay=this.style.display.toString()||(this.nodeName!="span"?"block":"inline");this.style.display="none"}f.isFunction(a)&&a(this)})},show:function(a){return this.each(function(){this.style.display=
(this._oldDisplay&&this._oldDisplay!=""?this._oldDisplay:null)||(this.nodeName!="span"?"block":"inline");f.isFunction(a)&&a(this)})},css:function(a,b){if(typeof a==="string"&&b==null)return this[0].style[I(a)];else{a=typeof a==="string"?P(a,b):a;return this.each(function(){var d=this;typeof d.style!="undefined"&&f.each(a,function(c,e){e=typeof e==="number"?e+"px":e;var g=I(c);d.style[g]||(g=c);d.style[g]=e})})}},load:function(a,b,d){if(f.isFunction(b)){d=b;b={}}return this.each(function(){var c=function(e,
g){var h=arguments.callee;if(e){var j=H(e);h.elem.innerHTML=j.data;f.evalScripts(j.scripts)}f.isFunction(h.cback)&&h.cback(e,g)};c.cback=d;c.elem=this;f.ajax.send(a,b,c)})},html:function(a){return a?this.each(function(){var b=H(a);this.innerHTML=b.data;f.evalScripts(b.scripts)}):this[0].innerHTML},attr:function(a,b){return typeof a==="string"&&b==null?this[0]?this[0].getAttribute(a):"":this.each(function(){a=typeof a==="string"?P(a,b):a;for(var d in a)this.setAttribute(d,a[d])})},eq:function(a){var b=
this.toArray();this.context=this[0]=a<0?b[b.length+a]:b[a];this.length=1;return this},first:function(){this.context=this[0]=this.toArray()[0];this.length=1;return this},last:function(){var a=this.toArray();this.context=this[0]=a[a.length-1];this.length=1;return this},index:function(a){var b=-1;if(this.length!=0){var d=this[0];if(a){var c=f(a)[0];this.each(function(g){if(this===c){b=g;return false}})}else{a=this.parent()[0].firstChild;for(var e=[];a!=null;){a.nodeType===m&&e.push(a);a=a.nextSibling}f.each(a,
function(g){if(this===d){b=g;return false}})}}return b},next:function(a){var b=[];if(a){var d=f(a);this.each(function(){for(var c=this.nextSibling;c!=null&&c.nodeType!==m;)c=c.nextSibling;if(c!=null){var e=false;d.each(function(){if(this==c){e=true;return false}});e&&b.push(c)}})}else this.each(function(){for(var c=this.nextSibling;c!=null&&c.nodeType!==m;)c=c.nextSibling;c!=null&&b.push(c)});return f(b)},prev:function(a){var b=[];if(a){var d=f(a);this.each(function(){for(var c=this.previousSibling;c!=
null&&c.nodeType!==m;)c=c.previousSibling;if(c!=null){var e=false;d.each(function(){if(this==c){e=true;return false}});e&&b.push(c)}})}else this.each(function(){for(var c=this.previousSibling;c!=null&&c.nodeType!==m;)c=c.previousSibling;c!=null&&b.push(c)});return f(b)},parent:function(a){var b=[];if(a){var d=f(a);this.each(function(){var c=this.parentNode,e=false;d.each(function(){if(this==c){e=true;return false}});e&&b.push(c)})}else this.each(function(){b.push(this.parentNode)});return f(b)},parents:function(a){var b=
[];if(a){var d=f(a);this.each(function(){for(var c=this;c!=i.body;){d.each(function(){this==c&&b.push(c)});c=c.parentNode}})}else this.each(function(){for(var c=this;c!=i.body;){c=c.parentNode;b.push(c)}});return f(b)},children:function(a){var b=[];if(a){var d=f(a);this.each(function(){for(var c=this.firstChild;c!=null;){c.nodeType==m&&d.each(function(){this===c&&b.push(c)});c=c.nextSibling}})}else this.each(function(){for(var c=this.firstChild;c!=null;){c.nodeType==m&&b.push(c);c=c.nextSibling}});
return f(b)},append:function(a){a=F(a);return this.each(function(){for(var b=0;b<a.length;b++)this.appendChild(a[b])})},remove:function(a){return this.each(function(){a?$(a,this).remove():this.parentNode.removeChild(this)})},empty:function(){return this.each(function(){this.innerHTML=""})},val:function(a){if(a==null){var b=null;if(this&&this.length!=0&&typeof this[0].value!="undefined")b=this[0].value;return b}else return this.each(function(){if(typeof this.value!="undefined")this.value=a})},bind:function(a,
b){return this.each(function(){K(this,a,b)})},trigger:function(a,b){return this.each(function(){var d;var c;c=a;if(typeof c==="string")c=c.toLowerCase();var e=null,g=J[c]||"Event";if(i.createEvent){e=i.createEvent(g);e._eventClass=g;c&&e.initEvent(c,true,true)}if(i.createEventObject){e=i.createEventObject();if(c){e.type=c;e._eventClass=g}}c=e;if(c._eventClass!=="Event"){c.data=b;d=this.dispatchEvent(c)}else if(e=(this._handlers||{})[a])for(g=0;g<e.length;g++){var h=f.isArray(b)?b:[];h.unshift(c);
h=e[g].apply(this,h);if(!(typeof h=="undefined"?true:h))break}return d})},submit:function(a){return this.each(function(){if(f.isFunction(a))K(this,"onsubmit",a);else this.submit&&this.submit()})}};if(i.addEventListener)q=function(){i.removeEventListener("DOMContentLoaded",q,false);f.ready()};else if(i.attachEvent)q=function(){if(i.readyState==="complete"){i.detachEvent("onreadystatechange",q);f.ready()}};if(!N){N=true;if(i.readyState==="complete")return f.ready();if(i.addEventListener){i.addEventListener("DOMContentLoaded",
q,false);window.addEventListener("load",f.ready,false)}else if(i.attachEvent){i.attachEvent("onreadystatechange",q);window.attachEvent("onload",f.ready)}}var P=function(a,b){var d={};d[a]=b;return d},F=function(a){if(a.nodeType&&(a.nodeType===m||a.nodeType===E))a=[a];else if(typeof a==="string")a=f(a).toArray();else if(a.jquery&&typeof a.jquery==="string")a=a.toArray();return a};if(typeof window.jQuery=="undefined"){window.jQuery=f;window.jQuery.fn=x.prototype;window.$=window.jQuery;window.now=B}jQuery.extend=
jQuery.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,c=false,e,g,h,j;if(typeof a==="boolean"){c=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!jQuery.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(g in e){h=a[g];j=e[g];if(a!==j)if(c&&j&&(jQuery.isPlainObject(j)||jQuery.isArray(j))){h=h&&(jQuery.isPlainObject(h)||jQuery.isArray(h))?h:jQuery.isArray(j)?[]:{};a[g]=jQuery.extend(c,h,j)}else if(j!==undefined)a[g]=j}return a};jQuery.each("click,dblclick,mouseover,mouseout,mousedown,mouseup,keydown,keypress,keyup,focus,blur,change,select,error,load,unload,scroll,resize,touchstart,touchend,touchmove".split(","),
function(a,b){jQuery.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)}})})();
(function() {
this.Dosbox = (function() {
function Dosbox(options) {
this.onload = options.onload;
this.onrun = options.onrun;
this.ui = new Dosbox.UI(options);
this.module = new Dosbox.Module({
canvas: this.ui.canvas
});
this.ui.onStart((function(_this) {
return function() {
_this.ui.showLoader();
return _this.downloadScript();
};
})(this));
}
Dosbox.prototype.run = function(archiveUrl, executable) {
return new Dosbox.Mount(this.module, archiveUrl, {
success: (function(_this) {
return function() {
var func, hide;
_this.ui.updateMessage("Launching " + executable);
hide = function() {
return _this.ui.hideLoader();
};
func = function() {
return _this._dosbox_main(_this, executable);
};
setTimeout(func, 1000);
return setTimeout(hide, 3000);
};
})(this),
progress: (function(_this) {
return function(total, current) {
return _this.ui.updateMessage("Mount " + executable + " (" + (current * 100 / total | 0) + "%)");
};
})(this)
});
};
Dosbox.prototype.requestFullScreen = function() {
if (this.module.requestFullScreen) {
return this.module.requestFullScreen(true, false);
}
};
Dosbox.prototype.downloadScript = function() {
this.module.setStatus('Downloading js-dos');
this.ui.updateMessage('Downloading js-dos');
return new Dosbox.Xhr('https://js-dos.com/cdn/js-dos-v3.js', {
success: (function(_this) {
return function(script) {
var func;
_this.ui.updateMessage('Initializing dosbox');
func = function() {
return _this._jsdos_init(_this.module, script, _this.onload);
};
return setTimeout(func, 1000);
};
})(this),
progress: (function(_this) {
return function(total, current) {
return _this.ui.updateMessage("Downloading js-dos (" + (current * 100 / total | 0) + "%)");
};
})(this)
});
};
Dosbox.prototype._jsdos_init = function(module, script, onload) {
var Module;
Module = module;
eval(script);
if (onload) {
return onload(this);
}
};
Dosbox.prototype._dosbox_main = function(dosbox, executable) {
var exception, func;
try {
if (dosbox.onrun) {
func = function() {
return dosbox.onrun(dosbox, executable);
};
setTimeout(func, 1000);
}
return dosbox.module.ccall('dosbox_main', 'int', ['string'], [executable]);
} catch (error) {
exception = error;
if (exception === 'SimulateInfiniteLoop') {
} else {
return typeof console !== "undefined" && console !== null ? typeof console.error === "function" ? console.error(exception) : void 0 : void 0;
}
}
};
return Dosbox;
})();
}).call(this);
(function() {
Dosbox.Module = (function() {
function Module(options) {
this.elCanvas = options.canvas;
this.canvas = this.elCanvas[0];
}
Module.prototype.preRun = [];
Module.prototype.postRun = [];
Module.prototype.totalDependencies = 0;
Module.prototype.print = function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(text) : void 0 : void 0;
};
Module.prototype.printErr = function(text) {
text = Array.prototype.slice.call(arguments).join(' ');
return typeof console !== "undefined" && console !== null ? typeof console.error === "function" ? console.error(text) : void 0 : void 0;
};
Module.prototype.setStatus = function(text) {
return typeof console !== "undefined" && console !== null ? typeof console.log === "function" ? console.log(text) : void 0 : void 0;
};
Module.prototype.monitorRunDependencies = function(left) {
var status;
this.totalDependencies = Math.max(this.totalDependencies, left);
status = left ? "Preparing... (" + (this.totalDependencies - left) + "/" + this.totalDependencies + ")" : 'All downloads complete.';
return this.setStatus(status);
};
return Module;
})();
}).call(this);
(function() {
Dosbox.Mount = (function() {
function Mount(module, url, options) {
this.module = module;
new Dosbox.Xhr(url, {
success: (function(_this) {
return function(data) {
var bytes;
bytes = _this._toArray(data);
if (_this._mountZip(bytes)) {
return options.success();
} else {
return typeof console !== "undefined" && console !== null ? typeof console.error === "function" ? console.error('Unable to mount', url) : void 0 : void 0;
}
};
})(this),
progress: options.progress
});
}
Mount.prototype._mountZip = function(bytes) {
var buffer, extracted;
buffer = this.module._malloc(bytes.length);
this.module.HEAPU8.set(bytes, buffer);
extracted = this.module.ccall('extract_zip', 'int', ['number', 'number'], [buffer, bytes.length]);
this.module._free(buffer);
return extracted === 0;
};
Mount.prototype._toArray = function(data) {
var arr, i, len;
if (typeof data === 'string') {
arr = new Array(data.length);
i = 0;
len = data.length;
while (i < len) {
arr[i] = data.charCodeAt(i);
++i;
}
return arr;
}
return data;
};
return Mount;
})();
}).call(this);
(function() {
Dosbox.UI = (function() {
function UI(options) {
this.appendCss();
this.div = $('#' + (options.id || 'dosbox'));
this.wrapper = $('<div class="dosbox-container">');
this.canvas = $('<canvas class="dosbox-canvas" oncontextmenu="event.preventDefault()">');
this.overlay = $('<div class="dosbox-overlay">');
this.loaderMessage = $('<div class="dosbox-loader-message">');
this.loader = $('<div class="dosbox-loader">').append($('<div class="st-loader">').append($('<span class="equal">'))).append(this.loaderMessage);
this.start = $('<div class="dosbox-start">Click to start');
this.div.append(this.wrapper);
this.wrapper.append(this.canvas);
this.wrapper.append(this.loader);
this.wrapper.append(this.overlay);
this.overlay.append($('<div class="dosbox-powered">Powered by &nbsp;').append($('<a href="http://js-dos.com">js-dos.com')));
this.overlay.append(this.start);
}
UI.prototype.onStart = function(fun) {
return this.start.click((function(_this) {
return function() {
fun();
return _this.overlay.hide();
};
})(this));
};
UI.prototype.appendCss = function() {
var head, style;
head = document.head || document.getElementsByTagName('head')[0];
style = document.createElement('style');
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = this.css;
} else {
style.appendChild(document.createTextNode(this.css));
}
return head.appendChild(style);
};
UI.prototype.showLoader = function() {
this.loader.show();
return this.loaderMessage.html('');
};
UI.prototype.updateMessage = function(message) {
return this.loaderMessage.html(message);
};
UI.prototype.hideLoader = function() {
return this.loader.hide();
};
UI.prototype.css = '.dosbox-container { position: relative; min-width: 320px; min-height: 200px; } .dosbox-canvas { } .dosbox-overlay, .dosbox-loader { position: absolute; left: 0; right: 0; top: 0; bottom: 0; background-color: #333; } .dosbox-start { text-align: center; position: absolute; left: 0; right: 0; bottom: 50%; color: #f80; font-size: 1.5em; text-decoration: underline; cursor: pointer; } .dosbox-overlay a { color: #f80; } .dosbox-loader { display: none; } .dosbox-powered { position: absolute; right: 1em; bottom: 1em; font-size: 0.8em; color: #9C9C9C; } .dosbox-loader-message { text-align: center; position: absolute; left: 0; right: 0; bottom: 50%; margin: 0 0 -3em 0; box-sizing: border-box; color: #f80; font-size: 1.5em; } @-moz-keyframes loading { 0% { left: 0; } 50% { left: 8.33333em; } 100% { left: 0; } } @-webkit-keyframes loading { 0% { left: 0; } 50% { left: 8.33333em; } 100% { left: 0; } } @keyframes loading { 0% { left: 0; } 50% { left: 8.33333em; } 100% { left: 0; } } .st-loader { width: 10em; height: 2.5em; position: absolute; top: 50%; left: 50%; margin: -1.25em 0 0 -5em; box-sizing: border-box; } .st-loader:before, .st-loader:after { content: ""; display: block; position: absolute; top: 0; bottom: 0; width: 1.25em; box-sizing: border-box; border: 0.25em solid #f80; } .st-loader:before { left: -0.76923em; border-right: 0; } .st-loader:after { right: -0.76923em; border-left: 0; } .st-loader .equal { display: block; position: absolute; top: 50%; margin-top: -0.5em; left: 4.16667em; height: 1em; width: 1.66667em; border: 0.25em solid #f80; box-sizing: border-box; border-width: 0.25em 0; -moz-animation: loading 1.5s infinite ease-in-out; -webkit-animation: loading 1.5s infinite ease-in-out; animation: loading 1.5s infinite ease-in-out; }';
return UI;
})();
}).call(this);
(function() {
Dosbox.Xhr = (function() {
function Xhr(url, options) {
var e;
this.success = options.success;
this.progress = options.progress;
if (window.ActiveXObject) {
try {
this.xhr = new ActiveXObject('Microsoft.XMLHTTP');
} catch (error) {
e = error;
this.xhr = null;
}
} else {
this.xhr = new XMLHttpRequest();
}
this.xhr.open('GET', url, true);
this.xhr.overrideMimeType('text/plain; charset=x-user-defined');
this.xhr.addEventListener('progress', (function(_this) {
return function(evt) {
if (_this.progress) {
return _this.progress(evt.total, evt.loaded);
}
};
})(this));
this.xhr.onreadystatechange = (function(_this) {
return function() {
return _this._onReadyStateChange();
};
})(this);
this.xhr.send();
}
Xhr.prototype._onReadyStateChange = function() {
if (this.xhr.readyState === 4 && this.success) {
return this.success(this.xhr.responseText);
}
};
return Xhr;
})();
}).call(this);

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,41 @@
{
"name": "Its Eric Woodward! (dotcom)",
"icons": [
{
"src": "\/images\/favicons\/android-icon-36x36.png",
"sizes": "36x36",
"type": "image\/png",
"density": "0.75"
},
{
"src": "\/images\/favicons\/android-icon-48x48.png",
"sizes": "48x48",
"type": "image\/png",
"density": "1.0"
},
{
"src": "\/images\/favicons\/android-icon-72x72.png",
"sizes": "72x72",
"type": "image\/png",
"density": "1.5"
},
{
"src": "\/images\/favicons\/android-icon-96x96.png",
"sizes": "96x96",
"type": "image\/png",
"density": "2.0"
},
{
"src": "\/images\/favicons\/android-icon-144x144.png",
"sizes": "144x144",
"type": "image\/png",
"density": "3.0"
},
{
"src": "\/images\/favicons\/android-icon-192x192.png",
"sizes": "192x192",
"type": "image\/png",
"density": "4.0"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

View File

@ -0,0 +1,422 @@
┌─────┐ ┌──────┐ ┌───────┐ ┌─────┐ ┌─┐ ┌─┐ ┌──────┐ ┌─────┐ ┌─────┐ ┌─┐ ┌─┐
│ ┌─┐ │ │ ┌──┐ │ │ ┌┐ ┌┐ │ │ ┌─┐ │ │ │ │ │ │ ┌──┐ │ │ ┌─┐ │ │ ┌─┐ │ │ │ │ │
│ │ └─┘ │ └──┘ │ │ ││ ││ │ │ └─┘ │ │ └─┘ │ │ └──┘ │ │ └─┘ │ │ └─┘ │ │ └─┘ │
│ │ ┌─┐ │ ┌──┐ │ │ │└─┘│ │ │ ┌───┘ │ ┌─┐ │ │ ┌──┐ │ │ ┌───┘ │ ┌───┘ └─┐ ┌─┘
│ └─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
└─────┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘
┌───┐ ┌─────┐ ┌─┐ ┌──────┐ ┌───┐ ┌─┐ ┌──────┐
└┐ ┌┘ │ ┌───┘ │ │ │ ┌──┐ │ │ └┐ │ │ └┐ ┌─┐ │
│ │ │ └───┐ │ │ │ └──┘ │ │ ├┐ └┐│ │ │ │ │ │
│ │ └───┐ │ │ │ │ ┌──┐ │ │ │└┐ └┤ │ │ │ │ │
┌┘ └┐ ┌───┘ │ │ └──┐ │ │ │ │ │ │ └┐ │ ┌┘ └─┘ │
└───┘ └─────┘ └────┘ └─┘ └─┘ └─┘ └───┘ └──────┘
▄██████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ██████▄ ▄██████ ██
██ ██ ██ ██▄▄▄██ ██▄▄▄▄ ██▄▄▄▄ ██▄▄▄██ ██ ██ ██ ██▄▄▄▄ ██
██ ██ ██ ██▀▀▀██ ▀▀▀▀██ ▀▀▀▀██ ██▀▀▀██ ██ ██████ ██▀▀▀▀ ▀▀
██ ██ ██ ██ ██ ▀█████▀ ▀█████▀ ██ ██ ▀█████▀ ██ ▀██ ▀██████ ██
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"We Have Such Sights To Show You... "
=============================================================================
Camp Happy Island Massacre FAQ v1.1
by Eric Woodward (hey@itsericwoodward.com)
=============================================================================
Table of Contents
Section 1 - The Beginning
1.0 - SPECIAL NOTE - 20 Years Later
1.1 - What is 'Camp Happy Island Massacre'?
1.2 - Who Wrote This Wonderful Game?
1.3 - What Kind of System Do I Need to Run This Game?
1.4 - How Do I Install and Run This Game?
1.5 - How Can I Play This Game Online?
Section 2 - The Game
2.1 - The Story So Far...
2.2 - So How Do I Play?
2.3 - Is There a Map?
2.4 - What Horror Movies Inspired This Game (aka The Reference Section)?
2.5 - Will There Be a Sequel?
Section 3 - The Boring Stuff
3.0 - SPECIAL NOTE!
3.1 - What About Bugs?
3.2 - What License is CHIM Released Under?
3.3 - Why Isn't CHIM Open Source?
3.4 - Where Can I Find This Game?
3.5 - Game Version History
3.6 - FAQ Version History
3.7 - Acknowledgements
3.8 - Support (Such As It Is)
Section 4 - The Secrets
4.1 - The Big (well, Fairly Big) Secret
4.2 - How Do I Win?
4.3 - How (Can / Do) I Cheat?
-------------------------
Section 1 - The Beginning
-------------------------
1.0 - SPECIAL NOTE - 20 Years Later
The FAQ that you are about to read was originally written in January 1997,
when this game was first released. Since then, alot has happened, and much
of this FAQ had grown *severely* out-of-date. As a result, I've gone in and
cleaned-up some of the questions, updated several of the references in the
answers, and just generally tried to improve the usefulness of these
Frequently Asked Questions.
Thanks for taking a look!
1.1 - What is 'Camp Happy Island Massacre'?
'Camp Happy Island Massacre' (from here-on-out referred to as 'CHIM') is a
single-player computer game, with an interface not entirely unlike the old
(I prefer the term 'classic') script-adventure style of games, like
'The Hitchhiker's Guide to the Galaxy' or the original 'Zork' (among
others).
Why this interface? For several reasons:
1) This is my first full game, and I knew that I couldn't start with
anything much more involved than a script-adventure,
2) This game was programmed in C++, a language that I am still not
completely at-ease with (and don't particularly care for, TBH), and
3) I wanted to prove that a game could still be fun without it being 100+
MBs big and requiring a 3-D graphics card (something that, I feel,
far too many game-making companies don't believe).
If this sounds boring, it should be noted that the bulk of the game's
appeal lies in it's comedic elements. As one can probably tell based on the
story, the game is supposed to be a tongue-in-cheek parody of horror films,
and, I feel that the game works fairly well in a picture-less format (after
all, the works of Poe, Lovecraft, and even King are much scarier as books
than as movies for just this reason). If you disagree, though, you'll be
glad to hear that the sequel (if it ever happens) will be more visual in
nature (for more information, see the question concerning the sequel).
1.2 - Who Wrote This Wonderful Game?
I did, over 20 years ago.
My name is Eric Woodward and, at the time of this game's release, I was in
my second year of college, slowly approaching a degree in Computer Science
(which I completed in 2000).
In the 20+ years since, I've gotten married, had a couple of kids, and
managed to carve out a fairly comfortable life as a coder / web developer.
But, through it all, CHIM remains the only computer game that I ever
released (although I did manage to put out a tabletop card game earlier
this year called 'Mythic Wars: Clash of the Gods', available at many fine
gaming stores from Excalibre Games -- find out more at
https://mythicwarsgame.com!).
Oh yeah... I wrote this FAQ, too.
1.3 - What Kind of System Do I Need to Run This Game?
Initially, I'd said "you need a computer; that's about it. Anything from a
286 on up to the latest Pentium II should be sufficient to run the game",
and that was true at the time (when MS-DOS still ruled the world). However,
to run this game in 2018, you probably need a copy of DOSBox (see the next
question for more information).
Or, if you don't want to wait, you can most likely play it in your browser
*right now* on my website: https://www.itsericwoodward.com/chim/.
1.4 - How Do I Install and Run This Game?
To play the game on your own machine, you'll need a couple of different
programs. Since the game is distributed in the pkzip archive format, you
may need a program to 'unzip' it (unless your OS does it for you, like
Windows 10). Additionally, you'll need a copy of
DOSBox (https://www.dosbox.com/). If I were you, I'd unzip CHIM to its
own folder (ex: `c:\games\chim`), and startup DOSBox. Next, mount the CHIM
folder (ex: `mount m c:\games\chim`) and switch to that drive (ex: `m:`).
Finally, start the game with the `chim` command.
1.5 - How Can I Play This Game Online?
Thanks to modern software miracles like DOSBox (https://www.dosbox.com/),
Emscripten (http://emscripten.org/), and their unholy lovechild,
JS-DOS (https://js-dos.com/), you can play this game in a modern browser
on my website: https://www.itsericwoodward.com/chim/.
--------------------
Section 2 - The Game
--------------------
2.1 - The Story So Far...
Some 15 years ago, someone decided to build a summer camp on
'Happy Island', a small island in Lake Hades, near the town of Springfield.
Despite the fact that the island had been known as 'Death Island' (that is,
until the Springfield Tourism Bureau decided to rename it), or the fact
that the town's resident drunk, homeless, crazy, old Native American, John,
continually warned of a great, ancient evil living on the island, the camp
was built.
Then, the night before the campers were supposed to arrive, something
horrible happened. The next day, all 13 counselors were found dead, their
body parts scattered all over the island. Needless to say, the camp was
closed.
Then, 5 years later, someone else decided to re-open the camp, despite the
still unsolved murders of 5 years before, and despite the fact that many of
Springfield's residents had seen strange lights circling in the sky above
the island. Again, 13 counselors were brought from the surrounding areas to
help run the camp, and again, the night before the campers were to arrive,
something horrbile happened. The next day, the strange lights were gone...
and so were the counselors - nothing remained of them but a few scattered
arms and legs. And, again, the camp was closed down.
And it remained closed until 5 years later, when someone else decided to
re-open the summer camp. This person, being a little superstitious, decided
to hire 14 counselors instead of just 13, hoping to break the apparent
curse over the camp. Unbeknownst to anyone, however, the 14th counselor,
named Jason Frederick Bates-Myers (his friends called him 'Jack', but his
enemies called him 'Mr. Leather-Ghost-Face Man'), was a violent psycopath
who, on the night before the campers were to arrive, killed the 13 other
counselors using a leaf blower and homemade 'explosive' attachment. In the
end, Jack was taken to Belleward Mental Institute, and the camp was closed
down.
And that was 5 years ago. Now, they want to re-open it again, this time
with 13 brand-new counselors, and a fresh look on life. Finding counselors
was easy - after all, what teenager doesn't want to spend 6 weeks as
relatively unsupervised counselors at a co-ed summer camp, away from
parents, teachers, and other adults? So what if John, the Native American
is talking about the 'ancient evil' again? So what if the strange lights
have re-appeared? And so what if that Bates-Myers guy has escaped from
Belleward? That doesn't mean anything, right?
Wrong... Because it's the night before the campers are to arrive... And the
camp head, Ranger Bud, has gone into town... And the other 10 counselors
have all disappeared into the woods, leaving only the three of you back at
camp... And you're scared... You're scared that you might not survive...
The Camp Happy Island Massacre!
2.2 - So How Do I Play?
When you run the game, it will ask if you need instructions. Moving around
the island is accomplished via four keys: 'N' for North, 'S' for South,
'E' for East, and 'W' for West. You can't always go all four ways, so it
pays to read the descriptions of the areas you find yourself in (if
something is blocking a certain direction, you can't go that way).
Incidentally, if you can't remember where you can go, you can always use
the 'L' (Look) command to read the description again.
If you encounter an item while you are walking around, you can try to pick
it up by using the 'G' (Get) command, but don't be surprised if it tells
you that you can't pick an item up (typically, this only happens with items
that are particularly nasty or heavy). Once you have an item, you can take
a closer look at it by using the 'X' (eXamine) command, and you can try to
use it via the 'U' (Use) command. Again, don't be surprised if you beat the
game without ever using certain items - many of them are just there for fun
and can't be used at all.
The remaining commands are either used to get on-line help ('?' or 'H' for
Quick Help, or 'M' to read the Manual), or to quit the game ('Q').
Oh yeah - there are some hidden rooms on the island, one of which you will
have to find in order to beat the game... Good Luck.
2.3 - Is There a Map?
Included in the distribution should be "MAP.TXT", a map in UTF-8 text-file
format (if your distribution didn't include one, get a new distribution).
Granted, this map is only slightly helpful, as it doesn't have any actual
trails drawn on it, just major landmarks. If you were in fact looking for
a map of trails... sorry, but there isn't one (at least, there isn't one
that I'm distributing...) I feel that having that kind of a map would
subtract SEVERELY from the game's fun-factor. So, in short, make due with
what you got, and if you are an enterprising individual and wish to make
a map, it shouldn't be too hard.
2.4 - What Horror Movies Inspired This Game (aka The Reference Section)?
This game was, more or less, inspired by a whole slew of movies. Some are
more obvious than others (the many and varied 'Friday the 13th' movies
stand out in my mind), and sometimes it isn't even movies that inspired
certain elements of this game, but rather books (like the works of one H.P.
Lovecraft). Regardless, here's a brief list of movies and movie series that
inspired me:
Evil Dead (including Army of Darkness), Scream, Friday the 13th, Psycho,
Hellraiser, A Nightmare on Elm Street, Nightbreed, Carrie, Firestarter,
Phantasm, Halloween, Texas Chainsaw Massacre, The Re-Animator, Bad Taste,
Puppet Master, Dead Alive (Braindead), The Frighteners, Alien, Showgirls,
Night of the Living Dead, There's Nothing Out There, and countless others.
2.5 - Will There Be a Sequel?
Initially, I had planned to make one, "Cause nowadays, ya' gotta have a
sequel!". I had even begun work on one, a CD-ROM-based game complete with
full-motion video, special effects, even a script! But, it never came to
fruition, and by now, the code is lost to time (along with the video,
the photos, and the age of most of the original cast).
On the other hand, I have considered making a board or card game based on
the characters and concepts from CHIM, so... Who knows what the future has
in store for CHIM?
----------------------------
Section 3 - The Boring Stuff
----------------------------
3.0 - SPECIAL NOTE!
This version (v1.0) of the game will be the only version ever to be
released. This was not my original intention; I had fully intended to fix
any and all bugs that turned up, colorize the output (to make it pretty),
and then re-release it. Unfortunately, the best laid plans of mice and
men (and me) can't stand up to a total hard-drive loss, especially when
the last remaining copy of the source code was contained therein. So, you
can keep playing CHIM all you want, but there almost certainly won't be a
new version of it, ever. We now return you to your regularly scheduled FAQ.
3.1 - What About Bugs?
What Bugs? I write perfect programs.
Not really. Despite my best efforts, my program does have some unintended
(and unwanted) 'features'. The complete (to my knowledge) list is below:
- Some problems with garbage characters popping into descriptions of some
of the areas (caused by a char-pointer problem).
If you find a bug that isn't listed here, please feel free to e-mail me
at hey@itsericwoodward.com, or snail-mail them to me at the address listed
a little later.
3.2 - What License is CHIM Released Under?
CHIM was originally released as "<Whatever>-ware", wherein I asked people
who enjoyed the game to "send me something (preferably not something that
is alive, ticking, or that has a strange odor)." In the 20 years since the
game's original release, free software licensing has grown by leaps and
bounds (plus, I'm kind of afraid to ask people to send me things now).
As a result of these factors, I'm re-releasing CHIM under the so-called
MIT License (a copy of which should have been included in your ZIP file
distribution as "LICENSE.TXT") and which can be found at:
https://www.itsericwoodward.com/chim/LICENSE.TXT
3.3 - Is CHIM Open Source?
Well, it would be, but (as noted above), the code was lost during a hard-
drive crash in mid-1997 (just a couple of months after the game was
released). As a result, there is no source to open (otherwise, I would
definitely have re-released it under an open-source license years ago).
3.4 - Where Can I Find This Game?
The latest version of the game, FAQ, and other related files can always be
found at:
https://www.itsericwoodward.com/chim/
3.5 - Game Version History
v0.9b - released on 10/31/97 (Halloween) - Original Beta Release
v1.0 - released on 01/05/97 - Original Full Release
- Fixed small problem with movement and the map
- Made output prettier
3.6 - FAQ Version History
v1.0 - released on 01/05/1997 - Original Full Release
v1.1 - released on 10/10/2018 - 21 Years Worth of Updates
3.7 - Acknowledgements
There are many people I want to thank, but I can't, so I'll just hit the
major ones...
- Mark, Donnie, Tommy, Jeff, and Sarah for not laughing at me when I
mentioned that I was writing a game;
- My playtester, Mike, for actually doing playtesting (maps and all);
- All of my professors and friends at school for their help, both direct
and implicit;
- And my wonderful wife, Stacie, for supporting me.
3.8 - Support (Such As It Is)
I offer no promises for support, but feel free to contact me at any of the
following addresses / locations:
Email: hey@itsericwoodward.com
WWW: https://www.itsericwoodward.com/chim/
Fediverse: @eric@social.wonderdome.net
-----------------------
Section 4 - The Secrets
-----------------------
WARNING! - This section contains many gratuitous spoilers. If you read this
section, there is a chance that some of the fun of the game will be lost.
So, I urge you to skip this section entirely. Just so no secrets get out,
let me fill up the rest of this space with, um, filler:
Alright... You asked for it...
4.1 - The Big (well, Fairly Big) Secret
The Fairly Big Secret is that the game doesn't just have 1 ending. Each
time the game is loaded, there is a random chance that the killer will be
either a group of aliens, an unnamable Thing, or a vengeful psychopath
(Jason Frederick Bates-Myers, to be more specific).
4.2 - How Do I Win?
First off, you MUST find the radio tower and signal for help (it may sound
like the message didn't get out, but rest assured that it did).
After that, it depends on who the killer is.
- If it's the Aliens - you need to get the credit card type-thing out of
the cave, take it to the secret room (in the lodge) and use it, and then
go to the baseball field, where the ship will land.
- If it's the Thing - you need to get the Unholy Book from the secret room
(in the lodge), take it to the baseball field and use it, and then go to
the cave, where the thing will be waiting.
- If it's the Psycho - you need to get the flashlight from the baseball
field, take it to the cave and use it to read the words on the wall, and
then you need to go to the secret room (in the lodge) to confront Jason.
4.3 - How (Can / Do) I Cheat?
Well, it seems to me that that's exactly what you're doing right now. But,
you can cheat by entering one or two command line arguments when executing
the game. They are called by typing 'chim -arg1 -arg2' where the args
can be:
'-nokiller' - tired of the killer getting you before you can get him? Use
this little switch to prevent him from ever finding you until the end of
the game.
'-aliens' - forces the bad guys to be the aliens.
'-thing' - forces the bad guy to be the unnamable thing.
'-psycho' - forces the bad guy to be Jason Frederick Bates-Myers.
So, to make the bad guys be the aliens, type 'chim -aliens' at the
command prompt. Or, to make it be the psycho, and to keep him from killing
you first, type 'chim -nokiller -psycho'. Of course, entering the
command 'chim -aliens -thing' doesn't work; the bad guy winds up being
the thing.
And that, my friend, is how you cheat.

Binary file not shown.

View File

@ -0,0 +1,9 @@
Camp Happy Island Massacre v1.0 (1997):
Tongue-in-cheek menu-driven text game,
inspired by 80s horror movies. Play as
the only surviving counselors of an ill-
fated summer camp as you try to figure
out who (or what) is killing everyone
(and how you can survive). 1 player.
Freeware (MIT License) by Eric Woodward.
https://www.itsericwoodward.com/chim/

View File

@ -0,0 +1,22 @@
┌─────┐ ┌──────┐ ┌───────┐ ┌─────┐ ┌─┐ ┌─┐ ┌──────┐ ┌─────┐ ┌─────┐ ┌─┐ ┌─┐
│ ┌─┐ │ │ ┌──┐ │ │ ┌┐ ┌┐ │ │ ┌─┐ │ │ │ │ │ │ ┌──┐ │ │ ┌─┐ │ │ ┌─┐ │ │ │ │ │
│ │ └─┘ │ └──┘ │ │ ││ ││ │ │ └─┘ │ │ └─┘ │ │ └──┘ │ │ └─┘ │ │ └─┘ │ │ └─┘ │
│ │ ┌─┐ │ ┌──┐ │ │ │└─┘│ │ │ ┌───┘ │ ┌─┐ │ │ ┌──┐ │ │ ┌───┘ │ ┌───┘ └─┐ ┌─┘
│ └─┘ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
└─────┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘ └─┘
┌───┐ ┌─────┐ ┌─┐ ┌──────┐ ┌───┐ ┌─┐ ┌──────┐
└┐ ┌┘ │ ┌───┘ │ │ │ ┌──┐ │ │ └┐ │ │ └┐ ┌─┐ │
│ │ │ └───┐ │ │ │ └──┘ │ │ ├┐ └┐│ │ │ │ │ │
│ │ └───┐ │ │ │ │ ┌──┐ │ │ │└┐ └┤ │ │ │ │ │
┌┘ └┐ ┌───┘ │ │ └──┐ │ │ │ │ │ │ └┐ │ ┌┘ └─┘ │
└───┘ └─────┘ └────┘ └─┘ └─┘ └─┘ └───┘ └──────┘
▄██████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ▄█████▄ ██████▄ ▄██████ ██
██ ██ ██ ██▄▄▄██ ██▄▄▄▄ ██▄▄▄▄ ██▄▄▄██ ██ ██ ██ ██▄▄▄▄ ██
██ ██ ██ ██▀▀▀██ ▀▀▀▀██ ▀▀▀▀██ ██▀▀▀██ ██ ██████ ██▀▀▀▀ ▀▀
██ ██ ██ ██ ██ ▀█████▀ ▀█████▀ ██ ██ ▀█████▀ ██ ▀██ ▀██████ ██
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
"We Have Such Sights To Show You... "

View File

@ -0,0 +1,22 @@
Copyright (c) 1997-2018 Eric Woodward (https://itsericwoodward.com).
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.
Camp Happy Island Massacre and all characters and locations from the software
are trademarks of Eric Woodward.

View File

@ -0,0 +1,24 @@
 /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\ /\
/^\/^\/^\/^\ T H E M O U N T A I N S /^\/^\/^\/^\
/^\/^\/^\/^\/^\/^\/^\/^\/^\/^\(*)/^\/^\/^\/^\/^\/^\/^\
|#++++++++++++++++++++++++++++ ^--- Cave(?) ++++++++#|
|#+ o ++++++++++++++++++++++++++++++++++++++++++++++#|
|#+ | ++++++++++++++++++++++++++++++++++++++++++++++#|
|#+ | <-- Radio Tower ++++++++++++++++++++++ [] ++++#|
LAKE |# /-\ +++++++++++++++++++++++++++++++++++ / \ ++#| LAKE
HADES |#++++++++++++++++++++++++++++++++++++ > [] [] #| HADES
|#++++++++ /^\ <-- Hunting ++++++++++ | ++ \ / ++#|
|#++++++ /| |\ Lodge(?) +++++++++ | ++++ [] ++++#|
|#+++++++ |___| +++++++++++++++++++++ | ++++++++++++#|
|#+++++++++++++++++++++++++++++++++++ \- Baseball ++#|
|#+++++++++++++++++++++++++++++++++++++++ Field(?) +#|
|#++ [====] ++++++++++++++++++++++++++++++++++++++++#|
|#++ |Camp| ++++++++++++++++++++++++++++++++++++++++#|
\#+ [====] +++++++++++++++++++++++++++++++++++++++#/
\##############################################/
--------------------------------------------
# = Beach LAKE Note: Map is NOT
+ = Woods HADES drawn to scale.
ASCII Map for "Camp Happy Island Massacre" by Eric Woodward,
playable online at https://www.itsericwoodward.com/chim/.

View File

@ -0,0 +1,88 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
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
cTTc
=tIaI
-----END PGP PUBLIC KEY BLOCK-----

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,43 @@
Copyright (c) 2013, Natanael Gama (www.ndiscovered.com . info(at)ndiscovered.com), with Reserved Font Name Exo.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Some files were not shown because too many files have changed in this diff Show More