[ongeki] Basic Gacha implement

This commit is contained in:
samnyan 2020-03-24 22:37:18 +09:00
parent 91d93e15d9
commit b8e1acb4d6
7 changed files with 444 additions and 2 deletions

View File

@ -0,0 +1,207 @@
.control {
z-index: 1500;
}
.gacha-button-group {
width: 100%;
display: flex;
}
.gacha-button-group button {
margin: auto;
width: 128px;
height: 128px;
background-color: dimgray;
}
.full-screen {
z-index: 1000;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
background: url("//static.samnyan.icu/ongeki/gameUi/Evt_BattleStart_UI_CMN_Transition_bg.png") no-repeat center;
background-size: cover;
}
/* Wide screen */
.card-container {
padding: 0 5%;
height: 100%;
margin: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
overflow: scroll;
-ms-overflow-style: none;
}
.card-container::-webkit-scrollbar {
display: none;
}
.result-card {
margin: 30px 20px;
width: 30%;
height: fit-content;
max-width: 200px;
overflow: hidden;
position: relative;
}
.result-card .overlay {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
}
.result-card .overlay img {
left: 0;
position: absolute;
height: 100%;
transform: scaleX(-1);
opacity: 60%;
animation-duration: 3s;
animation-name: overlay-glow;
animation-iteration-count: infinite;
animation-direction: normal;
}
.result-card .overlay-rarity {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
}
.result-card .overlay-rarity img {
left: 0;
position: absolute;
height: 100%;
opacity: 0;
animation-duration: 2.5s;
animation-name: overlay-rarity;
animation-iteration-count: infinite;
animation-direction: alternate;
}
@keyframes overlay-glow {
from {
left: -1000%;
}
to {
left: 300%;
}
}
@keyframes overlay-rarity {
20% {
opacity: 0;
}
50% {
opacity: 60%;
}
80% {
opacity: 0;
}
}
.flip {
transform: rotateY(180deg);
}
.card-image {
transition: transform 0.8s;
transform-style: preserve-3d;
}
.card-image .image-back {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
transform: rotateY(180deg);
}
.card-image .image-back img {
left: 0;
position: absolute;
height: 100%;
}
.image-front, .image-back {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
.image-front img, .image-back img {
width: 100%;
}
@media screen and (max-width: 600px) {
.result-card {
margin: 30px 15px;
max-width: 150px;
}
.gacha-button-group button {
width: 72px;
height: 72px;
}
}
@media screen and (max-height: 500px) {
.result-card {
max-width: 110px;
}
}
@media screen and (max-width: 420px) {
.result-card {
margin: 30px 5px;
max-width: 100px;
}
}
/**
Card animation part
*/
.card-animation-full-screen {
z-index: 1200;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
height: 100%;
width: 100%;
display: none;
}
#card_animation {
margin: auto;
max-height: 100%;
max-width: 100%;
}
#image_loader {
display: none;
}

View File

@ -0,0 +1,55 @@
<mat-card *ngIf="!isStarted" [@control] class="control">
<mat-card-content>
<h2>Regular Gacha</h2>
<p>This is still work in progress. Animation isn't implemented.</p>
<div class="gacha-button-group">
<button (click)="start(1)" mat-raised-button>Gacha<br>1x</button>
<button (click)="start(5)" mat-raised-button>Gacha<br>5x</button>
<button (click)="start(11)" mat-raised-button>Gacha<br>11x</button>
</div>
<p>Detail:<br>
R: 70%<br>
SR: 25%<br>
SSR: 5%
</p>
<p *ngIf="cardResultList.length > 0">
Card data saved in server: {{submitSuccessful}} , failed {{cardResultList.length - submitSuccessful}}
</p>
</mat-card-content>
</mat-card>
<div class="full-screen">
<div (click)="open()" *ngIf="isStarted" [@control] class="card-container">
<div *ngFor="let item of cardResultList" class="result-card" style>
<div [class.flip]="item.show" class="card-image">
<div class="image-front">
<img src="{{assets}}UI_CMN_CardBackSide.png">
</div>
<div class="image-back">
<img src="{{host}}ongeki/card/UI_Card_{{item.card.id|formatNumber:6}}.png">
</div>
</div>
<div class="overlay">
<img src="{{assets}}SB_CMN_CardGet_Card_kira.png">
</div>
<div *ngIf="item.card.rarity == 'SR' && !item.show" class="overlay-rarity">
<img src="{{assets}}UI_CMN_CardGlow_SR.png">
</div>
<div *ngIf="item.card.rarity == 'SSR' && !item.show" class="overlay-rarity">
<img src="{{assets}}UI_CMN_CardGlow_SSR.png">
</div>
</div>
</div>
</div>
<div (click)="open()" class="card-animation-full-screen">
<canvas #card_animation height="1920" id="card_animation" width="1080"></canvas>
</div>
<div id="image_loader">
<img id="new_card_get" src="{{assets}}UI_CMN_CardGet_NEW.png">
<img src="{{assets}}UI_CMN_CardGet_NEW_Right_Eff_00_1.png">
<img src="{{assets}}SB_CMN_CardGet_class_N.png">
<img src="{{assets}}SB_CMN_CardGet_Title.png">
<img src="{{assets}}UI_CMN_CardCharacter_Mask.png">
</div>

View File

