- replay the shooter's combat update a second time when it launches a projectile, matching the client's own component-pass re-entry - carry the shooter's damage effect on every projectile and null a shot's target/source when either leaves the board - derive pending physical damage from the shots actually in flight instead of an incremental total, so it can never go negative - split the spawn ring's two angle registers so three- and five-unit cards land where the client puts them - attribute a combat target left at NONE to the exact site that did it, gated to report each object once - number and annotate every checksum field so a client-reported mismatch resolves straight to a name
111 lines
3.3 KiB
Rust
111 lines
3.3 KiB
Rust
use crate::data::scaled::{level_scale, ScaleKind};
|
|
use std::sync::Arc;
|
|
use titan::{CsvRow, CsvTable, GlobalId};
|
|
#[derive(Debug, Clone)]
|
|
pub struct LogicData {
|
|
csv_table: Arc<CsvTable>,
|
|
row_index: usize,
|
|
global_id: GlobalId,
|
|
name: String,
|
|
}
|
|
impl LogicData {
|
|
pub fn new(csv_table: Arc<CsvTable>, row_index: usize, table_index: i32) -> Self {
|
|
let name = csv_table
|
|
.row_at(row_index)
|
|
.map(|row| row.string(0).to_owned())
|
|
.unwrap_or_default();
|
|
Self {
|
|
csv_table,
|
|
row_index,
|
|
global_id: GlobalId::new(table_index, row_index as i32),
|
|
name,
|
|
}
|
|
}
|
|
pub fn name(&self) -> &str {
|
|
&self.name
|
|
}
|
|
pub fn global_id(&self) -> GlobalId {
|
|
self.global_id
|
|
}
|
|
pub fn table_index(&self) -> i32 {
|
|
self.global_id.class_id
|
|
}
|
|
pub fn instance_id(&self) -> i32 {
|
|
self.global_id.instance_id
|
|
}
|
|
pub fn row_index(&self) -> usize {
|
|
self.row_index
|
|
}
|
|
pub fn csv_table(&self) -> &Arc<CsvTable> {
|
|
&self.csv_table
|
|
}
|
|
pub fn row(&self) -> Option<&CsvRow> {
|
|
self.csv_table.row_at(self.row_index)
|
|
}
|
|
pub fn array_size(&self) -> usize {
|
|
self.row().map(CsvRow::array_size).unwrap_or_default()
|
|
}
|
|
fn column(&self, name: &str) -> Option<usize> {
|
|
self.csv_table.column_index(name)
|
|
}
|
|
pub fn int(&self, column: &str) -> i32 {
|
|
match (self.row(), self.column(column)) {
|
|
(Some(row), Some(index)) => row.int(index),
|
|
_ => 0,
|
|
}
|
|
}
|
|
pub fn int_at(&self, column: &str, level: usize) -> i32 {
|
|
match (self.row(), self.column(column)) {
|
|
(Some(row), Some(index)) => row.scaled_int(index, level),
|
|
_ => 0,
|
|
}
|
|
}
|
|
pub fn scaled_value(&self, column: &str, level: i32, kind: ScaleKind) -> i32 {
|
|
let (Some(row), Some(index)) = (self.row(), self.column(column)) else {
|
|
return 0;
|
|
};
|
|
let count = row.value_count(index);
|
|
if count == 0 {
|
|
return 0;
|
|
}
|
|
if count == 1 {
|
|
return row
|
|
.int(index)
|
|
.wrapping_mul(level_scale(kind.percent(), level))
|
|
/ 100;
|
|
}
|
|
let index_at = if count < 8 {
|
|
level.min(count as i32 - 1)
|
|
} else {
|
|
level
|
|
};
|
|
if index_at < 0 {
|
|
return 0;
|
|
}
|
|
row.int_at(index, index_at as usize)
|
|
}
|
|
pub fn string_at(&self, column: &str, index: usize) -> &str {
|
|
match (self.row(), self.column(column)) {
|
|
(Some(row), Some(column)) => row.string_at(column, index),
|
|
_ => "",
|
|
}
|
|
}
|
|
pub fn value_count(&self, column: &str) -> usize {
|
|
match (self.row(), self.column(column)) {
|
|
(Some(row), Some(column)) => row.value_count(column),
|
|
_ => 0,
|
|
}
|
|
}
|
|
pub fn string(&self, column: &str) -> &str {
|
|
match (self.row(), self.column(column)) {
|
|
(Some(row), Some(index)) => row.string(index),
|
|
_ => "",
|
|
}
|
|
}
|
|
pub fn boolean(&self, column: &str) -> bool {
|
|
match (self.row(), self.column(column)) {
|
|
(Some(row), Some(index)) => row.boolean(index),
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|