mirror of
https://github.com/osmarks/ewo3.git
synced 2025-01-02 21:40:36 +00:00
useful items, multitile positions, etc
This commit is contained in:
parent
db02d05778
commit
fbf154fa7c
17
Cargo.lock
generated
17
Cargo.lock
generated
@ -130,6 +130,12 @@ dependencies = [
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.10"
|
||||
@ -149,6 +155,7 @@ dependencies = [
|
||||
"fastrand",
|
||||
"futures-util",
|
||||
"hecs",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"noise-functions",
|
||||
"seahash",
|
||||
@ -286,6 +293,16 @@ version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.11"
|
||||
|
@ -19,4 +19,5 @@ serde_json = "1"
|
||||
slab = "0.4"
|
||||
lazy_static = "1"
|
||||
seahash = "4"
|
||||
noise-functions = "0.2"
|
||||
noise-functions = "0.2"
|
||||
indexmap = "2"
|
@ -78,6 +78,11 @@
|
||||
{#if health}
|
||||
Your health is {health}.
|
||||
{/if}
|
||||
<ul>
|
||||
{#each inventory as item}
|
||||
<li>{item[0]} x{item[2]}: {item[1]}</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -89,6 +94,7 @@
|
||||
let dead = false
|
||||
let health
|
||||
let players
|
||||
let inventory = []
|
||||
|
||||
let ws
|
||||
const connect = () => {
|
||||
@ -105,6 +111,7 @@
|
||||
}
|
||||
grid = newGrid
|
||||
health = data.Display.health
|
||||
inventory = data.Display.inventory
|
||||
}
|
||||
if (data === "Dead") {
|
||||
dead = true
|
||||
@ -164,7 +171,8 @@
|
||||
"a": "Left",
|
||||
"d": "Right",
|
||||
"z": "DownLeft",
|
||||
"x": "DownRight"
|
||||
"x": "DownRight",
|
||||
"f": "Dig"
|
||||
}
|
||||
|
||||
connect()
|
||||
|
311
src/main.rs
311
src/main.rs
@ -1,12 +1,13 @@
|
||||
use hecs::{Entity, World};
|
||||
use hecs::{CommandBuffer, Entity, World};
|
||||
use euclid::{Point3D, Point2D, Vector2D};
|
||||
use futures_util::{stream::TryStreamExt, SinkExt, StreamExt};
|
||||
use indexmap::IndexMap;
|
||||
use noise_functions::Sample3;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_tungstenite::tungstenite::protocol::Message;
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use anyhow::{Result, Context, anyhow};
|
||||
use std::{collections::{hash_map::Entry, HashMap}, hash::{Hash, Hasher}, net::SocketAddr, sync::Arc, thread::current, time::Duration};
|
||||
use std::{collections::{hash_map::Entry, HashMap, HashSet, VecDeque}, convert::TryFrom, hash::{Hash, Hasher}, net::SocketAddr, sync::Arc, time::Duration};
|
||||
use slab::Slab;
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
@ -90,8 +91,9 @@ enum Input {
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
enum Frame {
|
||||
Dead,
|
||||
Display { nearby: Vec<(i64, i64, char, f32)>, health: f32 },
|
||||
PlayerCount(usize)
|
||||
Display { nearby: Vec<(i64, i64, char, f32)>, health: f32, inventory: Vec<(String, String, u64)> },
|
||||
PlayerCount(usize),
|
||||
Message(String)
|
||||
}
|
||||
|
||||
struct Client {
|
||||
@ -106,16 +108,45 @@ struct GameState {
|
||||
ticks: u64
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
enum Item {
|
||||
Dirt,
|
||||
Bone
|
||||
}
|
||||
|
||||
impl Item {
|
||||
fn name(&self) -> &'static str {
|
||||
use Item::*;
|
||||
match self {
|
||||
Dirt => "Dirt",
|
||||
Bone => "Bone"
|
||||
}
|
||||
}
|
||||
|
||||
fn description(&self) -> &'static str {
|
||||
use Item::*;
|
||||
match self {
|
||||
Dirt => "It's from the ground. You're carrying it for some reason.",
|
||||
Bone => "Disassembling your enemies for resources is probably ethical."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct PlayerCharacter;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
struct Position(Coord);
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
struct Position(VecDeque<Coord>);
|
||||
|
||||
impl Position {
|
||||
fn head(&self) -> Coord {
|
||||
*self.0.front().unwrap()
|
||||
}
|
||||
|
||||
fn single_tile(c: Coord) -> Self {
|
||||
Self(VecDeque::from([c]))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
struct MovingInto(Coord);
|
||||
@ -123,6 +154,13 @@ struct MovingInto(Coord);
|
||||
#[derive(Debug, Clone)]
|
||||
struct Health(f32, f32);
|
||||
|
||||
impl Health {
|
||||
fn pct(&self) -> f32 {
|
||||
if self.1 == 0.0 { 0.0 }
|
||||
else { self.0 / self.1 }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Render(char);
|
||||
|
||||
@ -153,20 +191,17 @@ struct Collidable;
|
||||
#[derive(Debug, Clone)]
|
||||
struct Velocity(CoordVec);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DeferredRandomly<T: Clone + std::fmt::Debug + hecs::Bundle>(u64, T);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Terrain;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Obstruction { entry_cost: StochasticNumber, exit_cost: StochasticNumber }
|
||||
struct Obstruction { entry_multiplier: f32, exit_multiplier: f32 }
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Energy { current: f32, regeneration_rate: f32, burst: f32 }
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DespawnOnImpact;
|
||||
struct Drops(Vec<(Item, StochasticNumber)>);
|
||||
|
||||
impl Energy {
|
||||
fn try_consume(&mut self, cost: f32) -> bool {
|
||||
@ -179,6 +214,46 @@ impl Energy {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct DespawnOnImpact;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Inventory(indexmap::IndexMap<Item, u64>);
|
||||
|
||||
impl Inventory {
|
||||
fn add(&mut self, item: Item, qty: u64) {
|
||||
*self.0.entry(item).or_default() += qty;
|
||||
}
|
||||
|
||||
fn take(&mut self, item: Item, qty: u64) -> bool {
|
||||
match self.0.entry(item) {
|
||||
indexmap::map::Entry::Occupied(mut o) => {
|
||||
let current = o.get_mut();
|
||||
if *current >= qty {
|
||||
*current -= qty;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
indexmap::map::Entry::Vacant(_) => return false
|
||||
}
|
||||
}
|
||||
|
||||
fn extend(&mut self, other: &Inventory) {
|
||||
for (item, count) in other.0.iter() {
|
||||
self.add(item.clone(), *count);
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.0.iter().any(|(_, c)| *c > 0)
|
||||
}
|
||||
|
||||
fn empty() -> Self {
|
||||
Self(IndexMap::new())
|
||||
}
|
||||
}
|
||||
|
||||
const VIEW: i64 = 15;
|
||||
const WALL: i64 = 128;
|
||||
const RANDOM_DESPAWN_INV_RATE: u64 = 4000;
|
||||
@ -239,21 +314,22 @@ struct EnemySpec {
|
||||
initial_health: f32,
|
||||
move_delay: usize,
|
||||
attack_cooldown: u64,
|
||||
ranged: bool
|
||||
ranged: bool,
|
||||
drops: Vec<(Item, StochasticNumber)>
|
||||
}
|
||||
|
||||
impl EnemySpec {
|
||||
// Numbers ported from original EWO. Fudge constants added elsewhere.
|
||||
fn random() -> EnemySpec {
|
||||
match fastrand::usize(0..650) {
|
||||
0..=99 => EnemySpec { symbol: 'I', min_damage: 10.0, damage_range: 5.0, initial_health: 50.0, move_delay: 70, attack_cooldown: 10, ranged: false }, // IBIS
|
||||
100..=199 => EnemySpec { symbol: 'K', min_damage: 5.0, damage_range: 15.0, initial_health: 30.0, move_delay: 40, attack_cooldown: 10, ranged: false }, // KESTREL
|
||||
200..=299 => EnemySpec { symbol: 'S', min_damage: 5.0, damage_range: 5.0, initial_health: 20.0, move_delay: 50, attack_cooldown: 10, ranged: false }, // SNAKE
|
||||
300..=399 => EnemySpec { symbol: 'E', min_damage: 10.0, damage_range: 20.0, initial_health: 80.0, move_delay: 80, attack_cooldown: 10, ranged: false }, // EMU
|
||||
400..=499 => EnemySpec { symbol: 'O', min_damage: 8.0, damage_range: 17.0, initial_health: 150.0, move_delay: 100, attack_cooldown: 10, ranged: false }, // OGRE
|
||||
500..=599 => EnemySpec { symbol: 'R', min_damage: 5.0, damage_range: 5.0, initial_health: 15.0, move_delay: 40, attack_cooldown: 10, ranged: false }, // RAT
|
||||
600..=609 => EnemySpec { symbol: 'M' , min_damage: 20.0, damage_range: 10.0, initial_health: 150.0, move_delay: 70, attack_cooldown: 10, ranged: false }, // MOA
|
||||
610..=649 => EnemySpec { symbol: 'P', min_damage: 10.0, damage_range: 5.0, initial_health: 15.0, move_delay: 20, attack_cooldown: 10, ranged: true }, // PLATYPUS
|
||||
0..=99 => EnemySpec { symbol: 'I', min_damage: 10.0, damage_range: 5.0, initial_health: 50.0, move_delay: 70, attack_cooldown: 10, ranged: false, drops: vec![] }, // IBIS
|
||||
100..=199 => EnemySpec { symbol: 'K', min_damage: 5.0, damage_range: 15.0, initial_health: 30.0, move_delay: 40, attack_cooldown: 10, ranged: false, drops: vec![] }, // KESTREL
|
||||
200..=299 => EnemySpec { symbol: 'S', min_damage: 5.0, damage_range: 5.0, initial_health: 20.0, move_delay: 50, attack_cooldown: 10, ranged: false, drops: vec![] }, // SNAKE
|
||||
300..=399 => EnemySpec { symbol: 'E', min_damage: 10.0, damage_range: 20.0, initial_health: 80.0, move_delay: 80, attack_cooldown: 10, ranged: false, drops: vec![] }, // EMU
|
||||
400..=499 => EnemySpec { symbol: 'O', min_damage: 8.0, damage_range: 17.0, initial_health: 150.0, move_delay: 100, attack_cooldown: 10, ranged: false, drops: vec![] }, // OGRE
|
||||
500..=599 => EnemySpec { symbol: 'R', min_damage: 5.0, damage_range: 5.0, initial_health: 15.0, move_delay: 40, attack_cooldown: 10, ranged: false, drops: vec![] }, // RAT
|
||||
600..=609 => EnemySpec { symbol: 'M' , min_damage: 20.0, damage_range: 10.0, initial_health: 150.0, move_delay: 70, attack_cooldown: 10, ranged: false, drops: vec![] }, // MOA
|
||||
610..=649 => EnemySpec { symbol: 'P', min_damage: 10.0, damage_range: 5.0, initial_health: 15.0, move_delay: 20, attack_cooldown: 10, ranged: true, drops: vec![] }, // PLATYPUS
|
||||
_ => unreachable!()
|
||||
}
|
||||
}
|
||||
@ -308,6 +384,10 @@ impl StochasticNumber {
|
||||
}
|
||||
}
|
||||
|
||||
fn sample_rounded<T: TryFrom<i128>>(&self) -> T {
|
||||
T::try_from(self.sample().round() as i128).map_err(|_| "convert fail").unwrap()
|
||||
}
|
||||
|
||||
fn triangle_from_min_range(min: f32, range: f32) -> Self {
|
||||
StochasticNumber::Triangle { min: min, max: min + range, mode: (min + range) / 2.0 }
|
||||
}
|
||||
@ -318,41 +398,48 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
let mut positions = HashMap::new();
|
||||
|
||||
for (entity, pos) in state.world.query_mut::<hecs::With<&Position, &Collidable>>() {
|
||||
positions.insert(pos.0, entity);
|
||||
for subpos in pos.0.iter() {
|
||||
positions.insert(*subpos, entity);
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, pos) in state.world.query_mut::<hecs::With<&Position, &Terrain>>() {
|
||||
terrain_positions.insert(pos.0, entity);
|
||||
for subpos in pos.0.iter() {
|
||||
terrain_positions.insert(*subpos, entity);
|
||||
}
|
||||
}
|
||||
|
||||
let mut buffer = hecs::CommandBuffer::new();
|
||||
|
||||
// Spawn enemies
|
||||
for (_entity, (Position(pos), EnemyTarget { spawn_range, spawn_density, spawn_rate_inv, .. })) in state.world.query::<(&Position, &EnemyTarget)>().iter() {
|
||||
for (_entity, (pos, EnemyTarget { spawn_range, spawn_density, spawn_rate_inv, .. })) in state.world.query::<(&Position, &EnemyTarget)>().iter() {
|
||||
let pos = pos.head();
|
||||
if fastrand::usize(0..*spawn_rate_inv) == 0 {
|
||||
let c = count_hexes(*spawn_range.end());
|
||||
let mut newpos = *pos + sample_range(*spawn_range.end());
|
||||
let mut newpos = pos + sample_range(*spawn_range.end());
|
||||
let mut occupied = false;
|
||||
for _ in 0..(c as f32 / spawn_density * 0.005).ceil() as usize {
|
||||
if positions.contains_key(&newpos) {
|
||||
occupied = true;
|
||||
break;
|
||||
}
|
||||
newpos = *pos + sample_range(*spawn_range.end());
|
||||
newpos = pos + sample_range(*spawn_range.end());
|
||||
}
|
||||
if !occupied && get_base_terrain(newpos).can_enter() && hex_distance(newpos, *pos) >= *spawn_range.start() {
|
||||
let spec = EnemySpec::random();
|
||||
if !occupied && get_base_terrain(newpos).can_enter() && hex_distance(newpos, pos) >= *spawn_range.start() {
|
||||
let mut spec = EnemySpec::random();
|
||||
spec.drops.push((Item::Bone, StochasticNumber::Triangle { min: 0.7 * spec.initial_health / 40.0, max: 1.3 * spec.initial_health / 40.0, mode: spec.initial_health / 40.0 }));
|
||||
if spec.ranged {
|
||||
buffer.spawn((
|
||||
Render(spec.symbol),
|
||||
Health(spec.initial_health, spec.initial_health),
|
||||
Enemy,
|
||||
RangedAttack { damage: StochasticNumber::triangle_from_min_range(spec.min_damage, spec.damage_range), energy: spec.attack_cooldown as f32, range: 4 },
|
||||
Position(newpos),
|
||||
Position::single_tile(newpos),
|
||||
MoveCost(StochasticNumber::Triangle { min: 0.0, max: 2.0 * spec.move_delay as f32 / 3.0, mode: spec.move_delay as f32 / 3.0 }),
|
||||
Collidable,
|
||||
DespawnRandomly(RANDOM_DESPAWN_INV_RATE),
|
||||
Energy { regeneration_rate: 1.0, current: 0.0, burst: 0.0 }
|
||||
Energy { regeneration_rate: 1.0, current: 0.0, burst: 0.0 },
|
||||
Drops(spec.drops)
|
||||
));
|
||||
} else {
|
||||
buffer.spawn((
|
||||
@ -360,11 +447,12 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
Health(spec.initial_health, spec.initial_health),
|
||||
Enemy,
|
||||
Attack { damage: StochasticNumber::triangle_from_min_range(spec.min_damage, spec.damage_range), energy: spec.attack_cooldown as f32 },
|
||||
Position(newpos),
|
||||
Position::single_tile(newpos),
|
||||
MoveCost(StochasticNumber::Triangle { min: 0.0, max: 2.0 * spec.move_delay as f32 / 3.0, mode: spec.move_delay as f32 / 3.0 }),
|
||||
Collidable,
|
||||
DespawnRandomly(RANDOM_DESPAWN_INV_RATE),
|
||||
Energy { regeneration_rate: 1.0, current: 0.0, burst: 0.0 }
|
||||
Energy { regeneration_rate: 1.0, current: 0.0, burst: 0.0 },
|
||||
Drops(spec.drops)
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -372,11 +460,13 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
}
|
||||
|
||||
// Process enemy motion and ranged attacks
|
||||
for (entity, (Position(pos), ranged, energy)) in state.world.query::<hecs::With<(&Position, Option<&mut RangedAttack>, Option<&mut Energy>), &Enemy>>().iter() {
|
||||
for (entity, (pos, ranged, energy)) in state.world.query::<hecs::With<(&Position, Option<&mut RangedAttack>, Option<&mut Energy>), &Enemy>>().iter() {
|
||||
let pos = pos.head();
|
||||
|
||||
for direction in DIRECTIONS.iter() {
|
||||
if let Some(target) = positions.get(&(*pos + *direction)) {
|
||||
if let Some(target) = positions.get(&(pos + *direction)) {
|
||||
if let Ok(_) = state.world.get::<&EnemyTarget>(*target) {
|
||||
buffer.insert_one(entity, MovingInto(*pos + *direction));
|
||||
buffer.insert_one(entity, MovingInto(pos + *direction));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@ -386,11 +476,12 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
|
||||
// TODO we maybe need a spatial index for this
|
||||
for (_entity, (target_pos, target)) in state.world.query::<(&Position, &EnemyTarget)>().iter() {
|
||||
let distance = hex_distance(*pos, target_pos.0);
|
||||
let target_pos = target_pos.head();
|
||||
let distance = hex_distance(pos, target_pos);
|
||||
if distance < target.aggression_range {
|
||||
match closest {
|
||||
Some((_pos, old_distance)) if old_distance < distance => closest = Some((target_pos.0, distance)),
|
||||
None => closest = Some((target_pos.0, distance)),
|
||||
Some((_pos, old_distance)) if old_distance < distance => closest = Some((target_pos, distance)),
|
||||
None => closest = Some((target_pos, distance)),
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
@ -400,10 +491,10 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
if let Some(ranged_attack) = ranged {
|
||||
// slightly smart behaviour for ranged attacker: try to stay just within range
|
||||
let direction = DIRECTIONS.iter().min_by_key(|dir|
|
||||
(hex_distance(*pos + **dir, target_pos) - (ranged_attack.range as i64 - 1)).abs()).unwrap();
|
||||
buffer.insert_one(entity, MovingInto(*pos + *direction));
|
||||
(hex_distance(pos + **dir, target_pos) - (ranged_attack.range as i64 - 1)).abs()).unwrap();
|
||||
buffer.insert_one(entity, MovingInto(pos + *direction));
|
||||
// do ranged attack if valid
|
||||
let atk_dir = target_pos - *pos;
|
||||
let atk_dir = target_pos - pos;
|
||||
if on_axis(atk_dir) && (energy.is_none() || energy.unwrap().try_consume(ranged_attack.energy)) {
|
||||
let atk_dir = atk_dir.clamp(-CoordVec::one(), CoordVec::one());
|
||||
buffer.spawn((
|
||||
@ -411,23 +502,23 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
Enemy,
|
||||
Attack { damage: ranged_attack.damage, energy: 0.0 },
|
||||
Velocity(atk_dir),
|
||||
Position(*pos),
|
||||
Position::single_tile(pos),
|
||||
DespawnOnTick(state.ticks.wrapping_add(ranged_attack.range))
|
||||
));
|
||||
}
|
||||
} else {
|
||||
let direction = DIRECTIONS.iter().min_by_key(|dir| hex_distance(*pos + **dir, target_pos)).unwrap();
|
||||
buffer.insert_one(entity, MovingInto(*pos + *direction));
|
||||
let direction = DIRECTIONS.iter().min_by_key(|dir| hex_distance(pos + **dir, target_pos)).unwrap();
|
||||
buffer.insert_one(entity, MovingInto(pos + *direction));
|
||||
}
|
||||
} else {
|
||||
// wander randomly (ethical)
|
||||
buffer.insert_one(entity, MovingInto(*pos + *fastrand::choice(DIRECTIONS).unwrap()));
|
||||
buffer.insert_one(entity, MovingInto(pos + *fastrand::choice(DIRECTIONS).unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
// Process velocity
|
||||
for (entity, (Position(pos), Velocity(vel))) in state.world.query_mut::<(&Position, &Velocity)>() {
|
||||
buffer.insert_one(entity, MovingInto(*pos + *vel));
|
||||
for (entity, (pos, Velocity(vel))) in state.world.query_mut::<(&Position, &Velocity)>() {
|
||||
buffer.insert_one(entity, MovingInto(pos.head() + *vel));
|
||||
}
|
||||
|
||||
buffer.run_on(&mut state.world);
|
||||
@ -435,6 +526,9 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
// Process inputs
|
||||
for (_id, client) in state.clients.iter_mut() {
|
||||
let mut next_movement = CoordVec::zero();
|
||||
let position = state.world.get::<&Position>(client.entity)?.head();
|
||||
let mut energy = state.world.get::<&mut Energy>(client.entity)?;
|
||||
let mut inventory = state.world.get::<&mut Inventory>(client.entity)?;
|
||||
loop {
|
||||
let recv = client.inputs_rx.try_recv();
|
||||
match recv {
|
||||
@ -447,28 +541,81 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
Input::DownRight => next_movement = CoordVec::new(0, 1),
|
||||
Input::DownLeft => next_movement = CoordVec::new(-1, 1),
|
||||
Input::Dig => {
|
||||
|
||||
if terrain_positions.get(&position).is_none() && energy.try_consume(5.0) {
|
||||
buffer.spawn((
|
||||
Terrain,
|
||||
Render('_'),
|
||||
Obstruction { entry_multiplier: 5.0, exit_multiplier: 5.0 },
|
||||
DespawnOnTick(state.ticks.wrapping_add(StochasticNumber::triangle_from_min_range(5000.0, 5000.0).sample().round() as u64)),
|
||||
Position::single_tile(position)
|
||||
));
|
||||
inventory.add(Item::Dirt, StochasticNumber::triangle_from_min_range(1.0, 3.0).sample_rounded());
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => return Err(e.into())
|
||||
}
|
||||
}
|
||||
let position = state.world.get::<&mut Position>(client.entity)?.0;
|
||||
|
||||
let target = position + next_movement;
|
||||
if get_base_terrain(target).can_enter() && target != position {
|
||||
state.world.insert_one(client.entity, MovingInto(target)).unwrap();
|
||||
buffer.insert_one(client.entity, MovingInto(target));
|
||||
}
|
||||
}
|
||||
|
||||
// Process motion and attacks
|
||||
for (entity, (Position(current_pos), MovingInto(target_pos), damage, mut energy, move_cost, despawn_on_impact)) in state.world.query::<(&mut Position, &MovingInto, Option<&mut Attack>, Option<&mut Energy>, Option<&MoveCost>, Option<&DespawnOnImpact>)>().iter() {
|
||||
let mut move_cost = move_cost.map(|x| x.0.sample()).unwrap_or(0.0);
|
||||
if let Some(current_terrain) = terrain_positions.get(current_pos) {
|
||||
move_cost += 1.0;
|
||||
buffer.run_on(&mut state.world);
|
||||
|
||||
let mut despawn_buffer = HashSet::new();
|
||||
|
||||
// This might lead to a duping glitch, which would at least be funny.
|
||||
let kill = |buffer: &mut CommandBuffer, despawn_buffer: &mut HashSet<Entity>, state: &GameState, entity: Entity, killer: Option<Entity>, position: Option<Coord>| {
|
||||
let position = position.unwrap_or_else(|| state.world.get::<&Position>(entity).unwrap().head());
|
||||
despawn_buffer.insert(entity);
|
||||
buffer.despawn(entity);
|
||||
let mut materialized_drops = Inventory::empty();
|
||||
if let Ok(drops) = state.world.get::<&Drops>(entity) {
|
||||
for (drop, frequency) in drops.0.iter() {
|
||||
materialized_drops.add(drop.clone(), frequency.sample_rounded())
|
||||
}
|
||||
}
|
||||
// TODO will break attacks kind of, desirable? Doubtful.
|
||||
if let Ok(other_inv) = state.world.get::<&Inventory>(entity) {
|
||||
materialized_drops.extend(&other_inv);
|
||||
}
|
||||
let killer_consumed_items = if let Some(killer) = killer {
|
||||
if let Ok(mut inv) = state.world.get::<&mut Inventory>(killer) {
|
||||
inv.extend(&materialized_drops);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else { false };
|
||||
if !killer_consumed_items && !materialized_drops.is_empty() {
|
||||
buffer.spawn((
|
||||
Position::single_tile(position),
|
||||
Render('☒'),
|
||||
materialized_drops
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Process motion and attacks
|
||||
for (entity, (current_pos, MovingInto(target_pos), damage, mut energy, move_cost, despawn_on_impact)) in state.world.query::<(&mut Position, &MovingInto, Option<&mut Attack>, Option<&mut Energy>, Option<&MoveCost>, Option<&DespawnOnImpact>)>().iter() {
|
||||
let mut move_cost = move_cost.map(|x| x.0.sample()).unwrap_or(0.0);
|
||||
|
||||
for tile in current_pos.0.iter() {
|
||||
// TODO: perhaps large enemies should not be exponentially more vulnerable to environmental hazards
|
||||
if let Some(current_terrain) = terrain_positions.get(tile) {
|
||||
if let Ok(obstruction) = state.world.get::<&Obstruction>(*current_terrain) {
|
||||
move_cost *= obstruction.exit_multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: attacks into obstructions are still cheap; is this desirable?
|
||||
if let Some(target_terrain) = terrain_positions.get(target_pos) {
|
||||
move_cost += 1.0;
|
||||
if let Ok(obstruction) = state.world.get::<&Obstruction>(*target_terrain) {
|
||||
move_cost *= obstruction.entry_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
if get_base_terrain(*target_pos).can_enter() {
|
||||
@ -485,16 +632,16 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
_ => ()
|
||||
}
|
||||
if despawn_on_impact.is_some() {
|
||||
buffer.despawn(entity);
|
||||
kill(&mut buffer, &mut despawn_buffer, &state, entity, Some(target_entity), Some(*target_pos));
|
||||
}
|
||||
if x.0 <= 0.0 {
|
||||
buffer.despawn(target_entity);
|
||||
if x.0 < 0.0 {
|
||||
kill(&mut buffer, &mut despawn_buffer, &state, target_entity, Some(entity), Some(*target_pos));
|
||||
Some(Entry::Occupied(o))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None // TODO: on pickup or something
|
||||
None // no "on pickup" exists; emulated with health 0
|
||||
}
|
||||
},
|
||||
Entry::Vacant(v) => Some(Entry::Vacant(v))
|
||||
@ -503,8 +650,8 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
// TODO: perhaps this should be applied to attacks too?
|
||||
if consume_energy_if_available(&mut energy, move_cost) {
|
||||
*entry.or_insert(entity) = entity;
|
||||
positions.remove(current_pos);
|
||||
*current_pos = *target_pos;
|
||||
positions.remove(¤t_pos.0.pop_back().unwrap());
|
||||
current_pos.0.push_front(*target_pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -518,26 +665,37 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
}
|
||||
|
||||
// Process transient entities
|
||||
for (entity, tick) in state.world.query_mut::<&DespawnOnTick>() {
|
||||
for (entity, tick) in state.world.query::<&DespawnOnTick>().iter() {
|
||||
if state.ticks == tick.0 {
|
||||
buffer.despawn(entity);
|
||||
kill(&mut buffer, &mut despawn_buffer, &state, entity, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
for (entity, DespawnRandomly(inv_rate)) in state.world.query_mut::<&DespawnRandomly>() {
|
||||
for (entity, DespawnRandomly(inv_rate)) in state.world.query::<&DespawnRandomly>().iter() {
|
||||
if fastrand::u64(0..*inv_rate) == 0 {
|
||||
buffer.despawn(entity);
|
||||
kill(&mut buffer, &mut despawn_buffer, &state, entity, None, None);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.run_on(&mut state.world);
|
||||
|
||||
let mut delete = vec![];
|
||||
for (position, entity) in positions.iter() {
|
||||
if despawn_buffer.contains(entity) {
|
||||
delete.push(*position);
|
||||
}
|
||||
}
|
||||
for position in delete {
|
||||
positions.remove(&position);
|
||||
}
|
||||
|
||||
// Send views to clients
|
||||
// TODO: terrain layer below others
|
||||
for (_id, client) in state.clients.iter() {
|
||||
client.frames_tx.send(Frame::PlayerCount(state.clients.len())).await?;
|
||||
let mut nearby = vec![];
|
||||
if let Ok(pos) = state.world.get::<&Position>(client.entity) {
|
||||
let pos = pos.0;
|
||||
let pos = pos.head();
|
||||
for q in -VIEW..=VIEW {
|
||||
for r in (-VIEW).max(-q - VIEW)..= VIEW.min(-q+VIEW) {
|
||||
let offset = CoordVec::new(q, r);
|
||||
@ -548,9 +706,12 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
if let Some(entity) = positions.get(&pos) {
|
||||
let render = state.world.get::<&Render>(*entity)?;
|
||||
let health = if let Ok(h) = state.world.get::<&Health>(*entity) {
|
||||
h.0 / h.1
|
||||
h.pct()
|
||||
} else { 1.0 };
|
||||
nearby.push((q, r, render.0, health))
|
||||
nearby.push((q, r, render.0, health));
|
||||
} else if let Some(entity) = terrain_positions.get(&pos) {
|
||||
let render = state.world.get::<&Render>(*entity)?;
|
||||
nearby.push((q, r, render.0, 1.0));
|
||||
} else {
|
||||
let mut rng = rng_from_hash(pos);
|
||||
let bg = if rng.usize(0..10) == 0 { ',' } else { '.' };
|
||||
@ -560,7 +721,9 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
||||
}
|
||||
}
|
||||
let health = state.world.get::<&Health>(client.entity)?.0;
|
||||
client.frames_tx.send(Frame::Display { nearby, health }).await?;
|
||||
let inventory = state.world.get::<&Inventory>(client.entity)?.0
|
||||
.iter().map(|(i, q)| (i.name().to_string(), i.description().to_string(), *q)).filter(|(_, _, q)| *q > 0).collect();
|
||||
client.frames_tx.send(Frame::Display { nearby, health, inventory }).await?;
|
||||
} else {
|
||||
client.frames_tx.send(Frame::Dead).await?;
|
||||
}
|
||||
@ -599,7 +762,7 @@ fn add_new_player(state: &mut GameState) -> Result<Entity> {
|
||||
}
|
||||
};
|
||||
Ok(state.world.spawn((
|
||||
Position(pos),
|
||||
Position::single_tile(pos),
|
||||
PlayerCharacter,
|
||||
Render(random_identifier()),
|
||||
Collidable,
|
||||
@ -610,7 +773,9 @@ fn add_new_player(state: &mut GameState) -> Result<Entity> {
|
||||
spawn_range: 3..=10,
|
||||
spawn_rate_inv: 20,
|
||||
aggression_range: 5
|
||||
}
|
||||
},
|
||||
Energy { current: 0.0, regeneration_rate: 1.0, burst: 5.0 },
|
||||
Inventory::empty()
|
||||
)))
|
||||
}
|
||||
|
||||
|
177
static/app.js
177
static/app.js
@ -387,18 +387,23 @@
|
||||
var { window: window_1 } = globals;
|
||||
function get_each_context(ctx, list, i) {
|
||||
const child_ctx = ctx.slice();
|
||||
child_ctx[17] = list[i];
|
||||
child_ctx[19] = i;
|
||||
child_ctx[18] = list[i];
|
||||
return child_ctx;
|
||||
}
|
||||
function get_each_context_1(ctx, list, i) {
|
||||
const child_ctx = ctx.slice();
|
||||
child_ctx[20] = list[i];
|
||||
child_ctx[21] = list[i];
|
||||
child_ctx[23] = i;
|
||||
return child_ctx;
|
||||
}
|
||||
function create_each_block_1(ctx) {
|
||||
function get_each_context_2(ctx, list, i) {
|
||||
const child_ctx = ctx.slice();
|
||||
child_ctx[24] = list[i];
|
||||
return child_ctx;
|
||||
}
|
||||
function create_each_block_2(ctx) {
|
||||
let div;
|
||||
let t_value = ctx[20][0] + "";
|
||||
let t_value = ctx[24][0] + "";
|
||||
let t;
|
||||
let div_style_value;
|
||||
return {
|
||||
@ -406,16 +411,16 @@
|
||||
div = element("div");
|
||||
t = text(t_value);
|
||||
attr(div, "class", "cell svelte-oncm9j");
|
||||
attr(div, "style", div_style_value = `width: ${ctx[5]}px; height: ${ctx[6]}px; line-height: ${ctx[6]}px; opacity: ${ctx[20][1] * 100}%`);
|
||||
attr(div, "style", div_style_value = `width: ${ctx[6]}px; height: ${ctx[7]}px; line-height: ${ctx[7]}px; opacity: ${ctx[24][1] * 100}%`);
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
append(div, t);
|
||||
},
|
||||
p(ctx2, dirty) {
|
||||
if (dirty & 8 && t_value !== (t_value = ctx2[20][0] + ""))
|
||||
if (dirty & 16 && t_value !== (t_value = ctx2[24][0] + ""))
|
||||
set_data(t, t_value);
|
||||
if (dirty & 8 && div_style_value !== (div_style_value = `width: ${ctx2[5]}px; height: ${ctx2[6]}px; line-height: ${ctx2[6]}px; opacity: ${ctx2[20][1] * 100}%`)) {
|
||||
if (dirty & 16 && div_style_value !== (div_style_value = `width: ${ctx2[6]}px; height: ${ctx2[7]}px; line-height: ${ctx2[7]}px; opacity: ${ctx2[24][1] * 100}%`)) {
|
||||
attr(div, "style", div_style_value);
|
||||
}
|
||||
},
|
||||
@ -425,14 +430,14 @@
|
||||
}
|
||||
};
|
||||
}
|
||||
function create_each_block(ctx) {
|
||||
function create_each_block_1(ctx) {
|
||||
let div;
|
||||
let t;
|
||||
let div_style_value;
|
||||
let each_value_1 = ctx[17];
|
||||
let each_value_2 = ctx[21];
|
||||
let each_blocks = [];
|
||||
for (let i = 0; i < each_value_1.length; i += 1) {
|
||||
each_blocks[i] = create_each_block_1(get_each_context_1(ctx, each_value_1, i));
|
||||
for (let i = 0; i < each_value_2.length; i += 1) {
|
||||
each_blocks[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i));
|
||||
}
|
||||
return {
|
||||
c() {
|
||||
@ -442,7 +447,7 @@
|
||||
}
|
||||
t = space();
|
||||
attr(div, "class", "row svelte-oncm9j");
|
||||
attr(div, "style", div_style_value = `height: ${ctx[6]}px; ` + (ctx[19] % 2 === 1 ? `padding-left: ${ctx[5] / 2}px` : ""));
|
||||
attr(div, "style", div_style_value = `height: ${ctx[7]}px; ` + (ctx[23] % 2 === 1 ? `padding-left: ${ctx[6] / 2}px` : ""));
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, div, anchor);
|
||||
@ -452,15 +457,15 @@
|
||||
append(div, t);
|
||||
},
|
||||
p(ctx2, dirty) {
|
||||
if (dirty & 104) {
|
||||
each_value_1 = ctx2[17];
|
||||
if (dirty & 208) {
|
||||
each_value_2 = ctx2[21];
|
||||
let i;
|
||||
for (i = 0; i < each_value_1.length; i += 1) {
|
||||
const child_ctx = get_each_context_1(ctx2, each_value_1, i);
|
||||
for (i = 0; i < each_value_2.length; i += 1) {
|
||||
const child_ctx = get_each_context_2(ctx2, each_value_2, i);
|
||||
if (each_blocks[i]) {
|
||||
each_blocks[i].p(child_ctx, dirty);
|
||||
} else {
|
||||
each_blocks[i] = create_each_block_1(child_ctx);
|
||||
each_blocks[i] = create_each_block_2(child_ctx);
|
||||
each_blocks[i].c();
|
||||
each_blocks[i].m(div, t);
|
||||
}
|
||||
@ -468,7 +473,7 @@
|
||||
for (; i < each_blocks.length; i += 1) {
|
||||
each_blocks[i].d(1);
|
||||
}
|
||||
each_blocks.length = each_value_1.length;
|
||||
each_blocks.length = each_value_2.length;
|
||||
}
|
||||
},
|
||||
d(detaching) {
|
||||
@ -497,7 +502,7 @@
|
||||
insert(target, a, anchor);
|
||||
insert(target, t2, anchor);
|
||||
if (!mounted) {
|
||||
dispose = listen(a, "click", ctx[4]);
|
||||
dispose = listen(a, "click", ctx[5]);
|
||||
mounted = true;
|
||||
}
|
||||
},
|
||||
@ -567,6 +572,47 @@
|
||||
}
|
||||
};
|
||||
}
|
||||
function create_each_block(ctx) {
|
||||
let li;
|
||||
let t0_value = ctx[18][0] + "";
|
||||
let t0;
|
||||
let t1;
|
||||
let t2_value = ctx[18][2] + "";
|
||||
let t2;
|
||||
let t3;
|
||||
let t4_value = ctx[18][1] + "";
|
||||
let t4;
|
||||
return {
|
||||
c() {
|
||||
li = element("li");
|
||||
t0 = text(t0_value);
|
||||
t1 = text(" x");
|
||||
t2 = text(t2_value);
|
||||
t3 = text(": ");
|
||||
t4 = text(t4_value);
|
||||
},
|
||||
m(target, anchor) {
|
||||
insert(target, li, anchor);
|
||||
append(li, t0);
|
||||
append(li, t1);
|
||||
append(li, t2);
|
||||
append(li, t3);
|
||||
append(li, t4);
|
||||
},
|
||||
p(ctx2, dirty) {
|
||||
if (dirty & 8 && t0_value !== (t0_value = ctx2[18][0] + ""))
|
||||
set_data(t0, t0_value);
|
||||
if (dirty & 8 && t2_value !== (t2_value = ctx2[18][2] + ""))
|
||||
set_data(t2, t2_value);
|
||||
if (dirty & 8 && t4_value !== (t4_value = ctx2[18][1] + ""))
|
||||
set_data(t4, t4_value);
|
||||
},
|
||||
d(detaching) {
|
||||
if (detaching)
|
||||
detach(li);
|
||||
}
|
||||
};
|
||||
}
|
||||
function create_fragment(ctx) {
|
||||
let h1;
|
||||
let t1;
|
||||
@ -576,16 +622,23 @@
|
||||
let div1;
|
||||
let t3;
|
||||
let t4;
|
||||
let t5;
|
||||
let ul;
|
||||
let mounted;
|
||||
let dispose;
|
||||
let each_value_1 = ctx[4];
|
||||
let each_blocks_1 = [];
|
||||
for (let i = 0; i < each_value_1.length; i += 1) {
|
||||
each_blocks_1[i] = create_each_block_1(get_each_context_1(ctx, each_value_1, i));
|
||||
}
|
||||
let if_block0 = ctx[0] && create_if_block_2(ctx);
|
||||
let if_block1 = ctx[2] && create_if_block_1(ctx);
|
||||
let if_block2 = ctx[1] && create_if_block(ctx);
|
||||
let each_value = ctx[3];
|
||||
let each_blocks = [];
|
||||
for (let i = 0; i < each_value.length; i += 1) {
|
||||
each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i));
|
||||
}
|
||||
let if_block0 = ctx[0] && create_if_block_2(ctx);
|
||||
let if_block1 = ctx[2] && create_if_block_1(ctx);
|
||||
let if_block2 = ctx[1] && create_if_block(ctx);
|
||||
return {
|
||||
c() {
|
||||
h1 = element("h1");
|
||||
@ -593,8 +646,8 @@
|
||||
t1 = space();
|
||||
div2 = element("div");
|
||||
div0 = element("div");
|
||||
for (let i = 0; i < each_blocks.length; i += 1) {
|
||||
each_blocks[i].c();
|
||||
for (let i = 0; i < each_blocks_1.length; i += 1) {
|
||||
each_blocks_1[i].c();
|
||||
}
|
||||
t2 = space();
|
||||
div1 = element("div");
|
||||
@ -606,6 +659,11 @@
|
||||
t4 = space();
|
||||
if (if_block2)
|
||||
if_block2.c();
|
||||
t5 = space();
|
||||
ul = element("ul");
|
||||
for (let i = 0; i < each_blocks.length; i += 1) {
|
||||
each_blocks[i].c();
|
||||
}
|
||||
attr(div0, "class", "game-display svelte-oncm9j");
|
||||
attr(div1, "class", "controls");
|
||||
attr(div2, "class", "wrapper svelte-oncm9j");
|
||||
@ -615,8 +673,8 @@
|
||||
insert(target, t1, anchor);
|
||||
insert(target, div2, anchor);
|
||||
append(div2, div0);
|
||||
for (let i = 0; i < each_blocks.length; i += 1) {
|
||||
each_blocks[i].m(div0, null);
|
||||
for (let i = 0; i < each_blocks_1.length; i += 1) {
|
||||
each_blocks_1[i].m(div0, null);
|
||||
}
|
||||
append(div2, t2);
|
||||
append(div2, div1);
|
||||
@ -628,32 +686,37 @@
|
||||
append(div1, t4);
|
||||
if (if_block2)
|
||||
if_block2.m(div1, null);
|
||||
append(div1, t5);
|
||||
append(div1, ul);
|
||||
for (let i = 0; i < each_blocks.length; i += 1) {
|
||||
each_blocks[i].m(ul, null);
|
||||
}
|
||||
if (!mounted) {
|
||||
dispose = [
|
||||
listen(window_1, "keydown", ctx[7]),
|
||||
listen(window_1, "keyup", ctx[8])
|
||||
listen(window_1, "keydown", ctx[8]),
|
||||
listen(window_1, "keyup", ctx[9])
|
||||
];
|
||||
mounted = true;
|
||||
}
|
||||
},
|
||||
p(ctx2, [dirty]) {
|
||||
if (dirty & 104) {
|
||||
each_value = ctx2[3];
|
||||
if (dirty & 208) {
|
||||
each_value_1 = ctx2[4];
|
||||
let i;
|
||||
for (i = 0; i < each_value.length; i += 1) {
|
||||
const child_ctx = get_each_context(ctx2, each_value, i);
|
||||
if (each_blocks[i]) {
|
||||
each_blocks[i].p(child_ctx, dirty);
|
||||
for (i = 0; i < each_value_1.length; i += 1) {
|
||||
const child_ctx = get_each_context_1(ctx2, each_value_1, i);
|
||||
if (each_blocks_1[i]) {
|
||||
each_blocks_1[i].p(child_ctx, dirty);
|
||||
} else {
|
||||
each_blocks[i] = create_each_block(child_ctx);
|
||||
each_blocks[i].c();
|
||||
each_blocks[i].m(div0, null);
|
||||
each_blocks_1[i] = create_each_block_1(child_ctx);
|
||||
each_blocks_1[i].c();
|
||||
each_blocks_1[i].m(div0, null);
|
||||
}
|
||||
}
|
||||
for (; i < each_blocks.length; i += 1) {
|
||||
each_blocks[i].d(1);
|
||||
for (; i < each_blocks_1.length; i += 1) {
|
||||
each_blocks_1[i].d(1);
|
||||
}
|
||||
each_blocks.length = each_value.length;
|
||||
each_blocks_1.length = each_value_1.length;
|
||||
}
|
||||
if (ctx2[0]) {
|
||||
if (if_block0) {
|
||||
@ -685,12 +748,30 @@
|
||||
} else {
|
||||
if_block2 = create_if_block(ctx2);
|
||||
if_block2.c();
|
||||
if_block2.m(div1, null);
|
||||
if_block2.m(div1, t5);
|
||||
}
|
||||
} else if (if_block2) {
|
||||
if_block2.d(1);
|
||||
if_block2 = null;
|
||||
}
|
||||
if (dirty & 8) {
|
||||
each_value = ctx2[3];
|
||||
let i;
|
||||
for (i = 0; i < each_value.length; i += 1) {
|
||||
const child_ctx = get_each_context(ctx2, each_value, i);
|
||||
if (each_blocks[i]) {
|
||||
each_blocks[i].p(child_ctx, dirty);
|
||||
} else {
|
||||
each_blocks[i] = create_each_block(child_ctx);
|
||||
each_blocks[i].c();
|
||||
each_blocks[i].m(ul, null);
|
||||
}
|
||||
}
|
||||
for (; i < each_blocks.length; i += 1) {
|
||||
each_blocks[i].d(1);
|
||||
}
|
||||
each_blocks.length = each_value.length;
|
||||
}
|
||||
},
|
||||
i: noop,
|
||||
o: noop,
|
||||
@ -701,13 +782,14 @@
|
||||
detach(t1);
|
||||
if (detaching)
|
||||
detach(div2);
|
||||
destroy_each(each_blocks, detaching);
|
||||
destroy_each(each_blocks_1, detaching);
|
||||
if (if_block0)
|
||||
if_block0.d();
|
||||
if (if_block1)
|
||||
if_block1.d();
|
||||
if (if_block2)
|
||||
if_block2.d();
|
||||
destroy_each(each_blocks, detaching);
|
||||
mounted = false;
|
||||
run_all(dispose);
|
||||
}
|
||||
@ -719,6 +801,7 @@
|
||||
let dead = false;
|
||||
let health;
|
||||
let players;
|
||||
let inventory = [];
|
||||
let ws;
|
||||
const connect = () => {
|
||||
ws = new WebSocket(window.location.protocol === "https:" ? "wss://ewo.osmarks.net/" : "ws://localhost:8080/");
|
||||
@ -731,8 +814,9 @@
|
||||
const row = r;
|
||||
newGrid[row + OFFSET][col + OFFSET] = [c, o];
|
||||
}
|
||||
$$invalidate(3, grid = newGrid);
|
||||
$$invalidate(4, grid = newGrid);
|
||||
$$invalidate(1, health = data.Display.health);
|
||||
$$invalidate(3, inventory = data.Display.inventory);
|
||||
}
|
||||
if (data === "Dead") {
|
||||
$$invalidate(0, dead = true);
|
||||
@ -782,10 +866,11 @@
|
||||
"a": "Left",
|
||||
"d": "Right",
|
||||
"z": "DownLeft",
|
||||
"x": "DownRight"
|
||||
"x": "DownRight",
|
||||
"f": "Dig"
|
||||
};
|
||||
connect();
|
||||
return [dead, health, players, grid, restart, HORIZ, VERT, keydown, keyup];
|
||||
return [dead, health, players, inventory, grid, restart, HORIZ, VERT, keydown, keyup];
|
||||
}
|
||||
var App = class extends SvelteComponent {
|
||||
constructor(options) {
|
||||
|
Loading…
Reference in New Issue
Block a user