mirror of
https://github.com/Oreolek/undum.git
synced 2024-05-17 00:18:17 +03:00
Removed mobile detection and progress bars
My goal is to make Undum more hackable, the JS code faster and more readable. The mobile detection and progress bars are visual effects. A framework doesn not need many of the visual effects and they should be done in CSS, not in JS. Also, useragent sniffing is bad.
This commit is contained in:
parent
64b0a0c282
commit
1e16ea7be7
10
README.md
10
README.md
|
@ -38,6 +38,16 @@ scripting system with limited functionality. You can take control of
|
|||
anything you want. Or you can just keep things simple using a bunch of
|
||||
simple functions provided by Undum.
|
||||
|
||||
## What is the difference from the original Undum?
|
||||
|
||||
* This branch is rewritten on pure Javascript. You are no longer bound by jQuery.
|
||||
Also, the code is more hackable now.
|
||||
|
||||
* The code uses Gulp as build system, making it even more hack-friendly.
|
||||
|
||||
* The code no longer thinks about CSS design. It should not think about whether the player is on mobile or not, it is a job for CSS. Also, animations are all CSS.
|
||||
|
||||
* No progress bars at the moment, so no animateQuality.
|
||||
|
||||
## Compatibility
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -47,12 +47,6 @@ var hasLocalStorage = function() {
|
|||
return hasStorage;
|
||||
};
|
||||
|
||||
var isMobileDevice = function() {
|
||||
return (navigator.userAgent.toLowerCase().search(
|
||||
/iphone|ipad|palm|blackberry|android/
|
||||
) >= 0 || document.querySelectorAll('html').offsetWidth <= 640);
|
||||
};
|
||||
|
||||
/// Animations - you can totally redefine these! Fade in and fade out by default.
|
||||
/// @param id string or object
|
||||
var showBlock = function(id) {
|
||||
|
@ -66,11 +60,24 @@ var showBlock = function(id) {
|
|||
}
|
||||
|
||||
var hideBlock = function(id) {
|
||||
var block = id; // typeof block === "element"
|
||||
if (typeof id === "string") {
|
||||
var block = document.getElementById(id);
|
||||
}
|
||||
if (typeof id === "element") {
|
||||
var block = id;
|
||||
if (typeof id === "object") { // probably NodeList
|
||||
if (id.length == 0)
|
||||
return;
|
||||
Array.prototype.forEach.call(id, function(element, index) {
|
||||
element.classList.add('hide');
|
||||
element.classList.remove('show');
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (typeof block.classList === "undefined")
|
||||
{
|
||||
console.log("Tried to hide an undefined block.");
|
||||
console.log(id);
|
||||
return;
|
||||
}
|
||||
block.classList.add('hide');
|
||||
block.classList.remove('show');
|
||||
|
@ -111,4 +118,3 @@ var extend = function(out) {
|
|||
|
||||
return out;
|
||||
};
|
||||
|
||||
|
|
109
src/private.js
109
src/private.js
|
@ -31,9 +31,6 @@ var character = null;
|
|||
/* Tracks whether we're in interactive mode or batch mode. */
|
||||
var interactive = true;
|
||||
|
||||
/* Tracks whether we're mobile or not. */
|
||||
var mobile = isMobileDevice();
|
||||
|
||||
/* The system time when the game was initialized. */
|
||||
var startTime;
|
||||
|
||||
|
@ -87,29 +84,28 @@ var parseFn = function(str) {
|
|||
var loadHTMLSituations = function() {
|
||||
var $htmlSituations = document.querySelectorAll("div.situation");
|
||||
Array.prototype.forEach.call($htmlSituations, function($situation){
|
||||
var id = $situation.getAttribute("id");
|
||||
assert(game.situations[id] === undefined,
|
||||
"existing_situation".l({id:id}));
|
||||
var id = $situation.getAttribute("id");
|
||||
assert(game.situations[id] === undefined, "existing_situation".l({id:id}));
|
||||
|
||||
var content = $situation.innerHTML;
|
||||
var opts = {
|
||||
var content = $situation.innerHTML;
|
||||
var opts = {
|
||||
// Situation content
|
||||
optionText: $situation.getAttribute("data-option-text"),
|
||||
canView: parseFn($situation.getAttribute("data-can-view")),
|
||||
canChoose: parseFn($situation.getAttribute("data-can-choose")),
|
||||
priority: parse($situation.getAttribute("data-priority")),
|
||||
frequency: parse($situation.getAttribute("data-frequency")),
|
||||
displayOrder: parse($situation.getAttribute("data-display-order")),
|
||||
tags: parseList($situation.getAttribute("data-tags"), false),
|
||||
// Simple Situation content.
|
||||
heading: $situation.getAttribute("data-heading"),
|
||||
choices: parseList($situation.getAttribute("data-choices"), true),
|
||||
minChoices: parse($situation.getAttribute("data-min-choices")),
|
||||
maxChoices: parse($situation.getAttribute("data-max-choices"))
|
||||
};
|
||||
optionText: $situation.getAttribute("data-option-text"),
|
||||
canView: parseFn($situation.getAttribute("data-can-view")),
|
||||
canChoose: parseFn($situation.getAttribute("data-can-choose")),
|
||||
priority: parse($situation.getAttribute("data-priority")),
|
||||
frequency: parse($situation.getAttribute("data-frequency")),
|
||||
displayOrder: parse($situation.getAttribute("data-display-order")),
|
||||
tags: parseList($situation.getAttribute("data-tags"), false),
|
||||
// Simple Situation content.
|
||||
heading: $situation.getAttribute("data-heading"),
|
||||
choices: parseList($situation.getAttribute("data-choices"), true),
|
||||
minChoices: parse($situation.getAttribute("data-min-choices")),
|
||||
maxChoices: parse($situation.getAttribute("data-max-choices"))
|
||||
};
|
||||
|
||||
game.situations[id] = new SimpleSituation(content, opts);
|
||||
});
|
||||
game.situations[id] = new SimpleSituation(content, opts);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
@ -175,8 +171,8 @@ var showHighlight = function(domElement) {
|
|||
}
|
||||
showBlock(highlight);
|
||||
setTimeout(function() {
|
||||
hideBlock(highlight);
|
||||
}, 2000);
|
||||
hideBlock(highlight);
|
||||
}, 2000);
|
||||
};
|
||||
|
||||
/* Finds the correct location and inserts a particular DOM element
|
||||
|
@ -308,8 +304,8 @@ var continueOutputTransaction = function() {
|
|||
var endOutputTransaction = function() {
|
||||
var scrollPoint = scrollStack.pop();
|
||||
if (scrollStack.length === 0 && scrollPoint !== null) {
|
||||
if (interactive && !mobile) {
|
||||
$("body, html").animate({scrollTop: scrollPoint}, 500);
|
||||
if (interactive) {
|
||||
window.scroll(0,scrollPoint);
|
||||
}
|
||||
scrollPoint = null;
|
||||
}
|
||||
|
@ -421,33 +417,23 @@ var doTransitionTo = function(newSituationId) {
|
|||
if (game.exit) {
|
||||
game.exit(character, system, oldSituationId, newSituationId);
|
||||
}
|
||||
|
||||
// Remove links and transient sections.
|
||||
$('#content a').each(function(index, element) {
|
||||
var a = $(element);
|
||||
if (a.hasClass('sticky') || a.attr("href").match(/[?&]sticky[=&]?/))
|
||||
return;
|
||||
a.replaceWith($("<span>").addClass("ex_link").html(a.html()));
|
||||
});
|
||||
var contentToHide = $('#content .transient, #content ul.options');
|
||||
contentToHide.add($("#content a").filter(function(){
|
||||
return this.attr("href").match(/[?&]transient[=&]?/);
|
||||
}));
|
||||
if (interactive) {
|
||||
if (mobile) {
|
||||
contentToHide.fadeOut(2000);
|
||||
} else {
|
||||
contentToHide.
|
||||
animate({opacity: 0}, 1500).
|
||||
slideUp(500, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
contentToHide.remove();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove links and transient sections.
|
||||
var content = document.getElementById("content");
|
||||
links = content.querySelectorAll("a");
|
||||
Array.prototype.forEach.call(links, function(element, index) {
|
||||
var a = element;
|
||||
if (a.classList.contains('sticky') || a.getAttribute("href").match(/[?&]sticky[=&]?/))
|
||||
return;
|
||||
if (a.getAttribute("href").match(/[?&]transient[=&]?/)) {
|
||||
hideBlock(a);
|
||||
}
|
||||
a.innerHTML = "<span class='ex_link'>"+a.innerHTML+"</span>";
|
||||
});
|
||||
hideBlock(content.querySelectorAll(".transient"));
|
||||
hideBlock(content.querySelectorAll("ul.options"));
|
||||
|
||||
// Move the character.
|
||||
current = newSituationId;
|
||||
|
||||
|
@ -473,26 +459,25 @@ var augmentLinks = function(content) {
|
|||
output.innerHTML = content;
|
||||
var links = output.querySelectorAll("a");
|
||||
Array.prototype.forEach.call(links, function(element, index){
|
||||
var href = element.getAttribute('href');
|
||||
if (!element.classList.contains("raw")|| href.match(/[?&]raw[=&]?/)) {
|
||||
var href = element.getAttribute('href');
|
||||
if (!element.classList.contains("raw")|| href.match(/[?&]raw[=&]?/)) {
|
||||
if (href.match(linkRe)) {
|
||||
element.addEventListener("click", function(event) {
|
||||
element.onclick = function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
// If we're a once-click, remove all matching
|
||||
// links.
|
||||
// If we're a once-click, remove all matching links.
|
||||
if (element.classList.contains("once") || href.match(/[?&]once[=&]?/)) {
|
||||
system.clearLinks(href);
|
||||
system.clearLinks(href);
|
||||
}
|
||||
|
||||
processClick(href);
|
||||
return false;
|
||||
});
|
||||
};
|
||||
} else {
|
||||
element.classList.add("raw");
|
||||
element.classList.add("raw");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return output.innerHTML;
|
||||
};
|
||||
|
|
18
src/setup.js
18
src/setup.js
|
@ -74,11 +74,11 @@ ready(function() {
|
|||
// Handle storage.
|
||||
if (hasLocalStorage()) {
|
||||
var erase = document.getElementById("erase");
|
||||
erase.addEventListener("click", doErase());
|
||||
erase.addEventListener("keydown", doErase());
|
||||
erase.onclick = doErase;
|
||||
erase.keydown = doErase;
|
||||
var save = document.getElementById("save");
|
||||
save.addEventListener("click", saveGame);
|
||||
save.addEventListener("keydown", saveGame);
|
||||
save.onclick = saveGame;
|
||||
save.keydown = saveGame;
|
||||
|
||||
var storedCharacter = localStorage[getSaveId()];
|
||||
if (storedCharacter) {
|
||||
|
@ -95,23 +95,25 @@ ready(function() {
|
|||
startGame();
|
||||
}
|
||||
} else {
|
||||
$(".buttons").html("<p>"+"no_local_storage".l()+"</p>");
|
||||
document.querySelector(".buttons").innerHTML = "<p>"+"no_local_storage".l()+"</p>";
|
||||
startGame();
|
||||
}
|
||||
|
||||
// Display the "click to begin" message. (We do this in code
|
||||
// so that, if Javascript is off, it doesn't happen.)
|
||||
document.getElementById("click_message").style.display = '';
|
||||
showBlock("click_message");
|
||||
|
||||
// Show the game when we click on the title.
|
||||
document.getElementById("title").addEventListener('click', function() {
|
||||
// Note: if you do events with onclick, you have to have only one click event handler.
|
||||
// You can use more complex methods if you expect to have more.
|
||||
document.getElementById("title").onclick = function() {
|
||||
showBlock("content")
|
||||
showBlock("content_wrapper");
|
||||
showBlock("legal");
|
||||
showBlock("tools_wrapper");
|
||||
document.getElementById("title").style.cursor = "default";
|
||||
hideBlock("click_message");
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
// Any point that an option list appears, its options are its
|
||||
|
|
127
src/system.js
127
src/system.js
|
@ -385,133 +385,10 @@ System.prototype.setQuality = function(quality, newValue) {
|
|||
showHighlight(qualityBlock);
|
||||
};
|
||||
|
||||
/* Changes a quality to a new value, but also shows a progress bar
|
||||
* animation of the change. This probably only makes sense for
|
||||
* qualities that are numeric, especially ones that the player is
|
||||
* grinding to increase. The quality and newValue parameters are
|
||||
* as for setQuality. The progress bar is controlled by the
|
||||
* following options in the opts parameter:
|
||||
*
|
||||
* from - The proportion along the progress bar where the
|
||||
* animation starts. Defaults to 0, valid range is 0-1.
|
||||
*
|
||||
* to - The proportion along the progress bar where the
|
||||
* animation ends. Defaults to 1, valid range is 0-1.
|
||||
*
|
||||
* showValue - If true (the default) then the new value of the
|
||||
* quality is displayed above the progress bar.
|
||||
*
|
||||
* displayValue - If this is given, and showValue is true, then
|
||||
* the displayValue is used above the progress bar. If this
|
||||
* isn't given, and showValue is true, then the display value
|
||||
* will be calculated from the QualityDefinition, as
|
||||
* normal. This option is useful for qualities that don't have
|
||||
* a definition, because they don't normally appear in the UI.
|
||||
*
|
||||
* title - The title of the progress bar. If this is not given,
|
||||
* then the title of the quality is used. As for displayValue
|
||||
* this is primarily used when the progress bar doesn't have a
|
||||
* QualityDefinition, and therefore doesn't have a title.
|
||||
*
|
||||
* leftLabel, rightLabel - Underneath the progress bar you can
|
||||
* place two labels at the left and right extent of the
|
||||
* track. These can help to give scale to the bar. So if the
|
||||
* bar signifies going from 10.2 to 10.5, you might label the
|
||||
* left and right extents with "10" and "11" respectively. If
|
||||
* these are not given, then the labels will be omitted.
|
||||
*/
|
||||
/* Changes a quality to a new value, but also should show a progress bar
|
||||
* animation of the change. Removed with the progress bar functionality. */
|
||||
System.prototype.animateQuality = function(quality, newValue, opts) {
|
||||
var currentValue = character.qualities[quality];
|
||||
if (!currentValue) currentValue = 0;
|
||||
|
||||
// Change the base UI.
|
||||
this.setQuality(quality, newValue);
|
||||
if (!interactive) return;
|
||||
|
||||
// Overload default options.
|
||||
var myOpts = {
|
||||
from: 0,
|
||||
to: 1,
|
||||
title: null,
|
||||
showValue: true,
|
||||
displayValue: null,
|
||||
leftLabel: null,
|
||||
rightLabel: null
|
||||
};
|
||||
if (newValue < currentValue) {
|
||||
myOpts.from = 1;
|
||||
myOpts.to = 0;
|
||||
}
|
||||
extend(opts, myOpts);
|
||||
|
||||
// Run through the quality definition.
|
||||
var qualityDefinition = game.qualities[quality];
|
||||
if (qualityDefinition) {
|
||||
// Work out how to display the value
|
||||
if (myOpts.displayValue === null) {
|
||||
myOpts.displayValue = qualityDefinition.format(character, newValue);
|
||||
}
|
||||
|
||||
// Use the title.
|
||||
if (myOpts.title === null) {
|
||||
myOpts.title = qualityDefinition.title;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the animated bar.
|
||||
var totalWidth = 496;
|
||||
var bar = document.getElementById("progress_bar").cloneNode(true);
|
||||
bar.setAttribute("id", undefined);
|
||||
var widthElement = bar.find("[data-attr='width']"); // TODO
|
||||
widthElement.css('width', myOpts.from*totalWidth);
|
||||
|
||||
// Configure its labels
|
||||
var titleLabel = bar.find("[data-attr='name']");
|
||||
var valueLabel = bar.find("[data-attr='value']");
|
||||
var leftLabel = bar.find("[data-attr='left_label']");
|
||||
var rightLabel = bar.find("[data-attr='right_label']");
|
||||
if (myOpts.title) {
|
||||
titleLabel.html(myOpts.title);
|
||||
} else {
|
||||
titleLabel.remove();
|
||||
}
|
||||
if (myOpts.showValue && myOpts.displayValue !== null) {
|
||||
valueLabel.html(myOpts.displayValue);
|
||||
} else {
|
||||
valueLabel.remove();
|
||||
}
|
||||
if (myOpts.leftLabel) {
|
||||
leftLabel.html(myOpts.leftLabel);
|
||||
} else {
|
||||
leftLabel.remove();
|
||||
}
|
||||
if (myOpts.rightLabel) {
|
||||
rightLabel.html(myOpts.rightLabel);
|
||||
} else {
|
||||
rightLabel.remove();
|
||||
}
|
||||
document.getElementById('content').appendChild(bar);
|
||||
|
||||
// Start the animation
|
||||
setTimeout(function() {
|
||||
widthElement.animate(
|
||||
{'width': myOpts.to*totalWidth}, 1000,
|
||||
function() {
|
||||
// After a moment to allow the bar to be read, we can
|
||||
// remove it.
|
||||
setTimeout(function() {
|
||||
if (mobile) {
|
||||
bar.fadeOut(1500, function() {$(this).remove();});
|
||||
} else {
|
||||
bar.animate({opacity: 0}, 1500).
|
||||
slideUp(500, function() {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
/* The character that is passed into each situation is of this
|
||||
|
|
Loading…
Reference in a new issue