Skip to content

Commit

Permalink
Merge pull request #3687 from wowsims/apl
Browse files Browse the repository at this point in the history
Add most of the UI infra to support fully-launched APL, and also Simp…
  • Loading branch information
jimmyt857 authored Sep 16, 2023
2 parents cc27484 + 205040a commit e210973
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 36 deletions.
2 changes: 1 addition & 1 deletion proto/apl.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import "shaman.proto";
// Rotation options are based heavily on APL. See https://github.com/simulationcraft/simc/wiki/ActionLists.

message APLRotation {
bool enabled = 20; // If false, use old rotation options.
bool enabled = 20 [deprecated = true]; // If false, use old rotation options.

enum Type {
TypeUnknown = 0;
Expand Down
52 changes: 30 additions & 22 deletions ui/core/components/individual_sim_ui/rotation_tab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,7 @@ export class RotationTab extends SimTab {

this.buildAutoContent();
this.buildAplContent();

// Legacy
this.buildRotationSettings();
this.buildCooldownSettings();
this.buildSimpleOrLegacyContent(this.simUI.player.hasSimpleRotationGenerator());

this.buildSavedDataPickers();
}
Expand Down Expand Up @@ -100,10 +97,17 @@ export class RotationTab extends SimTab {
values: aplLaunchStatus == LaunchStatus.Alpha ? [
{ value: APLRotationType.TypeLegacy, name: 'Legacy' },
{ value: APLRotationType.TypeAPL, name: 'APL' },
] : [
] : aplLaunchStatus == LaunchStatus.Beta ? [
{ value: APLRotationType.TypeAuto, name: 'Auto' },
{ value: APLRotationType.TypeAPL, name: 'APL' },
{ value: APLRotationType.TypeLegacy, name: 'Legacy' },
] : this.simUI.player.hasSimpleRotationGenerator() ? [
{ value: APLRotationType.TypeAuto, name: 'Auto' },
{ value: APLRotationType.TypeSimple, name: 'Simple' },
{ value: APLRotationType.TypeAPL, name: 'APL' },
] : [
{ value: APLRotationType.TypeAuto, name: 'Auto' },
{ value: APLRotationType.TypeAPL, name: 'APL' },
],
changedEvent: (player: Player<any>) => player.rotationChangeEmitter,
getValue: (player: Player<any>) => player.getRotationType(),
Expand All @@ -129,11 +133,13 @@ export class RotationTab extends SimTab {
new APLRotationPicker(content, this.simUI, this.simUI.player);
}

private buildRotationSettings() {
private buildSimpleOrLegacyContent(isSimple: boolean) {
const cssClass = isSimple ? 'rotation-tab-simple' : 'rotation-tab-legacy';

const contentBlock = new ContentBlock(this.leftPanel, 'rotation-settings', {
header: { title: 'Rotation' }
});
contentBlock.rootElem.classList.add('rotation-tab-legacy');
contentBlock.rootElem.classList.add(cssClass);

const rotationIconGroup = Input.newGroupContainer();
rotationIconGroup.classList.add('rotation-icon-group', 'icon-group');
Expand All @@ -152,15 +158,13 @@ export class RotationTab extends SimTab {
contentBlock.bodyElement.querySelectorAll('.input-root').forEach(elem => {
elem.classList.add('input-inline');
})
}

private buildCooldownSettings() {
const contentBlock = new ContentBlock(this.leftPanel, 'cooldown-settings', {
const cooldownsContentBlock = new ContentBlock(this.leftPanel, 'cooldown-settings', {
header: { title: 'Cooldowns', tooltip: Tooltips.COOLDOWNS_SECTION }
});
contentBlock.rootElem.classList.add('rotation-tab-legacy');
cooldownsContentBlock.rootElem.classList.add(cssClass);

new CooldownsPicker(contentBlock.bodyElement, this.simUI.player);
new CooldownsPicker(cooldownsContentBlock.bodyElement, this.simUI.player);
}

private configureInputSection(sectionElem: HTMLElement, sectionConfig: InputSection) {
Expand Down Expand Up @@ -192,28 +196,32 @@ export class RotationTab extends SimTab {
}

private buildSavedDataPickers() {
const launchStatus = aplLaunchStatuses[this.simUI.player.spec];

const savedRotationsManager = new SavedDataManager<Player<any>, SavedRotation>(this.rightPanel, this.simUI, this.simUI.player, {
label: 'Rotation',
header: { title: 'Saved Rotations' },
storageKey: this.simUI.getSavedRotationStorageKey(),
getData: (player: Player<any>) => SavedRotation.create({
rotation: APLRotation.clone(player.aplRotation),
specRotationOptionsJson: JSON.stringify(player.specTypeFunctions.rotationToJson(player.getRotation())),
cooldowns: player.getCooldowns(),
specRotationOptionsJson: launchStatus == LaunchStatus.Launched ? '' : JSON.stringify(player.specTypeFunctions.rotationToJson(player.getRotation())),
cooldowns: LaunchStatus.Launched ? undefined : player.getCooldowns(),
}),
setData: (eventID: EventID, player: Player<any>, newRotation: SavedRotation) => {
TypedEvent.freezeAllAndDo(() => {
player.setAplRotation(eventID, newRotation.rotation || APLRotation.create());
if (newRotation.specRotationOptionsJson) {
try {
const json = JSON.parse(newRotation.specRotationOptionsJson);
const specRot = player.specTypeFunctions.rotationFromJson(json);
player.setRotation(eventID, specRot);
} catch (e) {
console.warn('Error parsing rotation spec options: ' + e);
if (launchStatus != LaunchStatus.Launched) {
if (newRotation.specRotationOptionsJson) {
try {
const json = JSON.parse(newRotation.specRotationOptionsJson);
const specRot = player.specTypeFunctions.rotationFromJson(json);
player.setRotation(eventID, specRot);
} catch (e) {
console.warn('Error parsing rotation spec options: ' + e);
}
}
player.setCooldowns(eventID, newRotation.cooldowns || Cooldowns.create());
}
player.setCooldowns(eventID, newRotation.cooldowns || Cooldowns.create());
});
},
changeEmitters: [this.simUI.player.rotationChangeEmitter, this.simUI.player.cooldownsChangeEmitter],
Expand Down
8 changes: 6 additions & 2 deletions ui/core/individual_sim_ui.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { aplLaunchStatuses, LaunchStatus, simLaunchStatuses } from './launched_sims';
import { AutoRotationGenerator, Player } from './player';
import { Player, AutoRotationGenerator, SimpleRotationGenerator } from './player';
import { SimUI, SimWarning } from './sim_ui';
import { EventID, TypedEvent } from './typed_event';

Expand Down Expand Up @@ -138,6 +138,7 @@ export interface IndividualSimUIConfig<SpecType extends Spec> {
},

autoRotation?: AutoRotationGenerator<SpecType>,
simpleRotation?: SimpleRotationGenerator<SpecType>,
}

export interface GearAndStats {
Expand Down Expand Up @@ -205,6 +206,9 @@ export abstract class IndividualSimUI<SpecType extends Spec> extends SimUI {
}
player.setAutoRotationGenerator(config.autoRotation);
}
if (aplLaunchStatuses[player.spec] == LaunchStatus.Launched && config.simpleRotation) {
player.setSimpleRotationGenerator(config.simpleRotation);
}

this.addWarning({
updateOn: this.player.gearChangeEmitter,
Expand Down Expand Up @@ -425,7 +429,7 @@ export abstract class IndividualSimUI<SpecType extends Spec> extends SimUI {
this.player.setConsumes(eventID, this.individualConfig.defaults.consumes);
if (aplLaunchStatuses[this.player.spec] < LaunchStatus.Beta) {
this.player.setRotation(eventID, this.individualConfig.defaults.rotation);
} else {
} else if (aplLaunchStatuses[this.player.spec] == LaunchStatus.Beta) {
this.player.setRotation(eventID, this.player.specTypeFunctions.rotationCreate());
}
this.player.setTalentsString(eventID, this.individualConfig.defaults.talents.talentsString);
Expand Down
90 changes: 79 additions & 11 deletions ui/core/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
APLRotation,
APLRotation_Type as APLRotationType,
APLValue,
SimpleRotation,
} from './proto/apl.js';
import {
DungeonDifficulty,
Expand Down Expand Up @@ -187,6 +188,7 @@ export class UnitMetadataList {
}

export type AutoRotationGenerator<SpecType extends Spec> = (player: Player<SpecType>) => APLRotation;
export type SimpleRotationGenerator<SpecType extends Spec> = (player: Player<SpecType>, simpleRotation: SpecRotation<SpecType>) => APLRotation;

// Manages all the gear / consumes / other settings for a single Player.
export class Player<SpecType extends Spec> {
Expand Down Expand Up @@ -218,6 +220,7 @@ export class Player<SpecType extends Spec> {
private healingEnabled: boolean = false;

private autoRotationGenerator: AutoRotationGenerator<SpecType> | null = null;
private simpleRotationGenerator: SimpleRotationGenerator<SpecType> | null = null;

private itemEPCache = new Array<Map<number, number>>();
private gemEPCache = new Map<number, number>();
Expand Down Expand Up @@ -541,15 +544,28 @@ export class Player<SpecType extends Spec> {

getCooldowns(): Cooldowns {
// Make a defensive copy
return Cooldowns.clone(this.cooldowns);
if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
return Cooldowns.clone(this.aplRotation.simple?.cooldowns || Cooldowns.create());
} else {
return Cooldowns.clone(this.cooldowns);
}
}

setCooldowns(eventID: EventID, newCooldowns: Cooldowns) {
if (Cooldowns.equals(this.cooldowns, newCooldowns))
if (Cooldowns.equals(this.getCooldowns(), newCooldowns))
return;

// Make a defensive copy
this.cooldowns = Cooldowns.clone(newCooldowns);
if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
if (!this.aplRotation.simple) {
this.aplRotation.simple = SimpleRotation.create();
}
this.aplRotation.simple.cooldowns = newCooldowns;
this.rotationChangeEmitter.emit(eventID);
} else {
// Make a defensive copy
this.cooldowns = Cooldowns.clone(newCooldowns);
}

this.cooldownsChangeEmitter.emit(eventID);
}

Expand Down Expand Up @@ -639,14 +655,32 @@ export class Player<SpecType extends Spec> {
}

getRotation(): SpecRotation<SpecType> {
return this.specTypeFunctions.rotationCopy(this.rotation);
if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
try {
const json = JSON.parse(this.aplRotation.simple?.specRotationJson || '');
return this.specTypeFunctions.rotationFromJson(json);
} catch (e) {
console.warn('Error parsing rotation spec options: ' + e);
return this.specTypeFunctions.rotationCreate();
}
} else {
return this.specTypeFunctions.rotationCopy(this.rotation);
}
}

setRotation(eventID: EventID, newRotation: SpecRotation<SpecType>) {
if (this.specTypeFunctions.rotationEquals(newRotation, this.rotation))
if (this.specTypeFunctions.rotationEquals(newRotation, this.getRotation()))
return;

this.rotation = this.specTypeFunctions.rotationCopy(newRotation);
if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
if (!this.aplRotation.simple) {
this.aplRotation.simple = SimpleRotation.create();
}
this.aplRotation.simple.specRotationJson = this.specTypeFunctions.rotationToJson(newRotation);
} else {
this.rotation = this.specTypeFunctions.rotationCopy(newRotation);
}

this.rotationChangeEmitter.emit(eventID);
}

Expand All @@ -668,15 +702,28 @@ export class Player<SpecType extends Spec> {
}
}

setAutoRotationGenerator(arg: AutoRotationGenerator<SpecType>) {
this.autoRotationGenerator = arg;
setAutoRotationGenerator(generator: AutoRotationGenerator<SpecType>) {
this.autoRotationGenerator = generator;
}

setSimpleRotationGenerator(generator: SimpleRotationGenerator<SpecType>) {
this.simpleRotationGenerator = generator;
}

hasSimpleRotationGenerator(): boolean {
return this.simpleRotationGenerator != null;
}

getResolvedAplRotation(): APLRotation {
const type = this.getRotationType();
if (type == APLRotationType.TypeAuto && this.autoRotationGenerator) {
// Clone to avoid modifying preset rotations, which are often returned directly.
return APLRotation.clone(this.autoRotationGenerator(this));
} else if (type == APLRotationType.TypeSimple && this.simpleRotationGenerator) {
// Clone to avoid modifying preset rotations, which are often returned directly.
const rot = APLRotation.clone(this.simpleRotationGenerator(this, this.getRotation()));
rot.type = APLRotationType.TypeAPL; // Set this here for convenience, so the generator functions don't need to.
return rot;
} else {
return this.aplRotation;
}
Expand Down Expand Up @@ -1196,6 +1243,25 @@ export class Player<SpecType extends Spec> {
}
}

if (aplLaunchStatuses[this.spec] == LaunchStatus.Launched) {
const rot = this.specTypeFunctions.rotationFromPlayer(proto);
if (rot && !this.specTypeFunctions.rotationEquals(rot, this.specTypeFunctions.rotationCreate())) {
if (this.simpleRotationGenerator) {
proto.rotation = APLRotation.create({
type: APLRotationType.TypeSimple,
simple: {
specRotationJson: this.specTypeFunctions.rotationToJson(rot),
cooldowns: proto.cooldowns,
},
});
} else {
proto.rotation = APLRotation.create({
type: APLRotationType.TypeAuto,
});
}
}
}

TypedEvent.freezeAllAndDo(() => {
this.setName(eventID, proto.name);
this.setRace(eventID, proto.race);
Expand All @@ -1204,7 +1270,6 @@ export class Player<SpecType extends Spec> {
this.setConsumes(eventID, proto.consumes || Consumes.create());
this.setBonusStats(eventID, Stats.fromProto(proto.bonusStats || UnitStats.create()));
this.setBuffs(eventID, proto.buffs || IndividualBuffs.create());
this.setCooldowns(eventID, proto.cooldowns || Cooldowns.create());
this.setTalentsString(eventID, proto.talentsString);
this.setGlyphs(eventID, proto.glyphs || Glyphs.create());
this.setProfession1(eventID, proto.profession1);
Expand All @@ -1213,7 +1278,10 @@ export class Player<SpecType extends Spec> {
this.setInFrontOfTarget(eventID, proto.inFrontOfTarget);
this.setDistanceFromTarget(eventID, proto.distanceFromTarget);
this.setHealingModel(eventID, proto.healingModel || HealingModel.create());
this.setRotation(eventID, this.specTypeFunctions.rotationFromPlayer(proto));
if (aplLaunchStatuses[this.spec] != LaunchStatus.Launched) {
this.setCooldowns(eventID, proto.cooldowns || Cooldowns.create());
this.setRotation(eventID, this.specTypeFunctions.rotationFromPlayer(proto));
}
this.setAplRotation(eventID, proto.rotation || APLRotation.create())
this.setSpecOptions(eventID, this.specTypeFunctions.optionsFromPlayer(proto));

Expand Down

0 comments on commit e210973

Please sign in to comment.