Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions lib/const/ability_vision.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import 'dart:math' as math;

import 'package:icarus/const/agents.dart';
import 'package:icarus/const/utilities.dart';

/// The directional vision an ability contributes to a strategy.
class AbilityVisionConeSpec {
const AbilityVisionConeSpec({
required this.angleDegrees,
this.maxRangeMeters,
});

/// Full rendered wedge angle in degrees.
final double angleDegrees;

/// Fixed in-game detection distance. Null uses the normal adjustable
/// line-of-sight cone length.
final double? maxRangeMeters;

static const double _defaultLength = 50;

static const Map<(AgentType, int), AbilityVisionConeSpec> _specs = {
(AgentType.killjoy, 2): AbilityVisionConeSpec(angleDegrees: 100),
(AgentType.cypher, 2): AbilityVisionConeSpec(angleDegrees: 103),
(AgentType.raze, 0): AbilityVisionConeSpec(
angleDegrees: 40,
maxRangeMeters: 15,
),
(AgentType.gekko, 1): AbilityVisionConeSpec(
angleDegrees: 40,
maxRangeMeters: 15,
),
};

static AbilityVisionConeSpec? forAbility(AbilityInfo ability) {
return _specs[(ability.type, ability.index)];
}

double maximumLength(double mapScale) {
final rangeMeters = maxRangeMeters;
if (rangeMeters == null) {
return ViewConeUtility.maxLength;
}
return math.min(
rangeMeters * AgentData.inGameMeters * mapScale,
ViewConeUtility.maxLength,
);
}

double resolveLength({
required double storedLength,
required double mapScale,
}) {
final maximum = maximumLength(mapScale);
final minimum = math.min(ViewConeUtility.minLength, maximum);
final fallback = maxRangeMeters == null ? _defaultLength : maximum;
final storedScale = maxRangeMeters == null ? 1.0 : mapScale;
final requested = storedLength > 0 ? storedLength * storedScale : fallback;
return requested.clamp(minimum, maximum).toDouble();
}

double storedLengthFromRendered({
required double renderedLength,
required double mapScale,
}) {
final maximum = maximumLength(mapScale);
final minimum = math.min(ViewConeUtility.minLength, maximum);
final clamped = renderedLength.clamp(minimum, maximum).toDouble();
return maxRangeMeters == null ? clamped : clamped / mapScale;
}
}
12 changes: 10 additions & 2 deletions lib/const/placed_classes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:hive_ce_flutter/hive_flutter.dart';
import 'package:icarus/const/abilities.dart';
import 'package:icarus/const/ability_vision.dart';
import 'package:icarus/const/agents.dart';
import 'package:icarus/const/coordinate_system.dart';
import 'package:icarus/const/json_converters.dart';
Expand Down Expand Up @@ -937,9 +938,10 @@ class PlacedAbility extends PlacedWidget {
abilitySize: Settings.abilitySize,
);
final abilityData = data.abilityData!;
final shouldRotate = isRotatable(abilityData);
final hasVisionCone = AbilityVisionConeSpec.forAbility(data) != null;
final shouldRotate = isRotatable(abilityData) || hasVisionCone;
final shouldUseRotatableFlipCompensation =
shouldRotate && abilityData is! DeadlockBarrierMeshAbility;
isRotatable(abilityData) && abilityData is! DeadlockBarrierMeshAbility;

final scaledAbilitySize = fullAbilityWidgetSize.scale(
CoordinateSystem.instance.scaleFactor,
Expand Down Expand Up @@ -1047,6 +1049,7 @@ class AbilityVisualState {
this.showRangeFill = true,
this.showInnerOutline = true,
this.showInnerFill = true,
this.showVisionCone = true,
});

