Repository: genshinsim/gcsim
Pull request #160 (2022-01-12 02:35:17)

refactor char keys out of keys


Patch:
diff --git a/cmd/gcsim/.gitignore b/cmd/gcsim/.gitignore
index f885dfce6..cab7b6ef9 100644
--- a/cmd/gcsim/.gitignore
+++ b/cmd/gcsim/.gitignore
@@ -1,4 +1,9 @@
-gsim.exe
+*.exe
 gsim
 *.html
-tests
\ No newline at end of file
+tests
+*.gz
+*.txt
+gcsim
+*.json
+*.zip
\ No newline at end of file
diff --git a/internal/characters/albedo/albedo.go b/internal/characters/albedo/albedo.go
index c581420e6..08f48de93 100644
--- a/internal/characters/albedo/albedo.go
+++ b/internal/characters/albedo/albedo.go
@@ -5,11 +5,10 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Albedo, NewChar)
+	core.RegisterCharFunc(core.Albedo, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/aloy/aloy.go b/internal/characters/aloy/aloy.go
index 4449a963b..b9357b8d6 100644
--- a/internal/characters/aloy/aloy.go
+++ b/internal/characters/aloy/aloy.go
@@ -3,7 +3,6 @@ package aloy
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -13,7 +12,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.Aloy, NewChar)
+	core.RegisterCharFunc(core.Aloy, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/amber/amber.go b/internal/characters/amber/amber.go
index c2aec3336..a0e9f7c2b 100644
--- a/internal/characters/amber/amber.go
+++ b/internal/characters/amber/amber.go
@@ -3,11 +3,10 @@ package amber
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Amber, NewChar)
+	core.RegisterCharFunc(core.Amber, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/ayaka/ayaka.go b/internal/characters/ayaka/ayaka.go
index 8fda66dcb..8942cba89 100644
--- a/internal/characters/ayaka/ayaka.go
+++ b/internal/characters/ayaka/ayaka.go
@@ -3,7 +3,6 @@ package ayaka
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -11,7 +10,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.Ayaka, NewChar)
+	core.RegisterCharFunc(core.Ayaka, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/barbara/barbara.go b/internal/characters/barbara/barbara.go
index 307e7b6e1..da8a51c40 100644
--- a/internal/characters/barbara/barbara.go
+++ b/internal/characters/barbara/barbara.go
@@ -3,11 +3,10 @@ package barbara
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Barbara, NewChar)
+	core.RegisterCharFunc(core.Barbara, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/beidou/beidou.go b/internal/characters/beidou/beidou.go
index 3869f2357..76f0f516b 100644
--- a/internal/characters/beidou/beidou.go
+++ b/internal/characters/beidou/beidou.go
@@ -3,11 +3,10 @@ package beidou
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Beidou, NewChar)
+	core.RegisterCharFunc(core.Beidou, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/bennett/bennett.go b/internal/characters/bennett/bennett.go
index 517fea7e1..dba1e78e2 100644
--- a/internal/characters/bennett/bennett.go
+++ b/internal/characters/bennett/bennett.go
@@ -5,12 +5,11 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/player"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Bennett, NewChar)
+	core.RegisterCharFunc(core.Bennett, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/chongyun/chongyun.go b/internal/characters/chongyun/chongyun.go
index 98827af65..da2aa93cf 100644
--- a/internal/characters/chongyun/chongyun.go
+++ b/internal/characters/chongyun/chongyun.go
@@ -3,11 +3,10 @@ package chongyun
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Chongyun, NewChar)
+	core.RegisterCharFunc(core.Chongyun, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/diluc/diluc.go b/internal/characters/diluc/diluc.go
index 0f48f58dd..f8b6f863f 100644
--- a/internal/characters/diluc/diluc.go
+++ b/internal/characters/diluc/diluc.go
@@ -3,11 +3,10 @@ package diluc
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Diluc, NewChar)
+	core.RegisterCharFunc(core.Diluc, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/diona/diona.go b/internal/characters/diona/diona.go
index 83a6ec0f4..289a7be66 100644
--- a/internal/characters/diona/diona.go
+++ b/internal/characters/diona/diona.go
@@ -3,11 +3,10 @@ package diona
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Diona, NewChar)
+	core.RegisterCharFunc(core.Diona, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/eula/eula.go b/internal/characters/eula/eula.go
index d74b255d4..6753ccdd5 100644
--- a/internal/characters/eula/eula.go
+++ b/internal/characters/eula/eula.go
@@ -3,11 +3,10 @@ package eula
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Eula, NewChar)
+	core.RegisterCharFunc(core.Eula, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/fischl/fischl.go b/internal/characters/fischl/fischl.go
index a6b524897..019899a5e 100644
--- a/internal/characters/fischl/fischl.go
+++ b/internal/characters/fischl/fischl.go
@@ -5,11 +5,10 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Fischl, NewChar)
+	core.RegisterCharFunc(core.Fischl, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/ganyu/ganyu.go b/internal/characters/ganyu/ganyu.go
index fee7eb5e0..ecce1f97d 100644
--- a/internal/characters/ganyu/ganyu.go
+++ b/internal/characters/ganyu/ganyu.go
@@ -3,11 +3,10 @@ package ganyu
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Ganyu, NewChar)
+	core.RegisterCharFunc(core.Ganyu, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/gorou/gorou.go b/internal/characters/gorou/gorou.go
index a2ceed1f6..e2dbe2e63 100644
--- a/internal/characters/gorou/gorou.go
+++ b/internal/characters/gorou/gorou.go
@@ -3,11 +3,10 @@ package gorou
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Gorou, NewChar)
+	core.RegisterCharFunc(core.Gorou, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/hutao/hutao.go b/internal/characters/hutao/hutao.go
index 879d37354..cd1d36a39 100644
--- a/internal/characters/hutao/hutao.go
+++ b/internal/characters/hutao/hutao.go
@@ -3,11 +3,10 @@ package hutao
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Hutao, NewChar)
+	core.RegisterCharFunc(core.Hutao, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/jean/jean.go b/internal/characters/jean/jean.go
index 3197ae7e5..93b01eba8 100644
--- a/internal/characters/jean/jean.go
+++ b/internal/characters/jean/jean.go
@@ -5,11 +5,10 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Jean, NewChar)
+	core.RegisterCharFunc(core.Jean, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/kaeya/kaeya.go b/internal/characters/kaeya/kaeya.go
index 56cc39fc8..5d975ac53 100644
--- a/internal/characters/kaeya/kaeya.go
+++ b/internal/characters/kaeya/kaeya.go
@@ -5,11 +5,10 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Kaeya, NewChar)
+	core.RegisterCharFunc(core.Kaeya, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/kazuha/abil.go b/internal/characters/kazuha/abil.go
index 6ab565c95..d0dd11e04 100644
--- a/internal/characters/kazuha/abil.go
+++ b/internal/characters/kazuha/abil.go
@@ -2,7 +2,6 @@ package kazuha
 
 import (
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func (c *char) Attack(p map[string]int) (int, int) {
@@ -33,7 +32,7 @@ func (c *char) Attack(p map[string]int) (int, int) {
 func (c *char) HighPlungeAttack(p map[string]int) (int, int) {
 	f, a := c.ActionFrames(core.ActionHighPlunge, p)
 	ele := core.Physical
-	if c.Core.LastAction.Target == keys.Kazuha && c.Core.LastAction.Typ == core.ActionSkill {
+	if c.Core.LastAction.Target == core.Kazuha && c.Core.LastAction.Typ == core.ActionSkill {
 		ele = core.Anemo
 	}
 
diff --git a/internal/characters/kazuha/frames.go b/internal/characters/kazuha/frames.go
index ae202952e..88eb5eb1a 100644
--- a/internal/characters/kazuha/frames.go
+++ b/internal/characters/kazuha/frames.go
@@ -2,7 +2,6 @@ package kazuha
 
 import (
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
@@ -13,7 +12,7 @@ func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
 		//TODO: need to add atkspd mod
 		case 0:
 			//add frames if last action is also attack
-			if c.Core.LastAction.Target == keys.Kazuha && c.Core.LastAction.Typ == core.ActionAttack {
+			if c.Core.LastAction.Target == core.Kazuha && c.Core.LastAction.Typ == core.ActionAttack {
 				f += 60
 			}
 			f = 14
@@ -30,7 +29,7 @@ func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
 		return f, f
 	case core.ActionHighPlunge:
 		c.Core.Log.Debugw("plunge skill check", "event", core.LogCharacterEvent, "frame", c.Core.F, "previous", c.Core.LastAction)
-		if c.Core.LastAction.Target == keys.Kazuha && c.Core.LastAction.Typ == core.ActionSkill {
+		if c.Core.LastAction.Target == core.Kazuha && c.Core.LastAction.Typ == core.ActionSkill {
 			_, ok := c.Core.LastAction.Param["hold"]
 			if ok {
 				return 41, 41
diff --git a/internal/characters/kazuha/kazuha.go b/internal/characters/kazuha/kazuha.go
index 569b8a5a1..6d9b1dd6e 100644
--- a/internal/characters/kazuha/kazuha.go
+++ b/internal/characters/kazuha/kazuha.go
@@ -3,11 +3,10 @@ package kazuha
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Kazuha, NewChar)
+	core.RegisterCharFunc(core.Kazuha, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/keqing/keqing.go b/internal/characters/keqing/keqing.go
index 4f7b8b562..eaac57227 100644
--- a/internal/characters/keqing/keqing.go
+++ b/internal/characters/keqing/keqing.go
@@ -3,11 +3,10 @@ package keqing
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Keqing, NewChar)
+	core.RegisterCharFunc(core.Keqing, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/klee/klee.go b/internal/characters/klee/klee.go
index d252ae50d..376117711 100644
--- a/internal/characters/klee/klee.go
+++ b/internal/characters/klee/klee.go
@@ -3,11 +3,10 @@ package klee
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Klee, NewChar)
+	core.RegisterCharFunc(core.Klee, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/kokomi/kokomi.go b/internal/characters/kokomi/kokomi.go
index d0a1e1e74..de5191908 100644
--- a/internal/characters/kokomi/kokomi.go
+++ b/internal/characters/kokomi/kokomi.go
@@ -3,11 +3,10 @@ package kokomi
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Kokomi, NewChar)
+	core.RegisterCharFunc(core.Kokomi, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/lisa/lisa.go b/internal/characters/lisa/lisa.go
index b248fe9e1..24aeebe9f 100644
--- a/internal/characters/lisa/lisa.go
+++ b/internal/characters/lisa/lisa.go
@@ -3,11 +3,10 @@ package lisa
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Lisa, NewChar)
+	core.RegisterCharFunc(core.Lisa, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/mona/mona.go b/internal/characters/mona/mona.go
index 48b65c8b7..85c242e6d 100644
--- a/internal/characters/mona/mona.go
+++ b/internal/characters/mona/mona.go
@@ -3,11 +3,10 @@ package mona
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Mona, NewChar)
+	core.RegisterCharFunc(core.Mona, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/ningguang/ningguang.go b/internal/characters/ningguang/ningguang.go
index 2d7be3238..34659603f 100644
--- a/internal/characters/ningguang/ningguang.go
+++ b/internal/characters/ningguang/ningguang.go
@@ -3,11 +3,10 @@ package ningguang
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Ningguang, NewChar)
+	core.RegisterCharFunc(core.Ningguang, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/noelle/noelle.go b/internal/characters/noelle/noelle.go
index 48101bf0e..f71625b89 100644
--- a/internal/characters/noelle/noelle.go
+++ b/internal/characters/noelle/noelle.go
@@ -3,12 +3,11 @@ package noelle
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/shield"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Noelle, NewChar)
+	core.RegisterCharFunc(core.Noelle, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/qiqi/qiqi.go b/internal/characters/qiqi/qiqi.go
index 26f2bf498..337d11421 100644
--- a/internal/characters/qiqi/qiqi.go
+++ b/internal/characters/qiqi/qiqi.go
@@ -3,11 +3,10 @@ package qiqi
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Qiqi, NewChar)
+	core.RegisterCharFunc(core.Qiqi, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/raiden/frames.go b/internal/characters/raiden/frames.go
index 2552018de..24234d241 100644
--- a/internal/characters/raiden/frames.go
+++ b/internal/characters/raiden/frames.go
@@ -2,7 +2,6 @@ package raiden
 
 import (
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
@@ -13,7 +12,7 @@ func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
 			switch c.NormalCounter {
 			//TODO: need to add atkspd mod
 			case 0:
-				if c.Core.LastAction.Target == keys.Raiden && c.Core.LastAction.Typ == core.ActionAttack {
+				if c.Core.LastAction.Target == core.Raiden && c.Core.LastAction.Typ == core.ActionAttack {
 					f += 21
 				}
 				f = 14
@@ -31,7 +30,7 @@ func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
 			//TODO: need to add atkspd mod
 			case 0:
 				//add frames if last action is also attack
-				if c.Core.LastAction.Target == keys.Raiden && c.Core.LastAction.Typ == core.ActionAttack {
+				if c.Core.LastAction.Target == core.Raiden && c.Core.LastAction.Typ == core.ActionAttack {
 					f += 32
 				}
 				f = 12
diff --git a/internal/characters/raiden/raiden.go b/internal/characters/raiden/raiden.go
index d60e4a592..84cc085b4 100644
--- a/internal/characters/raiden/raiden.go
+++ b/internal/characters/raiden/raiden.go
@@ -3,7 +3,6 @@ package raiden
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -18,7 +17,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.Raiden, NewChar)
+	core.RegisterCharFunc(core.Raiden, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/rosaria/rosaria.go b/internal/characters/rosaria/rosaria.go
index c3a264e4e..9741b12cb 100644
--- a/internal/characters/rosaria/rosaria.go
+++ b/internal/characters/rosaria/rosaria.go
@@ -3,7 +3,6 @@ package rosaria
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -11,7 +10,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.Rosaria, NewChar)
+	core.RegisterCharFunc(core.Rosaria, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/sara/sara.go b/internal/characters/sara/sara.go
index e09cd1df3..bb3916937 100644
--- a/internal/characters/sara/sara.go
+++ b/internal/characters/sara/sara.go
@@ -3,11 +3,10 @@ package sara
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Sara, NewChar)
+	core.RegisterCharFunc(core.Sara, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/shenhe/shenhe.go b/internal/characters/shenhe/shenhe.go
index 54dc1d850..4c73aca4b 100644
--- a/internal/characters/shenhe/shenhe.go
+++ b/internal/characters/shenhe/shenhe.go
@@ -2,13 +2,13 @@ package shenhe
 
 import (
 	"fmt"
+
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Shenhe, NewChar)
+	core.RegisterCharFunc(core.Shenhe, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/sucrose/sucrose.go b/internal/characters/sucrose/sucrose.go
index 3eb97f376..dcf5896a6 100644
--- a/internal/characters/sucrose/sucrose.go
+++ b/internal/characters/sucrose/sucrose.go
@@ -3,11 +3,10 @@ package sucrose
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Sucrose, NewChar)
+	core.RegisterCharFunc(core.Sucrose, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/tartaglia/tartaglia.go b/internal/characters/tartaglia/tartaglia.go
index b83307ae7..729ccfd43 100644
--- a/internal/characters/tartaglia/tartaglia.go
+++ b/internal/characters/tartaglia/tartaglia.go
@@ -3,11 +3,10 @@ package tartaglia
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Tartaglia, NewChar)
+	core.RegisterCharFunc(core.Tartaglia, NewChar)
 }
 
 const riptideDuration = 18 * 60 // riptide duration lasts 18 sec
diff --git a/internal/characters/travelerelectro/travelerelectro.go b/internal/characters/travelerelectro/travelerelectro.go
index 4443a1c60..6adcf3f0d 100644
--- a/internal/characters/travelerelectro/travelerelectro.go
+++ b/internal/characters/travelerelectro/travelerelectro.go
@@ -3,7 +3,6 @@ package travelerelectro
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -11,7 +10,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.TravelerElectro, NewChar)
+	core.RegisterCharFunc(core.TravelerElectro, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/travelergeo/travelergeo.go b/internal/characters/travelergeo/travelergeo.go
index f5fef5515..39765b609 100644
--- a/internal/characters/travelergeo/travelergeo.go
+++ b/internal/characters/travelergeo/travelergeo.go
@@ -3,7 +3,6 @@ package travelergeo
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -11,7 +10,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.TravelerGeo, NewChar)
+	core.RegisterCharFunc(core.TravelerGeo, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/venti/venti.go b/internal/characters/venti/venti.go
index 6d7ef3284..12f393593 100644
--- a/internal/characters/venti/venti.go
+++ b/internal/characters/venti/venti.go
@@ -3,7 +3,6 @@ package venti
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -12,7 +11,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.Venti, NewChar)
+	core.RegisterCharFunc(core.Venti, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/xiangling/guoba.go b/internal/characters/xiangling/guoba.go
index 978f4d24a..6a5cba201 100644
--- a/internal/characters/xiangling/guoba.go
+++ b/internal/characters/xiangling/guoba.go
@@ -2,7 +2,6 @@ package xiangling
 
 import (
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/core/target"
 	"github.com/genshinsim/gcsim/pkg/reactable"
 )
@@ -25,7 +24,7 @@ func newGuoba(c *core.Core) *panda {
 
 func (p *panda) Attack(atk *core.AttackEvent) (float64, bool) {
 	//don't take damage, trigger swirl reaction only on sucrose E
-	if p.Core.Chars[atk.Info.ActorIndex].Key() != keys.Sucrose {
+	if p.Core.Chars[atk.Info.ActorIndex].Key() != core.Sucrose {
 		return 0, false
 	}
 	if atk.Info.AttackTag != core.AttackTagElementalArt {
diff --git a/internal/characters/xiangling/xiangling.go b/internal/characters/xiangling/xiangling.go
index c9e834725..b0cc70e43 100644
--- a/internal/characters/xiangling/xiangling.go
+++ b/internal/characters/xiangling/xiangling.go
@@ -5,11 +5,10 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Xiangling, NewChar)
+	core.RegisterCharFunc(core.Xiangling, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/xiao/abil.go b/internal/characters/xiao/abil.go
index 35b478c9a..bb97b4fff 100644
--- a/internal/characters/xiao/abil.go
+++ b/internal/characters/xiao/abil.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 // Normal attack damage queue generator
@@ -156,7 +155,7 @@ func (c *char) Skill(p map[string]int) (int, int) {
 	// According to KQM library, double E is only 60 frames long whereas single cast is 36
 	// No idea how this works, but add a special case to reduce the frames of the 2nd cast
 	// TODO: No data listed on how 3 casts work - this might be too few frames compared to actual 3x usage
-	if c.Core.LastAction.Target == keys.Xiao && c.Core.LastAction.Typ == core.ActionSkill {
+	if c.Core.LastAction.Target == core.Xiao && c.Core.LastAction.Typ == core.ActionSkill {
 		f = 60 - f
 		a = 60 - a
 	}
diff --git a/internal/characters/xiao/xiao.go b/internal/characters/xiao/xiao.go
index 0908aca00..83a44f5d7 100644
--- a/internal/characters/xiao/xiao.go
+++ b/internal/characters/xiao/xiao.go
@@ -3,11 +3,10 @@ package xiao
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Xiao, NewChar)
+	core.RegisterCharFunc(core.Xiao, NewChar)
 }
 
 // Xiao specific character implementation
diff --git a/internal/characters/xingqiu/xingqiu.go b/internal/characters/xingqiu/xingqiu.go
index ef66d1f54..712015d42 100644
--- a/internal/characters/xingqiu/xingqiu.go
+++ b/internal/characters/xingqiu/xingqiu.go
@@ -5,7 +5,6 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -20,7 +19,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.Xingqiu, NewChar)
+	core.RegisterCharFunc(core.Xingqiu, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/internal/characters/yanfei/yanfei.go b/internal/characters/yanfei/yanfei.go
index d7e41eb94..91a7f99b5 100644
--- a/internal/characters/yanfei/yanfei.go
+++ b/internal/characters/yanfei/yanfei.go
@@ -3,11 +3,10 @@ package yanfei
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Yanfei, NewChar)
+	core.RegisterCharFunc(core.Yanfei, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/yoimiya/yoimiya.go b/internal/characters/yoimiya/yoimiya.go
index 20ac34c39..5ae68cca0 100644
--- a/internal/characters/yoimiya/yoimiya.go
+++ b/internal/characters/yoimiya/yoimiya.go
@@ -3,11 +3,10 @@ package yoimiya
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Yoimiya, NewChar)
+	core.RegisterCharFunc(core.Yoimiya, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/yunjin/yunjin.go b/internal/characters/yunjin/yunjin.go
index 6b0086163..58053e739 100644
--- a/internal/characters/yunjin/yunjin.go
+++ b/internal/characters/yunjin/yunjin.go
@@ -2,13 +2,13 @@ package yunjin
 
 import (
 	"fmt"
+
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func init() {
-	core.RegisterCharFunc(keys.Yunjin, NewChar)
+	core.RegisterCharFunc(core.Yunjin, NewChar)
 }
 
 type char struct {
diff --git a/internal/characters/zhongli/zhongli.go b/internal/characters/zhongli/zhongli.go
index a81066f83..790820816 100644
--- a/internal/characters/zhongli/zhongli.go
+++ b/internal/characters/zhongli/zhongli.go
@@ -3,7 +3,6 @@ package zhongli
 import (
 	"github.com/genshinsim/gcsim/pkg/character"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type char struct {
@@ -14,7 +13,7 @@ type char struct {
 }
 
 func init() {
-	core.RegisterCharFunc(keys.Zhongli, NewChar)
+	core.RegisterCharFunc(core.Zhongli, NewChar)
 }
 
 func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
diff --git a/pkg/character/curve.go b/pkg/character/curve.go
index 7cec07155..1eaaf6221 100644
--- a/pkg/character/curve.go
+++ b/pkg/character/curve.go
@@ -5,15 +5,14 @@ import (
 
 	"github.com/genshinsim/gcsim/pkg/character/curves"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func (t *Tmpl) CalcBaseStats() error {
 	//calculate char base t.Stats
 	ck := t.Base.Key
 	//TODO: do something about traveler :(
-	if ck < keys.TravelerDelim {
-		ck = keys.TravelerMale
+	if ck < core.TravelerDelim {
+		ck = core.TravelerMale
 	}
 	b, ok := curves.CharBaseMap[ck]
 	if !ok {
diff --git a/pkg/character/curves/charactercurves.go b/pkg/character/curves/charactercurves.go
index 65813136a..905520958 100644
--- a/pkg/character/curves/charactercurves.go
+++ b/pkg/character/curves/charactercurves.go
@@ -2,11 +2,10 @@ package curves
 
 import (
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
-var CharBaseMap = map[keys.Char]CharBase{
-	keys.TravelerMale: {
+var CharBaseMap = map[core.CharKey]CharBase{
+	core.TravelerMale: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -66,7 +65,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Albedo: {
+	core.Albedo: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -126,7 +125,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Aloy: {
+	core.Aloy: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -186,7 +185,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Amber: {
+	core.Amber: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -246,7 +245,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Itto: {
+	core.Itto: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -306,7 +305,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Barbara: {
+	core.Barbara: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -366,7 +365,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Beidou: {
+	core.Beidou: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -426,7 +425,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Bennett: {
+	core.Bennett: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -486,7 +485,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Chongyun: {
+	core.Chongyun: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -546,7 +545,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Diluc: {
+	core.Diluc: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -606,7 +605,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Diona: {
+	core.Diona: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -666,7 +665,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Eula: {
+	core.Eula: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -726,7 +725,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Fischl: {
+	core.Fischl: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -786,7 +785,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Ganyu: {
+	core.Ganyu: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -846,7 +845,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Gorou: {
+	core.Gorou: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -906,7 +905,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Hutao: {
+	core.Hutao: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -966,7 +965,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Jean: {
+	core.Jean: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1026,7 +1025,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Kazuha: {
+	core.Kazuha: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1086,7 +1085,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Kaeya: {
+	core.Kaeya: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1146,7 +1145,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Ayaka: {
+	core.Ayaka: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1206,7 +1205,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Keqing: {
+	core.Keqing: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1266,7 +1265,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Klee: {
+	core.Klee: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1326,7 +1325,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Sara: {
+	core.Sara: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1386,7 +1385,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Lisa: {
+	core.Lisa: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1446,7 +1445,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.TravelerFemale: {
+	core.TravelerFemale: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1506,7 +1505,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Mona: {
+	core.Mona: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1566,7 +1565,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Ningguang: {
+	core.Ningguang: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1626,7 +1625,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Noelle: {
+	core.Noelle: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1686,7 +1685,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Qiqi: {
+	core.Qiqi: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1746,7 +1745,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Raiden: {
+	core.Raiden: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1806,7 +1805,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Razor: {
+	core.Razor: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1866,7 +1865,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Rosaria: {
+	core.Rosaria: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -1926,7 +1925,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Kokomi: {
+	core.Kokomi: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -1986,7 +1985,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Sayu: {
+	core.Sayu: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2046,7 +2045,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Shenhe: {
+	core.Shenhe: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -2106,7 +2105,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Sucrose: {
+	core.Sucrose: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2166,7 +2165,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Tartaglia: {
+	core.Tartaglia: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -2226,7 +2225,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Thoma: {
+	core.Thoma: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2286,7 +2285,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Venti: {
+	core.Venti: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -2346,7 +2345,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Xiangling: {
+	core.Xiangling: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2406,7 +2405,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Xiao: {
+	core.Xiao: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -2466,7 +2465,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Xingqiu: {
+	core.Xingqiu: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2526,7 +2525,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Xinyan: {
+	core.Xinyan: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2586,7 +2585,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Yanfei: {
+	core.Yanfei: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2646,7 +2645,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Yoimiya: {
+	core.Yoimiya: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
@@ -2706,7 +2705,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Yunjin: {
+	core.Yunjin: {
 		HPCurve:     GROW_CURVE_HP_S4,
 		AtkCurve:    GROW_CURVE_ATTACK_S4,
 		DefCurve:    GROW_CURVE_HP_S4,
@@ -2766,7 +2765,7 @@ var CharBaseMap = map[keys.Char]CharBase{
 			},
 		},
 	},
-	keys.Zhongli: {
+	core.Zhongli: {
 		HPCurve:     GROW_CURVE_HP_S5,
 		AtkCurve:    GROW_CURVE_ATTACK_S5,
 		DefCurve:    GROW_CURVE_HP_S5,
diff --git a/pkg/character/info.go b/pkg/character/info.go
index 18b8b01f3..4ed03ee6a 100644
--- a/pkg/character/info.go
+++ b/pkg/character/info.go
@@ -2,14 +2,13 @@ package character
 
 import (
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func (c *Tmpl) CharIndex() int {
 	return c.Index
 }
 
-func (c *Tmpl) Key() keys.Char {
+func (c *Tmpl) Key() core.CharKey {
 	return c.Base.Key
 }
 
diff --git a/pkg/character/tmpl.go b/pkg/character/tmpl.go
index 52ddf6195..160250b25 100644
--- a/pkg/character/tmpl.go
+++ b/pkg/character/tmpl.go
@@ -108,6 +108,10 @@ func (c *Tmpl) SetWeaponKey(k string) {
 	c.Weapon.Key = k
 }
 
+func (c *Tmpl) WeaponKey() string {
+	return c.Weapon.Key
+}
+
 func (c *Tmpl) AddWeaponInfuse(inf core.WeaponInfusion) {
 	c.Infusion = inf
 }
diff --git a/pkg/core/action.go b/pkg/core/action.go
index f426d8ace..71d14e1bd 100644
--- a/pkg/core/action.go
+++ b/pkg/core/action.go
@@ -3,8 +3,6 @@ package core
 import (
 	"errors"
 	"strings"
-
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type CommandHandler interface {
@@ -76,7 +74,7 @@ type ActionBlock struct {
 	Type  ActionBlockType
 	//sequence is only relevant to ActionBlockTypeSequence
 	Sequence     []ActionItem
-	SequenceChar keys.Char
+	SequenceChar CharKey
 
 	ChainSequences []ActionBlock
 
@@ -88,10 +86,10 @@ type ActionBlock struct {
 	Timeout    int           //the action block cannot be used again for x frames
 
 	//options
-	SwapTo            keys.Char //character to swap to after this block
-	SwapLock          int       //must stay on current char for x frames
-	Try               bool      //if true then drop rest of queue if any action is not ready
-	TryDropIfNotReady bool      //if false will keep trying next action; other wise drop sequence. Only if Try is set to true
+	SwapTo            CharKey //character to swap to after this block
+	SwapLock          int     //must stay on current char for x frames
+	Try               bool    //if true then drop rest of queue if any action is not ready
+	TryDropIfNotReady bool    //if false will keep trying next action; other wise drop sequence. Only if Try is set to true
 
 	//tracking
 	NumQueued  int //number of times this action block has been queued
@@ -105,7 +103,7 @@ type ActionBlock struct {
 type ActionItem struct {
 	Typ    ActionType
 	Param  map[string]int
-	Target keys.Char
+	Target CharKey
 }
 
 func (a *ActionItem) Type() CommandType { return CommandTypeAction }
@@ -289,7 +287,7 @@ func (a *ActionCtrl) checkMod(c Condition) bool {
 	//.<character>.modname
 	name := strings.TrimPrefix(c.Fields[0], ".")
 	m := strings.TrimPrefix(c.Fields[1], ".")
-	ck, ok := keys.CharNameToKey[name]
+	ck, ok := CharNameToKey[name]
 	if !ok {
 		a.core.Log.Debugw("invalid char for mod condition", "frame", a.core.F, "event", LogActionEvent, "character", name)
 		return false
diff --git a/pkg/core/action_old.go b/pkg/core/action_old.go
index 0ea633581..7277a213e 100644
--- a/pkg/core/action_old.go
+++ b/pkg/core/action_old.go
@@ -2,7 +2,7 @@ package core
 
 // type Action struct {
 // 	Name   string
-// 	Target keys.Char //either character or a sequence name
+// 	Target core.Char //either character or a sequence name
 
 // 	Exec     []ActionItem //if len > 1 then it's a sequence
 // 	IsSeq    bool         // is this a sequence
@@ -14,8 +14,8 @@ package core
 // 	Last       int //last time this was executed (frame)
 // 	ActionLock int //how many frames this action is locked from executing again
 
-// 	ActiveCond keys.Char
-// 	SwapTo     keys.Char
+// 	ActiveCond core.Char
+// 	SwapTo     core.Char
 // 	SwapLock   int
 // 	PostAction ActionType
 
diff --git a/pkg/core/cfg.go b/pkg/core/cfg.go
index e2d9451d8..8c8bfb543 100644
--- a/pkg/core/cfg.go
+++ b/pkg/core/cfg.go
@@ -1,13 +1,11 @@
 package core
 
-import "github.com/genshinsim/gcsim/pkg/core/keys"
-
 type Config struct {
 	Label      string
 	DamageMode bool
 	Targets    []EnemyProfile
 	Characters struct {
-		Initial keys.Char
+		Initial CharKey
 		Profile []CharacterProfile
 	}
 	Rotation []ActionBlock
@@ -28,50 +26,51 @@ type RunOpt struct {
 }
 
 type CharacterProfile struct {
-	Base      CharacterBase
-	Weapon    WeaponProfile
-	Talents   TalentProfile
-	Stats     []float64
-	Sets      map[string]int
-	SetParams map[string]map[string]int
-	Params    map[string]int
+	Base      CharacterBase             `json:"base"`
+	Weapon    WeaponProfile             `json:"weapon"`
+	Talents   TalentProfile             `json:"talents"`
+	Stats     []float64                 `json:"stats"`
+	Sets      map[string]int            `json:"sets"`
+	SetParams map[string]map[string]int `json:"-"`
+	Params    map[string]int            `json:"-"`
 }
 
 type CharacterBase struct {
-	Key      keys.Char
-	Element  EleType
-	Level    int
-	MaxLevel int
-	HP       float64
-	Atk      float64
-	Def      float64
-	Cons     int
-	StartHP  float64
+	Key      CharKey `json:"-"`
+	Name     string  `json:"name"`
+	Element  EleType `json:"element"`
+	Level    int     `json:"level"`
+	MaxLevel int     `json:"max_level"`
+	HP       float64 `json:"-"`
+	Atk      float64 `json:"-"`
+	Def      float64 `json:"-"`
+	Cons     int     `json:"cons"`
+	StartHP  float64 `json:"-"`
 }
 
 type WeaponProfile struct {
-	Name     string
-	Key      string //use this to match with weapon curve mapping
-	Class    WeaponClass
-	Refine   int
-	Level    int
-	MaxLevel int
-	Atk      float64
-	Params   map[string]int
+	Name     string         `json:"name"`
+	Key      string         `json:""` //use this to match with weapon curve mapping
+	Class    WeaponClass    `json:"-"`
+	Refine   int            `json:"refine"`
+	Level    int            `json:"level"`
+	MaxLevel int            `json:"max_level"`
+	Atk      float64        `json:"-"`
+	Params   map[string]int `json:"-"`
 }
 
 type TalentProfile struct {
-	Attack int
-	Skill  int
-	Burst  int
+	Attack int `json:"attack"`
+	Skill  int `json:"skill"`
+	Burst  int `json:"burst"`
 }
 
 type EnemyProfile struct {
-	Level          int
-	HP             float64
-	Resist         map[EleType]float64
-	Size           float64
-	CoordX, CoordY float64
+	Level          int                 `json:"level"`
+	HP             float64             `json:"-"`
+	Resist         map[EleType]float64 `json:"-"`
+	Size           float64             `json:"-"`
+	CoordX, CoordY float64             `json:"-"`
 }
 
 type EnergyEvent struct {
diff --git a/pkg/core/character.go b/pkg/core/character.go
index 6b23bf925..fc4ad7279 100644
--- a/pkg/core/character.go
+++ b/pkg/core/character.go
@@ -1,19 +1,18 @@
 package core
 
-import "github.com/genshinsim/gcsim/pkg/core/keys"
-
 type Character interface {
 	Init(index int) //to be called when everything including weapon and artifacts has been loaded
 	Tick()          //function to be called every frame
 
 	//information functions
-	Key() keys.Char
+	Key() CharKey
 	Name() string
 	CharIndex() int
 	Ele() EleType
 	Level() int
 	WeaponClass() WeaponClass
 	SetWeaponKey(k string)
+	WeaponKey() string
 	Zone() ZoneType
 	CurrentEnergy() float64 //current energy
 	MaxEnergy() float64
diff --git a/pkg/core/keys/keys.go b/pkg/core/charkey.go
similarity index 69%
rename from pkg/core/keys/keys.go
rename to pkg/core/charkey.go
index 244a05e04..416baf88d 100644
--- a/pkg/core/keys/keys.go
+++ b/pkg/core/charkey.go
@@ -1,9 +1,9 @@
-package keys
+package core
 
-type Char int
+type CharKey int
 
 const (
-	NoChar Char = iota
+	NoChar CharKey = iota
 	TravelerElectro
 	TravelerAnemo
 	TravelerGeo
@@ -61,11 +61,11 @@ const (
 	Yunjin
 )
 
-func (c Char) String() string {
+func (c CharKey) String() string {
 	return charNames[c]
 }
 
-var CharNameToKey = map[string]Char{
+var CharNameToKey = map[string]CharKey{
 	"travelerelectro":    TravelerElectro,
 	"traveleranemo":      TravelerAnemo,
 	"travelergeo":        TravelerGeo,
@@ -191,3 +191,58 @@ var charNames = []string{
 	"shenhe",
 	"yunjin",
 }
+
+var CharKeyToEle = map[CharKey]EleType{
+	TravelerElectro: Electro,
+	TravelerAnemo:   Anemo,
+	TravelerGeo:     Geo,
+	TravelerHydro:   Hydro,
+	TravelerCryo:    Cryo,
+	TravelerPyro:    Pyro,
+	TravelerDendro:  Dendro,
+	Albedo:          Geo,
+	Aloy:            Cryo,
+	Amber:           Pyro,
+	Barbara:         Hydro,
+	Beidou:          Electro,
+	Bennett:         Pyro,
+	Chongyun:        Cryo,
+	Diluc:           Pyro,
+	Diona:           Cryo,
+	Eula:            Cryo,
+	Fischl:          Electro,
+	Ganyu:           Cryo,
+	Hutao:           Pyro,
+	Jean:            Anemo,
+	Kazuha:          Anemo,
+	Kaeya:           Cryo,
+	Ayaka:           Cryo,
+	Keqing:          Electro,
+	Klee:            Pyro,
+	Sara:            Electro,
+	Lisa:            Electro,
+	Mona:            Hydro,
+	Ningguang:       Geo,
+	Noelle:          Geo,
+	Qiqi:            Cryo,
+	Raiden:          Electro,
+	Razor:           Electro,
+	Rosaria:         Cryo,
+	Kokomi:          Hydro,
+	Sayu:            Anemo,
+	Sucrose:         Anemo,
+	Tartaglia:       Hydro,
+	Thoma:           Pyro,
+	Venti:           Anemo,
+	Xiangling:       Pyro,
+	Xiao:            Anemo,
+	Xingqiu:         Hydro,
+	Xinyan:          Pyro,
+	Yanfei:          Pyro,
+	Yoimiya:         Pyro,
+	Zhongli:         Geo,
+	Gorou:           Geo,
+	Itto:            Geo,
+	Shenhe:          Cryo,
+	Yunjin:          Geo,
+}
diff --git a/pkg/core/core.go b/pkg/core/core.go
index 41c70ac23..c41015a6e 100644
--- a/pkg/core/core.go
+++ b/pkg/core/core.go
@@ -6,7 +6,6 @@ import (
 	"math/rand"
 	"time"
 
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"go.uber.org/zap"
 	"go.uber.org/zap/zapcore"
 )
@@ -51,10 +50,10 @@ type Core struct {
 	lastStamUse  int
 
 	//track characters
-	ActiveChar     int               // index of currently active char
-	ActiveDuration int               // duration in frames that the current char has been on field for
-	Chars          []Character       // array holding all the characters on the team
-	CharPos        map[keys.Char]int // map of character string name to their index (for quick lookup by name)
+	ActiveChar     int             // index of currently active char
+	ActiveDuration int             // duration in frames that the current char has been on field for
+	Chars          []Character     // array holding all the characters on the team
+	CharPos        map[CharKey]int // map of character string name to their index (for quick lookup by name)
 
 	//track targets
 	Targets     []Target
@@ -84,7 +83,7 @@ func New(cfg ...func(*Core) error) (*Core, error) {
 	var err error
 	c := &Core{}
 
-	c.CharPos = make(map[keys.Char]int)
+	c.CharPos = make(map[CharKey]int)
 	c.Flags.Custom = make(map[string]int)
 	c.Stam = MaxStam
 	c.stamModifier = make([]stamMod, 0, 10)
@@ -171,7 +170,9 @@ func (c *Core) AddChar(v CharacterProfile) (Character, error) {
 	char.SetWeaponKey(wk)
 
 	//add set bonus
+	total := 0
 	for key, count := range v.Sets {
+		total += count
 		f, ok := setMap[key]
 		if ok {
 			f(char, c, count, v.SetParams[key])
@@ -179,13 +180,16 @@ func (c *Core) AddChar(v CharacterProfile) (Character, error) {
 			c.Log.Warnw(fmt.Sprintf("character %v has unrecognized set %v", v.Base.Key, key), "frame", -1, "event", LogArtifactEvent)
 		}
 	}
+	if total > 5 {
+		return nil, fmt.Errorf("total set count cannot exceed 5, got %v", total)
+	}
 
 	err = char.CalcBaseStats()
 
 	return char, err
 }
 
-func (c *Core) CharByName(key keys.Char) (Character, bool) {
+func (c *Core) CharByName(key CharKey) (Character, bool) {
 	pos, ok := c.CharPos[key]
 	if !ok {
 		return nil, false
@@ -193,7 +197,7 @@ func (c *Core) CharByName(key keys.Char) (Character, bool) {
 	return c.Chars[pos], true
 }
 
-func (c *Core) Swap(next keys.Char) int {
+func (c *Core) Swap(next CharKey) int {
 	prev := c.ActiveChar
 	c.ActiveChar = c.CharPos[next]
 	c.SwapCD = SwapCDFrames
diff --git a/pkg/core/queue.go b/pkg/core/queue.go
index 42f742606..5d577df6e 100644
--- a/pkg/core/queue.go
+++ b/pkg/core/queue.go
@@ -5,8 +5,6 @@ import (
 	"fmt"
 	"strconv"
 	"strings"
-
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type QueueHandler interface {
@@ -156,7 +154,7 @@ func (q *Queuer) createQueueFromChain(a ActionBlock) []Command {
 	}
 
 	//if swapto, add to end of sequence
-	if a.SwapTo > keys.NoChar {
+	if a.SwapTo > NoChar {
 		res = append(res, &ActionItem{
 			Typ:    ActionSwap,
 			Target: a.SwapTo,
@@ -190,7 +188,7 @@ func (q *Queuer) createQueueFromSequence(a ActionBlock) []Command {
 	}
 
 	//if swapto, add to end of sequence
-	if a.SwapTo > keys.NoChar {
+	if a.SwapTo > NoChar {
 		res = append(res, &ActionItem{
 			Typ:    ActionSwap,
 			Target: a.SwapTo,
@@ -493,7 +491,7 @@ func (q *Queuer) evalAbilReady(cond Condition) (bool, error) {
 		return false, errors.New("eval abil: unexpected short field, expected at least 3")
 	}
 	cs := strings.TrimPrefix(cond.Fields[2], ".")
-	key := keys.CharNameToKey[cs]
+	key := CharNameToKey[cs]
 	char, ok := q.core.CharByName(key)
 	if !ok {
 		return false, nil
@@ -601,7 +599,7 @@ func (q *Queuer) evalCD(cond Condition) (bool, error) {
 	}
 	//check target is valid
 	name := strings.TrimPrefix(cond.Fields[1], ".")
-	key := keys.CharNameToKey[name]
+	key := CharNameToKey[name]
 	char, ok := q.core.CharByName(key)
 	if !ok {
 		return false, errors.New("eval cd: invalid char in condition")
@@ -624,7 +622,7 @@ func (q *Queuer) evalEnergy(cond Condition) (bool, error) {
 		return false, errors.New("eval energy: unexpected short field, expected at least 2")
 	}
 	name := strings.TrimPrefix(cond.Fields[1], ".")
-	key := keys.CharNameToKey[name]
+	key := CharNameToKey[name]
 	char, ok := q.core.CharByName(key)
 	if !ok {
 		return false, errors.New("eval energy: invalid char in condition")
@@ -649,7 +647,7 @@ func (q *Queuer) evalTags(cond Condition) (bool, error) {
 		return false, errors.New("eval tags: unexpected short field, expected at least 3")
 	}
 	name := strings.TrimPrefix(cond.Fields[1], ".")
-	key := keys.CharNameToKey[name]
+	key := CharNameToKey[name]
 	char, ok := q.core.CharByName(key)
 	if !ok {
 		return false, errors.New("eval tags: invalid char in condition")
@@ -666,7 +664,7 @@ func (q *Queuer) evalCharMods(cond Condition) (bool, error) {
 		return false, errors.New("eval tags: unexpected short field, expected at least 3")
 	}
 	name := strings.TrimPrefix(cond.Fields[1], ".")
-	key := keys.CharNameToKey[name]
+	key := CharNameToKey[name]
 	char, ok := q.core.CharByName(key)
 	if !ok {
 		return false, errors.New("eval tags: invalid char in condition")
diff --git a/pkg/core/queue_old.go b/pkg/core/queue_old.go
index e8944ac4d..36de1bdd5 100644
--- a/pkg/core/queue_old.go
+++ b/pkg/core/queue_old.go
@@ -270,7 +270,7 @@ package core
 // 		return false, errors.New("eval abil: unexpected short field, expected at least 3")
 // 	}
 // 	cs := strings.TrimPrefix(c.Fields[2], ".")
-// 	key := keys.CharNameToKey[cs]
+// 	key := core.CharNameToKey[cs]
 // 	char, ok := q.core.CharByName(key)
 // 	if !ok {
 // 		return false, nil
@@ -361,7 +361,7 @@ package core
 // 	}
 // 	//check target is valid
 // 	name := strings.TrimPrefix(c.Fields[1], ".")
-// 	key := keys.CharNameToKey[name]
+// 	key := core.CharNameToKey[name]
 // 	char, ok := q.core.CharByName(key)
 // 	if !ok {
 // 		return false, errors.New("eval cd: invalid char in condition")
@@ -384,7 +384,7 @@ package core
 // 		return false, errors.New("eval energy: unexpected short field, expected at least 2")
 // 	}
 // 	name := strings.TrimPrefix(c.Fields[1], ".")
-// 	key := keys.CharNameToKey[name]
+// 	key := core.CharNameToKey[name]
 // 	char, ok := q.core.CharByName(key)
 // 	if !ok {
 // 		return false, errors.New("eval energy: invalid char in condition")
@@ -409,7 +409,7 @@ package core
 // 		return false, errors.New("eval tags: unexpected short field, expected at least 3")
 // 	}
 // 	name := strings.TrimPrefix(c.Fields[1], ".")
-// 	key := keys.CharNameToKey[name]
+// 	key := core.CharNameToKey[name]
 // 	char, ok := q.core.CharByName(key)
 // 	if !ok {
 // 		return false, errors.New("eval tags: invalid char in condition")
diff --git a/pkg/core/register.go b/pkg/core/register.go
index 2ab52c3b0..ec3d6fdb5 100644
--- a/pkg/core/register.go
+++ b/pkg/core/register.go
@@ -2,13 +2,11 @@ package core
 
 import (
 	"sync"
-
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 var (
 	mu        sync.RWMutex
-	charMap   = make(map[keys.Char]NewCharacterFunc)
+	charMap   = make(map[CharKey]NewCharacterFunc)
 	setMap    = make(map[string]NewSetFunc)
 	weaponMap = make(map[string]NewWeaponFunc)
 )
@@ -17,7 +15,7 @@ type NewCharacterFunc func(core *Core, p CharacterProfile) (Character, error)
 type NewSetFunc func(c Character, core *Core, count int, param map[string]int)
 type NewWeaponFunc func(c Character, core *Core, r int, param map[string]int) string
 
-func RegisterCharFunc(char keys.Char, f NewCharacterFunc) {
+func RegisterCharFunc(char CharKey, f NewCharacterFunc) {
 	mu.Lock()
 	defer mu.Unlock()
 	if _, dup := charMap[char]; dup {
diff --git a/pkg/parse/lex.go b/pkg/parse/lex.go
index ceb4bb759..379401afa 100644
--- a/pkg/parse/lex.go
+++ b/pkg/parse/lex.go
@@ -6,7 +6,7 @@ import (
 	"unicode"
 	"unicode/utf8"
 
-	"github.com/genshinsim/gcsim/pkg/core/keys"
+	"github.com/genshinsim/gcsim/pkg/core"
 )
 
 type Pos int
@@ -365,7 +365,7 @@ func checkIdentifier(word string) ItemType {
 	if _, ok := eleKeys[word]; ok {
 		return itemElementKey
 	}
-	if _, ok := keys.CharNameToKey[word]; ok {
+	if _, ok := core.CharNameToKey[word]; ok {
 		return itemCharacterKey
 	}
 	if _, ok := actionKeys[word]; ok {
diff --git a/pkg/parse/parse.go b/pkg/parse/parse.go
index 409edf5dc..0830fae55 100644
--- a/pkg/parse/parse.go
+++ b/pkg/parse/parse.go
@@ -8,19 +8,18 @@ import (
 	"strconv"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 type Parser struct {
 	l              *lexer
 	tokens         []item
-	pos            int       //current position
-	currentCharKey keys.Char //current character being parsed
+	pos            int          //current position
+	currentCharKey core.CharKey //current character being parsed
 
 	//results
 	cfg    *core.Config
 	opt    *core.RunOpt
-	chars  map[keys.Char]*core.CharacterProfile
+	chars  map[core.CharKey]*core.CharacterProfile
 	macros map[string]core.ActionBlock
 }
 
@@ -38,7 +37,7 @@ func (p *Parser) Parse() (core.Config, core.RunOpt, error) {
 	var err error
 
 	p.cfg = &core.Config{}
-	p.chars = make(map[keys.Char]*core.CharacterProfile)
+	p.chars = make(map[core.CharKey]*core.CharacterProfile)
 	p.macros = make(map[string]core.ActionBlock)
 	p.opt = &core.RunOpt{}
 
@@ -65,7 +64,7 @@ func (p *Parser) Parse() (core.Config, core.RunOpt, error) {
 	}
 	sort.Strings(sk)
 	for _, v := range sk {
-		k := keys.CharNameToKey[v]
+		k := core.CharNameToKey[v]
 		p.cfg.Characters.Profile = append(p.cfg.Characters.Profile, *p.chars[k])
 	}
 
diff --git a/pkg/parse/parseActive.go b/pkg/parse/parseActive.go
index e6490d63c..58ceba8f3 100644
--- a/pkg/parse/parseActive.go
+++ b/pkg/parse/parseActive.go
@@ -3,7 +3,7 @@ package parse
 import (
 	"fmt"
 
-	"github.com/genshinsim/gcsim/pkg/core/keys"
+	"github.com/genshinsim/gcsim/pkg/core"
 )
 
 func parseActiveChar(p *Parser) (parseFn, error) {
@@ -11,7 +11,7 @@ func parseActiveChar(p *Parser) (parseFn, error) {
 	if n.typ != itemCharacterKey {
 		return nil, fmt.Errorf("active char expecting char key got %v at line %v", n, p.tokens)
 	}
-	p.cfg.Characters.Initial = keys.CharNameToKey[n.val]
+	p.cfg.Characters.Initial = core.CharNameToKey[n.val]
 	n = p.next()
 	if n.typ != itemTerminateLine {
 		return nil, fmt.Errorf("active char expecting ; got %v at line %v", n, p.tokens)
diff --git a/pkg/parse/parseCharAction.go b/pkg/parse/parseCharAction.go
index b61498043..2cd6b71e3 100644
--- a/pkg/parse/parseCharAction.go
+++ b/pkg/parse/parseCharAction.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func parseCharActions(p *Parser) (parseFn, error) {
@@ -87,7 +86,7 @@ func (p *Parser) acceptActionBlockFlags(a *core.ActionBlock) error {
 		a.Conditions, err = p.parseIf()
 	case itemSwap:
 		x, err = p.acceptSeqReturnLast(itemEqual, itemCharacterKey)
-		key, ok := keys.CharNameToKey[x.val]
+		key, ok := core.CharNameToKey[x.val]
 		if !ok {
 			err = fmt.Errorf("bad token at line %v - %v: %v; invalid char name; line: %v", x.line, x.pos, x, p.tokens)
 		}
diff --git a/pkg/parse/parseCharLine.go b/pkg/parse/parseCharLine.go
index a3f358144..819e63017 100644
--- a/pkg/parse/parseCharLine.go
+++ b/pkg/parse/parseCharLine.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func parseChar(p *Parser) (parseFn, error) {
@@ -23,7 +22,7 @@ func parseChar(p *Parser) (parseFn, error) {
 	}
 }
 
-func (p *Parser) newChar(key keys.Char) {
+func (p *Parser) newChar(key core.CharKey) {
 	r := core.CharacterProfile{}
 	r.Base.Key = key
 	r.Stats = make([]float64, len(core.StatTypeString))
diff --git a/pkg/parse/parseMacro.go b/pkg/parse/parseMacro.go
index 76f7de808..17ef308a5 100644
--- a/pkg/parse/parseMacro.go
+++ b/pkg/parse/parseMacro.go
@@ -4,7 +4,6 @@ import (
 	"fmt"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func parseMacro(p *Parser) (parseFn, error) {
@@ -17,7 +16,7 @@ func parseMacro(p *Parser) (parseFn, error) {
 	switch n.typ {
 	case itemCharacterKey:
 		//lex should have checked this already
-		key, ok := keys.CharNameToKey[n.val]
+		key, ok := core.CharNameToKey[n.val]
 		if !ok {
 			return nil, fmt.Errorf("unexpected error, should be a recognized character key: %v", n)
 		}
diff --git a/pkg/parse/parseRows.go b/pkg/parse/parseRows.go
index a290d68dd..0dd824272 100644
--- a/pkg/parse/parseRows.go
+++ b/pkg/parse/parseRows.go
@@ -4,7 +4,7 @@ import (
 	"errors"
 	"fmt"
 
-	"github.com/genshinsim/gcsim/pkg/core/keys"
+	"github.com/genshinsim/gcsim/pkg/core"
 )
 
 func parseRows(p *Parser) (parseFn, error) {
@@ -53,7 +53,7 @@ func parseRows(p *Parser) (parseFn, error) {
 		//abilities
 
 		//lex should have checked this already
-		key, ok := keys.CharNameToKey[n.val]
+		key, ok := core.CharNameToKey[n.val]
 		if !ok {
 			return nil, fmt.Errorf("unexpected error, should be a recognized character key: %v", n)
 		}
diff --git a/pkg/parse/parseWait.go b/pkg/parse/parseWait.go
index 2d5e82c86..84fb66930 100644
--- a/pkg/parse/parseWait.go
+++ b/pkg/parse/parseWait.go
@@ -5,7 +5,6 @@ import (
 	"fmt"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func parseCalcModeWait(p *Parser) (parseFn, error) {
@@ -153,7 +152,7 @@ func (p *Parser) acceptWait() (core.ActionBlock, error) {
 				//expecting a
 				act := core.ActionItem{
 					Typ:    actionKeys[n.val],
-					Target: keys.NoChar, //since it's active char only
+					Target: core.NoChar, //since it's active char only
 				}
 				//optional params
 				act.Param, err = p.acceptOptionalParamReturnMap()
diff --git a/scripts/charstat/main.go b/scripts/charstat/main.go
index d6c96bf90..0f914e542 100644
--- a/scripts/charstat/main.go
+++ b/scripts/charstat/main.go
@@ -77,9 +77,9 @@ import (
 )
 
 
-var CharBaseMap = map[keys.Char]CharBase{
+var CharBaseMap = map[core.Char]CharBase{
 	{{- range $key, $value := . }}
-	keys.{{$value.Key}}: {
+	core.{{$value.Key}}: {
 		HPCurve: {{$value.Curve.HP}},
 		AtkCurve: {{$value.Curve.Atk}},
 		DefCurve: {{$value.Curve.Def}},
diff --git a/sim.go b/sim.go
index 9f6249d3d..700ce3bdf 100644
--- a/sim.go
+++ b/sim.go
@@ -7,7 +7,6 @@ import (
 	"strings"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 )
@@ -69,145 +68,8 @@ func NewSim(cfg core.Config, seed int64, opts core.RunOpt, cust ...func(*Simulat
 	}
 	s.stats.IsDamageMode = cfg.DamageMode
 
-	var sb strings.Builder
-
 	if s.opts.LogDetails {
-		s.stats.ReactionsTriggered = make(map[core.ReactionType]int)
-		//add new targets
-		s.C.Events.Subscribe(core.OnTargetAdded, func(args ...interface{}) bool {
-			t := args[0].(core.Target)
-
-			s.C.Log.Debugw("Target Added", "frame", s.C.F, "event", core.LogSimEvent, "target_type", t.Type())
-
-			s.stats.ElementUptime = append(s.stats.ElementUptime, make(map[core.EleType]int))
-
-			return false
-		}, "sim-new-target-stats")
-		//add call backs to track details
-		s.C.Events.Subscribe(core.OnDamage, func(args ...interface{}) bool {
-			t := args[0].(core.Target)
-
-			// No need to pull damage stats for non-enemies
-			if t.Type() != core.TargettableEnemy {
-				return false
-			}
-			atk := args[1].(*core.AttackEvent)
-
-			//skip if do not log
-			if atk.Info.DoNotLog {
-				return false
-			}
-
-			dmg := args[2].(float64)
-			sb.Reset()
-			sb.WriteString(atk.Info.Abil)
-			if atk.Info.Amped {
-				if atk.Info.AmpMult == 1.5 {
-					sb.WriteString(" [amp: 1.5]")
-				} else if atk.Info.AmpMult == 2 {
-					sb.WriteString(" [amp: 2.0]")
-				}
-			}
-			s.stats.DamageByChar[atk.Info.ActorIndex][sb.String()] += dmg
-			if dmg > 0 {
-				s.stats.DamageInstancesByChar[atk.Info.ActorIndex][sb.String()] += 1
-			}
-			s.stats.DamageByCharByTargets[atk.Info.ActorIndex][t.Index()] += dmg
-
-			// Want to capture information in 0.25s intervals - allows more flexibility in bucketizing
-			frameBucket := int(s.C.F/15) * 15
-			details := DamageDetails{
-				FrameBucket: frameBucket,
-				Char:        atk.Info.ActorIndex,
-				Target:      t.Index(),
-			}
-			// Go defaults to 0 for map values that don't exist
-			s.stats.DamageDetailByTime[details] += dmg
-			return false
-		}, "dmg-log")
-
-		s.C.Events.Subscribe(core.OnOverload, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.Overload]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnSuperconduct, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.Superconduct]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnMelt, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.Melt]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnVaporize, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.Vaporize]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnFrozen, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.Freeze]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnElectroCharged, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.ElectroCharged]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnSwirlHydro, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.SwirlHydro]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnSwirlCryo, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.SwirlCryo]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnSwirlElectro, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.SwirlElectro]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnSwirlPyro, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.SwirlPyro]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnCrystallizeCryo, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.CrystallizeCryo]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnCrystallizeElectro, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.CrystallizeElectro]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnCrystallizeHydro, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.CrystallizeHydro]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnCrystallizePyro, func(args ...interface{}) bool {
-			s.stats.ReactionsTriggered[core.CrystallizePyro]++
-			return false
-		}, "reaction-log")
-
-		s.C.Events.Subscribe(core.OnParticleReceived, func(args ...interface{}) bool {
-			p := args[0].(core.Particle)
-			s.stats.ParticleCount[p.Source] += p.Num
-			return false
-		}, "particles-log")
-
-		s.C.Events.Subscribe(core.PreBurst, func(args ...interface{}) bool {
-			activeChar := s.C.Chars[s.C.ActiveChar]
-			s.stats.EnergyWhenBurst[s.C.ActiveChar] = append(s.stats.EnergyWhenBurst[s.C.ActiveChar], activeChar.CurrentEnergy())
-			return false
-		}, "energy-calc-log")
-
+		s.initDetailLog()
 	}
 
 	err = s.initQueuer(cfg)
@@ -226,6 +88,19 @@ func NewSim(cfg core.Config, seed int64, opts core.RunOpt, cust ...func(*Simulat
 
 	c.Init()
 
+	if s.opts.LogDetails {
+		//grab a snapshot for each char
+		for i, c := range s.C.Chars {
+			stats := c.Snapshot(&core.AttackInfo{
+				Abil:      "stats-check",
+				AttackTag: core.AttackTagNone,
+			})
+			s.stats.CharDetails[i].SnapshotStats = stats.Stats[:]
+			s.stats.CharDetails[i].Element = c.Ele().String()
+			s.stats.CharDetails[i].Weapon.Name = c.WeaponKey()
+		}
+	}
+
 	// log.Println(s.cfg.Energy)
 
 	return s, nil
@@ -308,7 +183,7 @@ func (s *Simulation) initTargets(cfg core.Config) error {
 }
 
 func (s *Simulation) initChars(cfg core.Config) error {
-	dup := make(map[keys.Char]bool)
+	dup := make(map[core.CharKey]bool)
 	res := make(map[core.EleType]int)
 
 	count := len(cfg.Characters.Profile)
@@ -319,6 +194,7 @@ func (s *Simulation) initChars(cfg core.Config) error {
 
 	if s.opts.LogDetails {
 		s.stats.CharNames = make([]string, count)
+		s.stats.CharDetails = make([]CharDetail, 0, count)
 		s.stats.DamageByChar = make([]map[string]float64, count)
 		s.stats.DamageInstancesByChar = make([]map[string]int, count)
 		s.stats.DamageByCharByTargets = make([]map[int]float64, count)
@@ -357,6 +233,26 @@ func (s *Simulation) initChars(cfg core.Config) error {
 			s.stats.AbilUsageCountByChar[i] = make(map[string]int)
 			s.stats.CharNames[i] = v.Base.Key.String()
 			s.stats.EnergyWhenBurst[i] = make([]float64, 0, s.opts.Duration/12+2)
+
+			//log the character data
+			s.stats.CharDetails = append(s.stats.CharDetails, CharDetail{
+				Name:     v.Base.Key.String(),
+				Level:    v.Base.Level,
+				MaxLevel: v.Base.Level,
+				Cons:     v.Base.Cons,
+				Weapon: WeaponDetail{
+					Refine:   v.Weapon.Refine,
+					Level:    v.Weapon.Level,
+					MaxLevel: v.Weapon.MaxLevel,
+				},
+				Talents: TalentDetail{
+					Attack: v.Talents.Attack,
+					Skill:  v.Talents.Skill,
+					Burst:  v.Talents.Burst,
+				},
+				Sets: v.Sets,
+			})
+
 		}
 
 	}
@@ -395,3 +291,143 @@ func (s *Simulation) initQueuer(cfg core.Config) error {
 	err := s.C.Queue.SetActionList(cfg.Rotation)
 	return err
 }
+
+func (s *Simulation) initDetailLog() {
+	var sb strings.Builder
+	s.stats.ReactionsTriggered = make(map[core.ReactionType]int)
+	//add new targets
+	s.C.Events.Subscribe(core.OnTargetAdded, func(args ...interface{}) bool {
+		t := args[0].(core.Target)
+
+		s.C.Log.Debugw("Target Added", "frame", s.C.F, "event", core.LogSimEvent, "target_type", t.Type())
+
+		s.stats.ElementUptime = append(s.stats.ElementUptime, make(map[core.EleType]int))
+
+		return false
+	}, "sim-new-target-stats")
+	//add call backs to track details
+	s.C.Events.Subscribe(core.OnDamage, func(args ...interface{}) bool {
+		t := args[0].(core.Target)
+
+		// No need to pull damage stats for non-enemies
+		if t.Type() != core.TargettableEnemy {
+			return false
+		}
+		atk := args[1].(*core.AttackEvent)
+
+		//skip if do not log
+		if atk.Info.DoNotLog {
+			return false
+		}
+
+		dmg := args[2].(float64)
+		sb.Reset()
+		sb.WriteString(atk.Info.Abil)
+		if atk.Info.Amped {
+			if atk.Info.AmpMult == 1.5 {
+				sb.WriteString(" [amp: 1.5]")
+			} else if atk.Info.AmpMult == 2 {
+				sb.WriteString(" [amp: 2.0]")
+			}
+		}
+		s.stats.DamageByChar[atk.Info.ActorIndex][sb.String()] += dmg
+		if dmg > 0 {
+			s.stats.DamageInstancesByChar[atk.Info.ActorIndex][sb.String()] += 1
+		}
+		s.stats.DamageByCharByTargets[atk.Info.ActorIndex][t.Index()] += dmg
+
+		// Want to capture information in 0.25s intervals - allows more flexibility in bucketizing
+		frameBucket := int(s.C.F/15) * 15
+		details := DamageDetails{
+			FrameBucket: frameBucket,
+			Char:        atk.Info.ActorIndex,
+			Target:      t.Index(),
+		}
+		// Go defaults to 0 for map values that don't exist
+		s.stats.DamageDetailByTime[details] += dmg
+		return false
+	}, "dmg-log")
+
+	s.C.Events.Subscribe(core.OnOverload, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.Overload]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnSuperconduct, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.Superconduct]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnMelt, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.Melt]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnVaporize, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.Vaporize]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnFrozen, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.Freeze]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnElectroCharged, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.ElectroCharged]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnSwirlHydro, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.SwirlHydro]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnSwirlCryo, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.SwirlCryo]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnSwirlElectro, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.SwirlElectro]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnSwirlPyro, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.SwirlPyro]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnCrystallizeCryo, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.CrystallizeCryo]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnCrystallizeElectro, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.CrystallizeElectro]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnCrystallizeHydro, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.CrystallizeHydro]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnCrystallizePyro, func(args ...interface{}) bool {
+		s.stats.ReactionsTriggered[core.CrystallizePyro]++
+		return false
+	}, "reaction-log")
+
+	s.C.Events.Subscribe(core.OnParticleReceived, func(args ...interface{}) bool {
+		p := args[0].(core.Particle)
+		s.stats.ParticleCount[p.Source] += p.Num
+		return false
+	}, "particles-log")
+
+	s.C.Events.Subscribe(core.PreBurst, func(args ...interface{}) bool {
+		activeChar := s.C.Chars[s.C.ActiveChar]
+		s.stats.EnergyWhenBurst[s.C.ActiveChar] = append(s.stats.EnergyWhenBurst[s.C.ActiveChar], activeChar.CurrentEnergy())
+		return false
+	}, "energy-calc-log")
+
+}
diff --git a/worker.go b/worker.go
index 87a6c27eb..fc8f115a4 100644
--- a/worker.go
+++ b/worker.go
@@ -23,6 +23,7 @@ import (
 type Stats struct {
 	IsDamageMode          bool                      `json:"is_damage_mode"`
 	CharNames             []string                  `json:"char_names"`
+	CharDetails           []CharDetail              `json:"char_details"`
 	DamageByChar          []map[string]float64      `json:"damage_by_char"`
 	DamageInstancesByChar []map[string]int          `json:"damage_instances_by_char"`
 	DamageByCharByTargets []map[int]float64         `json:"damage_by_char_by_targets"`
@@ -41,6 +42,32 @@ type Stats struct {
 	seed int64
 }
 
+type CharDetail struct {
+	Name          string         `json:"name"`
+	Element       string         `json:"element"`
+	Level         int            `json:"level"`
+	MaxLevel      int            `json:"max_level"`
+	Cons          int            `json:"cons"`
+	Weapon        WeaponDetail   `json:"weapon"`
+	Talents       TalentDetail   `json:"talents"`
+	Sets          map[string]int `json:"sets"`
+	Stats         []float64      `json:"stats"`
+	SnapshotStats []float64      `json:"snapshot"`
+}
+
+type WeaponDetail struct {
+	Name     string `json:"name"`
+	Refine   int    `json:"refine"`
+	Level    int    `json:"level"`
+	MaxLevel int    `json:"max_level"`
+}
+
+type TalentDetail struct {
+	Attack int `json:"attack"`
+	Skill  int `json:"skill"`
+	Burst  int `json:"burst"`
+}
+
 // Used to track all damage instances for use in damage over time charts
 // Currently only the FrameBucket results are exported, but per Char/Target views are likely also going to be useful
 type DamageDetails struct {
@@ -69,15 +96,18 @@ type Result struct {
 	DPSByTarget    map[int]FloatResult    `json:"dps_by_target"`
 	DamageOverTime map[string]FloatResult `json:"damage_over_time"`
 	Iterations     int                    `json:"iter"`
-	Text           string                 `json:"text"`
-	Debug          string                 `json:"debug"`
 	Runtime        time.Duration          `json:"runtime"`
 	//other info
-	Config     string `json:"config_file"`
-	NumTargets int    `json:"num_targets"`
+	NumTargets    int                 `json:"num_targets"` //TODO: to deprecate this
+	CharDetails   []CharDetail        `json:"char_details"`
+	TargetDetails []core.EnemyProfile `json:"target_details"`
 	//for tracking min/max run
 	MinSeed int64 `json:"-"`
 	MaxSeed int64 `json:"-"`
+	//put these last so result is kinda readable by human
+	Config string `json:"config_file"`
+	Text   string `json:"text"`
+	Debug  string `json:"debug"`
 }
 
 type IntResult struct {
@@ -217,6 +247,11 @@ func Run(src string, opt core.RunOpt, cust ...func(*Simulation) error) (Result,
 	result.Runtime = time.Since(start)
 	result.Config = src
 	result.NumTargets = len(cfg.Targets)
+	result.CharDetails = data[0].CharDetails
+	for i := range result.CharDetails {
+		result.CharDetails[i].Stats = cfg.Characters.Profile[i].Stats
+	}
+	result.TargetDetails = cfg.Targets
 
 	return result, nil
 }


Test patch:
diff --git a/internal/characters/albedo/albedo_test.go b/internal/characters/albedo/albedo_test.go
index e9c7024aa..4d596cf9c 100644
--- a/internal/characters/albedo/albedo_test.go
+++ b/internal/characters/albedo/albedo_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Albedo, core.Geo, 6)
+	prof := tests.CharProfile(core.Albedo, core.Geo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/aloy/aloy_test.go b/internal/characters/aloy/aloy_test.go
index 2832527c8..4cd4c67a5 100644
--- a/internal/characters/aloy/aloy_test.go
+++ b/internal/characters/aloy/aloy_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Aloy, core.Cryo, 6)
+	prof := tests.CharProfile(core.Aloy, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/amber/amber_test.go b/internal/characters/amber/amber_test.go
index e91a11433..0dc63920e 100644
--- a/internal/characters/amber/amber_test.go
+++ b/internal/characters/amber/amber_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Amber, core.Pyro, 6)
+	prof := tests.CharProfile(core.Amber, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/ayaka/ayaka_test.go b/internal/characters/ayaka/ayaka_test.go
index de0a03437..68874872b 100644
--- a/internal/characters/ayaka/ayaka_test.go
+++ b/internal/characters/ayaka/ayaka_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Ayaka, core.Cryo, 6)
+	prof := tests.CharProfile(core.Ayaka, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/barbara/barbara_test.go b/internal/characters/barbara/barbara_test.go
index 443c58b15..04d42982d 100644
--- a/internal/characters/barbara/barbara_test.go
+++ b/internal/characters/barbara/barbara_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -34,7 +33,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Barbara, core.Hydro, 6)
+	prof := tests.CharProfile(core.Barbara, core.Hydro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/beidou/beidou_test.go b/internal/characters/beidou/beidou_test.go
index ffc83374f..ba16d17b2 100644
--- a/internal/characters/beidou/beidou_test.go
+++ b/internal/characters/beidou/beidou_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Beidou, core.Electro, 6)
+	prof := tests.CharProfile(core.Beidou, core.Electro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/bennett/bennett_test.go b/internal/characters/bennett/bennett_test.go
index cb2082e0b..b29bc3746 100644
--- a/internal/characters/bennett/bennett_test.go
+++ b/internal/characters/bennett/bennett_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Bennett, core.Pyro, 6)
+	prof := tests.CharProfile(core.Bennett, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/chongyun/chongyun_test.go b/internal/characters/chongyun/chongyun_test.go
index ee5fdacf1..7013989af 100644
--- a/internal/characters/chongyun/chongyun_test.go
+++ b/internal/characters/chongyun/chongyun_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Chongyun, core.Cryo, 6)
+	prof := tests.CharProfile(core.Chongyun, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/diluc/diluc_test.go b/internal/characters/diluc/diluc_test.go
index 68689283b..2f4a042a0 100644
--- a/internal/characters/diluc/diluc_test.go
+++ b/internal/characters/diluc/diluc_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Diluc, core.Pyro, 6)
+	prof := tests.CharProfile(core.Diluc, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/diona/diona_test.go b/internal/characters/diona/diona_test.go
index 6b06c9d68..e230e8d4f 100644
--- a/internal/characters/diona/diona_test.go
+++ b/internal/characters/diona/diona_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Diona, core.Cryo, 6)
+	prof := tests.CharProfile(core.Diona, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/eula/eula_test.go b/internal/characters/eula/eula_test.go
index 11c82cf19..ef9c10781 100644
--- a/internal/characters/eula/eula_test.go
+++ b/internal/characters/eula/eula_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Eula, core.Cryo, 6)
+	prof := tests.CharProfile(core.Eula, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/fischl/fischl_test.go b/internal/characters/fischl/fischl_test.go
index 811106c63..20f5e3e34 100644
--- a/internal/characters/fischl/fischl_test.go
+++ b/internal/characters/fischl/fischl_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Fischl, core.Electro, 6)
+	prof := tests.CharProfile(core.Fischl, core.Electro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/ganyu/ganyu_test.go b/internal/characters/ganyu/ganyu_test.go
index c500ad465..cf550d350 100644
--- a/internal/characters/ganyu/ganyu_test.go
+++ b/internal/characters/ganyu/ganyu_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Ganyu, core.Cryo, 6)
+	prof := tests.CharProfile(core.Ganyu, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/gorou/gorou_test.go b/internal/characters/gorou/gorou_test.go
index 600a1d2ca..a356edf96 100644
--- a/internal/characters/gorou/gorou_test.go
+++ b/internal/characters/gorou/gorou_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Gorou, core.Geo, 6)
+	prof := tests.CharProfile(core.Gorou, core.Geo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/hutao/hutao_test.go b/internal/characters/hutao/hutao_test.go
index 2e44159e8..21da7f970 100644
--- a/internal/characters/hutao/hutao_test.go
+++ b/internal/characters/hutao/hutao_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Hutao, core.Pyro, 6)
+	prof := tests.CharProfile(core.Hutao, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/jean/jean_test.go b/internal/characters/jean/jean_test.go
index 3064185a0..5246fdd1e 100644
--- a/internal/characters/jean/jean_test.go
+++ b/internal/characters/jean/jean_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Jean, core.Anemo, 6)
+	prof := tests.CharProfile(core.Jean, core.Anemo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/kaeya/kaeya_test.go b/internal/characters/kaeya/kaeya_test.go
index aa884701b..cf8d0caaa 100644
--- a/internal/characters/kaeya/kaeya_test.go
+++ b/internal/characters/kaeya/kaeya_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Kaeya, core.Cryo, 6)
+	prof := tests.CharProfile(core.Kaeya, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/kazuha/kazuha_test.go b/internal/characters/kazuha/kazuha_test.go
index ca149b756..cfdf38acb 100644
--- a/internal/characters/kazuha/kazuha_test.go
+++ b/internal/characters/kazuha/kazuha_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Kazuha, core.Anemo, 6)
+	prof := tests.CharProfile(core.Kazuha, core.Anemo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/keqing/keqing_test.go b/internal/characters/keqing/keqing_test.go
index 8f1e65e8b..78d835083 100644
--- a/internal/characters/keqing/keqing_test.go
+++ b/internal/characters/keqing/keqing_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Keqing, core.Electro, 6)
+	prof := tests.CharProfile(core.Keqing, core.Electro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/klee/klee_test.go b/internal/characters/klee/klee_test.go
index 184dd0331..9c84b5880 100644
--- a/internal/characters/klee/klee_test.go
+++ b/internal/characters/klee/klee_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Klee, core.Pyro, 6)
+	prof := tests.CharProfile(core.Klee, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/kokomi/kokomi_test.go b/internal/characters/kokomi/kokomi_test.go
index 162902a48..a0b33e910 100644
--- a/internal/characters/kokomi/kokomi_test.go
+++ b/internal/characters/kokomi/kokomi_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Kokomi, core.Hydro, 6)
+	prof := tests.CharProfile(core.Kokomi, core.Hydro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/lisa/lisa_test.go b/internal/characters/lisa/lisa_test.go
index 25028a42f..92e688483 100644
--- a/internal/characters/lisa/lisa_test.go
+++ b/internal/characters/lisa/lisa_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Lisa, core.Electro, 6)
+	prof := tests.CharProfile(core.Lisa, core.Electro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/mona/mona_test.go b/internal/characters/mona/mona_test.go
index 68ea4c216..73d6ab050 100644
--- a/internal/characters/mona/mona_test.go
+++ b/internal/characters/mona/mona_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Mona, core.Hydro, 6)
+	prof := tests.CharProfile(core.Mona, core.Hydro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/ningguang/ningguang_test.go b/internal/characters/ningguang/ningguang_test.go
index db1e42cb8..d00f4a66b 100644
--- a/internal/characters/ningguang/ningguang_test.go
+++ b/internal/characters/ningguang/ningguang_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Ningguang, core.Geo, 6)
+	prof := tests.CharProfile(core.Ningguang, core.Geo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/noelle/noelle_test.go b/internal/characters/noelle/noelle_test.go
index 7c85db46d..46a373127 100644
--- a/internal/characters/noelle/noelle_test.go
+++ b/internal/characters/noelle/noelle_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Noelle, core.Geo, 6)
+	prof := tests.CharProfile(core.Noelle, core.Geo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/qiqi/qiqi_test.go b/internal/characters/qiqi/qiqi_test.go
index a78e8599e..deeb5a9cf 100644
--- a/internal/characters/qiqi/qiqi_test.go
+++ b/internal/characters/qiqi/qiqi_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Qiqi, core.Cryo, 6)
+	prof := tests.CharProfile(core.Qiqi, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/raiden/raiden_test.go b/internal/characters/raiden/raiden_test.go
index c2aa83120..122b2e90c 100644
--- a/internal/characters/raiden/raiden_test.go
+++ b/internal/characters/raiden/raiden_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Raiden, core.Electro, 6)
+	prof := tests.CharProfile(core.Raiden, core.Electro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/rosaria/rosaria_test.go b/internal/characters/rosaria/rosaria_test.go
index 2b1e43789..ba41dacad 100644
--- a/internal/characters/rosaria/rosaria_test.go
+++ b/internal/characters/rosaria/rosaria_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Rosaria, core.Cryo, 6)
+	prof := tests.CharProfile(core.Rosaria, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/sara/sara_test.go b/internal/characters/sara/sara_test.go
index c093a51b6..6a93f0002 100644
--- a/internal/characters/sara/sara_test.go
+++ b/internal/characters/sara/sara_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Sara, core.Electro, 6)
+	prof := tests.CharProfile(core.Sara, core.Electro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/shenhe/shenhe_test.go b/internal/characters/shenhe/shenhe_test.go
index 9a4d17086..495c95390 100644
--- a/internal/characters/shenhe/shenhe_test.go
+++ b/internal/characters/shenhe/shenhe_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -40,7 +39,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
@@ -95,7 +94,7 @@ func TestSkillCDCon0(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	prof.Base.Cons = 0
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
@@ -178,7 +177,7 @@ func TestBurstCDBasic(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
@@ -248,7 +247,7 @@ func TestSkillCDCon1(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	prof.Base.Cons = 1
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
@@ -417,7 +416,7 @@ func TestFlatCDReduction(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	prof.Base.Cons = 0
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
@@ -510,7 +509,7 @@ func TestFlatCDReductionCon1(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	prof.Base.Cons = 1
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
@@ -592,7 +591,7 @@ func TestResetSkillCD(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	prof.Base.Cons = 1
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
@@ -682,7 +681,7 @@ func TestResetSkillCooldownReduction(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Shenhe, core.Cryo, 6)
+	prof := tests.CharProfile(core.Shenhe, core.Cryo, 6)
 	prof.Base.Cons = 1
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
diff --git a/internal/characters/sucrose/sucrose_test.go b/internal/characters/sucrose/sucrose_test.go
index eaa87e986..8fdfdad8b 100644
--- a/internal/characters/sucrose/sucrose_test.go
+++ b/internal/characters/sucrose/sucrose_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestSkillCDWithC4(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Sucrose, core.Anemo, 6)
+	prof := tests.CharProfile(core.Sucrose, core.Anemo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	sucrose := x.(*char)
diff --git a/internal/characters/tartaglia/tartaglia_test.go b/internal/characters/tartaglia/tartaglia_test.go
index 72aba5436..638c82029 100644
--- a/internal/characters/tartaglia/tartaglia_test.go
+++ b/internal/characters/tartaglia/tartaglia_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Tartaglia, core.Hydro, 6)
+	prof := tests.CharProfile(core.Tartaglia, core.Hydro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/travelergeo/travelergeo_test.go b/internal/characters/travelergeo/travelergeo_test.go
index 2325b29dd..e70f3a983 100644
--- a/internal/characters/travelergeo/travelergeo_test.go
+++ b/internal/characters/travelergeo/travelergeo_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.TravelerGeo, core.Geo, 6)
+	prof := tests.CharProfile(core.TravelerGeo, core.Geo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/venti/venti_test.go b/internal/characters/venti/venti_test.go
index 93144ba3d..490ad5b41 100644
--- a/internal/characters/venti/venti_test.go
+++ b/internal/characters/venti/venti_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Venti, core.Anemo, 6)
+	prof := tests.CharProfile(core.Venti, core.Anemo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/xiangling/xiangling_test.go b/internal/characters/xiangling/xiangling_test.go
index f3b15c085..d86d80343 100644
--- a/internal/characters/xiangling/xiangling_test.go
+++ b/internal/characters/xiangling/xiangling_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Xiangling, core.Pyro, 6)
+	prof := tests.CharProfile(core.Xiangling, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/xiao/xiao_test.go b/internal/characters/xiao/xiao_test.go
index c97a4baf9..a28ce7ca4 100644
--- a/internal/characters/xiao/xiao_test.go
+++ b/internal/characters/xiao/xiao_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Xiao, core.Anemo, 6)
+	prof := tests.CharProfile(core.Xiao, core.Anemo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/xingqiu/xingqiu_test.go b/internal/characters/xingqiu/xingqiu_test.go
index 5b16497b8..0f51166fb 100644
--- a/internal/characters/xingqiu/xingqiu_test.go
+++ b/internal/characters/xingqiu/xingqiu_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Xingqiu, core.Hydro, 6)
+	prof := tests.CharProfile(core.Xingqiu, core.Hydro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/yanfei/yanfei_test.go b/internal/characters/yanfei/yanfei_test.go
index e33abbff7..6877e2d4a 100644
--- a/internal/characters/yanfei/yanfei_test.go
+++ b/internal/characters/yanfei/yanfei_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Yanfei, core.Pyro, 6)
+	prof := tests.CharProfile(core.Yanfei, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/yoimiya/yoimiya_test.go b/internal/characters/yoimiya/yoimiya_test.go
index 55c11cdd2..651b85ca3 100644
--- a/internal/characters/yoimiya/yoimiya_test.go
+++ b/internal/characters/yoimiya/yoimiya_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Yoimiya, core.Pyro, 6)
+	prof := tests.CharProfile(core.Yoimiya, core.Pyro, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/characters/zhongli/zhongli_test.go b/internal/characters/zhongli/zhongli_test.go
index 6908b1ec3..78c1507af 100644
--- a/internal/characters/zhongli/zhongli_test.go
+++ b/internal/characters/zhongli/zhongli_test.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/genshinsim/gcsim/internal/tests"
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 	"github.com/genshinsim/gcsim/pkg/enemy"
 	"github.com/genshinsim/gcsim/pkg/player"
 	"go.uber.org/zap"
@@ -39,7 +38,7 @@ func TestBasicAbilUsage(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	prof := tests.CharProfile(keys.Zhongli, core.Geo, 6)
+	prof := tests.CharProfile(core.Zhongli, core.Geo, 6)
 	x, err := NewChar(c, prof)
 	//cast it to *char so we can access private members
 	// this := x.(*char)
diff --git a/internal/tests/profiles.go b/internal/tests/profiles.go
index a3b4df3ba..67176fd12 100644
--- a/internal/tests/profiles.go
+++ b/internal/tests/profiles.go
@@ -2,10 +2,9 @@ package tests
 
 import (
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
-func CharProfile(key keys.Char, ele core.EleType, cons int) core.CharacterProfile {
+func CharProfile(key core.CharKey, ele core.EleType, cons int) core.CharacterProfile {
 	r := core.CharacterProfile{}
 	r.Base.Element = ele
 	r.Base.Key = key
diff --git a/pkg/character/curve_test.go b/pkg/character/curve_test.go
index 46e5588b3..b773d39c9 100644
--- a/pkg/character/curve_test.go
+++ b/pkg/character/curve_test.go
@@ -4,7 +4,6 @@ import (
 	"testing"
 
 	"github.com/genshinsim/gcsim/pkg/core"
-	"github.com/genshinsim/gcsim/pkg/core/keys"
 )
 
 func TestBasicAbilUsage(t *testing.T) {
@@ -15,7 +14,7 @@ func TestBasicAbilUsage(t *testing.T) {
 	}
 	prof := core.CharacterProfile{}
 	prof.Base.Element = core.Pyro
-	prof.Base.Key = keys.Xiangling
+	prof.Base.Key = core.Xiangling
 	prof.Stats = make([]float64, core.EndStatType)
 	prof.Base.Level = 80
 	prof.Base.MaxLevel = 90