aminus
Version:
Genshin Impact stat and damage calculation library
442 lines (343 loc) • 10.8 kB
Markdown
JavaScript and TypeScript bindings for the Aminus Genshin Impact calculation library.
- **Full TypeScript Support**: Complete type definitions for all functions and classes
- **ESM Module Support**: Uses modern ES modules
- **WebAssembly**: High-performance calculations compiled from Rust
- **Cross-Platform**: Works in Node.js, browsers, and bundlers
- **Zero Dependencies**: No runtime dependencies besides the WASM module
- **Artifact Optimization**: Advanced algorithms for finding optimal artifact combinations
```bash
npm install aminus-js
```
```typescript
import { loadWasm, Stat, Element, DamageType, BaseScaling, Amplifier } from 'aminus-js';
async function calculateDamage() {
// Load the WASM module
const aminus = await loadWasm();
// Create a character stat table
const character = aminus.StatTableWrapper.fromArray([
[],
[],
[],
[],
[],
]);
// Calculate damage
const damage = aminus.calculateDamage(
Element.Pyro,
DamageType.Skill,
BaseScaling.ATK,
Amplifier.None,
1.0, // instances
2.5, // motion value
character
);
console.log(`Skill damage: ${damage}`);
}
calculateDamage();
```
```javascript
import { loadWasm, Stat, Element, DamageType, BaseScaling, Amplifier } from 'aminus-js';
const aminus = await loadWasm();
// Create artifact builder
const builder = aminus.ArtifactBuilderWrapper.kqmAll5Star(
Stat.ATKPercent, // sands
Stat.PyroDMGBonus, // goblet
Stat.CritRate // circlet
);
// Add substat rolls
builder.roll(Stat.CritDMG, 4, 5, 6); // RollQuality.AVG, rarity 5, 6 rolls
// Get final stats
const artifacts = builder.build();
console.log(`Total Crit DMG: ${artifacts.get(Stat.CritDMG)}`);
```
```typescript
import {
createStatTable,
createRotation,
optimizeArtifactMainStats,
optimizeArtifactSubstats,
Stat, Element, DamageType, BaseScaling, Amplifier
} from 'aminus-js';
async function optimizeCharacter() {
// Create character stats
const characterStats = createStatTable();
characterStats.set(Stat.BaseATK, 106.0);
characterStats.set(Stat.CritRate, 0.05);
characterStats.set(Stat.CritDMG, 0.5);
// Create rotation (damage sequence)
const rotation = createRotation();
rotationadd(
'pyro_attack',
Element.Pyro,
DamageType.Normal,
BaseScaling.ATK,
Amplifier.None,
1.0, // instances
1.0 // motion value
);
// Optimize artifact main stats
const bestMainStats = await optimizeArtifactMainStats(characterStats, rotation);
console.log('Best main stats:', bestMainStats); // [sands, goblet, circlet]
// Optimize artifact substats
const substatDistribution = await optimizeArtifactSubstats(
characterStats,
rotation,
1.2, // energy recharge requirement
flower, feather, sands, goblet, circlet
);
console.log('Optimal substat distribution:', substatDistribution);
}
```
- [Rust](https://rustup.rs/) (latest stable)
- [wasm-pack](https://rustwasm.github.io/wasm-pack/installer/)
- [Node.js](https://nodejs.org/) (16+)
```bash
npm install
npm run build
npm run build:nodejs
npm run build:web
npm test
npm run dev
```
- `Stat`: Character and artifact statistics
- `Element`: Elemental types (Pyro, Hydro, etc.)
- `DamageType`: Attack types (Normal, Charged, Skill, Burst, etc.)
- `BaseScaling`: Damage scaling base (ATK, DEF, HP)
- `Amplifier`: Reaction amplifiers (None, Forward, Reverse)
- `RollQuality`: Artifact substat roll quality
#### Core Classes
##### StatTableWrapper
Manages character statistics.
```typescript
const stats = new StatTableWrapper();
stats.add(Stat.ATKPercent, 0.5);
const atkPercent = stats.get(Stat.ATKPercent);
// Create from array
const stats2 = StatTableWrapper.fromArray([
[],
[]
]);
```
Builds artifact stat combinations.
```typescript
// Manual construction
const builder = new ArtifactBuilderWrapper(flower, feather, sands, goblet, circlet);
// KQM presets
const builder = ArtifactBuilderWrapper.kqmAll5Star(
Stat.ATKPercent, // sands main stat
Stat.PyroDMGBonus, // goblet main stat
Stat.CritRate // circlet main stat
);
// Roll substats
builder.roll(Stat.CritDMG, RollQuality.AVG, 5, 4);
const finalStats = builder.build();
```
Defines a sequence of damage operations for optimization.
```typescript
const rotation = createRotation();
// Add damage operations
rotationadd(
'normal_attack',
Element.Pyro,
DamageType.Normal,
BaseScaling.ATK,
Amplifier.None,
1.0, // instances
1.0 // motion value
);
rotationadd(
'skill_attack',
Element.Pyro,
DamageType.Skill,
BaseScaling.ATK,
Amplifier.None,
1.0,
2.5
);
// Evaluate total damage
const totalDamage = rotation.evaluate(characterStats);
```
```typescript
const damage = calculateDamage(
element: Element,
damageType: DamageType,
scaling: BaseScaling,
amplifier: Amplifier,
instances: number,
motionValue: number,
character: StatTableWrapper,
buffs?: StatTableWrapper
): number;
```
```typescript
// Optimize artifact main stats
const bestMainStats = await optimizeArtifactMainStats(
characterStats: StatTableWrapper,
target: Rotation
): Promise<number[]>; // [sands, goblet, circlet] stat IDs
// Optimize artifact substats
const substatDistribution = await optimizeArtifactSubstats(
characterStats: StatTableWrapper,
target: Rotation,
energyRechargeRequirements?: number,
flower?: ArtifactPiece,
feather?: ArtifactPiece,
sands?: ArtifactPiece,
goblet?: ArtifactPiece,
circlet?: ArtifactPiece
): Promise<Record<number, number>>; // stat ID -> roll count
// Calculate stat gradients
const gradients = await calculateStatGradients(
base: StatTableWrapper,
target: Rotation,
slopes: Record<number, number>
): Promise<Record<number, number>>;
// Find effective stats (ReLU heuristic)
const effectiveStats = await calculateReluHeuristic(
base: StatTableWrapper,
target: Rotation,
slopes: Record<number, number>
): Promise<number[]>;
```
```typescript
// Calculate total stats
const totalATK = calculateTotalAtkFromTable(stats);
const totalDEF = calculateTotalDefFromTable(stats);
const totalHP = calculateTotalHpFromTable(stats);
// Calculate multipliers
const critMultiplier = calculateAvgCritMultiplierFromTable(stats);
const defMultiplier = calculateDefMultiplier(charLevel, enemyLevel, defReduction, defIgnore);
```
```typescript
// Convert stat names
const stat = statFromString('atk'); // Returns Stat.ATKPercent
const name = getStatName(Stat.ATKPercent); // Returns 'ATKPercent'
// Check stat properties
const isElemental = isElementalDmgBonus(Stat.PyroDMGBonus); // true
```
```typescript
import { loadWasm, Stat, Element, DamageType, BaseScaling, Amplifier } from 'aminus-js';
async function calculateDilucDamage() {
const aminus = await loadWasm();
// Diluc base stats
const character = aminus.StatTableWrapper.fromArray([
[],
[],
[],
]);
// Wolf's Gravestone weapon
const weapon = aminus.StatTableWrapper.fromArray([
[],
[],
]);
// Artifact stats
const artifacts = aminus.StatTableWrapper.fromArray([
[],
[],
[],
[],
[],
]);
// Combine all stats
character.addTable(weapon);
character.addTable(artifacts);
// Calculate E skill damage
const skillDamage = aminus.calculateDamage(
Element.Pyro,
DamageType.Skill,
BaseScaling.ATK,
Amplifier.None,
1.0,
2.016, // Diluc E motion value
character
);
console.log(`Diluc E damage: ${skillDamage.toFixed(0)}`);
}
```
```typescript
import { loadWasm, Stat, RollQuality } from 'aminus-js';
async function optimizeArtifacts() {
const aminus = await loadWasm();
// Create KQM 5-star artifact set
const builder = aminus.ArtifactBuilderWrapper.kqmAll5Star(
Stat.ATKPercent, // sands
Stat.PyroDMGBonus, // goblet
Stat.CritRate // circlet
);
// Simulate rolling substats
builder.roll(Stat.CritDMG, RollQuality.HIGH, 5, 6);
builder.roll(Stat.ATKPercent, RollQuality.AVG, 5, 4);
builder.roll(Stat.FlatATK, RollQuality.LOW, 5, 2);
const artifacts = builder.build();
console.log('Final artifact stats:');
console.log(`ATK%: ${(artifacts.get(Stat.ATKPercent) * 100).toFixed(1)}%`);
console.log(`Crit Rate: ${(artifacts.get(Stat.CritRate) * 100).toFixed(1)}%`);
console.log(`Crit DMG: ${(artifacts.get(Stat.CritDMG) * 100).toFixed(1)}%`);
console.log(`Pyro DMG: ${(artifacts.get(Stat.PyroDMGBonus) * 100).toFixed(1)}%`);
}
```
For browser environments, you may need to handle WASM loading differently:
```html
<!DOCTYPE html>
<html>
<head>
<script type="module">
import { loadWasm, Stat } from './node_modules/aminus-js/pkg/aminus_js.js';
async function main() {
const aminus = await loadWasm();
const stats = new aminus.StatTableWrapper();
stats.add(Stat.ATKPercent, 0.5);
console.log('ATK%:', stats.get(Stat.ATKPercent));
}
main().catch(console.error);
</script>
</head>
<body>
<h1>Aminus Web Example</h1>
</body>
</html>
```
Run all tests:
```bash
npm test
```
Run tests in watch mode:
```bash
npm run test:watch
```
Test files are located in the `tests/` directory.
1. Make changes to the Rust source code in `src/`
2. Run `npm run build` to compile WASM
3. Add or update tests in `