Crookfinger's voice: he babbles in a squeaky imp language in the rhythm of his hints, important messages and the mission briefings (can be switched off in the sound settings)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
776c9ad9de
commit
ada14951c4
9 files changed
+118
-3
No files matched your search
@@ -923,3 +923,4 @@ ov.title = Deine Kreaturen
|
|||||||
ov.summary = {0} Kreaturen - Lohn {1} Gold pro Zahltag - Laune {2}%
|
ov.summary = {0} Kreaturen - Lohn {1} Gold pro Zahltag - Laune {2}%
|
||||||
ov.none = Noch keine Kreaturen. Baue ein Quartier und einen Pilzgarten und nimm ein Portal ein.
|
ov.none = Noch keine Kreaturen. Baue ein Quartier und einen Pilzgarten und nimm ein Portal ein.
|
||||||
ov.hint = St., Laune, Lohn, Tätigkeit, Leben - Klick: hingehen
|
ov.hint = St., Laune, Lohn, Tätigkeit, Leben - Klick: hingehen
|
||||||
|
set.voice = Krummfingers Stimme
|
||||||
@@ -924,3 +924,4 @@ ov.title = Your creatures
|
|||||||
ov.summary = {0} creatures - wages {1} gold per payday - mood {2}%
|
ov.summary = {0} creatures - wages {1} gold per payday - mood {2}%
|
||||||
ov.none = No creatures yet. Build a lair and a mushroom garden and claim a portal.
|
ov.none = No creatures yet. Build a lair and a mushroom garden and claim a portal.
|
||||||
ov.hint = Lv, mood, wage, job, health - click: go there
|
ov.hint = Lv, mood, wage, job, health - click: go there
|
||||||
|
set.voice = Crookfinger's voice
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<script>var register_plugin; /* expected by the quad_net plugin in the bundle */</script>
|
<script>var register_plugin; /* expected by the quad_net plugin in the bundle */</script>
|
||||||
<script>
|
<script>
|
||||||
// the game module is embedded: fetching it is answered from these bytes
|
// the game module is embedded: fetching it is answered from these bytes
|
||||||
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/f399fX98AGAGf39/f319AGAEf399fQBgBX9/fX1/AGAHf39/fX1/fABgB39/f39/fX0AYAN/f30AYA1/f319fX9/f39/f39/AGAFfX19fX8AYAZ9fX19fX8AYAV/fX1/fwBgB39/f319fX8AYAZ/f39/fH8AYAh/f39/f39/fABgCX99fX9/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
|
var DC_WASM = "AGFzbQEAAAAB2wZnYAF/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/f399fX98AGAGf39/f319AGAEf399fQBgBX9/fX1/AGAHf39/fX1/fABgB39/f39/fX0AYAN/f30AYAV/f398fwBgDX9/fX19f39/f39/f38AYAV9fX19fwBgBn19fX19fwBgBX99fX9/AGAHf39/fX19fwBgBn9/f398fwBgCH9/f39/f398AGAJf319f399f39/AGAMf319f399f39/f39/AGABfwF9YAl/f39/fX19f30AYAZ/f399fX8Bf2AFf39/fX0AYAJ9fQF/YAJ/fgBgB399fX99f30AYAN/f30BfWAHf399fX1/fwBgAn19AX1gCH9/f399fX19AGAJf399fX1/f39/AGACf3wAYAV/fX9/fwBgCH9/fX19fX1/AGAEfX1/fQBgBH9/fX8AYAN/fX8AYAR/fX9/AGAKf39/f39/f399fQBgBn9/fX19fQF/YAN/f30Bf2ACf38BfWAIf399f39/f38AYAV/f319fwF/YAR/f399AGAEf39/fgBgBX9/f35/AGAHf319fX1/fwBgBH9/fX0BfWATf39/f39/f39/f39/f39/f39/fwF/YAF/AX5gBn9+f39/fwBgCX9/f39/f35+fgBgBH99f38Bf2ADf31/AX9gA39+fgBgC39/f39/f39/f39/AX9gAAF+YAJ/fgF/YAN+fn4BfmAIf39/f39/f38Bf2AFf39+fX0AYAN/fn8AYAt/f39/f39/f39/fwBgAnx/AX9gAX0BfWAFf35+fn4AYAJ9fwF9AtcUegNlbnYLY29uc29sZV9sb2cAAANlbnYHZGNfaHR0cAARA2VudgxkY19zZXRfdG9rZW4ABwNlbnYDbm93ABIDZW52EWRjX2NvYXJzZV9wb2ludGVyABMDZW52DGRjX2ZpbGVfcG9sbAATA2VudgxkY19maWxlX3Rha2UAAANlbnYMZGNfaHR0cF9wb2xsAAoDZW52C2RjX2h0dHBfbGVuAAoDZW52DGRjX2h0dHBfdGFrZQAHA2Vudg5kY19zdG9yYWdlX3NldAAGA2VudhdkY19mZXRjaF9kb3dubG9hZF9zdGF0ZQATA2VudgtkY19kb3dubG9hZAAGA2VudgxkY19vcGVuX2ZpbGUAAQNlbnYLZGNfb3Blbl91cmwABwNlbnYMZGNfY2FjaGVfc2V0AAYDZW52EWRjX2ZldGNoX2Rvd25sb2FkAAYDZW52CmluaXRfd2ViZ2wAAANlbnYRc2V0dXBfY2FudmFzX3NpemUAAANlbnYMY2FudmFzX3dpZHRoABMDZW52DWNhbnZhc19oZWlnaHQAEwNlbnYJZHBpX3NjYWxlABQDZW52EnJ1bl9hbmltYXRpb25fbG9vcAAAA2Vudg5kY19zdG9yYWdlX2dldAAJA2Vudg9kY19zdG9yYWdlX3Rha2UAAANlbnYJZGNfcHJvbXB0AAsDZW52DWdsQmluZFRleHR1cmUABwNlbnYQZ2xDb3B5VGV4SW1hZ2UyRAAVA2Vudg1nbEdldEludGVnZXJ2AAcDZW52EWdsR2VuVmVydGV4QXJyYXlzAAcDZW52EWdsQmluZFZlcnRleEFycmF5AAADZW52C2dsR2V0U3RyaW5nAAoDZW52FHNhcHBfc2NoZWR1bGVfdXBkYXRlAAEDZW52DGdsR2VuUXVlcmllcwAHA2VudgxnbEJlZ2luUXVlcnkABwNlbnYKZ2xFbmRRdWVyeQAAA2VudgdnbEZsdXNoAAEDZW52CGdsRmluaXNoAAEDZW52DGNvbnNvbGVfd2FybgAAA2Vudg9nbENyZWF0ZVByb2dyYW0AEwNlbnYOZ2xBdHRhY2hTaGFkZXIABwNlbnYNZ2xMaW5rUHJvZ3JhbQAAA2Vudg5nbERldGFjaFNoYWRlcgAHA2Vudg5nbERlbGV0ZVNoYWRlcgAAA2Vudg5nbEdldFByb2dyYW1pdgADA2VudhNnbEdldFByb2dyYW1JbmZvTG9nAAYDZW52DGdsVXNlUHJvZ3JhbQAAA2VudhRnbEdldFVuaWZvcm1Mb2NhdGlvbgAJA2Vudg1nbEdlblRleHR1cmVzAAcDZW52D2dsQWN0aXZlVGV4dHVyZQAAA2Vudg1nbFBpeGVsU3RvcmVpAAcDZW52EmdsR2VuUmVuZGVyYnVmZmVycwAHA2VudhJnbEJpbmRSZW5kZXJidWZmZXIABwNlbnYgZ2xSZW5kZXJidWZmZXJTdG9yYWdlTXVsdGlzYW1wbGUABQNlbnYMZ2xUZXhJbWFnZTJEABYDZW52D2dsVGV4UGFyYW1ldGVyaQADA2VudhBnbEdlbmVyYXRlTWlwbWFwAAADZW52EWdsR2VuRnJhbWVidWZmZXJzAAcDZW52EWdsQmluZEZyYW1lYnVmZmVyAAcDZW52FmdsRnJhbWVidWZmZXJUZXh0dXJlMkQABQNlbnYMZ2xSZWFkUGl4ZWxzABcDZW52FGdsRGVsZXRlRnJhbWVidWZmZXJzAAcDZW52D2dsVGV4U3ViSW1hZ2UyRAAWA2VudhlnbEZyYW1lYnVmZmVyUmVuZGVyYnVmZmVyAAYDZW52DWdsRHJhd0J1ZmZlcnMABwNlbnYYZ2xDaGVja0ZyYW1lYnVmZmVyU3RhdHVzAAoDZW52FWdsRGVsZXRlUmVuZGVyYnVmZmVycwAHA2VudhBnbERlbGV0ZVRleHR1cmVzAAcDZW52E2dsR2V0QXR0cmliTG9jYXRpb24ACQNlbnYIZ2xFbmFibGUAAANlbnYJZ2xEaXNhYmxlAAADZW52C2dsRGVwdGhGdW5jAAADZW52C2dsRnJvbnRGYWNlAAADZW52CmdsQ3VsbEZhY2UAAANlbnYTZ2xCbGVuZEZ1bmNTZXBhcmF0ZQAGA2VudhdnbEJsZW5kRXF1YXRpb25TZXBhcmF0ZQAHA2VudgtnbEJsZW5kRnVuYwAHA2VudhNnbFN0ZW5jaWxPcFNlcGFyYXRlAAYDZW52FWdsU3RlbmNpbEZ1bmNTZXBhcmF0ZQAGA2VudhVnbFN0ZW5jaWxNYXNrU2VwYXJhdGUABwNlbnYLZ2xDb2xvck1hc2sABgNlbnYMZ2xHZW5CdWZmZXJzAAcDZW52DGdsQmluZEJ1ZmZlcgAHA2VudgxnbEJ1ZmZlckRhdGEABgNlbnYPZ2xCdWZmZXJTdWJEYXRhAAYDZW52D2dsRGVsZXRlQnVmZmVycwAHA2VudhpnbERpc2FibGVWZXJ0ZXhBdHRyaWJBcnJheQAAA2Vudg9nbERlbGV0ZVByb2dyYW0AAANlbnYKZ2xWaWV3cG9ydAAGA2VudglnbFNjaXNzb3IABgNlbnYLZ2xVbmlmb3JtMWkABwNlbnYWZ2xWZXJ0ZXhBdHRyaWJJUG9pbnRlcgAFA2VudhVnbFZlcnRleEF0dHJpYlBvaW50ZXIABANlbnYVZ2xWZXJ0ZXhBdHRyaWJEaXZpc29yAAcDZW52GWdsRW5hYmxlVmVydGV4QXR0cmliQXJyYXkAAANlbnYMZ2xVbmlmb3JtMWZ2AAMDZW52DGdsVW5pZm9ybTJmdgADA2VudgxnbFVuaWZvcm0zZnYAAwNlbnYMZ2xVbmlmb3JtNGZ2AAMDZW52DGdsVW5pZm9ybTFpdgADA2VudgxnbFVuaWZvcm0yaXYAAwNlbnYMZ2xVbmlmb3JtM2l2AAMDZW52DGdsVW5pZm9ybTRpdgADA2VudhJnbFVuaWZvcm1NYXRyaXg0ZnYABgNlbnYMZ2xDbGVhckNvbG9yABgDZW52DWdsQ2xlYXJEZXB0aGYAGQNlbnYOZ2xDbGVhclN0ZW5jaWwAAANlbnYHZ2xDbGVhcgAAA2VudgxnbFJlYWRCdWZmZXIAAANlbnYRZ2xCbGl0RnJhbWVidWZmZXIAGgNlbnYXZ2xEcmF3RWxlbWVudHNJbnN0YW5jZWQABQNlbnYOZ2xDcmVhdGVTaGFkZXIACgNlbnYOZ2xTaGFkZXJTb3VyY2UABgNlbnYPZ2xDb21waWxlU2hhZGVyAAADZW52DWdsR2V0U2hhZGVyaXYAAwNlbnYSZ2xHZXRTaGFkZXJJbmZvTG9nAAYDZW52EnNhcHBfc2V0X2NsaXBib2FyZAAHA2VudhJmc19nZXRfYnVmZmVyX3NpemUACgNlbnYOZnNfdGFrZV9idWZmZXIAAwNlbnYUc2FwcF9zZXRfY3Vyc29yX2dyYWIAAANlbnYPc2FwcF9zZXRfY3Vyc29yAAcDZW52E3NhcHBfc2V0X2Z1bGxzY3JlZW4AAAOqCqgKAwMJBgIAAwkDAQkDBwkDAAIDFwoABwAHABYXFhUVAhYFFQYWBQkHAxsJHAkcCQEAAQABAQEGBQAAAAAAAAAAAAAAAAADAAAAAAAHAAAAAAAAAAAAAAAAAAUDBwcCAwIHBwIHBwcJCQAJAwIAAwkdBwcAAwQHBQcFBwUHBQMEAwQDBAwLCQsLCwwMAwMDAwEDAwYGBgMHBwcDAxcEBAQEFxcXBgQDBwcHBwkJCwIMDAIHCwseCwsfCwsMByAJDAICCQICAgIMAgIJAgcCAwMKCgkMLine truncated
|
||||||
(function () {
|
(function () {
|
||||||
var realFetch = window.fetch ? window.fetch.bind(window) : null;
|
var realFetch = window.fetch ? window.fetch.bind(window) : null;
|
||||||
window.fetch = function (url, opts) {
|
window.fetch = function (url, opts) {
|
||||||
|
|||||||
+11
-1
@@ -54,14 +54,24 @@ pub enum Sfx {
|
|||||||
Convert,
|
Convert,
|
||||||
Slap,
|
Slap,
|
||||||
Punch,
|
Punch,
|
||||||
|
// Crookfinger's voice: squeaky syllables
|
||||||
|
Voice1,
|
||||||
|
Voice2,
|
||||||
|
Voice3,
|
||||||
|
Voice4,
|
||||||
|
Voice5,
|
||||||
|
Voice6,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const SFX_ALL: [Sfx; 36] = [
|
pub const VOICE_SYLLABLES: [Sfx; 6] = [Sfx::Voice1, Sfx::Voice2, Sfx::Voice3, Sfx::Voice4, Sfx::Voice5, Sfx::Voice6];
|
||||||
|
|
||||||
|
pub const SFX_ALL: [Sfx; 42] = [
|
||||||
Sfx::Dig, Sfx::GoldDig, Sfx::DigDone, Sfx::Claim, Sfx::Deposit, Sfx::Build, Sfx::Sell, Sfx::MeleeHit,
|
Sfx::Dig, Sfx::GoldDig, Sfx::DigDone, Sfx::Claim, Sfx::Deposit, Sfx::Build, Sfx::Sell, Sfx::MeleeHit,
|
||||||
Sfx::Shoot, Sfx::Death, Sfx::HeroDeath, Sfx::Lightning, Sfx::Heal, Sfx::Summon, Sfx::PortalArrive,
|
Sfx::Shoot, Sfx::Death, Sfx::HeroDeath, Sfx::Lightning, Sfx::Heal, Sfx::Summon, Sfx::PortalArrive,
|
||||||
Sfx::WaveAlarm, Sfx::WaveStart, Sfx::CoreHit, Sfx::GateHit, Sfx::GateDefenders, Sfx::LevelUp,
|
Sfx::WaveAlarm, Sfx::WaveStart, Sfx::CoreHit, Sfx::GateHit, Sfx::GateDefenders, Sfx::LevelUp,
|
||||||
Sfx::Pickup, Sfx::Drop, Sfx::Payday, Sfx::FlagSet, Sfx::Victory, Sfx::Defeat, Sfx::Click, Sfx::Error,
|
Sfx::Pickup, Sfx::Drop, Sfx::Payday, Sfx::FlagSet, Sfx::Victory, Sfx::Defeat, Sfx::Click, Sfx::Error,
|
||||||
Sfx::Reinforce, Sfx::Cannon, Sfx::Magic, Sfx::Capture, Sfx::Convert, Sfx::Slap, Sfx::Punch,
|
Sfx::Reinforce, Sfx::Cannon, Sfx::Magic, Sfx::Capture, Sfx::Convert, Sfx::Slap, Sfx::Punch,
|
||||||
|
Sfx::Voice1, Sfx::Voice2, Sfx::Voice3, Sfx::Voice4, Sfx::Voice5, Sfx::Voice6,
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Sfx {
|
impl Sfx {
|
||||||
|
|||||||
+19
@@ -30,6 +30,7 @@ mod sound;
|
|||||||
mod spells;
|
mod spells;
|
||||||
mod touch;
|
mod touch;
|
||||||
mod ui;
|
mod ui;
|
||||||
|
mod voice;
|
||||||
mod wilds;
|
mod wilds;
|
||||||
mod xm;
|
mod xm;
|
||||||
|
|
||||||
@@ -402,6 +403,8 @@ async fn main() {
|
|||||||
// until the mouse has really moved its position is (0, 0): no edge scrolling before that
|
// until the mouse has really moved its position is (0, 0): no edge scrolling before that
|
||||||
let mut mouse_moved = false; // automatic mode: touch interface after a touch, desktop after a mouse click
|
let mut mouse_moved = false; // automatic mode: touch interface after a touch, desktop after a mouse click
|
||||||
let mut autosave_t = 0.0f32;
|
let mut autosave_t = 0.0f32;
|
||||||
|
let mut voice = voice::Voice::new();
|
||||||
|
let mut was_briefing = false;
|
||||||
let mut cache_t = 0.0f32;
|
let mut cache_t = 0.0f32;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@@ -1028,8 +1031,24 @@ async fn main() {
|
|||||||
);
|
);
|
||||||
music.update(&app.game, on_title, (app.cam.tx, app.cam.tz));
|
music.update(&app.game, on_title, (app.cam.tx, app.cam.tz));
|
||||||
for m in app.game.msgs.drain(..) {
|
for m in app.game.msgs.drain(..) {
|
||||||
|
if app.settings.voice && voice::speaks(m.key) {
|
||||||
|
voice.say(&i18n::trf(m.key, &m.args), now, 12);
|
||||||
|
}
|
||||||
ui.log.push((m, now));
|
ui.log.push((m, now));
|
||||||
}
|
}
|
||||||
|
// a briefing opens: Crookfinger tells the story
|
||||||
|
let briefing = matches!(menu.page, Some(Page::Briefing { .. }));
|
||||||
|
if briefing && !was_briefing && app.settings.voice {
|
||||||
|
if let Some(Page::Briefing { mission, .. }) = menu.page {
|
||||||
|
let key = campaign::missions().get(mission).map(|d| format!("camp.{}.story", d.key)).unwrap_or_default();
|
||||||
|
voice.say(&i18n::tr(&key), now, 22);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !briefing && was_briefing {
|
||||||
|
voice.stop();
|
||||||
|
}
|
||||||
|
was_briefing = briefing;
|
||||||
|
voice.update(now, if app.settings.muted { 0.0 } else { app.settings.volume });
|
||||||
|
|
||||||
draw_frame(&mut app, &gfx, &mut r3d, &mut ui, &mut menu, &cloud, &inp, (mx, my), touch_mode, now);
|
draw_frame(&mut app, &gfx, &mut r3d, &mut ui, &mut menu, &cloud, &inp, (mx, my), touch_mode, now);
|
||||||
next_frame().await
|
next_frame().await
|
||||||
|
|||||||
+5
-1
@@ -50,12 +50,13 @@ pub enum Item {
|
|||||||
GameSpeed,
|
GameSpeed,
|
||||||
Hints,
|
Hints,
|
||||||
TouchUi,
|
TouchUi,
|
||||||
|
Voice,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_items(tab: usize) -> &'static [Item] {
|
fn tab_items(tab: usize) -> &'static [Item] {
|
||||||
use Item::*;
|
use Item::*;
|
||||||
match tab {
|
match tab {
|
||||||
0 => &[Language, Volume, MusicVolume, Mute],
|
0 => &[Language, Volume, MusicVolume, Mute, Voice],
|
||||||
1 => &[RotateSpeed, ZoomSpeed, EdgeScroll, InvertMouse],
|
1 => &[RotateSpeed, ZoomSpeed, EdgeScroll, InvertMouse],
|
||||||
2 => &[Outlines, WallDetail, Lights, UiScale, RenderScale],
|
2 => &[Outlines, WallDetail, Lights, UiScale, RenderScale],
|
||||||
3 => &[GameSpeed, Hints, TouchUi],
|
3 => &[GameSpeed, Hints, TouchUi],
|
||||||
@@ -81,6 +82,7 @@ fn item_label(it: Item) -> String {
|
|||||||
Item::GameSpeed => "set.game_speed",
|
Item::GameSpeed => "set.game_speed",
|
||||||
Item::Hints => "set.hints",
|
Item::Hints => "set.hints",
|
||||||
Item::TouchUi => "set.touch_ui",
|
Item::TouchUi => "set.touch_ui",
|
||||||
|
Item::Voice => "set.voice",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +100,7 @@ fn item_value(it: Item, s: &Settings) -> String {
|
|||||||
Item::Volume => pct(s.volume),
|
Item::Volume => pct(s.volume),
|
||||||
Item::MusicVolume => pct(s.music_volume),
|
Item::MusicVolume => pct(s.music_volume),
|
||||||
Item::Mute => onoff(s.muted),
|
Item::Mute => onoff(s.muted),
|
||||||
|
Item::Voice => onoff(s.voice),
|
||||||
Item::RotateSpeed => pct(s.rotate_speed),
|
Item::RotateSpeed => pct(s.rotate_speed),
|
||||||
Item::ZoomSpeed => pct(s.zoom_speed),
|
Item::ZoomSpeed => pct(s.zoom_speed),
|
||||||
Item::EdgeScroll => onoff(s.edge_scroll),
|
Item::EdgeScroll => onoff(s.edge_scroll),
|
||||||
@@ -155,6 +158,7 @@ fn item_change(it: Item, s: &mut Settings, d: i32) {
|
|||||||
Item::Volume => s.volume = step(s.volume, d, 0.0, 1.0, 0.1),
|
Item::Volume => s.volume = step(s.volume, d, 0.0, 1.0, 0.1),
|
||||||
Item::MusicVolume => s.music_volume = step(s.music_volume, d, 0.0, 1.0, 0.1),
|
Item::MusicVolume => s.music_volume = step(s.music_volume, d, 0.0, 1.0, 0.1),
|
||||||
Item::Mute => s.muted = !s.muted,
|
Item::Mute => s.muted = !s.muted,
|
||||||
|
Item::Voice => s.voice = !s.voice,
|
||||||
Item::RotateSpeed => s.rotate_speed = step(s.rotate_speed, d, 0.25, 2.5, 0.25),
|
Item::RotateSpeed => s.rotate_speed = step(s.rotate_speed, d, 0.25, 2.5, 0.25),
|
||||||
Item::ZoomSpeed => s.zoom_speed = step(s.zoom_speed, d, 0.25, 2.5, 0.25),
|
Item::ZoomSpeed => s.zoom_speed = step(s.zoom_speed, d, 0.25, 2.5, 0.25),
|
||||||
Item::EdgeScroll => s.edge_scroll = !s.edge_scroll,
|
Item::EdgeScroll => s.edge_scroll = !s.edge_scroll,
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ pub struct Settings {
|
|||||||
pub volume: f32,
|
pub volume: f32,
|
||||||
pub music_volume: f32,
|
pub music_volume: f32,
|
||||||
pub muted: bool,
|
pub muted: bool,
|
||||||
|
pub voice: bool, // Crookfinger's babbling voice
|
||||||
// camera
|
// camera
|
||||||
pub rotate_speed: f32,
|
pub rotate_speed: f32,
|
||||||
pub zoom_speed: f32,
|
pub zoom_speed: f32,
|
||||||
@@ -165,6 +166,7 @@ impl Default for Settings {
|
|||||||
volume: 0.7,
|
volume: 0.7,
|
||||||
music_volume: 0.6,
|
music_volume: 0.6,
|
||||||
muted: false,
|
muted: false,
|
||||||
|
voice: true,
|
||||||
rotate_speed: 1.0,
|
rotate_speed: 1.0,
|
||||||
zoom_speed: 1.0,
|
zoom_speed: 1.0,
|
||||||
edge_scroll: true,
|
edge_scroll: true,
|
||||||
|
|||||||
@@ -157,6 +157,13 @@ fn def(s: Sfx, r: f32) -> Vec<f32> {
|
|||||||
Sfx::Convert => l(&arp(Saw, &[220.0, 262.0, 330.0, 440.0], 0.1, 0.22), 47),
|
Sfx::Convert => l(&arp(Saw, &[220.0, 262.0, 330.0, 440.0], 0.1, 0.22), 47),
|
||||||
Sfx::Slap => mix(vec![l(&[seg(Noise, 5000.0, 2500.0, 0.06, 0.5, 40.0)], 48), l(&[seg(Square(0.5), 900.0, 500.0, 0.05, 0.12, 30.0)], 49)]),
|
Sfx::Slap => mix(vec![l(&[seg(Noise, 5000.0, 2500.0, 0.06, 0.5, 40.0)], 48), l(&[seg(Square(0.5), 900.0, 500.0, 0.05, 0.12, 30.0)], 49)]),
|
||||||
Sfx::Punch => mix(vec![l(&[seg(Tri, 160.0, 60.0, 0.15, 0.8, 18.0)], 50), l(&[seg(Noise, 3000.0, 800.0, 0.08, 0.4, 30.0)], 51)]),
|
Sfx::Punch => mix(vec![l(&[seg(Tri, 160.0, 60.0, 0.15, 0.8, 18.0)], 50), l(&[seg(Noise, 3000.0, 800.0, 0.08, 0.4, 30.0)], 51)]),
|
||||||
|
// Crookfinger's syllables: a nasal squeak gliding up or down, some with a hissy consonant
|
||||||
|
Sfx::Voice1 => mix(vec![l(&[vib(seg(Square(0.2), 620.0, 760.0, 0.08, 0.16, 6.0), 0.04)], 60), l(&[seg(Noise, 6000.0, 4000.0, 0.02, 0.05, 40.0)], 61)]),
|
||||||
|
Sfx::Voice2 => l(&[vib(seg(Square(0.18), 780.0, 600.0, 0.09, 0.16, 6.0), 0.05)], 62),
|
||||||
|
Sfx::Voice3 => mix(vec![l(&[vib(seg(Square(0.22), 520.0, 640.0, 0.07, 0.15, 7.0), 0.03)], 63), l(&[seg(Tri, 260.0, 300.0, 0.07, 0.12, 8.0)], 64)]),
|
||||||
|
Sfx::Voice4 => l(&[seg(Square(0.15), 900.0, 980.0, 0.05, 0.13, 10.0), vib(seg(Square(0.2), 700.0, 560.0, 0.06, 0.14, 8.0), 0.04)], 65),
|
||||||
|
Sfx::Voice5 => mix(vec![l(&[vib(seg(Square(0.25), 470.0, 560.0, 0.1, 0.15, 5.0), 0.06)], 66), l(&[seg(Noise, 3000.0, 2000.0, 0.025, 0.06, 30.0)], 67)]),
|
||||||
|
Sfx::Voice6 => l(&[vib(seg(Square(0.2), 680.0, 820.0, 0.06, 0.15, 9.0), 0.05), seg(Square(0.2), 820.0, 700.0, 0.04, 0.12, 12.0)], 68),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
// Crookfinger's voice: he "speaks" in a squeaky made-up imp language - short synthesised
|
||||||
|
// syllables in the rhythm of the text (the text itself is shown as usual).
|
||||||
|
|
||||||
|
use crate::audio;
|
||||||
|
use crate::game::VOICE_SYLLABLES;
|
||||||
|
|
||||||
|
pub struct Voice {
|
||||||
|
queue: Vec<(f64, usize)>, // time, sound index
|
||||||
|
busy_until: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
// which messages Crookfinger says out loud
|
||||||
|
pub fn speaks(key: &str) -> bool {
|
||||||
|
key.starts_with("hint.")
|
||||||
|
|| key.starts_with("msg.wave_warn")
|
||||||
|
|| matches!(
|
||||||
|
key,
|
||||||
|
"msg.payday_soon" | "msg.researched" | "msg.mission_start" | "msg.mission_won" | "msg.rune_chest" | "msg.trap_made" | "msg.revenant_rises" | "msg.altar_gift" | "msg.altar_blessing" | "msg.brawl" | "msg.alarm" | "msg.creature_freed" | "msg.spring_taken"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Voice {
|
||||||
|
pub fn new() -> Voice {
|
||||||
|
Voice { queue: Vec::new(), busy_until: 0.0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
// babble a text: one syllable per vowel group, pauses at spaces and punctuation
|
||||||
|
pub fn say(&mut self, text: &str, now: f64, max_syllables: usize) {
|
||||||
|
if now < self.busy_until {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut t = now + 0.05;
|
||||||
|
let mut n = 0;
|
||||||
|
let mut h: u32 = 2166136261;
|
||||||
|
let mut in_vowel = false;
|
||||||
|
for ch in text.chars() {
|
||||||
|
h = (h ^ ch as u32).wrapping_mul(16777619);
|
||||||
|
let vowel = "aeiouäöüyAEIOUÄÖÜY".contains(ch);
|
||||||
|
if vowel && !in_vowel {
|
||||||
|
let v = VOICE_SYLLABLES[(h >> 7) as usize % VOICE_SYLLABLES.len()];
|
||||||
|
self.queue.push((t, v.index()));
|
||||||
|
t += 0.085 + ((h >> 13) % 4) as f64 * 0.012;
|
||||||
|
n += 1;
|
||||||
|
if n >= max_syllables {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in_vowel = vowel;
|
||||||
|
match ch {
|
||||||
|
' ' => t += 0.035,
|
||||||
|
',' | ';' | '-' => t += 0.14,
|
||||||
|
'.' | '!' | '?' => t += 0.28,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.busy_until = t + 4.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, now: f64, volume: f32) {
|
||||||
|
let due: Vec<usize> = self.queue.iter().filter(|q| q.0 <= now).map(|q| q.1).collect();
|
||||||
|
self.queue.retain(|q| q.0 > now);
|
||||||
|
for i in due {
|
||||||
|
audio::play_sfx(i, 0.75 * volume.max(0.0), 0.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stop(&mut self) {
|
||||||
|
self.queue.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in new issue
Block a user