Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor to new challenge system #8303

Merged
merged 10 commits into from
May 17, 2021
1 change: 0 additions & 1 deletion config/formats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -843,7 +843,6 @@ export const Formats: FormatList = [
mod: 'gen8',
gameType: 'freeforall',
rated: false,
challengeShow: false,
tournamentShow: false,
ruleset: ['Standard Doubles', 'Sleep Clause Mod', 'Dynamax Clause', '!Gravity Sleep Clause'],
banlist: [
Expand Down
220 changes: 106 additions & 114 deletions server/chat-commands/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1059,6 +1059,9 @@ export const commands: Chat.ChatCommands = {
battle.p2.name = target.slice(nameIndex2 + 8, nameNextQuoteIndex2);
}
battleRoom.auth.set(user.id, Users.HOST_SYMBOL);
for (const player of battleRoom.battle!.players) {
player.hasTeam = true;
}
this.parse(`/join ${battleRoom.roomid}`);
setTimeout(() => {
// timer to make sure this goes under the battle
Expand Down Expand Up @@ -1264,48 +1267,13 @@ export const commands: Chat.ChatCommands = {
this.addModAction(room.tr`${user.name} hid the replay of this battle.`);
},

addplayer(target, room, user) {
addplayer: 'invitebattle',
invitebattle(target, room, user, connection) {
room = this.requireRoom();
if (!target) return this.parse('/help addplayer');
if (!room.battle) return this.errorReply(this.tr`You can only do this in battle rooms.`);
if (room.rated) return this.errorReply(this.tr`You can only add a Player to unrated battles.`);

const {targetUser, rest: slot} = this.requireUser(target, {exactName: true});
if (slot !== 'p1' && slot !== 'p2' && slot !== 'p3' && slot !== 'p4') {
this.errorReply(this.tr`Player must be set to "p1" or "p2", not "${slot}".`);
return this.parse('/help addplayer');
}

if (!targetUser.inRooms.has(room.roomid)) {
return this.errorReply(this.tr`User ${targetUser.name} must be in the battle room already.`);
}
this.checkCan('joinbattle', null, room);
if (room.battle[slot].id) {
return this.errorReply(this.tr`This room already has a player in slot ${slot}.`);
}
if (targetUser.id in room.battle.playerTable) {
return this.errorReply(this.tr`${targetUser.name} is already a player in this battle.`);
}

room.auth.set(targetUser.id, Users.PLAYER_SYMBOL);
const success = room.battle.joinGame(targetUser, slot);
if (!success) {
room.auth.delete(targetUser.id);
return;
}
const playerNum = slot.slice(1);
this.addModAction(room.tr`${targetUser.name} was added to the battle as Player ${playerNum} by ${user.name}.`);
this.modlog('ROOMPLAYER', targetUser.getLastId());
},
addplayerhelp: [
`/addplayer [username], p1 - Allow the specified user to join the battle as Player 1.`,
`/addplayer [username], p2 - Allow the specified user to join the battle as Player 2.`,
],

invitebattle(target, room, user, connection) {
room = this.requireRoom();
if (!room.battle) return this.errorReply(this.tr`You can only do this in battle rooms.`);
if (room.rated) return this.errorReply(this.tr`You can only add a Player to unrated battles.`);

const {targetUser, targetUsername: name, rest: slot} = this.splitUser(target, {exactName: true});
if (slot !== 'p1' && slot !== 'p2' && slot !== 'p3' && slot !== 'p4') {
Expand All @@ -1323,11 +1291,14 @@ export const commands: Chat.ChatCommands = {
battle.sendInviteForm(connection);
return this.errorReply(this.tr`User ${name} not found.`);
}
this.checkCan('joinbattle', null, room);
if (player.id) {
battle.sendInviteForm(connection);
return this.errorReply(this.tr`This room already has a player in slot ${slot}.`);
}
if (player.invite) {
battle.sendInviteForm(connection);
return this.errorReply(`Someone else (${player.invite}) has already been invited to be ${slot}!`);
}
if (targetUser.id in battle.playerTable) {
battle.sendInviteForm(connection);
return this.errorReply(this.tr`${targetUser.name} is already a player in this battle.`);
Expand All @@ -1340,16 +1311,19 @@ export const commands: Chat.ChatCommands = {
}

// INVITE
if (!targetUser.inRooms.has(room.roomid)) {
if (player.invite) {
battle.sendInviteForm(connection);
return this.errorReply(`Someone else (${player.invite}) has already been invited to be ${slot}!`);
}

if (!targetUser.inRooms.has(room.roomid) || !player.hasTeam) {
player.invite = targetUser.id;
const playerNames = battle.players.map(p => p.id && p.name).filter(Boolean).join(', ');
targetUser.send(`|pm|${user.getIdentity()}|${targetUser.getIdentity()}|/uhtml battleinvite,You've been invited to join a ${battle.format} battle (with ${playerNames})<br /><button name="send" value="/acceptbattle ${room.roomid}, ${user.id}" class="button"><strong>Accept</strong></button> <button name="send" value="/rejectbattle ${room.roomid}, ${user.id}" class="button">Reject</button>`);
user.send(`|pm|${user.getIdentity()}|${targetUser.getIdentity()}|/text Invite sent for <<${room.roomid}>>`);
battle.sendInviteForm(connection);
const ready = player.hasTeam ? battle.format : new Ladders.BattleReady(user.id, battle.format, user.battleSettings);
Ladders.challenges.add(
new Ladders.BattleInvite(user.id, targetUser.id, ready, {
acceptCommand: `/acceptbattle ${user.id}`,
message: `You're invited to join a battle (with ${playerNames})`,
roomid: room.roomid,
})
);
battle.sendInviteForm(battle.invitesFull() ? true : connection);
return this.add(`||Invite sent to ${targetUser.name}!`);
}

Expand All @@ -1359,74 +1333,57 @@ export const commands: Chat.ChatCommands = {
room.auth.delete(targetUser.id);
return;
}
if (!battle.started) {
battle.sendInviteForm(connection);
}
if (!battle.started) battle.sendInviteForm(connection);
},

acceptbattle(target, room, user) {
const [roomid, senderID] = target.split(',').map(part => part.trim());
const targetRoom = Rooms.get(roomid);
if (!targetRoom) return this.errorReply(`Room ${roomid} not found`);
if (!targetRoom.battle) return this.errorReply(`Room ${roomid} is not a battle`);
const battle = targetRoom.battle;
async acceptbattle(target, room, user, connection) {
const chall = Ladders.challenges.resolveAcceptCommand(this);

const targetRoom = Rooms.get(chall.roomid);
if (!targetRoom) return this.errorReply(`Room ${chall.roomid} not found`);
const battle = targetRoom.battle!;
const player = battle.players.find(maybe => maybe.invite === user.id);
if (!player) {
return this.errorReply(`You haven't been invited to that battle.`);
}
const slot = player.slot;
if (!battle[slot]) {
return this.errorReply(this.tr`This battle can't have players in slot ${slot}.`);
}
if (battle[slot].id) {
return this.errorReply(this.tr`This room already has a player in slot ${roomid}.`);
if (player.id) {
throw new Error(`Player ${player.slot} in ${chall.roomid} should not have both 'id' and 'invite'`);
}

user.send(`|pm| ${senderID}|${user.getIdentity()}|/uhtmlchange battleinvite, You accepted the battle invite`);
this.parse(`/join ${targetRoom.roomid}`);
battle.joinGame(user, slot);
},

rejectbattle(target, room, user) {
const [roomid, senderID] = target.split(',').map(part => part.trim());
const targetRoom = Rooms.get(roomid);
if (!targetRoom) return this.errorReply(`Room ${roomid} not found`);
if (!targetRoom.battle) return this.errorReply(`Room ${roomid} is not a battle`);
const battle = targetRoom.battle;
const player = battle.players.find(maybe => maybe.invite === user.id);
if (!player) {
return this.errorReply(`You haven't been invited to that battle.`);
let playerOpts = undefined;
if (!player.hasTeam) {
const ladder = Ladders(battle.format);
const ready = await ladder.prepBattle(connection, 'challenge');
if (!ready) return;
playerOpts = ready.settings;
}

player.invite = '';
user.send(`|pm| ${senderID}|${user.getIdentity()}|/uhtmlchange battleinvite, You rejected the battle invite`);
const fromUser = Ladders.challenges.accept(this);

this.pmTarget = fromUser;
this.sendChatMessage(`/text You accepted the battle invite`);
this.parse(`/join ${targetRoom.roomid}`);
battle.joinGame(user, slot, playerOpts);
},

uninvitebattle(target, room, user, connection) {
room = this.requireRoom();
if (!room.battle) return this.errorReply(this.tr`You can only do this in battle rooms.`);
if (room.rated) return this.errorReply(this.tr`You can only add a Player to unrated battles.`);

const {targetUser, targetUsername: name} = this.splitUser(target, {exactName: true});
const battle = room.battle;

if (!targetUser) {
battle.sendInviteForm(connection);
return this.errorReply(this.tr`User ${name} not found.`);
}
this.checkCan('joinbattle', null, room);

for (const player of battle.players) {
if (player.invite === targetUser.id) {
targetUser.send(`|pm|${user.getIdentity()}|${targetUser.getIdentity()}|/uhtml battleinvite,The battle invite was changed to someone else, sorry!`);
user.send(`|pm|${user.getIdentity()}|${targetUser.getIdentity()}|/text Invite cancelled for <<${room.roomid}>>`);
player.invite = '';
battle.sendInviteForm(connection);
return;
if (!room.battle) return this.errorReply(this.tr`You can only do this in battle rooms.`);
const invitesFull = room.battle.invitesFull();
const challenges = Ladders.challenges.get(target as ID);

if (!challenges) throw new Chat.ErrorMessage(`User ${target} is not currently invited to the battle`);
for (const challenge of challenges) {
if (challenge.to === target && challenge.roomid === room.roomid) {
Ladders.challenges.remove(challenge);
Ladders.challenges.send(challenge.from, target, `/text The battle invite was changed to someone else; sorry!`);
}
}

battle.sendInviteForm(connection);
this.errorReply(`User ${targetUser.name} is not currently invited to the battle`);
room.battle.sendInviteForm(invitesFull ? true : connection);
},

restoreplayers(target, room, user) {
Expand All @@ -1435,13 +1392,11 @@ export const commands: Chat.ChatCommands = {
if (room.rated) return this.errorReply(this.tr`You can only add a Player to unrated battles.`);

let didSomething = false;
if (!room.battle.p1.id && room.battle.p1.name !== 'Player 1') {
this.parse(`/addplayer ${room.battle.p1.name}, p1`);
didSomething = true;
}
if (!room.battle.p2.id && room.battle.p2.name !== this.tr`Player 2`) {
this.parse(`/addplayer ${room.battle.p2.name}, p2`);
didSomething = true;
for (const player of room.battle.players) {
if (!player.id && player.name !== `Player ${player.num}`) {
this.parse(`/invitebattle ${player.name}, ${player.slot}`);
didSomething = true;
}
}

if (!didSomething) {
Expand Down Expand Up @@ -1624,6 +1579,8 @@ export const commands: Chat.ChatCommands = {
this.popupReply(this.tr`This server requires you to be rank ${groupName} or higher to challenge users.`);
return false;
}

this.pmTarget = targetUser;
Zarel marked this conversation as resolved.
Show resolved Hide resolved
return Ladders(formatName).makeChallenge(connection, targetUser);
},

Expand Down Expand Up @@ -1653,23 +1610,58 @@ export const commands: Chat.ChatCommands = {
allowchallengeshelp: [
`/unblockchallenges - Unblocks challenges so you can be challenged again. Block them with /blockchallenges.`,
],
cchall: 'cancelChallenge',
cancelchallenge(target, room, user) {
Ladders.cancelChallenging(user);
cchall: 'cancelchallenge',
cancelchallenge(target, room, user, connection) {
const {targetUser, targetUsername, rest} = this.splitUser(target);
if (rest) return this.popupReply(this.tr`This command does not support specifying multiple users`);
this.pmTarget = targetUser || this.pmTarget;
if (!this.pmTarget) return this.popupReply(this.tr`User "${targetUsername}" not found.`);

const chall = Ladders.challenges.search(user.id, this.pmTarget.id);
if (!chall || chall.from !== user.id) {
connection.popup(`You are not challenging ${this.pmTarget.name}. Maybe they accepted/rejected before you cancelled?`);
return false;
}

this.sendChatMessage(`/text ${user.name} cancelled the challenge.`);
return Ladders.challenges.remove(chall);
},

accept(target, room, user, connection) {
let {targetUser, targetUsername, rest} = this.splitUser(target);
async accept(target, room, user, connection) {
const {targetUser, targetUsername, rest} = this.splitUser(target);
if (rest) return this.popupReply(this.tr`This command does not support specifying multiple users`);
targetUser = targetUser || this.pmTarget;
if (!targetUser) return this.popupReply(this.tr`User "${targetUsername}" not found.`);
return Ladders.acceptChallenge(connection, targetUser);
this.pmTarget = targetUser || this.pmTarget;
if (!this.pmTarget) return this.popupReply(this.tr`User "${targetUsername}" not found.`);

const chall = Ladders.challenges.search(user.id, this.pmTarget.id);
if (!chall || chall.to !== user.id) {
connection.popup(`${this.pmTarget.id} is not challenging you. Maybe they cancelled before you accepted?`);
return false;
}

if (chall.acceptCommand) {
return this.parse(chall.acceptCommand);
}
const gameRoom = await Ladders.acceptChallenge(connection, chall as Ladders.BattleChallenge);
if (!gameRoom) return false;
this.sendChatMessage(`/text ${user.name} accepted the challenge, starting <<${gameRoom.roomid}>>`);
return true;
},

reject(target, room, user) {
target = toID(target);
if (!target && this.pmTarget) target = this.pmTarget.id;
Ladders.rejectChallenge(user, target);
reject(target, room, user, connection) {
const {targetUser, targetUsername, rest} = this.splitUser(target);
if (rest) return this.popupReply(this.tr`This command does not support specifying multiple users`);
this.pmTarget = targetUser || this.pmTarget;
if (!this.pmTarget) return this.popupReply(this.tr`User "${targetUsername}" not found.`);

const chall = Ladders.challenges.search(user.id, this.pmTarget.id);
if (!chall || chall.to !== user.id) {
connection.popup(`${this.pmTarget.id} is not challenging you. Maybe they cancelled before you rejected?`);
return false;
}

this.sendChatMessage(`/text ${user.name} rejected the challenge.`);
return Ladders.challenges.remove(chall, false);
},

saveteam: 'useteam',
Expand Down
26 changes: 5 additions & 21 deletions server/chat-commands/info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -492,29 +492,13 @@ export const commands: Chat.ChatCommands = {
if (!(user1.id in room.users) || !(user2.id in room.users)) {
return this.errorReply(`Both users must be in this room.`);
}
const challenges = [];
const user1Challs = Ladders.challenges.get(user1.id);
if (user1Challs) {
for (const chall of user1Challs) {
if (chall.from === user1.id && Users.get(chall.to) === user2) {
challenges.push(Utils.html`${user1.name} is challenging ${user2.name} in ${Dex.formats.get(chall.formatid).name}.`);
break;
}
}
}
const user2Challs = Ladders.challenges.get(user2.id);
if (user2Challs) {
for (const chall of user2Challs) {
if (chall.from === user2.id && Users.get(chall.to) === user1) {
challenges.push(Utils.html`${user2.name} is challenging ${user1.name} in ${Dex.formats.get(chall.formatid).name}.`);
break;
}
}
}
if (!challenges.length) {
const chall = Ladders.challenges.search(user1.id, user2.id);

if (!chall) {
return this.sendReplyBox(Utils.html`${user1.name} and ${user2.name} are not challenging each other.`);
}
this.sendReplyBox(challenges.join(`<br />`));
const [from, to] = user1.id === chall.from ? [user1, user2] : [user2, user1];
this.sendReplyBox(Utils.html`${from.name} is challenging ${to.name} in ${Dex.formats.get(chall.format).name}.`);
},
checkchallengeshelp: [`!checkchallenges [user1], [user2] - Check if the specified users are challenging each other. Requires: * @ # &`],

Expand Down