萃香bug修复

This commit is contained in:
daixiawu 2026-06-25 00:18:42 +08:00
parent 19e0cd6b70
commit 1519d4cca4
36 changed files with 9945 additions and 8685 deletions

View File

@ -0,0 +1,37 @@
---
name: th1-dashboard-port
description: TH1 Dashboard server port guard. Use whenever Codex starts, restarts, opens, tests, verifies, or manages Tools/Dashboard, serve.py, local Dashboard URLs, or any TH1 Dashboard dev server.
---
# TH1 Dashboard Port
## Rule
TH1 Dashboard must only run on port `8080`.
Allowed local URLs:
- `http://127.0.0.1:8080/`
- `http://localhost:8080/`
Do not start Dashboard on fallback ports such as `8090`, `8091`, `3000`, or any other port.
## Workflow
When starting or restarting Dashboard:
1. Inspect listening processes on ports `8080` and any previously used Dashboard test ports.
2. If any Dashboard or `serve.py` process is listening on a non-`8080` port, stop it.
3. If port `8080` is occupied, inspect the owning process command line. For Dashboard work, stop the process occupying `8080` before restarting.
4. Start Dashboard from `C:\TH1\TH1\Tools\Dashboard` with:
```powershell
python serve.py 8080
```
5. Verify that `http://127.0.0.1:8080/` responds successfully.
6. Verify that no non-`8080` Dashboard server remains listening.
## Cleanup
Do not leave temporary Dashboard server logs in `Tools/Dashboard` unless they are intentionally needed for debugging. Remove temporary files such as `tmp-serve-*.log` after verification.

View File

@ -0,0 +1,3 @@
interface:
display_name: "TH1 Dashboard Port"
short_description: "Enforces Dashboard on port 8080 only"

View File

@ -5797,3 +5797,693 @@ body::after {
.sidebar { display: none; }
.main { margin-left: 0; }
}
/* Mobile shell and broad responsive overrides. Desktop rules above remain unchanged. */
.mobile-nav-toggle,
.mobile-nav-backdrop {
display: none;
}
@media (max-width: 900px) {
:root {
--sidebar-width: min(320px, 86vw);
}
body {
overflow-x: hidden;
}
body::after {
left: 0;
}
.header {
height: 56px;
padding: 8px 14px;
gap: 10px;
}
.header-left,
.header-right {
min-width: 0;
}
.header-left {
gap: 8px;
flex: 1;
}
.header-logo {
height: 24px;
}
.header-title {
font-size: 14px;
letter-spacing: 1px;
white-space: nowrap;
}
.header-sub,
.header-info {
display: none;
}
.btn-refresh {
min-height: 40px;
padding: 8px 10px;
flex-shrink: 0;
}
.layout {
display: block;
margin-top: 56px;
min-height: calc(100vh - 56px);
}
.main {
width: 100%;
max-width: 100%;
margin-left: 0;
padding: 16px;
}
.mobile-nav-toggle {
display: inline-flex;
width: 40px;
height: 40px;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
flex-shrink: 0;
border: 1px solid var(--border-color);
border-radius: 8px;
background: var(--bg-secondary);
color: var(--text-primary);
cursor: pointer;
}
.mobile-nav-toggle span {
width: 18px;
height: 2px;
border-radius: 2px;
background: currentColor;
transition: transform 0.18s, opacity 0.18s;
}
body.mobile-nav-open .mobile-nav-toggle span:nth-child(1) {
transform: translateY(6px) rotate(45deg);
}
body.mobile-nav-open .mobile-nav-toggle span:nth-child(2) {
opacity: 0;
}
body.mobile-nav-open .mobile-nav-toggle span:nth-child(3) {
transform: translateY(-6px) rotate(-45deg);
}
.mobile-nav-backdrop {
position: fixed;
inset: 56px 0 0;
z-index: 210;
background: rgba(15, 23, 42, 0.32);
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease;
display: block;
}
body.mobile-nav-open .mobile-nav-backdrop {
opacity: 1;
pointer-events: auto;
}
.sidebar {
display: block;
top: 56px;
left: calc(-1 * var(--sidebar-width));
width: var(--sidebar-width);
max-width: 86vw;
padding: 12px 0 20px;
transform: none;
transition: none;
z-index: 240;
box-shadow: 12px 0 32px rgba(15, 23, 42, 0.16);
overscroll-behavior: contain;
}
body.mobile-nav-open .sidebar,
.sidebar.is-open {
left: 0 !important;
transform: none !important;
}
.sidebar-tab {
min-height: 44px;
padding: 11px 18px;
cursor: pointer;
}
.nav-section-label {
padding-top: 14px;
}
.module-card,
.oc-dashboard,
.oc-timeline-section,
.mkt-timeline-section,
.dev-timeline-section,
.ver-timeline-section,
.ver-current-card,
.ai-hero {
border-radius: 8px;
}
.module-card {
margin-bottom: 16px;
}
.module-header {
align-items: flex-start;
gap: 8px;
padding: 12px 14px;
flex-wrap: wrap;
}
.module-body {
padding: 14px;
}
.module-badge {
max-width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.summary-grid,
.gb-stats-grid {
grid-template-columns: repeat(auto-fit, minmax(136px, 1fr));
}
.home-summaries-grid,
.card-grid,
.oc-dash-cards,
.ai-section-grid,
.ai-module-grid {
grid-template-columns: 1fr;
}
.summary-card {
padding: 14px;
}
.summary-card .value {
font-size: 24px;
}
#arch-canvas {
height: 380px;
}
.timeline-container,
.table-wrap,
#gb-data-table-wrap,
.gbhd-table-wrap,
.gbhp-table-wrap,
.gb-detail-table-wrap,
.gbhp-detail-table-wrap,
.fh-table-wrap,
.corefb-table-wrap,
.mkt-timeline-scroll,
.dev-timeline-scroll,
.ver-timeline-scroll {
max-width: 100%;
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
.toolbar,
.fh-toolbar,
.gb-filter-grid,
.gb-filter-actions,
.gb-version-bar,
.gbhp-toolbar,
.gbhd-toolbar,
.bug-detail-status-change,
.bug-form-row,
.mkt-form-row {
align-items: stretch;
flex-direction: column;
}
.search-input,
.filter-select,
.gb-filter-select,
.fh-toolbar .search-input,
.mkt-form-half,
.gbhp-model-meta,
.gbhp-controls,
.gbhp-controls .gb-filter-select {
width: 100%;
min-width: 0;
}
.sub-tabs,
#sns-sub-tabs {
gap: 4px;
padding: 4px;
margin-bottom: 14px;
overflow-x: auto;
scrollbar-width: none;
}
.sub-tabs::-webkit-scrollbar,
#sns-sub-tabs::-webkit-scrollbar {
display: none;
}
.sub-tab,
#sns-sub-tabs .sub-tab {
min-height: 40px;
padding: 9px 14px;
}
.dtable,
.gb-table,
.ai-table,
.gbhp-table,
.gbhp-detail-table {
min-width: 720px;
}
.gbhd-compare-table {
min-width: 900px;
}
.fh-skill-table,
.fh-collection-table,
.corefb-table {
min-width: 820px;
}
.loading-card {
width: min(340px, calc(100vw - 32px));
min-width: 0;
padding: 28px 22px;
}
.toast {
top: 66px;
left: 16px;
right: 16px;
text-align: center;
}
.mechanics-layout {
min-height: 0;
gap: 12px;
}
.mechanics-sidebar {
max-height: 260px;
}
.mechanics-reader {
height: 70vh;
min-height: 480px;
}
.ai-hero-top,
.bug-header-bar,
.sent-header-bar,
.corefb-header-bar,
.rawfb-header-bar,
.sns-header-bar,
.mkt-header-bar,
.dev-header-bar,
.ver-list-header,
.oc-dash-header {
align-items: stretch;
flex-direction: column;
gap: 10px;
}
.ai-title {
font-size: 20px;
}
.ai-source {
white-space: normal;
}
.ai-stats {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
.sent-card,
.sns-comment-row,
.bug-card {
align-items: stretch;
flex-direction: column;
gap: 10px;
}
.sent-card-right,
.bug-card-right {
margin-left: 0;
justify-content: flex-start;
flex-wrap: wrap;
}
.bug-card-left {
align-items: flex-start;
}
.bug-card-title,
.sns-comment-title,
.qr-title,
.ver-row-summary,
.home-sent-title,
.home-task-title,
.home-bug-title,
.home-todo-title {
white-space: normal;
overflow-wrap: anywhere;
}
.corefb-collection-card,
.rawfb-set-card,
.rawfb-current-set,
.rawfb-item-card {
grid-template-columns: 1fr;
}
.corefb-header-actions,
.rawfb-header-actions,
.sns-header-actions,
.qr-row-actions,
.mkt-note-actions,
.dev-desc-actions,
.fh-modal-footer,
.qr-modal-footer,
.mkt-modal-footer,
.sent-detail-footer,
.bug-detail-footer {
flex-wrap: wrap;
justify-content: flex-start;
}
.oc-row-main,
.mkt-event-main,
.dev-task-main,
.ver-group-header,
.ver-row-main {
align-items: flex-start;
flex-wrap: wrap;
}
.oc-row-info,
.mkt-ev-info,
.dev-task-info {
flex-basis: min(100%, 420px);
}
.mkt-ev-platforms,
.mkt-stats-row,
.dev-stats-row,
.ver-stats-row,
.gb-pagination {
justify-content: flex-start;
}
.dev-cycle-tabs {
grid-template-columns: 1fr;
}
.dev-subtask-add {
flex-direction: column;
}
.dev-subtask-item {
grid-template-columns: 1fr 28px;
}
.dev-subtask-stage-group {
grid-column: 1 / -1;
}
.ver-current-header,
.ver-group-header,
.ver-row-main {
flex-wrap: wrap;
}
.ver-current-badge,
.ver-row-ver {
min-width: auto;
}
.art-gallery,
.art-gallery.large {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
}
.art-lightbox-info {
left: 12px;
right: 12px;
bottom: 12px;
transform: none;
text-align: center;
white-space: normal;
}
.artdev-icon-list {
padding: 12px;
}
.gbh-metric-bar,
.gbh-metric-radio {
width: 100%;
}
.gbh-metric-bar {
flex-direction: column;
}
.gbh-tier-row {
flex-direction: column;
}
.gbh-tier-label {
width: 100%;
min-width: 0;
min-height: 34px;
letter-spacing: 1px;
}
.gbh-tier-heroes {
justify-content: flex-start;
}
.fh-readonly-line {
grid-template-columns: 1fr;
gap: 4px;
}
.fh-action-cell .gb-btn-sm {
margin-bottom: 6px;
}
.bug-detail-overlay,
.sent-detail-overlay,
.mkt-modal-overlay,
.gb-modal-overlay,
.qr-modal {
align-items: flex-start;
justify-content: center;
padding: 12px;
overflow-y: auto;
}
.bug-detail-modal,
.bug-add-modal,
.sent-detail-modal,
.sent-add-modal,
.mkt-modal-card,
.gb-modal,
.qr-modal-card,
.sns-discord-modal-card {
width: 100%;
max-width: 100%;
max-height: calc(100vh - 24px);
border-radius: 10px;
}
}
@media (max-width: 640px) {
.header {
padding: 8px 10px;
}
.header-logo {
display: none;
}
.btn-refresh span {
display: none;
}
.btn-refresh {
width: 40px;
justify-content: center;
padding: 0;
}
.main {
padding: 12px;
}
.module-header,
.module-body,
.bug-detail-header,
.bug-detail-body,
.bug-detail-footer,
.sent-detail-header,
.sent-detail-body,
.sent-detail-footer,
.mkt-modal-header,
.mkt-modal-body,
.mkt-modal-footer,
.qr-modal-header,
.qr-modal-body,
.qr-modal-footer,
.gb-modal-header,
.gb-modal-body {
padding-left: 12px;
padding-right: 12px;
}
.summary-grid,
.gb-stats-grid,
.ai-stats {
grid-template-columns: 1fr;
}
.summary-card .value {
font-size: 22px;
}
#arch-canvas {
height: 300px;
}
.data-card-header,
.chat-player-header,
.qr-row-header,
.sns-comment-main,
.oc-dash-card {
align-items: flex-start;
flex-wrap: wrap;
}
.data-card-stats {
grid-template-columns: 1fr;
}
.home-mod-row,
.home-bug-item,
.home-todo-item,
.home-sent-item {
align-items: flex-start;
flex-wrap: wrap;
}
.home-task-pri,
.home-task-status,
.home-bug-pri,
.home-bug-status,
.home-sent-st,
.home-sent-date {
min-width: 0;
text-align: left;
}
.chat-bubble {
max-width: 100%;
}
.sent-card-left,
.bug-card-left {
gap: 8px;
}
.sent-detail-meta,
.bug-detail-meta,
.gb-detail-meta,
.gb-detail-stats {
flex-wrap: wrap;
gap: 8px;
}
.mkt-event-main,
.dev-task-main,
.oc-row-main,
.ver-row-main {
gap: 8px;
padding: 10px 12px;
}
.oc-row-seq,
.dev-task-seq {
width: 42px;
height: 42px;
border-radius: 8px;
font-size: 14px;
}
.mkt-ev-platforms,
.mkt-ev-status,
.dev-task-pri,
.dev-task-status,
.oc-row-status {
flex-basis: auto;
}
.ver-current-header {
gap: 8px;
}
.ver-current-badge {
font-size: 18px;
padding: 5px 12px;
}
.art-gallery,
.art-gallery.large {
grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
}
.artdev-batch-head,
.artdev-round-head,
.artdev-object-row {
padding: 10px;
}
.artdev-feedback {
grid-template-columns: 1fr;
}
.gb-page-info {
width: 100%;
margin-left: 0;
margin-top: 4px;
}
.fh-detail-grid,
.sns-discord-grid,
.sns-discord-detail-meta {
grid-template-columns: 1fr;
}
}

View File

@ -705,6 +705,7 @@ async function renderHomeSummaries() {
// ============ Tab Navigation ============
const SIDEBAR_ORDER_KEY = 'th1-dashboard-sidebar-order-v1';
const MOBILE_NAV_MEDIA = '(max-width: 900px)';
let sidebarDraggedTab = null;
function getSidebarChildToken(child) {
@ -834,6 +835,7 @@ function initSidebarDragOrder() {
});
loadPersistedSidebarOrder(sidebar);
syncSidebarDragMode();
}
function initTabs() {
@ -857,10 +859,87 @@ function initTabs() {
if (tab.dataset.tab === 'art-dev' && typeof artDevLoad === 'function') {
artDevLoad();
}
closeMobileNav();
});
});
}
function isMobileNavViewport() {
return window.matchMedia(MOBILE_NAV_MEDIA).matches;
}
function syncSidebarDragMode() {
const isMobile = isMobileNavViewport();
document.querySelectorAll('.sidebar-tab').forEach(tab => {
tab.draggable = !isMobile;
});
}
function setMobileNavOpen(isOpen) {
document.body.classList.toggle('mobile-nav-open', isOpen);
const sidebar = document.querySelector('.sidebar');
if (sidebar) {
sidebar.classList.toggle('is-open', isOpen);
sidebar.style.left = isMobileNavViewport()
? (isOpen ? '0' : 'calc(-1 * var(--sidebar-width))')
: '';
sidebar.style.transform = '';
}
const toggle = document.querySelector('.mobile-nav-toggle');
if (toggle) {
toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false');
}
}
function closeMobileNav() {
setMobileNavOpen(false);
}
function initMobileNav() {
const headerLeft = document.querySelector('.header-left');
const sidebar = document.querySelector('.sidebar');
if (!headerLeft || !sidebar || document.querySelector('.mobile-nav-toggle')) {
syncSidebarDragMode();
return;
}
sidebar.id = sidebar.id || 'dashboard-sidebar';
const toggle = document.createElement('button');
toggle.type = 'button';
toggle.className = 'mobile-nav-toggle';
toggle.setAttribute('aria-label', '打开导航');
toggle.setAttribute('aria-controls', sidebar.id);
toggle.setAttribute('aria-expanded', 'false');
toggle.innerHTML = '<span></span><span></span><span></span>';
headerLeft.insertBefore(toggle, headerLeft.firstChild);
const backdrop = document.createElement('div');
backdrop.className = 'mobile-nav-backdrop';
document.body.appendChild(backdrop);
toggle.addEventListener('click', () => {
setMobileNavOpen(!document.body.classList.contains('mobile-nav-open'));
});
backdrop.addEventListener('click', closeMobileNav);
window.addEventListener('keydown', event => {
if (event.key === 'Escape') closeMobileNav();
});
const media = window.matchMedia(MOBILE_NAV_MEDIA);
const onMediaChange = () => {
if (!media.matches) closeMobileNav();
syncSidebarDragMode();
};
if (media.addEventListener) {
media.addEventListener('change', onMediaChange);
} else {
media.addListener(onMediaChange);
}
syncSidebarDragMode();
}
// ============ Update Data (export_data.py) ============
function initRefreshButton() {
@ -957,6 +1036,7 @@ function escHtml(str) {
document.addEventListener('DOMContentLoaded', () => {
initSidebarDragOrder();
initTabs();
initMobileNav();
initRefreshButton();
loadAllData();
});

View File

@ -1,4 +0,0 @@
[API] "GET /api/devplan/list?t=1781448916680 HTTP/1.1" 200 -
[API] "GET /api/marketing/events?t=1781448916680 HTTP/1.1" 200 -
[API] "GET /api/todos/list?t=1781448916680 HTTP/1.1" 200 -
[API] "GET /api/bugs/list?t=1781448916680 HTTP/1.1" 200 -

View File

@ -1,13 +0,0 @@
[API] "GET /api/devplan/list?t=1781448774357 HTTP/1.1" 200 -
[API] "GET /api/bugs/list?t=1781448774358 HTTP/1.1" 200 -
[API] "GET /api/marketing/events?t=1781448774358 HTTP/1.1" 200 -
[API] "GET /api/todos/list?t=1781448774358 HTTP/1.1" 200 -
[API] "GET /api/sns/steam?t=1781448804764 HTTP/1.1" 200 -
[API] "GET /api/devplan/list?t=1781448804885 HTTP/1.1" 200 -
[API] "GET /api/marketing/events?t=1781448804885 HTTP/1.1" 200 -
[API] "GET /api/bugs/list?t=1781448804885 HTTP/1.1" 200 -
[API] "GET /api/todos/list?t=1781448804885 HTTP/1.1" 200 -
[API] "GET /api/sns/discord?t=1781448805238 HTTP/1.1" 200 -
[API] "POST /api/sns/discord/paste HTTP/1.1" 200 -
[API] "GET /api/sns/discord?t=1781448819374 HTTP/1.1" 200 -
[API] "POST /api/sns/discord/delete HTTP/1.1" 200 -

View File

@ -1,148 +0,0 @@
[API] "GET /api/devplan/list?t=1780306049605 HTTP/1.1" 200 -
[API] "GET /api/marketing/events?t=1780306049605 HTTP/1.1" 200 -
[API] "GET /api/bugs/list?t=1780306049606 HTTP/1.1" 200 -
[API] "GET /api/todos/list?t=1780306049606 HTTP/1.1" 200 -
----------------------------------------
Exception occurred during processing of request from ('127.0.0.1', 60534)
Traceback (most recent call last):
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 697, in process_request_thread
self.finish_request(request, client_address)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 362, in finish_request
self.RequestHandlerClass(request, client_address, self)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 672, in __init__
super().__init__(*args, **kwargs)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 766, in __init__
self.handle()
~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 436, in handle
self.handle_one_request()
~~~~~~~~~~~~~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 424, in handle_one_request
method()
~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 411, in do_GET
self._send_json(_load_raw_feedback())
~~~~~~~~~~~~~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 264, in _load_raw_feedback
data = json.load(f)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 293, in load
return loads(fp.read(),
cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 335, in loads
raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",
s, 0)
json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
----------------------------------------
----------------------------------------
Exception occurred during processing of request from ('127.0.0.1', 60535)
Traceback (most recent call last):
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 697, in process_request_thread
self.finish_request(request, client_address)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 362, in finish_request
self.RequestHandlerClass(request, client_address, self)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 672, in __init__
super().__init__(*args, **kwargs)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 766, in __init__
self.handle()
~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 436, in handle
self.handle_one_request()
~~~~~~~~~~~~~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 424, in handle_one_request
method()
~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 411, in do_GET
self._send_json(_load_raw_feedback())
~~~~~~~~~~~~~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 264, in _load_raw_feedback
data = json.load(f)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 293, in load
return loads(fp.read(),
cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 335, in loads
raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",
s, 0)
json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
----------------------------------------
----------------------------------------
Exception occurred during processing of request from ('127.0.0.1', 62880)
Traceback (most recent call last):
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 697, in process_request_thread
self.finish_request(request, client_address)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 362, in finish_request
self.RequestHandlerClass(request, client_address, self)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 672, in __init__
super().__init__(*args, **kwargs)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 766, in __init__
self.handle()
~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 436, in handle
self.handle_one_request()
~~~~~~~~~~~~~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 424, in handle_one_request
method()
~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 411, in do_GET
self._send_json(_load_raw_feedback())
~~~~~~~~~~~~~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 264, in _load_raw_feedback
data = json.load(f)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 293, in load
return loads(fp.read(),
cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 335, in loads
raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",
s, 0)
json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
----------------------------------------
----------------------------------------
Exception occurred during processing of request from ('127.0.0.1', 62881)
Traceback (most recent call last):
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 697, in process_request_thread
self.finish_request(request, client_address)
~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 362, in finish_request
self.RequestHandlerClass(request, client_address, self)
~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 672, in __init__
super().__init__(*args, **kwargs)
~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\socketserver.py", line 766, in __init__
self.handle()
~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 436, in handle
self.handle_one_request()
~~~~~~~~~~~~~~~~~~~~~~~^^
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\http\server.py", line 424, in handle_one_request
method()
~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 411, in do_GET
self._send_json(_load_raw_feedback())
~~~~~~~~~~~~~~~~~~^^
File "C:\TH1\TH1\Tools\Dashboard\serve.py", line 264, in _load_raw_feedback
data = json.load(f)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 293, in load
return loads(fp.read(),
cls=cls, object_hook=object_hook,
parse_float=parse_float, parse_int=parse_int,
parse_constant=parse_constant, object_pairs_hook=object_pairs_hook, **kw)
File "C:\Users\daixiawu\AppData\Local\Programs\Python\Python313\Lib\json\__init__.py", line 335, in loads
raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",
s, 0)
json.decoder.JSONDecodeError: Unexpected UTF-8 BOM (decode using utf-8-sig): line 1 column 1 (char 0)
----------------------------------------

Binary file not shown.

View File

@ -24876,4 +24876,4 @@ Release Date:26.6.23
5.여러 아이콘 누락 bug 수정
6.이누바시리 모미지 근처의 이단 대상이 사망한 뒤 이동 범위가 잘못 표시될 수 있던 bug 수정
7.온라인 대전 중 스미레코 스킬 대칭 처리에서 동기화가 어긋나던 bug 수정
8.데인겔드 지불 시 금액이 잘못될 수 있던 bug 수정%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21722%$#@!False%$#@!%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21723%$#@!True%$#@!每2回合在附近空地上生成**<庄稼>**周围每片农田额外提供1点**<城市发展度>**(上限2点),为**<市场>**提供额外金币。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21724%$#@!True%$#@!死亡后在原地留下**<英灵符文>**。位于**<英灵符文>**地块之上的维京文明**<普通单位>**在受到致死伤害时能够立回恢复6点生命值。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21725%$#@!True%$#@!陆田灌渠%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21726%$#@!True%$#@!可在**<山脉>**附近或与至少三片水域相邻的**<平原>**地块上建造**<灌溉工程>**。每2回合在附近空地上生成**<庄稼>**周围每片农田额外提供1点**<城市发展度>**(上限2点),为**<市场>**提供额外金币。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21727%$#@!True%$#@!潮汐灌渠%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21728%$#@!True%$#@!可在**<山脉>**附近或与至少三片陆地相邻的**<浅海>**地块上建造**<灌溉工程>**。每2回合在附近空地上生成**<庄稼>**周围每片农田额外提供1点**<城市发展度>**(上限2点),为**<市场>**提供额外金币。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21729%$#@!True%$#@!已发起征收%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21730%$#@!True%$#@!使该单位消散。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%
8.데인겔드 지불 시 금액이 잘못될 수 있던 bug 수정%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21722%$#@!False%$#@!%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21723%$#@!True%$#@!每2回合在附近空地上生成**<庄稼>**周围每片农田额外提供1点**<城市发展度>**(上限2点),为**<市场>**提供额外金币。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21724%$#@!True%$#@!死亡后在原地留下**<英灵符文>**。位于**<英灵符文>**地块之上的维京文明**<普通单位>**在受到致死伤害时能够立回恢复6点生命值。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21725%$#@!True%$#@!陆田灌渠%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21726%$#@!True%$#@!可在**<山脉>**附近或与至少三片水域相邻的**<平原>**地块上建造**<灌溉工程>**。每2回合在附近空地上生成**<庄稼>**周围每片农田额外提供1点**<城市发展度>**(上限2点),为**<市场>**提供额外金币。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21727%$#@!True%$#@!潮汐灌渠%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21728%$#@!True%$#@!可在**<山脉>**附近或与至少三片陆地相邻的**<浅海>**地块上建造**<灌溉工程>**。每2回合在附近空地上生成**<庄稼>**周围每片农田额外提供1点**<城市发展度>**(上限2点),为**<市场>**提供额外金币。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21729%$#@!True%$#@!已发起征收%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21730%$#@!True%$#@!使该单位消散。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21731%$#@!True%$#@!清除英灵符文%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%21732%$#@!True%$#@!消耗**<施法行动点>**,清除当前地块上的**<英灵符文>**。%$#@!%$#@!%$#@!%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!False%$#@!False%$#@!False%$#@!%$#@!!@#$%

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 172 KiB

View File

@ -6166,6 +6166,40 @@ MonoBehaviour:
NoNeedTech: 1
SpriteSize: {x: 130, y: 130}
SpritePos: {x: 0, y: 20}
- ActionId:
ActionType: 6
WonderType: 0
ResourceType: 0
FeatureType: 0
TerrainType: 0
UnitType: 0
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 43
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
SkillType: 0
TechType: 0
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
ActionName: "\u6E05\u9664\u82F1\u7075\u7B26\u6587"
Desc: "\u6D88\u8017**<\u65BD\u6CD5\u884C\u52A8\u70B9>**\uFF0C\u6E05\u9664\u5F53\u524D\u5730\u5757\u4E0A\u7684**<\u82F1\u7075\u7B26\u6587>**\u3002"
NeedTechDesc: 0
TechDesc:
NeedLockDesc: 0
LockDesc:
Icon: {fileID: 21300000, guid: 3ad261c8686a44e3a2ad547b27152c49, type: 3}
IconViewSizeType: 2
VarientIcon: 0
IconList: []
Cost: 0
CityExp: 0
NoNeedTech: 1
SpriteSize: {x: 130, y: 130}
SpritePos: {x: 0, y: 20}
- ActionId:
ActionType: 4
WonderType: 0

View File

@ -3968,7 +3968,7 @@ MonoBehaviour:
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 303
SkillViewType: 4
SkillViewType: 2
SkillName: "\u5764\u8F74\u5927\u9B3C"
SkillDesc: "**<\u8403\u5F71>**\u8FBE\u52303\u5C42\u65F6\u7684\u7279\u6B8A\u5F62\u6001\u3002"
NotShow: 0
@ -3982,12 +3982,12 @@ MonoBehaviour:
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 304
SkillViewType: 3
SkillViewType: 2
SkillName: "\u767E\u4E07\u540C\u4E00\u9B3C"
SkillDesc: "**<\u8403\u5F71>**\u8FBE\u52304\u5C42\u65F6\u7684\u7279\u6B8A\u5F62\u6001\u3002"
NotShow: 0
ShowOnUnitMono: 0
SkillIcon: {fileID: 21300000, guid: a09ad4f3ec7548e5a283e80754c0b9b6, type: 3}
SkillIcon: {fileID: 21300000, guid: 16cf6c501c724fe2b89c47a26fdf41c8, type: 3}
HasShowList: 0
SkillShowList: []
skillPriority: 1
@ -3996,11 +3996,11 @@ MonoBehaviour:
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 305
SkillViewType: 2
SkillViewType: 4
SkillName: "\u5927\u6C5F\u5C71\u6089\u7686\u6740"
SkillDesc: "\u5DE8\u5927\u8403\u9999\u53EF\u964D\u843D\u5230\u76EE\u6807\u5730\u683C\u5E76\u9020\u6210\u5468\u56F4\u6E85\u5C04\u4F24\u5BB3\u3002"
NotShow: 0
ShowOnUnitMono: 0
ShowOnUnitMono: 1
SkillIcon: {fileID: 21300000, guid: 16cf6c501c724fe2b89c47a26fdf41c8, type: 3}
HasShowList: 0
SkillShowList: []
@ -4009,6 +4009,20 @@ MonoBehaviour:
ReserveLeaveCarry: 1
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 329
SkillViewType: 4
SkillName: "\u5929\u624B\u529B\u7537\u6295"
SkillDesc: "\u5C06\u9644\u8FD1\u53CB\u65B9\u5355\u4F4D\u6295\u63B7\u5230\u6307\u5B9A\u5730\u5757\uFF0C\u5E76\u9020\u6210\u8303\u56F4\u6E85\u5C04\u4F24\u5BB3\u3002"
NotShow: 0
ShowOnUnitMono: 1
SkillIcon: {fileID: 21300000, guid: 8f94281b69b7435ea3d60f6c4e974fd5, type: 3}
HasShowList: 0
SkillShowList: []
skillPriority: 0
ReserveOnCarry: 1
ReserveLeaveCarry: 1
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 306
SkillViewType: 2
SkillName: "\u71D0\u706B\u5316\u9B3C"

View File

@ -8604,7 +8604,7 @@ MonoBehaviour:
AttackRange: 1
SightRange: 1
Cost: 0
Skills: 02000000130000000b0100002c0100002e010000
Skills: 130000002d0100002c0100002e0100002f010000
Sprite: {fileID: 21300000, guid: 2a03491d89974e64ad385166fa959357, type: 3}
IsSpriteVarient: 0
SpriteList: []
@ -8631,24 +8631,6 @@ MonoBehaviour:
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
- ActionType: 6
WonderType: 0
ResourceType: 0
FeatureType: 0
TerrainType: 0
UnitType: 0
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 39
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
SkillType: 0
TechType: 0
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
- UnitType: 14
GiantType: 25
UnitLevel: 4
@ -8667,7 +8649,7 @@ MonoBehaviour:
AttackRange: 1
SightRange: 1
Cost: 0
Skills: 02000000130000000b0100002c0100002e01000031010000
Skills: 130000002d0100002c0100002e0100002f01000030010000
Sprite: {fileID: 21300000, guid: 2a03491d89974e64ad385166fa959357, type: 3}
IsSpriteVarient: 0
SpriteList: []
@ -8694,24 +8676,6 @@ MonoBehaviour:
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
- ActionType: 6
WonderType: 0
ResourceType: 0
FeatureType: 0
TerrainType: 0
UnitType: 0
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 39
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
SkillType: 0
TechType: 0
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
- UnitType: 14
GiantType: 26
UnitLevel: 1

View File

@ -6166,6 +6166,40 @@ MonoBehaviour:
NoNeedTech: 1
SpriteSize: {x: 130, y: 130}
SpritePos: {x: 0, y: 20}
- ActionId:
ActionType: 6
WonderType: 0
ResourceType: 0
FeatureType: 0
TerrainType: 0
UnitType: 0
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 43
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
SkillType: 0
TechType: 0
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
ActionName: 21731
Desc: 21732
NeedTechDesc: 0
TechDesc:
NeedLockDesc: 0
LockDesc:
Icon: {fileID: 21300000, guid: 3ad261c8686a44e3a2ad547b27152c49, type: 3}
IconViewSizeType: 2
VarientIcon: 0
IconList: []
Cost: 0
CityExp: 0
NoNeedTech: 1
SpriteSize: {x: 130, y: 130}
SpritePos: {x: 0, y: 20}
- ActionId:
ActionType: 4
WonderType: 0

View File

@ -1209274,4 +1209274,112 @@ MonoBehaviour:
IsActive: 1
Color:
Icon:
- ID: 21731
ZH: "\u6E05\u9664\u82F1\u7075\u7B26\u6587"
TDZH:
EN:
JP:
KR:
RU:
ES:
PT:
FR:
DE:
IDN:
TH:
PL:
VI:
MS:
UK:
KZ:
TR:
IT:
NL:
FI:
SV:
NO:
CS:
HU:
EL:
RO:
ET:
LT:
HR:
SR:
SL:
SK:
BE:
HE:
BG:
UZ:
KY:
MN:
AR:
DA:
TL:
Custom:
IsProperNoun: 0
IsDialogue: 0
DialogueSpeaker:
IsDeprecated: 0
IsCustom: 0
IsSpecialTerm: 0
IsSecondary: 0
IsActive: 1
Color:
Icon:
- ID: 21732
ZH: "\u6D88\u8017**<18871>**\uFF0C\u6E05\u9664\u5F53\u524D\u5730\u5757\u4E0A\u7684**<21563>**\u3002"
TDZH:
EN:
JP:
KR:
RU:
ES:
PT:
FR:
DE:
IDN:
TH:
PL:
VI:
MS:
UK:
KZ:
TR:
IT:
NL:
FI:
SV:
NO:
CS:
HU:
EL:
RO:
ET:
LT:
HR:
SR:
SL:
SK:
BE:
HE:
BG:
UZ:
KY:
MN:
AR:
DA:
TL:
Custom:
IsProperNoun: 0
IsDialogue: 0
DialogueSpeaker:
IsDeprecated: 0
IsCustom: 0
IsSpecialTerm: 0
IsSecondary: 0
IsActive: 1
Color:
Icon:
TargetTypes:

View File

@ -3963,7 +3963,7 @@ MonoBehaviour:
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 303
SkillViewType: 3
SkillViewType: 2
SkillName: 21509
SkillDesc: 21620
NotShow: 0
@ -3977,12 +3977,12 @@ MonoBehaviour:
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 304
SkillViewType: 3
SkillViewType: 2
SkillName: 21510
SkillDesc: 21621
NotShow: 0
ShowOnUnitMono: 0
SkillIcon: {fileID: 21300000, guid: a09ad4f3ec7548e5a283e80754c0b9b6, type: 3}
SkillIcon: {fileID: 21300000, guid: 16cf6c501c724fe2b89c47a26fdf41c8, type: 3}
HasShowList: 0
SkillShowList: []
skillPriority: 1
@ -3991,11 +3991,11 @@ MonoBehaviour:
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 305
SkillViewType: 2
SkillViewType: 4
SkillName: 21515
SkillDesc: 21311
NotShow: 0
ShowOnUnitMono: 0
ShowOnUnitMono: 1
SkillIcon: {fileID: 21300000, guid: 16cf6c501c724fe2b89c47a26fdf41c8, type: 3}
HasShowList: 0
SkillShowList: []
@ -4004,6 +4004,20 @@ MonoBehaviour:
ReserveLeaveCarry: 1
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 329
SkillViewType: 4
SkillName: 21496
SkillDesc: 21583
NotShow: 0
ShowOnUnitMono: 1
SkillIcon: {fileID: 21300000, guid: 8f94281b69b7435ea3d60f6c4e974fd5, type: 3}
HasShowList: 0
SkillShowList: []
skillPriority: 0
ReserveOnCarry: 1
ReserveLeaveCarry: 1
ReserveGiantUpgrade: 1
ReserveCommonTransform: 1
- SkillType: 306
SkillViewType: 2
SkillName: 21495

View File

@ -8566,8 +8566,26 @@ MonoBehaviour:
ProjectileType: 1
ForceMelee: 0
SameUnitCountLimit: 1
EnableAction: 0
EnableActions: []
EnableAction: 1
EnableActions:
- ActionType: 6
WonderType: 0
ResourceType: 0
FeatureType: 0
TerrainType: 0
UnitType: 0
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 37
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
SkillType: 0
TechType: 0
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
- UnitType: 14
GiantType: 25
UnitLevel: 3
@ -8586,7 +8604,7 @@ MonoBehaviour:
AttackRange: 1
SightRange: 1
Cost: 0
Skills: 02000000130000000b0100002c0100002e010000
Skills: 130000002d0100002c0100002e0100002f010000
Sprite: {fileID: 21300000, guid: 2a03491d89974e64ad385166fa959357, type: 3}
IsSpriteVarient: 0
SpriteList: []
@ -8604,7 +8622,7 @@ MonoBehaviour:
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 39
UnitActionType: 37
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
@ -8631,7 +8649,7 @@ MonoBehaviour:
AttackRange: 1
SightRange: 1
Cost: 0
Skills: 02000000130000000b0100002c0100002e01000031010000
Skills: 130000002d0100002c0100002e0100002f01000030010000
Sprite: {fileID: 21300000, guid: 2a03491d89974e64ad385166fa959357, type: 3}
IsSpriteVarient: 0
SpriteList: []
@ -8649,7 +8667,7 @@ MonoBehaviour:
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 39
UnitActionType: 37
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
@ -9631,7 +9649,7 @@ MonoBehaviour:
AttackRange: 1
SightRange: 1
Cost: 0
Skills: 02000000030000005300000032010000
Skills: 02000000030000005300000032010000d7000000
Sprite: {fileID: 21300000, guid: 48bd27e4d8c44678bfe75a2dd83d3918, type: 3}
IsSpriteVarient: 0
SpriteList: []
@ -9640,6 +9658,24 @@ MonoBehaviour:
SameUnitCountLimit: 0
EnableAction: 1
EnableActions:
- ActionType: 6
WonderType: 0
ResourceType: 0
FeatureType: 0
TerrainType: 0
UnitType: 0
GiantType: 0
UnitLevel: 0
Vegetation: 0
UnitActionType: 30
CityLevelUpActionType: 0
CityActionType: 0
GridMiscActionType: 0
SkillType: 0
TechType: 0
PlayerActionType: 0
AIParamType: 0
CultureCardType: 0
- ActionType: 6
WonderType: 0
ResourceType: 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -0,0 +1,127 @@
fileFormatVersion: 2
guid: 8f94281b69b7435ea3d60f6c4e974fd5
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 13
mipmaps:
mipMapMode: 0
enableMipMap: 0
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
flipGreenChannel: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
vTOnly: 0
ignoreMipmapLimit: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 1
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 8
textureShape: 1
singleChannelComponent: 0
flipbookRows: 1
flipbookColumns: 1
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
ignorePngGamma: 0
applyGammaDecoding: 0
swizzle: 50462976
cookieLightType: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Standalone
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
- serializedVersion: 3
buildTarget: Server
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
ignorePlatformSupport: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID: 5e97eb03825dee720800000000000000
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
nameFileIdTable: {}
mipmapLimitGroupName:
pSDRemoveMatte: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -236,6 +236,22 @@ public class UnitTypeDataAssets : ScriptableObject
}
}
if (unitData.UnitType == UnitType.Giant && unitData.GiantType == GiantType.NorwaySuika
&& unitData.GetSkill(SkillType.SuikaMiniStack, out var suikaStack))
{
if (suikaStack.Level >= 4 && ResourceCache.Instance.SpriteCache.NorwaySuikaSuperHuge != null)
{
sprite = ResourceCache.Instance.SpriteCache.NorwaySuikaSuperHuge;
return true;
}
if (suikaStack.Level >= 3 && ResourceCache.Instance.SpriteCache.NorwaySuikaHuge != null)
{
sprite = ResourceCache.Instance.SpriteCache.NorwaySuikaHuge;
return true;
}
}
if (!unitInfo.IsSpriteVarient)
{
sprite = unitInfo.Sprite;

View File

@ -805,6 +805,9 @@ namespace Logic.Action
case UnitActionType.HakureiAbsorbRune:
ActionLogicDict[commonActionId] = new UnitActionHakureiAbsorbRune(commonActionId);
continue;
case UnitActionType.ClearHakureiRune:
ActionLogicDict[commonActionId] = new UnitActionClearHakureiRune(commonActionId);
continue;
case UnitActionType.Demolish:
ActionLogicDict[commonActionId] = new UnitActionAction(commonActionId);
continue;

View File

@ -187,9 +187,7 @@ namespace Logic.Action
private bool IsInThrowForm(UnitData suika)
{
return suika != null
&& (suika.GetSkill(SkillType.SuikaBigForm, out _)
|| suika.GetSkill(SkillType.SuikaGiantForm, out _));
return HakureiNorwayHeroSkillUtil.IsSuikaThrowForm(suika);
}
private bool TryFindThrowable(CommonActionParams actionParams, out UnitData target)

View File

@ -59,6 +59,7 @@ namespace Logic.Action
HakureiAbsorbRune = 40,
Demolish = 41,
Disperse = 42,
ClearHakureiRune = 43,
}
public static class UnitActionUpgradeHelper
@ -1756,6 +1757,47 @@ namespace Logic.Action
return CheckCan(actionParams);
}
}
public class UnitActionClearHakureiRune : UnitActionAction
{
public UnitActionClearHakureiRune(CommonActionId id) : base(id)
{
}
protected override bool Execute(CommonActionParams actionParams)
{
var unit = actionParams.UnitData;
if (unit == null || !unit.Grid(actionParams.MapData, out var grid)) return false;
if (!grid.HasSpType(GridSpType.HakureiRune)) return false;
grid.RemoveSpType(GridSpType.HakureiRune, actionParams.MapData);
unit.ReduceActionPoint(ActionPointType.Capture);
if (grid.InMainSight())
{
grid.Renderer(actionParams.MapData)?.PlayVFXInSight(new GridVFXParams(GridVFXType.Fog));
grid.Renderer(actionParams.MapData)?.InstantUpdateGrid();
unit.Renderer(actionParams.MapData)?.InstantUpdateUnit(true);
}
return true;
}
public override bool CheckCan(CommonActionParams actionParams)
{
if (!UnitActionCheckBaseInfo(actionParams)) return false;
if (!UnitActionCheckUnitBelongPlayer(actionParams)) return false;
if (!UnitActionCheckUnitHasCP(actionParams)) return false;
return actionParams.UnitData.Grid(actionParams.MapData, out var grid)
&& grid.HasSpType(GridSpType.HakureiRune);
}
public override bool CheckShow(CommonActionParams actionParams, out ShowType showType)
{
showType = ShowType.None;
return CheckCan(actionParams);
}
}
public class UnitActionRemiliaAbsorb : UnitActionAction
{

View File

@ -686,18 +686,21 @@ namespace Logic.Skill
{
if (!IsSuika(suika)) return;
var level = GetMiniSuikaStack(suika);
if (level >= 4)
{
suika.RemoveSkill(SkillType.SuikaBigForm, map);
suika.AddOrOverrideSkill(SkillType.SuikaGiantForm, map, suika.Id);
return;
}
suika.RemoveSkill(SkillType.SuikaGiantForm, map);
if (level >= 3)
suika.AddOrOverrideSkill(SkillType.SuikaBigForm, map, suika.Id);
suika.AddOrOverrideSkill(SkillType.SuikaThrowUnitBuff, map, suika.Id);
else
suika.RemoveSkill(SkillType.SuikaBigForm, map);
suika.RemoveSkill(SkillType.SuikaThrowUnitBuff, map);
if (level >= 4)
suika.AddOrOverrideSkill(SkillType.SuikaFallingSplash, map, suika.Id);
else
suika.RemoveSkill(SkillType.SuikaFallingSplash, map);
if (level < 3)
ClearSuikaThrowState(map, suika);
suika.Renderer(map)?.InstantUpdateUnit(false);
suika.Renderer(map)?.SyncStatusWithUnitSkills();
}
public static bool TrySpawnMiniSuika(MapData map, UnitData suika, GridData preferred, out UnitData mini)
@ -762,15 +765,20 @@ namespace Logic.Skill
public static bool IsSuikaThrowForm(UnitData suika)
{
return IsSuika(suika)
&& (suika.GetSkill(SkillType.SuikaBigForm, out _)
|| suika.GetSkill(SkillType.SuikaGiantForm, out _));
return IsSuika(suika) && GetMiniSuikaStack(suika) >= 3;
}
public static bool IsSuikaGiantThrowForm(UnitData suika)
{
return IsSuika(suika) && GetMiniSuikaStack(suika) >= 4;
}
public static bool CanSuikaPrepareThrow(MapData map, UnitData suika, UnitData target)
{
if (map == null || suika == null || target == null) return false;
if (!IsSuikaThrowForm(suika)) return false;
if (suika.GetSkill(SkillType.SuikaThrowReady, out _)) return false;
if (TryFindSuikaThrownUnit(map, suika, out _)) return false;
if (!SameUnion(map, suika, target)) return false;
if (suika.Id == target.Id) return false;
if (!suika.IsAlive() || !target.IsAlive()) return false;
@ -779,6 +787,15 @@ namespace Logic.Skill
return map.GridMap.CalcDistance(suikaGrid, targetGrid) <= 1;
}
public static bool ExecuteSuikaPrepareThrow(MapData map, UnitData suika, UnitData target)
{
if (!CanSuikaPrepareThrow(map, suika, target)) return false;
ClearSuikaThrowState(map, suika);
suika.AddOrOverrideSkill(SkillType.SuikaThrowReady, map, suika.Id);
target.AddOrOverrideSkill(SkillType.SuikaThrownUnit, map, suika.Id);
return true;
}
public static bool TryFindSuikaThrownUnit(MapData map, UnitData suika, out UnitData thrownUnit)
{
thrownUnit = null;
@ -833,8 +850,7 @@ namespace Logic.Skill
public static bool CanSuikaFlyToGrid(MapData map, UnitData suika, GridData target)
{
if (map == null || suika == null || target == null) return false;
if (!IsSuikaThrowForm(suika)) return false;
if (HeroLevel(suika) < 4) return false;
if (!IsSuikaGiantThrowForm(suika)) return false;
if (suika.GetSkill(SkillType.SuikaThrowReady, out _)) return false;
if (suika.GetActionPoint(ActionPointType.Attack) <= 0) return false;
if (target.RealUnit(map, out _)) return false;
@ -847,8 +863,7 @@ namespace Logic.Skill
public static bool CanSuikaFlyAttackTarget(MapData map, UnitData suika, UnitData target)
{
if (map == null || suika == null || target == null) return false;
if (!IsSuikaThrowForm(suika)) return false;
if (HeroLevel(suika) < 4) return false;
if (!IsSuikaGiantThrowForm(suika)) return false;
if (suika.GetSkill(SkillType.SuikaThrowReady, out _)) return false;
if (suika.GetActionPoint(ActionPointType.Attack) <= 0) return false;
if (!IsEnemy(map, suika, target)) return false;
@ -876,8 +891,7 @@ namespace Logic.Skill
var target = attackInfo?.DamageTarget;
var targetGrid = attackInfo?.DamageTargetGrid;
if (map == null || suika == null || target == null || targetGrid == null) return false;
if (!IsSuikaThrowForm(suika)) return false;
if (HeroLevel(suika) < 4) return false;
if (!IsSuikaGiantThrowForm(suika)) return false;
if (suika.GetSkill(SkillType.SuikaThrowReady, out _)) return false;
if (suika.GetActionPoint(ActionPointType.Attack) > 0) return false;
if (attackInfo.OriginPlayer == null || attackInfo.TargetPlayer == null) return false;
@ -900,6 +914,7 @@ namespace Logic.Skill
if (!CanSuikaThrowToGround(map, suika, target)) return false;
if (!TryFindSuikaThrownUnit(map, suika, out var thrownUnit)) return false;
if (!TryMoveUnit(map, thrownUnit, target, MoveType.PushMove)) return false;
RefreshSightForThrownUnit(map, thrownUnit);
var damage = Mathf.Max(1, Mathf.RoundToInt(suika.GetAllAttackValue(map)));
var impactDelay = map == Main.MapData
@ -926,6 +941,18 @@ namespace Logic.Skill
return true;
}
private static void RefreshSightForThrownUnit(MapData map, UnitData unit)
{
if (map == null || unit == null) return;
var player = unit.Player(map);
if (player == null || !unit.Grid(map, out var grid)) return;
var exploredCount = Main.PlayerLogic.UpdateSight_LogicView(map, player,
map.GridMap.GetAroundGridIdList(unit.GetSightRange(map), grid));
unit.HeroTask(map)?.OnExploredGrids(map, unit, exploredCount);
foreach (var kv in player.PlayerHeroData.HeroTaskDict)
kv.Value.OnAnyExploredGrids(map, unit, exploredCount);
}
public static void PlayDamageVisual(MapData map, UnitData target, GridData targetGrid, CityData targetCity,
UnitRenderer targetRenderer, bool targetCanBeKilled, SettlementInfo settlement, int damage,
bool showoff = false, float delay = 0f)
@ -1917,6 +1944,41 @@ namespace Logic.Skill
if (mapData?.UnitMap != null && mapData.UnitMap.GetUnitDataByUnitId(selfId, out var suika))
HakureiNorwayHeroSkillUtil.RefreshSuikaForm(mapData, suika);
}
public override bool IsCanAttackAlly()
{
return false;
}
public override bool IsCanAttackTargetAlly(MapData map, UnitData self, UnitData target)
{
return true;
}
public override int GetAttackAllyRange(MapData mapData, UnitData self)
{
return 0;
}
public override bool AttackAllyEnable(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public override bool AttackAllyIsNotProjectile(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public override bool AttackAllyConsumeActionPointBeforeExecute(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public override bool AttackAllyExecute(MapData mapData, UnitData self, UnitData target)
{
return false;
}
}
public partial class SuikaDamageHalveDropMiniSkill : SkillBase
@ -1960,6 +2022,20 @@ namespace Logic.Skill
{
return SkillType.SuikaBigForm;
}
public override void OnSkillAdd(MapData mapData, uint originId)
{
base.OnSkillAdd(mapData, originId);
if (mapData?.UnitMap != null && mapData.UnitMap.GetUnitDataByUnitId(originId, out var suika))
HakureiNorwayHeroSkillUtil.RefreshSuikaForm(mapData, suika);
}
public override void OnSkillOverride(MapData mapData, uint originId, uint selfId)
{
base.OnSkillOverride(mapData, originId, selfId);
if (mapData?.UnitMap != null && mapData.UnitMap.GetUnitDataByUnitId(selfId, out var suika))
HakureiNorwayHeroSkillUtil.RefreshSuikaForm(mapData, suika);
}
}
public partial class SuikaGiantFormSkill : SkillBase
@ -1975,20 +2051,34 @@ namespace Logic.Skill
{
return SkillType.SuikaGiantForm;
}
public override void OnSkillAdd(MapData mapData, uint originId)
{
base.OnSkillAdd(mapData, originId);
if (mapData?.UnitMap != null && mapData.UnitMap.GetUnitDataByUnitId(originId, out var suika))
HakureiNorwayHeroSkillUtil.RefreshSuikaForm(mapData, suika);
}
public override void OnSkillOverride(MapData mapData, uint originId, uint selfId)
{
base.OnSkillOverride(mapData, originId, selfId);
if (mapData?.UnitMap != null && mapData.UnitMap.GetUnitDataByUnitId(selfId, out var suika))
HakureiNorwayHeroSkillUtil.RefreshSuikaForm(mapData, suika);
}
}
public partial class SuikaFallingSplashSkill : SkillBase
public partial class SuikaThrowUnitBuffSkill : SkillBase
{
public SuikaFallingSplashSkill()
public SuikaThrowUnitBuffSkill()
{
IsPermanent = true;
TurnsLimit = 0;
Score = 3;
Score = 2;
}
public override SkillType GetSkillType()
{
return SkillType.SuikaFallingSplash;
return SkillType.SuikaThrowUnitBuff;
}
public override bool IsCanAttackAlly()
@ -2008,7 +2098,7 @@ namespace Logic.Skill
public override bool AttackAllyEnable(MapData mapData, UnitData self, UnitData target)
{
return IsCanAttackTargetAlly(mapData, self, target);
return HakureiNorwayHeroSkillUtil.CanSuikaPrepareThrow(mapData, self, target);
}
public override bool AttackAllyConsumeActionPointBeforeExecute(MapData mapData, UnitData self, UnitData target)
@ -2023,13 +2113,59 @@ namespace Logic.Skill
public override bool AttackAllyExecute(MapData mapData, UnitData self, UnitData target)
{
if (!AttackAllyEnable(mapData, self, target)) return false;
HakureiNorwayHeroSkillUtil.ClearSuikaThrowState(mapData, self);
self.AddOrOverrideSkill(SkillType.SuikaThrowReady, mapData, self.Id);
target.AddOrOverrideSkill(SkillType.SuikaThrownUnit, mapData, self.Id);
return HakureiNorwayHeroSkillUtil.ExecuteSuikaPrepareThrow(mapData, self, target);
}
}
public partial class SuikaFallingSplashSkill : SkillBase
{
public SuikaFallingSplashSkill()
{
IsPermanent = true;
TurnsLimit = 0;
Score = 3;
}
public override SkillType GetSkillType()
{
return SkillType.SuikaFallingSplash;
}
public override bool IsCanAttackAlly()
{
return false;
}
public override bool IsCanAttackTargetAlly(MapData map, UnitData self, UnitData target)
{
return true;
}
public override int GetAttackAllyRange(MapData mapData, UnitData self)
{
return 0;
}
public override bool AttackAllyEnable(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public override bool AttackAllyConsumeActionPointBeforeExecute(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public override bool AttackAllyIsNotProjectile(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public override bool AttackAllyExecute(MapData mapData, UnitData self, UnitData target)
{
return false;
}
public override bool IsCanAttackTargetGrid(MapData map, UnitData self, GridData target)
{
return HakureiNorwayHeroSkillUtil.CanSuikaThrowToGround(map, self, target)

View File

@ -771,6 +771,7 @@ namespace Logic.Skill
{
if (identifier is not UnitData reimu || moveUnit == null) return;
if (moveUnit.Id != reimu.Id || !ReimuNorwaySkillUtil.IsReimu(reimu)) return;
if (moveType != MoveType.ActiveMove && moveType != MoveType.SkillMove) return;
ReimuNorwaySkillUtil.PulseExterminationDamage(map, reimu);
}

View File

@ -323,6 +323,7 @@ namespace Logic.Skill
[MemoryPackUnion(315, typeof(SumirekoOrbSwapMaxValueBuffSkill))]
[MemoryPackUnion(316, typeof(SuikaThrowReadySkill))]
[MemoryPackUnion(317, typeof(SuikaThrownUnitSkill))]
[MemoryPackUnion(318, typeof(SuikaThrowUnitBuffSkill))]
public abstract partial class SkillBase
{
}

View File

@ -0,0 +1,12 @@
// Auto-generated SuikaThrowUnitBuffSkill partial class with MemoryPackable attribute
// This file follows the existing MemoryPackUnionGenerator output shape.
using MemoryPack;
namespace Logic.Skill
{
[MemoryPackable]
public partial class SuikaThrowUnitBuffSkill
{
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1df90132862745a5b0fbdc798502f84d
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -367,6 +367,7 @@ public enum SkillType
SumirekoOrbSwapMaxValueBuff = 326,
SuikaThrowReady = 327,
SuikaThrownUnit = 328,
SuikaThrowUnitBuff = 329,
//补充之前的bug问题
Max = 999,
}

View File

@ -91,6 +91,8 @@ namespace TH1Resource
public Sprite MeilingZzz;
public Sprite KanakoSit;
public Sprite UtsuhoBoom;
public Sprite NorwaySuikaHuge;
public Sprite NorwaySuikaSuperHuge;
public Dictionary<ChessType, Sprite> ChessSpriteDict = new();
public void Init()
@ -206,6 +208,8 @@ namespace TH1Resource
MeilingZzz = TH1Resource.ResourceLoader.Load<Sprite>("ArtResources/TH1Units/Giant/EgyptianMeilingZzz");
KanakoSit = TH1Resource.ResourceLoader.Load<Sprite>("ArtResources/TH1Units/Giant/GermanyKanako_Sit");
UtsuhoBoom = TH1Resource.ResourceLoader.Load<Sprite>("ArtResources/TH1Units/Giant/IndianUtsuho_Boom");
NorwaySuikaHuge = TH1Resource.ResourceLoader.Load<Sprite>("ArtResources/TH1Units/Giant/NorwaySuika_Huge");
NorwaySuikaSuperHuge = TH1Resource.ResourceLoader.Load<Sprite>("ArtResources/TH1Units/Giant/NorwaySuika_Juggernaut");
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff