Wilder maps: rivers of water and lava across free maps (neutral bridges), natural caverns until about a quarter of every map is open
- caverns in free play and the campaign (mission pieces, fortresses, islands, sealed caves and dwarf caves stay untouched) - caverns sometimes hold a pool of water or lava, veins in their walls, rarely a small treasure or a creature in chains to free - rivers keep away from the start; test: open share, liquids and a hero path to the core - standalone file in play/ rebuilt Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
51814b8ac8
commit
2de0a1de8a
6 files changed
+198
-2
No files matched your search
@@ -17,7 +17,7 @@
|
||||
<script>var register_plugin; /* expected by the quad_net plugin in the bundle */</script>
|
||||
<script>
|
||||
// the game module is embedded: fetching it is answered from these bytes
|
||||
var DC_WASM = "AGFzbQEAAAABywZlYAF/AGAAAGADf39/AX9gA39/fwBgBn9/f39/fwBgBX9/f39/AGAEf39/fwBgAn9/AGAHf39/f39/fwF/YAF/AX9gAn9/AX9gBH9/f38Bf2AFf39/f38Bf2ADf319AGAFf319fX0AYAd/fX19fX19AGAGf39/fX9/AGAGf39/f39/AX9gAAF8YAABf2AAAX1gCH9/f39/f39/AGAJf39/f39/f39/AGAHf39/f39/fwBgBH19fX0AYAF9AGAKf39/f39/f39/fwBgBH5+f38BfmADfn5/AX5gAX4Bf2AEf39/fQF/YAJ+fwF/YAJ/fQBgAn1/AX9gBn99fXx/fwBgB39/f39/f3wAYAV/f39/fABgC39/f39/f399fX98AGAHf39/f399fQBgBH9/fX0AYAV/f319fwBgB39/f319f3wAYAN/f30AYA1/f319fX9/f39/f39/AGAFfX19fX8AYAZ9fX19fX8AYAV/fX1/fwBgB39/f319fX8AYAZ/f39/fH8AYAh/f39/f39/fABgBn9/f399fQBgCX99fX9/fX9/fwBgDH99fX9/fX9/f39/fwBgAX8BfWAJf39/f319fX99AGAGf39/fX1/AX9gCH9/fX19fX1/AGACfX0Bf2ACf34AYAd/fX1/fX99AGADf399AX1gB39/fX19f38AYAJ9fQF9YAh/f39/fX19fQBgCX9/fX19f39/fwBgAn98AGAFf31/f38AYAR9fX99AGAEf399fwBgA399fwBgBH99f38AYAp/f39/f39/f319AGAGf399fX19AX9gA39/fQF/YAJ/fwF9YAh/f31/f39/fwBgBX9/fX1/AX9gBH9/f30AYAR/f39+AGAFf39/fn8AYAd/fX19fX9/AGAEf399fQF9YBN/f39/f39/f39/f39/f39/f39/AX9gAX8BfmAGf35/f39/AGAJf39/f39/fn5+AGAEf31/fwF/YAN/fX8Bf2ADf35+AGALf39/f39/f39/f38Bf2AAAX5gAn9+AX9gA35+fgF+YAh/f39/f39/fwF/YAV/f359fQBgA39+fwBgC39/f39/f39/f39/AGACfH8Bf2ABfQF9YAV/fn5+fgBgAn1/AX0C1xR6A2Vudgtjb25zb2xlX2xvZwAAA2VudgdkY19odHRwABEDZW52DGRjX3NldF90b2tlbgAHA2VudgNub3cAEgNlbnYRZGNfY29hcnNlX3BvaW50ZXIAEwNlbnYMZGNfZmlsZV9wb2xsABMDZW52DGRjX2ZpbGVfdGFrZQAAA2VudgxkY19odHRwX3BvbGwACQNlbnYLZGNfaHR0cF9sZW4ACQNlbnYMZGNfaHR0cF90YWtlAAcDZW52DmRjX3N0b3JhZ2Vfc2V0AAYDZW52F2RjX2ZldGNoX2Rvd25sb2FkX3N0YXRlABMDZW52C2RjX2Rvd25sb2FkAAYDZW52DGRjX29wZW5fZmlsZQABA2VudgtkY19vcGVuX3VybAAHA2VudgxkY19jYWNoZV9zZXQABgNlbnYRZGNfZmV0Y2hfZG93bmxvYWQABgNlbnYKaW5pdF93ZWJnbAAAA2VudhFzZXR1cF9jYW52YXNfc2l6ZQAAA2VudgxjYW52YXNfd2lkdGgAEwNlbnYNY2FudmFzX2hlaWdodAATA2VudglkcGlfc2NhbGUAFANlbnYScnVuX2FuaW1hdGlvbl9sb29wAAADZW52DmRjX3N0b3JhZ2VfZ2V0AAoDZW52D2RjX3N0b3JhZ2VfdGFrZQAAA2VudglkY19wcm9tcHQACwNlbnYNZ2xCaW5kVGV4dHVyZQAHA2VudhBnbENvcHlUZXhJbWFnZTJEABUDZW52DWdsR2V0SW50ZWdlcnYABwNlbnYRZ2xHZW5WZXJ0ZXhBcnJheXMABwNlbnYRZ2xCaW5kVmVydGV4QXJyYXkAAANlbnYLZ2xHZXRTdHJpbmcACQNlbnYUc2FwcF9zY2hlZHVsZV91cGRhdGUAAQNlbnYMZ2xHZW5RdWVyaWVzAAcDZW52DGdsQmVnaW5RdWVyeQAHA2VudgpnbEVuZFF1ZXJ5AAADZW52B2dsRmx1c2gAAQNlbnYIZ2xGaW5pc2gAAQNlbnYMY29uc29sZV93YXJuAAADZW52D2dsQ3JlYXRlUHJvZ3JhbQATA2Vudg5nbEF0dGFjaFNoYWRlcgAHA2Vudg1nbExpbmtQcm9ncmFtAAADZW52DmdsRGV0YWNoU2hhZGVyAAcDZW52DmdsRGVsZXRlU2hhZGVyAAADZW52DmdsR2V0UHJvZ3JhbWl2AAMDZW52E2dsR2V0UHJvZ3JhbUluZm9Mb2cABgNlbnYMZ2xVc2VQcm9ncmFtAAADZW52FGdsR2V0VW5pZm9ybUxvY2F0aW9uAAoDZW52DWdsR2VuVGV4dHVyZXMABwNlbnYPZ2xBY3RpdmVUZXh0dXJlAAADZW52DWdsUGl4ZWxTdG9yZWkABwNlbnYSZ2xHZW5SZW5kZXJidWZmZXJzAAcDZW52EmdsQmluZFJlbmRlcmJ1ZmZlcgAHA2VudiBnbFJlbmRlcmJ1ZmZlclN0b3JhZ2VNdWx0aXNhbXBsZQAFA2VudgxnbFRleEltYWdlMkQAFgNlbnYPZ2xUZXhQYXJhbWV0ZXJpAAMDZW52EGdsR2VuZXJhdGVNaXBtYXAAAANlbnYRZ2xHZW5GcmFtZWJ1ZmZlcnMABwNlbnYRZ2xCaW5kRnJhbWVidWZmZXIABwNlbnYWZ2xGcmFtZWJ1ZmZlclRleHR1cmUyRAAFA2VudgxnbFJlYWRQaXhlbHMAFwNlbnYUZ2xEZWxldGVGcmFtZWJ1ZmZlcnMABwNlbnYPZ2xUZXhTdWJJbWFnZTJEABYDZW52GWdsRnJhbWVidWZmZXJSZW5kZXJidWZmZXIABgNlbnYNZ2xEcmF3QnVmZmVycwAHA2VudhhnbENoZWNrRnJhbWVidWZmZXJTdGF0dXMACQNlbnYVZ2xEZWxldGVSZW5kZXJidWZmZXJzAAcDZW52EGdsRGVsZXRlVGV4dHVyZXMABwNlbnYTZ2xHZXRBdHRyaWJMb2NhdGlvbgAKA2VudghnbEVuYWJsZQAAA2VudglnbERpc2FibGUAAANlbnYLZ2xEZXB0aEZ1bmMAAANlbnYLZ2xGcm9udEZhY2UAAANlbnYKZ2xDdWxsRmFjZQAAA2VudhNnbEJsZW5kRnVuY1NlcGFyYXRlAAYDZW52F2dsQmxlbmRFcXVhdGlvblNlcGFyYXRlAAcDZW52C2dsQmxlbmRGdW5jAAcDZW52E2dsU3RlbmNpbE9wU2VwYXJhdGUABgNlbnYVZ2xTdGVuY2lsRnVuY1NlcGFyYXRlAAYDZW52FWdsU3RlbmNpbE1hc2tTZXBhcmF0ZQAHA2VudgtnbENvbG9yTWFzawAGA2VudgxnbEdlbkJ1ZmZlcnMABwNlbnYMZ2xCaW5kQnVmZmVyAAcDZW52DGdsQnVmZmVyRGF0YQAGA2Vudg9nbEJ1ZmZlclN1YkRhdGEABgNlbnYPZ2xEZWxldGVCdWZmZXJzAAcDZW52GmdsRGlzYWJsZVZlcnRleEF0dHJpYkFycmF5AAADZW52D2dsRGVsZXRlUHJvZ3JhbQAAA2VudgpnbFZpZXdwb3J0AAYDZW52CWdsU2Npc3NvcgAGA2VudgtnbFVuaWZvcm0xaQAHA2VudhZnbFZlcnRleEF0dHJpYklQb2ludGVyAAUDZW52FWdsVmVydGV4QXR0cmliUG9pbnRlcgAEA2VudhVnbFZlcnRleEF0dHJpYkRpdmlzb3IABwNlbnYZZ2xFbmFibGVWZXJ0ZXhBdHRyaWJBcnJheQAAA2VudgxnbFVuaWZvcm0xZnYAAwNlbnYMZ2xVbmlmb3JtMmZ2AAMDZW52DGdsVW5pZm9ybTNmdgADA2VudgxnbFVuaWZvcm00ZnYAAwNlbnYMZ2xVbmlmb3JtMWl2AAMDZW52DGdsVW5pZm9ybTJpdgADA2VudgxnbFVuaWZvcm0zaXYAAwNlbnYMZ2xVbmlmb3JtNGl2AAMDZW52EmdsVW5pZm9ybU1hdHJpeDRmdgAGA2VudgxnbENsZWFyQ29sb3IAGANlbnYNZ2xDbGVhckRlcHRoZgAZA2Vudg5nbENsZWFyU3RlbmNpbAAAA2VudgdnbENsZWFyAAADZW52DGdsUmVhZEJ1ZmZlcgAAA2VudhFnbEJsaXRGcmFtZWJ1ZmZlcgAaA2VudhdnbERyYXdFbGVtZW50c0luc3RhbmNlZAAFA2Vudg5nbENyZWF0ZVNoYWRlcgAJA2Vudg5nbFNoYWRlclNvdXJjZQAGA2Vudg9nbENvbXBpbGVTaGFkZXIAAANlbnYNZ2xHZXRTaGFkZXJpdgADA2VudhJnbEdldFNoYWRlckluZm9Mb2cABgNlbnYSc2FwcF9zZXRfY2xpcGJvYXJkAAcDZW52EmZzX2dldF9idWZmZXJfc2l6ZQAJA2Vudg5mc190YWtlX2J1ZmZlcgADA2VudhRzYXBwX3NldF9jdXJzb3JfZ3JhYgAAA2Vudg9zYXBwX3NldF9jdXJzb3IABwNlbnYTc2FwcF9zZXRfZnVsbHNjcmVlbgAAA4wKigoDAwoGAgADCgMBCgMHCgMAAgMXCQAHAAcAFhcWFRUCFgUVBhYFCgcDGwocChwKAQABAAEBAQYFAAAAAAAAAAAAAAAAAAMAAAAAAAcAAAAAAAAAAAAAAAAABQMHBwIDAgcHAgcHBwoKAAoDAgADCh0HBwAHBQcFBwUDBAsKCwsMAwMBAwYHBwMEBAQXBgQDBwcHBwoKCwIMDAIHCwseCwsfCwsMByAKDAICCgICAgIMAgIKAgcCAwMJCQoMCgMDCwMDAwMDAwMDBwMCCgcHCSEHBwcHBwcHBwcJCQcHBwcHBwcHLine truncated
|
||||
var DC_WASM = "AGFzbQEAAAAB0wZmYAF/AGAAAGADf39/AX9gA39/fwBgBn9/f39/fwBgBX9/f39/AGAEf39/fwBgAn9/AGAHf39/f39/fwF/YAJ/fwF/YAF/AX9gBH9/f38Bf2AFf39/f38Bf2ADf319AGAFf319fX0AYAd/fX19fX19AGAGf39/fX9/AGAGf39/f39/AX9gAAF8YAABf2AAAX1gCH9/f39/f39/AGAJf39/f39/f39/AGAHf39/f39/fwBgBH19fX0AYAF9AGAKf39/f39/f39/fwBgBH5+f38BfmADfn5/AX5gAX4Bf2AEf39/fQF/YAJ+fwF/YAJ/fQBgAn1/AX9gBn99fXx/fwBgB39/f39/f3wAYAV/f39/fABgC39/f39/f399fX98AGAHf39/f399fQBgBH9/fX0AYAV/f319fwBgB39/f319f3wAYAN/f30AYA1/f319fX9/f39/f39/AGAFfX19fX8AYAZ9fX19fX8AYAV/fX1/fwBgB39/f319fX8AYAZ/f39/fH8AYAh/f39/f39/fABgBn9/f399fQBgCX99fX9/fX9/fwBgDH99fX9/fX9/f39/fwBgAX8BfWAJf39/f319fX99AGAGf39/fX1/AX9gBX9/f319AGACfX0Bf2ACf34AYAd/fX1/fX99AGADf399AX1gB39/fX19f38AYAJ9fQF9YAh/f39/fX19fQBgCX9/fX19f39/fwBgAn98AGAFf31/f38AYAh/f319fX19fwBgBH19f30AYAR/f31/AGADf31/AGAEf31/fwBgCn9/f39/f39/fX0AYAZ/f319fX0Bf2ADf399AX9gAn9/AX1gCH9/fX9/f39/AGAFf399fX8Bf2AEf39/fQBgBH9/f34AYAV/f39+fwBgB399fX19f38AYAR/f319AX1gE39/f39/f39/f39/f39/f39/f38Bf2ABfwF+YAZ/fn9/f38AYAl/f39/f39+fn4AYAR/fX9/AX9gA399fwF/YAN/fn4AYAt/f39/f39/f39/fwF/YAABfmACf34Bf2ADfn5+AX5gCH9/f39/f39/AX9gBX9/fn19AGADf35/AGALf39/f39/f39/f38AYAJ8fwF/YAF9AX1gBX9+fn5+AGACfX8BfQLXFHoDZW52C2NvbnNvbGVfbG9nAAADZW52B2RjX2h0dHAAEQNlbnYMZGNfc2V0X3Rva2VuAAcDZW52A25vdwASA2VudhFkY19jb2Fyc2VfcG9pbnRlcgATA2VudgxkY19maWxlX3BvbGwAEwNlbnYMZGNfZmlsZV90YWtlAAADZW52DGRjX2h0dHBfcG9sbAAKA2VudgtkY19odHRwX2xlbgAKA2VudgxkY19odHRwX3Rha2UABwNlbnYOZGNfc3RvcmFnZV9zZXQABgNlbnYXZGNfZmV0Y2hfZG93bmxvYWRfc3RhdGUAEwNlbnYLZGNfZG93bmxvYWQABgNlbnYMZGNfb3Blbl9maWxlAAEDZW52C2RjX29wZW5fdXJsAAcDZW52DGRjX2NhY2hlX3NldAAGA2VudhFkY19mZXRjaF9kb3dubG9hZAAGA2Vudgppbml0X3dlYmdsAAADZW52EXNldHVwX2NhbnZhc19zaXplAAADZW52DGNhbnZhc193aWR0aAATA2Vudg1jYW52YXNfaGVpZ2h0ABMDZW52CWRwaV9zY2FsZQAUA2VudhJydW5fYW5pbWF0aW9uX2xvb3AAAANlbnYOZGNfc3RvcmFnZV9nZXQACQNlbnYPZGNfc3RvcmFnZV90YWtlAAADZW52CWRjX3Byb21wdAALA2Vudg1nbEJpbmRUZXh0dXJlAAcDZW52EGdsQ29weVRleEltYWdlMkQAFQNlbnYNZ2xHZXRJbnRlZ2VydgAHA2VudhFnbEdlblZlcnRleEFycmF5cwAHA2VudhFnbEJpbmRWZXJ0ZXhBcnJheQAAA2VudgtnbEdldFN0cmluZwAKA2VudhRzYXBwX3NjaGVkdWxlX3VwZGF0ZQABA2VudgxnbEdlblF1ZXJpZXMABwNlbnYMZ2xCZWdpblF1ZXJ5AAcDZW52CmdsRW5kUXVlcnkAAANlbnYHZ2xGbHVzaAABA2VudghnbEZpbmlzaAABA2Vudgxjb25zb2xlX3dhcm4AAANlbnYPZ2xDcmVhdGVQcm9ncmFtABMDZW52DmdsQXR0YWNoU2hhZGVyAAcDZW52DWdsTGlua1Byb2dyYW0AAANlbnYOZ2xEZXRhY2hTaGFkZXIABwNlbnYOZ2xEZWxldGVTaGFkZXIAAANlbnYOZ2xHZXRQcm9ncmFtaXYAAwNlbnYTZ2xHZXRQcm9ncmFtSW5mb0xvZwAGA2VudgxnbFVzZVByb2dyYW0AAANlbnYUZ2xHZXRVbmlmb3JtTG9jYXRpb24ACQNlbnYNZ2xHZW5UZXh0dXJlcwAHA2Vudg9nbEFjdGl2ZVRleHR1cmUAAANlbnYNZ2xQaXhlbFN0b3JlaQAHA2VudhJnbEdlblJlbmRlcmJ1ZmZlcnMABwNlbnYSZ2xCaW5kUmVuZGVyYnVmZmVyAAcDZW52IGdsUmVuZGVyYnVmZmVyU3RvcmFnZU11bHRpc2FtcGxlAAUDZW52DGdsVGV4SW1hZ2UyRAAWA2Vudg9nbFRleFBhcmFtZXRlcmkAAwNlbnYQZ2xHZW5lcmF0ZU1pcG1hcAAAA2VudhFnbEdlbkZyYW1lYnVmZmVycwAHA2VudhFnbEJpbmRGcmFtZWJ1ZmZlcgAHA2VudhZnbEZyYW1lYnVmZmVyVGV4dHVyZTJEAAUDZW52DGdsUmVhZFBpeGVscwAXA2VudhRnbERlbGV0ZUZyYW1lYnVmZmVycwAHA2Vudg9nbFRleFN1YkltYWdlMkQAFgNlbnYZZ2xGcmFtZWJ1ZmZlclJlbmRlcmJ1ZmZlcgAGA2Vudg1nbERyYXdCdWZmZXJzAAcDZW52GGdsQ2hlY2tGcmFtZWJ1ZmZlclN0YXR1cwAKA2VudhVnbERlbGV0ZVJlbmRlcmJ1ZmZlcnMABwNlbnYQZ2xEZWxldGVUZXh0dXJlcwAHA2VudhNnbEdldEF0dHJpYkxvY2F0aW9uAAkDZW52CGdsRW5hYmxlAAADZW52CWdsRGlzYWJsZQAAA2VudgtnbERlcHRoRnVuYwAAA2VudgtnbEZyb250RmFjZQAAA2VudgpnbEN1bGxGYWNlAAADZW52E2dsQmxlbmRGdW5jU2VwYXJhdGUABgNlbnYXZ2xCbGVuZEVxdWF0aW9uU2VwYXJhdGUABwNlbnYLZ2xCbGVuZEZ1bmMABwNlbnYTZ2xTdGVuY2lsT3BTZXBhcmF0ZQAGA2VudhVnbFN0ZW5jaWxGdW5jU2VwYXJhdGUABgNlbnYVZ2xTdGVuY2lsTWFza1NlcGFyYXRlAAcDZW52C2dsQ29sb3JNYXNrAAYDZW52DGdsR2VuQnVmZmVycwAHA2VudgxnbEJpbmRCdWZmZXIABwNlbnYMZ2xCdWZmZXJEYXRhAAYDZW52D2dsQnVmZmVyU3ViRGF0YQAGA2Vudg9nbERlbGV0ZUJ1ZmZlcnMABwNlbnYaZ2xEaXNhYmxlVmVydGV4QXR0cmliQXJyYXkAAANlbnYPZ2xEZWxldGVQcm9ncmFtAAADZW52CmdsVmlld3BvcnQABgNlbnYJZ2xTY2lzc29yAAYDZW52C2dsVW5pZm9ybTFpAAcDZW52FmdsVmVydGV4QXR0cmliSVBvaW50ZXIABQNlbnYVZ2xWZXJ0ZXhBdHRyaWJQb2ludGVyAAQDZW52FWdsVmVydGV4QXR0cmliRGl2aXNvcgAHA2VudhlnbEVuYWJsZVZlcnRleEF0dHJpYkFycmF5AAADZW52DGdsVW5pZm9ybTFmdgADA2VudgxnbFVuaWZvcm0yZnYAAwNlbnYMZ2xVbmlmb3JtM2Z2AAMDZW52DGdsVW5pZm9ybTRmdgADA2VudgxnbFVuaWZvcm0xaXYAAwNlbnYMZ2xVbmlmb3JtMml2AAMDZW52DGdsVW5pZm9ybTNpdgADA2VudgxnbFVuaWZvcm00aXYAAwNlbnYSZ2xVbmlmb3JtTWF0cml4NGZ2AAYDZW52DGdsQ2xlYXJDb2xvcgAYA2Vudg1nbENsZWFyRGVwdGhmABkDZW52DmdsQ2xlYXJTdGVuY2lsAAADZW52B2dsQ2xlYXIAAANlbnYMZ2xSZWFkQnVmZmVyAAADZW52EWdsQmxpdEZyYW1lYnVmZmVyABoDZW52F2dsRHJhd0VsZW1lbnRzSW5zdGFuY2VkAAUDZW52DmdsQ3JlYXRlU2hhZGVyAAoDZW52DmdsU2hhZGVyU291cmNlAAYDZW52D2dsQ29tcGlsZVNoYWRlcgAAA2Vudg1nbEdldFNoYWRlcml2AAMDZW52EmdsR2V0U2hhZGVySW5mb0xvZwAGA2VudhJzYXBwX3NldF9jbGlwYm9hcmQABwNlbnYSZnNfZ2V0X2J1ZmZlcl9zaXplAAoDZW52DmZzX3Rha2VfYnVmZmVyAAMDZW52FHNhcHBfc2V0X2N1cnNvcl9ncmFiAAADZW52D3NhcHBfc2V0X2N1cnNvcgAHA2VudhNzYXBwX3NldF9mdWxsc2NyZWVuAAADqQqnCgMDCQYCAAMJAwEJAwcJAwACAxcKAAcABwAWFxYVFQIWBRUGFgUJBwMbCRwJHAkBAAEAAQEBBgUAAAAAAAAAAAAAAAAAAwAAAAAABwAAAAAAAAAAAAAAAAAFAwcHAgMCBwcCBwcHCQkACQMCAAMJHQcHAAMEBwUHBQcFBwUDBAMEAwQMCwkLCwsMDAMDAwMBAwMGBgYDBwcHAwMXBAQEBBcXFwYEAwcHBwcJCQsCDAwCBwsLHgsLHwsLDAcgCQwCAgkCAgICDAICCQIHAgMDCgoJDAkDAwsDAwMDLine truncated
|
||||
(function () {
|
||||
var realFetch = window.fetch ? window.fetch.bind(window) : null;
|
||||
window.fetch = function (url, opts) {
|
||||
|
||||
@@ -504,6 +504,7 @@ pub fn start_mission(i: usize, carry: Option<&Carry>) -> Game {
|
||||
g.add_rival(side, x, y, diff);
|
||||
}
|
||||
g.place_dwarf_caves(d.seed);
|
||||
g.add_wilds(d.seed, false);
|
||||
g.init_research(Some(i));
|
||||
g.init_fog();
|
||||
let heart = d.gate || d.forts.iter().any(|f| f.heart);
|
||||
|
||||
+2
-1
@@ -629,6 +629,7 @@ impl Game {
|
||||
g.wave_timer = FIRST_WAVE + ((dist - 45.0) * 1.5).clamp(0.0, 300.0);
|
||||
g.guard_treasures();
|
||||
g.place_dwarf_caves(seed);
|
||||
g.add_wilds(seed, true);
|
||||
g.init_research(None);
|
||||
g.init_fog();
|
||||
g.msg("msg.welcome1", vec![]);
|
||||
@@ -5133,7 +5134,7 @@ mod tests {
|
||||
// rubies fill their own tiles
|
||||
g.add_res(1200.0, 2);
|
||||
g.update(0.01);
|
||||
let rubies: i32 = g.map.t.iter().filter(|t| t.kind == Kind::Treasury && t.res == 2).map(|t| t.gold).sum();
|
||||
let rubies: i32 = g.map.t.iter().filter(|t| t.kind == Kind::Treasury && t.owner == Owner::Player && t.res == 2).map(|t| t.gold).sum();
|
||||
assert_eq!(rubies, 1200);
|
||||
assert!(g.map.t.iter().all(|t| t.gold <= TREASURY_TILE as i32 || t.vein().is_some()));
|
||||
// spending empties tiles (randomly), the total always matches the gold counter
|
||||
|
||||
@@ -30,6 +30,7 @@ mod sound;
|
||||
mod spells;
|
||||
mod touch;
|
||||
mod ui;
|
||||
mod wilds;
|
||||
mod xm;
|
||||
|
||||
use cloud::{Cloud, CloudEvent};
|
||||
|
||||
@@ -85,6 +85,7 @@ impl Game {
|
||||
g.msg("msg.welcome1", vec![]);
|
||||
g.msg("msg.welcome2", vec![]);
|
||||
g.msg(if rivals == 0 { "msg.welcome_humans" } else if humans { "msg.welcome_both" } else { "msg.welcome_rivals" }, vec![rivals.to_string()]);
|
||||
g.add_wilds(seed, true);
|
||||
g.init_research(None);
|
||||
g.init_fog();
|
||||
g.place_dwarf_caves(seed);
|
||||
|
||||
+192
@@ -0,0 +1,192 @@
|
||||
// The wild underground: rivers of water and lava across free maps (with neutral bridges) and
|
||||
// natural caverns until about a quarter of the map is open - some with a pool, veins along the
|
||||
// walls, now and then a small treasure or a creature in chains waiting to be freed.
|
||||
// Everything the map or the mission placed on purpose (core, portals, gate, fortresses,
|
||||
// islands, sealed caves, dwarf caves, rival dungeons) is left alone.
|
||||
|
||||
use crate::game::*;
|
||||
use crate::map::*;
|
||||
|
||||
const OPEN_SHARE: f32 = 0.25;
|
||||
|
||||
impl Game {
|
||||
pub fn add_wilds(&mut self, seed: u32, rivers: bool) {
|
||||
let mut rng = Rng(seed.wrapping_mul(0x2c1b_3c6d) ^ 0x297a_2d39 | 1);
|
||||
for _ in 0..6 {
|
||||
rng.next();
|
||||
}
|
||||
let (w, h) = (mw() as i32, mh() as i32);
|
||||
let area = (w * h) as f32;
|
||||
if rivers {
|
||||
// one river per ~100x100 tiles, at least one; some of lava
|
||||
let n = ((area / 10000.0).round() as i32).clamp(1, 4);
|
||||
for k in 0..n {
|
||||
// away from the start: vertical rivers in the right part, horizontal ones above or below the core
|
||||
let vertical = rng.f() < 0.5;
|
||||
let pos = if vertical { 0.42 + rng.f() * 0.4 } else if rng.f() < 0.5 { 0.12 + rng.f() * 0.18 } else { 0.7 + rng.f() * 0.18 };
|
||||
let r = River { lava: rng.f() < 0.4, vertical, pos, width: 3 + (rng.f() < 0.5) as i32, bridges: 2 + (area > 15000.0) as u32 };
|
||||
let bridges = self.map.carve_river(&r, seed.wrapping_add(k as u32 * 7919));
|
||||
// free maps: the bridges belong to nobody (anybody may claim them)
|
||||
for b in bridges {
|
||||
self.map.t[b].owner = Owner::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
// what must not be touched: owned ground, buildings, mission pieces and their surroundings
|
||||
let n = self.map.t.len();
|
||||
let mut keep = vec![false; n];
|
||||
let mark = |keep: &mut Vec<bool>, x: i32, y: i32, r: i32| {
|
||||
for yy in y - r..=y + r {
|
||||
for xx in x - r..=x + r {
|
||||
if inb(xx, yy) {
|
||||
keep[idx(xx, yy)] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
for i in 0..n {
|
||||
let t = &self.map.t[i];
|
||||
let special = t.owner != Owner::None
|
||||
|| matches!(t.kind, Kind::Core | Kind::Portal | Kind::Gate | Kind::Tower | Kind::Shard | Kind::Treasury | Kind::ManaSpring | Kind::RuneChest | Kind::Bridge | Kind::LavaBridge);
|
||||
if special {
|
||||
let (x, y) = xy(i);
|
||||
mark(&mut keep, x, y, 3);
|
||||
}
|
||||
}
|
||||
for c in self.dwarf_caves.clone() {
|
||||
mark(&mut keep, c.at.0, c.at.1, 7);
|
||||
}
|
||||
for e in self.ents.iter().filter(|e| e.alive && (e.chained || e.hero || e.dwarf() || e.rival())) {
|
||||
mark(&mut keep, e.x as i32, e.y as i32, 4);
|
||||
}
|
||||
let (cx, cy) = (self.map.core.0 + 1, self.map.core.1 + 1);
|
||||
mark(&mut keep, cx, cy, 9);
|
||||
let open = |m: &Map| m.t.iter().filter(|t| !t.solid() && t.kind != Kind::Rock).count() as f32;
|
||||
let mut tries = 0;
|
||||
while open(&self.map) < area * OPEN_SHARE && tries < 400 {
|
||||
tries += 1;
|
||||
let (x, y) = (rng.range(4, w - 5), rng.range(4, h - 5));
|
||||
if keep[idx(x, y)] {
|
||||
continue;
|
||||
}
|
||||
self.carve_cavern(x, y, &keep, &mut rng);
|
||||
}
|
||||
}
|
||||
|
||||
// a cavern: a few overlapping blobs, ragged edges
|
||||
fn carve_cavern(&mut self, x: i32, y: i32, keep: &[bool], rng: &mut Rng) {
|
||||
let (w, h) = (mw() as i32, mh() as i32);
|
||||
let blobs = 2 + rng.range(0, 3);
|
||||
let mut tiles: Vec<usize> = Vec::new();
|
||||
let (mut bx, mut by) = (x, y);
|
||||
for _ in 0..blobs {
|
||||
let r = rng.range(2, 5);
|
||||
for yy in by - r..=by + r {
|
||||
for xx in bx - r..=bx + r {
|
||||
if xx < 3 || yy < 3 || xx >= w - 3 || yy >= h - 3 {
|
||||
continue;
|
||||
}
|
||||
let d = (((xx - bx).pow(2) + (yy - by).pow(2)) as f32).sqrt();
|
||||
if d > r as f32 + 0.3 - rng.f() * 0.9 {
|
||||
continue;
|
||||
}
|
||||
let i = idx(xx, yy);
|
||||
let t = &self.map.t[i];
|
||||
if keep[i] || !t.solid() || t.kind == Kind::Rock || t.kind == Kind::Steel {
|
||||
continue;
|
||||
}
|
||||
tiles.push(i);
|
||||
}
|
||||
}
|
||||
bx += rng.range(-r, r);
|
||||
by += rng.range(-r, r);
|
||||
}
|
||||
if tiles.len() < 6 {
|
||||
return;
|
||||
}
|
||||
for &i in &tiles {
|
||||
let t = &mut self.map.t[i];
|
||||
t.kind = Kind::Floor;
|
||||
t.owner = Owner::None;
|
||||
t.gold = 0;
|
||||
t.marked = false;
|
||||
t.reinforced = false;
|
||||
}
|
||||
// veins in the walls around
|
||||
for &i in &tiles {
|
||||
let (tx, ty) = xy(i);
|
||||
for &(dx, dy) in &DIR4 {
|
||||
let (nx, ny) = (tx + dx, ty + dy);
|
||||
if !inb(nx, ny) || keep[idx(nx, ny)] {
|
||||
continue;
|
||||
}
|
||||
let t = &mut self.map.t[idx(nx, ny)];
|
||||
if t.kind == Kind::Earth && rng.f() < 0.07 {
|
||||
let r = rng.f();
|
||||
let (k, g) = if r < 0.06 { (Kind::Ruby, rng.range(500, 900)) } else if r < 0.3 { (Kind::Silver, rng.range(300, 600)) } else { (Kind::Gold, rng.range(400, 900)) };
|
||||
t.kind = k;
|
||||
t.gold = g;
|
||||
t.hp = 6.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// the middle: sometimes a pool, rarely a treasure or a creature in chains
|
||||
let (sx, sy) = tiles.iter().fold((0i64, 0i64), |a, &i| (a.0 + xy(i).0 as i64, a.1 + xy(i).1 as i64));
|
||||
let (mx, my) = ((sx / tiles.len() as i64) as i32, (sy / tiles.len() as i64) as i32);
|
||||
let inside = |m: &Map, x: i32, y: i32| inb(x, y) && m.t[idx(x, y)].kind == Kind::Floor && m.t[idx(x, y)].owner == Owner::None;
|
||||
let roll = rng.f();
|
||||
if tiles.len() > 30 && roll < 0.35 {
|
||||
let kind = if rng.f() < 0.4 { Kind::Lava } else { Kind::Water };
|
||||
for yy in my - 1..=my + 1 {
|
||||
for xx in mx - 1..=mx + 1 {
|
||||
// only where floor surrounds it, so the cavern stays passable around the pool
|
||||
if inside(&self.map, xx, yy) && DIR8.iter().all(|&(dx, dy)| !self.map.get(xx + dx, yy + dy).map_or(true, |t| t.solid())) {
|
||||
self.map.t[idx(xx, yy)].kind = kind;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if roll < 0.47 && inside(&self.map, mx, my) {
|
||||
for &(dx, dy) in &[(0, 0), (1, 0)] {
|
||||
if inside(&self.map, mx + dx, my + dy) {
|
||||
let t = &mut self.map.t[idx(mx + dx, my + dy)];
|
||||
t.kind = Kind::Treasury;
|
||||
t.gold = rng.range(200, 450);
|
||||
t.res = (rng.f() < 0.3) as u8 * 2;
|
||||
}
|
||||
}
|
||||
} else if roll < 0.53 && inside(&self.map, mx, my) {
|
||||
let kinds = [EK::Spider, EK::Kobold, EK::Ogre, EK::Bat, EK::Mage];
|
||||
let kind = kinds[rng.range(0, kinds.len() as i32 - 1) as usize];
|
||||
let level = rng.range(2, 4) as u32;
|
||||
let id = self.spawn(kind, false, mx as f32 + 0.5, my as f32 + 0.5, level);
|
||||
if let Some(e) = self.ents.iter_mut().find(|e| e.id == id) {
|
||||
e.chained = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn open_share(g: &Game) -> f32 {
|
||||
g.map.t.iter().filter(|t| !t.solid() && t.kind != Kind::Rock).count() as f32 / g.map.t.len() as f32
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn free_maps_have_rivers_and_caverns_and_the_heroes_find_a_way() {
|
||||
for seed in 1..=8u32 {
|
||||
for &(w, h) in &[(64usize, 64usize), (128, 128)] {
|
||||
let g = Game::new(seed, w, h);
|
||||
let share = open_share(&g);
|
||||
assert!(share > 0.2, "seed {} {}x{}: only {:.0}% open", seed, w, h, share * 100.0);
|
||||
assert!(g.map.t.iter().any(|t| t.liquid()), "rivers or pools");
|
||||
let (gx, gy) = g.map.gate;
|
||||
assert!(g.map.hero_path_to_core(idx(gx + 1, gy + 1)).is_some(), "seed {}: heroes reach the core", seed);
|
||||
let (cx, cy) = g.map.core;
|
||||
assert_eq!(g.map.t[idx(cx + 1, cy + 1)].kind, Kind::Core);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user