Skip to content
This repository has been archived by the owner on Feb 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #29 from MahjongPantheon/TYR-23-other-tables-preview
Browse files Browse the repository at this point in the history
Tyr 23 other tables preview
  • Loading branch information
Oleg Klimenko authored May 19, 2017
2 parents e4f59d2 + d8b7512 commit 5ed8ad2
Show file tree
Hide file tree
Showing 20 changed files with 945 additions and 33 deletions.
42 changes: 26 additions & 16 deletions src/app/app.component.html
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<!--
Tyr - Allows online game recording in japanese (riichi) mahjong sessions
Copyright (C) 2016 Oleg Klimenko aka ctizen <me@ctizen.net>
Tyr - Allows online game recording in japanese (riichi) mahjong sessions
Copyright (C) 2016 Oleg Klimenko aka ctizen <me@ctizen.net>
This file is part of Tyr.
This file is part of Tyr.
Tyr is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Tyr is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Tyr is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Tyr is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Tyr. If not, see <http://www.gnu.org/licenses/>.
-->
You should have received a copy of the GNU General Public License
along with Tyr. If not, see <http://www.gnu.org/licenses/>.
-->
<div class="page-wrap">
<nav-bar
[state]="state"
Expand Down Expand Up @@ -63,8 +63,18 @@
[state]="state"
></screen-confirmation>
<screen-last-round
*ngIf="state.currentScreen() === 'lastRound'"
[state]="state"
*ngIf="state.currentScreen() === 'lastRound'"
[state]="state"
></screen-last-round>
<screen-other-tables-list
*ngIf="state.currentScreen() === 'otherTablesList'"
[state]="state"
></screen-other-tables-list>
<screen-other-table
*ngIf="state.currentScreen() === 'otherTable'"
[state]="state"
[players]="state.getCurrentOtherTablePlayers()"
[lastRound]="state.getCurrentOtherTableLastRound()"
></screen-other-table>
</div>
</div>
4 changes: 4 additions & 0 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import { NewGameScreen } from './components/screen-new-game';
import { LastResultsScreen } from './components/screen-last-results';
import { LastRoundScreen } from './components/screen-last-round';
import { LoginScreen } from './components/screen-login';
import { OtherTablesListScreen } from './components/screen-other-tables-list';
import { OtherTableScreen } from './components/screen-other-table';

import { UserItemComponent } from './components/element-user-item';
import { YakuItemButtonComponent } from './components/element-yaku-item-button';
Expand All @@ -59,7 +61,9 @@ import { RiichiApiService } from './services/riichiApi';
NewGameScreen,
LastResultsScreen,
LastRoundScreen,
OtherTablesListScreen,
LoginScreen,
OtherTableScreen,