@ -0,0 +1,25 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {OngekiCardGachaComponent} from './ongeki-card-gacha.component';
describe('OngekiCardGachaComponent', () => {
let component: OngekiCardGachaComponent;
let fixture: ComponentFixture<OngekiCardGachaComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [OngekiCardGachaComponent]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OngekiCardGachaComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,151 @@
import {AfterViewInit, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {environment} from '../../../../environments/environment';
import {OngekiCard} from '../model/OngekiCard';
import {ApiService} from '../../../api.service';
import {AuthenticationService} from '../../../auth/authentication.service';
import {MessageService} from '../../../message.service';
import {NgxIndexedDBService} from 'ngx-indexed-db';
import {animate, style, transition, trigger} from '@angular/animations';
@Component({
selector: 'app-ongeki-card-gacha',
templateUrl: './ongeki-card-gacha.component.html',
styleUrls: ['./ongeki-card-gacha.component.css'],
animations: [
trigger('control', [
transition(':enter', [
style({opacity: 0}),
animate('1s 0.5s ease-out',
style({opacity: 1}))
]),
transition(':leave', [
style({opacity: 1}),
animate('1s ease-in',
style({opacity: 0}))
])
])
]
})
export class OngekiCardGachaComponent implements OnInit, AfterViewInit {
@ViewChild('card_animation', {static: true})
canvas: ElementRef<HTMLCanvasElement>;
host = environment.assetsHost;
assets = environment.assetsHost + 'ongeki/gameUi/';
isStarted = false;
rarity: number[] = [];
rList: OngekiCard[] = [];
srList: OngekiCard[] = [];
ssrList: OngekiCard[] = [];
cardResultList: CardResult[] = [];
currentShowedIndex = 0;
submitSuccessful = 0;
constructor(
private api: ApiService,
private auth: AuthenticationService,
private messageService: MessageService,
private dbService: NgxIndexedDBService
) {
}
ngOnInit() {
this.rarity = Array(70).fill(1);
this.rarity = this.rarity.concat(Array(25).fill(2));
this.rarity = this.rarity.concat(Array(5).fill(3));
this.dbService.getAll<OngekiCard>('ongekiCard').then(
x => {
x.forEach(y => {
switch (y.rarity) {
case 'R':
this.rList.push(y);
break;
case 'SR':
this.srList.push(y);
break;
case 'SSR':
this.ssrList.push(y);
break;
}
});
}
);
}
ngAfterViewInit() {
// const ctx = this.canvas.nativeElement.getContext('2d');
//
// const img = new Image();
// img.onload = () => ctx.drawImage(img, 0, 0);
// img.src = this.assets + 'UI_CMN_CardGet_NEW.png';
}
start(count: number) {
this.cardResultList = [];
this.currentShowedIndex = 0;
this.isStarted = true;
this.submitSuccessful = 0;
for (let i = 0; i < count; i++) {
const rarity = this.rarity[Math.floor(Math.random() * 99)];
console.log('rarity: ' + rarity);
let card;
switch (rarity) {
case 1:
card = this.rList[Math.floor(Math.random() * this.rList.length)];
break;
case 2:
card = this.srList[Math.floor(Math.random() * this.srList.length)];
break;
case 3:
card = this.ssrList[Math.floor(Math.random() * this.ssrList.length)];
break;
}
console.log('card: ' + JSON.stringify(card));
this.cardResultList.push({
show: false,
card
});
}
this.submitCardData();
return;
}
open() {
console.log('clicked');
if (this.currentShowedIndex < this.cardResultList.length) {
const card = this.cardResultList[this.currentShowedIndex];
card.show = true;
this.currentShowedIndex = this.currentShowedIndex + 1;
} else {
this.isStarted = false;
}
}
submitCardData() {
const aimeId = this.auth.currentUserValue.extId;
this.cardResultList.forEach(x => {
this.api.post('api/game/ongeki/card', {
aimeId,
cardId: x.card.id
}).subscribe(
data => {
this.submitSuccessful = this.submitSuccessful + 1;
return;
},
error => this.messageService.notice(error)
);
});
}
}
interface CardResult {
show: boolean;
card: OngekiCard;
}

View File

@ -11,7 +11,7 @@
</p>
<div class="action">
<button mat-button routerLink="all">Show All Cards</button>
<!-- <button mat-button>Gacha</button>-->
<button mat-button routerLink="gacha">Gacha(Work in progress)</button>
</div>
</mat-card-content>
</mat-card>

View File

@ -17,6 +17,7 @@ import {OngekiRatingComponent} from './ongeki-rating/ongeki-rating.component';
import {ToLevelDecimalPipe} from './util/to-level-decimal.pipe';
import {ToBattleSpritePipe} from './util/to-battle-sprite.pipe';
import {ToTechSpritePipe} from './util/to-tech-sprite.pipe';
import {OngekiCardGachaComponent} from './ongeki-card-gacha/ongeki-card-gacha.component';
@NgModule({
@ -31,7 +32,8 @@ import {ToTechSpritePipe} from './util/to-tech-sprite.pipe';
OngekiRatingComponent,
ToLevelDecimalPipe,
ToBattleSpritePipe,
ToTechSpritePipe
ToTechSpritePipe,
OngekiCardGachaComponent
],
imports: [
CommonModule,

View File

@ -6,6 +6,7 @@ import {OngekiRecentComponent} from './ongeki-recent/ongeki-recent.component';
import {OngekiSongListComponent} from './ongeki-song-list/ongeki-song-list.component';
import {OngekiBattlePointComponent} from './ongeki-battle-point/ongeki-battle-point.component';
import {OngekiRatingComponent} from './ongeki-rating/ongeki-rating.component';
import {OngekiCardGachaComponent} from './ongeki-card-gacha/ongeki-card-gacha.component';
const routes: Routes = [
@ -16,6 +17,7 @@ const routes: Routes = [
{path: 'rating', component: OngekiRatingComponent},
{path: 'card', component: OngekiCardComponent},
{path: 'card/all', component: OngekiCardListComponent},
{path: 'card/gacha', component: OngekiCardGachaComponent},
];
export const OngekiRoutes = RouterModule.forChild(routes);