Imps reinforce walls next to claimed ground (heroes avoid them, ~10x slower to break)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c508e232b0
commit
a1fe02ffa6
8 files changed
+197
-13
No files matched your search
@@ -272,3 +272,8 @@ newgame.width = Kartenbreite
|
|||||||
newgame.height = Kartenhöhe
|
newgame.height = Kartenhöhe
|
||||||
newgame.start = Starten
|
newgame.start = Starten
|
||||||
newgame.info = {0} Portal(e), {1} bewachte Schatzhöhle(n)
|
newgame.info = {0} Portal(e), {1} bewachte Schatzhöhle(n)
|
||||||
|
|
||||||
|
# ---- Wände, Türme, Folter
|
||||||
|
job.reinforce = Verstärkt eine Wand
|
||||||
|
tile.reinforced = Verstärkte Wand
|
||||||
|
tile.reinforced_info = Helden kommen kaum durch
|
||||||
@@ -273,3 +273,8 @@ newgame.width = Map width
|
|||||||
newgame.height = Map height
|
newgame.height = Map height
|
||||||
newgame.start = Start
|
newgame.start = Start
|
||||||
newgame.info = {0} portal(s), {1} guarded treasure cave(s)
|
newgame.info = {0} portal(s), {1} guarded treasure cave(s)
|
||||||
|
|
||||||
|
# ---- walls, towers, torture
|
||||||
|
job.reinforce = Reinforcing a wall
|
||||||
|
tile.reinforced = Reinforced wall
|
||||||
|
tile.reinforced_info = heroes can hardly break through
|
||||||
+75
-2
@@ -42,13 +42,19 @@ pub enum Sfx {
|
|||||||
Defeat,
|
Defeat,
|
||||||
Click,
|
Click,
|
||||||
Error,
|
Error,
|
||||||
|
Reinforce,
|
||||||
|
Cannon,
|
||||||
|
Magic,
|
||||||
|
Capture,
|
||||||
|
Convert,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const SFX_ALL: [Sfx; 29] = [
|
pub const SFX_ALL: [Sfx; 34] = [
|
||||||
Sfx::Dig, Sfx::GoldDig, Sfx::DigDone, Sfx::Claim, Sfx::Deposit, Sfx::Build, Sfx::Sell, Sfx::MeleeHit,
|
Sfx::Dig, Sfx::GoldDig, Sfx::DigDone, Sfx::Claim, Sfx::Deposit, Sfx::Build, Sfx::Sell, Sfx::MeleeHit,
|
||||||
Sfx::Shoot, Sfx::Death, Sfx::HeroDeath, Sfx::Lightning, Sfx::Heal, Sfx::Summon, Sfx::PortalArrive,
|
Sfx::Shoot, Sfx::Death, Sfx::HeroDeath, Sfx::Lightning, Sfx::Heal, Sfx::Summon, Sfx::PortalArrive,
|
||||||
Sfx::WaveAlarm, Sfx::WaveStart, Sfx::CoreHit, Sfx::GateHit, Sfx::GateDefenders, Sfx::LevelUp,
|
Sfx::WaveAlarm, Sfx::WaveStart, Sfx::CoreHit, Sfx::GateHit, Sfx::GateDefenders, Sfx::LevelUp,
|
||||||
Sfx::Pickup, Sfx::Drop, Sfx::Payday, Sfx::FlagSet, Sfx::Victory, Sfx::Defeat, Sfx::Click, Sfx::Error,
|
Sfx::Pickup, Sfx::Drop, Sfx::Payday, Sfx::FlagSet, Sfx::Victory, Sfx::Defeat, Sfx::Click, Sfx::Error,
|
||||||
|
Sfx::Reinforce, Sfx::Cannon, Sfx::Magic, Sfx::Capture, Sfx::Convert,
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Sfx {
|
impl Sfx {
|
||||||
@@ -128,6 +134,7 @@ pub enum Job {
|
|||||||
AttackCore,
|
AttackCore,
|
||||||
Leave,
|
Leave,
|
||||||
Rally,
|
Rally,
|
||||||
|
Reinforce(usize), // wall tile
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize)]
|
#[derive(Clone, Serialize, Deserialize)]
|
||||||
@@ -840,7 +847,7 @@ impl Game {
|
|||||||
EK::Dwarf => 3.0,
|
EK::Dwarf => 3.0,
|
||||||
EK::Paladin => 2.0,
|
EK::Paladin => 2.0,
|
||||||
_ => 1.0,
|
_ => 1.0,
|
||||||
};
|
} * if tile.reinforced { 0.2 } else { 1.0 };
|
||||||
self.fx(6, tx as f32 + 0.5, ty as f32 + 0.5, 0.0, 0.0, 0.4, 0xff3a5a7a);
|
self.fx(6, tx as f32 + 0.5, ty as f32 + 0.5, 0.0, 0.0, 0.4, 0xff3a5a7a);
|
||||||
self.sfx(Sfx::Dig, tx as f32 + 0.5, ty as f32 + 0.5);
|
self.sfx(Sfx::Dig, tx as f32 + 0.5, ty as f32 + 0.5);
|
||||||
let t = &mut self.map.t[next];
|
let t = &mut self.map.t[next];
|
||||||
@@ -884,6 +891,7 @@ impl Game {
|
|||||||
t.kind = Kind::Floor;
|
t.kind = Kind::Floor;
|
||||||
t.owner = Owner::None;
|
t.owner = Owner::None;
|
||||||
t.marked = false;
|
t.marked = false;
|
||||||
|
t.reinforced = false;
|
||||||
t.reserved = 0;
|
t.reserved = 0;
|
||||||
t.hp = 0.0;
|
t.hp = 0.0;
|
||||||
t.gold = 0;
|
t.gold = 0;
|
||||||
@@ -1054,6 +1062,22 @@ impl Game {
|
|||||||
found
|
found
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// an earth wall next to own floor that can be reinforced from tile i
|
||||||
|
fn reinforce_neighbor(&self, i: usize, me: u32) -> Option<usize> {
|
||||||
|
if self.map.t[i].owner != Owner::Player {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let (x, y) = xy(i);
|
||||||
|
for &(dx, dy) in &DIR4 {
|
||||||
|
if let Some(t) = self.map.get(x + dx, y + dy) {
|
||||||
|
if t.kind == Kind::Earth && !t.reinforced && !t.marked && (t.reserved == 0 || t.reserved == me) {
|
||||||
|
return Some(idx(x + dx, y + dy));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn claimable(&self, i: usize, me: u32) -> bool {
|
fn claimable(&self, i: usize, me: u32) -> bool {
|
||||||
let t = &self.map.t[i];
|
let t = &self.map.t[i];
|
||||||
if t.owner != Owner::None || !matches!(t.kind, Kind::Floor | Kind::Portal) {
|
if t.owner != Owner::None || !matches!(t.kind, Kind::Floor | Kind::Portal) {
|
||||||
@@ -1147,6 +1171,27 @@ impl Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Job::Reinforce(w) => {
|
||||||
|
let t = self.map.t[w];
|
||||||
|
if !adj4(cur, w) || t.kind != Kind::Earth || t.reinforced || t.marked {
|
||||||
|
self.unreserve(me);
|
||||||
|
e.job = Job::None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let (wx, wy) = xy(w);
|
||||||
|
e.face = sgn(wx as f32 + 0.5 - e.x);
|
||||||
|
e.working = true;
|
||||||
|
e.timer += dt;
|
||||||
|
if e.timer >= 2.5 {
|
||||||
|
self.unreserve(me);
|
||||||
|
e.job = Job::None;
|
||||||
|
let tl = &mut self.map.t[w];
|
||||||
|
tl.reinforced = true;
|
||||||
|
tl.hp = 12.0; // the player's imps need twice as long to dig it out again
|
||||||
|
self.fx(8, wx as f32 + 0.5, wy as f32 + 0.5, 0.0, 0.0, 0.5, 0xff4040e0);
|
||||||
|
self.sfx(Sfx::Reinforce, wx as f32 + 0.5, wy as f32 + 0.5);
|
||||||
|
}
|
||||||
|
}
|
||||||
Job::Claim(t) => {
|
Job::Claim(t) => {
|
||||||
if cur != t || !self.claimable(t, me) {
|
if cur != t || !self.claimable(t, me) {
|
||||||
self.unreserve(me);
|
self.unreserve(me);
|
||||||
@@ -1229,6 +1274,16 @@ impl Game {
|
|||||||
e.job = Job::Claim(end);
|
e.job = Job::Claim(end);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if let Some(p) = self.map.search(cur, self.map.walk_cost(), |i| self.reinforce_neighbor(i, me).is_some(), true) {
|
||||||
|
let end = p.last().copied().unwrap_or(cur);
|
||||||
|
if let Some(w) = self.reinforce_neighbor(end, me) {
|
||||||
|
self.map.t[w].reserved = me;
|
||||||
|
e.path = p;
|
||||||
|
e.job = Job::Reinforce(w);
|
||||||
|
e.timer = 0.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
e.think = 1.0;
|
e.think = 1.0;
|
||||||
self.wander(e, 3);
|
self.wander(e, 3);
|
||||||
}
|
}
|
||||||
@@ -1875,6 +1930,7 @@ pub fn job_name(e: &Ent) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Job::Leave => "job.leave",
|
Job::Leave => "job.leave",
|
||||||
|
Job::Reinforce(_) => "job.reinforce",
|
||||||
Job::Rally => {
|
Job::Rally => {
|
||||||
if e.working {
|
if e.working {
|
||||||
"job.gate_attack"
|
"job.gate_attack"
|
||||||
@@ -1962,4 +2018,21 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn imps_reinforce_walls() {
|
||||||
|
let mut g = Game::new(3, 64, 64);
|
||||||
|
for _ in 0..(120 * 20) {
|
||||||
|
g.update(0.05);
|
||||||
|
}
|
||||||
|
let n = g.map.t.iter().filter(|t| t.reinforced).count();
|
||||||
|
println!("reinforced walls after 2 minutes: {}", n);
|
||||||
|
assert!(n >= 10);
|
||||||
|
// heroes avoid them: the hero path cost through reinforced earth is 10x
|
||||||
|
let (cx, cy) = g.map.core;
|
||||||
|
let w = (cx - 4, cy);
|
||||||
|
if g.map.t[idx(w.0, w.1)].reinforced {
|
||||||
|
assert_eq!(g.map.hero_cost()(idx(w.0, w.1)), Some(70));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+78
-2
@@ -6,7 +6,7 @@ use macroquad::prelude::*;
|
|||||||
|
|
||||||
pub const CELL: f32 = 16.0;
|
pub const CELL: f32 = 16.0;
|
||||||
const ATLAS_COLS: u32 = 16;
|
const ATLAS_COLS: u32 = 16;
|
||||||
const ATLAS_ROWS: u32 = 5;
|
const ATLAS_ROWS: u32 = 6;
|
||||||
|
|
||||||
// atlas indices (4 variants each)
|
// atlas indices (4 variants each)
|
||||||
pub const T_ROCK: u32 = 0;
|
pub const T_ROCK: u32 = 0;
|
||||||
@@ -40,6 +40,12 @@ pub const I_FLAG: u32 = 68;
|
|||||||
pub const I_BOLT: u32 = 69;
|
pub const I_BOLT: u32 = 69;
|
||||||
pub const I_HEAL: u32 = 70;
|
pub const I_HEAL: u32 = 70;
|
||||||
pub const I_SUMMON: u32 = 71;
|
pub const I_SUMMON: u32 = 71;
|
||||||
|
pub const T_REINF: u32 = 72; // 4 variants: reinforced wall masonry
|
||||||
|
pub const T_TORTURE: u32 = 76; // 4 variants: torture chamber floor
|
||||||
|
pub const D_RACK: u32 = 80;
|
||||||
|
pub const I_ARROW: u32 = 81;
|
||||||
|
pub const I_CANNON: u32 = 82;
|
||||||
|
pub const I_MAGIC: u32 = 83;
|
||||||
|
|
||||||
pub struct Gfx {
|
pub struct Gfx {
|
||||||
pub atlas: Texture2D,
|
pub atlas: Texture2D,
|
||||||
@@ -365,6 +371,40 @@ fn gen_atlas() -> Image {
|
|||||||
_ => c(100, 60, 120),
|
_ => c(100, 60, 120),
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
// reinforced walls: masonry in dark stone with the player's red mortar
|
||||||
|
for v in 0..4u32 {
|
||||||
|
cv.cell(T_REINF + v, |x, y| {
|
||||||
|
let row = y / 4;
|
||||||
|
let off = if row % 2 == 1 { 4 } else { 0 };
|
||||||
|
if y % 4 == 3 || (x + off) % 8 == 7 {
|
||||||
|
return Some(shade((70, 26, 30), (rnd(x, y, v + 90) * 8.0) as i32));
|
||||||
|
}
|
||||||
|
let brick = hash((x + off) / 8, row, v + 91) % 4;
|
||||||
|
Some(shade((84, 76, 74), (rnd(x, y, v + 92) * 14.0) as i32 - 7 + brick as i32 * 5 - 6 + if y % 4 == 0 { 10 } else { 0 }))
|
||||||
|
});
|
||||||
|
// torture chamber: dark stones with rusty iron grates
|
||||||
|
cv.cell(T_TORTURE + v, |x, y| {
|
||||||
|
let grate = (4..=11).contains(&x) && (4..=11).contains(&y) && (x % 3 == 1 || y % 3 == 1);
|
||||||
|
if grate {
|
||||||
|
return Some(shade((96, 44, 30), (rnd(x, y, v + 93) * 12.0) as i32));
|
||||||
|
}
|
||||||
|
Some(stones(x, y, v + 94, (52, 40, 44), (26, 16, 18), 10))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// torture wheel
|
||||||
|
cv.cell(D_RACK, |x, y| {
|
||||||
|
let (dx, dy) = (x as f32 - 7.5, y as f32 - 7.0);
|
||||||
|
let d = (dx * dx + dy * dy).sqrt();
|
||||||
|
let a = dy.atan2(dx);
|
||||||
|
if (5.5..7.0).contains(&d) || (d < 6.0 && (a * 4.0 / std::f32::consts::PI).fract().abs() < 0.12) {
|
||||||
|
Some(c(110, 76, 44))
|
||||||
|
} else if y >= 13 && (x == 7 || x == 8 || (4..=11).contains(&x) && y == 15) {
|
||||||
|
Some(c(70, 48, 30))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// tool icons
|
// tool icons
|
||||||
cv.cell(I_PICK, |x, y| {
|
cv.cell(I_PICK, |x, y| {
|
||||||
let handle = (x + y == 16 || x + y == 17) && x >= 3;
|
let handle = (x + y == 16 || x + y == 17) && x >= 3;
|
||||||
@@ -423,6 +463,39 @@ fn gen_atlas() -> Image {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
cv.cell(I_ARROW, |x, y| {
|
||||||
|
// a small wooden tower with an arrow
|
||||||
|
if (4..=11).contains(&x) && y >= 7 {
|
||||||
|
Some(if (x + y) % 4 == 0 { c(90, 60, 34) } else { c(130, 88, 50) })
|
||||||
|
} else if y == 6 && x % 2 == 0 && (4..=11).contains(&x) {
|
||||||
|
Some(c(130, 88, 50))
|
||||||
|
} else if y == 3 && (2..=13).contains(&x) || (x == 12 && (y == 2 || y == 4)) {
|
||||||
|
Some(c(220, 220, 220))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cv.cell(I_CANNON, |x, y| {
|
||||||
|
let barrel = y >= 4 && y <= 7 && x >= 5 && x <= 14 && (y - 4) as i32 <= 3;
|
||||||
|
let wheel = (x - 6) * (x - 6) + (y - 10) * (y - 10) <= 9;
|
||||||
|
if barrel {
|
||||||
|
Some(c(50, 50, 56))
|
||||||
|
} else if wheel {
|
||||||
|
Some(c(120, 80, 40))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
cv.cell(I_MAGIC, |x, y| {
|
||||||
|
let crystal = (x - 8).abs() + (y - 5).abs() <= 3;
|
||||||
|
if crystal {
|
||||||
|
Some(c(190, 110, 255))
|
||||||
|
} else if (6..=9).contains(&x) && y >= 9 {
|
||||||
|
Some(c(110, 110, 124))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
cv.cell(I_SUMMON, |x, y| {
|
cv.cell(I_SUMMON, |x, y| {
|
||||||
let d = (((x - 8) * (x - 8) + (y - 8) * (y - 8)) as f32).sqrt();
|
let d = (((x - 8) * (x - 8) + (y - 8) * (y - 8)) as f32).sqrt();
|
||||||
let a = ((y - 8) as f32).atan2((x - 8) as f32);
|
let a = ((y - 8) as f32).atan2((x - 8) as f32);
|
||||||
@@ -645,10 +718,11 @@ impl Gfx {
|
|||||||
Rect::new((i * SPR_CELL + 1) as f32, (1 + if white { SPR_CELL } else { 0 }) as f32, SPR as f32, SPR as f32)
|
Rect::new((i * SPR_CELL + 1) as f32, (1 + if white { SPR_CELL } else { 0 }) as f32, SPR as f32, SPR as f32)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tile_tex(kind: Kind, claimed: bool, hero: bool, var: u8) -> u32 {
|
pub fn tile_tex(kind: Kind, claimed: bool, hero: bool, var: u8, reinforced: bool) -> u32 {
|
||||||
let v = var as u32 & 3;
|
let v = var as u32 & 3;
|
||||||
match kind {
|
match kind {
|
||||||
Kind::Rock => T_ROCK + v,
|
Kind::Rock => T_ROCK + v,
|
||||||
|
Kind::Earth if reinforced => T_REINF + v,
|
||||||
Kind::Earth => T_EARTH + v,
|
Kind::Earth => T_EARTH + v,
|
||||||
Kind::Gold => T_GOLD + v,
|
Kind::Gold => T_GOLD + v,
|
||||||
Kind::Floor => {
|
Kind::Floor => {
|
||||||
@@ -668,6 +742,8 @@ impl Gfx {
|
|||||||
Kind::Core => T_COREFLOOR + v,
|
Kind::Core => T_COREFLOOR + v,
|
||||||
Kind::Portal => T_PORTALFLOOR + v,
|
Kind::Portal => T_PORTALFLOOR + v,
|
||||||
Kind::Gate => T_GATEFLOOR + v,
|
Kind::Gate => T_GATEFLOOR + v,
|
||||||
|
Kind::Torture => T_TORTURE + v,
|
||||||
|
Kind::Tower => T_CLAIM + v,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-6
@@ -46,9 +46,12 @@ pub enum Kind {
|
|||||||
Core,
|
Core,
|
||||||
Portal,
|
Portal,
|
||||||
Gate,
|
Gate,
|
||||||
|
Torture,
|
||||||
|
Tower, // floor tile occupied by a defence tower (not walkable)
|
||||||
}
|
}
|
||||||
|
|
||||||
const KINDS: [Kind; 12] = [
|
// order matters for the packed savegame codec: only append new kinds
|
||||||
|
const KINDS: [Kind; 14] = [
|
||||||
Kind::Rock,
|
Kind::Rock,
|
||||||
Kind::Earth,
|
Kind::Earth,
|
||||||
Kind::Gold,
|
Kind::Gold,
|
||||||
@@ -61,6 +64,8 @@ const KINDS: [Kind; 12] = [
|
|||||||
Kind::Core,
|
Kind::Core,
|
||||||
Kind::Portal,
|
Kind::Portal,
|
||||||
Kind::Gate,
|
Kind::Gate,
|
||||||
|
Kind::Torture,
|
||||||
|
Kind::Tower,
|
||||||
];
|
];
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||||
@@ -93,11 +98,13 @@ pub struct Tile {
|
|||||||
pub bed: u32, // entity id + 1 sleeping here
|
pub bed: u32, // entity id + 1 sleeping here
|
||||||
#[serde(rename = "v")]
|
#[serde(rename = "v")]
|
||||||
pub var: u8, // texture variant
|
pub var: u8, // texture variant
|
||||||
|
#[serde(rename = "rf", default)]
|
||||||
|
pub reinforced: bool, // wall reinforced by imps
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Tile {
|
impl Tile {
|
||||||
pub fn new(kind: Kind) -> Tile {
|
pub fn new(kind: Kind) -> Tile {
|
||||||
Tile { kind, owner: Owner::None, hp: 0.0, gold: 0, marked: false, reserved: 0, food: 0.0, bed: 0, var: 0 }
|
Tile { kind, owner: Owner::None, hp: 0.0, gold: 0, marked: false, reserved: 0, food: 0.0, bed: 0, var: 0, reinforced: false }
|
||||||
}
|
}
|
||||||
pub fn solid(&self) -> bool {
|
pub fn solid(&self) -> bool {
|
||||||
matches!(self.kind, Kind::Rock | Kind::Earth | Kind::Gold)
|
matches!(self.kind, Kind::Rock | Kind::Earth | Kind::Gold)
|
||||||
@@ -108,7 +115,7 @@ impl Tile {
|
|||||||
pub fn walkable(&self) -> bool {
|
pub fn walkable(&self) -> bool {
|
||||||
matches!(
|
matches!(
|
||||||
self.kind,
|
self.kind,
|
||||||
Kind::Floor | Kind::Treasury | Kind::Lair | Kind::Farm | Kind::Training | Kind::Library | Kind::Portal | Kind::Gate
|
Kind::Floor | Kind::Treasury | Kind::Lair | Kind::Farm | Kind::Training | Kind::Library | Kind::Portal | Kind::Gate | Kind::Torture
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -429,9 +436,10 @@ impl Map {
|
|||||||
move |i| {
|
move |i| {
|
||||||
let tl = &self.t[i];
|
let tl = &self.t[i];
|
||||||
match tl.kind {
|
match tl.kind {
|
||||||
|
Kind::Earth if tl.reinforced => Some(70),
|
||||||
Kind::Earth => Some(7),
|
Kind::Earth => Some(7),
|
||||||
Kind::Gold => Some(12),
|
Kind::Gold => Some(12),
|
||||||
Kind::Rock | Kind::Core => None,
|
Kind::Rock | Kind::Core | Kind::Tower => None,
|
||||||
_ => Some(1),
|
_ => Some(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,7 +513,7 @@ fn tile_record(t: &Tile) -> [u8; REC] {
|
|||||||
let mut r = [0u8; REC];
|
let mut r = [0u8; REC];
|
||||||
r[0] = KINDS.iter().position(|&k| k == t.kind).unwrap_or(0) as u8;
|
r[0] = KINDS.iter().position(|&k| k == t.kind).unwrap_or(0) as u8;
|
||||||
r[1] = OWNERS.iter().position(|&o| o == t.owner).unwrap_or(0) as u8;
|
r[1] = OWNERS.iter().position(|&o| o == t.owner).unwrap_or(0) as u8;
|
||||||
r[2] = t.marked as u8;
|
r[2] = t.marked as u8 | (t.reinforced as u8) << 1;
|
||||||
r[3..7].copy_from_slice(&t.hp.to_le_bytes());
|
r[3..7].copy_from_slice(&t.hp.to_le_bytes());
|
||||||
r[7..11].copy_from_slice(&t.gold.to_le_bytes());
|
r[7..11].copy_from_slice(&t.gold.to_le_bytes());
|
||||||
r[11..15].copy_from_slice(&t.food.to_le_bytes());
|
r[11..15].copy_from_slice(&t.food.to_le_bytes());
|
||||||
@@ -567,7 +575,8 @@ fn tiles_de<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<Tile>, D::Error> {
|
|||||||
let t = Tile {
|
let t = Tile {
|
||||||
kind: *KINDS.get(r[0] as usize).unwrap_or(&Kind::Rock),
|
kind: *KINDS.get(r[0] as usize).unwrap_or(&Kind::Rock),
|
||||||
owner: *OWNERS.get(r[1] as usize).unwrap_or(&Owner::None),
|
owner: *OWNERS.get(r[1] as usize).unwrap_or(&Owner::None),
|
||||||
marked: r[2] != 0,
|
marked: r[2] & 1 != 0,
|
||||||
|
reinforced: r[2] & 2 != 0,
|
||||||
hp: f32::from_le_bytes(f(3)),
|
hp: f32::from_le_bytes(f(3)),
|
||||||
gold: i32::from_le_bytes(f(7)),
|
gold: i32::from_le_bytes(f(7)),
|
||||||
food: f32::from_le_bytes(f(11)),
|
food: f32::from_le_bytes(f(11)),
|
||||||
|
|||||||
+6
-3
@@ -14,7 +14,7 @@ pub const WALL_H: f32 = 0.95;
|
|||||||
const CHUNK: i32 = 16;
|
const CHUNK: i32 = 16;
|
||||||
pub const MAX_LIGHTS: usize = 24;
|
pub const MAX_LIGHTS: usize = 24;
|
||||||
const ATLAS_W: f32 = 256.0;
|
const ATLAS_W: f32 = 256.0;
|
||||||
const ATLAS_H: f32 = 80.0;
|
const ATLAS_H: f32 = 96.0;
|
||||||
const DISP_AMP: f32 = 0.11; // wall surface irregularity
|
const DISP_AMP: f32 = 0.11; // wall surface irregularity
|
||||||
|
|
||||||
// ------------------------------------------------------------------ camera
|
// ------------------------------------------------------------------ camera
|
||||||
@@ -482,7 +482,7 @@ fn build_chunk(map: &Map, amb: &[f32], cx: i32, cz: i32, gold_fill: f32, atlas:
|
|||||||
for z in cz * CHUNK..((cz + 1) * CHUNK).min(mh() as i32) {
|
for z in cz * CHUNK..((cz + 1) * CHUNK).min(mh() as i32) {
|
||||||
for x in cx * CHUNK..((cx + 1) * CHUNK).min(mw() as i32) {
|
for x in cx * CHUNK..((cx + 1) * CHUNK).min(mw() as i32) {
|
||||||
let t = map.t[idx(x, z)];
|
let t = map.t[idx(x, z)];
|
||||||
let tex = Gfx::tile_tex(t.kind, t.owner == Owner::Player, t.owner == Owner::Hero, t.var);
|
let tex = Gfx::tile_tex(t.kind, t.owner == Owner::Player, t.owner == Owner::Hero, t.var, t.reinforced);
|
||||||
let (ua, ub) = cell_uv(tex);
|
let (ua, ub) = cell_uv(tex);
|
||||||
let (xf, zf) = (x as f32, z as f32);
|
let (xf, zf) = (x as f32, z as f32);
|
||||||
if !t.solid() {
|
if !t.solid() {
|
||||||
@@ -694,7 +694,7 @@ fn chunk_sig(map: &Map, cx: i32, cz: i32, fill_bucket: u8) -> u64 {
|
|||||||
Kind::Treasury => fill_bucket as u64,
|
Kind::Treasury => fill_bucket as u64,
|
||||||
_ => 0,
|
_ => 0,
|
||||||
};
|
};
|
||||||
(t.kind as u64) | (t.owner as u64) << 4 | (t.marked as u64) << 6 | extra << 8
|
(t.kind as u64) | (t.owner as u64) << 5 | (t.marked as u64) << 7 | (t.reinforced as u64) << 8 | extra << 9
|
||||||
}
|
}
|
||||||
None => 0xff,
|
None => 0xff,
|
||||||
};
|
};
|
||||||
@@ -1268,6 +1268,7 @@ fn draw_overlay(map: &Map, ov: &Overlay, n: usize) {
|
|||||||
pub fn minimap_color(tile: &Tile) -> [u8; 4] {
|
pub fn minimap_color(tile: &Tile) -> [u8; 4] {
|
||||||
match tile.kind {
|
match tile.kind {
|
||||||
Kind::Rock => [40, 40, 48, 255],
|
Kind::Rock => [40, 40, 48, 255],
|
||||||
|
Kind::Earth if tile.reinforced => [120, 70, 70, 255],
|
||||||
Kind::Earth => [86, 60, 40, 255],
|
Kind::Earth => [86, 60, 40, 255],
|
||||||
Kind::Gold => [220, 180, 50, 255],
|
Kind::Gold => [220, 180, 50, 255],
|
||||||
Kind::Floor => match tile.owner {
|
Kind::Floor => match tile.owner {
|
||||||
@@ -1283,5 +1284,7 @@ pub fn minimap_color(tile: &Tile) -> [u8; 4] {
|
|||||||
Kind::Core => [255, 60, 60, 255],
|
Kind::Core => [255, 60, 60, 255],
|
||||||
Kind::Portal => [190, 90, 240, 255],
|
Kind::Portal => [190, 90, 240, 255],
|
||||||
Kind::Gate => [230, 230, 255, 255],
|
Kind::Gate => [230, 230, 255, 255],
|
||||||
|
Kind::Torture => [120, 40, 40, 255],
|
||||||
|
Kind::Tower => [255, 140, 60, 255],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -146,6 +146,15 @@ fn def(s: Sfx, r: f32) -> Vec<f32> {
|
|||||||
Sfx::Defeat => l(&[seg(Square(0.5), 220.0, 110.0, 0.6, 0.22, 2.0)], 38),
|
Sfx::Defeat => l(&[seg(Square(0.5), 220.0, 110.0, 0.6, 0.22, 2.0)], 38),
|
||||||
Sfx::Click => l(&[seg(Square(0.5), 1000.0, 1000.0, 0.02, 0.12, 50.0)], 39),
|
Sfx::Click => l(&[seg(Square(0.5), 1000.0, 1000.0, 0.02, 0.12, 50.0)], 39),
|
||||||
Sfx::Error => l(&[seg(Square(0.5), 110.0, 105.0, 0.16, 0.2, 4.0)], 40),
|
Sfx::Error => l(&[seg(Square(0.5), 110.0, 105.0, 0.16, 0.2, 4.0)], 40),
|
||||||
|
Sfx::Reinforce => l(&[seg(Tri, 320.0, 200.0, 0.07, 0.5, 20.0), seg(Tri, 260.0, 170.0, 0.09, 0.5, 20.0)], 41),
|
||||||
|
Sfx::Cannon => mix(vec![l(&[seg(Noise, 1500.0, 150.0, 0.5, 0.6, 6.0)], 42), l(&[seg(Tri, 75.0, 32.0, 0.45, 0.9, 5.0)], 43)]),
|
||||||
|
Sfx::Magic => mix(vec![l(&[seg(Sine, 880.0, 1760.0, 0.16, 0.35, 8.0)], 44), l(&[vib(seg(Square(0.125), 1320.0, 1320.0, 0.16, 0.08, 10.0), 0.05)], 45)]),
|
||||||
|
Sfx::Capture => l(&[
|
||||||
|
seg(Square(0.25), 1400.0, 1400.0, 0.04, 0.16, 20.0),
|
||||||
|
seg(Square(0.25), 1100.0, 1100.0, 0.04, 0.16, 20.0),
|
||||||
|
seg(Square(0.25), 900.0, 900.0, 0.08, 0.16, 15.0),
|
||||||
|
], 46),
|
||||||
|
Sfx::Convert => l(&arp(Saw, &[220.0, 262.0, 330.0, 440.0], 0.1, 0.22), 47),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -617,6 +617,10 @@ pub fn hover_info(g: &Game, ent: Option<usize>, tile: Option<(i32, i32)>) -> Vec
|
|||||||
};
|
};
|
||||||
match t.kind {
|
match t.kind {
|
||||||
Kind::Rock => out.push((tr("tile.rock"), DIM)),
|
Kind::Rock => out.push((tr("tile.rock"), DIM)),
|
||||||
|
Kind::Earth if t.reinforced => {
|
||||||
|
out.push((tr("tile.reinforced"), FRAME_HI));
|
||||||
|
out.push((tr("tile.reinforced_info"), DIM));
|
||||||
|
}
|
||||||
Kind::Earth => out.push((tr("tile.earth"), FRAME_HI)),
|
Kind::Earth => out.push((tr("tile.earth"), FRAME_HI)),
|
||||||
Kind::Gold => {
|
Kind::Gold => {
|
||||||
out.push((tr("tile.gold"), GOLD));
|
out.push((tr("tile.gold"), GOLD));
|
||||||
|
|||||||
Reference in new issue
Block a user