Etterna 0.74.4
Loading...
Searching...
No Matches
RunningMan.h
1#pragma once
2#include "../../PatternModHelpers.h"
3#include "../HD_Sequencers/RMSequencing.h"
4
17{
18 const CalcPatternMod _pmod = RanMan;
19 const std::string name = "RunningManMod";
20
21#pragma region params
22
23 float min_mod = 1.F;
24 float max_mod = 1.1F;
25 float base = 0.5F;
26 float min_anchor_len = 5.F;
27 float min_taps_in_rm = 1.F;
28 float min_off_taps_same = 1.F;
29
30 float offhand_tap_prop_scaler = 1.F;
31 float offhand_tap_prop_min = 0.F;
32 float offhand_tap_prop_max = 1.F;
33 float offhand_tap_prop_base = 1.7F;
34
35 float offhand_tap_prop_anch_diff_base = 1.7F;
36 float offhand_tap_prop_anch_diff_scaler = 1.1F;
37 float offhand_tap_prop_anch_diff_min = 0.75F;
38 float offhand_tap_prop_anch_diff_max = 1.F;
39
40 float off_tap_same_prop_scaler = 1.F;
41 float off_tap_same_prop_min = 0.F;
42 float off_tap_same_prop_max = 1.25F;
43 float off_tap_same_prop_base = 0.8F;
44
45 float anchor_len_divisor = 5.F;
46 float anchor_len_comp_min = 0.F;
47 float anchor_len_comp_max = 1.25F;
48
49 float min_jack_taps_for_bonus = 1.F;
50 float jack_bonus_base = 0.1F;
51
52 float min_oht_taps_for_bonus = 1.F;
53 float oht_bonus_base = 0.1F;
54
55 // params for rm_sequencing, these define conditions for resetting
56 // runningmen sequences
57 float max_oht_len = 2.F;
58 float max_off_len = 3.F;
59 float max_ot_sh_len = 2.F;
60 float max_burst_len = 6.F;
61 float max_jack_len = 3.F;
62 float max_anch_len = 5.F;
63
64 const std::vector<std::pair<std::string, float*>> _params{
65
66 { "min_mod", &min_mod },
67 { "max_mod", &max_mod },
68 { "base", &base },
69
70 { "min_anchor_len", &min_anchor_len },
71 { "min_taps_in_rm", &min_taps_in_rm },
72 { "min_off_taps_same", &min_off_taps_same },
73
74 { "offhand_tap_prop_scaler", &offhand_tap_prop_scaler },
75 { "offhand_tap_prop_min", &offhand_tap_prop_min },
76 { "offhand_tap_prop_max", &offhand_tap_prop_max },
77 { "offhand_tap_prop_base", &offhand_tap_prop_base },
78
79 { "offhand_tap_prop_anch_diff_base", &offhand_tap_prop_anch_diff_base },
80 { "offhand_tap_prop_anch_diff_scaler", &offhand_tap_prop_anch_diff_scaler },
81 { "offhand_tap_prop_anch_diff_min", &offhand_tap_prop_anch_diff_min },
82 { "offhand_tap_prop_anch_diff_max", &offhand_tap_prop_anch_diff_max },
83
84 { "off_tap_same_prop_scaler", &off_tap_same_prop_scaler },
85 { "off_tap_same_prop_min", &off_tap_same_prop_min },
86 { "off_tap_same_prop_max", &off_tap_same_prop_max },
87 { "off_tap_same_prop_base", &off_tap_same_prop_base },
88
89 { "anchor_len_divisor", &anchor_len_divisor },
90
91 { "min_jack_taps_for_bonus", &min_jack_taps_for_bonus },
92 { "jack_bonus_base", &jack_bonus_base },
93
94 { "min_oht_taps_for_bonus", &min_oht_taps_for_bonus },
95 { "oht_bonus_base", &oht_bonus_base },
96
97 // params for rm_sequencing
98 { "max_oht_len", &max_oht_len },
99 { "max_off_len", &max_off_len },
100 { "max_ot_sh_len", &max_ot_sh_len },
101 { "max_burst_len", &max_burst_len },
102 { "max_jack_len", &max_jack_len },
103 { "max_anch_len", &max_anch_len },
104 };
105#pragma endregion params and param map
106
107 // stuff for making mod
108 std::array<RM_Sequencer, num_cols_per_hand> rms;
109
110 // for an interval, active rm sequence with the highest difficulty
111 RM_Sequencer highest_rm;
112
113 int test = 0;
114 float offhand_tap_prop = 0.F;
115 float off_tap_same_prop = 0.F;
116
117 float anchor_len_comp = 0.F;
118 float jack_bonus = 0.F;
119 float oht_bonus = 0.F;
120
121 float pmod = neutral;
122
123 void full_reset()
124 {
125 for (auto& rm : rms) {
126 rm.full_reset();
127 }
128
129 offhand_tap_prop = 0.F;
130 off_tap_same_prop = 0.F;
131
132 anchor_len_comp = 0.F;
133 jack_bonus = 0.F;
134 oht_bonus = 0.F;
135
136 pmod = neutral;
137 }
138
139 /* keep parallel rm sequencers for both left and right column for each
140 * hand, this way we don't have to worry about trying to figure out
141 * which column the runningman anchor should be on */
142 void setup()
143 {
144 for (const auto& c : ct_loop_no_jumps) {
145 rms.at(c)._ct = c;
146 rms.at(c).set_params(max_oht_len,
147 max_off_len,
148 max_ot_sh_len,
149 max_burst_len,
150 max_jack_len,
151 max_anch_len);
152 }
153 }
154
155 void advance_off_hand_sequencing()
156 {
157 for (const auto& c : ct_loop_no_jumps) {
158 rms.at(c).advance_off_hand_sequencing();
159 }
160 }
161
162 void advance_sequencing(const col_type& ct,
163 const base_type& bt,
164 const meta_type& mt,
165 const AnchorSequencer& as)
166 {
167 for (const auto& c : ct_loop_no_jumps) {
168 rms.at(c)(ct, bt, mt, *as.anch.at(c));
169 }
170
171 highest_rm = get_active_rm_with_higher_difficulty();
172 }
173
174 [[nodiscard]] auto get_highest_anchor_difficulty() const -> float
175 {
176
177 /* see off_hand_tap_prop for a detailed explanation, basically only the
178 * rm mod was downscaling rolls, short burst rolls that escaped roll
179 * detection but flagged high on on rm diff were super overrated without
180 * this adjustment, so we should probably calculated and apply it here
181 * as well- this is called immediately after advance_sequencing, so
182 * we've already determined which sequence to use as rm */
183
184 float oht_p = offhand_tap_prop_anch_diff_base -
185 (highest_rm._rm.get_offhand_tap_prop() *
186 offhand_tap_prop_anch_diff_scaler);
187
188 oht_p = std::clamp(oht_p,
189 offhand_tap_prop_anch_diff_min,
190 offhand_tap_prop_anch_diff_max);
191
192 return highest_rm.get_difficulty() * oht_p;
193 }
194
195 [[nodiscard]] auto get_active_rm_with_higher_difficulty() const
196 -> RM_Sequencer
197 {
198 if (rms.at(col_left)._status == rm_running &&
199 rms.at(col_right)._status == rm_running) {
200
201 return rms.at(col_left).get_difficulty() >
202 rms.at(col_right).get_difficulty()
203 ? rms.at(col_left)
204 : rms.at(col_right);
205 }
206
207 return rms.at(col_left)._status == rm_running ? rms.at(col_left)
208 : rms.at(col_right);
209 }
210
211 /* Note: this mod is only used for pushing up runningmen focused stream/js
212 * _patterns_, the anchor difficulty isn't used here, that's used in tech.
213 * since we don't have to push the mod to extreme levels anymore to get
214 * runningmen registering in tech, we can tune this mod with the expectation
215 * that it will push up some files that don't need to be pushed up */
216 void set_pmod(const int& total_taps)
217 {
218 /* nothing here */
219 if (total_taps == 0) {
220 pmod = neutral;
221 return;
222 }
223
224 const auto& rm = highest_rm._rm;
225
226 /* we could decay in this but it may conflict/be redundant with how
227 * runningmen sequences are constructed, if decays are used we would
228 * probably generate the mod not from the highest of any interval, but
229 * from whatever sequences are still alive by the end */
230
231 // min mod optimization
232 if (rm._len < min_anchor_len || rm.ran_taps < min_taps_in_rm ||
233 rm.off_taps_sh < min_off_taps_same) {
234 pmod = min_mod;
235 return;
236 }
237
238 /* the larger the share of off hand taps to anchor taps, the higher the
239 * probability we're just looking at something like rolls, assuming
240 * 1234123412341234 (which may technically count as a runningmen
241 * depending on the logic params), off hand taps will outnumber the
242 * anchor taps 2:1, whereas traditional runningmen generally have under
243 * a 1:1 ratio (1:1 but this will be split among same hand off anchor
244 * taps as well, essentially, here the lower the better (as long as we
245 * properly eliminate ohts from registering)). It seems prudent for this
246 * to only be a nerf. Note: this will also push down detection for heavy
247 * ohjump usage on the other hand, which is probably? good */
248 offhand_tap_prop = offhand_tap_prop_base - (rm.get_offhand_tap_prop() *
249 offhand_tap_prop_scaler);
250 offhand_tap_prop = std::clamp(
251 offhand_tap_prop, offhand_tap_prop_min, offhand_tap_prop_max);
252
253 /* number of same hand off anchor taps / anchor taps, basically stuffs
254 * is really hard when this is high (a value of 0.5 is a triplet every
255 * other anchor), although actually really anything above 0.75 is
256 * basically oht, but we should be screening out pure ohts from this
257 * anyway, actually actually rolls will technically have a 1:1 ratio
258 * between anchor taps and same hand taps as well.. but those should
259 * also be diluted via detection logic and hopefully other things, if
260 * not we could do something with 0.5 designated as an inflection point
261 * but that seems hacky */
262 off_tap_same_prop =
263 off_tap_same_prop_base +
264 (rm.get_off_tap_same_prop() * off_tap_same_prop_scaler);
265
266 off_tap_same_prop = std::clamp(
267 off_tap_same_prop, off_tap_same_prop_min, off_tap_same_prop_max);
268
269 /* anchor length component, we want longer runningmen to inherently
270 * register more strongly, but not to an infinite degree, we don't want
271 * long sequences of slow runningmen to ramp up the mod to max value
272 * just through length alone */
273 anchor_len_comp = static_cast<float>(rm._len) / anchor_len_divisor;
274 anchor_len_comp =
275 std::clamp(anchor_len_comp, anchor_len_comp_min, anchor_len_comp_max);
276
277 // jacks in anchor component, give a small bonus i guess
278 jack_bonus =
279 rm.jack_taps >= min_jack_taps_for_bonus ? jack_bonus_base : 0.F;
280
281 // ohts in anchor component, give a small bonus i guess
282 oht_bonus =
283 rm.oht_taps >= min_oht_taps_for_bonus ? oht_bonus_base : 0.F;
284
285 pmod = base + anchor_len_comp + jack_bonus + oht_bonus;
286 pmod = std::clamp(fastsqrt(pmod * off_tap_same_prop * offhand_tap_prop),
287 min_mod,
288 max_mod);
289 }
290
291 [[nodiscard]] auto operator()(const int& total_taps) -> float
292 {
293 set_pmod(total_taps);
294
295 interval_end();
296 return pmod;
297 }
298
299 void interval_end() { highest_rm.full_reset(); }
300};
Definition GenericSequencing.h:308
std::array< std::unique_ptr< Anchor_Sequencing >, num_cols_per_hand > anch
anchor sequencers for each finger
Definition GenericSequencing.h:310
Definition RMSequencing.h:178
Definition RunningMan.h:17
auto get_offhand_tap_prop() const -> float
off hand taps to anchor len
Definition RMSequencing.h:156