Implement tick-based game loop, combat overhaul, and attack-any-NPC

Replace immediate combat with a 3-second tick engine that resolves
actions, NPC AI, status effects, respawns, and passive regeneration.
Players queue combat actions (attack/defend/flee/use) that resolve on
the next tick. Any NPC can now be attacked — non-hostile targets incur
attitude penalties instead of being blocked. Status effects persist in
the database and continue ticking while players are offline.

Made-with: Cursor
This commit is contained in:
AI Agent
2026-03-14 15:12:44 -06:00
parent a083c38326
commit 5fd2c10198
9 changed files with 890 additions and 181 deletions

View File

@@ -52,8 +52,18 @@ impl Player {
}
}
#[derive(Debug, Clone)]
pub enum CombatAction {
Attack,
Defend,
Flee,
UseItem(usize),
}
pub struct CombatState {
pub npc_id: String,
pub action: Option<CombatAction>,
pub defending: bool,
}
pub struct NpcInstance {
@@ -69,11 +79,42 @@ pub struct PlayerConnection {
pub combat: Option<CombatState>,
}
pub struct XorShift64 {
state: u64,
}
impl XorShift64 {
pub fn new(seed: u64) -> Self {
XorShift64 {
state: if seed == 0 { 0xdeadbeefcafe1234 } else { seed },
}
}
pub fn next(&mut self) -> u64 {
let mut x = self.state;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
self.state = x;
x
}
pub fn next_range(&mut self, min: i32, max: i32) -> i32 {
if min >= max {
return min;
}
let range = (max - min + 1) as u64;
(self.next() % range) as i32 + min
}
}
pub struct GameState {
pub world: World,
pub db: Arc<dyn GameDb>,
pub players: HashMap<usize, PlayerConnection>,
pub npc_instances: HashMap<String, NpcInstance>,
pub rng: XorShift64,
pub tick_count: u64,
}
pub type SharedState = Arc<Mutex<GameState>>;
@@ -93,11 +134,17 @@ impl GameState {
);
}
}
let seed = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
GameState {
world,
db,
players: HashMap::new(),
npc_instances,
rng: XorShift64::new(seed),
tick_count: 0,
}
}