Support making and receiving challenges

Actually playing the game that opens when you accept a challenge is,
of course, yet to be implemented.
This commit is contained in:
Guangcong Luo 2019-09-04 01:36:43 -05:00
parent 64b95e23cc
commit 6b66a0322e
3 changed files with 99 additions and 12 deletions

View File

@ -559,6 +559,7 @@ const PS = new class extends PSModel {
case 'html':
case 'raw':
case 'challstr':
case 'popup':
case '':
return [cmd, str.slice(index + 1)];
case 'c':
@ -878,11 +879,18 @@ const PS = new class extends PSModel {
}
return buf;
}
getPMRoom(userid: ID) {
const myUserid = PS.user.userid;
const roomid = `pm-${[userid, myUserid].sort().join('-')}` as RoomID;
if (this.rooms[roomid]) return this.rooms[roomid] as ChatRoom;
this.join(roomid);
return this.rooms[roomid] as ChatRoom;
}
addRoom(options: RoomOptions, noFocus?: boolean) {
// support hardcoded PM room-IDs
if (options.id.startsWith('challenge-')) {
options.id = `pm-${options.id.slice(10)}` as RoomID;
options.challenging = true;
options.challengeMenuOpen = true;
}
if (options.id.startsWith('pm-') && options.id.indexOf('-', 3) < 0) {
const userid1 = PS.user.userid;
@ -902,7 +910,7 @@ const PS = new class extends PSModel {
}
}
if (!noFocus) {
if (options.challenging) {
if (options.challengeMenuOpen) {
(this.rooms[options.id] as ChatRoom).openChallenge();
}
this.focusRoom(options.id);

View File

@ -9,12 +9,17 @@ class ChatRoom extends PSRoom {
readonly classType: string = 'chat';
users: {[userid: string]: string} = {};
userCount = 0;
// PM-only properties
pmTarget: string | null = null;
challenging = false;
challengeMenuOpen = false;
challengingFormat: string | null = null;
challengedFormat: string | null = null;
constructor(options: RoomOptions) {
super(options);
if (options.pmTarget) this.pmTarget = options.pmTarget as string;
if (options.challenging) this.challenging = true;
if (options.challengeMenuOpen) this.challengeMenuOpen = true;
this.updateTarget(true);
this.connect();
}
@ -64,6 +69,9 @@ class ChatRoom extends PSRoom {
}
this.openChallenge();
return true;
} case 'cchall': case 'cancelchallenge': {
this.cancelChallenge();
return true;
}}
return false;
}
@ -72,7 +80,21 @@ class ChatRoom extends PSRoom {
this.receive(`|error|Can only be used in a PM.`);
return;
}
this.challenging = true;
this.challengeMenuOpen = true;
this.update('');
}
cancelChallenge() {
if (!this.pmTarget) {
this.receive(`|error|Can only be used in a PM.`);
return;
}
if (this.challengingFormat) {
this.send('/cancelchallenge', true);
this.challengingFormat = null;
this.challengeMenuOpen = true;
} else {
this.challengeMenuOpen = false;
}
this.update('');
}
send(line: string, direct?: boolean) {
@ -310,30 +332,57 @@ class ChatPanel extends PSRoomPanel<ChatRoom> {
challenge = (e: Event, format: string, team?: Team) => {
const room = this.props.room;
const packedTeam = team ? team.packedTeam : '';
room.challenging = false;
if (!room.pmTarget) throw new Error("Not a PM room");
PS.send(`|/utm ${packedTeam}`);
PS.send(`|/challenge ${room.pmTarget}, ${format}`);
room.challengeMenuOpen = false;
room.challengingFormat = format;
room.update('');
};
acceptChallenge = (e: Event, format: string, team?: Team) => {
const room = this.props.room;
const packedTeam = team ? team.packedTeam : '';
if (!room.pmTarget) throw new Error("Not a PM room");
PS.send(`|/utm ${packedTeam}`);
this.props.room.send(`/accept`);
room.challengedFormat = null;
room.update('');
};
cancelChallenge = (e: Event) => {
e.preventDefault();
e.stopImmediatePropagation();
this.props.room.cancelChallenge();
};
rejectChallenge = (e: Event) => {
const room = this.props.room;
room.challenging = false;
room.update('');
room.challengedFormat = null;
room.send(`/reject`);
};
render() {
const room = this.props.room;
const challenge = room.challenging ? <div class="challenge">
const challengeTo = room.challengingFormat ? <div class="challenge">
<TeamForm format={room.challengingFormat} onSubmit={null}>
<button onClick={this.cancelChallenge} class="button">Cancel</button>
</TeamForm>
</div> : room.challengeMenuOpen ? <div class="challenge">
<TeamForm onSubmit={this.challenge}>
<button type="submit" class="button disabled"><strong>Challenge</strong></button> {}
<button onClick={this.cancelChallenge} class="button">Cancel</button>
</TeamForm>
</div> : null;
const challengeFrom = room.challengedFormat ? <div class="challenge">
<TeamForm format={room.challengedFormat} onSubmit={this.acceptChallenge}>
<button type="submit" class="button disabled"><strong>Accept</strong></button> {}
<button onClick={this.rejectChallenge} class="button">Reject</button>
</TeamForm>
</div> : null;
return <PSPanelWrapper room={this.props.room}>
<div class="tournament-wrapper hasuserlist"></div>
<ChatLog class="chat-log hasuserlist" room={this.props.room} onClick={this.focusIfNoSelection}>
{challenge}
{challengeTo || challengeFrom && [challengeTo, challengeFrom]}
</ChatLog>
<ChatTextEntry room={this.props.room} onMessage={this.send} onKey={this.onKey} />
<ChatUserList room={this.props.room} />

View File

@ -38,6 +38,9 @@ class MainMenuRoom extends PSRoom {
case 'updateuser':
PS.user.setName(tokens[1], tokens[2] === '1', tokens[3]);
return;
case 'updatechallenges':
this.receiveChallenges(tokens[1]);
return;
case 'queryresponse':
this.handleQueryResponse(tokens[1] as ID, JSON.parse(tokens[2]));
return;
@ -47,10 +50,37 @@ class MainMenuRoom extends PSRoom {
case 'formats':
this.parseFormats(tokens);
return;
case 'popup':
alert(tokens[1]);
return;
}
const lobby = PS.rooms['lobby'];
if (lobby) lobby.receive(line);
}
receiveChallenges(dataBuf: string) {
let json;
try {
json = JSON.parse(dataBuf);
} catch {}
for (const userid in json.challengesFrom) {
PS.getPMRoom(toID(userid));
}
if (json.challengeTo) {
PS.getPMRoom(toID(json.challengeTo.to));
}
for (const roomid in PS.rooms) {
const room = PS.rooms[roomid] as ChatRoom;
if (!room.pmTarget) continue;
const targetUserid = toID(room.pmTarget);
if (!room.challengedFormat && !(targetUserid in json.challengesFrom) &&
!room.challengingFormat && (json.challengeTo || {}).to !== targetUserid) {
continue;
}
room.challengedFormat = json.challengesFrom[targetUserid] || null;
room.challengingFormat = json.challengeTo.to === targetUserid ? json.challengeTo.format : null;
room.update('');
}
}
parseFormats(formatsList: string[]) {
let isSection = false;
let section = '';
@ -378,7 +408,7 @@ class TeamDropdown extends preact.Component<{format: string}> {
class TeamForm extends preact.Component<{
children: preact.ComponentChildren, class?: string, format?: string,
onSubmit: (e: Event, format: string, team?: Team) => void,
onSubmit: null | ((e: Event, format: string, team?: Team) => void),
}> {
state = {format: '[Gen 7] Random Battle'};
changeFormat = (e: Event) => {
@ -389,7 +419,7 @@ class TeamForm extends preact.Component<{
const format = (this.base!.querySelector('button[name=format]') as HTMLButtonElement).value;
const teamKey = (this.base!.querySelector('button[name=team]') as HTMLButtonElement).value;
const team = teamKey ? PS.teams.byKey[teamKey] : undefined;
this.props.onSubmit(e, format, team);
if (this.props.onSubmit) this.props.onSubmit(e, format, team);
};
render() {
return <form class={this.props.class} onSubmit={this.submit}>