feat(herowars): migrate JSON static imports to HttpClient assets

Replace all TypeScript JSON imports with an HWDataService that loads
6 data files from /assets/files-data/ via HttpClient with shareReplay(1).
Update all 9 consumer components to subscribe asynchronously. Remove
unused qcm-bpa.json import and dead methods from QcmComponent.
Add ADR 0013 documenting the decision.
This commit is contained in:
2026-04-26 23:46:35 +02:00
parent 86c075db10
commit fa00719a3e
23 changed files with 89071 additions and 551 deletions
@@ -0,0 +1,67 @@
# Migrate HeroWars static JSON imports to HttpClient assets
- Status: accepted
- Date: 2026-04-26
## Context and Problem Statement
Six large JSON files (36 KB3 MB) were statically imported directly into Angular components and services via TypeScript `import` statements. This bundled all the data into the main JavaScript bundle, inflating the initial download regardless of whether the user navigated to the HeroWars section. The largest file (`hw-guild-raids.json`, 3 MB) alone would significantly delay first contentful paint for all users.
Additionally, a seventh file (`qcm-bpa.json`, 210 KB) was imported but never consumed — the component had switched to a route resolver but the dead import remained.
## Decision Drivers
- Static JSON imports are compiled into the bundle — they cannot be lazy-loaded or cached by the browser independently.
- Updating the data files required a recompile and redeployment; serving them as assets allows hot-swapping without a rebuild.
- The HeroWars section is used by a small subset of users; its data should not penalise all other users' load time.
## Considered Options
- Keep static imports
- Move JSON to assets and load via `HttpClient`
- Fetch data from the API backend
## Decision Outcome
Chosen option: "Move JSON to assets and load via HttpClient", because it removes the data from the bundle, enables browser-level HTTP caching, and requires no backend changes. The files are not secret (guild-internal analytics data) so serving them as static assets is appropriate.
Structure:
- JSON files moved from `src/files-data/` to `src/assets/files-data/`
- `HWDataService` created at `src/app/core/services/herowars/hw-data.service.ts` — exposes one `readonly` observable per file, each piped through `shareReplay(1)` so the HTTP request fires at most once per app lifecycle
- `HWClanService.loadClan()` and `HWMemberService.loadMembers()` converted from synchronous return values to `Observable<T>`
- All consumer components inject `HWDataService` and subscribe in `ngOnInit()`; template-called methods that previously accessed `guildWarSlots.slots` inline now read from a `_slots` class property populated in the subscribe callback
- Unused `import data from 'src/qcm-bpa.json'` and two dead import-helper methods removed from `qcm.component.ts`
### Positive Consequences
- HeroWars JSON (~4 MB total) no longer shipped in the initial bundle.
- Each JSON file is independently cacheable by the browser with standard HTTP cache headers.
- Data files can be updated without recompiling the Angular application.
- `shareReplay(1)` ensures a single HTTP request per session even when multiple components subscribe to the same observable.
### Negative Consequences
- Components now initialize asynchronously — there is a brief render before data arrives (consistent with the rest of the app's HTTP-driven components).
- `Object.entries()` on `any`-typed HTTP responses requires explicit `Record<string, T>` casts to satisfy strict TypeScript, adding minor verbosity.
## Pros and Cons of the Options
### Keep static imports
- Good, because synchronous — no async lifecycle complexity.
- Bad, because adds up to 4 MB to the initial bundle.
- Bad, because data updates require a full rebuild and redeployment.
### Move JSON to assets and load via HttpClient
- Good, because removes data from the bundle entirely.
- Good, because browser-cacheable independently of the app JS.
- Good, because hot-swappable without a recompile.
- Bad, because converts synchronous service methods to observables, requiring component updates.
### Fetch from API backend
- Good, because data could be managed dynamically via the admin interface.
- Bad, because requires significant backend work (new endpoints, data model) for data that is currently managed as flat files.
- Bad, because adds latency and a failure mode not present with local assets.
@@ -17,9 +17,6 @@ import { ClanViewModel, MembersViewModel } from '@viewmodels/herowars';
import { HWClanService, HWMemberService, UtilitiesService } from '@services'; import { HWClanService, HWMemberService, UtilitiesService } from '@services';
import { MAX_CLAN_MEMBERS } from '@constants'; import { MAX_CLAN_MEMBERS } from '@constants';
import guildData from '@data/hw-guild-data.json'; // page Membres
import guildStatistics from '@data/hw-guild-statistics.json'; // page Overview -> Statistics
@Component({ @Component({
selector: 'app-herowars-guildwar', selector: 'app-herowars-guildwar',
imports: [ imports: [
@@ -93,29 +90,35 @@ export class HerowarsGuildwarComponent implements OnInit {
} }
loadClan(): void { loadClan(): void {
this._guildClan = this._clanService.loadClan(); this._clanService.loadClan().subscribe((clan) => {
this._guildClan = clan;
this.clanViewModel = new ClanViewModel(this._guildClan); this.clanViewModel = new ClanViewModel(this._guildClan);
this.clanLoaded = true; this.clanLoaded = true;
});
} }
loadData(): void { loadData(): void {
this._guildClan = this._clanService.loadClan(); this._clanService.loadClan().subscribe((clan) => {
this._guildMembers = this._memberService.loadMembers(this._guildClan); this._guildClan = clan;
this._memberService.loadMembers(this._guildClan).subscribe((members) => {
this._guildMembers = members;
this._guildClan = this._clanService.loadClanStats(this._guildClan, this._guildMembers); this._guildClan = this._clanService.loadClanStats(this._guildClan, this._guildMembers);
this.daysToWarn = parseInt(this._guildClan.daysToKick) / 2; this.daysToWarn = parseInt(this._guildClan.daysToKick) / 2;
this.clanViewModel = new ClanViewModel(this._guildClan); this.clanViewModel = new ClanViewModel(this._guildClan);
this.clanLoaded = true; this.clanLoaded = true;
this.membersViewModel = new MembersViewModel(this._guildMembers); this.membersViewModel = new MembersViewModel(this._guildMembers);
this.membersLoaded = true; this.membersLoaded = true;
console.log(this._guildClan); });
console.log(this._guildMembers); });
} }
loadMembers(): void { loadMembers(): void {
this._guildMembers = this._memberService.loadMembers(this._guildClan); this._memberService.loadMembers(this._guildClan).subscribe((members) => {
this._guildMembers = members;
this.membersViewModel = new MembersViewModel(this._guildMembers); this.membersViewModel = new MembersViewModel(this._guildMembers);
this._guildClan = this._clanService.loadClanStats(this._guildClan, this._guildMembers); this._guildClan = this._clanService.loadClanStats(this._guildClan, this._guildMembers);
this.membersLoaded = true; this.membersLoaded = true;
});
} }
getMembers(): HWMember[] { getMembers(): HWMember[] {
@@ -124,11 +127,9 @@ export class HerowarsGuildwarComponent implements OnInit {
syncClan(): void { syncClan(): void {
console.log(this._guildClan); console.log(this._guildClan);
console.log(guildData);
} }
syncMembers(): void { syncMembers(): void {
console.log(this._guildMembers); console.log(this._guildMembers);
console.log(guildStatistics);
} }
} }
@@ -1,6 +1,8 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideAnimations } from '@angular/platform-browser/animations'; import { provideAnimations } from '@angular/platform-browser/animations';
import { provideNativeDateAdapter } from '@angular/material/core'; import { provideNativeDateAdapter } from '@angular/material/core';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { HerowarsComponent } from './herowars.component'; import { HerowarsComponent } from './herowars.component';
@@ -14,9 +16,10 @@ describe('HerowarsComponent', () => {
providers: [ providers: [
provideAnimations(), provideAnimations(),
provideNativeDateAdapter(), provideNativeDateAdapter(),
provideHttpClient(),
provideHttpClientTesting(),
], ],
}) }).compileComponents();
.compileComponents();
fixture = TestBed.createComponent(HerowarsComponent); fixture = TestBed.createComponent(HerowarsComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
@@ -18,10 +18,7 @@ import {
MembersStatisticsComponent, MembersStatisticsComponent,
} from '@components/shared'; } from '@components/shared';
import { Errors, HWMember } from '@models'; import { Errors, HWMember } from '@models';
import guildData from 'src/files-data/hw-guild-data.json'; // page Membres import { HWDataService } from '@services';
//import guildWarData from 'src/files-data/hw-guild-war.json'; // page Overview -> Statistics | page Guild War -> Guild War
//import guildStatistics from 'src/files-data/hw-guild-statistics.json'; // page Overview -> Statistics
//import guildWarSlots from 'src/files-data/hw-guild-war-slots.json'; // no pages
@Component({ @Component({
selector: 'app-herowars', selector: 'app-herowars',
@@ -51,6 +48,7 @@ import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
}) })
export class HerowarsComponent implements OnInit { export class HerowarsComponent implements OnInit {
private _titleService = inject(Title); private _titleService = inject(Title);
private _dataService = inject(HWDataService);
public title = 'HeroWars GM Tools'; public title = 'HeroWars GM Tools';
public description = 'Tools in working progress.'; public description = 'Tools in working progress.';
@@ -60,23 +58,20 @@ export class HerowarsComponent implements OnInit {
private _guildMembers: HWMember[] = []; private _guildMembers: HWMember[] = [];
ngOnInit() { ngOnInit() {
try {
this._titleService.setTitle(this.title); this._titleService.setTitle(this.title);
this._dataService.guildData$.subscribe({
next: (guildData) => {
for (const data of Object.entries(guildData.clan.members)) { for (const data of Object.entries(guildData.clan.members)) {
const member: HWMember = {} as HWMember; const member: HWMember = {} as HWMember;
Object.assign(member, data[1]); Object.assign(member, data[1]);
if (guildData.clan.warriors.indexOf(parseInt(member.id)) !== -1) { member.champion = guildData.clan.warriors.indexOf(parseInt(member.id)) !== -1;
member.champion = true;
} else {
member.champion = false;
}
member.heroes = { power: 0, teams: [] }; member.heroes = { power: 0, teams: [] };
member.titans = { power: 0, teams: [] }; member.titans = { power: 0, teams: [] };
this._guildMembers.push(member); this._guildMembers.push(member);
} }
} catch (error) { },
console.error(error); error: (err) => console.error(err),
} });
} }
getMembers(): HWMember[] { getMembers(): HWMember[] {
-50
View File
@@ -18,7 +18,6 @@ import { MenuItems } from '@components/shared';
import { ListErrorsComponent } from '@components/shared'; import { ListErrorsComponent } from '@components/shared';
import { Errors, Qcm, QcmCategory, QcmQuestion, QcmQuestionState, User } from '@models'; import { Errors, Qcm, QcmCategory, QcmQuestion, QcmQuestionState, User } from '@models';
import { QcmService, UserService } from '@services'; import { QcmService, UserService } from '@services';
import data from 'src/qcm-bpa.json';
@Component({ @Component({
selector: 'app-qcm', selector: 'app-qcm',
@@ -174,55 +173,6 @@ export class QcmComponent implements OnInit, OnDestroy {
console.log('onSubmit'); console.log('onSubmit');
} }
private _importChoices() {
try {
data.categories.forEach((category) => {
category.questions.forEach((question) => {
this._qcmService
.saveChoices(question)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: (question) => {
console.log('question : ', question);
//console.log(`Le saut n°${jump.numero} a été ajouté !`, 'OK');
},
error: (err) => {
console.log('err : ', err);
//console.log(`Le saut n'a pas été ajouté !`, 'Error', err);
this.errors = err;
},
});
});
});
} catch (error) {
console.error(error);
}
}
private _importQuestions() {
try {
data.categories.forEach((category) => {
//Object.assign(this.jump, category);
this._qcmService
.saveQuestions(category)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe({
next: (category) => {
console.log('category : ', category);
//console.log(`Le saut n°${jump.numero} a été ajouté !`, 'OK');
},
error: (err) => {
console.log('err : ', err);
//console.log(`Le saut n'a pas été ajouté !`, 'Error', err);
this.errors = err;
},
});
});
} catch (error) {
console.error(error);
}
}
private _resetErrors(): void { private _resetErrors(): void {
this.errors = { errors: {} }; this.errors = { errors: {} };
} }
@@ -1,4 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { GuildCardComponent } from './guild-card.component'; import { GuildCardComponent } from './guild-card.component';
@@ -8,9 +10,9 @@ describe('GuildCardComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [GuildCardComponent] imports: [GuildCardComponent],
}) providers: [provideHttpClient(), provideHttpClientTesting()],
.compileComponents(); }).compileComponents();
fixture = TestBed.createComponent(GuildCardComponent); fixture = TestBed.createComponent(GuildCardComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
@@ -1,12 +1,11 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, inject } from '@angular/core';
import { DatePipe, DecimalPipe } from '@angular/common'; import { DatePipe, DecimalPipe } from '@angular/common';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { HWGuildData, HWGuildClan, HWGuildStat } from '@models'; import { HWGuildData, HWGuildClan, HWGuildStat } from '@models';
import { HWDataService } from '@services';
import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
@Component({ @Component({
selector: 'app-guild-card', selector: 'app-guild-card',
@@ -15,18 +14,34 @@ import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
styleUrl: './guild-card.component.scss', styleUrl: './guild-card.component.scss',
}) })
export class GuildCardComponent implements OnInit { export class GuildCardComponent implements OnInit {
public guildData: HWGuildData = {} as HWGuildData; private _dataService = inject(HWDataService);
public guildData: HWGuildData = {
clan: { warriors: [] as number[] } as unknown as HWGuildClan,
stat: {} as HWGuildStat,
slots: {},
membersStat: [],
serverResetTime: 0,
clanWarEndSeasonTime: 0,
freeClanChangeInterval: { start: 0, end: 0 },
giftUids: [],
};
private _guildClan: HWGuildClan = {} as HWGuildClan; private _guildClan: HWGuildClan = {} as HWGuildClan;
private _guildStat: HWGuildStat = {} as HWGuildStat; private _guildStat: HWGuildStat = {} as HWGuildStat;
ngOnInit() { ngOnInit() {
Object.assign(this._guildClan, guildData.clan); this._dataService.guildData$.subscribe({
next: (data) => {
Object.assign(this._guildClan, data.clan);
this.guildData.clan = this._guildClan; this.guildData.clan = this._guildClan;
Object.assign(this._guildStat, guildData.stat); Object.assign(this._guildStat, data.stat);
this.guildData.stat = this._guildStat; this.guildData.stat = this._guildStat;
this.guildData.serverResetTime = guildData.serverResetTime; this.guildData.serverResetTime = data.serverResetTime;
this.guildData.clanWarEndSeasonTime = guildData.clanWarEndSeasonTime; this.guildData.clanWarEndSeasonTime = data.clanWarEndSeasonTime;
this.guildData.freeClanChangeInterval = guildData.freeClanChangeInterval; this.guildData.freeClanChangeInterval = data.freeClanChangeInterval;
this.guildData.giftUids = guildData.giftUids; this.guildData.giftUids = data.giftUids;
},
error: (err) => console.error(err),
});
} }
} }
@@ -13,8 +13,7 @@ import { MatStepperIntl, MatStepperModule } from '@angular/material/stepper';
import { MatTooltipModule } from '@angular/material/tooltip'; import { MatTooltipModule } from '@angular/material/tooltip';
import { HWGuildRaid, HWGuildRaidAttacker, HWGuildRaidAttackers, HWGuildRaidStage, HWMember } from '@models'; import { HWGuildRaid, HWGuildRaidAttacker, HWGuildRaidAttackers, HWGuildRaidStage, HWMember } from '@models';
import { HWDataService } from '@services';
import guildRaids from 'src/files-data/hw-guild-raids.json'; // page Asgard -> Guild Raid -> Log
@Injectable() @Injectable()
export class StepperIntl extends MatStepperIntl { export class StepperIntl extends MatStepperIntl {
@@ -49,6 +48,7 @@ export class StepperIntl extends MatStepperIntl {
}) })
export class GuildraidsLogComponent implements OnInit { export class GuildraidsLogComponent implements OnInit {
private fb = inject(FormBuilder); private fb = inject(FormBuilder);
private _dataService = inject(HWDataService);
public now: Date; public now: Date;
public raidsForm: FormGroup; public raidsForm: FormGroup;
@@ -107,18 +107,18 @@ export class GuildraidsLogComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.guildMembers.map((data) => { this.guildMembers.map((data) => {
data.raids = []; data.raids = [];
data.raidsInfo = { data.raidsInfo = { variationAvg: 0, variationSum: 0 };
variationAvg: 0,
variationSum: 0,
};
return data; return data;
}); });
this._dataService.guildRaids$.subscribe({
next: (guildRaids) => {
for (const member of Object.entries(guildRaids)) { for (const member of Object.entries(guildRaids)) {
const index = this.guildMembers.findIndex((item) => item.id === member[0]); const index = this.guildMembers.findIndex((item) => item.id === member[0]);
const memberRaids: HWGuildRaid[] = []; const memberRaids: HWGuildRaid[] = [];
if (index !== -1) { if (index !== -1) {
for (const data of Object.entries(member[1])) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const data of Object.entries(member[1] as Record<string, any>)) {
const raid: HWGuildRaid = {} as HWGuildRaid; const raid: HWGuildRaid = {} as HWGuildRaid;
Object.assign(raid, data[1]); Object.assign(raid, data[1]);
const startTime: number = parseInt(raid.startTime) * 1000; const startTime: number = parseInt(raid.startTime) * 1000;
@@ -133,14 +133,11 @@ export class GuildraidsLogComponent implements OnInit {
} else { } else {
raid.stage = this._raidStages[0]; raid.stage = this._raidStages[0];
} }
raid.damage = data[1].result.damage; raid.damage = data[1].result.damage;
raid.level = data[1].result.level; raid.level = data[1].result.level;
raid.userName = this.guildMembers[index].name; raid.userName = this.guildMembers[index].name;
const attackers: HWGuildRaidAttackers = {} as HWGuildRaidAttackers; const attackers: HWGuildRaidAttackers = {} as HWGuildRaidAttackers;
Object.assign(attackers, data[1].attackers); Object.assign(attackers, data[1].attackers);
//Object.assign(raid.attackers, data[1].attackers);
raid.attackers = []; raid.attackers = [];
raid.power = 0; raid.power = 0;
for (const item of Object.entries(attackers)) { for (const item of Object.entries(attackers)) {
@@ -168,39 +165,24 @@ export class GuildraidsLogComponent implements OnInit {
} }
value = raid.power - raid.stage.maxPower; value = raid.power - raid.stage.maxPower;
} }
raid.variation = { raid.variation = { value, percent, color };
value: value,
percent: percent,
color: color,
};
raid.tooltip = Math.floor(raid.variation.value / 1000) + 'k'; raid.tooltip = Math.floor(raid.variation.value / 1000) + 'k';
//console.log(raid.attackers);
memberRaids.push(raid); memberRaids.push(raid);
this._guildRaids.push(raid); this._guildRaids.push(raid);
} }
this.guildMembers[index].raids = memberRaids; this.guildMembers[index].raids = memberRaids;
const varSum = memberRaids.reduce( const varSum = memberRaids.reduce((acc, cur) => acc + cur.variation.percent, 0);
(accumulator, currentValue) => accumulator + currentValue.variation.percent,
0,
);
this.guildMembers[index].raidsInfo = { this.guildMembers[index].raidsInfo = {
variationAvg: varSum / memberRaids.length, variationAvg: varSum / memberRaids.length,
variationSum: varSum, variationSum: varSum,
}; };
} }
} }
this._guildRaids this._guildRaids.sort((a, b) => (a.startTime < b.startTime ? -1 : 1));
.sort((a, b) => (a.startTime < b.startTime ? -1 : 1)) this.guildMembers.sort((a, b) => (a.raidsInfo.variationAvg > b.raidsInfo.variationAvg ? -1 : 1));
.map((data) => { },
return data; error: (err) => console.error(err),
}); });
this.guildMembers.sort((a, b) =>
a.raidsInfo.variationAvg > b.raidsInfo.variationAvg ? -1 : 1,
); /*.map((data) => {
console.log(data.name, data.raidsInfo.variationAvg, data.raids.length);
return data;
});*/
} }
public getMembers(): HWMember[] { public getMembers(): HWMember[] {
@@ -1,4 +1,6 @@
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { provideHttpClient } from '@angular/common/http';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { GuildwarAttackComponent } from './guildwar-attack.component'; import { GuildwarAttackComponent } from './guildwar-attack.component';
@@ -8,9 +10,9 @@ describe('GuildwarAttackComponent', () => {
beforeEach(async () => { beforeEach(async () => {
await TestBed.configureTestingModule({ await TestBed.configureTestingModule({
imports: [GuildwarAttackComponent] imports: [GuildwarAttackComponent],
}) providers: [provideHttpClient(), provideHttpClientTesting()],
.compileComponents(); }).compileComponents();
fixture = TestBed.createComponent(GuildwarAttackComponent); fixture = TestBed.createComponent(GuildwarAttackComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit, inject } from '@angular/core';
import { DatePipe, DecimalPipe } from '@angular/common'; import { DatePipe, DecimalPipe } from '@angular/common';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
@@ -14,12 +14,8 @@ import {
HWGuildWarSlots, HWGuildWarSlots,
HWMember, HWMember,
} from '@models'; } from '@models';
import { HWDataService } from '@services';
/* JSON data */ import { combineLatest } from 'rxjs';
import guildWarData from 'src/files-data/hw-guild-war.json'; // page Overview -> Statistics | page Guild War -> Guild War
import guildWarInfo from 'src/files-data/hw-guild-war-info.json'; // page Guild War -> Guild War
//import guildWarLog from 'src/files-data/hw-guild-war-log.json'; // page Guild War -> Guild War
import guildWarSlots from 'src/files-data/hw-guild-war-slots.json'; // no pages
@Component({ @Component({
selector: 'app-guildwar-attack', selector: 'app-guildwar-attack',
@@ -28,8 +24,10 @@ import guildWarSlots from 'src/files-data/hw-guild-war-slots.json'; // no pages
styleUrl: './guildwar-attack.component.scss', styleUrl: './guildwar-attack.component.scss',
}) })
export class GuildwarAttackComponent implements OnInit { export class GuildwarAttackComponent implements OnInit {
private _dataService = inject(HWDataService);
private _championsByPower: HWMember[] = []; private _championsByPower: HWMember[] = [];
private _enemySlots: number[] = []; private _enemySlots: number[] = [];
private _slots: HWGuildWarSlots = {};
public guildEnemies: HWMember[] = []; public guildEnemies: HWMember[] = [];
public guildMembers: HWMember[] = []; public guildMembers: HWMember[] = [];
public title = 'Fortifications'; public title = 'Fortifications';
@@ -49,18 +47,29 @@ export class GuildwarAttackComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
combineLatest([
this._dataService.guildWarData$,
this._dataService.guildWarInfo$,
this._dataService.guildWarSlots$,
]).subscribe({
next: ([guildWarData, guildWarInfo, guildWarSlotsData]) => {
this._slots = guildWarSlotsData.slots;
this.warInfo.day = parseInt(guildWarInfo.day); this.warInfo.day = parseInt(guildWarInfo.day);
this.warInfo.clanEnemyName = guildWarInfo.enemyClan.title; this.warInfo.clanEnemyName = guildWarInfo.enemyClan.title;
this.warInfo.endTime = guildWarInfo.endTime * 1000; this.warInfo.endTime = guildWarInfo.endTime * 1000;
this.warInfo.nextWarTime = guildWarInfo.nextWarTime * 1000; this.warInfo.nextWarTime = guildWarInfo.nextWarTime * 1000;
this.warInfo.nextLockTime = guildWarInfo.nextLockTime * 1000; this.warInfo.nextLockTime = guildWarInfo.nextLockTime * 1000;
this._setChampionsByPower(); this._setChampionsByPower(guildWarData);
this._setEnemies(); this._setEnemies(guildWarInfo);
},
error: (err) => console.error(err),
});
} }
private _getFortification(indexes: number[], type: string, name: string): HWGuildWarFortification { private _getFortification(indexes: number[], type: string, name: string): HWGuildWarFortification {
const teams: HWMember[] = []; const teams: HWMember[] = [];
const slots: HWGuildWarSlots = guildWarSlots.slots; const slots: HWGuildWarSlots = this._slots;
if (!slots[name]) return { name, type, count: 0, teams: [], positions: this._enemySlots, slots: [] };
let count = 0; let count = 0;
indexes.forEach((data) => { indexes.forEach((data) => {
switch (type) { switch (type) {
@@ -88,7 +97,8 @@ export class GuildwarAttackComponent implements OnInit {
private _getDefense(type: string, name: string): HWGuildWarFortification { private _getDefense(type: string, name: string): HWGuildWarFortification {
const teams: HWMember[] = []; const teams: HWMember[] = [];
const slots: HWGuildWarSlots = guildWarSlots.slots; const slots: HWGuildWarSlots = this._slots;
if (!slots[name]) return { name, type, count: 0, teams: [], positions: this._enemySlots, slots: [] };
let count = 0; let count = 0;
slots[name].forEach((slot) => { slots[name].forEach((slot) => {
let enemies: HWMember[] = []; let enemies: HWMember[] = [];
@@ -120,12 +130,15 @@ export class GuildwarAttackComponent implements OnInit {
}; };
} }
private _setEnemies(): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any
private _setEnemies(guildWarInfo: any): void {
const warriors: number[] = []; const warriors: number[] = [];
for (const data of Object.entries(guildWarInfo.enemyClanTries)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const data of Object.entries(guildWarInfo.enemyClanTries as Record<string, any>)) {
warriors.push(parseInt(data[0])); warriors.push(parseInt(data[0]));
} }
for (const data of Object.entries(guildWarInfo.enemyClanMembers)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const data of Object.entries(guildWarInfo.enemyClanMembers as Record<string, any>)) {
const enemy: HWMember = {} as HWMember; const enemy: HWMember = {} as HWMember;
Object.assign(enemy, data[1]); Object.assign(enemy, data[1]);
if (warriors.indexOf(parseInt(enemy.id)) !== -1) { if (warriors.indexOf(parseInt(enemy.id)) !== -1) {
@@ -141,16 +154,17 @@ export class GuildwarAttackComponent implements OnInit {
this.guildEnemies.push(enemy); this.guildEnemies.push(enemy);
} }
for (const data of Object.entries(guildWarInfo.enemySlots)) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const data of Object.entries(guildWarInfo.enemySlots as Record<string, any>)) {
const ennemySlot: HWGuildWarEnemySlot = {} as HWGuildWarEnemySlot; const ennemySlot: HWGuildWarEnemySlot = {} as HWGuildWarEnemySlot;
Object.assign(ennemySlot, data[1]); Object.assign(ennemySlot, data[1]);
//console.log(ennemySlot.slotId, ennemySlot.user.name, ennemySlot.user.id); //, ennemySlot.team["1"].power);
ennemySlot.teams = []; ennemySlot.teams = [];
let totalHeroPower = 0; let totalHeroPower = 0;
let totalTitanPower = 0; let totalTitanPower = 0;
if (ennemySlot.user !== null) { if (ennemySlot.user !== null) {
this._enemySlots[ennemySlot.slotId] = parseInt(ennemySlot.user.id); this._enemySlots[ennemySlot.slotId] = parseInt(ennemySlot.user.id);
for (const team of Object.entries(data[1].team[0])) { // eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const team of Object.entries(data[1].team[0] as Record<string, any>)) {
/* /*
ennemySlot.teams.push(enemyTeam); ennemySlot.teams.push(enemyTeam);
console.log(enemyTeam); console.log(enemyTeam);
@@ -194,8 +208,10 @@ export class GuildwarAttackComponent implements OnInit {
} }
} }
private _setChampionsByPower(): void { // eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const [teamId, teamValues] of Object.entries(guildWarData.teams)) { private _setChampionsByPower(guildWarData: any): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const [teamId, teamValues] of Object.entries(guildWarData.teams as Record<string, any>)) {
const index = this.guildMembers.findIndex((member) => member.id === teamId); const index = this.guildMembers.findIndex((member) => member.id === teamId);
if (index === -1) continue; if (index === -1) continue;
let totalHeroPower = 0; let totalHeroPower = 0;
@@ -203,11 +219,15 @@ export class GuildwarAttackComponent implements OnInit {
const heroes: HWGuildWarHeroTeam[] = []; const heroes: HWGuildWarHeroTeam[] = [];
const titans: HWGuildWarTitanTeam[] = []; const titans: HWGuildWarTitanTeam[] = [];
for (const unit of Object.entries(teamValues.clanDefence_heroes.units)) { for (const unit of Object.entries(
teamValues.clanDefence_heroes.units as Record<string, HWGuildWarHeroTeam>,
)) {
heroes.push(unit[1]); heroes.push(unit[1]);
totalHeroPower += unit[1].power; totalHeroPower += unit[1].power;
} }
for (const unit of Object.entries(teamValues.clanDefence_titans.units)) { for (const unit of Object.entries(
teamValues.clanDefence_titans.units as Record<string, HWGuildWarTitanTeam>,
)) {
titans.push(unit[1]); titans.push(unit[1]);
totalTitanPower += unit[1].power; totalTitanPower += unit[1].power;
} }
@@ -1,4 +1,4 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit, inject } from '@angular/core';
//import { DecimalPipe } from '@angular/common'; //import { DecimalPipe } from '@angular/common';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
@@ -7,10 +7,8 @@ import { MatIconModule } from '@angular/material/icon';
import { FortificationCardContentComponent } from '../fortification-card-content/fortification-card-content.component'; import { FortificationCardContentComponent } from '../fortification-card-content/fortification-card-content.component';
//import { HWMember } from '@models'; //import { HWMember } from '@models';
import { HWGuildWarFortification, HWGuildWarHeroTeam, HWGuildWarSlots, HWGuildWarTitanTeam, HWMember } from '@models'; import { HWGuildWarFortification, HWGuildWarHeroTeam, HWGuildWarSlots, HWGuildWarTitanTeam, HWMember } from '@models';
import { HWDataService } from '@services';
/* JSON data */ import { combineLatest } from 'rxjs';
import guildWarData from 'src/files-data/hw-guild-war.json'; // page Overview -> Statistics | page Guild War -> Guild War
import guildWarSlots from 'src/files-data/hw-guild-war-slots.json'; // no pages
@Component({ @Component({
selector: 'app-guildwar-defence', selector: 'app-guildwar-defence',
@@ -19,8 +17,10 @@ import guildWarSlots from 'src/files-data/hw-guild-war-slots.json'; // no pages
styleUrl: './guildwar-defence.component.scss', styleUrl: './guildwar-defence.component.scss',
}) })
export class GuildwarDefenceComponent implements OnInit { export class GuildwarDefenceComponent implements OnInit {
private _dataService = inject(HWDataService);
private _championsByPower: HWMember[] = []; private _championsByPower: HWMember[] = [];
private _championSlots: number[] = []; private _championSlots: number[] = [];
private _slots: HWGuildWarSlots = {};
public guildMembers: HWMember[] = []; public guildMembers: HWMember[] = [];
public teamType: string = ''; public teamType: string = '';
public title = 'Fortifications'; public title = 'Fortifications';
@@ -35,7 +35,11 @@ export class GuildwarDefenceComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
for (const [teamId, teamValues] of Object.entries(guildWarData.teams)) { combineLatest([this._dataService.guildWarData$, this._dataService.guildWarSlots$]).subscribe({
next: ([guildWarData, guildWarSlotsData]) => {
this._slots = guildWarSlotsData.slots;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const [teamId, teamValues] of Object.entries(guildWarData.teams as Record<string, any>)) {
const index = this.guildMembers.findIndex((member) => member.id === teamId); const index = this.guildMembers.findIndex((member) => member.id === teamId);
if (index === -1) continue; if (index === -1) continue;
let totalHeroPower = 0; let totalHeroPower = 0;
@@ -44,39 +48,45 @@ export class GuildwarDefenceComponent implements OnInit {
const titans: HWGuildWarTitanTeam[] = []; const titans: HWGuildWarTitanTeam[] = [];
this.guildMembers[index].heroes = { power: totalHeroPower, teams: heroes }; this.guildMembers[index].heroes = { power: totalHeroPower, teams: heroes };
this.guildMembers[index].titans = { power: totalTitanPower, teams: titans }; this.guildMembers[index].titans = { power: totalTitanPower, teams: titans };
for (const unit of Object.entries(
for (const unit of Object.entries(teamValues.clanDefence_heroes.units)) { teamValues.clanDefence_heroes.units as Record<string, HWGuildWarHeroTeam>,
)) {
heroes.push(unit[1]); heroes.push(unit[1]);
totalHeroPower += unit[1].power; totalHeroPower += unit[1].power;
} }
for (const unit of Object.entries(teamValues.clanDefence_titans.units)) { for (const unit of Object.entries(
teamValues.clanDefence_titans.units as Record<string, HWGuildWarTitanTeam>,
)) {
titans.push(unit[1]); titans.push(unit[1]);
totalTitanPower += unit[1].power; totalTitanPower += unit[1].power;
} }
this.guildMembers[index].heroes = { power: totalHeroPower, teams: heroes }; this.guildMembers[index].heroes = { power: totalHeroPower, teams: heroes };
this.guildMembers[index].titans = { power: totalTitanPower, teams: titans }; this.guildMembers[index].titans = { power: totalTitanPower, teams: titans };
} }
for (const data of Object.entries(guildWarData.slots)) { for (const data of Object.entries(guildWarData.slots as Record<string, number>)) {
this._championSlots[parseInt(data[0])] = data[1]; this._championSlots[parseInt(data[0])] = data[1];
} }
switch (this.teamType) { switch (this.teamType) {
case 'heroes': case 'heroes':
this.title = 'Heroes Fortifications'; this.title = 'Heroes Fortifications';
this.guildMembers.sort((a, b) => (a.heroes.power > b.heroes.power ? -1 : 1)); // Descending this.guildMembers.sort((a, b) => (a.heroes.power > b.heroes.power ? -1 : 1));
break; break;
case 'titans': case 'titans':
this.title = 'Titans Fortifications'; this.title = 'Titans Fortifications';
this.guildMembers.sort((a, b) => (a.titans.power > b.titans.power ? -1 : 1)); // Descending this.guildMembers.sort((a, b) => (a.titans.power > b.titans.power ? -1 : 1));
break; break;
default: default:
break; break;
} }
this._championsByPower = this.guildMembers.filter((member) => member.champion === true); this._championsByPower = this.guildMembers.filter((member) => member.champion === true);
},
error: (err) => console.error(err),
});
} }
private _getFortification(indexes: number[], type: string, name: string): HWGuildWarFortification { private _getFortification(indexes: number[], type: string, name: string): HWGuildWarFortification {
const teams: HWMember[] = []; const teams: HWMember[] = [];
const slots: HWGuildWarSlots = guildWarSlots.slots; const slots: HWGuildWarSlots = this._slots;
let count = 0; let count = 0;
indexes.forEach((data) => { indexes.forEach((data) => {
const member = this._championsByPower[data]; const member = this._championsByPower[data];
@@ -1,12 +1,11 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit, inject } from '@angular/core';
import { DecimalPipe } from '@angular/common'; import { DecimalPipe } from '@angular/common';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { HWGuildWarHeroTeam, HWGuildWarTitanTeam, HWMember } from '@models'; import { HWGuildWarHeroTeam, HWGuildWarTitanTeam, HWMember } from '@models';
import { HWDataService } from '@services';
import guildWarData from 'src/files-data/hw-guild-war.json'; // page Overview -> Statistics | page Guild War -> Guild War
@Component({ @Component({
selector: 'app-guildwar-teams', selector: 'app-guildwar-teams',
@@ -15,6 +14,7 @@ import guildWarData from 'src/files-data/hw-guild-war.json'; // page Overview ->
styleUrl: './guildwar-teams.component.scss', styleUrl: './guildwar-teams.component.scss',
}) })
export class GuildwarTeamsComponent implements OnInit { export class GuildwarTeamsComponent implements OnInit {
private _dataService = inject(HWDataService);
private _championsByPower: HWMember[] = []; private _championsByPower: HWMember[] = [];
public guildMembers: HWMember[] = []; public guildMembers: HWMember[] = [];
public title = 'Members'; public title = 'Members';
@@ -32,29 +32,29 @@ export class GuildwarTeamsComponent implements OnInit {
this.guildMembers this.guildMembers
.sort((a, b) => ((a.heroes.power + a.titans.power) / 2 > (b.heroes.power + b.titans.power) / 2 ? -1 : 1)) .sort((a, b) => ((a.heroes.power + a.titans.power) / 2 > (b.heroes.power + b.titans.power) / 2 ? -1 : 1))
.map((data) => { .map((data) => {
/*
this.guildTotalHeroesPower += data.heroes.power;
this.guildTotalTitansPower += data.titans.power;
this.guildAverageHeroesPower = Math.floor((this.guildTotalHeroesPower / this.guildMembers.length));
this.guildAverageTitansPower = Math.floor((this.guildTotalTitansPower / this.guildMembers.length));
*/
this._championsByPower.push(data); this._championsByPower.push(data);
return data; return data;
}); });
for (const [teamId, teamValues] of Object.entries(guildWarData.teams)) { this._dataService.guildWarData$.subscribe({
next: (guildWarData) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
for (const [teamId, teamValues] of Object.entries(guildWarData.teams as Record<string, any>)) {
const index = this._championsByPower.findIndex((member) => member.id === teamId); const index = this._championsByPower.findIndex((member) => member.id === teamId);
if (index === -1) continue; if (index === -1) continue;
let totalHeroPower = 0; let totalHeroPower = 0;
let totalTitanPower = 0; let totalTitanPower = 0;
const heroes: HWGuildWarHeroTeam[] = []; const heroes: HWGuildWarHeroTeam[] = [];
const titans: HWGuildWarTitanTeam[] = []; const titans: HWGuildWarTitanTeam[] = [];
for (const unit of Object.entries(
for (const unit of Object.entries(teamValues.clanDefence_heroes.units)) { teamValues.clanDefence_heroes.units as Record<string, HWGuildWarHeroTeam>,
)) {
heroes.push(unit[1]); heroes.push(unit[1]);
totalHeroPower += unit[1].power; totalHeroPower += unit[1].power;
} }
for (const unit of Object.entries(teamValues.clanDefence_titans.units)) { for (const unit of Object.entries(
teamValues.clanDefence_titans.units as Record<string, HWGuildWarTitanTeam>,
)) {
titans.push(unit[1]); titans.push(unit[1]);
totalTitanPower += unit[1].power; totalTitanPower += unit[1].power;
} }
@@ -68,6 +68,9 @@ export class GuildwarTeamsComponent implements OnInit {
} }
this.guildAverageHeroesPower = Math.floor(this.guildTotalHeroesPower / this._championsByPower.length); this.guildAverageHeroesPower = Math.floor(this.guildTotalHeroesPower / this._championsByPower.length);
this.guildAverageTitansPower = Math.floor(this.guildTotalTitansPower / this._championsByPower.length); this.guildAverageTitansPower = Math.floor(this.guildTotalTitansPower / this._championsByPower.length);
},
error: (err) => console.error(err),
});
} }
getChampionsByPower(): HWMember[] { getChampionsByPower(): HWMember[] {
@@ -1,14 +1,12 @@
import { Component, Input, OnInit, inject } from '@angular/core'; import { Component, Input, OnInit, inject } from '@angular/core';
import { combineLatest } from 'rxjs';
import { DatePipe, DecimalPipe } from '@angular/common'; import { DatePipe, DecimalPipe } from '@angular/common';
import { MatCardModule } from '@angular/material/card'; import { MatCardModule } from '@angular/material/card';
import { MatDividerModule } from '@angular/material/divider'; import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon'; import { MatIconModule } from '@angular/material/icon';
import { HWActivityStat, HWGuildClan, HWGuildData, HWMember, HWMemberStat, HWWeekStat } from '@models/herowars'; import { HWActivityStat, HWGuildClan, HWGuildData, HWMember, HWMemberStat, HWWeekStat } from '@models/herowars';
import { UtilitiesService } from '@services'; import { HWDataService, UtilitiesService } from '@services';
import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
import guildStatistics from 'src/files-data/hw-guild-statistics.json'; // page Overview -> Statistics
@Component({ @Component({
selector: 'app-members-statistics', selector: 'app-members-statistics',
@@ -18,6 +16,7 @@ import guildStatistics from 'src/files-data/hw-guild-statistics.json'; // page O
}) })
export class MembersStatisticsComponent implements OnInit { export class MembersStatisticsComponent implements OnInit {
private _utilitiesService = inject(UtilitiesService); private _utilitiesService = inject(UtilitiesService);
private _dataService = inject(HWDataService);
public guildMembers: HWMember[] = []; public guildMembers: HWMember[] = [];
public title = 'Members'; public title = 'Members';
@@ -85,6 +84,8 @@ export class MembersStatisticsComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
combineLatest([this._dataService.guildData$, this._dataService.guildStatistics$]).subscribe({
next: ([guildData, guildStatistics]) => {
const oneDayInSec = 24 * 60 * 60; const oneDayInSec = 24 * 60 * 60;
const today = new Date(); const today = new Date();
const daysToKick = parseInt(guildData.clan.daysToKick); const daysToKick = parseInt(guildData.clan.daysToKick);
@@ -101,15 +102,14 @@ export class MembersStatisticsComponent implements OnInit {
timeLeft = diff - daysLeft * oneDayInSec; timeLeft = diff - daysLeft * oneDayInSec;
} }
data.inactivity = { data.inactivity = {
daysLeft: daysLeft, daysLeft,
timeLeft: timeLeft, timeLeft,
dropDelay: `${daysLeft}d, ${this._utilitiesService.secondsToDuration(timeLeft)}`, dropDelay: `${daysLeft}d, ${this._utilitiesService.secondsToDuration(timeLeft)}`,
dropDate: Math.floor(exp.getTime() / 1000), dropDate: Math.floor(exp.getTime() / 1000),
}; };
this._membersByName.push(data); this._membersByName.push(data);
return data; return data;
}); });
Object.assign(this._guildClan, guildData.clan); Object.assign(this._guildClan, guildData.clan);
this.guildData.clan = this._guildClan; this.guildData.clan = this._guildClan;
this.guildData.membersStat = guildData.membersStat; this.guildData.membersStat = guildData.membersStat;
@@ -118,15 +118,15 @@ export class MembersStatisticsComponent implements OnInit {
const index = this._membersByName.findIndex((member) => member.id === item[1].id); const index = this._membersByName.findIndex((member) => member.id === item[1].id);
if (index !== -1) { if (index !== -1) {
this._membersByName[index].adventureSum = item[1].adventureStat.reduce( this._membersByName[index].adventureSum = item[1].adventureStat.reduce(
(accumulator, currentValue) => accumulator + currentValue, (acc: number, cur: number) => acc + cur,
0, 0,
); );
this._membersByName[index].clanGiftsSum = item[1].clanGifts.reduce( this._membersByName[index].clanGiftsSum = item[1].clanGifts.reduce(
(accumulator, currentValue) => accumulator + currentValue, (acc: number, cur: number) => acc + cur,
0, 0,
); );
this._membersByName[index].clanWarSum = item[1].clanWarStat.reduce( this._membersByName[index].clanWarSum = item[1].clanWarStat.reduce(
(accumulator, currentValue) => accumulator + currentValue, (acc: number, cur: number) => acc + cur,
0, 0,
); );
this.guildSumTotal.adventure += this._membersByName[index].adventureSum; this.guildSumTotal.adventure += this._membersByName[index].adventureSum;
@@ -135,34 +135,23 @@ export class MembersStatisticsComponent implements OnInit {
const stat: HWWeekStat = {} as HWWeekStat; const stat: HWWeekStat = {} as HWWeekStat;
Object.assign(stat, item[1]); Object.assign(stat, item[1]);
this._membersByName[index].weekStat = stat; this._membersByName[index].weekStat = stat;
} else {
console.log(item[1].id);
} }
} }
for (const data of Object.entries(guildData.membersStat)) { for (const data of Object.entries(guildData.membersStat)) {
const stat: HWMemberStat = {} as HWMemberStat; const stat: HWMemberStat = {} as HWMemberStat;
Object.assign(stat, data[1]); Object.assign(stat, data[1]);
const index = this._membersByName.findIndex((member) => member.id === data[1].userId); // eslint-disable-next-line @typescript-eslint/no-explicit-any
if (index !== -1) { const index = this._membersByName.findIndex((member) => member.id === (data[1] as any).userId);
if (index === -1) continue;
this._membersByName[index].stat = stat; this._membersByName[index].stat = stat;
} else { this._membersByName[index].score = stat.dungeonActivitySum + stat.activitySum + stat.prestigeSum;
continue;
}
this._membersByName[index].score =
this._membersByName[index].stat.dungeonActivitySum +
this._membersByName[index].stat.activitySum +
this._membersByName[index].stat.prestigeSum;
this._membersByName[index].warGifts = this._membersByName[index].warGifts =
((guildData.clan.giftsCount / 2) * ((this._membersByName[index].clanWarSum * 10) / 100)) / 20; ((guildData.clan.giftsCount / 2) * ((this._membersByName[index].clanWarSum * 10) / 100)) / 20;
this.guildTodayTotal.activity += stat.todayActivity; this.guildTodayTotal.activity += stat.todayActivity;
this.guildTodayTotal.prestige += stat.todayPrestige; this.guildTodayTotal.prestige += stat.todayPrestige;
this.guildTodayTotal.titanite += stat.todayDungeonActivity; this.guildTodayTotal.titanite += stat.todayDungeonActivity;
this.guildTodayTotal.score += this.guildTodayTotal.score += stat.todayDungeonActivity + stat.todayActivity + stat.todayPrestige;
this._membersByName[index].stat.todayDungeonActivity +
this._membersByName[index].stat.todayActivity +
this._membersByName[index].stat.todayPrestige;
this.guildSumTotal.activity += stat.activitySum; this.guildSumTotal.activity += stat.activitySum;
this.guildSumTotal.prestige += stat.prestigeSum; this.guildSumTotal.prestige += stat.prestigeSum;
this.guildSumTotal.titanite += stat.dungeonActivitySum; this.guildSumTotal.titanite += stat.dungeonActivitySum;
@@ -171,7 +160,6 @@ export class MembersStatisticsComponent implements OnInit {
this._deviations.activity.push(stat.activitySum); this._deviations.activity.push(stat.activitySum);
this._deviations.prestige.push(stat.prestigeSum); this._deviations.prestige.push(stat.prestigeSum);
this._deviations.titanite.push(stat.dungeonActivitySum); this._deviations.titanite.push(stat.dungeonActivitySum);
//this.guildSumTotal.warGifts += ((this._membersByName[index].score / this.guildSumTotal.score) * (guildData.clan.giftsCount / 2))
} }
this.guildSumTotal.scoreGifts = guildData.clan.giftsCount - Math.floor(this.guildSumTotal.warGifts); this.guildSumTotal.scoreGifts = guildData.clan.giftsCount - Math.floor(this.guildSumTotal.warGifts);
this.guildTodayAverages.activity = this.guildTodayTotal.activity / this._membersByName.length; this.guildTodayAverages.activity = this.guildTodayTotal.activity / this._membersByName.length;
@@ -185,7 +173,6 @@ export class MembersStatisticsComponent implements OnInit {
this.guildSumAverages.adventure = this.guildSumTotal.adventure / this._membersByName.length; this.guildSumAverages.adventure = this.guildSumTotal.adventure / this._membersByName.length;
this.guildSumAverages.gifts = this.guildSumTotal.gifts / this._membersByName.length; this.guildSumAverages.gifts = this.guildSumTotal.gifts / this._membersByName.length;
this.guildSumAverages.score = this.guildSumTotal.score / this._membersByName.length; this.guildSumAverages.score = this.guildSumTotal.score / this._membersByName.length;
this._membersByName.map((data) => { this._membersByName.map((data) => {
data.scoreGifts = (data.score / this.guildSumTotal.score) * this.guildSumTotal.scoreGifts; data.scoreGifts = (data.score / this.guildSumTotal.score) * this.guildSumTotal.scoreGifts;
data.rewards = Math.floor(data.scoreGifts + data.warGifts); data.rewards = Math.floor(data.scoreGifts + data.warGifts);
@@ -193,6 +180,9 @@ export class MembersStatisticsComponent implements OnInit {
return data; return data;
}); });
this.guildSumAverages.rewards = this.guildSumTotal.rewards / this._membersByName.length; this.guildSumAverages.rewards = this.guildSumTotal.rewards / this._membersByName.length;
},
error: (err) => console.error(err),
});
} }
getMembersByName(): HWMember[] { getMembersByName(): HWMember[] {
@@ -14,12 +14,12 @@ import {
HWGuildClan, HWGuildClan,
HWMember, HWMember,
} from '@models'; } from '@models';
import { HWDataService } from './hw-data.service';
import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class HWClanService { export class HWClanService {
private apiService = inject(ApiService); private apiService = inject(ApiService);
private _dataService = inject(HWDataService);
private _apiDomain = '/herowars'; private _apiDomain = '/herowars';
@@ -87,15 +87,18 @@ export class HWClanService {
} }
} }
loadClan(): HWGuildClan { loadClan(): Observable<HWGuildClan> {
return this._dataService.guildData$.pipe(
map((guildData) => {
const guildClan: HWGuildClan = {} as HWGuildClan; const guildClan: HWGuildClan = {} as HWGuildClan;
Object.assign(guildClan, guildData.clan); Object.assign(guildClan, guildData.clan);
guildClan.sumAverages = this._resetActivity(); guildClan.sumAverages = this._resetActivity();
guildClan.sumTotal = this._resetActivity(); guildClan.sumTotal = this._resetActivity();
guildClan.todayAverages = this._resetActivity(); guildClan.todayAverages = this._resetActivity();
guildClan.todayTotal = this._resetActivity(); guildClan.todayTotal = this._resetActivity();
return guildClan; return guildClan;
}),
);
} }
loadClanStats(clan: HWGuildClan, members: HWMember[]): HWGuildClan { loadClanStats(clan: HWGuildClan, members: HWMember[]): HWGuildClan {
@@ -0,0 +1,44 @@
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { shareReplay } from 'rxjs/operators';
import { HWGuildData, HWGuildWarSlots, HWWeekStat } from '@models';
export interface HWGuildStatistics {
stat: (HWWeekStat & { id: string })[];
}
@Injectable({ providedIn: 'root' })
export class HWDataService {
private _http = inject(HttpClient);
readonly guildData$: Observable<HWGuildData> = this._http
.get<HWGuildData>('/assets/files-data/hw-guild-data.json')
.pipe(shareReplay(1));
readonly guildStatistics$: Observable<HWGuildStatistics> = this._http
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.get<HWGuildStatistics>('/assets/files-data/hw-guild-statistics.json')
.pipe(shareReplay(1));
// eslint-disable-next-line @typescript-eslint/no-explicit-any
readonly guildRaids$: Observable<Record<string, Record<string, any>>> = this._http
// eslint-disable-next-line @typescript-eslint/no-explicit-any
.get<Record<string, Record<string, any>>>('/assets/files-data/hw-guild-raids.json')
.pipe(shareReplay(1));
/* eslint-disable @typescript-eslint/no-explicit-any */
readonly guildWarData$: Observable<any> = this._http
.get<any>('/assets/files-data/hw-guild-war.json')
.pipe(shareReplay(1));
readonly guildWarInfo$: Observable<any> = this._http
.get<any>('/assets/files-data/hw-guild-war-info.json')
.pipe(shareReplay(1));
/* eslint-enable @typescript-eslint/no-explicit-any */
readonly guildWarSlots$: Observable<{ slots: HWGuildWarSlots }> = this._http
.get<{ slots: HWGuildWarSlots }>('/assets/files-data/hw-guild-war-slots.json')
.pipe(shareReplay(1));
}
@@ -1,6 +1,6 @@
import { Injectable, inject } from '@angular/core'; import { Injectable, inject } from '@angular/core';
import { HttpParams } from '@angular/common/http'; import { HttpParams } from '@angular/common/http';
import { Observable } from 'rxjs'; import { combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators'; import { map } from 'rxjs/operators';
import { ApiService, UtilitiesService } from '@services'; import { ApiService, UtilitiesService } from '@services';
@@ -14,13 +14,12 @@ import {
HWMemberPageData, HWMemberPageData,
HWMemberStat, HWMemberStat,
} from '@models'; } from '@models';
import { HWDataService } from './hw-data.service';
import guildData from 'src/files-data/hw-guild-data.json'; // page Membres
import guildStatistics from 'src/files-data/hw-guild-statistics.json'; // page Overview -> Statistics
@Injectable({ providedIn: 'root' }) @Injectable({ providedIn: 'root' })
export class HWMemberService { export class HWMemberService {
private _apiService = inject(ApiService); private _apiService = inject(ApiService);
private _utilitiesService = inject(UtilitiesService); private _utilitiesService = inject(UtilitiesService);
private _dataService = inject(HWDataService);
private _apiDomain = '/herowars'; private _apiDomain = '/herowars';
@@ -76,7 +75,9 @@ export class HWMemberService {
} }
} }
loadMembers(clan: HWGuildClan): HWMember[] { loadMembers(clan: HWGuildClan): Observable<HWMember[]> {
return combineLatest([this._dataService.guildData$, this._dataService.guildStatistics$]).pipe(
map(([guildData, guildStatistics]) => {
const guildMembers: HWMember[] = []; const guildMembers: HWMember[] = [];
const now = new Date(); const now = new Date();
const oneDayInSec = 24 * 60 * 60; const oneDayInSec = 24 * 60 * 60;
@@ -84,11 +85,7 @@ export class HWMemberService {
for (const data of Object.entries(guildData.clan.members)) { for (const data of Object.entries(guildData.clan.members)) {
const member: HWMember = {} as HWMember; const member: HWMember = {} as HWMember;
Object.assign(member, data[1]); Object.assign(member, data[1]);
if (guildData.clan.warriors.indexOf(parseInt(member.id)) !== -1) { member.champion = guildData.clan.warriors.indexOf(parseInt(member.id)) !== -1;
member.champion = true;
} else {
member.champion = false;
}
member.heroes = { power: 0, teams: [] }; member.heroes = { power: 0, teams: [] };
member.titans = { power: 0, teams: [] }; member.titans = { power: 0, teams: [] };
guildMembers.push(member); guildMembers.push(member);
@@ -109,12 +106,10 @@ export class HWMemberService {
dropDate: Math.floor(exp.getTime() / 1000), dropDate: Math.floor(exp.getTime() / 1000),
}; };
data.raids = []; data.raids = [];
data.raidsInfo = { data.raidsInfo = { variationAvg: 0, variationSum: 0 };
variationAvg: 0,
variationSum: 0,
};
data.stat = {} as HWMemberStat; data.stat = {} as HWMemberStat;
let index = guildData.membersStat.findIndex((stat) => stat.userId === data.id); // eslint-disable-next-line @typescript-eslint/no-explicit-any
let index = guildData.membersStat.findIndex((stat) => (stat as any).userId === data.id);
if (index !== -1) { if (index !== -1) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
const { userId, ...memberStatWithoutUserId } = guildData.membersStat[index] as any; const { userId, ...memberStatWithoutUserId } = guildData.membersStat[index] as any;
@@ -128,15 +123,15 @@ export class HWMemberService {
Object.assign(stat, weekStatSource); Object.assign(stat, weekStatSource);
data.weekStat = stat; data.weekStat = stat;
data.adventureSum = guildStatistics.stat[index].adventureStat.reduce( data.adventureSum = guildStatistics.stat[index].adventureStat.reduce(
(accumulator, currentValue) => accumulator + currentValue, (acc: number, cur: number) => acc + cur,
0, 0,
); );
data.clanGiftsSum = guildStatistics.stat[index].clanGifts.reduce( data.clanGiftsSum = guildStatistics.stat[index].clanGifts.reduce(
(accumulator, currentValue) => accumulator + currentValue, (acc: number, cur: number) => acc + cur,
0, 0,
); );
data.clanWarSum = guildStatistics.stat[index].clanWarStat.reduce( data.clanWarSum = guildStatistics.stat[index].clanWarStat.reduce(
(accumulator, currentValue) => accumulator + currentValue, (acc: number, cur: number) => acc + cur,
0, 0,
); );
} else { } else {
@@ -145,14 +140,14 @@ export class HWMemberService {
data.clanGiftsSum = 0; data.clanGiftsSum = 0;
data.clanWarSum = 0; data.clanWarSum = 0;
} }
data.score = data.stat.dungeonActivitySum + data.stat.activitySum + data.stat.prestigeSum; data.score = data.stat.dungeonActivitySum + data.stat.activitySum + data.stat.prestigeSum;
data.warGifts = ((clan.giftsCount / 2) * ((data.clanWarSum * 10) / 100)) / 20; data.warGifts = ((clan.giftsCount / 2) * ((data.clanWarSum * 10) / 100)) / 20;
data.scoreGifts = 0; data.scoreGifts = 0;
data.rewards = 0; data.rewards = 0;
return data; return data;
}); });
return guildMembers; return guildMembers;
}),
);
} }
} }
+1 -1
View File
@@ -1,3 +1,3 @@
export * from './hw-clan.service'; export * from './hw-clan.service';
export * from './hw-data.service';
export * from './hw-member.service'; export * from './hw-member.service';
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+277
View File
@@ -0,0 +1,277 @@
{
"stat": [
{
"id": "276065239",
"activity": [5772, 4182, 4060, 6246, 5354, 7454, 8662],
"dungeonActivity": [375, 375, 375, 375, 375, 375, 375],
"adventureStat": [0, 10, 0, 0, 0, 0, 0],
"clanWarStat": [2, 0, 2, 2, 0, 0, 0],
"prestigeStat": [4370, 4600, 4770, 4370, 4270, 4650, 4370],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276066337",
"activity": [108, 1998, 2396, 1938, 12976, 3122, 2884],
"dungeonActivity": [150, 454, 519, 504, 694, 874, 688],
"adventureStat": [0, 1, 3, 0, 1, 11, 1],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [0, 4840, 5000, 4670, 5000, 5080, 4460],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276067105",
"activity": [5712, 12358, 5314, 3972, 19208, 19986, 6186],
"dungeonActivity": [527, 75, 1052, 491, 405, 577, 301],
"adventureStat": [1, 1, 2, 2, 1, 5, 1],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [4310, 3900, 4410, 4410, 4330, 4330, 4340],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276071624",
"activity": [0, 0, 0, 0, 0, 0, 0],
"dungeonActivity": [0, 0, 0, 0, 0, 0, 0],
"adventureStat": [0, 0, 0, 0, 0, 0, 0],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [0, 0, 0, 0, 0, 0, 0],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276077824",
"activity": [6458, 23290, 36592, 4746, 4338, 7508, 2196],
"dungeonActivity": [1639, 1300, 1243, 774, 789, 1456, 893],
"adventureStat": [2, 2, 4, 0, 4, 11, 0],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [5000, 5000, 6590, 3720, 5000, 5020, 4670],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276088477",
"activity": [4840, 6912, 4670, 5240, 21956, 64, 4738],
"dungeonActivity": [435, 435, 435, 435, 510, 0, 435],
"adventureStat": [1, 2, 1, 1, 1, 0, 7],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [4360, 3860, 3730, 4130, 5810, 50, 3910],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276091174",
"activity": [5420, 2910, 1750, 1750, 1466, 1740, 1460],
"dungeonActivity": [234, 152, 159, 152, 159, 152, 159],
"adventureStat": [1, 2, 4, 1, 5, 1, 1],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [5790, 3550, 3540, 3570, 2690, 2640, 2670],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276091473",
"activity": [2880, 6436, 7460, 4246, 6546, 5510, 7466],
"dungeonActivity": [225, 225, 75, 225, 225, 225, 225],
"adventureStat": [1, 2, 1, 2, 3, 3, 10],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [3630, 4370, 4440, 4340, 4190, 4130, 3970],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276098983",
"activity": [3334, 3118, 1138, 1758, 16872, 20794, 1018],
"dungeonActivity": [3090, 1216, 1576, 3796, 2476, 4443, 4482],
"adventureStat": [1, 3, 1, 3, 1, 13, 1],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [5010, 4610, 4180, 4960, 4210, 4430, 4010],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276123996",
"activity": [850, 1890, 2070, 3450, 2250, 2470, 5530],
"dungeonActivity": [75, 369, 234, 322, 526, 166, 448],
"adventureStat": [1, 1, 1, 5, 0, 8, 1],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [2180, 3890, 2860, 4310, 3190, 3460, 3540],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276125516",
"activity": [4184, 16714, 2491, 998, 10314, 1094, 12612],
"dungeonActivity": [795, 167, 343, 75, 435, 75, 435],
"adventureStat": [1, 1, 0, 0, 0, 0, 31],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [4670, 4290, 4080, 3740, 4050, 3860, 4600],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276135490",
"activity": [11056, 850, 1150, 850, 2938, 226, 850],
"dungeonActivity": [330, 420, 90, 75, 165, 75, 75],
"adventureStat": [3, 1, 0, 0, 0, 0, 6],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [3310, 3160, 1090, 3270, 2960, 1600, 3150],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276140072",
"activity": [290, 1900, 1130, 3624, 5202, 454, 3016],
"dungeonActivity": [0, 165, 165, 165, 165, 75, 165],
"adventureStat": [0, 3, 1, 3, 0, 1, 8],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [110, 3770, 3720, 4320, 3830, 3190, 4150],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276173310",
"activity": [4730, 5502, 4194, 4024, 3798, 9262, 3942],
"dungeonActivity": [315, 735, 225, 225, 225, 225, 225],
"adventureStat": [3, 2, 1, 6, 2, 1, 5],
"clanWarStat": [2, 2, 2, 2, 0, 0, 0],
"prestigeStat": [4440, 4210, 3750, 4110, 3820, 3710, 3550],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276183485",
"activity": [1956, 4446, 13694, 3216, 7316, 4180, 2086],
"dungeonActivity": [4480, 1707, 1380, 1384, 1151, 0, 338],
"adventureStat": [2, 11, 2, 0, 0, 0, 6],
"clanWarStat": [0, 2, 2, 2, 2, 0, 0],
"prestigeStat": [5000, 5000, 5060, 5160, 3590, 100, 4370],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276201648",
"activity": [4458, 2858, 3488, 2288, 30380, 4518, 2088],
"dungeonActivity": [225, 225, 225, 225, 225, 225, 225],
"adventureStat": [1, 4, 1, 0, 10, 4, 1],
"clanWarStat": [0, 0, 2, 2, 0, 0, 0],
"prestigeStat": [5000, 4710, 4390, 4370, 4600, 4600, 4600],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276202599",
"activity": [0, 0, 0, 0, 0, 0, 0],
"dungeonActivity": [0, 0, 0, 0, 0, 0, 0],
"adventureStat": [0, 0, 0, 0, 0, 0, 0],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [0, 0, 0, 0, 0, 0, 0],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "276204664",
"activity": [6590, 3550, 2380, 1510, 57752, 24950, 2850],
"dungeonActivity": [609, 507, 209, 587, 630, 489, 225],
"adventureStat": [1, 3, 1, 3, 1, 13, 1],
"clanWarStat": [0, 2, 0, 2, 2, 0, 0],
"prestigeStat": [4520, 4600, 2300, 4140, 6900, 4140, 4040],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "277175311",
"activity": [180, 960, 880, 180, 2572, 190, 180],
"dungeonActivity": [75, 388, 388, 388, 388, 388, 388],
"adventureStat": [0, 1, 0, 0, 0, 0, 0],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [2280, 3900, 3700, 3540, 3720, 3540, 3540],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "283734465",
"activity": [3342, 4762, 4560, 2942, 13418, 15438, 10040],
"dungeonActivity": [375, 375, 375, 375, 374, 377, 375],
"adventureStat": [1, 1, 1, 1, 3, 3, 1],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [4180, 4500, 4500, 4260, 4340, 4340, 4340],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "283942135",
"activity": [2426, 2402, 3980, 3080, 24900, 16498, 4640],
"dungeonActivity": [411, 523, 555, 555, 555, 555, 536],
"adventureStat": [1, 4, 1, 1, 4, 3, 10],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [4060, 5740, 4260, 4600, 4450, 4880, 4480],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "284997421",
"activity": [0, 4718, 724, 1198, 1394, 2588, 0],
"dungeonActivity": [240, 150, 150, 75, 75, 300, 0],
"adventureStat": [0, 2, 4, 0, 2, 9, 1],
"clanWarStat": [2, 0, 2, 0, 0, 0, 0],
"prestigeStat": [1020, 5000, 4380, 3580, 3900, 7280, 380],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "284998229",
"activity": [880, 3980, 1900, 2298, 8258, 11062, 1602],
"dungeonActivity": [360, 495, 435, 510, 360, 510, 150],
"adventureStat": [2, 2, 4, 2, 1, 4, 9],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [2300, 4690, 4600, 6240, 2400, 6480, 2540],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "285266598",
"activity": [3680, 2030, 5370, 4500, 31484, 21878, 4366],
"dungeonActivity": [219, 249, 419, 354, 255, 240, 946],
"adventureStat": [1, 1, 0, 0, 21, 7, 0],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [2240, 2990, 3870, 4670, 4100, 3860, 3960],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "285405675",
"activity": [5050, 2394, 4350, 4316, 4356, 57802, 3830],
"dungeonActivity": [435, 435, 435, 435, 435, 435, 435],
"adventureStat": [1, 2, 2, 2, 3, 1, 4],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [4600, 5000, 5000, 5000, 5000, 5000, 5000],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "286288116",
"activity": [3800, 12890, 2750, 2610, 2156, 15670, 2700],
"dungeonActivity": [603, 569, 449, 564, 574, 454, 519],
"adventureStat": [2, 4, 3, 2, 1, 3, 4],
"clanWarStat": [0, 2, 2, 2, 2, 0, 0],
"prestigeStat": [4600, 4600, 4600, 4600, 4600, 4600, 5000],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "286289402",
"activity": [8036, 18022, 7664, 4806, 19456, 21418, 4970],
"dungeonActivity": [555, 390, 562, 490, 568, 591, 575],
"adventureStat": [1, 1, 1, 1, 1, 1, 6],
"clanWarStat": [0, 0, 0, 0, 0, 0, 0],
"prestigeStat": [4600, 4600, 4440, 4600, 4520, 4600, 4600],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "286549064",
"activity": [1394, 2146, 2080, 2080, 1412, 1830, 3610],
"dungeonActivity": [225, 75, 225, 435, 225, 225, 225],
"adventureStat": [2, 2, 5, 1, 2, 5, 3],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [4500, 4500, 4620, 4290, 4110, 4000, 4070],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "286570009",
"activity": [0, 1378, 5354, 534, 6360, 13142, 1732],
"dungeonActivity": [0, 1097, 962, 1421, 555, 1095, 1035],
"adventureStat": [0, 2, 3, 3, 4, 3, 1],
"clanWarStat": [0, 2, 2, 0, 2, 0, 0],
"prestigeStat": [0, 4250, 4420, 4270, 4090, 4600, 3840],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
},
{
"id": "287684146",
"activity": [6250, 11810, 3250, 2220, 16950, 21110, 3952],
"dungeonActivity": [675, 735, 675, 675, 675, 225, 675],
"adventureStat": [3, 3, 1, 1, 4, 1, 11],
"clanWarStat": [2, 2, 2, 2, 2, 0, 0],
"prestigeStat": [5050, 5450, 4860, 4720, 5800, 2970, 6970],
"clanGifts": [0, 0, 0, 0, 0, 0, 0]
}
],
"today": "202678",
"dayInWeek": "5",
"giftsCount": 338
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
{
"slots": {
"Mage Academy": [1, 2, 31],
"Lighthouse": [3, 4, 32],
"Barracks": [5, 6, 33],
"Foundry": [13, 14, 15, 36],
"Citadel": [25, 26, 27, 28, 29, 30, 40],
"Bridge": [7, 8, 9, 34],
"Bastion Of Ice": [22, 23, 24, 39],
"Bastion Of Fire": [19, 20, 21, 38],
"Gates Of Nature": [16, 17, 18, 37],
"Spring Of Elements": [10, 11, 12, 35]
}
}
File diff suppressed because it is too large Load Diff