mirror of
https://github.com/osmarks/ewo3.git
synced 2025-01-04 22: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",
|
"crypto-common",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "euclid"
|
name = "euclid"
|
||||||
version = "0.22.10"
|
version = "0.22.10"
|
||||||
@ -149,6 +155,7 @@ dependencies = [
|
|||||||
"fastrand",
|
"fastrand",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"hecs",
|
"hecs",
|
||||||
|
"indexmap",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"noise-functions",
|
"noise-functions",
|
||||||
"seahash",
|
"seahash",
|
||||||
@ -286,6 +293,16 @@ version = "1.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
|
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]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.11"
|
version = "1.0.11"
|
||||||
|
@ -19,4 +19,5 @@ serde_json = "1"
|
|||||||
slab = "0.4"
|
slab = "0.4"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
seahash = "4"
|
seahash = "4"
|
||||||
noise-functions = "0.2"
|
noise-functions = "0.2"
|
||||||
|
indexmap = "2"
|
@ -78,6 +78,11 @@
|
|||||||
{#if health}
|
{#if health}
|
||||||
Your health is {health}.
|
Your health is {health}.
|
||||||
{/if}
|
{/if}
|
||||||
|
<ul>
|
||||||
|
{#each inventory as item}
|
||||||
|
<li>{item[0]} x{item[2]}: {item[1]}</li>
|
||||||
|
{/each}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -89,6 +94,7 @@
|
|||||||
let dead = false
|
let dead = false
|
||||||
let health
|
let health
|
||||||
let players
|
let players
|
||||||
|
let inventory = []
|
||||||
|
|
||||||
let ws
|
let ws
|
||||||
const connect = () => {
|
const connect = () => {
|
||||||
@ -105,6 +111,7 @@
|
|||||||
}
|
}
|
||||||
grid = newGrid
|
grid = newGrid
|
||||||
health = data.Display.health
|
health = data.Display.health
|
||||||
|
inventory = data.Display.inventory
|
||||||
}
|
}
|
||||||
if (data === "Dead") {
|
if (data === "Dead") {
|
||||||
dead = true
|
dead = true
|
||||||
@ -164,7 +171,8 @@
|
|||||||
"a": "Left",
|
"a": "Left",
|
||||||
"d": "Right",
|
"d": "Right",
|
||||||
"z": "DownLeft",
|
"z": "DownLeft",
|
||||||
"x": "DownRight"
|
"x": "DownRight",
|
||||||
|
"f": "Dig"
|
||||||
}
|
}
|
||||||
|
|
||||||
connect()
|
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 euclid::{Point3D, Point2D, Vector2D};
|
||||||
use futures_util::{stream::TryStreamExt, SinkExt, StreamExt};
|
use futures_util::{stream::TryStreamExt, SinkExt, StreamExt};
|
||||||
|
use indexmap::IndexMap;
|
||||||
use noise_functions::Sample3;
|
use noise_functions::Sample3;
|
||||||
use tokio::net::{TcpListener, TcpStream};
|
use tokio::net::{TcpListener, TcpStream};
|
||||||
use tokio_tungstenite::tungstenite::protocol::Message;
|
use tokio_tungstenite::tungstenite::protocol::Message;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
use anyhow::{Result, Context, anyhow};
|
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 slab::Slab;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
@ -90,8 +91,9 @@ enum Input {
|
|||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
enum Frame {
|
enum Frame {
|
||||||
Dead,
|
Dead,
|
||||||
Display { nearby: Vec<(i64, i64, char, f32)>, health: f32 },
|
Display { nearby: Vec<(i64, i64, char, f32)>, health: f32, inventory: Vec<(String, String, u64)> },
|
||||||
PlayerCount(usize)
|
PlayerCount(usize),
|
||||||
|
Message(String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Client {
|
struct Client {
|
||||||
@ -106,16 +108,45 @@ struct GameState {
|
|||||||
ticks: u64
|
ticks: u64
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
enum Item {
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
struct PlayerCharacter;
|
struct PlayerCharacter;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
struct Position(Coord);
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
struct MovingInto(Coord);
|
struct MovingInto(Coord);
|
||||||
@ -123,6 +154,13 @@ struct MovingInto(Coord);
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Health(f32, f32);
|
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)]
|
#[derive(Debug, Clone)]
|
||||||
struct Render(char);
|
struct Render(char);
|
||||||
|
|
||||||
@ -153,20 +191,17 @@ struct Collidable;
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Velocity(CoordVec);
|
struct Velocity(CoordVec);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct DeferredRandomly<T: Clone + std::fmt::Debug + hecs::Bundle>(u64, T);
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Terrain;
|
struct Terrain;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Obstruction { entry_cost: StochasticNumber, exit_cost: StochasticNumber }
|
struct Obstruction { entry_multiplier: f32, exit_multiplier: f32 }
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Energy { current: f32, regeneration_rate: f32, burst: f32 }
|
struct Energy { current: f32, regeneration_rate: f32, burst: f32 }
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct DespawnOnImpact;
|
struct Drops(Vec<(Item, StochasticNumber)>);
|
||||||
|
|
||||||
impl Energy {
|
impl Energy {
|
||||||
fn try_consume(&mut self, cost: f32) -> bool {
|
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 VIEW: i64 = 15;
|
||||||
const WALL: i64 = 128;
|
const WALL: i64 = 128;
|
||||||
const RANDOM_DESPAWN_INV_RATE: u64 = 4000;
|
const RANDOM_DESPAWN_INV_RATE: u64 = 4000;
|
||||||
@ -239,21 +314,22 @@ struct EnemySpec {
|
|||||||
initial_health: f32,
|
initial_health: f32,
|
||||||
move_delay: usize,
|
move_delay: usize,
|
||||||
attack_cooldown: u64,
|
attack_cooldown: u64,
|
||||||
ranged: bool
|
ranged: bool,
|
||||||
|
drops: Vec<(Item, StochasticNumber)>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EnemySpec {
|
impl EnemySpec {
|
||||||
// Numbers ported from original EWO. Fudge constants added elsewhere.
|
// Numbers ported from original EWO. Fudge constants added elsewhere.
|
||||||
fn random() -> EnemySpec {
|
fn random() -> EnemySpec {
|
||||||
match fastrand::usize(0..650) {
|
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
|
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 }, // KESTREL
|
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 }, // SNAKE
|
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 }, // EMU
|
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 }, // OGRE
|
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 }, // RAT
|
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 }, // MOA
|
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 }, // PLATYPUS
|
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!()
|
_ => 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 {
|
fn triangle_from_min_range(min: f32, range: f32) -> Self {
|
||||||
StochasticNumber::Triangle { min: min, max: min + range, mode: (min + range) / 2.0 }
|
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();
|
let mut positions = HashMap::new();
|
||||||
|
|
||||||
for (entity, pos) in state.world.query_mut::<hecs::With<&Position, &Collidable>>() {
|
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>>() {
|
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();
|
let mut buffer = hecs::CommandBuffer::new();
|
||||||
|
|
||||||
// Spawn enemies
|
// 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 {
|
if fastrand::usize(0..*spawn_rate_inv) == 0 {
|
||||||
let c = count_hexes(*spawn_range.end());
|
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;
|
let mut occupied = false;
|
||||||
for _ in 0..(c as f32 / spawn_density * 0.005).ceil() as usize {
|
for _ in 0..(c as f32 / spawn_density * 0.005).ceil() as usize {
|
||||||
if positions.contains_key(&newpos) {
|
if positions.contains_key(&newpos) {
|
||||||
occupied = true;
|
occupied = true;
|
||||||
break;
|
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() {
|
if !occupied && get_base_terrain(newpos).can_enter() && hex_distance(newpos, pos) >= *spawn_range.start() {
|
||||||
let spec = EnemySpec::random();
|
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 {
|
if spec.ranged {
|
||||||
buffer.spawn((
|
buffer.spawn((
|
||||||
Render(spec.symbol),
|
Render(spec.symbol),
|
||||||
Health(spec.initial_health, spec.initial_health),
|
Health(spec.initial_health, spec.initial_health),
|
||||||
Enemy,
|
Enemy,
|
||||||
RangedAttack { damage: StochasticNumber::triangle_from_min_range(spec.min_damage, spec.damage_range), energy: spec.attack_cooldown as f32, range: 4 },
|
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 }),
|
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,
|
Collidable,
|
||||||
DespawnRandomly(RANDOM_DESPAWN_INV_RATE),
|
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 {
|
} else {
|
||||||
buffer.spawn((
|
buffer.spawn((
|
||||||
@ -360,11 +447,12 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
|||||||
Health(spec.initial_health, spec.initial_health),
|
Health(spec.initial_health, spec.initial_health),
|
||||||
Enemy,
|
Enemy,
|
||||||
Attack { damage: StochasticNumber::triangle_from_min_range(spec.min_damage, spec.damage_range), energy: spec.attack_cooldown as f32 },
|
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 }),
|
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,
|
Collidable,
|
||||||
DespawnRandomly(RANDOM_DESPAWN_INV_RATE),
|
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
|
// 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() {
|
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) {
|
if let Ok(_) = state.world.get::<&EnemyTarget>(*target) {
|
||||||
buffer.insert_one(entity, MovingInto(*pos + *direction));
|
buffer.insert_one(entity, MovingInto(pos + *direction));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -386,11 +476,12 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
|||||||
|
|
||||||
// TODO we maybe need a spatial index for this
|
// TODO we maybe need a spatial index for this
|
||||||
for (_entity, (target_pos, target)) in state.world.query::<(&Position, &EnemyTarget)>().iter() {
|
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 {
|
if distance < target.aggression_range {
|
||||||
match closest {
|
match closest {
|
||||||
Some((_pos, old_distance)) if old_distance < distance => closest = Some((target_pos.0, distance)),
|
Some((_pos, old_distance)) if old_distance < distance => closest = Some((target_pos, distance)),
|
||||||
None => closest = Some((target_pos.0, 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 {
|
if let Some(ranged_attack) = ranged {
|
||||||
// slightly smart behaviour for ranged attacker: try to stay just within range
|
// slightly smart behaviour for ranged attacker: try to stay just within range
|
||||||
let direction = DIRECTIONS.iter().min_by_key(|dir|
|
let direction = DIRECTIONS.iter().min_by_key(|dir|
|
||||||
(hex_distance(*pos + **dir, target_pos) - (ranged_attack.range as i64 - 1)).abs()).unwrap();
|
(hex_distance(pos + **dir, target_pos) - (ranged_attack.range as i64 - 1)).abs()).unwrap();
|
||||||
buffer.insert_one(entity, MovingInto(*pos + *direction));
|
buffer.insert_one(entity, MovingInto(pos + *direction));
|
||||||
// do ranged attack if valid
|
// 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)) {
|
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());
|
let atk_dir = atk_dir.clamp(-CoordVec::one(), CoordVec::one());
|
||||||
buffer.spawn((
|
buffer.spawn((
|
||||||
@ -411,23 +502,23 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
|||||||
Enemy,
|
Enemy,
|
||||||
Attack { damage: ranged_attack.damage, energy: 0.0 },
|
Attack { damage: ranged_attack.damage, energy: 0.0 },
|
||||||
Velocity(atk_dir),
|
Velocity(atk_dir),
|
||||||
Position(*pos),
|
Position::single_tile(pos),
|
||||||
DespawnOnTick(state.ticks.wrapping_add(ranged_attack.range))
|
DespawnOnTick(state.ticks.wrapping_add(ranged_attack.range))
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let direction = DIRECTIONS.iter().min_by_key(|dir| hex_distance(*pos + **dir, target_pos)).unwrap();
|
let direction = DIRECTIONS.iter().min_by_key(|dir| hex_distance(pos + **dir, target_pos)).unwrap();
|
||||||
buffer.insert_one(entity, MovingInto(*pos + *direction));
|
buffer.insert_one(entity, MovingInto(pos + *direction));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// wander randomly (ethical)
|
// 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
|
// Process velocity
|
||||||
for (entity, (Position(pos), Velocity(vel))) in state.world.query_mut::<(&Position, &Velocity)>() {
|
for (entity, (pos, Velocity(vel))) in state.world.query_mut::<(&Position, &Velocity)>() {
|
||||||
buffer.insert_one(entity, MovingInto(*pos + *vel));
|
buffer.insert_one(entity, MovingInto(pos.head() + *vel));
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.run_on(&mut state.world);
|
buffer.run_on(&mut state.world);
|
||||||
@ -435,6 +526,9 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
|||||||
// Process inputs
|
// Process inputs
|
||||||
for (_id, client) in state.clients.iter_mut() {
|
for (_id, client) in state.clients.iter_mut() {
|
||||||
let mut next_movement = CoordVec::zero();
|
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 {
|
loop {
|
||||||
let recv = client.inputs_rx.try_recv();
|
let recv = client.inputs_rx.try_recv();
|
||||||
match recv {
|
match recv {
|
||||||
@ -447,28 +541,81 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
|||||||
Input::DownRight => next_movement = CoordVec::new(0, 1),
|
Input::DownRight => next_movement = CoordVec::new(0, 1),
|
||||||
Input::DownLeft => next_movement = CoordVec::new(-1, 1),
|
Input::DownLeft => next_movement = CoordVec::new(-1, 1),
|
||||||
Input::Dig => {
|
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())
|
Err(e) => return Err(e.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let position = state.world.get::<&mut Position>(client.entity)?.0;
|
|
||||||
let target = position + next_movement;
|
let target = position + next_movement;
|
||||||
if get_base_terrain(target).can_enter() && target != position {
|
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
|
buffer.run_on(&mut state.world);
|
||||||
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);
|
let mut despawn_buffer = HashSet::new();
|
||||||
if let Some(current_terrain) = terrain_positions.get(current_pos) {
|
|
||||||
move_cost += 1.0;
|
// 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) {
|
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() {
|
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() {
|
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 {
|
if x.0 < 0.0 {
|
||||||
buffer.despawn(target_entity);
|
kill(&mut buffer, &mut despawn_buffer, &state, target_entity, Some(entity), Some(*target_pos));
|
||||||
Some(Entry::Occupied(o))
|
Some(Entry::Occupied(o))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None // TODO: on pickup or something
|
None // no "on pickup" exists; emulated with health 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Entry::Vacant(v) => Some(Entry::Vacant(v))
|
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?
|
// TODO: perhaps this should be applied to attacks too?
|
||||||
if consume_energy_if_available(&mut energy, move_cost) {
|
if consume_energy_if_available(&mut energy, move_cost) {
|
||||||
*entry.or_insert(entity) = entity;
|
*entry.or_insert(entity) = entity;
|
||||||
positions.remove(current_pos);
|
positions.remove(¤t_pos.0.pop_back().unwrap());
|
||||||
*current_pos = *target_pos;
|
current_pos.0.push_front(*target_pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -518,26 +665,37 @@ async fn game_tick(state: &mut GameState) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process transient entities
|
// 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 {
|
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 {
|
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);
|
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
|
// Send views to clients
|
||||||
|
// TODO: terrain layer below others
|
||||||
for (_id, client) in state.clients.iter() {
|
for (_id, client) in state.clients.iter() {
|
||||||
client.frames_tx.send(Frame::PlayerCount(state.clients.len())).await?;
|
client.frames_tx.send(Frame::PlayerCount(state.clients.len())).await?;
|
||||||
let mut nearby = vec![];
|
let mut nearby = vec![];
|
||||||
if let Ok(pos) = state.world.get::<&Position>(client.entity) {
|
if let Ok(pos) = state.world.get::<&Position>(client.entity) {
|
||||||
let pos = pos.0;
|
let pos = pos.head();
|
||||||
for q in -VIEW..=VIEW {
|
for q in -VIEW..=VIEW {
|
||||||
for r in (-VIEW).max(-q - VIEW)..= VIEW.min(-q+VIEW) {
|
for r in (-VIEW).max(-q - VIEW)..= VIEW.min(-q+VIEW) {
|
||||||
let offset = CoordVec::new(q, r);
|
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) {
|
if let Some(entity) = positions.get(&pos) {
|
||||||
let render = state.world.get::<&Render>(*entity)?;
|
let render = state.world.get::<&Render>(*entity)?;
|
||||||
let health = if let Ok(h) = state.world.get::<&Health>(*entity) {
|
let health = if let Ok(h) = state.world.get::<&Health>(*entity) {
|
||||||
h.0 / h.1
|
h.pct()
|
||||||
} else { 1.0 };
|
} 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 {
|
} else {
|
||||||
let mut rng = rng_from_hash(pos);
|
let mut rng = rng_from_hash(pos);
|
||||||
let bg = if rng.usize(0..10) == 0 { ',' } else { '.' };
|
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;
|
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 {
|
} else {
|
||||||
client.frames_tx.send(Frame::Dead).await?;
|
client.frames_tx.send(Frame::Dead).await?;
|
||||||
}
|
}
|
||||||
@ -599,7 +762,7 @@ fn add_new_player(state: &mut GameState) -> Result<Entity> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(state.world.spawn((
|
Ok(state.world.spawn((
|
||||||
Position(pos),
|
Position::single_tile(pos),
|
||||||
PlayerCharacter,
|
PlayerCharacter,
|
||||||
Render(random_identifier()),
|
Render(random_identifier()),
|
||||||
Collidable,
|
Collidable,
|
||||||
@ -610,7 +773,9 @@ fn add_new_player(state: &mut GameState) -> Result<Entity> {
|
|||||||
spawn_range: 3..=10,
|
spawn_range: 3..=10,
|
||||||
spawn_rate_inv: 20,
|
spawn_rate_inv: 20,
|
||||||
aggression_range: 5
|
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;
|
var { window: window_1 } = globals;
|
||||||
function get_each_context(ctx, list, i) {
|
function get_each_context(ctx, list, i) {
|
||||||
const child_ctx = ctx.slice();
|
const child_ctx = ctx.slice();
|
||||||
child_ctx[17] = list[i];
|
child_ctx[18] = list[i];
|
||||||
child_ctx[19] = i;
|
|
||||||
return child_ctx;
|
return child_ctx;
|
||||||
}
|
}
|
||||||
function get_each_context_1(ctx, list, i) {
|
function get_each_context_1(ctx, list, i) {
|
||||||
const child_ctx = ctx.slice();
|
const child_ctx = ctx.slice();
|
||||||
child_ctx[20] = list[i];
|
child_ctx[21] = list[i];
|
||||||
|
child_ctx[23] = i;
|
||||||
return child_ctx;
|
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 div;
|
||||||
let t_value = ctx[20][0] + "";
|
let t_value = ctx[24][0] + "";
|
||||||
let t;
|
let t;
|
||||||
let div_style_value;
|
let div_style_value;
|
||||||
return {
|
return {
|
||||||
@ -406,16 +411,16 @@
|
|||||||
div = element("div");
|
div = element("div");
|
||||||
t = text(t_value);
|
t = text(t_value);
|
||||||
attr(div, "class", "cell svelte-oncm9j");
|
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) {
|
m(target, anchor) {
|
||||||
insert(target, div, anchor);
|
insert(target, div, anchor);
|
||||||
append(div, t);
|
append(div, t);
|
||||||
},
|
},
|
||||||
p(ctx2, dirty) {
|
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);
|
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);
|
attr(div, "style", div_style_value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -425,14 +430,14 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
function create_each_block(ctx) {
|
function create_each_block_1(ctx) {
|
||||||
let div;
|
let div;
|
||||||
let t;
|
let t;
|
||||||
let div_style_value;
|
let div_style_value;
|
||||||
let each_value_1 = ctx[17];
|
let each_value_2 = ctx[21];
|
||||||
let each_blocks = [];
|
let each_blocks = [];
|
||||||
for (let i = 0; i < each_value_1.length; i += 1) {
|
for (let i = 0; i < each_value_2.length; i += 1) {
|
||||||
each_blocks[i] = create_each_block_1(get_each_context_1(ctx, each_value_1, i));
|
each_blocks[i] = create_each_block_2(get_each_context_2(ctx, each_value_2, i));
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
c() {
|
c() {
|
||||||
@ -442,7 +447,7 @@
|
|||||||
}
|
}
|
||||||
t = space();
|
t = space();
|
||||||
attr(div, "class", "row svelte-oncm9j");
|
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) {
|
m(target, anchor) {
|
||||||
insert(target, div, anchor);
|
insert(target, div, anchor);
|
||||||
@ -452,15 +457,15 @@
|
|||||||
append(div, t);
|
append(div, t);
|
||||||
},
|
},
|
||||||
p(ctx2, dirty) {
|
p(ctx2, dirty) {
|
||||||
if (dirty & 104) {
|
if (dirty & 208) {
|
||||||
each_value_1 = ctx2[17];
|
each_value_2 = ctx2[21];
|
||||||
let i;
|
let i;
|
||||||
for (i = 0; i < each_value_1.length; i += 1) {
|
for (i = 0; i < each_value_2.length; i += 1) {
|
||||||
const child_ctx = get_each_context_1(ctx2, each_value_1, i);
|
const child_ctx = get_each_context_2(ctx2, each_value_2, i);
|
||||||
if (each_blocks[i]) {
|
if (each_blocks[i]) {
|
||||||
each_blocks[i].p(child_ctx, dirty);
|
each_blocks[i].p(child_ctx, dirty);
|
||||||
} else {
|
} 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].c();
|
||||||
each_blocks[i].m(div, t);
|
each_blocks[i].m(div, t);
|
||||||
}
|
}
|
||||||
@ -468,7 +473,7 @@
|
|||||||
for (; i < each_blocks.length; i += 1) {
|
for (; i < each_blocks.length; i += 1) {
|
||||||
each_blocks[i].d(1);
|
each_blocks[i].d(1);
|
||||||
}
|
}
|
||||||
each_blocks.length = each_value_1.length;
|
each_blocks.length = each_value_2.length;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
d(detaching) {
|
d(detaching) {
|
||||||
@ -497,7 +502,7 @@
|
|||||||
insert(target, a, anchor);
|
insert(target, a, anchor);
|
||||||
insert(target, t2, anchor);
|
insert(target, t2, anchor);
|
||||||
if (!mounted) {
|
if (!mounted) {
|
||||||
dispose = listen(a, "click", ctx[4]);
|
dispose = listen(a, "click", ctx[5]);
|
||||||
mounted = true;
|
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) {
|
function create_fragment(ctx) {
|
||||||
let h1;
|
let h1;
|
||||||
let t1;
|
let t1;
|
||||||
@ -576,16 +622,23 @@
|
|||||||
let div1;
|
let div1;
|
||||||
let t3;
|
let t3;
|
||||||
let t4;
|
let t4;
|
||||||
|
let t5;
|
||||||
|
let ul;
|
||||||
let mounted;
|
let mounted;
|
||||||
let dispose;
|
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_value = ctx[3];
|
||||||
let each_blocks = [];
|
let each_blocks = [];
|
||||||
for (let i = 0; i < each_value.length; i += 1) {
|
for (let i = 0; i < each_value.length; i += 1) {
|
||||||
each_blocks[i] = create_each_block(get_each_context(ctx, each_value, i));
|
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 {
|
return {
|
||||||
c() {
|
c() {
|
||||||
h1 = element("h1");
|
h1 = element("h1");
|
||||||
@ -593,8 +646,8 @@
|
|||||||
t1 = space();
|
t1 = space();
|
||||||
div2 = element("div");
|
div2 = element("div");
|
||||||
div0 = element("div");
|
div0 = element("div");
|
||||||
for (let i = 0; i < each_blocks.length; i += 1) {
|
for (let i = 0; i < each_blocks_1.length; i += 1) {
|
||||||
each_blocks[i].c();
|
each_blocks_1[i].c();
|
||||||
}
|
}
|
||||||
t2 = space();
|
t2 = space();
|
||||||
div1 = element("div");
|
div1 = element("div");
|
||||||
@ -606,6 +659,11 @@
|
|||||||
t4 = space();
|
t4 = space();
|
||||||
if (if_block2)
|
if (if_block2)
|
||||||
if_block2.c();
|
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(div0, "class", "game-display svelte-oncm9j");
|
||||||
attr(div1, "class", "controls");
|
attr(div1, "class", "controls");
|
||||||
attr(div2, "class", "wrapper svelte-oncm9j");
|
attr(div2, "class", "wrapper svelte-oncm9j");
|
||||||
@ -615,8 +673,8 @@
|
|||||||
insert(target, t1, anchor);
|
insert(target, t1, anchor);
|
||||||
insert(target, div2, anchor);
|
insert(target, div2, anchor);
|
||||||
append(div2, div0);
|
append(div2, div0);
|
||||||
for (let i = 0; i < each_blocks.length; i += 1) {
|
for (let i = 0; i < each_blocks_1.length; i += 1) {
|
||||||
each_blocks[i].m(div0, null);
|
each_blocks_1[i].m(div0, null);
|
||||||
}
|
}
|
||||||
append(div2, t2);
|
append(div2, t2);
|
||||||
append(div2, div1);
|
append(div2, div1);
|
||||||
@ -628,32 +686,37 @@
|
|||||||
append(div1, t4);
|
append(div1, t4);
|
||||||
if (if_block2)
|
if (if_block2)
|
||||||
if_block2.m(div1, null);
|
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) {
|
if (!mounted) {
|
||||||
dispose = [
|
dispose = [
|
||||||
listen(window_1, "keydown", ctx[7]),
|
listen(window_1, "keydown", ctx[8]),
|
||||||
listen(window_1, "keyup", ctx[8])
|
listen(window_1, "keyup", ctx[9])
|
||||||
];
|
];
|
||||||
mounted = true;
|
mounted = true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
p(ctx2, [dirty]) {
|
p(ctx2, [dirty]) {
|
||||||
if (dirty & 104) {
|
if (dirty & 208) {
|
||||||
each_value = ctx2[3];
|
each_value_1 = ctx2[4];
|
||||||
let i;
|
let i;
|
||||||
for (i = 0; i < each_value.length; i += 1) {
|
for (i = 0; i < each_value_1.length; i += 1) {
|
||||||
const child_ctx = get_each_context(ctx2, each_value, i);
|
const child_ctx = get_each_context_1(ctx2, each_value_1, i);
|
||||||
if (each_blocks[i]) {
|
if (each_blocks_1[i]) {
|
||||||
each_blocks[i].p(child_ctx, dirty);
|
each_blocks_1[i].p(child_ctx, dirty);
|
||||||
} else {
|
} else {
|
||||||
each_blocks[i] = create_each_block(child_ctx);
|
each_blocks_1[i] = create_each_block_1(child_ctx);
|
||||||
each_blocks[i].c();
|
each_blocks_1[i].c();
|
||||||
each_blocks[i].m(div0, null);
|
each_blocks_1[i].m(div0, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (; i < each_blocks.length; i += 1) {
|
for (; i < each_blocks_1.length; i += 1) {
|
||||||
each_blocks[i].d(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 (ctx2[0]) {
|
||||||
if (if_block0) {
|
if (if_block0) {
|
||||||
@ -685,12 +748,30 @@
|
|||||||
} else {
|
} else {
|
||||||
if_block2 = create_if_block(ctx2);
|
if_block2 = create_if_block(ctx2);
|
||||||
if_block2.c();
|
if_block2.c();
|
||||||
if_block2.m(div1, null);
|
if_block2.m(div1, t5);
|
||||||
}
|
}
|
||||||
} else if (if_block2) {
|
} else if (if_block2) {
|
||||||
if_block2.d(1);
|
if_block2.d(1);
|
||||||
if_block2 = null;
|
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,
|
i: noop,
|
||||||
o: noop,
|
o: noop,
|
||||||
@ -701,13 +782,14 @@
|
|||||||
detach(t1);
|
detach(t1);
|
||||||
if (detaching)
|
if (detaching)
|
||||||
detach(div2);
|
detach(div2);
|
||||||
destroy_each(each_blocks, detaching);
|
destroy_each(each_blocks_1, detaching);
|
||||||
if (if_block0)
|
if (if_block0)
|
||||||
if_block0.d();
|
if_block0.d();
|
||||||
if (if_block1)
|
if (if_block1)
|
||||||
if_block1.d();
|
if_block1.d();
|
||||||
if (if_block2)
|
if (if_block2)
|
||||||
if_block2.d();
|
if_block2.d();
|
||||||
|
destroy_each(each_blocks, detaching);
|
||||||
mounted = false;
|
mounted = false;
|
||||||
run_all(dispose);
|
run_all(dispose);
|
||||||
}
|
}
|
||||||
@ -719,6 +801,7 @@
|
|||||||
let dead = false;
|
let dead = false;
|
||||||
let health;
|
let health;
|
||||||
let players;
|
let players;
|
||||||
|
let inventory = [];
|
||||||
let ws;
|
let ws;
|
||||||
const connect = () => {
|
const connect = () => {
|
||||||
ws = new WebSocket(window.location.protocol === "https:" ? "wss://ewo.osmarks.net/" : "ws://localhost:8080/");
|
ws = new WebSocket(window.location.protocol === "https:" ? "wss://ewo.osmarks.net/" : "ws://localhost:8080/");
|
||||||
@ -731,8 +814,9 @@
|
|||||||
const row = r;
|
const row = r;
|
||||||
newGrid[row + OFFSET][col + OFFSET] = [c, o];
|
newGrid[row + OFFSET][col + OFFSET] = [c, o];
|
||||||
}
|
}
|
||||||
$$invalidate(3, grid = newGrid);
|
$$invalidate(4, grid = newGrid);
|
||||||
$$invalidate(1, health = data.Display.health);
|
$$invalidate(1, health = data.Display.health);
|
||||||
|
$$invalidate(3, inventory = data.Display.inventory);
|
||||||
}
|
}
|
||||||
if (data === "Dead") {
|
if (data === "Dead") {
|
||||||
$$invalidate(0, dead = true);
|
$$invalidate(0, dead = true);
|
||||||
@ -782,10 +866,11 @@
|
|||||||
"a": "Left",
|
"a": "Left",
|
||||||
"d": "Right",
|
"d": "Right",
|
||||||
"z": "DownLeft",
|
"z": "DownLeft",
|
||||||
"x": "DownRight"
|
"x": "DownRight",
|
||||||
|
"f": "Dig"
|
||||||
};
|
};
|
||||||
connect();
|
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 {
|
var App = class extends SvelteComponent {
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
|
Loading…
Reference in New Issue
Block a user