import {FS} from '../../lib/fs'; const DISHES_FILE = 'config/chat-plugins/thecafe-foodfight.json'; const FOODFIGHT_COOLDOWN = 5 * 60 * 1000; const thecafe = Rooms.get('thecafe') as ChatRoom; const dishes: {[k: string]: string[]} = JSON.parse(FS(DISHES_FILE).readIfExistsSync() || "{}"); function saveDishes() { void FS(DISHES_FILE).write(JSON.stringify(dishes)); } function generateTeam(generator = '') { let potentialPokemon = Object.keys(Dex.data.Pokedex).filter(mon => { const species = Dex.getSpecies(mon); return species.baseSpecies === species.name; }); let speciesClause = true; switch (generator) { case 'ou': potentialPokemon = potentialPokemon.filter(mon => { const species = Dex.getSpecies(mon); return species.tier === 'OU'; }).concat(potentialPokemon.filter(mon => { // There is probably a better way to get the ratios right, oh well. const species = Dex.getSpecies(mon); return species.tier === 'OU' || species.tier === 'UU'; })); break; case 'ag': potentialPokemon = potentialPokemon.filter(mon => { const species = Dex.getSpecies(mon); const unviable = species.tier === 'NFE' || species.tier === 'PU' || species.tier === '(PU)' || species.tier.startsWith("LC"); const illegal = species.tier === 'Unreleased' || species.tier === 'Illegal' || species.tier.startsWith("CAP"); return !(unviable || illegal); }); speciesClause = false; break; default: potentialPokemon = potentialPokemon.filter(mon => { const species = Dex.getSpecies(mon); const op = species.tier === 'AG' || species.tier === 'Uber' || species.tier.slice(1, -1) === 'Uber'; const unviable = species.tier === 'Illegal' || species.tier.includes("LC"); return !(op || unviable); }); potentialPokemon.push('miltank', 'miltank', 'miltank', 'miltank'); // 5x chance for miltank for flavor purposes. } const team: string[] = []; while (team.length < 6) { const randIndex = Math.floor(Math.random() * potentialPokemon.length); const potentialMon = potentialPokemon[randIndex]; if (team.includes(potentialMon)) continue; team.push(potentialMon); if (speciesClause) potentialPokemon.splice(randIndex, 1); } return team.map(mon => Dex.getSpecies(mon).name); } function generateDish(): [string, string[]] { const keys = Object.keys(dishes); const entry = dishes[keys[Math.floor(Math.random() * keys.length)]].slice(); const dish = entry.splice(0, 1)[0]; const ingredients = []; while (ingredients.length < 6) { ingredients.push(entry.splice(Math.floor(Math.random() * entry.length), 1)[0]); } return [dish, ingredients]; } export const commands: ChatCommands = { foodfight(target, room, user) { room = this.requireRoom(thecafe.roomid); if (!Object.keys(dishes).length) return this.errorReply("No dishes found. Add some dishes first."); if (user.foodfight && user.foodfight.timestamp + FOODFIGHT_COOLDOWN > Date.now()) { return this.errorReply("Please wait a few minutes before using this command again."); } target = toID(target); let team: string[] = []; let importable; const [newDish, newIngredients] = generateDish(); if (!target) { const bfTeam = Dex.generateTeam('gen7bssfactory'); importable = Dex.stringifyTeam(bfTeam, newIngredients); team = bfTeam.map(val => val.species); } else { team = generateTeam(target); } user.foodfight = {generatedTeam: team, dish: newDish, ingredients: newIngredients, timestamp: Date.now()}; const importStr = importable ? `
Importable team:
${importable}
` : ''; return this.sendReplyBox(`
${team.map(mon => ``).join('')}${newIngredients.map(ingredient => ``).join('')}${importStr}
Your dish is: ${newDish}
Team ${mon}
Ingredients${ingredient}
`); }, checkfoodfight(target, room, user) { room = this.requireRoom(thecafe.roomid); const targetUser = this.targetUserOrSelf(target, false); if (!targetUser) return this.errorReply(`User ${this.targetUsername} not found.`); const self = targetUser === user; if (!self) this.checkCan('mute', targetUser, room); if (!targetUser.foodfight) { return this.errorReply(`${self ? `You don't` : `This user doesn't`} have an active Foodfight team.`); } return this.sendReplyBox(`
${targetUser.foodfight.generatedTeam.map(mon => ``).join('')}${targetUser.foodfight.ingredients.map(ingredient => ``).join('')}
${self ? `Your` : `${this.targetUsername}'s`} dish is: ${targetUser.foodfight.dish}
Team ${mon}
Ingredients${ingredient}
`); }, addingredients: 'adddish', adddish(target, room, user, connection, cmd) { room = this.requireRoom(thecafe.roomid); this.checkCan('mute', null, room); let [dish, ...ingredients] = target.split(','); dish = dish.trim(); if (!dish || !ingredients.length) return this.parse('/help foodfight'); const id = toID(dish); if (id === 'constructor') return this.errorReply("Invalid dish name."); ingredients = ingredients.map(ingredient => ingredient.trim()); if ([...ingredients.entries()].some(([index, ingredient]) => ingredients.indexOf(ingredient) !== index)) { return this.errorReply("Please don't enter duplicate ingredients."); } if (ingredients.some(ingredient => ingredient.length > 19)) { return this.errorReply("Ingredients can only be 19 characters long."); } if (cmd === 'adddish') { if (dishes[id]) return this.errorReply("This dish already exists."); if (ingredients.length < 6) return this.errorReply("Dishes need at least 6 ingredients."); dishes[id] = [dish]; } else { if (!dishes[id]) return this.errorReply(`Dish not found: ${dish}`); if (ingredients.some(ingredient => dishes[id].includes(ingredient))) { return this.errorReply("Please don't enter duplicate ingredients."); } } dishes[id] = dishes[id].concat(ingredients); saveDishes(); this.sendReply(`${cmd.slice(3)} '${dish}: ${ingredients.join(', ')}' added successfully.`); }, removedish(target, room, user) { room = this.requireRoom(thecafe.roomid); this.checkCan('mute', null, room); const id = toID(target); if (id === 'constructor') return this.errorReply("Invalid dish."); if (!dishes[id]) return this.errorReply(`Dish '${target}' not found.`); delete dishes[id]; saveDishes(); this.sendReply(`Dish '${target}' deleted successfully.`); }, viewdishes(target, room, user, connection) { room = this.requireRoom(thecafe.roomid); return this.parse(`/join view-foodfight`); }, foodfighthelp: [ `/foodfight - Gives you a randomly generated Foodfight dish, ingredient list and team. Generator can be either 'random', 'ou', 'ag', or left blank. If left blank, uses Battle Factory to generate an importable team.`, `/checkfoodfight - Gives you the last team and dish generated for the entered user, or your own if left blank. Anyone can check their own info, checking other people requires: % @ # &`, `/adddish , , , ... - Adds a dish to the database. Requires: % @ # &`, `/addingredients , , , ... - Adds extra ingredients to a dish in the database. Requires: % @ # &`, `/removedish - Removes a dish from the database. Requires: % @ # &`, `/viewdishes - Shows the entire database of dishes. Requires: % @ # &`, ], }; export const pages: PageTable = { foodfight(query, user, connection) { if (!user.named) return Rooms.RETRY_AFTER_LOGIN; let buf = `|title|Foodfight\n|pagehtml|

Foodfight Dish list

`; if (!user.can('mute', null, thecafe)) { return buf + `

Access denied

`; } const content = Object.keys(dishes).map(entry => { const [dish, ...ingredients] = dishes[entry]; return `${dish}${ingredients.join(', ')}`; }).join(''); if (!content) { buf += `

There are no dishes in the database.

`; } else { buf += `${content}

Dishes

Ingredients

`; } buf += ``; return buf; }, };