diff --git a/macrovision.css b/macrovision.css
index c9b9867f..c66daabf 100644
--- a/macrovision.css
+++ b/macrovision.css
@@ -645,6 +645,7 @@ i.far {
font-size: 150%;
}
+#entity-form,
#entity-view {
text-align: center;
font-weight: bold;
diff --git a/macrovision.html b/macrovision.html
index aed82471..278f4b43 100644
--- a/macrovision.html
+++ b/macrovision.html
@@ -128,6 +128,10 @@
+
+ Form
+
+
View
diff --git a/macrovision.js b/macrovision.js
index 886565ab..7c6aca4f 100644
--- a/macrovision.js
+++ b/macrovision.js
@@ -1068,14 +1068,14 @@ function drawHorizontalScale(ifDirty = false) {
// It is invoked like makeEntity so that it can be dropped in easily,
// but returns an object that lets you construct many copies of an entity,
// rather than creating a new entity.
-function createEntityMaker(info, views, sizes) {
+function createEntityMaker(info, views, sizes, forms) {
const maker = {};
maker.name = info.name;
maker.info = info;
maker.sizes = sizes;
- maker.constructor = () => makeEntity(info, views, sizes);
+ maker.constructor = () => makeEntity(info, views, sizes, forms);
maker.authors = [];
maker.owners = [];
maker.nsfw = false;
@@ -1110,7 +1110,7 @@ function createEntityMaker(info, views, sizes) {
// references to a common object. This allows for the objects to be
// safely mutated.
-function makeEntity(info, views, sizes) {
+function makeEntity(info, views, sizes, forms = {}) {
const entityTemplate = {
name: info.name,
identifier: info.name,
@@ -1119,18 +1119,31 @@ function makeEntity(info, views, sizes) {
info: JSON.parse(JSON.stringify(info)),
views: JSON.parse(JSON.stringify(views), math.reviver),
sizes: sizes === undefined ? [] : JSON.parse(JSON.stringify(sizes), math.reviver),
+ forms: forms,
init: function () {
const entity = this;
+
+ Object.entries(this.forms).forEach(([formKey, form]) => {
+ if (form.default) {
+ this.defaultForm = formKey;
+ }
+ });
+
Object.entries(this.views).forEach(([viewKey, view]) => {
view.parent = this;
if (this.defaultView === undefined) {
this.defaultView = viewKey;
this.view = viewKey;
+ this.form = view.form;
}
if (view.default) {
- this.defaultView = viewKey;
- this.view = viewKey;
+ if (forms === {} || this.defaultForm === view.form)
+ {
+ this.defaultView = viewKey;
+ this.view = viewKey;
+ this.form = view.form;
+ }
}
// to remember the units the user last picked
@@ -1231,6 +1244,14 @@ function makeEntity(info, views, sizes) {
}
)
+ this.formViews = {};
+
+ Object.entries(views).forEach(([key, value]) => {
+ if (value.default) {
+ this.formViews[value.form] = key;
+ }
+ });
+
delete this.init;
return this;
}
@@ -1353,6 +1374,7 @@ function select(target) {
displayAttribution(selectedEntity.views[selectedEntity.view].image.source);
+ configFormList(selectedEntity, selectedEntity.form);
configViewList(selectedEntity, selectedEntity.view);
configEntityOptions(selectedEntity, selectedEntity.view);
configViewOptions(selectedEntity, selectedEntity.view);
@@ -1364,6 +1386,35 @@ function select(target) {
document.querySelector("#fit").disabled = false;
}
+function configFormList(entity, selectedForm) {
+ const label = document.querySelector("#options-label-form");
+ const list = document.querySelector("#entity-form");
+
+ list.innerHTML = "";
+
+
+ if (selectedForm === undefined) {
+ label.style.display = "none";
+ list.style.display = "none";
+ return;
+ }
+
+ label.style.display = "block";
+ list.style.display = "block";
+
+ Object.keys(entity.forms).forEach(form => {
+ const option = document.createElement("option");
+ option.innerText = entity.forms[form].name;
+ option.value = form;
+
+ if (form === selectedForm) {
+ option.selected = true;
+ }
+
+ list.appendChild(option);
+ });
+}
+
function configViewList(entity, selectedView) {
const list = document.querySelector("#entity-view");
@@ -1372,6 +1423,11 @@ function configViewList(entity, selectedView) {
list.style.display = "block";
Object.keys(entity.views).forEach(view => {
+ if (Object.keys(entity.forms).length > 0) {
+ if (entity.views[view].form !== entity.form) {
+ return;
+ }
+ }
const option = document.createElement("option");
option.innerText = entity.views[view].name;
option.value = view;
@@ -1488,18 +1544,35 @@ function configEntityOptions(entity, view) {
holder.appendChild(nameLabel);
holder.appendChild(nameRow);
+ configSizeList(entity);
+
+ document.querySelector("#options-order-display").innerText = entity.priority;
+ document.querySelector("#options-brightness-display").innerText = entity.brightness;
+ document.querySelector("#options-ordering").style.display = "flex";
+}
+
+function configSizeList(entity) {
const defaultHolder = document.querySelector("#options-entity-defaults");
defaultHolder.innerHTML = "";
entity.sizes.forEach(defaultInfo => {
+ if (Object.keys(entity.forms).length > 0) {
+ if (defaultInfo.form !== entity.form) {
+ return;
+ }
+ }
const button = document.createElement("button");
button.classList.add("options-button");
button.innerText = defaultInfo.name;
button.addEventListener("click", e => {
- entity.views[entity.defaultView].height = defaultInfo.height;
+ if (Object.keys(entity.forms).length > 0) {
+ entity.views[entity.formViews[entity.form]].height = defaultInfo.height;
+ } else {
+ entity.views[entity.defaultView].height = defaultInfo.height;
+ }
entity.dirty = true;
updateEntityOptions(entity, entity.view);
updateViewOptions(entity, entity.view);
@@ -1517,10 +1590,6 @@ function configEntityOptions(entity, view) {
defaultHolder.appendChild(button);
});
-
- document.querySelector("#options-order-display").innerText = entity.priority;
- document.querySelector("#options-brightness-display").innerText = entity.brightness;
- document.querySelector("#options-ordering").style.display = "flex";
}
function updateEntityOptions(entity, view) {
@@ -3206,6 +3275,33 @@ document.addEventListener("DOMContentLoaded", () => {
clickUp(fakeEvent);
});
+ const formList = document.querySelector("#entity-form");
+
+ formList.addEventListener("input", e => {
+ const entity = entities[selected.dataset.key];
+ entity.form = e.target.value;
+ entity.view = entity.formViews[entity.form];
+
+ configViewList(entity, entity.view);
+
+ const image = entity.views[entity.view].image;
+ selected.querySelector(".entity-image").src = image.source;
+
+ configViewOptions(entity, entity.view);
+
+ displayAttribution(image.source);
+
+ if (image.bottom !== undefined) {
+ selected.querySelector(".entity-image").style.setProperty("--offset", ((-1 + image.bottom) * 100) + "%")
+ } else {
+ selected.querySelector(".entity-image").style.setProperty("--offset", ((-1) * 100) + "%")
+ }
+ configSizeList(entity);
+ updateSizes();
+ updateEntityOptions(entities[selected.dataset.key], e.target.value);
+ updateViewOptions(entities[selected.dataset.key], entity.view);
+ });
+
const viewList = document.querySelector("#entity-view");
document.querySelector("#entity-view").addEventListener("input", e => {
@@ -4492,6 +4588,7 @@ function exportScene() {
scale: entity.scale,
rotation: entity.rotation,
view: entity.view,
+ form: entity.form,
x: element.dataset.x,
y: element.dataset.y,
priority: entity.priority,
@@ -4652,6 +4749,7 @@ function importScene(data) {
entity.rotation = entityInfo.rotation;
entity.priority = entityInfo.priority;
entity.brightness = entityInfo.brightness;
+ entity.form = entityInfo.form;
displayEntity(entity, entityInfo.view, entityInfo.x, entityInfo.y);
});
diff --git a/media/attribution.js b/media/attribution.js
index ea827455..8bfa087e 100644
--- a/media/attribution.js
+++ b/media/attribution.js
@@ -17423,6 +17423,54 @@ const attributionData = {
"mongrelist"
]
},
+ {
+ prefix: "./media/characters/alex-xuria/",
+ files: [
+ { name: "demon-back.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ { name: "demon-cock.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: true },
+ { name: "demon-foot.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ { name: "demon-front.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ { name: "demon-hand.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ { name: "demon-head.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ { name: "demon-paw.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ { name: "demon-tail-closed.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ { name: "demon-tail-open.svg", source: "https://www.furaffinity.net/view/37480472/ ", nsfw: false },
+ ],
+ authors: [
+ "shadoweyenoom"
+ ],
+ owners: [
+ "gula"
+ ]
+ },
+ {
+ prefix: "./media/characters/alex-xuria/",
+ files: [
+ { name: "incubus-back.svg", source: "https://www.furaffinity.net/view/41985342/", nsfw: false },
+ { name: "incubus-front.svg", source: "https://www.furaffinity.net/view/41985342/", nsfw: false },
+ { name: "incubus-head.svg", source: "https://www.furaffinity.net/view/41985342/", nsfw: false },
+ ],
+ authors: [
+ "mihaelt"
+ ],
+ owners: [
+ "gula"
+ ]
+ },
+ {
+ prefix: "./media/characters/alex-xuria/",
+ files: [
+ { name: "rabbit-back.svg", source: "https://www.furaffinity.net/view/22584929/", nsfw: true },
+ { name: "rabbit-front.svg", source: "https://www.furaffinity.net/view/22584929/", nsfw: true },
+ { name: "rabbit-side.svg", source: "https://www.furaffinity.net/view/22584929/", nsfw: true },
+ ],
+ authors: [
+ "lustbubbles"
+ ],
+ owners: [
+ "gula"
+ ]
+ },
//characters
{
prefix: "./media/dildos/chance/",
@@ -24113,6 +24161,22 @@ const attributionData = {
"name": "Terry",
"url": null
},
+ "gula": {
+ "name": "Gula",
+ "url": "https://www.furaffinity.net/user/gula/"
+ },
+ "shadoweyenoom": {
+ "name": "ShadowEyenoom",
+ "url": "https://www.furaffinity.net/user/shadoweyenoom"
+ },
+ "mihaelt": {
+ "name": "Mihaelt",
+ "url": "https://www.furaffinity.net/user/mihaelt"
+ },
+ "lustbubbles": {
+ "name": "LustBubbles",
+ "url": "https://www.furaffinity.net/user/lustbubbles"
+ },
}
}
diff --git a/media/characters/alex-xuria/demon-back.svg b/media/characters/alex-xuria/demon-back.svg
new file mode 100644
index 00000000..44fd238c
--- /dev/null
+++ b/media/characters/alex-xuria/demon-back.svg
@@ -0,0 +1,768 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-cock.svg b/media/characters/alex-xuria/demon-cock.svg
new file mode 100644
index 00000000..3d145f2a
--- /dev/null
+++ b/media/characters/alex-xuria/demon-cock.svg
@@ -0,0 +1,197 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-foot.svg b/media/characters/alex-xuria/demon-foot.svg
new file mode 100644
index 00000000..48d35db6
--- /dev/null
+++ b/media/characters/alex-xuria/demon-foot.svg
@@ -0,0 +1,81 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-front.svg b/media/characters/alex-xuria/demon-front.svg
new file mode 100644
index 00000000..e00f18fc
--- /dev/null
+++ b/media/characters/alex-xuria/demon-front.svg
@@ -0,0 +1,1184 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-hand.svg b/media/characters/alex-xuria/demon-hand.svg
new file mode 100644
index 00000000..d2f0ccaf
--- /dev/null
+++ b/media/characters/alex-xuria/demon-hand.svg
@@ -0,0 +1,109 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-head.svg b/media/characters/alex-xuria/demon-head.svg
new file mode 100644
index 00000000..26cd3282
--- /dev/null
+++ b/media/characters/alex-xuria/demon-head.svg
@@ -0,0 +1,371 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-paw.svg b/media/characters/alex-xuria/demon-paw.svg
new file mode 100644
index 00000000..3244847a
--- /dev/null
+++ b/media/characters/alex-xuria/demon-paw.svg
@@ -0,0 +1,165 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-tail-closed.svg b/media/characters/alex-xuria/demon-tail-closed.svg
new file mode 100644
index 00000000..b514f385
--- /dev/null
+++ b/media/characters/alex-xuria/demon-tail-closed.svg
@@ -0,0 +1,54 @@
+
+
+
diff --git a/media/characters/alex-xuria/demon-tail-open.svg b/media/characters/alex-xuria/demon-tail-open.svg
new file mode 100644
index 00000000..8a3e852d
--- /dev/null
+++ b/media/characters/alex-xuria/demon-tail-open.svg
@@ -0,0 +1,190 @@
+
+
+
diff --git a/media/characters/alex-xuria/incubus-back.svg b/media/characters/alex-xuria/incubus-back.svg
new file mode 100644
index 00000000..c97b3207
--- /dev/null
+++ b/media/characters/alex-xuria/incubus-back.svg
@@ -0,0 +1,559 @@
+
+
+
diff --git a/media/characters/alex-xuria/incubus-front.svg b/media/characters/alex-xuria/incubus-front.svg
new file mode 100644
index 00000000..9d89f92d
--- /dev/null
+++ b/media/characters/alex-xuria/incubus-front.svg
@@ -0,0 +1,675 @@
+
+
+
diff --git a/media/characters/alex-xuria/incubus-head.svg b/media/characters/alex-xuria/incubus-head.svg
new file mode 100644
index 00000000..223af5be
--- /dev/null
+++ b/media/characters/alex-xuria/incubus-head.svg
@@ -0,0 +1,424 @@
+
+
+
diff --git a/media/characters/alex-xuria/rabbit-back.svg b/media/characters/alex-xuria/rabbit-back.svg
new file mode 100644
index 00000000..7c57827d
--- /dev/null
+++ b/media/characters/alex-xuria/rabbit-back.svg
@@ -0,0 +1,295 @@
+
+
+
diff --git a/media/characters/alex-xuria/rabbit-front.svg b/media/characters/alex-xuria/rabbit-front.svg
new file mode 100644
index 00000000..f030d8ba
--- /dev/null
+++ b/media/characters/alex-xuria/rabbit-front.svg
@@ -0,0 +1,360 @@
+
+
+
diff --git a/media/characters/alex-xuria/rabbit-side.svg b/media/characters/alex-xuria/rabbit-side.svg
new file mode 100644
index 00000000..541484ef
--- /dev/null
+++ b/media/characters/alex-xuria/rabbit-side.svg
@@ -0,0 +1,258 @@
+
+
+
diff --git a/presets/characters.js b/presets/characters.js
index e731c0ea..11b98655 100644
--- a/presets/characters.js
+++ b/presets/characters.js
@@ -1,6 +1,6 @@
const characterMakers = [];
-function makeCharacter(info, viewInfo, defaultSizes) {
+function makeCharacter(info, viewInfo, defaultSizes, forms) {
views = {};
Object.entries(viewInfo).forEach(([key, value]) => {
@@ -14,6 +14,7 @@ function makeCharacter(info, viewInfo, defaultSizes) {
}
},
image: value.image,
+ form: value.form,
name: value.name,
info: value.info,
rename: value.rename,
@@ -57,7 +58,7 @@ function makeCharacter(info, viewInfo, defaultSizes) {
}
});
- return createEntityMaker(info, views, defaultSizes);
+ return createEntityMaker(info, views, defaultSizes, forms);
}
const speciesData = {
@@ -46144,6 +46145,181 @@ characterMakers.push(() => makeCharacter(
]
))
+characterMakers.push(() => makeCharacter(
+ { name: "Alex Xuria", species: ["demon", "rabbit"], tags: ["anthro"] },
+ {
+ demonFront: {
+ height: math.unit(36, "feet"),
+ name: "Front",
+ image: {
+ source: "./media/characters/alex-xuria/demon-front.svg",
+ extra: 1705/1673,
+ bottom: 198/1903
+ },
+ form: "demon",
+ default: true
+ },
+ demonBack: {
+ height: math.unit(36, "feet"),
+ name: "Back",
+ image: {
+ source: "./media/characters/alex-xuria/demon-back.svg",
+ extra: 1725/1693,
+ bottom: 70/1795
+ },
+ form: "demon"
+ },
+ demonHead: {
+ height: math.unit(2.14, "meters"),
+ name: "Head",
+ image: {
+ source: "./media/characters/alex-xuria/demon-head.svg"
+ },
+ form: "demon"
+ },
+ demonHand: {
+ height: math.unit(1.61, "meters"),
+ name: "Hand",
+ image: {
+ source: "./media/characters/alex-xuria/demon-hand.svg"
+ },
+ form: "demon"
+ },
+ demonPaw: {
+ height: math.unit(1.35, "meters"),
+ name: "Paw",
+ image: {
+ source: "./media/characters/alex-xuria/demon-paw.svg"
+ },
+ form: "demon"
+ },
+ demonFoot: {
+ height: math.unit(2.2, "meters"),
+ name: "Foot",
+ image: {
+ source: "./media/characters/alex-xuria/demon-foot.svg"
+ },
+ form: "demon"
+ },
+ demonCock: {
+ height: math.unit(1.74, "meters"),
+ name: "Cock",
+ image: {
+ source: "./media/characters/alex-xuria/demon-cock.svg"
+ },
+ form: "demon"
+ },
+ demonTailClosed: {
+ height: math.unit(1.47, "meters"),
+ name: "Tail (Closed)",
+ image: {
+ source: "./media/characters/alex-xuria/demon-tail-closed.svg"
+ },
+ form: "demon"
+ },
+ demonTailOpen: {
+ height: math.unit(2.85, "meters"),
+ name: "Tail (Open)",
+ image: {
+ source: "./media/characters/alex-xuria/demon-tail-open.svg"
+ },
+ form: "demon"
+ },
+ incubusFront: {
+ height: math.unit(12, "feet"),
+ name: "Front",
+ image: {
+ source: "./media/characters/alex-xuria/incubus-front.svg",
+ extra: 1754/1677,
+ bottom: 125/1879
+ },
+ form: "incubus",
+ default: true
+ },
+ incubusBack: {
+ height: math.unit(12, "feet"),
+ name: "Back",
+ image: {
+ source: "./media/characters/alex-xuria/incubus-back.svg",
+ extra: 1702/1647,
+ bottom: 30/1732
+ },
+ form: "incubus"
+ },
+ incubusHead: {
+ height: math.unit(3.45, "feet"),
+ name: "Head",
+ image: {
+ source: "./media/characters/alex-xuria/incubus-head.svg"
+ },
+ form: "incubus"
+ },
+ rabbitFront: {
+ height: math.unit(6, "feet"),
+ name: "Front",
+ image: {
+ source: "./media/characters/alex-xuria/rabbit-front.svg",
+ extra: 1369/1349,
+ bottom: 45/1414
+ },
+ form: "rabbit",
+ default: true
+ },
+ rabbitSide: {
+ height: math.unit(6, "feet"),
+ name: "Side",
+ image: {
+ source: "./media/characters/alex-xuria/rabbit-side.svg",
+ extra: 1370/1356,
+ bottom: 37/1407
+ },
+ form: "rabbit"
+ },
+ rabbitBack: {
+ height: math.unit(6, "feet"),
+ name: "Back",
+ image: {
+ source: "./media/characters/alex-xuria/rabbit-back.svg",
+ extra: 1375/1358,
+ bottom: 43/1418
+ },
+ form: "rabbit"
+ },
+ },
+ [
+ {
+ name: "Normal",
+ height: math.unit(6, "feet"),
+ default: true,
+ form: "rabbit"
+ },
+ {
+ name: "Incubus",
+ height: math.unit(12, "feet"),
+ default: true,
+ form: "incubus"
+ },
+ {
+ name: "Demon",
+ height: math.unit(36, "feet"),
+ default: true,
+ form: "demon"
+ }
+ ],
+ {
+ "demon": {
+ name: "Demon",
+ default: true
+ },
+ "incubus": {
+ name: "Incubus",
+ },
+ "rabbit": {
+ name: "Rabbit"
+ }
+ }
+))
+
//characters
function makeCharacters() {