yumi-flutter/lib/shared/tools/sc_cp_relation_level_utils.dart
2026-05-23 19:12:58 +08:00

260 lines
6.6 KiB
Dart

import 'package:yumi/shared/business_logic/models/res/login_res.dart';
import 'package:yumi/shared/business_logic/models/res/sc_cp_rights_config_res.dart';
String? scCpRelationLevelTextFromCabin(
SCCpCabinRes? cabin, {
String? fallbackText,
}) {
if (cabin == null) {
return _blankAsNull(fallbackText);
}
final currentValue =
cabin.cpVal?.toDouble() ?? cabin.cpPairUserProfileCO?.cpValue;
final computedLevel = _currentLevelFromDynamicConfigs(
cabin.cpCabinConfigCO ?? const <dynamic>[],
currentValue,
);
final computedText = _formattedLevelText(
level: computedLevel?.level,
name: computedLevel?.levelName,
);
if (computedText != null) {
return computedText;
}
if (currentValue != null && currentValue <= 0) {
return 'Lv.0';
}
final currentConfig = _CpRelationLevelSource.fromDynamic(
cabin.currentCpCabinConfigCO,
);
final currentText = _formattedLevelText(
level: currentConfig.level,
name: currentConfig.levelName,
);
if (currentText != null) {
return currentText;
}
return _blankAsNull(cabin.cpPairUserProfileCO?.levelText) ??
_blankAsNull(fallbackText);
}
String? scCpRelationLevelTextFromRightsConfig(
SCCpRightsConfigRes config, {
String? fallbackText,
}) {
final currentValue = config.intimacyValue?.toDouble();
final computedLevel = _currentLevelFromRightsLevels(
config.levels,
currentValue,
);
final computedText = _formattedLevelText(
level: computedLevel?.level,
name: computedLevel?.levelName,
);
if (computedText != null) {
return computedText;
}
if (currentValue != null && currentValue <= 0) {
return 'Lv.0';
}
return _blankAsNull(fallbackText);
}
String scCpRelationLevelDisplayText(String? rawText) {
final text = rawText?.trim() ?? '';
final level = scCpRelationLevelIndexOrNull(text);
if (text.isEmpty) {
return 'Lv.0';
}
if (_isSimpleLoveLevelOneText(text, level)) {
return 'Lv.1';
}
final compactLevelPattern = RegExp(
r'^(?:Lv\.?|Level)?\s*\d+$',
caseSensitive: false,
);
if (compactLevelPattern.hasMatch(text) && level != null) {
return 'Lv.$level';
}
return text;
}
int scCpRelationLevelIndex(String? levelText) {
return scCpRelationLevelIndexOrNull(levelText) ?? 0;
}
int? scCpRelationLevelIndexOrNull(String? levelText) {
final text = levelText?.trim() ?? '';
if (text.isEmpty) {
return null;
}
final levelMatch = RegExp(
r'(?:Lv\.?|Level|等级|级)\s*([0-5])|([0-5])\s*(?:级|Level)',
caseSensitive: false,
).firstMatch(text);
final compactMatch = RegExp(r'^[0-5]$').firstMatch(text);
final rawLevel =
levelMatch?.group(1) ?? levelMatch?.group(2) ?? compactMatch?.group(0);
final level = int.tryParse(rawLevel ?? '');
return level?.clamp(0, 5).toInt();
}
_CpRelationLevelSource? _currentLevelFromDynamicConfigs(
List<dynamic> configs,
double? currentValue,
) {
final levels =
configs.map(_CpRelationLevelSource.fromDynamic).where((level) {
return level.requiredValue != null;
}).toList()
..sort(
(left, right) => left.requiredValue!.compareTo(right.requiredValue!),
);
return _currentLevelFromSources(levels, currentValue);
}
_CpRelationLevelSource? _currentLevelFromRightsLevels(
List<SCCpRightsLevelRes> levels,
double? currentValue,
) {
final sources =
levels.map(_CpRelationLevelSource.fromRightsLevel).where((level) {
return level.requiredValue != null;
}).toList()
..sort(
(left, right) => left.requiredValue!.compareTo(right.requiredValue!),
);
return _currentLevelFromSources(sources, currentValue);
}
_CpRelationLevelSource? _currentLevelFromSources(
List<_CpRelationLevelSource> levels,
double? currentValue,
) {
if (levels.isEmpty || currentValue == null) {
return null;
}
_CpRelationLevelSource? currentLevel;
for (final level in levels) {
if ((level.requiredValue ?? 0) <= currentValue) {
currentLevel = level;
}
}
return currentLevel;
}
String? _formattedLevelText({int? level, String? name}) {
final levelName = name?.trim() ?? '';
if (level == 1 && _isSimpleLoveName(levelName)) {
return 'Lv.1';
}
if (level != null && levelName.isNotEmpty) {
final nameLevel = scCpRelationLevelIndexOrNull(levelName);
if (nameLevel == level) {
return scCpRelationLevelDisplayText(levelName);
}
return 'Lv.$level $levelName';
}
if (level != null) {
return 'Lv.$level';
}
return _blankAsNull(levelName);
}
bool _isSimpleLoveLevelOneText(String text, int? level) {
if (level != 1) {
return false;
}
return RegExp(
r'^(?:Lv\.?|Level)?\s*1\s+Simple\s+Love$',
caseSensitive: false,
).hasMatch(text.trim());
}
bool _isSimpleLoveName(String text) {
return RegExp(
r'^(?:Lv\.?|Level)?\s*1?\s*Simple\s+Love$',
caseSensitive: false,
).hasMatch(text.trim());
}
String? _blankAsNull(String? value) {
final text = value?.trim() ?? '';
return text.isEmpty ? null : text;
}
class _CpRelationLevelSource {
const _CpRelationLevelSource({
this.level,
this.levelName,
this.requiredValue,
});
factory _CpRelationLevelSource.fromRightsLevel(SCCpRightsLevelRes level) {
return _CpRelationLevelSource(
level: level.level,
levelName: level.levelName,
requiredValue: level.requiredIntimacyValue,
);
}
factory _CpRelationLevelSource.fromDynamic(dynamic value) {
final source = value is Map ? value : const <String, dynamic>{};
return _CpRelationLevelSource(
level: _asInt(
_firstValue(source, const ['level', 'cpLevel', 'cabinLevel', 'lv']),
),
levelName: _asString(
_firstValue(source, const [
'levelName',
'name',
'title',
'cabinName',
'cpName',
]),
),
requiredValue: _asInt(
_firstValue(source, const [
'requiredIntimacyValue',
'requiredCpValue',
'requiredCpVal',
'cpValue',
'cpVal',
'intimacyValue',
]),
),
);
}
final int? level;
final String? levelName;
final int? requiredValue;
}
dynamic _firstValue(Map source, List<String> keys) {
for (final key in keys) {
if (source.containsKey(key) && source[key] != null) {
return source[key];
}
}
return null;
}
String? _asString(dynamic value) {
final text = value?.toString().trim();
return text == null || text.isEmpty ? null : text;
}
int? _asInt(dynamic value) {
if (value is int) {
return value;
}
if (value is num) {
return value.toInt();
}
return int.tryParse(value?.toString() ?? '');
}