Files
Josh Goldberg d9c385d469 Loading saved game files works
Hurray. Added a bit more functionality to MenuGraphr and StateHoldr.
2015-02-15 13:13:55 -05:00

872 lines
22 KiB
JavaScript

/**
* MenuGraphr.js
*
*
*
* @author "Josh Goldberg" <josh@fullscreenmario.com>
*/
function MenuGraphr(settings) {
"use strict";
if (this === window) {
return new PixelDrawr(settings);
}
var self = this,
EightBitter,
menus,
activeMenu,
schemas,
aliases,
replacements,
replacerKey,
replaceFromStatsHolder,
replacementStatistics;
/**
*
*/
self.reset = function (settings) {
EightBitter = settings.EightBitter;
schemas = settings.schemas || {};
aliases = settings.aliases || {};
replacements = settings.replacements || {};
replacerKey = settings.replacerKey || "%%%%%%%";
replaceFromStatsHolder = settings.replaceFromStatsHolder;
replacementStatistics = settings.replacementStatistics;
menus = {};
};
/* Simple gets
*/
/**
*
*/
self.getMenus = function () {
return menus;
};
/* Menu positioning
*/
/**
*
*/
self.createMenu = function (name, attributes) {
var schema = schemas[name],
menu = EightBitter.ObjectMaker.make("Menu", schema),
container = schema.container
? menus[schema.container]
: {
"top": 0,
"left": 0,
"right": EightBitter.MapScreener.width,
"bottom": EightBitter.MapScreener.height,
"width": Math.ceil(
EightBitter.MapScreener.width / EightBitter.unitsize
),
"height": Math.ceil(
EightBitter.MapScreener.height / EightBitter.unitsize
),
"EightBitter": EightBitter
};
self.deleteMenu(name);
menus[name] = menu;
menu.name = name;
self.positionItem(menu, schema.size, schema.position, container);
menu.children = [];
menu.textAreaWidth = (menu.width - menu.textXOffset * 2) * EightBitter.unitsize;
if (menu.childrenSchemas) {
menu.childrenSchemas.forEach(self.createChild.bind(undefined, name));
}
if (container.children) {
container.children.push(menu);
}
EightBitter.proliferate(menu, attributes);
};
/**
*
*/
self.createChild = function (name, schema) {
switch (schema.type) {
case "menu":
self.createMenu(schema.name);
break;
case "text":
self.createMenuWord(name, schema);
break;
case "thing":
self.createMenuThing(name, schema);
break;
}
};
/**
*
*/
self.createMenuWord = function (name, schema) {
var menu = menus[name],
container = EightBitter.ObjectMaker.make("Menu");
self.positionItem(container, schema.size, schema.position, menu, true);
menu.textX = container.left;
self.addMenuWord(name, schema.words, 0, container.left, container.top)
};
/**
*
*/
self.createMenuThing = function (name, schema) {
var menu = menus[name],
thing = EightBitter.ObjectMaker.make(schema.thing, schema.args);
self.positionItem(thing, schema.size, schema.position, menu);
EightBitter.GroupHolder.switchObjectGroup(
thing,
thing.groupType,
"Text"
);
menu.children.push(thing);
return thing;
};
/**
*
*/
self.deleteMenu = function (name) {
var menu = menus[name];
if (menu) {
self.deleteMenuChild(menu);
}
};
/**
*
*/
self.deleteActiveMenu = function () {
self.deleteMenu(activeMenu.name);
};
/**
*
*/
self.deleteMenuChild = function (child) {
if (activeMenu === child) {
if (child.backMenu) {
self.setActiveMenu(child.backMenu);
} else {
activeMenu = undefined;
}
}
if (child.killOnB) {
child.killOnB.forEach(self.deleteMenu);
}
if (child.name) {
delete menus[child.name];
}
EightBitter.killNormal(child);
self.deleteMenuChildren(name);
if (child.onDelete) {
child.onDelete.call(EightBitter);
}
if (child.children) {
child.children.forEach(self.deleteMenuChild)
}
};
/**
*
*/
self.deleteMenuChildren = function (name) {
var menu = menus[name];
if (menu && menu.children) {
menu.children.forEach(self.deleteMenuChild);
}
};
/**
*
*/
self.positionItem = function (item, size, position, container, skipAdd) {
var offset, i;
if (!position) {
position = {};
offset = {};
} else {
offset = position.offset || {};
}
if (!size) {
size = {};
}
if (size.width) {
EightBitter.setWidth(item, size.width);
} else if (position.horizontal === "stretch") {
EightBitter.setLeft(item, 0);
EightBitter.setWidth(
item,
container.width - (offset.left || 0) - (offset.right || 0)
);
}
if (size.height) {
EightBitter.setHeight(item, size.height);
} else if (position.vertical === "stretch") {
EightBitter.setTop(item, 0);
EightBitter.setHeight(
item,
container.height - (offset.top || 0) - (offset.bottom || 0)
);
}
switch (position.horizontal) {
case "center":
EightBitter.setMidXObj(item, container);
break;
case "right":
EightBitter.setRight(item, container.right);
break;
default:
EightBitter.setLeft(item, container.left);
break;
}
switch (position.vertical) {
case "center":
EightBitter.setMidYObj(item, container);
break;
case "bottom":
EightBitter.setBottom(item, container.bottom);
break;
default:
EightBitter.setTop(item, container.top);
break;
}
if (offset.top) {
EightBitter.shiftVert(
item, position.offset.top * EightBitter.unitsize
);
}
if (offset.left) {
EightBitter.shiftHoriz(
item, position.offset.left * EightBitter.unitsize
);
}
if (!skipAdd) {
EightBitter.addThing(item, item.left, item.top);
}
};
/* Menu text
*/
/**
*
*/
self.addMenuDialog = function (name, dialog, onCompletion) {
if (dialog.constructor === String) {
dialog = [dialog];
}
self.addMenuText(name, dialog[0], function () {
if (dialog.length === 1) {
if (onCompletion) {
onCompletion();
}
return true;
} else {
self.deleteMenuChildren(name);
self.addMenuDialog(name, dialog.slice(1), onCompletion);
return false;
}
});
};
/**
*
*/
self.addMenuText = function (name, words, onCompletion) {
var menu = menus[name],
x = EightBitter.getMidX(menu) - menu.textAreaWidth / 2,
y = menu.top + menu.textYOffset * EightBitter.unitsize;
if (words.constructor === String) {
words = words.split(/ /);
}
menu.callback = self.continueMenu;
menu.textX = x;
self.addMenuWord(name, words, 0, x, y, onCompletion);
};
/**
*
*
* @todo The calculation of whether a word can fit assumes equal width for
* all children, although apostrophes are tiny.
*/
self.addMenuWord = function (name, words, i, x, y, onCompletion) {
var menu = menus[name],
word = filterWord(words[i]),
textProperties = EightBitter.ObjectMaker.getPropertiesOf("Text"),
textWidth, textHeight, textPaddingX, textPaddingY, textSpeed,
title, character, j;
if (word.constructor === Object && word.command) {
switch (word.command) {
case "attribute":
menu[word.attribute + "Old"] = menu[word.attribute];
menu[word.attribute] = word.value;
if (word.applyUnitsize) {
menu[word.attribute] *= EightBitter.unitsize;
}
break;
case "attributeReset":
menu[word.attribute] = menu[word.attribute + "Old"];
break;
case "position":
if (word.x) {
x += word.x;
}
if (word.y) {
y += word.y;
}
break;
}
}
textSpeed = menu.textSpeed;
textWidth = (menu.textWidth || textProperties.width) * EightBitter.unitsize,
textHeight = (menu.textHeight || textProperties.height) * EightBitter.unitsize,
textPaddingX = (menu.textPaddingX || textProperties.paddingX) * EightBitter.unitsize;
textPaddingY = (menu.textPaddingY || textProperties.paddingY) * EightBitter.unitsize;
if (word.constructor === Object && word.command) {
switch (word.command) {
case "padLeft":
word = EightBitter.stringOf(
" ", word.length - filterWord(word.word).length
) + filterWord(word.word);
break;
}
}
if (word.constructor === String && word !== "\n") {
for (j = 0; j < word.length; j += 1) {
if (word[j] !== " ") {
title = "Char" + getCharacterEquivalent(word[j]);
character = EightBitter.ObjectMaker.make(title);
character.paddingY = textPaddingY;
menu.children.push(character);
if (textSpeed) {
EightBitter.TimeHandler.addEvent(
EightBitter.addThing.bind(EightBitter),
j * textSpeed,
character,
x,
y
);
} else {
EightBitter.addThing(character, x, y);
}
if (menu.textWidthMultiplier) {
x += menu.textWidthMultiplier * (
character.width * EightBitter.unitsize + textPaddingX
);
} else {
x += character.width * EightBitter.unitsize + textPaddingX;
}
} else {
x += textWidth;
}
}
}
if (i === words.length - 1) {
menu.progress = {
"complete": true,
"onCompletion": onCompletion
};
return;
}
if (!word.skipSpacing) {
if (
word === "\n"
|| (
x + (
(filterWord(words[i + 1]).length + 1) * textWidth
+ menu.textXOffset * EightBitter.unitsize
)
> EightBitter.getMidX(menu) + menu.textAreaWidth / 2
)
) {
x = menu.textX;
y += textPaddingY;
} else {
x += textWidth;
}
}
if (y >= menu.bottom - (menu.textYOffset - 1) * EightBitter.unitsize) {
menu.progress = {
"words": words,
"i": i + 1,
"x": x,
"y": y - (textPaddingY),
"onCompletion": onCompletion
};
return;
}
if (textSpeed) {
EightBitter.TimeHandler.addEvent(
self.addMenuWord,
(j + 1) * textSpeed,
name,
words,
i + 1,
x,
y,
onCompletion
);
} else {
self.addMenuWord(name, words, i + 1, x, y, onCompletion);
}
}
/**
*
*/
self.continueMenu = function (name) {
var menu = menus[name],
children = menu.children,
progress = menu.progress,
character, i;
if (!progress || progress.working) {
return;
}
if (progress.complete) {
if (!progress.onCompletion || progress.onCompletion(EightBitter, menu)) {
self.deleteMenu(name);
}
return;
}
progress.working = true;
for (i = 0; i < children.length; i += 1) {
character = children[i];
EightBitter.TimeHandler.addEventInterval(
scrollCharacterUp,
1,
character.paddingY / EightBitter.unitsize,
character,
menu,
-1
);
}
EightBitter.TimeHandler.addEvent(
self.addMenuWord,
character.paddingY / EightBitter.unitsize + 1,
name,
progress.words,
progress.i,
progress.x,
progress.y,
progress.onCompletion
);
}
/* Lists
*/
/**
*
*/
self.addMenuList = function (name, settings) {
var menu = menus[name],
options = settings.options instanceof Function
? settings.options()
: settings.options,
index = settings.index || 0,
left = menu.left + menu.textXOffset * EightBitter.unitsize,
top = menu.top + menu.textYOffset * EightBitter.unitsize,
textProperties = EightBitter.ObjectMaker.getPropertiesOf("Text"),
textWidth = (menu.textWidth || textProperties.width) * EightBitter.unitsize,
textHeight = (menu.textWidth || textProperties.height) * EightBitter.unitsize,
textPaddingY = (menu.textPaddingY || textProperties.paddingY) * EightBitter.unitsize,
arrowXOffset = (menu.arrowXOffset || 0) * EightBitter.unitsize,
arrowYOffset = (menu.arrowYOffset || 0) * EightBitter.unitsize,
option, schema, title, character,
y = top,
x, i, j;
menu.options = options;
for (i = 0; i < options.length; i += 1) {
x = left;
option = options[i];
if (option.things) {
for (j = 0; j < option.things.length; j += 1) {
schema = option.things[j];
character = self.createMenuThing(name, schema);
menu.children.push(character);
if (!schema.position || !schema.position.relative) {
EightBitter.shiftVert(character, y - menu.top);
}
}
}
schema = filterWord(option.text);
if (schema !== "\n") {
for (j = 0; j < schema.length; j += 1) {
if (schema[j].command) {
if (schema[j].x) {
x += schema[j].x * EightBitter.unitsize;
}
if (schema[j].y) {
y += schema[j].y * EightBitter.unitsize;
}
} else if (schema[j] !== " ") {
title = "Char" + getCharacterEquivalent(schema[j]);
character = EightBitter.ObjectMaker.make(title);
menu.children.push(character);
EightBitter.addThing(character, x, y);
x += character.width * EightBitter.unitsize;
} else {
x += textWidth;
}
}
}
y += textPaddingY;
}
menu.selectedIndex = index;
character = EightBitter.ObjectMaker.make("CharArrowRight");
menu.children.push(character);
menu.arrow = character;
character.hidden = (activeMenu !== menu);
menu.callback = self.selectMenuListOption;
menu.onActive = self.activateMenuList;
menu.onInactive = self.deactivateMenuList;
EightBitter.addThing(
character,
menu.left + arrowXOffset,
top + arrowYOffset + index * textPaddingY
);
};
/**
*
*/
self.activateMenuList = function (name) {
menus[name].arrow.hidden = false;
};
/**
*
*/
self.deactivateMenuList = function (name) {
menus[name].arrow.hidden = true;
};
/**
*
*/
self.shiftMenuArrow = function (name, difference) {
var menu = menus[name],
textProperties = EightBitter.ObjectMaker.getPropertiesOf("Text"),
textWidth = textProperties.width * EightBitter.unitsize,
textHeight = textProperties.height * EightBitter.unitsize,
textPaddingY = (menu.textPaddingY || textProperties.paddingY) * EightBitter.unitsize;
if (difference > 0) {
if (difference + menu.selectedIndex > menu.options.length - 1) {
difference = Math.min(
menu.options.length - menu.selectedIndex,
0
);
}
} else {
if (difference + menu.selectedIndex < 0) {
difference = Math.max(menu.selectedIndex, 0);
}
}
if (!difference) {
return;
}
menu.selectedIndex += difference;
EightBitter.shiftVert(menu.arrow, difference * textPaddingY);
};
/**
*
*/
self.selectMenuListOption = function (name, index) {
var menu = menus[name],
selected = menu.options[index || menu.selectedIndex];
if (selected.callback) {
selected.callback(name);
}
};
/* Interactivity
*/
/**
*
*/
self.setActiveMenu = function (name) {
if (activeMenu && activeMenu.onInactive) {
activeMenu.onInactive(activeMenu.name);
}
activeMenu = menus[name];
if (activeMenu.onActive) {
activeMenu.onActive(name);
}
};
/**
*
*/
self.getActiveMenu = function () {
return activeMenu;
};
/**
*
*/
self.getActiveMenuName = function () {
return activeMenu.name;
};
/**
*
*/
self.registerLeft = function () {
if (!activeMenu) {
return;
}
};
/**
*
*/
self.registerRight = function () {
if (!activeMenu) {
return;
}
};
/**
*
*/
self.registerUp = function () {
if (!activeMenu) {
return;
}
if (typeof activeMenu.selectedIndex !== "undefined") {
self.shiftMenuArrow(activeMenu.name, -1);
}
};
/**
*
*/
self.registerDown = function () {
if (!activeMenu) {
return;
}
if (typeof activeMenu.selectedIndex !== "undefined") {
self.shiftMenuArrow(activeMenu.name, 1);
}
};
/**
*
*/
self.registerA = function () {
if (!activeMenu) {
return;
}
if (activeMenu.callback) {
activeMenu.callback(activeMenu.name);
}
};
/**
*
*/
self.registerB = function () {
if (!activeMenu) {
return;
}
if (activeMenu.keepOnBack) {
self.setActiveMenu(activeMenu.backMenu);
} else {
self.deleteMenu(activeMenu.name);
}
};
/**
*
*/
self.registerStart = function () {
if (!activeMenu) {
return;
}
if (activeMenu.startMenu) {
self.setActiveMenu(activeMenu.startMenu);
} else if (activeMenu.callback) {
activeMenu.callback(activeMenu.name);
}
};
/* Utilities
*/
/**
*
*/
function scrollCharacterUp(character, menu) {
EightBitter.shiftVert(character, -EightBitter.unitsize);
if (character.top < menu.top + (menu.textYOffset - 1) * EightBitter.unitsize) {
EightBitter.killNormal(character);
return true;
}
}
/**
*
*/
function getCharacterEquivalent(character) {
if (aliases.hasOwnProperty(character)) {
return aliases[character];
}
return character;
}
/**
*
*/
function filterWord(word) {
var start = 0,
end, inside;
if (word.constructor !== String) {
return word;
}
while (true) {
start = word.indexOf("%%%%%%%", start);
end = word.indexOf("%%%%%%%", start + 1);
if (start === -1 || end === -1) {
return word;
}
inside = word.substring(start + "%%%%%%%".length, end);
word =
word.substring(0, start)
+ getReplacement(inside)
+ word.substring(end + "%%%%%%%".length);
start = end;
}
return word;
}
/**
*
*/
function getReplacement(key) {
var value = replacements[key];
if (typeof value === "undefined") {
return value;
}
if (replacementStatistics && replacementStatistics[value]) {
return replacements[value](EightBitter);
}
if (replaceFromStatsHolder) {
if (EightBitter.StatsHolder.hasKey(value)) {
return EightBitter.StatsHolder.get(value);
}
}
return value;
}
self.reset(settings || {});
}