diff --git a/draugnorak.nvgt b/draugnorak.nvgt index 65f6bad..56dbe06 100644 --- a/draugnorak.nvgt +++ b/draugnorak.nvgt @@ -16,6 +16,7 @@ sound_pool p(300); #include "src/enemies/bandit.nvgt" #include "src/enemies/ground_game.nvgt" #include "src/enemies/flying_creatures.nvgt" +#include "src/enemies/corpse_worm.nvgt" #include "src/world/world_drops.nvgt" #include "src/world/world_snares.nvgt" #include "src/world/world_fires.nvgt" @@ -313,11 +314,13 @@ void run_game() { update_bandits(); update_boars(); update_flying_creatures(); + update_corpse_worms(); update_weapon_range_audio_all(); update_world_drops(); update_pets(); update_blessings(); update_notifications(); + update_goal_announcements(); // Fire damage check (only if not jumping) WorldFire @fire_on_tile = get_fire_at(x); diff --git a/files/instructions.md b/files/instructions.md index 0162628..836f2d2 100644 --- a/files/instructions.md +++ b/files/instructions.md @@ -11,10 +11,6 @@ Explore gather craft defend your base through the day and night cycle chmod +x draugnorak ``` -### Android Note -- Android builds require an external keyboard (USB or Bluetooth) -- Touch controls are not implemented because too many game controls to do this in a good way for playability - ## Quick Start - Move with Left or Right - Jump with Up diff --git a/lang/af.ini b/lang/af.ini index bc1af96..8eaeba3 100644 --- a/lang/af.ini +++ b/lang/af.ini @@ -626,6 +626,7 @@ msg.096539f23463=Die oorlogstromme slaan! Berei voor vir geveg! msg.09c28ae8f83b=Klippe msg.09d016686864=Stal nie gebou nie msg.0a3bdcef1906=Rietmandjies +msg.0a6e19e37b4d=nuwe doelwit msg.0a9d6438c818==== Betowerde Melodie === msg.0b3d8d5cdca6=- Bereik die basis en vernietig die barrikade msg.0bcd9af79f2d=woud @@ -984,14 +985,25 @@ system.goals.completed.entry_one={goal}. 1 doelpunt toegeken. system.goals.completed.none=Nog geen doelwitte voltooi nie. system.goals.completed.prompt=Voltooide doelwitte. {option} system.goals.current=Huidige doelwit: {goal}. Onbestede doelpunte: {unspent}. +system.goals.new=Nuwe doelwit: {goal} Druk G om jou huidige doelwit te hersien. +system.goals.spend_hint=Om doelpunte te spandeer, druk P vir die karakterkieslys, kies dan Doelwitte en Bemeestering. system.goals.goal.build_altar.name=Bou 'n altaar. system.goals.goal.build_firepit.name=Bou 'n vuurput. system.goals.goal.build_storage.name=Bou berging. system.goals.goal.complete_quest.name=Voltooi 'n soektog. -system.goals.goal.completed=Doelwit voltooi: {goal}. {points} doelpunte toegeken. +system.goals.goal.completed=Doelwit voltooi: {goal}. 1 doelpunt toegeken. system.goals.goal.craft_knife.name=Maak 'n mes. system.goals.goal.craft_spear.name=Maak 'n speer. system.goals.goal.engrave_rune.name=Grafeer 'n rune. +system.goals.goal.craft_axe.name=Maak 'n klipbyl. +system.goals.goal.chop_tree_1.name=Kap 'n boom af. +system.goals.goal.chop_tree_4.name=Kap 4 bome af. +system.goals.goal.chop_tree_9.name=Kap 9 bome af. +system.goals.goal.chop_tree_27.name=Kap 27 bome af. +system.goals.goal.craft_snare.name=Maak 'n strik. +system.goals.goal.catch_snare.name=Vang iets in 'n strik. +system.goals.goal.butcher_game.name=Slag wild. +system.goals.goal.craft_sling.name=Maak 'n slinger. system.goals.goal.light_fire.name=Steek 'n vuur aan. system.goals.goal.unknown.name=Onbekende doelwit. system.goals.goal.win_adventure.name=Wen 'n avontuur. @@ -1014,14 +1026,29 @@ system.goals.mastery.prompt=Bemeestering. {option} system.goals.mastery.crafting.level1=Gemaakte basiese gereedskap is stewiger. system.goals.mastery.crafting.level2=Ontsluit groot speer resep. system.goals.mastery.crafting.level3=Groot speer bereik 2 teëls maar het 'n stadiger aanvalsvenster. +system.goals.mastery.crafting.level4=Persoonlike gereedskap, wapens en klere slyt nie meer uit nie. system.goals.mastery.crafting.name=Handwerk Bemeestering +system.goals.mastery.leadership.level1=Inwoners is versigtig en breek minder gereedskap en wapens. +system.goals.mastery.leadership.level2=Inwoners is georganiseer en slaag meer gereeld met werk. +system.goals.mastery.leadership.level3=Inwoners tree vinniger op tydens verdediging en dringende werk. +system.goals.mastery.leadership.level4=Inwoners rantsoeneer kos en eet elke 12 uur in plaas van elke 8. +system.goals.mastery.leadership.name=Leierskap Bemeestering +system.goals.mastery.taming.level1=Troeteldiere kan gevalle items haal. +system.goals.mastery.taming.level2=Troeteldiere is sterker en kry een maksimum gesondheid. +system.goals.mastery.taming.level3=Troeteldiere herstel vinniger van bewusteloosheid. +system.goals.mastery.taming.level4=Gebonde troeteldiere benodig nie meer kos nie en bly ten volle lojaal. +system.goals.mastery.taming.name=Tem Bemeestering system.goals.mastery.unknown.name=Onbekende bemeestering system.goals.mastery.unknown_level=Onbekende ontgrendeling -system.goals.mastery.maxed=Handwerk Bemeestering: maks bereik +system.goals.mastery.maxed={category}: maks bereik system.goals.mastery.summary={category} vlak {level} van {max}. Volgende koste: {cost} doelpunte. system.goals.mastery.unlocked=Ontgrendel {unlock}. system.goals.mastery.confirm=Spandeer {cost} doelpunte om {unlock} te ontgrendel? -system.goals.mastery.already_maxed=Handwerk Bemeestering is reeds maks. +system.goals.mastery.already_maxed={category} is reeds maks. system.goals.mastery.not_enough_points=Benodig {cost} doelpunte. Jy het {unspent}. system.goals.mastery.spend_failed=Kon nie die doelpunt spandeer nie. system.goals.menu.back=Terug + +msg.06c9fefbd6d7=lykwurm sterf +msg.c6d56016b910=lykwurm +msg.d2e0d305bc2b='n Swak kruipende vyand wat buite die basis begin verskyn na dag 1. diff --git a/lang/en.ini b/lang/en.ini index 0c1edfb..2848b0c 100644 --- a/lang/en.ini +++ b/lang/en.ini @@ -233,9 +233,11 @@ character.info.goals=Goals: {unspent} unspent, {earned} earned character.info.goals_menu=Goals character.word.hour=hour character.word.hours=hours +goals.new=New goal: {goal} Press G to review your current goal. goals.current=Current goal: {goal}. Unspent goal points: {unspent}. goals.all_complete=All current goals complete. Unspent goal points: {unspent}. -goals.goal.completed=Goal complete: {goal}. Awarded {points} goal points. +goals.spend_hint=To spend goal points, press P for the character menu, then choose Goals and Mastery. +goals.goal.completed=Goal complete: {goal}. Awarded 1 goal point. goals.reward.one=Reward: 1 goal point. goals.reward.many=Reward: {count} goal points. goals.goal.craft_knife.name=Craft a knife. @@ -247,6 +249,15 @@ goals.goal.build_altar.name=Build an altar. goals.goal.complete_quest.name=Complete a quest. goals.goal.win_adventure.name=Win an adventure. goals.goal.engrave_rune.name=Engrave a rune. +goals.goal.craft_axe.name=Craft a stone axe. +goals.goal.chop_tree_1.name=Chop down a tree. +goals.goal.chop_tree_4.name=Chop down 4 trees. +goals.goal.chop_tree_9.name=Chop down 9 trees. +goals.goal.chop_tree_27.name=Chop down 27 trees. +goals.goal.craft_snare.name=Craft a snare. +goals.goal.catch_snare.name=Catch something in a snare. +goals.goal.butcher_game.name=Butcher game. +goals.goal.craft_sling.name=Craft a sling. goals.goal.unknown.name=Unknown goal. goals.menu.prompt=Goals. {option} goals.menu.current=Current goal: {goal} @@ -264,18 +275,29 @@ goals.completed.entry_one={goal}. Awarded 1 goal point. goals.completed.entry_many={goal}. Awarded {points} goal points. goals.mastery.prompt=Mastery. {option} goals.mastery.crafting.name=Crafting Mastery +goals.mastery.leadership.name=Leadership Mastery +goals.mastery.taming.name=Taming Mastery goals.mastery.unknown.name=Unknown mastery goals.mastery.unknown_level=Unknown unlock -goals.mastery.maxed=Crafting Mastery: maxed out +goals.mastery.maxed={category}: maxed out goals.mastery.summary={category} level {level} of {max}. Next cost: {cost} goal points. goals.mastery.unlocked=Unlocked {unlock}. goals.mastery.confirm=Spend {cost} goal points to unlock {unlock}? -goals.mastery.already_maxed=Crafting Mastery is already maxed out. +goals.mastery.already_maxed={category} is already maxed out. goals.mastery.not_enough_points=Need {cost} goal points. You have {unspent}. goals.mastery.spend_failed=Could not spend the goal point. goals.mastery.crafting.level1=Crafted basic tools are sturdier. goals.mastery.crafting.level2=Unlock great spear recipe. goals.mastery.crafting.level3=Great spear reaches 2 tiles but has a slower attack window. +goals.mastery.crafting.level4=Personal tools, weapons, and clothing no longer wear out. +goals.mastery.leadership.level1=Residents are careful and break fewer tools and weapons. +goals.mastery.leadership.level2=Residents are organized and succeed at work more often. +goals.mastery.leadership.level3=Residents act faster during defense and urgent work. +goals.mastery.leadership.level4=Residents ration food, eating every 12 hours instead of every 8. +goals.mastery.taming.level1=Pets can retrieve dropped items. +goals.mastery.taming.level2=Pets are heartier and gain one maximum health. +goals.mastery.taming.level3=Pets recover from knockout faster. +goals.mastery.taming.level4=Bonded pets no longer require food and stay fully loyal. crafting.weapons.option.great_spear=Great Spear (1 Spear, 1 Log, 2 Vines, 2 Sinew) [Requires Crafting Mastery 2] crafting.weapons.crafted.great_spear=Crafted a Great Spear. crafting.weapons.great_spear.requires_mastery=Great spear requires Crafting Mastery 2. @@ -670,7 +692,7 @@ msg.03268c7d7239=backpacks msg.03976920927e=rope ; src/item_registry.nvgt:item_display:small_game msg.0406529811c7=Small Game -; draugnorak.nvgt:653:ui_info_box[2] +; draugnorak.nvgt:656:ui_info_box[2] msg.041d8413712d=A crash log was written to your save data folder. ; seed:learn_sounds_label:sounds/enemies/vampyr1.ogg msg.04a8916a9494=vampyr1 @@ -678,6 +700,8 @@ msg.04a8916a9494=vampyr1 msg.0532db2192fa=incense ; src/bosses/bandit_hideout.nvgt:787:insert_last[0] msg.066723a92e94==== Victory Rewards === +; seed:learn_sounds_label:sounds/enemies/corpse_worm_dies.ogg +msg.06c9fefbd6d7=corpse worm dies ; src/quests/enchanted_melody_game.nvgt:36:insert_last[0] msg.07e9ab246cdd=After closing this screen, you can practice the notes. ; src/item_registry.nvgt:item_plural:sinew @@ -690,6 +714,8 @@ msg.09c28ae8f83b=Stones msg.09d016686864=Stable not built ; src/item_registry.nvgt:item_display:reed_baskets msg.0a3bdcef1906=Reed Baskets +; seed:learn_sounds_label:sounds/new_goal.ogg +msg.0a6e19e37b4d=new goal ; src/quests/enchanted_melody_game.nvgt:26:insert_last[0] msg.0a9d6438c818==== Enchanted Melody === ; src/bosses/bandit_hideout.nvgt:305:insert_last[0] @@ -736,7 +762,7 @@ msg.18b837bb6f43=bat1 msg.18c77e824a2a=yeti dies ; seed:learn_sounds_label:sounds/enemies/bandit_dies.ogg msg.1a853cbfbc36=bandit dies -; src/sound_settings.nvgt:48:learn_sounds_add_description[1] +; src/sound_settings.nvgt:50:learn_sounds_add_description[1] msg.1bb8ef2886a3=Basic undead. ; seed:learn_sounds_label:sounds/nature/night.ogg msg.1be2a44cb53d=night @@ -878,7 +904,7 @@ msg.444d4678962b=falling msg.4537c16badd9=bow fire ; src/item_registry.nvgt:item_plural:snares msg.455b11a16eb7=snares -; src/sound_settings.nvgt:38:learn_sounds_add_description[1] +; src/sound_settings.nvgt:40:learn_sounds_add_description[1] msg.4671b16b0398=Not quite a zombie nor a vampyr. These undead remember they used to have a home, and maybe there's food there... ; seed:learn_sounds_label:sounds/terrain/draugrhaugr_step.ogg msg.46fcdd91c99e=draugrhaugr step @@ -916,13 +942,13 @@ msg.5009c8e190ee=turkey msg.508e8bc0cc57=- Use Up/Down to change the number ; seed:learn_sounds_label:sounds/weapons/sling_low_range.ogg msg.5123c22ce6a5=sling low range -; draugnorak.nvgt:112:insert_last[0] +; draugnorak.nvgt:113:insert_last[0] msg.528fb1c4e5fb=New Game ; seed:learn_sounds_label:sounds/quests/squish.ogg msg.5460f2e3c43e=squish ; seed:learn_sounds_label:sounds/actions/fishpole_break.ogg msg.546330ee64db=fishpole break -; draugnorak.nvgt:653:ui_info_box[0] +; draugnorak.nvgt:656:ui_info_box[0] msg.54fc2d657ed6=Draugnorak ; seed:learn_sounds_label:sounds/enemies/vampyr3.ogg msg.55748cf43121=vampyr3 @@ -950,7 +976,7 @@ msg.5cac31311668=Pasture is full. No livestock were recovered. msg.5cc7429297c8=that can be destroyed with an axe. ; seed:learn_sounds_label:sounds/actions/cast_strength.ogg msg.5dfc869b7588=cast strength -; src/time_system.nvgt:794:notify[0] +; src/time_system.nvgt:801:notify[0] msg.5ea84b1201bb={arg1} favor grants you the eyes of an eagle. ; seed:learn_sounds_label:sounds/menu/menu_select.ogg ; seed:learn_sounds_label:sounds/menu.bak/menu_select.ogg @@ -1034,8 +1060,8 @@ msg.7e83de91b4a2=bandit female dies msg.7f7e3bbf8864=An enemy is no longer in range of your currently wielded weapon. ; src/item_registry.nvgt:item_display:backpacks msg.7fae743693f4=Backpacks -; draugnorak.nvgt:231:ui_info_box[1] -; draugnorak.nvgt:245:ui_info_box[1] +; draugnorak.nvgt:232:ui_info_box[1] +; draugnorak.nvgt:246:ui_info_box[1] msg.802aa6576577=Load Game ; src/item_registry.nvgt:item_plural:sticks msg.803e4c41bbd2=sticks @@ -1044,12 +1070,12 @@ msg.803e4c41bbd2=sticks msg.80655da8d80a=tree ; src/item_registry.nvgt:item_plural:canoes msg.806baaf8e039=canoes -; src/time_system.nvgt:776:notify[0] -; src/time_system.nvgt:786:notify[0] +; src/time_system.nvgt:783:notify[0] +; src/time_system.nvgt:793:notify[0] msg.81df04a81e24={arg1} favor shines upon you. {arg2} ; src/quests/catch_the_boomerang_game.nvgt:22:insert_last[0] msg.823be948275f=- Better timing earns more points (up to 4) -; src/sound_settings.nvgt:41:learn_sounds_add_description[1] +; src/sound_settings.nvgt:43:learn_sounds_add_description[1] msg.83865f248682=More powerful undead. They do not lose track of you when you are in the base. ; seed:learn_sounds_label:sounds/nature/wind_high.ogg msg.8443de859917=wind high @@ -1071,7 +1097,7 @@ msg.86e257c3efcb=Oldest notification. {arg1} msg.87383ce4344d=Bowstrings ; src/item_registry.nvgt:item_plural:ropes msg.87cdb5c91437=ropes -; src/time_system.nvgt:790:notify[0] +; src/time_system.nvgt:797:notify[0] msg.8818f1d55166={arg1} radiance fills residents with purpose. ; src/quests/bat_invasion_game.nvgt:7:insert_last[0] msg.88749dbbbf09==== Bat Invasion === @@ -1089,7 +1115,7 @@ msg.8f993ac20023=You enter a narrow mountain pass. A massive Unicorn blocks your msg.917ee46db0cd=bee ; seed:learn_sounds_label:sounds/player_male_damage.ogg msg.924e0eb6b6f1=player male damage -; src/time_system.nvgt:781:notify[0] +; src/time_system.nvgt:788:notify[0] msg.9281d2fe5f08={arg1} favor shines upon you. You feel swift for a while. ; seed:learn_sounds_label:sounds/actions/climb_rope.ogg msg.92abc6d6953f=climb rope @@ -1140,9 +1166,9 @@ msg.a3cb738850fa=wolf msg.a3f2439ef01a=skin hat ; seed:learn_sounds_label:sounds/quests/bone1.ogg msg.a56be51786dc=bone1 -; src/sound_settings.nvgt:43:learn_sounds_add_description[1] -; src/sound_settings.nvgt:44:learn_sounds_add_description[1] ; src/sound_settings.nvgt:45:learn_sounds_add_description[1] +; src/sound_settings.nvgt:46:learn_sounds_add_description[1] +; src/sound_settings.nvgt:47:learn_sounds_add_description[1] msg.a570b024087d=Ever hungry, they feed upon your residents. ; src/quests/escape_from_hel_game.nvgt:15:insert_last[0] msg.a641f39a6317=- The run ends when you fall into a grave @@ -1218,7 +1244,7 @@ msg.c01409b3a728=When casting release control when over water. When catching rel msg.c05feaca7269=No items were recovered. ; src/item_registry.nvgt:item_singular:snare msg.c1104d82c0bd=snare -; src/sound_settings.nvgt:37:learn_sounds_add_description[1] +; src/sound_settings.nvgt:39:learn_sounds_add_description[1] msg.c48d642737b2=Inhabits mountainous regions. ; src/item_registry.nvgt:item_display:smoked_fish msg.c4f7e9c21601=Smoked Fish @@ -1228,6 +1254,8 @@ msg.c50641e24962=Your cast missed the water and landed in nearby foliage where i msg.c52888225c69=fall ; seed:learn_sounds_label:sounds/enemies/bandit1.ogg msg.c65e5dfc5ba7=bandit1 +; seed:learn_sounds_label:sounds/enemies/corpse_worm.ogg +msg.c6d56016b910=corpse worm ; src/bosses/bandit_hideout.nvgt:789:insert_last[0] msg.c72b7844929d=Bandits defeated: {arg1}. ; seed:learn_sounds_label:sounds/terrain/stream.ogg @@ -1259,7 +1287,7 @@ msg.ce3897c6b3be={arg1}: {arg2} ; seed:learn_sounds_label:sounds/items/item_breaks.ogg msg.ce5aeca95fb9=item breaks ; src/i18n.nvgt:677:speak_with_history[0] -; src/pet_system.nvgt:926:speak_with_history[0] +; src/pet_system.nvgt:935:speak_with_history[0] ; src/save_system.nvgt:344:speak_with_history[0] msg.cea88b214070={arg1} {arg2} ; seed:manual @@ -1268,11 +1296,13 @@ msg.cfe2650b4c6e=Found a stone. msg.d04a59a6a71b=Controls: LEFT/RIGHT to move, UP to jump, CTRL to attack, ESC to flee ; src/item_registry.nvgt:item_singular:skin msg.d0cb2acd0739=skin +; src/sound_settings.nvgt:37:learn_sounds_add_description[1] +msg.d2e0d305bc2b=A weak crawling enemy that starts appearing outside the base after day 1. ; seed:learn_sounds_label:sounds/enemies/enemy_falls.ogg msg.d34b6217ea75=enemy falls ; src/item_registry.nvgt:item_display:skin_gloves msg.d373510877fd=Skin Gloves -; draugnorak.nvgt:653:ui_info_box[1] +; draugnorak.nvgt:656:ui_info_box[1] ; seed:manual msg.d39008c4a392=Unhandled exception ; src/item_registry.nvgt:item_singular:backpack @@ -1295,7 +1325,7 @@ msg.da0b5a461d98=F or K - First note (lowest pitch) msg.da11dc5e090c=sling hit ; seed:learn_sounds_label:sounds/enemies/vampyr_dies.ogg msg.da93fb9992a3=vampyr dies -; src/sound_settings.nvgt:47:learn_sounds_add_description[1] +; src/sound_settings.nvgt:49:learn_sounds_add_description[1] msg.dadd68431e60=Inhabits snowy regions. ; seed:learn_sounds_label:sounds/enemies/exit_range.ogg msg.db2a8514a0fc=exit range @@ -1365,7 +1395,7 @@ msg.e7409fcb54bc=feed fire msg.e8002c169040=wood ; src/quests/escape_from_hel_game.nvgt:13:insert_last[0] msg.e8216720d30b=- Press SPACE to jump over graves -; draugnorak.nvgt:132:insert_last[0] +; draugnorak.nvgt:133:insert_last[0] msg.e8db3a42f86f=Learn Sounds ; src/bosses/bandit_hideout.nvgt:845:insert_last[0] msg.e8e8f4ee7a5a=You can now engrave weapons with this rune at the crafting menu. @@ -1396,7 +1426,7 @@ msg.f3d964225e83=player female damage msg.f5cabf873590=reed ; src/bosses/bandit_hideout.nvgt:301:insert_last[0] msg.f77fc018cb7b=You find a hidden bandit base deep in the forest. -; draugnorak.nvgt:148:insert_last[0] +; draugnorak.nvgt:149:insert_last[0] msg.f83b6fe3aebf=Exit ; src/inventory_items.nvgt:504:speak_with_history[0] ; src/quests/skeletal_bard_game.nvgt:26:speak_with_history[0] diff --git a/lang/en.template.ini b/lang/en.template.ini index 0c1edfb..2848b0c 100644 --- a/lang/en.template.ini +++ b/lang/en.template.ini @@ -233,9 +233,11 @@ character.info.goals=Goals: {unspent} unspent, {earned} earned character.info.goals_menu=Goals character.word.hour=hour character.word.hours=hours +goals.new=New goal: {goal} Press G to review your current goal. goals.current=Current goal: {goal}. Unspent goal points: {unspent}. goals.all_complete=All current goals complete. Unspent goal points: {unspent}. -goals.goal.completed=Goal complete: {goal}. Awarded {points} goal points. +goals.spend_hint=To spend goal points, press P for the character menu, then choose Goals and Mastery. +goals.goal.completed=Goal complete: {goal}. Awarded 1 goal point. goals.reward.one=Reward: 1 goal point. goals.reward.many=Reward: {count} goal points. goals.goal.craft_knife.name=Craft a knife. @@ -247,6 +249,15 @@ goals.goal.build_altar.name=Build an altar. goals.goal.complete_quest.name=Complete a quest. goals.goal.win_adventure.name=Win an adventure. goals.goal.engrave_rune.name=Engrave a rune. +goals.goal.craft_axe.name=Craft a stone axe. +goals.goal.chop_tree_1.name=Chop down a tree. +goals.goal.chop_tree_4.name=Chop down 4 trees. +goals.goal.chop_tree_9.name=Chop down 9 trees. +goals.goal.chop_tree_27.name=Chop down 27 trees. +goals.goal.craft_snare.name=Craft a snare. +goals.goal.catch_snare.name=Catch something in a snare. +goals.goal.butcher_game.name=Butcher game. +goals.goal.craft_sling.name=Craft a sling. goals.goal.unknown.name=Unknown goal. goals.menu.prompt=Goals. {option} goals.menu.current=Current goal: {goal} @@ -264,18 +275,29 @@ goals.completed.entry_one={goal}. Awarded 1 goal point. goals.completed.entry_many={goal}. Awarded {points} goal points. goals.mastery.prompt=Mastery. {option} goals.mastery.crafting.name=Crafting Mastery +goals.mastery.leadership.name=Leadership Mastery +goals.mastery.taming.name=Taming Mastery goals.mastery.unknown.name=Unknown mastery goals.mastery.unknown_level=Unknown unlock -goals.mastery.maxed=Crafting Mastery: maxed out +goals.mastery.maxed={category}: maxed out goals.mastery.summary={category} level {level} of {max}. Next cost: {cost} goal points. goals.mastery.unlocked=Unlocked {unlock}. goals.mastery.confirm=Spend {cost} goal points to unlock {unlock}? -goals.mastery.already_maxed=Crafting Mastery is already maxed out. +goals.mastery.already_maxed={category} is already maxed out. goals.mastery.not_enough_points=Need {cost} goal points. You have {unspent}. goals.mastery.spend_failed=Could not spend the goal point. goals.mastery.crafting.level1=Crafted basic tools are sturdier. goals.mastery.crafting.level2=Unlock great spear recipe. goals.mastery.crafting.level3=Great spear reaches 2 tiles but has a slower attack window. +goals.mastery.crafting.level4=Personal tools, weapons, and clothing no longer wear out. +goals.mastery.leadership.level1=Residents are careful and break fewer tools and weapons. +goals.mastery.leadership.level2=Residents are organized and succeed at work more often. +goals.mastery.leadership.level3=Residents act faster during defense and urgent work. +goals.mastery.leadership.level4=Residents ration food, eating every 12 hours instead of every 8. +goals.mastery.taming.level1=Pets can retrieve dropped items. +goals.mastery.taming.level2=Pets are heartier and gain one maximum health. +goals.mastery.taming.level3=Pets recover from knockout faster. +goals.mastery.taming.level4=Bonded pets no longer require food and stay fully loyal. crafting.weapons.option.great_spear=Great Spear (1 Spear, 1 Log, 2 Vines, 2 Sinew) [Requires Crafting Mastery 2] crafting.weapons.crafted.great_spear=Crafted a Great Spear. crafting.weapons.great_spear.requires_mastery=Great spear requires Crafting Mastery 2. @@ -670,7 +692,7 @@ msg.03268c7d7239=backpacks msg.03976920927e=rope ; src/item_registry.nvgt:item_display:small_game msg.0406529811c7=Small Game -; draugnorak.nvgt:653:ui_info_box[2] +; draugnorak.nvgt:656:ui_info_box[2] msg.041d8413712d=A crash log was written to your save data folder. ; seed:learn_sounds_label:sounds/enemies/vampyr1.ogg msg.04a8916a9494=vampyr1 @@ -678,6 +700,8 @@ msg.04a8916a9494=vampyr1 msg.0532db2192fa=incense ; src/bosses/bandit_hideout.nvgt:787:insert_last[0] msg.066723a92e94==== Victory Rewards === +; seed:learn_sounds_label:sounds/enemies/corpse_worm_dies.ogg +msg.06c9fefbd6d7=corpse worm dies ; src/quests/enchanted_melody_game.nvgt:36:insert_last[0] msg.07e9ab246cdd=After closing this screen, you can practice the notes. ; src/item_registry.nvgt:item_plural:sinew @@ -690,6 +714,8 @@ msg.09c28ae8f83b=Stones msg.09d016686864=Stable not built ; src/item_registry.nvgt:item_display:reed_baskets msg.0a3bdcef1906=Reed Baskets +; seed:learn_sounds_label:sounds/new_goal.ogg +msg.0a6e19e37b4d=new goal ; src/quests/enchanted_melody_game.nvgt:26:insert_last[0] msg.0a9d6438c818==== Enchanted Melody === ; src/bosses/bandit_hideout.nvgt:305:insert_last[0] @@ -736,7 +762,7 @@ msg.18b837bb6f43=bat1 msg.18c77e824a2a=yeti dies ; seed:learn_sounds_label:sounds/enemies/bandit_dies.ogg msg.1a853cbfbc36=bandit dies -; src/sound_settings.nvgt:48:learn_sounds_add_description[1] +; src/sound_settings.nvgt:50:learn_sounds_add_description[1] msg.1bb8ef2886a3=Basic undead. ; seed:learn_sounds_label:sounds/nature/night.ogg msg.1be2a44cb53d=night @@ -878,7 +904,7 @@ msg.444d4678962b=falling msg.4537c16badd9=bow fire ; src/item_registry.nvgt:item_plural:snares msg.455b11a16eb7=snares -; src/sound_settings.nvgt:38:learn_sounds_add_description[1] +; src/sound_settings.nvgt:40:learn_sounds_add_description[1] msg.4671b16b0398=Not quite a zombie nor a vampyr. These undead remember they used to have a home, and maybe there's food there... ; seed:learn_sounds_label:sounds/terrain/draugrhaugr_step.ogg msg.46fcdd91c99e=draugrhaugr step @@ -916,13 +942,13 @@ msg.5009c8e190ee=turkey msg.508e8bc0cc57=- Use Up/Down to change the number ; seed:learn_sounds_label:sounds/weapons/sling_low_range.ogg msg.5123c22ce6a5=sling low range -; draugnorak.nvgt:112:insert_last[0] +; draugnorak.nvgt:113:insert_last[0] msg.528fb1c4e5fb=New Game ; seed:learn_sounds_label:sounds/quests/squish.ogg msg.5460f2e3c43e=squish ; seed:learn_sounds_label:sounds/actions/fishpole_break.ogg msg.546330ee64db=fishpole break -; draugnorak.nvgt:653:ui_info_box[0] +; draugnorak.nvgt:656:ui_info_box[0] msg.54fc2d657ed6=Draugnorak ; seed:learn_sounds_label:sounds/enemies/vampyr3.ogg msg.55748cf43121=vampyr3 @@ -950,7 +976,7 @@ msg.5cac31311668=Pasture is full. No livestock were recovered. msg.5cc7429297c8=that can be destroyed with an axe. ; seed:learn_sounds_label:sounds/actions/cast_strength.ogg msg.5dfc869b7588=cast strength -; src/time_system.nvgt:794:notify[0] +; src/time_system.nvgt:801:notify[0] msg.5ea84b1201bb={arg1} favor grants you the eyes of an eagle. ; seed:learn_sounds_label:sounds/menu/menu_select.ogg ; seed:learn_sounds_label:sounds/menu.bak/menu_select.ogg @@ -1034,8 +1060,8 @@ msg.7e83de91b4a2=bandit female dies msg.7f7e3bbf8864=An enemy is no longer in range of your currently wielded weapon. ; src/item_registry.nvgt:item_display:backpacks msg.7fae743693f4=Backpacks -; draugnorak.nvgt:231:ui_info_box[1] -; draugnorak.nvgt:245:ui_info_box[1] +; draugnorak.nvgt:232:ui_info_box[1] +; draugnorak.nvgt:246:ui_info_box[1] msg.802aa6576577=Load Game ; src/item_registry.nvgt:item_plural:sticks msg.803e4c41bbd2=sticks @@ -1044,12 +1070,12 @@ msg.803e4c41bbd2=sticks msg.80655da8d80a=tree ; src/item_registry.nvgt:item_plural:canoes msg.806baaf8e039=canoes -; src/time_system.nvgt:776:notify[0] -; src/time_system.nvgt:786:notify[0] +; src/time_system.nvgt:783:notify[0] +; src/time_system.nvgt:793:notify[0] msg.81df04a81e24={arg1} favor shines upon you. {arg2} ; src/quests/catch_the_boomerang_game.nvgt:22:insert_last[0] msg.823be948275f=- Better timing earns more points (up to 4) -; src/sound_settings.nvgt:41:learn_sounds_add_description[1] +; src/sound_settings.nvgt:43:learn_sounds_add_description[1] msg.83865f248682=More powerful undead. They do not lose track of you when you are in the base. ; seed:learn_sounds_label:sounds/nature/wind_high.ogg msg.8443de859917=wind high @@ -1071,7 +1097,7 @@ msg.86e257c3efcb=Oldest notification. {arg1} msg.87383ce4344d=Bowstrings ; src/item_registry.nvgt:item_plural:ropes msg.87cdb5c91437=ropes -; src/time_system.nvgt:790:notify[0] +; src/time_system.nvgt:797:notify[0] msg.8818f1d55166={arg1} radiance fills residents with purpose. ; src/quests/bat_invasion_game.nvgt:7:insert_last[0] msg.88749dbbbf09==== Bat Invasion === @@ -1089,7 +1115,7 @@ msg.8f993ac20023=You enter a narrow mountain pass. A massive Unicorn blocks your msg.917ee46db0cd=bee ; seed:learn_sounds_label:sounds/player_male_damage.ogg msg.924e0eb6b6f1=player male damage -; src/time_system.nvgt:781:notify[0] +; src/time_system.nvgt:788:notify[0] msg.9281d2fe5f08={arg1} favor shines upon you. You feel swift for a while. ; seed:learn_sounds_label:sounds/actions/climb_rope.ogg msg.92abc6d6953f=climb rope @@ -1140,9 +1166,9 @@ msg.a3cb738850fa=wolf msg.a3f2439ef01a=skin hat ; seed:learn_sounds_label:sounds/quests/bone1.ogg msg.a56be51786dc=bone1 -; src/sound_settings.nvgt:43:learn_sounds_add_description[1] -; src/sound_settings.nvgt:44:learn_sounds_add_description[1] ; src/sound_settings.nvgt:45:learn_sounds_add_description[1] +; src/sound_settings.nvgt:46:learn_sounds_add_description[1] +; src/sound_settings.nvgt:47:learn_sounds_add_description[1] msg.a570b024087d=Ever hungry, they feed upon your residents. ; src/quests/escape_from_hel_game.nvgt:15:insert_last[0] msg.a641f39a6317=- The run ends when you fall into a grave @@ -1218,7 +1244,7 @@ msg.c01409b3a728=When casting release control when over water. When catching rel msg.c05feaca7269=No items were recovered. ; src/item_registry.nvgt:item_singular:snare msg.c1104d82c0bd=snare -; src/sound_settings.nvgt:37:learn_sounds_add_description[1] +; src/sound_settings.nvgt:39:learn_sounds_add_description[1] msg.c48d642737b2=Inhabits mountainous regions. ; src/item_registry.nvgt:item_display:smoked_fish msg.c4f7e9c21601=Smoked Fish @@ -1228,6 +1254,8 @@ msg.c50641e24962=Your cast missed the water and landed in nearby foliage where i msg.c52888225c69=fall ; seed:learn_sounds_label:sounds/enemies/bandit1.ogg msg.c65e5dfc5ba7=bandit1 +; seed:learn_sounds_label:sounds/enemies/corpse_worm.ogg +msg.c6d56016b910=corpse worm ; src/bosses/bandit_hideout.nvgt:789:insert_last[0] msg.c72b7844929d=Bandits defeated: {arg1}. ; seed:learn_sounds_label:sounds/terrain/stream.ogg @@ -1259,7 +1287,7 @@ msg.ce3897c6b3be={arg1}: {arg2} ; seed:learn_sounds_label:sounds/items/item_breaks.ogg msg.ce5aeca95fb9=item breaks ; src/i18n.nvgt:677:speak_with_history[0] -; src/pet_system.nvgt:926:speak_with_history[0] +; src/pet_system.nvgt:935:speak_with_history[0] ; src/save_system.nvgt:344:speak_with_history[0] msg.cea88b214070={arg1} {arg2} ; seed:manual @@ -1268,11 +1296,13 @@ msg.cfe2650b4c6e=Found a stone. msg.d04a59a6a71b=Controls: LEFT/RIGHT to move, UP to jump, CTRL to attack, ESC to flee ; src/item_registry.nvgt:item_singular:skin msg.d0cb2acd0739=skin +; src/sound_settings.nvgt:37:learn_sounds_add_description[1] +msg.d2e0d305bc2b=A weak crawling enemy that starts appearing outside the base after day 1. ; seed:learn_sounds_label:sounds/enemies/enemy_falls.ogg msg.d34b6217ea75=enemy falls ; src/item_registry.nvgt:item_display:skin_gloves msg.d373510877fd=Skin Gloves -; draugnorak.nvgt:653:ui_info_box[1] +; draugnorak.nvgt:656:ui_info_box[1] ; seed:manual msg.d39008c4a392=Unhandled exception ; src/item_registry.nvgt:item_singular:backpack @@ -1295,7 +1325,7 @@ msg.da0b5a461d98=F or K - First note (lowest pitch) msg.da11dc5e090c=sling hit ; seed:learn_sounds_label:sounds/enemies/vampyr_dies.ogg msg.da93fb9992a3=vampyr dies -; src/sound_settings.nvgt:47:learn_sounds_add_description[1] +; src/sound_settings.nvgt:49:learn_sounds_add_description[1] msg.dadd68431e60=Inhabits snowy regions. ; seed:learn_sounds_label:sounds/enemies/exit_range.ogg msg.db2a8514a0fc=exit range @@ -1365,7 +1395,7 @@ msg.e7409fcb54bc=feed fire msg.e8002c169040=wood ; src/quests/escape_from_hel_game.nvgt:13:insert_last[0] msg.e8216720d30b=- Press SPACE to jump over graves -; draugnorak.nvgt:132:insert_last[0] +; draugnorak.nvgt:133:insert_last[0] msg.e8db3a42f86f=Learn Sounds ; src/bosses/bandit_hideout.nvgt:845:insert_last[0] msg.e8e8f4ee7a5a=You can now engrave weapons with this rune at the crafting menu. @@ -1396,7 +1426,7 @@ msg.f3d964225e83=player female damage msg.f5cabf873590=reed ; src/bosses/bandit_hideout.nvgt:301:insert_last[0] msg.f77fc018cb7b=You find a hidden bandit base deep in the forest. -; draugnorak.nvgt:148:insert_last[0] +; draugnorak.nvgt:149:insert_last[0] msg.f83b6fe3aebf=Exit ; src/inventory_items.nvgt:504:speak_with_history[0] ; src/quests/skeletal_bard_game.nvgt:26:speak_with_history[0] diff --git a/lang/es.ini b/lang/es.ini index 89dba4b..36bff3d 100644 --- a/lang/es.ini +++ b/lang/es.ini @@ -626,6 +626,7 @@ msg.096539f23463=Retumban los tambores de guerra! Preparate para el combate! msg.09c28ae8f83b=Piedras msg.09d016686864=Establo no construido msg.0a3bdcef1906=Cestas de junco +msg.0a6e19e37b4d=nuevo objetivo msg.0a9d6438c818==== Melodia Encantada === msg.0b3d8d5cdca6=- Llega a la base y destruye la barricada msg.0bcd9af79f2d=bosque @@ -984,14 +985,25 @@ system.goals.completed.entry_one={goal}. 1 punto otorgado. system.goals.completed.none=Aún no se han completado objetivos. system.goals.completed.prompt=Objetivos completados. {option} system.goals.current=Objetivo actual: {goal}. Puntos sin gastar: {unspent}. +system.goals.new=Nuevo objetivo: {goal} Pulsa G para revisar tu objetivo actual. +system.goals.spend_hint=Para gastar puntos de objetivo, pulsa P para abrir el menu de personaje, y luego elige Objetivos y Maestria. system.goals.goal.build_altar.name=Construye un altar. system.goals.goal.build_firepit.name=Construye un fogón. system.goals.goal.build_storage.name=Construye almacenamiento. system.goals.goal.complete_quest.name=Completa una misión. -system.goals.goal.completed=Objetivo completado: {goal}. {points} puntos otorgados. +system.goals.goal.completed=Objetivo completado: {goal}. 1 punto otorgado. system.goals.goal.craft_knife.name=Fabrica un cuchillo. system.goals.goal.craft_spear.name=Fabrica una lanza. system.goals.goal.engrave_rune.name=Graba una runa. +system.goals.goal.craft_axe.name=Fabrica un hacha de piedra. +system.goals.goal.chop_tree_1.name=Tala un arbol. +system.goals.goal.chop_tree_4.name=Tala 4 arboles. +system.goals.goal.chop_tree_9.name=Tala 9 arboles. +system.goals.goal.chop_tree_27.name=Tala 27 arboles. +system.goals.goal.craft_snare.name=Fabrica una trampa. +system.goals.goal.catch_snare.name=Atrapa algo en una trampa. +system.goals.goal.butcher_game.name=Descuartiza caza. +system.goals.goal.craft_sling.name=Fabrica una honda. system.goals.goal.light_fire.name=Enciende un fuego. system.goals.goal.unknown.name=Objetivo desconocido. system.goals.goal.win_adventure.name=Gana una aventura. @@ -1014,14 +1026,29 @@ system.goals.mastery.prompt=Maestría. {option} system.goals.mastery.crafting.level1=Las herramientas básicas fabricadas son más resistentes. system.goals.mastery.crafting.level2=Desbloquea la receta de la lanza grande. system.goals.mastery.crafting.level3=La lanza grande alcanza 2 casillas pero tiene una ventana de ataque más lenta. +system.goals.mastery.crafting.level4=Las herramientas, armas y ropa personales ya no se desgastan. system.goals.mastery.crafting.name=Maestría de Fabricación +system.goals.mastery.leadership.level1=Los residentes son cuidadosos y rompen menos herramientas y armas. +system.goals.mastery.leadership.level2=Los residentes son organizados y tienen éxito en el trabajo más a menudo. +system.goals.mastery.leadership.level3=Los residentes actúan más rápido durante la defensa y el trabajo urgente. +system.goals.mastery.leadership.level4=Los residentes racionan comida y comen cada 12 horas en vez de cada 8. +system.goals.mastery.leadership.name=Maestría de Liderazgo +system.goals.mastery.taming.level1=Las mascotas pueden recuperar objetos caídos. +system.goals.mastery.taming.level2=Las mascotas son más resistentes y ganan una salud máxima. +system.goals.mastery.taming.level3=Las mascotas se recuperan más rápido tras quedar inconscientes. +system.goals.mastery.taming.level4=Las mascotas vinculadas ya no requieren comida y se mantienen totalmente leales. +system.goals.mastery.taming.name=Maestría de Domesticación system.goals.mastery.unknown.name=Maestría desconocida system.goals.mastery.unknown_level=Desbloqueo desconocido -system.goals.mastery.maxed=Maestría de Fabricación: al máximo +system.goals.mastery.maxed={category}: al máximo system.goals.mastery.summary={category} nivel {level} de {max}. Coste siguiente: {cost} puntos. system.goals.mastery.unlocked=Desbloqueado {unlock}. system.goals.mastery.confirm=¿Gastar {cost} puntos para desbloquear {unlock}? -system.goals.mastery.already_maxed=La Maestría de Fabricación ya está al máximo. +system.goals.mastery.already_maxed={category} ya está al máximo. system.goals.mastery.not_enough_points=Necesitas {cost} puntos. Tienes {unspent}. system.goals.mastery.spend_failed=No se pudo gastar el punto. system.goals.menu.back=Atrás + +msg.06c9fefbd6d7=gusano de cadáver muere +msg.c6d56016b910=gusano de cadáver +msg.d2e0d305bc2b=Un enemigo rastrero débil que empieza a aparecer fuera de la base después del día 1. diff --git a/scripts/generate_i18n_catalog.py b/scripts/generate_i18n_catalog.py index 6036071..4b76303 100644 --- a/scripts/generate_i18n_catalog.py +++ b/scripts/generate_i18n_catalog.py @@ -736,9 +736,11 @@ def write_catalog(entries: Dict[str, Dict[str, object]], output_path: Path) -> N ("system.character.info.goals_menu", "Goals"), ("system.character.word.hour", "hour"), ("system.character.word.hours", "hours"), + ("system.goals.new", "New goal: {goal} Press G to review your current goal."), ("system.goals.current", "Current goal: {goal}. Unspent goal points: {unspent}."), ("system.goals.all_complete", "All current goals complete. Unspent goal points: {unspent}."), - ("system.goals.goal.completed", "Goal complete: {goal}. Awarded {points} goal points."), + ("system.goals.spend_hint", "To spend goal points, press P for the character menu, then choose Goals and Mastery."), + ("system.goals.goal.completed", "Goal complete: {goal}. Awarded 1 goal point."), ("system.goals.reward.one", "Reward: 1 goal point."), ("system.goals.reward.many", "Reward: {count} goal points."), ("system.goals.goal.craft_knife.name", "Craft a knife."), @@ -750,6 +752,15 @@ def write_catalog(entries: Dict[str, Dict[str, object]], output_path: Path) -> N ("system.goals.goal.complete_quest.name", "Complete a quest."), ("system.goals.goal.win_adventure.name", "Win an adventure."), ("system.goals.goal.engrave_rune.name", "Engrave a rune."), + ("system.goals.goal.craft_axe.name", "Craft a stone axe."), + ("system.goals.goal.chop_tree_1.name", "Chop down a tree."), + ("system.goals.goal.chop_tree_4.name", "Chop down 4 trees."), + ("system.goals.goal.chop_tree_9.name", "Chop down 9 trees."), + ("system.goals.goal.chop_tree_27.name", "Chop down 27 trees."), + ("system.goals.goal.craft_snare.name", "Craft a snare."), + ("system.goals.goal.catch_snare.name", "Catch something in a snare."), + ("system.goals.goal.butcher_game.name", "Butcher game."), + ("system.goals.goal.craft_sling.name", "Craft a sling."), ("system.goals.goal.unknown.name", "Unknown goal."), ("system.goals.menu.prompt", "Goals. {option}"), ("system.goals.menu.current", "Current goal: {goal}"), @@ -767,18 +778,29 @@ def write_catalog(entries: Dict[str, Dict[str, object]], output_path: Path) -> N ("system.goals.completed.entry_many", "{goal}. Awarded {points} goal points."), ("system.goals.mastery.prompt", "Mastery. {option}"), ("system.goals.mastery.crafting.name", "Crafting Mastery"), + ("system.goals.mastery.leadership.name", "Leadership Mastery"), + ("system.goals.mastery.taming.name", "Taming Mastery"), ("system.goals.mastery.unknown.name", "Unknown mastery"), ("system.goals.mastery.unknown_level", "Unknown unlock"), - ("system.goals.mastery.maxed", "Crafting Mastery: maxed out"), + ("system.goals.mastery.maxed", "{category}: maxed out"), ("system.goals.mastery.summary", "{category} level {level} of {max}. Next cost: {cost} goal points."), ("system.goals.mastery.unlocked", "Unlocked {unlock}."), ("system.goals.mastery.confirm", "Spend {cost} goal points to unlock {unlock}?"), - ("system.goals.mastery.already_maxed", "Crafting Mastery is already maxed out."), + ("system.goals.mastery.already_maxed", "{category} is already maxed out."), ("system.goals.mastery.not_enough_points", "Need {cost} goal points. You have {unspent}."), ("system.goals.mastery.spend_failed", "Could not spend the goal point."), ("system.goals.mastery.crafting.level1", "Crafted basic tools are sturdier."), ("system.goals.mastery.crafting.level2", "Unlock great spear recipe."), ("system.goals.mastery.crafting.level3", "Great spear reaches 2 tiles but has a slower attack window."), + ("system.goals.mastery.crafting.level4", "Personal tools, weapons, and clothing no longer wear out."), + ("system.goals.mastery.leadership.level1", "Residents are careful and break fewer tools and weapons."), + ("system.goals.mastery.leadership.level2", "Residents are organized and succeed at work more often."), + ("system.goals.mastery.leadership.level3", "Residents act faster during defense and urgent work."), + ("system.goals.mastery.leadership.level4", "Residents ration food, eating every 12 hours instead of every 8."), + ("system.goals.mastery.taming.level1", "Pets can retrieve dropped items."), + ("system.goals.mastery.taming.level2", "Pets are heartier and gain one maximum health."), + ("system.goals.mastery.taming.level3", "Pets recover from knockout faster."), + ("system.goals.mastery.taming.level4", "Bonded pets no longer require food and stay fully loyal."), ("system.crafting.weapons.option.great_spear", "Great Spear (1 Spear, 1 Log, 2 Vines, 2 Sinew) [Requires Crafting Mastery 2]"), ("system.crafting.weapons.crafted.great_spear", "Crafted a Great Spear."), ("system.crafting.weapons.great_spear.requires_mastery", "Great spear requires Crafting Mastery 2."), diff --git a/sounds/enemies/corpse_worm.ogg b/sounds/enemies/corpse_worm.ogg new file mode 100644 index 0000000..67d9dd7 --- /dev/null +++ b/sounds/enemies/corpse_worm.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eeb21a4a1fdd627798f13d488779dbd459995a277f83a4c22c401a528f6a8edd +size 57956 diff --git a/sounds/enemies/corpse_worm_dies.ogg b/sounds/enemies/corpse_worm_dies.ogg new file mode 100644 index 0000000..7e5e4ed --- /dev/null +++ b/sounds/enemies/corpse_worm_dies.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d98b639771801a7d4f48b2d82e3fc6571c825628952227f70fb26bac7015e2be +size 6954 diff --git a/sounds/new_goal.ogg b/sounds/new_goal.ogg new file mode 100644 index 0000000..e393277 --- /dev/null +++ b/sounds/new_goal.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6e9cb4ee0c7b87f0e35ffc09505aebb82faac9d0f05126a819ebd249356d6ef0 +size 29193 diff --git a/src/base_system.nvgt b/src/base_system.nvgt index cb81c29..fd98ca8 100644 --- a/src/base_system.nvgt +++ b/src/base_system.nvgt @@ -2,7 +2,7 @@ int get_food_requirement() { if (residents_count <= 0) return 0; - return residents_count; // 1 food per resident per 8 hours + return residents_count; } int get_total_storage_food() { @@ -68,6 +68,7 @@ int get_horse_resident_cooldown_reduction() { int get_resident_success_chance(int base_chance) { int chance = base_chance * get_resident_effect_multiplier(); + chance += get_leadership_success_bonus(); chance += get_horse_success_bonus(); if (chance > 100) chance = 100; @@ -75,9 +76,12 @@ int get_resident_success_chance(int base_chance) { } int get_resident_break_chance(int base_chance) { + int chance = (base_chance * get_leadership_break_percent()) / 100; + if (chance < 1 && base_chance > 0) + chance = 1; if (!blessing_resident_active) - return base_chance; - int reduced = base_chance / get_resident_effect_multiplier(); + return chance; + int reduced = chance / get_resident_effect_multiplier(); if (reduced < 1 && base_chance > 0) reduced = 1; return reduced; @@ -93,7 +97,8 @@ int get_resident_escape_chance(int base_chance) { } int get_resident_cooldown(int base_cooldown) { - int cooldown = base_cooldown / get_resident_effect_multiplier(); + int cooldown = (base_cooldown * get_leadership_cooldown_percent()) / 100; + cooldown = cooldown / get_resident_effect_multiplier(); if (cooldown < 1) cooldown = 1; return cooldown; diff --git a/src/combat.nvgt b/src/combat.nvgt index 392cdd2..503c785 100644 --- a/src/combat.nvgt +++ b/src/combat.nvgt @@ -21,6 +21,7 @@ const int BOW_HIT_BANDIT = 1; const int BOW_HIT_BOAR = 2; const int BOW_HIT_ZOMBIE = 3; const int BOW_HIT_FLYING = 4; +const int BOW_HIT_CORPSE_WORM = 5; void speak_ammo_blocked(string message) { if (message == lastAmmoBlockMessage && ammoBlockTimer.elapsed < AMMO_BLOCK_COOLDOWN_MS) { @@ -33,10 +34,11 @@ void speak_ammo_blocked(string message) { } int find_ranged_enemy(int player_x, int range, int direction, bool allow_flying, bool& out hit_bandit, - bool& out hit_boar, bool& out hit_flying_creature) { + bool& out hit_boar, bool& out hit_flying_creature, bool& out hit_corpse_worm) { hit_bandit = false; hit_boar = false; hit_flying_creature = false; + hit_corpse_worm = false; for (int dist = 1; dist <= range; dist++) { int check_x = player_x + (dist * direction); @@ -55,6 +57,12 @@ int find_ranged_enemy(int player_x, int range, int direction, bool allow_flying, return check_x; } + CorpseWorm @corpse_worm = get_corpse_worm_at(check_x); + if (corpse_worm != null) { + hit_corpse_worm = true; + return check_x; + } + Undead @undead = get_zombie_at(check_x); if (undead != null) { return check_x; @@ -86,6 +94,10 @@ int attack_enemy_ranged(int start_x, int end_x, int damage) { if (damage_boar_at(check_x, damage)) { return check_x; } + // Then check corpse worms + if (damage_corpse_worm_at(check_x, damage)) { + return check_x; + } } return -1; } @@ -99,6 +111,10 @@ bool attack_enemy(int target_x, int damage) { if (damage_boar_at(target_x, damage)) { return true; } + // Check corpse worms + if (damage_corpse_worm_at(target_x, damage)) { + return true; + } // Then check zombies return damage_zombie_at(target_x, damage); } @@ -115,6 +131,8 @@ void perform_spear_attack(int current_x) { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, BANDIT_SOUND_VOLUME_STEP); } else if (get_boar_at(hit_pos) != null) { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, BOAR_SOUND_VOLUME_STEP); + } else if (get_corpse_worm_at(hit_pos) != null) { + play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, CORPSE_WORM_SOUND_VOLUME_STEP); } else { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, ZOMBIE_SOUND_VOLUME_STEP); } @@ -136,6 +154,8 @@ void perform_axe_attack(int current_x) { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, BANDIT_SOUND_VOLUME_STEP); } else if (get_boar_at(current_x) != null) { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, BOAR_SOUND_VOLUME_STEP); + } else if (get_corpse_worm_at(current_x) != null) { + play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, CORPSE_WORM_SOUND_VOLUME_STEP); } else { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, current_x, ZOMBIE_SOUND_VOLUME_STEP); } @@ -259,6 +279,8 @@ void update_bow_shot() { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_x, BOAR_SOUND_VOLUME_STEP); } else if (hit_type == BOW_HIT_ZOMBIE) { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_x, ZOMBIE_SOUND_VOLUME_STEP); + } else if (hit_type == BOW_HIT_CORPSE_WORM) { + play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_x, CORPSE_WORM_SOUND_VOLUME_STEP); } } if (drop_pending && drop_pos >= 0) { @@ -284,8 +306,10 @@ void release_bow_attack(int player_x) { bool hit_bandit = false; bool hit_flying_creature = false; bool hit_boar = false; + bool hit_corpse_worm = false; int target_x = - find_ranged_enemy(player_x, BOW_RANGE, search_direction, true, hit_bandit, hit_boar, hit_flying_creature); + find_ranged_enemy(player_x, BOW_RANGE, search_direction, true, hit_bandit, hit_boar, hit_flying_creature, + hit_corpse_worm); bool hit_tree = false; if (target_x == -1) { @@ -313,6 +337,9 @@ void release_bow_attack(int player_x) { } else if (hit_flying_creature) { damage_flying_creature_at(target_x, damage); hit_type = BOW_HIT_FLYING; + } else if (hit_corpse_worm) { + damage_corpse_worm_at(target_x, damage); + hit_type = BOW_HIT_CORPSE_WORM; } else { damage_zombie_at(target_x, damage); hit_type = BOW_HIT_ZOMBIE; @@ -361,8 +388,10 @@ void release_sling_attack(int player_x) { bool hit_bandit = false; bool hit_flying_creature = false; bool hit_boar = false; + bool hit_corpse_worm = false; target_x = - find_ranged_enemy(player_x, SLING_RANGE, search_direction, true, hit_bandit, hit_boar, hit_flying_creature); + find_ranged_enemy(player_x, SLING_RANGE, search_direction, true, hit_bandit, hit_boar, hit_flying_creature, + hit_corpse_worm); // If no enemy found, check for trees (but don't damage them) if (target_x == -1) { @@ -404,6 +433,12 @@ void release_sling_attack(int player_x) { play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, PLAYER_WEAPON_SOUND_VOLUME_STEP); // Falling sound handled by damage_flying_creature_at + } else if (hit_corpse_worm) { + damage_corpse_worm_at(target_x, damage); + play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, + PLAYER_WEAPON_SOUND_VOLUME_STEP); + play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", player_x, target_x, + CORPSE_WORM_SOUND_VOLUME_STEP); } else { damage_zombie_at(target_x, damage); play_1d_with_volume_step("sounds/weapons/sling_hit.ogg", player_x, target_x, false, @@ -424,6 +459,8 @@ void perform_great_spear_attack(int current_x) { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, BANDIT_SOUND_VOLUME_STEP); } else if (get_boar_at(hit_pos) != null) { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, BOAR_SOUND_VOLUME_STEP); + } else if (get_corpse_worm_at(hit_pos) != null) { + play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, CORPSE_WORM_SOUND_VOLUME_STEP); } else { play_creature_hit_sound("sounds/enemies/zombie_hit.ogg", x, hit_pos, ZOMBIE_SOUND_VOLUME_STEP); } diff --git a/src/constants.nvgt b/src/constants.nvgt index c83138a..903186c 100644 --- a/src/constants.nvgt +++ b/src/constants.nvgt @@ -118,6 +118,20 @@ const int BOAR_SIGHT_RANGE = 4; const int BOAR_CHARGE_SPEED = 500; // ms per tile when charging const int BOAR_SPAWN_CHANCE_PER_HOUR = 30; +// Corpse worm settings +const int CORPSE_WORM_START_DAY = 2; +const int CORPSE_WORM_HEALTH = 2; +const int CORPSE_WORM_MAX_COUNT = 2; +const int CORPSE_WORM_MOVE_INTERVAL = 1200; +const int CORPSE_WORM_ATTACK_INTERVAL = 3500; +const int CORPSE_WORM_DAMAGE = 1; +const int CORPSE_WORM_SPAWN_ROLL_INTERVAL = 30000; +const int CORPSE_WORM_SPAWN_CHANCE = 8; +const int CORPSE_WORM_SPAWN_MIN_DISTANCE = 5; +const int CORPSE_WORM_SPAWN_MAX_DISTANCE = 14; +const int CORPSE_WORM_FOOTSTEP_MAX_DISTANCE = 4; +const float CORPSE_WORM_SOUND_VOLUME_STEP = 3.0; + // Barricade configuration const int BARRICADE_BASE_HEALTH = 40; const int BARRICADE_MAX_HEALTH = 500; diff --git a/src/crafting/craft_materials.nvgt b/src/crafting/craft_materials.nvgt index a5f4d75..3d08b2a 100644 --- a/src/crafting/craft_materials.nvgt +++ b/src/crafting/craft_materials.nvgt @@ -442,6 +442,8 @@ void butcher_small_game() { speak_with_history(trf("system.crafting.materials.butcher.default", butcherDefaultArgs), true); } + complete_goal(GOAL_BUTCHER_GAME); + // Play sound p.play_stationary("sounds/items/miscellaneous.ogg", false); } else { @@ -561,6 +563,7 @@ void butcher_small_game_max() { butcherMaxArgs.set("feathers", total_feathers); butcherMaxArgs.set("down", total_down); butcherMaxArgs.set("sinew", total_sinew); + complete_goal(GOAL_BUTCHER_GAME); speak_with_history(trf("system.crafting.materials.butcher.max_result", butcherMaxArgs), true); p.play_stationary("sounds/items/miscellaneous.ogg", false); } diff --git a/src/crafting/craft_tools.nvgt b/src/crafting/craft_tools.nvgt index 54d8882..0886703 100644 --- a/src/crafting/craft_tools.nvgt +++ b/src/crafting/craft_tools.nvgt @@ -127,6 +127,7 @@ void craft_knife_max() { simulate_crafting(craft_time); add_personal_count(ITEM_STONES, -(max_possible * 2)); add_personal_count(ITEM_KNIVES, max_possible); + complete_goal(GOAL_CRAFT_KNIFE); dictionary knifeArgs; knifeArgs.set("count", max_possible); speak_with_history(trf("system.crafting.tools.crafted_max.stone_knives", knifeArgs), true); @@ -148,6 +149,7 @@ void craft_snare() { add_personal_count(ITEM_STICKS, -1); add_personal_count(ITEM_VINES, -2); add_personal_count(ITEM_SNARES, 1); + complete_goal(GOAL_CRAFT_SNARE); speak_with_history(tr("system.crafting.tools.crafted.snare"), true); } else { speak_crafting_missing(missing); @@ -186,6 +188,7 @@ void craft_snare_max() { add_personal_count(ITEM_STICKS, -max_craft); add_personal_count(ITEM_VINES, -(max_craft * 2)); add_personal_count(ITEM_SNARES, max_craft); + complete_goal(GOAL_CRAFT_SNARE); dictionary snareArgs; snareArgs.set("count", max_craft); speak_with_history(trf("system.crafting.tools.crafted_max.snares", snareArgs), true); diff --git a/src/crafting/craft_weapons.nvgt b/src/crafting/craft_weapons.nvgt index d15708f..b900db2 100644 --- a/src/crafting/craft_weapons.nvgt +++ b/src/crafting/craft_weapons.nvgt @@ -132,6 +132,7 @@ void craft_spear_max() { add_personal_count(ITEM_VINES, -max_craft); add_personal_count(ITEM_STONES, -max_craft); add_personal_count(ITEM_SPEARS, max_craft); + complete_goal(GOAL_CRAFT_SPEAR); dictionary spearArgs; spearArgs.set("count", max_craft); speak_with_history(trf("system.crafting.weapons.crafted_max.spears", spearArgs), true); @@ -153,6 +154,7 @@ void craft_sling() { add_personal_count(ITEM_SKINS, -1); add_personal_count(ITEM_VINES, -2); add_personal_count(ITEM_SLINGS, 1); + complete_goal(GOAL_CRAFT_SLING); speak_with_history(tr("system.crafting.weapons.crafted.sling"), true); } else { speak_crafting_missing(missing); @@ -191,6 +193,7 @@ void craft_sling_max() { add_personal_count(ITEM_SKINS, -max_craft); add_personal_count(ITEM_VINES, -(max_craft * 2)); add_personal_count(ITEM_SLINGS, max_craft); + complete_goal(GOAL_CRAFT_SLING); dictionary slingArgs; slingArgs.set("count", max_craft); speak_with_history(trf("system.crafting.weapons.crafted_max.slings", slingArgs), true); @@ -276,6 +279,7 @@ void craft_axe() { add_personal_count(ITEM_VINES, -1); add_personal_count(ITEM_STONES, -2); add_personal_count(ITEM_AXES, 1); + complete_goal(GOAL_CRAFT_AXE); speak_with_history(tr("system.crafting.weapons.crafted.stone_axe"), true); } else { speak_crafting_missing(missing); @@ -323,6 +327,7 @@ void craft_axe_max() { add_personal_count(ITEM_VINES, -max_craft); add_personal_count(ITEM_STONES, -(max_craft * 2)); add_personal_count(ITEM_AXES, max_craft); + complete_goal(GOAL_CRAFT_AXE); dictionary axeArgs; axeArgs.set("count", max_craft); speak_with_history(trf("system.crafting.weapons.crafted_max.stone_axes", axeArgs), true); diff --git a/src/environment.nvgt b/src/environment.nvgt index 0d7ab86..08502a3 100644 --- a/src/environment.nvgt +++ b/src/environment.nvgt @@ -577,6 +577,7 @@ void damage_tree(int target_x, int damage) { if (target.health <= 0) { target.is_chopped = true; + record_tree_chopped_goal_progress(); target.depleted = true; target.regen_timer.restart(); target.minutes_since_depletion = 0; diff --git a/src/goals.nvgt b/src/goals.nvgt index fb52820..73a16f2 100644 --- a/src/goals.nvgt +++ b/src/goals.nvgt @@ -11,30 +11,44 @@ const int GOAL_BUILD_ALTAR = 5; const int GOAL_COMPLETE_QUEST = 6; const int GOAL_WIN_ADVENTURE = 7; const int GOAL_ENGRAVE_RUNE = 8; -const int GOAL_COUNT = 9; +const int GOAL_CRAFT_AXE = 9; +const int GOAL_CHOP_TREE_1 = 10; +const int GOAL_CHOP_TREE_4 = 11; +const int GOAL_CHOP_TREE_9 = 12; +const int GOAL_CHOP_TREE_27 = 13; +const int GOAL_CATCH_SNARE = 14; +const int GOAL_BUTCHER_GAME = 15; +const int GOAL_CRAFT_SLING = 16; +const int GOAL_CRAFT_SNARE = 17; +const int GOAL_COUNT = 18; // Mastery spend categories. const int MASTERY_CRAFTING = 0; -const int MASTERY_CATEGORY_COUNT = 1; +const int MASTERY_LEADERSHIP = 1; +const int MASTERY_TAMING = 2; +const int MASTERY_CATEGORY_COUNT = 3; -// Maximum spendable Crafting Mastery level in the first pass. -const int CRAFTING_MASTERY_MAX_LEVEL = 3; +// Maximum spendable mastery level. Four levels keep the system Norse-themed while preserving a 9-point cap. +const int MASTERY_MAX_LEVEL = 4; +const string GOAL_ANNOUNCEMENT_SOUND_PATH = "sounds/new_goal"; bool[] goalsCompleted; int goalPointsUnspent = 0; int goalPointsEarned = 0; int craftingMasteryLevel = 0; +int leadershipMasteryLevel = 0; +int tamingMasteryLevel = 0; +int treesChopped = 0; +bool goalAnnouncementPending = false; +bool goalAnnouncementWaitForNextHour = false; +bool goalAnnouncementWaitingForSound = false; +int goalAnnouncementGoalId = -1; +int lastAnnouncedGoalId = -1; +string lastAnnouncedGoalMessage = ""; +sound goalAnnouncementSound; int goal_reward_points(int goalId) { - if (goalId == GOAL_CRAFT_KNIFE) return 1; - if (goalId == GOAL_CRAFT_SPEAR) return 1; - if (goalId == GOAL_BUILD_FIREPIT) return 1; - if (goalId == GOAL_LIGHT_FIRE) return 1; - if (goalId == GOAL_BUILD_STORAGE) return 2; - if (goalId == GOAL_BUILD_ALTAR) return 2; - if (goalId == GOAL_COMPLETE_QUEST) return 2; - if (goalId == GOAL_WIN_ADVENTURE) return 3; - if (goalId == GOAL_ENGRAVE_RUNE) return 3; + if (is_valid_goal_id(goalId)) return 1; return 0; } @@ -48,6 +62,15 @@ string goal_name_key(int goalId) { if (goalId == GOAL_COMPLETE_QUEST) return "system.goals.goal.complete_quest.name"; if (goalId == GOAL_WIN_ADVENTURE) return "system.goals.goal.win_adventure.name"; if (goalId == GOAL_ENGRAVE_RUNE) return "system.goals.goal.engrave_rune.name"; + if (goalId == GOAL_CRAFT_AXE) return "system.goals.goal.craft_axe.name"; + if (goalId == GOAL_CHOP_TREE_1) return "system.goals.goal.chop_tree_1.name"; + if (goalId == GOAL_CHOP_TREE_4) return "system.goals.goal.chop_tree_4.name"; + if (goalId == GOAL_CHOP_TREE_9) return "system.goals.goal.chop_tree_9.name"; + if (goalId == GOAL_CHOP_TREE_27) return "system.goals.goal.chop_tree_27.name"; + if (goalId == GOAL_CRAFT_SNARE) return "system.goals.goal.craft_snare.name"; + if (goalId == GOAL_CATCH_SNARE) return "system.goals.goal.catch_snare.name"; + if (goalId == GOAL_BUTCHER_GAME) return "system.goals.goal.butcher_game.name"; + if (goalId == GOAL_CRAFT_SLING) return "system.goals.goal.craft_sling.name"; return "system.goals.goal.unknown.name"; } @@ -67,6 +90,15 @@ string goal_summary(int goalId) { else if (goalId == GOAL_COMPLETE_QUEST) name = "Complete a quest."; else if (goalId == GOAL_WIN_ADVENTURE) name = "Win an adventure."; else if (goalId == GOAL_ENGRAVE_RUNE) name = "Engrave a rune."; + else if (goalId == GOAL_CRAFT_AXE) name = "Craft a stone axe."; + else if (goalId == GOAL_CHOP_TREE_1) name = "Chop down a tree."; + else if (goalId == GOAL_CHOP_TREE_4) name = "Chop down 4 trees."; + else if (goalId == GOAL_CHOP_TREE_9) name = "Chop down 9 trees."; + else if (goalId == GOAL_CHOP_TREE_27) name = "Chop down 27 trees."; + else if (goalId == GOAL_CRAFT_SNARE) name = "Craft a snare."; + else if (goalId == GOAL_CATCH_SNARE) name = "Catch something in a snare."; + else if (goalId == GOAL_BUTCHER_GAME) name = "Butcher game."; + else if (goalId == GOAL_CRAFT_SLING) name = "Craft a sling."; } int points = goal_reward_points(goalId); if (points == 1) { @@ -78,6 +110,7 @@ string goal_summary(int goalId) { } void init_goal_state() { + goalAnnouncementSound.close(); goalsCompleted.resize(GOAL_COUNT); for (int i = 0; i < GOAL_COUNT; i++) { goalsCompleted[i] = false; @@ -85,6 +118,15 @@ void init_goal_state() { goalPointsUnspent = 0; goalPointsEarned = 0; craftingMasteryLevel = 0; + leadershipMasteryLevel = 0; + tamingMasteryLevel = 0; + treesChopped = 0; + goalAnnouncementPending = false; + goalAnnouncementWaitForNextHour = false; + goalAnnouncementWaitingForSound = false; + goalAnnouncementGoalId = -1; + lastAnnouncedGoalId = -1; + lastAnnouncedGoalMessage = ""; } void reset_goal_state() { @@ -102,6 +144,16 @@ bool is_goal_completed(int goalId) { } int get_current_goal_id() { + int[] goalOrder = {GOAL_CRAFT_KNIFE, GOAL_CRAFT_AXE, GOAL_CHOP_TREE_1, GOAL_CRAFT_SPEAR, + GOAL_CRAFT_SNARE, GOAL_CATCH_SNARE, GOAL_BUILD_FIREPIT, GOAL_LIGHT_FIRE, + GOAL_BUTCHER_GAME, GOAL_CRAFT_SLING, GOAL_BUILD_STORAGE, GOAL_BUILD_ALTAR, + GOAL_COMPLETE_QUEST, GOAL_WIN_ADVENTURE, GOAL_ENGRAVE_RUNE, GOAL_CHOP_TREE_4, + GOAL_CHOP_TREE_9, GOAL_CHOP_TREE_27}; + for (uint i = 0; i < goalOrder.length(); i++) { + if (!goalsCompleted[goalOrder[i]]) { + return goalOrder[i]; + } + } for (int i = 0; i < GOAL_COUNT; i++) { if (!goalsCompleted[i]) { return i; @@ -110,6 +162,113 @@ int get_current_goal_id() { return -1; } +string new_goal_announcement_message(int goalId) { + dictionary args; + args.set("goal", goal_summary(goalId)); + return trf("system.goals.new", args); +} + +bool speak_new_goal_announcement(int goalId) { + string message = new_goal_announcement_message(goalId); + if (message == lastAnnouncedGoalMessage) { + lastAnnouncedGoalId = goalId; + return false; + } + speak_with_history(message, true); + lastAnnouncedGoalId = goalId; + lastAnnouncedGoalMessage = message; + return true; +} + +bool goal_announcement_try_play_sound() { + goalAnnouncementSound.close(); + + if (goalAnnouncementSound.load(GOAL_ANNOUNCEMENT_SOUND_PATH)) { + goalAnnouncementSound.play(); + return true; + } + + string oggPath = GOAL_ANNOUNCEMENT_SOUND_PATH + ".ogg"; + if (goalAnnouncementSound.load(oggPath)) { + goalAnnouncementSound.play(); + return true; + } + + string wavPath = GOAL_ANNOUNCEMENT_SOUND_PATH + ".wav"; + if (!goalAnnouncementSound.load(wavPath)) { + return false; + } + + goalAnnouncementSound.play(); + return true; +} + +void queue_current_goal_announcement(bool waitForNextHour) { + int currentId = get_current_goal_id(); + if (currentId == -1) + return; + if (currentId == lastAnnouncedGoalId) + return; + goalAnnouncementPending = true; + goalAnnouncementWaitForNextHour = waitForNextHour; + goalAnnouncementWaitingForSound = false; + goalAnnouncementGoalId = currentId; +} + +void allow_delayed_goal_announcement() { + goalAnnouncementWaitForNextHour = false; + if (!goalAnnouncementPending) { + queue_current_goal_announcement(false); + } +} + +void update_goal_announcements() { + if (!goalAnnouncementPending) + return; + if (goalAnnouncementWaitForNextHour) + return; + + int currentId = get_current_goal_id(); + if (currentId == -1) { + goalAnnouncementPending = false; + goalAnnouncementWaitingForSound = false; + goalAnnouncementGoalId = -1; + goalAnnouncementSound.close(); + return; + } + if (currentId == lastAnnouncedGoalId) { + goalAnnouncementPending = false; + goalAnnouncementWaitingForSound = false; + goalAnnouncementGoalId = -1; + goalAnnouncementSound.close(); + return; + } + if (goalAnnouncementGoalId != currentId) { + goalAnnouncementGoalId = currentId; + goalAnnouncementWaitingForSound = false; + goalAnnouncementSound.close(); + } + + if (goalAnnouncementWaitingForSound) { + if (goalAnnouncementSound.playing) + return; + speak_new_goal_announcement(goalAnnouncementGoalId); + goalAnnouncementPending = false; + goalAnnouncementWaitingForSound = false; + goalAnnouncementGoalId = -1; + return; + } + + if (goal_announcement_try_play_sound()) { + goalAnnouncementWaitingForSound = true; + return; + } + + speak_new_goal_announcement(goalAnnouncementGoalId); + goalAnnouncementPending = false; + goalAnnouncementGoalId = -1; +} + bool complete_goal(int goalId) { if (!is_valid_goal_id(goalId)) { return false; @@ -126,9 +285,18 @@ bool complete_goal(int goalId) { args.set("goal", goal_name(goalId)); args.set("points", points); notify(trf("system.goals.goal.completed", args)); + queue_current_goal_announcement(true); return true; } +void record_tree_chopped_goal_progress() { + treesChopped++; + if (treesChopped >= 1) complete_goal(GOAL_CHOP_TREE_1); + if (treesChopped >= 4) complete_goal(GOAL_CHOP_TREE_4); + if (treesChopped >= 9) complete_goal(GOAL_CHOP_TREE_9); + if (treesChopped >= 27) complete_goal(GOAL_CHOP_TREE_27); +} + string speak_current_goal() { int currentId = get_current_goal_id(); dictionary unspentArgs; @@ -142,24 +310,38 @@ string speak_current_goal() { return trf("system.goals.current", args); } +string speak_current_goal_with_spend_hint() { + string message = speak_current_goal(); + if (goalPointsUnspent > 0) { + message += " " + tr("system.goals.spend_hint"); + } + return message; +} + void check_goal_key() { if (key_pressed(KEY_G)) { - speak_with_history(speak_current_goal(), true); + speak_with_history(speak_current_goal_with_spend_hint(), true); } } string mastery_category_name(int categoryId) { if (categoryId == MASTERY_CRAFTING) return tr("system.goals.mastery.crafting.name"); + if (categoryId == MASTERY_LEADERSHIP) return tr("system.goals.mastery.leadership.name"); + if (categoryId == MASTERY_TAMING) return tr("system.goals.mastery.taming.name"); return tr("system.goals.mastery.unknown.name"); } int mastery_current_level(int categoryId) { if (categoryId == MASTERY_CRAFTING) return craftingMasteryLevel; + if (categoryId == MASTERY_LEADERSHIP) return leadershipMasteryLevel; + if (categoryId == MASTERY_TAMING) return tamingMasteryLevel; return 0; } int mastery_max_level(int categoryId) { - if (categoryId == MASTERY_CRAFTING) return CRAFTING_MASTERY_MAX_LEVEL; + if (categoryId == MASTERY_CRAFTING) return MASTERY_MAX_LEVEL; + if (categoryId == MASTERY_LEADERSHIP) return MASTERY_MAX_LEVEL; + if (categoryId == MASTERY_TAMING) return MASTERY_MAX_LEVEL; return 0; } @@ -168,10 +350,11 @@ int mastery_cost_for_next_level(int categoryId) { int maxLevel = mastery_max_level(categoryId); if (level >= maxLevel) return -1; - if (categoryId == MASTERY_CRAFTING) { - if (level == 0) return 2; - if (level == 1) return 3; - if (level == 2) return 4; + if (categoryId == MASTERY_CRAFTING || categoryId == MASTERY_LEADERSHIP || categoryId == MASTERY_TAMING) { + if (level == 0) return 1; + if (level == 1) return 2; + if (level == 2) return 3; + if (level == 3) return 3; } return -1; } @@ -181,6 +364,19 @@ string mastery_unlock_key(int categoryId, int level) { if (level == 1) return "system.goals.mastery.crafting.level1"; if (level == 2) return "system.goals.mastery.crafting.level2"; if (level == 3) return "system.goals.mastery.crafting.level3"; + if (level == 4) return "system.goals.mastery.crafting.level4"; + } + if (categoryId == MASTERY_LEADERSHIP) { + if (level == 1) return "system.goals.mastery.leadership.level1"; + if (level == 2) return "system.goals.mastery.leadership.level2"; + if (level == 3) return "system.goals.mastery.leadership.level3"; + if (level == 4) return "system.goals.mastery.leadership.level4"; + } + if (categoryId == MASTERY_TAMING) { + if (level == 1) return "system.goals.mastery.taming.level1"; + if (level == 2) return "system.goals.mastery.taming.level2"; + if (level == 3) return "system.goals.mastery.taming.level3"; + if (level == 4) return "system.goals.mastery.taming.level4"; } return "system.goals.mastery.unknown_level"; } @@ -194,7 +390,9 @@ string mastery_level_summary(int categoryId) { int maxLevel = mastery_max_level(categoryId); int cost = mastery_cost_for_next_level(categoryId); if (level >= maxLevel) { - return tr("system.goals.mastery.maxed"); + dictionary maxedArgs; + maxedArgs.set("category", mastery_category_name(categoryId)); + return trf("system.goals.mastery.maxed", maxedArgs); } dictionary args; args.set("category", mastery_category_name(categoryId)); @@ -219,17 +417,35 @@ bool spend_mastery_point(int categoryId) { return false; if (goalPointsUnspent < cost) return false; + if (mastery_current_level(categoryId) >= mastery_max_level(categoryId)) + return false; + + int newLevel = mastery_current_level(categoryId) + 1; + if (categoryId == MASTERY_CRAFTING) { - if (craftingMasteryLevel >= CRAFTING_MASTERY_MAX_LEVEL) - return false; - goalPointsUnspent -= cost; - craftingMasteryLevel++; - dictionary args; - args.set("unlock", mastery_unlock_label(categoryId, craftingMasteryLevel)); - notify(trf("system.goals.mastery.unlocked", args)); - return true; + craftingMasteryLevel = newLevel; + } else if (categoryId == MASTERY_LEADERSHIP) { + leadershipMasteryLevel = newLevel; + } else if (categoryId == MASTERY_TAMING) { + tamingMasteryLevel = newLevel; + if (petActive && !is_pet_knocked_out()) { + int maxHealth = get_pet_max_health(); + if (petHealth < maxHealth) + petHealth = maxHealth; + } + if (tamingMasteryLevel >= 4 && petActive) { + petLoyalty = PET_LOYALTY_MAX; + petHungryWarned = false; + } + } else { + return false; } - return false; + + goalPointsUnspent -= cost; + dictionary args; + args.set("unlock", mastery_unlock_label(categoryId, newLevel)); + notify(trf("system.goals.mastery.unlocked", args)); + return true; } float get_crafting_mastery_break_multiplier() { @@ -238,6 +454,56 @@ float get_crafting_mastery_break_multiplier() { return 1.0; } +bool crafting_mastery_prevents_personal_wear() { + return craftingMasteryLevel >= 4; +} + +int get_leadership_break_percent() { + if (leadershipMasteryLevel >= 1) + return 75; + return 100; +} + +int get_leadership_success_bonus() { + if (leadershipMasteryLevel >= 3) + return 10; + if (leadershipMasteryLevel >= 2) + return 5; + return 0; +} + +int get_leadership_cooldown_percent() { + if (leadershipMasteryLevel >= 3) + return 75; + return 100; +} + +int get_resident_food_interval_hours() { + if (leadershipMasteryLevel >= 4) + return 12; + return 8; +} + +bool taming_mastery_allows_retrieval() { + return tamingMasteryLevel >= 1; +} + +int get_pet_max_health() { + if (tamingMasteryLevel >= 2) + return PET_HEALTH_MAX + 1; + return PET_HEALTH_MAX; +} + +int get_pet_knockout_cooldown_hours() { + if (tamingMasteryLevel >= 3) + return 2; + return PET_KNOCKOUT_COOLDOWN_HOURS; +} + +bool taming_mastery_prevents_pet_hunger() { + return tamingMasteryLevel >= 4; +} + int get_goal_points_unspent() { return goalPointsUnspent; } @@ -250,6 +516,14 @@ int get_crafting_mastery_level() { return craftingMasteryLevel; } +int get_leadership_mastery_level() { + return leadershipMasteryLevel; +} + +int get_taming_mastery_level() { + return tamingMasteryLevel; +} + void run_goals_menu() { string[] options; int currentId = get_current_goal_id(); @@ -422,7 +696,9 @@ void run_mastery_menu() { } int categoryId = optionCategories[selection]; if (mastery_current_level(categoryId) >= mastery_max_level(categoryId)) { - speak_with_history(tr("system.goals.mastery.already_maxed"), true); + dictionary maxedArgs; + maxedArgs.set("category", mastery_category_name(categoryId)); + speak_with_history(trf("system.goals.mastery.already_maxed", maxedArgs), true); continue; } int cost = mastery_cost_for_next_level(categoryId); diff --git a/src/menus/base_info.nvgt b/src/menus/base_info.nvgt index 9a79fee..9552440 100644 --- a/src/menus/base_info.nvgt +++ b/src/menus/base_info.nvgt @@ -18,7 +18,7 @@ void run_base_info_menu() { if (world_storages.length() > 0) { options.insert_last("Storage built. Total items " + get_storage_total_items()); - int daily_food = residents_count * 3; // 1 per resident per 8 hours = 3 per day + int daily_food = residents_count * (24 / get_resident_food_interval_hours()); int meat_in_storage = get_storage_count(ITEM_MEAT); int smoked_fish_in_storage = get_storage_count(ITEM_SMOKED_FISH); int basket_food_in_storage = get_storage_count(ITEM_BASKET_FOOD); diff --git a/src/menus/character_info.nvgt b/src/menus/character_info.nvgt index e8103d5..73a9999 100644 --- a/src/menus/character_info.nvgt +++ b/src/menus/character_info.nvgt @@ -138,7 +138,7 @@ void run_character_info_menu() { dictionary petArgs; petArgs.set("pet", get_pet_display_name()); petArgs.set("health", petHealth); - petArgs.set("max_health", PET_HEALTH_MAX); + petArgs.set("max_health", get_pet_max_health()); petArgs.set("loyalty", petLoyalty); petArgs.set("max_loyalty", PET_LOYALTY_MAX); string petInfo = trf("system.character.info.pet", petArgs); diff --git a/src/pet_system.nvgt b/src/pet_system.nvgt index 89594f9..df54344 100644 --- a/src/pet_system.nvgt +++ b/src/pet_system.nvgt @@ -237,7 +237,7 @@ string get_health_report() { dictionary petArgs; petArgs.set("pet", get_pet_display_name()); petArgs.set("health", petHealth); - petArgs.set("max", PET_HEALTH_MAX); + petArgs.set("max", get_pet_max_health()); petArgs.set("loyalty", petLoyalty); petArgs.set("max_loyalty", PET_LOYALTY_MAX); report += ", " + trf("system.character.report.pet", petArgs); @@ -375,7 +375,7 @@ void knock_out_pet() { if (!petActive) return; petHealth = 0; - petKnockoutHoursRemaining = PET_KNOCKOUT_COOLDOWN_HOURS; + petKnockoutHoursRemaining = get_pet_knockout_cooldown_hours(); petOut = false; petPositionValid = false; stop_pet_travel(); @@ -537,11 +537,18 @@ void clamp_pet_loyalty() { petLoyalty = 0; if (petLoyalty > PET_LOYALTY_MAX) petLoyalty = PET_LOYALTY_MAX; + if (taming_mastery_prevents_pet_hunger() && petActive) + petLoyalty = PET_LOYALTY_MAX; } void adjust_pet_loyalty(int delta) { if (!petActive) return; + if (taming_mastery_prevents_pet_hunger() && delta < 0) { + petLoyalty = PET_LOYALTY_MAX; + petHungryWarned = false; + return; + } petLoyalty += delta; clamp_pet_loyalty(); if (petLoyalty <= 0) { @@ -611,13 +618,13 @@ void adopt_pet(const string& in soundPath) { petSoundPath = soundPath; petType = get_pet_name_from_sound_path(soundPath); petGender = (random(0, 1) == 0) ? "Male" : "Female"; - petLoyalty = PET_START_LOYALTY; + petLoyalty = taming_mastery_prevents_pet_hunger() ? PET_LOYALTY_MAX : PET_START_LOYALTY; petHungryWarned = false; petOut = false; petRecallRequested = false; petPosition = 0; petPositionValid = false; - petHealth = PET_HEALTH_MAX; + petHealth = get_pet_max_health(); petKnockoutHoursRemaining = 0; petAttackTimer.restart(); petRetrieveTimer.restart(); @@ -844,6 +851,8 @@ void update_pet_travel() { hit = damage_undead_at(petTravelTargetPos, damage); } else if (petTravelTargetKind == 2) { hit = damage_boar_at(petTravelTargetPos, damage); + } else if (petTravelTargetKind == 3) { + hit = damage_corpse_worm_at(petTravelTargetPos, damage); } } if (hit) { @@ -859,7 +868,7 @@ void update_pet_travel() { string nextTargetLabel = ""; int nextTargetKind = -1; bool hasNextTarget = find_pet_attack_target(nextTargetPos, nextTargetLabel, nextTargetKind); - if (!hasNextTarget) { + if (!hasNextTarget && taming_mastery_allows_retrieval()) { WorldDrop @drop = find_pet_drop_target(); if (drop !is null) { petRetrieveTimer.restart(); @@ -1070,6 +1079,8 @@ WorldDrop @find_pet_drop_target() { void update_pet_retrieval() { if (!petActive) return; + if (!taming_mastery_allows_retrieval()) + return; if (!petOut) return; if (petLoyalty <= 0) @@ -1141,6 +1152,18 @@ bool find_pet_attack_target(int& out targetPos, string& out targetLabel, int& ou } } + for (uint i = 0; i < corpse_worms.length(); i++) { + int distance = abs(corpse_worms[i].position - origin); + if (distance > PET_RANGE) + continue; + if (distance < bestDistance) { + bestDistance = distance; + targetPos = corpse_worms[i].position; + targetLabel = i18n_text("corpse worm"); + targetKind = 3; + } + } + return targetPos != -1; } @@ -1189,6 +1212,17 @@ bool find_pet_attack_target_by_kind(int targetKind, int referencePos, int& out t targetPos = ground_games[i].position; } } + } else if (targetKind == 3) { + for (uint i = 0; i < corpse_worms.length(); i++) { + int distanceToOrigin = abs(corpse_worms[i].position - origin); + if (distanceToOrigin > PET_RANGE) + continue; + int distance = abs(corpse_worms[i].position - referencePos); + if (distance < bestDistance) { + bestDistance = distance; + targetPos = corpse_worms[i].position; + } + } } return targetPos != -1; @@ -1230,9 +1264,11 @@ void update_pet_follow() { if (petTravelActive) return; - WorldDrop @drop = find_pet_drop_target(); - if (drop !is null) - return; + if (taming_mastery_allows_retrieval()) { + WorldDrop @drop = find_pet_drop_target(); + if (drop !is null) + return; + } int targetPos = -1; string targetLabel = ""; @@ -1281,17 +1317,25 @@ void handle_pet_hourly_update(int hour) { petKnockoutHoursRemaining--; if (petKnockoutHoursRemaining <= 0) { petKnockoutHoursRemaining = 0; - petHealth = PET_HEALTH_MAX; + petHealth = get_pet_max_health(); dictionary recoveredArgs; recoveredArgs.set("pet", get_pet_display_name()); notify(trf("system.pet.recovered_from_injuries", recoveredArgs)); } } - if (petKnockoutHoursRemaining <= 0 && !petOut && petHealth > 0 && petHealth < PET_HEALTH_MAX) { + int maxHealth = get_pet_max_health(); + if (petKnockoutHoursRemaining <= 0 && !petOut && petHealth > 0 && petHealth < maxHealth) { petHealth += 1; - if (petHealth > PET_HEALTH_MAX) - petHealth = PET_HEALTH_MAX; + if (petHealth > maxHealth) + petHealth = maxHealth; + } + + if (taming_mastery_prevents_pet_hunger()) { + petLoyalty = PET_LOYALTY_MAX; + petHungryWarned = false; + attempt_pet_random_find(); + return; } if (petOut) { diff --git a/src/save_system.nvgt b/src/save_system.nvgt index 78196c3..b5b566b 100644 --- a/src/save_system.nvgt +++ b/src/save_system.nvgt @@ -554,6 +554,7 @@ void clear_world_objects() { clear_zombies(); clear_bandits(); + clear_corpse_worms(); clear_flying_creatures(); clear_mountains(); clear_world_drops(); @@ -628,6 +629,7 @@ void reset_game_state() { // Reset inventory using the registry system reset_inventory(); + reset_rune_data(); spear_equipped = false; great_spear_equipped = false; @@ -718,6 +720,7 @@ void start_new_game() { if (current_save_file == "") { current_save_file = get_save_filename_for_name(player_name); } + queue_current_goal_announcement(false); save_game_state(); } @@ -981,6 +984,9 @@ bool save_game_state() { saveData.set("goal_points_unspent", goalPointsUnspent); saveData.set("goal_points_earned", goalPointsEarned); saveData.set("crafting_mastery_level", craftingMasteryLevel); + saveData.set("leadership_mastery_level", leadershipMasteryLevel); + saveData.set("taming_mastery_level", tamingMasteryLevel); + saveData.set("trees_chopped", treesChopped); saveData.set("weather_data", serialize_weather()); @@ -1224,16 +1230,21 @@ bool load_game_state_from_file(const string& in filename) { if (petActive) { if (petKnockoutHoursRemaining < 0) petKnockoutHoursRemaining = 0; - if (petKnockoutHoursRemaining > PET_KNOCKOUT_COOLDOWN_HOURS) { - petKnockoutHoursRemaining = PET_KNOCKOUT_COOLDOWN_HOURS; + int knockoutCooldown = get_pet_knockout_cooldown_hours(); + if (petKnockoutHoursRemaining > knockoutCooldown) { + petKnockoutHoursRemaining = knockoutCooldown; } if (petKnockoutHoursRemaining > 0) { petHealth = 0; } else { if (petHealth <= 0) - petHealth = PET_HEALTH_MAX; - if (petHealth > PET_HEALTH_MAX) - petHealth = PET_HEALTH_MAX; + petHealth = get_pet_max_health(); + if (petHealth > get_pet_max_health()) + petHealth = get_pet_max_health(); + } + if (taming_mastery_prevents_pet_hunger()) { + petLoyalty = PET_LOYALTY_MAX; + petHungryWarned = false; } petAttackTimer.restart(); petRetrieveTimer.restart(); @@ -1580,14 +1591,45 @@ bool load_game_state_from_file(const string& in filename) { goalPointsUnspent = int(get_number(saveData, "goal_points_unspent", 0)); goalPointsEarned = int(get_number(saveData, "goal_points_earned", 0)); craftingMasteryLevel = int(get_number(saveData, "crafting_mastery_level", 0)); + leadershipMasteryLevel = int(get_number(saveData, "leadership_mastery_level", 0)); + tamingMasteryLevel = int(get_number(saveData, "taming_mastery_level", 0)); + treesChopped = int(get_number(saveData, "trees_chopped", 0)); if (goalPointsUnspent < 0) goalPointsUnspent = 0; if (goalPointsEarned < goalPointsUnspent) goalPointsEarned = goalPointsUnspent; if (craftingMasteryLevel < 0) craftingMasteryLevel = 0; - if (craftingMasteryLevel > CRAFTING_MASTERY_MAX_LEVEL) - craftingMasteryLevel = CRAFTING_MASTERY_MAX_LEVEL; + if (craftingMasteryLevel > MASTERY_MAX_LEVEL) + craftingMasteryLevel = MASTERY_MAX_LEVEL; + if (leadershipMasteryLevel < 0) + leadershipMasteryLevel = 0; + if (leadershipMasteryLevel > MASTERY_MAX_LEVEL) + leadershipMasteryLevel = MASTERY_MAX_LEVEL; + if (tamingMasteryLevel < 0) + tamingMasteryLevel = 0; + if (tamingMasteryLevel > MASTERY_MAX_LEVEL) + tamingMasteryLevel = MASTERY_MAX_LEVEL; + if (treesChopped < 0) + treesChopped = 0; + if (petActive) { + int knockoutCooldown = get_pet_knockout_cooldown_hours(); + if (petKnockoutHoursRemaining > knockoutCooldown) + petKnockoutHoursRemaining = knockoutCooldown; + if (petKnockoutHoursRemaining > 0) { + petHealth = 0; + } else { + int maxPetHealth = get_pet_max_health(); + if (petHealth <= 0) + petHealth = maxPetHealth; + if (petHealth > maxPetHealth) + petHealth = maxPetHealth; + } + if (taming_mastery_prevents_pet_hunger()) { + petLoyalty = PET_LOYALTY_MAX; + petHungryWarned = false; + } + } if (current_hour < 0) current_hour = 0; diff --git a/src/sound_settings.nvgt b/src/sound_settings.nvgt index a8d8ed4..480d727 100644 --- a/src/sound_settings.nvgt +++ b/src/sound_settings.nvgt @@ -34,6 +34,8 @@ void configure_project_learn_sounds() { learn_sounds_add_description("sounds/enemies/invasion.ogg", i18n_text("The war drums pound! Prepare for combat!")); learn_sounds_add_description("sounds/items/miscellaneous.ogg", i18n_text("You picked up an item for which there is no specific sound.")); + learn_sounds_add_description("sounds/enemies/corpse_worm.ogg", + i18n_text("A weak crawling enemy that starts appearing outside the base after day 1.")); learn_sounds_add_description("sounds/enemies/goblin1.ogg", i18n_text("Inhabits mountainous regions.")); learn_sounds_add_description("sounds/enemies/undead_resident1.ogg", i18n_text("Not quite a zombie nor a vampyr. These undead remember they used to have a home, and " diff --git a/src/time_system.nvgt b/src/time_system.nvgt index 438e7e9..79a66a6 100644 --- a/src/time_system.nvgt +++ b/src/time_system.nvgt @@ -122,6 +122,11 @@ void resolve_pending_player_item_break() { return; if (x > BASE_END) return; + if (crafting_mastery_prevents_personal_wear()) { + playerItemBreakPending = false; + playerItemBreakPendingType = -1; + return; + } if (playerItemBreakPendingType == -1) { playerItemBreakPending = false; return; @@ -138,6 +143,8 @@ void resolve_pending_player_item_break() { void attempt_player_item_break_check() { if (playerItemBreakPending) return; + if (crafting_mastery_prevents_personal_wear()) + return; if (playerItemBreaksToday >= PLAYER_ITEM_BREAKS_PER_DAY) return; @@ -820,6 +827,7 @@ void update_time() { if (hour_timer.elapsed >= MS_PER_HOUR) { hour_timer.restart(); current_hour++; + allow_delayed_goal_announcement(); if (current_hour >= 24) { current_hour = 0; current_day++; @@ -835,8 +843,8 @@ void update_time() { playerItemBreaksToday = 0; } - // Residents consume food every 8 hours (at midnight, 8am, 4pm) - if (current_hour % 8 == 0) { + // Residents consume food on the interval controlled by Leadership Mastery. + if (current_hour % get_resident_food_interval_hours() == 0) { consume_food_for_residents(); } handle_pet_hourly_update(current_hour); diff --git a/src/world/world_snares.nvgt b/src/world/world_snares.nvgt index 3af0961..0b15815 100644 --- a/src/world/world_snares.nvgt +++ b/src/world/world_snares.nvgt @@ -102,6 +102,7 @@ class WorldSnare { snareCaughtArgs.set("catch_type", i18n_translate_fragment_value(catch_type)); snareCaughtArgs.set("position", position); notify(trf("system.snare.caught_at", snareCaughtArgs)); + complete_goal(GOAL_CATCH_SNARE); } } }