JavaScript must be enabled to play.
Browser lacks capabilities required to play.
Upgrade or switch to another browser.
Loading…
<<cacheaudio "track01" setup.AudioPath+"track01.ogg">> <<waitforaudio>> <<cacheaudio "track02" setup.AudioPath+"track02.ogg">> <<cacheaudio "track03" setup.AudioPath+"track03.ogg">> <<cacheaudio "track04" setup.AudioPath+"track04.ogg">> <<cacheaudio "track05" setup.AudioPath+"track05.ogg">> <<cacheaudio "track06" setup.AudioPath+"track06.ogg">> <<cacheaudio "track07" setup.AudioPath+"track07.ogg">> <<cacheaudio "track08" setup.AudioPath+"track08.ogg">> <<cacheaudio "track09" setup.AudioPath+"track09.ogg">> <<cacheaudio "track10" setup.AudioPath+"track10.ogg">> <<cacheaudio "track11" setup.AudioPath+"track11.ogg">> <<cacheaudio "track12" setup.AudioPath+"track12.ogg">> <<cacheaudio "track13" setup.AudioPath+"track13.ogg">> <<cacheaudio "track14" setup.AudioPath+"track14.ogg">> <<cacheaudio "track15" setup.AudioPath+"track15.ogg">> <<cacheaudio "track16" setup.AudioPath+"track16.ogg">> <<cacheaudio "track17" setup.AudioPath+"track17.ogg">> <<cacheaudio "track18" setup.AudioPath+"track18.ogg">> <<cacheaudio "track19" setup.AudioPath+"track19.ogg">> <<cacheaudio "track20" setup.AudioPath+"track20.ogg">> <<cacheaudio "track21" setup.AudioPath+"track21.ogg">> <<cacheaudio "track22" setup.AudioPath+"track22.ogg">> <<cacheaudio "track23" setup.AudioPath+"track23.ogg">> <<cacheaudio "track24" setup.AudioPath+"track24.ogg">> <<cacheaudio "track25" setup.AudioPath+"track25.ogg">>
<<for _i=0;_i isnot tags().length;_i++>> <<switch tags()[_i]>> <<case "black">> <<run $('body').css('background', 'var(--bg-body)');>> <<case "white">> <<run $('body').css('background', 'white');>> <<case "meetingroom">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg/meetingroom01.webp');>> <<case "bedroom">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg/bedroom02.webp)');>> <<case "bg-town">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg-town.png)');>> <<case "bg-temple">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/temple/coupleroom.png)');>> <<case "dungeon">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg/dungeon01.webp)');>> <<case "quest">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg/qwall.webp)');>> <<case "summon">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg/temple01.webp)');>> <<case "storage">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg/storage01.webp)');>> <<case "codextest">> <<run $('body').css('background-image', 'url(' + setup.ImagePath + '/ui/bg/oldpaper.webp)');>> <<default>> <<run $('body').css('background', 'var(--bg-body)');>> <</switch>> <</for>>
<<set _nowweek = Math.max(Math.trunc($times.total / 7), 1)>> <<BattleWaves _nowweek>> <<set $battle.captures = generateCaptures(_nowweek)>> <<include "Battle_CSS">> <<include "Battle_Script">> <div class="battle-group" id="scalediv"> <div class="topbattle"> <div class="game-container"> <canvas id="gameCanvas"></canvas> <<do tag "gridArea">> <div class="grid-area" id="gridArea"> <<set _slotmax = $buildingEffects.slots.bastion >> <<for _row = 0; _row < 3; _row++>> <<for _col = 0; _col < 7; _col++>> <<set _slotindex = _col * 3 + _row >> <<set _unit = $buildings.hawkridgepass.bastion[_slotindex]>> <<capture _row, _col, _slotindex _unit>> <<if _unit>> <<if _unit.id.startsWith("wall")>><<set _classgrid = "grid-cell wall">> <<elseif _unit.id.startsWith("gate")>><<set _classgrid = "grid-cell gate">> <<else>><<set _classgrid = "grid-cell tower">> <</if>> <<else>> <<set _classgrid = "grid-cell">> <</if>> <div @class="_classgrid" @data-row="_row" @data-col="_col"> <<if !(_unit) && (_slotindex < _slotmax)>> <<link "Build">> <<SimpleUI "Battle_Deploy" 90>> <</link>> <</if>> <<if (_unit) && (_slotindex < _slotmax)>> <<set _slotimg = setup.bdunitdata[_unit.id].img>> <<set _slotlv = "lv"+ _unit.lv >> <<if _unit.id.startsWith("tower")>> <<set _arrowConfig = getunitdata(_unit.id, _unit.lv, 'arrowRef') >><<set _unitConfig = setup.arrow[_arrowConfig.type]['levels'][_arrowConfig.lv]['behavior']>> <<set _towerlv = "lv"+ _unit.lv >> <<csslink "<<widthframe _slotimg _slotlv 4>>" "towerlist">> <<SimpleUI "Battle_Deploy" 90>> <</csslink>> <<else>> <<csslink "<<widthframe _slotimg _slotlv 4>>" "towerlist">> <<SimpleUI "Battle_Deploy" 90>> <</csslink>> <</if>> <</if>> </div> <</capture>> <</for>> <</for>> </div> <</do>> </div> </div> <div class="battle-container"> <div class="battle-panel module-border"> <div class="module-header">Dark Legion</div> <div class="stat-bar"> <span class="stat-name">BossHP:</span> <div class="bar-container"> <div class="bar health-bar" id="attacker-health-bar"></div> </div> <span class="stat-value" id="attacker-health-text"></span> </div> <div class="stat-bar"> <span class="stat-name">Attack:</span> <div class="bar-container"> <div class="bar attack-bar" id="attacker-attack-bar"></div> </div> <span class="stat-value" id="attacker-attack-text"></span> </div> <div class="enemies-stats-container"> <div class="enemies-stats" id="enemies-stats"></div> </div> <<include "Battle_Capture">> <div class="battle-log-container module-border"> <div class="module-header">Battle Log</div> <div class="battle-log" id="battle-log"> <div class="log-entry"><h3>Heed This Warning, Noble Commander!</h3> <p>Once the battle commences, no further structures may be erected. Ensure your defenses are fully prepared before pressing the "Start Battle" button!</p> <p><i>(Note: This feature is still under forge in the smithy of development. Some elements remain incomplete and are not flaws of the craft.)</i></p></div> </div> </div> </div> <div class="mid-space"> <<include "Battle_Skillbook">> <div class="module-border"> <div class="module-header">Battle Command</div> <div class="module-grid-1col"> <<cssbutton "🛡️Deploy Defenses" "deploy-battle">> <<SimpleUI "Battle_Deploy" 90>> <</cssbutton>> <<if Flag('battleday')>> <<cssbutton "⚔️Start Battle" "start-battle">> <<if !$battle.gameStarted>> <<dropall $battlebag>> <<addclass ".UItopbar" "hidden">> <<addclass ".bdoperate" "hidden">> <<replace "#start-battle">>In Battle<</replace>> <<run UIBar.hide().stow();>> <<remove "#tiredbattle">> <<run startBattle()>> <</if>> <</cssbutton>> <div id="tiredbattle"> <<button "💔Tired of War">> <<SetFlag 'battleday' false>> <<set $battle.demonattackdeadline = 7 >> <<goto "Battle_UI">> <</button>> <<HoverTip "After click it, close the battle directly, but you can't get loot either, so it's suitable for those who are tired of war!">> <span>❓</span> <</HoverTip>> </div> <<else>> A Calm Day <</if>> </div> </div> <div class="module-border"> <div class="module-header">Aiming Mode</div> <div class="module-grid-1col"> <label><<radiobutton "$battle.defense.aimMode" "nearest" autocheck>>Nearest</label> <label><<radiobutton "$battle.defense.aimMode" "focusBoss" autocheck>>Focus Boss</label> <label><<radiobutton "$battle.defense.aimMode" "oneToOne" autocheck>>One To One</label> </div> </div> </div> <<include "Battle_Right">> </div> </div>
<style> :root { --blood-red: #8b0000; --dark-red: #5c0000; --crimson: #dc143c; --royal-blue: #0a2463; --dark-blue: #05142e; --steel-blue: #4682b4; --gothic-gold: #d4af37; --gothic-dark: var(--bg-body); --gothic-light: #333333; --gothic-accent: #666666; --gothic-text: var(--text-primary); } .mid-space { display: flex; gap: 6px; margin: 0 0.5rem; flex: 1; position: relative; flex-direction: column; } #start-battle { --color-pulse: #5d0000a1; } .enemy-sprite { z-index: 2; position: absolute; image-rendering: -webkit-optimize-contrast; image-rendering: crisp-edges; } .enemy-minion-normal { filter: none; } .enemy-minion-speed { filter: hue-rotate(120deg); } .enemy-minion-shield { filter: hue-rotate(240deg); } .enemy-boss-elite { filter: hue-rotate(300deg); } .enemy-boss-boss { filter: hue-rotate(0deg) brightness(1.2); } .enemy-boss-commander { filter: none; } .game-container { width: 100%; height: 320px; background-image: url("images/ui/bg/path.png"); background-repeat: repeat-x; background-position: 0 0; background-size: auto 320px; position: relative; } .game-container::before { top: 0; } .game-container::after { bottom: 0; } #gameCanvas { position: absolute; top: 0; left: 0; z-index: 1; pointer-events: none; width: 100%; height: 320px;} .grid-area { width: 448px; height: 192px; display: grid; grid-template-columns: repeat(7, 64px); grid-template-rows: repeat(3, 64px); gap: 0; position: absolute; right: 0; top: 64px; z-index: 10; } .grid-cell { display: flex; width: 64px; height: 64px; border: 1px solid #cccccc3d; box-sizing: border-box; background-color: var(--color-t); position: relative; align-items: flex-end; justify-content: center; } .grid-rock { width: 64px; height: 64px; border: 1px solid var(--bg-surface); box-sizing: border-box; background-color:rgb(0, 0, 0); position: relative; } .grid-cell.wall { } .grid-cell.gate { } .grid-cell:not(.wall):not(.gate):not(.tower) { cursor: pointer; var(--color-t); } .grid-cell:not(.wall):not(.gate):not(.tower):hover { background-color: #d0d0d0; } .grid-cell a { width: 100%; height: 100%; line-height: 0; text-align: center; text-decoration: none; color: #000; z-index: 12; position: relative; } .tower-panel { position: absolute; background-color: rgba(255, 255, 255, 0.9); border: 1px solid #333; padding: 16px; z-index: 13; display: none; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); } .tower-panel button:disabled { background-color: #ccc; cursor: not-allowed; } .hp-display { position: absolute; color: white; background: rgba(0, 0, 0, 0.5); padding: 2px 5px; font-size: 12px; z-index: 5; } .aim-mode { display: grid; color: var(--text-primary); } .aim-mode select { padding: 5px; font-size: 16px; } .item { cursor: pointer; } .defender-troop { pointer-events: none; position: absolute; z-index: 3; background: [img[setup.ImagePath + "ui/icon/cavalrymen.png"]]; } .defender-troop-hp { position: absolute; color: white; background: rgba(0, 0, 0, 0.5); padding: 2px 5px; font-size: 16px; z-index: 5; } #towerlist img { object-fit: contain; height: 64px; width: 64px; } .battle-group { flex-direction: column; flex: 1; transform-origin: top left; animation: fadeIn 0.3s ease; } .Gcontainer{ width: 100%; background-color: var(--bg-body); color: var(--text-primary); margin: 0; padding: 0px; } .battle-container { max-width: 1920px; min-width: 1664px; width: 100%; margin: 0 auto; flex: 1; display: flex; justify-content: space-between; box-shadow: 0 0 20px rgba(0, 0, 0, 0.7); } .army { width: 45%; display: flex; flex-direction: column; align-items: center; box-sizing: border-box; } .stats { width: 100%; background-color: rgba(0, 0, 0, 0.3); padding: 15px; border-radius: 5px; border: 1px solid var(--gothic-accent); } .stat-bar { display: flex; align-items: center; margin-bottom: 10px; } .stat-name { margin-left: 8px; width: 88px; font-weight: bold; color: var(--gothic-gold); text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.7); } .bar-container { height: 20px; width: calc(100% - 150px); background-color: var(--gothic-light); border-radius: 3px; overflow: hidden; border: 1px solid var(--gothic-accent); } .bar { height: 100%; transition: width 0.5s ease; } .health-bar { background: linear-gradient(to right, var(--dark-red), var(--crimson)); } .attack-bar { background: linear-gradient(to right, #8a6d00, var(--gothic-gold)); } .stat-value { width: 60px; text-align: right; font-family: 'Courier New', monospace; color: var(--gothic-text); } .towers { display: flex; gap: 10px; width: 100%; justify-content: center; } .generals { grid-template-columns: repeat(auto-fill, minmax(clamp(80px, 15%, 192px), 1fr)); display: grid; gap: 8px; margin-bottom: 8px; width: 100%; } .general { flex: 1; max-width: 128px; height: 112px; width: 112px; background: linear-gradient(to bottom, var(--dark-red), var(--blood-red)); border-radius: 5px; position: relative; cursor: pointer; transition: transform 0.2s; } .defender .general { width: 112px; background: linear-gradient(to bottom, var(--dark-blue), var(--royal-blue)); } .general.commander::after { font-size: 32px; } .enemies-stats { width: 100%; display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; } .enemies-stat { font-size: 14px; padding: 3px 6px; border: 1px solid var(--cm-border); transition: all 0.2s; } .enemies-stat-name { font-weight: bold; color: #D4A017; flex: 1; white-space: nowrap; } .enemies-stat-value { width: 100%; text-align: right; color: var(--text-primary); } .enemies-stats-container { display: grid; grid-template-columns: repeat(1, 1fr); width: 100%; gap: 8px; margin: 0px auto; } .soldiers { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; margin: 10px 0; max-width: 100%; } .soldier { width: 30px; height: 50px; background: linear-gradient(to bottom, var(--dark-red), var(--blood-red)); border: 1px solid var(--crimson); position: relative; } .defender .soldier { background: linear-gradient(to bottom, var(--dark-blue), var(--royal-blue)); border: 1px solid var(--steel-blue); } .soldier::before { content: ''; position: absolute; top: 10px; left: 50%; transform: translateX(-50%); width: 15px; height: 15px; background-color: var(--gothic-text); border-radius: 50%; opacity: 0.3; } .soldier::after { content: ''; position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); width: 20px; height: 10px; background-color: rgba(0, 0, 0, 0.5); } .captives { display: flex; gap: 10px; justify-content: center; margin: 0px 0; } .action-button0 { flex: 1; padding: 8px; font-size: 12px; font-family: 'Times New Roman', serif; background: linear-gradient(to bottom, #3a3a3a, #1a1a1a); color: var(--gothic-gold); border: 1px solid var(--gothic-gold); border-radius: 3px; text-align: center; cursor: pointer; transition: all 0.3s; } .action-button0:hover { background: linear-gradient(to bottom, #4a4a4a, #2a2a2a); } .controls { background-color: rgba(0, 0, 0, 0.8); padding: 20px; border: 2px solid var(--gothic-gold); border-radius: 10px; display: flex; gap: 10px; flex-direction: column; } .battle-log-container { min-height: 64px; height: calc(100vh - 716px); flex: 1; width: 100%; display: flex; flex-direction: column; } .battle-log { width: 100%; overflow-y: auto; font-size: 14px; } .log-entry { margin-bottom: 8px; border-bottom: 1px solid #3C2F2F; opacity: 50; animation: fadeIn 0.5s forwards; } .battle-log::-webkit-scrollbar { width: 8px; } .battle-log::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.5); } .battle-log::-webkit-scrollbar-thumb { background: #D4A017; border-radius: 4px; } .topbattle { max-width: 1920px; min-width: 1664px; width: 100%; margin: 0 auto; height: 320px; } @media (max-height: 940px) { .module-header { font-size: 14px; margin-bottom: 6px; padding-bottom: 0px; } .module-border { background: #151515; border-radius: 4px; padding: 4px; } } </style>
<style> .capture { min-height: 140px; direction: rtl; grid-template-columns: repeat(auto-fill, minmax(110px, 1fr)); } .hostage-cell { width: 110px; position: relative; height: 140px; background: #151515; border: 1px solid #3a3a3a; border-radius: 4px; display: flex; flex-direction: column; align-items: center; justify-content: center; transition: all 0.5s ease; overflow: hidden; } .hostage-cell::before { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: repeating-linear-gradient( 90deg, transparent, transparent 8px, rgba(90, 90, 90, 0.3) 8px, rgba(90, 90, 90, 0.3) 10px ); z-index: 1; pointer-events: none; } .hostage-cell::after { content: ""; position: absolute; top: 0; left: 0; right: 0; bottom: 0; border: 1px solid #5a5a5a; border-radius: 4px; opacity: 0.3; pointer-events: none; } .hostage-content { display: flex; flex-direction: column; align-items: center; justify-content: center; z-index: 2; position: relative; width: 100%; height: 100%; } .hostage-type { font-size: 12px; color: #c0c0c0; text-align: center; margin-bottom: 3px; } .hostage-status { font-size: 10px; padding: 2px 6px; border-radius: 3px; background: #8b0000; color: #fff; } .hostage-cell.rescued { border: 1px solid #d4af37; background: #2a1a1a; color: #d4af37; box-shadow: 0 0 5px rgba(212, 175, 55, 0.3); } .hostage-cell.rescued::before { opacity: 0.1; } .hostage-cell.rescued .hostage-status { background: #005a00; } </style> <div class="module-border"> <div class="module-header"> <span>Prisoner Convoy</span> </div> <div class="module-grid-list capture"> <<do tag "capture">> <<if Flag('battleday')>> <<for _i, _capture range $battle.captures>> <<capture _capture>> <div @class="'hostage-cell ' + (_capture.stat =='Rescued'?'rescued':'')"> <div class="hostage-content"> <div class="hostage-type">_capture.type</div> <div class="hostage-status">_capture.stat</div> </div> </div> <</capture>> <</for>><</if>> <</do>> </div> </div>
<style> :root { --color-keep-a: #936600; } .keep-container { height: 100%; max-width: 87.5rem; margin: 0 auto; display: flex; gap: 1rem; } .keep-left-panel { flex: 1; display: flex; flex-direction: column; gap: 1rem; } .keep-header { text-align: center; padding: 0.75rem; border-bottom: 2px solid var(--color-keep-a); } .keep-title { color: var(--color-keep-a); text-transform: uppercase; letter-spacing: 0.1rem; } .keep-slots-section { background: #1a1a1a; border-radius: 0.5rem; padding: 0.75rem; border: 1px solid #333; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3); } .keep-section-title { font-size: 1.2rem; color: var(--color-keep-a); margin-bottom: 0.75rem; padding-bottom: 0.25rem; border-bottom: 1px solid #333; } .keep-slots-grid { grid-auto-flow: column; display: grid; grid-template-columns: repeat(7, 1fr); grid-template-rows: repeat(3, 1fr); gap: 0.5rem; aspect-ratio: 7/3; } .keep-slot { aspect-ratio: 1 / 1; background: #262626; border: 2px dashed #444; border-radius: 0.25rem; display: flex; justify-content: center; cursor: pointer; transition: all 0.3s ease; position: relative; } .keep-slot:hover { border-color: var(--color-keep-a); background: #2a2a2a; } .keep-slot.has-building { background: #1a1a1a; } .keep-slot.selected { border: 2px solid #694400eb; box-shadow: 0 0 0.75rem rgba(212, 175, 55, 0.5); } .keep-slot-building { text-align: center; align-content: center; } .keep-slot-name { font-size: 0.7rem; font-weight: 600; color: #e0e0e0; line-height: 1.1; margin-bottom: 0.15rem; } .keep-slot-type { font-size: 0.6rem; color: var(--color-keep-a); background: rgba(212, 175, 55, 0.1); padding: 0.1rem 0.3rem; border-radius: 0.25rem; display: inline-block; } .keep-slot-level { position: absolute; top: 0.15rem; right: 0.15rem; background: var(--color-keep-a); color: #0d0d0d; font-size: 0.6rem; font-weight: bold; width: 1rem; height: 1rem; border-radius: 50%; display: flex; align-items: center; justify-content: center; } .keep-selection-section { overflow-y: auto; height: 100%; background: #1a1a1a; border-radius: 0.5rem; padding: 0.75rem; border: 1px solid #333; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3); } .keep-building-selection { overflow-y: auto; background-color: var(--bg-body); display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.5rem; } .keep-building-card { background: #1a1a1a; border-radius: 0.5rem; overflow: hidden; transition: all 0.3s ease; border: 1px solid #333; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3); aspect-ratio: 2/2; display: flex; flex-direction: column; position: relative; cursor: pointer; } .keep-building-card:hover { box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.5), 0 0 0.75rem rgba(212, 175, 55, 0.2); border-color: var(--color-keep-a); } .keep-building-card.selected { border-color: var(--color-keep-a); box-shadow: 0 0 1rem rgba(212, 175, 55, 0.4); } .keep-building-image { height: 5rem; background: #262626; display: flex; align-items: center; justify-content: center; position: relative; overflow: hidden; border-bottom: 1px solid #333; } .keep-building-image::after { content: ''; position: absolute; top: 0; left: 0; right: 0; bottom: 0; background: linear-gradient(to bottom, transparent 70%, rgba(0, 0, 0, 0.7) 100%); } .keep-building-image-placeholder { font-size: 2rem; color: #444; } .keep-building-info { padding: 0.5rem; flex: 1; display: flex; flex-direction: column; justify-content: space-between; } .keep-building-card .keep-building-name { font-size: 0.8rem; font-weight: 600; margin-bottom: 0.25rem; color: #e0e0e0; line-height: 1.1; } .keep-building-card .keep-building-type { font-size: 0.7rem; color: var(--color-keep-a); background: rgba(212, 175, 55, 0.1); padding: 0.1rem 0.4rem; border-radius: 0.5rem; display: inline-block; align-self: flex-start; } .keep-right-panel { width: 22rem; background: #1a1a1a; border-radius: 0.5rem; padding: 1rem; border: 1px solid #333; box-shadow: 0 0.25rem 0.5rem rgba(0, 0, 0, 0.3); display: flex; flex-direction: column; } .keep-building-details { max-height: calc(100% - 2.5rem); display: flex; flex: 1; flex-direction: column; } .keep-building-details-placeholder { display: flex; align-items: center; justify-content: center; height: 100%; color: #777; font-size: 1.1rem; text-align: center; } .keep-building-large-image { height: 10rem; background: #262626; border-radius: 0.5rem; margin-bottom: 0.75rem; display: flex; align-items: center; justify-content: center; border: 1px solid #333; } .keep-building-large-image-placeholder { } .keep-building-description { margin-bottom: 0.75rem; line-height: 1.4; color: #b0b0b0; font-size: 0.9rem; } .keep-resource-list { margin-bottom: 0.75rem; } .keep-resource-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.25rem; } .keep-resource-item { display: flex; justify-content: space-between; padding: 0.25rem 0.5rem; background: rgba(40, 40, 40, 0.5); border-radius: 0.25rem; font-size: 0.8rem; } .keep-resource-name { color: #e0e0e0; } .keep-resource-value { color: var(--color-keep-a); font-weight: 600; } .keep-upgrade-table { width: 100%; border-collapse: collapse; margin-bottom: 0.75rem; font-size: 0.8rem; } .keep-upgrade-table th { background: rgba(212, 175, 55, 0.2); color: var(--color-keep-a); padding: 0.5rem; text-align: left; border: 1px solid #333; } .keep-upgrade-table td { padding: 0.5rem; border: 1px solid #333; color: #e0e0e0; } .keep-current-level { background: rgba(212, 175, 55, 0.1); color: var(--color-keep-a); font-weight: 600; position: relative; } .keep-actions { height: 2.5rem; display: flex; gap: 0.5rem; margin-top: auto; } .keep-button { height: 2.5rem; flex: 1; padding: 0.6rem; border: none; border-radius: 0.5rem; font-weight: 600; cursor: pointer; transition: all 0.3s ease; font-size: 0.85rem; } .keep-button-build { background: green; color: #0d0d0d; } .keep-button-upgrade { background: #2a5a2a; color: #e0e0e0; } .keep-button-demolish { background: #5a2a2a; color: #e0e0e0; } .keep-button-cancel { background: #333; color: #e0e0e0; } .keep-button:hover { opacity: 0.9; } .keep-button:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } .keep-gothic-corner { position: absolute; width: 1.2rem; height: 1.2rem; } .keep-corner-tl { top: 0; left: 0; border-top: 2px solid var(--color-keep-a); border-left: 2px solid var(--color-keep-a); } .keep-corner-tr { top: 0; right: 0; border-top: 2px solid var(--color-keep-a); border-right: 2px solid var(--color-keep-a); } .keep-corner-bl { bottom: 0; left: 0; border-bottom: 2px solid var(--color-keep-a); border-left: 2px solid var(--color-keep-a); } .keep-corner-br { bottom: 0; right: 0; border-bottom: 2px solid var(--color-keep-a); border-right: 2px solid var(--color-keep-a); } @media (max-width: 64rem) { .keep-container { flex-direction: column; } .keep-right-panel { width: 100%; } .keep-building-selection { grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); } } @media (max-width: 48rem) { .keep-building-selection { grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr)); } .keep-title { font-size: 1.7rem; } } @media (max-width: 30rem) { .keep-building-selection { grid-template-columns: repeat(auto-fill, minmax(5rem, 1fr)); } body { padding: 0.5rem; } .keep-actions { flex-direction: column; } } .locked { align-items: center; cursor: auto; } #lvlabel { left: 0rem; position: absolute; color: darkred; font-size: 0.7rem; z-index: 2; display: block; line-height: 1; } .keep-upgrade-box { flex: 1; overflow-y: auto; } .keep-unit-label { justify-self: center; width: 6rem; bottom: 0; position: absolute; } </style> <div class="keep-container"> <!-- Left panel --> <div class="keep-left-panel"> <!-- Header section --> <<do tag "zone">> <<set _currenzone = $buildings["hawkridgepass"]["bastion"]>> <!-- Empty slots grid --> <section class="keep-slots-section"> <h2 class="keep-section-title">Land Units</h2> <div class="keep-slots-grid" id="keepSlotsGrid"> <!-- 3x7 slots will be generated by JavaScript --> <<for _index range 21>> <<set _currenunit = _currenzone[_index] >> <<capture _index _currenunit>> <<if _currenunit>> <<set _unit = $buildings.hawkridgepass.bastion[_index]>> <<set _slotimg = setup.bdunitdata[_unit.id].img>> <<set _slotlv = "lv"+ _unit.lv >> <<link '<div class="keep-slot has-building" @id="_index"> <div class="keep-slot-building"> <div><<widthframe _slotimg _slotlv 4>></div> <div class="keep-unit-label"> <div class="keep-slot-name"><<= getunitdata(_currenunit.id,"name")>></div> <div class="keep-slot-type"></div> </div> <div class="keep-slot-level"><<= _currenunit.lv>></div></div> </div>'>> <<set _showunit = _currenzone[_index] >> <<set _showzone = $buildings["hawkridgepass"]["bastion"]>> <<set _showindex = _index>> <<redo "unit">><<redo "build">> <<removeclass '.keep-slot' 'selected'>><<addclass `"#"+_index` 'selected'>> <</link>> <<elseif _index < $buildingEffects["slots"][_currenzone.id]>> <<link '<div class="keep-slot" style="background-color: var(--bg-body);" @id="_index"></div>'>> <<set _showindex = _index>> <<unset _showunit>> <<set _showzone = $buildings["hawkridgepass"]["bastion"]>> <<redo "unit build">> <<removeclass '.keep-slot' 'selected'>><<addclass `"#"+_index` 'selected'>> <</link>> <<else>> <div class="keep-slot locked"> <!-- Empty slot -->🔒 </div> <</if>> <</capture>> <</for>> </div> </section> <</do>> <!-- Building selection area --> <section class="keep-selection-section"> <<do tag "build">> <<if (_showindex || _showindex==0) && _currenunit == null>> <h2 class="keep-section-title">Optional Units</h2> <div class="keep-building-selection" id="keepBuildingSelection"> <!-- Building cards will be added here by JavaScript when empty slot is selected --> <!-- Building cards --> <<set _chunits = Object.values(setup.bdunitdata) .filter(b => b.type === _currenzone.id)>> <<for _chunit range _chunits>> <<capture _chunit>> <<set _newsr = _chunit[1]["cost"] >> <<link '<div class="keep-building-card" @id="_chunit.id"> <div class="keep-building-image"> <div class="keep-building-image-placeholder"><<showframe _chunit.img lv1 4>></div> </div> <div class="keep-building-info"> <div class="keep-building-name"><<= getunitdata(_chunit.id,"name")>></div> <div class="keep-building-type"><<= _chunit.class>></div> </div> </div>'>> <<set _showunit = {"id": _chunit.id, "lv": 0} >><<redo "unit">> <<removeclass '.keep-building-card' 'selected'>><<addclass `"#"+_chunit.id` 'selected'>> <</link>> <</capture>> <</for>> <<else>> <</if>> <</do>> </section> </div> <!-- Right panel with building details --> <<do tag "unit">> <div class="keep-right-panel"> <<if _showunit && (_showindex || _showindex==0)>> <<set _upgradecost = getunitdata(_showunit.id, (_showunit.lv + 1), "cost") >> <div class="keep-building-details" id="keepBuildingDetails"> <h2 class="keep-building-name"><<= getunitdata(_showunit.id,"name")>></h2> <div class="keep-building-type"></div> <p class="keep-building-description"><<if getzonedata(_currenzone.id,"desc")>><<= getzonedata(_currenzone.id,"desc")>><</if>></p> <<if _showunit.id.startsWith("tower")>> <div class="keep-resource-list"> <h3 class="keep-section-title">Arrow Stat</h3> <<set _arrowConfig = getunitdata(_showunit.id, (_showunit.lv || 1), 'arrowRef') >> <<set _unitConfig = setup.arrow[_arrowConfig.type]['levels'][_arrowConfig.lv]['behavior']>> <div class="keep-resource-grid"> <div class="keep-resource-item"> <span class="keep-resource-name">Type</span> <span class="keep-resource-value"><<= _unitConfig.type>></span> </div> <div class="keep-resource-item"> <span class="keep-resource-name">Level</span> <span class="keep-resource-value"><<= _unitConfig.lv>></span> </div> <div class="keep-resource-item"> <span class="keep-resource-name">Damage</span> <span class="keep-resource-value"><<= _unitConfig['physicalDamage']>></span> </div> <div class="keep-resource-item"> <span class="keep-resource-name">Proj. Speed</span> <span class="keep-resource-value"><<= _unitConfig['projectileSpeed']>></span> </div> <div class="keep-resource-item"> <span class="keep-resource-name">Fire Rate</span> <span class="keep-resource-value"><<= _unitConfig['fireRate'] +"/ s">></span> </div> </div> </div> <</if>> <div class="keep-resource-list"> <h3 class="keep-section-title"><<= _showunit.lv != 0 ? "Upgrade " :"Building ">>Cost</h3> <div class="keep-resource-grid"> <<if _showunit.lv < getunitdata(_showunit.id,"maxlv")>> <<for _res, _amount range _upgradecost>> <div class="keep-resource-item"> <span class="keep-resource-name"><<=_res>></span> <span class="keep-resource-value"><<=_amount>></span> </div> <</for>> <<else>> Maximum Level! <</if>> </div> </div> <h3 class="keep-section-title">Upgrade path</h3> <div class="keep-upgrade-box"> <table class="keep-upgrade-table"> <thead> <tr> <th style="width: 1rem;">Lv</th> <th style="width: 8.25rem;">Effect</th> <th>Cost</th> </tr> </thead> <tbody> <<for _i range getunitdata(_showunit.id,"maxlv")>> <<set _reallv = _i +1 >> <tr @class="(_reallv == _showunit.lv) ? 'keep-current-level' : ''"> <td><<= _reallv>><<if _reallv == _showunit.lv>><div id="lvlabel">curren</div><</if>></td> <td><<= EffectUtils.autoDesc(getunitdata(_showunit.id, _reallv, "effect"))>></td> <td> <<set _upgradecost = getunitdata(_showunit.id, _reallv, "cost") >> <<for _res, _amount range _upgradecost>> <span class="res-icon res-_res.type"><<=_res>> x<<=_amount>>,</span> <</for>> </td> </tr> <</for>> </tbody> </table> </div> </div> <div class="keep-actions"> <<if _showunit && _showunit.lv == 0>> <<link '<div class="keep-button keep-button-build">Build</div>'>><<BuildUnitByIndexId _showindex _showunit.id>><<redo "gridArea">><<run initTowers()>><</link>> <<elseif _showunit>> <<link '<div class="keep-button keep-button-upgrade">Upgrade</div>'>><<UpgradeUnitByIndexId _showindex _showunit.id>><<redo "gridArea">><<run initTowers()>><</link>> <<link '<div class="keep-button keep-button-demolish">Demolish</div>'>><<DemolishUnitByIndexId _showindex _showunit.id>><<redo "gridArea">><<run initTowers()>><</link>> <</if>> <<link '<div class="keep-button keep-button-cancel">Cancel</div>'>><<unset _showunit>><<unset _showindex>><<redo "topbar build zone unit">><</link>> </div> <<else>> <</if>> </div> <</do>> </div>
<style> .character-module { height: 45rem; display: flex; flex-direction: column; width: 60rem; background: #1a1a1a; border: 2px solid #3a3a3a; border-radius: 5px; overflow: hidden; } .content-area { display: flex; flex: 1; } .character-portrait { flex: 1; display: flex; justify-content: center; align-items: center; position: relative; overflow: hidden; border-right: 1px solid #3a3a3a; } .character-image { width: 100%; height: 100%; background-color: var(--bg-body); display: flex; justify-content: center; align-items: center; color: #7a5a5a; } .character-controls { width: 20rem; padding: 20px; display: flex; flex-direction: column; background: var(--bg-body); } .controls-title { text-align: center; margin-bottom: 25px; color: #c9a87a; font-size: 24px; letter-spacing: 2px; position: relative; } .controls-title::after { content: ""; display: block; width: 80%; height: 1px; background: linear-gradient(90deg, transparent, #5a3a3a, transparent); margin: 8px auto 0; } .button-container { display: flex; flex-direction: column; gap: 15px; flex-grow: 1; } .control-button { background: #2a2a2a; border: 1px solid #5a3a3a; color: #c9a87a; padding: 12px; text-align: center; cursor: pointer; font-size: 16px; letter-spacing: 1px; } .control-button:hover { background: #3a2a2a; border-color: #8b0000; } .description-area { margin-top: 1rem; padding: 0.5rem; background: #1a0a0a; border: 1px solid #5a3a3a; height: 10rem; color: #a0a0a0; font-size: 0.85rem; overflow-y: auto; } .attributes-area { border-top: 1px solid #3a3a3a; padding: 1rem; background: var(--bg-body); height: 9.5rem; color: #c9a87a; } .attributes-content { text-align: center; margin-top: 2.5rem; letter-spacing: 1px; } .speech-bubble { position: absolute; top: 20px; right: 20px; background: #1a0a0a; border: 1px solid #5a3a3a; padding: 12px 15px; border-radius: 8px; max-width: 70%; color: #c9a87a; font-size: 14px; box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); } .speech-bubble::after { content: ''; position: absolute; bottom: -10px; left: 20px; border-width: 10px 10px 0; border-style: solid; border-color: #1a0a0a transparent transparent; } .speech-bubble::before { content: ''; position: absolute; bottom: -11px; left: 19px; border-width: 11px 11px 0; border-style: solid; border-color: #5a3a3a transparent transparent; } </style> <div class="character-module"> <div class="content-area"> <div class="character-portrait"> <div class="character-image"> <<NewMonster>> <<run setup.initBodyTraining($addmonster)>> <div class='GetE1img'><<ShowPortrait $addmonster>></div> <<set _CharSay = [ "Lord Baron, thank you for my rescue! I wish to serve in your lands!", "The demons’ chains are broken! Thanks to your timely rescue, I’m saved!", "Your valor will echo across the land! I’ll sing of Lord Baron’s deeds!", "Lord Baron saved me from demon claws! I’ll stay to repay you!", "The demon prison is broken, Lord Baron. I offer my loyalty and strength!" ].random() >> <div class="speech-bubble"> <<= _CharSay>> </div> </div> </div> <div class="character-controls"> <h2 class="controls-title">Captive Handling</h2> <div class="button-container"> <<link "<div class='control-button'>Join</div>">><<AddMonster $addmonster>><<unset $addmonster>><<run popup.close()>><</link>> <<link "<div class='control-button'>Shelter</div>">><<set $domain.monsterpp += 1>><<unset $addmonster>><<run popup.close()>><</link>> <<link "<div class='control-button'>Release</div>">><<unset $addmonster>><<run popup.close()>><</link>> </div> <div class="description-area"><ul> <li>Join: Welcome the monster girl into the temple for further actions.</li> <li>Shelter: Take the monster girl into the barony, counted as population but not actionable.</li> <li>Release: Set free beyond the barony, left to fend for themselves.</li></ul> </div> </div> </div> <div class="attributes-area"> <div class="attributes-content"> <<Infocard $addmonster>> </div> </div> </div>
<style> .UItopbar { width: 100%; position: absolute; } .battle-panel { height: calc(100vh - 332px); width: 43%; display: flex; gap: 6px; flex-direction: column; } .stat-container { background: #151515; border: 1px solid #3a3a3a; border-radius: 4px; padding: 4px 10px; position: relative; } .stat-container::before { content: ""; position: absolute; top: 0; left: 0; width: 3px; height: 100%; background: #d4af37; border-radius: 4px 0 0 4px; } .stat-label { display: flex; justify-content: space-between; margin-bottom: 6px; font-size: 14px; font-weight: bold; } .stat-bar { height: 18px; background: #2a2a2a; border: 1px solid #5a5a5a; border-radius: 9px; position: relative; } .defense-bar { background: linear-gradient(90deg, #005a8b, #0088cc); width: 80%; height: 100%; border-radius: 9px; display: flex; align-items: center; justify-content: flex-end; color: white; font-size: 11px; font-weight: bold; text-shadow: 0 0 2px rgba(0, 0, 0, 0.8); } .morale-bar { background: linear-gradient(90deg, #8b5a00, #cc8800); width: 60%; height: 100%; border-radius: 9px; display: flex; align-items: center; justify-content: flex-end; padding-right: 8px; color: white; font-size: 11px; font-weight: bold; text-shadow: 0 0 2px rgba(0, 0, 0, 0.8); } .generals-grid { display: grid; grid-template-columns: repeat(6, 1fr); gap: 10px; margin-bottom: 10px; } .general-card { background: #151515; border: 1px solid #3a3a3a; border-radius: 4px; padding: 8px; text-align: center; transition: all 0.3s ease; position: relative; display: flex; flex-direction: column; height: 140px; } .general-card:hover { border-color: #d4af37; box-shadow: 0 0 8px rgba(212, 175, 55, 0.5); } .general-card::after { content: ""; position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background: #8b0000; border-radius: 0 0 4px 4px; } .general-portrait { width: 94px; height: 94px; margin: 0 auto 6px; background: #2a2a2a; border: 2px solid #5a5a5a; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #8b8b8b; font-size: 10px; position: relative; overflow: hidden; } .general-portrait::after { content: ""; position: absolute; top: -3px; left: -3px; right: -3px; bottom: -3px; border: 1px solid #d4af37; border-radius: 6px; opacity: 0.3; } .general-info { position: absolute; bottom: 0; left: 0; right: 0; background: rgba(0, 0, 0, 0.7); color: #d4af37; font-size: 10px; padding: 2px; text-align: center; display: flex; flex-direction: column; } .general-name { font-weight: bold; } .general-name span{ font-size: 3rem; } .general-role { color: #c0c0c0; font-size: 9px; } .action-button { width: 100%; padding: 4px 0; background: #2a2a2a; border: 1px solid #5a5a5a; border-radius: 3px; color: #c0c0c0; cursor: not-allowed; pointer-events: none; transition: all 0.2s ease; font-size: 11px; margin-top: auto; opacity: 0.5; &:hover { background: linear-gradient(to bottom, #4a4a4a, #2a2a2a); } } .action-button.ready { pointer-events: all; opacity: 1; cursor: pointer; background: #8b0000; border-color: #a00; animation: pulse 1.5s infinite; } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(139, 0, 0, 0.7); } 70% { box-shadow: 0 0 0 5px rgba(139, 0, 0, 0); } 100% { box-shadow: 0 0 0 0 rgba(139, 0, 0, 0); } } .casting-bar-container { position: relative; margin-top: 5px; } .casting-label { position: absolute; top: 0; left: 0; right: 0; text-align: center; font-size: 12px; z-index: 2; line-height: 16px; } .casting-bar { height: 16px; background: #2a2a2a; border: 1px solid #5a5a5a; border-radius: 8px; overflow: hidden; position: relative; } .casting-progress { height: 100%; background: linear-gradient(90deg, #8b0000, #a00); border-radius: 8px; transition: width 0.5s ease; display: flex; align-items: center; justify-content: flex-end; color: white; font-size: 10px; font-weight: bold; text-shadow: 0 0 2px rgba(0, 0, 0, 0.8); } .attribute-name { color: #a0a0a0; font-weight: bold; white-space: nowrap; } .attribute-value { color: #d4af37; font-weight: bold; } .sanctuary-section { grid-area: sanctuary; background: #151515; border: 1px solid #3a3a3a; border-radius: 4px; padding: 10px; } .sanctuary-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; } .rescue-count { top: 0; right: 25px; position: absolute; font-size: 12px; color: #d4af37; } .rescue-count span { margin-left: 10px; } .sanctuary-grid { display: grid; grid-template-columns: repeat(12, 1fr); justify-content: center; gap: 8px; } .sanctuary-slot { height: 70px; width: 50px; background: #2a2a2a; border: 1px dashed #5a5a5a; border-radius: 4px; display: flex; align-items: center; justify-content: center; color: #5a5a5a; font-size: 10px; transition: all 0.3s ease; position: relative; } .sanctuary-slot.filled { background: linear-gradient(to bottom, var(--dark-red), var(--blood-red)); border: 1px solid var(--crimson); opacity: 1; } .sanctuary-slot.filled::before { content: ''; position: absolute; top: 10px; left: 50%; transform: translateX(-50%); width: 15px; height: 15px; background-color: var(--gothic-text); border-radius: 50%; opacity: 0.3; } .sanctuary-slot.filled::after { content: ''; position: absolute; bottom: 5px; left: 50%; transform: translateX(-50%); width: 20px; height: 10px; background-color: rgba(0, 0, 0, 0.5); } #generalbutton { overflow: hidden; } #generalbutton img { height: 100%; object-fit: cover; width: 100%; object-position: top; } </style> <div class="battle-panel module-border"> <div class="module-grid-2col"> <div class="stat-container"> <div class="stat-label"> <span>Defense</span> <span id="defender-fortification-text">80 / 100</span> </div> <div class="stat-bar"> <div class="defense-bar" id="defender-fortification-bar"></div> </div> </div> <div class="stat-container"> <div class="stat-label"> <span>Morale</span> <span id="defender-morale-text">60 / 100</span> </div> <div class="stat-bar"> <div class="morale-bar" id="defender-morale-bar"></div> </div> </div> </div> <div class="module-border"> <div class="module-header">Battle Command</div> <div class="generals-grid"> <<do tag "Commander">> <<set _generalinfo = $battle.defense.generals["Commander"]>> <<set _general = getMonsterByID(_generalinfo.id)>> <<HoverTip "_generalinfo.desc" >> <div class="general-card general" data-name="Commander"> <div class="general-portrait" id="generalbutton"> <<if _general>><<HeadImageByID _generalinfo.id>><</if>> <div class="general-info"> <div class="general-name"><<= _general?.name || "<span>+</span>">></div> <div class="general-role">Commander</div> </div> </div> <button class="action-button" data-action="troop">Gallant Charge</button> </div> <</HoverTip>> <</do>> <<do tag "Warden">> <<set _generalinfo2 = $battle.defense.generals["Warden"] >> <<set _general = getMonsterByID(_generalinfo2.id)>> <<HoverTip "_generalinfo2.desc" >> <div class="general-card general" data-name="Warden"> <<link '<div class="general-portrait" id="generalbutton"> <<if _general>><<HeadImageByID _generalinfo2.id>><</if>> <div class="general-info"> <div class="general-name"><<= _general?.name || "<span>+</span>">></div> <div class="general-role">Warden</div> </div> </div>'>> <<run Dialog.create("", "SummonCG")>> <<run Dialog.wiki("<<GeneralFilter 'Warden'>>")>> <<run Dialog.open()>><</link>> <button class="action-button" data-action="repair-defense">Repair Defense</button> </div> <</HoverTip>> <</do>> <<do tag "Guardian">> <<set _generalinfo3 = $battle.defense.generals["Guardian"] >> <<set _general = getMonsterByID(_generalinfo3.id)>> <<HoverTip "_generalinfo3.desc" >> <div class="general-card general" data-name="Guardian"> <<link '<div class="general-portrait" id="generalbutton"> <<if _general>><<HeadImageByID _generalinfo3.id>><</if>> <div class="general-info"> <div class="general-name"><<= _general?.name || "<span>+</span>">></div> <div class="general-role">Guardian</div> </div> </div>'>> <<run Dialog.create("", "SummonCG")>> <<run Dialog.wiki("<<GeneralFilter 'Guardian'>>")>> <<run Dialog.open()>><</link>> <button class="action-button" data-action="rush-enemies">Rush Enemies</button> </div> <</HoverTip>> <</do>> <<do tag "Bard">> <<set _generalinfo4 = $battle.defense.generals["Bard"] >> <<set _general = getMonsterByID(_generalinfo4.id)>> <<HoverTip "_generalinfo4.desc" >> <div class="general-card general" data-name="Bard"> <<link '<div class="general-portrait" id="generalbutton"> <<if _general>><<HeadImageByID _generalinfo4.id>><</if>> <div class="general-info"> <div class="general-name"><<= _general?.name || "<span>+</span>">></div> <div class="general-role">Bard</div> </div> </div>'>> <<run Dialog.create("", "SummonCG")>> <<run Dialog.wiki("<<GeneralFilter 'Bard'>>")>> <<run Dialog.open()>><</link>> <button class="action-button" data-action="quick-heal">Quick Heal</button> </div> <</HoverTip>> <</do>> <<do tag "Ranger">> <<set _generalinfo5 = $battle.defense.generals["Ranger"] >> <<set _general = getMonsterByID(_generalinfo5.id)>> <<HoverTip "_generalinfo5.desc" >> <div class="general-card general" data-name="Ranger"> <<link '<div class="general-portrait" id="generalbutton"> <<if _general>><<HeadImageByID _generalinfo5.id>><</if>> <div class="general-info"> <div class="general-name"><<= _general?.name || "<span>+</span>">></div> <div class="general-role">Ranger</div> </div> </div>'>> <<run Dialog.create("", "SummonCG")>> <<run Dialog.wiki("<<GeneralFilter 'Ranger'>>")>> <<run Dialog.open()>><</link>> <button class="action-button" data-action="assassinate">Assassinate</button> </div> <</HoverTip>> <</do>> <<do tag "Strategist">> <<set _generalinfo6 = $battle.defense.generals["Strategist"] >> <<set _general = getMonsterByID(_generalinfo6.id)>> <<HoverTip "_generalinfo6.desc" >> <div class="general-card general" data-name="Strategist"> <div class="general-portrait" id="generalbutton"> <<if _general>>[img[setup.ImagePath + _general.head]]<</if>> <div class="general-info"> <div class="general-name"><<= _general?.name || "<span>+</span>">></div> <div class="general-role">Lord</div> </div> </div> <button class="action-button" data-action="capture">Captive Rescue</button> </div> <</HoverTip>> <</do>> </div> <!-- Casting Bar integrated with generals section --> <div class="casting-bar-container"> <div class="casting-label">Command Charge</div> <div class="casting-bar"> <div class="casting-progress" id="defender-charging-bar"></div> </div> </div> </div> <!-- Attributes Section --> <div class="module-grid-2col"> <div class="module-border"> <div class="module-header">Knight's Valor</div> <div class="module-grid-2col" id="general-stats"> <div class="module-item-flex"> <span class="attribute-name"></span> <span class="attribute-value"></span> </div> </div> </div> <div class="module-border"> <div class="module-header">Towers' Might</div> <div class="module-grid-2col" id="towers-stats"> <div class="module-item-flex"> <span class="attribute-name"></span> <span class="attribute-value"></span> </div> </div> </div> </div> <div class="module-border"> <div class="module-header"><<HoverTip "Successfully Rescued Monster Girls.">>Rescue Camp<</HoverTip>> <div class="rescue-count"> </div> </div> <div class="sanctuary-grid" id="defender-captives"> <div class="sanctuary-slot filled">Rescued</div> <div class="sanctuary-slot"></div> </div> </div> </div>
<<done>> <<script>> var canvas = document.getElementById('gameCanvas'); if (!canvas) { console.error('Canvas not found!'); } var physicalWidth = Math.max(window.screen.width, window.screen.height); var dpr = window.devicePixelRatio || 1; var screenWidth = physicalWidth ; if (screenWidth < 1664) { canvas.width = 1664; var scale = screenWidth / 1664; var battleGroup = document.getElementById('scalediv'); battleGroup.style.width = "1664px"; battleGroup.style.transform = `scale(${scale})`; }else { canvas.width = Math.clamp(1664, screenWidth, 1920); } canvas.height = 20 * 16; var ctx = canvas.getContext('2d'); var fps = 60; var gravity = 0.5; var gridArea = document.getElementById('gridArea'); var rows = 3; var cols = 7; var gridSize = 4 * 16; var gridWidth = 28 * 16; var gridHeight = 12 * 16; var gridOffsetX = canvas.width - gridWidth; const buildingpara = State.variables.buildingEffects; var defensePara = { ...D.zc.defense, name: "Defenders", isCharging: false, get maxfortitude() {return buildingpara.stats.defense;}, get maxMorale() {return Math.trunc((State.variables.domain.humanpp + State.variables.domain.monsterpp) * 0.2);}, get troopAttack() {return (getMonsterByID(this.generals.Commander.id)?.str.lv || 0) + buildingpara.stats.attack;}, get troopArmor() {return (getMonsterByID(this.generals.Guardian.id)?.dex.lv || 0) + buildingpara.stats.armor;}, get troopMaxHp() {return (getMonsterByID(this.generals.Warden.id)?.sta.lv || 0) + buildingpara.stats.health;}, get troopRegen() {return (getMonsterByID(this.generals.Bard.id)?.cha.lv || 0) / 10 + buildingpara.stats.regen;}, get troopCrit() {return Math.clamp(((getMonsterByID(this.generals.Ranger.id)?.fer.lv || 0) + buildingpara.stats.crit), 0 , 50);}, get charge() {return Math.clamp((2000 - State.variables.chars.dataMap.get("you").wil.lv * 3 - buildingpara.stats.charge), 500 , 2000);}, get captivesCount() {return buildingpara.capacities.rescuecamp;}, towersBoost: { allFireRate: 1, allDamage: 1 }, captives: [], speed: 300, combatDuration: 10, isDeployed: false, isRetreating: false, currentSpeed: 0, lastAttackTime: 0, pushTime: 0, collidedEnemies: [] } function updateState() { D.zc.defense = defensePara; } function initArmies() { $("#defender-captives").empty(); for (let i = 0; i < defensePara.captivesCount; i++) { const isFilled = defensePara.captives[i] ? "filled" : ""; $("#defender-captives").append(`<div class="sanctuary-slot ${isFilled}"></div>`); } defensePara.hp = defensePara.troopMaxHp; defensePara.fortitude = defensePara.maxfortitude; defensePara.morale = defensePara.maxMorale; D.zc.totalEnemiesKilled = 0; D.zc.gameStarted = false; D.zc.gameEnded = false; resetChargingBars(); hideActionButtons(); updateStats(); } initArmies(); function getBossHealth() { try { const boss = enemies.filter(e => e.element.className.includes('boss')); if (boss) { $("#attacker-health-bar").css("width", (Math.max(0, boss[0].hp) / D.zc.attack.maxHealth * 100) + "%"); $("#attacker-health-text").text(Math.round(Math.max(0, boss[0].hp)) + "/" + D.zc.attack.maxHealth); return ; } else { $("#attacker-health-bar").css("width", 0 + "%"); $("#attacker-health-text").text("NoBoss"); } } catch (e) { return 0; } } function updatefortitude() { $("#defender-fortification-bar").css("width", (Math.max(0, defensePara.fortitude) / defensePara.maxfortitude * 100) + "%"); $("#defender-fortification-text").text( Math.clamp(Math.trunc(defensePara.fortitude), 0, defensePara.maxfortitude) + "/" + defensePara.maxfortitude); } function updatemorale() { $("#defender-morale-bar").css("width", (Math.max(0, defensePara.morale) / defensePara.maxMorale * 100) + "%"); $("#defender-morale-text").text( Math.clamp(Math.trunc(defensePara.morale), 0, defensePara.maxMorale) + "/" + defensePara.maxMorale); } function updateStats() { $("#attacker-attack-bar").css("width", (100) + "%"); $("#defender-morale-bar").css("width", (Math.max(0, defensePara.morale) / defensePara.maxMorale * 100) + "%"); $("#defender-morale-text").text( Math.clamp(Math.trunc(defensePara.morale), 0, defensePara.maxMorale) + "/" + defensePara.maxMorale); $("#defender-fortification-bar").css("width", (Math.max(0, defensePara.fortitude) / defensePara.maxfortitude * 100) + "%"); $("#defender-fortification-text").text( Math.clamp(Math.trunc(defensePara.fortitude), 0, defensePara.maxfortitude) + "/" + defensePara.maxfortitude); updateState(); } function moraleRegen() { if (D.zc.gameEnded) return; defensePara.morale = defensePara.morale + (10 / defensePara.charge); updatemorale(); } $(".action-button").on("click", function() { const action = $(this).data("action"); executeAction(action); }); function addLog(message) { const timestamp = new Date().toLocaleTimeString('en-US', { hour12: false }); const logEntry = document.createElement('div'); logEntry.className = 'log-entry'; logEntry.textContent = `[${timestamp}] ${message}`; const battleLog = document.getElementById('battle-log'); if (!battleLog || !logEntry) { console.warn('#battle-log none'); return; } battleLog.prepend(logEntry); if (battleLog.children.length > 50) { battleLog.lastChild.remove(); } } function resetChargingBars() { $("#defender-charging-bar").css({ width: "0", transition: "none" }); } function hideActionButtons() { const skillButtons = document.querySelectorAll('.action-button'); skillButtons.forEach(button => { button.classList.remove('ready'); }); } function startCharging(defensePara) { if (defensePara.isCharging) return; defensePara.isCharging = true; const barId = "#defender-charging-bar"; const $bar = $(barId); if (!$bar.length) { return; } $bar.css({ width: "0", transition: "none" }); setTimeout(() => { $bar.css({ transition: `width ${defensePara.charge / 100}s linear`, width: "100%" }); }, 10); setTimeout(() => { if (defensePara.isCharging) { defensePara.isCharging = false; showActionButtons(defensePara); const skillButtons = document.querySelectorAll('.action-button'); skillButtons.forEach(button => { button.classList.add('ready'); }); updateState(); } }, (defensePara.charge * 10)); } function showActionButtons(defensePara) { const skillButtons = document.querySelectorAll('.action-button'); skillButtons.forEach(button => { button.classList.add('ready'); }); addLog(`Finished charging. Choose an action!`); } function executeAction(action) { const barId = "#defender-charging-bar"; switch (action) { case "troop": { if (defensePara.isDeployed) { addLog("Cannot deploy: Lord’s Guard already deployed!"); return; } else if (defensePara.hp <= 0) { addLog("Cannot deploy: Lord’s Guard has fallen!"); return; } else { defenderTroop = new DefenderTroop(); defensePara.isDeployed = true; addLog("Lord’s Guard unleashes Gallant Charge, shattering the demonic line!"); addLog("Note: This skill is for survival only—extremely powerful and unbalanced. Use with caution!"); updateStats(); startCharging(defensePara); break; } } case "repair-defense": defensePara.fortitude = Math.min(defensePara.maxfortitude, defensePara.fortitude + 10); addLog(`Defenders urgently bolsters fortifications, gaining 10 fortitude!`); updateStats(); startCharging(defensePara); break; case "capture": { if (Math.random() < defensePara.captivesodds) { const emptyIndex = defensePara.captives.length; if (emptyIndex < defensePara.captivesCount) { defensePara.captives.push(true); function rescueNext(captures) { const index = captures.findIndex(item => item.stat === "Captured"); if (index !== -1) { captures[index].stat = "Rescued"; } return captures; }; rescueNext(D.zc.captures); $.wiki('<<redo "capture">>'); $(`#defender-captives .sanctuary-slot`).eq(emptyIndex).addClass("filled"); addLog(`You’ve freed the monster girl from the demons’ clutches! She’s safe.`); } else { addLog(`You tried to rescue the monster girl, but all safe havens in Hawkridge Pass are full.`); } } else { addLog(`The rescue attempt failed. The demons overpowered your team, and the monster girl remains captive!`); } updateStats(); startCharging(defensePara); break; } case "quick-heal": defensePara.hp = Math.min(defensePara.troopMaxHp, defensePara.hp + 10); addLog(`Lord’s Guard swiftly recovers, gaining 10 hp!`); updateStats(); startCharging(defensePara); break; case "assassinate": { addLog(`Seize the moment when the enemy is distracted during their attack to assassinate their leader. If successful, the enemy Boss loses 50% of their health. Success rate equals the current critical hit chance.`); if ((100 * Math.random()) < defensePara.troopCrit) { const maxhp = getEnemyWithMaxHP(); maxhp.hp = maxhp.hp / 2; addLog(`Assassination Successful!`); } else { addLog(`Assassination Failed.`); } updateStats(); startCharging(defensePara); break;} case "rush-enemies": if (State.variables.enemies.rushed) { addLog("Enemies already hastened!"); return; } State.variables.enemies.waveInterval = Math.max(3, State.variables.enemies.waveInterval / 2); State.variables.enemies.rushed = true; addLog("Enemy waves hasten! Interval halved!"); updateStats(); startCharging(defensePara); break; } const skillButtons = document.querySelectorAll('.action-button'); skillButtons.forEach(button => { button.classList.remove('ready'); }); $(barId).css({ width: "0", transition: "none" }); } function getTowerColRow(index) { let col = Math.floor(index / 3); let row = (index % 3) + 1; return { col: col, row: row }; } function getTowerIndex(col, row) { let clampedRow = Math.max(1, Math.min(row, 3)); return col * 3 + clampedRow - 1; } function Tower(index, towerId, lv, towerindex) { this.towerId = towerId; this.lv = lv; this.row = getTowerColRow(towerindex).row; this.col = getTowerColRow(towerindex).col; this.x = gridOffsetX + (this.col + 0.5) * gridSize; this.y = (this.row + 0.5) * gridSize; this.fireCounter = 0; this.index = index; this.target = null; this.towerConfig = setup.bdunitdata[towerId][lv]; var arrowRef = this.towerConfig.arrowRef; this.arrowConfig = setup.arrow[arrowRef.type].levels[arrowRef.lv]; this.fireRate = this.arrowConfig.behavior.fireRate; } Tower.prototype.fire = function() { this.fireCounter++; var framesPerShot = Math.floor(fps / this.fireRate); if (this.fireCounter >= framesPerShot) { this.fireCounter = 0; var targetEnemy = selectTarget(this, enemies, defensePara.aimMode); if (targetEnemy) { var dx = targetEnemy.x - this.x; var dy = targetEnemy.y - this.y; var distance = Math.hypot(dx, dy); var t = distance / this.arrowConfig.behavior.projectileSpeed; var arrow = arrowPool.getArrow(); if (arrow) { arrow.reset(this.x, this.y, dx / t, dy / t, this.arrowConfig, targetEnemy); } } } }; var towers = []; window.initTowers = function () { let slotCount = buildingpara.slots.bastion; let slotsbastion = D.jz.hawkridgepass.bastion; towers = []; for (let i = 0; i < slotCount; i++) { let slot = slotsbastion[i]; if (slot?.id?.startsWith("tower")) { towers.push(new Tower(towers.length, slot.id, slot.lv, i)); } }; }; function Arrow() { this.x = 0; this.y = 0; this.vx = 0; this.vy = 0; this.active = false; this.params = null; this.isRebounding = false; this.target = null; }; var arrowPool = { pool: [], initialSize: 50, maxSize: 2000, activeCount: 0, init: function() { this.pool = []; for (var i = 0; i < this.initialSize; i++) { this.pool.push(new Arrow()); } this.activeCount = 0; }, getArrow: function() { for (var i = 0; i < this.pool.length; i++) { if (!this.pool[i].active) { this.activeCount++; return this.pool[i]; } } if (this.pool.length < this.maxSize) { var newArrow = new Arrow(); this.pool.push(newArrow); this.activeCount++; return newArrow; } console.warn("Arrow pool exhausted. Consider increasing maxSize."); return null; }, releaseArrow: function(arrow) { if (arrow.active) { arrow.active = false; this.activeCount--; } }, update: function(dt) { for (var i = 0; i < this.pool.length; i++) { if (this.pool[i].active) { this.pool[i].update(dt); } } }, draw: function() { for (var i = 0; i < this.pool.length; i++) { if (this.pool[i].active) { this.pool[i].draw(); } } } }; Arrow.prototype.reset = function(x, y, vx, vy, arrowConfig, target) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.active = true; this.params = { behavior: arrowConfig.behavior, draw: arrowConfig.draw }; this.physicalDamage = arrowConfig.behavior.physicalDamage; this.isRebounding = false; this.target = target; }; Arrow.prototype.draw = function() { if (!this.active) return; ctx.fillStyle = this.params.draw.fillStyle; ctx.strokeStyle = this.params.draw.strokeStyle; ctx.lineWidth = this.params.draw.lineWidth; var angle = Math.atan2(this.vy, this.vx); ctx.save(); ctx.translate(this.x, this.y); ctx.rotate(angle); this.params.draw.commands.forEach(command => { ctx.beginPath(); if (command.type === 'path') { command.points.forEach((point, index) => { if (index === 0) { ctx.moveTo(point.x, point.y); } else { ctx.lineTo(point.x, point.y); } }); if (command.closePath) ctx.closePath(); ctx.fill(); ctx.stroke(); } else if (command.type === 'arc') { ctx.arc(0, 0, command.radius, command.startAngle, command.endAngle); ctx.fill(); ctx.stroke(); } else if (command.type === 'image') { var img = new Image(); img.src = command.src; ctx.drawImage(img, -command.width / 2, -command.height / 2, command.width, command.height); } else if (command.type === 'text') { ctx.font = command.font; ctx.textAlign = "center"; ctx.textBaseline = "middle"; ctx.fillText(command.content, command.offsetX || 0, command.offsetY || 0); if (this.params.draw.strokeStyle && this.params.draw.lineWidth) { ctx.strokeText(command.content, command.offsetX || 0, command.offsetY || 0); } } }); ctx.restore(); }; Arrow.prototype.update = function() { if (!this.active) return; var oldX = this.x; var oldY = this.y; var newX = this.x + this.vx / fps; var newY = this.y + this.vy / fps; if (newX < 0 || newX > canvas.width || newY < 0 || newY > canvas.height) { arrowPool.releaseArrow(this); return; } if (newY < 64 || newY > 256) { arrowPool.releaseArrow(this); return; } var hitEnemy = null; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var enemyWidth = enemy.width * 1.1; var enemyHeight = enemy.height * 1.1; var rx = enemy.x - enemyWidth / 2; var ry = enemy.y - enemyHeight / 2; if ( newX > rx && newX < rx + enemyWidth && newY > ry && newY < ry + enemyHeight ) { hitEnemy = enemy; break; } var t = this.lineIntersectsRect(oldX, oldY, newX, newY, rx, ry, enemyWidth, enemyHeight); if (t >= 0 && t <= 1) { hitEnemy = enemy; break; } } if (hitEnemy) { hitEnemy.hp -= this.physicalDamage; hitEnemy.element.style.filter = 'brightness(2)'; setTimeout(function() { if (hitEnemy.element) hitEnemy.element.style.filter = ''; }, 100); arrowPool.releaseArrow(this); return; } this.x = newX; this.y = newY; }; Arrow.prototype.lineIntersectsRect = function(x1, y1, x2, y2, rx, ry, rw, rh) { var left = rx; var right = rx + rw; var top = ry; var bottom = ry + rh; var tValues = []; if (x1 !== x2) { var t = (left - x1) / (x2 - x1); if (t >= 0 && t <= 1) { var y = y1 + t * (y2 - y1); if (y >= top && y <= bottom) tValues.push(t); } } if (x1 !== x2) { var t = (right - x1) / (x2 - x1); if (t >= 0 && t <= 1) { var y = y1 + t * (y2 - y1); if (y >= top && y <= bottom) tValues.push(t); } } if (y1 !== y2) { var t = (top - y1) / (y2 - y1); if (t >= 0 && t <= 1) { var x = x1 + t * (x2 - x1); if (x >= left && x <= right) tValues.push(t); } } if (y1 !== y2) { var t = (bottom - y1) / (y2 - y1); if (t >= 0 && t <= 1) { var x = x1 + t * (x2 - x1); if (x >= left && x <= right) tValues.push(t); } } if (tValues.length === 0) return -1; var minT = tValues[0]; for (var i = 1; i < tValues.length; i++) { if (tValues[i] < minT) minT = tValues[i]; } return minT; }; function selectTarget(tower, enemies, mode) { if (enemies.length === 0) return null; if (mode === 'focusBoss') { var bigBoss = enemies.find(function(enemy) { return enemy.element.className.includes('enemy-big-boss'); }); if (bigBoss) return bigBoss; var smallBoss = enemies.find(function(enemy) { return enemy.element.className.includes('enemy-small-boss'); }); if (smallBoss) return smallBoss; var closestEnemy = null; var minDist = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dist = Math.hypot(tower.x - enemy.x, tower.y - enemy.y); if (dist < minDist) { minDist = dist; closestEnemy = enemy; } } return closestEnemy; } else if (mode === 'oneToOne') { if (tower.target && enemies.includes(tower.target) && tower.target.hp > 0) { return tower.target; } var closestEnemy = null; var minDist = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dist = Math.hypot(tower.x - enemy.x, tower.y - enemy.y); if (dist < minDist) { minDist = dist; closestEnemy = enemy; } } tower.target = closestEnemy; return closestEnemy; } else if (mode === 'spread') { var targetedEnemies = []; for (var i = 0; i < arrowPool.length; i++) { if (arrowPool[i].active && arrowPool[i].target) { targetedEnemies.push(arrowPool[i].target); } } for (var i = 0; i < towers.length; i++) { if (towers[i] !== tower && towers[i].target) { targetedEnemies.push(towers[i].target); } } var availableEnemies = enemies.filter(function(enemy) { return !targetedEnemies.includes(enemy); }); if (availableEnemies.length > 0) { var closestEnemy = null; var minDist = Infinity; for (var i = 0; i < availableEnemies.length; i++) { var enemy = availableEnemies[i]; var dist = Math.hypot(tower.x - enemy.x, tower.y - enemy.y); if (dist < minDist) { minDist = dist; closestEnemy = enemy; } } return closestEnemy; } var closestEnemy = null; var minDist = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dist = Math.hypot(tower.x - enemy.x, tower.y - enemy.y); if (dist < minDist) { minDist = dist; closestEnemy = enemy; } } return closestEnemy; } else { var closestEnemy = null; var minDist = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dist = Math.hypot(tower.x - enemy.x, tower.y - enemy.y); if (dist < minDist) { minDist = dist; closestEnemy = enemy; } } return closestEnemy; } } function Dropitem(id, x, y, config) { this.id = id; this.x = x; this.y = y; this.config = config; this.element = document.createElement('div'); this.element.className = 'item ' + id; const width = 30; const height = 30; this.element.style.width = `${width}px`; this.element.style.height = `${height}px`; this.element.style.left = `${x - width / 2}px`; this.element.style.top = `${y - height / 2}px`; const imagePath = setup.ImagePath; const imageFile = (id === "gold" ? "ui/coins.png" : "ui/chests.png"); this.element.style.background = `url(${imagePath}${imageFile}) no-repeat center`; this.element.style.backgroundSize = 'contain'; this.element.style.position = 'absolute'; this.element.style.zIndex = '1'; const container = document.querySelector('.game-container'); if (container) { container.appendChild(this.element); } var self = this; this.element.addEventListener('click', function() { self.pickup(); }); } Dropitem.prototype.pickup = function() { if (this.id.startsWith("skill")) { let skillid = this.id.slice(6); State.variables.skillbag[skillid]++; setup.SkillManager.renderSkillList(); addLog('Picked up skill: ' + this.id); } else { addLog('Picked up: ' + this.id + " x" + this.config.amount); } this.element.remove(); var index = dropitems.indexOf(this); if (index !== -1) { dropitems.splice(index, 1); } }; var dropitems = []; function Enemy(id) { const params = generateSpecificEnemy(id); if (!params) { console.error(`Enemy data for ${id} not found`); return; } const [idrace, idtype] = id.split('_'); this.id = id; this.hp = params.hp; this.movexspeed = params.speed; this.power = params.power; this.x = 0; this.y = Math.random() * 160 + 80; this.width = params.size; this.height = this.width; this.movetype= params.movetype || "linear"; this.movey= 0; this.moveyspeed= 0; this.phase= 'takeoff'; this.phaseProgress= 0; this.jumpy= this.y; this.nowheight = this.height; this.nowwidth = this.width; this.angle = 0; this.origin = "bottom center"; this.opacity = 1; this.display = "inline-block"; this.spriteDataVar = params.spriteData || 'enemySprite'; this.frameId = idrace; this.element = document.createElement('span'); this.hasReachedGate = false; this.damageTimer = 0; this.drops = params.drops; this.initRender = function() { const spriteData = setup.iconSpriteData['enemySprite']; const frame = spriteData?.frames?.[this.frameId]; if (!frame || !spriteData?.grid) { console.error(`Sprite data for ${this.spriteDataVar} or frame ${this.frameId} not found`); return; } const { rows, cols, frameWidth, frameHeight } = spriteData.grid; const scale = this.width / frameWidth; const scalex = this.nowwidth / this.width; const scaley = this.nowheight / this.height; const x = frame.col * frameWidth * scale; const y = frame.row * frameHeight * scale; this.cssClass = 'enemy-' + Math.random().toString(36).substr(2, 7); this.element.className = `${params.style} enemy-sprite ${this.cssClass}`; let styleSheet = document.getElementById('enemy-styles'); if (!styleSheet) { styleSheet = document.createElement('style'); styleSheet.id = 'enemy-sprite'; document.head.appendChild(styleSheet); } const cssRule = ` .${this.cssClass} { transform-origin: ${this.origin}; display: inline-block; rotate(${this.angle}deg) scale(${scalex}, ${scaley}); background-image: url(${spriteData.img}); background-position: -${x}px -${y}px; width: ${this.width}px; height: ${this.height}px; background-size: ${frameWidth * cols * scale}px ${frameHeight * rows * scale}px; left: ${this.x - this.width / 2}px; top: ${this.y - this.movey - (this.nowheight / 2)}px; opacity: ${this.opacity}; `; styleSheet.sheet.insertRule(cssRule, styleSheet.sheet.cssRules.length); this.lastX = 0; this.lastY = null; this.lastMovey = 0; this.lastOpacity = 1; this.lastAngle = 0; this.lastdisplay = "inline-block"; this.lastorigin = "bottom center"; this.lastnowheight = this.height; this.lastnowwidth = this.width; this.dirty = false; }; this.initRender(); this.renderSprite = function() { if (this.x !== this.lastX || this.y !== this.lastY || this.movey !== this.lastMovey || this.opacity !== this.lastOpacity || this.angle !== this.lastAngle || this.display !== this.lastdisplay || this.origin !== this.lastorigin || this.nowheight !== this.lastnowheight || this.nowwidth !== this.lastnowwidth) { this.dirty = true; } else { return; } const elem = this.element; if (this.dirty &&(this.x !== this.lastX || this.nowwidth !== this.lastnowwidth)) { elem.style.left = `${Math.round(this.x - this.nowwidth / 2)}px`; this.lastX = this.x; } if (this.dirty &&(this.y !== this.lastY || this.movey !== this.lastMovey || this.nowheight !== this.lastnowheight)) { elem.style.top = `${Math.round(this.y - this.movey - this.nowheight / 2)}px`; this.lastY = this.y; this.lastMovey = this.movey; } if (this.dirty &&(this.opacity !== this.lastOpacity)) { elem.style.opacity = this.opacity; this.lastOpacity = this.opacity; } if (this.dirty &&(this.display !== this.lastdisplay)) { elem.style.display = this.display; this.lastdisplay = this.display; } if (this.dirty &&(this.origin !== this.lastorigin)) { elem.style.transformOrigin = `${this.origin}`; this.lastorigin = this.origin; } if (this.dirty &&(this.nowheight != this.lastnowheight || this.nowwidth != this.lastnowwidth || this.angle !== this.lastAngle)) { const scalex = this.nowwidth / this.width; const scaley = this.nowheight / this.height; elem.style.transform = `rotate(${this.angle}deg) scale(${scalex}, ${scaley})`; this.lastAngle = this.angle; this.lastnowwidth = this.nowwidth; this.lastnowheight = this.nowheight; } this.dirty = false; }; const container = document.querySelector('.game-container'); if (container) container.appendChild(this.element); else console.error('Game container not found!'); } window.sineLUT = (() => { const table = []; const resolution = 360; for (let i = 0; i < resolution; i++) { table[i] = Math.sin((i / resolution) * 2 * Math.PI); } return table; })(); Enemy.prototype.update = function() { if (defensePara.morale <= 0) { this.element.remove(); enemies.splice(enemies.indexOf(this), 1); return; } if (this.hasReachedGate) { this.damageTimer += 1 / fps; var damageInterval = 5 / this.movexspeed; if (this.damageTimer >= damageInterval) { if (defensePara.fortitude > 0) { defensePara.fortitude -= this.power; updatefortitude(); } else { defensePara.morale -= this.power; updatemorale(); } addLog('Gate sustained damage: power=' + this.power + ', gate.hp=' + defensePara.fortitude); this.damageTimer = 0; } if (this.hp <= 0) { this.element.remove(); enemies.splice(enemies.indexOf(this), 1); D.zc.totalEnemiesKilled++; State.variables.battlebag.pickup("gold", this.power); if (this.drops) { this.drops.forEach(function(drop) { if (Math.random() < drop.probability) { if (drop.id.startsWith("skill")) { var config = {}; addLog('Dropped skill: ' + drop.id + " on the path"); dropitems.push(new Dropitem(drop.id, this.x, this.y, config)); } else { var config = { amount: drop.amount }; State.variables.battlebag.pickup(drop.id, drop.amount); addLog('Dropped ' + drop.id + " x" + drop.amount + " on the path and automatically picked up"); } } }, this); } else { console.error('No drops for enemy type:', this.id); } } return; } if (this.movetype == "linear") { this.x += this.movexspeed; } else if (this.movetype == "float") { this.phaseProgress += 1/120; this.x += this.movexspeed; const index = Math.trunc((this.phaseProgress % 2) * 180); this.movey = Math.trunc(16 * sineLUT[index]); } else if(this.movetype == "roll") { this.origin = "center center"; this.x += this.movexspeed; const radius = this.width / 2; const dtheta = (this.movexspeed / radius) * 57; this.angle = (this.angle + dtheta).toFixed(1); this.angle %= 360; } else if (this.movetype == "randomy") { this.phaseProgress += 1/30; this.x += this.movexspeed; if (this.phaseProgress >= 1) { this.phaseProgress = 0; this.moveyspeed = 0.03 * (Math.random() < 0.5 ? -1 : 1); } this.movey += this.moveyspeed; this.y = this.y + this.movey; if (this.y < 80) { this.y = 80; this.moveyspeed = Math.abs(this.moveyspeed); } else if (this.y > 240) { this.y = 240; this.moveyspeed = -Math.abs(this.moveyspeed); } } else if (this.movetype == "teleport") { switch (this.phase) { case 'takeoff': this.phaseProgress += 1/120; this.opacity = Math.min(this.phaseProgress / 0.15, 1); this.display = "inline-block"; if (this.phaseProgress >= 1) { this.phase = 'hidden'; this.phaseProgress = 0; } break; case 'hidden': this.phaseProgress += 1/60; this.opacity = Math.max(1 - this.phaseProgress / 0.3, 0); this.display = this.opacity > 0 ? "inline-block" : "none"; if (this.phaseProgress >= 1) { this.phase = 'takeoff'; this.phaseProgress = 0; this.x += this.movexspeed * 120 + Math.random() * this.width; this.opacity = 0; } break; } } else if (this.movetype == "wobble") { this.phaseProgress += 1/30; this.x += this.movexspeed; const index = Math.trunc((this.phaseProgress % 2) * 180); this.angle = 3 * sineLUT[index]; this.nowwidth = this.width; this.nowheight = this.height; this.movey = 0; this.origin = "center center"; } else if (this.movetype == "wriggle") { switch (this.phase) { case 'takeoff': this.origin = "right bottom"; this.phaseProgress += this.movexspeed / 60; this.nowwidth = this.width * (1 - this.phaseProgress * 0.4); this.x -= this.width * this.movexspeed / 300; if (this.phaseProgress >= 1) { this.origin = "left bottom"; this.x += this.width * 0.4; this.phase = 'extend'; this.phaseProgress = 0; } break; case 'extend': this.phaseProgress += this.movexspeed /60; this.nowwidth = this.width * (0.6 + this.phaseProgress * 0.4); this.x += this.width * this.movexspeed / 300; if (this.phaseProgress >= 1) { this.origin = "right bottom"; this.phase = 'takeoff'; this.phaseProgress = 0; } break; } } else if (this.movetype == "jump") { this.origin = "bottom center"; switch (this.phase) { case 'takeoff': this.phaseProgress += 1/15; this.nowheight = this.height * (1 - this.phaseProgress * 0.3); this.nowwidth = this.width * (1 + this.phaseProgress * 0.1); if (this.phaseProgress >= 1) { this.phase = 'jumping'; this.phaseProgress = 0; this.moveyspeed = 10; this.nowheight = this.height; this.nowwidth = this.width; } break; case 'jumping': this.phaseProgress += 1/30; this.movey += this.moveyspeed; this.moveyspeed -= 0.5; this.x += this.movexspeed * 5; if (this.movey >= this.height) { this.movey = this.height; this.moveyspeed = -this.moveyspeed * 0.3; } if (this.movey <= 0 && this.moveyspeed < 0) { this.movey = 0; this.moveyspeed = 0; this.phase = 'landing'; this.phaseProgress = 0; } break; case 'landing': this.phaseProgress += 1/20; this.nowheight = this.height * (0.7 + this.phaseProgress * 0.3); this.nowwidth = this.width * (1.1 - this.phaseProgress * 0.1); this.nowheight = this.height; this.nowwidth = this.width; if (this.phaseProgress >= 1) { this.phase = 'takeoff'; this.phaseProgress = 0; this.nowheight = this.height; this.nowwidth = this.width; } break; } } else if (this.movetype == "crawl") { this.origin = "bottom center"; this.phaseProgress += 1/30; this.x += this.movexspeed * 0.5; const index = Math.trunc((this.phaseProgress % 2) * 180); this.nowwidth = this.width * (1 + 0.1 * sineLUT[index]); this.nowheight = this.height * (1 - 0.1 * sineLUT[index]); this.angle = 0; } else if (this.movetype == "crawl02") { this.origin = "bottom center"; this.phaseProgress += 1/30; this.x += this.movexspeed * 0.5; const index = Math.trunc((this.phaseProgress % 2) * 180); this.nowwidth = this.width * (1 + 0.1 * sineLUT[index]); this.nowheight = this.height * (1 - 0.1 * sineLUT[index]); this.angle = 0; } else if (this.movetype == "tremble") { this.phaseProgress += 1/60; this.x += this.movexspeed * 0.3; this.nowwidth = this.width * (0.95 + Math.random() * 0.1); this.nowheight = this.height * (0.95 + Math.random() * 0.1); this.angle = 5 * (Math.random() * 2 - 1); } else if (this.movetype == "bounceroll") { this.origin = "center center"; switch (this.phase) { case 'takeoff': this.phaseProgress += 1/15; this.nowheight = this.height * (1 - this.phaseProgress * 0.3); this.nowwidth = this.width * (1 + this.phaseProgress * 0.1); if (this.phaseProgress >= 1) { this.phase = 'jumping'; this.phaseProgress = 0; this.moveyspeed = 7; this.nowheight = this.height; this.nowwidth = this.width; } break; case 'jumping': this.phaseProgress += 1/30; this.movey += this.moveyspeed; this.moveyspeed -= 0.5; this.x += this.movexspeed * 5; const radius = this.width / 2; const dtheta = (this.movexspeed * 5 / radius) * (180 / Math.PI); this.angle += dtheta; this.angle %= 360; if (this.movey >= this.height / 2) { this.movey = this.height / 2; this.moveyspeed = -this.moveyspeed * 0.3; } if (this.movey <= 0 && this.moveyspeed < 0) { this.movey = 0; this.moveyspeed = 0; this.phase = 'landing'; this.phaseProgress = 0; } break; case 'landing': this.phaseProgress += 1/20; this.nowheight = this.height * (0.7 + this.phaseProgress * 0.3); this.nowwidth = this.width * (1.1 - this.phaseProgress * 0.1); if (this.phaseProgress >= 1) { this.phase = 'takeoff'; this.phaseProgress = 0; this.nowheight = this.height; this.nowwidth = this.width; } break; } }; if (this.x >= gridOffsetX - 32 - this.width / 2) { this.x = gridOffsetX - 32 - this.width / 2; this.hasReachedGate = true; if (defensePara.fortitude > 0) { defensePara.fortitude -= 2 * this.power; updatefortitude(); } else { defensePara.morale -= 2 * this.power; updatemorale(); } this.damageTimer = 0; } this.renderSprite(); if (this.hp <= 0) { this.element.remove(); enemies.splice(enemies.indexOf(this), 1); D.zc.totalEnemiesKilled++; State.variables.battlebag.pickup("gold", this.power); if (this.drops) { this.drops.forEach(function(drop) { if (Math.random() < drop.probability) { if (drop.id.startsWith("skill")) { var config = {}; addLog('Dropped skill: ' + drop.id + " on the path"); dropitems.push(new Dropitem(drop.id, this.x, this.y, config)); } else { var config = { amount: drop.amount }; State.variables.battlebag.pickup(drop.id, drop.amount); addLog('Dropped ' + drop.id + " x" + drop.amount + " on the path and automatically picked up"); } } }, this); } else { console.error('No drops for enemy type:', this.id); } } if (currentWave >= State.variables.enemies.waves.length - 1) { if (enemies.length === 0 && spawnQueue.length === 0) { endBattle("victory"); } } }; function DefenderTroop() { var params = defensePara; this.attack = params.troopAttack; this.armor = params.troopArmor; this.hp = params.hp; this.maxHp = params.troopMaxHp; this.regen = params.troopRegen; this.speed = params.speed; this.width = params.width; this.height = params.height; this.combatDuration = params.combatDuration; this.x = gridOffsetX + 40; this.y = canvas.height / 2; this.isDeployed = true; this.isRetreating = false; this.currentSpeed = this.speed; this.pushTime = 0; this.lastAttackTime = 0; this.collidedEnemies = []; this.element = document.createElement('div'); this.element.className = 'defender-troop'; this.element.style.width = '102px'; this.element.style.height = '256px'; this.element.style.left = (this.x - this.width / 2) + 'px'; this.element.style.top = '15px'; this.hpElement = document.createElement('div'); this.hpElement.className = 'defender-troop-hp'; this.hpElement.style.left = (this.x - this.width / 2) + 'px'; this.hpElement.style.top = (this.y - this.height / 2 - 20) + 'px'; this.hpElement.textContent = `HP: ${Math.round(this.hp)}`; var container = document.querySelector('.game-container'); if (container) { container.appendChild(this.element); container.appendChild(this.hpElement); } else { console.error('Game container not found for DefenderTroop!'); } } DefenderTroop.prototype.update = function() { this.hp = Math.min(this.maxHp, this.hp + this.regen / fps); this.hpElement.textContent = `HP: ${Math.round(this.hp)}`; this.hpElement.style.left = (this.x - this.width / 2) + 'px'; this.hpElement.style.top = (this.y - this.height / 2 - 20) + 'px'; $("#defender-general-health").text(Math.round(this.hp) +"/"+ defensePara.troopMaxHp); if (this.hp <= 0) { this.destroy(); return; } this.collidedEnemies = []; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var rx = enemy.x - enemy.width / 2; var ry = enemy.y - enemy.height / 2; if ( this.x - this.width / 2 < rx + enemy.width && this.x + this.width / 2 > rx && this.y - this.height / 2 < ry + enemy.height && this.y + this.height / 2 > ry ) { this.collidedEnemies.push(enemy); } } this.pushTime += 1 / fps; if (this.pushTime >= this.combatDuration && !this.isRetreating) { this.isRetreating = true; this.currentSpeed = this.speed; this.collidedEnemies = []; } if (this.isRetreating) { this.x += this.speed / fps; this.element.style.transform = "scaleX(-1)"; if (this.x >= gridOffsetX + 40) { this.destroy(); return; } } else if (this.collidedEnemies.length > 0) { var t = this.pushTime / 5; this.currentSpeed = this.speed * (1 - t); if (this.currentSpeed < 0) this.currentSpeed = 0; this.collidedEnemies.forEach(function(enemy) { enemy.x -= this.currentSpeed / fps; enemy.element.style.left = (enemy.x - enemy.width / 2) + 'px'; }, this); } else { this.currentSpeed = this.speed; if (this.x > 600) { this.x -= this.speed / fps; } } this.element.style.left = (this.x - this.width / 2) + 'px'; if (this.collidedEnemies.length > 0) { if (this.lastAttackTime === 0) { this.collidedEnemies.forEach(function(enemy) { enemy.hp -= this.attack * 2; enemy.element.style.filter = 'brightness(2)'; setTimeout(function() { if (enemy.element) enemy.element.style.filter = ''; }, 100); }, this); } this.lastAttackTime += 1 / fps; var attackInterval = 5 / this.speed; if (this.lastAttackTime >= attackInterval) { this.collidedEnemies.forEach(function(enemy) { enemy.hp -= this.attack; enemy.element.style.filter = 'brightness(2)'; setTimeout(function() { if (enemy.element) enemy.element.style.filter = ''; }, 100); }, this); this.lastAttackTime = 0; } this.collidedEnemies.forEach(function(enemy) { var damage = enemy.power / fps; var reduction = this.armor / (this.armor + 100); this.hp -= damage * (1 - reduction); }, this); } }; DefenderTroop.prototype.destroy = function() { defensePara.hp = this.hp; defensePara.isDeployed = false; if (this.element && this.element.parentNode) { this.element.remove(); } if (this.hpElement && this.hpElement.parentNode) { this.hpElement.remove(); } defenderTroop = null; }; var enemies = []; var defenderTroop = null; var spawnTimer = 0; var waveTimer = 0; var currentWave = 0; var spawnQueue = []; function startWave(waveIndex) { spawnQueue = []; var wave = State.variables.enemies.waves[waveIndex]; wave.bosses.forEach(function(boss) { for (var i = 0; i < boss.count; i++) { spawnQueue.push(boss.id); const bossdata = generateSpecificEnemy(boss.id); D.zc.attack.maxHealth = bossdata.hp; $("#attacker-attack-text").text(bossdata.power); updateStats(); } }); wave.minions.forEach(function(minion) { for (var i = 0; i < minion.count; i++) { spawnQueue.push(minion.id); } }); for (var i = spawnQueue.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var temp = spawnQueue[i]; spawnQueue[i] = spawnQueue[j]; spawnQueue[j] = temp; } } function spawnEnemy() { if (defensePara.morale <= 0) { endBattle("defeat"); return; } if (currentWave >= State.variables.enemies.waves.length) { if (enemies.length === 0 && spawnQueue.length === 0) { endBattle("victory"); } return; } if (spawnQueue.length === 0 && enemies.length === 0) { waveTimer++; if (waveTimer >= State.variables.enemies.waveInterval * fps) { currentWave++; if (currentWave < State.variables.enemies.waves.length) { startWave(currentWave); addLog(`Wave ${currentWave + 1} approaches!`); } waveTimer = 0; } return; } spawnTimer++; if (spawnTimer >= State.variables.enemies.spawnInterval * fps && spawnQueue.length > 0) { var enemyId = spawnQueue.shift(); enemies.push(new Enemy(enemyId)); spawnTimer = 0; } } startWave(0); var aimSelect = document.getElementById('aimModeSelect'); if (aimSelect) { aimSelect.addEventListener('change', function() { defensePara.aimMode = aimSelect.value; addLog('Aim mode changed to: ' + defensePara.aimMode); towers.forEach(function(tower) { tower.target = null; }); }); } setup.SkillManager = { skills: { fireRateBoost: { name: "Fire Rate", description: "Increases all towers' fire rate by 50% for 10 seconds", duration: 10, cooldown: 30, effect: function() { addLog("Use skill book: Increases all towers' fire rate by 50% for 10 seconds"); const boost = 1.5; defensePara.towersBoost.allFireRate *= boost; towers.forEach(tower => tower.fireRate *= boost); setTimeout(() => { defensePara.towersBoost.allFireRate /= boost; towers.forEach(tower => tower.fireRate /= boost); }, this.duration * 1000); } }, gateHeal: { name: "Repair", description: "Restores 10 HP to the gate", cooldown: 30, effect: function() { defensePara.fortitude = Math.min(defensePara.fortitude + 10, 1000); addLog("Use skill book: Restores 10 HP to the gate"); updatefortitude(); } }, freeze: { name: "Freeze", description: "Freezes all enemies for 5 seconds", duration: 5, cooldown: 30, effect: function() { addLog("Use skill book: Freezes all enemies for 5 seconds"); enemies.forEach(enemy => { enemy.originalSpeed = enemy.movexspeed; enemy.movexspeed = 0; }); setTimeout(() => { enemies.forEach(enemy => { if (enemy.originalSpeed) { enemy.movexspeed = enemy.originalSpeed; delete enemy.originalSpeed; } }); }, this.duration * 1000); } } }, cooldowns: {}, useSkill(skillId) { if (!(skillId in this.skills)) { throw new Error(`Invalid skill: ${skillId}`); } if (this.cooldowns[skillId]) { return; } if (State.variables.skillbag[skillId] <= 0) { return; } State.variables.skillbag[skillId]--; this.skills[skillId].effect(); if (this.skills[skillId].cooldown) { const cooldownEnd = Date.now() + this.skills[skillId].cooldown * 1000; this.cooldowns[skillId] = cooldownEnd; const updateCooldown = () => { const remaining = Math.ceil((this.cooldowns[skillId] - Date.now()) / 1000); if (remaining <= 0) { delete this.cooldowns[skillId]; this.renderSkillList(); } else { this.renderSkillList(); setTimeout(updateCooldown, 1000); } }; updateCooldown(); } this.renderSkillList(); }, renderSkillList() { const container = document.querySelector('.skillbook-container'); if (!container) return; container.innerHTML = ` ${Object.entries(State.variables.skillbag) .filter(([_, count]) => count > 0) .map(([skillId, count]) => { const skill = this.skills[skillId]; const cooldownEnd = this.cooldowns[skillId]; let buttonText = 'Use'; let isDisabled = false; if (cooldownEnd) { const remaining = Math.ceil((cooldownEnd - Date.now()) / 1000); if (remaining > 0) { buttonText = `Cooling (${remaining}s)`; isDisabled = true; } } return ` <button class="skillbook-button" data-skill-id="${skillId}" ${isDisabled ? 'disabled' : ''}> <div class="skillbook-name">📜${skill.name}</div> <div class="skillbook-text">${buttonText}</div> <div class="skillbook-count"> x${count}</div> </button> `; }).join('')} `; }, init() { const container = document.querySelector('.skillbook-container'); if (!container) return; container.addEventListener('click', (e) => { const button = e.target.closest('.skillbook-button'); if (button && !button.disabled) { const skillId = button.dataset.skillId; this.useSkill(skillId); } }); this.renderSkillList(); } }; setup.SkillManager.init(); const StatsManager = { stats: [ { name: "Attack", id: "attack", format: (data) => data, source: () => defensePara.troopAttack }, { name: "Armor", id: "defense", format: (data) => data, source: () => defensePara.troopArmor }, { name: "Health", id: "health", format: (data) => `${data.hp}/${data.troopMaxHp}`, source: () => ({ hp: Math.max(0, Math.trunc(defensePara.hp)), troopMaxHp: defensePara.troopMaxHp }) }, { name: "Regen", id: "regen", format: (data) => `${data}/s`, source: () => defensePara.troopRegen }, { name: "Crit Chance", id: "crit", format: (data) => `${data}%`, source: () => defensePara.troopCrit }, { name: "Charge Time", id: "charge", format: (data) => `${(data / 100).toFixed(1)}s`, source: () => defensePara.charge } ], renderStats() { const container = document.getElementById('general-stats'); if (!container) return; container.innerHTML = this.stats .map(stat => { const data = stat.source(); const value = stat.format(data); return ` <div class="module-item-flex"> <span class="attribute-name">${stat.name}:</span> <span class="attribute-value" id="defender-general-${stat.id}">${value}</span> </div> `; }) .join(''); moraleRegen(); }, statstimer: null, init() { this.renderStats(); this.statstimer = setInterval(() => this.renderStats(), 1000); }, stop() { if (this.statstimer) { clearInterval(this.statstimer); this.statstimer = null; } } }; StatsManager.renderStats(); const TowersManager = { stats: [ { name: "Fire Rate Boost", id: "fireRate", format: (data) => `${(data * 100).toFixed(0)}%`, source: () => defensePara.towersBoost.allFireRate }, { name: "Damage Boost", id: "damage", format: (data) => `${(data * 100).toFixed(0)}%`, source: () => defensePara.towersBoost.allDamage }, { name: "Aim Mode", id: "aimMode", format: (data) => data || "Nearest", source: () => defensePara.aimMode }, { name: "Arrow Count", id: "arrowCount", format: (data) => data, source: () => "developing" }, { name: "Rescue Chance", id: "capturechance", format: (data) => `${(data * 100).toFixed(0)}%`, source: () => defensePara.captivesodds } ], renderStats() { const container = document.getElementById('towers-stats'); if (!container) return; container.innerHTML = this.stats .map(stat => { const data = stat.source(); const value = stat.format(data); return ` <div class="module-item-flex"> <span class="attribute-name">${stat.name}:</span> <span class="attribute-value" id="towers-general-${stat.id}">${value}</span> </div> `; }) .join(''); }, statstimer: null, init() { this.renderStats(); this.statstimer = setInterval(() => this.renderStats(), 1000); }, stop() { if (this.statstimer) { clearInterval(this.statstimer); this.statstimer = null; } } }; TowersManager.renderStats(); window.updateBattleStats = function() { TowersManager.renderStats(); StatsManager.renderStats(); }; const EnemiesManager = { stats: [ { name: "Enemy Count", id: "count", format: (data) => data, source: () => enemies.length }, { name: "Wave", id: "wave", format: (data) => `${data.current}/${data.total}`, source: () => ({ current: currentWave + 1, total: State.variables.enemies.waves.length }) }, { name: "Boss Count", id: "bossCount", format: (data) => data, source: () => enemies.filter(e => e.element.className.includes('boss')).length }, { name: "Total Enemies Killed", id: "killed", format: (data) => data, source: () => D.zc.totalEnemiesKilled || 0 } ], renderStats() { const container = document.getElementById('enemies-stats'); if (!container) return; container.innerHTML = this.stats .map(stat => { const data = stat.source(); const value = stat.format(data); return ` <div class="enemies-stat module-item-flex"> <span class="enemies-stat-name">${stat.name}:</span> <span class="enemies-stat-value" id="enemies-general-${stat.id}">${value}</span> </div> `; }) .join(''); getBossHealth(); }, statstimer: null, init() { this.renderStats(); this.statstimer = setInterval(() => this.renderStats(), 1000); }, stop() { if (this.statstimer) { clearInterval(this.statstimer); this.statstimer = null; } } }; EnemiesManager.renderStats(); function endBattle(outcome) { D.zc.gameEnded = true; addLog(outcome === "victory" ? "Victory achieved!" : "Defeat! The gate has fallen!"); addLog(outcome === "victory" ? "Resources have been automatically picked up and settled. The skill book requires you to click to pick it up!" : ""); D.zc.defense = defensePara; const outcometext = outcome === "victory" ? "Battle_Victory" : "Event_Badend"; Dialog.create("", "fullpop fit"); Dialog.wikiPassage(outcometext); Dialog.open(); resetChargingBars(); hideActionButtons(); TowersManager.stop(); StatsManager.stop(); EnemiesManager.stop(); document.getElementById("start-battle").disabled = true; $("#start-battle").text(outcome === "victory" ? "Victory!" : "Defeat!"); document.querySelectorAll(".action-button").forEach(btn => btn.disabled = true); document.querySelectorAll(".skillbook-button").forEach(btn => btn.disabled = true); }; function getEnemyWithMaxHP() { if (enemies.length === 0) return null; let maxEnemy = enemies[0]; for (let i = 1; i < enemies.length; i++) { if (enemies[i].hp > maxEnemy.hp) { maxEnemy = enemies[i]; } } return maxEnemy; }; function gameLoop() { if (D.zc.gameEnded) return; ctx.clearRect(0, 0, canvas.width, canvas.height); spawnEnemy(); enemies.forEach(function(enemy) { enemy.update(); }); towers.forEach(function(tower) { tower.fire(); }); arrowPool.update(1 / fps); arrowPool.draw(); if (defenderTroop) { defenderTroop.update(); } dropitems.forEach(function(item) { }); requestAnimationFrame(gameLoop); } window.startBattle = function (){ if (D.zc.gameStarted) return; D.zc.gameStarted = true; addLog("Battle commenced!"); arrowPool.init(); initTowers(); startCharging(defensePara); EnemiesManager.init(); TowersManager.init(); StatsManager.init(); gameLoop(); }; <</script>> <</done>>
<style> .skillbook-container { left: -70px; top: -60px; position: absolute; display: flex; gap: 15px; margin: 0 auto; justify-content: center; } .skillbook-button { z-index: 5; position: relative; width: 120px; height: 55px; background: linear-gradient(145deg, #1a1a1a, #0d0d0d); border: 2px solid #3a1f1f; border-radius: 6px; color: #c9a87a; cursor: pointer; display: flex; flex-direction: column; align-items: center; justify-content: space-between; padding: 10px 5px; transition: all 0.3s ease; overflow: hidden; } .skillbook-button:disabled { opacity: 0.5; border-color: #2a1515; background: #333333; cursor: not-allowed; } .skillbook-button:disabled:hover { border-color: #2a1515; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.1); transform: none; } .skillbook-text { font-size: 12px; } .skillbook-name { font-size: 12px; text-align: center; font-weight: bold; letter-spacing: 1px; } .skillbook-count { bottom: 5px; right: 5px; position: absolute; font-size: 14px; font-weight: bold; color: #a67c52; padding: 2px 8px; } </style> <div class="skillbook-container"></div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Battle">> </div> </div> <<set _FacUpgrade = true >> <<set _currenwing = "hawkridgepass">> <<set _currenzone = $buildings["hawkridgepass"]["bastion"]>> <<set _FacHelp = "CodexEntry_Battle_DefenseCommand">> <<if Flag('miraelflag') == 1>> <<AddMonster $chars.dataMap.get("mirael")>> <<run Msg.add('notify', "[<span>Mirael</span>] Joined")>> <<SetFlag 'miraelflag' 2>> <</if>> <<run Msg.show();>>
<style> .victory-screen { min-height: 85vh; width: 70vw; background: #1a1a1a; border: 5px double #d4af37; padding: 1rem; position: relative; margin: 0 auto; } .victory-header { font-size: 2rem; color: #d4af37; text-shadow: 0 0 1rem #6b0504; text-align: center; margin-bottom: 1rem; } .reward-panel { background: rgba(43,25,28,0.9); border: 3px ridge #8b4513; padding: 0.75rem; margin-bottom: 10px; } .reward-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0.5rem; } .reward-item { padding: 0.5rem; background: rgba(26,26,26,0.7); border-left: 4px solid #6b0504; font-size: 0.9rem; } .loot-list { height: 8rem; display: flex; flex-wrap: nowrap; gap: 8px; overflow-x: auto; padding-bottom: 8px; } .loot-item { flex: 0 0 9.5rem; padding: 8px; background: rgba(26,26,26,0.7); border-left: 4px solid #6b0504; font-size: 0.9rem; } .prisoner-list { min-height: 10rem; display: grid; grid-template-columns: repeat(auto-fit, 10rem); gap: 1rem; overflow-y: auto; } .prisoner-card { width: 10rem; height: 13rem; background: #2d2d2d; border: 2px solid #4682b4; padding: 0.5rem; margin: 0; text-align: center; font-size: 0.9rem; } .prisoner-actions { display: flex; gap: 5px; margin-top: 0.5rem; flex-direction: column; align-items: center; } .prisoner-actions button { padding: 5px 0.5rem; font-size: 0.8rem; } .refugee-actions { display: flex; gap: 0.5rem; margin-top: 0.5rem; justify-content: flex-start; } .victory-seal { position: absolute; right: 1rem; bottom: 1rem; width: 7.5rem; opacity: 0.7; } .loot-list::-webkit-scrollbar, .prisoner-list::-webkit-scrollbar { height: 0.5rem; width: 0.5rem; } .loot-list::-webkit-scrollbar-thumb, .prisoner-list::-webkit-scrollbar-thumb { background: #6b0504; border-radius: 4px; } </style> <div class="victory-screen"> <div class="victory-header">Victory</div> <div class="reward-panel"> <h2 class="item-illuminated-text">Spoils of War</h2> <div class="reward-grid"> <div class="reward-item"> <h3>Slaughter’s Glory</h3> <p>Demons Slain: <<= $battle.totalEnemiesKilled>></p> <<set _GainedXP = Math.trunc($battle.totalEnemiesKilled * $times.total / 10)>> <<set _generalsid = [$battle.defense.generals.Commander.id, $battle.defense.generals.Warden.id, $battle.defense.generals.Bard.id, $battle.defense.generals.Guardian.id, $battle.defense.generals.Ranger.id, $battle.defense.generals.Strategist.id]>> <<run $chars.tempList.filter(monsterId => _generalsid.includes(monsterId)).forEach(monsterId => $chars.dataMap.get(monsterId).gainxp(_GainedXP / 6));>> <p>Experience Gained: <<= _GainedXP>></p> <p style="font-style: italic;">The EXP will be divided equally among the characters who participate in the battle!</p> <p>Piety: +5</p><<set $domain.piety = Math.clamp($domain.piety + 5 , 0, 200) >> </div> <div class="reward-item"> <<set _humanpd = random(10, $battle.totalEnemiesKilled)>> <h3>Barony's Renown</h3> <p>Refugees’ Allegiance: <<= _humanpd>> Humans</p> <p style="font-style: italic;">Word of your victory spreads, drawing more refugees to seek your protection!</p> <div class="refugee-actions"> <<button "Accept">> <<replace ".refugee-actions">>Accepted!<<run addHumanPop(_humanpd)>><<redo tag 'topbar'>><</replace>> <</button>> <<button "Reject">> <<replace ".refugee-actions">>Rejected!<set _humanpd = 0>><</replace>> <</button>> </div> </div> <div class="reward-item"> <h3 class="item-illuminated-text">Plunder</h3> <div class="loot-list"> <<inv $battlebag>> </div> </div> </div> </div> <div class="reward-panel"> <h2 class="item-illuminated-text">Rescued Monsters</h2> <<do tag "prisoner">> <div class="prisoner-list"> <<for _i range $battle.defense.captives.length>> <div class="prisoner-card"> <h3>???</h3> <p>Race: ???</p> <div class="prisoner-actions"> <<button "Examine">> <<nobr>> <<run $battle.defense.captives.splice(_i, 1);>> <<run popup('<<include "Battle_ExamineCaptive">>', { class: "popup-special", buttons: [ { text: "Ignore", action: () => popup.close() } ] })>> <<redo 'prisoner'>> <</nobr>> <</button>> </div> </div> <</for>> </div> <</do>> </div> <div style="text-align:center; margin-top:15px;"> <<silent>> <<removeclass ".UItopbar" "hidden">> <<run UIBar.stow().show()>> <<= $storage.merge($battlebag)>> <<SetFlag 'battleday' false>> <<set $battle.demonattackdeadline = 7 >> <</silent>> <<button "Return in Glory">> <<if Flag('miraelflag') < 1>> <<run Msg.add('eventcard', 'Event_Mirael_01')>> <<goto "Battle_UI">> <</if>> <<run Dialog.close();>> <</button>> </div> </div>
<<widget BattleWaves>> <<if _args[0]>> <<set _weekindex = _args[0] >> <<else>> <<set _weekindex = Math.clamp(Math.trunc($times.total / 7), 1, 19)>> <</if>> <<if _weekindex == 1>> <<set $enemies = setup.weekEnemieWaves[1] >> <<elseif _weekindex == 2>> <<set $enemies = setup.weekEnemieWaves[2] >> <<else>> <<set $enemies = generateWeekEnemyWaves(_weekindex) >> <</if>> <</widget>> <<widget GeneralFilter>> <<set _newquest = new Quest({})>> <<set _newquest.reqlevel= 10>> <<set _newquest.hasunique= true>> <<set _filtergather = filterMonsterByCondition($chars.tempList, _newquest).ids>> <<set $chars.selected = null>> <div class="itemfilter-title">General Filter</div> <div class="itemfilter"> <div class="itemfilter-list"> <<MapList _filtergather>> </div> <div class="itemfilter-info"> <div class="itemfilter-requirement-card"> <h3>Requirements</h3> <div class="itemfilter-requirement-content"> Level >= 10; </div> </div> <div> <<do tag "selected">> <<if $chars.selected>> <<Infocard $chars.selected>> <</if>> <</do>> </div> </div> <div class="itemfilter-act"> <<button "Join">> <<if !$chars.selected>> <<run UI.alert(L.selecttarget)>> <<elseif $chars.selected.worktags>> <<run UI.alert(L.Alreadywork)>> <<elseif $battle.defense.generals[_args[0]].id != "">> <<set _genpost = $battle.defense.generals[_args[0]] >> <<set _postchar = getMonsterByID($battle.defense.generals[_args[0]].id)>> <<set _postchar.worktags = "">> <<set _genpost['id'] = "" >> <<set _genpost['id'] = $chars.selected.id >> <<set $chars.selected.worktags = "🛡️" >> <<set $chars.selected = null>> <<redo _args[0]>><<run updateBattleStats()>> <<run Dialog.close()>> <<else>> <<set _genpost = $battle.defense.generals[_args[0]] >> <<set _genpost['id'] = $chars.selected.id >> <<set $chars.selected.worktags = "🛡️" >> <<set $chars.selected = null>> <<redo _args[0]>><<run updateBattleStats()>> <<run Dialog.close()>> <</if>> <</button>> <<button "Empty">> <<set _genpost = $battle.defense.generals[_args[0]] >> <<if _genpost.id != "">> <<set _postchar = getMonsterByID($battle.defense.generals[_args[0]].id)>> <<set _postchar.worktags = "">> <<set _genpost['id'] = "" >> <<redo _args[0]>><<run updateBattleStats()>> <<run Dialog.close()>> <</if>> <</button>> <<button "Close">> <<run Dialog.close()>> <</button>> </div> </div> <</widget>>
<style> .bedroom-action-panel { height: 100%; width: 100%; overflow: auto; border: 1px solid #b38736; } .bedroom-action-panel .UIbox { display: grid; grid-template-columns: 12rem 1fr; grid-template-rows: 100%; } .bedroom-btn { display: flex; align-items: center; width: 100%; padding: 12px; margin: 10px 0; background: #2d2d2d; border: 2px solid #4a4130; color: #e0d3af; cursor: pointer; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .bedroom-btn:hover { background: #4a4130; transform: translateX(10px); border-color: #9e8561; } .bedroom-btn::before { content: ''; width: 24px; height: 24px; margin-right: 10px; background-size: contain; filter: sepia(1) brightness(0.8); } #ui-dialog-titlebar { width:55vw;} .UIbox { background: none; } </style> <div class="UIbox"> <div class="nav-list-col"> <<link '<div class="nav-tab" id="bedroomsleep"><<= L.sleep>></div>'>> <<if Flag('battleday')>> <<run Msg.add("popup", L.notimesleep);>> <<else>> <<Sleep>> <</if>> <<if Flag('tutorial') == 6>><<SetFlag 'tutorial' 7>><</if>> <</link>> <<if $manage.preg.infuserDays > 0>> <<link "<div class='nav-tab' id='Girls'><<= '🤰Girls'>></div>">><<ToggleTags "nav-tab" 'Girls'>><<replace ".bedroom-action-panel">><<include "Girl">><</replace>><</link>> <</if>> <<if Flag('tutorial') >= 6>> <<link "<div class='nav-tab' id='codex'><<= '✍Codex'>></div>">><<ToggleTags "nav-tab" 'codex'>><<replace ".bedroom-action-panel">><<include "Codex">><</replace>><</link>> <<link "<div class='nav-tab' id='Jour'><<= '⁉️Journal'>></div>">><<ToggleTags "nav-tab" 'Jour'>><<replace ".bedroom-action-panel">><<include "Journal">><</replace>><</link>> <<link "<div class='nav-tab' id='Report'><<= '📊Report'>></div>">><<ToggleTags "nav-tab" 'Report'>><<replace ".bedroom-action-panel">><<include "DomainReport">><</replace>><</link>> <</if>> <<link "<div class='nav-tab'><<= setup.langbank.savegame[$lang]>></div>" "Bedroom_UI">><<run UI.saves()>><</link>> </div> <div class="bedroom-action-panel"> <center><h1>Calendar</h1> <h2><<= setup.TimeSystem.getCurrentInfo()>> <br>Day $times.total</h2></center> </div>
<<SimpleUI "Bedroom_MorningNews" "fullpop">>
<style> #story { margin: 0em 0em 0em 17.5em; } .news-container { position: relative; height: 100%; background: #1a1a1a; color: #e0d3af; border: 3px double #4a4130; box-shadow: 0 0 1.25rem rgba(106, 81, 26, 0.3); margin: 0; } .news-banner { text-align: center; max-height: 45%; position: relative; overflow: hidden; border-bottom: 5px groove #8b4513; } .news-banner img { max-height: 25rem; object-fit: scale-down; } .news-category { z-index:10; position: absolute; top: 1.25rem; left: 1.25rem; padding: 0.5rem 1rem; background: rgba(107,5,4,0.8); border: 2px solid #6b0504; font-size: 1.1rem; text-shadow: 2px 2px 3px #000; } .news-body { display: grid; grid-template-columns: repeat(2, minmax(25rem, 1fr)); gap: 2rem; padding: 1rem; } .news-panel { background: rgba(43, 25, 28, 0.9); border: 2px ridge #4a4130; padding: 1.5rem; position: relative; transition: transform 0.3s ease; } .news-panel:hover { border-color: #9e8561; } .news-title { font-size: 1.5rem; color: #d4af37; border-bottom: 2px dotted #8b4513; margin-bottom: 1rem; text-shadow: 0 0 0.625rem rgba(212, 175, 55, 0.3); } .news-item { margin: 0.5rem 0px; padding: 0px 0.5rem 0 0.5rem; background: rgba(26, 26, 26, 0.5); border-left: 3px solid #8b4513; transition: all 0.3s ease; } .news-item:hover { border-left-color: #d4af37; background: rgba(43, 25, 28, 0.7); } .news-timestamp { color: #756d5e; font-size: 0.9rem; margin-bottom: 0.5rem; } .news-parchment-scroll { position: absolute; right: -1.875rem; bottom: -1.875rem; width: 7.5rem; opacity: 0.3; animation: news-scroll-drift 15s infinite linear; } @keyframes news-scroll-drift { 0% { transform: rotate(5deg) translateY(0); } 50% { transform: rotate(-5deg) translateY(20px); } 100% { transform: rotate(5deg) translateY(0); } } .news-seal { bottom: -1rem; right: 35%; height: 8rem; position: absolute; cursor: pointer; } .news-seal img { width:8rem; } .news-seal img:hover { transform: scale(1.05); filter: brightness(1.2); } </style> <<set _newsimage = [ 'event/memory/hole.webp', 'event/memory/run.webp', 'event/memory/lost.webp', 'event/memory/entry.webp' ].random(); >> <div class="news-container"> <<if passage() != "Meeting_UI">> <div class="news-banner"> <img @src="setup.ImagePath+_newsimage" class="news-banner-image"> </div> <</if>> <div class="news-body"> <div class="news-panel"> <h3 class="news-title"><<= setup.langbank.worldinfo[$lang]>></h3> <div class="news-item"> <h4><span class="urgent-mark" style="color:#6b0504">⚔</span><<= L.survivors>>: <<= "4??M"+" (War chaos, no precise stats)">> </h4> </div> <div class="news-item"> <h4><span class="urgent-mark" style="color:#6b0504">⚔</span><<= L.fiefdoms>>: <<= "2???"+" (War chaos, no precise stats)">> </h4> </div> <<if Flag('wartaxs')>> <div class="news-item"> <h4><<if $battle.demonattackdeadline == 0>>The demons are massing and will attack the Mountain Pass today!! <<else>>The Dark Legion are expected to attack in <<= $battle.demonattackdeadline>> days! <</if>></h4> </div> <div class="news-item"> <h4><<= L.remaining>> <<= $domain.wartaxdeadline>> <<= L.day>>,<<= L.taxsubmit>> <<= $domain.wartax[0]>>g <<= L.taxes>></h4> </div><</if>> </div> <div class="news-panel"> <h3 class="news-title"><<= L.territory>></h3> <div class="news-item"> <h4><<= "Building Output">>: <br> <<= L.gold>>:$buildingEffects.resources.gold, <<= L.food>>:$buildingEffects.resources.food, <<= L.wood>>:$buildingEffects.resources.wood, <<= L.stone>>:$buildingEffects.resources.stone, <<= L.ore>>:$buildingEffects.resources.ore </h4> <br> <h4><<= "Work Output">>: (<<= "Workers">>:<<= $job?.summary?.workers || 0>>)<br> <<= L.gold>>:<<= $job?.summary?.income?.gold || 0>>, <<= L.food>>:<<= $job?.summary?.income?.food || 0>>, <<= L.wood>>:<<= $job?.summary?.income?.wood || 0>>, <<= L.stone>>:<<= $job?.summary?.income?.stone || 0>>, <<= L.ore>>:<<= $job?.summary?.income?.ore || 0>>, <<= "Crystal">>:<<= $job?.summary?.income?.crystal || 0>> </h4> </div> <div class="news-item"> <h4><<= L.currentgold>>: <<= $storage.count('gold')>>g</h4> </div> </div> </div> <<if passage() != "Meeting_UI">> <div class="news-seal"> <<link [img[setup.ImagePath + 'ui/icon/waxsealred.png']]>> <<if $gamestatus.isLoad == true>><<set $gamestatus.isLoad = false >><</if>> <<run $gamestatus.autoSave("Morning")>> <<goto 'MainHall_UI'>><<run Dialog.close()>> <<NextDayEvent>> <</link>> </div> <</if>> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Bedroom">> </div> </div> <<done>> <<if Flag('tutorial') == 6>> <<append "#bedroomsleep">> <<set _tutorl = [ { el: '#bedroomsleep', text: L.sleepdesc} ] >> <<run UIBar.show().stow();>> <<TutorList _tutorl>> <</append>> <</if>> <</done>> <<run Msg.show();>>
<style> .UIbox { background: url(images/ui/bg/blueprint.webp) #2d2d2d; } </style> <div class="UIbox module-border"> <div class="nav-list-col"> <<for _wing range Object.keys($buildings)>> <<capture _wing>> <<link '<div class="nav-tab" @id="_wing"><<= setup.bdwingdata[_wing]["name"][$lang]>></div>'>> <<ToggleTags "nav-tab" _wing>><<set _currenwing = _wing>><<unset _currenzone>><<redo "zones">> <<if Flag('tutorialWing')>> <<SetFlag 'tutorialWing' false >> <<if $("#tutorialWing").length>> <<remove "#tutorialWing">> <</if>> <<SetFlag 'tutorialZone'>> <<redo "tutorial">> <</if>> <</link>> <</capture>> <</for>> </div> <<do tag "zones">> <<if def _currenwing>> <div class="facility-slots-panel facility-zones"> <h2><<= setup.bdwingdata[_currenwing]["name"][$lang]>></h2> <h4><<= setup.bdwingdata[_currenwing]["desc"][$lang]>></h4> <<if visited() <= 1 >> <</if>><div class="facility-slots-list"> <<for _zone range Object.keys($buildings[_currenwing])>> <<set _zonefull = $buildings[_currenwing][_zone] >> <<capture _zone _zonefull>> <div class="facility-card" @id="_zone"> <<set _linktext = setup.bdzonedata[_zone].name[$lang]>> <<csslink _linktext "facility-image">> <<set _currenzone = _zonefull>><<redo "zones slots">> <<if Flag('tutorialZone')>> <<SetFlag 'tutorialZone' false >> <<if $("#tutorialZone").length>> <<remove "#tutorialZone">> <</if>> <<SetFlag 'tutorialUnit'>> <<redo "tutorial">> <</if>> <</csslink>> <div class="zones-stat-item"> <style> #bdprogress progress{ max-width: 6rem; } </style> <span id="bdprogress">Lv: _zonefull.lv <progress @value="_zonefull.lv" @max="getzonedata(_zonefull.id,'maxlv')"></progress><<= "(" + _zonefull.lv + "/" + getzonedata(_zonefull.id,'maxlv') + ")">></span> </div> <div class="buttons-group-row" style="padding: 0.5rem 0;"> <<if _zonefull.lv >= setup.bdzonedata[_zone].maxlv>> <<button "Maximum">><</button>> <<else>> <<set _zonecost = setup.bdzonedata[_zone][_zonefull.lv+1].cost >> <<capture _zonecost>> <<set _buttontext = _zonefull.lv == 1? '🔺Upgrade' : '🔺Upgrade' >> <<button _buttontext>> <<run Dialog.create();>> <<run Dialog.wiki("<<ZoneUpgrade _zonefull _zonecost>>")>> <<run Dialog.open();>> <</button>> <</capture>> <</if>> <<button "🔨Build">> <<set _currenzone = _zonefull>><<redo "zones slots">> <<if Flag('tutorialZone')>> <<SetFlag 'tutorialZone' false >> <<if $("#tutorialZone").length>> <<remove "#tutorialZone">> <</if>> <<SetFlag 'tutorialUnit'>> <<redo "tutorial">> <</if>> <</button>> </div> </div> <</capture>> <</for>> </div> </div> <</if>> <</do>> <<do tag "zones slots">> <<if def _currenzone>> <div class="facility-slots-panel facility-units"> <h4><<= getzonedata(_currenzone.id,"name")>></h4> <p><<= getzonedata(_currenzone.id,"desc")>></p> Total Effect: <div class="stat-item"><<= EffectUtils.autoDesc(getzonedata(_currenzone.id,_currenzone.lv,"effect"))>></div> <<= EffectUtils.zoneAll(_currenzone).desc>> <div class="facility-slots-list"> <<for _index range $buildingEffects["slots"][_currenzone.id]>> <<set _currenunit = _currenzone[_index] >> <<capture _index _currenunit>> <<if _currenunit>> <div class="facility-slot-item filled"> <p><<= getunitdata(_currenunit.id,"name")>></p> <p>Lv:<<= _currenunit.lv>></p> <<button "❌Demolish">> <<run Dialog.create();>> <<run Dialog.wiki("<<BuildingDemolish _currenunit _index>>")>> <<run Dialog.open();>> <</button>> <<button "🔺Upgrade">> <<run Dialog.create();>> <<run Dialog.wiki("<<BuildingUpgrade _currenunit _index>>")>> <<run Dialog.open();>> <</button>> </div> <<else>> <div class="facility-slot-item" @id="'unit3'+ _index"> <<button "⚒️Build">> <<if Flag('tutorialUnit')>> <<SetFlag 'tutorialUnit' false >> <<if $("#tutorialUnit").length>> <<remove "#tutorialUnit">> <</if>> <</if>> <<run Dialog.create();>> <<run Dialog.wiki("<<BuildingNew _currenzone _index>>")>> <<run Dialog.open();>> <</button>></div> <</if>> <</capture>> <</for>> </div> </div><</if>> <</do>> </div>
<<widget TowerInfo>> <<set _arrowConfig = getunitdata(_args[0].id, _args[0].lv, 'arrowRef') >> <<set _unitConfig = setup.arrow[_arrowConfig.type]['levels'][_arrowConfig.lv]['behavior']>> <<= getunitdata(_args[0].id,'name')>><br> <<= getunitdata(_args[0].id,'desc')>><br> Lv: _args[0].lv<br>Arrow<br> Arrow Type: _unitConfig.type<br> Arrow lv: _unitConfig.lv<br> Physical Damage: <<= _unitConfig['physicalDamage']>><br> Fire Rate: <<= _unitConfig['fireRate']>><br> Projectile Speed: <<= _unitConfig['projectileSpeed']>> <</widget>> <<widget BuildUnitByIndexId>> <<set _bdunit = setup.bdunitdata[_args[1]] >> <<if $domain.getWorkforce() <= $domain.getUnitsWF()>> <<run Msg.add('notify', langBank("moreworkforce"))>> <<elseif !$storage.compare(_bdunit["1"]["cost"])>> <<run Msg.add('notify', langBank("moreresources"))>> <<else>> <<= $storage.unmerge(_bdunit[1]["cost"])>> <<run changeEffects(0, 1, _bdunit.type, _args[0], _args[1])>> <<run Msg.add('notify', 'Building constructed!');>> <<unset _showunit>> <<redo "topbar build zone unit">> <</if>> <</widget>> <<widget UpgradeUnitByIndexId>> <<set _bdunit = setup.bdunitdata[_args[1]] >> <<set _bdzone = setup.bdzonedata[_bdunit.type] >> <<set _bdzonedata = $buildings[_bdzone.type][_bdunit.type]>> <<set _nowlv = _bdzonedata[_args[0]].lv >> <<set _newlv = _nowlv +1 >> <<if $domain.getWorkforce() <= $domain.getUnitsWF()>> <<run Msg.add('notify', langBank("moreworkforce"))>> <<elseif !$storage.compare(_bdunit[_newlv]["cost"])>> <<run Msg.add('notify', langBank("moreresources"), 0)>> <<else>> <<= $storage.unmerge(_bdunit[_newlv]["cost"])>> <<run changeEffects(_nowlv, _newlv, _bdunit.type, _args[0], _args[1])>> <<run Msg.add('notify', 'Building Upgraded!');>> <<set _showunit = _bdzonedata[_args[0]] >> <<redo "topbar build zone unit">> <</if>> <</widget>> <<widget DemolishUnitByIndexId>> <<set _bdunit = setup.bdunitdata[_args[1]] >> <<if !_demolishnotice>> <<set _demolishnotice = true >> <<run Msg.add('notify', 'Demolition does not return resources!');>> <<run Msg.add('notify', 'Are you sure you want to demolish?');>> <<else>> <<run changeEffects(1, 0, _bdunit.type, _args[0], _args[1])>> <<run Msg.add('notify', 'Building has been demolished!');>> <<unset _showunit>> <<redo "unit topbar build zone">> <</if>> <</widget>> <<widget ZoneUpgrade>> <<set _zonevs = _args[0] >> <<set _zonecost = _args[1] >> <<set _oldlv = _args[0].lv >> <div class="upgrade-container"> <div class="upgrade-unitname"><<= getzonedata(_zonevs.id,"name")>></div> <div class="upgrade-unitdesc"><<= getzonedata(_zonevs.id,"desc")>></div> <table class="upgrade-table"> <thead> <tr> <th class="attribute">Attribute</th> <th>Current Level</th> <th>Next Level</th> </tr> </thead> <tbody> <tr> <td class="attribute">Level</td> <td><<= _zonevs.lv>></td> <td class="upgrade-highlight"> <<if _oldlv >= getzonedata(_zonevs.id,"maxlv")>> - <<else>> <<= _zonevs.lv+1>> <</if>> </td> </tr> <tr> <td class="attribute">Effect</td> <td><<= EffectUtils.autoDesc(getzonedata(_zonevs.id, (_zonevs.lv), "effect"))>></td> <td class="upgrade-highlight"> <<if _oldlv >= getzonedata(_zonevs.id,"maxlv")>> - <<else>> <<= EffectUtils.autoDesc(getzonedata(_zonevs.id, (_zonevs.lv +1), "effect"))>> <</if>> </td> </tr> <tr> <td class="attribute">Upgrade Cost</td> <td>-</td> <td> <<if _oldlv >= getzonedata(_zonevs.id,"maxlv")>> - <<else>> <<for _res, _amount range _zonecost>> <span class="res-icon res-_res.type"><<=_res>> x<<=_amount>>,</span> <</for>> <</if>> </td> </tr> </tbody> </table> <<if _oldlv >= getzonedata(_zonevs.id,"maxlv")>> Maximum lv! <<else>> <<button "Upgrade">> <<if $storage.compare(_zonecost)>> <<= $storage.unmerge(_zonecost)>> <<run changeEffects(_zonevs.lv, (_zonevs.lv + 1), _zonevs.id)>> <<redo>><<run Dialog.close();>> <<else>> <<run UI.alert(setup.langbank.moreresources[$lang])>> <</if>> <</button>> <</if>> </div> <</widget>> <<widget BuildingNew>> <style> .build-container { margin: 1rem auto; } .build-card { border: 2px solid #754600; box-shadow: 0 0 10px rgba(255, 215, 0, 0.5); padding: 1rem; margin-bottom: 1rem; } .unit-effect, .build-cost { margin-bottom: 0.5rem; text-align: left; text-indent: -5.6rem; padding-left: 5.6rem; } </style> <<if _args[0].id == "bastion" && (_args[1] == 0 || _args[1] == 2)>> <div class="build-container"> <<set _newsr = getunitdata("wall_defense",1,"cost")>> <p>In order to seal off the valley, only a wall could be built here!</p> <div class="build-card"> <div class="upgrade-unitname"><<= getunitdata("wall_defense","name")>></div> <div class="upgrade-unitdesc">Description: <<= getunitdata("wall_defense","desc")>></div> <div class="unit-effect">Unit Effect: <<= EffectUtils.autoDesc(getunitdata("wall_defense",1,"effect"))>></div> <div class="build-cost">Build Cost: <span class="upgrade-highlight"> <<for _res, _amount range _newsr>> <span class="res-icon res-_res.type"><<=_res>> x<<=_amount>>,</span> <</for>> </span> </div> <<button "Build">> <<if $storage.compare(getunitdata("wall_defense",1,"cost"))>> <<= $storage.unmerge(getunitdata("wall_defense",1,"cost"))>> <<run changeEffects(0, 1, _args[0].id, _args[1], "wall_defense")>> <<redo>><<run Dialog.close();>> <<else>> <<run UI.alert(setup.langbank.moreresources[$lang])>> <</if>> <</button>> </div> <<elseif _args[0].id == "bastion" && (_args[1] == 1)>> <div class="build-container"> <<set _newsr = getunitdata("gate_defense",1,"cost")>> <p>In order to allow the circulation of goods and people, <br>only a gate can be built here!</p> <div class="build-card"> <div class="upgrade-unitname"><<= getunitdata("gate_defense","name")>></div> <div class="upgrade-unitdesc">Description: <<= getunitdata("gate_defense","desc")>></div> <div class="unit-effect">Unit Effect: <<= EffectUtils.autoDesc(getunitdata("gate_defense",1,"effect"))>></div> <div class="build-cost">Build Cost: <span class="upgrade-highlight"> <<for _res, _amount range _newsr>> <span class="res-icon res-_res.type"><<=_res>> x<<=_amount>>,</span> <</for>> </span> </div> <<button "Build">> <<if $storage.compare(getunitdata("gate_defense",1,"cost"))>> <<= $storage.unmerge(getunitdata("gate_defense",1,"cost"))>> <<run changeEffects(0, 1, _args[0].id, _args[1], "gate_defense")>> <<redo>><<run Dialog.close();>> <<else>> <<run UI.alert(setup.langbank.moreresources[$lang])>> <</if>> <</button>> </div> <<else>> <<set _chunits = Object.values(setup.bdunitdata) .filter(b => b.type === _args[0].id)>> <div class="build-container"> <<for _chunit range _chunits>> <<capture _chunit>> <<set _newsr = _chunit[1]["cost"] >> <div class="build-card"> <div class="upgrade-unitname"><<= getunitdata(_chunit.id,"name")>><<= setup.bdunitdata[_chunit.id].jobs ? " [+Job]": "">></div> <div class="upgrade-unitdesc">Description: <<= getunitdata(_chunit.id,"desc")>></div> <div class="unit-effect">Unit Effect: <<= EffectUtils.autoDesc(_chunit[1]["effect"])>></div> <div class="build-cost">Build Cost: <span class="upgrade-highlight"> <<for _res, _amount range _newsr>> <span class="res-icon res-_res.type"><<=_res>> x<<=_amount>>,</span> <</for>>Workforce x 1 </span> </div> <<button "Build">> <<if $domain.getWorkforce() <= $domain.getUnitsWF()>> <<run UI.alert(setup.langbank.moreworkforce[$lang])>> <<elseif !$storage.compare(_chunit["1"]["cost"])>> <<run UI.alert(setup.langbank.moreresources[$lang])>> <<else>> <<= $storage.unmerge(_chunit[1]["cost"])>> <<run changeEffects(0, 1, _args[0].id, _args[1], _chunit.id)>> <<redo>><<run Dialog.close();>> <</if>> <</button>> </div> <</capture>> <</for>> </div> <</if>> <</widget>> <<widget BuildingUpgrade>> <<set _oldlv = _args[0].lv >> <<set _newlv = _args[0].lv + 1 >> <<set _slotIndex = _args[1] >> <<set _unitId = _args[0].id >> <<set _zoneId = getunitdata(_unitId,"type") >> <<set _upgradecost = getunitdata(_unitId, _newlv, "cost") >> <<set _upgradeeffect = getunitdata(_unitId, _newlv, "effect") >> <div class="upgrade-container"> <div class="upgrade-unitname"><<= getunitdata(_unitId,"name")>><<= setup.bdunitdata[_args[0].id].jobs ? "[+Job]": "">></div> <div class="upgrade-unitdesc"><<= getunitdata(_unitId,"desc")>></div> <table class="upgrade-table"> <thead> <tr> <th class="attribute">Attribute</th> <th>Current Level</th> <th>Next Level</th> </tr> </thead> <tbody> <tr> <td class="attribute">Level</td> <td><<= _oldlv>></td> <td class="upgrade-highlight"> <<if _oldlv >= getunitdata(_unitId,"maxlv")>> - <<else>> <<= _oldlv+1>> <</if>> </td> </tr> <tr> <td class="attribute">Effect</td> <td><<= EffectUtils.autoDesc(getunitdata(_unitId,_oldlv,"effect"))>></td> <td class="upgrade-highlight"> <<if _oldlv >= getunitdata(_unitId,"maxlv")>> - <<else>> <<= EffectUtils.autoDesc(_upgradeeffect)>> <</if>> </td> </tr> <tr> <td class="attribute">Upgrade Cost</td> <td>-</td> <td> <<if _oldlv >= getunitdata(_unitId,"maxlv")>> - <<else>> <<for _res, _amount range _upgradecost>> <span class="res-icon res-_res.type"><<=_res>> x<<=_amount>>,</span> <</for>>Workforce x 1 <</if>> </td> </tr> </tbody> </table> <<if _oldlv >= getunitdata(_unitId,"maxlv")>> Maximum lv! <<else>> <<button "Upgrade">> <<if $domain.getWorkforce() <= $domain.getUnitsWF()>> <<run UI.alert(setup.langbank.moreworkforce[$lang])>> <<elseif !$storage.compare(_upgradecost)>> <<run UI.alert(setup.langbank.moreresources[$lang])>> <<else>> <<= $storage.unmerge(_upgradecost)>> <<run changeEffects(_oldlv, _newlv, _zoneId, _slotIndex, _unitId)>> <<redo>><<run Dialog.close();>> <</if>> <</button>> <</if>> </div> <</widget>> <<widget BuildingDemolish>> <<set _oldlv = _args[0].lv >> <<set _slotIndex = _args[1] >> <<set _unitId = _args[0].id >> <<set _zoneId = getunitdata(_unitId,"type") >> <div> <p>Curren Unit: <<= getunitdata(_unitId,"name")>> </p> <p>Curren lv: _oldlv </p> <p>Curren Effect: <<= EffectUtils.autoDesc(getunitdata(_unitId,_oldlv,"effect"))>></p> <p>Demolition does not return resources</p> <<button "Demolish">> <<run changeEffects(_oldlv, 0, _zoneId, _slotIndex, _unitId)>> <<redo>><<run Dialog.close();>> <</button>> </div> <</widget>>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Building">> </div> </div> <<if Flag('tutorial') == 11>> <<set $domain.maps[1] = "MapVale02" >> <<pickup $storage "wood" 20 "food" 20 "gold" 200>> <<JournalActive "main_rebuild">> <<run Msg.add('wiki', "<<run UI.alert('Hawkridge Vale is secure! Garvin’s gathered resources are stored in the warehouse. With the valley revitalized by a mysterious force, it’s time to restore it. <br>Activate quest: Resettle Refugees and Rebuild Hawkridge Vale.');>>")>> <<SetFlag 'tutorial' 12>> <<SetFlag 'endintro'>> <<SetFlag 'tutorialWing'>> <<run Msg.add('notify', "Unlock [Hawkridge Vale] Map")>> <<run Msg.add('notify', "Unlock [Building Workshop] Building")>> <<run Msg.add('notify', "Unlock [Verdant Wilds] [Hawkridge Hamlet] [Rockvein Mines] Zone")>> <<run Msg.add('notify', "Activate [Vale Rebuild] Quest")>> <<run Msg.add('notify', "+ [20 Wood] + [20 Food] + [200 Good]")>> <</if>> <<do tag "tutorial">> <<done>> <<if Flag('tutorialWing')>> <<append "#hawkridgevale">> <<set _tutorl = [ { el: '#hawkridgevale', text: "Wings → Zones → Units, the building system is a three-tier structure. Clicking expands the next level of content. This is Wings."}, { el: '#verdant_wilds', text: "Wings → Zones → Units. This is Zones. Clicking expands the next level of content."}, { el: '#unit30', text: "Wings → Zones → Units. This is Units. Clicking expands the next level of content."} ] >> <<TutorList _tutorl>> <</append>> <<SetFlag 'tutorialWing' false>> <</if>> <</done>> <</do>> <<run Msg.show();>>
<style> .calendar-container { color: var(--text-primary); height: 34.5rem; width: 34rem; background: var(--bg-body); border: 1px solid var(--color-c); overflow: hidden; } .calendar-header { background:var(--bg-body); padding:0.5rem 1rem; border-bottom:1px solid var(--color-e); display:flex; justify-content:space-between; align-items:center; } .month-year { text-transform: uppercase; font-size: 1.2rem; font-weight: bold; color: var(--color-e); } .nav-buttons { display:flex; gap:0.5rem; } .nav-btn { background: var(--bg-surface); border: 1px solid var(--color-c); width: 1.8rem; border-radius: 0.25rem; cursor: pointer; } .nav-btn:hover { background:var(--bg-deepred); border-color:var(--color-e); } .weekdays { display:grid; grid-template-columns:repeat(7,1fr); background:var(--bg-midred); padding:0.3rem 0; border-bottom:1px solid var(--cm-border); } .weekday { text-align:center; font-size:0.8rem; font-weight:bold; color:var(--color-e); } .calendar-grid { display:grid; grid-template-columns:repeat(7,1fr); gap:0.15rem; padding:0.15rem; min-height:24rem; } .day-cell { aspect-ratio:1; background:var(--bg-surface); border:1px solid var(--cm-border); padding:0.2rem; position:relative; cursor:pointer; display:flex; flex-direction:column; transition:all 0.2s; } .day-cell:hover { border-color:var(--color-e); background:var(--bg-surface); } .day-cell.today { background: var(--tr-common); border-color: var(--bg-deepred); } .day-number { font-size: 0.85rem; font-weight: bold; margin-bottom: 0.15rem; } .day-cell.today .day-number { color:var(--color-e); } .event-dots { flex-grow: 1; display: flex; flex-wrap: wrap; gap: 0.25rem; justify-content: center; align-items: center; } .event-dot { width: 0.75rem; height: 0.75rem; border-radius: 50%; } .event-dot.weekly { background:#8a2be2; } .event-dot.monthly { background:#ff8c00; } .event-dot.special { background:#dc143c; } .event-item { background:var(--bg-surface); border-left:0.25rem solid var(--color-e); padding:0.6rem; border-radius:0.15rem; margin-bottom:0.5rem; } .event-name { font-weight:bold; color:var(--color-e); } .event-type { font-size:0.7rem; text-transform:uppercase; color:var(--text-secondary); } .empty-state { text-align:center; color:var(--text-secondary); font-style:italic; padding:2rem; } .event-icon { font-size: 1rem; line-height: 1; } </style> <div class="calendar-container" id="gameCalendar"> <div class="calendar-header"> <div class="month-year" id="monthYear"></div> <div class="nav-buttons"> <button class="nav-btn" id="prevMonth">«</button> <button class="nav-btn" id="todayBtn">•</button> <button class="nav-btn" id="nextMonth">»</button> </div> </div> <div class="weekdays"> <div class="weekday">Sun</div><div class="weekday">Mon</div><div class="weekday">Tue</div> <div class="weekday">Wed</div><div class="weekday">Thu</div><div class="weekday">Fri</div><div class="weekday">Sat</div> </div> <div class="calendar-grid" id="calendarGrid"></div> </div> <<done>> <<script>> const GameCalendar = { viewYear : null, viewMonth : null, init() { this.viewYear = State.variables.times.year; this.viewMonth = State.variables.times.month; this.render(); document.getElementById('prevMonth').onclick = () => { this.viewMonth--; if (this.viewMonth < 1) { this.viewMonth = 12; this.viewYear--; } this.render(); }; document.getElementById('nextMonth').onclick = () => { this.viewMonth++; if (this.viewMonth > 12) { this.viewMonth = 1; this.viewYear++; } this.render(); }; document.getElementById('todayBtn').onclick = () => { this.viewYear = State.variables.times.year; this.viewMonth = State.variables.times.month; this.render(); }; }, render(year = this.viewYear, month = this.viewMonth) { this.viewYear = year; this.viewMonth = month; const t = State.variables.times; const cfg = setup.TimeConfig; document.getElementById('monthYear').textContent = `${cfg.monthsOfYear[month-1]}`; const grid = document.getElementById('calendarGrid'); grid.innerHTML = ''; const firstWeekday = setup.TimeSystem.getWeekday(year, month, 1); for (let i = 0; i < firstWeekday; i++) { grid.appendChild(this.createEmptyCell()); }; for (let day = 1; day <= cfg.daysInMonth; day++) { const cell = document.createElement('div'); cell.className = 'day-cell'; cell.dataset.year = year; cell.dataset.month = month; cell.dataset.day = day; if (year === t.year && month === t.month && day === t.day) { cell.classList.add('today'); }; const num = document.createElement('div'); num.className = 'day-number'; num.textContent = day; cell.appendChild(num); const events = setup.TimeSystem.getEventsForDay(year, month, day); if (events.length) this.addEventDots(cell, events); cell.onclick = () => this.showModal(year, month, day, events); grid.appendChild(cell); } }, addEventDots(cell, events) { if (events.length === 0) return; const container = document.createElement('div'); container.className = 'event-dots'; const iconEvents = events.filter(ev => ev.icon).slice(0, 3); if (iconEvents.length > 0) { iconEvents.forEach(ev => { const iconSpan = document.createElement('span'); iconSpan.className = 'event-icon'; iconSpan.textContent = ev.icon; iconSpan.title = ev.name; container.appendChild(iconSpan); }); } else { const dots = document.createElement('div'); dots.className = 'event-dots'; const count = {}; events.forEach(e => count[e.type] = (count[e.type] || 0) + 1); Object.keys(count).forEach(type => { const dot = document.createElement('div'); dot.className = `event-dot ${type}`; dot.title = `${count[type]} ${type} event(s)`; dots.appendChild(dot); }); container.appendChild(dots); } cell.appendChild(container); }, showModal(year, month, day, events) { const list = document.createElement('div'); list.innerHTML = ''; if (events.length === 0) { list.innerHTML = '<div class="empty-state">No events today</div>'; } else { events.forEach(ev => { const item = document.createElement('div'); item.className = 'event-item'; item.innerHTML = ` <div class="event-name">${ev.icon || ''} ${ev.name}</div> <div class="event-type">${ev.type}</div> <div>${ev.desc}</div> `; list.appendChild(item); }); }; popup(list.innerHTML, { class: "popup-special", buttons: [ { text: "Close", action: () => popup.close() } ] }); }, createEmptyCell() { const d = document.createElement('div'); d.className = 'day-cell'; d.style.opacity = '0'; return d; } }; GameCalendar.init(); <</script>> <</done>>
<<widget "CardPop">> <<set $cards = [] >> <<silent>> <<CardLottery _args[0] _args[1] _args[2]>> <</silent>> <<run Dialog.create("cards", "fullpop");>> <<run Dialog.wiki("<<CardShow _args[0] _args[1]>>") >> <<run Dialog.open()>> <</widget>> <<widget CardLottery>> <<set _cardnum = _args[2] ? _args[2] : 1>> <<for _i range _cardnum>> <<switch MonsterBreeder.run(_args[0], _args[1])>> <<case 1>> <<Get2 _args[0] _args[1]>> <<case 2 3>> <<Get3 _args[0] _args[1]>> <<case 4>> <<Get4 _args[0] _args[1]>> <<case 5 6 7 8>> <<Get5 _args[0] _args[1]>> <<set _args[0].fer.rate = 0>> <<set _args[1].fer.rate = 0>> <<default>> <</switch>> <</for>> <</widget>> <<widget "CardShow">> <div class="card-title">Ritual Rewards</div> <div class="card-row" id="card-row"> <<for _card range $cards.length>> <<capture _card>> <div class="card-container"> <div class="card-inner" @id='"card-"+_card'> <div class="card-back">Flip Card</div> <div @class='"card-front card-type-" + $cards[_card].type'> <<= $cards[_card].wiki>> <div class="card-cardlabel"> <h2><<= $cards[_card].type.toUpperFirst()>></h2> <span @class='"card-affix card-" + $cards[_card].affix'> <<= $cards[_card].affix>> </span> </div> <div class="card-select"><<button "Select">><<= $cards[_card].select>><<run Dialog.close()>><</button>></div> </div> </div> </div> <</capture>> <</for>> </div> <<done>> <<script>> for (let i = 0; i < State.variables.cards.length; i++) { const card = document.querySelector(`#card-${i}`); if (card) { card.addEventListener('click', (e) => { if (e.target.tagName !== 'BUTTON') { card.classList.toggle('card-flipped'); } }); } else { console.error(`Card with ID #card-${i} not found`); } } <</script>> <<toggleclass ".card-inner" "card-flipped">> <</done>> <</widget>> <<widget "ItemCardShow">> <<set _item = _args[0] >> <div class="item-detail-card"> <div class="item-ornate-frame"> <div id="item-detailImg" class="item-featured"> [img[setup.ImagePath + getiteminfo(_item,"img")]] </div> </div> <div class="item-title"> <h2 id="item-itemName" class="item-illuminated-text"> <<= getiteminfo(_item,"name")>> </h2> <div class="item-title-decoration"></div> </div> <div class="item-scroll-container"> <div class="item-parchment"> <p id="item-itemDesc" class="item-calligraphic-text"> <<= getiteminfo(_item,"desc")>> <div class="item-heraldic-overlay"> <<if getiteminfo(_item,"duration")>> <span>⏳: <<if getiteminfo(_item,"duration") == -1>><<= setup.langbank.permanent[$lang]>><<else>><<= getiteminfo(_item,"duration")>><<= setup.langbank.day[$lang]>><</if>></span> <</if>> <span><<= setup.langbank.quality[$lang]>>: <<= getiteminfo(_item,"lv")>> </span> <span class="item-rarity-star"> <<NumToStars `getiteminfo(_item,"lv")`>> </span> </div> <div class="item-heraldic-overlay"> <span><<= setup.langbank.quantity[$lang]>>: x<<if _args[1]>><<= _args[1]>><<else>><<= $storage.count(_item)>><</if>></span> <span><<= setup.langbank.price[$lang]>>: <<= getiteminfo(_item,"price")>>g</span> <span><<= setup.langbank.type[$lang]>>: <<= getiteminfo(_item,"type")>></span> </div> </p> </div> </div> <</widget>> <<widget "CharacterCardShow">> <style> .attr-other-attributes { color: #2A1F1F; border-left: 4px solid #E8B923; margin-top: 0.25rem; } .attr-other-attributes .attr-grid { color: white; display: grid; grid-template-columns: 3rem 1fr; gap: 0.25rem; } .character-ornate-frame { position: relative; z-index: -1; height: 12rem; justify-self: center; width: 60%; margin-bottom: -3rem; } .character-decoration { line-height: 1.67; text-align: right; right: 0.5rem; top: -8.5rem; display: flex; position: absolute; flex-direction: column; } </style> <<set _item = _args[0] >> <div class="item-detail-card"> <div class="character-ornate-frame"> <div id="item-detailImg" class="item-featured"> <<if _args[0].type == "wrong">> [img[setup.ImagePath + _args[0].img]] <<else>> <div @id="_args[0].id"></div> <<displayHeadPortrait _args[0] _args[0].id>> <</if>> </div> </div> <div class="item-title"> <div><<= _args[0].name>> <<= setup.gender2pic[_args[0].gender]>></div> <div class="character-decoration"> <span>Kin: <<= _args[0].race.toUpperFirst()>></span> <span>Folk: <<= setup.racedata[_args[0].race][_args[0].rank]["name"][$lang]>></span> <span>Look: <<= _args[0].getLook>></span> <span>Daily: <<= _args[0].dailyAP.toFixed(1)>>❤️</span> <span>Growth: <<= _args[0].getGrowth>></span> </div> </div> <div class="item-scroll-container"> <div class="item-parchment"> <p id="item-itemDesc" class="item-calligraphic-text"> <<RadarImage _item>> <div class="attr-other-attributes"> <div class="attr-grid"> <span>Trait</span> <span><<TraitsList _args[0]>></span> </div> </div> <div class="item-heraldic-overlay"> <span class="item-rarity-star"></span> </div> </p> </div> </div> <</widget>> <<widget "Get1">> <<set $cards[_i] = { id: _i, type: 'none', affix: '', name: 'none', wiki: 'Nothing', select: '' } >> <</widget>> <<widget "Get2">> <<set _getit = getRandomItem("sundries") >> <<set $cards[_i] = { id: _i, type: 'sundries', affix: 'Junk', name: 'sundries A', item: _getit.key, wiki: '<<ItemCardShow '+ _getit.key+ ' '+ 1+'>>', select: '<<pickup $storage '+ _getit.key+ ' '+ 1+'>>' } >> <</widget>> <<widget "Get3">> <<set _getit = getRandomItem("resources") >> <<set _getnum = Math.clamp(random(_args[0].getStatFinal("cha"), _args[1].getStatFinal("cha")), 5, 20)>> <<if _getit.key == "gold">><<set _getnum *= 10 >><</if>> <<set $cards[_i] = { id: _i, type: 'resources', affix: 'Common', name: 'resource A', item: _getit.key, wiki: '<<ItemCardShow '+ _getit.key+ ' '+ _getnum+'>>', select: '<<pickup $storage '+ _getit.key+ ' '+ _getnum+'>><<redo "topbar">>' } >> <<if random(1,100) < 10>> <<set $cards[_i] = { id: _i, type: 'resources', affix: 'Double', name: 'resource A', item: _getit.key, wiki: '<<ItemCardShow '+ _getit.key+ ' '+ _getnum *2 +'>>', select: '<<pickup $storage '+ _getit.key+ ' '+ _getnum *2 +'>><<redo "topbar">>' } >> <</if>> <</widget>> <<widget "Get4">> <<set _getrandom = random(1,100) >> <<set _lvrandom = random(_args[0].getStatFinal("wil"), _args[1].getStatFinal("wil")) >> <<if _getrandom < 80>> <<set _affix = 'Common' >> <<set _getlv = Math.clamp(_lvrandom / 10, 1, 5) >> <<set _getit = getRandomItem("alchemy", 1, _getlv) >> <<set _getnum = 1>> <<elseif _getrandom < 90>> <<set _affix = 'Double' >> <<set _getlv = Math.clamp(_lvrandom / 10, 1, 5) >> <<set _getit = getRandomItem("alchemy", 1, _getlv) >> <<set _getnum = 2>> <<elseif _getrandom < 95>> <<set _affix = 'Critical' >> <<set _getlv = Math.clamp(_lvrandom / 20, 1, 5) >> <<set _getit = getRandomItem("alchemy", 2, _getlv) >> <<set _getnum = 1>> <<elseif _getrandom < 100>> <<set _affix = 'Legendary' >> <<set _getlv = Math.clamp(_lvrandom / 30, 1, 5) >> <<set _getit = getRandomItem("alchemy", 3, _getlv) >> <<set _getnum = 1>> <<else>> <<set _affix = 'Common' >> <<set _getit = getRandomItem("alchemy") >> <<set _getnum = 1>> <</if>> <<set $cards[_i] = { id: _i, type: 'alchemy', affix: _affix, name: 'alchemy A', item: _getit.key, wiki: '<<ItemCardShow '+ _getit.key+ ' '+ _getnum +'>>', select: '<<pickup $storage '+ _getit.key+ ' '+ _getnum +'>>' } >> <</widget>> <<widget "Get5">> <<if (_args[0].parental.includes(_args[1].name) || _args[0].offspring.includes(_args[1].name) )>> <<MergeWrong _args[0] _args[1]>> <<else>> <<MergeMonsters _args[0] _args[1]>> <</if>> <<if _args[2] == "Birth">> <<set _newmonster.look.base = Math.max(_args[0].look.base, _args[1].look.base) + random(-5,5)>> <<for _attrn range setup.coreStats>> <<set _newmonster[_attrn].op = Math.max(_args[0][_attrn].op, _args[1][_attrn].op) + $manage.opmutation>> <<set _newmonster[_attrn].lv = random(1, _newmonster[_attrn].op * $manage.lvmutation);>> <</for>> <</if>> <<set _char = clone(_newmonster) >> <<capture _char>> <<set $cards[_i] = { id: _i, type: "character", affix: rankToBloodline(_char.rank), name: _char.name, char: _char, wiki: `<<CharacterCardShow $cards[${_i}].char>>`, select: `<<AddMonster $cards[${_i}].char>><<unset _char>> <<set _args[0].fer.rate = 0>> <<set _args[1].fer.rate = 0>> <<run _args[0].offspring.push($cards[${_i}].char.name)>> <<run _args[1].offspring.push($cards[${_i}].char.name)>> <<run Dialog.close()>>` } >><</capture>> <<unset $addmonster>> <</widget>> <<widget "RadarImage">> <div class="radar-character-card"> <<RadarImageByChar _args[0]>> <div class="radar-stat-table buttons-group-col"> <<set _infolistRole = _args[0] >> <<for _attr range setup.coreStats>> <<capture _attr _infolistRole _replacecss>> <<set _replacecss = "#radar-"+ _infolistRole.id>> <<link "<<AttrShow _infolistRole _attr>>">> <<replace _replacecss>> <<set _atttostars = _infolistRole.atttostars(_attr) >> <div class="data-display-container"> <div class="data-display-title h1" style="text-align: center;"><<= setup.langbank[_attr][$lang]>> Stats</div> <div class="data-display-table"> <div class="data-display-row"> <progress class="attribute-progress" @max="_infolistRole.upgradeXp(_attr)" @value="_infolistRole.currentExp(_attr)"></progress> </div> <div class="data-display-header"> <div class="data-display-cell">Attribute</div> <div class="data-display-cell numeric">Value</div> </div> <div class="data-display-row"> <div class="data-display-cell">Stat</div> <div class="data-display-cell numeric"><<= _infolistRole[_attr].lv>></div> </div> <div class="data-display-row"> <div class="data-display-cell">EXP</div> <div class="data-display-cell numeric"><<= _infolistRole.currentExp(_attr)>></div> </div> <div class="data-display-row"> <div class="data-display-cell">Potential</div> <div class="data-display-cell numeric"><<= _infolistRole[_attr].op>></div> </div> <div class="data-display-row"> <div class="data-display-cell">Tier</div> <div class="data-display-cell numeric"><<= _infolistRole.getRank(_attr)>></div> </div> <div class="data-display-row"> <div class="data-display-cell">Exp Rate</div> <div class="data-display-cell numeric"><<= `${(_infolistRole.attrRate(_attr) * 100).toFixed(0)}%`>></div> </div> <div class="data-display-row"> <div class="data-display-cell">UpgradeEXP</div> <div class="data-display-cell numeric"><<= _infolistRole.upgradeXp(_attr)>></div> </div> <div class="data-display-row"> <div class="data-display-cell">AddBonus</div> <div class="data-display-cell numeric"><<= _infolistRole[_attr].add>></div> </div> <div class="data-display-row"> <div class="data-display-cell">MultRate</div> <div class="data-display-cell numeric"><<= _infolistRole[_attr].rate>></div> </div> <div class="data-display-row"> <div class="data-display-cell">TrueAdd</div> <div class="data-display-cell numeric"><<= _infolistRole[_attr].real>></div> </div> <div class="data-display-row"> Effect: <<= setup.langbank[_attr+'desc'][$lang]>> </div> <div class="data-display-row"> Final Value = (Level + AddBonus) × (1 + MultRate) + TrueAdd </div> </div> </div> <</replace>> <</link>> <</capture>> <</for>> </div> </div> <</widget>>
<style> .log-container { max-width: 56rem; margin: 0 auto; } .log-subtitle { text-align: center; font-size: 1rem; color: #8a8a8a; letter-spacing: 2px; } .log-tabs { display: flex; flex-direction: column; gap: 1.5rem; } .log-tab-content { padding: 1.25rem; } .log-section-title { padding: 0.5rem; border-bottom: 1px solid #2a2a2a; background: rgba(40, 0, 0, 0.3); margin: 1.25rem 0 0.65rem; font-size: 1.1rem; display: flex; align-items: center; gap: 0.5rem; } .log-section-title:first-child { margin-top: 0; } .log-list { list-style: none; padding-left: 1.25rem; } .log-list li { margin-bottom: 0.5rem; position: relative; padding-left: 1.25rem; &::before { content: "✦"; position: absolute; left: 0; color: #c9a96e; } } .log-ornament { text-align: center; margin: 1rem 0; color: #5a1a1a; font-size: 1.5rem; letter-spacing: 1rem; } @media (max-width: 48rem) { .log-container { padding: 0 0.625rem; } .log-title { font-size: 2rem; } .log-header::before, .log-header::after { width: 3.125rem; } .log-header::before { left: 10%; } .log-header::after { right: 10%; } } @media (max-width: 30rem) { .log-title { font-size: 1.5rem; } } </style> <div class="log-container"> <div class="module-bigtitle">Changelogs</div> <div class="log-subtitle">This game is under active development. The latest version is available on Patreon, where I'm working on it full-time. Your support is greatly appreciated!</div> <div class="log-ornament">◈◈◈</div> <div class="log-tabs"> <div class="module-border"> <div class="module-header">📜Version: v0.51.1 🕐Update Time: 2025.12.18</div> <div class="log-tab-content"> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Added Goddess Tech Tree (points from summons, infusions, and training).</li> <li>Moved Infuser, Energy Regen, Energy Cap, Summon Boost from buildings to tech tree.</li> <li>Optimized save/load logic.</li> <li>Adjusted summon probability calculation.Balanced adjustments to monster prices and attributes.</li> <li>Refined trait mechanics; created traits for new systems.</li> <li>Added Christmas event.</li> <li>Added "Plane Stun" mechanic: Newly summoned monsters cannot be operated.</li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>Thanks for playing and for all the feedback – keeps a solo dev like me going! </li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.50.3 🕐Update Time: 2025.12.13</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="module-ul"> <li>Display conflict between tutorial guidance and persistent sidebar.</li> <li>Icon font display issues.</li> <li>Morning report page freeze bug.</li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Save files are fully compatible — no need to start a new game.</li> <li>Mutual exclusion and fusion traits are still in design and will be implemented very soon.</li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.50.2 🕐Update Time: 2025.12.11</div> <div class="log-tab-content"> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Overhauled the trait inheritance system. <br>Added trait fusion and mutual exclusion mechanics. Inheritance probability has been reworked from the previous 100% guarantee to a new multi-condition judgment system. Details are available on the in-game help page.</li> <li>Performed performance optimizations, fixing the lag issue in the new list view.</li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Save files are fully compatible — no need to start a new game.</li> <li>Mutual exclusion and fusion traits are still in design and will be implemented very soon.</li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.50.1 🕐Update Time: 2025.12.10</div> <div class="log-tab-content"> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Completely revamped the character list module—it's super powerful now! <br>Supports: 3 data views, multi-condition filtering, multiple sorting options, custom tags (emojis & text), and tag-based filtering.</li> <li>Added favorites to the summoning page for quick character access.</li> <li>Tweaked training costs and values for better balance.</li> <li>Refined calculations for various character attributes.</li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>Big data changes mean lots of UI tweaks—some bugs might've slipped through. Please report any issues so we can make the game even better. </li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.49.1 🕐Update Time: 2025.12.5</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="log-list"> <li>Fully resolved music overlap playback issue.</li> <li>Optimized UI and icon.</li> </ul> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Additions: Baron Territory Management Tech Tree. <br>Management Tech Points gained from added human population. Currently offers Dispatch, Black Market, and Slavery branches.</li> <li>Additions: 6 new Stat Core Potions: Raise character Stat Tier caps (available via rare Black Market items, 20K price!)</li> <li>Optimizations: Improved beginner tutorial guidance. I was never fully satisfied with it before—now it's finally presentable.</li> <li>New Feature: Calendar system: View event timings (currently weekly events only).</li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>Thanks for playing and for all the feedback – keeps a solo dev like me going! </li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.48.4 🕐Update Time: 2025.11.29</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="log-list"> <li>Fixed some display issues.</li> <li>Optimized UI and icon.</li> </ul> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Merged all race-specific housing into a single Residential Zone (no more scattered exclusive Zones).</li> <li>Completely reworked the tech tree! Cleaned up the structure and prepared new tech trees for future content (new tech trees themselves will drop in later updates).</li> <li>Expanded the monster girl job system – now their individual stats actually affect how much money they bring in. (Might split the whole job module into its own thing later for easier management.)</li> <li>Added the Commercial Street storyline (unlocks on Day 31). Once it opens, you’ll get access to the new Commercial District with way more job options.</li> <li>Various balance tweaks and number adjustments behind the scenes.</li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>Thanks for playing and for all the feedback – keeps a solo dev like me going! </li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.48.3 🕐Update Time: 2025.11.23</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="log-list"> <li>Optimized UI and icon.</li> </ul> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Brand New "Part Training → Inheritance → Stats" Core System<br> I've completely redesigned the full progression loop: from training individual body parts → genetic inheritance in offspring → final character stats. I'm super happy with how it turned out!<br> It's a bit complex, so I've added an in-game explanation page. If it's still confusing after reading, feel free to hop into the Discord and ask—I'll break it down step by step. </li> <li>Training System Framework Fully Reworked<br> The entire training module has been rebuilt from the ground up to match the new logic. Next up: spending time on number balancing, fine-tuning details, and adding tons more action options.<br> A bunch of artwork and event images haven't caught up yet, so this is still internal testing only—no public release for now. </li> <li>Hybrid Breeding + New System = Endless Evolution Paths<br> Pairing this with the hybrid breeding feature unlocks ridiculously huge evolution trees for offspring.<br> Tons of wild combos, hidden traits, and special evolutions are still in heavy testing. Data tables are being filled out, and images are nowhere near done...<br> This build is just an early preview—nowhere close to finished. Think of it as a sneak peek! </li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>Core mechanics are solid and full of potential, but balancing, details, and art are way behind.<br> Thanks for sticking with me through this long dev cycle—it'll only get better and more fun from here!<br> Questions on the new system? Hit me up anytime. Cheers!</li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.48.2 🕐Update Time: 2025.11.18</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="log-list"> <li>Optimized UI and icon.</li> </ul> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Added Gene Details module with enhanced inheritance mechanics.<br> Monster girls no longer have random stats—instead, their attributes are determined by their genes. Continuous development of a single monster girl yields greater inheritance advantages, while mutations remain a factor. </li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>This version comes with a ton of bugs and data inconsistencies—use at your own risk.<br>This version has several unfinished elements and serves primarily as a proof-of-concept for the development direction. Known issues: Training module data anomalies and some trait data glitches—these will be fixed during future training module refinements!<br>Most illustrations are still in production, so there will be plenty of placeholder/incorrect images for now. </li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.48.1 🕐Update Time: 2025.11.13</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="log-list"> <li>Fixed some display issues on the Android version.</li> <li>Optimized UI and icon.</li> </ul> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Added Hybrid Mode. As it's still in the debugging stage, it is disabled by default and can be enabled on the Settings page.</li> <li>Added status display for each body part. Currently, the display is quite simplistic and rudimentary.</li> <li>Effectively added a new species: Hybrid.</li> </ul> <h4 class="log-section-title">💼Developer's Note:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>Due to my limited capabilities as a solo developer, my production and update pace is slower than ideal, and I must sincerely apologize for that. Rest assured, I've been steadily working on the game without slacking off. The community channel hasn't been very active lately, so I've had fewer opportunities to interact with everyone—when I'm deep in focused development, time flies by incredibly fast, and I often realize days have passed when I check back. <br> This update was actually meant to come out about a week later, as it ties into the Hybrid Mode's Training features and the new Prostitution mechanics (a Commercial Street will be added to the Valley). <br> In the coming period, this version will receive ongoing additional updates, including:<br> - Modularization and datafication of each Monster Girl's body parts, with overall abilities linked to body modules that can be freely combined.<br> - Work/output tied to required attributes/stats.<br> - Images may lag behind progress, but data displays will be prioritized.<br> - Per-body-part sexual experience status for each Monster Girl, with part-specific training to craft the ultimate Monster Girl!<br> </li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.47.4 🕐Update Time: 2025.10.30</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="log-list"> <li>Fixed Bugs.</li> <li>Optimized UI and icon.</li> </ul> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Enhanced battlefield captive system - Added prisoner rescue mechanics.</li> <li>19 unique monsters with 5 variants, each featuring diverse movement patterns.</li> <li>Race-specific defensive towers introduced: Cat Tower and Dog Tower (more to come in future updates).</li> <li>Battlefield roles now fully swappable - Assign different characters for real gameplay impact (requires Level 10+).</li> <li>New battlefield deployment system - Deploy units anytime, even mid-battle.</li> </ul> <h4 class="log-section-title">💼Tips:</h4> <ul class="module-ul"> <li>Major Gameplay & Data Update: You'll need to clear your data and start a new game.</li> <li>Got any great ideas or found a bug? Feel free to share your feedback with me!Thank you for your support!</li> </ul> </div> </div> <div class="module-border"> <div class="module-header">📜Version: v0.46.1 🕐Update Time: 2025.10.21</div> <div class="log-tab-content"> <h4 class="log-section-title">🛠️Fixes:</h4> <ul class="module-ul"> <li>Optimized many UI and icon.</li> <li>Fixed Hover Text Truncation.</li></ul> <h4 class="log-section-title">🌟Changes:</h4> <ul class="module-ul"> <li>Added Work Feature: You can now put your monster girls to work! This feature is still a work in progress, with plans to add bonuses based on their attributes, experience gains, and more.</li> <li>Introduced Meeting Room Feature: We've added a meeting room to centralize scattered information, making it easier for you to manage and oversee everything as a Lord.</li> <li>Training Interface for All Monsters: Every monster can now access the training interface via the Monster Girl Residential Area. This feature is still being refined, so stay tuned for improvements!</li> </ul> <h4 class="log-section-title">💼Tips:</h4> <ul class="module-ul"> <li>Got any great ideas or found a bug? Feel free to share your feedback with me!Thank you for your support!</li> </ul> </div> </div> <div class="log-ornament">◈◈◈</div> </div>
<<include "Codex_CSS">> <<for _category, _categorydata range $gamestatus.codexdata>> <<for _entry range _categorydata>> <<if _entry.seal === "seal">> <<silent>> <<include _entry.id>> <<if _condition && _condition()>> <<set _entry.seal = "unseal">> <<set _condition = false>> <</if>> <</silent>> <</if>> <</for>> <</for>> <style> #ui-dialog-body .UIbox { grid-template-rows: 100%; display: grid; grid-template-columns: 12rem 1fr; } </style> <div class="UIbox"> <div class="tome-index"> <<for _category, _entries range $gamestatus.codexdata>> <div class="category-title" > <<capture _category>> <<csslink setup.codexcategory[_category][$lang] category-title>> <<set _tempcategory = "#CodexCategory_"+_category >> <<toggleclass _tempcategory 'hidden'>> <</csslink>><</capture>></div> <div @id='"CodexCategory_"+_category' class="hidden"> <<for _entry range _entries>> <<capture _entry>> <<if _entry.seal === "seal">> <<else>> <<link '<div class="entry-item"><<= _entry.title>></div>'>> <<include _entry.id >> <<replace '#codexContent'>> <<= setup.entrycontent[$lang]>> <</replace>> <</link>> <</if>> <</capture>> <</for>> <</for>> </div> <div id="codexContent" class="tome-content"> <<include "CodexEntry_index">> </div> </div>
<<set _entries = [] >> <<set _categoryid = "Attributes" >> <<set _categorylang = { "en": `Attributes` } >> <<set _passages = _codex.filter(function (p) {return p.tags.includes(_categoryid);})>> <<for _tmp range _passages>> <<include _tmp.name>> <<run _entries.push({id: _tmp.name, title: setup.entrytitle[$lang], seal: "seal"})>> <</for>> <<set _entries = _entries.sort()>>
<<set _entries = [] >> <<set _categoryid = "Battle" >> <<set _categorylang = { "en": `Battle` } >> <<set _passages = _codex.filter(function (p) {return p.tags.includes(_categoryid);})>> <<for _tmp range _passages>> <<include _tmp.name>> <<run _entries.push({id: _tmp.name, title: setup.entrytitle[$lang], seal: "seal"})>> <</for>> <<set _entries = _entries.sort()>>
<<set _entries = [] >> <<set _categoryid = "Building" >> <<set _categorylang = { "en": `Building` } >> <<set _passages = _codex.filter(function (p) {return p.tags.includes(_categoryid);})>> <<for _tmp range _passages>> <<include _tmp.name>> <<run _entries.push({id: _tmp.name, title: setup.entrytitle[$lang], seal: "seal"})>> <</for>> <<set _entries = _entries.sort()>>
<<set _entries = [] >> <<set _categoryid = "Characters" >> <<set _categorylang = { "en": `Characters` } >> <<set _passages = _codex.filter(function (p) {return p.tags.includes(_categoryid);})>> <<for _tmp range _passages>> <<include _tmp.name>> <<run _entries.push({id: _tmp.name, title: setup.entrytitle[$lang], seal: "seal"})>> <</for>> <<set _entries = _entries.sort()>>
<<set _entries = [] >> <<set _categoryid = "Notes" >> <<set _categorylang = { "en": `Notes` } >> <<set _passages = _codex.filter(function (p) {return p.tags.includes(_categoryid);})>> <<for _tmp range _passages>> <<include _tmp.name>> <<run _entries.push({id: _tmp.name, title: setup.entrytitle[$lang], seal: "seal"})>> <</for>> <<set _entries = _entries.sort()>>
<<set _entries = [] >> <<set _categoryid = "Species" >> <<set _categorylang = { "en": `Species` } >> <<set _passages = _codex.filter(function (p) {return p.tags.includes(_categoryid);})>> <<for _tmp range _passages>> <<include _tmp.name>> <<run _entries.push({id: _tmp.name, title: setup.entrytitle[$lang], seal: "seal"})>> <</for>> <<set _entries = _entries.sort()>>
<<set _codexdata = {}>> <<set _codexcategory = {}>> <<set _codex = Story.filter(function (c) {return c.tags.includes("codex");})>> >> <<set _categories = _codex.filter(function (c) {return c.tags.includes("codexcategory");})>> <<for _tmp range _categories>> <<include _tmp.name>> <<set _codexdata[_categoryid] = _entries >> <<set _codexcategory[_categoryid] = _categorylang >> <</for>> <<include "CodexCategory_Manually">> <<set $gamestatus.codexdata = _codexdata >> <<set setup.codexcategory = _codexcategory >> <<SetFlag 'entry_format' 1>> <<SetFlag 'entry_Characters_griffinknight' 1>>
<<set _condition = () => false>> <<set setup.entrytitle = { "en": `Appearance` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> <table class="codex-table"> <thead> <tr> <th>Score Range</th> <th>Tier</th> <th>Term</th> <th>Description</th> </tr> </thead> <tbody> <tr> <td>181-200</td> <td>Mythic 1</td> <td>???</td> <td></td> </tr> <tr> <td>161-180</td> <td>Mythic 2</td> <td>???</td> <td></td> </tr> <tr> <td>141-160</td> <td>Mythic 3</td> <td>???</td> <td></td> </tr> <tr> <td>121-140</td> <td>Mythic 4</td> <td>???</td> <td></td> </tr> <tr> <td>101-120</td> <td>Mythic 5</td> <td>Otherworldly Grace</td> <td>Appearance beyond mortal aesthetics, with planar elegance and ineffable charm, both familiar and alien.</td> </tr> <tr> <td>91-100</td> <td>Mortal 1</td> <td>Breathtaking Beauty</td> <td>Stunningly captivating appearance, rare in the mortal realm, drawing all eyes instantly.</td> </tr> <tr> <td>81-90</td> <td>Mortal 2</td> <td>Striking Beauty</td> <td>Exceedingly beautiful with strong visual impact and refined presence, deeply memorable.</td> </tr> <tr> <td>71-80</td> <td>Mortal 3</td> <td>Radiant Charm</td> <td>Beauty and charisma combined, radiating an enchanting aura, warm and captivating.</td> </tr> <tr> <td>61-70</td> <td>Mortal 4</td> <td>Captivating Allure</td> <td>Attractive with distinctive charm, easily enthralling others.</td> </tr> <tr> <td>51-60</td> <td>Mortal 5</td> <td>Attractive</td> <td>Pleasing appearance, aligning with common aesthetics, harmonious but not striking.</td> </tr> <tr> <td>41-50</td> <td>Mortal 6</td> <td>Pleasant</td> <td>Ordinary but inoffensive appearance, mild and unremarkable.</td> </tr> <tr> <td>31-40</td> <td>Mortal 7</td> <td>Unremarkable</td> <td>Utterly average appearance, blending into the crowd, lacking allure.</td> </tr> <tr> <td>21-30</td> <td>Mortal 8</td> <td>Plain</td> <td>Below-average appearance, lacking beauty, easily overlooked.</td> </tr> <tr> <td>11-20</td> <td>Mortal 9</td> <td>Unappealing</td> <td>Off-putting appearance, with discordant features or flaws, hard to like.</td> </tr> <tr> <td>0-10</td> <td>Mortal 10</td> <td>Repulsive</td> <td>Repellent or terrifying appearance, possibly with grotesque traits.</td> </tr> </tbody> </table> <div> <p><strong>Appearance Mechanism:</strong> The Appearance value affects the increase in happiness during interactions and the likelihood of developing hobbies. It is determined by a base value and Charisma, and can be inherited.</p> </div> ` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Core Stats` } >> <<set setup.entrycontent = { "en": `<div class="codex-container"> <div class="codex-header"> <i class="fas fa-book-reader"></i> <<= setup.entrytitle[$lang]>> </div> <div style="text-align:center; color:#777; font-style:italic; margin-bottom:10px;"> Core Attributes governing the soul's potential. </div> <!-- 核心网格容器 --> <div class="grimoire-grid"> <!-- CHA: Charisma --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-comments"></i></div> <div class="slab-title-group"> <span class="slab-abbr">CHA</span> <span class="slab-name"><<= setup.langbank.cha[$lang]>></span> </div> </div> <div class="slab-desc"><<= setup.langbank.chadesc[$lang]>></div> <div class="slab-effects"> <div class="effect-row"> <i class="fas fa-circle-notch effect-icon eff-ritual"></i> <span><span class="eff-ritual">Ritual:</span> Amplifies EXP gained.</span> </div> <div class="effect-row eff-locked"> <i class="fas fa-lock effect-icon"></i> <span><span class="eff-combat">Combat:</span> <em>Unknown mystery...</em></span> </div> </div> </div> <!-- DEX: Dexterity --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-wind"></i></div> <div class="slab-title-group"> <span class="slab-abbr">DEX</span> <span class="slab-name"><<= setup.langbank.dex[$lang]>></span> </div> </div> <div class="slab-desc"><<= setup.langbank.dexdesc[$lang]>></div> <div class="slab-effects"> <div class="effect-row"> <i class="fas fa-circle-notch effect-icon eff-ritual"></i> <span><span class="eff-ritual">Ritual:</span> Reduces Energy cost.</span> </div> <div class="effect-row eff-locked"> <i class="fas fa-lock effect-icon"></i> <span><span class="eff-combat">Combat:</span> <em>Unknown mystery...</em></span> </div> </div> </div> <!-- FER: Fertility/Ferocity --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-seedling"></i></div> <div class="slab-title-group"> <span class="slab-abbr">FER</span> <span class="slab-name"><<= setup.langbank.fer[$lang]>></span> </div> </div> <div class="slab-desc"><<= setup.langbank.ferdesc[$lang]>></div> <div class="slab-effects"> <div class="effect-row"> <i class="fas fa-circle-notch effect-icon eff-ritual"></i> <span><span class="eff-ritual">Ritual:</span> Boosts success rates & fate.</span> </div> <div class="effect-row eff-locked"> <i class="fas fa-lock effect-icon"></i> <span><span class="eff-combat">Combat:</span> <em>Unknown mystery...</em></span> </div> </div> </div> <!-- STA: Stamina --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-heartbeat"></i></div> <div class="slab-title-group"> <span class="slab-abbr">STA</span> <span class="slab-name"><<= setup.langbank.sta[$lang]>></span> </div> </div> <div class="slab-desc"><<= setup.langbank.stadesc[$lang]>></div> <div class="slab-effects"> <div class="effect-row"> <i class="fas fa-circle-notch effect-icon eff-ritual"></i> <span><span class="eff-ritual">Ritual:</span> Max Daily Actions limit.</span> </div> <div class="effect-row eff-locked"> <i class="fas fa-lock effect-icon"></i> <span><span class="eff-combat">Combat:</span> <em>Unknown mystery...</em></span> </div> </div> </div> <!-- STR: Strength --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-fist-raised"></i></div> <div class="slab-title-group"> <span class="slab-abbr">STR</span> <span class="slab-name"><<= setup.langbank.str[$lang]>></span> </div> </div> <div class="slab-desc"><<= setup.langbank.strdesc[$lang]>></div> <div class="slab-effects"> <div class="effect-row"> <i class="fas fa-circle-notch effect-icon eff-ritual"></i> <span><span class="eff-ritual">Ritual:</span> Enhances EXP potency.</span> </div> <div class="effect-row eff-locked"> <i class="fas fa-lock effect-icon"></i> <span><span class="eff-combat">Combat:</span> <em>Unknown mystery...</em></span> </div> </div> </div> <!-- WIL: Willpower --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-eye"></i></div> <div class="slab-title-group"> <span class="slab-abbr">WIL</span> <span class="slab-name"><<= setup.langbank.wil[$lang]>></span> </div> </div> <div class="slab-desc"><<= setup.langbank.wildesc[$lang]>></div> <div class="slab-effects"> <div class="effect-row"> <i class="fas fa-circle-notch effect-icon eff-ritual"></i> <span><span class="eff-ritual">Ritual:</span> Summon quality & quantity.</span> </div> <div class="effect-row eff-locked"> <i class="fas fa-lock effect-icon"></i> <span><span class="eff-combat">Combat:</span> <em>Unknown mystery...</em></span> </div> </div> </div> </div> <!-- End Grid --> <!-- 成长公式区域 (保持之前的样式,稍微调整间距) --> <div class="codex-section theme-abyss" style="margin-top: 30px;"> <div class="section-title txt-abyss"> <i class="fas fa-infinity"></i> The Law of Growth </div> <div class="formula-display"> ( <span class="var-base">Base</span> + <span class="var-base">Add</span> ) × ( 1 + <span class="var-mult">Rate</span> ) × Coefficient + <span class="var-real">RealValue</span> </div> <div style="font-size: 0.9em; color: #888;"> * Growth rate and limitations are dictated by one's natural <span class="txt-abyss">Talent</span>. </div> </div> </div>` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Main Traits` } >> <<set setup.entrycontent = { "en": `<div class="codex-container"> <div class="codex-header"> <i class="fas fa-dna"></i> Main Traits Codex </div> <div style="text-align:center; color:#777; font-style:italic; margin-bottom:15px;"> Innate abilities and bloodline gifts manifested in the physical form. </div> <div class="grimoire-grid"> <!-- 已实装特质 (Implemented) --> <<for _key, _trait range setup.traitdata_talent>> <<TraitCard _key>> <</for>> <!-- 开发中特质 (In Progress) --> </div> </div>` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Genes Base` } >> <<set setup.entrycontent = { "en": `<div class="codex-container"> <div class="codex-header"> <i class="fas fa-microchip"></i> Mechanics of Evolution </div> <div style="text-align:center; color:#777; font-style:italic; margin-bottom:25px;"> From the mastery of flesh to the manifestation of power. </div> <div class="evo-container"> <!-- ================= PHASE I: GENETIC ORIGIN ================= --> <div> <div class="phase-header" style="border-color: #8a1c1c;"> <span class="phase-title" style="color: #d9534f;">I. The Origin</span> <span class="phase-tag tag-internal">INTERNAL · HERITABLE</span> </div> <div class="grimoire-grid"> <!-- 1. Bodypart Mastery --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-dumbbell"></i></div> <div class="slab-title-group"> <span class="slab-abbr">MASTERY</span> <span class="slab-name">Bodypart Experience</span> </div> </div> <div class="slab-desc"> Accumulated memory of the flesh. When a specific body part reaches mastery limits (100%), it triggers a permanent genetic evolution. </div> <div> <div style="display:flex; justify-content:space-between; font-size:0.75em; color:#aaa;"> <span>Evolution Progress</span> <span>60%</span> </div> <div class="progress-mini-container"> <div class="progress-mini-fill" style="width: 60%;"></div> </div> </div> <div class="source-icons"> <span class="source-item" title="Sex Acts"><i class="fas fa-heart"></i> Intimacy</span> <span class="source-item" title="Training"><i class="fas fa-key"></i> Training</span> </div> </div> <!-- 2. Genetic Potential --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-dna"></i></div> <div class="slab-title-group"> <span class="slab-abbr">GENES</span> <span class="slab-name">Innate Potential</span> </div> </div> <div class="slab-desc"> The biological limit of the soul. <br> <em>Formula: Base + (Innate Mod + Mastery Mod)</em><br> These values can be <strong>inherited</strong> by offspring, potentially mutating into stronger forms. </div> <div class="slab-effects"> <span class="txt-highlight">Example:</span> STR +9, STA +3, DEX -2 </div> </div> <!-- 3. Synthesis (Tier) --> <div class="stat-slab" style="border-top-color: #d9534f;"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-layer-group"></i></div> <div class="slab-title-group"> <span class="slab-abbr">TIER</span> <span class="slab-name">Total Synthesis</span> </div> </div> <div class="slab-desc"> The aggregate score of all genetic potentials. This determines the entity's <strong>Growth Tier</strong> (S, A, B...). </div> <div class="slab-effects"> <span class="txt-highlight">Effect:</span> Higher Tier = Massive EXP Multiplier (up to 800%). </div> </div> </div> </div> <!-- Flow Connector --> <div class="flow-connector"> <i class="fas fa-chevron-down"></i> </div> <!-- ================= PHASE II: MANIFESTATION ================= --> <div> <div class="phase-header" style="border-color: #1c3b8a;"> <span class="phase-title" style="color: #5bc0de;">II. The Manifestation</span> <span class="phase-tag tag-external">EXTERNAL · TEMPORARY</span> </div> <div class="grimoire-grid"> <!-- 4. Stat Experience --> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-flask"></i></div> <div class="slab-title-group"> <span class="slab-abbr">EXP</span> <span class="slab-name">Stat Experience</span> </div> </div> <div class="slab-desc"> Daily growth gained from actions. The rate of gain is strictly controlled by the <strong>Tier</strong>. </div> <div> <div style="display:flex; justify-content:space-between; font-size:0.75em; color:#aaa;"> <span>To Next Level</span> <span>45%</span> </div> <div class="progress-mini-container"> <div class="progress-mini-fill" style="width: 45%; background-color: #5bc0de;"></div> </div> </div> <div class="source-icons"> <span class="source-item"><i class="fas fa-fist-raised"></i> Battle</span> <span class="source-item"><i class="fas fa-hammer"></i> Labor</span> </div> </div> <!-- 5. Stat Value (Final) --> <div class="stat-slab" style="border-top-color: #5bc0de;"> <div class="slab-header"> <div class="slab-icon"><i class="fas fa-chart-bar"></i></div> <div class="slab-title-group"> <span class="slab-abbr">STATS</span> <span class="slab-name">True Power</span> </div> </div> <div class="slab-desc"> The visible power value (STR: 15). This is the result, not the cause. It resets upon rebirth/death, unlike Genes. </div> <div class="slab-effects"> <div class="effect-row"><i class="fas fa-check effect-icon eff-combat"></i> Determines Mission Success</div> <div class="effect-row"><i class="fas fa-check effect-icon eff-ritual"></i> Determines Resource Yield</div> </div> </div> </div> </div> <!-- ================= SUMMARY FOOTER ================= --> <div class="summary-list"> <div style="margin-bottom:10px; font-family:'Georgia',serif; color:#cbb06a; text-transform:uppercase;"> <i class="fas fa-info-circle"></i> Key Takeaways </div> <ul class="codex-list"> <li> A freshly summoned high-tier monster will have <span class="txt-highlight">High Genetic Potential</span> (S-Tier) but <span class="txt-highlight">Low Base Stats</span> (Level 1). </li> <li> However, thanks to her <span class="txt-highlight">S-Tier</span> genes, she will gain EXP at <strong>800% speed</strong>, quickly surpassing common monsters. </li> <li> <strong>Genes are eternal; Stats are transient.</strong> Focus on breeding better genes to raise the ceiling of your army. </li> </ul> </div> </div> </div>` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Genes Desc` } >> <<set setup.entrycontent = { "en": `<<include "Gene_Help">>` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Trait Bloodline` } >> <<set setup.entrycontent = { "en": `<div class="codex-container"> <div class="codex-header"> <i class="fas fa-book-dead"></i> The Bloodline Codex </div> <div class="codex-section theme-abyss"> <div class="section-title txt-abyss"> <i class="fas fa-balance-scale"></i> Laws of Inheritance </div> <div>Power is not guaranteed; it is inherited through the strength of blood.</div> <ul class="codex-list"> <li><span class="txt-light">Trait Mastery:</span> The higher the Trait Level, the greater the chance it will pass to the offspring.</li> <li><span class="txt-light">Rarity Penalty:</span> Legendary and Rare traits are volatile and harder to inherit than common ones.</li> <li><span class="txt-light">Unique Souls:</span> Traits marked as <span class="txt-dim">"Non-hereditary"</span> are bound to the individual and die with them.</li> </ul> </div> <div class="codex-section theme-blood"> <div class="section-title txt-blood"> <i class="fas fa-heartbeat"></i> Blood Resonance </div> <div>When <span class="txt-light">both parents</span> possess the same trait, the bloodline strengthens.</div> <ul class="codex-list"> <li>The chance of inheritance is <span class="txt-light">significantly increased</span>.</li> <li>The offspring will inherit the trait at a <span class="txt-light">Higher Level</span> than the parents (up to the cap).</li> </ul> <div class="txt-dim" style="margin-top:5px;">"Pure bloodlines breed power."</div> </div> <div class="codex-section theme-gold"> <div class="section-title" style="color: #cbb06a;"> <i class="fas fa-flask"></i> Alchemy of Flesh </div> <div>Conflicting or complementary blood can trigger unpredictable reactions:</div> <ul class="codex-list"> <li> <span class="txt-light">Fusion:</span> Certain trait combinations will merge, consuming the old traits to create a <span class="txt-abyss">New, Superior Trait</span>. </li> <li> <span class="txt-light">Dominance:</span> Conflicting traits (e.g., Ice & Fire) cannot coexist. The <span class="txt-blood">Stronger Trait</span> (Higher Level) will devour the weaker one. </li> </ul> </div> <div class="codex-section theme-abyss"> <div class="section-title txt-abyss"> <i class="fas fa-dna"></i> Spontaneous Mutation </div> <div> Rarely, the blood may evolve on its own. A newborn may manifest a trait at a higher potential than its lineage should allow. </div> </div> <div style="text-align: center; font-size: 0.8em; color: #555; margin-top: 15px; letter-spacing: 1px;"> — VERITAS VOS LIBERABIT — </div> </div>` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Aiming Modes` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> [img[setup.ImagePath + "ui/help/battlehelp.png"]] <h3>Nearest</h3>Nearest mode commands arrow towers to target the closest Dark Legion foe within range, striking swiftly to halt their advance. It excels at repelling swift siege-breakers, preserving Fortitude by thinning the vanguard before they erode the walls, delaying the perilous Morale phase. <h3>FocusBoss</h3>FocusBoss mode prioritizes the Dark Legion’s mightiest champions, targeting bosses or high-health foes. It delivers concentrated fire to slay commanders of the infernal horde, reducing threats to Fortitude and preventing a swift Morale phase. Ideal for countering the Dark Legion Command’s elite. <h3>OneToOne</h3>OneToOne mode locks arrow towers onto a single Dark Legion foe until its demise, ensuring no enemy escapes. It maximizes damage on resilient targets, protecting Fortitude by eliminating threats methodically, staving off the Morale phase. Perfect for relentless focus. <h3>Spread</h3>Spread mode scatters arrow tower fire across multiple Dark Legion foes, weakening the horde’s numbers. It thins enemy waves to ease pressure on Fortitude, delaying the Morale phase. Best for overwhelming swarms sent by the Dark Legion Command. ` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Battle Skills` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> [img[setup.ImagePath + "ui/help/battleskill.png"]] <h3>Gallant Charge</h3>Launch a charge and crush all enemies. <h3>Repair Fortifications</h3>Restores the city’s Fortitude, mending ramparts battered by the Dark Legion’s siege-breakers. <h3>Quick Heal</h3>Rapidly restores the Lord’s Guard’s health, sustaining their fight against the Dark Legion’s relentless assaults. <h3>Capture Prisoner</h3>Captures a Dark Legion foe, weakening their forces.(The capture limit can be unlocked through related buildings) <h3>Rush Enemies</h3>The current enemy is too weak! Shorten the interval between enemy attack waves. <h3>Fire Rate Boost</h3>Increases arrow towers’ attack speed, raining relentless volleys on the Dark Legion. <h3>Gate Repair</h3>Reinforces the city’s gates, restoring key Fortitude points. It fortifies critical defenses against the Dark Legion’s onslaught, preventing breaches. <h3>Freeze Enemies</h3>Slows the Dark Legion’s advance, freezing their movement. It delays enemy assaults, protecting Fortitude and buying time to avoid the Morale phase. ` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Defense Command` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> <h3>Commander</h3>[STR] → Attack The Commander is the indomitable vanguard of the Lord’s Guard, a heavy cavalry forged from the ruins of fallen towns to counter the Dark Legion’s swift, city-shattering siege-breakers. Strength governs the Guard’s critical damage during the Gallant Charge, enabling devastating strikes that cleave through demonic ranks with unrelenting force. In defensive stands, strength enhances the Guard’s combat power, crushing enemies who dare approach the ramparts with crushing might. This position ensures the Guard’s charges obliterate the Dark Legion’s vanguard, preventing their deadly advance toward the city walls. By wielding raw power, the Commander fortifies humanity’s defenses, standing as a relentless bulwark against the Dark Legion Command’s infernal pacts, securing victory through sheer dominance on the battlefield. <h3>Warden</h3>[STA] → Health The Warden is the unyielding cornerstone of the Lord’s Guard, tasked with enduring the savage onslaught of the Dark Legion’s demonic hordes to protect humanity’s last strongholds. Stamina bolsters the Guard’s maximum health, granting the resilience needed to withstand prolonged battles against relentless foes. When the city’s fortifications falter, stamina amplifies the potency of repair skills, enabling rapid restoration of ramparts to hold back the enemy’s advance. By absorbing the brunt of the Dark Legion’s assaults, the Warden buys precious time for allies to reinforce defenses, ensuring the Guard remains a steadfast shield. This position anchors the survival of the Guard, defying the Dark Legion Command’s apocalyptic wrath and preserving humanity’s hope against the tide of destruction. <h3>Bard</h3>[CHA] → Regen The Bard is the vital spirit of the Lord’s Guard, wielding enchanting hymns to heal wounds and sustain the knights through the brutal clashes with the Dark Legion’s forces. Charisma accelerates the Guard’s health regeneration, ensuring swift recovery from the relentless injuries inflicted by demonic blades. It also enhances the effectiveness of healing skills, restoring greater vitality to battle-worn troops. The Bard’s songs counter the Dark Legion’s unyielding aggression, maintaining the Guard’s fighting strength in the face of overwhelming odds. This position keeps the Guard resilient, rallying their vigor to stand firm against the Dark Legion Command’s merciless schemes, ensuring humanity’s defenders endure the crucible of war with unbroken resolve. [img[setup.ImagePath + "ui/help/battlehelp.png"]] <h3>Guardian</h3>[DEX] → Armor The Guardian is the vigilant protector of the Lord’s Guard, deflecting the Dark Legion’s ferocious strikes with agile precision to shield allies from harm. Dexterity strengthens the Guard’s armor, reducing damage taken from demonic assaults, and accelerates their charge speed, enabling rapid, decisive strikes against the enemy’s ranks. Beyond the melee, dexterity amplifies the attack speed of all arrow towers, unleashing relentless volleys to thin the Dark Legion’s hordes before they reach the walls. The Guardian’s nimble defenses ensure the Guard and the city’s fortifications withstand the Dark Legion Command’s savage onslaught, fortifying humanity’s ramparts against the encroaching abyss and securing a bulwark for survival. <h3>Ranger</h3>[FER] → Crit The Ranger is the Lord’s Guard’s master of precision, blessed with fortune to deliver lethal blows against the Dark Legion’s champions. Fertility, embodying divine luck, elevates the Guard’s critical hit chance, ensuring devastating strikes that fell even the mightiest demons. It also enhances the critical hit chance of all arrow towers, raining catastrophic volleys upon the enemy. Additionally, fertility boosts the success rate of capture and assassination skills, disrupting the Dark Legion’s key forces with surgical precision. The Ranger’s uncanny accuracy turns the tide of battle, enabling the Guard to dismantle the Dark Legion Command’s infernal hierarchy and protect humanity from annihilation with every fortunate strike. <h3>Strategist</h3>[WIL] → Charge The Strategist is the tactical mastermind of the Lord’s Guard, orchestrating maneuvers to outwit the Dark Legion Command’s apocalyptic schemes and protect the city’s Fortitude. Willpower shortens skill charge times, enabling frequent use of abilities like Gallant Charge to repel demonic hordes. It also accelerates Morale recovery, sustaining the warriors’ courage—calculated as their number times their valor, initially resolute—when Fortitude holds. If the walls fall, triggering the Morale phase, the Strategist’s resolve maintains Morale by rallying troops to sacrifice themselves, a desperate stand where courage wanes but recovers slowly to match the enemy—a state to avoid, lest Morale reaches zero, signaling the death of all defenders and defeat. This position secures strategic dominance, guiding humanity to triumph over the Dark Legion’s infernal abyss. ` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Fortitude & Morale` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> [img[setup.ImagePath + "ui/help/battlehelp.png"]] <h3>Fortitude</h3>Fortitude represents the collective vitality of the city’s fortifications, the unyielding stone and iron ramparts that stand as humanity’s first defense against the Dark Legion’s relentless assault. It is the total health of all defensive structures, absorbing the savage blows of demonic siege-breakers intent on shattering the walls and extinguishing civilization. The Dark Legion’s attacks erode Fortitude, each strike chipping away at the city’s resilience. The Warden’s repair skills, fueled by stamina, restore Fortitude, mending breaches to hold back the enemy’s advance. Should Fortitude fall to zero, the walls collapse, plunging the defenders into the perilous Morale phase, where only the warriors’ courage can stem the tide. Preserving Fortitude is paramount, for every moment the ramparts endure delays the Dark Legion Command’s apocalyptic victory, granting the Lord’s Guard time to repel the infernal horde and safeguard humanity’s last bastions. <h3>Morale</h3>Morale embodies the defiant spirit of the city’s warriors, a measure of their collective courage multiplied by their number, standing as humanity’s final bulwark when the Dark Legion breaches the city’s Fortitude. Once the walls fall, Morale becomes the shield against the enemy’s wrath, with each demonic assault diminishing the warriors’ courage—a vital force that begins resolute but wanes under relentless damage. The Strategist’s willpower accelerates courage recovery, sustaining Morale to match the Dark Legion’s ferocity, while in dire moments, their resolve rallies troops to sacrifice themselves, maintaining Morale at a grievous cost. This phase is a desperate stand to avoid, for if Morale reaches zero, every defender perishes, leaving the lord defeated before the Dark Legion Command’s infernal might. Bolstering Morale and averting this stage ensures the Lord’s Guard’s defiance, preserving humanity’s hope against annihilation. ` }>>
<<set _condition = () => $times.total >= 4>> <<set setup.entrytitle = { "en": `Black Market` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header">Black Market Help</h2> Black Market "In a world overrun by demons, the fact this marketplace operates freely should give you pause. Rumors suggest formidable backers - perhaps beyond your darkest imaginings. Their wares can be lifesaving... if you can stomach the prices." Dynamic Pricing All item prices fluctuate (both buying & selling) Speculation requires deep pockets and luck Reputation System Base markup: 200% (before fluctuations) Discounts unlock through: ✓ Repeated transactions ✓ Special events Warning: "Loyalty" here costs more gold than goodwill Weekly Rare Items Stock refreshes every week May include: • Critical resources • Legendary-tier artifacts (Come prepared - the best deals empty purses fastest) ` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Chambers` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header">Repurposed Chambers</h2> I can’t stop thinking about the Repurposed Chambers. This place—it’s like nothing I’ve ever seen, and yet it weighs on me in ways I didn’t expect. Tucked into the shadow of the mountains, it sprawls beneath the Lost Temple, a labyrinth of stone rooms carved with a purpose I wish I could erase. Once, it was a prison. A cruel, cold cage for Beastkin slaves, built by hands that saw them as less than human. The walls still whisper of that shame—scratches where chains once hung, corners darkened by despair. Knowing what happened here twists my gut. How could anyone build something so vast just to trap others? And vast it is. From a modern perspective, it’s staggering—countless chambers branching off winding halls, each one big enough to be a home. Some are small, like quiet nooks; others could hold whole families. The stonework, rough but deliberate, tells me this wasn’t some hasty dungeon. It took effort, planning, maybe even pride. That’s what haunts me most: the care put into something so wrong. I imagine the Beastkin huddled here, their spirits broken, and I wonder how many stories these walls hold. Why did the old lords keep this place secret? Why let it decay instead of tearing it down? I don’t get it, and the questions gnaw at me. But that’s the past. I’ve changed things. The cages are gone—ripped out by my own orders. The Beastkin are free now, every single one. No locks, no chains, just open doors. Problem is, we’ve got nowhere else for them to go, so they’re still here, living in the same rooms that once trapped them. It feels wrong, like I’m failing them already, but I swear it’s temporary. These chambers aren’t a prison anymore. They’re a start—a chance to build something better. With some cleaning and care, this place could house hundreds, maybe thousands. Not just Beastkin, but any Species who join us—humans, monster girls, whoever fights for our cause. There’s space for Bedrooms, workshops, even markets if we dream big. The bones of this place are strong; we just need to give it a heart. I’m calling it the Repurposed Chambers because that’s what it is: a second chance. For the Beastkin, for me, for this whole battered fief. As Lord, I’ll make it more than a grim relic. I see families laughing here someday, lights in every window, voices echoing where there was only silence. It’s a lot to take on, but I’ve got to try. This world’s falling apart—demons at our gates, taxes bleeding us dry—but these chambers feel like a foothold. A place to stand and fight. I just hope I’m up to the task.` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Main Hall` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header">Main Hall & The Lost Temple</h2> This Lost Temple takes my breath away. Carved deep into the heart of the mountains, it’s a masterpiece of dark gothic grandeur—towering stone spires, intricate carvings, and countless chambers whispering of the devotion poured into its creation. As someone from the modern world, I’m floored by its haunting beauty. Every chiseled detail screams craftsmanship that must’ve taken lifetimes. This place was no mere cave; it was a thriving hub once, brimming with purpose—vast halls, elaborate rooms, everything you’d need to sustain a community. Yet, somehow, it’s been forgotten, left to crumble in silence. Why? It baffles me. The lords before me treated it like some secret hideout, never sharing its wonders with the world. They didn’t even bother to repair it, letting time gnaw at its glory. Were they afraid of its power? Or did they just not care? It’s a puzzle I can’t shake, and it stings to see something so magnificent left to rot. At the entrance lies the Main Hall, a cavernous space centered around a ritual platform. It’s scarred now—shattered by an explosion that tore through here, leaving the stone jagged and broken. But in its ruin, something incredible appeared: a Planar Rift, pulsing with a force that makes my skin prickle. The goddess told me that this rift was created by her capturing the demon's portal, which caused a violent energy outburst and exhausted her divine power. This rift holds the key to summoning interdimensional allies—a power both thrilling and terrifying. .Looking at the goddess in front of me, who was the size of a ceramic figurine and had no divine power at all, I remained skeptical.But I can feel the rift's energy, like a heartbeat from another world. The temple’s sprawling network of abandoned rooms, big and small, feels like a gift in these desperate times. They’re dusty and worn, but they’ll serve as my temporary Lord’s manor. I can already see their potential—storage, workshops, maybe even homes for my people. I’ll breathe life back into this place, step by step. It deserves better than oblivion.` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Planar Rift` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header">Planar Rift Help</h2> [img[setup.ImagePath + "ui/help/summonhelpen.webp"]] <span style="color:red;font-weight: bold;">Analysis I should use alchemy potion enhancement to give priority to cultivating a high-lv monster girl, so that all the monster girls paired with it can quickly improve their attributes. Breeding is not the main purpose, but the bounty for completing quests after the attributes are improved. And a large number of high-quality harvests brought by high levels.</span> Summoning & Breeding System: Mechanic By channeling summoners's soul resonance, the ritual activates planar rifts to obtain cross-dimensional rewards. Pregnancy Success: If conception occurs (gaining a "Life Seed"),allowing Summon to beings from other planar, Summoned beings' attributes correlate with the seed's properties. Warning Exercise caution during special events (e.g., Blood Moon) - unknown dangers may emerge. Breeding & Cost Mechanics: Base Fertility = FER value also as lucky.Failures increase Fertility; successes reset it.Total Fertility affects success rate and summoned items.Some Attributes determine summoning quality and quantity.Energy Cost per summon is based on DEX.Reduce cost with Dexterity Potions and harvest(effectively grants more daily summons). Six core attributes: STA (Stamina) STR (Strength) CHA (Charisma) WIL (Willpower) DEX (Dexterity) FER(Fertility) Each attribute has:A Talent Tier (governs growth rate & cap). Breakthroughs improve Talent and affect summoning results. Dispatch may have attribute requirements. Action Points (AP) : Calculated from STA (Stamina) as whole numbers (decimals accumulate). Replenished by resting or feeding. EXP Bar: The Summoner's soul resonance strengthens through training, accumulating EXP for lv-ups. Values are determined by both parties. Experience & Leveling: Gaining EXP increases your lv. Attributes grow based on their respective Talents. Character Selection: Characters who agree to summoning rituals enter the Candidate Pool. Click here to select/change your Summoner. Feed Function: Apply Buffs when possible (check the Consumables tab). Effects may be short-term or long-term—strategize for efficiency. Harvest Function: Yield Calculation • Quantity = Current AP + Stored Production • Quality = Based on Monster lv and WIL Attribute Mechanic Notes Drains all remaining Action Points (AP) upon harvest Higher WIL (Willpower) enhances yield and resource refinement Buff/Debuff: Granted via feeding or events. Effects may be short-term or long-term—strategize for efficiency. Hover to view details. ` }>>
<<set _condition = () => $times.total >= 2>> <<set setup.entrytitle = { "en": `Slaver Caravan` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header">Slaver Caravan Help</h2> Slaver Caravan These wretched Slaver Caravans are a stain on this world, peddling misery for gold. Run by heartless mercenaries, they trap Beastkin with vile tricks—capture, lies, kidnapping, even poison. Every fiber of my being loathes them. Yet, as much as it sickens me, their slaves are a fast way to bolster our fief’s strength. Mark my words: one day, I’ll make them pay for what they’ve done. Species The war drags on, bringing new Species to these caravans. Some won’t show up without meeting certain conditions first. Price Empire laws flood the market with slaves, so Prices are dirt cheap—for now. Traits Traits are rare and powerful Abilities. Always check if these slaves carry ones we need. ` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Summon Probability` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header">Probability Percentage Table</h2> [img[setup.ImagePath + "ui/help/probability percentage table.png"]] Class 0: Nothing (this option is temporarily cancelled and changed to be equivalent to class 1 on May 15) Class 1: Planar garbage Class 2: Resources Class 3: Alchemy potion Class 4: Summon successfully(New Monster) Tips: <li>1. SUM is the sum of the odds of both sides, and can be paired with characters with a very high chance and a rare but very low chance character.</li> <li>2. Chance to be used through Feed, Traits, Buildings, Hobbies, Equipment and other bonuses. And the chance will accumulate, change the pair, and the accumulated chance will still exist.</li> <li>3. When the conditions for summoning monsters are not met, only items and resources will be summoned, such as F&F, M&M. (This feature will be extended in later versions)</li> <li>4. Monsters that are pregnant in the bedroom can no longer participate in the summoning ritual to prevent genetic contamination.</li> ` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Protagonist` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header">Me, the So-Called Savior</h2> Okay, let’s get this straight—I’m no hero. Picture me: blonde hair, office buzzcut, now crammed into black Armor that’s way too noble for a guy who once panicked over a jammed copier. I was your average office drone, slogging through emails till midnight. One night, stumbling home, I saw a blood moon—creepy, cinematic, like a horror flick’s warning. I went to snap a pic, and zap—lightning fries me. Next thing I know, I’m sprawled in a Lost Temple, carved into some mountain like a fantasy movie set. Welcome to Hawkridge Barony, they say. I’m Baron Wilderen, Lord of this dirt patch. Me. The guy who forgot his lines in a school play. The old lord? Dead, heroically or not, fighting for the fief. Now it’s my turn, except nobody sent the memo about demons ripping the world apart. Yeah, demons—big, ugly ones, flooding through Portals like it’s an apocalypse sale. My grand inheritance? A ruined territory, starving peasants, and a target on my back. I thought nobles got castles and feasts, not "nightmare mode" survival. To top it off, a ceramic angel—yes, a talking statue—shows up, claiming it yanked me here to save the world. Save it? I can barely save a Word doc without crashing. Here’s the kicker: I’m not buying the whole "chosen one" shtick. I’ve binged enough TV dramas to smell a plot twist. That angel’s hiding something, and so is everyone else. I catch weird looks in the temple—Selena’s too-quick smiles, Raymond’s dodged questions, little clues that don’t add up. This world’s got secrets, and I’m the odd man out. Friend or foe, I don’t know what’s waiting, so I’m playing dumb—faked amnesia, classic move. "Oh, my head hurts, tell me who I am." Works like a charm, but I’m watching, waiting for the truth to slip. Demons want me dead, human "allies" are probably scheming, and starvation’s knocking. My big plan? Team up with monster girls—because, sure, that’s normal—and lean on folks like Garvin and Erin to keep this mess from collapsing. Modern me wants to scream, "I’m not cut out for this!" but whining won’t stop the end of the world. So, I’ll grumble, fake some lordly confidence, and dig for answers. Why me? Why monster girls? Is this a goddess’s game or something worse? I’ll find out, even if it’s just to prove I’m not that guy who flunks at saving humanity. [img[setup.ImagePath + $chars.dataMap.get("you").img]]` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Head Maid` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= $chars.dataMap.get("archmaid").name>>, My Head Maid</h2> I woke up in the Lost Temple, head pounding, and there she was—Selena, my head maid. First thing I saw: her gentle hands wiping my forehead with a cloak, her voice soft but sure. "The light’s still there... The Goddess chose you. I’m your head maid, Selena." Those words hit me hard. Me, chosen? I’m just a guy who got zapped here, but her faith—it’s unshakable. It’s humbling. Selena’s a paradox: tough as steel, tender as a whisper. Her gold hair glows like sunlight, and her blue eyes hold a kindness that cuts through this world’s gloom. She’s beautiful, sure, but it’s her heart that floors me. Word is, during our escape, she defied the butler to save stragglers—dozens owe her their lives. Compassion like that? It’s rare, and it makes me want to be better. As Lord, I rely on her strength. She runs the temple like it’s her own, firm but never cold. I catch her smiling at the freed Beastkin, and I think: maybe she sees hope where I see chaos. Selena’s not just a maid—she’s a lifeline. I’m lucky she believes in me. [img[setup.ImagePath + $chars.dataMap.get("archmaid").img]]` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Butler` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= $chars.dataMap.get("butler").name>>, My Butler</h2> I met Raymond in the Lost Temple’s flickering torchlight, and let me tell you—he’s something else. At 50, he’s the picture of an English butler, down to his emerald velvet tailcoat, not a crease out of place. Gray hair, stern face, but there’s a warmth in his eyes that sneaks through, like he’s sizing you up and rooting for you at the same time. Even after demons destroyed our town, the man’s grace doesn’t falter. It’s unreal—I’d be a mess, but Raymond? Steady as stone. He’s been with my fief’s family forever, they say, serving the old Lord through who-knows-what. Reliable doesn’t cover it; he’s a rock. When he speaks, it’s all poise, every word measured, yet kind. I caught him organizing supplies with a nod and a smile—chaos doesn’t faze him. There’s a story behind those eyes, especially about the old lord. I need to ask him, soon. For now, as Lord, I’m just glad Raymond’s here to keep me from screwing this up. [img[setup.ImagePath + $chars.dataMap.get("butler").img]]` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Captain` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= $chars.dataMap.get("captain").name>>, My Guard Captain</h2> Garvin’s a walking fortress. I met him in the Lost Temple, clad in Armor so battered it’s more scratches than steel—marks of battles I can’t even imagine. He’s the guard captain, and he’s as straight-talking as they come: no fluff, just truth. Reliable? That’s an understatement. He never takes off his helmet—word is, a bad scar hides underneath. I respect his choice to keep it private; some wounds don’t need sharing. During our escape, demons—elite ones—ambushed us. Garvin faced them head-on, buying our survival with his blood. He’s alive, but barely. Injuries sapped his strength, yet he still stands watch at the valley’s defenses, day and night, keeping enemies out. It’s humbling, but it worries me. As Lord, I’ve got to train strong soldiers and build solid fortifications fast. Garvin deserves rest to heal—not another fight. [img[setup.ImagePath + $chars.dataMap.get("captain").img]]` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Catgirl Maid` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= $chars.dataMap.get("catmaid").name>>, My Catgirl Maid</h2> Today, I met someone who turned my world upside down—again. Her name’s Stellafall, a catgirl from the Repurposed Chambers, and I can’t get her out of my head. I found her in what used to be a dungeon cell, curled up in a corner of that grim stone maze. Back when it was a prison, she was one of the orcs’ kind—well, a monster girl, locked away like so many others. churn. This world’s cruelty keeps hitting me like a slap, but Stellafall? She’s the opposite—a burst of light in the dark. I offered her freedom, no questions asked, and then something crazy happened: I hired her as my maid. Yeah, me, a guy who barely managed office coffee runs, now has a catgirl maid. And she’s not just any maid—she’s a whirlwind of energy. Stellafall’s got this infectious cheer that makes you forget the demons outside our gates. She bounced right up, tail swishing, and said, "I’m Stellafall. They say the hens laid double-yolk eggs the night I was born." I laughed—couldn’t help it. Double-yolk eggs? That’s her story, and she swears it means she’s lucky. Her look is... wow. She’s a perfect blend of feral and beautiful, like a lynx that decided to walk as a woman. Orange hair spills down her shoulders, wild and bright as a sunset, catching every flicker of torchlight. Her eyes? Pure gold, sharp and warm, like they see right through you but choose to smile anyway. She moves with a grace that’s half predator, half dancer—claws tucked away, but you know they’re there. I keep catching myself staring, then feeling like an idiot. She’s a Species all her own, a monster girl who’s more alive than half the nobles I’ve met. Stellafall’s not just a pretty face, though. She’s quick—body and mind. I’ve seen her dart around the chambers, tidying up rubble like it’s a game, chatting with the freed Beastkin to lift their spirits. She’s already made herself at home, claiming a cozy Bedroom with a cracked window she says "lets in the stars." I asked her what she wants, expecting anger or fear after her time locked up. Instead, she grinned and said, "Fish, freedom, and maybe a dance or two!" That’s her—unbreakable, even after everything. Hiring her felt right, but it’s more than that. I think she’s a sign—my lucky break in this mess of a world. The Repurposed Chambers are filling up, and with Stellafall here, it’s starting to feel less like a ghost town. I’m no Lord in shining armor, but if she believes in double-yolk luck, maybe I can believe in her. She’s helping me see this place for what it could be: a home, not a cage. I just hope I don’t let her down. [img[setup.ImagePath + $chars.dataMap.get("catmaid").img]]` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Goddess` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= $chars.dataMap.get("leah").name>>, My Very Questionable Goddess</h2> I’m officially losing it. Meet Leah, a ceramic angel—think dollar-store cherub with a faint glow, parked in my hands like I’m some nutcase collector. Found her in the Lost Temple, and boom, she’s chatting in my head, a voice like radio static only I hear. Says she’s a goddess, names herself Leah, and I’m her Lord. Sure, why not? Now I’m muttering to a figurine while Selena and Raymond side-eye me like I’ve gone feral. Tell them it’s their deity, down to her last spark? Nope, I’d rather eat my Armor. Leah’s no small fry—she made the Planar Rift, a cosmic gash in the temple that could save Hawkridge Barony from demon hordes. Problem? Her stunt blew the Ranch to rubble. Great miracle, Leah. She swears we’ll save the world, but then—brace yourself—she drops this: I need to waltz into the Planar Corridor, play matchmaker for monsters, and, uh, sometimes join in to speed up baby-making. What?! A fertility goddess who’s also our apocalypse fixer? I’m screaming inside—am I nuts, or is she? My Netflix binges didn’t prep me for this plot twist. I’m dodging everyone’s stares harder than demon claws. Leah wants monster girls or others in on this? Spare me! I’m not roping Garvin or Erin into her wild breeding scheme—my dignity’s hanging by a thread. Still, that rift’s our shot against the chaos tearing the fief apart. So, I grit my teeth, clutch my pottery pal, and roll with the monster crew to play savior. Leah’s hiding stuff—her power drain, that explosion, this whole setup smells fishy. I’ll crack her secrets eventually. For now, I’m the guy who’ll save the world, even if it means looking like a total weirdo. One day, they’ll see: I’m the real deal. [img[setup.ImagePath + $chars.dataMap.get("leah").img]]` }>>
<<set _condition = () => $times.total >= 5>> <<set setup.entrytitle = { "en": `Griffin Knight` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= $chars.dataMap.get("griffinknight").name>>, the Griffin Captain</h2> I met Gregory today, and holy hell, he’s intense. Swooped into the Lost Temple on a griffin—think lion-eagle hybrid, claws like knives. He’s Captain of the 3rd Griffin Squadron, all business in gleaming Armor, voice like a whip: "I’m Gregory. Demons overrun the land; I’m here to scout and collect war taxes." Taxes? My fief’s got 2000 gold, and he wants 100,000. I’m screwed. The guy’s a walking fortress, cold as a spreadsheet, but there’s this... weight to him, like he’s seen too much. Raymond tried to soften him, pleading, "Sir Gregory, for the sake of your bond with the late lord, could you show mercy? He hoped you’d look after his heir." My dad, apparently, trusted him. Gregory didn’t budge much—just gave us a payment plan, not a pass. Cold, sure, but fair? Maybe. Word is, he’s military royalty: dad was a legendary armsmaster, mom the empire’s top alchemist. Garvin swears Gregory smashed Beastfolk wolfriders at Blackstone Gorge with only seven griffins. Seven! I’d trip over a baby one. He rides that griffin across the empire, collecting coin while demons shred everything. Powerful? No question—his presence alone could scare off half my problems. But there’s a chill to him, like he’s all duty, no heart. Still, I caught a flicker when Raymond mentioned my dad—something human. Makes me wonder what he’s not saying. As Lord, I need him on my side, not just my wallet. For now, I’ll pay what I can and keep my eyes on him. This guy’s a force, but nobody’s that untouchable. [img[setup.ImagePath + $chars.dataMap.get("griffinknight").img]]` }>>
<<set _condition = () => true>> <<set setup.entrytitle = { "en": `Novice Maid` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= $chars.dataMap.get("promaid").name>>, the Novice Maid</h2> I noticed Erin today, our apprentice maid, slipping through the Lost Temple’s halls like a shadow. She’s shy—painfully so—black hair framing a face that ducks every glance. Her maid outfit’s too big, hanging off her like it belongs to someone else, but she works with a focus that’s almost eerie. Words? Barely a whisper. Yet when she tends the wounded, it’s magic—gentle hands, steady as a surgeon. She’s got a gift. Here’s the weird part: Raymond, Garvin, even Selena hover over her like hawks. They’re kind, but cagey. I asked about her past—nothing. Just tight lips and wary looks. There’s a story here, something big, and it’s driving me nuts. As Lord, I’ll figure it out. For now, I’m just glad Erin’s in our Bedroom wing, quietly holding us together. [img[setup.ImagePath + $chars.dataMap.get("promaid").img]]` }>>
<<set _condition = () => $times.total > 1>> <<set setup.entrytitle = { "en": `[Day 01]` } >> <<set setup.entrycontent = { "en": `[Day 01] The Starter Pack & Gacha Mechanics 【Event: Awakening & Stellafall the Catgirl】 Subjects: The Ceramic Goddess (Leah), Planar Rift, Stellafall. Mechanics Analysis: UI/Interaction: Leah is a terrible "Guide NPC." Aside from handing out quests, there’s absolutely no documentation. Her so-called "Planar Rift" is clearly just a Gacha pool. Starter Unit (Unit-01: Stellafall): Traits: High DEX, Low STA. A typical Assassin/Scout build. Logic: She showed extreme loyalty immediately upon release (Max Affinity at start?). Her personality type is "Tsundere/Genki," but she displays a terrifying killer instinct in combat. Interaction Test: I touched her when releasing her and felt a static shock. Leah calls it "Soul Binding," but it feels more like Bluetooth pairing. Once paired, I can vaguely sense her location. Daily Summary: Give them a fish, and they'll die for you? Labor costs in this world are ridiculously low.` }>>
<<set _condition = () => $times.total > 2>> <<set _day = 2 >> <<set setup.entrytitle = { "en": `[Day 02]` } >> <<set setup.entrycontent = { "en": `[Day 02] The Slave Market & The "Blind Box" Economy 【Event: The Slaver Caravan's Visit】 Subjects: Slaver, Monster Girls for sale. Market Analysis: Slave trade is legal here, and they have a surprisingly robust pricing system. Pricing Logic: Labor > Aesthetics > Combat. Combat units are cheap because they are volatile and prone to rebellion. Hidden Gems: I eyed some low-level monster girls tagged as "Defective." To the locals, they're scrap. To me, they are top-tier broodmothers. Daily Decision: Spent 500 Gold on three "defects." Raymond the Butler nearly fainted from the cost. Just watch—next week, I'll turn these "scraps" into the territory's greatest assets.` }>>
<<set _condition = () => $times.total > 5>> <<set _day = 5 >> <<set setup.entrytitle = { "en": `[Day 05]` } >> <<set setup.entrycontent = { "en": `[Day 05] The Medieval Sh*tshow & Resource Management 【Event: Inventory Check】 Subjects: Reach Refugees, Moldy Bread, Raymond. Environment Analysis: Don't believe the light novels. The Middle Ages aren't romantic. It stinks here. Hygiene: The refugee camp has no latrines. I had to draw up a blueprint for a septic tank and told Garvin: "Build this, or we die of cholera long before the demons get us." Economic Model: Only few Gold left. With 80+ mouths to feed, if we don't generate revenue, we go bankrupt (and starve) in 10 days. Magic Agriculture: The only upside is "Dawnveil Lake," formed by the explosion. Plants grow 5x faster there. The water is full of high-concentration "Mana Radiation." I've got farmers planting potatoes—hopefully, the crop won't try to eat us. Daily Summary: This isn't an RPG; it's a hardcore survival RTS. Priority one isn't slaying monsters—it's building toilets and farming.` }>>
<<set _condition = () => $times.total > 8>> <<set _day = 8 >> <<set setup.entrytitle = { "en": `[Day 08]` } >> <<set setup.entrycontent = { "en": `[Day 08] Core Gameplay Unlocked: Injection & Speed-Ups 【Event: Acquired Mirael the Succubus】 Subjects: Mirael, The Energy Infuser. Mechanics Analysis: Caught the broken-winged succubus. Leah immediately unlocked the [Energy Infusion] feature. I thought this was just for the H-scenes, and... well, the process is erotic, but functionally, it's a cheat code. Biological Miracle: The Infuser compresses a 30-day gestation period into minutes. Screw thermodynamics. The energy must be deducted directly from Leah's "Divine Power Pool." Output: No babies here—offspring are sent to the Rift (AFK plane) and return as adults. Time inside the Temple domain is clearly non-linear. Mirael's Stats: Despite her broken wings (flight disabled), her [Charm] is off the charts. Even in a weakened state, her presence gives nearby males a "Morale +10%" buff... and a "Sanity -5%" debuff. Side Note: I feel like a sweatshop owner. Mirael seems to enjoy it though (Masochist trait?). As long as we're mass-producing succubus units, ethics can take a backseat. We're surrounded by demons, after all.` }>>
<<set _condition = () => $times.total > 15>> <<set _day = 15 >> <<set setup.entrytitle = { "en": `[Day 15]` } >> <<set setup.entrycontent = { "en": `[Day 15] Intro to Demon Ecology 【Event: Post-Defense Autopsy】 Subjects: Imp Corpses, Mutated Beasts. Biological Analysis: Garvin burned the corpses, but I kept a tissue sample. Under the microscope (I ground the lenses myself), the cells are hyperactive, like cancer. Blood Poison: This isn't a virus. It's corrosive mana residue. It forcibly rewrites DNA, turning humans into zombies. Immunity: Stellafall and Mirael are immune. Their "non-human genes" act as natural antibodies. Conclusion: Humans are weak in this patch. The winning meta is an "All-Monster Girl" or "Hybrid Legion" build. Pure human soldiers are just cannon fodder. It's cruel, but the data doesn't lie.` }>>
<<set _condition = () => $times.total > 17>> <<set _day = 17 >> <<set setup.entrytitle = { "en": `[Day 17]` } >> <<set setup.entrycontent = { "en": `[Day 17] The Hidden Path NPC 【Event: Sariya the Lizard Prophet】 Subjects: Sariya, Ancient Tablet. Trigger: A strange lizard-girl arrived today, claiming to be a prophet. Trait Analysis: [Cold-Blooded]. Her body temp shifts with the environment, making her invisible to thermal detection magic. The perfect stealth unit. Questline: She mentioned an "Ancient Passage." Definitely a main quest clue. There's likely a massive dungeon system beneath the Temple. Social Game: She's not gullible like Stellafall or submissive like Mirael. She has her own agenda. I need to play the "mysterious leader" card perfectly. Daily Summary: Hired her as a "Maid" (actually a Geologist). Her scales are cool to the touch—great for saving on AC in the summer. But I have to watch her. When NPCs get independent AI, variables happen.` }>>
<<set _condition = () => $times.total > 20>> <<set _day = 20 >> <<set setup.entrytitle = { "en": `[Day 20]` } >> <<set setup.entrycontent = { "en": `[Day 20] Religion & The "Signal Tower" Theory 【Event: Fluctuations in the Temple Barrier】 Subjects: The Lost Temple, Leah's Statue. Theory Building: Why can't demons find the entrance? I studied the runes. It's not just magic; it's "Frequency Jamming." The Temple emits a high-frequency mana wave that scrambles their radar. As for the "Goddess of Fertility" doctrine... the "Love and Peace" stuff is total PR fluff. The core doctrine is simple: "Increase Population" (aka Compute Power/Sample Size). Religion is just a management tool here. It keeps the population's SAN values from zeroing out. I'll use that. Am I the Chosen One? Sure, whatever. Kneel before me.` }>>
<<set _condition = () => $times.total > 25>> <<set _day = 25 >> <<set setup.entrytitle = { "en": `[Day 25]` } >> <<set setup.entrycontent = { "en": `[Day 25] Capital & The Black Market 【Event: Hizuki the Fox & The Silver Tail Guild】 Subjects: Hizuki (Nine-tailed? No, Two-tailed), Black Market Ledger. Economy Upgrade: Finally, someone who understands business. Hizuki, a vixen (literally). Business Model: She wants to open a permanent black market here. My territory is the only "Neutral Zone" friendly to monster girls. Negotiation: She tried to lower the tax rate using her looks. Please. I look at succubi all day. All I care about are profit margins. Deal Struck: I provide the venue and security (Gavin's job); she handles the channels and fencing stolen goods. Strategic Value: Eagle's Reach has officially upgraded from "Refugee Camp" to "Trade Hub." I can sell the Temple's excess "produce" to rich idiots outside for supplies. Gold is finally flowing. With money, I can expand faster and buy more blueprints from the System Shop (if it exists).` }>>
<<set _condition = () => $times.total > 30>> <<set _day = 30 >> <<set setup.entrytitle = { "en": `[Day 30]` } >> <<set setup.entrycontent = { "en": `[Day 30] Monthly Review: From Survival to Management 【Event: First Major Defense Successful】 Data Review: Population: 80 -> [Current] (absorbed refugees and bought slaves). Funds: 2,000 -> [Current] (Trade and Quest rewards). Combat Power: One Commander -> A Mixed Squad (Stellafall's Scouts + Mirael's Crowd Control + Sariya's Underground Ops). Reflection: We survived. It's exhausting—babysitting Leah, watching my back with Hizuki, and feeding the vale like livestock. But I've figured out about 70% of this "game's" mechanics. Current Blind Spots: I still view this as a "Hardcore Resource Management Game." Demons are environmental hazards. Monster girls are functional NPCs. I am the Player. I haven't realized what the tablet Sariya found actually implies. Nor have I noticed that the look in Mirael's eyes isn't just submission anymore—it's something heavy, called "love." Whatever. Next month's goal: Deal with Gregory the Tax Collector (Quest delayed 3 months to lower difficulty). Even in an Isekai, the IRS is the toughest Boss.` }>>
<<set _condition = () => false>> <<set setup.entrytitle = { "en": `Note10` } >> <<set setup.entrycontent = { "en": `` }>>
<<set _condition = () => false>> <<set setup.entrytitle = { "en": `Note11` } >> <<set setup.entrycontent = { "en": `` }>>
<<set _condition = () => $manage.ownrace.has("avian")>> <<set setup.entrytitle = { "en": `Avian Beings` } >> <<set setup.entrycontent = { "en": ` <h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> <table> <thead> <tr> <th>Species</th> <th>Rank</th> <th>Gender</th> <th>Harvest</th> <th>Size</th> <th>Feat</th> </tr> </thead> <tbody> <tr> <td><<= setup.racedata["avian"][0]["name"][$lang]>></td> <td>lv0</td> <td>female</td> <td><<= getiteminfo("avianmucus","name")>></td> <td>Medium--</td> <td>CHA</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["avian"][1]["name"][$lang]>></td> <td>lv1</td> <td>female</td> <td><<= getiteminfo("avianmucus","name")>></td> <td>Medium--</td> <td>CHA</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["avian"][2]["name"][$lang]>></td> <td>lv2</td> <td>female</td> <td><<= getiteminfo("avianmucus","name")>></td> <td>Medium--</td> <td>CHA</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["avian"][3]["name"][$lang]>></td> <td>lv3</td> <td>female</td> <td><<= getiteminfo("avianmucus","name")>></td> <td>Medium--</td> <td>CHA</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> </tbody> </table> <div><p><strong>Notes:</strong> [More details require further observation]</p></div> ` }>>
<<set _condition = () => $manage.ownrace.has("bovine")>> <<set setup.entrytitle = { "en": `Bovine Beings` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> <table> <thead> <tr> <th>Species</th> <th>Rank</th> <th>Gender</th> <th>Harvest</th> <th>Size</th> <th>Feat</th> </tr> </thead> <tbody> <tr> <td><<= setup.racedata["bovine"][0]["name"][$lang]>></td> <td>lv0</td> <td>female</td> <td><<=getiteminfo("bovinemilk","name")>></td> <td>Large</td> <td>FER</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["bovine"][1]["name"][$lang]>></td> <td>lv1</td> <td>female</td> <td><<=getiteminfo("bovinemilk","name")>></td> <td>Large</td> <td>FER</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["bovine"][2]["name"][$lang]>></td> <td>lv2</td> <td>female</td> <td><<=getiteminfo("bovinemilk","name")>></td> <td>Large</td> <td>FER</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["bovine"][3]["name"][$lang]>></td> <td>lv3</td> <td>female</td> <td><<=getiteminfo("bovinemilk","name")>></td> <td>Large</td> <td>FER</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> </tbody> </table> <div><p><strong>Notes:</strong> [More details require further observation]</p></div> ` }>>
<<set _condition = () => $manage.ownrace.has("canine")>> <<set setup.entrytitle = { "en": `Canine Beings` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> <table class="codex-table"> <thead> <tr> <th>Species</th> <th>Rank</th> <th>Gender</th> <th>Harvest</th> <th>Size</th> <th>Feat</th> </tr> </thead> <tbody> <tr> <td><<= setup.racedata["canine"][0]["name"][$lang]>></td> <td>lv0</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["canine"][1]["name"][$lang]>></td> <td>lv1</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td><<=getiteminfo("caninesemen","name")>></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["canine"][2]["name"][$lang]>></td> <td>lv2</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td><<=getiteminfo("caninesemen","name")>></td> <td>Medium</td> <td>STA</td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["canine"][3]["name"][$lang]>></td> <td>lv3</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td><<=getiteminfo("caninesemen","name")>></td> <td>Medium</td> <td>STA</td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> </tbody> </table> <div><p><strong>Notes:</strong> [More details require further observation]</p></div> ` }>>
<<set _condition = () => $manage.ownrace.has("equine")>> <<set setup.entrytitle = { "en": `Equine Beings` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> <table> <thead> <tr> <th>Species</th> <th>Rank</th> <th>Gender</th> <th>Harvest</th> <th>Size</th> <th>Feat</th> </tr> </thead> <tbody> <tr> <td><<= setup.racedata["equine"][0]["name"][$lang]>></td> <td>lv0</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["equine"][1]["name"][$lang]>></td> <td>lv1</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td><<= getiteminfo("equinesemen","name")>></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["equine"][2]["name"][$lang]>></td> <td>lv2</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td><<= getiteminfo("equinesemen","name")>></td> <td>Large+</td> <td>STR</td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["equine"][3]["name"][$lang]>></td> <td>lv3</td> <td>female</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>male</td> <td><<= getiteminfo("equinesemen","name")>></td> <td>Large+</td> <td>STR</td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> </tbody> </table> <div><p><strong>Notes:</strong> [More details require further observation]</p></div> ` }>>
<<set _condition = () => $manage.ownrace.has("feline")>> <<set setup.entrytitle = { "en": `Feline Beings` } >> <<set setup.entrycontent = { "en": `<h2 class="entry-header"><<= setup.entrytitle[$lang]>></h2> <table class="codex-table"> <thead> <tr> <th>Species</th> <th>Rank</th> <th>Gender</th> <th>Harvest</th> <th>Size</th> <th>Feat</th> </tr> </thead> <tbody> <tr> <td><<= setup.racedata["feline"][0]["name"][$lang]>></td> <td>lv0</td> <td>female</td> <td><<= getiteminfo("felinejuice","name")>></td> <td>Medium-</td> <td>DEX</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["feline"][1]["name"][$lang]>></td> <td>lv1</td> <td>female</td> <td><<= getiteminfo("felinejuice","name")>></td> <td>Medium-</td> <td>DEX</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td></td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td></td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["feline"][2]["name"][$lang]>></td> <td>lv2</td> <td>female</td> <td><<= getiteminfo("felinejuice","name")>></td> <td>Medium-</td> <td>DEX</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td><<= setup.racedata["feline"][3]["name"][$lang]>></td> <td>lv3</td> <td>female</td> <td><<= getiteminfo("felinejuice","name")>></td> <td>Medium-</td> <td>DEX</td> </tr> <tr> <td></td> <td></td> <td>male</td> <td>??</td> <td></td> <td></td> </tr> <tr> <td></td> <td></td> <td>Futanari</td> <td>??</td> <td></td> <td></td> </tr> </tbody> </table> <div><p><strong>Notes:</strong> [More details require further observation]</p></div> ` }>>
<center><h1>Calendar</h1> <h2><<= setup.TimeSystem.getCurrentInfo()>> <br>Day $times.total</h2></center> You open your codex, containing all the knowledge you've acquired so far. <br>You realize the hidden dangers of this world - keeping thorough records shall become your greatest weapon <br>when destiny calls! <<include "Calendar">>
<style> .codex-container { background-color: #0a0a0c; width: 100%; border: 1px solid #333; border-top: 3px solid #8a1c1c; border-radius: 4px; padding: 1rem; color: var(--text-primary); box-shadow: 0 0 15px rgba(0, 0, 0, 0.8); margin: 0 auto; } .codex-header { text-align: center; font-size: 1.6em; color: #8a1c1c; text-transform: uppercase; letter-spacing: 2px; border-bottom: 1px solid #333; padding-bottom: 10px; margin-bottom: 20px; text-shadow: 0 2px 4px rgba(0,0,0,0.5); } .codex-section { background: rgba(20, 20, 25, 0.6); border-left: 3px solid #444; margin-bottom: 15px; padding: 12px; transition: transform 0.2s; } .codex-section:hover { background: rgba(30, 30, 35, 0.8); } .theme-blood { border-left-color: #8a1c1c; } .theme-abyss { border-left-color: #1c3b8a; } .theme-gold { border-left-color: #856404; } .section-title { font-weight: bold; font-size: 1.1em; margin-bottom: 8px; display: flex; align-items: center; gap: 10px; } .txt-blood { color: #d9534f; } .txt-abyss { color: #5bc0de; } .txt-light { color: #e0e0e0; font-weight: bold; } .txt-dim { color: #777; font-size: 0.9em; font-style: italic; } ul.codex-list { margin: 5px 0 5px 20px; padding: 0; list-style-type: square; } ul.codex-list li { margin-bottom: 6px; line-height: 1.5; } </style> <style> .grimoire-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); gap: 1rem; margin-top: 1rem; } .stat-slab { display: flex; height: 19rem; background: rgba(20, 20, 25, 0.6); border: 1px solid #444; border-top: 3px solid #666; border-radius: 4px; padding: 0.75rem; transition: transform 0.2s, box-shadow 0.2s, border-color 0.2s; position: relative; overflow: hidden; flex-direction: column; } .stat-slab:hover { transform: translateY(-3px); box-shadow: 0 5px 15px rgba(0,0,0,0.5); background: rgba(30, 30, 35, 0.8); border-top-color: #8a1c1c; } .slab-header { display: flex; align-items: center; border-bottom: 1px solid #333; margin-bottom: 10px; } .slab-icon { font-size: 1.8em; color: #cbb06a; margin-right: 12px; width: 40px; text-align: center; } .slab-title-group { display: flex; flex-direction: column; } .slab-abbr { font-family: "Georgia", serif; font-size: 1.4em; font-weight: bold; color: #e0e0e0; letter-spacing: 2px; } .slab-name { font-size: 0.85em; color: #888; text-transform: uppercase; } .slab-desc { font-size: 0.9rem; color: #b0b0b0; line-height: 1.5; margin-bottom: 0.5rem; } .slab-effects { background: rgba(0, 0, 0, 0.3); border-radius: 4px; padding: 10px; font-size: 0.85em; } .effect-row { display: flex; align-items: flex-start; margin-bottom: 8px; line-height: 1.4; } .effect-row:last-child { margin-bottom: 0; } .effect-icon { margin-right: 8px; margin-top: 2px; font-size: 1em; } .eff-ritual { color: #5bc0de; } .eff-combat { color: #d9534f; } .eff-locked { color: #555; } .txt-highlight { color: #fff; font-weight: bold; opacity: 0.9; } </style> <style> .evo-container { display: flex; flex-direction: column; gap: 30px; margin-top: 20px; } .phase-header { display: flex; align-items: center; border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 15px; } .phase-title { font-family: "Georgia", serif; font-size: 1.2em; color: #cbb06a; text-transform: uppercase; letter-spacing: 2px; margin-right: 15px; } .phase-tag { font-size: 0.7em; padding: 2px 8px; border-radius: 4px; font-weight: bold; letter-spacing: 1px; } .tag-internal { background: rgba(138, 28, 28, 0.2); color: #d9534f; border: 1px solid #8a1c1c; } .tag-external { background: rgba(91, 192, 222, 0.2); color: #5bc0de; border: 1px solid #1c3b8a; } .flow-connector { text-align: center; color: #666; font-size: 1.5em; margin: -15px 0; animation: pulse 2s infinite; } @keyframes pulse { 0% { opacity: 0.3; transform: translateY(0); } 50% { opacity: 1; transform: translateY(3px); } 100% { opacity: 0.3; transform: translateY(0); } } .progress-mini-container { background: #111; border: 1px solid #333; height: 6px; margin-top: 8px; border-radius: 2px; overflow: hidden; } .progress-mini-fill { height: 100%; background: #8a1c1c; width: 0%; transition: width 0.5s; } .source-icons { display: flex; gap: 15px; margin-top: 15px; padding-top: 10px; border-top: 1px dashed #333; font-size: 0.8em; color: #888; } .source-item i { margin-right: 5px; color: #cbb06a; } .summary-list { background: rgba(0,0,0,0.2); border-left: 3px solid #555; padding: 15px; margin-top: 20px; font-size: 0.9em; color: #b0b0b0; } .summary-list li { margin-bottom: 8px; } .txt-highlight { color: #e0e0e0; font-weight: bold; } </style> <style> .alchemy-card { font-size: 0.85rem; overflow-y: auto; max-height: 10rem; border: 1px solid #333; border-left: 4px solid #d4af37; padding: 0.5rem; } .card-header { display: flex; justify-content: space-between; border-bottom: 1px solid #333; padding-bottom: 5px; margin-bottom: 8px; } .trait-name { font-weight: bold; color: #eee; text-transform: uppercase; font-size: 1.1em; } .trait-tier { margin-left: auto; font-size: 0.8em; padding: 0 4px; border-radius: 2px; } .tier-base { background: #333; color: #aaa; } .tier-high { background: #d4af37; color: #111; font-weight: bold; } .info-row { margin-bottom: 4px; display: flex; align-items: center; } .label { color: #888; margin-right: 6px; } .natural { font-style: italic; color: #666; } .apex { color: #d4af37; } .group-title { font-size: 0.8rem; border-bottom: 1px dashed #333; } .fusion-item { background: #1a1a1a; margin-top: 2px; display: flex; align-items: center; font-size: 0.75rem; } .needs { color: #aaa; } .arrow { color: #555; margin: 0 5px; } .result { color: #81d4fa; font-weight: bold; } .tier-tag { font-size: 0.7rem; margin-left: auto; background: #222; color: #cbcbcb; padding: 0 3px; } .conflict-group .conflict-list { display: flex; flex-wrap: wrap; gap: 5px; } .conflict-tag { background: #3a1111; color: #ff6666; border: 1px solid #500; padding: 1px 5px; font-size: 0.85em; border-radius: 3px; } </style>
<<widget "Sleep">> <<DailyUpdate>> <<run setup.TimeSystem.passOneDay();>> <<goto "Bedroom_Morning">> <</widget>> <<widget "DailyUpdate">> <<script TwineScript>> Inventory.events.update.one( function (ev) { if (ev.inventory.count('food') === 0) { UI.alert(setup.langbank.foodpanic[$lang]); $domain.humanpp -= random(1,10); } }); <</script>> <<set $domain.wartaxdeadline -= 1 >> <<set $battle.demonattackdeadline -= 1 >> <<set $manage.energy.current = Math.clamp($manage.energy.current + $manage.energy.regen, 1000, $manage.getMaxEnergy()) >> <<pickup $storage "food" `getResourceOutput('food')`>> <<pickup $storage "wood" `getResourceOutput('wood')`>> <<pickup $storage "stone" `getResourceOutput('stone')`>> <<pickup $storage "ore" `getResourceOutput('ore')`>> <<pickup $storage "gold" `getResourceOutput('gold')`>> <<set $job.summary = Job.dailyJobs()>> <<set $domain.fooduse = Math.trunc($domain.getTotalPop() / 10 ) >> <<drop $storage "food" $domain.fooduse >> <<set $messages.messageFast = []>> <<set $messages.isFastMessageShowing = false>> <<set $messages.notices = []>> <<set $slave.slaveList = []>> <<run $slave.slaveMap.clear()>> <<set $manage.monstersCap = {} >> <<set $manage.monstersCap = countRaceGender($chars.tempList) >> <<run $chars.tempList.forEach(monsterId => $chars.dataMap.get(monsterId).dailyupdate());>> <<QuestUpdate>> <</widget>> <<widget "NextDayEvent">> <<switch $times.total>> <<case 2>> <<case 3>><<run Msg.add('eventcard', 'Event_Captain_Purged')>><<SetFlag 'tutorial' 9>><<SetFlag 'enbuild' 2>><<SetFlag 'captainflag' 1>><<run changeEffects(0, 1, "hawkridge_hamlet")>><<run changeEffects(0, 1, "verdant_wilds")>><<run changeEffects(0, 1, "quarry_mines")>> <<case 5>><<run Msg.add('eventcard', 'Event_Gregory_Taxes')>> <<case 6>><<run Msg.add('eventcard', 'Event_Selena_01')>> <<case 8>> <<case 17>><<run Msg.add('eventcard', 'Event_ScaleMaid_01')>> <<case 18>><<run Msg.add('eventcard', 'Event_ScaleMaid_02')>> <<case 19>><<run Msg.add('eventcard', 'Event_ScaleMaid_03')>> <<case 20>><<run Msg.add('eventcard', 'Event_ScaleMaid_04')>> <<case 21>><<run Msg.add('eventcard', 'Event_ScaleMaid_05')>> <<case 25>><<run Msg.add('eventcard', 'Event_Hizuki_01')>> <<case 28>><<run Msg.add('eventcard', 'Event_Leah_Train')>> <<case 31>><<run Msg.add('eventcard', 'Event_Hizuki_tradezone')>><<run changeEffects(0, 1, "tradezone")>> <<default>> <</switch>> <<PregnancyUpdate>> <<switch $times.weekday>> <<case 0>> <<case 1>><<run Msg.add('eventcard', 'Event_Report_Defend')>> <<case 2>><<set $slave.refresh = 1 >><<NewSlave>><<run Msg.add('eventcard', 'Event_Report_Slaver')>> <<case 3>> <<case 4>><<run newMarket()>><<if $times.total <= 25>><<run Msg.add('eventcard', 'Event_Report_Market')>><<else>><<run Msg.add('eventcard', 'Event_Report_Market2')>><</if>> <<case 5>> <<case 6>><<run Msg.add('eventcard', 'Event_Report_Refuge')>> <<default>><<run UI.alert("week time error")>> <</switch>> <<if $domain.getTotalPop() > 300>><</if>> <</widget>>
<style> .dispatch-area1 { font-size: 1rem; flex: 1 1 auto; height: 100%; } .dispatch-area2 { display: flex; flex: 0 0 21rem; max-width: 21rem; height: 100%; flex-direction: column; justify-content: center; } .dispatch-text { overflow-y: auto; height: 25vh; padding: 1rem; border-radius: 0.5rem; position: absolute; width: 70rem; background-color: #3d342c; bottom: 1rem; right: 4.5rem; z-index: 10; } .dispatch-close { font-size: 2rem; top: 1%; right: 1%; position: absolute; } .dispatch-text img { position: absolute; height: 54rem; pointer-events: none; top: -31rem; left: 72.5rem; z-index: 9; } .dispatch-list { margin: 1rem; align-content: start; border: 0.5rem solid #371d08; display: grid; grid-template-columns: repeat(auto-fill, minmax(clamp(12rem, 30%, 14rem), 1fr)); overflow-y: auto; height: 88vh; max-height: 88vh; background: #1a1816d1; padding: 0.5rem; gap: 1rem; } .dispatch-block { aspect-ratio: 9 / 10; position: relative; width: 100%; background-size: cover; cursor: pointer; flex-direction: column; } .dispatch-block img { position: absolute; width: 100%; } .dispatch-block:hover { filter: brightness(1.03); transform: translateY(-2px); } .dispatch-block-1 { pointer-events: none; position: relative; display: flex; top: 0.5rem; justify-content: center; } .dispatch-block-1 img { position: relative; width: 3rem; height: 3rem; } .dispatch-requirelist { color: var(--bg-body); pointer-events: none; position: relative; margin-top: 1rem; left: 0.75rem; width: 90%; height: 48%; } .dispatch-require { color: var(--bg-body); } .dispatch-deadline { display: block; top: -1.5rem; position: absolute; right: 0; color: var(--color-c); } .dispatch-block-3 { bottom: 0.75rem; pointer-events: none; position: absolute; left: 0.75rem; width: 90%; color: var(--bg-body); background-color: orange; } .dispatch-bounty-item { display: flex; } .dispatch-publisherimg img { max-height: 39rem; } .dispatch-matchCount { display: flex; justify-content: center; align-items: center; border-radius: 50%; height: 2rem; width: 2rem; background: red; right: 0rem; top: -0.5rem; position: absolute; color: white; font-size: 1rem; text-align: center; } </style> <div class="UIbox"> <!-- 分区1 --> <div class="dispatch-area1"> <!-- 任务列表 --> <<do tag "questslist">> <div class="dispatch-list"> <<for _i range $dispatch.quests.length>> <<DispatchSolt _i>> <</for>> </div> <</do>> </div> <!-- 分区2 --> <div class="dispatch-area2"> <<do tag "questlist">> <<if def _choiceDispatch>> <div class="dispatch-publisherimg">[img[setup.ImagePath +$dispatch.quests[_choiceDispatch].publisherpic]]</div> <div class="dispatch-text"> <span><<= $dispatch.quests[_choiceDispatch].publisher>> :</span><br> <span><<= $dispatch.quests[_choiceDispatch].text>></span><br> <div class="dispatch-close"><<link "❌">><<unset _choiceDispatch>><<redo "questlist">><</link>></div> <div class="dispatch-window"> <<button `langBank("FilterEligible")`>> <<run Dialog.create("", "SummonCG")>> <<run Dialog.wiki("<<DispatchFilter $dispatch.quests[_choiceDispatch]>>")>> <<run Dialog.open()>> <</button>> </div> </div> <<else>> <</if>> <</do>> </div> </div>
<<set _newquest = new Quest({})>> <<= _newquest.randomprop("wil",5);>> <<set _newquest.publisher = L.butler>> <<set _newquest.publisherpic = $chars.dataMap.get("butler").img>> <<set _newquest.bounty.gold += 50 >> <<set _newquest.text = L.questtip1>> <<run $dispatch.quests.push(_newquest);>> <<run randomDispatch()>> <<run randomDispatch()>> <<run randomDispatch()>>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Dispatch">> </div> </div> <<if visited() === 1 && $times.total < 5>> <<run Msg.add('eventcard', 'Event_Butler_Dispatch', 3)>> <</if>> <<run Msg.show();>>
<<widget QuestUpdate>> <<nobr>> <<run $dispatch.quests = $dispatch.quests.filter(q => { q.deadline--; return q.deadline > 0; }) >> <<set _addnum = $dispatch.capacity - $dispatch.quests.length >> <<if _addnum > 0>> <<for _i range _addnum>><<run randomDispatch()>><</for>> <</if>> <</nobr>> <</widget>> <<widget "DispatchSolt">> <<set _matchCount = filterMonsterByCondition($chars.tempList, $dispatch.quests[_args[0]]).count>> <<capture _matchCount>> <div class="dispatch-block" @id="_args[0]"> <<link [img[setup.ImagePath + 'ui/quest/paper360x400.webp']]>> <<set _choiceDispatch = _args[0]>> <<redo "questlist">> <<if _matchCount > 0>> <<run Dialog.create("", "SummonCG")>> <<run Dialog.wiki("<<DispatchFilter $dispatch.quests[_args[0]]>>")>> <<run Dialog.open()>> <</if>> <</link>> <div class="dispatch-block-1"> <<print $dispatch.quests[_args[0]].getDifficultyStars()>> <div class="dispatch-matchCount"><<= _matchCount>></div> </div> <div class="dispatch-requirelist"> <<print $dispatch.quests[_args[0]].getRequirementList()>> </div> <div class="dispatch-block-3"> <div class="dispatch-deadline"> <<= L.Avail>>:<<= $dispatch.quests[_args[0]].deadline>><<= "d">> </div> <div class="dispatch-publisher"><<= L.publisher>>:<<= $dispatch.quests[_args[0]].publisher>></div> <div class="dispatch-bounty-item"><<= L.bounty>>: <<print $dispatch.quests[_args[0]].getBountyList()>> </div> </div> </div><</capture>> <</widget>> <<widget DispatchFilter>> <style> .itemfilter-requirement-card { font-size: 0.8rem; overflow-y: auto; } </style> <<set _filtergather = filterMonsterByCondition($chars.tempList, _args[0]).ids>> <<set $chars.selected = null>> <div class="itemfilter-title">Filter results</div> <div class="itemfilter"> <div class="itemfilter-list"> <<MapList _filtergather>> </div> <div class="itemfilter-info"> <<if visited() <= 3 >> <div class="itemfilter-requirement-card"> <p><<= L.DispatchNotice1>></p> </div><</if>> <div> <<do tag "selected">> <<if $chars.selected>> <<Infocard $chars.selected>> <</if>> <</do>> </div> </div> <div class="itemfilter-act"> <<button setup.langbank.Dispatch[$lang]>> <<if !$chars.selected>> <<run UI.alert(L.selecttarget)>> <<elseif $chars.selected["type"] == "unique">> <<run UI.alert(L.nospecial)>> <<elseif $chars.selected["worktags"]>> <<run UI.alert(L.currentlyworking)>> <<else>> <<run $domain.Changes($dispatch.quests[_choiceDispatch].bounty)>> <<DelMonster $chars.selected>> <<run $dispatch.quests.deleteAt(_choiceDispatch)>> <<run $domain.monsterpp += 1>> <<unset _choiceDispatch>> <<set $chars.selected = null>> <<set $summon.slots to [null, null]>> <<set $summon.currentsolt = -1>> <<set $manage.dispatchCount += 1 >> <<redo "questslist topbar">> <<run Dialog.close()>> <</if>> <</button>> <<button "Close">> <<run Dialog.close()>> <</button>> </div> </div> <</widget>>
<style> .territory-report { overflow-y: auto; height: 100%; width: 100%; padding: 1rem; border: 1px solid #b38736; } .report-title { text-align: center; font-size: 1.2rem; color: #b38736; text-transform: uppercase; margin-bottom: 0.5rem; padding-bottom: 0.3rem; border-bottom: 1px solid #b38736; } .category-section { margin-bottom: 0.5rem; } .grid-container { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 0.5rem 0.5rem; padding: 0.3rem; background: var(--bg-body); } .category-title { margin: 0px 0; grid-column: 1 / -1; font-size: 1.1rem; color: #e0d3af; padding: 0rem; text-transform: uppercase; text-align: center; } .data-cell { align-self: end; display: flex; flex-direction: column; } .data-key { font-size: 1rem; font-weight: bold; color: #e0d3af; padding: 0.3rem; line-height: 1.2; } .data-key-placeholder { height: 1.2rem; padding: 0.3rem; } .data-value { font-size: 1rem; color: #e0d3af; padding: 0.3rem; border-radius: 2px; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; line-height: 1.2; transition: background 0.2s; } .data-value:hover { background: #4b4b4b; } .no-data { text-align: center; color: #cb007a; font-size: 1rem; padding: 0.5rem; } @media (max-width: 600px) { .territory-report { margin: 0.5rem; padding: 0.5rem; } .report-title { font-size: 1.1rem; } .category-title { font-size: 1rem; } .grid-container { grid-template-columns: repeat(auto-fit, minmax(100px, 1fr)); } .data-key, .data-value, .data-key-placeholder { font-size: 1rem; padding: 0.2rem; } } </style> <<set setup.territoryData = { population: { humankind: $domain.humanpp, otherkindOutside: $domain.monsterpp, otherkindInside: $chars.tempList.length - 1 }, resources: { gold: $storage.count("gold") +"(+"+ $buildingEffects.resources.gold +")", energy: $manage.energy.current +"/"+$manage.getMaxEnergy(), food: $storage.count("food") +"(+"+ $buildingEffects.resources.food +")", wood: $storage.count("wood") +"(+"+ $buildingEffects.resources.wood +")", stone: $storage.count("stone") +"(+"+ $buildingEffects.resources.stone +")", ore: $storage.count("ore") +"(+"+ $buildingEffects.resources.ore +")" }, defense: { fortitude: $buildingEffects.stats.defense, wrath: $buildingEffects.stats.wrath }, working: { totalSummons: $manage.summon.count, totalFeed: $manage.feed.count, totalHarvest: $manage.harvest.count, totalDispatch: $manage.dispatchCount }, buildings: { unlockedTrait: $tech.enTraits.length, unlockedSpecies: $manage.ownrace.size }, event: { demonAttack: $battle.demonattackdeadline + " days", warTax: $domain.wartaxdeadline + " days", SlaverCaravan: "Tuesday", BlackMarket: "Thursday" } }; >> <<territoryReport>>
<<widget EventCard>> <div id="speechDisplay"></div> <<include _args[0]>> <<set _speeches = [] >> <<set _speeches = setup.speechData >> <<set _speechindex = 0 >> <<set _speechlength = _speeches.length >> <<timed 0.1s>> <<script TwineScript>> window.speechcontents = function (event) { const skip = _speech.skip; if (skip || skip === undefined) { if (_speechindex < _speechlength - 1) { $.wiki(`<<set _speechindex += 1 >><<redo "speech">>`); } } event.stopPropagation(); }; const speechContents = document.querySelector('#speechDisplay'); speechContents.addEventListener('click', function(event) { const skip = _speech.skip; const isButton = event.target.tagName === 'BUTTON'; if (isButton) { speechEnd(); } else { if (skip || skip === undefined) { if (_speechindex < _speechlength - 1) { $.wiki(`<<set _speechindex += 1 >><<redo "speech">>`);} else { speechEnd(); }; } else { }; } }); function speechEnd() { Dialog.close(); }; <</script>> <</timed>> <<do tag "speech">> <<set _speech = _speeches[_speechindex] >> <<set _onespeech = { "length": _speechlength, "index": _speechindex, "content": _speech[$lang] || _speech.en, "img": _speech.img, "id": _speech.id } >> <<timed 0.05s>><<SpeechOne _onespeech>><</timed>> <</do>> <</widget>> <<widget SpeechOne>> <<replace "#speechDisplay">> <<if _args[0].id != "narr">> <span class="speech-avatar"> <<if _args[0].img>> [img[setup.ImagePath + _args[0].img]] <<else>> <<set _eventimg = $chars.dataMap.get(_args[0].id)?.img >> [img[setup.ImagePath + _eventimg]] <</if>> </span> <</if>> <div class="speech"> <<if _args[0].id != "narr" && _args[0].id != "inst">><div class="speech-name"><<= $chars.dataMap.get(_args[0].id)?.name>></div><</if>> <<if _args[0].content>><div class="speech-contents"><<= _args[0].content>></div><</if>> <<SpeechSkip _args[0]>> </div> <</replace>> <</widget>> <<widget SpeechSkip>> <<if _args[0].index < _args[0].length-1>><div class="speech-skip"> <div onclick='speechcontents(event)'> <<button `L.continue+"▶"`>><</button>> </div> <div onclick='speechcontents(event)'> <<button `L.skip+"⏭️"`>><<set _speechindex = _args[0].length -2 >><</button>> </div> </div> <</if>> <</widget>>
<style> .container { width: 100%; place-items: center; padding: 2rem; border: 3px solid #8b0000; background-color: rgba(20, 20, 20, 0.8); } h1 { font-size: 4rem; color: #8b0000; text-shadow: 3px 3px 5px #000; margin: 1rem; } .consequence { font-style: italic; color: #a52a2a; border-top: 1px solid #8b0000; border-bottom: 1px solid #8b0000; padding: 1rem 0; margin: 2rem 0; } .buttoncss { display: inline-block; padding: 0.8rem 2rem; background-color: #8b0000; color: #c0c0c0; text-decoration: none; border: 1px solid #600; transition: all 0.3s ease; } .buttoncss:hover { background-color: #600; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); } .ur { display: none; } </style> <div class="blood-effect"></div> <div class="container"> <h1>GAME OVER</h1> <p>You have failed in your sacred duty...</p> <div class="consequence"> <p>The Baron's Keep has fallen to the demonic hordes.</p> <p>All inhabitants were slaughtered without mercy.</p> <p>Your failure sealed the fate of mankind.</p> </div> <p>Within months, the abyssal corruption spread across the realm.</p> <p>One by one, the last bastions of humanity crumbled into dust.</p> <p>Civilization is no more. The age of man has ended.</p> <div style="margin-top: 3rem;"> <<link '<div class="buttoncss">Main Menu</div>'>><<goto 'Welcome'>><<run Engine.restart();>><</link>> <<link '<div class="buttoncss">Load Save</div>'>><<run UI.saves()>><</link>> </div> </div>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"> <p>A crude wooden wall stands in a corner of the temple, hung with planks scrawled with writing, rough but practical.</p> </div>` },{ "id": "you", "en": `<div class="monologue"> <p><em>A task board? For this tiny crew? Feels like corporate crunch all over again...</em></p> </div>` },{ "id": "butler", "en": `<div class="narration"> <p>Raymond approaches the wooden wall, his eyes scanning the scrawled notes, his tone steady and resolute.</p> </div> <p>My Lord, this is our new task board. Even with few hands, order is vital. It lists requests from us and the fleeing peasants—a first step to rebuilding.</p>` },{ "id": "you", "en": `<div class="narration"> <p>You nod, your eyes scanning the scribbled notes on the planks, catching lines marked with rewards.</p> </div> <p>Good idea. I see rewards listed. By the way, we didn’t get to this earlier—how’s our food supply?</p>` },{ "id": "butler", "en": `<div class="narration"> <p>Raymond frowns, his tone heavy with concern, his gaze drifting toward the temple’s depths.</p> </div> <p>Our supplies are dire, my Lord. We fled in haste, bringing only scraps of food. We need provisions urgently to shelter more fleeing peasants.</p>` },{ "id": "you", "en": `<div class="narration"> <p>You frown, picturing the old days of Cloudvale Pasture in your mind.</p> </div> <p>This was a ranch before—shouldn’t there be plenty of food?</p>` },{ "id": "butler", "en": `<div class="narration"> <p>Raymond shakes his head, a flicker of resignation in his eyes.</p> </div> <p>Alas, the explosion and fires destroyed everything—the pasture’s resources are gone. We must start anew. For now, we gather what little the mountains offer, but it’s no lasting solution. We need farms, a ranch, and more hands.</p>` },{ "id": "you", "en": `<div class="narration"> <p>You pause, your tone calm but probing.</p> </div> <p>How many made it here with us?</p>` },{ "id": "butler", "en": `<div class="narration"> <p>Raymond meets your gaze, his tone grave, pointing to a rough tally board above.</p> </div> <p>Only 80 souls, all weary. Check the tally above: population 80, food 80. With strict rations, 10 people need 1 unit of food daily—enough for 10 days. If food runs dry, starvation or desertion will strike swiftly. Take heed, my Lord.</p>` },{ "id": "you", "en": `<div class="narration"> <p>You take a deep breath, your gaze hardening with resolve.</p> </div> <p>Food’s the priority. How do we boost production?</p>` },{ "id": "butler", "en": `<div class="narration"> <p>Raymond bows slightly, a hint of hope in his tone.</p> </div> <p>Demons still lurk outside the temple. We must reclaim a small patch of land to recover. Captain Garvin is clearing the way—progress should come in a few days.</p>` },{ "id": "you", "en": `<div class="narration"> <p>You glance toward the temple’s entrance, a spark of exploration in your eyes.</p> </div> <p>I want to check the valley outside, get a feel for the terrain.</p>` },{ "id": "butler", "en": `<div class="narration"> <p>Raymond’s brow furrows, his tone cautious yet firm, gesturing toward the entrance.</p> </div> <p>My Lord, it’s unwise now. Though most demons retreated, unable to find the temple’s entrance, stragglers still lurk in the valley. Captain Garvin has sealed the valley’s mouth and built simple fortifications. Best to wait until it’s fully cleared.</p>` },{ "id": "you", "img": "", "skip": true, "en": `<div class="narration"> <p>You nod, your eyes scanning the temple’s stone walls, pondering the next steps.</p> </div> <p>It seems we’ll have to hold out a few days until the valley’s cleared for resource gathering and rebuilding.</p> <p>Hope for good news soon</p><<SetFlag 'butleflag' 1>><<SetFlag 'endispatch' 1>><<SetFlag 'tutorial' 8>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"> <p>In the temple hall, Garvin strides forward, his scarred armor glinting, eyes blazing with resolve beneath his helmet.</p> </div>` },{ "id": "captain", "en": `<p>My Lord, good tidings! We fought fiercely and purged the valley of demon stragglers. Hold the valley’s mouth, and the land is secure!</p>` },{ "id": "you", "en": `<div class="narration"> <p>You nod, your gaze on Garvin approving, your tone buoyed with relief.</p> </div> <p>Well done! We can start production and find proper homes. Packing all in the temple is no answer. Tell me how it was done.</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin stands tall, his tone resolute, carrying the grit of a fresh victory.</p></div> <p>The day you awoke, the demon pursuers, unable to find the temple’s entrance, mostly withdrew, leaving few lurking in the valley. I led our men to seal the valley’s mouth, built simple fortifications, and used the terrain to trap and purge the fiends. Our beastfolk allies aided the hunt—their prowess sped our work. I misjudged them; they’re strong and true allies. We also cleared the ruins, set up a resource zone and living quarters, and moved scant supplies from the wreckage to the warehouse. </p>` },{ "id": "you", "en": `<div class="narration"> <p>You pause, your gaze sharpening as you plan the next steps.</p> </div> <p>The valley’s small—using it wisely is key. Once we’ve mustered enough strength, we’ll retake Hawkridge Town.</p>` },{ "id": "captain", "en": `<div class="narration"> <p>Garvin nods, his armor clinking softly, his tone edged with anticipation.</p> </div> <p>We must avenge our fallen brothers! By the way, my Lord, have you noticed? The goddess aids us. In mere days, the scorched pasture has regained its green vigor. The blast crater in the valley’s heart, filled by recent rains and mountain streams, has formed a radiant round lake—a marvel divine. They say this land holds a mystic power to breed exceptional steeds,I believe it is also true.</p>` },{ "id": "you", "en": `<div class="narration"><p>You nod, clapping Garvin’s armored shoulder, your tone steady, eyes sharp with resolve.</p></div> <p>This lake is a gift of the goddess. Let us name it Dawnveil Lake. With the goddess’s blessing and this fine land, we must first hold our ground. Garvin, the valley’s defense is yours. The griffon knights warn of a demon assault in three days. If danger strikes, we’ll all stand with you. Command every soul—man, woman, child, human, or beastfolk—as warriors. This world spares no strength for petty quarrels; only together can we survive.</p> <<SetFlag 'enmeeting' 2>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "butler", "en": `<div class="narration"><p>Holding a ledger, frowning deeply as he looks at the tree in the center of the hall</p></div> <p>My Lord, I would never dare question your wisdom... but bringing a tree of this size indoors? And hanging it with... er, this glittering 'rubbish'? Is this truly in keeping with noble etiquette? This cost us a full 50 units of our wood reserves.</p>` },{ "id": "you", "en": `<div class="monologue"><p><em>It’s called a holiday limited-edition skin, old Raymond. Besides, this tree is actually a signal booster to receive the system's holiday loot box.</em></p></div> <p>"Raymond, this is a 'Starfall Tree.' In my homeland..., according to the goddess's instructions, this symbolizes gift—I mean, a convergence of good fortune. Trust me, the return on investment—will far exceed the cost.</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Squatting at the very top of the tree, tail swishing madly, eyes locked onto a glowing glass star</p></div> <p>Meow! Boss! This thing... it’s glowing! Can I catch it? Can I take a bite? It looks like the egg of some high-energy prey!</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Standing expressionless beneath the tree, clutching a feather duster, voice ice-cold</p></div> <p>Miss Starfall, if you dare break that 'ornament,' there will be no special roast meat for you tonight. That is made of extremely fragile glass, though I fail to understand why the Master would create a decoration with zero defense stats.</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Fur bristles instantly; she leaps from the tree and lands gracefully</p></div> <p>Tch... Selina is so stingy. The Boss is better. Hey Boss, is this tree a scratching post for me?</p>` },{ "id": "captain", "en": `<div class="narration"><p>Fully armored, pacing vigilantly around the tree, tapping the trunk with his sword hilt</p></div> <p>Baron! I understand now! This must be a new type of camouflaged defense tower! These colored spheres hanging on the branches... are they filled with alchemical explosives? So if a demon gets close—BOOM?</p> <p>As expected of you! Even when celebrating, you’re laying tactical traps!</p>` },{ "id": "you", "en": `<p>Gavin, while I appreciate your crisis awareness, those really are just... never mind. Just think of them as 'Spirit Totems' that buff morale.</p>` },{ "id": "promaid", "en": `<div class="narration"><p>Holding a tray of steaming gingerbread—oddly shaped, some look like slimes, others like skulls</p></div> <p>Um... My Lord, is the story you told us before true? Tonight, will a 'Deity' in red clothes with a white beard really ride a flying deer, squeeze down the chimney, and deliver potions to us?</p>` },{ "id": "archmaid", "en": `<p>Erin, anyone attempting to invade via the chimney would be incinerated to ash by the barrier. Do not believe such illogical legends.</p>` },{ "id": "you", "en": `<div class="narration"><p>Smiling, eyes deep and thoughtful</p></div> <p>In this world, nothing is impossible, Erin. That 'Old Man in Red' might have been a Supply Officer from the Old Era. During the winter blockades, only he could break through the blizzard to deliver hope to every trench.</p> <div class="monologue"><p><em>Actually, it’s just a system airdrop patch.</em></p></div>` },{ "id": "you", "en": `<p>Alright, everyone. Whether it’s demons outside, or summer heat, or winter snow... tonight is the 'Starfall Festival.' At this special node in time, the laws of the world become... slightly more merciful.</p><div class="narration"><p>Walks under the tree and flips a switch</p></div> <p>Hold out your hands. Receive the gift of this special holiday—this is your 'Settlement Reward.'</p>` },{ "id": "narr", "en": `<p>(Light cascades from the crystals on the tree, materializing into physical objects that fall into everyone's hands)</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Catches a massive, pulsing ham</p></div> <p>Woah!! It’s meat! And... it feels like it holds some strange power! Thanks, Boss! I feel like I could sprint to the top of the snow mountain in one breath!</p>` },{ "id": "captain", "en": `<div class="narration"><p>Catches a bottle of whetstone oil glowing with a cold blue light</p></div> <p>This... Is this the legendary 'Permafrost Oil'? It claims to cut through steel? Thank you for this reward, My Lord! For Hawkridge, I shall slay every invader!</p>` },{ "id": "promaid", "en": `<div class="narration"><p>Holding a thick, yellowed book with complex alchemy symbols on the cover</p></div> <p>Is this... 'Basic Bio-Structure Analysis'? I don't understand the title, but the pictures inside... I feel like I can heal everyone more precisely now! Thank you, Baron!</p>` },{ "id": "butler", "en": `<div class="narration"><p>Trembling as he holds a small, heavy bag of gold coins stamped with ancient runes</p></div> <p>This quality... this purity... Is this the legendary 'Origin Gold'? Master, this could exchange for a small fortune! Where did you get this?</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Looking at her hand, holding nothing but an exquisite, glowing key</p></div> <p>Master? This is?</p>` },{ "id": "you", "en": `<p>It is an 'Enchanted Master Key,' Selina. With this, you won't need to carry that heavy ring of keys. You can enter and exit the temple freely with just one.</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>A faint blush appears on her cheeks along with a rare smile</p></div> <p>Thank you for your trust, my Master. Tonight’s 'Starfall Festival'... is not unpleasant.</p>` },{ "id": "you", "en": `<p>Raymond, use these gold coins to buy food and warm clothes for all the subjects in the territory.</p>` },{ "id": "narr", "en": `<div class="narration"><p></p></div> <p>Everyone: "Bless your benevolence!"</p>` },{ "id": "narr", "en": `<div class="narration"><p>(The group begins to enjoy the feast, laughter echoing through the warm hall)</p></div>` },{ "id": "you", "en": `<div class="monologue"><p><em>There’s no Santa Claus, of course. In this damn apocalypse, if I didn't hand out some welfare, these guys probably wouldn't survive the torment of these long days.</em></p></div> <div class="monologue"><p><em>But...</em></p></div> <div class="narration"><p>Glances back at Gavin fighting Starfall for a bone, and Selina wiping crumbs from Erin’s face</p></div>` },{ "id": "you", "en": `<div class="monologue"><p><em>If, at the end of a broken world, I can preserve a small, warm buffer zone like this...</em></p></div> <div class="monologue"><p><em>Maybe that’s the meaning of my existence as a 'Savior'.</em></p></div>` },{ "id": "you", "skip": false, "en": `<div class="narration"><p>(Raises glass)</p></div><p>Merry Christmas. To this buggy, broken world.</p> <div class="narration"><p>Received: Gold x 1000, Food x 100, Wood x 50 (Tree cost reimbursed).</p></div> <<pickup $storage "wood" 50 "food" 100 "gold" 1000>><<button "Got It!">><<goto 'MainHall_UI'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "catmaid", "en": `<div class="narration"><p>Stellafall’s gold eyes widen, pointing at the sky, voice buzzing with excitement.</p></div><p>Master! A massive bird in the sky! Come quick!</p>` },{ "id": "inst", "img": "event/npcs/griffin.png", "en": `<div class="narration"><p>Stellafall flicks her tail, pointing to the horizon.</p></div><p>Told you! Look, it’s swooping in!</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond steps forward, tone formal.</p></div><p>My Lord, it’s a griffin knight from the Empire. We must greet him with respect.</p>` },{ "id": "griffinknight", "en": `<div class="narration"><p>Gregory dismounts his griffin, armor clanking, eyes sharp as a hawk.</p></div><p>Baron Wilderen, first time we’ve met, I reckon. Your father spoke of your years in the North.</p><div class="narration"><p>He pauses, tone tinged with relief.</p></div><p>When your town fell to the demons, I feared for you. Seems the old Lord’s spirit kept you safe.</p>` },{ "id": "griffinknight", "en": `<div class="narration"><p>Gregory’s voice drops, urgent.</p></div><p>Name’s Gregory, Captain of the 3rd Griffin Squadron. The Dark Legion’s massing nearby—scouts say they’ll hit the Mountain Pass in 3 days. Get those defenses ready!</p><div class="narration"><p>His eyes glint with a friend’s concern.</p></div><p>Your father was a friend, so I’m here to give you a heads-up: the pass is a choke point. Use it right, and you’ll stop those demonic claws.</p>` },{ "id": "griffinknight", "en": `<div class="narration"><p>Gregory’s brow furrows, tone hardening.</p></div><p>One more thing: the Empire’s demanding war taxes. Your family owes 100,000 gold. Don’t take it personal—times are dire, and the Empire’s on its last legs.</p>` },{ "id": "you", "en": `<div class="narration"><p>Your eyes widen, voice shaky.</p></div><p>What? 100,000 gold?</p><div class="narration"><p>You turn to Raymond, tone urgent.</p></div><p>Butler, we’ve only got 2,000 gold, right?</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond bows his head, tone grave.</p></div><p>You’re correct, my Lord. Just 2,000 gold.</p><div class="narration"><p>He sighs.</p></div><p>We fled in haste—that’s all we could carry.</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond looks up, gaze earnest.</p></div><p>Sir Gregory, you and the late Lord were close. For his sake, could you grant us time? He hoped you’d watch over his heir. With our lands gone, we’re barely surviving here.</p>` },{ "id": "griffinknight", "en": `<div class="narration"><p>Gregory gives a wry chuckle, a flicker of warmth in his eyes.</p></div><p>Raymond, you old fox, always with the smooth talk.</p><div class="narration"><p>His tone sharpens.</p></div><p>Imperial rules don’t bend, but demons are a bigger threat than taxes. I’ll give you three months—hold the pass and survive first! Don’t expect soft words, Baron Wilderen. Show me some of your father’s grit!</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond bows slightly, tone grateful.</p></div><p>Thank you for your mercy, Sir Gregory! My Lord, please express your gratitude.</p>` },{ "id": "you", "en": `<div class="narration"><p>You take a deep breath, tone solemn.</p></div><p>Thank you, Sir Gregory.</p><div class="monologue"><p><em>I’ll focus on the defenses and figure out the rest.</em></p></div>` },{ "id": "griffinknight", "en": `<div class="narration"><p>Gregory nods, vaulting onto his griffin, eyes blazing.</p></div><p>Not bad—tougher than those pampered nobles. The demons hit in 3 days—don’t let the pass fall!</p><div class="narration"><p>Gregory’s eyes scan the temple, voice low.</p></div><p>Your father said Hawkridge Vale holds secrets even His Majesty values. Don’t let me down, Baron.</p><div class="narration"><p>He tightens the reins, tone stern.</p></div><p>I’m off; scouting demons ain’t a picnic. One tip: steer clear of Beastfolk.</p><div class="narration"><p>He shouts, voice ringing.</p></div><p>Glory to the Empire, and bones to the dust!</p>` },{ "id": "catmaid", "img": "event/npcs/griffin.png", "en": `<div class="narration"><p>Stellafall flicks her tail, tone teasing.</p></div><p>Master, stop staring—the griffin’s gone! Why’re you frozen?</p>` },{ "id": "you", "en": `<div class="narration"><p>You snap out of it, rubbing your face.</p></div><p>What a presence. Captain, how do you measure up to him?</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin bows his head, tone mixed with awe and self-deprecation.</p></div><p>Sir Gregory hails from military nobility. His father was a legendary armsmaster; his mother, the imperial alchemist. He earned the title ’Wing of the Empire’ by crushing Beastfolk wolfriders at Blackstone Gorge with seven griffins. My Lord, I couldn’t even handle a young griffin, let alone him.</p>` },{ "id": "you", "img": "", "skip": false, "en": `<div class="narration"><p>You frown, tone heavy.</p></div><p>The Empire’s on its last legs if elites like him are sent as messengers.</p><div class="monologue"><p><em>I’d better start building defenses now.</em></p></div><p><<SetFlag 'griffinknightflag' 1>><<SetFlag 'tutorial' 11>><<SetFlag 'enbastion' 2>><<button 'Prepare Defenses'>>><<goto 'MainHall_UI'>><</button>></p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "griffinknight", "en": `<div class="narration"><p>Gregory stands by his griffin, armor glinting in the sunset, eyes heavy.</p></div><p>Baron Wilderen, we meet again. I’ve flown over countless lands, seen towns burn, lords fall. Do you know how heavy these words are?</p>` },{ "id": "griffinknight", "en": `<div class="narration"><p>His tone softens, laced with approval.</p></div><p>You’re struggling, but you’ve outlasted most lords. Grow stronger, Baron Wilderen; maybe you’ll save the Empire. Paying the tax wasn’t easy. We’ll meet again.</p><<drop $storage "gold" $domain.wartax[0]>><<set $domain.wartaxdeadline = 15 >><<SetFlag 'wartaxs' false>><<run $domain.wartax.shift()>>` },{ "id": "you", "skip": false, "en": `<<button "好难">><</button>><div class="narration"><p>You take a deep breath, eyes resolute.</p></div><p><<button "I won’t let you down, Sir Gregory.">><</button>></p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "griffinknight", "en": `<div class="narration"><p>Gregory stands by his griffin, armor glinting coldly, eyes stern yet tinged with regret.</p></div><p>Baron Wilderen, this pains me. You’ve failed as a lord. The Empire reclaims your lands.</p>` }, { "id": "narr", "skip": false, "en": `<div class="narration"><p>Gregory vaults onto his griffin, not looking back, and soars away. Hawkridge Vale falls silent.</p></div><<button 'You Failed'>>><<SimpleUI 'Event_Badend'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "griffinknight", "en": `<div class="narration"><p>Gregory stands by his griffin, armor gleaming in the morning light, eyes tinged with approval.</p></div><p>Baron Wilderen, we meet again. You kept your word—I’m pleased.</p>` }, { "id": "griffinknight", "en": `<div class="narration"><p>He pats his griffin, tone brief but firm.</p></div><p>See you in two weeks. Good luck.</p><<drop $storage "gold" $domain.wartax[0]>><<set $domain.wartaxdeadline = 15 >><<run $domain.wartax.shift()>>` },{ "id": "you", "skip": false, "en": `<div class="narration"><p>You watch Gregory’s retreating figure, shaking your head with a wry smile.</p></div><div class="monologue"><p><em>Even in another world, gold rules all.</em></p></div><p><<button 'Gold rules all'>>><<goto 'MainHall_UI'>><</button>></p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "captain", "en": `<div class="narration"><p>Garvin strides forward, tone wary.</p></div><p>My Lord, someone seeks an audience! A foxfolk lady arrived with guards and black market traders. She doesn’t seem hostile, but her tails… so many, I’ve never seen such a foxfolk. She claims to be the chief scout of the Silvertail Clan and insists on meeting you.</p>` },{ "id": "you", "en": `<div class="narration"><p>You frown, lost in thought.</p></div><div class="monologue"><p><em>Foxfolk? The black market’s secrets are finally coming to light… but she sounds like trouble.</em></p></div><p>Let her in, Garvin. Stay vigilant—we need allies, but we can’t let our guard down.</p>` },{ "id": "foxmaid", img: "characters/foxmaid/foxmaid01.webp", "en": `<div class="narration"><p>Hizuki Silvertail enters the main hall, moving gracefully, her two silver tails swaying lightly, a teasing smile on her lips. Her fox ears twitch, and her sharp eyes scan the group.</p></div><p>Hmph, so you’re the lord of Hawkridge Vale. This mistress, Hizuki Silvertail, chief scout of the Silvertail Clan, didn’t come all this way for idle chatter. Your valley’s well-hidden, easy to defend, and has held off the demons’ claws more than once—not bad. Worth my time to check out.</p>` },{ "id": "you", "en": `<div class="narration"><p>You respond cautiously, keeping your tone steady.</p></div><p>The Silvertail Clan? Your nomadic foxfolk traders were well-known even before the war. I’m Baron Wilderen. What brings you here?</p>` },{ "id": "foxmaid", img: "characters/foxmaid/foxmaid01.webp", "en": `<div class="narration"><p>Hizuki flashes a sly grin, her tails flicking.</p></div><p>My business? Ha, this mistress doesn’t waste time beating around the bush. You’ve heard of the black market, right? Open every Thursday, selling materials, alchemical potions, and all kinds of intel. Looks like some shadowy group’s behind it, but it’s actually us Silvertail Clan running the show! Our illusions and magic-sniffing noses keep our caravans clear of those cursed demon patrols, bringing a spark of hope to every race.</p>` },{ "id": "you", "en": `<div class="narration"><p>You interject, tone cautious.</p></div><p>The black market? Trading on lands overrun by the demon hordes sounds insanely dangerous. Why would your foxfolk get mixed up in that?</p>` },{ "id": "foxmaid", img: "characters/foxmaid/foxmaid01.webp", "en": `<div class="narration"><p>Hizuki waves dismissively, but a flicker of duty crosses her eyes.</p></div><p>Dangerous? Maybe for you humans, but we foxfolk have the Moon Fox Spirit’s protection. Our illusions fool demon eyes, and our noses sniff out blood poison trails—we’re the best trade partners you’ll find! Here’s my offer: our caravans set up permanent camp in your valley, restocking every Thursday with supplies and vital intel. You provide safe passage and protection. And don’t even think about haggling—our clan’s survival depends on trade. You think this lady would budge easily?</p>` },{ "id": "you", "en": `<div class="narration"><p>You weigh her words inwardly, nodding outwardly.</p></div><div class="monologue"><p><em>This deal sounds solid, but that look in her eyes… she’s testing me.</em></p></div><p>The deal sounds promising, but why us? There are plenty of places out there. Why Hawkridge Vale?</p>` },{ "id": "foxmaid", img: "characters/foxmaid/foxmaid01.webp", "en": `<div class="narration"><p>Hizuki’s eyes narrow, her tone teasing but probing.</p></div><p>Why? This fox is just curious about you, that’s all. I want to see if you’ve got what it takes to be our partner!</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin clenches his sword hilt, anger flaring.</p></div><p>Foxfolk, your attitude’s too bold! My Lord, this deal sounds good, but her tone… they should be begging for our protection!</p>` },{ "id": "foxmaid", img: "characters/foxmaid/foxmaid01.webp", "en": `<div class="narration"><p>Hizuki huffs haughtily, turning away, but her tails curl slightly, betraying unease.</p></div><p>Bold? That’s confidence! You need my help for supplies and intel. This lady will stay to oversee the deal—and test if you’re strong enough for bigger partnerships, like… setting up an alchemy workshop!</p>` },{ "id": "you", "en": `<div class="narration"><p>You nod slowly, mulling it over.</p></div><div class="monologue"><p><em>Garvin’s right—she’s too bold. But the offer’s tempting, and the black market could really help rebuild the vale.</em></p></div><p>Garvin, clear out the old black market site at the valley’s entrance for their camp. Well then, Miss Hizuki, here’s to a fruitful partnership.</p> <<SetFlag 'foxmaidflag' 1>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "captain", "en": `<p>My Lord, Hizuki's here.</p>` },{ "id": "foxmaid", "en": `<p>haha~ Still up so late, my Lord? Were you waiting for this lady to tuck you in?</p>` },{ "id": "you", "en": `<p>I just want to know what you meant by ‘bigger cooperation’ last time. Cut the teasing, Hizuki.</p>` },{ "id": "foxmaid", "en": `<p>Simple! Turn the old black-market site at the valley entrance into a real Exotic Free Trade Zone! Human taverns, slime alchemy stalls, mermaid pearl shops, arachne silk boutiques… invite them all! Thursday black markets are pocket change. This lady is going to build you a seven-days-a-week, gold-raining super bazaar!</p>` },{ "id": "captain", "en": `<p>My Lord! Letting that many non-humans flood the valley is far too dangerous! If anything goes wrong—</p>` },{ "id": "foxmaid", "en": `<p>If anything goes wrong? Like humans starting a civil war on your own, or are you worried I’ll steal your precious lord away~? Relax. Us foxfolk can smell demon stench from a mile off — including that wingless succubus lurking beside him right now. Silver Tail Caravan guarantees security, collects taxes, and keeps order. All you have to do is lie back and count the coins~</p>` },{ "id": "mirael", "en": `<p>Little fox, are you trying to start a fight the moment you walk in?</p>` },{ "id": "archmaid", "en": `<p>My Lord… she’s insufferably smug, but the numbers don’t lie. According to her projections, the trade zone would wipe out our deficit in a single month and let us support a much larger population — and army.</p>` },{ "id": "you", "en": `<div class="monologue"><p><em>I just wanted to fight demons, how the hell did I become the night-market manager?</em></p></div> <p>Fine. We’ll use the old black-market grounds. What about the name?</p>` },{ "id": "foxmaid", "en": `<p>I’ve already decided! 『Crimson Moon Pleasure Street – Proudly Presented by Silver Tail Trading Company』! Neon magic-crystal signboards, and a nine-foot glowing statue of yours truly at the gate~</p>` },{ "id": "you", "en": `<p>No statue. Name change. It’ll be called 『Hawkridge Free Trade Zone』. Bring me the contract tomorrow morning — revenue split, security regulations, non-human residency rules — every single clause. I’m reading it word for word.</p>` },{ "id": "foxmaid", "en": `<p>Kyaa~ So strict! I love it when the Lord gets all serious with me~</p>` },{ "id": "captain", "en": `<div class="narration"><p>“shiiing” — half-draws his sword</p></div> <p>That’s ENOUGH, fox! Step away from His Lordship!</p>` },{ "id": "archmaid", "en": `<p>…I’ll go bring Erin her late-night snack. My Lord, please… get some rest.</p>` },{ "id": "you", "en": `<<SetFlag 'entrade' 2>><div class="monologue"><p><em>I just wanted to rebuild a home… how did I end up running a monster-girl red-light district?!</em></p></div>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"> <p>Driven to unravel this baron nonsense and the temple’s secrets, you slip from the group’s chaos into the main hall of the Lost Temple, carved deep in the Hawkridge Mountains.</p> <p>Sunshine seeps through cracks in a gothic dome, glinting off rubble and charred scars, intricate stone spires whispering of lost grandeur.</p> <p>The air hums with burnt wood and the rift’s eerie pulse, where a shattered platform holds a Planar Rift, liquid starlight pulsing with menace. Flickering rune fragments circle it, hinting at the cataclysm that razed the Wilderen family’s ancestral estate.</p> </div>` },{ "id": "narr", "en": `<div class="monologue"> <p><em>This place feels like a gothic ruin from some blockbuster... Last night I was slaving over slides, now I’m stuck in this nightmare with a murky baron title.</em></p> <p><em>The rift’s like a black hole, buzzing till my scalp crawls. The family estate blasted to rubble—bet it’s tied to this thing.</em></p> </div>` },{ "id": "you", "en": `<div class="narration"> <p>You tread carefully over rubble, armor chafing your shoulders, eyeing the rift with curiosity and caution.</p> </div> <div class="monologue"> <p><em>This rift’s no joke... like it’s alive, breathing. Nobody’s here—should I call out? Better not summon some beast.</em></p> </div> <p>"Oi! What’s this thing? Who left some arcane trinket lying around?"</p>` },{ "id": "you", "en": `<div class="narration"> <p>A soft clink sounds—you spot a ceramic angel figurine rolling from the debris, palm-sized, glowing faintly gold. Before you can blink, it floats upward, hovering by the rift.</p> </div> <div class="monologue"> <p><em>Holy hell! This thing moved on its own! Not just some relic, huh? That gold glow... same eerie light as that cursed blood moon last night. Heart, slow down—stay cool!</em></p> </div>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine stays still, but an ethereal voice blooms in your mind, soft as water, edged with forbidden echoes.</p> </div> <p>"This is a Planar Rift, forged from a demon’s portal, a crossroads to countless realms."</p> <div class="narration"> <p>Tone calm, probing.</p> </div> <p>"I am Leah, Goddess of Fertility... your soul, tempered by mortal trials, stirred my fading spark, wanderer from afar."</p>` },{ "id": "you", "en": `<div class="narration"> <p>You stumble back, pointing at the figurine, voice rising with shock.</p> </div> <div class="monologue"> <p><em>Talking in my head! Same trick as that blood moon last night... and this baron nonsense. A ceramic doll chats? Fine, I’ll bite—gotta grill her, no getting conned.</em></p> </div>` },{ "id": "you", "en": `<p>"You... you’re this busted angel trinket?!"</p> <div class="narration"> <p>Swallowing hard, you fake calm.</p> </div> <p>"Wanderer from afar? So you yanked me here, slapped with this weird baron title? Spill it—why pick a nobody like me? What’s this rift deal?"</p>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine quivers, gold light pulsing, voice heavy with exhaustion.</p> </div> <p>"This trinket is but a vessel for my dwindling spark... I cannot appear, only whisper through souls."</p> <div class="narration"> <p>Tone deepens.</p> </div> <p>"I wrested the rift from demon hands to save the Tainted Seed Epoch, but its unstable power shattered the estate and drained my divinity. Your steadfast soul is fit to wield it."</p>` },{ "id": "you", "en": `<div class="narration"> <p>Eyes wide, you gesture at the temple’s ruins, tone dripping with doubt.</p> </div> <div class="monologue"> <p><em>She blew up the family estate?! This goddess is a walking disaster! Tainted Seed Epoch? What’s that nonsense? Steadfast soul? What’s so special about a desk jockey like me? Make her come clean.</em></p> </div>` },{ "id": "you", "en": `<p>"Hold up—you turned the estate to rubble?!"</p> <div class="narration"> <p>Taking a deep breath, you curb your anger.</p> </div> <p>"That creepy blood moon last night—your work, right? Why drag me here? What kind of goddess are you? Why’s the world on me? What’re the demons chasing? No cryptic games!"</p>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine’s gold dims, voice a weary sigh.</p> </div> <p>"I am Leah, Goddess of Fertility and Life, guardian of the cycle... my power now fades."</p> <div class="narration"> <p>Pausing, evasive.</p> </div> <p>"Demons crave the rift to burn this world to cinders. Divine laws bind me from direct defiance. Only you, wielding the rift’s might, fueled by the temple’s ancient runes, can halt this doom."</p>` },{ "id": "you", "en": `<div class="narration"> <p>Arms crossed, you squint at the figurine, tone skeptical.</p> </div> <div class="monologue"> <p><em>Fertility goddess? That’s a wild title. Divine red tape screwing with this? She’s hiding something—glossing over the demons like I’d bail. The rift’s got potential—let’s figure out how it works.</em></p> </div>` },{ "id": "you", "en": `<p>"Alright, world’s collapsing, got it. But how’s this rift work? Summoning help? You’re not tossing me into a demon fight, are you? Who do I grab—any randoms work?"</p> <p>"Also, the estate’s gone—is this thing safe? No more surprise explosions, right?"</p>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine spins slowly, voice swelling with majesty, tinged with urgency.</p> </div> <p>"The rift is a gate of life, activated by a soul-binding ritual, drawing power from the temple’s ancient runes to awaken hope."</p> <div class="narration"> <p>Tone softens, guiding.</p> </div> <p>"Find two bound by trust or heart for the ritual, to birth a Life Seed. If successful, the rift opens, calling allies—from this realm’s monstergirls to otherworldly astral wardens."</p> <p>"But tread carefully—it drains Energy, and failure only delays your summons, its success tied to their Fertility."</p>` },{ "id": "you", "en": `<div class="narration"> <p>Your jaw drops, coughing hard, face flushing red.</p> </div> <div class="monologue"> <p><em>Soul-binding?! Not planting trees—it’s... matchmaking?! This quest’s crazier than any dungeon crawl! How’s a desk jockey stuck with this? Gotta double-check, no falling for traps.</em></p> </div>` },{ "id": "you", "en": `<p>"What?! Soul-binding ritual?!"</p> <div class="narration"> <p>Scratching your head awkwardly, voice stumbling.</p> </div> <p>"You mean... playing matchmaker, maybe even jumping in?! Charisma, Willpower—what, like a stat check? You fertility goddess, you’re way out there! How’s this ritual go down?"</p>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine’s gold flashes, voice laced with amusement.</p> </div> <p>"Life springs from the union of souls—mortals need not shy away."</p> <div class="narration"> <p>Tone turns solemn.</p> </div> <p>"The ritual needs two souls aligned in heart—or you may join them—Charisma to spark connection, Willpower to hold fast, Fertility to nurture hope, powered by the temple’s runes. Stamina and Dexterity shape the Energy’s cost."</p> <p>"The stronger their bond, the mightier the Life Seed, the greater the allies summoned."</p>` },{ "id": "you", "en": `<div class="narration"> <p>Rubbing your face, tone half-resigned, half-snarky.</p> </div> <div class="monologue"> <p><em>Alright, grow a spine, deal with this madness. Rune-powered ritual sounds legit—like a party raiding a dungeon, gotta pick the right crew. Energy, stats? Pure bard’s tale stuff. Gotta nail this down or I’m toast.</em></p> </div>` },{ "id": "you", "en": `<p>"Okay, soul-binding, picking a crew for the ritual—kinda getting it."</p> <div class="narration"> <p>Grimacing, you shrug.</p> </div> <p>"But there’s a catch, right? Where’s this rune Energy from? What if it runs out? And if it flops—won’t curse me or anything, will it?"</p>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine rises slightly, voice soft yet cautionary.</p> </div> <p>"The ritual flows like a river, driven by Energy from the temple’s runes, gathered over time."</p> <div class="narration"> <p>Tone eases, soothing.</p> </div> <p>"Failure harms none, only delaying your summons, but drained Energy demands rest to restore. Success births a Life Seed, unlocking mightier allies."</p> <p>"Choose companions wisely—their Strength and Charisma shape the outcome. Dexterity lowers Energy costs, but plan carefully."</p>` },{ "id": "you", "en": `<div class="narration"> <p>Staring at the rift, brow furrowed, tone sarcastic.</p> </div> <div class="monologue"> <p><em>Like rolling dice for epic loot—pick top-tier allies, hope for luck... That blood moon last night was creepy as hell, bad vibes. Failure’s fine, success gets backup, not a bad deal. She’s cagey about limits—definite trap. What’s in the summon pool?</em></p> </div>` },{ "id": "you", "en": `<p>"Got it—like rolling dice, gotta get the right combo."</p> <div class="narration"> <p>Grinning to hide nerves.</p> </div> <p>"These summons trustworthy? What do we get? Monstergirls sound like tavern gossip—or some real heavyweights? Don’t stick me with a bunch of enemies!"</p>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine’s gold trembles, voice low and cryptic.</p> </div> <p>"The rift is an abyss, its summons unpredictable."</p> <div class="narration"> <p>Pausing, tone grave.</p> </div> <p>"A stronger Life Seed calls mightier allies—from forest Felinefolk to voidborn Starbeasts. Yet, a flawed ritual or a Blood Moon’s dark omens may stir the rift, summoning hostile forces."</p> <p>"Beware the Blood Moon most of all—when it rises, the rift may twist, unleashing unknown perils."</p>` },{ "id": "you", "en": `<div class="narration"> <p>You pause, tapping the figurine, voice steadying.</p> </div> <div class="monologue"> <p><em>Blood Moon? That creepy thing yanked me here. High risk, high reward... She’s hiding stuff, and that estate explosion’s not the whole story. Fine, I’ll play along—I’ll dig up her game.</em></p> </div>` },{ "id": "you", "en": `<p>"Man, Blood Moon’s got a cursed vibe..."</p> <div class="narration"> <p>Chuckling wryly, you glance at the rift.</p> </div> <p>"Alright, Leah, I’ll try this ritual. Gotta round up some folks—hope your rift doesn’t blow again and this crumbling temple holds up."</p>` },{ "id": "leah", "en": `<div class="narration"> <p>The figurine settles into your hand, its gold fading, voice dissolving like a breeze.</p> </div> <p>"Go, guide of the rift... the Life Seed will spark hope for the Tainted Seed Epoch."</p> <div class="narration"> <p>The rift hums once, runes dimming, silence falling, leaving only the cold, heavy ceramic angel in your grip.</p> </div>` },{ "id": "you", "en": `<div class="monologue"><p><em>Another goddess with secrets...</em></p><p><em>Time to rally allies!'</em></p></div> <<SetFlag 'leahflag' 1>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="monologue"><p><em>Deep within the Lost Temple, flickering firelight casts shadows on the ceramic angel statue of Leah, the Goddess of Fertility.</em></p></div>` },{ "id": "you", "en": `<p>Leah, I get that the allies from the portal are getting stronger. But what about the monster girls I already have? I can’t just rely on newbies! Is there a way to boost their strength? Especially their potential tiers—I want to forge some real powerhouses. Being a savior’s no joke!</p>` },{ "id": "leah", "en": `<p>All things follow the natural cycle: birth, growth, decay. Regular training can hone skills and boost attributes, but potential is innate, like the paths of the stars—near impossible to change.</p>` },{ "id": "you", "en": `<p>Innate potential? That’s brutal! If their attributes are capped by potential, some monster girls are great at magic, others just swing fists. How am I supposed to build a balanced team? Saving the world needs something extraordinary! You’re a goddess—got any special tricks up your sleeve, or is it just grind and sweat?</p>` },{ "id": "leah", "en": `<p>Mortals and their impatience are always amusing. Indeed, that’s the usual way. But at the edge of life and death, a being can unleash power beyond the ordinary, shattering its fated limits. My strength has recently recovered a bit. I can infuse my divine power into a being at its most intense state, breaking the chains of potential. In your world’s terms, it’s like “unlocking the genetic code.”</p>` },{ "id": "you", "en": `<p>Sounds intense! How do we pull it off? You’re not gonna tell me to throw them into a demon horde to fight for their lives, are you?</p>` },{ "id": "leah", "en": `<p>I govern fertility. The moment of creation is life’s purest, most exalted peak. In that state, I can channel my power to ignite their potential and shatter innate limits. But it requires the life’s essence to bloom fully, with soul and body at their zenith.</p>` },{ "id": "you", "en": `<p>Wait… you’re saying it’s that kind of thing? The whole… procreation deal?</p>` },{ "id": "leah", "en": `<p>Mortal, the beauty of life lies in creation and continuity. Only in that ultimate union can life touch its core. My energy is limited, so it must be reserved for the chosen few. Go to the bedchamber and check the new ability I’ve unlocked for you. There, you’ll find your answers.</p>` },{ "id": "you", "en": `<p>Alright… I’ll check it out!</p>` },{ "id": "leah", "en": `<p>Go, Chosen One. The blooming of life’s essence will ignite your hope—and bring other surprising rewards.</p><<SetFlag 'entrain' 2>><<run Msg.add('notice', langBank('unlockTrain'));>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "you", "en": `<div class="narration"><p>You grip the ceramic angel figurine, voice probing, eyes wary.</p></div><p>Leah, can the creatures from the Dark Legion perform the summoning ritual? Won’t it attract enemies?</p>` },{ "id": "leah", "en": `<div class="narration"><p>The figurine’s light flickers, Leah’s voice ethereal with a hint of amusement.</p></div><p>You’ve misunderstood the summoning ritual. It’s faction-neutral. Those summoned will always be your allies, regardless of whether the summoners are from an enemy faction.</p>` },{ "id": "you", "en": `<div class="narration"><p>You raise an eyebrow, tone laced with surprise and probing.</p></div><p>That powerful? So, the summoned beings automatically join my side? Because I’m thinking of trying it with a succubus.</p>` },{ "id": "leah", "en": `<div class="narration"><p>Leah’s voice softens, laced with mystery.</p></div><p>No need to worry. Succubi are highly suitable for the summoning ritual. They have a high Fertility Talent, and their bodies can withstand the infusion of magical energy. I think it’s time to activate the gestation function.</p>` },{ "id": "leah", "en": `<div class="narration"><p>The figurine’s gold light swirls, Leah’s tone deepening.</p></div><p>Instead of summoning through newly formed life seeds, you can use direct gestation. By infusing magic and energy, you can accelerate pregnancy. Once the child is born, send it into the Planar Rift for 18 years of nurturing, then recall it. Due to the unique nature of time and space, this process is instantaneous.</p>` },{ "id": "you", "en": `<div class="narration"><p>You frown, tone probing yet puzzled.</p></div><p>So, the summoning we talked about before is actually nurturing a life seed for 18 years and then recalling it, right?</p>` },{ "id": "leah", "en": `<div class="narration"><p>The figurine falls silent briefly, Leah’s voice tinged with amusement.</p></div><p>...You’re remarkably perceptive. To avoid drawing the attention of a certain special entity, I erased their memories. Do you need me to erase yours as well?</p>` },{ "id": "you", "en": `<div class="narration"><p>You let out a nervous laugh, heart racing, waving it off.</p></div><p>Haha, no need to worry about my wild guesses!</p><div class="monologue"><p><em>Guess I’ll stop probing here.</em></p></div>` },{ "id": "leah", "en": `<div class="narration"><p>Leah’s voice steadies, carrying a touch of authority.</p></div><p>In any case, succubi are perfectly capable of participating in the summoning ritual. I’ve also activated the gestation function. If you build an energy infuser in your bedroom, you can unlock gestation abilities there. To hasten my divine power’s recovery, I’ve suppressed the gestation abilities of all creatures in the temple. This will also enable the accelerated pregnancy infusion function.</p>` },{ "id": "leah", "en": `<div class="narration"><p>The figurine’s light dims, Leah’s tone solemn.</p></div><p>The benefit of this approach is that it reduces randomness, making it easier to nurture offspring that combine the strongest attributes of their parents, potentially even surpassing their innate talents. However, this process greatly depletes the divine power I’ve painstakingly amassed, so I can only grant it to a select few.</p>` },{ "id": "you", "skip": false, "en": `<div class="narration"><p>You nod, eyes glinting with excitement yet caution.</p></div><p>Got it! I’ll give it a try as soon as possible!</p> <p><<button "Got it! ">> <<SetFlag 'leahflag' 3 >><<SetFlag 'miraelflag' 3>> <<goto "Summon_UI">><</button>></p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"><p>The seventh day’s battle against the Dark Legion ended in a pyrrhic victory, leaving Mountain Pass scarred, strewn with corpses and broken blades. As the Lord led the cleanup of the enemy camp, the Dark Legion’s prison revealed chained human captives—and an astonishing prisoner: the succubus Mirael.</p></div>` },{ "id": "narr", "en": `<div class="narration"><p>Her bat-like wings were cruelly severed, her pale, wounded form clad in a tattered dark gown. Her crimson eyes flickered in the torchlight, tiny demonic horns half-hidden in disheveled hair. She hung her head, as if awaiting her doom in despair.</p></div>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond steps forward, eyes wary, tone urgent.</p></div><p>My Lord, do not trust this creature! Her beauty is the devil’s venom, a trap to ensnare the soul!</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin grips his sword, voice low.</p></div><p>My Lord, we found her in the Dark Legion’s prison, caged with human captives. Her wings are severed, and she claims to be a prisoner—but this could be a ruse of the Legion!</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael whispers, voice trembling, crimson eyes lifting slightly.</p></div><p>Noble Lord, I am Mirael, a succubus... I served the Dark Legion against my will. They tore my wings and meant to drag me to their stronghold for execution. Please, shelter me!</p>` },{ "id": "you", "en": `<div class="narration"><p>You frown, eyes stern, tone probing.</p></div><p>Shelter you? Your kind slaughtered my men and nearly razed this stronghold! Why should I trust a demon’s plea?</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael lifts her head, red eyes brimming with tears, voice earnest.</p></div><p>I’ve taken no lives, Lord! I was tasked only with charm and scouting, and for defying orders, I was punished. The Dark Legion is merciless—I’d rather serve you and forsake that bloody abyss!</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond glares, tone fierce.</p></div><p>Lies! My Lord, a demon’s tears are but a mask! Her words are sweet poison leading to damnation!</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael gazes at you, voice sultry yet tinged with despair.</p></div><p>He misunderstands my heart... Lord, you bear a heavy burden, yet your eyes hide solitude. Let me ease it, if only... with a moment’s warmth.</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin steps back uneasily, voice shaking.</p></div><p>My Lord, her gaze unsettles the heart! My resolve wavers... she’s the devil incarnate!</p>` },{ "id": "you", "en": `<div class="narration"><p>You narrow your eyes, tone hard, probing deeper.</p></div><p>Mirael, your honeyed words don’t erase your demonic blood. Prove your worth, or I’ll cast you out!</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael rises slowly, chains clinking, breath warm.</p></div><p>I can bring you hope, Lord... My body can repay the blood debt, my offspring can breathe life into your stronghold. Let me serve as your maid, loyal until my soul fades to dust.</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond trembles with rage, voice shaking.</p></div><p>Blasphemy! My Lord, this fiend’s offer is a pit of corruption! How can you let her taint the Wilderen name?</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael chuckles softly, voice dreamlike, gaze profound.</p></div><p>Raymond, your loyalty is noble, yet blind to the heart’s cravings... Touch my hand, Lord, and feel my truth. My heart lies open to you.</p>` },{ "id": "you", "en": `<div class="narration"><p>You hesitate, eyes wavering, heart conflicted.</p></div><p>Your words are poison, Mirael... I cannot let the pass fall to my weakness!</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael hums softly, eyes blazing like stars, the air shivering.</p></div><p>Then let me offer you a glimpse of dreams, Lord... Close your eyes, see me serving as your maid, your stronghold thriving, my children fighting for you... This is no poison—it’s salvation.</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin steps back, voice urgent with fear.</p></div><p>My Lord! She’s weaving demonic illusions! Break free of her spell!</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael’s fingers brush your arm, warm as fire, voice a whisper.</p></div><p>Ignore their fears, Lord. Your heart already knows the answer... Let me be your maid, your secret weapon. Accept me, guardian of Wilderen.</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond shakes his head in despair, voice heavy.</p></div><p>The old Lord would never abide such corruption! My Lord, your will is our final bastion!</p>` },{ "id": "you", "en": `<div class="narration"><p>You breathe heavily, tone wry yet cautious.</p></div><p>Very well, Mirael, your visions are captivating. I accept your terms—stay as my maid. But betray me, and you’ll regret ever crossing this world!</p>` },{ "id": "mirael", "img": "characters/mirael/lock.webp", "en": `<div class="narration"><p>Mirael smiles, red eyes fathomless, tone enigmatic.</p></div><p>You won’t be disappointed, Lord... My service will bloom in the whispers of the night.</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin mutters, eyes filled with worry.</p></div><p>May the Empire’s light protect us... Her charm has seeped into every corner of the stronghold.</p>` },{ "id": "butler", "en": `<div class="narration"><p>Raymond sighs, voice heavy as stone.</p></div><p>Wilderen’s fate hangs by a thread... My Lord, may your choice be without regret.</p>` },{ "id": "narr", "en": `<div class="narration"><p>Mirael’s chains are unbound, her red eyes flickering in the torchlight. Guards whisper, morale rises with her allure, but the Dark Legion’s wrath seems kindled...</p></div>` },{ "id": "you", "skip": false, "en": `<div class="narration"><p>You smirk inwardly, eyes scanning Mirael, heart wary.</p></div><div class="monologue"><p><em>I’ve seen seduction tricks in movies far cleverer than this. I’ll play along to see her game—true or false, I won’t be the one to lose.</em></p></div><p></p> <<button "Take her in">><<goto "Battle_UI">><</button>><<SetFlag 'miraelflag' 1>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "mirael", "en": `<div class="narration"><p>Mirael leans on the bedroom’s cushioned chaise, crimson eyes glinting, hand gently caressing her swollen belly, her maid’s gown clinging to her curves, breath quick yet laced with a seductive smile.</p></div><p>My Lord, I feel... it’s coming soon! This belly’s so full it might burst, but there’s no pain—just... pure bliss~ Is this not the goddess’s miracle?</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Stellafall’s cat-eyes widen, tail flicking as she leans close to Mirael, curiously touching her belly, then glancing at her own flat abdomen, ears twitching.</p></div><p>Whoa, Mirael, your belly’s huge! Like you stuffed a giant pumpkin in there! Mine’s so tiny... How’s this even possible? How can a little tummy stretch like that?</p>` },{ "id": "leah", "en": `<div class="narration"><p>The ceramic angel figurine glows with golden light, Leah’s voice ethereal and commanding, carrying a guiding tone.</p></div><p>You escort Mirael to the Planar Rift. The ritual is prepared, the time is now. Fear not, I shall ensure all proceeds flawlessly.</p>` },{ "id": "narr", "en": `<div class="narration"><p>You support Mirael as Stellafall follows, her cat tail swaying, eyes brimming with curiosity and tension. The group traverses the temple’s corridors, arriving at the Planar Rift, its edges pulsing with purple arcane light, the air humming with energy.</p></div>` },{ "id": "leah", "en": `<div class="narration"><p>Leah’s figurine’s light flaring as a summoning circle materializes on the ground, runes swirling.</p></div><p>You have Mirael step into the summoning circle, and then You stand back and witness. The ritual begins!</p>` },{ "id": "narr", "en": `<div class="narration"><p>Mirael steps into the summoning circle, purple light rising from her feet, enveloping her. Suddenly, a dazzling golden beam bursts from her abdomen, surging into the Planar Rift, which trembles with a low roar. As the light fades, Mirael’s belly returns to its pre-pregnancy state, and she touches it in astonishment.</p></div>` },{ "id": "mirael", "img": "characters/mirael/miraelpreg0.png", "en": `<div class="narration"><p>Mirael’s crimson eyes gleam, her smile radiant, voice buoyant.</p></div><p>My Lord! I didn’t feel a thing! It’s like... nothing even happened! This is incredible! Goddess’s amazing!</p>` },{ "id": "narr", "en": `<<GetBirth "mirael">><div class="narration"><p>The Planar Rift flashes, and a new succubus steps forth, tail swaying, her smile seductive yet friendly. She waves at you, voice warm.</p></div><p>Hey there~ I’m the new girl! Lord, everyone, thanks for summoning me! This place looks lively—I can’t wait to join the fun!</p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "catmaid", "en": `<div class="narration"><p>Stellafall’s cat ears twitch, tail flicking faster as she leans toward the new succubus, voice stunned yet thrilled.</p></div><p>Whoa! Another sister?! Mirael, your belly was *huge* a second ago, and now you’ve popped out this gorgeous lady?! That’s way too fast!</p>` } ,{ "id": "narr", "en": `<div class="narration"><p>You and Mirael lock eyes, lips curling into subtle smiles, silent but sharing a knowing glance.</p></div>` },{ "id": "leah", "en": `<div class="narration"><p>Leah’s voice speaking directly to your mind.</p></div><p>Lord, this ritual was a triumph. Mirael gains vast experience, and the new ally boasts exceptional talents, proof of my mastery over fertility. Let this dispel your doubts—use this method often, and victory against the Dark Legion is assured!</p>` },{ "id": "you", "en": `<div class="narration"><p>You nod, glancing at the new succubus and Mirael, inwardly stunned but feigning calm.</p></div><p>Holy—Leah, that was next-level! Alright, I gotta plan how to make the most of this trick!</p><div class="monologue"><p><em>This fertility goddess is straight-up cheating...</em></p></div> <<SetFlag 'miraelflag' 7>><<SetFlag 'catmaidFlag' 5 >><<SetFlag 'firstbirth'>><<run $training.bedroomGirls.pushUnique("catmaid")>> <p><<button "Return to the Temple, Plan Next Steps!">><</button>></p><<run $chars.dataMap.get("mirael").calcAttrInfo("bond.vl", 1)>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "you", "en": `<div class="narration"><p>You smile at Mirael.</p></div><p>Mirael, the time has come to fulfill your promise!</p>` },{ "id": "mirael", "en": `<div class="narration"><p>Mirael’s crimson eyes glint, tone light.</p></div><p>Right here? No problem.</p>` },{ "id": "mirael", "img": !settings.NSFW ? "" : "event/nsfw/mirael0507144800.png", "en": `<div class="narration"><p>She sheds her dark gown, revealing her pale, scarred body, voice sultry.</p></div><p>Please be gentle, Lord, let me get used to your body... This is my first time.</p>` },{ "id": "you", "img": !settings.NSFW ? "" : "event/nsfw/mirael0507144800.png", "en": `<div class="narration"><p>You blink, tone surprised yet probing.</p></div><p>What...? This is nothing like I imagined! And how did your wounds heal so fast?</p>` },{ "id": "mirael", "img": !settings.NSFW ? "" : "event/nsfw/mirael0507144800.png", "en": `<div class="narration"><p>Mirael chuckles softly, crimson eyes gleaming, tinged with regret.</p></div><p>A succubus's first time, with her charm magic? Unbeatable. Sadly, severed wings stole all my power. But your essence can still heal me, Master. Please... help me recover more~~</p>` },{ "id": "you", "img": !settings.NSFW ? "" : "event/nsfw/mirael0507151415.png", "en": `<div class="narration"><p>You narrow your eyes, tone cautious, mind calculating.</p></div><p>This is crucial information. I plan to summon more succubi—I need to know everything about them.</p>` },{ "id": "mirael", "img": !settings.NSFW ? "" : "event/nsfw/mirael0507151415.png", "en": `<div class="narration"><p>Mirael draws closer, breath warm, voice solemn.</p></div><p>Yes, my Lord, I will give you a thousand offspring.</p>` },{ "id": "you", "skip": false, "img": !settings.NSFW ? "" : "event/nsfw/mirael0507151415.png", "en": `<div class="narration"><p>You raise an eyebrow, smirking inwardly.</p></div><div class="monologue"></div><p>Probably simpler than you think.</p><br><<button 'Maybe Later'>><<SetFlag 'miraelflag' 4>><<goto 'Summon_UI'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"><p>The energy infuser hums beside the bed, its arcane glow illuminating Mirael’s pale skin and crimson eyes. She reclines, her maid’s gown slightly loosened, exuding seductive yet trusting allure.</p></div>` },{ "id": "you", "en": `<div class="narration"><p>You watch Mirael.</p></div><p>Mirael, how’s it feel? I just pumped thirty days of pregnancy into you—bet that’s gotta be... something, right?</p>` },{ "id": "mirael", "en": `<div class="narration"><p>Mirael chuckles softly, crimson eyes glinting, hand gently caressing her abdomen.</p></div><p>My Lord, words fail to capture the delight... My belly feels so full, suffused with a satisfying warmth—no discomfort, only pleasure, like I’m floating on air~</p>` },{ "id": "you", "en": `<div class="narration"><p>You raise an eyebrow, tone surprised.</p></div><p>Floating, huh? Haha, sounds like you’re loving it. Good to know you’re not feeling off—I was worried this thing might be too intense.</p>` },{ "id": "leah", "en": `<div class="narration"><p>The ceramic angel figurine flickers, Leah’s telepathic voice ethereal and commanding.</p></div><p>Lord, the energy infuser drains vast reserves of my divine power, yet it is a necessity for these apocalyptic times, designed to hasten gestation. As the goddess of fertility, I ensure the mother’s safety—safer than mortal pregnancy, with greater benefits.</p>` },{ "id": "leah", "en": `<div class="narration"><p>Leah’s voice deepens, laced with mystery.</p></div><p>Upon birth, the offspring enters the Planar Rift for summoning, bypassing the perils of delivery. In time, I shall unlock early birth and extended gestation, each safe and uniquely potent. But my divine power is finite—use it wisely.</p>` },{ "id": "you", "en": `<div class="narration"><p>Your eyes widen, muttering in awe, mind reeling.</p></div><p>Holy crap, Leah, you fertility goddess, you’re the real deal! Playing with childbirth like this? Turbo mode, Planar Rift, early and extended options... it’s like coding new features. Wild stuff!</p>` },{ "id": "mirael", "en": `<div class="narration"><p>Mirael leans closer, breath warm, smile seductive.</p></div><p>My Lord, your shock is adorable... I find this blissful sensation far more enchanting than any demon world magic. Don’t you agree?</p>` },{ "id": "you", "en": `<div class="narration"><p>You cough, scratching your head to mask embarrassment.</p></div><p>Ahem, yeah, pretty... enchanting, alright. You rest up, I need to process this "god-tier maneuver."</p>` },{ "id": "leah", "en": `<div class="narration"><p>Leah’s voice turns commanding, laced with warning.</p></div><p>Lord, forget not the looming apocalypse. This gestation ensures offspring with the strongest parental traits, avoiding the unpredictability of Planar Rift summoning. The infuser is potent but drains my power dearly—I’ve suppressed temple gestation to conserve it. Use the summoning array against the Dark Legion!</p>` },{ "id": "narr", "en": `<div class="narration"><p>Mirael’s gestation ritual concludes under the bedroom’s arcane light, Leah’s divine act bringing new hope to Hawkridge Barony. You muse, energy allocation needs more caution.</p></div><div class="monologue"></div><<SetFlag 'miraelflag' 6>><<run $chars.dataMap.get("mirael").calcAttrInfo("bond.vl", 1)>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "captain", "en": `<div class="narration"><p>War drums rumble faintly in the distance. Guard Captain bursts in, clad in battle-worn armor, his face grim.</p></div><p>My Lord, get up! The demons are massing at the Mountain Pass! Scouts just reported—hordes of those cursed fiends, ready to charge any moment! We’ve gotta hold the pass today, or we’re done for! Throw on your armor and get to the pass interface—check the defenses now, we’re out of time!</p>` }, { "id": "you", "skip": false, "en": `<<NoticeAdd "Garvin" "The demons are massing at the Mountain Pass! Scouts just reported—hordes of those cursed fiends, ready to charge any moment!" '<<goto "Battle_UI">>'>><<SetFlag 'battleday'>><<button "Go to the Pass">><<goto "Battle_UI">><</button>><br><<button 'Maybe Later'>><<goto 'MainHall_UI'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "promaid", "en": `<p>Good morning... my lord. A merchant caravan has arrived today. Would you like to inspect it?</p>` }, { "id": "you", "skip": false, "en": `<<NoticeAdd "Erin" "A merchant caravan has arrived today." '<<goto "Market_UI">>'>><<button "Check It Out">><<goto 'Market_UI'>><</button>><br><<button 'Maybe Later'>><<goto 'MainHall_UI'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "promaid", "en": `<div class="narration"><p>Erin approaches, head lowered, her tone soft yet nervous.</p></div><p>Good morning, my lord… The Silvertail Clan’s caravan just arrived, restocking the black market. Miss Hizuki says… there are some fine goods this time, and you should definitely take a look.</p>` },{ "id": "foxmaid", "en": `<div class="narration"><p>Hizuki strides into the hall.</p></div><p>Hmph, my lord, this mistress’s caravan delivered fresh goods right on time! Miss them, and you’ll regret it! So, checking them out, or too busy?</p>` },{ "id": "you", "skip": false, "en": `<<NoticeAdd "Erin" "The Silvertail Clan’s caravan just arrived, restocking the black market." '<<goto "Market_UI">>'>><<button 'Check It Out'>><<goto 'Market_UI'>><</button>><br><<button 'Maybe Later'>><<goto 'MainHall_UI'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "captain", "en": `<p>Morning, my Lord. Today, <<set _humanpd = random(($domain.humanpp /10), ($domain.humanpp /5))>><<= _humanpd>> peasants from our old lands seek refuge. Your orders?</p>` }, { "id": "you", "skip": false, "en": `<<button 'Accept all'>><<run addHumanPop(_humanpd)>><<redo tag 'topbar'>><<goto 'MainHall_UI'>><</button>><br><<button 'Refuse'>><<goto 'MainHall_UI'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "archmaid", "en": `<p>Good morning, my lord. A Slaver Caravan selling Beastfolk arrived. Shall we check their stock?</p>` }, { "id": "you", "skip": false, "en": `<<NoticeAdd "Selena" "A Slaver Caravan selling monsters arrived." '<<goto "Slaver_UI">>'>><<button "Check It Out">><<goto "Slaver_UI">><</button>><br><<button "Maybe Later">><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "captain", "en": `<div class="narration"><p>Garvin strides forward, tense and urgent.</p></div><p>My Lord, a lizardfolk seeks audience outside the camp, claiming to be an envoy of the Thornscale Clan! Her gaze is chilling, no friend, I wager. Shall we admit her?</p>` },{ "id": "you", "en": `<div class="narration"><p>You frown, puzzled.</p></div><div class="monologue"><p><em>This world’s full of weirdos! What’s a lizardfolk want now?</em></p></div><p>Let her in, Garvin, but keep guards close.</p>` },{ "id": "captain", "en": `<div class="narration"><p>Garvin nods, tone wary.</p></div><p>As you command, my Lord! But take heed—outsiders can’t be trusted! That tail... it moves like a serpent!</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya steps forward, scales glinting in the torchlight, tone calm but probing.</p></div><p>Soft-skin human, I am Sariya Thornscale of the Thornscale Clan, here for my people. You are the lord of this valley, foe of demons?</p>` },{ "id": "you", "en": `<div class="narration"><p>You nod, eyes cautious.</p></div><div class="monologue"><p><em>Soft-skin? That’s a weird one!</em></p></div><p>I’m the Lord. You didn’t come all this way to talk about the weather, did you? What’s your business?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s lips curl faintly, with dry humor.</p></div><p>The weather? Soft-skins have curious minds. My clan has withdrawn to toxic swamps. The demons’ Blood Poison drives soft-skins mad, yet it cannot touch us.</p>` },{ "id": "you", "en": `<div class="narration"><p>Your eyes spark with curiosity.</p></div><p>Sounds like you’re tough. Cold blood? Immune to the Blood Poison?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya nods, faintly proud.</p></div><p>Cold blood, a gift from the Mother of Wetlands. Blood Poison holds no sway, letting us tread demon lands to spy their ways. Yet, the swamp starves. We need allies.</p>` },{ "id": "you", "en": `<div class="narration"><p>You frown, tone probing.</p></div><p>So you’re here for a deal? What do you offer, and what’s the cost?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s golden eyes glow, staring at you, voice low.</p></div><p>My Eyes of Prophecy see... you are no common soul, stranger from afar, your fate veiled. An ancient stone speaks of a hidden path in this valley, perchance a key to defy demons.</p>` },{ "id": "you", "en": `<div class="narration"><p>You force calm, heart racing.</p></div><div class="monologue"><p><em>Those eyes are like X-rays! Does she know I’m a transmigrator?</em></p></div><p>A hidden path? Worth a shot, but you say it’s just a prophecy—sure it’s real?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s tone is steady, gaze sharp.</p></div><p>Prophecies never lie, yet its trace must be sought. I seek to remain here, to find the path. What are your terms?</p>` },{ "id": "you", "en": `<div class="narration"><p>You ponder, tone cautious.</p></div><div class="monologue"><p><em>The path sounds promising, but outsiders are tricky—need a backup plan.</em></p></div><p>A path could help us all, so you can stay. But with demons around, humans are wary of outsiders, might cause trouble. How about... posing as my maid? Easier to move around nearby.</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya smirks, tail stiffening.</p></div><p>A maid? Soft-skins have odd notions. Very well, for the path, I’ll play your... Scale-Maid. Expect no tea-serving.</p>` },{ "id": "you", "en": `<div class="narration"><p>You grin, easing the tension.</p></div><div class="monologue"><p><em>Scale-Maid? That’s a killer title!</em></p></div><p>Scale-Maid? Creative! Tea’s someone else’s job, just focus on the path. If you find anything good, I want a cut!</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s tail flicks, tone dryly humorous.</p></div><p>A cut? Soft-skins are greedy. We shall see. If the path appears, we both gain. Do not disappoint me, Lord.</p>` },{ "id": "narr", "en": `<div class="narration"><p>Sariya dons a maid’s garb, remaining in Hawkridge Vale as the ’Scale-Maid.’ Her keen golden eyes watch the Lord’s deeds, seeking to affirm her prophecy of the ’Stranger from Afar’; meanwhile, she roams, hunting traces of the ancient path foretold by the stone. Campfolk whisper of the new Scale-Maid, fond of exploration, oft glimpsed ’twixt crags and ruins.</p></div>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "you", "en": `<div class="narration"><p>You grin at Sariya.</p></div><div class="monologue"><p><em>She looks ready to dig through the valley!</em></p></div><p>Sariya, how’s the Scale-Maid gig? Any luck with that hidden path?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s lips curl, tone confident.</p></div><p>Soft-skin Lord, the path lies close! My Eyes of Prophecy sense the stone’s aura. Once I scour these crags, victory is mine!</p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "you", "en": `<div class="narration"><p>You look at Sariya with concern.</p></div><div class="monologue"><p><em>She looks like she lost her tail!</em></p></div><p>Sariya, any progress on the path? Don’t wear yourself out—maid outfits aren’t for digging.</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya sighs, tone heavy.</p></div><p>Soft-skin, the path’s trace eludes me... Could the stone’s prophecy err? My heart’s troubled—spare me your words.</p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "you", "en": `<div class="narration"><p>You tease Sariya playfully.</p></div><div class="monologue"><p><em>She looks like she’s solving a cosmic riddle!</em></p></div><p>Sariya, daydreaming outside the temple? Found that path yet?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya frowns, tone cool.</p></div><p>Soft-skin Lord, the path stays hidden... The stone’s guidance grows vague, as if veiled by strange forces. My mind’s troubled—leave me be.</p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "you", "en": `<div class="narration"><p>You call out to Sariya.</p></div><p>Sariya!</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya steps forward, tail twitching, tone wary.</p></div><p>Soft-skin Lord, I’ll speak plainly. This temple hall sees new faces oft, their origins unvoiced. Are you hiding aught from me?</p>` },{ "id": "you", "en": `<div class="narration"><p>You feign surprise, teasing back.</p></div><div class="monologue"><p><em>This lizard lady’s sharp!</em></p></div><p>Ho, Sariya, do your Eyes of Prophecy double as spy lenses? The newcomers... not from the path, rest easy.</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya narrows her eyes, snorting.</p></div><p>Not the path? Soft-skin excuses convince no one. I serve my kin and prophecy—your secrets shan’t elude my gaze!</p>` },{ "id": "you", "en": `<div class="narration"><p>You smile mysteriously, lowering your voice.</p></div><div class="monologue"><p><em>I sound like a cult leader!</em></p></div><p>Alright, Sariya, I’ll let you in on a big secret. It involves... a goddess. Join, and you’re bound for life. Dare to try?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s tail stiffens, tone resolute.</p></div><p>A goddess? Soft-skin tricks, no doubt. Yet, for my kin and to prove the prophecy, I’ll brave the risk! Speak—what’s this secret?</p>` },{ "id": "you", "en": `<div class="narration"><p>You point to the altar, dramatically.</p></div><div class="monologue"><p><em>This sounds way too mystical!</em></p></div><p>Bold choice! The secret lies at the Planar Rift—a ’Summoning Rite.’ But, Sariya, there’s no turning back.</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s eyes widen, teeth gritted.</p></div><p>Planar Rift? Summoning Rite? Soft-skin, your mystic ploys nearly fooled me! Yet, even at life’s cost, I’ll see it through!</p>` },{ "id": "you", "en": `<div class="narration"><p>You wave a hand, tone casual.</p></div><div class="monologue"><p><em>She thinks I’m offing her?</em></p></div><p>Easy, it’s not that serious! Just a little rite, show you something wild.</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya snorts, tail flicking.</p></div><p>Hmph, soft-skin flippancy irks me. Lead on—I’ll see what secrets this rite unveils!</p>` },{ "id": "you", "en": `<div class="narration"><p>You grin slyly, heading to the rift.</p></div><p>Heh, keep up, Sariya!</p>` },{ "id": "narr", "en": `<div class="narration"><p>Guided by you, Sariya, fraught with doubt yet loath to interrupt, follows to the Planar Rift.</p></div>` },{ "id": "you", "en": `<div class="narration"><p>You gaze at Sariya, tone probing.</p></div><div class="monologue"><p><em>Will this lizard lady agree? Hope she doesn’t bite!</em></p></div><p>Sariya, the temple’s Summoning Rite needs you... through coupling to invoke the Goddess of Fertility’s blessing. Are you willing?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya’s tail stiffens, golden eyes narrowing, tone wary.</p></div><p>Coupling? Soft-skin audacity knows no bounds! If this rite aids my kin and defies demons, I’ll try... but if you deceive, my tail shall pierce your heart!</p>` },{ "id": "you", "en": `<div class="narration"><p>You feign ease, pointing to the rift.</p></div><div class="monologue"><p><em>She’s more riled than I expected!</em></p></div><p>Easy, Sariya! It’s the Goddess’s will—I must plant the seed of life within you to awaken the rift. No turning back. Are you sure?</p>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya grits her teeth, tone stern.</p></div><p>Soft-skin, your words jest, yet bear divine aura. For the Thornscale Clan, I’ll yield to the rite! Act swiftly, lest I regret!</p>` },{ "id": "narr", "en": `<div class="narration"><p>You lead Sariya to the Planar Rift, and the rite begins. The rift hums, torchlight glints off her scales, and as you couple, the life-force surges within her, awakening the Goddess’s power.</p></div>` },{ "id": "scalemaid", "img": !settings.NSFW ? "" : "event/nsfw/scalemaid0625044114.webp", "en": `<div class="narration"><p>Sariya’s breath quickens, golden eyes gleaming, tone angry yet awed.</p></div><p>Soft-skin! You... truly roused divine power! I feel life burgeoning within, as if the Goddess watches!</p>` },{ "id": "you", "img": !settings.NSFW ? "" : "event/nsfw/scalemaid0625044114.webp", "en": `<div class="narration"><p>You respond calmly, hiding a smile.</p></div><div class="monologue"><p><em>This lizard lady really knows how to freak out!</em></p></div><p>Feel it? That’s the Goddess’s blessing. Focus, Sariya—the stronger the life-force, the better the rite!</p>` },{ "id": "narr", "img": !settings.NSFW ? "" : "event/nsfw/scalemaid0625044114.webp", "en": `<div class="narration"><p>The rite ends, the rift’s glow fades, and Sariya emerges, scales shimmering, breath uneven, as if savoring the divine afterglow.</p></div>` },{ "id": "scalemaid", "en": `<div class="narration"><p>Sariya licks her lips, tone dryly humorous.</p></div><p>Hmph, soft-skin, you didn’t deceive... This rite is truly queer, life-force surging like a tide. Call me oft—I shan’t mind another try.</p>` },{ "id": "you", "skip": false, "en": `<div class="narration"><p>You shake your head, teasing back.</p></div><div class="monologue"><p><em>This lizard lady’s addicted!</em></p></div><p>Hooked on the rite, eh? Don’t forget, Scale-Maid, your main job’s the path!</p> <<SetFlag 'scalemaidflag' 1>><<button "OK">><<goto 'MainHall_UI'>><</button>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"> <p>Consciousness surges back like a tide, the hard stone slab digging into your spine. Firelight flickers, casting shadows on rugged rock walls, the air thick with scorched earth and ancient dust.</p> <p>This is a temple buried deep in the mountains of Hawkridge Vale, solid as a fortress, silent and mysterious. Mid-May sunlight spills through high cracks, illuminating a shattered goddess statue—its worn face regal, looming over your slab. Four figures stand nearby, their whispers tense, their gazes varied.</p> </div>` }, { "id": "narr", "en": `<div class="narration"> <p>You force your eyes open, throat parched, fighting panic as you slowly sit up. Glancing down, you’re shocked to see your suit gone, replaced by heavy armor, crusted with dried blood and scars on your chest and arms.</p> </div> <div class="monologue"> <p><em>What the hell? I was just getting zapped in the rain! Where’d this armor come from? When did I get these wounds? That blood moon, that "your soul is summoned" line... did I transmigrate? Their stares... what are they calling me? This temple’s religious vibe has me on edge—one wrong word, and I’m a heretic. Gotta stay cool, can’t slip up!</em></p> </div>` }, { "id": "you", "en": `<div class="narration"> <p>You mutter, touching your forehead, fingers slick with sticky blood, the armor’s weight making you sway slightly.</p> </div> <p>My head’s killing me.</p> <p>Where am I? Who... are you people?</p>` }, { "id": "archmaid", "en": `<div class="narration"> <p>A woman steps forward softly, holding a damp cloth, her face gentle but tinged with hidden tension. She carefully dabs your forehead, her touch soft but hesitant.</p> </div> <p>My Lord, you’re awake... thank the heavens.</p> <p>I’m Selina, your head maid. This is a hidden temple, buried deep in Hawkridge Vale’s mountains. We fled here, hunted by demons.</p>` }, { "id": "captain", "en": `<div class="narration"> <p>A burly man in scarred armor steps forward, his voice rough with anger and guilt. His fists clench, his head lowering.</p> </div> <p>Baron, I’m Garvin, captain of your guard.</p> <p>Yesterday morning, a massive boom echoed from the mountains north of town, sending the townsfolk into panic and sparking wild rumors. A demon legion struck in the chaos, breaching the western wall at noon and charging the keep. I fought to get you and the townsfolk out... but I failed to hold Hawkridge Town.</p>` }, { "id": "you", "en": `<div class="narration"> <p>You frown, feigning confusion, the armor’s weight feeling alien.</p> </div> <div class="monologue"> <p><em>Baron? You gotta be kidding—I’m just an office drone! This armor, these wounds... I was working late! Demons hunting me? Why are demons so obsessed? This temple and the explosion must be linked. Gotta stay low, dig up the truth!</em></p> </div>` }, { "id": "you", "en": `<div class="narration"> <p>You hesitate, rubbing your temples.</p> </div> <p>I see... please, go on.</p>` }, { "id": "butler", "en": `<div class="narration"> <p>An older man with graying hair steps forward, in an emerald coat, his tone heavy, eyes sharp with scrutiny. He bows slightly.</p> </div> <p>My Lord, I’m Raymond, your steward, serving the Wilderen family for thirty years.</p> <p>We fled to Hawkridge Vale for safety, where the family’s ancestral estate, once Cloudvale Pasture, stood—built on breeding fine warhorses. But when we arrived, everything was razed by an unknown explosion, likely the source of yesterday’s boom. We found a temple entrance hidden beneath the estate’s ruins and had no choice but to take refuge here.</p>` }, { "id": "archmaid", "en": `<div class="narration"> <p>Selina continues, her voice soft but tinged with uncertainty. She glances at the statue, a flicker of awe in her eyes.</p> </div> <p>My Lord, this temple’s entrance seems cloaked by some power, tied to the Wilderen family’s ancient legacy, perhaps the goddess’s protection.Demons chased us to the vale but couldn’t find us. When you were unconscious, we prayed to that statue for your recovery... and a faint light shone down, falling on you.</p>` }, { "id": "promaid", "en": `<div class="narration"> <p>A young woman huddles behind Selina, head bowed, clutching her skirt into wrinkles, her voice nearly drowned by the firepit’s crackle.</p> </div> <p>M-My Lord...</p> <p>I... I’m Erin, apprentice maid. You’re okay... thank goodness...</p>` }, { "id": "butler", "en": `<div class="narration"> <p>Raymond looks at you, his tone heavy.</p> </div> <p>The Wilderen family’s warhorse breeding was unmatched, but the old baron kept his methods secret. This temple... it might be tied to the family’s mysteries, but we know nothing. With demons ravaging the empire and supplies dwindling, this temple is our last stand.</p>` }, { "id": "captain", "en": `<div class="narration"> <p>Garvin grits his teeth, his voice low and thick with hatred. He pauses, his eyes blazing with resolve.</p> </div> <p>Those demons brought beasts—fur, claws, crueler than any beastfolk. I saw them rip my men apart...</p> <p>My Lord, I failed Hawkridge Town, but I swear, this temple will not fall!</p>` }, { "id": "archmaid", "en": `<div class="narration"> <p>Selina interrupts gently, her voice calm but firm. She looks at you, her gaze soft but resolute.</p> </div> <p>Garvin, steady yourself. Those weren’t beastfolk—just monstrosities twisted by demons. Misjudging our enemy will only put us in greater peril.</p>` }, { "id": "promaid", "en": `<div class="narration"> <p>Erin bows her head, clutching her skirt, her voice nearly drowned by the firepit’s crackle. She steals a glance, her eyes flickering with something hidden, then turns quickly.</p> </div> <p>I... I’ll go check on the wounded.</p>` }, { "id": "you", "en": `<div class="narration"> <p>You take a deep breath, forcing calm, your eyes flicking between the statue and the group, the armor’s weight feeling alien.</p> </div> <div class="monologue"> <p><em>Demons sent to kill a "baron"? I’m just an office drone! This armor, these wounds, the pasture’s explosion, this temple—it’s all a puzzle! That statue’s light, Erin’s weird look... I’ll uncover the truth myself, no slip-ups!</em></p> </div>` },{ "id": "you", "img": "", "skip": false, "en": `<div class="narration"> <p>You keep your tone steady, standing and brushing dust from your armor, your gaze firm.</p> </div> <p>Alright, I get it.</p> <p>First, take stock of supplies, check the wounded, and prepare defenses. Show me this temple... I need to know how long we can hold out.</p> <p><<SetFlag 'tutorial' 1>><<button "I must act as Baron to avoid being exposed.">><<goto 'MainHall_UI'>><</button>></p>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "img": "", "skip": true, "en": `With Hawkridge Vale slowly reclaimed, it’s time to end the chaos. <br>At your suggestion, a room is quickly cleared out and set up as a meeting room.` }, { "id": "narr", "img": "", "en": `<<run Msg.add('notify', "Meeting Room Unlocked.");>><<SetFlag 'endintro' false>>This will be the hub for gathering intel and making decisions. <br>In these perilous times, with demons closing in, a makeshift yet vital decision-making process takes shape here. <br>This room will witness the turning point of Baron Wilderen’s fate.` } ] <</script>>
<<script>> setup.speechData = [ { "id": "archmaid", "en": `<div class="narration"><p>Selina approaches softly, tone gentle.</p></div><p>Good morning, my Lord. The stronghold fares well. The wounded from our flight have nearly healed, and today we may remove their bandages for cleaning. Do you approve?</p>` },{ "id": "you", "en": `<div class="narration"><p>You stretch, rubbing your eyes.</p></div><div class="monologue"><p><em>Finally some good news—less stress for once.</em></p></div><p>Morning, Selina. Sounds good—go for it, make sure it’s done clean. With the wounded recovering, bet everyone’s mood’s picking up.</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina nods, smiling faintly.</p></div><p>Thank you, my Lord. I’ll see it done properly. Your care brings hope to the stronghold.</p>` },{ "id": "you", "en": `<div class="narration"><p>You raise an eyebrow, tone probing.</p></div><div class="monologue"><p><em>Erin’s like a ghost—hope she’s not stirring trouble.</em></p></div><p>Oh, by the way, Selina, how come I barely see that apprentice maid... what’s her name, Erin? Where’s she at?</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina pauses, tone slightly hesitant.</p></div><p>Erin... she’s quite busy, my Lord. She’s tasked with tending the wounded, with much to do. And as an apprentice, her training keeps her occupied, so... she’s not often seen.</p>` },{ "id": "you", "en": `<div class="narration"><p>You scratch your head, tone casual yet probing.</p></div><div class="monologue"><p><em>Busy like a corporate drone? Her reaction’s a bit off!</em></p></div><p>Oh? That busy, huh? Sounds tough. Alright, tell her not to overdo it—pop by the hall sometime. Everyone looks out for her.</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina chuckles softly, masking unease.</p></div><p>Yes, my Lord, I’ll pass it along. Erin will surely appreciate your kindness.</p>` },{ "id": "you", "en": `<div class="narration"><p>After days of rest, your medieval humans show remarkable recovery. Wounds healed, armor repaired—ready for greater challenges. (All character portraits updated.)<br>Selina’s report warms the bedroom with morning calm, yet her hesitant words about Erin hint at hidden truths.</p></div><div class="monologue"><p><em>What’s Erin up to? Something’s fishy—gotta check it out later.</em></p></div> <<run $chars.dataMap.get("you").img = "characters/you/you02.png">> <<run $chars.dataMap.get("promaid").img = "characters/promaid/promaid02.webp">> <<run $chars.dataMap.get("captain").img = "characters/captain/captain02.webp">> <<run $chars.dataMap.get('butler').img = "characters/butler/butler02.png">> <<run $chars.dataMap.get('archmaid').img = "characters/archmaid/archmaid02.webp">>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"><p>The chambers of the Lost Temple sink deep into the mountain’s core, moss-streaked walls lit by torches flickering across iron cages, glinting off chains. The air hangs damp and heavy, laced with shallow breaths and rare growls. Caged figures slump or grip bars, fur and scales shimmering faintly. In a far corner, a blaze of orange hair sparks, slicing the silence.</p></div>` },{ "id": "you", "en": `<div class="monologue"><p><em>This place feels like hell’s waiting room... Just got yanked into this world and now this? Yesterday I was stressing over deadlines, today I’m staring at cages. Those shapes—not human, Beastfolk? It’s creepy, but damn, I’m pissed—who did this?</em></p></div>` },{ "id": "you", "en": `<div class="narration"><p>You pause at the chamber’s threshold, frowning at the cages, voice edged with shock.</p></div><div class="monologue"><p><em>Cages? With living things?! This isn’t a treasure vault—it’s a damn dungeon! Selina’s right behind me; she’s gotta know the deal. Ask first, don’t screw this up.</em></p></div><p>Selina... what’s this? What’s in these cages? People? Animals? Or... something else?</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina steps forward slowly, skirts brushing the floor, tone soft but uneasy.</p></div><p>My Lord, these are... Beastfolk.</p><div class="narration"><p>She lowers her gaze, hands clasping lightly.</p></div><p>The empire ordered their confinement this past month. When we fled to Hawkridge Vale, we... followed commands, setting up these cells.</p>` },{ "id": "you", "en": `<div class="narration"><p>You snap your gaze to her, voice rising unconsciously, laced with anger.</p></div><div class="monologue"><p><em>Beastfolk? Locked up?! I just got here and they hit me with this? Hauling cages while running for your life—what’s wrong with these people?! Cool it... gotta know why. The empire’s no saint.</em></p></div>` },{ "id": "you", "en": `<p>Locked up?! You dragged cages while fleeing?!</p><div class="narration"><p>You take a deep breath, curbing rage.</p></div><p>Selina, why’s the empire doing this? What did the Beastfolk do to deserve this? Give me a reason!</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina’s eyes drop, voice a near-sigh, choosing words carefully.</p></div><p>My Lord, the empire claims... the Beastfolk are tied to demons.</p><div class="narration"><p>She raises her gaze, eyes layered.</p></div><p>In the past, humans and Beastfolk clashed—over trade, forest borders—but it never came to blades. Yet a month ago, demonic rifts erupted, slaughtering all. At the same time, Beastfolk tribes raised ’altars,’ and the empire suspects they colluded with demons, sowing chaos.</p>` },{ "id": "you", "en": `<div class="narration"><p>You frown, crossing arms, tone tinged with absurdity.</p></div><div class="monologue"><p><em>Altars? That’s it?! The empire’s been watching too many fantasy flicks! A month-old rumor, and they cage them like this? These bars are choking me just looking... Are Beastfolk really that bad?</em></p></div>` },{ "id": "you", "en": `<p>Altars? That’s why they’re caged like livestock?!</p><div class="narration"><p>Your gaze scans the cells, voice low.</p></div><p>Selina, a month-old story—where’s the proof? Didn’t Beastfolk just have spats with humans before? How’d they suddenly become demon lackeys?</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina’s lips part, as if to argue, then fall silent a moment.</p></div><p>My Lord, the empire... found Beastfolk tokens, akin to demonic runes near the rifts.</p><div class="narration"><p>Her voice slows, hesitant.</p></div><p>I know it’s harsh, but with demons rampant, the capital’s in panic. They needed... a reason, to quell the fear.</p>` },{ "id": "you", "en": `<div class="narration"><p>You scoff, eyes sharp as blades, tone unyielding.</p></div><div class="monologue"><p><em>Tokens? Runes? That excuse is trash! Scapegoats, plain and simple! These Beastfolk are skin and bones—colluding? The empire’s got nerve talking panic! Not in Hawkridge Vale, no way!</em></p></div>` },{ "id": "you", "en": `<p>A reason? Scapegoats, that’s what!</p><div class="narration"><p>You step forward, pointing at the cages.</p></div><p>Selina, look at them! Starved to skeletons, and they’re demon allies?! I don’t care what the empire thinks—Hawkridge Vale won’t stoop this low! Keys, now—free them all!</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina trembles faintly, eyes flicking to the cages, then hands over the keys.</p></div><p>My Lord... your will, I obey.</p><div class="narration"><p>Her voice low, tinged with worry.</p></div><p>But freeing Beastfolk may unsettle the folk, and the empire... may seek reckoning. Please, tread with care.</p>` },{ "id": "you", "en": `<div class="narration"><p>You take the keys, tone resolute but softening, reassuring.</p></div><div class="monologue"><p><em>Empire’s reckoning? Bring it! This place is crumbling—think I care about their threats? Selina means well, but this is non-negotiable. Gotta unlock these, find someone to talk to.</em></p></div><p>Selina, I know you’re worried, but this is right.</p><div class="narration"><p>You grip the keys, facing the cages.</p></div><p>If the empire wants trouble, let them try! We save people first—I’ll see who can talk.</p>` },{ "id": "you", "en": `<div class="narration"><p>You approach a row of cages, torchlight wavering, catching a figure in the corner. A catgirl leans against the bars, orange hair cascading like wildfire, golden eyes starry with wariness yet alive. Her tattered skirt can’t hide her grace, tail flicking, claw-scarred arms faintly bleeding.</p></div><div class="monologue"><p><em>This girl... like a flame in the dark, stunning, but hurt bad. Whoever caged her is a monster! Gotta free her, talk—she doesn’t look like trouble.</em></p></div>` },{ "id": "you", "en": `<div class="narration"><p>You crouch to her level, voice gentle as you can make it.</p></div><p>Hey... I’m, uh, Baron Wilderen.</p><div class="narration"><p>You jingle the keys, managing a smile.</p></div><p>Don’t worry, I’m getting you out. You okay? Those wounds... they alright? Can you tell me your name?</p>` },{ "id": "catmaid", "img": "characters/catmaid/catmaidslave.png", "en": `<div class="narration"><p>Her golden eyes size you up, then her lips curl, flashing tiny fangs, grinning like she nabbed a prize.</p></div><p>Baron? You don’t look like those nose-in-the-air nobles!</p><div class="narration"><p>Her voice bright, teasing.</p></div><p>I’m Stellafall, Felinefolk. Word is, hens laid double-yolk eggs when I was born—lucky streak, huh!</p><div class="narration"><p>She flicks her tail, leaning to the bars.</p></div><p>Thanks, for real... these cages got my claws itching something fierce.</p>` },{ "id": "you", "en": `<div class="narration"><p>You chuckle, keys clicking, cage swinging wide.</p></div><div class="monologue"><p><em>Double-yolk? Hah, she’s a gem! Hurt like that and still cracking jokes—the empire’s blind as hell! Gotta ask about those altars; smells like a setup.</em></p></div>` },{ "id": "you", "en": `<p>Double-yolk? Gotta rub some of that luck on me!</p><div class="narration"><p>You open the cage, stepping back.</p></div><p>Stellafall, you’re free. Look, the empire says you built altars, teamed up with demons—know anything about that? I want the truth.</p>` },{ "id": "catmaid", "img": "characters/catmaid/catmaidslave.png", "en": `<div class="narration"><p>She steps out, stretching wide, orange hair flaring like fire in the torchlight.</p></div><p>Altars? Hah, the empire’s brains must be pickled!</p><div class="narration"><p>Her gold eyes flash disdain, then spark with mirth.</p></div><p>What altars? A month back, our shamans said the ancestors warned—calamity’s coming, demon nonsense! We piled stone walls, dug traps to guard the rifts, and they call it sacrifice!</p><div class="narration"><p>She scoffs, tail whipping like a lash.</p></div><p>Collude with demons? Please, I wouldn’t toss those creeps a fishbone!</p>` },{ "id": "you", "en": `<div class="narration"><p>You nod, pinching your nose, tone half-fury, half-amused.</p></div><div class="monologue"><p><em>Walls as altars? That’s dumb as hell! The empire’s just dumping on Beastfolk—disgusting. Stellafall’s fire? I’m sold—she’s Hawkridge Vale material.</em></p></div><p>Alright, the empire wins clown of the year...</p><div class="narration"><p>You face Stellafall, grinning.</p></div><p>Stellafall, join us—as my maid, help me sort this mess. Fish on tap, freedom to roam—deal?</p>` },{ "id": "catmaid", "img": "characters/catmaid/catmaidslave.png", "en": `<div class="narration"><p>Her eyes widen, tail curling high, grinning like she hit the jackpot.</p></div><p>Fish?! Freedom?! Lord, you’re stealing my heart!</p><div class="narration"><p>She pats her chest, orange hair bouncing.</p></div><p>Done! Stellafall, reporting for duty! I’ll shine this place up like a fishpond!</p><div class="narration"><p>She leans in, winking.</p></div><p>But the fish—fresh ones, no backing out, right?</p>` },{ "id": "archmaid", "en": `<div class="narration"><p>Selina clears her throat, tone soft but shadowed with concern.</p></div><p>My Lord, your compassion is noble... but freeing Beastfolk so swiftly may spark distrust among the folk.</p><div class="narration"><p>She glances at Stellafall, eyes layered.</p></div><p>She... Felinefolk are deft at stealth, perhaps useful for scouting, but please, be cautious.</p>` },{ "id": "you", "en": `<div class="narration"><p>You wave a hand, gaze steady, sweeping the chambers.</p></div><div class="monologue"><p><em>Folk griping? Let ’em! Beastfolk aren’t the problem—demons are. Stellafall’s my key; gotta show everyone she’s gold. And Leah’s weird ritual... Selina? She’d deck me.</em></p></div><p>Selina, I’ll handle the distrust. Hawkridge Vale changes here—no more cages.</p><div class="narration"><p>You turn to Stellafall, voice warm.</p></div><p>Stellafall, look out for the others—make this feel like home. From now on, Hawkridge Vale’s Beastfolk are free!</p>` },{ "id": "you", "en": `<div class="narration"><p>You glance down—the ceramic angel figurine has appeared in your hand, cold and heavy. Selina and Stellafall are busy unlocking other cages, oblivious to your pause. Leah’s whisper surges in your mind like a tide.</p></div><div class="monologue"><p><em>Here we go... Leah’s sneaky doll trick, right on cue. That ritual... soul-bonding? Just thinking it makes my face burn! Ask Selina? No way, she’d knock me into next week! Stellafall? She’s got the fire, but it still feels weird.</em></p></div>` },{ "id": "leah", "img": "", "skip": true, "en": `<div class="narration"><p>Leah’s voice echoes ethereal, tinged with amusement, yet heavy with intent.</p></div><p>Riftbearer, your soul kindles hope...</p><div class="narration"><p>Her tone deepens, probing.</p></div><p>The catgirl’s spirit, swift as wind, resonates with the rift. She will aid you in stirring life’s power, summoning allies to defy the abyss. Free these souls—their bloodlines will reshape the Tainted Seed Epoch.</p>` },{ "id": "you", "en": `<div class="narration"><p>You grip the figurine, eyes catching Stellafall’s fiery hair, thoughts churning.</p></div><div class="monologue"><p><em>Resonance? She means Stellafall can boost the rift? The ritual... no, don’t go there! She seems solid, but I can’t even bring it up. Focus—settle the Beastfolk, let the empire try me!</em></p></div>` },{ "id": "you", "en": `<div class="narration"><p>You shout to the chambers.</p></div><p>Stellafall, get ready to roll! Everyone hear me! Hawkridge Vale has no cages from today! Beastfolk, humans—together, we crush demons!</p>` },{ "id": "catmaid", "img": "characters/catmaid/catmaidslave.png", "en": `<div class="narration"><p>Stellafall bounces up, tail spinning like a storm, laughter ringing clear.</p></div><p>Hail the Lord! Freedom forever!</p><div class="narration"><p>Her gold eyes blaze, waving to the Beastfolk.</p></div><p>Brothers, sisters—time for fish and fights! With the Baron, we’ll make Hawkridge Vale paradise!</p><div class="narration"><p>She sidles close, whispering.</p></div><p>Lord, I trust you—this’ll be home.</p>` },{ "id": "you", "en": `<div class="narration"><p>You nod, ready to lead Stellafall to the main hall.</p></div><p>Stellafall, follow the Head Maid to change, then follow me.</p>` },{ "id": "catmaid", "skip": false, "en": `<div class="narration"><p>After changing into the maid outfit, Stellafall twirled around in excitement.</p></div><p>This dress is so pretty!<p></p>Master! I'm at your command.<<set $summon.slots to [$chars.dataMap.get("you"), $chars.dataMap.get("catmaid")]>><<set $summon.currentsolt = 0>><p><<button "Let's go!">><<goto 'MainHall_UI'>><</button>></p></p> <<SetFlag 'catmaidflag' 1>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"><p>The main hall of the Lost Temple nestles deep in the mountain’s heart, moonlight leaking through a fractured dome, glinting off a charred stone dais and the hovering Planar Rift. The rift pulses like liquid stars, its low hum edged with forbidden notes, fractured runes around it whispering of the explosion that razed the ranch. The air thrums with unease, broken only by Stellafall’s sprightly steps, her orange hair flaring like flame in the gloom.</p></div>` },{ "id": "narr", "en": `<div class="monologue"><p><em>Back in this creepy dump... The rift’s still freaky, like it’s alive. Just got zapped here, and now I’m supposed to con Stellafall into Leah’s weird ritual? My face is burning already! She’s so chipper—how do I even start? If she slugs me, I’m screwed.</em></p></div>` },{ "id": "you", "en": `<div class="narration"><p>You halt before the rift, rubbing your hands, sneaking a glance at Stellafall, voice stiff.</p></div><div class="monologue"><p><em>Alright, deep breath... don’t choke! Leah says this ritual saves the world, but "soul resonance" sounds like... ugh, don’t go there! Test the waters, don’t spook her.</em></p></div>` },{ "id": "you", "en": `<p>Uh, Stellafall, this place... kinda cool, right?</p><div class="narration"><p>You flash an awkward grin, pointing at the rift.</p></div><p>This is, y’know, the Planar Rift—super epic thing! It can, uh, call in backup to smash demons.</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Stellafall tilts her head, gold eyes gleaming, tail swinging like a pendulum, tone curious.</p></div><p>Cool? Hmm... kinda spooky, like a fishbowl full of stars!</p><div class="narration"><p>She leans toward the rift, orange hair bouncing.</p></div><p>Backup? What kinda backup? More Felinefolk like me? Or some huge beastie?</p><div class="narration"><p>She grins, flashing tiny fangs.</p></div><p>Lord, you didn’t drag me here just for the view, did ya? Spill—what’s the fun part?</p>` },{ "id": "you", "en": `<div class="narration"><p>You swallow hard, palms sweaty, forcing calm.</p></div><div class="monologue"><p><em>Fishbowl? Hah, her metaphors... Focus, man! How do I say "ritual"? Soul resonance? Too weird! Pitch it as an adventure—yeah, adventure!</em></p></div>` },{ "id": "you", "en": `<p>Hey, the view’s just a bonus!</p><div class="narration"><p>You flash a grin, thumping your chest.</p></div><p>This rift needs us to... light it up! Yeah, spark it! Then we’ll call in tons of badass allies to save Hawkridge Vale. You love thrills, right? This is hero stuff!</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Stellafall’s eyes spark, tail shooting higher, tone buzzing.</p></div><p>Hero? Ooh, that sounds awesome!</p><div class="narration"><p>She hops half a step, orange hair flopping.</p></div><p>Light it up? How? Toss a torch? Do a dance?</p><div class="narration"><p>Her gold eyes narrow, half-teasing.</p></div><p>Lord, you’re not making me work for free, right? Any rewards? Fish—fresh ones, heh!</p>` },{ "id": "you", "en": `<div class="narration"><p>You crack a laugh, nerves easing, mind racing.</p></div><div class="monologue"><p><em>Fish? She’s got priorities! Fine, use fish as bait! But the ritual—say "soul-bond"? No, too lovey-dovey! Try... energy? Yeah, energy!</em></p></div>` },{ "id": "you", "en": `<p>Fish? Oh, you bet! Fresh, fat ones till you’re stuffed!</p><div class="narration"><p>You throw a thumbs-up, voice dropping conspiratorial.</p></div><p>But sparking this thing needs... energy resonance. You and me, uh, tapping the rift’s mojo! Not hard, just... a special kinda job.</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Stellafall tilts her head, tail slowing, gold eyes flickering with doubt.</p></div><p>Energy resonance? Sounds fancy...</p><div class="narration"><p>She scratches her cheek, grin wary.</p></div><p>Lord, be straight—what’s the catch? I’m not diving into that starry puddle, am I?</p><div class="narration"><p>She points at the rift, half-joking.</p></div><p>Fish are great, but I wanna know what’s up—your eyes are all sneaky!</p>` },{ "id": "you", "en": `<div class="narration"><p>Your face flushes, waving hands, voice stammering.</p></div><div class="monologue"><p><em>Sneaky?! I’m not! Her nose is sharper than a bloodhound’s! Can’t say soul-anything—spin something solid... saving the world! Yeah, she’ll bite!</em></p></div>` },{ "id": "you", "en": `<p>Catch? No catch!</p><div class="narration"><p>You force a chuckle, straightening up.</p></div><p>Stellafall, this is the key to saving the world! The rift needs our, uh, willpower to ignite hope or whatever. Demons are wrecking stuff out there—don’t you wanna show ’em who’s boss?</p>` },{ "id": "catmaid", "en": `<div class="narration"><p>Stellafall’s gold eyes narrow to slits, studying you, lips curling upward.</p></div><p>Save the world? Lord, your tongue’s sweeter than a honey jar!</p><div class="narration"><p>She giggles, tail whipping again.</p></div><p>Fine, I got willpower—Felinefolk souls don’t mess around!</p><div class="narration"><p>She pats her chest, orange hair bouncing.</p></div><p>But deal’s a deal—after this, I want a fish feast! When do we start? I’m itching to go!</p><p>Lord, why are you stripping me?! Ah~</p>` },{ "id": "you", "img": !settings.NSFW ? "" : "event/nsfw/stellafall0401235531.png", "en": `<div class="narration"><p>You clear your throat, tone as solemn as you can muster.</p></div><p>Stellafall, this is the Goddess’s way to save the world! Don’t be sad—I’m not hiding anything, this is the summoning ritual. Let’s see what we can call forth!</p>` },{ "id": "catmaid", "img": !settings.NSFW ? "" : "event/nsfw/stellafall0401235531.png", "en": `<div class="narration"><p>Stellafall blinks her gold eyes, voice with a hint of nerves.</p></div><p>Oh, so that’s it? Lord, I’ve got no experience... you’ll have to guide me, ah~!</p>` },{ "id": "narr", "en": `<<CardPop $chars.dataMap.get("you") $chars.dataMap.get("catmaid") 1>><<set $chars.dataMap.get("you").fer.rate = 0>><<set $chars.dataMap.get("catmaid").fer.rate = 0>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "narr", "en": `<div class="narration"><p>The rift flares, starlight surging, coalescing into a glowing cocoon that splits. A catgirl steps forth, tail flicking, her grace mirroring Stellafall’s. She nods to you, lips curving faintly, as if in salute.</p></div><div class="monologue"><p><em>Holy crap! It worked?! This catgirl... straight outta Stellafall’s playbook! Leah wasn’t kidding, but why’s my heart racing?</em></p><p><em>Chill, play it cool!</em></p></div>` },{ "id": "catmaid", "en": `<div class="narration"><p>Stellafall’s gold eyes widen, tail shooting up like a flag, voice a mix of shock and glee.</p></div><p>Lord! Is this... my sister?!</p><div class="narration"><p>She hops beside the new catgirl, orange hair bouncing.</p></div><p>Hey, how’d you get here? Spill!</p><div class="narration"><p>She turns to you, winking, tone teasing.</p></div><p>Lord, you nailed this! But... whew, I’m kinda beat. Fish—need fish to recharge!</p>` },{ "id": "you", "en": `<div class="narration"><p>You scratch your head, grinning dumbly, forcing calm.</p></div><div class="monologue"><p><em>Sister? Hah, her imagination! This catgirl looks legit, though—total badass vibe. Stellafall’s wiped? Ritual’s gotta cost something... Don’t let her dwell, hype her up!</em></p></div>` },{ "id": "you", "en": `<p>Beat? Heroes need breaks!</p><div class="narration"><p>You pat her shoulder, flashing a grin.</p></div><p>Stellafall, you’re the MVP! This, uh, sister? All ’cause you lit the rift! She’s gonna help us crush demons. Fish? Full spread tonight!</p>` },{ "id": "you", "en": `<div class="narration"><p>You glance down—the ceramic angel figurine rests in your palm, cold and quivering, its gold light threading into the rift’s star-sea. Stellafall and the new catgirl are chatting, oblivious, as a whisper echoes in your mind.</p></div><div class="monologue"><p><em>Leah! Your doll trick’s gonna kill me one day! What’s she on about? Souls? Don’t tell me we’re doing this again—heart, stay calm!</em></p></div>` },{ "id": "leah", "en": `<div class="narration"><p>Leah’s voice echoes ethereal, weary yet approving.</p></div><p>Riftbearer, the seed of life has sprouted...</p><div class="narration"><p>Her tone deepens, a warning edge.</p></div><p>The catgirl’s soul burns like starfire, yet your strength falters, summoning but one. To tear the rift wider, call greater guardians, you must hone your will, kindle your blood.</p>` },{ "id": "you", "en": `<div class="narration"><p>You grip the figurine, muttering under breath, voice shaky.</p></div><div class="monologue"><p><em>Hone what? Blood?! Leah, quit freaking me out! This catgirl’s plenty badass—good enough! Just don’t burn Stellafall out.</em></p></div><div class="narration"><p>You look up, calling to Stellafall.</p></div><p>Stellafall, take a breather! Let’s hit the hall—Hawkridge Vale’s got a ton of crap to sort!</p> <<SetFlag 'catmaidflag' 3>><<run $chars.dataMap.get("catmaid").calcAttrInfo("bond.vl", 1)>><<SetFlag 'enbedroom' 2>>` } ] <</script>>
<<script>> setup.speechData = [ { "id": "catmaid", "en": `<div class="narration"><p>In the bedroom, the energy infuser hums softly, purple arcane light glinting off Stellafall’s twitching cat ears. She stands by the bed, tail flicking excitedly, cat-like eyes sparkling with curiosity and anticipation, her maid’s gown slightly undone, revealing her flat abdomen.</p></div><p>Stellafall rubs her hands, voice brimming with excitement.</p><p>My Lord! I wanna try that... belly-growing magic too! Mirael said it felt like flying, and I gotta feel that!</p>` },{ "id": "you", "en": `<div class="narration"><p>You raise an eyebrow, lips curling with a hint of teasing.</p></div><p>Haha, Stellafall, your curiosity’s more intense than a cat’s! Alright, ready to feel that ’floating’ sensation?</p>` },{ "id": "leah", "en": `<div class="narration"><p>The ceramic angel figurine glows faintly, Leah’s voice soft yet authoritative.</p></div><p>Stellafall’s constitution is suitable for the infusion ritual. Her agility and spirit will imbue the offspring with unique talents. The ritual begins—I shall guide the energy, ensuring safety.</p>` },{ "id": "catmaid", "img": "summon/catmaid_you/pp0526023204.webp", "en": `<div class="narration"><p>Purple light enveloping her petite frame, Stellafall's tail flicking faster. The infuser’s hum intensifies, her abdomen slowly swelling. She widens her cat-eyes, touching her belly, letting out a surprised meow.</p></div><p>Whoa! It’s really growing! So magical! My Lord, it feels... like a warm magic orb in my tummy!</p>` },{ "id": "you", "en": `<div class="narration"><p>You can’t help but laugh, tone encouraging.</p></div><p>Haha, Stellafall, you’re too adorable! Feeling okay? </p><div class="monologue"><p><em>This catgirl’s enthusiasm is unstoppable...</em></p></div>` },{ "id": "leah", "en": `<p>The ritual proceeds smoothly. Stellafall’s gestation is accelerated—when the time is right, send her to the Planar Rift to summon a new ally. Prepare to welcome another gifted comrade!</p>` },{ "id": "narr", "en": `<div class="narration"><p>Stellafall clutches her belly, her cat tail spinning like a propeller, face brimming with excitement and anticipation. You muse that this ritual not only boosts combat strength but also lifts the team’s morale.</p></div><<SetFlag 'catmaidFlag' 6 >><<run $chars.dataMap.get("catmaid").calcAttrInfo("bond.vl", 1)>>` } ] <</script>>
<<set $Flags = { ...$Flags, sizemax: true, enbedroom: false, endispatch: false, enstorage: false, enbuild: false, enstudy: false, enbastion: false, entrain: false, miraelflag: false, catmaidflag: false, leahflag: false, butleflag: false, captainflag: false, griffinknightflag: false, scalemaidflag: false, foxmaidflag: false } >> <<set $cards = [] >> <<set $skillbag = { fireRateBoost: 3, gateHeal: 1, freeze: 1 }>> <<newinv $storage "storehouse">> <<newinv $blackmarket "blackmarket">> <<newinv $battlebag "battle">> <<pickup $storage "wood" 10 "food" 80 "gold" 2000>> <<pickup $storage "staPotion-5" 5 "staPotion-1" 5>> <<set $times.currentWeather = setup.TimeSystem.getRandomWeather() >> <<run changeEffects(0, 1, "bastion")>> <<run changeEffects(0, 1, "bastion", 1, "gate_defense")>> <<run changeEffects(0, 1, "bastion", 3, "tower_arrow")>> <<run changeEffects(0, 1, "warren")>> <<set $tech.enTraits = ["Valuable","Cheap"]>>
<<widget InitGame>> <<run Engine.restart();>> <</widget>> <<widget "ObjectList">> <<for _key, _value range _args[0]>> <span class="item-name"> | <<= _key>>: </span><span class="item-value"><<= _value>></span> <</for>> <</widget>> <<widget "ToggleTags">> <<set _cssclass = "." + _args[0]>> <<set _cssid = "#" + _args[1]>> <<removeclass _cssclass 'active'>> <<addclass _cssid 'active'>> <</widget>> <<widget SkipTutorial>> <<run Msg.add('notice', '<div style="padding: 2rem;border: 1px solid #ccc;border-radius: 0.5rem;"><p><strong>You have entered Free Mode:</strong></p><ol><li>Skip tutorial instructions.</li><li>Unlock restricted buildings.</li><li>Skip story and quests.</li><li>Timeline advances to 4 weeks after the traversal.</li></ol><p>If this is your first time playing, we recommend starting with Standard Mode for the full experience.</p><p style="font-style: italic; color: #666;">(This feature is still in testing. If you encounter any bugs, please report them to us. Thank you for playing!)</p></div>')>> <<set $gamestatus.stage = 3 >> <<set $domain.maps[1] = "MapVale02" >> <<pickup $storage "wood" 20 "food" 232 "gold" 2000>> <<set $battle.demonattackdeadline += 29 >> <<for _i range 29>> <<DailyUpdate>> <<run setup.TimeSystem.passOneDay();>> <</for>> <<NextDayEvent>> <<run newMarket()>> <<run $chars.dataMap.get('you').img = "characters/you/you02.png">> <<run $chars.dataMap.get('promaid').img = "characters/promaid/promaid02.webp">> <<run $chars.dataMap.get('captain').img = "characters/captain/captain02.webp">> <<run $chars.dataMap.get('butler').img = "characters/butler/butler02.png">> <<run $chars.dataMap.get('archmaid').img = "characters/archmaid/archmaid02.webp">> <<set $Flags = { ...$Flags, skiptutor: true, tutorial: 999, endintro: false, firstbirth: true, enbedroom: 2, endispatch: 2, enstorage: 2, enbuild: 2, enstudy: 2, enbastion: 2, entrain: 2, eninfuser: 2, enmeeting: 2, foxmaidflag: 9, scalemaidflag: 9, leahflag: 9, catmaidflag: 9, miraelflag: 9 } >> <<run $tech.enTrees.pushUnique("evolution");>> <<run $tech.enTrees.pushUnique("goddess");>> <<pickup $storage "Basic_Infuser_Core" 1>> <<run EventBus.emit("tech:requestUnlock", "infuser1");>> <<run changeEffects(0, 1, "warren")>> <<run changeEffects(0, 1, "hawkridge_hamlet")>> <<run changeEffects(0, 1, "verdant_wilds")>> <<run changeEffects(0, 1, "quarry_mines")>> <<run changeEffects(0, 1, "bountiful_fields")>> <<AddMonster $chars.dataMap.get("catmaid")>> <<run $training.bedroomGirls.pushUnique("catmaid")>> <<run $chars.dataMap.get("catmaid").calcAttrInfo("bond.vl", 2, false)>> <<AddMonster $chars.dataMap.get("mirael")>> <<= $manage.ownrace.set("succubus", 2) >> <<run $chars.dataMap.get("mirael").calcAttrInfo("bond.vl", 2, false)>> <<AddMonster $chars.dataMap.get("scalemaid")>> <<run $chars.dataMap.get("scalemaid").img = "characters/scalemaid/scalemaid02.png">> <<AddMonster $chars.dataMap.get("foxmaid")>> <<JournalActive "main_ritual">> <<JournalActive "main_rebuild">> <</widget>> <<widget "SimpleUI">> <<if _args[1] == 90>> <<run Dialog.create("", "simplepop w90");>> <<run Dialog.wikiPassage(_args[0])>> <<run Dialog.open();>> <<elseif _args[1] == 100>> <<run Dialog.create("", "simplepop w100");>> <<run Dialog.wikiPassage(_args[0])>> <<run Dialog.open();>> <<elseif _args[1] == "fit">> <<run Dialog.create("", "simplepop fit");>> <<run Dialog.wikiPassage(_args[0])>> <<run Dialog.open();>> <<elseif _args[1] == "fullpop">> <<run Dialog.create("", "fullpop");>> <<run Dialog.wikiPassage(_args[0])>> <<run Dialog.open();>> <<else>> <<run Dialog.create("", "simplepop");>> <<run Dialog.wikiPassage(_args[0])>> <<run Dialog.open();>> <</if>> <</widget>> <<widget SimpleWiki>> <<run Dialog.create(_args[2], _args[1])>> <<run Dialog.wiki(_args[0])>> <<run Dialog.open();>> <</widget>> <<widget "ToHome">> <<run Dialog.create("to main menu","");>> <<run Dialog.wiki('Return to Main? <br>Unsaved progress will be lost.<br><div class="buttons-group-row"><<button " OK ">><<run Engine.restart();>><</button>><<button " No ">><<run Dialog.close();>><</button>></div>')>> <<run Dialog.open();>> <</widget>> <<widget "SetFlag">> <<set _Fnam = $args[0].toLowerCase()>> <<if ndef $Flags>> <<set $Flags = {}>> <</if>> <<if def $args[1]>> <<if $args[1] == false>> <<if def $Flags[_Fnam]>> <<run delete $Flags[_Fnam]>> <</if>> <<else>> <<set $Flags[_Fnam] = $args[1]>> <</if>> <<else>> <<set $Flags[_Fnam] = true>> <</if>> <</widget>> <<widget "csslink" container>> <<set _xtt = `_args[0]` >> <<set _btt = `<div class="csslink" @id="_args[1]">_xtt</div>` >> <<link `_btt`>>_contents<</link>> <</widget>> <<widget "cssbutton" container>> <<set _xtt = `_args[0]` >> <<set _btt = `<div class="cssbutton" @id="_args[1]">_xtt</div>` >> <<button `_btt`>>_contents<</button>> <</widget>> <<widget DisplayToggle>> <<set _key to setup.newID()>> <<set _keyid to "#"+_key>> <<set _onText to _args[1] ?? "ON">> <<do tag _key>> <<set _toggleclass = "toggleButton " + (_args[0] ? 'active' : '')>> <<link '<div @class="_toggleclass" @id="_key"><<= _args[0] ? _onText : "">></div>'>> <<toggleclass _keyid "active">> <<set _args[0] = _args[0] ? false : true >> <<redo _key>> <</link>> <</do>> <</widget>> <<widget ToggleSetting>> <<set _key to setup.newID()>> <<set _keyid to "#"+_key>> <<set _onText to _args[1] ?? "ON">> <<do tag _key>> <<set _oldvalue = Setting.getValue(_args[0]) >> <<set _toggleclass = "toggleButton " + (_oldvalue ? 'active' : '')>> <<link '<div @class="_toggleclass" @id="_key"><<= _oldvalue ? _onText : "">></div>'>> <<toggleclass _keyid "active">> <<set _newvalue = _oldvalue ? false : true >> <<run Setting.setValue(_args[0], _newvalue)>> <<redo _key>> <</link>> <</do>> <</widget>> <<widget "CapList">> <<set _count = countRaceGender($chars.tempList) >> Capacity: <div>Population: <<= $domain.getTotalPop()>> / <<= $buildingEffects.capacities.resident || 0>></div> <<for _i, _j range $manage.ownrace>> <div><span class="text-capitalize">_i</span>: <<= _count[_i]?.count || 0>> / <<= $buildingEffects.capacities[_i] || 0>></div> <</for>> <</widget>> <<widget "TutorList">> <<done>> <<run new Tutorial(_args[0]);>> <</done>> <</widget>>
<style> .flow-container { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 2rem; padding: 1rem; background: rgba(0,0,0,0.3); border-radius: 0.25rem; border: 1px solid var(--bg-surface); } .flow-node { display: flex; flex-direction: column; align-items: center; position: relative; padding: 0.5rem; min-width: 4rem; } .flow-icon { width: 2.5rem; height: 2.5rem; border: 1px solid var(--color-c); background: var(--bg-body); color: var(--color-e); display: flex; align-items: center; justify-content: center; transform: rotate(45deg); margin-bottom: 0.75rem; font-size: 0.9rem; transition: all 0.3s; } .flow-icon span { transform: rotate(-45deg); } .flow-node.active .flow-icon { border-color: var(--bg-deepred); color: var(--bg-deepred); box-shadow: 0 0 10px rgba(140, 26, 18, 0.2); } .flow-text { font-size: 0.7rem; text-transform: uppercase; color: var(--text-primary); letter-spacing: 1px; } .flow-arrow { padding-bottom: 2rem; color: var(--bg-surface); font-size: 1.2rem; } .gothic-card { line-height: 1.4; padding: 1rem; background: rgba(20, 10, 10, 0.6); border-radius: 0.25rem; border: 1px solid var(--bg-surface); border-left: 3px solid var(--bg-deepred); transition: all 0.3s ease; height: 100%; display: flex; flex-direction: column; } .gothic-card:hover { background: rgba(61, 43, 36, 0.4); box-shadow: 0 4px 12px rgba(0,0,0,0.5); border-color: var(--bg-deepred); } .card-title { color: var(--tr-SS); position: relative; font-size: 1rem; margin-bottom: 0.5rem; display: flex; justify-content: space-between; } .card-tag { font-size: 0.6rem; padding: 0.1rem 0.4rem; border: 1px solid var(--cm-border); color: var(--color-e); border-radius: 2px; } .card-desc { font-size: 0.8rem; color: #999; margin-bottom: 1rem; flex-grow: 1; font-style: italic; } .stat-bar-mini { height: 0.4rem; background: #111; border: 1px solid #333; margin-top: 0.5rem; position: relative; } .stat-fill { height: 100%; background: var(--bg-deepred); width: 0%; } .value-big { font-size: 0.75rem; color: var(--color-e); text-align: right; line-height: 1; } .source-row { margin-top: auto; padding-top: 0.75rem; border-top: 1px dashed rgba(255,255,255,0.1); display: flex; gap: 0.75rem; font-size: 0.7rem; color: #777; } .source-row span i { color: var(--bg-deepred); margin-right: 0.25rem; font-style: normal; } @media (max-width: 600px) { .flow-arrow { transform: rotate(90deg); margin: 0.25rem 0; } .module-bigtitle { font-size: 1.5rem; } } .module-bigtitle::before { left: 10%; } .module-bigtitle::after { right: 10%; } .module-ul { color: var(--text-primary); } </style> <div style="max-width: 60rem; margin: 0 auto;"> <div class="module-bigtitle">Genetics Mechanics</div> <div class="flow-container"> <div class="flow-node active"> <div class="flow-icon"><span>XP</span></div> <div class="flow-text">Bodypart<br>Mastery</div> </div> <div class="flow-arrow">➜</div> <div class="flow-node"> <div class="flow-icon"><span>Mod</span></div> <div class="flow-text">Genetics<br>Potential</div> </div> <div class="flow-arrow">➜</div> <div class="flow-node"> <div class="flow-icon"><span>ΣPot</span></div> <div class="flow-text">Total<br>Potential</div> </div> <div class="flow-arrow">➜</div> <div class="flow-node active"> <div class="flow-icon"><span>Tier</span></div> <div class="flow-text">Stat<br>Tier</div> </div> <div class="flow-arrow">➜</div> <div class="flow-node"> <div class="flow-icon"><span>EXP</span></div> <div class="flow-text">Stat<br>Experience</div> </div> <div class="flow-arrow">➜</div> <div class="flow-node"> <div class="flow-icon"><span>Stat</span></div> <div class="flow-text">Stat<br>Value</div> </div> </div> <div class="module-border" style="margin-bottom: 1.5rem;"> <div class="module-header"> <span>I. The Origin (Genetic Evolution)</span> <span style="font-size: 0.7rem; color: #666;">INTERNAL</span> </div> <div class="grid-3col"> <div class="gothic-card"> <div class="card-title"> Bodyparts Mastery <span class="card-tag">XP</span> </div> <div class="card-desc"> Accumulated experience for specific body parts. Caps result in permanent genetic evolution. </div> <div> <div class="flex-row-sb1" style="font-size: 0.75rem; color: #aaa;"> <span>Progress</span> <span>60%</span> </div> <div class="stat-bar-mini"> <div class="stat-fill" style="width: 60%;"></div> </div> </div> <div class="source-row"> <span><i>💟</i>Sex Times</span> <span><i>🗝️</i>Training</span> <span><i>💋</i>Sexual Job</span> </div> </div> <div class="gothic-card"> <div class="card-title"> Bodyparts Genetic Potential <span class="card-tag">Mod</span> </div> <div class="card-desc"> Innate biological limit. Increases only when Bodyparts Mastery reaches 100%.<br> Bodyparts Genetic Base X Modifiers(Innate differences + Mastery) =Bodyparts Genetic Stat.(Can be inherited with mutation) </div> <div class="flex-row-sb1" style="align-items: center; margin-top: 1rem;"> <span style="font-size: 0.8rem; color: #777;">Current Value</span> <div class="value-big">STR +9, STA +3, DEX -2</div> </div> </div> <div class="gothic-card"> <div class="card-title"> Total Synthesis <span class="card-tag">SUM</span> </div> <div class="card-desc"> Aggregate score of all body parts. Dictates the entity's Gene Tier classification. </div> <div class="flex-row-sb1" style="align-items: center; margin-top: 1rem;"> <span style="font-size: 0.8rem; color: #777;">Aggregate</span> <div class="value-big">STR:75, STA:50, CHA:20, FER:35 ...</div> </div> </div> </div> </div> <div class="module-border"> <div class="module-header"> <span>II. The Manifestation (Stat Upgrade)</span> <span style="font-size: 0.7rem; color: #666;">EXTERNAL</span> </div> <div class="grid-3col"> <div class="gothic-card" style="border-color: var(--color-c);"> <div class="card-title"> Stat Tier <span class="card-tag" style="border-color: var(--bg-deepred); color: var(--bg-deepred);">CORE</span> </div> <div class="card-desc"> Upgrade stage. Higher tiers amplify XP gain from all sources.<br> Tier SS has an experience multiplier of 800%, while F has 100%. </div> <div class="flex-row-sb1" style="align-items: center; margin-top: 1rem;"> <span style="font-size: 0.8rem; color: #777;">STR Genetic Sum: 75 -> </span> <div class="item-value S">STR Tier: SS</div> </div> </div> <div class="gothic-card"> <div class="card-title"> Stat Experience <span class="card-tag">EXP</span> </div> <div class="card-desc"> Stat Experience gained via Battle, Labor and Rituals. Fills to raise Stat Value. </div> <div> <div class="flex-row-sb1" style="font-size: 0.75rem; color: #aaa;"> <span>To Next</span> <span>60%</span> </div> <div class="stat-bar-mini"> <div class="stat-fill" style="width: 60%; background: var(--color-c);"></div> </div> </div> <div class="source-row"> <span><i>🔮</i>Summon</span> <span><i>⚔️</i>Battle</span> <span><i>⚒️</i>Larbor Job</span> </div> </div> <div class="gothic-card" style="border-left-color: var(--color-e);"> <div class="card-title"> Stat Value <span class="card-tag" style="color: var(--color-e); border-color: var(--color-e);">Stat</span> </div> <div class="card-desc"> Visible power value. Determines Deployment eligibility and Resource Yield.<br> Individual stats, not inherited. </div> <div class="flex-row-sb1" style="align-items: center; margin-top: 1rem;"> <span style="font-size: 0.8rem; color: #777;">STR Stat</span> <div class="item-value B">15</div> </div> </div> </div> <div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid #222;"> <ul class="module-ul"> <li><span style="color: var(--text-primary);">Deployment Req:</span> Minimum level required to dispatch unit on missions.</li> <li><span style="color: var(--text-primary);">Resource Yield:</span> Production output multiplier for base job.</li> <li><span style="color: var(--text-primary);">Harvest Yield:</span> The yield of harvest is related to the Stats.</li> <li>Tier determine the growth potential (EXP gain multiplier) for corresponding Stat.</li> <li>A monster girl's own genes, individual differences, and training result modifiers can all be inherited with some degree of mutation.</li> <li>A freshly summoned powerful monster girl will have high gene value but low base stats—yet she'll level up extremely quickly.</li> </ul> </div> </div> </div>
<style> .character-card-container { display: flex; flex-wrap: wrap; gap: 1rem; padding: 1rem; overflow-y: auto; height: 100%; } .character-card { width: 13rem; height: 23rem; border: 3px double #8b4513; padding: 0rem; color: #e0d3af; position: relative; } .character-card:hover { box-shadow: 0 10px 20px rgba(0, 0, 0, 0.7), inset 0 0 12px rgba(184, 135, 54, 0.5); border-color: #9e8561; } .character-card::before { content: ''; position: absolute; top: -6px; left: -6px; width: 20px; height: 20px; background-size: contain; } .character-card::after { content: ''; position: absolute; bottom: -6px; right: -6px; width: 20px; height: 20px; background-size: contain; transform: rotate(180deg); } .character-image { width: 100%; height: 100%; object-fit: cover; position: relative; } .character-overlay { position: absolute; bottom: 0; left: 0; width: 100%; padding: 0.5rem; color: var(--text-primary); text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8), 0 0 4px rgba(184, 135, 54, 0.4); } .character-name { line-height: 1; font-size: 1.1rem; font-weight: bold; margin-bottom: 0rem; } .character-role { font-size: 0.8rem; } .character-info { background: deeppink; font-size: 0.8rem; text-align: left; margin: 0.3rem 0; line-height: 1.5; } .character-card .relation-bar { position: relative; height: 0.5rem; background: #1a1a1a; margin: 0rem 0; border: 1px solid #4a4130; } .character-card .relation-fill { height: 100%; background: linear-gradient(270deg, #cb007a 0%, #d4af37 50%, #004d91 100%); transition: width 0.5s ease; } .buttons-group-row button { width: -webkit-fill-available; } </style> <<run AudioBus.playPlaylist('bgm_girl');>> <<set $chars.selected = null >> <<do tag "pregnancylist">> <div class="character-card-container"> <<for _charid range $training.bedroomGirls>> <<set _char = $chars.dataMap.get(_charid)>> <<capture _char>> <div class="character-card"> <img class="character-image" @src="setup.ImagePath + _char.img" alt="_char.name"> <div class="character-overlay"> <div class="character-name">_char.name</div> <div class="character-role">❤️<<= _char.currentAPFloor>></div> <<if _char.preg > 0>> <div class="character-info"> <span>Pregnancy: <<= _char.preg>>days</span><span> Childbirth: <<= Math.clamp(270 - _char.preg, 1, 270)>>days</span> </div> <<else>> <</if>> <div class="buttons-group-row"> <<button "Status">> <<set $chars.selected = _char >> <<SimpleUI "Girl_Info" 90>> <</button>> <<HoverTip "-500⚡Energy, -1❤️AP(target), 100% chance of 🤰pregnancy, or accelerated pregnancy by <<= $manage.preg.infuserDays>> days.<br>You can increase infusion days on the Building Upgrade interface.">> <<button "Infuser">> <<if $manage.energy.current >=500 && _char.currentAPFloor >= 1>> <<GetPregnant _char.id>> <<if Flag('miraelflag') == 5>> <<run Msg.add('eventcard', 'Event_Mirael_pregnancy', 3)>> <<elseif Flag('catmaidflag') == 5 && _char.id == "catmaid">> <<run Msg.add('eventcard', 'Event_Stellafall_pregnancy', 3)>> <<else>> <</if>> <<set $manage.energy.current -= 500 >> <<run _char.useAP() >> <<run $manage.preg.infuserCount += 1>> <<run setup.pairOnce(_char, $chars.dataMap.get("you"), false)>> <<else>> <<run UI.alert("This action requires 500 energy and 1 AP from the target.")>> <</if>> <<redo "pregnancylist topbar">> <</button>><</HoverTip>> <<if Flag('entrain') == 2>> <<button "Train">> <<set $training.trainID.to = _char.id>><<goto 'Train_UI'>><<run Dialog.close();>> <<redo "pregnancylist">> <</button>> <</if>> <<if _char.preg > 0>> <<button `langBank('feed')`>> <<SetFlag 'enblefeed'>> <<set $manage.feed.feedCp.charID = _char.id>> <<SimpleUI "Storage">> <</button>> <</if>> </div> </div> </div> <</capture>> <</for>> </div> <</do>>
<style> .fullInfo { display: flex; } div[data-do-tags="selected shoplist"] .monsterinfoleftpic { margin-left: 18rem; } </style> <div class="fullInfo"> <<Infolist $chars.selected>> <<ShowPortrait $chars.selected>> <<set _withGirl = $chars.selected || $chars.dataMap.get("catmaid") >> <<TrainDataBox>> </div>
<<widget PregnancyUpdate>> <<for _pregnancyID range $training.pregs>> <<set _pregnancy = $chars.dataMap.get(_pregnancyID)>> <<run _pregnancy.preg += 1;>> <<if _pregnancy.preg >= 270>> <<if !Flag('firstbirth')>> <<run Msg.add('eventcard', 'Event_Mirael_Birth', 2)>> <<run Msg.add('eventcard', 'Event_Mirael_Birth2', 3)>> <<else>> <<set _popupcon = '<<GetBirth ' + _pregnancyID + ' >>' >> <<run Msg.add('wiki', _popupcon, 3)>> <</if>> <</if>> <<setPregCG _pregnancyID>> <</for>> <</widget>> <<widget GetBirth>> <<set _getbirth = $chars.dataMap.get(_args[0])>> <<if _getbirth.preg>> <<BirthMonsters _getbirth $chars.dataMap.get("you")>> <<set _getbirth.preg = 0 >> <<run $training.pregs.deleteAll(_args[0])>> <<setPregCG _args[0]>> <<else>> <</if>> <</widget>> <<widget GetPregnant>> <<set _getpregnancy = $chars.dataMap.get(_args[0])>> <<if _getpregnancy.preg>> <<run Dialog.create();>> <<run Dialog.wiki('[img[setup.getInteractionCGPath(_args[0], "preg")]]<br>Blessed by the divine power of the Fertility Goddess and the mystic infusion of the Energy Infuser. The Infuser has hastened her pregnancy! <span> <<button "OK">><<run Dialog.close();>><</button>></span>')>> <<run Dialog.open();>> <<set _getpregnancy.preg += $manage.preg.infuserDays>> <<setPregCG _args[0]>> <<else>> <<run Dialog.create();>> <<run Dialog.wiki('[img[setup.getInteractionCGPath(_args[0], "bed")]]<br>Blessed by the divine power of the Fertility Goddess and the mystic infusion of the Energy Infuser, your efforts have borne fruit, and she is now with child. The Infuser has hastened her pregnancy! <span> <<button "OK">><<run Dialog.close();>><</button>></span>')>> <<run Dialog.open();>> <<run $training.pregs.pushUnique(_args[0])>> <<set _getpregnancy.preg = $manage.preg.infuserDays>> <<setPregCG _args[0]>> <</if>> <<run _getpregnancy.gainxp($manage.preg.infuserDays) >> <</widget>> <<widget BirthMonsters>> <<script>> $(document).one(':dialogclosing', function (ev) { }); <</script>> <<set $cards = [] >> <<set _cardnum = $manage.cardSelect >> <<for _i range _cardnum>> <<Get5 _args[0] _args[1] "Birth">> <</for>> <<run Dialog.create("cards", "fullpop");>> <<run Dialog.wiki("<<CardShow _args[0] _args[1]>>") >> <<run Dialog.open()>> <<notify 2s>>[<span><<= _args[0].name>></span>] gave birth<</notify>> <</widget>>
<<widget AttrShow>> <div class="core-attr-box"> <span @class="'core-attr-talent module-class-color ' + _args[0].getRank(_args[1])"><<= _args[0].getRank(_args[1])>></span> <div class="core-attr-progress"> <progress class="core-attr-bar" @max='_args[0].upgradeXp(_args[1])' @value='_args[0].currentExp(_args[1])'></progress> <div class="core-attr-content"> <span class="core-attr-name"><<= _args[1]>></span> <span class="core-attr-level"><<= _args[0][_args[1]].lv>></span> </div> </div> </div> <</widget>> <<widget RadarImageByChar>> <div class="radar-top-section" @id="'radar-' + _args[0].id"> <div class="radar-leftbox"> <div class="radar-container"> <svg viewBox="0 0 100 100"> <defs> <linearGradient id="orangeGradient" x1="0%" y1="0%" x2="100%" y2="100%"> <stop offset="0%" style="stop-color:#ffa500;stop-opacity:1" /> <stop offset="100%" style="stop-color:#cc7000;stop-opacity:1" /> </linearGradient> </defs> <polygon class="radar-background" points="50,0 93.3,25 93.3,75 50,100 6.7,75 6.7,25" /> <line x1="50" y1="50" x2="50" y2="0" /> <line x1="50" y1="50" x2="93.3" y2="25" /> <line x1="50" y1="50" x2="93.3" y2="75" /> <line x1="50" y1="50" x2="50" y2="100" /> <line x1="50" y1="50" x2="6.7" y2="75" /> <line x1="50" y1="50" x2="6.7" y2="25" /> <polygon id="data-polygon" class="radar-data" /> </svg> <div class="radar-labels"> <div class="radar-label radar-label-strength">STR</div> <div class="radar-label radar-label-dexterity">DEX</div> <div class="radar-label radar-label-fertility">FER</div> <div class="radar-label radar-label-charisma">CHA</div> <div class="radar-label radar-label-willpower">WIL</div> <div class="radar-label radar-label-stamina">STA</div> </div> <div class="radar-mode-indicator"></div> </div> </div> </div> <<done>> <<script TwineScript>> const args0 = _args[0]; const data = { "stats": { "strength": args0.getRank("str"), "dexterity": args0.getRank("dex"), "fertility": args0.getRank("fer"), "charisma": args0.getRank("cha"), "willpower": args0.getRank("wil"), "stamina": args0.getRank("sta") }, "lv": { "strength": {"base": args0["str"].lv, "buff": args0.getStatFinal("str")}, "dexterity": {"base": args0["dex"].lv, "buff": args0.getStatFinal("dex")}, "fertility": {"base": args0["fer"].lv, "buff": args0.getStatFinal("fer")}, "charisma": {"base": args0["cha"].lv, "buff": args0.getStatFinal("cha")}, "willpower": {"base": args0["wil"].lv, "buff": args0.getStatFinal("wil")}, "stamina": {"base": args0["sta"].lv, "buff": args0.getStatFinal("sta")} } }; const PI = 3.1416; const normalLevelMap = { 'None': 0, 'N': 0, 'F': 0.14, 'E': 0.28, 'D': 0.42, 'C': 0.56, 'B': 0.70, 'A': 0.84, 'S': 1, 'SS': 1.1, 'SSS': 1.1, 'UR': 1.1, 'GOD': 1.1 }; const highLevelMap = { 'None': 0, 'N': 0, 'F': 0.1, 'E': 0.15, 'D': 0.2, 'C': 0.25, 'B': 0.3, 'A': 0.35, 'S': 0.4, 'SS': 0.6, 'SSS': 0.8, 'UR': 1.0, 'GOD': 1.1 }; const stats = [ { name: 'strength', angle: -PI / 2 }, { name: 'dexterity', angle: PI / 3 - PI / 2 }, { name: 'fertility', angle: 2 * PI / 3 - PI / 2 }, { name: 'charisma', angle: PI - PI / 2 }, { name: 'willpower', angle: 4 * PI / 3 - PI / 2 }, { name: 'stamina', angle: 5 * PI / 3 - PI / 2 } ]; const radius = 50, centerX = 50, centerY = 50; const highLevelCount = Object.values(data.stats).filter(level => ['SS', 'SSS', 'UR', 'GOD'].includes(level)).length; const isHighLevel = highLevelCount >= 2; const levelMap = isHighLevel ? highLevelMap : normalLevelMap; const radarCont = document.getElementById(`radar-${args0.id}`); const radar = radarCont.querySelector('.radar-container'); radar.style.setProperty('--background-color', isHighLevel ? '#ffd700' : '#d3d3d3'); radar.style.setProperty('--stat-fill-color', isHighLevel ? 'url(#orangeGradient)' : 'rgba(0, 128, 255, 0.3)'); radar.style.setProperty('--stat-stroke-color', isHighLevel ? '#cc7000' : '#0052cc'); const points = stats.map(stat => { const level = levelMap[data.stats[stat.name]] || 0; const x = centerX + radius * Math.cos(stat.angle) * level; const y = centerY + radius * Math.sin(stat.angle) * level; return `${x.toFixed(2)},${y.toFixed(2)}`; }); radarCont.querySelector('#data-polygon').setAttribute('points', points.join(' ')); radarCont.querySelector('.radar-mode-indicator').textContent = isHighLevel ? 'Advanced' : 'Basic'; <</script>> <</done>> <</widget>>
<<widget "TechDesc">> <div id="confirmBox"> <h3></h3>_args[0].name <<if $tech.techNodes.includes(_args[0].id)>> <p>Unlocked:</p> <<else>> <p>Spend <span id="confirmCost">_args[0].cost _args[0].track points</span> </p> <<if _args[0].keyItems>><p><span id="confirmCost"><<ObjectList _args[0].keyItems>></span> </p><</if>> <p>to unlock:</p> <</if>> <p id="confirmName" style="font-size: 1rem;color: #4191c1;">_args[0].bonusDesc</p> </div> <</widget>> <<widget "NumToStars">> <div class="talent-stars"> <<set _atttostars = _args[0] >> <<set _atttostarsCSS = Math.ceil(_atttostars / 2) >> <<for _i range _atttostarsCSS>> <<if _atttostars >= (_i + 1) * 2>> <<set _addstar = "talent-star active">> <<else>> <<set _addstar = "talent-star">> <</if>> <div @class='_addstar'></div> <</for>> </div> <</widget>> <<widget FeedItem>> <<set _feedItemId = _args[1].split('-')[0] >> <<set _feedItemLv = _args[1].split('-')[1] || 1>> <<set _feedMon = getMonsterByID(_args[0])>> <<if _feedMon.effects[_feedItemId]>> <<run popup(L.duplicateWarning);>> <<else>> <<run _feedMon.feedItem(_feedItemId, _feedItemLv)>> <<drop $storage _args[1] 1>> <<set $manage.feed.count += 1 >> <<set $manage.feed.feedCp.itemIDLv = null >> <</if>> <<redo "summon charStatus">> <</widget>> <<widget HarvestMon>> <<set _harvestmon = _args[0] >> <<if _harvestmon.harvest && ($manage.energy.current gte _harvestmon.costEnergy)>> <<set _pdstoreid = _harvestmon.harvest >> <<set _pdstorenum = _harvestmon.stamina.store >> <<set _pdstorelv = Math.clamp(random(1, Math.trunc(_harvestmon.level.lv / 10 + 1)) , 1 , setup.harvestdata[_pdstoreid].upcap) >> <<set _pdstorezuhe = _pdstoreid + "-" + _pdstorelv >> <<set _pdapid = _harvestmon.harvest >> <<set _pdapnum = _harvestmon.currentAPFloor >> <<set _pdaplv = Math.clamp(random(1, Math.trunc(_harvestmon.level.lv / 10 + 1)) , 1 , setup.harvestdata[_pdstoreid].upcap) >> <<set _pdapzuhe = _pdapid + "-" + _pdaplv >> <<if _pdstorenum gt 0 || _pdapnum gt 0>> <<set $manage.energy.current -= _harvestmon.costEnergy >> <<set _harvestmon.stamina.store = 0 >> <<if _pdstorenum gt 0>> <<pickup $storage _pdstorezuhe _pdstorenum >> <</if>> <<set _harvestmon.stamina.currentAP %= 1 >> <<if _pdapnum gt 0>> <<pickup $storage _pdapzuhe _pdapnum >> <</if>> <<set $manage.harvest.count += 1 >> <<notify 2s>><<= L.stored>><</notify>> <<redo "topbar">> <<else>> <<run UI.alert(L.emptyharv)>> <</if>> <<elseif _harvestmon.harvest && ($manage.energy.current lt _harvestmon.costEnergy)>> <<run UI.alert(L.noenergy)>> <<else>> <<run UI.alert(L.noharvest)>> <</if>> <</widget>>
<div class="left-panel"> <<do tag "VerdantWilds">> <div class="building"> <div class="section-title">🌳Verdant Wilds</div> <div class="positions-grid"> <<UnitPosts "berry_grove" 'VerdantWilds'>> <<UnitPosts "lumber_camp" 'VerdantWilds'>> </div> </div> <</do>> <<do tag "VerdantWilds">> <div class="building"> <div class="section-title">⛰️Rockvein Mines</div> <div class="positions-grid"> <<UnitPosts "quarry" 'VerdantWilds'>> <<UnitPosts "iron_mine" 'VerdantWilds'>> </div> </div> <</do>> <<if $buildingEffects.slots.bountiful_fields > 0>> <<do tag "VerdantWilds">> <div class="building"> <div class="section-title">🏞️Bountiful Fields</div> <div class="positions-grid"> <<UnitPosts "farmland" 'VerdantWilds'>> </div> </div> <</do>> <</if>> <<if Flag('entrade') == 2>> <<do tag "tradezone">> <div class="building"> <div class="section-title">💰Trade Zone</div> <div class="positions-grid"> <<UnitPosts "marketstall" 'tradezone'>> <<UnitPosts "tavern" 'tradezone'>> <<UnitPosts "brothel" 'tradezone'>> </div> </div> <</do>> <</if>> <<do tag "total">><div class="joblastpost">Total: <<ObjectList `Job.preview().income`>></div><</do>> </div>
<<widget UnitPosts>> <<for _i, _unit range getUnitRef(_args[0])>> <<capture _i _unit>> <<set _unitdata = setup.bdunitdata[_unit.id] >> <div @class="'position-item '+(_unit.workerId?'filled':'vacant')"> <div class="position-icon"><<= _unitdata.jobs.jobicon>></div> <<set _workername = _unit.workerId ? getMonsterByID(_unit.workerId).name : "➕">> <<button _workername>> <<run Dialog.create("", "SummonCG")>> <<run Dialog.wiki("<<JobCharFilter _unit _args[1]>>")>> <<run Dialog.open()>> <</button>> <div class="position-name"><<do tag _args[1]>><<= _unit.workerId ? Job.jobPencent(getMonsterByID(_unit.workerId), _unit) : 0>><</do>>%</div> </div> <</capture>> <</for>> <</widget>> <<widget JobCharFilter>> <<set _newquest = new Quest({})>> <<set _newquest.reqlevel= 5>> <<set _newquest.hasunique= true>> <<set _filtergather = filterMonsterByCondition($chars.tempList, _newquest).ids>> <<set $chars.selected = null>> <div class="itemfilter-title">Job Filter</div> <div class="itemfilter"> <div class="itemfilter-list"> <<MapList _filtergather>> </div> <div class="itemfilter-info"> <div class="itemfilter-requirement-card"> <h3>Job Requirements</h3> <div class="itemfilter-requirement-content"> Level >= 5; </div> </div> <div> <<do tag "selected">> <<if $chars.selected>> <<Infocard $chars.selected>> <</if>> <</do>> </div> </div> <div class="itemfilter-act"> <<button "Join">> <<if !$chars.selected>> <<run UI.alert(L.selecttarget)>> <<else>> <<run Job.fire(_args[0])>> <<run Job.assign($chars.selected, _args[0])>> <<set $chars.selected = null>> <<redo _args[1]>><<redo "total topbar">> <<run Dialog.close()>> <</if>> <</button>> <<button "Empty">> <<if _args[0].workerId>> <<run Job.fire(_args[0])>> <<redo _args[1]>><<redo "total topbar">> <<run Dialog.close()>> <</if>> <</button>> <<button "Close">> <<run Dialog.close()>> <</button>> </div> </div> <</widget>> <<script>> <</script>>
<style> .quests-container { width: 100%; display: flex; overflow-y: clip; height: 100%; background: #2a2a2a; border: 0.1rem solid #4a4a4a; border-radius: 0.4rem; padding: 1rem; box-shadow: 0 0.2rem 0.6rem rgba(0, 0, 0, 0.5); } .quests-tabs { width: 8rem; display: flex; border-right: 0.1rem solid #4a4a4a; margin-right: 1rem; flex-direction: column; align-items: flex-start; } .quests-tab { width: 100%; padding: 1rem 0; cursor: pointer; font-weight: bold; color: #888; transition: all 0.3s; text-transform: uppercase; font-size: 0.9rem; } .quests-tab.quests-active { color:rgb(255, 255, 255); border-bottom: 0.15rem solid #8b0000; } .quests-tab:hover { background: #3a3a3a; color: #e6e6e6; } .quests-task-list { width: 100%; overflow-y: auto; height: 100%; display: none; flex-direction: column; gap: 0.8rem; } .quests-task-list.quests-active { display: flex; } .quests-task-card { background: #222; border-left: 0.25rem solid; padding: 0.6rem; border-radius: 0.3rem; box-shadow: 0 0.1rem 0.4rem rgba(0, 0, 0, 0.7); transition: transform 0.2s; cursor: pointer; } .quests-task-card:hover { transform: translateY(-2px); } .quests-task-card.quests-mainline-task { border-left: 0.4rem solid #b22222; background: #7f4600; box-shadow: 0 0.2rem 0.6rem rgba(178, 34, 34, 0.5); } .quests-task-card.quests-main-task { border-left-color: #8b0000; background: #2c1e1e; } .quests-task-card.quests-sub-task { border-left-color: #4a4a4a; margin-left: 1rem; } .quests-task-card.quests-completed { border-left-color: #2e4d2e; background: #1e2c1e; } .quests-task-card.quests-failed { border-left-color: #555; background: #2c2c2c; } .quests-task-card.quests-inactive { border-left-color: #333; background: #1c1c1c; opacity: 0.7; } .quests-task-title-row { color: var(--text-primary); display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.4rem; } .quests-task-title { font-size: 1rem; letter-spacing: 0.05rem; } .quests-task-status-btn { display: flex; align-items: center; gap: 0.5rem; } .quests-task-content { display: none; } .quests-task-card.expanded .quests-task-content { display: block; } .quests-task-desc { font-size: 0.8rem; color: #a1a1a1; line-height: 1.4; margin-bottom: 0.4rem; } .quests-task-info { font-size: 0.75rem; color: #b0b0b0; line-height: 1.3; } .quests-task-status { font-size: 0.75rem; padding: 0.25rem 0.5rem; border-radius: 0.2rem; display: inline-block; } .quests-task-status.quests-inactive { background: #333; color: #888; } .quests-task-status.quests-active { background: #8b0000; color: #e6e6e6; } .quests-task-status.quests-completed { background: #0f1e0f; color: #e6e6e6; } .quests-task-status.quests-failed { background: #555; color: #e6e6e6; } .quests-btn { padding: 0.4rem 0.8rem; border: none; border-radius: 0.3rem; cursor: not-allowed; font-size: 0.75rem; transition: background 0.2s; text-transform: uppercase; background: #444; color: #888; } .quests-btn.quests-btn-enabled { background: #2e4d2e; color: #e6e6e6; cursor: pointer; } .quests-btn.quests-btn-enabled:hover { background: #3a693a; } </style> <<script>> const tasks = State.variables.journaldata; function canDisplayTask(task) { if (task.status === 'failed' || task.status === 'active') return true; return task.condition(); } function updateMainTaskStatus(mainTask) { if (mainTask.type !== 'main') return; const allSubTasksCompleted = mainTask.subTasks.every(subTask => subTask.status === 'completed'); } function renderTasks() { $('.quests-task-card').removeClass('expanded'); const currentList = $('#current').empty(); const completedList = $('#completed').empty(); const failedList = $('#failed').empty(); tasks.forEach(task => { if (!canDisplayTask(task)) return; const isMainlineTask = task.type === 'mainline'; const isMainTask = task.type === 'main'; const taskClasses = [ 'quests-task-card', isMainlineTask ? 'quests-mainline-task' : isMainTask ? 'quests-main-task' : 'quests-sub-task', task.status === 'completed' ? 'quests-completed' : '', task.status === 'failed' ? 'quests-failed' : '', task.status === 'inactive' ? 'quests-inactive' : '' ].filter(Boolean).join(' '); const statusText = { inactive: 'Not Started', active: 'In Progress', completed: 'Completed', failed: 'Failed' }[task.status]; const canComplete = task.condition() && task.status === 'active'; const buttonHtml = (task.status !== 'completed' && task.status !== 'failed') ? ` <button class="quests-btn ${canComplete ? 'quests-btn-enabled' : ''}" data-task-id="${task.id}" ${canComplete ? '' : 'disabled'}>Complete</button> ` : ''; const taskHtml = ` <div class="${taskClasses}" data-task-id="${task.id}"> <div class="quests-task-title-row"> <div class="quests-task-title">${task.title}</div> <div class="quests-task-status-btn"> <div class="quests-task-status quests-${task.status}">${statusText}</div> ${buttonHtml} </div> </div> <div class="quests-task-info">Rewards: ${task.rewardDesc}</div> <div class="quests-task-content"> <div class="quests-task-desc">${task.desc}</div> <div class="quests-task-info">Requirements: ${task.conditionDesc}</div> </div> </div> `; if (task.status === 'completed') { completedList.append(taskHtml); } else if (task.status === 'failed') { failedList.append(taskHtml); } else { currentList.append(taskHtml); } if ((isMainTask || isMainlineTask)&& task.subTasks) { task.subTasks.forEach(subTask => { const subTaskClasses = [ 'quests-task-card', 'quests-sub-task', subTask.status === 'completed' ? 'quests-completed' : '', subTask.status === 'failed' ? 'quests-failed' : '', subTask.status === 'inactive' ? 'quests-inactive' : '' ].filter(Boolean).join(' '); const subStatusText = { inactive: 'Not Started', active: 'In Progress', completed: 'Completed', failed: 'Failed' }[subTask.status]; const subCanComplete = subTask.condition() && subTask.status === 'active'; const subButtonHtml = (subTask.status !== 'completed' && subTask.status !== 'failed') ? ` <button class="quests-btn ${subCanComplete ? 'quests-btn-enabled' : ''}" data-task-id="${subTask.id}" ${subCanComplete ? '' : 'disabled'}>Complete</button> ` : ''; const subTaskHtml = ` <div class="${subTaskClasses}" data-task-id="${subTask.id}"> <div class="quests-task-title-row"> <div class="quests-task-title">${subTask.title}</div> <div class="quests-task-status-btn">${subButtonHtml}</div> </div> <div class="quests-task-info">Rewards: ${subTask.rewardDesc}</div> <div class="quests-task-content"> <div class="quests-task-desc">${subTask.desc}</div> <div class="quests-task-desc">${subTask.tips ? subTask.tips : ""}</div> <div class="quests-task-info">Requirements: ${subTask.conditionDesc}</div> </div> </div> `; if (subTask.status === 'completed' && task.status !== 'completed') { currentList.append(subTaskHtml); } else if (subTask.status === 'completed') { completedList.append(subTaskHtml); } else if (subTask.status === 'failed') { failedList.append(subTaskHtml); } else { currentList.append(subTaskHtml); } }); } }); $('.quests-task-card').on('click', function(e) { if ($(e.target).is('.quests-btn') || $(e.target).closest('.quests-btn').length) return; $(this).toggleClass('expanded'); }); bindCompleteButton(); } function bindCompleteButton() { $('.quests-btn.quests-btn-enabled').on('click', function(e) { e.stopPropagation(); const taskId = $(this).data('task-id'); let targetTask = null; let parentTask = null; tasks.forEach(task => { if (task.id === taskId) { targetTask = task; } else if (task.subTasks) { const subTask = task.subTasks.find(st => st.id === taskId); if (subTask) { targetTask = subTask; parentTask = task; } } }); if (!targetTask || !targetTask.condition()) return; targetTask.status = 'completed'; targetTask.reward(); $.wiki("<<redo 'topbar'>>"); if (parentTask) { updateMainTaskStatus(parentTask); } renderTasks(); }); } function simulateTaskUpdate(taskId, newStatus) { let targetTask = null; let parentTask = null; tasks.forEach(task => { if (task.id === taskId) { targetTask = task; } else if (task.subTasks) { const subTask = task.subTasks.find(st => st.id === taskId); if (subTask) { targetTask = subTask; parentTask = task; } } }); if (!targetTask) return; targetTask.status = newStatus; if (parentTask) { updateMainTaskStatus(parentTask); } renderTasks(); } $(document).ready(function() { $('.quests-tab').on('click', function() { $('.quests-tab').removeClass('quests-active'); $(this).addClass('quests-active'); $('.quests-task-list').removeClass('quests-active'); $('#' + $(this).data('tab')).addClass('quests-active'); }); renderTasks(); }); <</script>> <div class="quests-container"> <div class="quests-tabs"> <div class="quests-tab quests-active" data-tab="current">Ongoing</div> <div class="quests-tab" data-tab="completed">Completed</div> <div class="quests-tab" data-tab="failed">Failed</div> </div> <div class="quests-task-list quests-active" id="current"></div> <div class="quests-task-list" id="completed"></div> <div class="quests-task-list" id="failed"></div> </div>
<<set $journaldata = [] >> <<script>> State.variables.journaldata = []; State.variables.journaldata = [ { id: 'mainline_Foothold', type: 'mainline', title: '[MainLine] Quest: Phase One - Establish a Foothold', desc: 'An impregnable fortress requires a robust defense system, resilient warriors, and countless civilians to provide logistical support. Transform Hawkridge Vale into a formidable war stronghold, creating a sanctuary for humanity in a world ravaged by demons.', status: 'active', condition: () => State.variables.domain.getTotalPop() >= 1000, conditionDesc: 'Total Population ≥ 1000', reward: () => {State.variables.storage.pickup("gold", 1000); State.variables.journaldata.find(t => t.id === 'mainline_truth').status = 'active';}, rewardDesc: 'Gold: 1000; Unlock MainLine Quest: Phase Two - Truth' }, { id: 'mainline_truth', type: 'mainline', title: '[MainLine] Quest: Phase Two - Truth in the Mist: Uncover the World’s Secrets', desc: 'I’m yanked by a blood moon and lightning into the Aethel Kingdom, waking up battered as Baron Wilderen. Demons are hunting nobles like me, and Leah, the Fertility Goddess, calls me the "Chosen One" while dodging details. The Lost Temple in Hawkridge Vale might hold secrets about the Wilderen family, the demon invasion, Erin’s past, and Leah’s divine power. I need to unearth the truth behind my crossing, the demons’ motives, and the temple’s ultimate secret, all while dealing with suspicious allies and outside threats. The fate of Hawkridge Barony—and maybe the whole plane—depends on it.', status: 'inactive', condition: () => {}, conditionDesc: '', reward: () => {}, rewardDesc: '', subTasks: [ { id: 'sub_truth-1', title: 'Branch: Hidden Glances: Erin’s True Identity', desc: '<span style="color: orange;font-size: 1.2rem;">Main storyline development has progressed to this point—stay tuned for more updates, and enjoy the game!</span><br>Erin, the apprentice maid, keeps her head down in an oversized outfit, her shifty eyes screaming secrets. Selina, Raymond, and Garvin are oddly protective, refusing to spill her past. I found a noble pendant in the temple dormitory, engraved with the Wilderen crest—Erin’s, no less. I need to dig into her background while dodging everyone’s wariness about me getting too close ("Is she some big shot in disguise?").', status: 'inactive', condition: () => {}, conditionDesc: 'Under development', reward: () => {State.variables.journaldata.find(t => t.id === 'mainline_truth').subTasks.find(st => st.id === 'sub_truth-2').status = 'active';}, rewardDesc: 'Unlock Branch: Call of the Blood Moon: The Mystery of My Crossing;' }, { id: 'sub_truth-2', title: 'Branch: Call of the Blood Moon: The Mystery of My Crossing', desc: 'One brutal overtime night, a blood moon and lightning dumped me into the Aethel Kingdom as Baron Wilderen (worst career switch ever). I need to collect family journals and investigate the blood moon’s origins to figure out why I’m here.', status: 'inactive', condition: () => {}, conditionDesc: 'Under development', reward: () => {State.variables.journaldata.find(t => t.id === 'mainline_truth').subTasks.find(st => st.id === 'sub_truth-3').status = 'active';}, rewardDesc: 'Unlock Branch: Roots of Darkness: The Demon Invasion’s Mystery;' }, { id: 'sub_truth-3', title: 'Branch: Roots of Darkness: The Demon Invasion’s Mystery', desc: 'Demons pour through dimensional portals, spreading a blood plague that targets nobles like me. Garvin reports they’re digging up ancient relics in the Hawkridge Mountains, possibly the key to their invasion. I need to venture into the mountains, reclaim the relics, crack their portal tech.', status: 'inactive', condition: () => {}, conditionDesc: 'Under development', reward: () => {State.variables.journaldata.find(t => t.id === 'mainline_truth').subTasks.find(st => st.id === 'sub_truth-4').status = 'active';}, rewardDesc: 'Unlock Branch: The Cost of Divinity: Leah’s True Nature;' }, { id: 'sub_truth-4', title: 'Branch: The Cost of Divinity: Leah’s True Nature', desc: 'Leah, the Fertility Goddess, appears as a glowing ceramic statue, mockingly calling me the "Chosen One" while pushing a bizarre plan to ally with monster girls. Her temple barrier protects survivors, but her whispers drip with hidden meaning. I need to reach the temple’s core, unlock her "Sacred Domain," face her trials.', status: 'inactive', condition: () => {}, conditionDesc: 'Under development', reward: () => {}, rewardDesc: '' } ] }, { id: 'main_ritual', type: 'main', title: '🌌Quest: Tame the Planar Rift’s Sultry Secrets', desc: 'Leah’s Planar Rift pulses with forbidden energy, promising monster girl allies through rituals that make my face burn hotter than a dragon’s breath. Back on Earth, I’d be swiping on apps for a date—here, I’m orchestrating cosmic hookups and praying I don’t summon a sentient toaster. Complete all sub-tasks to master this interdimensional matchmaking game.', status: 'inactive', condition: () => State.variables.journaldata.find(t => t.id === 'main_ritual').subTasks.every(st => st.status === 'completed'), conditionDesc: 'Complete all sub-tasks: Perform Summoning Rituals, Feed Monster Girls, Harvest Monster Girls, Summon Monster Girls', reward: () => { State.variables.manage.energy.base += 50; State.variables.manage.energy.current += 50; }, rewardDesc: 'Max Energy +50;', subTasks: [ { id: 'sub_ritual-1', title: 'Perform 10 Summoning Rituals', desc: 'Each ritual requires... ahem, close contact to spark the rift’s magic, leaving me redder than a lobster. 10 times? It’s like starring in a cosmic romance flick, only to find the rift coughing up glowing socks or a cursed teapot instead of allies.', tips: '<b>Tips: Planar Rift -> Ritual Altar -> "Perform Ritual"</b>', status: 'active', condition: () => State.variables.manage.summon.count >= 10, conditionDesc: 'Perform Summoning Rituals ≥ 10', reward: () => {State.variables.Flags.enstudy = 2;D.tech.enTrees.push("evolution");}, rewardDesc: "Activate <b>Monster Girl Study</b> Room to fully unlock their potential. (Maps -> Temple -> Study)" }, { id: 'sub_ritual-2', title: 'Feed Monster Girls 10 Times', desc: 'These monster girls guzzle enchanted nectar like it’s mood hour at a magical tavern, their eyes sparkling as I beg them to slow down. 10 feedings? I’m like a bartender at a bachelorette party, shouting, "You can’t chug another one!" while catgirls pout and harpies demand seconds.', tips: '<b>Tips: Planar Rift -> Ritual Altar -> "Feed"</b>', status: 'active', condition: () => State.variables.manage.feed.count >= 10, conditionDesc: 'Feed Monster Girls ≥ 10', reward: () => State.variables.storage.pickup("gold", 200), rewardDesc: 'Gold +200' }, { id: 'sub_ritual-3', title: 'Harvest Monster Girls 10 Times', desc: 'Collecting milk from cowgirls sounds spicy until I’m dodging flirty winks and a tail that’s got a vendetta. 10 harvests? It’s a torturous dance of thrill and terror, like milking a siren while she hums a love song. Can’t they just bottle their own stuff? They’ve got hands—and curves that could start a riot.', tips: '<b>Tips: Planar Rift -> Ritual Altar -> "Harvest"</b>', status: 'active', condition: () => State.variables.manage.harvest.count >= 10, conditionDesc: 'Harvest Monster Girls ≥ 10', reward: () => State.variables.storage.pickup("food", 50), rewardDesc: 'Food +50' }, { id: 'sub_ritual-4', title: 'Summon 10 Monster Girls', desc: 'Summoning ten monster girls feels like opening a fantasy petting zoo, except they talk back and flirt outrageously. I’m juggling sleeping quarters and settling catgirl-harpie spats like a reality TV host. Leah calls it an army; I call it herding glittery divas who might claw each other over a shiny trinket.', tips: '<b>Tips: Planar Rift -> Ritual Altar -> "Perform summon" to Get Monster Girls</b>', status: 'active', condition: () => State.variables.manage.seedCount >= 10, conditionDesc: 'Summon Monster Girls ≥ 10', reward: () => {State.variables.tech.enTrees.pushUnique("goddess");State.variables.manage.energy.current += 1000;}, rewardDesc: "Unlock Goddess Tech Tree. And available energy + 1000 (Only one day, Not Max Energy)." } ] }, { id: 'main_defend', type: 'main', title: '🛡️Quest: Defend Hawkridge Vale Against the Dark Legion', desc: 'The Dark Legion of Demons gathers beyond Hawkridge Vale, their warhorns heralding slaughter under a sky choked with ash. Every seven days, they storm the valley’s choke point, driven by a merciless hunger to eradicate all life—especially the blood of the lord. With human steel and unyielding resolve, we must hold the line, for surrender means annihilation. Complete all sub-tasks to repel four relentless assaults and preserve our people.', status: 'inactive', condition: () => State.variables.journaldata.find(t => t.id === 'main_defend').subTasks.every(st => st.status === 'completed'), conditionDesc: 'Complete all sub-tasks: Construct an Arrow Tower, Repel the First Demon Assault, Repel the Second Demon Assault, Endure a Month of Siege', reward: () => changeEffects(0, 1, "bountiful_fields"), rewardDesc: 'Unlock "Bountiful Fields" Zone and "Farmland" unit; <b>(Tips: Building Workshop -> Hawkridge Vale -> Bountiful Fields -> "Build" Farmland)</b>', subTasks: [ { id: 'sub_defend-1', title: 'Branch: Construct an Arrow Tower', desc: 'Raise another arrow tower to rain death upon the demon horde. Each timber and stone is hewn with the sweat of our people, a fragile bulwark against the tide of darkness. Failure is not an option, for the tower stands as our first line of defiance against the abyss.', tips: '<b>Tips: Building Workshop -> Hawkridge Pass -> Bastion -> "Build" Arrow Tower</b>', status: 'active', condition: () => State.variables.buildingEffects.stats.wrath >= 16, conditionDesc: "Arrow Towers >= 2", reward: () => State.variables.storage.pickup("wood", 20), rewardDesc: 'Wood +20' }, { id: 'sub_defend-2', title: 'Branch: Repel the First Demon Assault', desc: 'The first wave of the Dark Legion crashes against our defenses, their claws and blades thirsting for noble blood. Our human guards stand resolute, wielding steel forged in desperation. We must break this assault, or the valley will drown in crimson.', status: 'active', condition: () => State.variables.times.total > 8, conditionDesc: "Days > 8", reward: () => State.variables.storage.pickup("wood", 40), rewardDesc: 'Wood +40' }, { id: 'sub_defend-3', title: 'Branch: Repel the Second Demon Assault', desc: 'The second assault surges with greater ferocity, demons wielding sorcery that rends earth and soul. Our soldiers, scarred but unbroken, face a foe that savors the hunt for the lord. With monster girls guarding, we fight not for glory, but for survival against a merciless enemy.', status: 'active', condition: () => State.variables.times.total > 15, conditionDesc: "day > 15", reward: () => State.variables.storage.pickup("wood", 80), rewardDesc: 'Wood +80' }, { id: 'sub_defend-4', title: 'Branch: Endure a Month of Siege', desc: 'For four weeks, the Dark Legion’s shadow looms, their weekly assaults testing our will to endure. Every moment is a knife’s edge, with human defenders and monster girls bleeding to hold the choke point. We stand as one, unyielding, to protect our home from utter destruction.', status: 'active', condition: () => State.variables.times.total > 29, conditionDesc: "day > 29", reward: () => State.variables.storage.pickup("gold", 1000), rewardDesc: 'Gold +1000' } ] }, { id: 'main_rebuild', type: 'main', title: '🛠️Quest: Resettle Refugees and Rebuild Hawkridge Vale', desc: 'The refugees cower in the temple, surrounded by crumbling stone . Rebuild the valley’s production to keep them alive. Back home, I’d order takeout and call it a day—here, I’m stuck playing medieval SimCity with no cheat codes.', status: 'inactive', condition: () => State.variables.journaldata.find(t => t.id === 'main_rebuild').subTasks.every(st => st.status === 'completed'), conditionDesc: 'Complete all sub-tasks: Erect Refugee Shelters, Restore Food Production, Reopen the Quarry, Resume Timber Harvesting', reward: () => { State.variables.domain.piety += 10; }, rewardDesc: 'Piety +10;', subTasks: [ { id: 'sub_rebuild-1', title: 'Erect Refugee Shelters', desc: 'The fleeing townsfolk are crammed in the temple like sardines; they need shelters, even if it’s just caves. I’d fight for a apartment right now—beats stitching canvas in this godforsaken mud pit.', tips: '<b>Tips: Building Workshop -> Hawkridge Vale -> Hawkridge Hamlet -> "Build" Residence</b>', status: 'active', condition: () => State.variables.buildingEffects.capacities.resident >= 100, conditionDesc: 'Residence Capacities ≥ 100', reward: () => State.variables.storage.pickup("gold", 200), rewardDesc: 'Gold +200;' }, { id: 'sub_rebuild-2', title: 'Restore Food Production', desc: 'Hunger gnaws at the valley; the fields must yield grain again. No grocery stores, no delivery apps—just me, praying these crops don’t flop like my startup dreams.', tips: '<b>Tips: Building Workshop -> Hawkridge Vale -> Verdant Wilds -> "Build" Berry Grove</b>', status: 'active', condition: () => State.variables.buildingEffects.resources.food >= 10, conditionDesc: 'Food production ≥ 10', reward: () => State.variables.storage.pickup("food", 20), rewardDesc: 'Food +20;' }, { id: 'sub_rebuild-3', title: 'Resume Timber Harvesting', desc: 'The trees must fall to rebuild the vale. Chopping wood by hand feels like punishment—no chainsaws, no YouTube tutorials, just blisters and existential dread.', tips: '<b>Tips: Building Workshop -> Hawkridge Vale -> Verdant Wilds -> "Build" Lunber Camp</b>', status: 'active', condition: () => State.variables.buildingEffects.resources.wood >= 10, conditionDesc: 'Wood production ≥ 10', reward: () => State.variables.storage.pickup("wood", 20), rewardDesc: 'Wood +20;' }, { id: 'sub_rebuild-4', title: 'Reopen the Quarry', desc: 'The valley’s defenses need stone, not crumbling dirt. Quarrying by hand? I’d rather debug a quantum computer than swing a pickaxe in this medieval hellscape.', tips: '<b>Tips: Building Workshop -> Hawkridge Vale -> Rockvein Mines -> "Build" Quarry</b>', status: 'active', condition: () => State.variables.buildingEffects.resources.stone >= 10, conditionDesc: 'Stone production ≥ 10', reward: () => State.variables.storage.pickup("stone", 20), rewardDesc: 'Stone +20;' } ] } ]; function extractTaskData(tasks) { return tasks.map(task => { const simplifiedTask = { id: task.id, type: task.type, status: task.status }; if (task.type === 'main' && task.subTasks) { simplifiedTask.subTasks = task.subTasks.map(subTask => ({ id: subTask.id, type: 'sub', status: subTask.status })); } return simplifiedTask; }); } <</script>>
<<widget JournalActive>> <<set _jour = $journaldata.find(t => t.id === _args[0]) >> <<set _jour.status = "active">> <<NoticeAdd "Quest Activation" _jour.title '<<SimpleUI "Journal">>'>> <</widget>> <<widget NoticeAdd>> <<set _noticeinfo = { title: _args[0], desc: _args[1] } >> <<if _args[2]>> <<set _noticeinfo.goto = _args[2]>> <</if>> <<run $messages.notices.push(_noticeinfo)>> <</widget>>
<<script>> (function () { l10nStrings.textAbort = '终止'; l10nStrings.textAborting = '终止中'; l10nStrings.textCancel = '取消'; l10nStrings.textClear = '清除'; l10nStrings.textClose = '关闭'; l10nStrings.textDelete = '删除'; l10nStrings.textExport = '导出'; l10nStrings.textIdentity = '游戏'; l10nStrings.textImport = '导入'; l10nStrings.textLoad = '加载'; l10nStrings.textOff = '关闭'; l10nStrings.textOk = '确认'; l10nStrings.textOn = '打开'; l10nStrings.textSave = '存档'; l10nStrings.textTurn = '行动回数'; l10nStrings.errorNonexistentPassage = '段落 "{passage}" 不存在'; l10nStrings.warningNoStorage = '缺少所有可用的存储 API. 可能的原因是禁用了第三方 Cookie 设置(这也影响了 Web 存储)或隐私浏览模式. '; l10nStrings.warningNoWebStorage = '缺少 Web 存储 API, 因此此 {textIdentity} 在降级模式下运行. 您可以继续, 但是, 某些部分可能无法正常工作. '; l10nStrings.warningDegraded = '缺少支持此 {textIdentity} 所需的一些功能, 因此它在降级模式下运行. 您可以继续, 但是, 某些部分可能无法正常工作. '; l10nStrings.warningNoSaves = '缺少支持保存所需的一些功能, 禁用保存. '; l10nStrings.saveErrorDisallowed = '目前不允许存档. '; l10nStrings.saveErrorDecodeFail = '无法解码存档, 可能是由于损坏'; l10nStrings.saveErrorDiskLoadFail = '无法从磁盘加载存档文件'; l10nStrings.saveErrorIdMismatch = '存档来自错误的 {textIdentity}'; l10nStrings.saveErrorInvalidData = '存档缺少必需的数据, 可能是由于损坏'; l10nStrings.saveErrorNonexistent = '存档不存在'; l10nStrings.uiBarLabelToggle = '切换UI栏'; l10nStrings.uiBarLabelBackward = '在 {textIdentity} 历史记录中向后移动'; l10nStrings.uiBarLabelForward = '在 {textIdentity} 历史记录中向前移动'; l10nStrings.uiBarLabelJumpto = '跳转到 {textIdentity} 历史记录中的特定点'; l10nStrings.alertTitle = '通知'; l10nStrings.restartTitle = '重新开始'; l10nStrings.restartMesgPrompt = '所有未保存的进度都将丢失. 您确定要重新启动吗? '; l10nStrings.continueTitle = '继续'; l10nStrings.savesTitle = '存档'; l10nStrings.savesHeaderBrowser = '缓存在浏览器'; l10nStrings.savesHeaderDisk = '保存在硬盘'; l10nStrings.savesLabelBrowserClear = '清除所有浏览器存档'; l10nStrings.savesLabelBrowserExport = '将浏览器存档导出到文件'; l10nStrings.savesLabelBrowserImport = '从文件导入进浏览器存档'; l10nStrings.savesLabelDiskLoad = '从硬盘加载'; l10nStrings.savesLabelDiskSave = '保存到硬盘'; l10nStrings.savesTextBrowserAuto = '自动'; l10nStrings.savesTextBrowserSlot = '档位'; l10nStrings.savesTextNoDate = '未知数据'; l10nStrings.settingsTitle = '设置'; l10nStrings.settingsTextReset = '返回默认设置'; l10nStrings.errorViewTitle = '错误'; l10nStrings.errorViewLabelToggle = '切换错误视图'; l10nStrings.debugBarLabelToggle = '切换调试栏'; l10nStrings.debugBarLabelViewsToggle = '切换调试视图'; l10nStrings.debugBarLabelWatchAdd = 'Add a new watch'; l10nStrings.debugBarLabelWatchAll = 'Watch all'; l10nStrings.debugBarLabelWatchClear = 'Clear all watches'; l10nStrings.debugBarLabelWatchDelete = 'Delete this watch'; l10nStrings.debugBarLabelWatchPlaceholder = 'variable name'; l10nStrings.debugBarLabelPassagePlaceholder = 'passage name'; l10nStrings.debugBarLabelPassagePlay = 'Play passage'; l10nStrings.debugBarLabelWatchToggle = 'Toggle the watch panel'; l10nStrings.debugBarMesgNoWatches = 'No watches set'; l10nStrings.debugBarTextAdd = 'Add'; l10nStrings.debugBarTextPassage = 'Passage'; l10nStrings.debugBarTextViews = 'Views'; l10nStrings.debugBarTextWatch = 'Watch'; l10nStrings.macroBackText = '返回'; l10nStrings.macroReturnText = '重来'; l10nStrings.autoloadTitle = '自动加载'; l10nStrings.autoloadMesgPrompt = '自动存档已经存在. 立即加载还是从头开始? '; l10nStrings.autoloadTextCancel = '从头开始'; l10nStrings.autoloadTextOk = '加载自动存档'; l10nStrings.jumptoTitle = '跳转到'; l10nStrings.jumptoMesgUnavailable = '目前没有可用的跳转点\u2026'; l10nStrings.shareTitle = '分享'; })(); <</script>>
<<script>> (() => { l10nStrings.textAbort = 'Abort'; l10nStrings.textAborting = 'Aborting'; l10nStrings.textCancel = 'Cancel'; l10nStrings.textClear = 'Clear'; l10nStrings.textClose = 'Close'; l10nStrings.textDelete = 'Delete'; l10nStrings.textExport = 'Export'; l10nStrings.textIdentity = 'game'; l10nStrings.textImport = 'Import'; l10nStrings.textLoad = 'Load'; l10nStrings.textOff = 'Off'; l10nStrings.textOk = 'OK'; l10nStrings.textOn = 'On'; l10nStrings.textSave = 'Save'; l10nStrings.textTurn = 'Turn'; l10nStrings.errorNonexistentPassage = 'the passage "{passage}" does not exist'; l10nStrings.warningNoStorage = 'All usable storage APIs are missing. Possible causes are a disabled third-party cookie setting, which also affects Web Storage, or a private browsing mode.'; l10nStrings.warningNoWebStorage = 'The Web Storage API is missing, so this {textIdentity} is running in a degraded mode. You may be able to continue, however, some parts may not work properly.'; l10nStrings.warningDegraded = 'Some capabilities required to support this {textIdentity} are missing, so it is running in a degraded mode. You may be able to continue, however, some parts may not work properly.'; l10nStrings.warningNoSaves = 'Some capabilities required to support saves are missing, so saves have been disabled for this session.'; l10nStrings.saveErrorDisallowed = 'Saving is currently disallowed.'; l10nStrings.saveErrorDecodeFail = 'unable to decode save, likely due to corruption'; l10nStrings.saveErrorDiskLoadFail = 'failed to load save file from disk'; l10nStrings.saveErrorIdMismatch = 'save is from the wrong {textIdentity}'; l10nStrings.saveErrorInvalidData = 'save is missing required data, likely due to corruption'; l10nStrings.saveErrorNonexistent = 'save does not exist'; l10nStrings.uiBarLabelToggle = 'Toggle the UI bar'; l10nStrings.uiBarLabelBackward = 'Go backward within the {textIdentity} history'; l10nStrings.uiBarLabelForward = 'Go forward within the {textIdentity} history'; l10nStrings.uiBarLabelJumpto = 'Jump to a specific point within the {textIdentity} history'; l10nStrings.alertTitle = 'Notices'; l10nStrings.restartTitle = 'Restart'; l10nStrings.restartMesgPrompt = 'All unsaved progress will be lost. Are you sure that you want to restart?'; l10nStrings.continueTitle = 'Continue'; l10nStrings.savesTitle = 'Saves'; l10nStrings.savesTitle = ''; l10nStrings.savesHeaderBrowser = 'In Browser'; l10nStrings.savesHeaderDisk = 'On Disk'; l10nStrings.savesLabelBrowserClear = 'Clear all browser saves'; l10nStrings.savesLabelBrowserExport = 'Export browser saves to bundle'; l10nStrings.savesLabelBrowserImport = 'Import browser saves from bundle'; l10nStrings.savesLabelDiskLoad = 'Load from disk'; l10nStrings.savesLabelDiskSave = 'Save to disk'; l10nStrings.savesTextBrowserAuto = 'Auto'; l10nStrings.savesTextBrowserSlot = 'Slot'; l10nStrings.savesTextNoDate = 'unknown date'; l10nStrings.settingsTitle = 'Settings'; l10nStrings.settingsTextReset = 'Reset to Defaults'; l10nStrings.errorViewTitle = 'Error'; l10nStrings.errorViewLabelToggle = 'Toggle the error view'; l10nStrings.debugBarLabelToggle = 'Toggle the debug bar'; l10nStrings.debugBarLabelViewsToggle = 'Toggle the debug views'; l10nStrings.debugBarLabelWatchAdd = 'Add a new watch'; l10nStrings.debugBarLabelWatchAll = 'Watch all'; l10nStrings.debugBarLabelWatchClear = 'Clear all watches'; l10nStrings.debugBarLabelWatchDelete = 'Delete this watch'; l10nStrings.debugBarLabelWatchPlaceholder = 'variable name'; l10nStrings.debugBarLabelPassagePlaceholder = 'passage name'; l10nStrings.debugBarLabelPassagePlay = 'Play passage'; l10nStrings.debugBarLabelWatchToggle = 'Toggle the watch panel'; l10nStrings.debugBarMesgNoWatches = 'No watches set'; l10nStrings.debugBarTextAdd = 'Add'; l10nStrings.debugBarTextPassage = 'Passage'; l10nStrings.debugBarTextViews = 'Views'; l10nStrings.debugBarTextWatch = 'Watch'; l10nStrings.macroBackText = 'Back'; l10nStrings.macroReturnText = 'Return'; l10nStrings.autoloadTitle = 'Autoload'; l10nStrings.autoloadMesgPrompt = 'An autosave exists. Load it now or go to the start?'; l10nStrings.autoloadTextCancel = 'Go to start'; l10nStrings.autoloadTextOk = 'Load autosave'; l10nStrings.jumptoTitle = 'Jump To'; l10nStrings.jumptoMesgUnavailable = 'No jump points currently available\u2026'; l10nStrings.shareTitle = 'Share'; })(); <</script>>
<style> .UIframe { overflow: hidden; } .hallbox { margin-right: auto; height: 100%; position: relative; } .hallcon { width: 100%; } .bg-container { width: 100%; height: 100%; } .bg-container img { width: 100%; height: 100%; object-fit: contain; display: block; margin: 0 auto; } #bedroomdoor img { width: 4rem; } #bedroomdoor img:hover { transform: scale(1.05); } #Dispatchdoor img { height: 4rem; } #Dispatchdoor img:hover { transform: scale(1.05); } #hallgate img { height: 8rem; } #hallrift img { width: 6.25rem; animation: pulseFloat 4s ease-in-out infinite; } .room span{ text-shadow: var(--bg-body) 1px 1px 4px; } .room { line-height: 1.25; display: flex; position: absolute; flex-direction: column; align-items: center; } #MeetingRoom { left: 13%; top: 30%; } #Storage { left: 4%; top: 50%; } #bedroom { left: 90%; top: 50%; } #questhall { left: 49%; top: 10%; } #Warren { left: 79%; top: 30%; } #planarrift { left: 48.5%; top: 42%; } </style> <div class="hallbox"> <div class="bg-container">[img[setup.ImagePath +'ui/bg/mainhall.webp']]</div> <div class="hallcon"> <div class="room" id="planarrift"> <span id="hallrift"><<link [img[setup.ImagePath+'ui/icon/rift01.png'][Summon_UI]]>> <<if Flag('tutorial') == 1>><<SetFlag 'tutorial' 2>><<run Msg.add('eventcard', 'Event_Leah_Ritual')>><</if>> <<if Flag('tutorial') == 4>><<SetFlag 'tutorial' 5>><</if>> <</link>></span> <b style="color:orange;text-shadow: 2px 2px 2px #000000;"><<if Flag('tutorial') >= 2>><<= "🌌"+L.planarrift>><<else>><<= "A strange rift">><</if>></b> </div> <<if Flag('tutorial') >= 3>> <div class="room" id="Warren"> <span id="hallgate"><<link "<<showframe 'door128' 'Warren' 7>>" "Monsters_UI">> <<if Flag('tutorial') == 3>><<SetFlag 'tutorial' 4>><<run Msg.add('eventcard', 'Event_Stellafall_Chambers')>><</if>> <</link>></span> <p><<if Flag('tutorial') >= 4>><<= "😸"+ L.Warren>><<else>><<= L.chambers>><</if>></p> </div> <</if>> <<if Flag('tutorial') >= 6>> <div class="room" id="bedroom"> <span id="bedroomdoor"><<link "<<showframe 'door128' 'bedroom' 7>>" "Bedroom_UI">> <</link>> </span> <p><<= "🌙"+ L.bedroom>></p> </div> <</if>> <<if Flag('endispatch') == 2 || Flag('tutorial') >= 7>> <div class="room" id="questhall"> <span id="Dispatchdoor"><<link "<<showframe 'door128' 'questhall' 5>>" "Dispatch_UI">> <</link>> </span> <p><<= "📋"+ L.Dispatch>></p> </div> <</if>> <<if Flag('tutorial') >= 8>> <div class="room" id="Storage"> <span id="hallgate"><<link "<<showframe 'door128' 'Storage' 7>>" "Storage_UI">><</link>></span> <<= "📦"+ L.storage>> </div> <</if>> <<if Flag('enmeeting') == 2>> <div class="room" id="MeetingRoom"> <span id="meetingdoor"><<link "<<showframe 'door128' 'MeetingRoom' 7>>" "Meeting_UI">> <<if Flag('tutorial') == 10>><<SetFlag 'tutorial' 11>><</if>> <</link>></span> <p><<= "👑"+"Meeting">></p> </div> <</if>> <<if $times.total >= 3>> <div class="room" style="top: 7rem;left: 25rem;"> <span id="chrtree"> <<link [img[setup.ImagePath +'event/christmas.png']]>> <<if !Flag('Christmasshow')>> <<run Msg.add('eventcard', 'Event_Festival_Christmas', 1)>> <<SetFlag 'Christmasshow' true>> <<run AudioBus.playPlaylist('bgm_happy');>> <<else>> <<run AudioBus.playPlaylist('bgm_happy');>> <</if>> <</link>></span> </div> <</if>> </div> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "MainHall">> </div> </div> <<done>> <<if Flag('tutorial') == 1>> <<append "#hallrift">> <<set _tutorl = [ { el: '#hallrift', text: 'A strange rift hovers in the main hall, curiously approach to explore.<br>Click glowing button to next step.' } ] >> <<TutorList _tutorl>> <</append>> <</if>> <<if Flag('tutorial') == 3>> <<append "#hallgate">> <<set _tutorl = [ { el: '#hallgate', text: "Where to find the right person? ...Wait, a noise from that chamber. Better take a look." } ] >> <<TutorList _tutorl>> <</append>> <</if>> <<if Flag('tutorial') == 4>> <<append "#hallrift">> <<set _tutorl = [ { el: '#hallrift', text: "Finally, all set! ...Why is this catgirl even more excited than me? Off to the summoning ritual!" } ] >> <<TutorList _tutorl>> <</append>> <</if>> <<if Flag('tutorial') == 6>> <<append "#bedroomdoor">> <<set _tutorl = [ { el: '#bedroomdoor', text: "When Energy or Stamina runs out, rest in the bedroom to start a new day."} ] >> <<TutorList _tutorl>> <</append>> <</if>> <<if Flag('tutorial') == 7>> <<append "#Dispatchdoor">> <<set _tutorl = [ { el: '#Dispatchdoor', text: L.DispatchNotice1 + "<br>" + L.sourceincome } ] >> <<TutorList _tutorl>> <</append>> <</if>> <<if Flag('tutorial') == 9>> <<append "#mapbtn">> <<set _tutorl = [ { el: '#mapbtn', text: 'Click the Map.' } ] >> <<TutorList _tutorl>> <</append>> <<SetFlag 'tutorial' 10>> <</if>> <<if Flag('tutorial') == 12>> <<append "#meetingdoor">> <<set _tutorl = [ { el: '#meetingdoor', text: "This will be the hub for gathering intel and making decisions." } ] >> <<TutorList _tutorl>> <</append>> <<SetFlag 'tutorial' 13>> <</if>> <</done>> <<if Flag('endintro')>> <<run Msg.add('eventcard', 'Event_Scene_EndTutorial')>> <</if>> <<if Flag('leahflag') == 1>> <<run Msg.add('notify', "Unlock [<span>Planar Rift</span>] Zone")>> <<run Msg.add('notify', "Unlock [<span>Summoning Ritual</span>] Function")>> <<run Msg.add('notify', "Unlock [<span>Chambers</span>] Zone")>> <<SetFlag 'leahflag' 2>> <</if>> <<if Flag('catmaidflag') == 1>> <<AddMonster $chars.dataMap.get("catmaid")>> <<run $chars.dataMap.get("catmaid").calcAttrInfo("bond.vl", 1)>> <<run Msg.add('notify', "[<span>Stellafall</span>] Joined")>> <<run Msg.add('notify', "Unlock [<span>Hollow Warren</span>] Zone")>> <<SetFlag 'catmaidflag' 2>> <</if>> <<if Flag('catmaidflag') == 3>> <<JournalActive "main_ritual">> <<run Msg.add('wiki', "<<run UI.alert('The Planar Rift is currently the only mysterious force you can harness, so you’d better study how to make the most of it. <br>Activate quest: Tame the Planar Rift’s Sultry Secrets. <br>Please click the Journal in the Right Bar.');>>")>> <<run Msg.add('notify', "Unlock [<span>Maps</span>] Function")>> <<run Msg.add('notify', "Unlock [<span>Journal</span>] Function")>> <<run Msg.add('notify', "Unlock [<span>Baron Bedroom</span>] Zone")>> <<run Msg.add('notify', "Activate [<span>Tame Ritual</span>] Quest")>> <<SetFlag 'catmaidflag' 4>> <</if>> <<if Flag('endispatch') == 1>> <<run Msg.add('notify', "Unlock [<span>Dispatch Board</span>] Zone")>> <<run Msg.add('notify', "Unlock [<span>Storage Chamber</span>] Zone")>> <<run Msg.add('notify', "Unlock [<span>Feed</span>] Function")>> <<run Msg.add('notify', "Unlock [<span>Harvest</span>] Function")>> <<SetFlag 'endispatch' 2>> <<SetFlag 'enstorage' 2>> <</if>> <<if Flag('griffinknightflag') == 1>> <<set $domain.wartaxdeadline = 90 >> <<set $battle.demonattackdeadline = 3 >> <<SetFlag 'wartaxs'>> <<SetFlag 'showwartaxs' false>> <<SetFlag 'entry_Characters_griffinknight' 0>> <<JournalActive "main_defend">> <<run Msg.add('wiki', "<<run UI.alert('The Griffin Knights bring urgent news: demons will attack in 3 days. Prepare your defenses now. <br>Activate quest: Defend Hawkridge Vale Against the Dark Legion. <br>Please click the map icon in the top-right corner and check the Journal in the pop-up window.');>>")>> <<run Msg.add('notify', "Activate [<span>Pass Defend</span>] Quest")>> <<run Msg.add('notify', "Unlock [<span>Bastion</span>] Zone")>> <<run Msg.add('notify', "Dark Legion attack in [<span>3 Days</span>]")>> <<SetFlag 'griffinknightflag' 2>> <</if>> <<if Flag('scalemaidflag') == 1>> <<AddMonster $chars.dataMap.get("scalemaid")>> <<run $chars.dataMap.get("scalemaid").img = "characters/scalemaid/scalemaid02.png">> <<notify 2s>>[<span>Sariya Thornscale</span>] Joined<</notify>> <<SetFlag 'scalemaidflag' 2>> <</if>> <<if Flag('foxmaidflag') == 1>> <<run Msg.add('wiki', "<<run UI.alert('The Black Market becomes a permanent fixture, changing from appearing every Thursday to restocking goods every Thursday, with rare alchemical potions now available for sale.');>>")>> <<SetFlag 'foxmaidflag' 2>> <</if>> <<if $domain.wartaxdeadline < 1 && Flag('wartaxs')>> <<if ($storage.count('gold') >= $domain.wartax[0]) && ($domain.wartax.length == 1)>> <<run Msg.add('eventcard', 'Event_Gregory_TaxesFinished')>> <<elseif ($storage.count('gold') >= $domain.wartax[0]) && ($domain.wartax.length > 1)>> <<run Msg.add('eventcard', 'Event_Gregory_TaxesYes')>> <<else>> <<run Msg.add('eventcard', 'Event_Gregory_TaxesNo')>> <</if>> <</if>> <<if $times.total === 8 && $gamestatus.stage === 1>> <<set $gamestatus.stage = 2>> <<run Msg.add('wiki','<<goto "Welcome_Curtain">>')>> <</if>> <<if $times.total === 28 && $gamestatus.stage === 2>> <<set $gamestatus.stage = 3>> <<run Msg.add('wiki','<<goto "Welcome_Curtain">>')>> <</if>> <<run Msg.show();>>
<<done>> <<script>> $('.mapsUI #barony_map .map-content').css('background-image', 'url("images/ui/map/Barony01.webp")'); <</script>> <<replace "#brmapName">>Barony Map<</replace>> <</done>> <div id="vale" class="marker" data-left="1160" data-top="0" data-width="128" data-height="144" data-bg-color="url('images/ui/map/vale.png') no-repeat center / contain" data-map="hawkridge_vale"><span>Hawkridge Vale</span></div>
<<done>> <<script>> $('.mapsUI #temple_map .map-content').css('background-image', 'url("images/ui/map/templeMap01.webp")'); <</script>> <<replace "#tpmapName">>Temple Interior Map<</replace>> <</done>> <div class="marker" data-left="225" data-top="482" data-width="144" data-height="144" data-bg-color="url('images/ui/map/rift.png') no-repeat center / contain" data-passage="Summon_UI"><span>Planar Rift</span></div> <<if Flag('endispatch') == 2>><div class="marker" data-left="16" data-top="335" data-width="144" data-height="144" data-bg-color="url('images/ui/map/dispatch.png') no-repeat center / contain" data-passage="Dispatch_UI"><span>Dispatch Board</span></div><</if>> <<if Flag('enmeeting') == 2>><div class="marker" data-left="16" data-top="545" data-width="144" data-height="144" data-bg-color="url('images/ui/map/meeting.png') no-repeat center / contain" data-passage="Meeting_UI"><span>Meeting Room</span></div><</if>> <<if Flag('enstorage') == 2>><div class="marker" data-left="192" data-top="193" data-width="144" data-height="144" data-bg-color="url('images/ui/map/storage.png') no-repeat center / contain" data-passage="Storage_UI"><span>Storage Chamber</span></div><</if>> <<if Flag('enbuild') == 2>><div class="marker" id="buildbtn" data-left="352" data-top="62" data-width="144" data-height="144" data-bg-color="url('images/ui/map/build.png') no-repeat center / contain" data-passage="Building_UI"><span>Building Workshop</span></div><</if>> <div class="marker" data-left="342" data-top="688" data-width="144" data-height="144" data-bg-color="url('images/ui/map/bedroom.png') no-repeat center / contain" data-passage="Bedroom_UI"><span>Baron Bedroom</span></div> <<if Flag('enstudy') == 2>><div class="marker" data-left="608" data-top="687" data-width="144" data-height="144" data-bg-color="url('images/ui/map/study.png') no-repeat center / contain" data-passage="TechTree_UI"><span>Monsters Study</span></div><</if>> <div class="marker" data-left="784" data-top="350" data-width="673" data-height="322" data-bg-color="url('images/ui/map/quarters.webp') no-repeat center / contain" data-passage="Monsters_UI"><span>Hollow Warren</span></div> <div class="marker" data-left="264" data-top="354" data-width="64" data-height="64" data-bg-color="url('images/ui/map/goddess.png') no-repeat center / contain"></div>
<<done>> <<script>> $('.mapsUI #hawkridge_vale .map-content').css('background-image', 'url("images/ui/bg/Vale01.png")'); <</script>> <<replace "#vlmapName">>Hawkridge Vale<</replace>> <</done>> <<if $times.weekday == 2>><div class="marker" data-left="310" data-top="260" data-width="70" data-height="70" data-bg-color="rgba(255, 0, 179, 0.5)" data-passage="Slaver_UI"><<= setup.langbank.Slaver[$lang]>></div><</if>> <<if $times.weekday == 4>><div class="marker" data-left="310" data-top="515" data-width="70" data-height="70" data-bg-color="rgba(0, 49, 209, 0.5)" data-passage="Market_UI"><<= setup.langbank.market[$lang]>></div><</if>> <div id="temple" class="marker" data-left="1510" data-top="375" data-width="85" data-height="100" data-bg-color="rgba(190, 190, 190, 0.4)" data-passage="MainHall_UI">Lost Temple Entrance</div>
<<done>> <<script>> $('.mapsUI #hawkridge_vale .map-content').css('background-image', 'url("images/ui/map/Vale02.webp")'); <</script>> <<replace "#vlmapName">>Hawkridge Vale<</replace>> <</done>> <<if Flag('enbastion') == 2>><div class="marker" data-left="240" data-top="360" data-width="192" data-height="192" data-bg-color="url('images/ui/map/bastion.png') no-repeat center / contain" data-passage="Battle_UI"><span>Bastion</span> <<if $times.total >= 25>><div class="maphead">[img['images/characters/captain/captain_head.png']]</div><</if>> </div><</if>> <<if $times.weekday == 2>><div class="marker" data-left="290" data-top="200" data-width="128" data-height="128" data-bg-color="url('images/ui/map/slaver.png') no-repeat center / contain" data-passage="Slaver_UI"><span><<= langBank("Slaver")>></span></div><</if>> <div class="marker" data-left="550" data-top="650" data-width="192" data-height="192" data-bg-color="url('images/ui/map/wilds.png') no-repeat center / contain" data-passage="Building_UI"><span>Verdant Wilds</span> <<if $times.total >= 25>><div class="maphead">[img['images/characters/archmaid/archmaid_head.png']]</div><</if>> </div> <<if Flag('entrade') == 2>> <div class="marker" data-left="290" data-top="570" data-width="192" data-height="192" data-bg-color="url('images/ui/map/trade.png') no-repeat center / contain" data-passage="Market_UI"><span>Trade Zone</span> <<if $times.total >= 25>><div class="maphead">[img['images/characters/foxmaid/foxmaid_head.png']]</div><</if>> </div> <<else>> <<if $times.weekday == 4 || $times.total >= 25>><div class="marker" data-left="290" data-top="580" data-width="128" data-height="128" data-bg-color="url('images/ui/map/market.png') no-repeat center / contain" data-passage="Market_UI"><span><<= langBank("market")>></span> <<if $times.total >= 25>><div class="maphead">[img['images/characters/foxmaid/foxmaid_head.png']]</div><</if>> </div><</if>> <</if>> <div class="marker" data-left="1025" data-top="530" data-width="192" data-height="192" data-bg-color="url('images/ui/map/hamlet.png') no-repeat center / contain" data-passage="Building_UI"><span>Hawkridge Hamlet</span> <<if $times.total >= 25>><div class="maphead">[img['images/characters/promaid/promaid_head.png']]</div><</if>> </div> <div class="marker" data-left="800" data-top="40" data-width="192" data-height="192" data-bg-color="url('images/ui/map/mines.png') no-repeat center / contain" data-passage="Building_UI"><span>Rockvein Mines</span> <<if $times.total >= 25>><div class="maphead">[img['images/characters/butler/butler_head.png']]</div><</if>> </div> <<if $buildingEffects.slots.bountiful_fields > 0>><div class="marker" data-left="1030" data-top="170" data-width="192" data-height="192" data-bg-color="url('images/ui/map/fields.png') no-repeat center / contain" data-passage="Building_UI"><span>Bountiful Fields</span></div><</if>> <div id="temple" class="marker" data-left="1510" data-top="405" data-width="85" data-height="100" data-bg-color="rgba(190, 190, 190, 0.4)" data-map="temple_map">Lost Temple Entrance <<if $times.total >= 25>><div class="maphead">[img['images/characters/you/you_head.png']]</div><</if>> </div> <div class="marker" data-left="800" data-top="330" data-width="192" data-height="192" data-bg-color="url('images/ui/map/lake.png') no-repeat center / contain" data-passage=""><span>Dawnveil Lake</span></div>
<style> .mapsUI { margin: auto; width: 100%; max-width: 1600px; height: 100%; max-height: 900px; display: flex; flex-direction: column; } .mapsUI .map-container { overflow: visible; flex-grow: 1; background: var(--bg-body); box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.8); display: none; position: relative; } .mapsUI .map-container.active { height: 100%; display: block; } .mapsUI .tabs { z-index: 99; position: absolute; top: 0rem; left: 0.75rem; display: flex; background: rgba(0, 0, 0, 0.8); border: 1px solid #4a2b0f; border-radius: 5px; padding: 5px; } .mapsUI .tab { z-index: 100; padding: 0.25rem 1rem; margin: 0 5px; font-size: 0.9rem; cursor: pointer; background: linear-gradient(180deg, #2a2a2a, #1a1a1a); border: 1px solid #4a2b0f; border-radius: 3px; text-transform: uppercase; transition: all 0.3s ease; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); } .mapsUI .tab:hover { color: #e0e0e0; background: linear-gradient(180deg, #3a3a3a, #2a2a2a); } .mapsUI .tab.active { color: #ffd700; background: linear-gradient(180deg, #4a2b0f, #2a2a2a); border-bottom: 1px solid #ffd700; } .mapsUI .map-content { width: 100%; height: 100%; background-size: contain; background-position: center; background-repeat: no-repeat; border-radius: 0.75rem; position: relative; } .mapsUI #barony_map .map-content { } .mapsUI #hawkridge_vale .map-content { } .mapsUI #temple_map .map-content { } .mapsUI .map-info { position: absolute; bottom: 0.5rem; left: 0.5rem; background: rgba(0, 0, 0, 0.9); padding: 0.75rem; border: 1px solid #4a2b0f; border-radius: 0.4rem; max-width: 18rem; line-height: 1.4; font-size: 0.9rem; } .marker { box-sizing: content-box; position: absolute; border: 2px solid #4a2b0f; border-radius: 8px; cursor: pointer; display: flex; align-items: center; justify-content: center; color: #ffffff; font-size: 0.875rem; text-align: center; width: 64px; height: 64px; } .marker span { bottom: -30%; position: relative; } .marker:hover { filter: brightness(1.2); } .mappopup{ left:5rem; } .maphead { box-sizing: content-box; bottom: 3rem; left: -3rem; position: absolute; border-radius: 2rem; border: 2px solid #4a2b0f; width: 4rem; height: 4rem; background: #878787; } .maphead img{ border-radius: 2rem; width:100%; } </style> <<script>> $(document).ready(function() { const originalImageSizes = { 'barony_map': { width: 1600, height: 900 }, 'hawkridge_vale': { width: 1600, height: 900 }, 'temple_map': { width: 1600, height: 900 } }; function scaleMarkers() { $('.map-container').each(function() { const mapId = $(this).attr('id'); const $mapContent = $(this).find('.map-content'); const $markers = $mapContent.find('.marker'); const contentWidth = $mapContent.width(); const contentHeight = $mapContent.height(); const original = originalImageSizes[mapId] || { width: 1600, height: 900 }; const scaleX = contentWidth / original.width; const scaleY = contentHeight / original.height; const scale = Math.min(scaleX, scaleY); const offsetX = (contentWidth - original.width * scale) / 2; const offsetY = (contentHeight - original.height * scale) / 2; $markers.each(function() { const $marker = $(this); const left = parseFloat($marker.data('left')); const top = parseFloat($marker.data('top')); const width = parseFloat($marker.data('width')); const height = parseFloat($marker.data('height')); const bgColor = $marker.data('bg-color') || 'rgba(255, 215, 0, 0.5)'; $marker.css({ left: offsetX + left * scale, top: offsetY + top * scale, width: width * scale, height: height * scale, background: bgColor }); }); }); } $('.tab').click(function() { const mapId = $(this).data('map'); $('.map-container').removeClass('active'); $('#' + mapId).addClass('active'); scaleMarkers(); }); $('div.marker:not([id="vale"], [id="temple"])').click(function() { Dialog.close(); }); $('#vale').click(function() { const mapId = "hawkridge_vale"; $('.map-container').removeClass('active'); $('#' + mapId).addClass('active'); scaleMarkers(); }); $('#temple').click(function() { const mapId = "temple_map"; $('.map-container').removeClass('active'); $('#' + mapId).addClass('active'); scaleMarkers(); }); $('#temple').click(function() { const mapId = "temple_map"; $('.map-container').removeClass('active'); $('#' + mapId).addClass('active'); scaleMarkers(); }); $(window).resize(scaleMarkers); scaleMarkers(); }); <</script>> <div class="mapsUI"> <div id="barony_map" class="map-container"> <div class="tabs"> <div class="tab" data-map="temple_map">🗺️Lost Temple</div> <div class="tab" data-map="hawkridge_vale">🗺️Hawkridge Vale</div> <div class="tab active" data-map="barony_map">🗺️Barony Map</div> </div> <div class="map-content"> <<include $domain.maps[2]>> </div> <div class="map-info"> <h3><span id="brmapName"></span></h3> <p><span id="brmapDesc"></span></p> </div> </div> <div id="hawkridge_vale" class="map-container"> <div class="tabs"> <div class="tab" data-map="temple_map">🗺️Lost Temple</div> <div class="tab active" data-map="hawkridge_vale">🗺️Hawkridge Vale</div> <div class="tab" data-map="barony_map">🗺️Barony Map</div> </div> <div class="map-content"> <<include $domain.maps[1]>> </div> <div class="map-info"> <h3><span id="vlmapName"></span></h3> <p><span id="vlmapDesc"></span></p> </div> </div> <div id="temple_map" class="map-container active"> <div class="tabs"> <div class="tab active" data-map="temple_map">🗺️Lost Temple</div> <div class="tab" data-map="hawkridge_vale">🗺️Hawkridge Vale</div> <div class="tab" data-map="barony_map">🗺️Barony Map</div> </div> <div class="map-content"> <<include $domain.maps[0]>> </div> <div class="map-info"> <h3><span id="tpmapName"></span></h3> <p><span id="tpmapDesc"></span></p> </div> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "MapsPopup">> </div> </div> <<done>> <<if Flag('tutorial') == 10>> <<append "#buildbtn">> <<set _tutorl = [ { el: '#buildbtn', text: 'Click on the Building Workshop.' } ] >> <<TutorList _tutorl>> <</append>> <<SetFlag 'tutorial' 11>> <</if>> <</done>> <<run Msg.show();>>
<style> .trading-hall { display: grid; grid-template-columns: 2fr 2fr 1fr; } @media screen and (max-width: 1024px) { .trading-hall { grid-template-columns: 2fr 2fr 0.4fr; background: #1a1a1a; } .trading-pane { padding: 0rem; } } .trading-hall:not(:has(> :nth-of-type(3))) { grid-template-columns: 1fr 1fr !important; } .trading-pane { height: 100%; padding: 1rem; background: #2d2d2d; border: 2px groove #8b4513; } .trading-grid { overflow: auto; height: 80vh; } .price-trend { display: inline-block; width: 12px; height: 12px; margin-left: 5px; clip-path: polygon(50% 0%, 0% 100%, 100% 100%); } .trend-up { background: #6b0504; } .trend-down { background: #228b22; transform: rotate(180deg);} .trade-totalbox { margin-bottom: 1rem; background: rgba(43,25,28,0.9); padding: 0.5rem 1rem; border-top: 2px solid #4a4130; } .trade-buttons { margin: 0.5rem; width: 10rem; position: relative; display: flex; } .trade-buttons button{ padding: 0.5rem; width: 5rem; } .item-card img{ width:4rem; } .item-img { position: relative; width: 4.5rem; } .item-card { display: flex; align-items: center; justify-content: space-between; } .trading-iteminfo { width: 60%; margin-left: 1rem; align-items: center; } .trading-itemstat { display: grid; grid-template-columns: repeat(2, 1fr); align-items: center; } hr { display: block; height: 1px; border: none; border-top: 1px solid #000000; margin: 0em 0; padding: 0; } .buyplus{ border: 2px groove #fff000; } input, select, textarea { width: 5rem; } input { line-height: 2.5rem; } .marketcg { color: var(--text-primary); background: var(--bg-surface); min-width: 10rem; display: flex; height: 100%; width: 100%; } .marketcg img { height: 100%; object-fit: cover; width: 100%; border: 0; } </style> <style> .talent-stars { display: flex; width: 100%; right: -3.5rem; bottom: 0.5rem; position: absolute; flex-direction: column-reverse; } </style> <div class="trading-hall UIbox"> <div class="trading-pane buyplus"> <div class="trade-totalbox"> <span class="item-illuminated-text"><b><<= L.yourinventory>></b></span> </div> <<do tag "sell_buy">> <div class="trading-grid"> <<if !_sellnum>> <<set _sellnum = [] >> <<set _buynum = [] >> <</if>> <<for _item, _amount range $storage.table>> <<if _item != "gold" && _item != "gold-1" && getiteminfo(_item,"type") != "special">> <<set _price = Math.round(getiteminfo(_item,"price") * $market.priceRate.sell)>> <<capture _item _amount _price>> <div class="item-card"> <div class="item-img"><<HoverTip '<<= getiteminfo(_item,"desc")>>'>> [img[setup.ImagePath + getiteminfo(_item,"img")]]<</HoverTip>> <<NumToStars `getiteminfo(_item,"lv")`>> </div> <div class="trading-iteminfo"> <div class="item-name"><<= getiteminfo(_item,"name")>></div> <div class="trading-itemstat"> <div class="item-stat"> <span><<= "🏷️ "+ _price +"g">></span> </div> <div class="item-qty"><<= "Stock: "+ _amount>></div> </div> </div> <div class="trade-buttons"> <<if !_sellnum[_item]>><<set _sellnum[_item] = 1 >><</if>> <<numberbox "_sellnum[_item]" _sellnum[_item]>> <<button "<<= setup.langbank.sell[$lang]>>">> <<if _sellnum[_item] > _amount>><<set _sellnum[_item] = _amount >><</if>> <<drop $storage _item _sellnum[_item] >> <<pickup $storage "gold" _price*_sellnum[_item]>> <<redo "sell_buy topbar">> <</button>> </div> </div> <hr> <</capture>> <</if>> <</for>> </div> <</do>> </div> <div class="trading-pane"> <div class="trade-totalbox"> <span class="item-illuminated-text"><b><<= L.blackmarket>></b></span> </div> <<do tag "sell_buy">> <div class="trading-grid"> <<for _item, _amount range $blackmarket.table>> <<set _price = Math.round(getiteminfo(_item,"price") * $market.priceRate.buy)>> <<capture _item _amount _price>> <div class="item-card"> <div class="item-img"><<HoverTip '<<= getiteminfo(_item,"desc")>>'>> [img[setup.ImagePath + getiteminfo(_item,"img")]]<</HoverTip>> <<NumToStars `getiteminfo(_item,"lv")`>> </div> <div class="trading-iteminfo"> <div class="item-name"><<= getiteminfo(_item,"name")>></div> <div class="trading-itemstat"> <div class="item-stat"> <span><<= "🏷️ "+ _price +"g">></span> </div> <div class="item-qty"><<= "Stock: "+ _amount>></div> </div> </div> <div class="trade-buttons"> <<if !_buynum[_item]>><<set _buynum[_item] = 1 >><</if>> <<numberbox "_buynum[_item]" _buynum[_item]>> <<button "<<= setup.langbank.buy[$lang]>>">> <<if _buynum[_item] >= _amount>><<set _buynum[_item] = _amount >><</if>> <<if $storage.count('gold') >= _price*_buynum[_item]>> <<transfer $blackmarket $storage _item _buynum[_item]>> <<drop $storage "gold" _price*_buynum[_item]>> <<redo "sell_buy topbar">> <<else>> <<run UI.alert(setup.langbank.moregold[$lang])>> <</if>> <</button>> </div> </div> <hr> <</capture>> <</for>> </div> <</do>> </div> <<if $times.total >= 25>><div class="marketcg"><<link [img['images/characters/foxmaid/foxmaid01.webp']]>><<notify 3s>>Don’t even think about haggling, our clan’s survival depends on trade.<</notify>><</link>></div><</if>> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Market">> </div> </div> <<set _FacHelp = "CodexEntry_Building_BlackMarket">> <<run Msg.show();>>
<style> :root { --dark-bg: #0f0f0f; --darker-bg: #080808; --accent-red: #8b0000; --accent-gold: #b8860b; --text-light: #e0e0e0; --text-gray: #a0a0a0; --card-bg: #1a1a1a; } .dashboard { width: 100%; display: grid; grid-template-columns: 17rem 1fr 20rem; grid-template-rows: 4rem 1fr 9.5rem; height: 100%; gap: 0.5rem; } .header { grid-column: 1 / 4; border: 1px solid var(--cm-border); border-radius: 5px; display: flex; align-items: center; justify-content: space-between; padding: 0 2rem; position: relative; } .left-panel { height: 100%; border: 1px solid var(--cm-border); grid-row: 2; display: flex; flex-direction: column; gap: 0.75rem; overflow-y: auto; } .section-title { font-size: 1rem; color: var(--accent-gold); margin-bottom: 0.5rem; padding-bottom: 0.25rem; border-bottom: 1px solid var(--cm-border); position: relative; } .section-title::before { content: '◆'; color: var(--accent-red); margin-right: 0.5rem; } .building { background-color: rgba(0, 0, 0, 0.6); border: 1px solid var(--cm-border); border-radius: 5px; padding: 0.5rem; box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3); } .building-header { display: flex; align-items: center; margin-bottom: 15px; cursor: pointer; } .building-icon { font-size: 1.5rem; margin-right: 10px; color: var(--accent-gold); } .building-name { font-weight: bold; flex: 1; } .building-toggle { color: var(--text-gray); transition: transform 0.3s ease; } .building.collapsed .building-toggle { transform: rotate(-90deg); } .positions-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr)); gap: 0.5rem; } .position-item { height: 7rem; width: 7rem; background-color: var(--darker-bg); border: 1px solid var(--cm-border); border-radius: 5px; padding: 0.5rem; text-align: center; cursor: pointer; transition: all 0.3s ease; position: relative; &.filled { border-left: 3px solid var(--accent-gold); } &.vacant { background-color: #65656565; border-left: 3px solid var(--accent-red); } &:hover { background-color: var(--bg-surface); } & button { max-width: 5rem; overflow: hidden; } } .position-icon { font-size: 1.5rem; color: var(--accent-gold); } .position-name { font-size: 0.8rem; margin-bottom: 3px; } .position-status { font-size: 0.65rem; padding: 2px 5px; border-radius: 3px; display: inline-block; } .status-filled { background-color: rgba(184, 134, 11, 0.2); color: var(--accent-gold); } .status-vacant { background-color: rgba(139, 0, 0, 0.2); color: var(--accent-red); } .map-container { grid-row: 2; border: 1px solid var(--cm-border); border-radius: 5px; overflow: hidden; position: relative; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); } .map { width: 100%; height: 100%; position: relative; overflow: hidden; } .map-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } .right-panel { border: 1px solid var(--cm-border); grid-row: 2; display: flex; flex-direction: column; overflow-y: auto; } .info-list { display: flex; flex-direction: column; gap: 12px; } .info-item { padding: 0.75rem; background-color: var(--card-bg); border-radius: 5px; border-left: 3px solid var(--accent-red); position: relative; box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2); &::before { content: '!'; position: absolute; top: 1rem; right: 0.5rem; width: 1.25rem; height: 1.25rem; background-color: var(--accent-red); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: bold; color: white; font-size: 1rem; } &.orange::before { background-color: orange; } &.green::before { background-color: green; } } .info-title { font-weight: bold; margin-bottom: 6px; color: var(--accent-gold); } .info-desc { font-size: 0.85rem; color: var(--text-gray); margin-bottom: 8px; line-height: 1.4; } .info-meta { gap: 1rem; display: flex; justify-content: flex-end; font-size: 0.8rem; color: var(--text-gray); } .info-priority { padding: 2px 8px; border-radius: 3px; } .red { background-color: rgba(139, 0, 0, 0.3); color: var(--accent-red); } .orange { background-color: rgba(184, 134, 11, 0.3); color: var(--accent-gold); } .blue { background-color: rgba(100, 100, 100, 0.3); color: var(--text-gray); } .management-panel { width: 100%; grid-column: 1 / 4; background-color: var(--darker-bg); border: 1px solid var(--cm-border); padding: 0.5rem; } .management-scroll { display: flex; gap: 0.5rem; overflow-x: auto; padding-bottom: 5px; } .manager-item { margin: 0.25rem; height: 8rem; width: 8rem; background-color: var(--card-bg); border: 1px solid var(--cm-border); border-radius: 5px; padding: 0.25rem; cursor: pointer; transition: all 0.3s ease; flex-shrink: 0; } .manager-item:hover { background-color: var(--bg-surface); border-color: var(--accent-gold); } .manager-avatar { background-color: var(--accent-red); display: flex; align-items: center; justify-content: center; margin: 0 auto; height: 4.5rem; & img { overflow: hidden; object-position: 50% 0%; object-fit: contain; height: 4.5rem; width: 7rem; } } .manager-avatar.gold { background-color: var(--accent-gold);} .manager-avatar.red { background-color: var(--accent-red);} .manager-avatar.blue { background-color: dimgrey;} .manager-avatar.green { background-color: green;} .manager-avatar.orange { background-color: orange;} .manager-name { font-weight: bold; text-align: center; } .manager-title { font-size: 0.8rem; color: var(--text-gray); text-align: center; } .speech-bubble { display: none; position: absolute; background-color: var(--darker-bg); border: 1px solid var(--cm-border); border-radius: 8px; padding: 15px; max-width: 250px; box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); z-index: 100; animation: fadeIn 0.3s ease; } .speech-bubble::after { content: ''; position: absolute; bottom: -10px; left: 20px; border-width: 10px 10px 0; border-style: solid; border-color: var(--darker-bg) transparent transparent; } .speech-bubble::before { content: ''; position: absolute; bottom: -11px; left: 19px; border-width: 11px 11px 0; border-style: solid; border-color: var(--cm-border) transparent transparent; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .speech-bubble-text { font-size: 0.9rem; line-height: 1.4; margin-bottom: 10px; } .position-item img{ object-fit: contain; height: 6rem; width: 6rem; } .position-item-img { z-index: 0; top: 0; position: absolute; } .joblastpost { background-color: var(--bg-body); position: sticky; bottom: 0; } .header-btn { width: 6rem; } .map-controls { position: absolute; bottom: 1rem; right: 1.5rem; display: flex; gap: 0.75rem; } .map-btn { background-color: rgb(59 0 0 / 80%); border: 1px solid var(--cm-border); color: var(--text-light); width: 5rem; height: 3rem; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s ease; } .map-btn:hover { background-color: var(--bg-surface); border-color: var(--accent-gold); } .sidebar { height: 100%; overflow-y: auto; } .sidebar-group-buttons { max-width: 100%; grid-template-columns: repeat(auto-fit, 3.5rem); } .sidebar-group { max-width: 100%; } </style> <<timed 1s>> <<script>> let currentPosition = ''; let speechBubbleTimeout; const candidates = { }; window.showSpeechBubble = function(event, message) { if (speechBubbleTimeout) { clearTimeout(speechBubbleTimeout); } const bubble = document.getElementById('speechBubble'); const bubbleText = document.getElementById('bubbleText'); bubbleText.textContent = message; const rect = event.currentTarget.getBoundingClientRect(); bubble.style.left = `${rect.left + window.scrollX}px`; bubble.style.top = `${rect.top + window.scrollY - bubble.offsetHeight - 10}px`; bubble.style.display = 'block'; speechBubbleTimeout = setTimeout(() => { bubble.style.display = 'none'; }, 2000); }; <</script>> <</timed>> <div class="dashboard"> <!-- 标题栏 --> <div class="header"> <h2>Council Chamber</h2> <div class="header-controls"> <button class="header-btn" onclick='$.wiki(`<<run UI.settings();>>`)'>Settings</button> </div> </div> <div class="left-panel"> <<include "UI_LeftBar">> </div> <!-- 中间地图区域 --> <div class="map-container"> <div class="map"> <div class="map-overlay"></div> </div> <div class="map-controls"> <button class="map-btn" onclick='$.wiki(`<<replace ".map-overlay">><<include "Job">><</replace>>`)'>Job</button> <button class="map-btn" onclick='$.wiki(`<<replace ".map-overlay">><<include "TechTree">><</replace>>`)'>Stewardship</button> <button class="map-btn" onclick='$.wiki(`<<replace ".map-overlay">><<include "MapsPopup">><</replace>>`)'>Maps</button> <button class="map-btn" onclick='$.wiki(`<<replace ".map-overlay">><<include "DomainReport">><</replace>>`)'>Report</button> <button class="map-btn" onclick='$.wiki(`<<replace ".map-overlay">><<include "Journal">><</replace>>`)'>Journal</button> <button class="map-btn" onclick='$.wiki(`<<replace ".map-overlay">><<include "MonstersInfo">><</replace>>`)'>Monsters</button> <button class="map-btn" onclick='$.wiki(`<<replace ".map-overlay">><<include "Bedroom_MorningNews">><</replace>>`)'>News</button> </div> </div> <div class="right-panel"> <<do tag "Notices">> <div class="section-title">Notices</div> <div class="info-list"> <<for _i, _notice range $messages.notices>> <<capture _i _notice>> <div class="info-item green"> <div class="info-title">_notice.title</div> <div class='info-desc'>_notice.desc</div> <div class="info-meta"> <<if _notice.goto>><<button "Proceed">>_notice.goto<</button>><</if>> <<button "Ignore">><<run $messages.notices.splice(_i, 1)>><<redo "Notices">><</button>> </div> </div> <</capture>> <</for>> <</do>> </div> <div class="management-panel"> <div class="management-scroll"> <<set _CharSay = [ "Why do I feel like I’m back crunching numbers at the office... Still a corporate drone even after crossing worlds!", "Is Leah’s plan even legit? Teaming up with monster girls... I can barely handle a PowerPoint!", "This temple’s ledger is messier than my company’s financial reports...", "Demons hunting nobles? All I want is to clock out and grab a coffee...", "Why do I feel like I’m back at my old desk from before I got zapped here...?" ].random() >> <div class="manager-item" @data-say="_CharSay" onclick="showSpeechBubble(event, this.dataset.say)"> <div class="manager-avatar orange">[img[setup.ImagePath + $chars.dataMap.get("you").head]]</div> <div class="manager-name"><<= $chars.dataMap.get("you").name>></div> <div class="manager-title">Baron Wilderen</div> </div> <<set _CharSay = [ "The temple’s magical barrier is stable, my lord. No need to worry...", "Erin’s skills are improving; the wounded are recovering faster than expected.", "The Goddess of Fertility’s guidance is mysterious, but your mission is no small feat...", "The townsfolk need hope. Plans to rebuild Hawkridge Town are in place." ].random() >> <div class="manager-item" @data-say="_CharSay" onclick="showSpeechBubble(event, this.dataset.say)"> <div class="manager-avatar blue">[img[setup.ImagePath + $chars.dataMap.get('archmaid').head]]</div> <div class="manager-name"><<= $chars.dataMap.get('archmaid').name>></div> <div class="manager-title">Head Maid</div> </div> <<set _CharSay = [ "Damn those demons! Next time, I’ll smash them to bits myself!", "Guard training can’t slack, my lord! The mountain outposts need reinforcing!", "I should’ve held the line of town. Never again!", "Leah’s plan is weird as hell, but if it beats demons, I’m in!" ].random() >> <div class="manager-item" @data-say="_CharSay" onclick="showSpeechBubble(event, this.dataset.say)"> <div class="manager-avatar blue">[img[setup.ImagePath + $chars.dataMap.get('captain').head]]</div> <div class="manager-name"><<= $chars.dataMap.get('captain').name>></div> <div class="manager-title">Captain of Guard</div> </div> <<set _CharSay = [ "The glory of House Wilderen endures, even in the darkest of times...", "The inventory is complete, my lord. Hawkridge Vale is quite stable.", "The family’s warhorse secrets... If we rebuild the pasture, glory will return." ].random() >> <div class="manager-item" @data-say="_CharSay" onclick="showSpeechBubble(event, this.dataset.say)"> <div class="manager-avatar blue">[img[setup.ImagePath + $chars.dataMap.get('butler').head]]</div> <div class="manager-name"><<= $chars.dataMap.get('butler').name>></div> <div class="manager-title">Steward</div> </div> <<set _CharSay = [ "I... I’ll do my best! I won’t hold everyone back...", "The herb mix for the wounded... I’ll check it again, just in case...", "Does my lord really... not remember me?", "The temple statue... it feels like it’s watching me..." ].random() >> <div class="manager-item" @data-say="_CharSay" onclick="showSpeechBubble(event, this.dataset.say)"> <div class="manager-avatar blue">[img[setup.ImagePath + $chars.dataMap.get('promaid').head]]</div> <div class="manager-name"><<= $chars.dataMap.get('promaid').name>></div> <div class="manager-title">Apprentice Maid</div> </div> <<set _CharSay = [ "For now, the others aren’t sold on letting monster girls join the territory’s council meetings.", "No monster girls in the meeting? Are medieval folks’ brains this rigid? That’s species discrimination!", "Monster girls can’t join the table? This meeting’s as exclusive as my old company’s boardroom!", "Not even catgirls in the council? What, scared they’ll claw up the table?", "Monster girls aren’t qualified? Please, I can’t even manage an Excel sheet, let alone who joins the meeting!" ].random() >> <div class="manager-item" @data-say="_CharSay" onclick="showSpeechBubble(event, this.dataset.say)"> <div class="manager-avatar blue">?</div> <div class="manager-name">+</div> <div class="manager-title">?</div> </div> </div> </div> </div> <!-- 对话气泡 --> <div id="speechBubble" class="speech-bubble"> <div class="speech-bubble-text" id="bubbleText"></div> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Meeting">> </div> </div> <<run Msg.show();>>
<style> .UIbox_Monsters { height: 100%; display: flex; } .monsterinfoleft { flex: 55%; display: flex; border: 2px solid var(--color-c); position: relative; overflow: hidden; } div[data-do-tags="selected shoplist"] { display: flex; flex-direction: row; flex: 1; } div[data-do-tags="selected shoplist"] .monsterinfoleftpic { margin-left: 0rem; } .monsterinforight { flex: 45%; display: flex; width: 100%; position: relative; flex-direction: column; } .monsterinfols { height: calc(100% - 7.5rem); flex: 1; display: flex; flex-direction: column; } .monsterinfooperate { flex: 0 0 7.5rem; align-items: start; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); padding: 1rem 0; background-color: var(--bg-body); } .Infolist { position: absolute; top:120px; left:20px; } #monssize { top: 1rem; text-align: -webkit-right; right: 1rem; display: block; width: 8rem; position: absolute; } .leftbox-buttons { margin: 1rem; gap: 1rem; bottom: 0; right: 0; position: absolute; display: flex; align-items: center; & button { width: 7rem; } } </style> <<set $chars.selected = null >> <div class="UIbox UIbox_Monsters"> <div class="monsterinfoleft"> <div class="flex-row-sb1" id="left-box" style="width: 100%;"> <<do tag "selected shoplist">> <<Infolist $chars.selected>> <<ShowPortrait $chars.selected>> <</do>> </div> <div class="leftbox-buttons"> <<do tag "selected shoplist">> <<if $chars.selected>> <<button "Genes">> <<replace "#left-box">> <<do tag "selected shoplist">> <<GenesList $chars.selected>> <<BodyDetail $chars.selected>> <</do>> <</replace>> <</button>> <<button "Stats">> <<replace "#left-box">> <<do tag "selected shoplist">> <<Infolist $chars.selected>> <<ShowPortrait $chars.selected>> <</do>> <</replace>> <</button>> <</if>> <</do>> </div> </div> <div class="monsterinforight"> <div class="monsterinfols"> <<MapList $chars.tempList>> </div> </div> </div>
<style> .capacity-bar { width: 100%; height: 8px; background-color: #2a2a2a; border-radius: 4px; overflow: hidden; margin-top: 5px; } .capacity-fill { height: 100%; background-color: #8a2a2a; border-radius: 4px; } .table-container { height: 100%; width: 100%; background-color: rgba(0, 0, 0, 0.6); border-radius: 8px; overflow-y: auto; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.7); } .table-title { background-color: #2a0a0a; color: #c9a87a; padding: 0.5rem; text-align: center; font-size: 1.5rem; border-bottom: 1px solid #3a1a1a; } table { width: 100%; border-collapse: collapse; text-align: center; } thead { background-color: #2a1a1a; } th { text-align: center; color: #c9a87a; border-bottom: 1px solid #3a2a2a; } tbody tr { border-bottom: 1px solid #2a2a2a; transition: background-color 0.3s; } tbody tr:hover { background-color: #2a2a2a; } td { padding: 12px 10px; color: #d0d0d0; } .species { color: #c9a87a; font-weight: 600; } </style> <<script>> <</script>> <<set _countRaceGender = countRaceGender($chars.tempList)>> <div class="table-container"> <div class="table-title">Monsters Information</div> <table> <thead> <tr> <th>Species</th> <th>Count</th> <th>Capacities</th> <th>Mood/day</th> <th>Female</th> <th>Male</th> <th>Futa</th> </tr> </thead> <tbody> <<for _race ,_v range _countRaceGender>> <<if _race != "human">> <<set _racecount = _countRaceGender[_race].count>> <<set _racetotalcapacities = $buildingEffects.capacities[_race] || 0>> <<set _racemood = Math.clamp(_racetotalcapacities - _racecount, -5 , 5) >> <tr> <td class="species"><<= _race.toUpperFirst()>></td> <td><<= _racecount>></td> <td><<= _racecount + "/"+ _racetotalcapacities >><<if _racecount > _racetotalcapacities>><<= " -Overcrowded!">><</if>> <div class="capacity-bar"> <<set _capacity = `width: ${(_racecount / _racetotalcapacities * 100).toFixed(0)}%;`>> <div class="capacity-fill" @style="_capacity"></div> </div> </td> <td><<= _racemood>></td> <td><<= _countRaceGender[_race].girl>></td> <td><<= _countRaceGender[_race].boy>></td> <td><<= _countRaceGender[_race].futa>></td> </tr> <</if>> <</for>> </tbody> </table> <div style="margin: 2rem;"><<= L.exceedhousingcapacity>> </div> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<if visited() > 1 || Flag('skiptutor')>> <<include "Monsters">> <</if>> </div> </div> <<set _FacStatus = true >> <<set _FacUpgrade = true >> <<set _currenwing = "rearhall">> <<if Flag('enbuild') == 2>><<set _currenzone = $buildings["rearhall"]["warren"]>><</if>> <<set _FacTechTree = true >> <<set _FacHelp = "CodexEntry_Building_Chambers" >> <<run Msg.show();>>
<<widget NewMonster>> <<set _newmonster = createMonster()>> <<set $addmonster = _newmonster>> <<unset _newmonster>> <</widget>> <<widget AddTrait>> <<set _tnum = _args[2] || 1 >> <<if def _args[1]>> <<set _tkey = _args[1]>> <<set _traitId = _args[1].split('-')[0] >> <<set _traitLv = _args[1].split('-')[1] || 1>> <<else>> <<set _tkey = setup.RandomTrait() >> <</if>> <<for _i=0; _i lt _tnum;_i++>> <<run _args[0].addTrait(_traitId, _traitLv)>> <</for>> <</widget>> <<widget NewSlave>> <<for _p to 0; _p lt $slave.capacity; _p++>> <<NewMonster>> <<run setup.initBodyTraining($addmonster)>> <<run $slave.slaveMap.set($addmonster.id, $addmonster)>> <<run $slave.slaveList.push($addmonster.id)>> <<unset $addmonster>> <</for>> <</widget>> <<widget AddMonster>> <<set _collnum = _args[0].race + _args[0].rank + "token" >> <<AddSpeciesRank _args[0].race _args[0].rank>> <<if _args[0].type == "unique">> <<run Npc.changeList(_args[0].id,"npcList","tempList");>> <<run Npc.sortByIndex()>> <<else>> <<run $chars.dataMap.set(_args[0].id, _args[0]);>> <<run $chars.tempList.push(_args[0].id)>> <<set _text = _args[0].name + " moved into the Warren." >> <<run Msg.add('notify', _text);>> <</if>> <<set $tech.techPoint[_args[0].race] = ($tech.techPoint[_args[0].race] ?? 0) + _args[0].rank;>> <</widget>> <<widget DelMonster>> <<run setup.delMonster(_args[0])>> <</widget>> <<widget AddSpeciesRank>> <<if _args[1]>> <<set _addrank = _args[1]>> <<else>> <<set _addrank = 1>> <</if>> <<if !$manage.ownrace.has(_args[0])>> <<run $manage.ownrace.set(_args[0], _addrank)>> <<run Msg.add('notify', "Unlocked a new species.")>> <<NoticeAdd "Unlocked:" "Get a new summon species. <br>Unlocked a new building in Rear Hall.">> <<elseif $manage.ownrace.get(_args[0]) < _addrank>> <<run $manage.ownrace.set(_args[0], _addrank)>> <<run Msg.add('notify', "Unlocked a new Bloodline.")>> <<else>> <</if>> <</widget>> <<widget HeadImageByID>> <<set _imagechar = getMonsterByID(_args[0])>> <<if _imagechar.type == "unique" && _imagechar.head>> [img[setup.ImagePath + _imagechar.head]] <<elseif _imagechar.type == "unique" || _imagechar.type == "wrong">> [img[setup.ImagePath + _imagechar.img]] <<else>> <div style="width: 100%;" @id="_imagechar.id"></div> <<displayHeadPortrait _imagechar _imagechar.id>> <</if>> <</widget>>
<<widget BodyDetail>> <style> .body-table { background-image: [img[setup.ImagePath+'sprites/bg.png']]; background-size: contain; background-position: center; background-repeat: no-repeat; aspect-ratio: 11 / 18; height: 54rem; display: grid; grid-template-columns: 2fr 1fr 1fr 2fr; grid-template-rows: 0.5fr 0.5fr repeat(4, 1fr); } .partbox { max-height: 10.25rem; width: calc(100% - 8px); height: calc(100% - 8px); display: flex; margin: 4px; box-sizing: border-box; align-items: center; justify-content: center; flex-direction: column; position: relative; } .part { height: 100%; width: 100%; flex: 1; } .part .image img{height: 100%; overflow: hidden;} .part .image {justify-self: center;display: none; animation: fadeIn 0.5s ease-in-out;} .part .detail { overflow-y: auto; height: 100%; display: block; width: 100%; } .part.show-detail .image {display: block;} .part.show-detail .detail {display: none; animation: fadeIn 1s ease-in-out;} @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .detailbutton { border-radius: 1rem 0 0 0; right: 0; height: 2rem; width: 2rem; bottom: 0; position: absolute; background: #b14a00; text-align: center; } .head { z-index: 5;grid-column: span 2 / span 2;grid-row: span 2 / span 2; grid-column-start: 2; grid-row-start: 1;} .part.show-detail#head .image { height: 100%; } .breast { z-index: 4; grid-column: span 2 / span 2; grid-column-start: 2; grid-row-start: 3; overflow: visible; height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: flex-start !important; } .belly { z-index: 3; grid-column: span 2 / span 2; grid-column-start: 2; grid-row-start: 4; overflow: visible; height: 100%; display: flex; flex-direction: column; align-items: center; } .sexual { z-index: 3; grid-column: span 2 / span 2; grid-column-start: 2; grid-row-start: 5; overflow: visible; height: 100%; display: flex; flex-direction: column; align-items: center; } .lowerbody {grid-column: span 2 / span 2;grid-column-start: 2;grid-row-start: 6;} .tentacle {grid-column-start: 4;grid-row-start: 6;} .tail {grid-column-start: 4;grid-row-start: 5;} .ass {grid-column-start: 1;grid-row-start: 5;} .arm-right {grid-column-start: 4;grid-row-start: 4;} .arm-left {grid-column-start: 1;grid-row-start: 4;} #arm-left .sprite-container { transform: rotateY(180deg); right: 0; position: absolute; } .wing-left {z-index: 0 !important;grid-column-start: 1;grid-row-start: 3;} .wing-right {z-index: 0 !important;grid-column-start: 4;grid-row-start: 3;} #wing-right .sprite-container { top: -6rem; left: 0; position: absolute; } #wing-left .sprite-container { transform: rotateY(180deg); top: -6rem; right: 0; position: absolute; } .back {grid-column-start: 1;grid-row-start: 6;} .div13 {grid-column-start: 1;grid-row-start: 1;} .div14 {grid-column-start: 1;grid-row-start: 2;} .div16 {grid-column-start: 4;grid-row-start: 1;} .div17 {grid-column-start: 4;grid-row-start: 2;} </style> <style> .passage { justify-content: center; display: flex; } .module-grid-1col { flex: 1; min-height: 0; } .module-item-flex { line-height: 1; padding: 0; } .module-header { margin: 0.5rem 0; font-size: 0.85rem; } </style> <div class="body-table"> <div class="partbox head"> <div class="part" id="head"> <div class="image"> <<HeadImageByID _args[0].id>> </div> <div class="detail module-border"> <div class="module-header"><<= "Head Stats">></div> <div class="module-grid-1col"> <<set _list = setup.trainingCore.formatStats(_args[0], 'head')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> <div class="module-header"><<= "Mouth Stats">></div> <div class="module-grid-1col"> <<set _list = setup.trainingCore.formatStats(_args[0], 'mouth')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "head">> <<link '<<if $("#head").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#head" "show-detail">> <<redo "head">> <</link>><</do>> </div> </div> <div class="partbox breast"> <<set _partbreast = getBreast(_args[0]) >> <div class="part" id="breast"> <div class="image"> <<showframe 'breast192' _partbreast.cupSize 12>> </div> <div class="detail module-border"> <div class="module-header"><<= "breast Stats">></div> <div class="module-grid-1col"> <<set _list = setup.trainingCore.formatStats(_args[0], 'breast')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "breast">> <<link '<<if $("#breast").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#breast" "show-detail">> <<redo "breast">> <</link>><</do>> </div> </div> <div class="partbox belly"> <div class="part" id="belly"> <div class="image"> <<set _charbelly = _args[0].gender == "boy" ? "Boy" : "Humanoid" >> <<showframe 'belly256' _charbelly 10>> </div> <div class="detail module-border"> <<set _list = []>> <<if _args[0].gender == "girl">> <<set _list = setup.trainingCore.formatStats(_args[0], 'womb')>> <div class="module-header"><<= "Womb Stats">></div> <<else>> <<set _list = setup.trainingCore.formatStats(_args[0], 'ball')>> <div class="module-header"><<= "Balls Stats">></div> <</if>> <div class="module-grid-1col"> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "belly">> <<link '<<if $("#belly").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#belly" "show-detail">> <<redo "belly">> <</link>><</do>> </div> </div> <div class="partbox sexual"> <<set _partpussy = getpussy(_args[0]) >> <div class="part" id="sexual"> <<if _args[0]?.gender != "boy">> <div class="image"> <<set _charpussy = _args[0].genes?.pussy?.type >> <<showframe 'pussy256' _charpussy 10>> </div> <div class="detail module-border"> <div class="module-header"><<= "Pussy Stats">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'pussy')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> <</if>> <<if _args[0]?.gender != "girl">> <div class="image"> <<set _charcock = _args[0].genes?.cock?.type >> <<showframe 'cock256' _charcock 10>> </div> <div class="detail module-border"> <div class="module-header"><<= "Cock Stats">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'cock')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> <</if>> </div> <div class="detailbutton"> <<do tag "sexual">> <<link '<<if $("#sexual").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#sexual" "show-detail">> <<redo "sexual">> <</link>><</do>> </div> </div> <div class="partbox ass"> <div class="part" id="ass"> <div class="image"> </div> <div class="detail module-border"> <div class="module-header"><<= "ass Stats">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'ass')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "ass">> <<link '<<if $("#ass").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#ass" "show-detail">> <<redo "ass">> <</link>><</do>> </div> </div> <<if _args[0].genes.tail>> <div class="partbox tail" @style="_args[0].genes?.tail ? '' : 'display: none;'"> <div class="part" id="tail"> <div class="image"> <<set _chartail = _args[0].genes?.tail?.type >> <<showframe 'tail160' _chartail 10>> </div> <div class="detail module-border"> <div class="module-header"><<= "Tail Stats">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'tail')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "tail">> <<link '<<if $("#tail").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#tail" "show-detail">> <<redo "tail">> <</link>><</do>> </div> </div> <</if>> <div class="partbox tentacle" @style="_args[0]?.parts?.tentacle ? '' : 'display: none;'"> <div class="part" id="tentacle"> <div class="image"> </div> <div class="detail module-border"> <div class="module-header"><<= "tentacle">></div> <div class="module-grid-1col"> <div class="module-item-flex"> <span class="item-name"><<= "Stat">></span> <span class="item-value"><<if _args[0]?.parts?.tentacle>><<= setup.SpriteData.getDataLabel("tentacle", _args[0])>><<else>><<= _bodycharRank + "'s">><</if>></span> </div> <div class="module-item-flex"> <span class="item-name"><<= "Score">></span> <span class="item-value"><<= "20">></span> </div> </div> </div> </div> <div class="detailbutton"> <<do tag "tentacle">> <<link '<<if $("#tentacle").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#tentacle" "show-detail">> <<redo "tentacle">> <</link>><</do>> </div> </div> <div class="partbox lowerbody"> <div class="part" id="lowerbody"> <div class="image"> <<set _charlowerbody = _args[0].genes.lowerbody.type >> <<showframe 'lowerbody384' _charlowerbody 24 300>> </div> <div class="detail module-border"> <div class="module-header"><<= "Lower Body">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'lowerbody')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "lowerbody">> <<link '<<if $("#lowerbody").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#lowerbody" "show-detail">> <<redo "lowerbody">> <</link>><</do>> </div> </div> <<if _args[0].genes.hand>> <<set _chararm = _args[0].genes.hand.type >> <<if _args[0].id == "you">><<set _chararm = "You">><</if>> <div class="partbox arm-left" @style="_args[0]?.genes?.hand ? '' : 'display: none;'"> <div class="part" id="arm-left"> <div class="image"> <<showframe 'hand160' _chararm 10>> </div> <div class="detail module-border"> <div class="module-header"><<= "Left Hand">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'hand')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> </div> <div class="partbox arm-right" @style="_args[0]?.genes?.hand ? '' : 'display: none;'"> <div class="part" id="arm-right"> <div class="image"> <<showframe 'hand160' _chararm 10>> </div> <div class="detail module-border"> <div class="module-header"><<= "Right Hand">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'hand')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "arm-right">> <<link '<<if $("#arm-right").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#arm-right" "show-detail">> <<toggleclass "#arm-left" "show-detail">> <<redo "arm-right">><<redo "arm-left">> <</link>><</do>> </div> </div> <</if>> <<if _args[0].genes.wing && _args[0].id != "mirael">> <<set _charWing = _args[0].genes.wing.type >> <<set _charWingsize = (_args[0].rank + 1) * 6>> <div class="partbox wing-left" @style="_args[0]?.genes?.wing ? '' : 'display: none;'"> <div class="part" id="wing-left"> <div class="image"> <<showframe 'wing384' _charWing _charWingsize>> </div> <div class="detail module-border"> <div class="module-header"><<= "Left Wing">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'wing')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> </div> <div class="partbox wing-right" @style="_args[0]?.genes?.wing ? '' : 'display: none;'"> <div class="part" id="wing-right"> <div class="image"> <<showframe 'wing384' _charWing _charWingsize>> </div> <div class="detail module-border"> <div class="module-header"><<= "Right Wing">></div> <div class="module-grid-1col"> <<set _list = []>> <<set _list = setup.trainingCore.formatStats(_args[0], 'wing')>> <<for _s range _list>> <div class="module-item-flex"> <span class="item-name"><<=_s.label>></span> <span class="item-value"><<=_s.value>></span> </div> <</for>> </div> </div> </div> <div class="detailbutton"> <<do tag "wing-right">> <<link '<<if $("#wing-right").hasClass("show-detail")>>📄<<else>>👀<</if>>'>> <<toggleclass "#wing-right" "show-detail">> <<toggleclass "#wing-left" "show-detail">> <<redo "wing-right">><<redo "wing-left">> <</link>><</do>> </div> </div> <</if>> </div> <</widget>>
<<widget "TraitsList">> <<for _trait range Object.entries(_args[0].traits)>> <<set [_id, _data] = _trait>> <<HoverTip '<<TraitCard _id>>'>> <span class="trait-tag"><<= setup.traitdata[_id].id.startsWith("talent")? setup.traitdata_racial[_id].icon : setup.traitdata[_id].icon>> Lv<<= _data.lv>></span> <</HoverTip>> <</for>> <</widget>> <<widget "EffectsList">> <<for _effect range Object.entries(_args[0].effects)>> <<set [_id, _data] = _effect>> <<HoverTip "<<= getItemDescByID(_id)>> <br> From: <<= getItemNameByID(_id)>> <br>Lv: _data.lv">> <div class="buff-icon">💡</div> <</HoverTip>> <</for>> <</widget>> <<widget "Infocard">> <<do tag "selected charStatus">> <<set _clid = _args[0] >> <<set _crid = _args[1] >> <<capture _clid _crid>> <<if _clid >> <div class="infocard"> <div class="infocardtraits"> <<run isVirgin(_clid)>> <<TraitsList _clid>> </div> <div id="name"> <span style="font-size: 1em;"> _clid.name</span> <<do tag "itemchecked">> <<EffectsList _clid>> <</do>> </div> <div id="lv"> </div> <div id="race"> <span style="font-size: 1em;"><<= setup.racedata[_clid.race].icon>><<= setup.gender2pic[_clid.gender]>></span> </div> <div id="staap"> <span style="font-size: 1em;"> <<HoverTip "<<= L.actionrecovery>>" >>❤️<<= _clid.currentAPFloor>><</HoverTip>><<if _clid.harvest>> <<HoverTip "<<= L.harvest>>: <<= getiteminfo(_clid.harvest,'name')>><br> <<= getiteminfo(_clid.harvest,'desc')>><br><<= L.productionstock>><br><<= L.productalert>>" >>👨🌾<<= _clid.stamina.store>>/<<= _clid.getMaxStoreAp>><</HoverTip>><</if>></span> </div> <div id="getxp"> <span style="font-size: 1em;">XP:</span> <<if _crid>><<set _laddxp = _crid.giveExp >><<else>><<set _laddxp = 0 >><</if>><meter @value="_clid.currentExp('level') + _laddxp " min="0" @max="_clid.upgradeXp('level')"></meter> <span style="font-size: 1em;">+<<= _laddxp>></span> </div><span style="font-size: 1em;">Lv<<= _clid.level.lv>></span> <span style="position: relative; right: -3rem;"> <<HoverTip "Bloodline">><<= rankToBloodline(_clid.rank)>><</HoverTip>></span> <span style="position: relative; right: -4rem;"><<HoverTip "Growth Potential: sums a character’s attribute talents, driving faster core attribute growth. Higher values suggest worth for nurturing, not current attribute values.">>Growth: <<= _clid.getGrowth>><</HoverTip>></span> <div id="coreAttr"> <div id="infos" @data-rank="_clid.getRank('sta')"><span id="alv">STA <<= _clid.sta.lv>></span><span id="aop"><<= _clid.getRank("sta")>></span></div> <div id="infos" @data-rank="_clid.getRank('str')"><span id="alv">STR <<= _clid.str.lv>></span><span id="aop"><<= _clid.getRank("str")>></span></div> <div id="infos" @data-rank="_clid.getRank('dex')"><span id="alv">DEX <<= _clid.dex.lv>></span><span id="aop"><<= _clid.getRank("dex")>></span></div> <div id="infos" @data-rank="_clid.getRank('wil')"><span id="alv">WIL <<= _clid.wil.lv>></span><span id="aop"><<= _clid.getRank("wil")>></span></div> <div id="infos" @data-rank="_clid.getRank('cha')"><span id="alv">CHA <<= _clid.cha.lv>></span><span id="aop"><<= _clid.getRank("cha")>></span></div> <div id="infos" @data-rank="_clid.getRank('fer')"><span id="alv">FER <<= _clid.fer.lv>></span><span id="aop"><<= _clid.getRank("fer")>></span></div> <div id="energy"> <<HoverTip setup.langbank.energycost[$lang]>><span style="font-size: 1em;"><<= L.cost>><<= _clid.costEnergy>>⚡</span><</HoverTip>> </div> <div id="luck"> <<HoverTip setup.langbank.luckboost[$lang]>><span style="font-size: 1em;"><<= `${_clid.giveFer}%`;>><<= setup.langbank.fer[$lang]>></span><</HoverTip>> </div> </div> </div> <</if>> <</capture>><</do>> <</widget>>
<<widget "Infolist">> <style> .radar-character-card { padding: 0; } .data-display-cell .talent-stars { justify-content: flex-end; } .profile-plane { left: 0; width: 16rem; max-height: 90vh; z-index: 10; position: absolute; margin: 0.5rem; display: flex; flex-direction: column; gap: 0.3rem; overflow-y: auto; } .module { background: #2d2d2d; border: 1px solid #a57313; padding: 0.3rem; border-radius: 4px; } .module-title { background: var(--bg-body); color: #d4af37; font-size: 1rem; font-weight: bold; text-transform: uppercase; border-bottom: 1px solid #8b4513; text-align: center; } .module-content { line-height: 1.5; padding: 0.2rem; display: block; } .plcollapsed + .module-content { max-height: 0; display: none; } .profile-header { display: flex; flex-direction: column; gap: 0.2rem; } .stat-item { display: flex; justify-content: space-between; font-size: 0.875rem; padding: 1px 3px; border-bottom: 1px solid #636363; align-items: baseline; } .stat-full { line-height: 1; } .stat-full meter { block-size: 1.2em; inline-size: 100%; } .attribute-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(6rem, 1fr)); gap: 0.3rem; } .attribute-card { border: 1px solid var(--color-op); padding: 0 0.3rem; border-radius: 2px; line-height: 1.1; transition: border-color 0.2s, box-shadow 0.2s; } .attribute-card:hover { border-color: #9e8561; box-shadow: 0 0 8px rgba(158, 133, 97, 0.3); } .attribute-progress { background: linear-gradient(90deg, #8b4513 0%, #d4af37 50%, #6b0504 100%); block-size: 0.3em; inline-size: 100%; transition: width 0.5s ease; } .buff-icon { width: 24px; height: 24px; border: 1px solid #4682b4; padding: 2px; display: flex; align-items: center; justify-content: center; font-size: 16px; background: #2d2d2d; } .buff-stack { position: absolute; bottom: -4px; right: -4px; background: #8b4513; min-width: 14px; font-size: 0.7em; text-align: center; border-radius: 2px; } .effect-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(2rem, 1fr)); gap: 4px; } .toggle-indicator { display: inline-block; width: 1rem; text-align: center; margin-right: 0.2rem; color: #d4af37; } .module-title.plcollapsed .toggle-indicator::before { content: '▶'; } .module-title .toggle-indicator::before { content: '▼'; } </style> <<script>> window.toggleModule = function(moduleId) { const module = document.getElementById(moduleId); if (module) { module.classList.toggle('plexpanded'); module.classList.toggle('plcollapsed'); } } <</script>> <<if _args[0]>> <<set _infolistRole = _args[0] >> <div class="profile-plane"> <div class="module profile-header"> <div class="module-title" id="profile" onclick="toggleModule(this.id)"> <<do tag "rename">><<= _infolistRole.name>><</do>> <span class="toggle-indicator"></span></div> <div class="module-content"> <div class="stat-item"> <span>Kin: <<= _infolistRole.race.toUpperFirst()>> <span style="font-size: 1em;"><<= setup.racedata[_infolistRole.race].icon>></span></span> <span><<HoverTip "Bloodline">><<= rankToBloodline(_infolistRole.rank)>><</HoverTip>></span> </div> <div class="stat-item"> <span>Folk: <<HoverTip setup.racedata[_infolistRole.race][_infolistRole.rank]["desc"][$lang]>><<= setup.racedata[_infolistRole.race][_infolistRole.rank]["name"][$lang]>><</HoverTip>></span> <span><<HoverTip "Growth Potential: sums a character’s attribute talents, driving faster core attribute growth. Higher values suggest worth for nurturing, not current attribute values.">>Growth: <<= _infolistRole.getGrowth>><</HoverTip>></span> </div> <div class="stat-item"> <span><<= L.gender>>: <span style="font-size: 1em;"><<= setup.gender2pic[_infolistRole.gender]>></span></span> <span><<HoverTip "Appearance: _infolistRole.getLook <br>Representing a character's physical attractiveness,this attribute is heritable.">>Look: <<= setup.lookTier.tag(_infolistRole.getLook)>><</HoverTip>></span> </div> <div class="stat-item"> <span><<= L.mood>>: <<= _infolistRole.mood.vl>></span> <span>AP: <<HoverTip '<<= L.actionrecovery>>'>>❤️<<= _infolistRole.currentAPFloor>><</HoverTip>></span> </div> <div class="stat-full"> <meter @value="_infolistRole.currentExp('level')" min="0" @max="_infolistRole.upgradeXp('level')"></meter> <div class="stat-item"><span><<HoverTip "(Max: <<= _infolistRole.level.max>>)">><<= L.lv>>: <<=_infolistRole.level.lv>><</HoverTip>></span> <span><<if _infolistRole.level.lv < _infolistRole.level.max>>XP: <<= _infolistRole.progressText('level')>><<else>>LVMAX<</if>></span></div> </div> </div> </div> <div class="module"> <div class="module-title" id="coreattributes" onclick="toggleModule(this.id)">core attributes <span class="toggle-indicator"></span></div> <div class="module-content attribute-grid"> <<RadarImage _args[0]>> </div> </div> <div class="module"> <div class="module-title" id="subattributes" onclick="toggleModule(this.id)">Sub Attributes <span class="toggle-indicator"></span></div> <div class="module-content"> <div class="stat-item" style="flex-wrap: wrap;"> </div> <div class="stat-item"> <span>Parental: <<HoverTip _infolistRole.parental[0]>>📜<</HoverTip>><<HoverTip _infolistRole.parental[1]>>📜<</HoverTip>></span> <<HoverTip "<<if _infolistRole.offspring.length==0>>None<<else>>_infolistRole.offspring<</if>>">><span>Offspring: <<= _infolistRole.offspring.length>></span><</HoverTip>> </div> <div class="stat-item"> <<HoverTip "Ritual Experience Gift sets the experience points granted to others in summoning rituals, linked to STR.">><span>GiveExp: <<= _infolistRole.giveExp>></span><</HoverTip>> <<HoverTip "Experience Amplification multiplies experience points received from summoning rituals, tied to the character’s CHA.">><span>GetExp: <<= `${_infolistRole.chaBonusRate}%`>></span><</HoverTip>> </div> <div class="stat-item"> <<HoverTip "Ritual Luck Blessing provides luck in summoning rituals, also indicating pregnancy chance if conditions are met (see probability table).">><span>Give🌱: <<= `${_infolistRole.giveFer}%`>></span><</HoverTip>> <<HoverTip "Ritual Energy Cost determines the energy consumed by the character during summoning rituals, linked to DEX.">><span>Cost⚡: <<= _infolistRole.costEnergy>></span><</HoverTip>> </div> <div class="stat-item"> <<HoverTip "the action points gained each day, tied to the character’s STA value.">><span>Daily❤️: <<= _infolistRole.dailyAP.toFixed(1)>></span><</HoverTip>> <<HoverTip "Quest Reward Amplification indicates the percentage increase in rewards gained upon completing tasks. Higher values yield greater rewards from quests.">><span>Value: <<= `${(_infolistRole.value.pricemul * 100).toFixed(0)}%`>></span><</HoverTip>> </div> <<if _infolistRole.harvest>> <div class="stat-item"> <<HoverTip "<<= getiteminfo(_infolistRole.harvest,'name')>> <br> <<= getiteminfo(_infolistRole.harvest,'desc')>><br>">><span>Harvest: 👨🌾</span><</HoverTip>> <<HoverTip "Harvest Yield tracks the current and maximum capacity of a Monster’s unique produce, tied to WIL.">><span><<= L.productionstock>>: <<= (_infolistRole.stamina?.store ?? 0)>>/<<= _infolistRole.getMaxStoreAp>></span><</HoverTip>> </div> <</if>> <div class="stat-item"> <span>Stamina: <<= Math.floor(_infolistRole.stamina.current)>> / <<= Math.floor(_infolistRole.dailyStamina)>></span> <span>Mental: <<= Math.floor(_infolistRole.mental.current)>> / <<= Math.floor(_infolistRole.getMental)>></span> </div> </div> </div> <div class="module"> <div class="module-title" id="traits" onclick="toggleModule(this.id)"><<= setup.langbank.traits[$lang]>> <span class="toggle-indicator"></span></div> <div class="module-content" trait-grid> <<TraitsList _infolistRole>> </div> </div> <div class="module"> <div class="module-title" id="effects" onclick="toggleModule(this.id)"><<= setup.langbank.effects[$lang]>> <span class="toggle-indicator"></span></div> <div class="module-content effect-grid"> <<EffectsList _infolistRole>> </div> </div> </div> <</if>> <</widget>>
<<widget "MapList">> <style> .maplist-container { height: 100%; max-width: 60rem; display: flex; gap: 0.5rem; min-height: 20rem; } .filter-panel { width: 10.5rem; background-color: var(--bg-surface); border-radius: 0.25rem; border: 1px solid var(--color-c); display: flex; flex-direction: column; overflow: hidden; } .filter-section { overflow-y: auto; padding: 0 0.5rem 0.25rem; border-bottom: 1px solid var(--cm-border); } .section-title { font-size: 1rem; margin-bottom: 0.5rem; color: var(--color-e); display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--color-c); } .section-title i { color: var(--color-e); font-size: 0.95rem; } .search-box { position: relative; } .search-input { width: 100%; padding: 0.5rem 2rem 0.5rem 0.5rem; background-color: var(--bg-elevated); border: 0.125rem solid var(--color-c); border-radius: 0.25rem; color: var(--text-primary); font-size: 0.9rem; transition: all 0.2s; } .search-input:focus { outline: none; border-color: var(--color-e); } .search-icon { position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); color: var(--color-e); cursor: pointer; } .race-icons { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.25rem; } .race-icon { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 0.25rem; background-color: var(--bg-elevated); border-radius: 0.25rem; border: 1px solid var(--color-c); cursor: pointer; transition: all 0.2s; text-align: center; } .race-icon:hover { border-color: var(--color-e); transform: translateY(-2px); } .race-icon.active { border-color: var(--color-e); background-color: rgba(120, 90, 40, 0.1); } .race-icon i { font-size: 2rem; margin-bottom: 0.5rem; color: var(--color-e); } .race-name { font-size: 0.85rem; color: var(--text-primary); font-weight: bold; } .filter-options { display: flex; flex-direction: column; gap: 1rem; } .filter-group { display: flex; flex-direction: column; gap: 0.5rem; } .filter-label { font-size: 0.9rem; color: var(--text-secondary); display: flex; justify-content: space-between; } .filter-select { background-color: var(--bg-elevated); border: 0.125rem solid var(--color-c); border-radius: 0.25rem; color: var(--text-primary); font-size: 0.9rem; cursor: pointer; transition: border-color 0.2s; } .filter-select:focus { outline: none; border-color: var(--color-e); } .range-slider { display: flex; flex-direction: column; gap: 0.75rem; } .range-values { display: flex; justify-content: space-between; font-size: 0.85rem; color: var(--text-secondary); } .range-input { width: 100%; height: 0.375rem; -webkit-appearance: none; background: var(--bg-elevated); border-radius: 0.1875rem; outline: none; } .range-input::-webkit-slider-thumb { -webkit-appearance: none; width: 1.125rem; height: 1.125rem; background: var(--color-e); border-radius: 50%; cursor: pointer; border: 0.125rem solid var(--bg-surface); } .range-input::-moz-range-thumb { width: 1.125rem; height: 1.125rem; background: var(--color-e); border-radius: 50%; cursor: pointer; border: 0.125rem solid var(--bg-surface); } .tag-cloud { overflow-y: auto; } .tags-container { display: flex; flex-wrap: wrap; gap: 0.2rem; margin-top: 0.5rem; } .tag-item { padding: 0.25rem 0.5rem; background-color: var(--bg-elevated); border-radius: 0.25rem; font-size: 0.85rem; color: var(--text-secondary); cursor: pointer; transition: all 0.2s; border: 1px solid var(--color-c); } .tag-item:hover { background-color: var(--bg-deepred); color: var(--text-white); border-color: var(--color-e); } .tag-item.active { background-color: var(--color-e); color: var(--bg-body); border-color: var(--color-e); } .filter-actions { display: flex; gap: 0.75rem; } .filter-btn { flex: 1; padding: 0.75rem; background-color: var(--bg-elevated); border: 0.125rem solid var(--color-c); border-radius: 0.25rem; color: var(--text-primary); cursor: pointer; font-size: 0.9rem; transition: all 0.2s; font-weight: bold; } .filter-btn:hover { background-color: var(--bg-deepred); color: var(--text-white); border-color: var(--color-e); } .filter-btn.primary { background-color: var(--bg-deepred); border-color: var(--color-e); color: var(--text-white); } .filter-btn.primary:hover { background-color: var(--color-e); color: var(--bg-body); } .content-panel { flex: 1; display: flex; flex-direction: column; background-color: var(--bg-surface); border-radius: 0.25rem; border: 1px solid var(--color-c); overflow: hidden; } .list-toolbar { display: flex; justify-content: space-between; align-items: center; padding: 0.5rem 0.25rem; border-bottom: 1px var(--cm-border); background-color: var(--bg-elevated); } .list-info { font-size: 0.95rem; color: var(--text-secondary); padding: 0.5rem; border-bottom: 1px solid var(--cm-border); } .list-info strong { color: var(--color-e); } .sort-toolbar { overflow-x: auto; display: flex; align-items: center; gap: 0.5rem; } .sort-label { font-size: 0.9rem; color: var(--text-secondary); margin-right: 0.5rem; } .sort-btn { width: 2.25rem; height: 2.25rem; display: flex; align-items: center; justify-content: center; background-color: var(--bg-surface); border: 0.125rem solid var(--color-c); border-radius: 0.25rem; color: var(--text-secondary); cursor: pointer; transition: all 0.2s; } .sort-btn:hover { background-color: var(--bg-deepred); color: var(--text-white); border-color: var(--color-e); } .sort-btn.active { background-color: var(--color-e); border-color: var(--color-e); color: var(--bg-body); } .view-options { display: flex; gap: 0.5rem; align-items: center; } .view-btn { width: 2.25rem; height: 2.25rem; display: flex; align-items: center; justify-content: center; background-color: var(--bg-surface); border: 0.125rem solid var(--color-c); border-radius: 0.25rem; color: var(--text-secondary); cursor: pointer; transition: all 0.2s; } .view-btn:hover { background-color: var(--bg-deepred); color: var(--text-white); border-color: var(--color-e); } .view-btn.active { background-color: var(--color-e); border-color: var(--color-e); color: var(--bg-body); } .character-list { flex: 1; padding: 0.5rem; overflow-y: auto; background-color: var(--bg-body); } .character-list.card-view { display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.5rem; align-content: start; } .character-list.list-view { display: flex; flex-direction: column; gap: 0.5rem; } .character-list.compact-view { display: grid; grid-template-columns: repeat(auto-fill, minmax(6rem, 1fr)); gap: 0.5rem; align-content: start; } .character-card { min-height: 3.5rem; max-height: 17.5rem; background-color: var(--bg-surface); border-radius: 0.25rem; border: 2px solid var(--color-c); overflow: hidden; transition: all 0.3s; cursor: pointer; } .character-card:hover { transform: translateY(-0.25rem); border-color: var(--color-e); } .character-card.selected { border-color: var(--color-e); } .card-view .character-card { height: 17rem; display: flex; flex-direction: column; } .card-view .card-header { position: relative; display: flex; align-items: center; padding: 0.5rem; background-color: var(--bg-elevated); border-bottom: 1px solid var(--cm-border); flex-direction: column; } .card-view .character-avatar { height: 8rem; border-radius: 5%; display: flex; font-size: 1.5rem; color: var(--text-white); border: 1px solid var(--color-c); background-color: var(--bg-deepred); } .card-view .character-avatar img { object-fit: scale-down; height: 100%; border: 0; } .card-view .character-avatar div { height: 100%; } .card-view .character-info { flex: 1; overflow: hidden; } .card-view .character-name { text-align: center; width: 100%; background: #00000052; bottom: 0; position: absolute; font-size: 1rem; font-weight: bold; color: var(--text-white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .card-view .character-race { font-size: 0.85rem; color: var(--text-secondary); display: flex; align-items: center; gap: 0.5rem; } .card-view .race-badge { padding: 0 0.5rem; border-radius: 0.25rem; font-size: 0.75rem; background-color: rgba(200, 160, 80, 0.2); color: var(--color-e); border: 1px solid var(--color-c); } .card-view .card-body { padding: 0.5rem; } .card-view .character-stats { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.5rem; } .card-view .stat-solt { display: flex; gap: 0.25rem; } .card-view .stat-label { font-size: 1rem; color: var(--text-secondary); } .card-view .stat-value { font-size: 0.95rem; font-weight: bold; color: var(--text-primary); } .card-view .stat-bar { height: 0.375rem; background-color: var(--bg-elevated); border-radius: 0.1875rem; overflow: hidden; margin-top: 0.125rem; border: 1px solid var(--color-c); } .card-view .stat-fill { height: 100%; border-radius: 0.1875rem; } .card-view .hp-fill { background-color: var(--dm-physical); } .card-view .mp-fill { background-color: var(--dm-cold); } .card-view .card-footer { padding: 0.5rem; border-top: 1px solid var(--cm-border); display: flex; justify-content: space-between; align-items: center; background-color: var(--bg-elevated); } .card-view .character-tags { height: 2rem; display: flex; gap: 0.125rem; flex-wrap: wrap; } .card-view .character-tag { padding: 0 0.25rem; background-color: rgba(200, 160, 80, 0.1); border-radius: 0.25rem; font-size: 0.95rem; color: var(--color-e); border: 1px solid var(--color-c); } .card-view .character-actions { display: flex; gap: 0.5rem; } .card-view .action-icon { font-size: 1rem; right: 0.5rem; position: absolute; cursor: pointer; transition: all 0.2s; } .card-view .action-icon:hover { background-color: var(--bg-deepred); color: var(--text-white); border-color: var(--color-e); } .list-view .character-card { display: flex; align-items: center; padding: 0.5rem 1rem; } .list-view .character-avatar { width: 2.5rem; height: 2.5rem; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1.25rem; color: var(--text-white); margin-right: 1rem; flex-shrink: 0; border: 0.125rem solid var(--color-c); background-color: var(--bg-deepred); } .list-view .character-info { flex: 1; display: flex; align-items: center; gap: 1rem; } .list-view .character-name { font-size: 1rem; font-weight: bold; color: var(--text-white); width: 3.5rem; } .list-view .character-race { font-size: 0.85rem; color: var(--text-secondary); width: 3rem; } .list-view .character-stats { display: flex; gap: 1rem; } .list-view .stat-solt { display: flex; flex-direction: column; align-items: center; min-width: 2rem; } .list-view .stat-label { font-size: 0.75rem; color: var(--text-secondary); } .list-view .stat-value { font-size: 0.9rem; font-weight: bold; color: var(--text-primary); } .list-view .character-actions { display: flex; gap: 0.5rem; margin-left: auto; } .compact-view .character-card { min-height: 10.5rem; display: flex; flex-direction: column; align-items: center; padding: 0.5rem; text-align: center; } .compact-view .character-avatar { width: 6rem; height: 6rem; border-radius: 5%; display: flex; align-items: center; justify-content: center; font-size: 1.5rem; color: var(--text-white); border: 0.125rem solid var(--color-c); background-color: var(--bg-deepred); } .compact-view .character-avatar img { object-fit: scale-down; height: 100%; border: 0; } .compact-view .character-avatar div { height: 100%; } .compact-view .character-name { font-size: 0.95rem; font-weight: bold; color: var(--text-white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; width: 100%; } .compact-view .character-race { font-size: 0.8rem; color: var(--text-secondary); margin-bottom: 0.75rem; } .compact-view .character-stats { display: flex; justify-content: space-around; width: 100%; margin-bottom: 0.75rem; } .compact-view .stat-solt { display: flex; flex-direction: column; align-items: center; } .compact-view .stat-label { font-size: 0.7rem; color: var(--text-secondary); } .compact-view .stat-value { font-size: 0.85rem; font-weight: bold; color: var(--text-primary); } .compact-view .character-actions { display: flex; gap: 0.5rem; } .action-bar { background-color: var(--bg-surface); padding: 0.5rem; } .action-title { font-size: 1rem; margin-bottom: 0.75rem; color: var(--color-e); display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--color-c); padding-bottom: 0.5rem; } .action-buttons { grid-template-columns: repeat(auto-fill, minmax(4rem, 1fr)); display: grid; gap: 0.5rem; } .action-btn { padding: 0.75rem 0.5rem; background-color: var(--bg-elevated); border: 0.125rem solid var(--color-c); border-radius: 0.25rem; color: var(--text-primary); cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 0.375rem; transition: all 0.2s; font-size: 0.8rem; } .action-btn:hover { background-color: var(--bg-deepred); color: var(--text-white); border-color: var(--color-e); transform: translateY(-0.125rem); } .action-btn i { font-size: 1rem; color: var(--color-e); } .tag-management { margin-top: 0.75rem; border-top: 1px solid var(--color-c); } .summon-management { height: 5rem; margin-top: 0.75rem; border-top: 1px solid var(--color-c); } .tag-input-row { display: flex; gap: 0.5rem; margin: 0.5rem 0; } .tag-input { flex: 1; padding: 0.5rem 0.75rem; background-color: var(--bg-elevated); border: 0.125rem solid var(--color-c); border-radius: 0.375rem; color: var(--text-primary); font-size: 0.85rem; } .tag-input:focus { outline: none; border-color: var(--color-e); } .tag-add-btn { padding: 0.5rem 1rem; background-color: var(--bg-deepred); border: 0.125rem solid var(--color-e); border-radius: 0.375rem; color: var(--text-white); cursor: pointer; font-size: 0.85rem; transition: background-color 0.2s; font-weight: bold; } .tag-add-btn:hover { background-color: var(--color-e); color: var(--bg-body); } .tag-cloud-list { display: flex; flex-wrap: wrap; gap: 0.5rem; max-height: 8rem; overflow-y: auto; padding: 0.5rem; background-color: var(--bg-elevated); border-radius: 0.375rem; border: 1px solid var(--color-c); } .tag-cloud-item { padding: 0 0.25rem; background-color: var(--bg-surface); border-radius: 0.25rem; font-size: 1rem; color: var(--text-secondary); cursor: pointer; transition: all 0.2s; border: 1px solid var(--color-c); } .tag-cloud-item:hover { background-color: var(--bg-deepred); color: var(--text-white); border-color: var(--color-e); } .tag-cloud-item.selected { background-color: var(--color-e); color: var(--bg-body); border-color: var(--color-e); } .team-selection { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--color-c); } .team-options { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.75rem; } .team-option { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem; background-color: var(--bg-elevated); border-radius: 0.375rem; cursor: pointer; transition: background-color 0.2s; border: 1px solid var(--color-c); } .team-option:hover { background-color: var(--bg-deepred); border-color: var(--color-e); } .team-option input { accent-color: var(--color-e); } .team-option span { color: var(--text-primary); font-size: 0.85rem; } .empty-state { grid-column: 1 / -1; text-align: center; padding: 3rem 1.25rem; color: var(--text-secondary); } .empty-state i { font-size: 2rem; margin-bottom: 1rem; opacity: 0.5; color: var(--color-c); } .empty-state h3 { font-size: 1.1rem; margin-bottom: 0.5rem; color: var(--text-secondary); } .summon-list { display: flex; } .summon-btn { width: 8.5rem; height: 4rem; } @media (max-width: 768px) { .maplist-container { max-width: 30rem; gap: 0rem; min-height: 20rem; } } </style> <div class="maplist-container"> <div class="filter-panel"> <div class="list-info"> Total: <strong id="display-count">0</strong> / <strong id="total-count">0</strong> </div> <div class="filter-section"> <div class="search-box"> <input type="text" class="search-input" id="search-input" placeholder="Name /Tag ..." style="min-width: 8rem;"> <div class="search-icon" id="search-btn"> <i class="fas fa-search"></i> </div> </div> </div> <div class="filter-section"> <div class="section-title"> <span>Species Filter</span> <i class="fas fa-sliders-h"></i> </div> <div class="race-icons" id="race-icons"> </div> </div> <!-- 属性筛选<i class="fas fa-sliders-h"></i> --> <div class="filter-section"> <div class="section-title"> <span>Rarity Filter</span> <i class="fa-regular fa-gem"></i> </div> <div class="filter-options"> <select class="filter-select" id="rarity-filter"> <option value=9 disabled>SupApex</option> <option value=8 disabled>Mythic</option> <option value=7 disabled>Legendary</option> <option value=6 disabled>Epic</option> <option value=5 disabled>Elite</option> <option value=4 disabled>Rare</option> <option value=3>Prime</option> <option value=2>Common</option> <option value=1>Lesser</option> <option value=0>Cursed</option> <option value="all" selected>All</option> </select> </div> </div> <div class="filter-section tag-cloud"> <div class="section-title"> <span>Tag Filter</span> <i class="fa-solid fa-tags"></i> </div> <div class="tags-container" id="tags-container"> </div> </div> <div class="filter-section"> <div class="filter-actions"> <button class="filter-btn" id="reset-filters">Reset</button> <button class="filter-btn primary" id="apply-filters">Apply</button> </div> </div> </div> <div class="content-panel"> <div class="list-toolbar"> <div class="sort-toolbar"> <div class="sort-btn" data-sort="order" title="Sort by Original Order"> <i class="fas fa-history"></i> </div> <div class="sort-btn" data-sort="name" title="Sort by Name"> <i class="fas fa-font"></i> </div> <div class="sort-btn" data-sort="level" title="Sort by Level"> <i class="fas fa-level-up-alt"></i> </div> <div class="sort-btn" data-sort="hp" title="Sort by AP"> <i class="fas fa-heart"></i> </div> <div class="sort-btn" data-sort="sta" title="Sort by Stamina"> <i class="fa-solid fa-hourglass-half"></i> </div> <div class="sort-btn" data-sort="str" title="Sort by Strength"> <i class="fas fa-fist-raised"></i> </div> <div class="sort-btn" data-sort="cha" title="Sort by Charisma"> <i class="fas fa-comments"></i> </div> <div class="sort-btn" data-sort="dex" title="Sort by Dexterity"> <i class="fas fa-wind"></i> </div> <div class="sort-btn" data-sort="wil" title="Sort by Willpower"> <i class="fas fa-eye"></i> </div> <div class="sort-btn" data-sort="fer" title="Sort by Fertility"> <i class="fa-solid fa-seedling"></i> </div> </div> <div class="view-options"> <div class="view-btn" data-view="card" title="Card View"> <i class="fas fa-th-large"></i> </div> <div class="view-btn" data-view="list" title="List View"> <i class="fas fa-list"></i> </div> <div class="view-btn" data-view="compact" title="Compact View"> <i class="fas fa-grip-horizontal"></i> </div> </div> </div> <div class="character-list card-view" id="character-list"> </div> <div class="action-bar"> <div class="summon-management" id="summon-management" style="display: none;"> <div class="summon-list" id="summon-list"> <<do tag "jionslot">> <<button "<div class='filter-btn summon-btn'>Jion Left Solt<br>🌌<<= $summon.slots[0]?.name || 'Nobody'>></div>">> <<set $summon.slots[0] = $chars.selected >> <<set $summon.slotsId[0] = $chars.selected.id >> <<if $summon.slots[0]?.id == $summon.slots[1]?.id>> <<set $summon.slots[1] = null >> <<set $summon.slotsId[1] = null >> <</if>> <<redo "jionslot">> <</button>> <<button "<div class='filter-btn summon-btn'>Jion Right Solt<br>🌌<<= $summon.slots[1]?.name || 'Nobody'>></div>">> <<set $summon.slots[1] = $chars.selected>> <<set $summon.slotsId[1] = $chars.selected.id >> <<if $summon.slots[1]?.id == $summon.slots[0]?.id>> <<set $summon.slots[0] = null >> <<set $summon.slotsId[0] = null >> <</if>> <<redo "jionslot">> <</button>> <<button "<div class='filter-btn summon-btn'>Goto Summon</div>">> <<goto "Summon_UI">> <</button>><</do>> </div> </div> <div class="tag-management" id="tag-management" style="display: none;"> <div class="tag-cloud-list" id="tag-cloud-list"> </div> <div class="tag-input-row"> <input type="text" class="tag-input" id="tag-input" placeholder="Enter new tag. Supports search and multi-select filtering"> <button class="tag-add-btn" id="tag-add-btn">Add</button> </div> </div> <div class="action-title"> <span>Character Actions</span> <span id="action-target">No Selected</span> </div> <div class="action-buttons"> <button class="action-btn" data-action="tag" id="tag-action-btn"> <i class="fa-solid fa-tags"></i> <span>Tags</span> </button> <<if passage() == "Monsters_UI">><button class="action-btn" data-action="summon"> <i class="fa-solid fa-user-plus"></i> <span>Summon</span> </button><</if>> <<if passage() == "Monsters_UI">><button class="action-btn" data-action="harvest"> <i class="fa-solid fa-bucket"></i> <span>Harvest</span> </button><</if>> <<if passage() == "Monsters_UI">><button class="action-btn" data-action="moveout"> <i class="fa-solid fa-person-walking-dashed-line-arrow-right"></i> <span>MoveOut</span> </button><</if>> <<if Flag('entrain') == 2 && passage() == "Monsters_UI">><button class="action-btn" data-action="train"> <i class="fa-solid fa-person-praying"></i> <span>Train</span> </button><</if>> <<if Flag('enmeeting') == 2>><button class="action-btn" data-action="stopjob"> <i class="fas fa-mug-hot"></i> <span>StopJob</span> </button><</if>> <button class="action-btn" data-action="rename"> <i class="fa-solid fa-pen"></i> <span>Rename</span> </button> </div> <div class="team-selection" id="team-selection" style="display: none;"> <div class="section-title" style="font-size: 0.95rem; margin-bottom: 0.5rem;"> <span>Select Team</span> </div> <div class="team-options" id="team-options"> <!-- 动态生成队伍选项 --> </div> </div> </div> </div> </div> <<done>> <<script>> let charMap = State.temporary.args[1] || State.variables.chars.dataMap; let charList = State.temporary.args[0]; let availableTags = State.variables.chars.availableTags; const teamOptions = ["team1", "team2", "team3", "team4"]; initDataStructures(); initClassIcons(); initTagCloud(); initTeamOptions(); initViewState(); renderCharacterList(); initEventListeners(); updateCounts(); function initDataStructures() { } function initViewState() { const savedViewMode = State.variables.chars.viewMode || 'card'; const savedSortMode = State.variables.chars.sortBy || 'ap'; document.querySelector(`.view-btn[data-view="${savedViewMode}"]`)?.classList.add('active'); document.querySelector(`.sort-btn[data-sort="${savedSortMode}"]`)?.classList.add('active'); } function initClassIcons() { const classIconsContainer = document.getElementById('race-icons'); classIconsContainer.innerHTML = ''; const racekeys = Object.keys(setup.iconSpriteData.species64.frames); racekeys.forEach(key => { const classIcon = document.createElement('div'); classIcon.className = 'race-icon'; classIcon.dataset.classId = key; if (State.variables.chars.filters.races.includes(key)) { classIcon.classList.add('active'); }; const classIconHTML = ` <<showframe 'species64' "${key}" 3.5>> `; $(classIcon).wiki(classIconHTML); classIconsContainer.appendChild(classIcon); }); } function initTagCloud() { const tagsContainer = document.getElementById('tags-container'); const tagCloudList = document.getElementById('tag-cloud-list'); tagsContainer.innerHTML = ''; tagCloudList.innerHTML = ''; availableTags.forEach(tag => { const tagElement = document.createElement('div'); tagElement.className = 'tag-item'; tagElement.textContent = tag; tagElement.dataset.tag = tag; if (State.variables.chars.filters.tags.includes(tag)) { tagElement.classList.add('active'); }; tagsContainer.appendChild(tagElement); }); availableTags.forEach(tag => { const tagElement = document.createElement('div'); tagElement.className = 'tag-cloud-item'; tagElement.textContent = tag; tagElement.dataset.tag = tag; tagCloudList.appendChild(tagElement); }); } function initTeamOptions() { const teamOptionsContainer = document.getElementById('team-options'); teamOptionsContainer.innerHTML = ''; teamOptions.forEach(team => { const teamOption = document.createElement('label'); teamOption.className = 'team-option'; teamOption.innerHTML = ` <input type="radio" name="team" value="${team}"> <span>${team}</span> `; teamOptionsContainer.appendChild(teamOption); }); } function initEventListeners() { document.getElementById('search-input').addEventListener('input', function() { State.variables.chars.filters.search = this.value; applyFilters(); }); document.getElementById('search-btn').addEventListener('click', function() { applyFilters(); }); document.querySelectorAll('.race-icon').forEach(icon => { icon.addEventListener('click', function() { const classId = this.dataset.classId; this.classList.toggle('active'); const activeraces = Array.from(document.querySelectorAll('.race-icon.active')) .map(icon => icon.dataset.classId); State.variables.chars.filters.races = activeraces; applyFilters(); }); }); document.getElementById('rarity-filter').addEventListener('change', function() { State.variables.chars.filters.rarity = this.value; applyFilters(); }); document.querySelectorAll('.tag-item').forEach(tag => { tag.addEventListener('click', function() { const tagName = this.dataset.tag; this.classList.toggle('active'); const activeTags = Array.from(document.querySelectorAll('.tag-item.active')) .map(tag => tag.dataset.tag); State.variables.chars.filters.tags = activeTags; applyFilters(); }); }); document.querySelectorAll('.sort-btn').forEach(btn => { btn.addEventListener('click', function() { const sortId = this.dataset.sort; if (sortId === State.variables.chars.sortBy) { State.variables.chars.sortDirection = State.variables.chars.sortDirection === 'asc' ? 'desc' : 'asc'; } else { State.variables.chars.sortBy = sortId; State.variables.chars.sortDirection = 'desc'; } document.querySelectorAll('.sort-btn').forEach(b => { b.classList.remove('active'); }); this.classList.add('active'); applyFilters(); }); }); document.querySelectorAll('.view-btn').forEach(btn => { btn.addEventListener('click', function() { const viewMode = this.dataset.view; document.querySelectorAll('.view-btn').forEach(b => { b.classList.remove('active'); }); this.classList.add('active'); State.variables.chars.viewMode = viewMode; renderCharacterList(); }); }); document.getElementById('reset-filters').addEventListener('click', function() { resetFilters(); }); document.getElementById('apply-filters').addEventListener('click', function() { applyFilters(); }); document.querySelectorAll('.action-btn').forEach(btn => { btn.addEventListener('click', function() { const action = this.dataset.action; handleAction(action); }); }); document.getElementById('tag-add-btn').addEventListener('click', function() { addNewTag(); }); document.getElementById('tag-input').addEventListener('keypress', function(e) { if (e.key === 'Enter') { addNewTag(); } }); document.getElementById('tag-cloud-list').addEventListener('click', function(e) { if (e.target.classList.contains('tag-cloud-item')) { toggleCharacterTag(e.target.dataset.tag); } }); document.getElementById('team-options').addEventListener('change', function(e) { if (e.target.type === 'radio') { assignCharacterToTeam(e.target.value); } }); document.getElementById('character-list').addEventListener('click', function(e) { const card = e.target.closest('.character-card'); if (!card) return; const charId = card.dataset.characterId; const actionIcon = e.target.closest('.action-icon'); if (actionIcon) { const action = actionIcon.dataset.action; if (action === 'favorite') { toggleFavorite(charId); } e.stopPropagation(); } else { selectCharacter(charId); } }); } function resetFilters() { State.variables.chars.filters = { search: "", races: [], rarity: "all", tags: [] }; document.getElementById('search-input').value = ''; document.querySelectorAll('.race-icon').forEach(icon => icon.classList.remove('active')); document.getElementById('rarity-filter').value = 'all'; document.querySelectorAll('.tag-item').forEach(tag => tag.classList.remove('active')); applyFilters(); } function applyFilters() { renderCharacterList(); updateCounts(); } function renderCharacterList() { const characterListContainer = document.getElementById('character-list'); let filteredCharacters = filterCharacters(); filteredCharacters = sortCharacters(filteredCharacters); document.getElementById('display-count').textContent = filteredCharacters.length; characterListContainer.className = `character-list ${State.variables.chars.viewMode}-view`; characterListContainer.innerHTML = ''; if (filteredCharacters.length === 0) { let emptyMessage = ` <div class="empty-state"> <i class="fas fa-search"></i> <h3>No Matching Characters Found</h3> <p>Try adjusting filter conditions or search keywords</p> `; const activeTags = State.variables.chars.filters.tags; if (activeTags.length === 1) { const activeTag = activeTags[0]; emptyMessage += ` <div class="empty-state-actions" style="margin-top: 20px;"> <button class="delete-tag-btn" data-tag="${activeTag}" style="padding: 8px 16px; background: var(--bg-deepred); color: white; border: none; border-radius: 4px; cursor: pointer;"> <i class="fas fa-trash"></i> Delete Tag "${activeTag}" </button> </div> `; } emptyMessage += `</div>`; characterListContainer.innerHTML = emptyMessage; const deleteBtn = characterListContainer.querySelector('.delete-tag-btn'); if (deleteBtn) { deleteBtn.addEventListener('click', function() { const tagToDelete = this.dataset.tag; deleteUnusedTag(tagToDelete); }); } return; } const fragment = document.createDocumentFragment(); filteredCharacters.forEach(characterId => { const characterCard = createCharacterCard(characterId); fragment.appendChild(characterCard); }); characterListContainer.appendChild(fragment); } function deleteUnusedTag(tag) { if (!confirm(`Delete tag "${tag}"? This will remove it from all characters.`)) { return; } let affectedCount = 0; charList.forEach(characterid => { const character = charMap.get(characterid); const index = character.tags.indexOf(tag); if (index !== -1) { character.tags.splice(index, 1); charMap.set(characterid, character); affectedCount++; } }); const tagIndex = availableTags.indexOf(tag); if (tagIndex !== -1) { availableTags.splice(tagIndex, 1); } State.variables.chars.filters.tags = State.variables.chars.filters.tags.filter(t => t !== tag); updateTagCloudUI(); renderCharacterList(); if (affectedCount > 0) { Msg.flash(`Tag "${tag}" removed from ${affectedCount} characters`); } else { Msg.flash(`Tag "${tag}" deleted`); } } function filterCharacters() { const allCharacters = charList; return allCharacters.filter(characterId => { const character = charMap.get(characterId); if (State.variables.chars.filters.search) { const searchLower = State.variables.chars.filters.search.toLowerCase(); const nameMatch = character.name.toLowerCase().includes(searchLower); const classMatch = character.race.toLowerCase().includes(searchLower); const tagMatch = character.tags.some(tag => tag.toLowerCase().includes(searchLower)); if (!nameMatch && !classMatch && !tagMatch) { return false; } } if (State.variables.chars.filters.races.length > 0) { let characterClassId = ''; switch(character.race) { case 'xxx': characterClassId = 'xxx'; break; default: characterClassId = character.race.toLowerCase().replace(/\s+/g, ''); } if (!State.variables.chars.filters.races.includes(characterClassId)) { return false; } } if (State.variables.chars.filters.rarity !== 'all' && character.rank !== parseInt(State.variables.chars.filters.rarity)) { return false; } if (State.variables.chars.filters.tags.length > 0) { const hasMatchingTag = State.variables.chars.filters.tags.some(tag => character.tags.includes(tag) ); if (!hasMatchingTag) { return false; } } return true; }); } function sortCharacters(characterIds) { return characterIds.sort((a, b) => { let aValue, bValue; switch(State.variables.chars.sortBy) { case 'order': aValue = charList.indexOf(a); bValue = charList.indexOf(b); break; case 'name': aValue = charMap.get(a).name; bValue = charMap.get(b).name; break; case 'level': aValue = charMap.get(a).level.lv; bValue = charMap.get(b).level.lv; break; case 'hp': aValue = charMap.get(a).stamina.currentAP; bValue = charMap.get(b).stamina.currentAP; break; case 'sta': aValue = charMap.get(a).sta.lv; bValue = charMap.get(b).sta.lv; break; case 'str': aValue = charMap.get(a).str.lv; bValue = charMap.get(b).str.lv; break; case 'cha': aValue = charMap.get(a).cha.lv; bValue = charMap.get(b).cha.lv; break; case 'dex': aValue = charMap.get(a).dex.lv; bValue = charMap.get(b).dex.lv; break; case 'wil': aValue = charMap.get(a).wil.lv; bValue = charMap.get(b).wil.lv; break; case 'fer': aValue = charMap.get(a).fer.lv; bValue = charMap.get(b).fer.lv; break; default: aValue = charMap.get(a).index; bValue = charMap.get(b).index; }; if (State.variables.chars.sortDirection === 'asc') { return aValue > bValue ? 1 : -1; } else { return aValue < bValue ? 1 : -1; } }); } function createCharacterCard(characterId) { const character = charMap.get(characterId); const card = document.createElement('div'); card.className = `character-card ${State.variables.chars.selected?.id === character.id ? 'selected' : ''}`; card.dataset.characterId = character.id; let rarityColor = 'var(--text-secondary)'; switch(character.rarity) { case 'common': rarityColor = 'var(--tr-common)'; break; case 'rare': rarityColor = 'var(--tr-B)'; break; case 'epic': rarityColor = 'var(--tr-LEG)'; break; case 'legendary': rarityColor = 'var(--tr-SS)'; break; } if (State.variables.chars.viewMode === 'card') { const raceIcon = setup.racedata[character.race]?.icon || ""; const genderPic = setup.gender2pic[character.gender] || ""; const uniqueIcon = character.type == "unique" ? "🔒" : ""; const pregIcon = character.preg > 0 ? "🤰" : ""; const workTags = character.worktags || ""; const virginIcon = isVirgin(character) ? "🌸" : ""; const stunIcon = character.traits["PlaneStun"] ? "💫" : ""; const htmlContent = ` <div class="card-header"> <div class="character-avatar" style="background-color: ${character.color};"> <<HeadImageByID ${character.id}>> </div> <div class="character-name">${character.name}</div> <div class="action-icon" data-action="favorite" title="${character.favorited ? 'Remove Favorite' : 'Add Favorite'}"> <i class="${character.favorited ? 'fas' : 'far'} fa-star"></i> </div> </div> <div class="card-footer" style="padding: 0 0.5rem;"> <div class="character-tags"> <span>${raceIcon}</span> <span>${genderPic}</span> <span>${uniqueIcon}</span> <span>${pregIcon}</span> <span>${workTags}</span> <span>${virginIcon}</span> <span>${stunIcon}</span> </div> </div> <div class="card-body"> <div class="character-stats"> <div class="character-race"> <span class="race-badge">Lv.${character.level.lv}</span> </div> <div class="stat-solt"> <div class="stat-label">💖</div> <div class="stat-value">${character.currentAPFloor}</div> </div> <<if ${character.race != "human"}>><div class="stat-solt"> <div class="stat-label">👨🌾</div> <div class="stat-value">${character.stamina.store}</div> </div><</if>> </div> </div> <div class="card-footer"> <div class="character-tags"> ${character.tags.slice(0, 3).map(tag => `<span class="character-tag">${tag}</span>`).join('')} ${character.tags.length > 3 ? `<span class="character-tag">+${character.tags.length - 3}</span>` : ''} </div> </div> `; card.innerHTML = ""; $(card).wiki(htmlContent); } else if (State.variables.chars.viewMode === 'list') { const htmlContent = ` <div class="character-info"> <div class="character-name">${character.name}</div> <div class="character-race"><<= setup.racedata["${character.race}"].icon>><<= setup.gender2pic["${character.gender}"]>></div> <div class="character-stats"> <div class="stat-solt"> <div class="stat-label">Level</div> <div class="stat-value">${character.level.lv}</div> </div> <div class="stat-solt"> <div class="stat-label">AP</div> <div class="stat-value">💖${character.currentAPFloor}</div> </div> <div class="stat-solt"> <div class="stat-label">STA</div> <div class="stat-value">${character.sta.lv}</div> </div> <div class="stat-solt"> <div class="stat-label">STR</div> <div class="stat-value">${character.str.lv}</div> </div> <div class="stat-solt"> <div class="stat-label">CHA</div> <div class="stat-value">${character.cha.lv}</div> </div> <div class="stat-solt"> <div class="stat-label">DEX</div> <div class="stat-value">${character.dex.lv}</div> </div> <div class="stat-solt"> <div class="stat-label">WIL</div> <div class="stat-value">${character.wil.lv}</div> </div> <div class="stat-solt"> <div class="stat-label">FER</div> <div class="stat-value">${character.fer.lv}</div> </div> </div> </div> <div class="character-actions"> <div class="action-icon" data-action="favorite" title="${character.favorited ? 'Remove Favorite' : 'Add Favorite'}"> <i class="${character.favorited ? 'fas' : 'far'} fa-star"></i> </div> </div> `; card.innerHTML = ""; $(card).wiki(htmlContent); } else if (State.variables.chars.viewMode === 'compact') { const htmlContent = ` <div class="character-avatar" style="background-color: ${character.color};"> <<HeadImageByID ${character.id}>> </div> <div class="character-name">${character.name}</div> <div class="character-actions"> <div class="action-icon" data-action="favorite" title="${character.favorited ? 'Remove Favorite' : 'Add Favorite'}"> <i class="${character.favorited ? 'fas' : 'far'} fa-star"></i> </div> </div> `; card.innerHTML = ""; $(card).wiki(htmlContent); } return card; } function selectCharacter(characterId) { if (!characterId) return; if (charMap.get(characterId).traits["PlaneStun"]) { Msg.flash("Plane Stun: Reels from plane shock—no actions today!"); return; }; if ((State.variables.chars.selected?.id == characterId) && (passage() == "Summon_UI")) { $.wiki(`<<addclass ".leftTogglePanel" "hidden">><<removeclass "#leftactionpanel" "hidden">> <<addclass ".rightTogglePanel" "hidden">><<removeclass "#rightactionpanel" "hidden">><<set $summon.currentsolt = -1 >>`); return; } State.variables.chars.selectedId = characterId; const character = charMap.get(characterId); State.variables.chars.selected = character; if (passage() == "Summon_UI") { EventBus.emit("summon:join", characterId); } $.wiki('<<redo "selected">>'); document.querySelectorAll('.character-card').forEach(card => { card.classList.remove('selected'); }); document.querySelector(`.character-card[data-character-id="${character.id}"]`).classList.add('selected'); document.getElementById('action-target').textContent = character.name; updateTeamSelection(character.id); updateTagCloud(character.id); } function toggleFavorite(characterId) { const character = charMap.get(characterId); character.favorited = !character.favorited; charMap.set(character.id, character); const icon = document.querySelector(`.character-card[data-character-id="${character.id}"] .fa-star`); if (icon) { icon.className = character.favorited ? 'fas fa-star' : 'far fa-star'; if (State.variables.chars.viewMode === 'card') { icon.style.color = character.favorited ? 'var(--tr-SS)' : ''; } } const message = character.favorited ? 'Added to Favorites' : 'Removed from Favorites'; Msg.flash(`${character.name} ${message}`); } function handleAction(action) { if (!State.variables.chars.selected) { Msg.flash('Please select a character first'); return; } const character = State.variables.chars.selected; switch(action) { case 'rename': popup(`<<textbox "$chars.selected.name" $chars.selected.name>>`, { buttons: [{ text: "OK", action: () => {$.wiki(`<<redo "selected">>`); renderCharacterList(); popup.close();} }]}); break; case 'stopjob': $.wiki(`<<if $chars.selected.worktags>> <<set _jobpos = Job.getRef($chars.selected.id)[0]>> <<run Job.fire(_jobpos)>> <<redo "monsterslist selected">><</if>>`); renderCharacterList(); Msg.flash(`${character.name}'s job was canceled`); break; case 'train': $.wiki(`<<if $chars.selected.gender == "girl">> <<set $training.trainID.to = $chars.selected.id>><<goto 'Train_UI'>> <<else>> <<run UI.alert("Currently only supports Female♀️ characters.")>> <</if>>`); break; case 'moveout': if (State.variables.chars.selected["type"] == "unique") {UI.alert(L.nospecial); break;}; if (State.variables.chars.selected["worktags"]) {UI.alert(L.currentlyworking); break;}; popup(`Release the monster into the barony as an ordinary member, still under the lord’s protection but no longer appearing in the temple.<br> <<= L.summon_option_2>>`, { class:"notice", buttons: [ { text: "Confirm", class: "btn-red", action: () => {$.wiki(`<<run setup.delMonster($chars.selected)>><<run $domain.monsterpp += 1>><<redo "monsterslist selected">><<set _now = passage()>><<goto _now>>`); popup.close()}}, { text: "Cancel", class: "btn-grey", action: () => popup.close() } ]}); renderCharacterList(); break; case 'harvest': $.wiki(`<<HarvestMon $chars.selected>><<redo "monsterslist selected">>`); renderCharacterList(); break; case 'equip': Msg.flash(`Change equipment for ${character.name}`); break; case 'skill': Msg.flash(`Select skills for ${character.name}`); break; case 'feed': Msg.flash(`Use potion for ${character.name}`); break; case 'summon': const tagManagementFl = document.getElementById('tag-management'); const teamSelectionFl = document.getElementById('team-selection'); const summonManagementFl = document.getElementById('summon-management'); if (summonManagementFl.style.display === 'block') { summonManagementFl.style.display = 'none'; } else { summonManagementFl.style.display = 'block'; teamSelectionFl.style.display = 'none'; tagManagementFl.style.display = 'none'; } break; case 'tag': const tagManagement = document.getElementById('tag-management'); const teamSelection = document.getElementById('team-selection'); if (tagManagement.style.display === 'block') { tagManagement.style.display = 'none'; } else { tagManagement.style.display = 'block'; teamSelection.style.display = 'none'; } break; case 'team': const teamSelectionEl = document.getElementById('team-selection'); const tagManagementEl = document.getElementById('tag-management'); if (teamSelectionEl.style.display === 'block') { teamSelectionEl.style.display = 'none'; } else { teamSelectionEl.style.display = 'block'; tagManagementEl.style.display = 'none'; } break; } } function addNewTag() { const tagInput = document.getElementById('tag-input'); const newTag = tagInput.value.trim(); if (!newTag) { Msg.flash('Please enter tag content'); return; } if (availableTags.includes(newTag)) { Msg.flash('Tag already exists'); return; } availableTags.push(newTag); const tagsContainer = document.getElementById('tags-container'); const tagElement = document.createElement('div'); tagElement.className = 'tag-item'; tagElement.textContent = newTag; tagElement.dataset.tag = newTag; tagsContainer.appendChild(tagElement); const tagCloudList = document.getElementById('tag-cloud-list'); const tagCloudItem = document.createElement('div'); tagCloudItem.className = 'tag-cloud-item'; tagCloudItem.textContent = newTag; tagCloudItem.dataset.tag = newTag; tagCloudList.appendChild(tagCloudItem); tagElement.addEventListener('click', function() { const tagName = this.dataset.tag; this.classList.toggle('active'); const activeTags = Array.from(document.querySelectorAll('.tag-item.active')) .map(tag => tag.dataset.tag); State.variables.chars.filters.tags = activeTags; applyFilters(); }); tagInput.value = ''; Msg.flash(`Tag "${newTag}" added`); } function toggleCharacterTag(tag) { if (!State.variables.chars.selected) return; const character = State.variables.chars.selected; const tagIndex = character.tags.indexOf(tag); if (tagIndex === -1) { character.tags.push(tag); Msg.flash(`Added tag to ${character.name}: ${tag}`); } else { character.tags.splice(tagIndex, 1); Msg.flash(`Removed tag from ${character.name}: ${tag}`); } charMap.set(character.id, character); updateTagCloud(character.id); renderCharacterList(); } function updateTagCloud(characterId) { const character = charMap.get(characterId); const tagCloudItems = document.querySelectorAll('.tag-cloud-item'); tagCloudItems.forEach(item => { const tag = item.dataset.tag; if (character.tags.includes(tag)) { item.classList.add('selected'); } else { item.classList.remove('selected'); } }); } function updateTeamSelection(characterId) { const character = charMap.get(characterId); const teamRadios = document.querySelectorAll('input[name="team"]'); teamRadios.forEach(radio => { radio.checked = radio.value === character.team; }); } function assignCharacterToTeam(team) { if (!State.variables.chars.selected) return; const character = State.variables.chars.selected; character.team = team; charMap.set(character.id, character); Msg.flash(`${character.name} assigned to ${team}`); } function updateCounts() { const allCharacters = charList; document.getElementById('total-count').textContent = allCharacters.length; const classCounts = { feline: 0, }; } function updateTagCloudUI() { const tagsContainer = document.getElementById('tags-container'); tagsContainer.innerHTML = ''; availableTags.forEach(tag => { const tagElement = document.createElement('div'); tagElement.className = 'tag-item'; tagElement.textContent = tag; tagElement.dataset.tag = tag; tagsContainer.appendChild(tagElement); }); const tagCloudList = document.getElementById('tag-cloud-list'); tagCloudList.innerHTML = ''; availableTags.forEach(tag => { const tagElement = document.createElement('div'); tagElement.className = 'tag-cloud-item'; tagElement.textContent = tag; tagElement.dataset.tag = tag; tagCloudList.appendChild(tagElement); }); bindTagClickEvents(); } function bindTagClickEvents() { document.querySelectorAll('.tag-item').forEach(tag => { tag.addEventListener('click', function() { const tagName = this.dataset.tag; this.classList.toggle('active'); const activeTags = Array.from(document.querySelectorAll('.tag-item.active')) .map(tag => tag.dataset.tag); State.variables.chars.filters.tags = activeTags; applyFilters(); }); }); } <</script>> <</done>> <</widget>>
<<widget "ShowPortrait">> <style> .monsterinfoleftpic { flex: 1; display: flex; overflow: hidden; } .canvas-container { display: flex; justify-content: center; align-items: center; width: 100%; } canvas { object-fit: cover; display: block; } </style> <<do tag "selected shoplist">> <<if _args[0]>> <<set _cssid = _args[1] ? _args[1] : "portrait-container" >> <div class="monsterinfoleftpic" @id="_cssid"></div> <<set _showimg = { src: "" }; >> <<set _rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize); >> <<set _showpreg = _args[0].preg > 0 ? ['breast', 'belly'] : ['breast'] >> <<if _args[0].type == "unique" || _args[0].type == "wrong">> <<set _showimg.src = setup.ImagePath + _args[0].img>> <<set _viewheight = Math.clamp((_args[0].height * 3.75) , 256 , document.body.offsetHeight * 0.9)>> <<capture _showimg _cssid _viewheight _showpreg>> <<timed 0.1s>> <<run AnimeShow.dynamicPortrait( _showimg.src, _viewheight, _showpreg, _cssid);>> <</timed>> <</capture>> <<else>> <<set _viewheight = Math.clamp(( _args[0].height * 3.75 + 128) , 256 , document.body.offsetHeight * 0.9)>> <<capture _args[0] _cssid _viewheight _showpreg>> <<timed 0.1s>><<run SpriteCombine.cgWebgl( _args[0], _viewheight, _showpreg, _cssid, ((_args[2] || !settings.NSFW) ?? true) );>><</timed>><</capture>> <</if>> <</if>> <</do>> <</widget>> <<widget "ShowPortraitByCGid">> <style> .monsterinfoleftpic { flex: 1; display: flex; overflow: hidden; } .canvas-container { display: flex; justify-content: center; align-items: center; width: 100%; } canvas { object-fit: cover; display: block; } </style> <<do tag "selected shoplist">> <<if _args[0]>> <div class="monsterinfoleftpic" id="portrait-container"></div> <<set _showimg = { src: "" }; >> <<set _rootFontSize = parseFloat(getComputedStyle(document.documentElement).fontSize); >> <<set _showpreg = _args[0].preg > 0 ? ['breast', 'belly'] : ['breast'] >> <<set _showimg.src = setup.getPortraitPath(_args[0], _args[1])>> <<set _viewheight = Math.clamp((_args[0].height * 3.75) , 256 , document.body.offsetHeight * 0.9)>> <<timed 0.1s>> <<run AnimeShow.dynamicPortrait( _showimg.src, _viewheight, _showpreg, "portrait-container" );>> <</timed>> <</if>> <</do>> <</widget>>
<<run $('#portrait-container').empty()>>
<<include 'BackgroundData'>>
<<if !settings.SideBarKeep>> <<run UIBar.stow(true);>> <</if>> <<switch passage()>> <<case "Welcome">> <<run AudioBus.playPlaylist('index_order');>> <<case "MainHall_UI">> <<run AudioBus.playPlaylist('bgm_loop');>> <<case "Girl" "Train_UI">> <<run AudioBus.playPlaylist('bgm_girl');>> <<case "Battle_UI">> <<run AudioBus.playPlaylist('bgm_heroic');>> <<case "Bedroom_UI">> <<run AudioBus.playPlaylist('bgm_romance');>> <<default>> <</switch>>
<style> :root { --royal-purple: #4B0082; --royal-gold: #D4AF37; --royal-silver: #C0C0C0; --deep-navy: #0A0A2A; --blood-red: #8B0000; --forest-green: #228B22; --parchment: #F5F5DC; --steel-gray: #71797E; --dark-wood: #654321; --light-gold: #FFD700; --gold-gradient: linear-gradient(135deg, #D4AF37 0%, #FFD700 50%, #D4AF37 100%); --green-gradient: linear-gradient(135deg, #228B22 0%, #32CD32 100%); --bg-gradient: linear-gradient(135deg, #0A0A2A 0%, #2A0A2A 100%); --card-gradient: linear-gradient(135deg, rgba(26, 26, 58, 0.95) 0%, rgba(42, 26, 58, 0.95) 100%); --gold-glow: 0 0 10px rgba(212, 175, 55, 0.5); } .patron-container { max-height: 55rem; max-width: 65rem; margin: 0 auto; background: var(--card-gradient); border-radius: 0.5rem; border: 2px solid var(--royal-gold); position: relative; overflow: hidden; box-shadow: 0 0 20px rgba(212, 175, 55, 0.3); } .patron-container::after { content: '⚜️'; position: absolute; bottom: 0.5rem; left: 0.5rem; font-size: 2rem; opacity: 0.3; z-index: 1; } .header { padding: 1rem 2rem; background: linear-gradient(to right, rgba(75, 0, 130, 0.3), rgba(139, 0, 0, 0.3), rgba(75, 0, 130, 0.3)); border-bottom: 2px solid var(--royal-gold); text-align: center; position: relative; } .header::before { content: '⚜️'; position: absolute; font-size: 10rem; opacity: 0.1; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 0; } .lord-title { font-size: 0.9rem; color: var(--royal-silver); letter-spacing: 2px; text-transform: uppercase; } .main-title { line-height: 1.5; font-size: 2.5rem; background: var(--gold-gradient); -webkit-background-clip: text; background-clip: text; color: transparent; text-shadow: 2px 2px 1px rgba(0, 0, 0, 0.5); } .subtitle { font-size: 0.9rem; color: var(--light-gold); max-width: 600px; margin: 0 auto; line-height: 1.4; } .main-content { display: grid; grid-template-columns: 25rem 1fr; gap: 1rem; padding: 1rem; } @media (max-width: 992px) { .main-content { grid-template-columns: 1fr; } } .left-panel { max-height: 32rem; display: flex; flex-direction: column; gap: 1rem; } .verification-panel { background: rgba(10, 10, 42, 0.8); border: 2px solid var(--royal-gold); border-radius: 0.5rem; padding: 1rem; position: relative; } .panel-title { display: flex; align-items: center; gap: 0.75rem; color: var(--royal-gold); font-size: 1.1rem; } .panel-title i { color: var(--royal-gold); } .code-input { width: 100%; padding: 0.5rem 1rem; background: rgba(255, 255, 255, 0.1); border: 1px solid var(--royal-gold); border-radius: 0.5rem; color: var(--parchment); font-size: 1rem; margin-bottom: 0.75rem; } .code-input:focus { outline: none; border-color: var(--light-gold); } .verify-btn { width: 100%; padding: 0.5rem; background: var(--gold-gradient); border: none; border-radius: 0.5rem; color: #000; font-weight: bold; font-size: 1rem; cursor: pointer; } .verify-btn:disabled { opacity: 0.7; cursor: not-allowed; } .rewards-panel { display: flex; max-height: 20rem; background: rgba(10, 10, 42, 0.8); border: 2px solid var(--royal-gold); border-radius: 0.5rem; padding: 1rem; flex-grow: 1; flex-direction: column; } .rewards-list { height: 100%; overflow-y: auto; display: flex; flex-direction: column; gap: 0.75rem; margin-top: 1rem; } .reward-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.5rem; background: rgba(212, 175, 55, 0.1); border-radius: 0.5rem; border-left: 3px solid var(--royal-gold); } .reward-item.locked { opacity: 0.5; filter: grayscale(0.7); } .reward-icon { width: 2.5rem; height: 2.5rem; background: var(--gold-gradient); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #000; font-size: 1.2rem; } .reward-info { flex: 1; } .reward-name { font-weight: bold; color: var(--royal-gold); font-size: 0.9rem; } .reward-desc { font-size: 0.8rem; color: var(--royal-silver); margin-top: 0.25rem; } .claim-btn { width: 5rem; padding: 0.5rem 1rem; background: var(--green-gradient); border-radius: 0.5rem; color: white; font-weight: bold; font-size: 0.75rem; cursor: pointer; border: none; } .claim-btn:disabled { background: var(--steel-gray); cursor: not-allowed; } .right-panel { height: 30rem; display: flex; flex-direction: column; } .console-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 1rem; padding-bottom: 0.75rem; border-bottom: 1px solid rgba(212, 175, 55, 0.3); } .lord-welcome { font-size: 1.1rem; color: var(--royal-gold); } .resources-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1.1rem; flex-grow: 1; } @media (max-width: 768px) { .resources-grid { grid-template-columns: repeat(2, 1fr); } } .resource-card { height: 13rem; width: 10rem; background: linear-gradient(135deg, rgba(75, 0, 130, 0.3) 0%, rgba(139, 0, 0, 0.3) 100%); border: 2px solid var(--royal-gold); border-radius: 0.5rem; padding: 1rem; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.75rem; position: relative; overflow: hidden; } .resource-icon { font-size: 2rem; color: var(--royal-gold); } .resource-info { text-align: center; } .resource-name { font-weight: bold; color: white; font-size: 0.95rem; } .resource-amount { font-size: 1.2rem; font-weight: bold; background: var(--gold-gradient); -webkit-background-clip: text; background-clip: text; color: transparent; margin: 0.25rem 0; } .resource-btn { padding: 0.5rem 1rem; width: 100%; background: var(--gold-gradient); border: none; border-radius: 0.5rem; color: #000; font-weight: bold; cursor: pointer; } .resource-btn:disabled { background: var(--steel-gray); cursor: not-allowed; } .status-message { position: fixed; top: 2rem; right: 2rem; padding: 1rem 1.5rem; background: var(--card-gradient); border: 2px solid var(--royal-gold); border-radius: 0.5rem; box-shadow: var(--gold-glow); z-index: 1000; max-width: 300px; display: none; } .footer { padding: 1rem 2rem; background: rgba(0, 0, 0, 0.3); border-top: 1px solid rgba(212, 175, 55, 0.3); text-align: center; font-size: 0.8rem; color: var(--royal-silver); } .patreon-link { color: var(--royal-gold); text-decoration: none; font-weight: bold; } .patreon-link:hover { text-decoration: underline; } .rewards-list::-webkit-scrollbar { width: 6px; } .rewards-list::-webkit-scrollbar-track { background: rgba(0, 0, 0, 0.2); border-radius: 3px; } .rewards-list::-webkit-scrollbar-thumb { background: var(--royal-gold); border-radius: 3px; } </style> <<run AudioBus.playPlaylist('patreon');>> <div class="patron-container"> <!-- 头部区域 --> <div class="header"> <div class="lord-title">The Patron's Treasury</div> <div class="main-title">Lord's Sanctum</div> <div class="subtitle"> Your noble generosity ensures the prosperity of the Barony. By your decree, these ancient boons are unlocked to aid in your conquest. </div> </div> <!-- 主内容区域 --> <div class="main-content"> <!-- 左侧面板 --> <div class="left-panel"> <!-- 验证面板 --> <div class="verification-panel"> <div class="panel-title"> <i class="fas fa-shield-alt"></i> <span>Lord's Seal Verification</span> </div> <input type="text" class="code-input" id="patreon-code" placeholder="Enter Patreon Supporter Code..."> <button class="verify-btn" id="verify-btn"> <i class="fas fa-check-circle"></i> Verify Noble Status </button> </div> <!-- 福利面板 - 静态内容 --> <div class="rewards-panel"> <div class="panel-title"> <i class="fas fa-crown"></i> <span>Royal Rewards</span> </div> <div class="rewards-list" id="rewards-list"> <div class="reward-item locked"> <div class="reward-icon"> <i class="fas fa-chess-king"></i> </div> <div class="reward-info"> <div class="reward-name">Royal Treasury Grant</div> <div class="reward-desc">Infinite Resource Access</div> </div> <button data-reward-id="0" class="claim-btn" disabled> Locked </button> </div> <div class="reward-item locked"> <div class="reward-icon"> <i class="fas fa-bolt"></i> </div> <div class="reward-info"> <div class="reward-name">Aetheric Surge</div> <div class="reward-desc">+500 Max Energy Capacity</div> </div> <button data-reward-id="1" class="claim-btn" disabled> Locked </button> </div> <div class="reward-item locked"> <div class="reward-icon"> <i class="fas fa-scroll"></i> </div> <div class="reward-info"> <div class="reward-name">Royal Edict of Summoning</div> <div class="reward-desc">Cards drawn increased by +1.</div> </div> <button data-reward-id="2" class="claim-btn" disabled> Locked </button> </div> <div class="reward-item locked"> <div class="reward-icon"> <i class="fas fa-cube"></i> </div> <div class="reward-info"> <div class="reward-name">Ancient Artifact Core</div> <div class="reward-desc">+1 Advanced Infuser Core</div> </div> <button data-reward-id="3" class="claim-btn" disabled> Locked </button> </div> <div class="reward-item locked"> <div class="reward-icon"> <i class="fas fa-book-reader"></i> </div> <div class="reward-info"> <div class="reward-name">Governance Mastery</div> <div class="reward-desc">+200 Stewardship Points</div> </div> <button data-reward-id="4" class="claim-btn" disabled> Locked </button> </div> <div class="reward-item locked"> <div class="reward-icon"> <i class="fa-solid fa-flask"></i> </div> <div class="reward-info"> <div class="reward-name">Master's Gift</div> <div class="reward-desc">+2 Omni-Boost Potion</div> </div> <button data-reward-id="5" class="claim-btn" disabled> Locked </button> </div> </div> </div> </div> <!-- 右侧面板 --> <div class="right-panel"> <div class="console-header"> <div class="lord-welcome" id="welcome-message"> Awaiting Verification... </div> </div> <!-- 资源面板 - 静态内容 --> <div class="resources-grid" id="resources-grid"> <div class="resource-card"> <div class="resource-icon"> <i class="fas fa-coins"></i> </div> <div class="resource-info"> <div class="resource-name">Gold</div> <div class="resource-amount">+1000</div> </div> <button data-resource-id="gold" class="resource-btn" disabled> Acquire </button> </div> <div class="resource-card"> <div class="resource-icon"> <i class="fa-solid fa-bread-slice"></i> </div> <div class="resource-info"> <div class="resource-name">Food</div> <div class="resource-amount">+100</div> </div> <button data-resource-id="food" class="resource-btn" disabled> Acquire </button> </div> <div class="resource-card"> <div class="resource-icon"> <i class="fa-solid fa-tree"></i> </div> <div class="resource-info"> <div class="resource-name">Wood</div> <div class="resource-amount">+50</div> </div> <button data-resource-id="wood" class="resource-btn" disabled> Acquire </button> </div> <div class="resource-card"> <div class="resource-icon"> <i class="fa-solid fa-cubes-stacked"></i> </div> <div class="resource-info"> <div class="resource-name">Stone</div> <div class="resource-amount">+50</div> </div> <button data-resource-id="stone" class="resource-btn" disabled> Acquire </button> </div> <div class="resource-card"> <div class="resource-icon"> <i class="fa-solid fa-diamond"></i> </div> <div class="resource-info"> <div class="resource-name">Ore</div> <div class="resource-amount">+50</div> </div> <button data-resource-id="ore" class="resource-btn" disabled> Acquire </button> </div> </div> </div> </div> <!-- 底部区域 --> <div class="footer"> <p> Exclusive tribute for our Noble Patrons.<br> Your support fuels the Barony's development.<br> Continue your patronage on <a href="https:\\www.patreon.com/c/AxiomNova" class="patreon-link" target="_blank">Patreon</a>. </p> </div> </div> <!-- 状态消息 --> <div class="status-message" id="status-message"></div> <<done>> <<script>> const CHAR_RING = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; const RING_LENGTH = CHAR_RING.length; setup.inputSolve = function(text, key = 5) { let result = ''; for (let i = 0; i < text.length; i++) { const char = text[i]; const index = CHAR_RING.indexOf(char); if (index !== -1) { const originalIndex = (index - key + RING_LENGTH) % RING_LENGTH; result += CHAR_RING[originalIndex]; } else { result += char; } } return result; }; if (recall(State.variables.premiumCode)) { showVerify(); }; bindEvents(); function bindEvents() { document.getElementById('verify-btn').addEventListener('click', verifyCode); document.getElementById('patreon-code').addEventListener('keypress', function(e) { if (e.key === 'Enter') { verifyCode(); } }); } function verifyCode() { const codeInput = document.getElementById('patreon-code'); const code = codeInput.value.trim(); if (!code) { showStatus('Enter your Patreon code, my Lord', 'error'); return; } if (code === setup.inputSolve(State.variables.patreonCode)) { memorize(State.variables.premiumCode, true); codeInput.value = '✓ VERIFIED'; codeInput.disabled = true; const verifyBtn = document.getElementById('verify-btn'); verifyBtn.innerHTML = '<i class="fas fa-crown"></i> Verified'; verifyBtn.disabled = true; showVerifiedState(); showStatus('Welcome, noble Lord! Your domain awaits.', 'success'); } else { showStatus('Invalid code. Check your Patreon posts.', 'error'); codeInput.value = ''; codeInput.focus(); } } function showVerify() { const codeInput = document.getElementById('patreon-code'); codeInput.value = '✓ VERIFIED'; codeInput.disabled = true; const verifyBtn = document.getElementById('verify-btn'); verifyBtn.innerHTML = '<i class="fas fa-crown"></i> Verified'; verifyBtn.disabled = true; showVerifiedState(); showStatus('Welcome, noble Lord! Your domain awaits.', 'success'); } function showVerifiedState() { document.getElementById('welcome-message').textContent = `Welcome, My Lord! Command your resources.`; const claimButtons = document.querySelectorAll('.claim-btn'); claimButtons.forEach(btn => { const rewardindex = btn.dataset.rewardId; const isindex = State.variables.gamestatus.reward[rewardindex]; if (isindex || rewardindex == 0) { btn.textContent = 'Claimed'; btn.disabled = true; btn.style.background = 'var(--steel-gray)'; } else { btn.textContent = 'Claim'; btn.disabled = false; btn.addEventListener('click', function() { claimReward(this); });} }); const resourceButtons = document.querySelectorAll('.resource-btn'); resourceButtons.forEach(btn => { btn.disabled = false; btn.addEventListener('click', function() { acquireResource(this); }); }); const rewardItems = document.querySelectorAll('.reward-item'); rewardItems.forEach(item => { item.classList.remove('locked'); }); } function claimReward(button) { if (State.variables.gamestatus.stage < 1) { showStatus(`My load, Please claim your rewards after the game launches.`, 'warning'); return; }; const rewardItem = button.closest('.reward-item'); const rewardindex = button.dataset.rewardId; State.variables.gamestatus.reward[rewardindex] = true; const rewardName = rewardItem.querySelector('.reward-name').textContent; switch (rewardindex) { case "1": State.variables.manage.energy.current += 500; State.variables.manage.energy.base += 500; break; case "2": State.variables.manage.cardSelect += 1; break; case "3": State.variables.storage.pickup("Advanced_Infuser_Core", 1); break; case "4": State.variables.tech.techPoint.stewardship += 200; break; case "5": State.variables.storage.pickup("allPotion", 2); break; default: break; } $.wiki(`<<redo "topbar">>`) ; button.textContent = 'Claimed'; button.disabled = true; button.style.background = 'var(--steel-gray)'; showStatus(`${rewardName} has been added to your treasury!`, 'success'); } function acquireResource(button) { if (State.variables.gamestatus.stage < 1) { showStatus(`My load, Please claim your rewards after the game launches.`, 'warning'); return; }; const resourceCard = button.closest('.resource-card'); const resource = button.dataset.resourceId; switch (resource) { case "gold": State.variables.storage.pickup("gold", 1000); break; case "food": State.variables.storage.pickup("food", 100); break; case "wood": State.variables.storage.pickup("wood", 50); break; case "stone": State.variables.storage.pickup("stone", 50); break; case "ore": State.variables.storage.pickup("ore", 50); break; default: break; } $.wiki(`<<redo "topbar">>`) ; const resourceName = resourceCard.querySelector('.resource-name').textContent; showStatus(`${resourceName} added to your stores!`, 'success'); } function showStatus(message, type) { const statusEl = document.getElementById('status-message'); statusEl.textContent = message; statusEl.style.display = 'block'; if (type === 'error') { statusEl.style.borderColor = 'var(--blood-red)'; } else if (type === 'warning') { statusEl.style.borderColor = 'var(--royal-gold)'; } else { statusEl.style.borderColor = 'var(--forest-green)'; } setTimeout(() => { statusEl.style.display = 'none'; }, 3000); } <</script>><</done>>
<style> img[title="shop"] { width: 4rem;height: 4rem;align-self: end; } .UIbox { grid-template-rows: 100%; display: grid; grid-template-columns: 1fr 1fr 17.5rem; } .slave-list { background: lightslategrey; display: grid; grid-template-columns: repeat(auto-fit, minmax(8.125rem, 1fr)); gap: 5px; height: 34.5rem; overflow-y: scroll; } .slave-card { overflow: hidden; justify-items: center; width: 8.5rem; max-height: 17.2rem; background: #2d2d2d; border: 1px solid; padding: 5px; cursor: pointer; transition: all 0.3s ease; } .slave-card img{ object-fit: cover; height: 100%; } .slave-card[data-gender="male"] { border-color: #4682b4; } .slave-card[data-gender="female"] { border-color: #8b4513; } .slave-card[data-gender="other"] { border-color: #756d5e; } .slave-card:hover { border-color: #9e8561; } .slave-display { width: 100%; height: 100%; min-width: 30%; position: relative; border: 3px groove #4a4130; } span[data-do-tags="showinfo shoplist monsterslist"] > img{ max-height: 80vh; } .slave-portrait { width: 100%; height: 60vh; object-fit: cover; border-bottom: 2px solid #4a4130; } .stats-panel { padding: 1rem; display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.625rem; } .stat-item { background: rgba(43,25,28,0.8); padding: 0.625rem; text-align: center; } .status-tag { position: absolute; top: 0.625rem; right: 0.625rem; background: #6b0504; padding: 5px 0.625rem; } .slave-infocard { overflow-y: auto; height: 100%; position: relative; } .slave-buy { z-index: 10; justify-content: space-evenly; } #shopprice { color: gold; background: #00000082; bottom: 2rem; position: relative; } </style> <style> #slave-price { color: gold; } </style> <<set $chars.selected = null>> <div class="UIbox"> <<do tag "slaver">> <<MapList $slave.slaveList $slave.slaveMap>> <</do>> <div class="slave-display"> <<do tag "selected">> <<if $chars.selected>> <div class="monsterinfoleftpic" id="portrait-container"></div> <<ShowPortrait $chars.selected>> <</if>> <div class="absolute-center-bottom flex-row-sb1 slave-buy"> <div class="op" style="justify-items: center;"> <<link '<<showframe "icon64" "buy" 4>>'>> <<if $chars.selected>> <<if $storage.count('gold') > $chars.selected.price>> <<drop $storage "gold" $chars.selected.price>> <<AddMonster $chars.selected>> <<run $slave.slaveList.deleteAll($chars.selected.id)>> <<set $chars.selected = null>> <<redo "slaver monsterslist selected topbar">> <<else>> <<run popup(L.moregold)>> <</if>> <</if>> <</link>> <div id="slave-price">Buy: <<if $chars.selected>><<= $chars.selected.price>><<else>>-<</if>>g</div> </div> <div class="op" style="justify-items: center;"> <<set _refreshcost = setup.getRefreshCost() >> <<link '<<showframe "icon64" "refresh" 4>>'>> <<if $storage.count('gold') > _refreshcost>> <<drop $storage "gold" _refreshcost>> <<set $chars.selected = null>> <<set $slave.slaveList = [] >> <<run $slave.slaveMap.clear()>> <<NewSlave>> <<set $slave.refresh += 1 >> <<redo "selected topbar slaver">> <<run Msg.add('notify', "Refresh Slaves");>> <<else>> <<run popup(L.moregold)>> <</if>> <</link>> <div id="slave-price">Refresh: <<= _refreshcost>> g</div> </div> </div> <</do>> </div> <div class="slave-infolist"> <div class="slave-infocard"> <<do tag "selected">> <<if $chars.selected>> <<Infolist $chars.selected>> <</if>> <</do>> </div> </div> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Slaver">> </div> </div> <<set _FacHelp = "CodexEntry_Building_SlaverCaravan" >> <<run Msg.show();>>
<<script>> setup.NPCData = { npc1: { id: "npc1", race: "reptilian", rank: "3", parts: { body: "girl" ,head: "head5" ,breast: "breast4" ,wing: "wing4" ,cloth: "cloth1" }, targetHSL: [60, 30, 10] }, npc2: { id: "npc2", race: "avian", rank: "2", gender: "girl", parts: { head: "head1", body: "boy", breast: "breast9" }, targetHSL: [205, 50, 50] } }; setup.SpriteData = { tuneHSL: { sourceHSL: [205, 50, 50], tolerance: [10, 10, 10], gradientStrength: 1 }, bodyData: { id: "body_sprite", name: "身体", width: 1920, height: 1024, grid: { rows: 1, cols: 3, frameWidth: 640, frameHeight: 1024 }, position: [0, 0], defaultScale: 1.0, zIndex: 2, frames: { girl: { row: 0, col: 0, label: "girl" }, boy: { row: 0, col: 1, label: "boy" }, futa: { row: 0, col: 2, label: "futa" } }, tuneMode: 'ps' }, clothData: { id: "cloth_sprite", name: "衣服", width: 939, height: 491, grid: { rows: 1, cols: 3, frameWidth: 313, frameHeight: 491 }, position: [0, 290], defaultScale: 1.0, zIndex: 10, frames: { cloth1: { row: 0, col: 0, label: "girl" }, cloth2: { row: 0, col: 1, label: "boy" }, cloth3: { row: 0, col: 2, label: "futa" } }, tuneMode: 'none' }, headData: { id: "head_sprite", name: "头部", width: 1210, height: 484, grid: { rows: 2, cols: 5, frameWidth: 242, frameHeight: 242 }, position: [0, 708], defaultScale: 1, zIndex: 3, frames: { head1: { row: 0, col: 0, label: "1" }, head2: { row: 0, col: 1, label: "2" }, head3: { row: 0, col: 2, label: "3" }, head4: { row: 0, col: 3, label: "4" }, head5: { row: 0, col: 4, label: "5" }, head6: { row: 1, col: 0, label: "6" }, head7: { row: 1, col: 1, label: "7" }, head8: { row: 1, col: 2, label: "8" }, head9: { row: 1, col: 3, label: "9" }, head10: { row: 1, col: 4, label: "10" } }, tuneMode: 'ps', img: "images/sprites/feline_head_sprite.png" }, breastData: { id: "breast_sprite", name: "胸部", width: 1210, height: 276, grid: { rows: 2, cols: 5, frameWidth: 242, frameHeight: 138 }, position: [0, 594], defaultScale: 1, zIndex: 10, frames: { breast1: { row: 0, col: 0, label: "Flat" }, breast2: { row: 0, col: 1, label: "Petite" }, breast3: { row: 0, col: 2, label: "Small" }, breast4: { row: 0, col: 3, label: "Modest" }, breast5: { row: 0, col: 4, label: "Medium" }, breast6: { row: 1, col: 0, label: "Full" }, breast7: { row: 1, col: 1, label: "Big" }, breast8: { row: 1, col: 2, label: "Large" }, breast9: { row: 1, col: 3, label: "Huge" }, breast10: { row: 1, col: 4, label: "Gigantic" } }, tuneMode: 'none' }, cockData: { id: "cock_sprite", name: "cock", width: 1210, height: 276, grid: { rows: 2, cols: 5, frameWidth: 242, frameHeight: 138 }, position: [0, 594], defaultScale: 1, zIndex: 10, frames: { cock1: { row: 0, col: 0, label: "Flat" }, cock2: { row: 0, col: 1, label: "Petite" }, cock3: { row: 0, col: 2, label: "Small" }, cock4: { row: 0, col: 3, label: "Modest" }, cock5: { row: 0, col: 4, label: "Medium" }, cock6: { row: 1, col: 0, label: "Full" }, cock7: { row: 1, col: 1, label: "Big" }, cock8: { row: 1, col: 2, label: "Large" }, cock9: { row: 1, col: 3, label: "Huge" }, cock10: { row: 1, col: 4, label: "Gigantic" } }, tuneMode: 'tolerance' }, wingData: { id: "wing_sprite", name: "翅膀", url: "wing_sprite.png", width: 1920, height: 1280, grid: { rows: 2, cols: 2, frameWidth: 960, frameHeight: 640 }, position: [0, 284], defaultScale: 1.0, zIndex: 0, frames: { wing1: { row: 0, col: 0, label: "wing1" }, wing2: { row: 0, col: 1, label: "wing2" }, wing3: { row: 1, col: 0, label: "wing3" }, wing4: { row: 1, col: 1, label: "wing4" } }, tuneMode: 'ps' }, assData: { id: "ass_sprite", name: "ass", url: "wing_sprite.png", width: 1920, height: 256, grid: { rows: 1, cols: 5, frameWidth: 384, frameHeight: 256 }, position: [0, 512], defaultScale: 1.0, zIndex: 5, frames: { ass1: { row: 0, col: 0, label: "wing1" }, ass2: { row: 0, col: 1, label: "wing2" }, ass3: { row: 0, col: 2, label: "wing3" }, ass4: { row: 0, col: 3, label: "wing4" }, ass5: { row: 0, col: 4, label: "wing5" } }, tuneMode: 'tolerance' }, getData(layerId, npc) { const data = this[layerId + 'Data']; if (!data) { return null; } if (layerId == "breast" || layerId == "cloth") {data.url = setup.ImagePath +"sprites/"+ data.id +".png";} else { const partrace = npc.genes[layerId].race; const partrank = npc.genes[layerId].rank; const primaryUrl = setup.ImagePath + "sprites/" + partrace + "_" + partrank + "_" + data.id + ".png"; const fallbackUrl = setup.ImagePath + "sprites/" + partrace + "_" + data.id + ".png"; data.url = setup.spriteCache && setup.spriteCache.has(primaryUrl) ? primaryUrl : fallbackUrl; } return data; }, getDataLabel(layerId, npc) { const data = this[layerId + 'Data']; if (!data) { return null; } if (layerId == "breast" || layerId == "cloth") {data.url = setup.ImagePath +"sprites/"+ data.id +".png";} else { const partrace = npc.genes[layerId].race; const partrank = npc.genes[layerId].rank; data.url = setup.ImagePath +"sprites/"+ partrace +"_"+ data.id +".png";} return data.frames[npc.parts[layerId]].label; } }; <</script>>
<style> #story { margin: 0em 0em 0em 17.5em; } #Gamerelease { padding: 20px; color: #fbf16a; left: 21%; top: 10%; background: #222; max-height: fit-content; width: 15%; position: absolute; } #menu-core { } .disclaimer { padding: 2rem; position: relative; left: 50%; top: 10%; transform: translate(-50%, 0%); width: 50%; max-height: fit-content; background: orange; color: #000000; } .disclaimer button { background: var(--bg-deepred); box-shadow: 7px 4px 10px rgb(255, 255, 255); } </style> <<script>> $(document).one(":passagerender", function (element) { $(element.content).find(".macro-radiobutton").on("change", function (el) { $.wiki("<<redo 'lang'>>"); $.wiki("<<run UI.update();>>"); }); }); <</script>> <<run UIBar.hide().stow();>> <<if Story.has("0_0DebugControl")>> <<timed 1s>> <<goto 'Welcome'>> <</timed>> <<else>> <div class="disclaimer"> <<do tag "lang">> <center><h2>⚠️<<= setup.langbank.disclaimer[$lang]>>⚠️</h2></center> <<= setup.langbank.disclaimerdesc[$lang]>> <<switch $lang>> <<case "chs">> <<include "LocalizationCHS">> <<default>> <<include "LocalizationEN">> <</switch>> <center><h2>🔞</h2> <<button setup.langbank.disclaimercontinue[$lang] "Welcome">><</button>></center> <</do>> </div> <</if>>
<style> .UIbox { grid-template-rows: 100%; display: grid; grid-template-columns: 1fr 28rem; } </style> <<script>> window.sortItems = function(criteria) { let sort = criteria.dataset.sort; document.querySelectorAll('.sort-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.sort === sort); }); }; <</script>> <<unset $choiceitems>> <div class="UIbox"> <div class="filter-sort-container"> <<do tag "sort-left">> <div class="nav-list-col"> <<link '<div class="nav-tab" id="all"><<= "All">></div>'>> <<ToggleTags "nav-tab" "all">> <<set $currentFilter = "All">><<redo "itemlist">> <</link>> <<for _type range setup.itemTypes>> <<capture _type>> <<link '<div class="nav-tab" @id="_type"><<= setup.langbank[_type][$lang]>></div>'>> <<ToggleTags "nav-tab" _type>> <<set $currentFilter = _type>> <<redo "itemlist">> <</link>> <</capture>> <</for>> </div> <</do>> <div style="display: flex; flex-direction: column;"> <div class="sort-controls"> <div class="sort-btn" data-sort='lv' onclick="sortItems(this)"><<button "🌟">><<set $sortBy = "lv" >><<redo "itemlist">><</button>></div> <div class="sort-btn" data-sort='num' onclick="sortItems(this)"><<button "📦">><<set $sortBy = "qty" >><<redo "itemlist">><</button>></div> <div class="sort-btn" data-sort='price' onclick="sortItems(this)"><<button "💰">><<set $sortBy = "price" >><<redo "itemlist">><</button>></div> <div class="sort-btn" data-sort='name' onclick="sortItems(this)"><<button "✏️">><<set $sortBy = "name" >><<redo "itemlist">><</button>></div> <div class="sort-btn" data-sort='order' onclick="sortItems(this)"><<button "🕐">><<set $sortBy = "order" >><<redo "itemlist">><</button>></div> </div> <<set $currentFilter = "All">> <<set $sortBy = "">> <<do tag "itemlist">> <<set _itemsindexs = Array.from($storage.list.keys());>> <<if $currentFilter == "All">> <<set _filteredindexs = _itemsindexs>> <<else>> <<set _filteredindexs = _itemsindexs.filter(index => getiteminfo($storage.list[index],"type") == $currentFilter)>> <</if>> <<if $sortBy == "name">> <<set _sortedindexs = _filteredindexs.sort((a, b) => getiteminfo($storage.list[a],"name").localeCompare(getiteminfo($storage.list[b],"name")))>> <<elseif $sortBy == "namedown">> <<set _sortedindexs = _filteredindexs.sort((a, b) => getiteminfo($storage.list[b],"name").localeCompare(getiteminfo($storage.list[a],"name")))>> <<elseif $sortBy == "lv">> <<set _sortedindexs = _filteredindexs.sort((a, b) => getiteminfo($storage.list[b],"lv") - (getiteminfo($storage.list[a],"lv")))>> <<elseif $sortBy == "qty">> <<set _sortedindexs = _filteredindexs.sort((a, b) => $storage.count($storage.list[b]) - $storage.count($storage.list[a]))>> <<elseif $sortBy == "price">> <<set _sortedindexs = _filteredindexs.sort((a, b) => getiteminfo($storage.list[b],"price") - (getiteminfo($storage.list[a],"price")))>> <<elseif $sortBy == "order">> <<set _sortedindexs = _filteredindexs>> <<else>> <<set _sortedindexs = _filteredindexs>> <</if>> <div class="inventory-grid"> <<for _itemindex range _sortedindexs>> <<set _item = $storage.list[_itemindex] >> <<capture _item>> <div class="item-slot" data-itemid="_itemindex" > <<set _imgadd = setup.ImagePath + getiteminfo(_item,"img") >> <<link [img[setup.ImagePath + getiteminfo(_item,"img")]]>> <<set $choiceitems = _item >><<redo "desc">> <</link>> <div class="item-glow" style="bottom: 0; position: absolute;">x<<= $storage.count(_item)>></div> <div class="item-quickinfo"> <span class="item-rarity-star"> <<NumToStars `getiteminfo(_item,"lv")`>> </span> </div> </div> <</capture>> <</for>> </div> <</do>> </div> </div> <<do tag "desc">> <div class="item-detail-container"> <<if def $choiceitems>> <<ItemCardShow $choiceitems>> <</if>> <div class="action-panel"> <<if def $choiceitems && (getiteminfo($choiceitems,"type") == "harvest" || getiteminfo($choiceitems,"type") == "alchemy")>> <<button `L.feed`>> <<if Flag('enblefeed')>> <<set $manage.feed.feedCp.itemIDLv = $choiceitems >> <<FeedItem $manage.feed.feedCp.charID $manage.feed.feedCp.itemIDLv>> <<SetFlag 'enblefeed' false >> <<run Dialog.close();>> <<else>> <<run popup(L.summonHint)>> <</if>> <</button>><span></span> <</if>> <div id="deletbutt"> <<if (def $choiceitems) && (getiteminfo($choiceitems,"type") != "special")>> <<button "🗑️-1">> <<drop $storage $choiceitems 1>> <<redo "itemlist topbar">> <</button>> <<button "🗑️-all">> <<drop $storage $choiceitems 9999>> <<redo "itemlist topbar">> <</button>> <</if>> </div> </div> </div> <</do>> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Storage">> </div> </div> <<run Msg.show();>>
<<script TwineScript>> window.getResourceOutput = function(resource) { const unitoutput = $buildingEffects.resources[resource] || 0; return unitoutput; }; <</script>>
<<if $gamestatus.stage == 0 >> <style> #story-banner img { width: 100%; } </style> [img[setup.ImagePath +'ui/logo.png']] <</if>>
<<if $gamestatus.stage > 0 >> <<include "UI_LeftBar">> <</if>> <<if Story.has("0_0DebugControl")>> <<set $nsfw = false >> <<include "0_0DebugControl">> <</if>> <<if $gamestatus.stage == 0 >> <style> #version-alert { border: dashed; padding: 1rem 0.5rem; position: relative; width: fit-content; color: var(--bg-body); background: gold; display: none; } </style> <div id="version-alert"> <div id="version-update"></div> </div> <<script>> $.get("https:\\axiomnova.github.io/version.html", function(data, status) { if (!data) {return;} let currentVersion = $('#version').text().slice(3); currentVersion = parseFloat(currentVersion); var data = data.slice(3); const lastVersion = parseFloat(data); if (lastVersion > currentVersion) { $('#version-update').html('Current Version (v0.' + currentVersion + ')<br> Latest Version (v0.' + lastVersion + ') <br />For the best experience, please update now:<a href="https:\\www.patreon.com/c/BreedingWorldWar" target="_blank"><br>🔗patreon</a> <a href="https:\\discord.gg/RNbvNpjXZW" target="_blank"><br>💬discord</a>.'); $('#version-alert').show(); } }); <</script>> <</if>>
<<if $gamestatus.stage == 0 >><<= setup.langbank.gametitle[$lang]>><</if>>
<<set $gamestatus.stage = 0>> <<set $lang to 'en'>> <<include "LocalizationEN">> <<set $premiumCode = 'Patreon1014' >> <<set $Flags = {}>> <<include "AudioData">> <<include "SpriteData">> <<set setup.allSpriteUrls = ["images/sprites/breast_sprite.png", "images/sprites/cloth_sprite.png", "images/sprites/canine_body_sprite.png", "images/sprites/canine_head_sprite.png", "images/sprites/feline_body_sprite.png", "images/sprites/feline_head_sprite.png", "images/sprites/avian_wing_sprite.png", "images/sprites/avian_body_sprite.png", "images/sprites/avian_head_sprite.png", "images/sprites/bovine_body_sprite.png", "images/sprites/bovine_head_sprite.png", "images/sprites/equine_body_sprite.png", "images/sprites/equine_head_sprite.png", "images/sprites/reptilian_1_body_sprite.png", "images/sprites/reptilian_2_body_sprite.png", "images/sprites/reptilian_3_body_sprite.png", "images/sprites/reptilian_head_sprite.png", "images/sprites/succubus_body_sprite.png", "images/sprites/succubus_head_sprite.png", "images/sprites/succubus_wing_sprite.png"] >> <<run SpriteCombine.initCache();>> <<include "CodexData">> <<include "Journal_Data">> <<include "Dispatch_Data">> <<if recall($premiumCode, false) is true>> <<notify 3s>>Patreon Code Activated!<</notify>> <</if>>
<<link '<i class="fa-solid fa-crown"></i>Patreon Code'>> <<SimpleUI "PatreonCode" "fit">> <</link>> <<link '<i class="fa-solid fa-screwdriver-wrench"></i>ChangeLogs'>> <<SimpleUI ChangeLogs>> <</link>> <a href="https:\\discord.gg/RNbvNpjXZW" target="_blank"><i class="fa-brands fa-discord"></i>Discord</a> <a href="https:\\www.patreon.com/c/AxiomNova" target="_blank"><i class="fa-brands fa-patreon"></i>Patreon</a> <a href="https:\\axiomnova.itch.io/thelastlord" target="_blank"><i class="fa-brands fa-itch-io"></i>Itch.io</a>
<<if $gamestatus.stage == 0 >> <<if !Story.has("0_0DebugControl")>><<= L.gamesubtitle>><br><</if>> <span id="version">V0.51.1 (2025.12.18)</span> <</if>>
<style> .UIbox_summon { grid-template-rows: 100%; display: grid; grid-template-columns: 1fr 12rem 1fr; } .couplepic { } .couplemiddle { display: flex; height: 100%; position: relative; flex-direction: column-reverse; } .coupleleft { z-index: 10; height: 100%; position: relative; background-color: rgba(0, 0, 0, 0.7); } .coupleright { z-index: 10; height: 100%; position: relative; background-color: rgba(0, 0, 0, 0.7); } #leftactionpanel { width: 100%; z-index: 10; display: flex; height: 100%; position: relative; background-color: rgba(0, 0, 0, 0.5); align-items: center; justify-content: center; } #rightactionpanel { width: 100%; z-index: 10; display: flex; height: 100%; position: relative; background-color: rgba(0, 0, 0, 0.5); align-items: center; justify-content: center; } #leftselectpanel { border: 2px solid var(--color-c); height: 100%; display: flex; width: 100%; position: relative; } #rightselectpanel { border: 2px solid var(--color-c); height: 100%; display: flex; width: 100%; position: relative; } #summonrift img { z-index: 0; position: absolute; top: 0%; left: 50%; max-height: 60vh; max-width: 50vh; animation: riftFloat 4s ease-in-out infinite; } @keyframes riftFloat { 0%, 100% { opacity: 1;transform: translateX(calc(-50% - 2px)) translateY(calc(0% - 2px)) scale(1.01); filter: drop-shadow(0 0 3rem var(--color-e));} 50% { opacity: 0.9;transform: translateX(calc(-50% + 2px)) translateY(calc(0% + 2px)) scale(1); filter: drop-shadow(0 0 0rem var(--color-c));} } #summonset { z-index: 90; font-size: 2rem; transition: transform 0.5s; &:hover { transform: rotate(180deg); } } #coupleicon { width: 100%; } .coupledo { position: relative; } #coupleicon img { width: 6rem; height: 6rem; animation: riftbell 4s ease-in-out infinite; } #coupleicon img:hover { transform: scale(1.02); filter: brightness(1.1); } #summoninfocard { font-size: 1rem; height: 8rem; max-width: 12rem; z-index: 100; padding: 0.5rem; border-radius: 0.25rem; margin: 1rem auto; position: relative; background: var(--bg-body); } #summonbell { font-size: 2rem; animation: riftbell 4s ease-in-out infinite; } @keyframes riftbell { 0%, 100% { opacity: 1;filter: drop-shadow(0 0 2rem var(--color-e));} 50% { opacity: 0.8;filter: drop-shadow(0 0 0rem var(--color-c));} } #summonbutton { position: relative; justify-self: center; } .SummonCG{ left:5rem; } .summonrift { height: 100%; width: 100%; position: absolute; } .summonLabel { left: 0; position: absolute; display: flex; flex-direction: column; } </style> <<set $chars.selected = null >> <<if $chars.dataMap.has($summon.slots[0]?.id)>><<set $summon.slots[0] = $chars.dataMap.get($summon.slots[0].id)>><<else>><<set $summon.slots[0] = null>><</if>> <<if $chars.dataMap.has($summon.slots[1]?.id)>><<set $summon.slots[1] = $chars.dataMap.get($summon.slots[1].id)>><<else>><<set $summon.slots[1] = null>><</if>> <div class="UIbox UIbox_summon"> <div class="coupleleft"> <div class="leftTogglePanel hidden" id="leftselectpanel"> <div style="top: 0rem;position: absolute;right: -5rem;z-index:100;"><button id="window-close" onclick='$.wiki(`<<addclass ".leftTogglePanel" "hidden">><<removeclass "#leftactionpanel" "hidden">><<set $summon.currentsolt = -1 >>`)'>❌</button></div> <div id="leftList" style="flex: 1;"></div> </div> <div class="leftTogglePanel" id="leftactionpanel"> <<do tag "selected summon">> <<if $chars.selected && ($summon.currentsolt == 0)>> <<elseif ($chars.selected?.id == $summon.slots[0]?.id) && ($summon.currentsolt == 1)>> <<set $summon.slots[0] = null >> <<set $summon.slotsId[0] = null >> <<else>> <</if>> <div class="couplepic"> <<if $summon.slots[0]>> <div style="top: 0%;position: absolute;right: 0px;z-index:100;"><button id="window-close" onclick='$.wiki(`<<set $summon.slots[0] = null>><<set $summon.currentsolt = -1>><<goto "Summon_UI">>`)'>❌</button></div> <<ShowPortrait $summon.slots[0] "left" false>> <</if>> </div> <div class="absolute-center-bottom" id="coupleaction"> <<Infocard $summon.slots[0] $summon.slots[1]>> <<if Flag('tutorial') >= 4>> <<SummonActionFlex $summon.slots[0] 0>> <</if>> </div> <</do>> </div> </div> <div class="couplemiddle"> <div class="summonrift" id="summonrift"> [img[setup.ImagePath+'ui/bg/planarrift.webp']] </div> <div class="coupledo"> <<do tag "selected summon">> <div class="summonLabel"> <<link "<div id='summonset'>⚙️</div>">><<run UI.settings();>><</link>> <<set _teleportImg = 'ui/couple/teleport.png' >> <<if $summon.slots[0] && $summon.slots[1] && ($summon.slots[0].parental.includes($summon.slots[1].name) || $summon.slots[0].offspring.includes($summon.slots[1].name))>> <<set _teleportImg = 'ui/couple/teleport02.png' >> <<HoverTip `L.notfavored`>><span id="summonbell">🔔</span><</HoverTip>> <</if>> </div> <</do>> <<do tag "selected summon">> <div id="coupleicon"> <div id="summonbutton"> <div id="summonstart"> <<link [img[setup.ImagePath+ _teleportImg][Summon_UI]]>> <<if Flag('tutorial') == 5>> <<SetFlag 'tutorial' 6>> <<run Msg.add('eventcard', 'Event_Stellafall_Ritual1')>> <<run Msg.add('eventcard', 'Event_Stellafall_Ritual2')>> <<run setup.pairOnce($summon.slots[0], $summon.slots[1])>> <</if>> <<if !$summon.slots[0] || !$summon.slots[1]>> <<run UI.alert(L.summonRequirement)>> <<elseif $summon.slots[0].preg || $summon.slots[1].preg>> <<run UI.alert(L.duringpreg);>> <<elseif ($manage.energy.current - ($summon.slots[0].costEnergy + $summon.slots[1].costEnergy)) lt 0>> <<run UI.alert(L.energyWarning)>> <<elseif $summon.slots[0].currentAPFloor lt 1 || $summon.slots[1].currentAPFloor lt 1>> <<run UI.alert(L.apWarning)>> <<else>> <<SummonStart $summon.slots[0] $summon.slots[1]>> <</if>> <</link>> </div> </div> </div> <</do>> <div id="summoninfocard"><<do tag "selected summon">> <<= L.totalluck>> <<if $summon.slots[0] && $summon.slots[1]>><<= ($summon.slots[0]?.giveFer + $summon.slots[1]?.giveFer)>><<else>>-<</if>><br> 🌱 <<if $summon.slots[0] && $summon.slots[1] >><<= MonsterBreeder.preview($summon?.slots[0], $summon?.slots[1])>>(+<<= ($manage.summon.chanceBonus * 100).toFixed(0)>>%)<<else>>-<</if>><br> <<= L.totalconsumption>>⚡<<if $summon.slots[0] && $summon.slots[1] >><<= $summon.slots[0]?.costEnergy + $summon.slots[1]?.costEnergy>><<else>>-<</if>><br> <<= L.currentstatus>>⚡<<= $manage.energy.current>> <</do>> </div> </div> <<include "Summon_Group">><<do tag "summon">><<timed 1s>><<run setup.renderGroups()>><</timed>><</do>> </div> <div class="coupleright"> <div class="rightTogglePanel hidden" id="rightselectpanel"> <div style="top: 0rem;position: absolute;left: 0rem;z-index:100;"><button id="window-close" onclick='$.wiki(`<<addclass ".rightTogglePanel" "hidden">><<removeclass "#rightactionpanel" "hidden">><<set $summon.currentsolt = -1 >>`)'>❌</button></div> <div id="rightList" style="flex: 1;"></div> </div> <div class="rightTogglePanel" id="rightactionpanel"> <<do tag "selected summon">> <<if $chars.selected && ($summon.currentsolt == 1)>> <<elseif ($chars.selected?.id == $summon.slots[1]?.id) && ($summon.currentsolt == 0)>> <<set $summon.slots[1] = null >> <<set $summon.slotsId[1] = null >> <<else>> <</if>> <div class="couplepic"> <<if $summon.slots[1]>> <div style="top: 0%;position: absolute;left:5rem;z-index:100;"><button id="window-close" onclick='$.wiki(`<<set $summon.slots[1] = null>><<goto "Summon_UI">>`)'>❌</button></div> <<ShowPortrait $summon.slots[1] "right" false>> <</if>> </div> <div class="absolute-center-bottom" id="coupleaction"> <<Infocard $summon.slots[1] $summon.slots[0]>> <<if Flag('tutorial') >= 4>> <<SummonActionFlex $summon.slots[1] 1>> <</if>> </div> <</do>> </div> </div>
<<widget "SummonActionFlex">> <<set _actionMon = _args[0] >> <<set _actionSide = _args[1] >> <<capture _actionMon _actionSide>> <div class="flex-row-sb1"> <div class="flex-col-center"> <div id="actionicon"> <<link "<<showframe 'icon64' 'Quick' 4.5>>">> <<set $summon.currentsolt = _actionSide>> <<if _actionSide == 0>> <<replace "#rightList">><<MapList $chars.tempList>><</replace>> <<replace "#leftList">><</replace>> <<addclass '.rightTogglePanel' 'hidden'>> <<removeclass "#rightselectpanel" 'hidden'>> <<elseif _actionSide == 1>> <<replace "#leftList">><<MapList $chars.tempList>><</replace>> <<replace "#rightList">><</replace>> <<addclass '.leftTogglePanel' 'hidden'>> <<removeclass "#leftselectpanel" 'hidden'>> <<else>> <</if>> <</link>> </div> <span id="actiontext"><<= "Quick">></span> </div> <div class="flex-col-center"> <div id="actionicon"> <<link "<<showframe 'icon64' 'Detail' 4.5>>">> <<set $summon.currentsolt = _actionSide>> <<goto "Monsters_UI">> <</link>> </div> <span id="actiontext"><<= "Detail">></span> </div> <<if Flag('tutorial') >= 8>> <div class="flex-col-center"> <div id="actionicon"> <<link "<<showframe 'icon64' 'Feed' 4.5>>">> <<if _actionMon>> <<SetFlag 'enblefeed'>> <<set $manage.feed.feedCp.charID = _actionMon.id >> <<SimpleUI "Storage">> <<else>> <<run UI.alert(L.selecttarget)>> <</if>> <</link>> </div> <span id="actiontext"><<= L.feed>></span> </div> <div class="flex-col-center"> <div id="actionicon"> <<link "<<showframe 'icon64' 'Harvest' 4.5>>">> <<if _actionMon>> <<HarvestMon _actionMon>> <<goto 'Summon_UI'>> <<else>> <<run UI.alert(L.selecttarget)>> <</if>> <</link>> </div> <span id="actiontext"><<= L.harvest>></span> </div> <</if>> </div> <</capture>> <</widget>>
<style> .collcontainer { width: 15rem; transform: translateX(-50%); left: 50%; position: relative; margin-bottom: 1rem; z-index: 99; max-width: 20rem; margin: 0 auto; } h1 { color: #e63946; font-size: 1.2rem; margin-bottom: 15px; text-align: center; } .section { border-radius: 6px; margin-bottom: 1rem; } .queue-slots { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; } .queue-slot { padding: 8px; background: rgba(20, 20, 30, 0.8); border-radius: 4px; border-left: 3px solid #457b9d; font-size: 0.85rem; } .queue-slot.filled { border-left-color: #e63946; } .slot-position { color: #457b9d; font-weight: bold; margin-bottom: 4px; font-size: 0.9rem; } .character-info { border-bottom: 1px solid #373737; grid-template-columns: 1.5rem 1fr 2rem 2rem 3rem; display: grid; } .character-name { color: #e0e0e0; } .character-ap { color: #ff6b72; font-weight: bold; } .group-list { display: none; flex-direction: column; gap: 0.5rem; margin-bottom: 0.5rem; max-height: 30rem; overflow-y: auto; } .group-list::-webkit-scrollbar { width: 4px; } .group-list::-webkit-scrollbar-track { background: rgba(10, 10, 15, 0.5); } .group-list::-webkit-scrollbar-thumb { background: #457b9d; } .group-item { background: rgba(20, 20, 30, 0.8); border-radius: 0.25rem; padding: 0.5rem; border: 1px solid #ffffff; } .group-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; } .group-name { color: #e0e0e0; font-weight: bold; font-size: 0.9rem; } .group-actions { display: flex; gap: 6px; } .btn { max-width: 6rem; padding: 0.5rem 1rem; border-radius: 0.25rem; border: none; cursor: pointer; font-size: 0.8rem; font-weight: bold; } .btn-primary { background: #e63946; color: white; } .btn-primary:hover { background: #ff6b72; } .btn-secondary { background: #457b9d; color: white; } .btn-secondary:hover { background: #5a9bc5; } .btn-small { padding: 4px 8px; font-size: 0.75rem; } .gpaction-btn { background: rgba(69, 123, 157, 0.7); border: none; color: white; width: 24px; height: 24px; border-radius: 3px; cursor: pointer; font-size: 0.8rem; } .gpaction-btn:hover { background: rgba(69, 123, 157, 1); } .delete-btn { background: rgba(230, 57, 70, 0.7); } .delete-btn:hover { background: rgba(230, 57, 70, 1); } .input-small { width: 40px; background: transparent; border: none; border-bottom: 1px solid #457b9d; color: #e0e0e0; text-align: center; font-size: 0.85rem; } .name-input { width: 8rem; background: rgb(51 51 51 / 80%); border: 1px solid #2a1f35; border-radius: 3px; color: #e0e0e0; padding: 0.25rem; font-size: 0.85rem; } .controls { display: flex; gap: 8px; margin-top: 10px; justify-content: space-around; } .empty-message { text-align: center; color: #8a8a9e; padding: 15px; font-size: 0.85rem; } </style> <div class="collcontainer"> <div class="section"> <div class="group-list" id="group-list"></div> <div class="controls"> <button id="save-btn" class="btn btn-secondary"><i class="fa-solid fa-star"></i><i class="fa-solid fa-user-group"></i></button> <button id="clear-btn" class="btn btn-primary"><i class="fa-solid fa-eye"></i><i class="fa-solid fa-user-group"></i></button> </div> </div> </div> <<done>><<script>> const charData = State.variables.chars; setup.renderGroups = renderGroups; function init() { renderGroups(); setupEventListeners(); } function renderGroups() { const groupList = document.getElementById('group-list'); groupList.innerHTML = ''; if (charData.groups.length === 0) { groupList.innerHTML = '<div class="empty-message">No Groups</div>'; return; } charData.groups.forEach((group, index) => { const item = document.createElement('div'); item.className = 'group-item'; item.dataset.index = index; group.characters = group.characters.filter(char => charData.dataMap.get(char) !== undefined); item.innerHTML = ` <div class="group-header"> <input type="text" class="name-input group-name-input" value="${group.name}" style="min-width: 7rem;"> <div class="group-actions"> <button class="btn btn-small btn-secondary load-btn"><i class="fa-solid fa-arrow-up-from-bracket"></i></button> <button class="gpaction-btn delete-btn">×</button> </div> </div> <div style="font-size: 0.8rem;"> ${group.characters.map((char, posIndex) => ` <div class="character-info"> <span>${setup.gender2pic[charData.dataMap.get(char).gender]}</span> <span>${charData.dataMap.get(char).name}</span> <span><i class="fa-solid fa-heart"></i>${Math.trunc(charData.dataMap.get(char).currentAPFloor)}</span> <span>Lv.${charData.dataMap.get(char).level.lv}</span> <span><i class="fa-solid fa-seedling"></i>${charData.dataMap.get(char).giveFer}%</span> </div> `).join('')} </div> `; const loadBtn = item.querySelector('.load-btn'); loadBtn.addEventListener('click', () => { loadGroup(index); }); const deleteBtn = item.querySelector('.delete-btn'); deleteBtn.addEventListener('click', () => { deleteGroup(index); }); const nameInput = item.querySelector('.group-name-input'); nameInput.addEventListener('change', () => { renameGroup(index, nameInput.value); }); groupList.appendChild(item); }); } function saveToGroup() { const hasCharacters = State.variables.summon.slots.some(slot => slot !== null); if (!hasCharacters) { return; } const name = prompt("Group name:", `Group ${charData.groups.length + 1}`); if (!name) return; const characters = State.variables.summon.slots .filter(slot => slot !== null) .map(char => char.id); charData.groups.push({ name: name, characters: characters }); renderGroups(); } function loadGroup(index) { const group = charData.groups[index]; group.characters = group.characters.filter(id => charData.dataMap.has(id)); const slots = State.variables.summon.slots; const chars = group.characters.map(id => charData.dataMap.get(id)); const emptyCount = slots.filter(s => s == null || s === '').length; if (chars.length === 2) { if (emptyCount >= 2) { setup.setSlotChar(slots, null, chars[0]); setup.setSlotChar(slots, null, chars[1]); } else { setup.setSlotChar(slots, 0, chars[0]); setup.setSlotChar(slots, 1, chars[1]); } } else if (chars.length === 1) { const hasEmpty = emptyCount > 0; setup.setSlotChar(slots, hasEmpty ? null : 0, chars[0]); } $.wiki(`<<redo "selected">>`); } function deleteGroup(index) { charData.groups.splice(index, 1); renderGroups(); } function renameGroup(index, newName) { if (newName.trim()) { charData.groups[index].name = newName.trim(); } } function showGroups() { const groupList = document.getElementById('group-list'); groupList.style.display = groupList.style.display == 'flex' ? 'none' : 'flex'; } function setupEventListeners() { document.getElementById('save-btn').addEventListener('click', saveToGroup); document.getElementById('clear-btn').addEventListener('click', showGroups); } init(); <</script>><</done>>
<style> .radar-stat-table { gap: 0.1rem; } .radar-container { height: auto; } .card-front button { cursor: pointer; font-size: 0.9rem; padding:0.4rem; } </style> <div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Summon">> </div> </div> <<set _FacHelp = "CodexEntry_Building_PlanarRift" >> <<done>> <<timed 0.2s>> <<if Flag('tutorial') == 2>> <<append "#pageClose">> <<set _tutorl = [ { el: '#pageClose', text: 'Click "Back" to return to the MainHall.' } ] >> <<TutorList _tutorl>> <</append>> <</if>> <<if Flag('tutorial') == 5>> <<append "#summonstart">> <<set _tutorl = [ { el: '#summonstart', text: L.summonStartButton } ] >> <<TutorList _tutorl>> <</append>> <</if>> <</timed>> <</done>> <<if Flag('miraelflag') == 2>> <<set $summon.slots = [$chars.dataMap.get("you"), $chars.dataMap.get("mirael")]>> <<run Msg.add('eventcard', 'Event_Leah_infuser')>> <</if>> <<if Flag('leahflag') == 3>> <<SetFlag 'eninfuser' 2>> <<SetFlag 'leahflag' 4>> <<run Msg.add('eventcard', 'Event_Mirael_first', 3)>> <</if>> <<if Flag('miraelflag') == 4>> <<run setup.pairOnce($chars.dataMap.get("mirael"), $chars.dataMap.get("you"))>> <<SetFlag 'miraelflag' 5>> <<pickup $storage "Basic_Infuser_Core" 1>> <<run EventBus.emit("tech:requestUnlock", "infuser1");>> <<run Msg.add('wiki', "<<run UI.alert('A device infused with planar energy, used to unlock and boost Normal Pregnancy chances, with upgrades increasing effectiveness.<br>Please click the [Girls] in the Bedroom.');>>")>> <</if>> <<run Msg.show();>>
<<widget SummonStart>> <<set _imglink = coupledata(_args[0], _args[1])>> <<if _imglink == null>> <<DualSummon $summon.slots[0] $summon.slots[1]>> <<else>> <<SimpleWiki "<<ShowSummonCG _imglink>>" "fullpop">> <</if>> <<redo "topbar selected summon">> <</widget>> <<widget ShowSummonCG>> <style> .summoncg { height: 100%; width: 100%; position: relative; } #ui-close { position: absolute; height: 3rem; z-index: 100; color: red; width: 3rem; font-size: 2.5rem; bottom: 2rem; right: 2rem; transition: transform 0.5s; } </style> <<link '<div class="summoncg">[img[setup.ImagePath + _args[0]]]<div id="ui-close">➡️</div></div>'>> <<DualSummon $summon.slots[0] $summon.slots[1]>><<redo "topbar selected">> <</link>> <</widget>> <<widget DualSummon>> <<nobr>> <<CardPop _args[0] _args[1] $manage.cardSelect>> <<run setup.pairOnce(_args[0], _args[1])>> <</nobr>> <</widget>> <<widget MergeWrong>> <<set _newmonster = mixMonsters(_args[0], _args[1])>> <<set _newmonster.gender = "girl" >> <<set _newmonster.name = setup.getname("girl") >> <<set _newmonster.id = setup.newID() + "wrong">> <<set _newmonster.rank = 0 >> <<set _newmonster.type = "wrong" >> <<set _newmonster.img = "monsters/wrong/rank0"+_newmonster.race+".webp">> <<for _attrn range setup.coreStats>> <<set _newmonster[_attrn].op = random(0, 1)>> <</for>> <<set _newmonster.look.base = random(0, 30) >> <<run _newmonster.parental = [_args[0].name, _args[1].name]>> <<run _newmonster.traits = {}>> <<run _newmonster.addTrait("cursed", 1)>> <<set $addmonster = _newmonster>> <<set $manage.seedCount += 1 >> <</widget>> <<widget MergeMonsters>> <<set _newmonster = mixMonsters(_args[0], _args[1])>> <<if _newmonster.gender != "boy">><<run _newmonster.addTrait("virginalpurity", 1);>><</if>> <<set $addmonster = _newmonster>> <<set $manage.seedCount += 1 >> <</widget>>
<style> .techtree-right { padding: 1rem; height: 100%; flex: 1; position: relative; overflow: auto; background: #1a1816; flex-direction: column; } .node-name { font-weight:bold; margin:0.5rem 0.3rem 0.2rem; line-height:1.3; } .node-cost { font-size:0.75rem; color:#ffaa66; } .node-tier { position:absolute; top:0.25rem; right:0.3rem; background:#000c; padding:1px 0.35rem; border-radius:0.25rem; font-size:0.75rem; color:#d7b35f; } #points { text-align: center; border: 1px solid var(--cm-border); font-size: 0.8rem; } .pointsbox { top: 1rem; right: 2rem; width: min-content; padding: 0.5rem; background: var(--bg-overlay); z-index: 500; position: absolute; font-size: 0.9rem; font-weight: initial; } #confirmBox { width: 20rem; text-align: center; } #confirmBox p { margin:0.75rem 0; } #confirmCost { color:#ffaa66; font-weight:bold; font-size:1.13rem; } .treeBox { position: relative; height: 100%; display: flex; width: 100%; } </style> <<timed 0.1s>> <div class="treeBox"> <div class="pointsbox"> <details open> <summary>🔺Points:</summary> <<do tag "tech">><<ObjectList $tech.techPoint>><</do>> </details> </div> <div class="nav-list-col" id="left"> <<do tag "tech">><<for _catKey range $tech.enTrees>> <<set _catData = setup.TechTreeConfig.categories[_catKey]>> <<capture _catKey _catData>> <<set _catclass = "nav-tab " + (_catKey == $tech.currentCategory ? "active":"" )>> <<link '<div @class="_catclass">_catData.name</div>'>> <<run setup.TechTreeConfig.switchCategory(_catKey)>> <</link>> <</capture>> <</for>><</do>> </div> <div class="techtree-right" id="right"> <<do tag "tech">> <<set _tracks = setup.TechTreeConfig.categories[$tech.currentCategory].tracks>> <<for _track range _tracks>> <<if $tech.techTracks.includes(_track.id)>><</if>> <<set _nodes = _track.nodes>> <div class="flex-row-left-center"> <div style="width: 4rem;text-align: center;"><span><<= _track.name>></span></div> <div @class='"flex-row-left-center "' id="nodes"> <<for _node range _nodes>> <<capture _node>> <div @class="_track.id + ' node ' + ($tech.techNodes.includes(_node.id) ? 'unlocked' : '')"> <div class="node-tier">T<<= _node.tier>></div> <div class="node-name"><<= _node.name>></div> <div class="node-name"><<= _node.bonusDesc>></div> <<if $tech.techNodes.includes(_node.id)>> <<link "Unlocked">><<set $tech.currentNode = _node.id >><<run popup(`<<TechDesc _node>>`, { title: "Confirm Unlock", buttons: [ { text: "Cancel", class: "btn-grey", action: () => popup.close() } ] })>> <</link>> <<else>> <div class="node-cost"><<= _node.cost>> pts</div> <<button "Unlock">><<set $tech.currentNode = _node.id >><<timed 0.1s>><<run popup(`<<TechDesc _node>>`, { title: "Confirm Unlock", class: "ingreen", buttons: [ { text: "Unlock", class: "btn-green", action: () => {EventBus.emit("tech:requestUnlock", D.tech.currentNode);popup.close();}}, { text: "Cancel", class: "btn-grey", action: () => popup.close() } ] })>><</timed>> <</button>> <</if>> </div> <</capture>> <</for>> </div> </div> <</for>> <</do>> </div> </div> <</timed>>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "TechTree">> </div> </div>
<style> .butt.active { opacity: 0.5; border: 2px solid rgb(167, 0, 103); } .interact-container { overflow-y: auto; min-height: 10rem; height: 100%; background: #111827; padding: 0.5rem; box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); border-radius: 0.25rem; } .interact-header { text-align: center; color: #b91c1c; margin-bottom: 0rem; text-shadow: 0 0 3px #b91c1c; } .interact-main { justify-self: center; bottom: 0; max-width: 60rem; width: 100%; position: absolute; display: flex; gap: 0rem; margin-bottom: 1rem; flex-direction: column; } .interact-portrait { flex: 1; background: rgba(31, 41, 55, 0.6); border: 1px solid #374151; border-radius: 0.25rem; padding: 0.75rem; text-align: center; } .interact-portrait img { max-width: 100%; height: auto; border-radius: 0.125rem; } .interact-portrait p { font-size: 1rem; margin-top: 0.5rem; } .interact-actions { flex: 1; display: flex; flex-direction: column; } .interact-primary, .interact-sub-menu { overflow-y: auto; height: 8rem; grid-template-rows: repeat(2, 1fr); display: grid; grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 0.5rem; margin-top: 0.5rem; padding: 0.5rem; background: rgb(31 41 55 / 80%); border: 1px solid #374151; border-radius: 0.25rem; } .interact-t { grid-template-rows: auto; height: auto; border: none; background: none; } .interact-button, .interact-sub-button { height: 3.2rem; line-height: 1.2; display: flex; align-items: center; justify-content: center; gap: 0.25rem; padding: 0.75rem; font-size: 0.875rem; font-weight: 500; color: #d1d5db; background: rgba(127, 29, 29, 0.9); border: 1px solid #374151; border-radius: 0.125rem; cursor: pointer; transition: background 0.2s, transform 0.2s; flex-direction: column; } .interact-sub-button { background: rgba(88, 28, 135, 0.8); } .interact-sub-button.locked { font-size: 1.25rem; background: rgb(135 135 135 / 80%); &:hover { background: rgb(100 100 100 / 80%); transform: scale(1);} } .interact-button.halflocked { background: rgb(67 16 16 / 80%); &:hover { background: rgb(67 16 16 / 80%); transform: scale(1);} } .interact-sub-button.halflocked { background: rgb(38 0 69 / 80%); &:hover { background: rgb(38 0 69 / 80%); transform: scale(1);} } .interact-button:hover, .interact-sub-button:hover { background: rgba(127, 29, 29, 0.8); transform: translateY(-4px); } .interact-sub-button:hover { background: rgba(88, 28, 135, 0.8); } .interact-sub-button.hidden { visibility: hidden; } .interact-info { background: rgba(31, 41, 55, 0.6); border: 1px solid #374151; border-radius: 0.25rem; padding: 0.75rem; height: 100%; overflow-y: auto; font-size: 0.875rem; } .interact-info h2 { font-size: 1rem; color: #b91c1c; margin-bottom: 0.5rem; } .interact-info p { margin: 0.25rem 0; } .interact-UI { display: grid; grid-template-columns: 35rem 1fr 20rem; grid-template-rows: 100%; } .interact-UI-left { position: relative; display: grid; grid-template-rows: 1fr; height: 100%; } .interact-UI-left-cg { height: 100%; } .interact-UI-right { overflow-y: auto; display: flex; flex-direction: column; } .interact-you { gap: 2rem; display: flex; flex-direction: row; align-items: center; } .interact-you-skill { line-height: normal; display: flex; flex-direction: column; } </style> <<set _doChar = $chars.dataMap.get($training.trainID.do) || $chars.dataMap.get("you") >> <<set _withGirl = $chars.dataMap.get($training.trainID.to) || $chars.dataMap.get("catmaid") >> <div class="interact-UI UIbox"> <div class="interact-UI-right" > <<do tag "interact">><<TrainDataBox>><</do>> <div class="interact-container"> <div class="interact-info" id="info-content"> <p>Select an action to interact with <<= _withGirl.name>>.</p> </div> </div> </div> <div class="interact-UI-left"> <div class="interact-UI-left-charcg" style="align-content: center;"> <style> .fullInfo { display: flex; } div[data-do-tags="selected shoplist"] .monsterinfoleftpic { margin-left: 18rem; } </style> <div class="fullInfo"> <<ShowPortrait _withGirl>> </div> </div> <div class="interact-main"> <div class="interact-actions"> <div class="interact-sub-menu interact-t"></div> <div class="interact-you"> <div class="interact-you-skill"><span><<= "Train Point: ">></span><<do tag "interact">><<LimitActions `Math.trunc(_doChar.stamina.current / 10)` `Math.trunc(_doChar.dailyStamina / 10)`>><</do>></div> </div> <div class="interact-primary"> <<set _unlockACTIONS = Object.keys(TRAINING_ACTIONS).filter(key => !["machine", "public", "tentacle", "cock", "special"].includes(key))>> <<for _cii, _cipi range _unlockACTIONS>> <<capture _cii _cipi>> <<if _cipi>> <<link '<div class="interact-button text-capitalize" @id="_cipi"><<= _cipi>></div>'>> <<set _cssid = "#" + _cipi>> <<removeclass '.interact-button' 'active'>> <<addclass _cssid 'active'>> <<redo "geneshow">> <<replace '.interact-sub-menu'>> <<ActionSubMenu _cipi _withGirl _doChar>> <</replace>> <</link>> <<else>> <<link '<div class="interact-button halflocked" onclick="Msg.add('notify', 'Requires higher attribute Tier.')"><<= _cipi>></div>'>> <<replace '.interact-sub-menu'>><</replace>> <</link>> <</if>> <</capture>> <</for>> <<link '<div class="interact-button" @id="_cipi"><<= "↩️Back">></div>'>><<goto 'Bedroom_UI'>><</link>> </div> </div> </div> </div> <<do tag "interact">><<GenesList _withGirl>><</do>> </div>
<div class="UIframe"> <<include "UI_TopBar">> <div class="Gcontainer"> <<include "Train">> </div> </div>
<<widget "ActionSubMenu">> <<do tag "interact">> <<for _cij, _cisi range Object.values(TRAINING_ACTIONS[_args[0]])>> <<capture _cij _cisi>> <<if _cisi["unlock"] && (_args[1].train.tame >= getFullAction(_cisi.id).require)>> <<link '<div class="interact-sub-button text-capitalize" @id="_cisi.name"><<= _cisi.name>><span class="text-fs075">Req Tame:<<= getFullAction(_cisi.id).require>></span></div>'>> <<if _args[2].stamina.current < 10>> <<prepend "#info-content">><p><<= "My Lord, You need more Train Point🟨!">></p><</prepend>> <<redo "interact">> <<elseif tryTrain(_cisi.id, _args[1]).success>> <<run Msg.add('wiki', '<<TrainCG _cisi.CG>>',5 ,true);>> <<run trainAction(_args[2], _args[1], _cisi.id)>> <<run _args[2].calcAttrInfo("stamina.current", -10, false)>> <<timed 1s>><<run _args[1].markDirty()>><<redo "interact">><</timed>> <<else>> <<prepend "#info-content">><p><<= tryTrain(_cisi.id, _args[1]).message>></p><</prepend>> <</if>> <</link>> <<elseif _cisi["unlock"]>> <<link '<div class="interact-sub-button halflocked" @id="_cisi.name" onclick="Msg.add('notify', 'Requires higher Tame.')"><<= _cisi.name>><span class="text-fs075">Req Tame:<<= getFullAction(_cisi.id).require>></span></div>'>> <</link>> <<else>> <<link '<div class="interact-sub-button locked" onclick="Msg.add('notify', 'Currently being debugged.')"><<= "🔒">></div>'>> <</link>> <</if>> <</capture>> <</for>> <</do>> <</widget>> <<widget "ShowInfo">> <<prepend "#info-content">> <span><<= langBank(_args[0])>></span> <</prepend>> <</widget>> <<widget "LimitStars">> <div class="limit-stars"> <<for _i range _args[1]>> <<if _i < _args[0]>> <div class="limit-star active"></div> <<else>> <div class="limit-star"></div> <</if>> <</for>> </div> <</widget>> <<widget "LimitHearts">> <div class="limit-hearts"> <<for _i range _args[1]>> <<if _i < _args[0]>> <div class="limit-heart">❤️</div> <<else>> <div class="limit-heart">🖤</div> <</if>> <</for>> </div> <</widget>> <<widget "LimitActions">> <div class="limit-actions"> <<if _args[0] > _args[1]>> <<for _i range _args[0]>> <div class="limit-action active"></div> <</for>> <<else>> <<for _i range _args[1]>> <<if _i < _args[0]>> <div class="limit-action active"></div> <<else>> <div class="limit-action"></div> <</if>> <</for>> <</if>> </div> <</widget>> <<widget "UnlockDNA">> <div class="dna-container" id="dnaHelix"></div> <<done>> <<script>> showDNA(); <</script>><</done>> <</widget>> <<widget "WinShow">> <style> .celebration-container { top: 0; left: 0; position: fixed; width: 100vw; height: 100vh; overflow: visible; z-index: -1; pointer-events: none; } .celebration-particle { position: absolute; will-change: transform, opacity; transition: all 0.8s ease-out; pointer-events: none; text-align: center; font-weight: bold; } </style> <<timed 0.1s>> <<run playEffect(_args[0])>> <</timed>> <<done>> <<script>> const box = document.getElementById('celebrationBox'); const colors = { gacha: ['#cc00ff', '#ff1515', '#ff0066', '#ff31c2'], levelup: ['#00ccff', '#00ffcc', '#00ff66', '#66ff00'], achievement: ['#ff00cc', '#ff0066', '#cc00ff', '#6600ff'], item: ['#ff9900', '#ffcc00', '#ffff00', '#ff6600'], victory: ['#ff0000', '#ffff00', '#ffffff', '#ff6600'] }; const symbols = { gacha: ['♥'], levelup: ['↑', '⇧', '⇑', '▲', '⤴'], achievement: ['★', '☆', '✧', '✦', '✪'], item: ['♦', '♢', '♠', '♥', '♣'], victory: ['⚔', '🛡', '🏆', '🎖', '🏅'] }; window.playEffect = function (type) { const particleCount = 20 + Math.floor(Math.random() * 30); const centerX = box.clientWidth / 2; const centerY = box.clientHeight / 2; for (let i = 0; i < particleCount; i++) { const p = document.createElement('div'); p.className = 'celebration-particle'; const colorIndex = Math.floor(Math.random() * colors[type].length); p.style.color = colors[type][colorIndex]; const symbolIndex = Math.floor(Math.random() * symbols[type].length); p.textContent = symbols[type][symbolIndex]; const size = 20 + Math.random() * 30; p.style.fontSize = `${size}px`; p.style.left = `${centerX - size/2}px`; p.style.top = `${centerY - size/2}px`; p.style.opacity = '0'; box.appendChild(p); setTimeout(() => { const angle = Math.random() * Math.PI * 2; const distance = 200 + Math.random() * 200; const tx = centerX + Math.cos(angle) * distance; const ty = centerY + Math.sin(angle) * distance; p.style.left = `${tx - size/2}px`; p.style.top = `${ty - size/2}px`; p.style.opacity = 1; setTimeout(() => { p.style.opacity = '0'; setTimeout(() => p.remove(), 100); }, 500); }, i * 10); }; }; <</script>> <</done>> <</widget>> <<widget "FlashShow">> <<timed 0.1s>> <<script>> const overlay = document.getElementById('flashOverlay'); overlay.style.animationIterationCount = State.temporary.args[0] ? State.temporary.args[0] : 2; overlay.style.display = 'block'; <</script>><</timed>> <</widget>> <<widget OpUpgrade>> <<run Dialog.create("", "infoPop")>> <<run Dialog.wiki(`<<UnlockDNA>>🔓<<= langBank(_args[0])>> Potential Tier Has Upgraded!`)>> <<run Dialog.open()>> <</widget>> <<widget TrainCG>> <<run Dialog.create("","cgPop")>> <<run Dialog.wiki(`<div id="flashOverlay" class="flash-overlay"></div>[img[setup.ImagePath +"event/train/"+_args[0]+".png"]]`)>> <<run Dialog.open()>> <</widget>>
<<widget TrainDataBox>> <style> .train-container { height: fit-content; display: flex; background: linear-gradient(to bottom, #111827, #2a0a0a); border: 2px solid #b91c1c; border-radius: 0.5rem; padding: 1rem; box-shadow: 0 0 1rem rgba(185, 28, 28, 0.4); } .train-right-column { width: 12.5rem; text-align: center; } .train-left-column { flex: 1; padding-right: 1rem; border-right: 1px solid #374151; } .train-title { font-size: 1.5rem; font-weight: bold; color: #b91c1c; margin-bottom: 0.5rem; text-shadow: 0 0 5px #b91c1c; } .train-sub-title { font-size: 1rem; color: #d1d5db; margin-bottom: 1rem; } .train-stat-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; padding: 5px; background: rgba(31, 41, 55, 0.6); border-radius: 5px; border: 1px solid #374151; height: 2rem; } .train-stat-label { font-size: 0.9rem; color: #d1d5db; width: 2.5rem; } .train-stat-bar { flex: 1; height: 1rem; background: rgba(127, 29, 29, 0.3); margin: 0; border-radius: 0.75rem; overflow: hidden; position: relative; } .train-stat-fill { height: 100%; background: linear-gradient(to right, #b91c1c, #dc2626); transition: width 0.3s ease; position: absolute; top: 0; left: 0; } .train-stat-value { font-size: 0.8rem; font-weight: bold; color: #f9fafb; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-shadow: 1px 1px 2px #000; min-width: 100%; text-align: center; } .train-stat-change { display: none; font-size: 0.8rem; color: #10b981; margin-left: 5px; } .train-traits { margin-top: 1rem; font-size: 0.85rem; color: #9ca3af; margin-bottom: 0.75rem; } .train-footer-stats { margin-left: 1rem; display: flex; justify-content: space-between; font-size: 0.85rem; color: #d1d5db; margin-bottom: 5px; flex-direction: column; align-items: flex-start; } .train-footer-stats div{ text-align: start; } .train-databox { min-width: 22rem; grid-template-columns: 1fr 10.5rem; gap: 1rem; display: grid; } .train-stat-opstars { display: grid; grid-template-columns: 2rem 1fr; align-items: center; } .train-stat-solo { width: 4.5rem; } .train-stat-one { grid-template-columns: 4rem 1fr; } .train-white { background: white;} .train-blue { background: var(--color-f);} .train-pink1 { background: var(--tr-unique);} .train-pink2 { background: var(--tr-preg);} .train-pink3 { background: #ffc0cbdd;} hr { border-top-color: rgb(67, 67, 67); } </style> <<script TwineScript>> <</script>> <<done>> <<script>> const attributes = { stamina: {}, mental: {}, heat: {} }; const animationRequests = {}; const TOTAL_STEPS = 50; const STEP_DURATION = 10; function initAttributes() { const char = State.temporary.withGirl; for (const attrName in attributes) { attributes[attrName].valueEl = document.getElementById(attrName+'-value'); attributes[attrName].barEl = document.getElementById(attrName+'-bar'); if (attrName ==="stamina") { attributes[attrName].max = char.dailyStamina; } else if (attrName ==="mental") { attributes[attrName].max = Math.trunc(Math.max(char.mental.current, char.getMental)); } else if (attrName ==="heat") { attributes[attrName].max = 100; } else { }; }; }; window.animateAttribute = function(attrName, changeValue) { const char = State.temporary.withGirl; if (attrName ==="stamina") { attributes[attrName].current = Math.trunc(char.stamina.current); } else if (attrName ==="mental") { attributes[attrName].current = Math.trunc(char.mental.current); } else if (attrName ==="heat") { attributes[attrName].current = char.train.heat; } else { attributes[attrName].current = setup.geneTier.currentExp(char[attrName].op) * 10; attributes[attrName].max = setup.geneTier.requiredExp(char[attrName].op) * 10; }; const targetValue = attributes[attrName].current + changeValue; if (changeValue === 0 || targetValue === attributes[attrName].current) { return; } let effectiveTarget = targetValue; if (attrName !="stamina") { if (targetValue > attributes[attrName].max) { effectiveTarget = attributes[attrName].max; } else if (targetValue < 0) { effectiveTarget = 0; } }; const startValue = attributes[attrName].current; const change = effectiveTarget - startValue; const stepValue = change / TOTAL_STEPS; let currentStep = 0; let startTime = null; if (animationRequests[attrName]) { cancelAnimationFrame(animationRequests[attrName]); animationRequests[attrName] = null; }; function step(timestamp) { if (!startTime) startTime = timestamp; const elapsed = timestamp - startTime; const progress = Math.min(elapsed / (TOTAL_STEPS * STEP_DURATION), 1); currentStep = Math.round(progress * TOTAL_STEPS); let current = Math.round(startValue + stepValue * currentStep); current = Math.min(Math.max(current, 0), attributes[attrName].max); const percent = (current / attributes[attrName].max) * 100; attributes[attrName].valueEl.textContent = current + ' / ' + attributes[attrName].max; attributes[attrName].barEl.style.width = percent + '%'; if (currentStep < TOTAL_STEPS) { animationRequests[attrName] = requestAnimationFrame(step); } else { attributes[attrName].current = effectiveTarget; animationRequests[attrName] = null; if (targetValue >= attributes[attrName].max) { attributes[attrName].current = 0; attributes[attrName].valueEl.textContent = '0 / ' + attributes[attrName].max; attributes[attrName].barEl.style.width = '0%'; setTimeout(() => { animateAttribute(attrName, targetValue - attributes[attrName].max); }, 200); } } } animationRequests[attrName] = requestAnimationFrame(step); }; initAttributes(); <</script>> <</done>> <div class="train-container"> <div class="train-left-column"> <div class="train-title"><<= _withGirl.name>><<if isVirgin(_withGirl)>> [🌸]<</if>></div> <div class="train-sub-title"><span style="font-size: 1.25rem;"><<LimitHearts _withGirl.bond.vl _withGirl.bond.max>></span></div> <div class="train-stat-row" id="Stamina" onclick='$.wiki(`<<ShowInfo "upgrade">>`)' > <span class="train-stat-label train-stat-solo">Stamina</span> <div class="train-stat-bar"> <<set _attoppro = `width: ${Math.trunc(_withGirl.stamina.current / _withGirl.dailyStamina * 100)}%;`>> <div class="train-stat-fill" id="stamina-bar" @style="_attoppro"></div> <span class="train-stat-value" id="stamina-value"><<= Math.trunc(_withGirl.stamina.current)>> / <<= _withGirl.dailyStamina>></span> </div> <span class="train-stat-change">+0%</span> </div> <div class="train-stat-row"> <span class="train-stat-label train-stat-solo">Mental</span> <div class="train-stat-bar"> <<set _attoppro = `width: ${Math.trunc(_withGirl.mental.current / _withGirl.getMental * 100)}%;`>> <div class="train-stat-fill train-blue" id="mental-bar" @style="_attoppro"></div> <span class="train-stat-value" id="mental-value"><<= Math.trunc(_withGirl.mental.current)>> / <<= Math.trunc(Math.max(_withGirl.getMental, _withGirl.mental.current))>></span> </div> <span class="train-stat-change">+0%</span> </div> <div class="train-stat-row"> <span class="train-stat-label train-stat-solo">Loyal</span> <div class="train-stat-bar"> <<set _attoppro = `width: ${(_withGirl.train.loyal).toFixed(0)}%;`>> <div class="train-stat-fill train-pink1" id="loyal-bar" @style="_attoppro"></div> <span class="train-stat-value" id="loyal-bar"><<= setup.loyalTier.tag(_withGirl.train.loyal)>><<= " | " + _withGirl.train.loyal + " / 100">></span> </div> <span class="train-stat-change">+0%</span> </div> <div class="train-stat-row"> <span class="train-stat-label train-stat-solo">Tame</span> <div class="train-stat-bar"> <<set _attoppro = `width: ${(_withGirl.train.tame).toFixed(0)}%;`>> <div class="train-stat-fill train-pink2" id="tame-bar" @style="_attoppro"></div> <span class="train-stat-value" id="tame-bar"><<= setup.tameTier.tag(_withGirl.train.tame)>><<= " | " + _withGirl.train.tame + " / 100">></span> </div> <span class="train-stat-change">+0%</span> </div> <div class="train-stat-row"> <span class="train-stat-label train-stat-solo">Heat</span> <div class="train-stat-bar"> <<set _attoppro = `width: ${(_withGirl.train.heat).toFixed(0)}%;`>> <div class="train-stat-fill train-white" id="heat-bar" @style="_attoppro"></div> <span class="train-stat-value" id="heat-value"><<= _withGirl.train.heat>> / 100</span> </div> <span class="train-stat-change">+0%</span> </div> <div class="train-databox"><div>Genes Mutation</div><div> <div class="train-stat-opstars"><span>Tier</span><span>Curren/Max</span></div></div></div> <<for _attop, _attopname range {"str":"STR", "dex":"DEX", "sta":"STA", "wil":"WIL", "cha":"CHA", "fer":"FER"}>> <<capture _attop _attopname>> <<set _nextop = setup.geneTier.upgrade(_withGirl[_attop].op);>> <<set _attoppro = `width: ${(setup.geneTier.progress(_withGirl[_attop].op) * 100).toFixed(0)}%;`>> <div class="train-databox"> <div class="train-stat-row"> <span class="train-stat-label"><<= _attopname>></span> <div class="train-stat-bar"> <div class="train-stat-fill" @id="_attop+'-bar'" @style="_attoppro"></div> <span class="train-stat-value" @id="_attop+'-value'"><<= Math.floor(setup.geneTier.currentExp(_withGirl[_attop].op) * 10)>> / <<= setup.geneTier.requiredExp(_withGirl[_attop].op) * 10>></span> </div> <span class="train-stat-change">+0%</span> </div> <div><<do tag "interact">> <div class="train-stat-opstars"><span><<= _withGirl.getRank(_attop)>></span> <span><<LimitStars _withGirl.atttostars(_attop) _withGirl.getattcap(_attop)>></span></div> <</do>></div> </div> <</capture>> <</for>> </div> <<do tag "interact">> <div class="train-right-column"> <div class="train-footer-stats"> <div>Sex Exp</div> <div>Oral: <<= _withGirl.genes.mouth.status.times || 0>></div> <div>Breast: <<= _withGirl.genes.breast.status.times || 0>></div> <div>Hands: <<= _withGirl.genes.hand.status.times || 0>></div> <div>Pussy: <<= _withGirl.genes.pussy.status.times || 0>></div> <div>Anal: <<= _withGirl.genes.ass.status.times || 0>></div> <div> </div> </div> <div class="train-footer-stats"> <div>Offspring: <<= _withGirl.offspring.length>></div> <div>Summon: <<= _withGirl.summon>></div> <div>Harvest: <br>-<<= getiteminfo(_withGirl.harvest,'name')>><br> </div> <div>Traits: <br><<TraitsList _withGirl>></div> </div> <div class="train-traits"></div> </div> <</do>> </div> <</widget>>
<<widget GenesList>> <style> .monster-status-panel { overflow-y: auto; max-width: 20rem; background: #1a1a2e; padding: 0.5rem; box-shadow: 0 1rem 2rem rgba(0,0,0,0.5); color: #e8e8ff; } .level2 { border-left-color: #ff69b494; margin-left: 1rem; background: #24132f; } .helptip { position: absolute; font-size: 1.25rem; } .item-name { color: #fff; } .item-value { color: #fff; } </style> <div class="monster-status-panel"> <<do tag "geneshow">> <<if def _cssid && _args[0].genes[_cipi]>> <div style="right: 21rem; position: absolute;"> <<OneGenePlus _args[0].genes[_cipi]>> </div> <</if>> <</do>> <div class="helptip"><<link "❓">><<SimpleUI "Gene_Help">><</link>></div> <div class="title-fs125"><span class="text-capitalize"><<= _args[0].name>></span><<if isVirgin(_args[0])>> [🌸]<</if>></div> <div class="grid-3col"> <<for _stat range setup.coreStats>> <<set _val to _args[0][_stat].op>> <div @class='"solt-center flex-col-center "+ _args[0].getRank(_stat)'> <div class="item-name"><<= _stat.toUpperCase() >></div> <div class="item-value"><<=_args[0].getRank(_stat)>><<= " ("+ _val + ")">></div> </div> <</for>> </div> <<for _onegene range setup.geneUtils.order(_args[0].genes)>> <<set _gene = _onegene.data >> <<OneGene _gene>> <</for>> </div> <</widget>> <<widget OneGene>> <<set _gene = _args[0] >> <<if !["cloth", "body", "skin"].includes(_gene.part)>> <div @class='"solt-card " + (_onegene.level == 2 ? "level2" : "")'> <div class="title-fs100"> <<= _gene.part.toUpperCase()>> <<if _gene.part == "pussy" && _gene.status.times === 0>>[Virgin]<</if>> </div> <div class="text-fs080"> <<=_gene.type>> </div> <div class="text-fs085 text-italic"> <<=setup.geneUtils.desc(_gene).slice(0,100)>> <<if setup.geneUtils.desc(_gene).length > 100>>...<</if>> </div> <div class="text-fs080"> <<if setup.genedata?.[_gene.part]?.isCalc>> <<set _mods to setup.geneUtils.modifiers(_gene)>> <<set _modText to []>> <<for _k, _v range _mods>> <<if Math.abs(_v) > 0.01>> <<set _modText.push(`${_k.toUpperCase()}${_v>0?"+":""}${_v.toFixed(1)}`)>> <</if>> <</for>> <<=_modText.join(" ")>> <</if>> </div> <div class="text-fs075"> <<if _gene.status?.sizeModifier !== undefined>> Modifier: <<= (setup.geneUtils.factor(_gene) * 100).toFixed(0) + "%">> | <</if>> Origin: <span class="text-capitalize"><<=_gene.race>></span> | Rank: <<=_gene.rank>> <<if _gene.isTraining>><br><span>Times: <<=_gene.status.times>> | First: <<=_gene.status.firstPartner>></span><</if>> </div> </div> <</if>> <</widget>> <<widget OneGenePlus>> <<set _gene = _args[0] >> <<if !["cloth", "body", "skin"].includes(_gene.part)>> <div class="solt-card" style="width: 20rem;"> <div class="title-fs100"> <<= _gene.part.toUpperCase()>> <<if _gene.part == "pussy" && _gene.status.times === 0>>[Virgin]<</if>> </div> <div class="text-fs080"> <<=_gene.type>> </div> <div class="text-fs085 text-italic"> <<=setup.geneUtils.desc(_gene).slice(0,100)>> <<if setup.geneUtils.desc(_gene).length > 100>>...<</if>> </div> <div class="text-fs080"> <<if setup.genedata?.[_gene.part]?.isCalc>> <<set _mods to setup.geneUtils.modifiers(_gene)>> <<set _modText to []>> <<for _k, _v range _mods>> <<if Math.abs(_v) > 0.01>> <<set _modText.push(`${_k.toUpperCase()}${_v>0?"+":""}${_v.toFixed(1)}`)>> <</if>> <</for>> <<=_modText.join(" ")>> <</if>> </div> <div class="text-fs075"> <<if _gene.status?.sizeModifier !== undefined>> Modifier: <<= (setup.geneUtils.factor(_gene) * 100).toFixed(0) + "%">> | <</if>> Origin: <span class="text-capitalize"><<=_gene.race>></span> | Rank: <<=_gene.rank>> <<if _gene.isTraining>><br><span>Times: <<=_gene.status.times>> | First: <<=_gene.status.firstPartner>></span><</if>> </div> <<set _status = _withGirl.genes[_gene.part].status >> <div> Awakening Level: <<= ""+ _status.level>> <<ProgressBar _gene.part setup.statLevel.currentExp(_status.experience) setup.statLevel.requiredExp(_status.experience)>></div> </div> <</if>> <</widget>> <<widget "ProgressBar">> <div class="train-stat-row"> <span class="train-stat-label train-stat-solo text-capitalize">_args[0]</span> <div class="train-stat-bar"> <<set _attoppro = `width: ${(_args[1] / _args[2] * 100).toFixed(0)}%;`>> <div class="train-stat-fill train-blue" @id="_args[0] + '-bar'" @style="_attoppro"></div> <span class="train-stat-value" @id="_args[0] + '-value'"><<= _args[1]>> / <<= _args[2]>></span> </div> <span class="train-stat-change">+0%</span> </div> <</widget>>
<<widget "TraitCard">> <<include "Codex_CSS">> <<set _key = _args[0]>> <<set _info to setup.queryTrait(_args[0])>> <<if _info>> <div class="stat-slab"> <div class="slab-header"> <div class="slab-icon"><<= getTraitIconByID(_key)>></div> <div class="slab-title-group"> <span class="trait-name"><<= getTraitNameByID(_key)>></span> </div> <<if _info.madeFrom>> <span class="trait-tier tier-high">TIER <<= _info.madeFrom.tier>></span> <<else>> <span class="trait-tier tier-base">BASE</span> <</if>> </div> <div class="slab-desc"><<= getTraitDescByID(_key)>></div> <div class="alchemy-card"> <div class="card-body"> <div class="info-group"> <div class="group-title">🔍Formula:</div> <<if _info.madeFrom>> <span class="fusion-item"> = <<= _info.madeFrom.src.join(" + ")>> </span> <<else>> <span class="fusion-item">Unknown</span> <</if>> </div> <div class="info-group"> <div class="group-title">🧪Fusion:</div> <<if _info.fusesInto.length > 0>> <<for _recipe range _info.fusesInto>> <div class="fusion-item"> <span class="plus">+</span> <span class="needs"><<= _recipe.needs.join(", ")>></span> <span class="arrow">➜</span> <span class="result"><<= _recipe.result>></span> <span class="tier-tag">T<<= _recipe.tier>></span> </div> <</for>> <<else>> <span class="fusion-item">Unknown</span> <</if>> </div> <div class="info-group conflict-group"> <div class="group-title">⚔️Conflicts:</div> <div class="conflict-list"> <<if _info.conflicts.length > 0>> <<for _enemy range _info.conflicts>> <span class="conflict-tag">- <<= _enemy>></span> <</for>> <<else>> <span class="fusion-item">Unknown</span> <</if>> </div> </div> </div> </div> </div> <</if>> <</widget>>
<div class="sidebar"> <h1>Hawkridge Console</h1> <div class="sidebar-group sidebar-domain"> <h2>Hawkridge - Pass</h2> <div class="sidebar-group-buttons"> <<if Flag('enbastion') == 2>><div class="sidebar-button" data-passage="Battle_UI"><span class="sidebar-icon">🛡️</span><<= "Bastion">></div><</if>> <<if $times.weekday == 4 || $times.total >= 25>><div class="sidebar-button" data-passage="Market_UI"><span class="sidebar-icon">💰</span><<= "Market">></div><</if>> <<if $times.weekday == 2>><div class="sidebar-button" data-passage="Slaver_UI"><span class="sidebar-icon">⛓️</span><<= "Slaver">></div><</if>> </div> </div> <<if Flag('enbuild') == 2>> <div class="sidebar-group sidebar-domain"> <h2>Hawkridge - Vale</h2> <div class="sidebar-group-buttons"> <div class="sidebar-button" onclick='$.wiki(`<<goto "Building_UI">>`)'><span class="sidebar-icon">🏘️</span>Hamlet</div> <div class="sidebar-button" data-passage="Building_UI"><span class="sidebar-icon">🌳</span>Wilds</div> <div class="sidebar-button" data-passage="Building_UI"><span class="sidebar-icon">⛰️</span>Mines</div> <<if $buildingEffects.slots.bountiful_fields > 0>><div class="sidebar-button" data-passage="Building_UI"><span class="sidebar-icon">🌾</span>Fields</div><</if>> </div> </div> <</if>> <div class="sidebar-group sidebar-temple"> <h2>Temple - MainWing</h2> <div class="sidebar-group-buttons"> <div class="sidebar-button" data-passage="MainHall_UI"><span class="sidebar-icon">🏛️</span><<= "MainHall">></div> <<if Flag('enmeeting') == 2>><div class="sidebar-button" data-passage="Meeting_UI"><span class="sidebar-icon">👑</span><<= "Meeting">></div><</if>> <<if Flag('enbedroom') == 2>><div class="sidebar-button" data-passage="Bedroom_UI"><span class="sidebar-icon">🌙</span><<= langBank("bedroom")>></div><</if>> <<if Flag('endispatch') == 2>><div class="sidebar-button" data-passage="Dispatch_UI"><span class="sidebar-icon">📋</span><<= langBank("Dispatch")>></div><</if>> <div class="sidebar-button" data-passage="Summon_UI"><span class="sidebar-icon">🌌</span>Rift</div> </div> </div> <<if Flag('tutorial') >= 8>> <div class="sidebar-group sidebar-temple"> <h2>Temple - SideWing</h2> <div class="sidebar-group-buttons"> <<if Flag('enstorage') == 2>><div class="sidebar-button" data-passage="Storage_UI"><span class="sidebar-icon">📦</span><<= langBank("storage")>></div><</if>> <<if Flag('enbuild') == 2>><div class="sidebar-button" data-passage="Building_UI"><span class="sidebar-icon">🏗️</span>Building</div><</if>> </div> </div> <</if>> <<if Flag('tutorial') >= 4>> <div class="sidebar-group sidebar-temple"> <h2>Temple - RearWing</h2> <div class="sidebar-group-buttons"> <div class="sidebar-button" data-passage="Monsters_UI"><span class="sidebar-icon">😸</span>Warren</div> <<if Flag('enstudy') == 2>><div class="sidebar-button" data-passage="TechTree_UI"><span class="sidebar-icon">🧪</span>Study</div><</if>> </div> </div> <</if>> <<if Flag('tutorial') >= 6>> <div class="sidebar-group sidebar-domain"> <h2>Management</h2> <div class="sidebar-group-buttons"> <div class="sidebar-button" onclick='$.wiki(`<<SimpleUI "MapsPopup">>`)'><span class="sidebar-icon">🗺️</span>Maps</div> <div class="sidebar-button" onclick='$.wiki(`<<SimpleUI "Codex">>`)'><span class="sidebar-icon">✍</span>Codex</div> <div class="sidebar-button" onclick='$.wiki(`<<SimpleUI "Journal">>`)'><span class="sidebar-icon">⁉️</span>Journal</div> <<if Flag('tutorial') >= 10>><div class="sidebar-button" onclick='$.wiki(`<<SimpleUI "DomainReport">>`)'><span class="sidebar-icon">📊</span>Report</div><</if>> <<if Flag('tutorial') >= 10>><div class="sidebar-button" onclick='$.wiki(`<<SimpleUI "Calendar" "fit">>`)'><span class="sidebar-icon">📆</span>Calendar</div><</if>> </div> </div> <</if>> <div class="sidebar-group sidebar-domain"> <h2>System</h2> <div class="sidebar-group-buttons"> <div class="sidebar-button" onclick='$.wiki(`<<run UI.settings();>>`)'><span class="sidebar-icon">⚙️</span>Settings</div> <div class="sidebar-button" onclick='$.wiki(`<<ToHome>>`)'><span class="sidebar-icon">🏰</span>Home</div> </div> </div> </div>
<style> #maptext { text-align: center; left: 0; width: 100%; text-shadow: #000000 -1px -1px 2px; bottom: -0.5rem; position: absolute; font-size: 0.85rem; } .pageClose { position: relative; z-index: 99; } .pageClose:hover { transform: scale(1.05); } .pageClose img{ width: 3.5rem; } .RightBar { height: 90%; width: 4rem; gap: 0.5rem; z-index: 100; position: fixed; display: flex; right: 0rem; top: 3rem; flex-direction: column; } </style> <div class="pageClose" id="pageClose"> <<link "<<showframe 'icon64' 'close' 3.5>><span id='maptext'><<= 'Back'>></span>">><<if Flag('tutorial') == 2>><<SetFlag 'tutorial' 3>><</if>> <<goto "MainHall_UI">> <</link>> </div> <<if Flag('tutorial') >= 6>> <div class="pageClose" id="mapbtn"> <<link "<<showframe 'icon64' 'maps' 3.5>><span id='maptext'><<= 'Maps'>></span>">> <<goto "MapsPopup_UI">> <</link>> </div> <<if Flag('enmeeting') == 2>> <div class="pageClose"> <<link "<<showframe 'icon64' 'journal' 3.5>><span id='maptext'><<= 'Meeting'>></span>">> <<goto "Meeting_UI">> <</link>> </div> <</if>> <<if Flag('enbedroom') == 2>> <div class="pageClose"> <<link "<<showframe 'icon64' 'bedroom' 3.5>><span id='maptext'><<= 'Bedroom'>></span>">> <<goto "Bedroom_UI">> <</link>> </div> <</if>> <<timed 0.2s>> <div class="bdoperate"> <<if def _FacStatus>> <div class="pageClose"> <<link "<<showframe 'icon64' 'status' 3.5>><span id='maptext'><<= 'Status'>></span>">> <<SimpleUI "MonstersInfo">> <</link>> </div> <</if>> <<if def _FacUpgrade && Flag('enbuild') == 2>> <div class="pageClose"> <<link "<<showframe 'icon64' 'upgrade' 3.5>><span id='maptext'><<= 'Upgrade'>></span>">> <<SimpleUI "Building">> <</link>> </div> <</if>> <<if def _FacTechTree && Flag('enstudy') == 2>> <div class="pageClose"> <<link "<<showframe 'icon64' 'evolution' 3.5>><span id='maptext'><<= 'Evolution'>></span>">> <<SimpleUI "TechTree">> <</link>> </div> <</if>> <<if def _FacHelp>> <div class="pageClose"> <<link "<<showframe 'icon64' 'help' 3.5>><span id='maptext'><<= setup.langbank.help[$lang]>></span>">> <<SimpleUI "Codex">><<include _FacHelp>><<timed 0.1s>> <<replace '#codexContent'>> <<= setup.entrycontent[$lang]>> <</replace>><</timed>> <</link>> </div> <</if>> </div> <</timed>> <</if>>
<style> .UItopbar { height: var(--topbar-height); background: linear-gradient(var(--bg-body) 20%,rgb(0, 0, 0) 80%); box-shadow: 0 0 10px 0 #333; z-index: 40; } #bottombar { position: fixed; left: 0; bottom: 0; width: 100%; height: 1.75rem; background-color: #333; border-top: 1px solid #444; box-shadow: 0 0 10px 0 #333; z-index: 40; } #bbblock { display: flex; margin: 0 1em; -o-transition: margin .2s ease-in; transition: margin .2s ease-in; justify-content: space-between; } #bbtextres { text-align: end; margin-right: 1rem; } #bbtexttime { display: flex; gap: 0.5rem; position: relative; left: 0rem; } @media screen and (max-width: 1136px) { #bbblock { margin-left: 1em; margin-right: 1em; } #ui-bar.stowed~#story #bbblock { margin-left: 1em; } } @media screen and (max-width: 768px) { #bbblock { margin: 0 0.5em; } } #bbtexttime img {width: 2.3em;height: 1.75em;align-self: end;} </style> <<if tags().includes("topbar")>> <<do tag "topbar">> <div class="UItopbar"> <div id="bbblock"> <div id="bbtexttime"> <<HoverTip "<<= 'Food'>><br><<= 'From Block: +'>>$buildingEffects.resources.food /day<br><<= 'From Work: +'>><<= Job.preview().income?.food || 0>> /day<br><<= 'Consume: -'>>$domain.fooduse /day">><<showframe 'resource56' 'food' 1.75>><<= $storage.count('food')>><</HoverTip>> <<HoverTip "<<= 'Gold'>><br><<= 'From Block: +'>>$buildingEffects.resources.gold /day<br><<= 'From Work: +'>><<= Job.preview().income?.gold || 0>> /day">><<showframe 'resource56' 'gold' 1.75>><<= $storage.count('gold')>><</HoverTip>> <<HoverTip "<<= 'Wood'>><br><<= 'From Block: +'>>$buildingEffects.resources.wood /day<br><<= 'From Work: +'>><<= Job.preview().income?.wood || 0>> /day">><<showframe 'resource56' 'wood' 1.75>><<= $storage.count('wood')>><</HoverTip>> <<HoverTip "<<= 'Stone'>><br><<= 'From Block: +'>>$buildingEffects.resources.stone /day<br><<= 'From Work: +'>><<= Job.preview().income?.stone || 0>> /day">><<showframe 'resource56' 'stone' 1.75>><<= $storage.count('stone')>><</HoverTip>> <<HoverTip "<<= 'Ore'>><br><<= 'From Block: +'>>$buildingEffects.resources.ore /day<br><<= 'From Work: +'>><<= Job.preview().income?.ore || 0>> /day">><<showframe 'resource56' 'ore' 1.75>><<= $storage.count('ore')>><</HoverTip>> <<HoverTip "Barony Statistics (Excluding Temple)<br>Total Population: <<= $domain.getTotalPop()>><br>Housing Capacity: $buildingEffects.capacities.resident<br><<= L.human_race>>: $domain.humanpp<br>Otherkind: $domain.monsterpp">><<showframe 'resource56' 'population' 1.75>><<= $domain.getTotalPop() >><</HoverTip>> <<HoverTip "Workforce<br>Total: <<= $domain.getWorkforce()>><br>Expended: <<= $domain.getUnitsWF()>><br>Available workforce, with values tied to total population and Piety.">><<showframe 'resource56' 'workforce' 1.75>><<= $domain.getWorkforce()>><</HoverTip>> <<HoverTip "Piety<br>Workforce: <<=$domain.getPiety()>>%<br> Soldiers: +<<=$domain.getPiety()>>%<br>Provides conversion rates for <br>population and various stats.">><<showframe 'resource56' 'piety' 1.75>><<= $domain.getPiety()>><</HoverTip>> <<HoverTip 'Energy<br>Current: $manage.energy.current <br>Max: <<= $manage.getMaxEnergy()>> <br>Regen: +$manage.energy.regen /day'>>⚡<span style="font-size:1.3rem;color:orange;"><<= $manage.energy.current >></span><</HoverTip>> <<HoverTip "<<CapList>>">><span style="font-size:1.3rem;color:orange;">🏘️</span><</HoverTip>> </div> <div id="bbtextres"> <<link "<span>📆 <<= setup.TimeSystem.getCurrentInfo()>></span>">><<SimpleUI "Calendar" "fit">><</link>> </div> </div> <div class="RightBar"> <<include "UI_RightBar">> </div> </div> <</do>> <</if>>
<style> .start-page { position: relative; width: 100%; height: 100%; display: flex; color: #e0d3af; overflow: hidden; justify-content: center; align-items: center; } .backimage-center { background: center; background-size: auto; background-repeat: no-repeat; } .start-content { position: relative; z-index: 2; text-align: center; padding: 1rem; max-width: 90%; z-index: 20; } .game-title { font-size: 2.5rem; color: #d4af37; text-transform: uppercase; margin-bottom: 0rem; text-shadow: 3px 3px 5px rgb(2 2 2),3px 3px 5px rgb(2 2 2); } .game-title:after { margin-top: -4.5rem; margin-left: -0.7rem; content: url(chmx.png); display: block; position: absolute; } .game-subtitle { margin-right: -7rem; font-size: 1.2rem; margin-bottom: 1rem; text-shadow: 2px 2px 3px rgb(0 0 0), 0px 0px 20px rgb(255 255 255); } .start-btn { width: 9.5rem; background: #2d2d2d; color: #e0d3af; border: 2px solid #4a4130; padding: 0.5rem 1rem; font-size: 1rem; text-transform: uppercase; cursor: pointer; } .start-btn:hover { background: #4a4130; border-color: #9e8561; } .settings-module { background: #2d2d2d; border: 2px solid #4a4130; padding: 0.5rem; margin-top: 1rem; width: 21rem; } .settings-title { background: #252525; color: #d4af37; font-size: 1rem; font-weight: bold; text-transform: uppercase; padding: 0.3rem; border-bottom: 1px solid #8b4513; cursor: pointer; } .settings-content { padding: 0.5rem; max-height: 200px; } .settings-option { display: flex; justify-content: space-between; align-items: center; margin: 0.3rem 0; } select, input[type="checkbox"] { background: #1a1a1a; color: #e0d3af; border: 1px solid #4a4130; padding: 0.2rem; } #monstershow { width: 20%; z-index: 2; left: 0rem; position: relative; bottom: -7rem; } </style> <<script>> $(document).ready(function(){ const backgrounds = ['images/ui/bg/dungeon01.webp', 'images/ui/bg/temple01.webp', "images/ui/bg/lost.webp"]; $('.start-page').css('background-image', `url(${backgrounds.random()})`); }); $(document).one(":passagerender", function (element) { $(element.content).find(".macro-listbox").on("change", function (el) { $.wiki("<<redo 'lang'>>"); $.wiki("<<run UI.update();>>"); }); }); <</script>> <<run UIBar.unstow().show();>> <div class="start-page backimage-center"> <<if !Story.has("0_0DebugControl")>> <div id="monstershow"> [img["images/characters/catmaid/christmas.png"]] </div> <</if>> <div class="start-content"> <<do tag "lang">> <h1 class="game-title"><<= setup.langbank.gametitle[$lang]>></h1> <<if !Story.has("0_0DebugControl")>><h3 class="game-subtitle"><<= setup.langbank.gamesubtitle[$lang]>></h3><</if>> <</do>> <div class="buttons-group-row" style="grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));"> <<link '<div class="start-btn"><<= L.newgame>></div>'>><<goto "Welcome_Intro1">><</link>> <button class="start-btn" onclick='$.wiki(' <<run UI.saves();>>')'>Load Game</button> <<link '<div class="start-btn"><<= "Free Mode">></div>'>><<silent>><<include "Game_Init">><<SkipTutorial>><</silent>><<goto "MainHall_UI">><</link>> <button class="start-btn" onclick="window.close()">Exit Game</button> </div> <div class="settings-module" id="settings-module"> <div class="settings-title"> Settings </div> <div class="settings-content"> <div class="settings-option"> <span>Language:</span> <<set _langOptions = { "English" : "en" } >> <<listbox "$lang" autoselect>> <<optionsfrom _langOptions>> <</listbox>> </div> <div class="settings-option"> <span>Fullscreen :</span><span><<ToggleSetting "fullscreen" "">></span> </div> <div class="settings-option"> <span>More :</span> </div> <div class="settings-option"> <<button "⚙️Settings">><<run UI.settings();>><</button>> <button class="welcome-clear" onclick='$.wiki(' <<run UI.restart();>><<run State.metadata.clear()>>')'>🧨Reset Data</button> </div> </div> </div> </div> </div>
<link href="https:\\fonts.googleapis.com/css2?family=Cinzel:wght@400;700;900&family=Marcellus&display=swap" rel="stylesheet"> <style> .storyact-container { align-items: center; justify-content: center; min-height: 100vh; background-color: #000; font-family: 'Marcellus', serif; height: 100%; display: flex; width: 100%; text-align: center; position: relative; flex-direction: column; } .act-title { font-family: 'Cinzel', serif; font-size: 5rem; font-weight: 900; letter-spacing: 0.5rem; color: #fff; text-transform: uppercase; margin-bottom: 2rem; line-height: 1.1; opacity: 0; animation: fadeInUp 3s ease 0.5s forwards; text-shadow: 0 0 30px rgba(255, 255, 255, 0.1); } .game-title::after { content: ''; position: absolute; bottom: -1rem; left: 50%; transform: translateX(-50%); width: 100px; height: 1px; background: linear-gradient(90deg, transparent, #b3b3b3, transparent); } .act-content { margin: 3rem 0; position: relative; } .act-number { font-family: 'Cinzel', serif; font-size: 1.8rem; font-weight: 400; letter-spacing: 0.5rem; margin-bottom: 2rem; color: #b3b3b3; text-transform: uppercase; opacity: 0; animation: fadeInUp 3s ease forwards; } .act-title { font-family: 'Cinzel', serif; font-size: 5rem; font-weight: 900; letter-spacing: 0.5rem; color: #fff; text-transform: uppercase; margin-bottom: 2rem; line-height: 1.1; opacity: 0; animation: fadeInUp 3s ease 0.5s forwards; text-shadow: 0 0 30px rgba(255, 255, 255, 0.1); } .act-description { font-size: 1.2rem; line-height: 1.8; max-width: 700px; margin: 3rem auto; color: #b3b3b3; font-weight: 300; letter-spacing: 0.05rem; opacity: 0; animation: fadeInUp 3s ease 1s forwards; } @keyframes fadeInUp { 0% { opacity: 0; transform: translateY(30px); } 75% { opacity: 1; transform: translateY(0); } 100% { opacity: 0; transform: translateY(30px); } } @media (max-width: 992px) { .act-title { font-size: 5.5rem; } } @media (max-width: 768px) { .act-title { font-size: 4rem; letter-spacing: 0.3rem; } .act-number { font-size: 1.5rem; } .decoration { display: none; } } @media (max-width: 576px) { .act-title { font-size: 3rem; letter-spacing: 0.2rem; } .act-number { font-size: 1.2rem; } .game-title { font-size: 1.2rem; letter-spacing: 0.3rem; } .storyact-container { padding: 1rem; } .navigation { flex-wrap: wrap; } } </style> <<set _storyact = $gamestatus.stage >> <<set _storyacts = [ {"stage":"Act 0", "title":"", "desc":"", "passage":""}, {"stage":"Act 1", "title":"Hello World", "desc":"", "passage":"Welcome_Intro3"}, {"stage":"Act 2", "title":"Firewall", "desc":"", "passage":"MainHall_UI"}, {"stage":"Act 3", "title":"Update", "desc":"", "passage":"MainHall_UI"}, {"stage":"Act 4", "title":"Root", "desc":"", "passage":"MainHall_UI"}, ] >> <div class="storyact-container"> <div class="game-title act-number"><<= _storyacts[_storyact].stage>></div> <div class="decoration decoration-left"></div> <div class="decoration decoration-right"></div> <div class="act-content" id="actContent"> <div class="act-title" id="actTitle"><<= _storyacts[_storyact].title>></div> <div class="act-description" id="actDescription"><<= _storyacts[_storyact].desc>></div> </div> </div> <<timed 4.5s>><<goto _storyacts[_storyact].passage>><</timed>>
<style> body { background-color: var(--bg-body); } #story { margin: 0em 0em 0em 17.5em; } .container { position: relative; width: 100%; height: 100vh; overflow: hidden; } .layer { position: absolute; top: 0; left: 0; height: 100vh; opacity: 0; transition: opacity 1s ease-in-out; background-size: cover; background-position: center; } .layer img{ height:100%; } .div01 { left: 0; } .div02 { left: 20%; } .div03 { left: 40%; } .div04 { left: 60%; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .div01 { animation: fadeIn 1s forwards 1.5s; } .div02 { animation: fadeIn 1s forwards 3s; } .div03 { animation: fadeIn 1s forwards 4.5s; } .div04 { animation: fadeIn 1s forwards 6s; } </style> <div class="container"> <div class="layer div01">[img[setup.ImagePath+'event/intro/intro01.webp']]</div> <div class="layer div02"> [img[setup.ImagePath+'event/intro/intro02.webp']]</div> <div class="layer div03"> [img[setup.ImagePath+'event/intro/intro03.webp']]</div> <div class="layer div04">[img[setup.ImagePath+'event/intro/intro04.webp']]</div> </div> <<timed 8s>><<goto "Welcome_Intro2">><</timed>> <<run AudioBus.playPlaylist('start_game');>> <<silent>><<include "Game_Init">><</silent>>
<style> #story { margin: 0em 0em 0em 17.5em; } .passage { background-color: var(--bg-body); align-items: center; justify-content: center; } </style> <<timed 1s>> <div style="align-content: center;text-align: center;width: 100%;height: 100%;position: relative;display: block;"> <p><<= setup.langbank.echoesinthedarkmind[$lang]>></p> <p><<= setup.langbank.doyouthinksoulshaveweight[$lang]>></p> <p><<button setup.langbank.continue[$lang] 'Welcome_Curtain'>><<set $gamestatus.stage = 1 >><</button>></p> </div> <</timed>>
<style> #story { margin: 0em 0em 0em 0em; } .intro3content { position: relative; width: 100%; height: 100vh; } .container { position: relative; width: 100%; height: 100vh; overflow: hidden; } .container .image { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-image: [img[setup.ImagePath+'ui/bg/temple01.webp']]; background-size: cover; background-position: center; opacity: 0; filter: blur(20px); transition: opacity 2s ease-in-out, filter 2s ease-in-out 2s; } @keyframes fadeInAndSharpen { 0% { opacity: 0; filter: blur(20px); } 50% { opacity: 0.2; filter: blur(20px); } 100% { opacity: 1; filter: blur(0); } } .image { animation: fadeInAndSharpen 4s forwards; } </style> <div class="intro3content"> <div class="container"> <div class="image"></div> <<run Msg.add('eventcard', 'Event_Scene_Awake')>> <<timed 3s>> <<run Msg.show();>> <</timed>> </div></div>