Merge branch 'develop'

This commit is contained in:
Dave 2023-05-27 18:34:14 +01:00
commit 53c1ad614c
7 changed files with 161 additions and 92 deletions

View file

@ -87,7 +87,7 @@ export default {
Gradients,
},
computed: {
...mapState(["grimoire", "session"]),
...mapState(["grimoire", "session", "modals"]),
...mapState("players", ["players"]),
},
data() {
@ -97,7 +97,7 @@ export default {
},
methods: {
keyup({ key, ctrlKey, metaKey }) {
if (ctrlKey || metaKey) return;
if (ctrlKey || metaKey || this.modals.role) return;
switch (key.toLocaleLowerCase()) {
case "g":
this.$store.commit("toggleGrimoire");

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View file

@ -1,7 +1,5 @@
<template>
<div class="token" @click="setRole" :class="[role.id]">
<span
class="icon"
v-if="role.id"
@ -29,7 +27,6 @@
<span class="leaf-orange" v-if="role.setup"></span>
<svg viewBox="0 0 150 150" class="name">
<path
d="M 13 75 C 13 160, 138 160, 138 75"
id="curve"
@ -43,19 +40,14 @@
class="label mozilla"
:font-size="nameToFontSize(role.name)"
>
<textPath xlink:href="#curve"> {{ role.name }} </textPath>
<textPath xlink:href="#curve">{{ role.name }}</textPath>
</text>
</svg>
<div class="edition" :class="[`edition-${role.edition}`, role.team]"></div>
<div class="ability" v-if="role.ability"> {{ role.ability }} </div>
<div class="ability" v-if="role.ability">{{ role.ability }}</div>
</div>
</template>
<script>
@ -70,7 +62,7 @@ export default {
},
},
computed: {
reminderLeaves: function() {
reminderLeaves: function () {
return (
(this.role.reminders || []).length +
(this.role.remindersGlobal || []).length
@ -83,7 +75,7 @@ export default {
},
methods: {
nameToFontSize(name) {
name && name.length > 10 ? "90%" : "110%";
return name && name.length > 10 ? "90%" : "110%";
},
setRole() {
this.$emit("set-role");
@ -245,4 +237,3 @@ export default {
}
}
</style>

View file

@ -21,7 +21,7 @@
: require('../../assets/icons/' +
(reminder.imageAlt || reminder.role) +
'.png')
})`
})`,
}"
></span>
<span class="text">{{ reminder.name }}</span>
@ -39,12 +39,14 @@ import { mapMutations, mapState } from "vuex";
* @param role The role for which the reminder should be generated
* @return {function(*): {image: string|string[]|string|*, role: *, name: *, imageAlt: string|*}}
*/
const mapReminder = ({ id, image, imageAlt }) => name => ({
role: id,
image,
imageAlt,
name
});
const mapReminder =
({ id, image, imageAlt }) =>
(name) => ({
role: id,
image,
imageAlt,
name,
});
export default {
components: { Modal },
@ -53,31 +55,40 @@ export default {
availableReminders() {
let reminders = [];
const { players, bluffs } = this.$store.state.players;
this.$store.state.roles.forEach(role => {
this.$store.state.roles.forEach((role) => {
// add reminders from player roles
if (players.some(p => p.role.id === role.id)) {
if (players.some((p) => p.role.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
}
// add reminders from bluff/other roles
else if (bluffs.some(bluff => bluff.id === role.id)) {
else if (bluffs.some((bluff) => bluff.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
}
// add global reminders
if (role.remindersGlobal && role.remindersGlobal.length) {
reminders = [
...reminders,
...role.remindersGlobal.map(mapReminder(role))
...role.remindersGlobal.map(mapReminder(role)),
];
}
});
// add fabled reminders
this.$store.state.players.fabled.forEach(role => {
this.$store.state.players.fabled.forEach((role) => {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
});
// add out of script traveler reminders
this.$store.state.otherTravelers.forEach(role => {
if (players.some(p => p.role.id === role.id)) {
this.$store.state.otherTravelers.forEach((role) => {
if (players.some((p) => p.role.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
}
});
// add out of script role reminders
this.$store.state.otherRoles.forEach((role) => {
if (players.some((p) => p.role.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
} else if (bluffs.some((bluff) => bluff.id === role.id)) {
reminders = [...reminders, ...role.reminders.map(mapReminder(role))];
}
});
@ -88,7 +99,7 @@ export default {
return reminders;
},
...mapState(["modals", "grimoire"]),
...mapState("players", ["players"])
...mapState("players", ["players"]),
},
methods: {
addReminder(reminder) {
@ -104,12 +115,12 @@ export default {
this.$store.commit("players/update", {
player,
property: "reminders",
value
value,
});
this.$store.commit("toggleModal", "reminder");
},
...mapMutations(["toggleModal"])
}
...mapMutations(["toggleModal"]),
},
};
</script>

View file

@ -8,7 +8,26 @@
: "bluffing"
}}
</h3>
<ul class="tokens" v-if="tab === 'editionRoles' || !otherTravelers.size">
<div
v-if="fabled.find((r) => r.id === 'plusone') && tab === 'editionRoles'"
>
<span>Find a Plus One character: </span>
<input type="text" v-model="filter" />
</div>
<ul class="tokens" v-if="filteredRoles.length > 0">
<li
v-for="role in filteredRoles.slice(0, 10)"
:class="[role.team]"
:key="role.id"
@click="setRole(role)"
>
<token :role="role" />
</li>
</ul>
<ul
class="tokens"
v-if="tab === 'editionRoles' || !otherTravelers.length > 0"
>
<li
v-for="role in availableRoles"
:class="[role.team]"
@ -18,9 +37,12 @@
<Token :role="role" />
</li>
</ul>
<ul class="tokens" v-if="tab === 'otherTravelers' && otherTravelers.size">
<ul
class="tokens"
v-if="tab === 'otherTravelers' && otherTravelers.length > 0"
>
<li
v-for="role in otherTravelers.values()"
v-for="role in otherTravelers"
:class="[role.team]"
:key="role.id"
@click="setRole(role)"
@ -30,7 +52,9 @@
</ul>
<div
class="button-group"
v-if="playerIndex >= 0 && otherTravelers.size && !session.isSpectator"
v-if="
playerIndex >= 0 && otherTravelers.length > 0 && !session.isSpectator
"
>
<span
class="button"
@ -57,15 +81,24 @@ export default {
components: { Token, Modal },
props: ["playerIndex"],
computed: {
filteredRoles() {
if (this.filter === "") {
return [];
}
var filteredRoles = this.otherRoles.filter((role) => {
return role.name.toLowerCase().includes(this.filter.toLowerCase());
});
return filteredRoles;
},
availableRoles() {
const availableRoles = [];
const players = this.$store.state.players.players;
this.$store.state.roles.forEach(role => {
this.$store.state.roles.forEach((role) => {
// don't show bluff roles that are already assigned to players
if (
this.playerIndex >= 0 ||
(this.playerIndex < 0 &&
!players.some(player => player.role.id === role.id))
!players.some((player) => player.role.id === role.id))
) {
availableRoles.push(role);
}
@ -74,12 +107,14 @@ export default {
return availableRoles;
},
...mapState(["modals", "roles", "session"]),
...mapState("players", ["players"]),
...mapState(["otherTravelers"])
...mapState("players", ["players", "fabled"]),
...mapState(["otherTravelers"]),
...mapState(["otherRoles"]),
},
data() {
return {
tab: "editionRoles"
tab: "editionRoles",
filter: "",
};
},
methods: {
@ -88,7 +123,7 @@ export default {
// assign to bluff slot (index < 0)
this.$store.commit("players/setBluff", {
index: this.playerIndex * -1 - 1,
role
role,
});
} else {
if (this.session.isSpectator && role.team === "traveler") return;
@ -97,7 +132,7 @@ export default {
this.$store.commit("players/update", {
player,
property: "role",
value: role
value: role,
});
}
this.tab = "editionRoles";
@ -105,10 +140,11 @@ export default {
},
close() {
this.tab = "editionRoles";
this.filter = "";
this.toggleModal("role");
},
...mapMutations(["toggleModal"])
}
...mapMutations(["toggleModal"]),
},
};
</script>

View file

@ -142,5 +142,15 @@
"name": "Deus ex Fiasco",
"team": "fabled",
"ability": "Once per game, the Storyteller will make a \"mistake\", correct it and publicly admit to it."
},
{
"id": "plusone",
"firstNightReminder": "",
"otherNightReminder": "",
"reminders": [],
"setup": true,
"name": "Plussy McOneface",
"team": "fabled",
"ability": "One role from off the script will be in play, it can be either a player or a demon bluff."
}
]

View file

@ -15,45 +15,59 @@ Vue.use(Vuex);
const getRolesByEdition = (edition = editionJSON[0]) => {
return new Map(
rolesJSON
.filter(r => r.edition === edition.id || edition.roles.includes(r.id))
.filter((r) => r.edition === edition.id || edition.roles.includes(r.id))
.sort((a, b) => b.team.localeCompare(a.team))
.map(role => [role.id, role])
.map((role) => [role.id, role])
);
};
const getTravelersNotInEdition = (edition = editionJSON[0]) => {
return new Map(
rolesJSON
.filter(
r =>
r.team === "traveler" &&
r.edition !== edition.id &&
!edition.roles.includes(r.id)
)
.map(role => [role.id, role])
return rolesJSON.filter(
(r) =>
r.team === "traveler" &&
r.edition !== edition.id &&
!edition.roles.includes(r.id)
);
};
const set = key => ({ grimoire }, val) => {
grimoire[key] = val;
const getRolesNotInEdition = (edition = editionJSON[0]) => {
return rolesJSON.filter(
(r) =>
r.team !== "traveler" &&
r.edition !== edition.id &&
!edition.roles.includes(r.id)
);
};
const toggle = key => ({ grimoire }, val) => {
if (val === true || val === false) {
const getRoleById = (id) => {
return rolesJSON.find((r) => r.id === id);
``;
};
const set =
(key) =>
({ grimoire }, val) => {
grimoire[key] = val;
} else {
grimoire[key] = !grimoire[key];
}
};
};
const clean = id => id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
const toggle =
(key) =>
({ grimoire }, val) => {
if (val === true || val === false) {
grimoire[key] = val;
} else {
grimoire[key] = !grimoire[key];
}
};
const clean = (id) => id.toLocaleLowerCase().replace(/[^a-z0-9]/g, "");
// global data maps
const editionJSONbyId = new Map(
editionJSON.map(edition => [edition.id, edition])
editionJSON.map((edition) => [edition.id, edition])
);
const rolesJSONbyId = new Map(rolesJSON.map(role => [role.id, role]));
const fabled = new Map(fabledJSON.map(role => [role.id, role]));
const rolesJSONbyId = new Map(rolesJSON.map((role) => [role.id, role]));
const fabled = new Map(fabledJSON.map((role) => [role.id, role]));
// jinxes
let jinxes = {};
@ -65,7 +79,7 @@ try {
jinxes = new Map(
jinxesJSON.map(({ id, hatred }) => [
clean(id),
new Map(hatred.map(({ id, reason }) => [clean(id), reason]))
new Map(hatred.map(({ id, reason }) => [clean(id), reason])),
])
);
// });
@ -88,13 +102,13 @@ const customRole = {
remindersGlobal: [],
setup: false,
team: "townsfolk",
isCustom: true
isCustom: true,
};
export default new Vuex.Store({
modules: {
players,
session
session,
},
state: {
grimoire: {
@ -106,7 +120,7 @@ export default new Vuex.Store({
isMuted: false,
isImageOptIn: false,
zoom: 0,
background: ""
background: "",
},
modals: {
edition: false,
@ -117,13 +131,14 @@ export default new Vuex.Store({
reminder: false,
role: false,
roles: false,
voteHistory: false
voteHistory: false,
},
edition: editionJSONbyId.get("tb"),
roles: getRolesByEdition(),
otherTravelers: getTravelersNotInEdition(),
otherRoles: getRolesNotInEdition(),
fabled,
jinxes
jinxes,
},
getters: {
/**
@ -138,9 +153,9 @@ export default new Vuex.Store({
const strippedProps = [
"firstNightReminder",
"otherNightReminder",
"isCustom"
"isCustom",
];
roles.forEach(role => {
roles.forEach((role) => {
if (!role.isCustom) {
customRoles.push({ id: role.id });
} else {
@ -159,7 +174,8 @@ export default new Vuex.Store({
});
return customRoles;
},
rolesJSONbyId: () => rolesJSONbyId
rolesJSONbyId: () => rolesJSONbyId,
roleById: getRoleById,
},
mutations: {
setZoom: set("zoom"),
@ -188,7 +204,7 @@ export default new Vuex.Store({
setCustomRoles(state, roles) {
const processedRoles = roles
// replace numerical role object keys with matching key names
.map(role => {
.map((role) => {
if (role[0]) {
const customKeys = Object.keys(customRole);
const mappedRole = {};
@ -203,19 +219,19 @@ export default new Vuex.Store({
}
})
// clean up role.id
.map(role => {
.map((role) => {
role.id = clean(role.id);
return role;
})
// map existing roles to base definition or pre-populate custom roles to ensure all properties
.map(
role =>
(role) =>
rolesJSONbyId.get(role.id) ||
state.roles.get(role.id) ||
Object.assign({}, customRole, role)
)
// default empty icons and placeholders, clean up firstNight / otherNight
.map(role => {
.map((role) => {
if (rolesJSONbyId.get(role.id)) return role;
role.imageAlt = // map team to generic icon
{
@ -223,32 +239,36 @@ export default new Vuex.Store({
outsider: "outsider",
minion: "minion",
demon: "evil",
fabled: "fabled"
fabled: "fabled",
}[role.team] || "custom";
role.firstNight = Math.abs(role.firstNight);
role.otherNight = Math.abs(role.otherNight);
return role;
})
// filter out roles that don't match an existing role and also don't have name/ability/team
.filter(role => role.name && role.ability && role.team)
.filter((role) => role.name && role.ability && role.team)
// sort by team
.sort((a, b) => b.team.localeCompare(a.team));
// convert to Map without Fabled
state.roles = new Map(
processedRoles
.filter(role => role.team !== "fabled")
.map(role => [role.id, role])
.filter((role) => role.team !== "fabled")
.map((role) => [role.id, role])
);
// update Fabled to include custom Fabled from this script
state.fabled = new Map([
...processedRoles.filter(r => r.team === "fabled").map(r => [r.id, r]),
...fabledJSON.map(role => [role.id, role])
...processedRoles
.filter((r) => r.team === "fabled")
.map((r) => [r.id, r]),
...fabledJSON.map((role) => [role.id, role]),
]);
// update extraTravelers map to only show travelers not in this script
state.otherTravelers = new Map(
rolesJSON
.filter(r => r.team === "traveler" && !roles.some(i => i.id === r.id))
.map(role => [role.id, role])
.filter(
(r) => r.team === "traveler" && !roles.some((i) => i.id === r.id)
)
.map((role) => [role.id, role])
);
},
setEdition(state, edition) {
@ -256,11 +276,12 @@ export default new Vuex.Store({
state.edition = editionJSONbyId.get(edition.id);
state.roles = getRolesByEdition(state.edition);
state.otherTravelers = getTravelersNotInEdition(state.edition);
state.otherRoles = getRolesNotInEdition(state.edition);
} else {
state.edition = edition;
}
state.modals.edition = false;
}
},
},
plugins: [persistence, socket]
plugins: [persistence, socket],
});