mirror of
https://github.com/smogon/pokemon-showdown.git
synced 2026-03-21 17:25:10 -05:00
Mafia: Add Search Capability (#10682)
Some checks are pending
Node.js CI / build (18.x) (push) Waiting to run
Some checks are pending
Node.js CI / build (18.x) (push) Waiting to run
This commit is contained in:
parent
c7c2f81ab2
commit
7e6cea3605
|
|
@ -146,6 +146,78 @@ function writeFile(path: string, data: AnyObject) {
|
|||
));
|
||||
}
|
||||
|
||||
function mafiaSearch(
|
||||
entries: [string, MafiaDataAlignment | MafiaDataRole | MafiaDataTheme | MafiaDataIDEA | MafiaDataTerm][],
|
||||
searchTarget: string, searchType: keyof MafiaData
|
||||
) {
|
||||
if (typeof (entries) === 'undefined' || searchType === `aliases` || searchTarget.length === 0) return entries;
|
||||
|
||||
// Handle negation
|
||||
const negation = searchTarget.startsWith('!');
|
||||
if (negation) searchTarget = searchTarget.substring(1).trim();
|
||||
|
||||
const entriesCopy = entries.slice();
|
||||
|
||||
// Check if the search term is an alias of something
|
||||
const alias = toID((toID(searchTarget) in MafiaData[`aliases`]) ?
|
||||
MafiaData[`aliases`][toID(searchTarget)] : searchTarget);
|
||||
|
||||
if (searchType === `themes` && searchTarget.includes(`players`) && searchTarget.includes(`pl`)) {
|
||||
// Search themes by playercount
|
||||
const inequalities = ['<=', '>=', '=', '<', '>'];
|
||||
const inequality = inequalities.find(x => searchTarget.includes(x));
|
||||
if (!inequality) return entries;
|
||||
|
||||
const players = Number(searchTarget.split(inequality)[1].trim());
|
||||
if (!!players && !isNaN(players)) {
|
||||
if (inequality === '=') {
|
||||
// Filter based on themes with the exact player count
|
||||
entries = entries.filter(([key]) => players in MafiaData[`themes`][key]);
|
||||
} else if (inequality === '<' || inequality === '<=') {
|
||||
// Filter based on themes less than / at most a certain amount of players
|
||||
// Creates an array of the potential playercounts, and then looks if any in the theme matches that
|
||||
entries = entries.filter(([key]) => ([...Array(players + (inequality === '<=' ? 1 : 0)).keys()])
|
||||
.some(playerCount => playerCount in (MafiaData[`themes`][key])));
|
||||
} else if (inequality === '>' || inequality === '>=') {
|
||||
// Filter based on themes greater than / at least a certain amount of players
|
||||
// Creates an array of the potential playercounts, and then looks if any in the theme matches that
|
||||
entries = entries.filter(([key]) => ([...Array(50 - Number(players)).keys()]
|
||||
.map(num => num + players + (inequality === '>=' ? 0 : 1)))
|
||||
.some(playerCount => playerCount in (MafiaData[`themes`][key])));
|
||||
}
|
||||
} else {
|
||||
return entries;
|
||||
}
|
||||
} else if (searchType === `themes` && alias in MafiaData[`roles`]) {
|
||||
// Search themes that contain a role
|
||||
// Creates a list of all potential playercounts, then verifies if any of them contains the role that we seek
|
||||
entries = entries.filter(([key, data]) => ([...Array(50).keys()])
|
||||
.some(playerCount => playerCount in (MafiaData[`themes`][key]) &&
|
||||
(MafiaData[`themes`][key])[playerCount].toString().toLowerCase().includes(alias)));
|
||||
} else if (searchType === `IDEAs` && alias in MafiaData[`roles`]) {
|
||||
// Search IDEAs that contain a role
|
||||
entries = entries.filter(([key, data]) => MafiaData[`IDEAs`][key].roles.map(role =>
|
||||
toID((toID(role) in MafiaData[`aliases`]) ? MafiaData[`aliases`][toID(role)] : role)).includes(alias));
|
||||
} else if (searchType === `roles` && alias in MafiaData[`themes`]) {
|
||||
// Search roles that appear in a theme
|
||||
// Filters entries based on whether the list of roles in the given theme contains it (or an alias of it)
|
||||
entries = entries.filter(([key, data]) => Object.keys(MafiaData[`themes`][alias])
|
||||
.filter((newKey: any) => toID((MafiaData[`themes`][alias])[newKey].toString()).includes(key)).length > 0);
|
||||
} else if (searchType === `roles` && alias in MafiaData[`IDEAs`]) {
|
||||
// Search roles that appear in an IDEA
|
||||
// Filters entries based on whether the list of roles in the given IDEA contains it (or an alias of it)
|
||||
entries = entries.filter(([key, data]) => MafiaData[`IDEAs`][alias].roles.map(role =>
|
||||
toID((toID(role) in MafiaData[`aliases`]) ? MafiaData[`aliases`][toID(role)] : role)).includes(toID(key)));
|
||||
} else {
|
||||
// Any other search type matches just on whether it is included in the text
|
||||
// Filters entries based on whether it contains the given string anywhere
|
||||
entries = entries.filter(([key]) => Object.entries(MafiaData[searchType][key])
|
||||
.some(([newKey, value]) => value.toString().toLowerCase().includes(searchTarget)));
|
||||
}
|
||||
// Inverses the found results for negation
|
||||
return negation ? entriesCopy.filter(element => !entries.includes(element)) : entries;
|
||||
}
|
||||
|
||||
// data assumptions -
|
||||
// the alignments "town" and "solo" always exist (defaults)
|
||||
// <role>.alignment is always a valid key in data.alignments
|
||||
|
|
@ -156,12 +228,16 @@ function writeFile(path: string, data: AnyObject) {
|
|||
MafiaData = readFile(DATA_FILE) || { alignments: {}, roles: {}, themes: {}, IDEAs: {}, terms: {}, aliases: {} };
|
||||
if (!MafiaData.alignments.town) {
|
||||
MafiaData.alignments.town = {
|
||||
name: 'town', plural: 'town', memo: [`This alignment is required for the script to function properly.`],
|
||||
name: 'Town',
|
||||
plural: 'Town',
|
||||
memo: [`This alignment is required for the script to function properly.`],
|
||||
};
|
||||
}
|
||||
if (!MafiaData.alignments.solo) {
|
||||
MafiaData.alignments.solo = {
|
||||
name: 'solo', plural: 'solo', memo: [`This alignment is required for the script to function properly.`],
|
||||
name: 'Solo',
|
||||
plural: 'Solo',
|
||||
memo: [`This alignment is required for the script to function properly.`],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -328,6 +404,7 @@ class Mafia extends Rooms.RoomGame<MafiaPlayer> {
|
|||
dlAt: number;
|
||||
|
||||
IDEA: MafiaIDEAModule;
|
||||
|
||||
constructor(room: ChatRoom, host: User) {
|
||||
super(room);
|
||||
|
||||
|
|
@ -340,6 +417,7 @@ class Mafia extends Rooms.RoomGame<MafiaPlayer> {
|
|||
|
||||
this.hostid = host.id;
|
||||
this.host = Utils.escapeHTML(host.name);
|
||||
|
||||
this.cohostids = [];
|
||||
this.cohosts = [];
|
||||
|
||||
|
|
@ -2207,7 +2285,6 @@ export const commands: Chat.ChatCommands = {
|
|||
}
|
||||
return this.parse('/help mafia');
|
||||
},
|
||||
|
||||
forcehost: 'host',
|
||||
nexthost: 'host',
|
||||
host(target, room, user, connection, cmd) {
|
||||
|
|
@ -4137,23 +4214,156 @@ export const commands: Chat.ChatCommands = {
|
|||
this.sendReply(`The entry ${entry} was deleted from the ${source} database.`);
|
||||
},
|
||||
deletedatahelp: [`/mafia deletedata source,entry - Removes an entry from the database. Requires % @ # ~`],
|
||||
listdata(target, room, user) {
|
||||
if (!(target in MafiaData)) {
|
||||
throw new Chat.ErrorMessage(`Invalid source. Valid sources are ${Object.keys(MafiaData).join(', ')}`);
|
||||
|
||||
randtheme: 'listdata',
|
||||
randrole: 'listdata',
|
||||
randalignment: 'listdata',
|
||||
randidea: 'listdata',
|
||||
randterm: 'listdata',
|
||||
randroles: 'listdata',
|
||||
randalignments: 'listdata',
|
||||
randideas: 'listdata',
|
||||
randterms: 'listdata',
|
||||
randdata: 'listdata',
|
||||
randomtheme: 'listdata',
|
||||
randomrole: 'listdata',
|
||||
randomalignment: 'listdata',
|
||||
randomidea: 'listdata',
|
||||
randomterm: 'listdata',
|
||||
randomdata: 'listdata',
|
||||
randomthemes: 'listdata',
|
||||
randomroles: 'listdata',
|
||||
randomalignments: 'listdata',
|
||||
randomideas: 'listdata',
|
||||
randomterms: 'listdata',
|
||||
randthemes: 'listdata',
|
||||
listthemes: 'listdata',
|
||||
listroles: 'listdata',
|
||||
listalignments: 'listdata',
|
||||
listideas: 'listdata',
|
||||
listterms: 'listdata',
|
||||
themes: 'listdata',
|
||||
roles: 'listdata',
|
||||
alignments: 'listdata',
|
||||
ideas: 'listdata',
|
||||
terms: 'listdata',
|
||||
ds: 'listdata',
|
||||
search: 'listdata',
|
||||
random: 'listdata',
|
||||
list: 'listdata',
|
||||
listdata(target, room, user, connection, cmd, message) {
|
||||
if (!this.runBroadcast()) return false;
|
||||
|
||||
// Determine non-search targets first, afterward searching is done with the remainder
|
||||
const targets = target.split(',').map(x => x.trim().toLowerCase());
|
||||
|
||||
// Determine search type
|
||||
let searchType: keyof MafiaData = 'aliases';
|
||||
let foundSearchType = false;
|
||||
const searchTypes: (keyof MafiaData)[] = ['themes', 'roles', 'alignments', 'IDEAs', 'terms', 'aliases'];
|
||||
for (const type of searchTypes) {
|
||||
const typeID = toID(type.substring(0, type.length - 1));
|
||||
if (cmd.includes(typeID) || targets.includes(typeID)) {
|
||||
searchType = type;
|
||||
foundSearchType = true;
|
||||
if (targets.includes(type)) targets.splice(targets.indexOf(type), 1);
|
||||
}
|
||||
}
|
||||
const dataSource = MafiaData[target as keyof MafiaData];
|
||||
if (dataSource === MafiaData.aliases) {
|
||||
const aliases = Object.entries(MafiaData.aliases)
|
||||
.map(([from, to]) => `${from}: ${to}`)
|
||||
.join('<br/>');
|
||||
return this.sendReplyBox(`Mafia aliases:<br/>${aliases}`);
|
||||
} else {
|
||||
const entries = Object.entries(dataSource)
|
||||
.map(([key, data]) => `<button class="button" name="send" value="/mafia dt ${key}">${data.name}</button>`)
|
||||
.join('');
|
||||
return this.sendReplyBox(`Mafia ${target}:<br/>${entries}`);
|
||||
if (cmd === 'random' || cmd === 'randomdata' || cmd === 'randdata') {
|
||||
searchType =
|
||||
([`themes`, `roles`, `alignments`, `IDEAs`, `terms`] as (keyof MafiaData)[])[Math.floor(Math.random() * 5)];
|
||||
foundSearchType = true;
|
||||
}
|
||||
|
||||
if (!foundSearchType) {
|
||||
return this.errorReply(`Invalid source. Valid sources are ${Object.keys(MafiaData).filter(key => key !== `aliases`).join(', ')}.`);
|
||||
}
|
||||
|
||||
const dataSource = MafiaData[searchType];
|
||||
|
||||
// determine whether the command should return a random subset of results
|
||||
const random = (cmd.includes('rand') || targets.includes(`random`));
|
||||
if (targets.includes(`random`)) targets.splice(targets.indexOf(`random`), 1);
|
||||
|
||||
// TODO: hide certain roles from appearing (unless the command includes the 'hidden' parameter)
|
||||
|
||||
// Number of results
|
||||
let number = random ? 1 : 0;
|
||||
for (let i = 0; i < targets.length; i++) {
|
||||
if ((!!targets[i] &&
|
||||
!isNaN(Number(targets[i].toString())))) {
|
||||
number = Number(targets[i]);
|
||||
targets.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
|
||||
// Convert to rows
|
||||
const themeRow = function (theme: MafiaDataTheme, players = 0) {
|
||||
return `<tr><td style="text-align:left;width:30%" ><button class="button" name = "send" value = "/mafia theme ${theme.name}" > ${theme.name} </button> </td><td style="text-align:left;width:70%">${players > 0 ? theme[players] : theme.desc} </td></tr >`;
|
||||
};
|
||||
const ideaRow = function (idea: MafiaDataIDEA) {
|
||||
return `<tr><td style="text-align:left;width:100%" ><button class="button" name = "send" value = "/mafia dt ${idea.name}" > ${idea.name} </button> </td></tr >`;
|
||||
};
|
||||
const row = function (role: MafiaDataRole | MafiaDataTerm | MafiaDataAlignment) {
|
||||
return `<tr><td style="text-align:left;width:30%" ><button class="button" name = "send" value = "/mafia role ${role.name}" > ${role.name} </button> </td><td style="text-align:left;width:70%">${role.memo.join(' ')} </td></tr >`;
|
||||
};
|
||||
|
||||
if (searchType === `aliases`) {
|
||||
// Handle aliases separately for differing functionality
|
||||
room = this.requireRoom();
|
||||
this.checkCan('mute', null, room);
|
||||
const aliases = Object.entries(MafiaData.aliases)
|
||||
.map(([from, to]) => `${from}: ${to}`)
|
||||
.join('<br/>');
|
||||
return this.sendReplyBox(`Mafia aliases:<br/>${aliases}`);
|
||||
} else {
|
||||
// Create a table for a pleasant viewing experience
|
||||
let table = `<div style="max-height:300px;overflow:auto;"><table border="1" style="border: 1px solid black;width: 100%">`;
|
||||
let entries: [string, MafiaDataAlignment | MafiaDataRole | MafiaDataTheme | MafiaDataIDEA | MafiaDataTerm][] =
|
||||
Object.entries(dataSource).sort();
|
||||
|
||||
for (const targetString of targets) {
|
||||
entries = targetString.split('|').map(x => x.trim())
|
||||
.map(searchTerm => mafiaSearch(entries.slice(), searchTerm, searchType))
|
||||
.reduce((aggregate, result) => [...new Set([...aggregate, ...result])]);
|
||||
}
|
||||
|
||||
if (typeof (entries) === 'undefined') return;
|
||||
|
||||
if (random) entries = Utils.shuffle(entries);
|
||||
if (number > 0) entries = entries.slice(0, number);
|
||||
|
||||
if (entries.length === 0) {
|
||||
return this.errorReply(`No ${searchType} found.`);
|
||||
}
|
||||
|
||||
if (entries.length === 1) {
|
||||
this.target = entries[0][0];
|
||||
return this.run((Chat.commands.mafia as Chat.ChatCommands).data as Chat.AnnotatedChatHandler);
|
||||
}
|
||||
|
||||
table += entries
|
||||
.map(([key, data]) => searchType === `themes` ?
|
||||
themeRow(MafiaData[searchType][key]) : searchType === `IDEAs` ?
|
||||
ideaRow(MafiaData[searchType][key]) : row(MafiaData[searchType][key]))
|
||||
.join('');
|
||||
table += `</table></div>`;
|
||||
return this.sendReplyBox(table);
|
||||
}
|
||||
}
|
||||
},
|
||||
listdatahelp: [
|
||||
`/mafia roles [parameter, paramater, ...] - Views all Mafia roles. Parameters: theme that must include role, text included in role data.`,
|
||||
`/mafia themes [parameter, paramater, ...] - Views all Mafia themes. Parameters: roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`,
|
||||
`/mafia alignments [parameter, paramater, ...] - Views all Mafia alignments. Parameters: text included in alignment data.`,
|
||||
`/mafia ideas [parameter, paramater, ...] - Views all Mafia IDEAs. Parameters: roles in IDEA, text included in IDEA data.`,
|
||||
`/mafia terms [parameter, paramater, ...] - Views all Mafia terms. Parameters: text included in term data.`,
|
||||
`/mafia randomrole [parameter, paramater, ...] - View a random Mafia role. Parameters: number of roles to be randomly generated, theme that must include role, text included in role data.`,
|
||||
`/mafia randomtheme [parameter, paramater, ...] - View a random Mafia theme. Parameters: number of themes to be randomly generated, roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`,
|
||||
`/mafia randomalignment [parameter, paramater, ...] - View a random Mafia alignment. Parameters: number of alignments to be randomly generated, text included in alignment data.`,
|
||||
`/mafia randomidea [parameter, paramater, ...] - View a random Mafia IDEA. Parameters: number of IDEAs to be randomly generated, roles in IDEA, text included in IDEA data.`,
|
||||
`/mafia randomterm [parameter, paramater, ...] - View a random Mafia term. Parameters: number of terms to be randomly generated, text included in term data.`,
|
||||
],
|
||||
|
||||
disable(target, room, user) {
|
||||
room = this.requireRoom();
|
||||
|
|
@ -4286,6 +4496,22 @@ export const commands: Chat.ChatCommands = {
|
|||
`/mafia (un)gameban [user], [duration] - Ban a user from playing games for [duration] days. Requires % @ # ~`,
|
||||
].join('<br/>');
|
||||
buf += `</details>`;
|
||||
buf += `</details><details><summary class="button">Mafia Dexsearch Commands</summary>`;
|
||||
buf += [
|
||||
`<br/><strong>Commands to search Mafia data</strong>:<br/>`,
|
||||
`/mafia dt [data] - Views Mafia data.`,
|
||||
`/mafia roles [parameter, paramater, ...] - Views all Mafia roles. Parameters: theme that must include role, text included in role data.`,
|
||||
`/mafia themes [parameter, paramater, ...] - Views all Mafia themes. Parameters: roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`,
|
||||
`/mafia alignments [parameter, paramater, ...] - Views all Mafia alignments. Parameters: text included in alignment data.`,
|
||||
`/mafia ideas [parameter, paramater, ...] - Views all Mafia IDEAs. Parameters: roles in IDEA, text included in IDEA data.`,
|
||||
`/mafia terms [parameter, paramater, ...] - Views all Mafia terms. Parameters: text included in term data.`,
|
||||
`/mafia randomrole [parameter, paramater, ...] - View a random Mafia role. Parameters: number of roles to be randomly generated, theme that must include role, text included in role data.`,
|
||||
`/mafia randomtheme [parameter, paramater, ...] - View a random Mafia theme. Parameters: number of themes to be randomly generated, roles in theme, players(< | <= | = | => | >)[x] for playercounts, text included in theme data.`,
|
||||
`/mafia randomalignment [parameter, paramater, ...] - View a random Mafia alignment. Parameters: number of alignments to be randomly generated, text included in alignment data.`,
|
||||
`/mafia randomidea [parameter, paramater, ...] - View a random Mafia IDEA. Parameters: number of IDEAs to be randomly generated, roles in IDEA, text included in IDEA data.`,
|
||||
`/mafia randomterm [parameter, paramater, ...] - View a random Mafia term. Parameters: number of terms to be randomly generated, text included in term data.`,
|
||||
].join('<br/>');
|
||||
buf += `</details>`;
|
||||
|
||||
return this.sendReplyBox(buf);
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user