buff fedora, add elite enemies and add impact to attack
This commit is contained in:
+57
-73
@@ -11,39 +11,15 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="4222a6b8-add3-4f43-9c42-fd6bb5a76169" name="Changes" comment="">
|
<list default="true" id="4222a6b8-add3-4f43-9c42-fd6bb5a76169" name="Changes" comment="">
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/AurHelper.asset" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/AurHelper.asset.meta" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/ConsultWiki.asset" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/ConsultWiki.asset.meta" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/Makepkg.asset" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/Makepkg.asset.meta" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/MinimalInstall.asset" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/MinimalInstall.asset.meta" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/PacmanSyu.asset" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/Assets/Data/Cards/Arch/PacmanSyu.asset.meta" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.linuxdistrosbuttheyarebreedable/.idea/indexLayout.xml" beforeDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/.idea.linuxdistrosbuttheyarebreedable/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/.idea.linuxdistrosbuttheyarebreedable/.idea/workspace.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Fedora/DnfUpdate.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Fedora/DnfUpdate.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Languages/C/Malloc.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Languages/C/Malloc.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Languages/C/Pointer.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Languages/C/Pointer.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Languages/C/Printf.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Languages/C/Printf.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Languages/Java/JavaNewObject.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Languages/Java/JavaNewObject.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Languages/Java/JavaPublicStaticMain.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Languages/Java/JavaPublicStaticMain.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Languages/Java/JavaSystemOutPrintln.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Languages/Java/JavaSystemOutPrintln.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Shop/Free.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Shop/Free.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Shop/KernelExploit.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Shop/KernelExploit.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Data/Cards/Shop/Sizeof.asset" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Data/Cards/Shop/Sizeof.asset" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Resources/CombatScene.uss" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Resources/CombatScene.uss" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Resources/CombatScene.uss" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Resources/CombatScene.uss" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CardEffects.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CardEffects.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CardEffects.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CardEffects.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatManager.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatManager.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatTuning.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatTuning.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatTuning.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatTuning.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatantState.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/CombatantState.cs" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/DamagePipeline.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/DamagePipeline.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/DamagePipeline.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/DamagePipeline.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyArchetype.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyArchetype.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyArchetype.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyArchetype.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyInstance.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyInstance.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyInstance.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/EnemyInstance.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/StatusEffectController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/StatusEffectController.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Combat/HandController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Combat/HandController.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Run/RepositoryOffer.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Run/RepositoryOffer.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Core/GameEvents.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Core/GameEvents.cs" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/Run/RunManager.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/Run/RunManager.cs" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/CombatSceneController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/CombatSceneController.cs" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Assets/Scripts/UI/CombatSceneController.cs" beforeDir="false" afterPath="$PROJECT_DIR$/Assets/Scripts/UI/CombatSceneController.cs" afterDir="false" />
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
@@ -128,23 +104,23 @@
|
|||||||
<component name="MetaFilesCheckinStateConfiguration" checkMetaFiles="true" />
|
<component name="MetaFilesCheckinStateConfiguration" checkMetaFiles="true" />
|
||||||
<component name="NextEditCompletionFeaturesState">
|
<component name="NextEditCompletionFeaturesState">
|
||||||
<decayedCancelled>
|
<decayedCancelled>
|
||||||
<entry key="MS100" value="1.743262326028233" />
|
<entry key="MS100" value="1.7402462730204733" />
|
||||||
<entry key="MS500" value="5.976929062573193" />
|
<entry key="MS500" value="5.212822426631115" />
|
||||||
<entry key="S2" value="17.53689034207893" />
|
<entry key="S2" value="15.469623231379348" />
|
||||||
<entry key="S5" value="27.584806857875957" />
|
<entry key="S5" value="29.842466880777515" />
|
||||||
<entry key="S10" value="33.27162043754185" />
|
<entry key="S10" value="48.17178903184381" />
|
||||||
<entry key="S30" value="38.14780953249457" />
|
<entry key="S30" value="103.86178571051501" />
|
||||||
<entry key="S60" value="39.53571836868974" />
|
<entry key="S60" value="151.400209690654" />
|
||||||
<entry key="M2" value="40.262726944904955" />
|
<entry key="M2" value="201.01266486561804" />
|
||||||
<entry key="M5" value="43.32220297215448" />
|
<entry key="M5" value="261.5973708018234" />
|
||||||
<entry key="M10" value="66.17309975101882" />
|
<entry key="M10" value="299.5317141800844" />
|
||||||
<entry key="M15" value="106.65472090214925" />
|
<entry key="M15" value="327.6694025041181" />
|
||||||
<entry key="M30" value="269.18080676227913" />
|
<entry key="M30" value="438.2042699732131" />
|
||||||
<entry key="H1" value="561.7788280748766" />
|
<entry key="H1" value="698.7973356946319" />
|
||||||
<entry key="H2" value="954.5740596560953" />
|
<entry key="H2" value="1110.17006856563" />
|
||||||
<entry key="H4" value="1474.025592246917" />
|
<entry key="H4" value="1669.5601617756433" />
|
||||||
<entry key="D1" value="4120.268616608263" />
|
<entry key="D1" value="4400.825096157156" />
|
||||||
<entry key="W1" value="6567.8208311010485" />
|
<entry key="W1" value="6908.8758325877625" />
|
||||||
</decayedCancelled>
|
</decayedCancelled>
|
||||||
<decayedSelected>
|
<decayedSelected>
|
||||||
<entry key="MS100" value="0.0" />
|
<entry key="MS100" value="0.0" />
|
||||||
@@ -155,34 +131,34 @@
|
|||||||
<entry key="S30" value="0.0" />
|
<entry key="S30" value="0.0" />
|
||||||
<entry key="S60" value="0.0" />
|
<entry key="S60" value="0.0" />
|
||||||
<entry key="M2" value="0.0" />
|
<entry key="M2" value="0.0" />
|
||||||
<entry key="M5" value="9.303102041982209E-153" />
|
<entry key="M5" value="3.867598516552587E-155" />
|
||||||
<entry key="M10" value="9.645258960745102E-77" />
|
<entry key="M10" value="6.219001942878838E-78" />
|
||||||
<entry key="M15" value="2.1031775057960495E-51" />
|
<entry key="M15" value="3.381793836897591E-52" />
|
||||||
<entry key="M30" value="4.586042805192191E-26" />
|
<entry key="M30" value="1.8389660182180397E-26" />
|
||||||
<entry key="H1" value="2.1436097902215403E-13" />
|
<entry key="H1" value="1.3574177751524003E-13" />
|
||||||
<entry key="H2" value="4.878952351612686E-7" />
|
<entry key="H2" value="3.8824911709080487E-7" />
|
||||||
<entry key="H4" value="9.54846149789426E-4" />
|
<entry key="H4" value="8.517759062521447E-4" />
|
||||||
<entry key="D1" value="0.9334224145260533" />
|
<entry key="D1" value="0.9158201973847542" />
|
||||||
<entry key="W1" value="3.244779666296953" />
|
<entry key="W1" value="3.235966878195177" />
|
||||||
</decayedSelected>
|
</decayedSelected>
|
||||||
<decayedShown>
|
<decayedShown>
|
||||||
<entry key="MS100" value="1.668533264021644" />
|
<entry key="MS100" value="1.684339997461739" />
|
||||||
<entry key="MS500" value="5.913573457970713" />
|
<entry key="MS500" value="5.1624842789484084" />
|
||||||
<entry key="S2" value="17.47919105497625" />
|
<entry key="S2" value="15.426146204785072" />
|
||||||
<entry key="S5" value="27.548161746345162" />
|
<entry key="S5" value="29.8122059339745" />
|
||||||
<entry key="S10" value="33.249516115897215" />
|
<entry key="S10" value="48.149217760882784" />
|
||||||
<entry key="S30" value="38.1393616328719" />
|
<entry key="S30" value="103.84706944591119" />
|
||||||
<entry key="S60" value="39.53134062098556" />
|
<entry key="S60" value="151.3898367310693" />
|
||||||
<entry key="M2" value="40.26049784489663" />
|
<entry key="M2" value="201.00547613774899" />
|
||||||
<entry key="M5" value="43.32125926171084" />
|
<entry key="M5" value="261.59163125526345" />
|
||||||
<entry key="M10" value="66.17242872752867" />
|
<entry key="M10" value="299.52705025178165" />
|
||||||
<entry key="M15" value="106.65399822613271" />
|
<entry key="M15" value="327.66555086022873" />
|
||||||
<entry key="M30" value="269.1797553890588" />
|
<entry key="M30" value="438.20154496052015" />
|
||||||
<entry key="H1" value="561.7775677248208" />
|
<entry key="H1" value="698.7952502773087" />
|
||||||
<entry key="H2" value="954.5729275577665" />
|
<entry key="H2" value="1110.1684860433274" />
|
||||||
<entry key="H4" value="1474.025680151079" />
|
<entry key="H4" value="1669.5598893072865" />
|
||||||
<entry key="D1" value="4121.2016186941155" />
|
<entry key="D1" value="4401.740444022695" />
|
||||||
<entry key="W1" value="6571.065502619166" />
|
<entry key="W1" value="6912.111683013967" />
|
||||||
</decayedShown>
|
</decayedShown>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectColorInfo">{
|
<component name="ProjectColorInfo">{
|
||||||
@@ -276,7 +252,7 @@
|
|||||||
<workItem from="1783519887449" duration="6425000" />
|
<workItem from="1783519887449" duration="6425000" />
|
||||||
<workItem from="1783578446731" duration="20118000" />
|
<workItem from="1783578446731" duration="20118000" />
|
||||||
<workItem from="1783615847433" duration="3791000" />
|
<workItem from="1783615847433" duration="3791000" />
|
||||||
<workItem from="1783665689762" duration="4791000" />
|
<workItem from="1783665689762" duration="8074000" />
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="implement main screen">
|
<task id="LOCAL-00001" summary="implement main screen">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -510,7 +486,15 @@
|
|||||||
<option name="project" value="LOCAL" />
|
<option name="project" value="LOCAL" />
|
||||||
<updated>1783620160977</updated>
|
<updated>1783620160977</updated>
|
||||||
</task>
|
</task>
|
||||||
<option name="localTasksCounter" value="30" />
|
<task id="LOCAL-00030" summary="implement more enemies, balance some programming languages and enemies to be less annoying">
|
||||||
|
<option name="closed" value="true" />
|
||||||
|
<created>1783671710808</created>
|
||||||
|
<option name="number" value="00030" />
|
||||||
|
<option name="presentableId" value="LOCAL-00030" />
|
||||||
|
<option name="project" value="LOCAL" />
|
||||||
|
<updated>1783671710808</updated>
|
||||||
|
</task>
|
||||||
|
<option name="localTasksCounter" value="31" />
|
||||||
<servers />
|
<servers />
|
||||||
</component>
|
</component>
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
<component name="TypeScriptGeneratedFilesManager">
|
||||||
@@ -526,7 +510,6 @@
|
|||||||
<component name="UnityUnitTestConfiguration" currentTestLauncher="Both" />
|
<component name="UnityUnitTestConfiguration" currentTestLauncher="Both" />
|
||||||
<component name="VcsManagerConfiguration">
|
<component name="VcsManagerConfiguration">
|
||||||
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
|
<option name="CLEAR_INITIAL_COMMIT_MESSAGE" value="true" />
|
||||||
<MESSAGE value="refactor card systems" />
|
|
||||||
<MESSAGE value="Add actual carts and shit Upgrade UI to Level 6 Balls" />
|
<MESSAGE value="Add actual carts and shit Upgrade UI to Level 6 Balls" />
|
||||||
<MESSAGE value="Implement more UX and cards for other distros" />
|
<MESSAGE value="Implement more UX and cards for other distros" />
|
||||||
<MESSAGE value="Fix some fucking bugs (FSFB)" />
|
<MESSAGE value="Fix some fucking bugs (FSFB)" />
|
||||||
@@ -551,7 +534,8 @@
|
|||||||
<MESSAGE value="Make multiple UX improvements in distro -> packages" />
|
<MESSAGE value="Make multiple UX improvements in distro -> packages" />
|
||||||
<MESSAGE value="Implement Arch and some additional changes" />
|
<MESSAGE value="Implement Arch and some additional changes" />
|
||||||
<MESSAGE value="Fix C, more enemies and less scaling for enemies." />
|
<MESSAGE value="Fix C, more enemies and less scaling for enemies." />
|
||||||
<option name="LAST_COMMIT_MESSAGE" value="Fix C, more enemies and less scaling for enemies." />
|
<MESSAGE value="implement more enemies, balance some programming languages and enemies to be less annoying" />
|
||||||
|
<option name="LAST_COMMIT_MESSAGE" value="implement more enemies, balance some programming languages and enemies to be less annoying" />
|
||||||
</component>
|
</component>
|
||||||
<component name="XDebuggerManager">
|
<component name="XDebuggerManager">
|
||||||
<breakpoint-manager>
|
<breakpoint-manager>
|
||||||
|
|||||||
@@ -59,6 +59,22 @@
|
|||||||
transition-duration: 0ms;
|
transition-duration: 0ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root-shake-major {
|
||||||
|
translate: 3px 0;
|
||||||
|
transition-property: translate;
|
||||||
|
transition-duration: 70ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-shake-massive {
|
||||||
|
translate: 6px 0;
|
||||||
|
transition-property: translate;
|
||||||
|
transition-duration: 90ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-shake-player {
|
||||||
|
background-color: #080202;
|
||||||
|
}
|
||||||
|
|
||||||
.scene-fade-curtain {
|
.scene-fade-curtain {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: 0;
|
left: 0;
|
||||||
@@ -502,6 +518,11 @@
|
|||||||
background-color: var(--kp-panel-targeted);
|
background-color: var(--kp-panel-targeted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.enemy-card-elite {
|
||||||
|
border-left-color: var(--kp-warning);
|
||||||
|
border-left-width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.enemy-name {
|
.enemy-name {
|
||||||
color: var(--kp-text);
|
color: var(--kp-text);
|
||||||
-unity-font-style: bold;
|
-unity-font-style: bold;
|
||||||
@@ -1120,6 +1141,12 @@
|
|||||||
border-color: var(--kp-kill);
|
border-color: var(--kp-kill);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.damage-vignette-massive {
|
||||||
|
opacity: 0.94;
|
||||||
|
border-color: var(--kp-kill);
|
||||||
|
background-color: rgba(255, 51, 51, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
.float-number {
|
.float-number {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
min-width: 60px;
|
min-width: 60px;
|
||||||
@@ -1137,6 +1164,14 @@
|
|||||||
color: var(--kp-damage);
|
color: var(--kp-damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.float-shield {
|
||||||
|
color: var(--kp-beneficial);
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-true {
|
||||||
|
color: var(--kp-kill);
|
||||||
|
}
|
||||||
|
|
||||||
.float-heal {
|
.float-heal {
|
||||||
color: var(--kp-beneficial);
|
color: var(--kp-beneficial);
|
||||||
}
|
}
|
||||||
@@ -1157,6 +1192,37 @@
|
|||||||
font-size: 23px;
|
font-size: 23px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.float-tier-minor {
|
||||||
|
font-size: 14px;
|
||||||
|
-unity-font-style: normal;
|
||||||
|
opacity: 0.86;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-tier-moderate {
|
||||||
|
font-size: 19px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-tier-major {
|
||||||
|
font-size: 25px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
color: var(--kp-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.float-tier-massive {
|
||||||
|
min-width: 92px;
|
||||||
|
height: 34px;
|
||||||
|
font-size: 32px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
color: var(--kp-kill);
|
||||||
|
background-color: rgba(255, 179, 71, 0.08);
|
||||||
|
border-left-width: 1px;
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-top-width: 1px;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-color: var(--kp-warning);
|
||||||
|
}
|
||||||
|
|
||||||
.float-static {
|
.float-static {
|
||||||
transition-duration: 0ms;
|
transition-duration: 0ms;
|
||||||
}
|
}
|
||||||
@@ -1195,6 +1261,29 @@
|
|||||||
border-color: var(--kp-beneficial);
|
border-color: var(--kp-beneficial);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.impact-tier-minor {
|
||||||
|
opacity: 0.72;
|
||||||
|
font-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.impact-tier-moderate {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.impact-tier-major {
|
||||||
|
min-width: 58px;
|
||||||
|
font-size: 11px;
|
||||||
|
border-color: var(--kp-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.impact-tier-massive {
|
||||||
|
min-width: 68px;
|
||||||
|
height: 22px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--kp-kill);
|
||||||
|
border-color: var(--kp-kill);
|
||||||
|
}
|
||||||
|
|
||||||
.feedback-card-ghost {
|
.feedback-card-ghost {
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
transition-property: left, top, opacity;
|
transition-property: left, top, opacity;
|
||||||
@@ -1227,6 +1316,36 @@
|
|||||||
transition-duration: 90ms;
|
transition-duration: 90ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.feedback-hit-minor {
|
||||||
|
background-color: var(--kp-panel-raised);
|
||||||
|
border-color: var(--kp-border);
|
||||||
|
scale: 0.98 0.98;
|
||||||
|
transition-property: scale, background-color, border-color;
|
||||||
|
transition-duration: 70ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-hit-major {
|
||||||
|
background-color: #251703;
|
||||||
|
border-color: var(--kp-warning);
|
||||||
|
translate: 6px 0;
|
||||||
|
scale: 1.055 0.94;
|
||||||
|
transition-property: scale, translate, background-color, border-color;
|
||||||
|
transition-duration: 140ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-hit-minor .meter-fill {
|
||||||
|
background-color: var(--kp-damage);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-hit .meter-fill,
|
||||||
|
.feedback-hit-major .meter-fill {
|
||||||
|
background-color: var(--kp-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-crit .meter-fill {
|
||||||
|
background-color: var(--kp-kill);
|
||||||
|
}
|
||||||
|
|
||||||
.feedback-crit {
|
.feedback-crit {
|
||||||
background-color: #2d0707;
|
background-color: #2d0707;
|
||||||
border-color: var(--kp-kill);
|
border-color: var(--kp-kill);
|
||||||
@@ -1245,6 +1364,20 @@
|
|||||||
transition-duration: 80ms;
|
transition-duration: 80ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.feedback-clang-major {
|
||||||
|
background-color: #061820;
|
||||||
|
border-color: var(--kp-beneficial);
|
||||||
|
translate: 5px 0;
|
||||||
|
scale: 1.045 0.96;
|
||||||
|
transition-property: scale, translate, background-color, border-color;
|
||||||
|
transition-duration: 130ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feedback-clang .meter-fill,
|
||||||
|
.feedback-clang-major .meter-fill {
|
||||||
|
background-color: var(--kp-beneficial);
|
||||||
|
}
|
||||||
|
|
||||||
.feedback-status {
|
.feedback-status {
|
||||||
border-color: var(--kp-status-harmful);
|
border-color: var(--kp-status-harmful);
|
||||||
}
|
}
|
||||||
@@ -1361,9 +1494,15 @@
|
|||||||
.reduced-motion .impact-marker,
|
.reduced-motion .impact-marker,
|
||||||
.reduced-motion .feedback-card-ghost,
|
.reduced-motion .feedback-card-ghost,
|
||||||
.reduced-motion .damage-vignette,
|
.reduced-motion .damage-vignette,
|
||||||
|
.reduced-motion .root-shake-major,
|
||||||
|
.reduced-motion .root-shake-massive,
|
||||||
|
.reduced-motion .root-shake-player,
|
||||||
|
.reduced-motion .feedback-hit-minor,
|
||||||
.reduced-motion .feedback-hit,
|
.reduced-motion .feedback-hit,
|
||||||
|
.reduced-motion .feedback-hit-major,
|
||||||
.reduced-motion .feedback-crit,
|
.reduced-motion .feedback-crit,
|
||||||
.reduced-motion .feedback-clang,
|
.reduced-motion .feedback-clang,
|
||||||
|
.reduced-motion .feedback-clang-major,
|
||||||
.reduced-motion .feedback-killed,
|
.reduced-motion .feedback-killed,
|
||||||
.reduced-motion .enemy-anticipating,
|
.reduced-motion .enemy-anticipating,
|
||||||
.reduced-motion .enemy-acting,
|
.reduced-motion .enemy-acting,
|
||||||
@@ -1376,3 +1515,18 @@
|
|||||||
.reduced-motion .death-ghost {
|
.reduced-motion .death-ghost {
|
||||||
transition-duration: 0ms;
|
transition-duration: 0ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.reduced-motion .feedback-hit-minor,
|
||||||
|
.reduced-motion .feedback-hit,
|
||||||
|
.reduced-motion .feedback-hit-major,
|
||||||
|
.reduced-motion .feedback-crit,
|
||||||
|
.reduced-motion .feedback-clang,
|
||||||
|
.reduced-motion .feedback-clang-major,
|
||||||
|
.reduced-motion .feedback-killed,
|
||||||
|
.reduced-motion .enemy-anticipating,
|
||||||
|
.reduced-motion .enemy-acting,
|
||||||
|
.reduced-motion .root-shake-major,
|
||||||
|
.reduced-motion .root-shake-massive {
|
||||||
|
translate: 0 0;
|
||||||
|
scale: 1 1;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1062,6 +1062,23 @@ namespace KernelPanic.Combat
|
|||||||
or "arch_pacman_syu" or "arch_consult_wiki" or "arch_makepkg" or "shop_c_free" or "shop_c_sizeof" or "shop_c_kernel_exploit";
|
or "arch_pacman_syu" or "arch_consult_wiki" or "arch_makepkg" or "shop_c_free" or "shop_c_sizeof" or "shop_c_kernel_exploit";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool CanReceiveBleedingEdgeDamageBonus(CardDefinition definition)
|
||||||
|
{
|
||||||
|
if (definition == null || definition.ResolutionTrack != ResolutionTrack.Native)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition.Id is "lang_js_console_log" or "lang_js_fetch" or "lang_js_typeof" or "mint_fix_broken"
|
||||||
|
or "lang_rs_fn_main" or "lang_rs_unwrap"
|
||||||
|
or "lang_c_malloc" or "lang_c_printf" or "lang_c_pointer"
|
||||||
|
or "lang_java_public_static_main" or "lang_java_system_out_println" or "lang_java_new_object"
|
||||||
|
or "shop_js_math_random" or "shop_js_promise_all" or "ubuntu_snap_install"
|
||||||
|
or "mint_nemo" or "fedora_dnf_autoremove" or "fedora_first_to_package" or "fedora_dnf_update"
|
||||||
|
or "arch_pacman_syu" or "arch_consult_wiki" or "arch_makepkg"
|
||||||
|
or "shop_c_free" or "shop_c_sizeof" or "shop_c_kernel_exploit";
|
||||||
|
}
|
||||||
|
|
||||||
public static bool TargetsAllEnemies(CardDefinition definition)
|
public static bool TargetsAllEnemies(CardDefinition definition)
|
||||||
{
|
{
|
||||||
return definition?.Id is "lang_py_for_loop";
|
return definition?.Id is "lang_py_for_loop";
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace KernelPanic.Combat
|
|||||||
private bool _skipNextAllocateDraw;
|
private bool _skipNextAllocateDraw;
|
||||||
private bool _ubuntuEmptyHandRefillUsed;
|
private bool _ubuntuEmptyHandRefillUsed;
|
||||||
private int _fedoraCardsDiscountedThisTurn;
|
private int _fedoraCardsDiscountedThisTurn;
|
||||||
private int _fedoraCrashChance = 10;
|
private int _fedoraCrashChance = CombatTuning.FedoraBleedingEdgeBaseCrashChance;
|
||||||
private int _cardsPlayedThisTurn;
|
private int _cardsPlayedThisTurn;
|
||||||
private int _cardsPlayedThisWave;
|
private int _cardsPlayedThisWave;
|
||||||
private int _cCardsPlayedThisTurn;
|
private int _cCardsPlayedThisTurn;
|
||||||
@@ -54,6 +54,7 @@ namespace KernelPanic.Combat
|
|||||||
private int _queuedRepeatCharges;
|
private int _queuedRepeatCharges;
|
||||||
private int _nextTurnCycleBonus;
|
private int _nextTurnCycleBonus;
|
||||||
private int _nextRacePairId = 1;
|
private int _nextRacePairId = 1;
|
||||||
|
private int _activeKernelRamPenalty;
|
||||||
private Coroutine _phaseCoroutine;
|
private Coroutine _phaseCoroutine;
|
||||||
|
|
||||||
public TurnPhase CurrentPhase => currentPhase;
|
public TurnPhase CurrentPhase => currentPhase;
|
||||||
@@ -71,6 +72,7 @@ namespace KernelPanic.Combat
|
|||||||
public bool AwaitingWaveContinue => _awaitingWaveContinue;
|
public bool AwaitingWaveContinue => _awaitingWaveContinue;
|
||||||
public bool RunLost => _runLost;
|
public bool RunLost => _runLost;
|
||||||
public int CurrentWaveNumber => _runManager == null ? 1 : _runManager.CurrentWaveNumber;
|
public int CurrentWaveNumber => _runManager == null ? 1 : _runManager.CurrentWaveNumber;
|
||||||
|
public int ActiveKernelRamPenalty => _activeKernelRamPenalty;
|
||||||
|
|
||||||
public event Action StateChanged;
|
public event Action StateChanged;
|
||||||
public event Action<string> CombatLog;
|
public event Action<string> CombatLog;
|
||||||
@@ -194,11 +196,11 @@ namespace KernelPanic.Combat
|
|||||||
|
|
||||||
if (card.IsLocked)
|
if (card.IsLocked)
|
||||||
{
|
{
|
||||||
Log($"{GetCardName(card)} is locked");
|
Log($"{GetCardName(card)} is locked; pay {EnemyArchetypeCatalog.DrmUnlockCycleCost} Cycle to unlock");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fedoraBonus = CanApplyFedoraBonus();
|
bool fedoraBonus = CanApplyFedoraBonus(card);
|
||||||
int cost = GetEffectiveCardCost(card);
|
int cost = GetEffectiveCardCost(card);
|
||||||
if (fedoraBonus)
|
if (fedoraBonus)
|
||||||
{
|
{
|
||||||
@@ -248,11 +250,13 @@ namespace KernelPanic.Combat
|
|||||||
}
|
}
|
||||||
|
|
||||||
GameEvents.RaiseCardPlayed(new CardPlayedEvent(card, track));
|
GameEvents.RaiseCardPlayed(new CardPlayedEvent(card, track));
|
||||||
|
ApplyTelemetryCardPlayed();
|
||||||
LogPlayIntent(card);
|
LogPlayIntent(card);
|
||||||
card.SetTargetSnapshot(CaptureTargets(card));
|
card.SetTargetSnapshot(CaptureTargets(card));
|
||||||
|
|
||||||
if (fedoraBonus)
|
if (fedoraBonus)
|
||||||
{
|
{
|
||||||
|
bool firstFedoraBonusThisTurn = _fedoraCardsDiscountedThisTurn == 0;
|
||||||
_fedoraCardsDiscountedThisTurn++;
|
_fedoraCardsDiscountedThisTurn++;
|
||||||
bool rawhideChargeUsed = _rawhideBonusCharges > 0;
|
bool rawhideChargeUsed = _rawhideBonusCharges > 0;
|
||||||
if (rawhideChargeUsed)
|
if (rawhideChargeUsed)
|
||||||
@@ -262,7 +266,7 @@ namespace KernelPanic.Combat
|
|||||||
|
|
||||||
if (!IsCrashImmune(card) && RandomRoll.RollRange(1, 100, new RollContext(_playerState)) <= _fedoraCrashChance)
|
if (!IsCrashImmune(card) && RandomRoll.RollRange(1, 100, new RollContext(_playerState)) <= _fedoraCrashChance)
|
||||||
{
|
{
|
||||||
_fedoraCrashChance = 10;
|
_fedoraCrashChance = CombatTuning.FedoraBleedingEdgeBaseCrashChance;
|
||||||
if (IsDistro("fedora") && _runConfig.DistroVersion >= 4)
|
if (IsDistro("fedora") && _runConfig.DistroVersion >= 4)
|
||||||
{
|
{
|
||||||
_playerState.Cycles += 1;
|
_playerState.Cycles += 1;
|
||||||
@@ -276,10 +280,12 @@ namespace KernelPanic.Combat
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_fedoraCrashChance = Mathf.Min(90, _fedoraCrashChance + 10);
|
_fedoraCrashChance = Mathf.Min(
|
||||||
|
CombatTuning.FedoraBleedingEdgeMaxCrashChance,
|
||||||
|
_fedoraCrashChance + CombatTuning.FedoraBleedingEdgeCrashChanceStep);
|
||||||
card.MarkFedoraNonCrashBonus();
|
card.MarkFedoraNonCrashBonus();
|
||||||
_playerState.DamageMultiplierPercent = _runConfig.DistroVersion >= 2 ? 175 : 150;
|
_playerState.DamageMultiplierPercent = _runConfig.DistroVersion >= 2 ? 175 : 150;
|
||||||
ApplyFedoraGrowth(card, rawhideChargeUsed);
|
ApplyFedoraGrowth(card, rawhideChargeUsed, firstFedoraBonusThisTurn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (card.Definition.Language == Language.C && _cCardsPlayedThisTurn == 0 && HasPackageEffect(PackageEffectKind.FirstCThisTurnDamageMultiplier, out PackageEffectData cPackageEffect))
|
if (card.Definition.Language == Language.C && _cCardsPlayedThisTurn == 0 && HasPackageEffect(PackageEffectKind.FirstCThisTurnDamageMultiplier, out PackageEffectData cPackageEffect))
|
||||||
@@ -372,6 +378,27 @@ namespace KernelPanic.Combat
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryUnlockCard(CardInstance card)
|
||||||
|
{
|
||||||
|
if (currentPhase != TurnPhase.Execute || card == null || !card.IsLocked || _playerState == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int cost = EnemyArchetypeCatalog.DrmUnlockCycleCost;
|
||||||
|
if (_playerState.Cycles < cost)
|
||||||
|
{
|
||||||
|
Log($"not enough cycles to unlock {GetCardName(card)}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_playerState.Cycles -= cost;
|
||||||
|
card.IsLocked = false;
|
||||||
|
Log($"unlocked {GetCardName(card)}");
|
||||||
|
StateChanged?.Invoke();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void ReportEffectResult(string message)
|
public void ReportEffectResult(string message)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(message))
|
if (!string.IsNullOrWhiteSpace(message))
|
||||||
@@ -626,7 +653,7 @@ namespace KernelPanic.Combat
|
|||||||
_awaitingWaveContinue = false;
|
_awaitingWaveContinue = false;
|
||||||
_loggedEffectTodos.Clear();
|
_loggedEffectTodos.Clear();
|
||||||
_ubuntuEmptyHandRefillUsed = false;
|
_ubuntuEmptyHandRefillUsed = false;
|
||||||
_fedoraCrashChance = 10;
|
_fedoraCrashChance = CombatTuning.FedoraBleedingEdgeBaseCrashChance;
|
||||||
_fedoraCardsDiscountedThisTurn = 0;
|
_fedoraCardsDiscountedThisTurn = 0;
|
||||||
_cardsPlayedThisTurn = 0;
|
_cardsPlayedThisTurn = 0;
|
||||||
_cardsPlayedThisWave = 0;
|
_cardsPlayedThisWave = 0;
|
||||||
@@ -700,6 +727,7 @@ namespace KernelPanic.Combat
|
|||||||
_interpreterQueue.Clear();
|
_interpreterQueue.Clear();
|
||||||
_enemies.Clear();
|
_enemies.Clear();
|
||||||
SpawnStructuralEnemies();
|
SpawnStructuralEnemies();
|
||||||
|
RecalculateKernelRamPressure();
|
||||||
PickEnemyIntents();
|
PickEnemyIntents();
|
||||||
StateChanged?.Invoke();
|
StateChanged?.Invoke();
|
||||||
yield return DrawOpeningHandSequenced();
|
yield return DrawOpeningHandSequenced();
|
||||||
@@ -715,6 +743,7 @@ namespace KernelPanic.Combat
|
|||||||
}
|
}
|
||||||
|
|
||||||
RevivePendingEnemies();
|
RevivePendingEnemies();
|
||||||
|
UnlockHandCards("license check expired");
|
||||||
_playerState.Cycles = _playerState.MaxCycles;
|
_playerState.Cycles = _playerState.MaxCycles;
|
||||||
_fedoraCardsDiscountedThisTurn = 0;
|
_fedoraCardsDiscountedThisTurn = 0;
|
||||||
_cardsPlayedThisTurn = 0;
|
_cardsPlayedThisTurn = 0;
|
||||||
@@ -856,7 +885,7 @@ namespace KernelPanic.Combat
|
|||||||
|
|
||||||
private IEnumerator DrawOpeningHandSequenced()
|
private IEnumerator DrawOpeningHandSequenced()
|
||||||
{
|
{
|
||||||
int openingDraw = Mathf.Min(CombatTuning.OpeningHandSize, _playerState.Ram);
|
int openingDraw = Mathf.Min(CombatTuning.OpeningHandSize, _handController == null ? _playerState.Ram : _handController.RamCapacity);
|
||||||
yield return DrawCardsToHandSequenced(openingDraw, "opening hand");
|
yield return DrawCardsToHandSequenced(openingDraw, "opening hand");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -954,9 +983,87 @@ namespace KernelPanic.Combat
|
|||||||
StartCoroutine(DrawCardsToHandSequenced(_playerState.Ram, "ubuntu 24.04 refill"));
|
StartCoroutine(DrawCardsToHandSequenced(_playerState.Ram, "ubuntu 24.04 refill"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanApplyFedoraBonus()
|
private bool CanApplyFedoraBonus(CardInstance card)
|
||||||
{
|
{
|
||||||
return CanApplyFedoraPassiveBonus() || _rawhideBonusCharges > 0;
|
return CardEffectFactory.CanReceiveBleedingEdgeDamageBonus(card?.Definition)
|
||||||
|
&& CardCanBenefitFromBleedingEdgeNow(card)
|
||||||
|
&& (CanApplyFedoraPassiveBonus() || _rawhideBonusCharges > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool CardCanBenefitFromBleedingEdgeNow(CardInstance card)
|
||||||
|
{
|
||||||
|
string id = card?.Definition?.Id;
|
||||||
|
if (string.IsNullOrWhiteSpace(id))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == "arch_consult_wiki")
|
||||||
|
{
|
||||||
|
return _handController == null || !HasBrokenHandCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id == "fedora_dnf_autoremove")
|
||||||
|
{
|
||||||
|
return HasJunkHandCard(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasBrokenHandCard()
|
||||||
|
{
|
||||||
|
if (_handController == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _handController.Cards.Count; i++)
|
||||||
|
{
|
||||||
|
if (_handController.Cards[i]?.IsBroken == true)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool HasJunkHandCard(CardInstance playedCard)
|
||||||
|
{
|
||||||
|
if (_handController == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < _handController.Cards.Count; i++)
|
||||||
|
{
|
||||||
|
CardInstance card = _handController.Cards[i];
|
||||||
|
if (card != playedCard && IsJunkForDnfAutoremove(card))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsJunkForDnfAutoremove(CardInstance card)
|
||||||
|
{
|
||||||
|
if (card == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string id = card.Definition?.Id ?? string.Empty;
|
||||||
|
string name = card.Definition?.DisplayName ?? string.Empty;
|
||||||
|
return card.Definition != null && card.Definition.IsToken
|
||||||
|
|| card.IsBroken
|
||||||
|
|| id.IndexOf("junk", StringComparison.OrdinalIgnoreCase) >= 0
|
||||||
|
|| id.IndexOf("nop", StringComparison.OrdinalIgnoreCase) >= 0
|
||||||
|
|| name.IndexOf("junk", StringComparison.OrdinalIgnoreCase) >= 0
|
||||||
|
|| name.IndexOf("NOP", StringComparison.OrdinalIgnoreCase) >= 0
|
||||||
|
|| name.IndexOf("broken", StringComparison.OrdinalIgnoreCase) >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DecayJavaWarmupForNextWave()
|
private void DecayJavaWarmupForNextWave()
|
||||||
@@ -981,9 +1088,9 @@ namespace KernelPanic.Combat
|
|||||||
return _fedoraCardsDiscountedThisTurn < limit;
|
return _fedoraCardsDiscountedThisTurn < limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ApplyFedoraGrowth(CardInstance card, bool rawhideChargeUsed)
|
private void ApplyFedoraGrowth(CardInstance card, bool rawhideChargeUsed, bool firstFedoraBonusThisTurn)
|
||||||
{
|
{
|
||||||
if (!IsDistro("fedora") || _runConfig.DistroVersion < 3 || card == null || !card.WasFirstCardThisTurn)
|
if (!IsDistro("fedora") || _runConfig.DistroVersion < 3 || card == null || !firstFedoraBonusThisTurn)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1739,6 +1846,7 @@ namespace KernelPanic.Combat
|
|||||||
_runManager?.AddBits(CombatTuning.BitsPerKill);
|
_runManager?.AddBits(CombatTuning.BitsPerKill);
|
||||||
HandleEnemyDeath(enemy);
|
HandleEnemyDeath(enemy);
|
||||||
_enemies.RemoveAt(i);
|
_enemies.RemoveAt(i);
|
||||||
|
RecalculateKernelRamPressure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1803,6 +1911,87 @@ namespace KernelPanic.Combat
|
|||||||
return candidates[index];
|
return candidates[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UnlockHandCards(string reason)
|
||||||
|
{
|
||||||
|
if (_handController == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int unlocked = 0;
|
||||||
|
for (int i = 0; i < _handController.Cards.Count; i++)
|
||||||
|
{
|
||||||
|
CardInstance card = _handController.Cards[i];
|
||||||
|
if (card != null && card.IsLocked)
|
||||||
|
{
|
||||||
|
card.IsLocked = false;
|
||||||
|
unlocked++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlocked > 0)
|
||||||
|
{
|
||||||
|
Log($"{reason}: unlocked {unlocked} card(s)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RecalculateKernelRamPressure()
|
||||||
|
{
|
||||||
|
if (_handController == null || _playerState == null || _runManager == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int penalty = 0;
|
||||||
|
for (int i = 0; i < _enemies.Count; i++)
|
||||||
|
{
|
||||||
|
EnemyInstance enemy = _enemies[i];
|
||||||
|
if (enemy.HasBehavior(EnemyBehaviorFlags.RamPressure) && !enemy.State.IsDefeated)
|
||||||
|
{
|
||||||
|
penalty += EnemyArchetypeCatalog.KernelPanicRamPenalty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_activeKernelRamPenalty = penalty;
|
||||||
|
int baseRam = _runManager.EffectiveRam();
|
||||||
|
int effectiveRam = Mathf.Max(1, baseRam - penalty);
|
||||||
|
_playerState.Ram = effectiveRam;
|
||||||
|
_handController.SetRamCapacity(effectiveRam);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyTelemetryCardPlayed()
|
||||||
|
{
|
||||||
|
int collectors = 0;
|
||||||
|
for (int i = 0; i < _enemies.Count; i++)
|
||||||
|
{
|
||||||
|
EnemyInstance enemy = _enemies[i];
|
||||||
|
if (enemy.HasBehavior(EnemyBehaviorFlags.TelemetryCollector) && !enemy.State.IsDefeated)
|
||||||
|
{
|
||||||
|
enemy.AddTelemetryStack();
|
||||||
|
collectors++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (collectors > 0)
|
||||||
|
{
|
||||||
|
Log($"telemetry collector profiled card: +{EnemyArchetypeCatalog.TelemetryDamageGrowthPerCard} damage");
|
||||||
|
StateChanged?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LockRandomHandCard(EnemyInstance source)
|
||||||
|
{
|
||||||
|
CardInstance card = RandomHandCard(cardInstance => cardInstance != null && !cardInstance.IsBroken && !cardInstance.IsLocked);
|
||||||
|
if (card == null)
|
||||||
|
{
|
||||||
|
Log("license check: no unlockable hand card");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
card.IsLocked = true;
|
||||||
|
Log($"{source.Name} locked {GetCardName(card)}");
|
||||||
|
}
|
||||||
|
|
||||||
private void BreakRacePair(EnemyInstance defeated)
|
private void BreakRacePair(EnemyInstance defeated)
|
||||||
{
|
{
|
||||||
EnemyInstance survivor = RacePartner(defeated);
|
EnemyInstance survivor = RacePartner(defeated);
|
||||||
@@ -1829,7 +2018,8 @@ namespace KernelPanic.Combat
|
|||||||
int count = Mathf.Clamp(CombatTuning.BaseEnemiesPerWave + countGrowth, CombatTuning.BaseEnemiesPerWave, CombatTuning.MaxEnemiesPerWave);
|
int count = Mathf.Clamp(CombatTuning.BaseEnemiesPerWave + countGrowth, CombatTuning.BaseEnemiesPerWave, CombatTuning.MaxEnemiesPerWave);
|
||||||
int waveUptimeBonus = Mathf.Max(0, wave - 1) * CombatTuning.EnemyUptimeGrowthPerWave;
|
int waveUptimeBonus = Mathf.Max(0, wave - 1) * CombatTuning.EnemyUptimeGrowthPerWave;
|
||||||
bool rootkitSpawned = false;
|
bool rootkitSpawned = false;
|
||||||
for (int i = 0; i < count; i++)
|
bool eliteSpawned = TrySpawnEliteEncounter(wave, waveUptimeBonus);
|
||||||
|
for (int i = eliteSpawned ? 1 : 0; i < count; i++)
|
||||||
{
|
{
|
||||||
string archetypeId = CreatePlaceholderArchetypeId(wave, i);
|
string archetypeId = CreatePlaceholderArchetypeId(wave, i);
|
||||||
if (archetypeId == "rootkit")
|
if (archetypeId == "rootkit")
|
||||||
@@ -1860,6 +2050,33 @@ namespace KernelPanic.Combat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TrySpawnEliteEncounter(int wave, int uptimeBonus)
|
||||||
|
{
|
||||||
|
if (wave < 5)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool guaranteedIntro = wave == 5;
|
||||||
|
bool rolledElite = RandomRoll.RollRange(1, 100, RollContext.None) <= 35;
|
||||||
|
if (!guaranteedIntro && !rolledElite)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnEnemy(CreateEliteArchetypeId(), uptimeBonus);
|
||||||
|
Log("elite process escorted by fodder");
|
||||||
|
// TODO: Replace this single-elite chance with authored elite encounter assets.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string CreateEliteArchetypeId()
|
||||||
|
{
|
||||||
|
string[] pool = { "daemon", "kernel_panic", "telemetry_collector", "drm_guardian" };
|
||||||
|
int index = RandomRoll.RollRange(0, pool.Length - 1, RollContext.None);
|
||||||
|
return pool[index];
|
||||||
|
}
|
||||||
|
|
||||||
private void SpawnRacePair(int uptimeBonus)
|
private void SpawnRacePair(int uptimeBonus)
|
||||||
{
|
{
|
||||||
int pairId = _nextRacePairId++;
|
int pairId = _nextRacePairId++;
|
||||||
@@ -2002,15 +2219,24 @@ namespace KernelPanic.Combat
|
|||||||
Log("TODO: buff intent needs enemy buff/status support.");
|
Log("TODO: buff intent needs enemy buff/status support.");
|
||||||
break;
|
break;
|
||||||
case EnemyIntentKind.Special:
|
case EnemyIntentKind.Special:
|
||||||
ExecuteSpecialEnemyIntent(enemy);
|
ExecuteSpecialEnemyIntent(enemy, value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
enemy.AdvanceCountdownAfterAction();
|
enemy.AdvanceCountdownAfterAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteSpecialEnemyIntent(EnemyInstance enemy)
|
private void ExecuteSpecialEnemyIntent(EnemyInstance enemy, int value)
|
||||||
{
|
{
|
||||||
|
if (enemy.HasBehavior(EnemyBehaviorFlags.CardLocker))
|
||||||
|
{
|
||||||
|
LockRandomHandCard(enemy);
|
||||||
|
value = ApplyIncomingAttackMitigation(value);
|
||||||
|
DamageResult result = _damagePipeline.DealDamage(new DamageRequest(enemy.State, _playerState, value, enemy.CurrentIntent.DamageType, enemy.CurrentIntent.TrueDamage, false));
|
||||||
|
GameEvents.RaisePlayerDamaged(new PlayerDamagedEvent(enemy, result.FinalAmount));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (enemy.HasBehavior(EnemyBehaviorFlags.SegfaultOnDeath))
|
if (enemy.HasBehavior(EnemyBehaviorFlags.SegfaultOnDeath))
|
||||||
{
|
{
|
||||||
enemy.AdvanceSegfaultCountdown();
|
enemy.AdvanceSegfaultCountdown();
|
||||||
|
|||||||
@@ -41,6 +41,12 @@ namespace KernelPanic.Combat
|
|||||||
public const int StatUpgradeHealPercent = 30;
|
public const int StatUpgradeHealPercent = 30;
|
||||||
public const int StatUpgradeRam = 1;
|
public const int StatUpgradeRam = 1;
|
||||||
public const int UpgradeMagnitudeBonus = 2;
|
public const int UpgradeMagnitudeBonus = 2;
|
||||||
|
public const int FedoraBleedingEdgeBaseCrashChance = 5;
|
||||||
|
public const int FedoraBleedingEdgeCrashChanceStep = 5;
|
||||||
|
public const int FedoraBleedingEdgeMaxCrashChance = 90;
|
||||||
|
public const int HitMagnitudeModeratePercent = 10;
|
||||||
|
public const int HitMagnitudeMajorPercent = 25;
|
||||||
|
public const int HitMagnitudeMassivePercent = 45;
|
||||||
|
|
||||||
public static int ScaleStatUpgradeMaxUptime(int maxUptime)
|
public static int ScaleStatUpgradeMaxUptime(int maxUptime)
|
||||||
{
|
{
|
||||||
@@ -61,4 +67,32 @@ namespace KernelPanic.Combat
|
|||||||
public const float EnemyTelegraphDelaySeconds = 0.35f;
|
public const float EnemyTelegraphDelaySeconds = 0.35f;
|
||||||
public const float EnemyActionDelaySeconds = 0.45f;
|
public const float EnemyActionDelaySeconds = 0.45f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum HitMagnitudeTier
|
||||||
|
{
|
||||||
|
Minor,
|
||||||
|
Moderate,
|
||||||
|
Major,
|
||||||
|
Massive
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HitMagnitude
|
||||||
|
{
|
||||||
|
public static HitMagnitudeTier Classify(int damageAmount, int targetMaxUptime, bool targetDefeated)
|
||||||
|
{
|
||||||
|
int maxUptime = UnityEngine.Mathf.Max(1, targetMaxUptime);
|
||||||
|
int amount = UnityEngine.Mathf.Max(0, damageAmount);
|
||||||
|
int percent = UnityEngine.Mathf.FloorToInt((amount * 100f) / maxUptime);
|
||||||
|
|
||||||
|
HitMagnitudeTier tier = percent >= CombatTuning.HitMagnitudeMassivePercent
|
||||||
|
? HitMagnitudeTier.Massive
|
||||||
|
: percent >= CombatTuning.HitMagnitudeMajorPercent
|
||||||
|
? HitMagnitudeTier.Major
|
||||||
|
: percent >= CombatTuning.HitMagnitudeModeratePercent
|
||||||
|
? HitMagnitudeTier.Moderate
|
||||||
|
: HitMagnitudeTier.Minor;
|
||||||
|
|
||||||
|
return targetDefeated && tier < HitMagnitudeTier.Major ? HitMagnitudeTier.Major : tier;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ namespace KernelPanic.Combat
|
|||||||
|
|
||||||
int finalAmount = ApplyShieldAndUptime(amount, request, out int absorbedAmount, out int shieldDamage, out int uptimeDamage);
|
int finalAmount = ApplyShieldAndUptime(amount, request, out int absorbedAmount, out int shieldDamage, out int uptimeDamage);
|
||||||
ApplyArchRollingReleaseSave(request);
|
ApplyArchRollingReleaseSave(request);
|
||||||
GameEvents.RaiseDamageDealt(new DamageDealtEvent(request.Source, request.Target, finalAmount, request.Language, amount, absorbedAmount, wasCritical, shieldDamage, uptimeDamage));
|
GameEvents.RaiseDamageDealt(new DamageDealtEvent(request.Source, request.Target, finalAmount, request.Language, amount, absorbedAmount, wasCritical, shieldDamage, uptimeDamage, request.TrueDamage));
|
||||||
|
|
||||||
bool defeated = false;
|
bool defeated = false;
|
||||||
if (request.Target.CurrentUptime <= 0 && !request.Target.IsDefeated)
|
if (request.Target.CurrentUptime <= 0 && !request.Target.IsDefeated)
|
||||||
|
|||||||
@@ -19,7 +19,16 @@ namespace KernelPanic.Combat
|
|||||||
SegfaultOnDeath = 1 << 7,
|
SegfaultOnDeath = 1 << 7,
|
||||||
RacePair = 1 << 8,
|
RacePair = 1 << 8,
|
||||||
RootkitMasked = 1 << 9,
|
RootkitMasked = 1 << 9,
|
||||||
Elite = 1 << 10
|
Elite = 1 << 10,
|
||||||
|
RamPressure = 1 << 11,
|
||||||
|
TelemetryCollector = 1 << 12,
|
||||||
|
CardLocker = 1 << 13
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EnemyTier
|
||||||
|
{
|
||||||
|
Fodder,
|
||||||
|
Elite
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class EnemyArchetypeDescriptor
|
public sealed class EnemyArchetypeDescriptor
|
||||||
@@ -30,7 +39,8 @@ namespace KernelPanic.Combat
|
|||||||
int baseUptimeMin,
|
int baseUptimeMin,
|
||||||
int baseUptimeMax,
|
int baseUptimeMax,
|
||||||
EnemyBehaviorFlags behaviorFlags,
|
EnemyBehaviorFlags behaviorFlags,
|
||||||
IReadOnlyList<EnemyIntent> intentPool)
|
IReadOnlyList<EnemyIntent> intentPool,
|
||||||
|
EnemyTier tier = EnemyTier.Fodder)
|
||||||
{
|
{
|
||||||
Id = string.IsNullOrWhiteSpace(id) ? "unknown_process" : id;
|
Id = string.IsNullOrWhiteSpace(id) ? "unknown_process" : id;
|
||||||
DisplayName = string.IsNullOrWhiteSpace(displayName) ? Id : displayName;
|
DisplayName = string.IsNullOrWhiteSpace(displayName) ? Id : displayName;
|
||||||
@@ -38,6 +48,7 @@ namespace KernelPanic.Combat
|
|||||||
BaseUptimeMax = Mathf.Max(BaseUptimeMin, baseUptimeMax);
|
BaseUptimeMax = Mathf.Max(BaseUptimeMin, baseUptimeMax);
|
||||||
BehaviorFlags = behaviorFlags;
|
BehaviorFlags = behaviorFlags;
|
||||||
IntentPool = intentPool ?? Array.Empty<EnemyIntent>();
|
IntentPool = intentPool ?? Array.Empty<EnemyIntent>();
|
||||||
|
Tier = tier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; }
|
public string Id { get; }
|
||||||
@@ -46,6 +57,7 @@ namespace KernelPanic.Combat
|
|||||||
public int BaseUptimeMax { get; }
|
public int BaseUptimeMax { get; }
|
||||||
public EnemyBehaviorFlags BehaviorFlags { get; }
|
public EnemyBehaviorFlags BehaviorFlags { get; }
|
||||||
public IReadOnlyList<EnemyIntent> IntentPool { get; }
|
public IReadOnlyList<EnemyIntent> IntentPool { get; }
|
||||||
|
public EnemyTier Tier { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -61,6 +73,9 @@ namespace KernelPanic.Combat
|
|||||||
public const int ForkBombTotalCap = 5;
|
public const int ForkBombTotalCap = 5;
|
||||||
public const int RootkitMaskedDamagePercent = 10;
|
public const int RootkitMaskedDamagePercent = 10;
|
||||||
public const int RacePairDamageBonus = 2;
|
public const int RacePairDamageBonus = 2;
|
||||||
|
public const int KernelPanicRamPenalty = 2;
|
||||||
|
public const int TelemetryDamageGrowthPerCard = 1;
|
||||||
|
public const int DrmUnlockCycleCost = 1;
|
||||||
public const int CronInitialCountdown = 2;
|
public const int CronInitialCountdown = 2;
|
||||||
public const int SegfaultInitialCountdown = 2;
|
public const int SegfaultInitialCountdown = 2;
|
||||||
|
|
||||||
@@ -106,7 +121,8 @@ namespace KernelPanic.Combat
|
|||||||
new[]
|
new[]
|
||||||
{
|
{
|
||||||
new EnemyIntent(EnemyIntentKind.Attack, 6, 8, Language.C, "daemon hit", "!")
|
new EnemyIntent(EnemyIntentKind.Attack, 6, 8, Language.C, "daemon hit", "!")
|
||||||
});
|
},
|
||||||
|
EnemyTier.Elite);
|
||||||
|
|
||||||
private static readonly EnemyArchetypeDescriptor Defender = new(
|
private static readonly EnemyArchetypeDescriptor Defender = new(
|
||||||
"firewalld",
|
"firewalld",
|
||||||
@@ -176,6 +192,42 @@ namespace KernelPanic.Combat
|
|||||||
new EnemyIntent(EnemyIntentKind.Attack, 1, 2, Language.C, "masked hit", "?")
|
new EnemyIntent(EnemyIntentKind.Attack, 1, 2, Language.C, "masked hit", "?")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private static readonly EnemyArchetypeDescriptor KernelPanic = new(
|
||||||
|
"kernel_panic",
|
||||||
|
"Kernel Panic",
|
||||||
|
25,
|
||||||
|
30,
|
||||||
|
EnemyBehaviorFlags.Elite | EnemyBehaviorFlags.RamPressure,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new EnemyIntent(EnemyIntentKind.Attack, 5, 7, Language.C, "panic hit", "!")
|
||||||
|
},
|
||||||
|
EnemyTier.Elite);
|
||||||
|
|
||||||
|
private static readonly EnemyArchetypeDescriptor TelemetryCollector = new(
|
||||||
|
"telemetry_collector",
|
||||||
|
"Telemetry Collector",
|
||||||
|
20,
|
||||||
|
25,
|
||||||
|
EnemyBehaviorFlags.Elite | EnemyBehaviorFlags.TelemetryCollector,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new EnemyIntent(EnemyIntentKind.Attack, 2, 3, Language.C, "profile hit", "!")
|
||||||
|
},
|
||||||
|
EnemyTier.Elite);
|
||||||
|
|
||||||
|
private static readonly EnemyArchetypeDescriptor DrmGuardian = new(
|
||||||
|
"drm_guardian",
|
||||||
|
"DRM Guardian",
|
||||||
|
25,
|
||||||
|
30,
|
||||||
|
EnemyBehaviorFlags.Elite | EnemyBehaviorFlags.CardLocker,
|
||||||
|
new[]
|
||||||
|
{
|
||||||
|
new EnemyIntent(EnemyIntentKind.Special, 4, 6, Language.C, "license check", "$")
|
||||||
|
},
|
||||||
|
EnemyTier.Elite);
|
||||||
|
|
||||||
public static EnemyArchetypeDescriptor Get(string id)
|
public static EnemyArchetypeDescriptor Get(string id)
|
||||||
{
|
{
|
||||||
return id switch
|
return id switch
|
||||||
@@ -189,6 +241,9 @@ namespace KernelPanic.Combat
|
|||||||
"segfault" => Segfault,
|
"segfault" => Segfault,
|
||||||
"race_condition" => RaceCondition,
|
"race_condition" => RaceCondition,
|
||||||
"rootkit" => Rootkit,
|
"rootkit" => Rootkit,
|
||||||
|
"kernel_panic" => KernelPanic,
|
||||||
|
"telemetry_collector" => TelemetryCollector,
|
||||||
|
"drm_guardian" => DrmGuardian,
|
||||||
_ => ZombieProcess
|
_ => ZombieProcess
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,14 +46,18 @@ namespace KernelPanic.Combat
|
|||||||
public bool LethalHitThisTurn { get; private set; }
|
public bool LethalHitThisTurn { get; private set; }
|
||||||
public int PairId { get; private set; } = -1;
|
public int PairId { get; private set; } = -1;
|
||||||
public int RaceEnrageStacks { get; private set; }
|
public int RaceEnrageStacks { get; private set; }
|
||||||
|
public int TelemetryStacks { get; private set; }
|
||||||
public bool SpawnedFromDeath { get; private set; }
|
public bool SpawnedFromDeath { get; private set; }
|
||||||
public bool HasPendingMarker => PendingRevive || HasRevived;
|
public bool HasPendingMarker => PendingRevive || HasRevived;
|
||||||
public bool HasSpecialSignal => HasSplitSignal || HasSegfaultSignal || HasRaceSignal || HasRootkitSignal || HasEliteSignal || SpawnedFromDeath;
|
public bool HasSpecialSignal => HasSplitSignal || HasSegfaultSignal || HasRaceSignal || HasRootkitSignal || HasEliteSignal || HasRamPressureSignal || HasTelemetrySignal || HasCardLockerSignal || SpawnedFromDeath;
|
||||||
public bool HasSplitSignal => HasBehavior(EnemyBehaviorFlags.Split);
|
public bool HasSplitSignal => HasBehavior(EnemyBehaviorFlags.Split);
|
||||||
public bool HasSegfaultSignal => HasBehavior(EnemyBehaviorFlags.SegfaultOnDeath);
|
public bool HasSegfaultSignal => HasBehavior(EnemyBehaviorFlags.SegfaultOnDeath);
|
||||||
public bool HasRaceSignal => HasBehavior(EnemyBehaviorFlags.RacePair);
|
public bool HasRaceSignal => HasBehavior(EnemyBehaviorFlags.RacePair);
|
||||||
public bool HasRootkitSignal => HasBehavior(EnemyBehaviorFlags.RootkitMasked);
|
public bool HasRootkitSignal => HasBehavior(EnemyBehaviorFlags.RootkitMasked);
|
||||||
public bool HasEliteSignal => HasBehavior(EnemyBehaviorFlags.Elite);
|
public bool HasEliteSignal => Archetype.Tier == EnemyTier.Elite || HasBehavior(EnemyBehaviorFlags.Elite);
|
||||||
|
public bool HasRamPressureSignal => HasBehavior(EnemyBehaviorFlags.RamPressure);
|
||||||
|
public bool HasTelemetrySignal => HasBehavior(EnemyBehaviorFlags.TelemetryCollector);
|
||||||
|
public bool HasCardLockerSignal => HasBehavior(EnemyBehaviorFlags.CardLocker);
|
||||||
public EnemyIntent DisplayIntent => BuildDisplayIntent();
|
public EnemyIntent DisplayIntent => BuildDisplayIntent();
|
||||||
|
|
||||||
public void PickNextIntent()
|
public void PickNextIntent()
|
||||||
@@ -93,6 +97,14 @@ namespace KernelPanic.Combat
|
|||||||
int maxValue = UnityEngine.Mathf.Min(CurrentIntent.MaxValue + growth, EnemyArchetypeCatalog.MemoryLeakAttackCapMax);
|
int maxValue = UnityEngine.Mathf.Min(CurrentIntent.MaxValue + growth, EnemyArchetypeCatalog.MemoryLeakAttackCapMax);
|
||||||
CurrentIntent = CurrentIntent.WithValues(minValue, maxValue, "growing leak");
|
CurrentIntent = CurrentIntent.WithValues(minValue, maxValue, "growing leak");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasBehavior(EnemyBehaviorFlags.TelemetryCollector) && CurrentIntent.Kind == EnemyIntentKind.Attack && TelemetryStacks > 0)
|
||||||
|
{
|
||||||
|
CurrentIntent = CurrentIntent.WithValues(
|
||||||
|
CurrentIntent.MinValue + TelemetryStacks,
|
||||||
|
CurrentIntent.MaxValue + TelemetryStacks,
|
||||||
|
"profiled hit");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool HasBehavior(EnemyBehaviorFlags behavior)
|
public bool HasBehavior(EnemyBehaviorFlags behavior)
|
||||||
@@ -128,6 +140,18 @@ namespace KernelPanic.Combat
|
|||||||
RaceEnrageStacks++;
|
RaceEnrageStacks++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddTelemetryStack()
|
||||||
|
{
|
||||||
|
TelemetryStacks += EnemyArchetypeCatalog.TelemetryDamageGrowthPerCard;
|
||||||
|
if (CurrentIntent.Kind == EnemyIntentKind.Attack)
|
||||||
|
{
|
||||||
|
CurrentIntent = CurrentIntent.WithValues(
|
||||||
|
CurrentIntent.MinValue + EnemyArchetypeCatalog.TelemetryDamageGrowthPerCard,
|
||||||
|
CurrentIntent.MaxValue + EnemyArchetypeCatalog.TelemetryDamageGrowthPerCard,
|
||||||
|
"profiled hit");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ResetTurnLethalMarker()
|
public void ResetTurnLethalMarker()
|
||||||
{
|
{
|
||||||
LethalHitThisTurn = false;
|
LethalHitThisTurn = false;
|
||||||
|
|||||||
@@ -54,6 +54,11 @@ namespace KernelPanic.Combat
|
|||||||
return card != null && _cards.Remove(card);
|
return card != null && _cards.Remove(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetRamCapacity(int ramCapacity)
|
||||||
|
{
|
||||||
|
RamCapacity = System.Math.Max(1, ramCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
public void Clear()
|
public void Clear()
|
||||||
{
|
{
|
||||||
_cards.Clear();
|
_cards.Clear();
|
||||||
|
|||||||
@@ -162,11 +162,16 @@ namespace KernelPanic.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DamageDealtEvent(CombatantState source, CombatantState target, int amount, Language language, int incomingAmount, int absorbedAmount, bool wasCritical)
|
public DamageDealtEvent(CombatantState source, CombatantState target, int amount, Language language, int incomingAmount, int absorbedAmount, bool wasCritical)
|
||||||
: this(source, target, amount, language, incomingAmount, absorbedAmount, wasCritical, 0, amount)
|
: this(source, target, amount, language, incomingAmount, absorbedAmount, wasCritical, 0, amount, false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public DamageDealtEvent(CombatantState source, CombatantState target, int amount, Language language, int incomingAmount, int absorbedAmount, bool wasCritical, int shieldDamage, int uptimeDamage)
|
public DamageDealtEvent(CombatantState source, CombatantState target, int amount, Language language, int incomingAmount, int absorbedAmount, bool wasCritical, int shieldDamage, int uptimeDamage)
|
||||||
|
: this(source, target, amount, language, incomingAmount, absorbedAmount, wasCritical, shieldDamage, uptimeDamage, false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DamageDealtEvent(CombatantState source, CombatantState target, int amount, Language language, int incomingAmount, int absorbedAmount, bool wasCritical, int shieldDamage, int uptimeDamage, bool trueDamage)
|
||||||
{
|
{
|
||||||
Source = source;
|
Source = source;
|
||||||
Target = target;
|
Target = target;
|
||||||
@@ -177,6 +182,7 @@ namespace KernelPanic.Core
|
|||||||
WasCritical = wasCritical;
|
WasCritical = wasCritical;
|
||||||
ShieldDamage = shieldDamage;
|
ShieldDamage = shieldDamage;
|
||||||
UptimeDamage = uptimeDamage;
|
UptimeDamage = uptimeDamage;
|
||||||
|
TrueDamage = trueDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CombatantState Source { get; }
|
public CombatantState Source { get; }
|
||||||
@@ -188,6 +194,7 @@ namespace KernelPanic.Core
|
|||||||
public bool WasCritical { get; }
|
public bool WasCritical { get; }
|
||||||
public int ShieldDamage { get; }
|
public int ShieldDamage { get; }
|
||||||
public int UptimeDamage { get; }
|
public int UptimeDamage { get; }
|
||||||
|
public bool TrueDamage { get; }
|
||||||
public bool WasFullyBlocked => IncomingAmount > 0 && UptimeDamage <= 0;
|
public bool WasFullyBlocked => IncomingAmount > 0 && UptimeDamage <= 0;
|
||||||
public bool WasMitigated => ShieldDamage > 0 || AbsorbedAmount > 0 || WasFullyBlocked;
|
public bool WasMitigated => ShieldDamage > 0 || AbsorbedAmount > 0 || WasFullyBlocked;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ namespace KernelPanic.UI
|
|||||||
private const string StyleResourcePath = "CombatScene";
|
private const string StyleResourcePath = "CombatScene";
|
||||||
private const string SharedScrollbarStyleResourcePath = "TerminalScrollbars";
|
private const string SharedScrollbarStyleResourcePath = "TerminalScrollbars";
|
||||||
private const string SharedRarityStyleResourcePath = "RarityPresentation";
|
private const string SharedRarityStyleResourcePath = "RarityPresentation";
|
||||||
|
private const int MaxFeedbackElements = 42;
|
||||||
private const string FilledCycle = "●";
|
private const string FilledCycle = "●";
|
||||||
private const string EmptyCycle = "○";
|
private const string EmptyCycle = "○";
|
||||||
|
|
||||||
@@ -66,6 +67,9 @@ namespace KernelPanic.UI
|
|||||||
private int _queueCascadeIndex;
|
private int _queueCascadeIndex;
|
||||||
private int _feedbackCascadeIndex;
|
private int _feedbackCascadeIndex;
|
||||||
private int _feedbackBeatVersion;
|
private int _feedbackBeatVersion;
|
||||||
|
private int _rootShakeVersion;
|
||||||
|
private int _hitStopVersion;
|
||||||
|
private int _lastHitStopFrame = -1;
|
||||||
|
|
||||||
private void Awake()
|
private void Awake()
|
||||||
{
|
{
|
||||||
@@ -143,6 +147,7 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
private void OnDisable()
|
private void OnDisable()
|
||||||
{
|
{
|
||||||
|
Time.timeScale = 1f;
|
||||||
SettleRunRewardsIfNeeded();
|
SettleRunRewardsIfNeeded();
|
||||||
_combatManager.StateChanged -= Refresh;
|
_combatManager.StateChanged -= Refresh;
|
||||||
_combatManager.CombatLog -= HandleCombatLog;
|
_combatManager.CombatLog -= HandleCombatLog;
|
||||||
@@ -432,6 +437,14 @@ namespace KernelPanic.UI
|
|||||||
Label ram = new($"ram hand cap {_combatManager.HandController.UsedRam}/{_combatManager.HandController.RamCapacity}");
|
Label ram = new($"ram hand cap {_combatManager.HandController.UsedRam}/{_combatManager.HandController.RamCapacity}");
|
||||||
ram.AddToClassList("ram-note");
|
ram.AddToClassList("ram-note");
|
||||||
_playerPanel.Add(ram);
|
_playerPanel.Add(ram);
|
||||||
|
if (_combatManager.ActiveKernelRamPenalty > 0)
|
||||||
|
{
|
||||||
|
Label ramPressure = new($"! kernel panic: RAM -{_combatManager.ActiveKernelRamPenalty}");
|
||||||
|
ramPressure.AddToClassList("status-label");
|
||||||
|
ramPressure.AddToClassList("intent-attack");
|
||||||
|
_playerPanel.Add(ramPressure);
|
||||||
|
}
|
||||||
|
|
||||||
_playerPanel.Add(StatusBlock(state, true));
|
_playerPanel.Add(StatusBlock(state, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,6 +478,7 @@ namespace KernelPanic.UI
|
|||||||
card.text = string.Empty;
|
card.text = string.Empty;
|
||||||
card.AddToClassList("enemy-card");
|
card.AddToClassList("enemy-card");
|
||||||
card.EnableInClassList("enemy-card-compact", compactEnemies);
|
card.EnableInClassList("enemy-card-compact", compactEnemies);
|
||||||
|
card.EnableInClassList("enemy-card-elite", enemy.HasEliteSignal);
|
||||||
bool highlighted = _combatManager.PendingTargetCard != null || _combatManager.SelectedEnemyIndex == index;
|
bool highlighted = _combatManager.PendingTargetCard != null || _combatManager.SelectedEnemyIndex == index;
|
||||||
if (highlighted)
|
if (highlighted)
|
||||||
{
|
{
|
||||||
@@ -823,7 +837,16 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
private VisualElement CardFace(CardInstance card)
|
private VisualElement CardFace(CardInstance card)
|
||||||
{
|
{
|
||||||
Button button = new(() => PlayCardWithFeedback(card));
|
Button button = new(() =>
|
||||||
|
{
|
||||||
|
if (card.IsLocked)
|
||||||
|
{
|
||||||
|
UnlockCardWithFeedback(card);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayCardWithFeedback(card);
|
||||||
|
});
|
||||||
button.text = string.Empty;
|
button.text = string.Empty;
|
||||||
button.AddToClassList("hand-card");
|
button.AddToClassList("hand-card");
|
||||||
if (_combatManager.PendingTargetCard == card)
|
if (_combatManager.PendingTargetCard == card)
|
||||||
@@ -839,7 +862,7 @@ namespace KernelPanic.UI
|
|||||||
if (card.IsBroken || card.IsLocked)
|
if (card.IsBroken || card.IsLocked)
|
||||||
{
|
{
|
||||||
button.AddToClassList("hand-card-unaffordable");
|
button.AddToClassList("hand-card-unaffordable");
|
||||||
button.SetEnabled(false);
|
button.SetEnabled(!card.IsBroken);
|
||||||
}
|
}
|
||||||
|
|
||||||
PopulateCardFace(button, card);
|
PopulateCardFace(button, card);
|
||||||
@@ -874,7 +897,7 @@ namespace KernelPanic.UI
|
|||||||
|
|
||||||
if (card.IsBroken || card.IsLocked)
|
if (card.IsBroken || card.IsLocked)
|
||||||
{
|
{
|
||||||
Label state = new(card.IsBroken ? "segv corrupt" : "locked");
|
Label state = new(card.IsBroken ? "segv corrupt" : $"locked: pay {EnemyArchetypeCatalog.DrmUnlockCycleCost}c");
|
||||||
state.AddToClassList("status-label");
|
state.AddToClassList("status-label");
|
||||||
target.Add(state);
|
target.Add(state);
|
||||||
}
|
}
|
||||||
@@ -926,6 +949,21 @@ namespace KernelPanic.UI
|
|||||||
row.Add(Tag("! elite", "intent-attack"));
|
row.Add(Tag("! elite", "intent-attack"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enemy.HasRamPressureSignal)
|
||||||
|
{
|
||||||
|
row.Add(Tag($"# RAM -{EnemyArchetypeCatalog.KernelPanicRamPenalty}", "intent-attack"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enemy.HasTelemetrySignal)
|
||||||
|
{
|
||||||
|
row.Add(Tag($"+ profile {enemy.TelemetryStacks}", "intent-special"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enemy.HasCardLockerSignal)
|
||||||
|
{
|
||||||
|
row.Add(Tag($"$ lock {EnemyArchetypeCatalog.DrmUnlockCycleCost}c", "intent-special"));
|
||||||
|
}
|
||||||
|
|
||||||
if (enemy.HasSplitSignal)
|
if (enemy.HasSplitSignal)
|
||||||
{
|
{
|
||||||
string text = CountLivingArchetype(enemies, enemy.ArchetypeId) >= EnemyArchetypeCatalog.ForkBombTotalCap
|
string text = CountLivingArchetype(enemies, enemy.ArchetypeId) >= EnemyArchetypeCatalog.ForkBombTotalCap
|
||||||
@@ -1155,6 +1193,29 @@ namespace KernelPanic.UI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool UnlockCardWithFeedback(CardInstance card)
|
||||||
|
{
|
||||||
|
int cyclesBefore = _combatManager.PlayerState == null ? 0 : _combatManager.PlayerState.Cycles;
|
||||||
|
bool unlocked = _combatManager.TryUnlockCard(card);
|
||||||
|
if (unlocked)
|
||||||
|
{
|
||||||
|
int spent = Mathf.Max(0, cyclesBefore - (_combatManager.PlayerState == null ? cyclesBefore : _combatManager.PlayerState.Cycles));
|
||||||
|
if (spent > 0)
|
||||||
|
{
|
||||||
|
PlayElementBeat(_turnResourceGrid, "cycles-spent-beat", 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_handCardElements.TryGetValue(card, out VisualElement face))
|
||||||
|
{
|
||||||
|
PlayElementBeat(face, "feedback-denied", 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void HandleCardPlayed(CardPlayedEvent payload)
|
private void HandleCardPlayed(CardPlayedEvent payload)
|
||||||
{
|
{
|
||||||
if (payload.Card == null)
|
if (payload.Card == null)
|
||||||
@@ -1216,24 +1277,30 @@ namespace KernelPanic.UI
|
|||||||
{
|
{
|
||||||
bool isPlayer = payload.Target == _combatManager.PlayerState;
|
bool isPlayer = payload.Target == _combatManager.PlayerState;
|
||||||
VisualElement target = CombatantElement(payload.Target);
|
VisualElement target = CombatantElement(payload.Target);
|
||||||
|
int magnitudeAmount = payload.UptimeDamage > 0 ? payload.UptimeDamage : payload.ShieldDamage > 0 ? payload.ShieldDamage : payload.Amount;
|
||||||
|
bool defeated = payload.Target != null && payload.Target.CurrentUptime <= 0;
|
||||||
|
HitMagnitudeTier tier = HitMagnitude.Classify(magnitudeAmount, payload.Target?.MaxUptime ?? 1, defeated);
|
||||||
|
bool shieldOnly = payload.WasFullyBlocked && payload.ShieldDamage > 0;
|
||||||
ScheduleCombatBeat(beatIndex =>
|
ScheduleCombatBeat(beatIndex =>
|
||||||
{
|
{
|
||||||
string text = payload.WasFullyBlocked ? "clang" : payload.UptimeDamage <= 0 ? "blocked" : $"-{Mathf.RoundToInt(payload.UptimeDamage)}";
|
string bypass = payload.TrueDamage ? "! " : string.Empty;
|
||||||
string tone = payload.WasFullyBlocked || payload.WasMitigated ? "float-muted" : "float-damage";
|
string text = shieldOnly
|
||||||
string beatClass = payload.WasFullyBlocked || payload.WasMitigated ? "feedback-clang" : payload.WasCritical ? "feedback-crit" : "feedback-hit";
|
? $"{bypass}block {payload.ShieldDamage}"
|
||||||
int beatDuration = payload.WasCritical ? 360 : payload.WasMitigated ? 260 : 240;
|
: payload.WasFullyBlocked
|
||||||
|
? $"{bypass}blocked"
|
||||||
if (payload.WasCritical || payload.UptimeDamage >= 8)
|
: $"{bypass}-{Mathf.RoundToInt(payload.UptimeDamage)}";
|
||||||
{
|
string tone = BuildDamageFloatClasses(tier, payload, shieldOnly);
|
||||||
tone += " float-large";
|
string beatClass = BuildDamageBeatClass(tier, payload, shieldOnly);
|
||||||
}
|
int beatDuration = HitBeatDuration(tier, payload.WasMitigated);
|
||||||
|
|
||||||
|
TriggerHitStop(tier);
|
||||||
|
TriggerRootShake(tier, isPlayer);
|
||||||
SpawnFloatingText(target, text, tone, beatIndex);
|
SpawnFloatingText(target, text, tone, beatIndex);
|
||||||
SpawnImpactMarker(target, payload.WasCritical, payload.WasMitigated, beatIndex);
|
SpawnImpactMarker(target, payload.WasCritical, payload.WasMitigated, beatIndex, tier, shieldOnly);
|
||||||
PlayElementBeat(target, beatClass, beatDuration);
|
PlayElementBeat(target, beatClass, beatDuration);
|
||||||
if (isPlayer && payload.UptimeDamage > 0)
|
if (isPlayer && payload.UptimeDamage > 0)
|
||||||
{
|
{
|
||||||
FlashDamageVignette(payload.WasCritical);
|
FlashDamageVignette(tier);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1308,6 +1375,80 @@ namespace KernelPanic.UI
|
|||||||
_previousShield[state] = state.Shield;
|
_previousShield[state] = state.Shield;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string BuildDamageFloatClasses(HitMagnitudeTier tier, DamageDealtEvent payload, bool shieldOnly)
|
||||||
|
{
|
||||||
|
string classes = shieldOnly || payload.WasFullyBlocked ? "float-shield" : "float-damage";
|
||||||
|
classes += " " + TierFloatClass(tier);
|
||||||
|
if (payload.TrueDamage)
|
||||||
|
{
|
||||||
|
classes += " float-true";
|
||||||
|
}
|
||||||
|
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string BuildDamageBeatClass(HitMagnitudeTier tier, DamageDealtEvent payload, bool shieldOnly)
|
||||||
|
{
|
||||||
|
if (shieldOnly || payload.WasFullyBlocked)
|
||||||
|
{
|
||||||
|
return tier >= HitMagnitudeTier.Major ? "feedback-clang-major" : "feedback-clang";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.WasCritical || tier == HitMagnitudeTier.Massive)
|
||||||
|
{
|
||||||
|
return "feedback-crit";
|
||||||
|
}
|
||||||
|
|
||||||
|
return tier switch
|
||||||
|
{
|
||||||
|
HitMagnitudeTier.Minor => "feedback-hit-minor",
|
||||||
|
HitMagnitudeTier.Moderate => "feedback-hit",
|
||||||
|
HitMagnitudeTier.Major => "feedback-hit-major",
|
||||||
|
_ => "feedback-crit"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TierFloatClass(HitMagnitudeTier tier)
|
||||||
|
{
|
||||||
|
return tier switch
|
||||||
|
{
|
||||||
|
HitMagnitudeTier.Minor => "float-tier-minor",
|
||||||
|
HitMagnitudeTier.Moderate => "float-tier-moderate",
|
||||||
|
HitMagnitudeTier.Major => "float-tier-major",
|
||||||
|
HitMagnitudeTier.Massive => "float-tier-massive",
|
||||||
|
_ => "float-tier-moderate"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TierImpactClass(HitMagnitudeTier tier)
|
||||||
|
{
|
||||||
|
return tier switch
|
||||||
|
{
|
||||||
|
HitMagnitudeTier.Minor => "impact-tier-minor",
|
||||||
|
HitMagnitudeTier.Moderate => "impact-tier-moderate",
|
||||||
|
HitMagnitudeTier.Major => "impact-tier-major",
|
||||||
|
HitMagnitudeTier.Massive => "impact-tier-massive",
|
||||||
|
_ => "impact-tier-moderate"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int HitBeatDuration(HitMagnitudeTier tier, bool mitigated)
|
||||||
|
{
|
||||||
|
if (mitigated && tier < HitMagnitudeTier.Major)
|
||||||
|
{
|
||||||
|
return 220;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tier switch
|
||||||
|
{
|
||||||
|
HitMagnitudeTier.Minor => 120,
|
||||||
|
HitMagnitudeTier.Moderate => 220,
|
||||||
|
HitMagnitudeTier.Major => 340,
|
||||||
|
HitMagnitudeTier.Massive => 460,
|
||||||
|
_ => 220
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private VisualElement CombatantElement(CombatantState state)
|
private VisualElement CombatantElement(CombatantState state)
|
||||||
{
|
{
|
||||||
if (state != null && _combatantElements.TryGetValue(state, out VisualElement element))
|
if (state != null && _combatantElements.TryGetValue(state, out VisualElement element))
|
||||||
@@ -1375,6 +1516,7 @@ namespace KernelPanic.UI
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PruneFeedbackLayer();
|
||||||
Rect anchorRect = anchor.worldBound;
|
Rect anchorRect = anchor.worldBound;
|
||||||
Rect rootRect = _root.worldBound;
|
Rect rootRect = _root.worldBound;
|
||||||
Label label = new(text);
|
Label label = new(text);
|
||||||
@@ -1412,18 +1554,20 @@ namespace KernelPanic.UI
|
|||||||
ScheduleFeedback(() => label.RemoveFromHierarchy(), 520);
|
ScheduleFeedback(() => label.RemoveFromHierarchy(), 520);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SpawnImpactMarker(VisualElement anchor, bool critical, bool mitigated, int cascadeOffset)
|
private void SpawnImpactMarker(VisualElement anchor, bool critical, bool mitigated, int cascadeOffset, HitMagnitudeTier tier = HitMagnitudeTier.Moderate, bool shieldOnly = false)
|
||||||
{
|
{
|
||||||
if (_feedbackLayer == null || anchor == null)
|
if (_feedbackLayer == null || anchor == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PruneFeedbackLayer();
|
||||||
Rect anchorRect = anchor.worldBound;
|
Rect anchorRect = anchor.worldBound;
|
||||||
Rect rootRect = _root.worldBound;
|
Rect rootRect = _root.worldBound;
|
||||||
Label marker = new(critical ? "crit" : mitigated ? "clang" : "hit");
|
Label marker = new(shieldOnly ? "shield" : critical ? "crit" : mitigated ? "clang" : "hit");
|
||||||
marker.AddToClassList("impact-marker");
|
marker.AddToClassList("impact-marker");
|
||||||
marker.AddToClassList(critical ? "impact-marker-crit" : mitigated ? "impact-marker-clang" : "impact-marker-hit");
|
marker.AddToClassList(shieldOnly || mitigated ? "impact-marker-clang" : critical ? "impact-marker-crit" : "impact-marker-hit");
|
||||||
|
marker.AddToClassList(TierImpactClass(tier));
|
||||||
|
|
||||||
float lane = (cascadeOffset % 4) * 18f;
|
float lane = (cascadeOffset % 4) * 18f;
|
||||||
marker.style.left = anchorRect.xMax - rootRect.x - 54f;
|
marker.style.left = anchorRect.xMax - rootRect.x - 54f;
|
||||||
@@ -1444,6 +1588,65 @@ namespace KernelPanic.UI
|
|||||||
ScheduleFeedback(() => marker.RemoveFromHierarchy(), 420);
|
ScheduleFeedback(() => marker.RemoveFromHierarchy(), 420);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TriggerHitStop(HitMagnitudeTier tier)
|
||||||
|
{
|
||||||
|
if (UIPreferences.ReducedMotion || tier < HitMagnitudeTier.Major || Time.frameCount == _lastHitStopFrame)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastHitStopFrame = Time.frameCount;
|
||||||
|
int version = ++_hitStopVersion;
|
||||||
|
float duration = tier == HitMagnitudeTier.Massive ? 0.16f : 0.09f;
|
||||||
|
Time.timeScale = 0.04f;
|
||||||
|
ScheduleFeedback(() =>
|
||||||
|
{
|
||||||
|
if (_hitStopVersion == version)
|
||||||
|
{
|
||||||
|
Time.timeScale = 1f;
|
||||||
|
}
|
||||||
|
}, Mathf.RoundToInt(duration * 1000f));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TriggerRootShake(HitMagnitudeTier tier, bool playerHit)
|
||||||
|
{
|
||||||
|
if (UIPreferences.ReducedMotion || tier < HitMagnitudeTier.Major || _root == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int version = ++_rootShakeVersion;
|
||||||
|
_root.RemoveFromClassList("root-shake-major");
|
||||||
|
_root.RemoveFromClassList("root-shake-massive");
|
||||||
|
_root.RemoveFromClassList("root-shake-player");
|
||||||
|
_root.AddToClassList(tier == HitMagnitudeTier.Massive ? "root-shake-massive" : "root-shake-major");
|
||||||
|
_root.EnableInClassList("root-shake-player", playerHit);
|
||||||
|
ScheduleFeedback(() =>
|
||||||
|
{
|
||||||
|
if (_rootShakeVersion != version)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_root.RemoveFromClassList("root-shake-major");
|
||||||
|
_root.RemoveFromClassList("root-shake-massive");
|
||||||
|
_root.RemoveFromClassList("root-shake-player");
|
||||||
|
}, tier == HitMagnitudeTier.Massive ? 220 : 140);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PruneFeedbackLayer()
|
||||||
|
{
|
||||||
|
if (_feedbackLayer == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (_feedbackLayer.childCount >= MaxFeedbackElements)
|
||||||
|
{
|
||||||
|
_feedbackLayer.ElementAt(0).RemoveFromHierarchy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void FlyCardGhost(CardInstance card, VisualElement source, VisualElement destination, string className)
|
private void FlyCardGhost(CardInstance card, VisualElement source, VisualElement destination, string className)
|
||||||
{
|
{
|
||||||
if (_feedbackLayer == null || card == null)
|
if (_feedbackLayer == null || card == null)
|
||||||
@@ -1514,21 +1717,29 @@ namespace KernelPanic.UI
|
|||||||
ScheduleFeedback(() => ghost.RemoveFromHierarchy(), 360);
|
ScheduleFeedback(() => ghost.RemoveFromHierarchy(), 360);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FlashDamageVignette(bool critical)
|
private void FlashDamageVignette(HitMagnitudeTier tier)
|
||||||
{
|
{
|
||||||
if (_damageVignette == null)
|
if (_damageVignette == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayElementBeat(_damageVignette, critical ? "damage-vignette-crit" : "damage-vignette-on", UIPreferences.ReducedMotion ? 120 : critical ? 320 : 240);
|
string className = tier >= HitMagnitudeTier.Massive
|
||||||
|
? "damage-vignette-massive"
|
||||||
|
: tier >= HitMagnitudeTier.Major
|
||||||
|
? "damage-vignette-crit"
|
||||||
|
: "damage-vignette-on";
|
||||||
|
PlayElementBeat(_damageVignette, className, UIPreferences.ReducedMotion ? 120 : tier >= HitMagnitudeTier.Massive ? 360 : tier >= HitMagnitudeTier.Major ? 300 : 180);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RemoveFeedbackBeatClasses(VisualElement element)
|
private static void RemoveFeedbackBeatClasses(VisualElement element)
|
||||||
{
|
{
|
||||||
|
element.RemoveFromClassList("feedback-hit-minor");
|
||||||
element.RemoveFromClassList("feedback-hit");
|
element.RemoveFromClassList("feedback-hit");
|
||||||
|
element.RemoveFromClassList("feedback-hit-major");
|
||||||
element.RemoveFromClassList("feedback-crit");
|
element.RemoveFromClassList("feedback-crit");
|
||||||
element.RemoveFromClassList("feedback-clang");
|
element.RemoveFromClassList("feedback-clang");
|
||||||
|
element.RemoveFromClassList("feedback-clang-major");
|
||||||
element.RemoveFromClassList("feedback-block");
|
element.RemoveFromClassList("feedback-block");
|
||||||
element.RemoveFromClassList("feedback-status");
|
element.RemoveFromClassList("feedback-status");
|
||||||
element.RemoveFromClassList("feedback-boost");
|
element.RemoveFromClassList("feedback-boost");
|
||||||
@@ -1543,6 +1754,7 @@ namespace KernelPanic.UI
|
|||||||
element.RemoveFromClassList("hand-card-drawn");
|
element.RemoveFromClassList("hand-card-drawn");
|
||||||
element.RemoveFromClassList("damage-vignette-on");
|
element.RemoveFromClassList("damage-vignette-on");
|
||||||
element.RemoveFromClassList("damage-vignette-crit");
|
element.RemoveFromClassList("damage-vignette-crit");
|
||||||
|
element.RemoveFromClassList("damage-vignette-massive");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ScheduleCombatBeat(Action action, int holdMs = 0)
|
private void ScheduleCombatBeat(Action action, int holdMs = 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user