-
-
Notifications
You must be signed in to change notification settings - Fork 47
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Showing
274 changed files
with
6,034 additions
and
1,717 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
{ | ||
"flutterSdkVersion": "3.7.12", | ||
"flutterSdkVersion": "3.13.1", | ||
"flavors": {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
22 changes: 22 additions & 0 deletions
22
lib/application/banner_history_count/banner_history_count_event.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
part of 'banner_history_count_bloc.dart'; | ||
|
||
@freezed | ||
class BannerHistoryCountEvent with _$BannerHistoryCountEvent { | ||
const factory BannerHistoryCountEvent.init() = _Init; | ||
|
||
const factory BannerHistoryCountEvent.typeChanged({ | ||
required BannerHistoryItemType type, | ||
}) = _TypeChanged; | ||
|
||
const factory BannerHistoryCountEvent.sortTypeChanged({ | ||
required BannerHistorySortType type, | ||
}) = _SortTypeChanged; | ||
|
||
const factory BannerHistoryCountEvent.versionSelected({ | ||
required double version, | ||
}) = _VersionSelected; | ||
|
||
const factory BannerHistoryCountEvent.itemsSelected({ | ||
required List<String> keys, | ||
}) = _CharactersSelected; | ||
} |
6 changes: 3 additions & 3 deletions
6
.../banner_history/banner_history_state.dart → ...ory_count/banner_history_count_state.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 0 additions & 8 deletions
8
lib/application/banner_history_item/banner_history_item_event.dart
This file was deleted.
Oops, something went wrong.
11 changes: 0 additions & 11 deletions
11
lib/application/banner_history_item/banner_history_item_state.dart
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
lib/application/banner_version_history/banner_version_history_event.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
part of 'banner_version_history_bloc.dart'; | ||
|
||
@freezed | ||
class BannerVersionHistoryEvent with _$BannerVersionHistoryEvent { | ||
const factory BannerVersionHistoryEvent.init({ | ||
required double version, | ||
}) = _Init; | ||
} |
11 changes: 11 additions & 0 deletions
11
lib/application/banner_version_history/banner_version_history_state.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
part of 'banner_version_history_bloc.dart'; | ||
|
||
@freezed | ||
class BannerVersionHistoryState with _$BannerVersionHistoryState { | ||
const factory BannerVersionHistoryState.loading() = _LoadingState; | ||
|
||
const factory BannerVersionHistoryState.loadedState({ | ||
required double version, | ||
required List<BannerHistoryGroupedPeriodModel> items, | ||
}) = _LoadedState; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
160 changes: 160 additions & 0 deletions
160
lib/application/wish_banner_history/wish_banner_history_bloc.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import 'package:bloc/bloc.dart'; | ||
import 'package:collection/collection.dart'; | ||
import 'package:darq/darq.dart'; | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
import 'package:shiori/domain/models/models.dart'; | ||
import 'package:shiori/domain/services/genshin_service.dart'; | ||
|
||
part 'wish_banner_history_bloc.freezed.dart'; | ||
part 'wish_banner_history_event.dart'; | ||
part 'wish_banner_history_state.dart'; | ||
|
||
class WishBannerHistoryBloc extends Bloc<WishBannerHistoryEvent, WishBannerHistoryState> { | ||
final GenshinService _genshinService; | ||
|
||
WishBannerHistoryBloc(this._genshinService) : super(const WishBannerHistoryState.loading()); | ||
|
||
@override | ||
Stream<WishBannerHistoryState> mapEventToState(WishBannerHistoryEvent event) async* { | ||
final s = event.map( | ||
init: (_) => _init(SortDirectionType.desc), | ||
groupTypeChanged: (e) => state.maybeMap( | ||
loaded: (state) => _groupTypeChanged(state, e.type), | ||
orElse: () => throw Exception('Invalid state'), | ||
), | ||
sortDirectionTypeChanged: (e) => state.maybeMap( | ||
loaded: (state) => _sortDirectionTypeChanged(state, e.type), | ||
orElse: () => throw Exception('Invalid state'), | ||
), | ||
itemsSelected: (e) => state.maybeMap( | ||
loaded: (state) => _itemsSelected(state, e.keys), | ||
orElse: () => throw Exception('Invalid state'), | ||
), | ||
); | ||
|
||
yield s; | ||
} | ||
|
||
List<ItemCommonWithNameOnly> getItemsForSearch() { | ||
return state.map( | ||
loading: (_) => throw Exception('Invalid state'), | ||
loaded: (state) => state.filteredPeriods.map((e) => ItemCommonWithNameOnly(e.groupingKey, e.groupingTitle)).toList(), | ||
); | ||
} | ||
|
||
WishBannerHistoryState _init(SortDirectionType sortDirectionType) { | ||
final grouped = _genshinService.bannerHistory.getWishBannersHistoryGroupedByVersion()..sort((x, y) => _sort(x, y, sortDirectionType)); | ||
return WishBannerHistoryState.loaded( | ||
allPeriods: grouped, | ||
filteredPeriods: grouped, | ||
sortDirectionType: sortDirectionType, | ||
groupedType: WishBannerGroupedType.version, | ||
); | ||
} | ||
|
||
WishBannerHistoryState _groupTypeChanged(_LoadedState state, WishBannerGroupedType type) { | ||
if (state.groupedType == type) { | ||
return state; | ||
} | ||
switch (type) { | ||
case WishBannerGroupedType.character: | ||
return _groupByCharacterOrWeapon(state, type); | ||
case WishBannerGroupedType.weapon: | ||
return _groupByCharacterOrWeapon(state, type); | ||
default: | ||
final periods = [...state.allPeriods]..sort((x, y) => _sort(x, y, state.sortDirectionType)); | ||
return state.copyWith.call(filteredPeriods: periods, groupedType: type, selectedItemKeys: []); | ||
} | ||
} | ||
|
||
WishBannerHistoryState _groupByCharacterOrWeapon(_LoadedState state, WishBannerGroupedType groupedType) { | ||
const sortType = SortDirectionType.asc; | ||
final groups = _getGroupedByCharacterOrWeaponPeriod(state.allPeriods, groupedType)..sort((x, y) => _sort(x, y, sortType)); | ||
return state.copyWith(filteredPeriods: groups, groupedType: groupedType, selectedItemKeys: [], sortDirectionType: sortType); | ||
} | ||
|
||
List<WishBannerHistoryGroupedPeriodModel> _getGroupedByCharacterOrWeaponPeriod( | ||
List<WishBannerHistoryGroupedPeriodModel> allPeriods, | ||
WishBannerGroupedType groupedType, | ||
) { | ||
assert(groupedType == WishBannerGroupedType.character || groupedType == WishBannerGroupedType.weapon); | ||
|
||
final groupByCharacter = groupedType == WishBannerGroupedType.character; | ||
final groupsMap = <String, List<WishBannerHistoryPartItemModel>>{}; | ||
final allParts = allPeriods.selectMany((e, _) => e.parts).toList(); | ||
for (final part in allParts) { | ||
final items = groupByCharacter ? part.featuredCharacters : part.featuredWeapons; | ||
for (final item in items) { | ||
groupsMap.putIfAbsent(item.key, () => []); | ||
groupsMap[item.key]!.add(part); | ||
} | ||
} | ||
|
||
return groupsMap.entries.map((e) { | ||
final parts = e.value..sort((x, y) => x.version.compareTo(y.version)); | ||
final firstPart = parts.first; | ||
final groupingTitle = (groupByCharacter ? firstPart.featuredCharacters : firstPart.featuredWeapons).firstWhere((c) => c.key == e.key).name; | ||
return WishBannerHistoryGroupedPeriodModel(groupingKey: e.key, groupingTitle: groupingTitle, parts: parts); | ||
}).toList(); | ||
} | ||
|
||
WishBannerHistoryState _sortDirectionTypeChanged(_LoadedState state, SortDirectionType type) { | ||
if (state.sortDirectionType == type) { | ||
return state; | ||
} | ||
|
||
final periods = [...state.filteredPeriods]..sort((x, y) => _sort(x, y, type)); | ||
return state.copyWith(filteredPeriods: periods, sortDirectionType: type); | ||
} | ||
|
||
WishBannerHistoryState _itemsSelected(_LoadedState state, List<String> keys) { | ||
if (keys.equals(state.selectedItemKeys)) { | ||
return state; | ||
} | ||
|
||
final filteredPeriods = <WishBannerHistoryGroupedPeriodModel>[]; | ||
if (keys.isNotEmpty) { | ||
switch (state.groupedType) { | ||
case WishBannerGroupedType.version: | ||
filteredPeriods.addAll(state.allPeriods.where((el) => keys.contains(el.groupingKey))); | ||
case WishBannerGroupedType.character: | ||
case WishBannerGroupedType.weapon: | ||
final groupByCharacter = state.groupedType == WishBannerGroupedType.character; | ||
final periods = _getGroupedByCharacterOrWeaponPeriod(state.allPeriods, state.groupedType); | ||
for (final period in periods) { | ||
final firstPart = period.parts.first; | ||
final promotedItem = (groupByCharacter ? firstPart.featuredCharacters : firstPart.featuredWeapons).firstWhere( | ||
(el) => el.key == period.groupingKey, | ||
); | ||
if (keys.contains(promotedItem.key)) { | ||
filteredPeriods.add(period); | ||
} | ||
} | ||
} | ||
} else { | ||
switch (state.groupedType) { | ||
case WishBannerGroupedType.version: | ||
filteredPeriods.addAll(state.allPeriods); | ||
case WishBannerGroupedType.character: | ||
case WishBannerGroupedType.weapon: | ||
final periods = _getGroupedByCharacterOrWeaponPeriod(state.allPeriods, state.groupedType); | ||
filteredPeriods.addAll(periods); | ||
} | ||
} | ||
|
||
return state.copyWith.call( | ||
filteredPeriods: filteredPeriods..sort((x, y) => _sort(x, y, state.sortDirectionType)), | ||
selectedItemKeys: keys, | ||
); | ||
} | ||
|
||
int _sort(WishBannerHistoryGroupedPeriodModel x, WishBannerHistoryGroupedPeriodModel y, SortDirectionType type) { | ||
switch (type) { | ||
case SortDirectionType.asc: | ||
return compareNatural(x.groupingTitle, y.groupingTitle); | ||
case SortDirectionType.desc: | ||
return compareNatural(y.groupingTitle, x.groupingTitle); | ||
} | ||
} | ||
} |
18 changes: 18 additions & 0 deletions
18
lib/application/wish_banner_history/wish_banner_history_event.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
part of 'wish_banner_history_bloc.dart'; | ||
|
||
@freezed | ||
class WishBannerHistoryEvent with _$WishBannerHistoryEvent { | ||
const factory WishBannerHistoryEvent.init() = _Init; | ||
|
||
const factory WishBannerHistoryEvent.groupTypeChanged( | ||
WishBannerGroupedType type, | ||
) = _GroupTypeChanged; | ||
|
||
const factory WishBannerHistoryEvent.sortDirectionTypeChanged( | ||
SortDirectionType type, | ||
) = _SortDirectionTypeChanged; | ||
|
||
const factory WishBannerHistoryEvent.itemsSelected({ | ||
required List<String> keys, | ||
}) = _ItemsSelected; | ||
} |
14 changes: 14 additions & 0 deletions
14
lib/application/wish_banner_history/wish_banner_history_state.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
part of 'wish_banner_history_bloc.dart'; | ||
|
||
@freezed | ||
class WishBannerHistoryState with _$WishBannerHistoryState { | ||
const factory WishBannerHistoryState.loading() = _LoadingState; | ||
|
||
const factory WishBannerHistoryState.loaded({ | ||
required List<WishBannerHistoryGroupedPeriodModel> allPeriods, | ||
required List<WishBannerHistoryGroupedPeriodModel> filteredPeriods, | ||
required SortDirectionType sortDirectionType, | ||
required WishBannerGroupedType groupedType, | ||
@Default(<String>[]) List<String> selectedItemKeys, | ||
}) = _LoadedState; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import 'package:bloc/bloc.dart'; | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
import 'package:shiori/domain/models/models.dart'; | ||
import 'package:shiori/domain/services/genshin_service.dart'; | ||
import 'package:shiori/domain/services/resources_service.dart'; | ||
import 'package:shiori/domain/services/telemetry_service.dart'; | ||
|
||
part 'wish_simulator_bloc.freezed.dart'; | ||
part 'wish_simulator_event.dart'; | ||
part 'wish_simulator_state.dart'; | ||
|
||
class WishSimulatorBloc extends Bloc<WishSimulatorEvent, WishSimulatorState> { | ||
final GenshinService _genshinServiceImpl; | ||
final ResourceService _resourceService; | ||
final TelemetryService _telemetryService; | ||
|
||
_LoadedState get currentState => state as _LoadedState; | ||
|
||
WishSimulatorBloc(this._genshinServiceImpl, this._resourceService, this._telemetryService) : super(const WishSimulatorState.loading()); | ||
|
||
@override | ||
Stream<WishSimulatorState> mapEventToState(WishSimulatorEvent event) async* { | ||
final s = await event.map( | ||
init: (e) => _init(), | ||
periodChanged: (e) => _periodChanged(e.version, e.from, e.until), | ||
bannerSelected: (e) async => _bannerChanged(e.index), | ||
); | ||
|
||
yield s; | ||
} | ||
|
||
void _checkLoadedState() { | ||
if (state is! _LoadedState) { | ||
throw Exception('Invalid state'); | ||
} | ||
} | ||
|
||
Future<WishSimulatorState> _init() async { | ||
final version = _genshinServiceImpl.bannerHistory.getBannerHistoryVersions(SortDirectionType.asc).last; | ||
final banner = _genshinServiceImpl.bannerHistory.getBanners(version).last; | ||
final period = _genshinServiceImpl.bannerHistory.getWishSimulatorBannerPerPeriod(version, banner.from, banner.until); | ||
await _telemetryService.trackWishSimulatorOpened(version); | ||
return WishSimulatorState.loaded( | ||
selectedBannerIndex: 0, | ||
wishIconImage: _getWishIconImage(period.banners.first.type), | ||
period: period, | ||
); | ||
} | ||
|
||
Future<WishSimulatorState> _periodChanged(double version, DateTime from, DateTime until) async { | ||
_checkLoadedState(); | ||
await _telemetryService.trackWishSimulatorOpened(version); | ||
final period = _genshinServiceImpl.bannerHistory.getWishSimulatorBannerPerPeriod(version, from, until); | ||
return WishSimulatorState.loaded( | ||
selectedBannerIndex: 0, | ||
wishIconImage: _getWishIconImage(period.banners.first.type), | ||
period: period, | ||
); | ||
} | ||
|
||
WishSimulatorState _bannerChanged(int index) { | ||
_checkLoadedState(); | ||
|
||
if (index < 0 || index > currentState.period.banners.length - 1) { | ||
throw Exception('The provided index = $index is not valid'); | ||
} | ||
|
||
if (index == currentState.selectedBannerIndex) { | ||
return currentState; | ||
} | ||
|
||
return currentState.copyWith(selectedBannerIndex: index, wishIconImage: _getWishIconImage(currentState.period.banners[index].type)); | ||
} | ||
|
||
String _getWishIconImage(BannerItemType type) { | ||
switch (type) { | ||
case BannerItemType.character: | ||
case BannerItemType.weapon: | ||
final material = _genshinServiceImpl.materials.getIntertwinedFate(); | ||
return _resourceService.getMaterialImagePath(material.image, material.type); | ||
case BannerItemType.standard: | ||
final material = _genshinServiceImpl.materials.getAcquaintFate(); | ||
return _resourceService.getMaterialImagePath(material.image, material.type); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
part of 'wish_simulator_bloc.dart'; | ||
|
||
@freezed | ||
class WishSimulatorEvent with _$WishSimulatorEvent { | ||
const factory WishSimulatorEvent.init() = _Init; | ||
|
||
const factory WishSimulatorEvent.periodChanged({ | ||
required double version, | ||
required DateTime from, | ||
required DateTime until, | ||
}) = _PeriodChanged; | ||
|
||
const factory WishSimulatorEvent.bannerSelected({required int index}) = _BannerSelected; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
part of 'wish_simulator_bloc.dart'; | ||
|
||
@freezed | ||
class WishSimulatorState with _$WishSimulatorState { | ||
const factory WishSimulatorState.loading() = _LoadingState; | ||
|
||
const factory WishSimulatorState.loaded({ | ||
required String wishIconImage, | ||
required int selectedBannerIndex, | ||
required WishSimulatorBannerItemsPerPeriodModel period, | ||
}) = _LoadedState; | ||
} |
116 changes: 116 additions & 0 deletions
116
lib/application/wish_simulator_pull_history/wish_simulator_pull_history_bloc.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import 'package:flutter_bloc/flutter_bloc.dart'; | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:intl/intl.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
import 'package:shiori/domain/models/models.dart'; | ||
import 'package:shiori/domain/services/data_service.dart'; | ||
import 'package:shiori/domain/services/genshin_service.dart'; | ||
|
||
part 'wish_simulator_pull_history_bloc.freezed.dart'; | ||
part 'wish_simulator_pull_history_event.dart'; | ||
part 'wish_simulator_pull_history_state.dart'; | ||
|
||
class WishSimulatorPullHistoryBloc extends Bloc<WishSimulatorPullHistoryEvent, WishSimulatorPullHistoryState> { | ||
final GenshinService _genshinService; | ||
final DataService _dataService; | ||
final DateFormat _formatter = DateFormat('yyyy-MM-dd HH:mm:ss'); | ||
final List<CharacterCardModel> _allCharacters = []; | ||
final List<WeaponCardModel> _allWeapons = []; | ||
|
||
static const int take = 5; | ||
|
||
WishSimulatorPullHistoryBloc(this._genshinService, this._dataService) : super(const WishSimulatorPullHistoryState.loading()); | ||
|
||
@override | ||
Stream<WishSimulatorPullHistoryState> mapEventToState(WishSimulatorPullHistoryEvent event) async* { | ||
if (_allCharacters.isEmpty) { | ||
final allCharacters = _genshinService.characters.getCharactersForCard(); | ||
_allCharacters.addAll(allCharacters); | ||
} | ||
|
||
if (_allWeapons.isEmpty) { | ||
final allWeapons = _genshinService.weapons.getWeaponsForCard(); | ||
_allWeapons.addAll(allWeapons); | ||
} | ||
|
||
final s = await event.map( | ||
init: (e) async => state.map( | ||
loading: (_) => _init(e.bannerType), | ||
loaded: (state) { | ||
if (state.bannerType == e.bannerType) { | ||
return state; | ||
} | ||
|
||
return _init(e.bannerType); | ||
}, | ||
), | ||
pageChanged: (e) async => state.map( | ||
loading: (_) => throw Exception('Invalid state'), | ||
loaded: (state) => _pageChanged(state, e.page), | ||
), | ||
deleteData: (e) => state.map( | ||
loading: (_) => throw Exception('Invalid state'), | ||
loaded: (state) => _deleteData(e.bannerType), | ||
), | ||
); | ||
|
||
yield s; | ||
} | ||
|
||
WishSimulatorPullHistoryState _init(BannerItemType bannerType) { | ||
final pullHistory = _dataService.wishSimulator.getBannerItemsPullHistoryPerType(bannerType).map((e) { | ||
final type = ItemType.values[e.itemType]; | ||
String name; | ||
int rarity; | ||
switch (type) { | ||
case ItemType.character: | ||
final character = _allCharacters.firstWhere((el) => el.key == e.itemKey); | ||
name = character.name; | ||
rarity = character.stars; | ||
case ItemType.weapon: | ||
final weapon = _allWeapons.firstWhere((el) => el.key == e.itemKey); | ||
name = weapon.name; | ||
rarity = weapon.rarity; | ||
default: | ||
throw Exception('Item type = $type is not valid here'); | ||
} | ||
|
||
return WishSimulatorBannerItemPullHistoryModel( | ||
key: e.itemKey, | ||
name: name, | ||
rarity: rarity, | ||
type: type, | ||
pulledOn: _formatter.format(e.pulledOnDate), | ||
); | ||
}).toList(); | ||
|
||
return WishSimulatorPullHistoryState.loaded( | ||
bannerType: bannerType, | ||
allItems: pullHistory, | ||
items: pullHistory.take(take).toList(), | ||
currentPage: 1, | ||
maxPage: pullHistory.isEmpty ? 1 : (pullHistory.length / take).ceil(), | ||
); | ||
} | ||
|
||
WishSimulatorPullHistoryState _pageChanged(_LoadedState state, int newPage) { | ||
final selectedPage = newPage - 1; | ||
if (selectedPage < 0 || selectedPage > state.maxPage) { | ||
throw Exception('Page = $newPage is not valid'); | ||
} | ||
|
||
if (state.currentPage == newPage) { | ||
return state; | ||
} | ||
|
||
return state.copyWith( | ||
currentPage: newPage, | ||
items: state.allItems.skip(take * selectedPage).take(take).toList(), | ||
); | ||
} | ||
|
||
Future<WishSimulatorPullHistoryState> _deleteData(BannerItemType bannerType) async { | ||
await _dataService.wishSimulator.clearBannerItemPullHistory(bannerType); | ||
return _init(bannerType); | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
lib/application/wish_simulator_pull_history/wish_simulator_pull_history_event.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
part of 'wish_simulator_pull_history_bloc.dart'; | ||
|
||
@freezed | ||
class WishSimulatorPullHistoryEvent with _$WishSimulatorPullHistoryEvent { | ||
const factory WishSimulatorPullHistoryEvent.init({required BannerItemType bannerType}) = _Init; | ||
|
||
const factory WishSimulatorPullHistoryEvent.pageChanged({required int page}) = _PageChanged; | ||
|
||
const factory WishSimulatorPullHistoryEvent.deleteData({required BannerItemType bannerType}) = _DeleteData; | ||
} |
14 changes: 14 additions & 0 deletions
14
lib/application/wish_simulator_pull_history/wish_simulator_pull_history_state.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
part of 'wish_simulator_pull_history_bloc.dart'; | ||
|
||
@freezed | ||
class WishSimulatorPullHistoryState with _$WishSimulatorPullHistoryState { | ||
const factory WishSimulatorPullHistoryState.loading() = _LoadingState; | ||
|
||
const factory WishSimulatorPullHistoryState.loaded({ | ||
required BannerItemType bannerType, | ||
required List<WishSimulatorBannerItemPullHistoryModel> allItems, | ||
required List<WishSimulatorBannerItemPullHistoryModel> items, | ||
required int currentPage, | ||
required int maxPage, | ||
}) = _LoadedState; | ||
} |
241 changes: 241 additions & 0 deletions
241
lib/application/wish_simulator_result/wish_simulator_result_bloc.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
import 'dart:math'; | ||
|
||
import 'package:bloc/bloc.dart'; | ||
import 'package:collection/collection.dart'; | ||
import 'package:darq/darq.dart'; | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
import 'package:shiori/domain/extensions/double_extensions.dart'; | ||
import 'package:shiori/domain/models/entities.dart'; | ||
import 'package:shiori/domain/models/models.dart'; | ||
import 'package:shiori/domain/services/data_service.dart'; | ||
import 'package:shiori/domain/services/telemetry_service.dart'; | ||
import 'package:shiori/domain/wish_banner_constants.dart'; | ||
|
||
part 'wish_simulator_result_bloc.freezed.dart'; | ||
part 'wish_simulator_result_event.dart'; | ||
part 'wish_simulator_result_state.dart'; | ||
|
||
class WishSimulatorResultBloc extends Bloc<WishSimulatorResultEvent, WishSimulatorResultState> { | ||
final DataService _dataService; | ||
final TelemetryService _telemetryService; | ||
final Random _random; | ||
|
||
WishSimulatorResultBloc(this._dataService, this._telemetryService) | ||
: _random = Random(), | ||
super(const WishSimulatorResultState.loading()); | ||
|
||
@override | ||
Stream<WishSimulatorResultState> mapEventToState(WishSimulatorResultEvent event) async* { | ||
final s = await event.map( | ||
init: (e) => _pull(e.pulls, e.bannerIndex, e.period), | ||
); | ||
|
||
yield s; | ||
} | ||
|
||
Future<WishSimulatorResultState> _pull(int pulls, int bannerIndex, WishSimulatorBannerItemsPerPeriodModel period) async { | ||
if (pulls <= 0) { | ||
throw Exception('The provided pulls = $pulls is not valid'); | ||
} | ||
|
||
if (bannerIndex < 0 || period.banners.elementAtOrNull(bannerIndex) == null) { | ||
throw Exception('The provided bannerIndex = $bannerIndex is not valid'); | ||
} | ||
|
||
final banner = period.banners[bannerIndex]; | ||
final bannerRates = _RatesPerBannerType(banner.type); | ||
final history = await _dataService.wishSimulator.getBannerPullHistory(banner.type, defaultXStarCount: bannerRates.getDefaultXStarCount); | ||
final results = <WishSimulatorBannerItemResultModel>[]; | ||
for (int i = 1; i <= pulls; i++) { | ||
final int randomRarity = bannerRates.getRarityIfGuaranteed(history) ?? _getRandomItemRarity(history.currentXStarCount, bannerRates); | ||
history.initXStarCountIfNeeded(randomRarity); | ||
|
||
final isRarityInFeatured = banner.featuredItems.isEmpty || banner.featuredItems.any((el) => el.rarity == randomRarity); | ||
final winsFiftyFifty = isRarityInFeatured && history.shouldWinFiftyFifty(randomRarity); | ||
final pool = [ | ||
...banner.characters.map( | ||
(e) => WishSimulatorBannerItemResultModel.character(key: e.key, image: e.image, rarity: e.rarity, elementType: e.elementType), | ||
), | ||
...banner.weapons.map( | ||
(e) => WishSimulatorBannerItemResultModel.weapon(key: e.key, image: e.image, rarity: e.rarity, weaponType: e.weaponType), | ||
), | ||
].where((el) { | ||
if (el.rarity != randomRarity) { | ||
return false; | ||
} | ||
|
||
if (!isRarityInFeatured) { | ||
return true; | ||
} | ||
|
||
return banner.featuredItems.isEmpty || banner.featuredItems.any((p) => winsFiftyFifty ? p.key == el.key : p.key != el.key); | ||
}).toList(); | ||
|
||
assert(pool.isNotEmpty); | ||
pool.shuffle(_random); | ||
final pickedItem = pool[_random.nextInt(pool.length)]; | ||
results.add(pickedItem); | ||
|
||
final bool? gotFeaturedItem = !bannerRates.canBeGuaranteed(randomRarity) | ||
? true | ||
: !isRarityInFeatured | ||
? null | ||
: winsFiftyFifty; | ||
await history.pull(randomRarity, gotFeaturedItem); | ||
|
||
final itemType = pickedItem.map(character: (_) => ItemType.character, weapon: (_) => ItemType.weapon); | ||
await _dataService.wishSimulator.saveBannerItemPullHistory(banner.type, pickedItem.key, itemType); | ||
} | ||
final fromUntilString = '${WishBannerConstants.dateFormat.format(period.from)}/${WishBannerConstants.dateFormat.format(period.until)}'; | ||
await _telemetryService.trackWishSimulatorResult(bannerIndex, period.version, banner.type, fromUntilString); | ||
|
||
final sortedResults = results.orderByDescending((el) => el.rarity).thenBy((el) { | ||
final typeName = el.map(character: (_) => ItemType.character.name, weapon: (_) => ItemType.weapon.name); | ||
return '$typeName-${el.key}'; | ||
}).toList(); | ||
return WishSimulatorResultState.loaded(results: sortedResults); | ||
} | ||
|
||
int _getRandomItemRarity(Map<int, int> currentXStarCount, _RatesPerBannerType bannerRates) { | ||
assert(bannerRates._rates.isNotEmpty); | ||
|
||
final probs = <double>[]; | ||
final randomRarities = <int>[]; | ||
for (final rate in bannerRates._rates) { | ||
final int pullCount = currentXStarCount[rate.rarity] ?? 0; | ||
final considerPullCount = rate.canBeGuaranteed && pullCount > rate.softRateIncreasesAt; | ||
final double prob = rate.getProb(pullCount, considerPullCount); | ||
|
||
final remainingProb = (100 - probs.sum).round(); | ||
if (remainingProb <= 0) { | ||
continue; | ||
} | ||
probs.add(prob); | ||
randomRarities.addAll(List.filled(prob.round(), rate.rarity)); | ||
} | ||
|
||
randomRarities.shuffle(); | ||
assert(randomRarities.isNotEmpty); | ||
|
||
return randomRarities[_random.nextInt(randomRarities.length)]; | ||
} | ||
} | ||
|
||
class _BannerRate { | ||
final int rarity; | ||
final int guaranteedAt; | ||
final double initialRate; | ||
final int softRateIncreasesAt; | ||
final int hardRateIncreasesAt; | ||
|
||
bool get canBeGuaranteed => !(guaranteedAt == -1); | ||
|
||
const _BannerRate( | ||
this.rarity, | ||
this.guaranteedAt, | ||
this.initialRate, | ||
this.softRateIncreasesAt, | ||
this.hardRateIncreasesAt, | ||
) : assert(rarity >= WishBannerConstants.minObtainableRarity), | ||
assert(guaranteedAt > 0 && guaranteedAt > hardRateIncreasesAt), | ||
assert(initialRate > 0 && initialRate < 100), | ||
assert(softRateIncreasesAt > 0 && softRateIncreasesAt < hardRateIncreasesAt); | ||
|
||
const _BannerRate.simple(this.rarity, this.initialRate) | ||
: guaranteedAt = -1, | ||
softRateIncreasesAt = -1, | ||
hardRateIncreasesAt = -1; | ||
|
||
double _getSoftRateMultiplier() { | ||
if (rarity == WishBannerConstants.maxObtainableRarity) { | ||
return 0.5; | ||
} | ||
|
||
return 1.5; | ||
} | ||
|
||
double _getHardRateMultiplier() { | ||
if (rarity == WishBannerConstants.maxObtainableRarity) { | ||
return 2; | ||
} | ||
return 3; | ||
} | ||
|
||
double _getRate(int pullCount) { | ||
double rate = initialRate / 100; | ||
if (!canBeGuaranteed) { | ||
return rate; | ||
} | ||
if (pullCount >= softRateIncreasesAt && pullCount < hardRateIncreasesAt) { | ||
rate *= _getSoftRateMultiplier(); | ||
} else if (pullCount >= hardRateIncreasesAt) { | ||
rate *= _getHardRateMultiplier(); | ||
} | ||
|
||
return rate; | ||
} | ||
|
||
double getProb(int pullCount, bool considerPullCount) { | ||
final double rate = _getRate(pullCount); | ||
double x = -1 * rate; | ||
if (considerPullCount) { | ||
x *= pullCount; | ||
} | ||
|
||
final double y = ((1 - exp(x)) * 100).truncateToDecimalPlaces(fractionalDigits: 4); | ||
if (y > 100) { | ||
return 100; | ||
} | ||
return y; | ||
} | ||
} | ||
|
||
class _RatesPerBannerType { | ||
final BannerItemType type; | ||
final List<_BannerRate> _rates = []; | ||
final Map<int, int> _defaultXStarCount = {}; | ||
|
||
Map<int, int> get getDefaultXStarCount => Map.of(_defaultXStarCount); | ||
|
||
_RatesPerBannerType(this.type) { | ||
switch (type) { | ||
case BannerItemType.character: | ||
case BannerItemType.standard: | ||
_rates.add(const _BannerRate(5, 90, 0.6, 40, 74)); | ||
_rates.add(const _BannerRate(4, 10, 5.1, 4, 7)); | ||
_rates.add(const _BannerRate.simple(3, 94.3)); | ||
case BannerItemType.weapon: | ||
_rates.add(const _BannerRate(5, 80, 0.7, 30, 64)); | ||
_rates.add(const _BannerRate(4, 10, 6.0, 4, 7)); | ||
_rates.add(const _BannerRate.simple(3, 93.3)); | ||
} | ||
_defaultXStarCount.addAll({for (final v in _rates) v.rarity: 0}); | ||
} | ||
|
||
bool canBeGuaranteed(int rarity) { | ||
final rate = _rates.firstWhereOrNull((el) => el.rarity == rarity); | ||
if (rate == null) { | ||
throw Exception('Rarity = $rarity does not have an associated rate'); | ||
} | ||
|
||
return rate.canBeGuaranteed; | ||
} | ||
|
||
int? getRarityIfGuaranteed(WishSimulatorBannerPullHistory history) { | ||
if (history.type != type.index) { | ||
throw Exception('The rates only apply to banners of type = $type'); | ||
} | ||
for (final rate in _rates) { | ||
if (!rate.canBeGuaranteed) { | ||
continue; | ||
} | ||
|
||
if (history.isItemGuaranteed(rate.rarity, rate.guaranteedAt)) { | ||
return rate.rarity; | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
lib/application/wish_simulator_result/wish_simulator_result_event.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
part of 'wish_simulator_result_bloc.dart'; | ||
|
||
@freezed | ||
class WishSimulatorResultEvent with _$WishSimulatorResultEvent { | ||
const factory WishSimulatorResultEvent.init({ | ||
required int bannerIndex, | ||
required int pulls, | ||
required WishSimulatorBannerItemsPerPeriodModel period, | ||
}) = _Init; | ||
} |
10 changes: 10 additions & 0 deletions
10
lib/application/wish_simulator_result/wish_simulator_result_state.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
part of 'wish_simulator_result_bloc.dart'; | ||
|
||
@freezed | ||
class WishSimulatorResultState with _$WishSimulatorResultState { | ||
const factory WishSimulatorResultState.loading() = _LoadingState; | ||
|
||
const factory WishSimulatorResultState.loaded({ | ||
required List<WishSimulatorBannerItemResultModel> results, | ||
}) = _LoadedState; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,4 +6,5 @@ enum AppBackupDataType { | |
customBuilds, | ||
gameCodes, | ||
notifications, | ||
wishSimulator, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,4 +8,6 @@ enum AppImageFolderType { | |
monsters, | ||
skills, | ||
weapons, | ||
wishBannerHistory, | ||
charactersIcon, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
enum BannerItemType { | ||
character, | ||
weapon, | ||
standard, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
enum WishBannerGroupedType { | ||
version, | ||
character, | ||
weapon, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
|
||
part 'backup_wish_simulator_model.freezed.dart'; | ||
part 'backup_wish_simulator_model.g.dart'; | ||
|
||
@freezed | ||
class BackupWishSimulatorModel with _$BackupWishSimulatorModel { | ||
const factory BackupWishSimulatorModel({ | ||
required List<BackupWishSimulatorBannerPullHistory> pullHistory, | ||
required List<BackupWishSimulatorBannerItemPullHistory> itemPullHistory, | ||
}) = _BackupWishSimulatorModel; | ||
|
||
factory BackupWishSimulatorModel.fromJson(Map<String, dynamic> json) => _$BackupWishSimulatorModelFromJson(json); | ||
} | ||
|
||
@freezed | ||
class BackupWishSimulatorBannerPullHistory with _$BackupWishSimulatorBannerPullHistory { | ||
const factory BackupWishSimulatorBannerPullHistory({ | ||
required BannerItemType type, | ||
required Map<int, int> currentXStarCount, | ||
required Map<int, bool> fiftyFiftyXStarGuaranteed, | ||
}) = _BackupWishSimulatorBannerPullHistory; | ||
|
||
factory BackupWishSimulatorBannerPullHistory.fromJson(Map<String, dynamic> json) => | ||
_$BackupWishSimulatorBannerPullHistoryFromJson(json); | ||
} | ||
|
||
@freezed | ||
class BackupWishSimulatorBannerItemPullHistory with _$BackupWishSimulatorBannerItemPullHistory { | ||
const factory BackupWishSimulatorBannerItemPullHistory({ | ||
required BannerItemType bannerType, | ||
required String itemKey, | ||
required ItemType itemType, | ||
required DateTime pulledOn, | ||
}) = _BackupWishSimulatorBannerItemPullHistory; | ||
|
||
factory BackupWishSimulatorBannerItemPullHistory.fromJson(Map<String, dynamic> json) => | ||
_$BackupWishSimulatorBannerItemPullHistoryFromJson(json); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
lib/domain/models/entities/wish_simulator/wish_simulator_banner_item_pull_history.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import 'package:hive/hive.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
|
||
part 'wish_simulator_banner_item_pull_history.g.dart'; | ||
|
||
@HiveType(typeId: 24) | ||
class WishSimulatorBannerItemPullHistory extends HiveObject { | ||
@HiveField(0) | ||
final int bannerType; | ||
|
||
@HiveField(1) | ||
final int itemType; | ||
|
||
@HiveField(2) | ||
final String itemKey; | ||
|
||
@HiveField(4) | ||
DateTime pulledOnDate; | ||
|
||
WishSimulatorBannerItemPullHistory(this.bannerType, this.itemType, this.itemKey, this.pulledOnDate); | ||
|
||
WishSimulatorBannerItemPullHistory.newOne(BannerItemType bannerType, ItemType itemType, this.itemKey) | ||
: bannerType = bannerType.index, | ||
itemType = itemType.index, | ||
pulledOnDate = DateTime.now().toUtc(); | ||
|
||
factory WishSimulatorBannerItemPullHistory.character(BannerItemType bannerType, String itemKey) => | ||
WishSimulatorBannerItemPullHistory.newOne(bannerType, ItemType.character, itemKey); | ||
|
||
factory WishSimulatorBannerItemPullHistory.weapon(BannerItemType bannerType, String itemKey) => | ||
WishSimulatorBannerItemPullHistory.newOne(bannerType, ItemType.weapon, itemKey); | ||
} |
82 changes: 82 additions & 0 deletions
82
lib/domain/models/entities/wish_simulator/wish_simulator_banner_pull_history.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
import 'dart:math'; | ||
|
||
import 'package:darq/darq.dart'; | ||
import 'package:hive/hive.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
|
||
part 'wish_simulator_banner_pull_history.g.dart'; | ||
|
||
@HiveType(typeId: 23) | ||
class WishSimulatorBannerPullHistory extends HiveObject { | ||
@HiveField(0) | ||
final int type; | ||
|
||
@HiveField(1) | ||
Map<int, int> currentXStarCount; | ||
|
||
@HiveField(2) | ||
Map<int, bool> fiftyFiftyXStarGuaranteed; | ||
|
||
WishSimulatorBannerPullHistory( | ||
this.type, | ||
this.currentXStarCount, | ||
this.fiftyFiftyXStarGuaranteed, | ||
); | ||
|
||
WishSimulatorBannerPullHistory.newOne(BannerItemType type, Map<int, int>? defaultXStarCount) | ||
: type = type.index, | ||
currentXStarCount = defaultXStarCount ?? {}, | ||
fiftyFiftyXStarGuaranteed = defaultXStarCount?.keys.toMap((rarity) => MapEntry(rarity, false), modifiable: true) ?? {}; | ||
|
||
void initXStarCountIfNeeded(int rarity) { | ||
if (currentXStarCount.containsKey(rarity)) { | ||
return; | ||
} | ||
|
||
currentXStarCount[rarity] = 0; | ||
} | ||
|
||
bool isItemGuaranteed(int rarity, int guaranteedAt) { | ||
if (rarity <= 0) { | ||
throw Exception('The provided rarity = $rarity is not valid'); | ||
} | ||
|
||
if (guaranteedAt <= 0) { | ||
throw Exception('The provided guaranteedAt = $guaranteedAt is not valid'); | ||
} | ||
|
||
final int current = currentXStarCount[rarity] ?? 0; | ||
return current + 1 >= guaranteedAt; | ||
} | ||
|
||
Future<void> pull(int rarity, bool? gotFeaturedItem) { | ||
if (rarity <= 0) { | ||
throw Exception('The provided rarity = $rarity is not valid'); | ||
} | ||
|
||
_increaseCurrentXStarCount(); | ||
|
||
if (gotFeaturedItem != null) { | ||
currentXStarCount[rarity] = 0; | ||
//this means that we may have won the 50/50 | ||
fiftyFiftyXStarGuaranteed[rarity] = !gotFeaturedItem; | ||
} | ||
|
||
return save(); | ||
} | ||
|
||
bool shouldWinFiftyFifty(int rarity) { | ||
if (fiftyFiftyXStarGuaranteed[rarity] == true) { | ||
return true; | ||
} | ||
|
||
return Random().nextBool(); | ||
} | ||
|
||
void _increaseCurrentXStarCount() { | ||
for (final key in currentXStarCount.keys) { | ||
final int currentCount = currentXStarCount[key] ?? 0; | ||
currentXStarCount[key] = currentCount + 1; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
lib/domain/models/wish_banner_history/wish_banner_history_grouped_period_model.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/models/models.dart'; | ||
|
||
part 'wish_banner_history_grouped_period_model.freezed.dart'; | ||
|
||
@freezed | ||
class WishBannerHistoryGroupedPeriodModel with _$WishBannerHistoryGroupedPeriodModel { | ||
const factory WishBannerHistoryGroupedPeriodModel({ | ||
required String groupingKey, | ||
required String groupingTitle, | ||
required List<WishBannerHistoryPartItemModel> parts, | ||
}) = _WishBannerHistoryGroupedPeriodModel; | ||
} | ||
|
||
@freezed | ||
class WishBannerHistoryPartItemModel with _$WishBannerHistoryPartItemModel { | ||
const factory WishBannerHistoryPartItemModel({ | ||
required List<ItemCommonWithNameAndRarity> featuredCharacters, | ||
required List<ItemCommonWithNameAndRarity> featuredWeapons, | ||
required List<String> bannerImages, | ||
required DateTime from, | ||
required DateTime until, | ||
required double version, | ||
}) = _WishBannerHistoryPartItemModel; | ||
} |
70 changes: 70 additions & 0 deletions
70
lib/domain/models/wish_simulator/wish_simulator_banner_item_model.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
import 'package:shiori/domain/wish_banner_constants.dart'; | ||
|
||
part 'wish_simulator_banner_item_model.freezed.dart'; | ||
|
||
@freezed | ||
class WishSimulatorBannerItemsPerPeriodModel with _$WishSimulatorBannerItemsPerPeriodModel { | ||
const factory WishSimulatorBannerItemsPerPeriodModel({ | ||
required double version, | ||
required DateTime from, | ||
required DateTime until, | ||
required List<WishSimulatorBannerItemModel> banners, | ||
}) = _WishSimulatorBannerItemsPerPeriodModel; | ||
} | ||
|
||
@freezed | ||
class WishSimulatorBannerItemModel with _$WishSimulatorBannerItemModel { | ||
List<String> get featuredImages { | ||
switch (type) { | ||
case BannerItemType.character: | ||
case BannerItemType.weapon: | ||
return featuredItems.where((el) => el.rarity == WishBannerConstants.maxObtainableRarity).map((e) => e.iconImage).toList(); | ||
case BannerItemType.standard: | ||
return [characters.firstWhere((el) => el.key == WishBannerConstants.commonFiveStarCharacterKeys.first).iconImage]; | ||
} | ||
} | ||
|
||
const factory WishSimulatorBannerItemModel({ | ||
required BannerItemType type, | ||
required String image, | ||
required List<WishSimulatorBannerFeaturedItemModel> featuredItems, | ||
@Default(<WishSimulatorBannerCharacterModel>[]) List<WishSimulatorBannerCharacterModel> characters, | ||
@Default(<WishSimulatorBannerWeaponModel>[]) List<WishSimulatorBannerWeaponModel> weapons, | ||
}) = _WishSimulatorBannerItemModel; | ||
|
||
const WishSimulatorBannerItemModel._(); | ||
} | ||
|
||
@freezed | ||
class WishSimulatorBannerFeaturedItemModel with _$WishSimulatorBannerFeaturedItemModel { | ||
const factory WishSimulatorBannerFeaturedItemModel({ | ||
required String key, | ||
required String iconImage, | ||
required int rarity, | ||
required ItemType type, | ||
}) = _WishSimulatorBannerFeaturedItemModel; | ||
} | ||
|
||
@freezed | ||
class WishSimulatorBannerCharacterModel with _$WishSimulatorBannerCharacterModel { | ||
const factory WishSimulatorBannerCharacterModel({ | ||
required String key, | ||
required int rarity, | ||
required String iconImage, | ||
required String image, | ||
required ElementType elementType, | ||
}) = _WisBSimulatorBannerCharacterModel; | ||
} | ||
|
||
@freezed | ||
class WishSimulatorBannerWeaponModel with _$WishSimulatorBannerWeaponModel { | ||
const factory WishSimulatorBannerWeaponModel({ | ||
required String key, | ||
required int rarity, | ||
required String iconImage, | ||
required String image, | ||
required WeaponType weaponType, | ||
}) = _WishSimulatorBannerWeaponModel; | ||
} |
15 changes: 15 additions & 0 deletions
15
lib/domain/models/wish_simulator/wish_simulator_banner_item_pull_history_model.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
|
||
part 'wish_simulator_banner_item_pull_history_model.freezed.dart'; | ||
|
||
@freezed | ||
class WishSimulatorBannerItemPullHistoryModel with _$WishSimulatorBannerItemPullHistoryModel { | ||
const factory WishSimulatorBannerItemPullHistoryModel({ | ||
required String key, | ||
required String name, | ||
required int rarity, | ||
required ItemType type, | ||
required String pulledOn, | ||
}) = _WishSimulatorBannerItemPullHistoryModel; | ||
} |
21 changes: 21 additions & 0 deletions
21
lib/domain/models/wish_simulator/wish_simulator_banner_item_result_model.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import 'package:freezed_annotation/freezed_annotation.dart'; | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
|
||
part 'wish_simulator_banner_item_result_model.freezed.dart'; | ||
|
||
@freezed | ||
class WishSimulatorBannerItemResultModel with _$WishSimulatorBannerItemResultModel { | ||
const factory WishSimulatorBannerItemResultModel.character({ | ||
required String key, | ||
required String image, | ||
required int rarity, | ||
required ElementType elementType, | ||
}) = _WishSimulatorBannerCharacterResultModel; | ||
|
||
const factory WishSimulatorBannerItemResultModel.weapon({ | ||
required String key, | ||
required String image, | ||
required int rarity, | ||
required WeaponType weaponType, | ||
}) = _WishSimulatorBannerWeaponResultModel; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
lib/domain/services/persistence/wish_simulator_data_service.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import 'package:shiori/domain/enums/enums.dart'; | ||
import 'package:shiori/domain/models/entities.dart'; | ||
import 'package:shiori/domain/models/models.dart'; | ||
import 'package:shiori/domain/services/persistence/base_data_service.dart'; | ||
|
||
abstract class WishSimulatorDataService implements BaseDataService { | ||
Future<WishSimulatorBannerPullHistory> getBannerPullHistory(BannerItemType type, {Map<int, int>? defaultXStarCount}); | ||
|
||
Future<void> saveBannerItemPullHistory(BannerItemType bannerType, String itemKey, ItemType itemType); | ||
|
||
Future<void> clearBannerItemPullHistory(BannerItemType bannerType); | ||
|
||
Future<void> clearAllBannerItemPullHistory(); | ||
|
||
List<WishSimulatorBannerItemPullHistory> getBannerItemsPullHistoryPerType(BannerItemType bannerType); | ||
|
||
Future<BackupWishSimulatorModel> getDataForBackup(); | ||
|
||
Future<void> restoreFromBackup(BackupWishSimulatorModel data); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
class FilterUtils { | ||
static List<T> handleTypeSelected<T extends Enum>(List<T> allValues, List<T> tempValues, T selectedValue) { | ||
if (tempValues.length == allValues.length) { | ||
return [selectedValue]; | ||
} | ||
if (tempValues.length == 1 && tempValues.first == selectedValue) { | ||
return allValues.toList(); | ||
} | ||
if (tempValues.contains(selectedValue)) { | ||
return tempValues.where((t) => t != selectedValue).toList(); | ||
} | ||
return tempValues + [selectedValue]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import 'package:intl/intl.dart'; | ||
|
||
class WishBannerConstants { | ||
static const int maxObtainableRarity = 5; | ||
static const int minObtainableRarity = 3; | ||
|
||
static final dateFormat = DateFormat('yyyy-MM-dd'); | ||
|
||
static const commonFiveStarCharacterKeys = [ | ||
'qiqi', | ||
'jean', | ||
'tighnari', | ||
'keqing', | ||
'mona', | ||
'dehya', | ||
'diluc', | ||
]; | ||
|
||
static const fourStarStandardBannerCharacterExclusiveKeys = ['lisa', 'amber', 'kaeya']; | ||
|
||
static const commonFiveStarWeaponKeys = [ | ||
'amos-bow', | ||
'skyward-harp', | ||
'lost-prayer-to-the-sacred-winds', | ||
'skyward-atlas', | ||
'skyward-pride', | ||
'wolfs-gravestone', | ||
'primordial-jade-winged-spear', | ||
'skyward-spine', | ||
'aquila-favonia', | ||
'skyward-blade', | ||
]; | ||
|
||
static const commonFourStarWeaponKeys = [ | ||
'favonius-warbow', | ||
'rust', | ||
'sacrificial-bow', | ||
'the-stringless', | ||
'eye-of-perception', | ||
'favonius-codex', | ||
'sacrificial-fragments', | ||
'the-widsith', | ||
'favonius-greatsword', | ||
'rainslasher', | ||
'sacrificial-greatsword', | ||
'the-bell', | ||
'dragons-bane', | ||
'favonius-lance', | ||
'favonius-sword', | ||
'lions-roar', | ||
'sacrificial-sword', | ||
'the-flute', | ||
]; | ||
|
||
static const commonThreeStarWeaponKeys = [ | ||
'raven-bow', | ||
'sharpshooters-oath', | ||
'slingshot', | ||
'emerald-orb', | ||
'magic-guide', | ||
'thrilling-tales-of-dragon-slayers', | ||
'bloodtainted-greatsword', | ||
'debate-club', | ||
'ferrous-shadow', | ||
'black-tassel', | ||
'cool-steel', | ||
'harbinger-of-dawn', | ||
'skyrider-sword', | ||
]; | ||
|
||
static List<String> commonWeaponKeys = | ||
commonFiveStarWeaponKeys + commonFourStarWeaponKeys + commonThreeStarWeaponKeys + commonFiveStarCharacterKeys; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.