mirror of
https://github.com/bra1n/townsquare.git
synced 2025-04-04 14:14:38 +00:00
Merge branch 'develop'
This commit is contained in:
commit
53c1ad614c
7 changed files with 161 additions and 92 deletions
|
@ -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");
|
||||
|
|
BIN
src/assets/icons/plusone.png
Normal file
BIN
src/assets/icons/plusone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 66 KiB |
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
]
|
||||
|
|
|
@ -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],
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue