diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 87b6370..e47d0f6 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -80,7 +80,11 @@ host's choice goes to every client with `"events"` in the create message) and wi dens of cave bears and giant beetles in the caverns of bigger random maps; neutral like the dwarves; beaten while a prison has room, a beast gives in and joins), traders (`src/traders.rs`: every ~10 minutes in single player a merchant waits a minute at an own portal; a click with the hand buys rune plans, an ember wyrm, a chainbreaker, -wood or a mana potion) and seasons (snow above ground in winter, pumpkins at Halloween, from the real date). +wood or a mana potion), seasons (snow above ground in winter, pumpkins at Halloween, from the real date) and +creature equipment (`src/armory.rs`: without orders the forge makes iron, steel and rune gear for gold; each +piece goes to the strongest creature without one that good: +10/22/38% health and attack, a coloured mark at +the health bar). The dynamic music (calm and battle) existed already and now also follows the camera to the +surface. Still open: the tutorial mission. 1. **Random events** – earthquake (tunnels cave in), lava eruption, flood, gold vein found. Rare (every 8–12 minutes), free play and multiplayer only, can be switched off in the menu; in diff --git a/lang/cs.lang b/lang/cs.lang index 58497f4..c2d1687 100644 --- a/lang/cs.lang +++ b/lang/cs.lang @@ -332,6 +332,10 @@ unit.priestess = Plamenná kněžka msg.hero_captured = Omráčen a zajat: {0}! Kořist: {1} zlata. msg.defected = Přeběhl na tvou stranu: {0}! msg.beast_tamed = Divoký {0} se vzdává a přidává se k tvé kobce! +gear.1 = železná výstroj +gear.2 = ocelová výstroj +gear.3 = runová výstroj +msg.gear_given = Kovárna předává {0}: {1}. msg.trader_arrives = Tvým portálem přišel obchodník: {0} za {1} zlata. Klikni na něj rukou a kup – čeká minutu. msg.trader_sold = Koupeno: {0}. Bylo mi potěšením, říká obchodník. msg.trader_left = Obchodník pokrčí rameny a odejde portálem. diff --git a/lang/da.lang b/lang/da.lang index 583bd08..9805d97 100644 --- a/lang/da.lang +++ b/lang/da.lang @@ -332,6 +332,10 @@ unit.priestess = Flammepræstinde msg.hero_captured = {0} slået bevidstløs og taget til fange! Bytte: {1} guld. msg.defected = {0} er gået over til din side! msg.beast_tamed = En vild {0} overgiver sig og slutter sig til dit fangehul! +gear.1 = jernudrustning +gear.2 = ståludrustning +gear.3 = runeudrustning +msg.gear_given = Smedjen giver {0} til: {1}. msg.trader_arrives = En handelsmand kom gennem din portal: {0} for {1} guld. Klik på ham med hånden for at købe – han venter et minut. msg.trader_sold = Købt: {0}. En fornøjelse, siger handelsmanden. msg.trader_left = Handelsmanden trækker på skuldrene og går gennem portalen. diff --git a/lang/de.lang b/lang/de.lang index 574df00..59b32af 100644 --- a/lang/de.lang +++ b/lang/de.lang @@ -331,6 +331,10 @@ unit.priestess = Flammenpriesterin msg.hero_captured = {0} bewusstlos geschlagen und gefangen genommen! Beute: {1} Gold. msg.defected = {0} ist zu dir übergelaufen! msg.beast_tamed = Ein wilder {0} ergibt sich und schließt sich deinem Kerker an! +gear.1 = Eisenausrüstung +gear.2 = Stahlausrüstung +gear.3 = Runenausrüstung +msg.gear_given = Die Schmiede übergibt {0} an: {1}. msg.trader_arrives = Ein Händler kam durch dein Portal: {0} für {1} Gold. Klick ihn mit der Hand an, um zu kaufen – er wartet eine Minute. msg.trader_sold = Gekauft: {0}. Ein Vergnügen, sagt der Händler. msg.trader_left = Der Händler zuckt mit den Schultern und geht durchs Portal. diff --git a/lang/el.lang b/lang/el.lang index afc8aab..1bacb64 100644 --- a/lang/el.lang +++ b/lang/el.lang @@ -332,6 +332,10 @@ unit.priestess = Ιέρεια της Φλόγας msg.hero_captured = Αναίσθητος και αιχμάλωτος: {0}! Λάφυρα: {1} χρυσός. msg.defected = Αυτομόλησε στο πλευρό σου: {0}! msg.beast_tamed = Ένα άγριο ον ({0}) παραδίνεται και μπαίνει στο μπουντρούμι σου! +gear.1 = σιδερένιος εξοπλισμός +gear.2 = ατσάλινος εξοπλισμός +gear.3 = ρουνικός εξοπλισμός +msg.gear_given = Το σιδηρουργείο δίνει {0} σε: {1}. msg.trader_arrives = Ένας έμπορος ήρθε από την πύλη σου: {0} για {1} χρυσό. Κάνε κλικ πάνω του με το χέρι για να αγοράσεις – περιμένει ένα λεπτό. msg.trader_sold = Αγοράστηκε: {0}. Ήταν χαρά μου, λέει ο έμπορος. msg.trader_left = Ο έμπορος σηκώνει τους ώμους και φεύγει από την πύλη. diff --git a/lang/en.lang b/lang/en.lang index 3d934e6..e164f59 100644 --- a/lang/en.lang +++ b/lang/en.lang @@ -332,6 +332,10 @@ unit.priestess = Flame Priestess msg.hero_captured = {0} knocked out and taken prisoner! Loot: {1} gold. msg.defected = {0} has defected to your side! msg.beast_tamed = A wild {0} gives in and joins your dungeon! +gear.1 = iron gear +gear.2 = steel gear +gear.3 = rune gear +msg.gear_given = The forge hands {0} to a {1}. msg.trader_arrives = A trader came through your portal: {0} for {1} gold. Click him with the hand to buy - he waits a minute. msg.trader_sold = Bought: {0}. Pleasure doing business, says the trader. msg.trader_left = The trader shrugs and leaves through the portal. diff --git a/lang/es.lang b/lang/es.lang index 481fd59..033ee48 100644 --- a/lang/es.lang +++ b/lang/es.lang @@ -332,6 +332,10 @@ unit.priestess = Sacerdotisa de llamas msg.hero_captured = ¡{0} noqueado y hecho prisionero! Botín: {1} de oro. msg.defected = ¡{0} se ha pasado a tu bando! msg.beast_tamed = ¡Un {0} salvaje se rinde y se une a tu mazmorra! +gear.1 = equipo de hierro +gear.2 = equipo de acero +gear.3 = equipo rúnico +msg.gear_given = La forja entrega {0} a: {1}. msg.trader_arrives = Un mercader ha cruzado tu portal: {0} por {1} de oro. Haz clic en él con la mano para comprar; espera un minuto. msg.trader_sold = Comprado: {0}. Un placer, dice el mercader. msg.trader_left = El mercader se encoge de hombros y se marcha por el portal. diff --git a/lang/fi.lang b/lang/fi.lang index 876ca56..865303b 100644 --- a/lang/fi.lang +++ b/lang/fi.lang @@ -332,6 +332,10 @@ unit.priestess = Liekkipapitar msg.hero_captured = {0} tyrmätty ja otettu vangiksi! Saalis: {1} kultaa. msg.defected = {0} on loikannut puolellesi! msg.beast_tamed = Villi {0} antautuu ja liittyy luolastoosi! +gear.1 = rautavarusteet +gear.2 = teräsvarusteet +gear.3 = riimuvarusteet +msg.gear_given = Paja antaa: {0} – {1}. msg.trader_arrives = Kauppias tuli portaalistasi: {0} hintaan {1} kultaa. Napsauta häntä kädellä ostaaksesi – hän odottaa minuutin. msg.trader_sold = Ostettu: {0}. Ilo tehdä kauppaa, sanoo kauppias. msg.trader_left = Kauppias kohauttaa olkapäitään ja lähtee portaalin kautta. diff --git a/lang/hr.lang b/lang/hr.lang index d28ddcd..4d9245a 100644 --- a/lang/hr.lang +++ b/lang/hr.lang @@ -332,6 +332,10 @@ unit.priestess = Svećenica plamena msg.hero_captured = Onesviješten i zarobljen: {0}! Plijen: {1} zlata. msg.defected = Prebjegao je na tvoju stranu: {0}! msg.beast_tamed = Divlji {0} se predaje i pridružuje tvojoj tamnici! +gear.1 = željezna oprema +gear.2 = čelična oprema +gear.3 = runska oprema +msg.gear_given = Kovačnica predaje {0}: {1}. msg.trader_arrives = Kroz tvoj portal došao je trgovac: {0} za {1} zlata. Klikni ga rukom da kupiš – čeka minutu. msg.trader_sold = Kupljeno: {0}. Zadovoljstvo, kaže trgovac. msg.trader_left = Trgovac sliježe ramenima i odlazi kroz portal. diff --git a/lang/hu.lang b/lang/hu.lang index 9153e7d..c0aa45d 100644 --- a/lang/hu.lang +++ b/lang/hu.lang @@ -332,6 +332,10 @@ unit.priestess = Lángpapnő msg.hero_captured = Kiütve és foglyul ejtve: {0}! Zsákmány: {1} arany. msg.defected = Átállt hozzád: {0}! msg.beast_tamed = Egy vad {0} megadja magát, és csatlakozik a kazamatádhoz! +gear.1 = vasfelszerelés +gear.2 = acélfelszerelés +gear.3 = rúnafelszerelés +msg.gear_given = A kovácsműhely átadja: {0} – {1}. msg.trader_arrives = Kereskedő érkezett a portálodon át: {0} {1} aranyért. Kattints rá a kézzel a vásárláshoz – egy percig vár. msg.trader_sold = Megvetted: {0}. Öröm volt, mondja a kereskedő. msg.trader_left = A kereskedő vállat von, és távozik a portálon át. diff --git a/lang/it.lang b/lang/it.lang index 1fe90b2..614f831 100644 --- a/lang/it.lang +++ b/lang/it.lang @@ -332,6 +332,10 @@ unit.priestess = Sacerdotessa della Fiamma msg.hero_captured = {0} stordito e fatto prigioniero! Bottino: {1} oro. msg.defected = {0} è passato dalla tua parte! msg.beast_tamed = Un {0} selvatico si arrende e si unisce al tuo dungeon! +gear.1 = equipaggiamento di ferro +gear.2 = equipaggiamento d'acciaio +gear.3 = equipaggiamento runico +msg.gear_given = La forgia consegna {0} a: {1}. msg.trader_arrives = Un mercante è arrivato dal tuo portale: {0} per {1} oro. Cliccalo con la mano per comprare: aspetta un minuto. msg.trader_sold = Comprato: {0}. Un piacere, dice il mercante. msg.trader_left = Il mercante alza le spalle e se ne va dal portale. diff --git a/lang/nl.lang b/lang/nl.lang index 2e5fad6..cb00928 100644 --- a/lang/nl.lang +++ b/lang/nl.lang @@ -332,6 +332,10 @@ unit.priestess = Vlampriesteres msg.hero_captured = {0} knock-out en gevangengenomen! Buit: {1} goud. msg.defected = {0} is naar jouw kant overgelopen! msg.beast_tamed = Een wilde {0} geeft zich over en sluit zich aan bij je kerker! +gear.1 = ijzeren uitrusting +gear.2 = stalen uitrusting +gear.3 = runenuitrusting +msg.gear_given = De smederij geeft {0} aan: {1}. msg.trader_arrives = Er kwam een handelaar door je portaal: {0} voor {1} goud. Klik hem aan met de hand om te kopen – hij wacht een minuut. msg.trader_sold = Gekocht: {0}. Prettig zakendoen, zegt de handelaar. msg.trader_left = De handelaar haalt zijn schouders op en vertrekt door het portaal. diff --git a/lang/no.lang b/lang/no.lang index 50d2607..d5d94a9 100644 --- a/lang/no.lang +++ b/lang/no.lang @@ -332,6 +332,10 @@ unit.priestess = Flammeprestinne msg.hero_captured = {0} slått bevisstløs og tatt til fange! Bytte: {1} gull. msg.defected = {0} har gått over til din side! msg.beast_tamed = En vill {0} overgir seg og slutter seg til fangehullet ditt! +gear.1 = jernutstyr +gear.2 = stålutstyr +gear.3 = runeutstyr +msg.gear_given = Smia gir {0} til: {1}. msg.trader_arrives = En handelsmann kom gjennom portalen din: {0} for {1} gull. Klikk på ham med hånden for å kjøpe – han venter et minutt. msg.trader_sold = Kjøpt: {0}. En fornøyelse, sier handelsmannen. msg.trader_left = Handelsmannen trekker på skuldrene og går gjennom portalen. diff --git a/lang/pt.lang b/lang/pt.lang index c752955..4d2a5f7 100644 --- a/lang/pt.lang +++ b/lang/pt.lang @@ -332,6 +332,10 @@ unit.priestess = Sacerdotisa das Chamas msg.hero_captured = {0} ficou inconsciente e foi feito prisioneiro! Saque: {1} de ouro. msg.defected = {0} desertou para o teu lado! msg.beast_tamed = Um {0} selvagem rende-se e junta-se à tua masmorra! +gear.1 = equipamento de ferro +gear.2 = equipamento de aço +gear.3 = equipamento rúnico +msg.gear_given = A forja entrega {0} a: {1}. msg.trader_arrives = Um mercador chegou pelo teu portal: {0} por {1} de ouro. Clica nele com a mão para comprar – espera um minuto. msg.trader_sold = Comprado: {0}. Foi um prazer, diz o mercador. msg.trader_left = O mercador encolhe os ombros e sai pelo portal. diff --git a/lang/ro.lang b/lang/ro.lang index ed58773..03fd56f 100644 --- a/lang/ro.lang +++ b/lang/ro.lang @@ -332,6 +332,10 @@ unit.priestess = Preoteasa Flăcării msg.hero_captured = {0} doborât și luat prizonier! Pradă: {1} aur. msg.defected = {0} a trecut de partea ta! msg.beast_tamed = Un {0} sălbatic se predă și se alătură temniței tale! +gear.1 = echipament de fier +gear.2 = echipament de oțel +gear.3 = echipament runic +msg.gear_given = Forja dă {0} lui: {1}. msg.trader_arrives = Un negustor a venit prin portalul tău: {0} pentru {1} aur. Dă clic pe el cu mâna ca să cumperi – așteaptă un minut. msg.trader_sold = Cumpărat: {0}. A fost o plăcere, spune negustorul. msg.trader_left = Negustorul ridică din umeri și pleacă prin portal. diff --git a/lang/ru.lang b/lang/ru.lang index 7794f3b..5ac8a15 100644 --- a/lang/ru.lang +++ b/lang/ru.lang @@ -332,6 +332,10 @@ unit.priestess = Жрица пламени msg.hero_captured = {0} оглушён и взят в плен! Добыча: {1} золота. msg.defected = {0} переходит на твою сторону! msg.beast_tamed = Дикий {0} сдаётся и присоединяется к твоему подземелью! +gear.1 = железное снаряжение +gear.2 = стальное снаряжение +gear.3 = руническое снаряжение +msg.gear_given = Кузница вручает {0}: {1}. msg.trader_arrives = Через твой портал пришёл торговец: {0} за {1} золота. Щёлкни по нему рукой, чтобы купить, — он ждёт минуту. msg.trader_sold = Куплено: {0}. Приятно иметь дело, говорит торговец. msg.trader_left = Торговец пожимает плечами и уходит через портал. diff --git a/lang/sk.lang b/lang/sk.lang index 8fea215..a9b6f0a 100644 --- a/lang/sk.lang +++ b/lang/sk.lang @@ -332,6 +332,10 @@ unit.priestess = Plamenná kňažka msg.hero_captured = Omráčený a zajatý: {0}! Korisť: {1} zlata. msg.defected = Prebehol na tvoju stranu: {0}! msg.beast_tamed = Divoký {0} sa vzdáva a pridáva sa k tvojej kobke! +gear.1 = železná výstroj +gear.2 = oceľová výstroj +gear.3 = runová výstroj +msg.gear_given = Vyhňa odovzdáva {0}: {1}. msg.trader_arrives = Cez tvoj portál prišiel obchodník: {0} za {1} zlata. Klikni naň rukou a kúp – čaká minútu. msg.trader_sold = Kúpené: {0}. Bolo mi potešením, hovorí obchodník. msg.trader_left = Obchodník pokrčí plecami a odíde cez portál. diff --git a/lang/sr.lang b/lang/sr.lang index 62e6738..169c1ef 100644 --- a/lang/sr.lang +++ b/lang/sr.lang @@ -332,6 +332,10 @@ unit.priestess = Свештеница пламена msg.hero_captured = Онесвешћен и заробљен: {0}! Плен: {1} злата. msg.defected = Пребегао је на твоју страну: {0}! msg.beast_tamed = Дивљи {0} се предаје и придружује твојој тамници! +gear.1 = гвоздена опрема +gear.2 = челична опрема +gear.3 = рунска опрема +msg.gear_given = Ковачница предаје {0}: {1}. msg.trader_arrives = Кроз твој портал дошао је трговац: {0} за {1} злата. Кликни га руком да купиш – чека минут. msg.trader_sold = Купљено: {0}. Задовољство, каже трговац. msg.trader_left = Трговац слеже раменима и одлази кроз портал. diff --git a/lang/sv.lang b/lang/sv.lang index e4c207c..bf95b4e 100644 --- a/lang/sv.lang +++ b/lang/sv.lang @@ -332,6 +332,10 @@ unit.priestess = Flamprästinna msg.hero_captured = {0} utslagen och tagen till fånga! Byte: {1} guld. msg.defected = {0} har gått över till din sida! msg.beast_tamed = En vild {0} ger sig och ansluter sig till din fängelsehåla! +gear.1 = järnutrustning +gear.2 = stålutrustning +gear.3 = runutrustning +msg.gear_given = Smedjan ger {0} till: {1}. msg.trader_arrives = En handlare kom genom din portal: {0} för {1} guld. Klicka på honom med handen för att köpa – han väntar en minut. msg.trader_sold = Köpt: {0}. Ett nöje, säger handlaren. msg.trader_left = Handlaren rycker på axlarna och går genom portalen. diff --git a/src/armory.rs b/src/armory.rs new file mode 100644 index 0000000..4a91414 --- /dev/null +++ b/src/armory.rs @@ -0,0 +1,104 @@ +// Creature equipment: when no trap or door is ordered, the forge makes armour and weapons in +// three tiers - iron, steel (once steel walls are known) and rune (once the magic door, the +// last discovery of the library, is known). Every finished piece goes to the strongest creature without anything that good yet: +// more health and harder blows. The tier shows as a little coloured mark at the health bar. + +use crate::game::*; + +// tier 1..3: forge work, gold, health and attack factor +pub const GEAR_WORK: [f32; 3] = [40.0, 70.0, 110.0]; +pub const GEAR_GOLD: [f32; 3] = [60.0, 140.0, 280.0]; +pub const GEAR_FACTOR: [f32; 4] = [1.0, 1.1, 1.22, 1.38]; + +pub fn gear_factor(gear: u8) -> f32 { + GEAR_FACTOR[(gear as usize).min(3)] +} + +impl Game { + // the best tier the forge can make now (1..3) + pub fn gear_tier(&self) -> u8 { + if self.known(31) { + 3 + } else if self.known(19) { + 2 + } else { + 1 + } + } + + // is a creature waiting for a piece of the best tier (and none in stock)? + pub fn gear_wanted(&self) -> bool { + let tier = self.gear_tier(); + self.gear_stock.get(tier as usize - 1).copied().unwrap_or(0) == 0 && self.ents.iter().any(|e| e.alive && e.creature() && e.gear < tier) + } + + // forge work without an order: the next piece of equipment + pub(crate) fn forge_gear(&mut self, work: f32) { + if !self.gear_wanted() { + return; + } + let tier = self.gear_tier(); + let k = tier as usize - 1; + self.gear_work += work; + if self.gear_work >= GEAR_WORK[k] && self.gold >= GEAR_GOLD[k] { + self.gear_work -= GEAR_WORK[k]; + self.gold -= GEAR_GOLD[k]; + if self.gear_stock.len() < 3 { + self.gear_stock.resize(3, 0); + } + self.gear_stock[k] += 1; + } + self.equip_from_stock(); + } + + // finished pieces go to the strongest creatures that have nothing that good + pub(crate) fn equip_from_stock(&mut self) { + for k in (0..self.gear_stock.len().min(3)).rev() { + while self.gear_stock[k] > 0 { + let tier = k as u8 + 1; + let best = (0..self.ents.len()) + .filter(|&j| { + let e = &self.ents[j]; + e.alive && e.creature() && e.gear < tier + }) + .max_by(|&a, &b| { + let (ea, eb) = (&self.ents[a], &self.ents[b]); + (ea.level, ea.max_hp as i32).cmp(&(eb.level, eb.max_hp as i32)) + }); + let Some(j) = best else { break }; + self.gear_stock[k] -= 1; + let e = &mut self.ents[j]; + let f = gear_factor(tier) / gear_factor(e.gear); + e.max_hp *= f; + e.hp *= f; + e.gear = tier; + let (x, y, kind) = (e.x, e.y, e.kind); + self.fx(7, x, y, 0.0, 0.0, 0.6, 0xff40c0ff); + self.msg("msg.gear_given", vec![format!("@gear.{}", tier), format!("@{}", st(kind).name)]); + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn the_forge_equips_the_strongest_creature() { + let mut g = Game::new(4, 64, 64); + let (cx, cy) = g.map.core; + let weak = g.spawn(EK::Kobold, false, cx as f32 + 4.5, cy as f32 + 1.5, 1); + let strong = g.spawn(EK::Ogre, false, cx as f32 + 5.5, cy as f32 + 1.5, 5); + g.gold = 1000.0; + assert!(g.gear_wanted()); + let hp0 = g.ents.iter().find(|e| e.id == strong).unwrap().max_hp; + g.forge_gear(GEAR_WORK[0] + 1.0); + let s = g.ents.iter().find(|e| e.id == strong).unwrap(); + let w = g.ents.iter().find(|e| e.id == weak).unwrap(); + assert_eq!(s.gear, 1, "the ogre got the first piece"); + assert_eq!(w.gear, 0); + assert!(s.max_hp > hp0 && s.atk() > st(EK::Ogre).atk * (1.0 + 0.12 * 4.0)); + assert!(g.gold < 1000.0); + } +} diff --git a/src/game.rs b/src/game.rs index f322a80..944d030 100644 --- a/src/game.rs +++ b/src/game.rs @@ -305,6 +305,8 @@ pub struct Ent { pub chained: bool, // a lone creature chained in a sealed cave: joins when the cave is claimed #[serde(default)] pub boss: u8, // a champion of the Order leading a wave (see bosses.rs), 0: none + #[serde(default)] + pub gear: u8, // equipment from the forge: 0 none, 1 iron, 2 steel, 3 rune (armory.rs) #[serde(skip, default = "zero_f32")] pub disp_x: f32, // smoothed screen position (not part of the saved or synced world state) #[serde(skip, default = "zero_f32")] @@ -381,6 +383,7 @@ impl Ent { target: None, chained: false, boss: 0, + gear: 0, disp_x: 0.0, disp_y: 0.0, } @@ -389,7 +392,7 @@ impl Ent { idx((self.x as i32).clamp(0, mw() as i32 - 1), (self.y as i32).clamp(0, mh() as i32 - 1)) } pub fn atk(&self) -> f32 { - st(self.kind).atk * (1.0 + 0.12 * (self.level - 1) as f32) + st(self.kind).atk * (1.0 + 0.12 * (self.level - 1) as f32) * crate::armory::gear_factor(self.gear) } // the player's creatures (not imps, heroes or the rivals' units) pub fn creature(&self) -> bool { @@ -494,6 +497,8 @@ pub struct MasterState { pub training_bill: f32, pub counts: [usize; N_ROOMS], pub room_level: Vec, + pub gear_stock: Vec, + pub gear_work: f32, pub springs: usize, pub known: Vec, pub researchable: Vec, @@ -531,6 +536,10 @@ pub struct Game { #[serde(skip)] pub counts: [usize; N_ROOMS], // room tiles per room kind (ROOM_KINDS order, recounted) #[serde(default)] + pub gear_stock: Vec, // finished equipment waiting for a creature, per tier (armory.rs) + #[serde(default)] + pub gear_work: f32, // forge work on the next piece + #[serde(default)] pub room_level: Vec, // level of every room kind (ROOM_KINDS order; 1 when missing), researched pub gate_hp: f32, pub gate_hit_t: f32, @@ -893,6 +902,8 @@ impl Game { hand: Vec::new(), counts: [0; N_ROOMS], room_level: Vec::new(), + gear_stock: Vec::new(), + gear_work: 0.0, gate_hp: GATE_HP, gate_hit_t: -100.0, gate_stage: 0, @@ -1463,6 +1474,8 @@ impl Game { training_bill: self.training_bill, counts: self.counts, room_level: self.room_level.clone(), + gear_stock: self.gear_stock.clone(), + gear_work: self.gear_work, springs: self.springs, known: std::mem::take(&mut self.known), researchable: std::mem::take(&mut self.researchable), @@ -1494,6 +1507,8 @@ impl Game { self.training_bill = m.training_bill; self.counts = m.counts; self.room_level = m.room_level; + self.gear_stock = m.gear_stock; + self.gear_work = m.gear_work; self.springs = m.springs; self.known = m.known; self.researchable = m.researchable; @@ -2374,7 +2389,7 @@ impl Game { e.xp -= 100.0 * e.level as f32; e.level += 1; let old = e.max_hp; - e.max_hp = s.hp * (1.0 + 0.15 * (e.level - 1) as f32); + e.max_hp = s.hp * (1.0 + 0.15 * (e.level - 1) as f32) * crate::armory::gear_factor(e.gear); e.hp += e.max_hp - old; self.fx(7, e.x, e.y, 0.0, 0.0, 0.8, 0xff44ddff); if !e.hero { diff --git a/src/main.rs b/src/main.rs index a9288b9..bbbb1f0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ // DungeonCraft – dungeon building & defence in the browser (Rust + macroquad, WASM). mod achievements; +mod armory; mod audio; mod campaign; mod campaign_doc; diff --git a/src/render3d.rs b/src/render3d.rs index 17b8860..1a1877e 100644 --- a/src/render3d.rs +++ b/src/render3d.rs @@ -1741,6 +1741,14 @@ impl R3d { if e.guard { gfx.glyph('☼', head.x - (GW * small * 0.5).floor(), head.y - 2.0 * GH * small, small, Color::from_rgba(255, 220, 90, 255)); } + // equipment from the forge: a little mark right of the health bar + if e.gear > 0 && px_per_tile >= 20.0 * F() as f32 { + let k = 2.0 * F() as f32; + let x = head.x + (cols as f32 * cw * 0.5).ceil() + 2.0 * k; + let c = [Color::from_rgba(170, 170, 180, 255), Color::from_rgba(110, 160, 255, 255), Color::from_rgba(90, 240, 255, 255)][(e.gear as usize - 1).min(2)]; + draw_rectangle(x, head.y - GH * small, 3.0 * k, 4.0 * k, Color::from_rgba(20, 16, 20, 255)); + draw_rectangle(x + k * 0.5, head.y - GH * small + k * 0.5, 2.0 * k, 3.0 * k, c); + } // rank badge left of the health bar if e.level > 1 && px_per_tile >= 20.0 * F() as f32 { let k = 2.0 * F() as f32; diff --git a/src/traps.rs b/src/traps.rs index 1f2a75f..509fb90 100644 --- a/src/traps.rs +++ b/src/traps.rs @@ -206,6 +206,10 @@ impl Game { // the forge: the work of the creatures there goes into the oldest order pub(crate) fn update_forge(&mut self, dt: f32) { let work = std::mem::take(&mut self.forge_work); + if !self.traps.iter().any(|t| t.stage == ORDERED) { + // no order: the forge makes equipment + self.forge_gear(work); + } if let Some(k) = self.traps.iter().position(|t| t.stage == ORDERED) { self.traps[k].progress += work; if self.traps[k].progress >= self.traps[k].kind.work() { @@ -244,7 +248,7 @@ impl Game { // is there forge work to do (an order waiting)? pub fn forge_orders(&self) -> bool { - self.traps.iter().any(|t| t.stage == ORDERED) + self.traps.iter().any(|t| t.stage == ORDERED) || self.gear_wanted() } // an imp looking for work: a finished crate to carry; returns the path to the crate diff --git a/src/ui.rs b/src/ui.rs index 24a2f8a..bc3876c 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1518,6 +1518,9 @@ pub fn hover_info(g: &Game, ent: Option, tile: Option<(i32, i32)>) -> Vec out.push((trf("info.level", &[tr(s.name), e.level.to_string()]), if e.hero { BLUE } else { FRAME_HI })); out.push((trf("info.rank", &[tr(&format!("rank.{}", e.level.clamp(1, 10)))]), GOLD)); out.push((trf("info.hp", &[(e.hp.max(0.0) as i32).to_string(), (e.max_hp as i32).to_string()]), GREEN)); + if e.gear > 0 { + out.push((tr(&format!("gear.{}", e.gear.min(3))), BLUE)); + } if e.hero && e.captive != 0 { out.push((tr(["", "info.unconscious", "info.carried", "info.tortured"][e.captive.min(3) as usize]), RED)); return out;