UserItemComponent,
YakuItemButtonComponent,
Expand Down
4 changes: 4 additions & 0 deletions src/app/components/element-custom-icon/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,8 @@ export const icons: { [key: string]: string } = {
'reload': '<svg viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg"><path d="M20,40C9,40,0,31,0,20S9,0,20,0c4.5,0,8.7,1.5,12.3,4.2c0.4,0.3,0.5,1,0.2,1.4c-0.3,0.4-1,0.5-1.4,0.2C27.9,3.3,24,2,20,2C10.1,2,2,10.1,2,20s8.1,18,18,18s18-8.1,18-18c0-3.2-0.9-6.4-2.5-9.2c-0.3-0.5-0.1-1.1,0.3-1.4c0.5-0.3,1.1-0.1,1.4,0.3C39,12.9,40,16.4,40,20C40,31,31,40,20,40z"/><path d="M30.5,20c0,0.6-0.4,1-1,1c-0.5,0-1-0.4-1-1c0-4.7-3.8-8.6-8.5-8.6c-4.7,0-8.5,3.8-8.5,8.6c0,4.7,3.8,8.6,8.5,8.6l-1.7-1.7c-0.4-0.4-0.4-1,0-1.4c0.4-0.4,1-0.4,1.4,0l3.1,3.1l0.2,0.1l0.2,0.2c0.2,0.2,0.3,0.4,0.3,0.7c0,0.3-0.1,0.5-0.3,0.7l-3.4,3.4C19.5,33.9,19.2,34,19,34s-0.5-0.1-0.7-0.3c-0.4-0.4-0.4-1,0-1.4l1.7-1.7c-5.8,0-10.5-4.7-10.5-10.6S14.2,9.4,20,9.4S30.5,14.2,30.5,20z"/></svg>',

'logout': '<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path transform="translate(-393, -407)" d="M396,408 L396,430 L411,430 L411,424 L409,424 L409,428 L398,428 L398,410 L409,410 L409,414 L411,414 L411,408 L396,408 Z M411,415 L413,414 L418,419 L413,423 L411,422 L414,420 L403,420 L403,418 L414,418 L411,415 Z" fill="#fff"/></svg>',

'details': '<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M11.98,6.99l19,0.02c0.552,0,1-0.448,1-1s-0.448-1-1-1l-19-0.02c-0.552,0-1,0.448-1,1 C10.98,6.542,11.428,6.99,11.98,6.99z"/><path d="M31,15l-19-0.02c-0.552,0-1,0.448-1,1s0.448,1,1,1L31,17c0.552,0,1-0.448,1-1C32,15.448,31.552,15,31,15z"/><path d="M31,25l-19-0.02c-0.552,0-1,0.448-1,1c0,0.552,0.448,1,1,1L31,27c0.552,0,1-0.448,1-1 C32,25.448,31.552,25,31,25z"/><path d="M3.99,2.02C1.786,2.02,0,3.806,0,6.01C0,8.214,1.786,10,3.99,10c2.204,0,3.99-1.786,3.99-3.99 C7.98,3.806,6.194,2.02,3.99,2.02z M3.98,8.01c-1.124,0-2.025-0.876-2.025-2s0.901-2,2.025-2c1.124,0,2,0.876,2,2 S5.104,8.01,3.98,8.01z"/><path d="M3.99,12.02C1.786,12.02,0,13.806,0,16.01C0,18.214,1.786,20,3.99,20c2.204,0,3.99-1.786,3.99-3.99 C7.98,13.806,6.194,12.02,3.99,12.02z M3.98,18.01c-1.124,0-2.025-0.876-2.025-2s0.901-2,2.025-2c1.124,0,2,0.876,2,2 S5.104,18.01,3.98,18.01z"/><path d="M3.99,22C1.786,22,0,23.786,0,25.99c0,2.204,1.786,3.99,3.99,3.99c2.204,0,3.99-1.786,3.99-3.99 C7.98,23.786,6.194,22,3.99,22z M3.98,27.99c-1.124,0-2.025-0.876-2.025-2s0.901-2,2.025-2c1.124,0,2,0.876,2,2 S5.104,27.99,3.98,27.99z"/></svg>',

'arrow_rotate': '<svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg"><path d="M1,2.053h3h7.455H19v0.025c6.117,0.264,11,5.292,11,11.475 c0,6.182-4.883,11.211-11,11.475v0.025h-7.545H4H3.362l3.294-3.294c0.389-0.389,0.389-1.025,0-1.414s-1.025-0.389-1.414,0 l-4.95,4.95c-0.201,0.201-0.293,0.467-0.287,0.732c-0.007,0.265,0.086,0.531,0.287,0.732l4.95,4.95c0.389,0.389,1.025,0.389,1.414,0 s0.389-1.025,0-1.414l-3.242-3.241H19c0.059,0,0.109-0.024,0.165-0.034C26.31,26.67,32,20.784,32,13.553 c0-7.456-6.044-13.5-13.5-13.5c-0.135,0-0.266,0.016-0.401,0.02c-0.034-0.004-0.064-0.02-0.099-0.02H1c-0.55,0-1,0.45-1,1 C0,1.603,0.45,2.053,1,2.053z"/></svg>'
};
13 changes: 13 additions & 0 deletions src/app/components/element-custom-icon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,28 @@ import { icons } from './icons';
selector: 'custom-icon',
template: `<i style='display: inline-block'
[innerHTML]="content"
[style.transform]="transforms"
[style.width]="resize + 'px'"
[style.height]="resize + 'px'"
></i>`
})
export class CustomIconComponent {
@Input() type: string;
@Input() resize: number = 28;
@Input() mirror: boolean = false;
content: SafeHtml = '';

get transforms() {
let t = [];
if (this.mirror) {
t.push('scaleX(-1)');
}

// TODO: more transforms?

return t.join(' ');
}

constructor(private sanitizer: DomSanitizer) { }

ngOnInit() {
Expand Down
11 changes: 10 additions & 1 deletion src/app/components/navbar/template.html
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,23 @@
Результаты
</div>
<div class="outcome-title"
*ngIf="isScreen('lastRound')">
*ngIf="isScreen('lastRound')">
Предыдущая раздача
</div>
<!-- Navbar for login screen -->
<div class="outcome-title"
*ngIf="isScreen('login')">
Вход в систему
</div>
<!-- Navbar for other tables list -->
<div class="outcome-title"
*ngIf="isScreen('otherTablesList')">
Столы в игре
</div>
<div class="outcome-title"
*ngIf="isScreen('otherTable')">
Просмотр стола
</div>
<button
*ngIf="isScreen('playersSelect')"
[disabled]="!mayGoNext('playersSelect')"
Expand Down
216 changes: 216 additions & 0 deletions src/app/components/screen-other-table/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
/*
* Tyr - Allows online game recording in japanese (riichi) mahjong sessions
* Copyright (C) 2016 Oleg Klimenko aka ctizen <me@ctizen.net>
*
* This file is part of Tyr.
*
* Tyr is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tyr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tyr. If not, see <http://www.gnu.org/licenses/>.
*/

import { Component, Input } from '@angular/core';
import { AppState } from '../../primitives/appstate';
import { YakuId, yakuMap } from '../../primitives/yaku';
import { Player } from '../../interfaces/common';
import { RRoundPaymentsInfo } from '../../interfaces/remote';

@Component({
selector: 'screen-other-table',
templateUrl: 'template.html',
styleUrls: ['style.css']
})
export class OtherTableScreen {
@Input() state: AppState;
@Input() players: Player[];
@Input() lastRound: RRoundPaymentsInfo;
/**
* Flag to prevent blinking on manual updates when all data was already loaded
*/
private _dataUpdated = false;
private _updateInterval: NodeJS.Timer;

self: Player;
shimocha: Player;
toimen: Player;
kamicha: Player;

seatSelf: string;
seatShimocha: string;
seatToimen: string;
seatKamicha: string;

_diffedBy: string = null;
_currentPlayer: number = 0;

get currentGameHash() {
return this.state.getCurrentOtherTableHash();
}

get currentTable() {
return this.state.getCurrentOtherTable().state;
}

get _loading() {
return !this._dataUpdated && this.state.isLoading('otherTable');
}

getScore(who) {
let score = this[who].score;
if (!this._diffedBy) {
return score;
}

if (this._diffedBy && this._diffedBy !== who) {
score -= this[this._diffedBy].score;
}
return (score > 0 && this._diffedBy !== who) ? '+' + score : score;
}

getChomboCount(who) {
return Math.abs(
(this[who].penalties || 0) /
this.state.getGameConfig('chomboPenalty')
) || '';
}

reloadOverview() {
this.state.updateOtherTable(this.currentGameHash);
}

viewLastRound() {
this.state.updateOtherTableLastRound(this.currentGameHash);
}

rotateTable(dir: boolean) {
if (dir) { // counter-clockwise
this._currentPlayer = (this._currentPlayer + 1) % 4;
} else { // clockwise
this._currentPlayer = (this._currentPlayer + 3) % 4;
}
this.updatePlayers();
}

playerClick(who: string) {
if (this._diffedBy === who) {
this._diffedBy = null;
} else {
this._diffedBy = who;
}
}

ngOnChanges() {
this.updatePlayers();
}

ngOnInit() {
this.updatePlayers();
this._updateInterval = setInterval(() => this._dataUpdated && this.reloadOverview(), 5000);
}

ngOnDestroy() {
clearInterval(this._updateInterval);
}

private updatePlayers() {
if (!this.players || this.players.length !== 4) {
this._dataUpdated = false;
return;
}

this._dataUpdated = true;

let players: Player[] = [].concat(this.players);
let seating = ['東', '南', '西', '北'];
for (let i = 1; i < this.currentTable.round; i++) {
seating = [seating.pop()].concat(seating);
}

for (let i = 0; i < 4; i++) {
if (this._currentPlayer === i) {
break;
}

players = players.slice(1).concat(players[0]);
seating = seating.slice(1).concat(seating[0]);
}

this.self = players[0];
this.shimocha = players[1];
this.toimen = players[2];
this.kamicha = players[3];

this.seatSelf = seating[0];
this.seatShimocha = seating[1];
this.seatToimen = seating[2];
this.seatKamicha = seating[3];
}

/// last round related

getWins(): Array<{ winner: string, han: number, fu: number, dora: number, yakuList: string }> {
switch (this.lastRound.outcome) {
case 'ron':
case 'tsumo':
return [{
winner: this._getWinnerName(this.lastRound.winner),
yakuList: this._getYakuList(this.lastRound.yaku),
han: this.lastRound.han,
fu: this.lastRound.fu,
dora: this.lastRound.dora
}];
case 'multiron':
let wins = [];
for (let idx in this.lastRound.winner) {
wins.push({
winner: this._getWinnerName(this.lastRound.winner[idx]),
yakuList: this._getYakuList(this.lastRound.yaku[idx]),
han: this.lastRound.han[idx],
fu: this.lastRound.fu[idx],
dora: this.lastRound.dora[idx]
});
}
return wins;
}
}

getOutcomeName() {
switch (this.lastRound.outcome) {
case 'ron': return 'Рон';
case 'tsumo': return 'Цумо';
case 'draw': return 'Ничья';
case 'abort': return 'Абортивная ничья';
case 'chombo': return 'Чомбо';
case 'multiron': return this.lastRound.winner.length === 2 ? 'Дабл-рон' : 'Трипл-рон';
}
}

private _getYakuList(str: string) {
const yakuIds: YakuId[] = str.split(',').map((y) => parseInt(y, 10));
const yakuNames: string[] = yakuIds.map((y) => yakuMap[y].name.toLowerCase());
return yakuNames.join(', ');
}

private _getWinnerName(winner) {
return this.players.reduce((acc, curr) => {
if (acc) {
return acc;
}

if (curr.id === winner) {
return curr.displayName;
}
}, null);
}
}


Loading

0 comments on commit 5ed8ad2

Please sign in to comment.