mirror of
https://github.com/SK8-ENGINE/skate-3-rust-engine.git
synced 2026-10-26 19:47:44 +00:00
Port native sequence publication ordering and line settlement
This commit is contained in:
1 parent
3c2860e376
commit
e66e81c8ae
5 files changed
+141
-4
No files matched your search
@@ -8,6 +8,7 @@ pub const SCORABLE_COUNT: usize = 332;
|
||||
pub const SCORE_TYPE_COUNT: usize = 14;
|
||||
pub mod carrier;
|
||||
pub mod catalog;
|
||||
pub mod session;
|
||||
pub mod timer;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
@@ -133,9 +134,16 @@ impl ScoreHolder {
|
||||
}
|
||||
/// Module82DA3B38 banks the line when its timer expires or reset is requested.
|
||||
pub fn finish_line(&mut self) {
|
||||
self.bank_line(true);
|
||||
}
|
||||
/// A zero line timer banks the current line even while a collector is
|
||||
/// active, but 82DA3B38 retains repetition in that case.
|
||||
pub fn bank_line(&mut self, clear_repetition: bool) {
|
||||
self.snapshot.completed_lines += self.snapshot.line;
|
||||
self.snapshot.line = 0.0;
|
||||
self.repetitions.fill(0);
|
||||
if clear_repetition {
|
||||
self.repetitions.fill(0);
|
||||
}
|
||||
}
|
||||
/// Native Reset82DA5C78 retains lifetime completed-lines (+24).
|
||||
pub fn reset(&mut self) {
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
//! Collectors supply the native clock and already-conditioned point factor.
|
||||
use super::Scorable;
|
||||
|
||||
/// 82DA45C8 multiplies the sum of authored and collector delay by the
|
||||
/// binary32 constant at 8303745C (0x42700000), then truncates to integer.
|
||||
pub fn delay_ticks(authored_seconds: f32, extra_seconds: f32) -> u32 {
|
||||
(((authored_seconds + extra_seconds) * f32::from_bits(0x42700000)) as i64) as u32
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Carrier {
|
||||
pub scorable: Scorable,
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
//! Module publication and line settlement, TU3 82DA37B0/82DA3B38.
|
||||
use super::{
|
||||
ScoreHolder,
|
||||
timer::{ComboTimer, PointTimer},
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Rules {
|
||||
pub combo_capacity: f32,
|
||||
pub combo_levels: [(f32, f32); 3],
|
||||
pub combo_refresh_threshold: f32,
|
||||
pub line_capacity: f32,
|
||||
pub bail_factor: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Session {
|
||||
pub holder: ScoreHolder,
|
||||
pub combo: ComboTimer,
|
||||
pub line: PointTimer,
|
||||
}
|
||||
|
||||
impl Session {
|
||||
/// The collector owns the decision to publish. Capture the multiplier
|
||||
/// before crediting timers: a threshold reached by this reward affects
|
||||
/// the next publication, not this publication's score (f29 at 82DA37B0).
|
||||
pub fn publish_sequence(
|
||||
&mut self,
|
||||
rules: &Rules,
|
||||
landing_factor: f32,
|
||||
penalized: bool,
|
||||
multiplier_enabled: bool,
|
||||
) -> f32 {
|
||||
self.holder.reward_sequence(landing_factor);
|
||||
let snapshot = &self.holder.snapshot;
|
||||
let mut raw =
|
||||
(snapshot.fingerflip_pending + snapshot.general_pending) + snapshot.accumulated;
|
||||
if penalized && rules.bail_factor < 1.0 {
|
||||
raw *= rules.bail_factor;
|
||||
}
|
||||
let multiplier = if multiplier_enabled {
|
||||
self.combo.multiplier
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
if multiplier_enabled {
|
||||
self.combo.credit(
|
||||
raw,
|
||||
rules.combo_capacity,
|
||||
rules.combo_levels,
|
||||
rules.combo_refresh_threshold,
|
||||
);
|
||||
if self.combo.multiplier > f32::from_bits(0x3f8147ae) {
|
||||
self.line.credit(raw, rules.line_capacity);
|
||||
}
|
||||
}
|
||||
let reward = multiplier * raw;
|
||||
self.holder
|
||||
.publish(reward, multiplier_enabled && self.line.points > 0.0);
|
||||
reward
|
||||
}
|
||||
|
||||
/// Reset/bail/output requests or the expiry edge reset line time and the
|
||||
/// multiplier. A merely empty line timer does not reset the multiplier.
|
||||
pub fn settle_line(&mut self, reset_requested: bool, collector_active: bool) {
|
||||
if reset_requested || self.line.expired {
|
||||
self.line = PointTimer::default();
|
||||
self.combo.multiplier = 1.0;
|
||||
self.holder.finish_line();
|
||||
} else if self.line.points <= 0.0 {
|
||||
self.holder.bank_line(!collector_active);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::scoring::Scorable;
|
||||
#[test]
|
||||
fn newly_crossed_threshold_applies_to_the_following_publication() {
|
||||
let rules = Rules {
|
||||
combo_capacity: 801.0,
|
||||
combo_levels: [(50.0, 1.5), (450.0, 2.0), (800.0, 3.0)],
|
||||
combo_refresh_threshold: 799.0,
|
||||
line_capacity: 400.0,
|
||||
bail_factor: 0.0,
|
||||
};
|
||||
let mut session = Session::default();
|
||||
let trick = Scorable {
|
||||
id: 96,
|
||||
class: 3,
|
||||
score_type: 2,
|
||||
};
|
||||
session.holder.end_trick(trick, 50.0);
|
||||
session.holder.finish_collector();
|
||||
assert_eq!(session.publish_sequence(&rules, 1.0, false, true), 50.0);
|
||||
assert_eq!(session.combo.multiplier, 1.5);
|
||||
session.holder.end_trick(trick, 10.0);
|
||||
session.holder.finish_collector();
|
||||
assert_eq!(session.publish_sequence(&rules, 1.0, false, true), 15.0);
|
||||
assert_eq!(session.holder.snapshot.line, 65.0);
|
||||
session.line.points = 0.0;
|
||||
session.settle_line(false, true);
|
||||
assert_eq!(session.holder.repetition_count(trick), Some(2));
|
||||
session.settle_line(false, false);
|
||||
assert_eq!(session.holder.repetition_count(trick), Some(0));
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,15 @@ pub struct ScoringData {
|
||||
}
|
||||
|
||||
impl ScoringData {
|
||||
pub fn session_rules(&self) -> skate_core::scoring::session::Rules {
|
||||
skate_core::scoring::session::Rules {
|
||||
combo_capacity: self.combo_capacity,
|
||||
combo_levels: self.combo_levels,
|
||||
combo_refresh_threshold: self.combo_refresh_threshold,
|
||||
line_capacity: self.line_capacity,
|
||||
bail_factor: self.bail_factor,
|
||||
}
|
||||
}
|
||||
pub fn load(data: &Collections) -> Result<Self, String> {
|
||||
let mut definitions = Vec::new();
|
||||
for (id, &(identifier, class, score_type)) in IDENTIFIERS.iter().enumerate() {
|
||||
|
||||
@@ -18,6 +18,9 @@ Implemented source components:
|
||||
* `skate-core::scoring`: ScoreHolder accounting at `82DA6198`, `82DA6260`,
|
||||
`82DA6408`, `82DA6468`, `82DA6538`; point timers at `82DA4C28`; carrier
|
||||
announcement/completion at `82DA45C8`, `82DA46D0`, `82DA5DE0`, `82DA5F98`.
|
||||
Session publication at `82DA37B0` captures the multiplier before crediting
|
||||
this reward to the combo timer. Settlement at `82DA3B38` preserves repetition
|
||||
on an empty line timer while a collector is active.
|
||||
* `skate-data::scoring`: resolves authored VLT points, labels, delay, repetition,
|
||||
announcement curves, combo thresholds and timer settings. Missing data is
|
||||
an error. The executable's 332-entry identifier metadata includes unused
|
||||
@@ -61,8 +64,9 @@ with a new overlay would not meet this port's requirements.
|
||||
Collectors are not wired into the production tick yet. Recognition still
|
||||
requires conditioner gates, descriptor conversion, air/ground/grind collector
|
||||
transitions, spin/flip bonuses, gaps, landing and sequence publication ordering.
|
||||
The native clock frequency must be recovered before converting authored
|
||||
announcement delays to ticks. A physics-frame count is not a safe substitute.
|
||||
Announcement delay conversion uses the verified binary32 60 at `8303745C`.
|
||||
The carrier's start/current tick source still needs to be connected at the
|
||||
matching production phase.
|
||||
|
||||
The HUD still needs a complete movie object hierarchy, native bindings,
|
||||
timeline execution and rendering with multiplicative and additive colors.
|
||||
@@ -84,7 +88,8 @@ release build/launcher remain to be integrated and checked. No game/recomp,
|
||||
controller harness, gameplay automation, `--check-assets`, or screenshot
|
||||
capture has been run for this work.
|
||||
|
||||
Validation so far: eight scoring unit tests pass (including unsigned clock
|
||||
Validation so far: nine scoring unit tests pass (including unsigned clock
|
||||
rollover, early completion, conversion, timer thresholds, one-time banking and
|
||||
cancelled rewards); the owned-data loader audit resolves 300 definitions; the
|
||||
APT data-only audit passes placement validation and constructor checks.
|
||||
|
||||
Reference in new issue
Block a user