@JsonKey(defaultValue: true)
Expand All @@ -1061,17 +1064,22 @@ class AbilityVisualState {
@JsonKey(defaultValue: true)
final bool showInnerFill;

@JsonKey(defaultValue: true)
final bool showVisionCone;

AbilityVisualState copyWith({
bool? showRangeOutline,
bool? showRangeFill,
bool? showInnerOutline,
bool? showInnerFill,
bool? showVisionCone,
}) {
return AbilityVisualState(
showRangeOutline: showRangeOutline ?? this.showRangeOutline,
showRangeFill: showRangeFill ?? this.showRangeFill,
showInnerOutline: showInnerOutline ?? this.showInnerOutline,
showInnerFill: showInnerFill ?? this.showInnerFill,
showVisionCone: showVisionCone ?? this.showVisionCone,
);
}

Expand Down
2 changes: 2 additions & 0 deletions lib/const/placed_classes.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 5 additions & 2 deletions lib/hive/hive_adapters.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion lib/hive/hive_adapters.g.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ types:
index: 8
AbilityVisualState:
typeId: 32
nextIndex: 6
nextIndex: 7
fields:
showRangeOutline:
index: 2
Expand All @@ -541,6 +541,8 @@ types:
index: 4
showInnerFill:
index: 5
showVisionCone:
index: 6
LineUpGroup:
typeId: 33
nextIndex: 3
Expand Down
38 changes: 38 additions & 0 deletions lib/migrations/ability_vision_cone_migration.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import 'package:icarus/const/line_provider.dart';
import 'package:icarus/const/placed_classes.dart';
import 'package:icarus/providers/strategy_page.dart';

class AbilityVisionConeMigration {
static const int version = 95;

static List<StrategyPage> migratePages({required List<StrategyPage> pages}) {
return [
for (final page in pages)
page.copyWith(
abilityData: [
for (final ability in page.abilityData) _migrateAbility(ability),
],
lineUpGroups: [
for (final group in page.lineUpGroups) _migrateLineUpGroup(group),
],
),
];
}

static LineUpGroup _migrateLineUpGroup(LineUpGroup group) {
return group.copyWith(
items: [
for (final item in group.items)
item.copyWith(ability: _migrateAbility(item.ability)),
],
);
}

static PlacedAbility _migrateAbility(PlacedAbility ability) {
final migrated = ability.copyWith(
visualState: ability.visualState.copyWith(showVisionCone: true),
);
migrated.isDeleted = ability.isDeleted;
return migrated;
}
}
45 changes: 41 additions & 4 deletions lib/providers/strategy_provider.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import 'package:icarus/const/coordinate_system.dart';
import 'package:icarus/const/folder_icons.dart';
import 'package:icarus/const/hive_boxes.dart';
import 'package:icarus/const/settings.dart';
import 'package:icarus/migrations/ability_vision_cone_migration.dart';
import 'package:icarus/migrations/ability_scale_migration.dart';
import 'package:icarus/migrations/custom_circle_wrapper_migration.dart';
import 'package:icarus/migrations/lineup_group_migration.dart';
Expand Down Expand Up @@ -493,7 +494,11 @@ class StrategyProvider extends Notifier<StrategyState> {
final customCircleMigrated =
migrateCustomCircleWrapper(squareAoeMigrated);
final lineUpGroupMigrated = migrateLineUpGroups(customCircleMigrated);
if (lineUpGroupMigrated != customCircleMigrated) {
final abilityVisionMigrated =
migrateAbilityVisionCones(lineUpGroupMigrated);
if (abilityVisionMigrated != lineUpGroupMigrated) {
await box.put(abilityVisionMigrated.id, abilityVisionMigrated);
} else if (lineUpGroupMigrated != customCircleMigrated) {
await box.put(lineUpGroupMigrated.id, lineUpGroupMigrated);
} else if (customCircleMigrated != squareAoeMigrated) {
await box.put(customCircleMigrated.id, customCircleMigrated);
Expand Down Expand Up @@ -594,15 +599,39 @@ class StrategyProvider extends Notifier<StrategyState> {

static StrategyData migrateToCurrentVersion(StrategyData strat,
{bool forceAbilityScale = false}) {
final needsAbilityVisionMigration =
strat.versionNumber < AbilityVisionConeMigration.version;
final needsPageNameProvenanceMigration =
strat.versionNumber < PageNameProvenanceMigration.version;
final worldMigrated = migrateToWorld16x9(strat);
final abilityScaleMigrated =
migrateAbilityScale(worldMigrated, force: forceAbilityScale);
final squareAoeMigrated = migrateSquareAoeCenter(abilityScaleMigrated);
final customCircleMigrated = migrateCustomCircleWrapper(squareAoeMigrated);
final lineUpGroupMigrated = migrateLineUpGroups(customCircleMigrated);
return migratePageNameProvenance(
final abilityVisionMigrated = migrateAbilityVisionCones(
lineUpGroupMigrated,
force: strat.versionNumber < PageNameProvenanceMigration.version,
force: needsAbilityVisionMigration,
);
return migratePageNameProvenance(
abilityVisionMigrated,
force: needsPageNameProvenanceMigration,
);
}

static StrategyData migrateAbilityVisionCones(StrategyData strat,
{bool force = false}) {
if (!force && strat.versionNumber >= AbilityVisionConeMigration.version) {
return strat;
}

final migratedPages = AbilityVisionConeMigration.migratePages(
pages: strat.pages,
);
return strat.copyWith(
pages: migratedPages,
versionNumber: Settings.versionNumber,
lastEdited: DateTime.now(),
);
}

Expand Down Expand Up @@ -709,10 +738,18 @@ class StrategyProvider extends Notifier<StrategyState> {
abilityScaleMigrated,
force: originalVersion < SquareAoeCenterMigration.version,
);
return migrateCustomCircleWrapper(
final customCircleMigrated = migrateCustomCircleWrapper(
squareAoeMigrated,
force: originalVersion < CustomCircleWrapperMigration.version,
);
final lineUpGroupMigrated = migrateLineUpGroups(
customCircleMigrated,
force: originalVersion < LineUpGroupMigration.version,
);
return migrateAbilityVisionCones(
lineUpGroupMigrated,
force: originalVersion < AbilityVisionConeMigration.version,
);
}

static StrategyData migrateToWorld16x9(StrategyData strat,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:icarus/const/abilities.dart';
import 'package:icarus/const/ability_vision.dart';
import 'package:icarus/const/line_provider.dart';
import 'package:icarus/const/placed_classes.dart';
import 'package:icarus/const/settings.dart';
Expand Down Expand Up @@ -65,7 +66,19 @@ List<ShadContextMenuItem> _buildVisibilityItems(
String? lineUpGroupId,
String? lineUpItemId,
}) {
final controls = _buildVisibilityControls(abilityData, ability.visualState);
final controls = [
..._buildVisibilityControls(abilityData, ability.visualState),
if (lineUpGroupId == null &&
lineUpItemId == null &&
AbilityVisionConeSpec.forAbility(ability.data) != null)
_AbilityVisibilityControl(
label: 'Vision Cone',
isEnabled: ability.visualState.showVisionCone,
toggle: (state) => state.copyWith(
showVisionCone: !state.showVisionCone,
),
),
];
return controls
.map(
(control) => _buildToggleItem(
Expand Down
Loading
Loading