dsh-piggy 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1348 -0
- package/LICENSE +32 -0
- package/README.md +322 -0
- package/THIRD-PARTY.md +31 -0
- package/assets/elder.svg +35 -0
- package/assets/pig-king-bathe.svg +38 -0
- package/assets/pig-king-eat.svg +45 -0
- package/assets/pig-king-pet.svg +45 -0
- package/assets/pig-king-play.svg +45 -0
- package/assets/pig-king-relaxed.svg +56 -0
- package/assets/pig-king-study.svg +45 -0
- package/assets/pig-king-trip.svg +45 -0
- package/assets/pig-king-work.svg +45 -0
- package/assets/pig-king.svg +56 -0
- package/assets/piglet.svg +30 -0
- package/client.js +3438 -0
- package/commands.js +252 -0
- package/cordis.patch.yml +15 -0
- package/core.js +11 -0
- package/data.js +10 -0
- package/index.js +68 -0
- package/package.json +72 -0
- package/packages/pet-core/package.json +21 -0
- package/packages/pet-core/src/core/activity.js +80 -0
- package/packages/pet-core/src/core/care.js +160 -0
- package/packages/pet-core/src/core/clock.js +122 -0
- package/packages/pet-core/src/core/constants.js +109 -0
- package/packages/pet-core/src/core/daily.js +204 -0
- package/packages/pet-core/src/core/diary.js +108 -0
- package/packages/pet-core/src/core/drops.js +82 -0
- package/packages/pet-core/src/core/effects.js +84 -0
- package/packages/pet-core/src/core/egg.js +120 -0
- package/packages/pet-core/src/core/evolution.js +90 -0
- package/packages/pet-core/src/core/growth.js +110 -0
- package/packages/pet-core/src/core/illness.js +235 -0
- package/packages/pet-core/src/core/interests.js +55 -0
- package/packages/pet-core/src/core/inventory.js +177 -0
- package/packages/pet-core/src/core/lines.js +157 -0
- package/packages/pet-core/src/core/migrate.js +226 -0
- package/packages/pet-core/src/core/profile.js +102 -0
- package/packages/pet-core/src/core/random.js +79 -0
- package/packages/pet-core/src/core/school.js +81 -0
- package/packages/pet-core/src/core/settlement.js +308 -0
- package/packages/pet-core/src/core/state.js +210 -0
- package/packages/pet-core/src/core/travel.js +58 -0
- package/packages/pet-core/src/core/upgrades.js +193 -0
- package/packages/pet-core/src/core/views.js +55 -0
- package/packages/pet-core/src/core/work.js +50 -0
- package/packages/pet-core/src/core.js +95 -0
- package/packages/pet-core/src/data/daily.js +121 -0
- package/packages/pet-core/src/data/drops.js +34 -0
- package/packages/pet-core/src/data/evolution.js +41 -0
- package/packages/pet-core/src/data/growth.js +72 -0
- package/packages/pet-core/src/data/illness.js +188 -0
- package/packages/pet-core/src/data/interests.js +39 -0
- package/packages/pet-core/src/data/jobs.js +150 -0
- package/packages/pet-core/src/data/life.js +88 -0
- package/packages/pet-core/src/data/lines.js +198 -0
- package/packages/pet-core/src/data/minutes.js +23 -0
- package/packages/pet-core/src/data/profile.js +60 -0
- package/packages/pet-core/src/data/school.js +99 -0
- package/packages/pet-core/src/data/shop.js +142 -0
- package/packages/pet-core/src/data/traits.js +42 -0
- package/packages/pet-core/src/data/travel.js +87 -0
- package/packages/pet-core/src/data.js +52 -0
- package/packages/pet-core/src/index.js +12 -0
- package/render.js +357 -0
- package/routes.js +201 -0
- package/scripts/build-client.mjs +100 -0
- package/scripts/typecheck.mjs +122 -0
- package/snapshot.js +368 -0
- package/src/client/art.js +29 -0
- package/src/client/constants.js +88 -0
- package/src/client/css-base.js +225 -0
- package/src/client/css-card.js +63 -0
- package/src/client/css-tabs.js +250 -0
- package/src/client/css-tiles.js +89 -0
- package/src/client/dom.js +33 -0
- package/src/client/effects.js +159 -0
- package/src/client/format.js +27 -0
- package/src/client/index.js +393 -0
- package/src/client/io.js +100 -0
- package/src/client/layout.js +103 -0
- package/src/client/normalize.js +352 -0
- package/src/client/panel.js +319 -0
- package/src/client/scene.js +113 -0
- package/src/client/storage.js +19 -0
- package/src/client/styles.js +13 -0
- package/src/client/tabs/bag.js +203 -0
- package/src/client/tabs/card.js +110 -0
- package/src/client/tabs/crown.js +59 -0
- package/src/client/tabs/dev.js +101 -0
- package/src/client/tabs/home.js +72 -0
- package/src/client/tabs/shop.js +78 -0
- package/src/client/tabs/status.js +223 -0
- package/src/client/tabs/study.js +128 -0
- package/src/client/tabs/travel.js +89 -0
- package/src/client/tabs/update.js +164 -0
- package/src/client/tabs/work.js +106 -0
- package/src/client/values.js +11 -0
- package/src/client/widgets.js +126 -0
- package/store/api.js +260 -0
- package/store/state-file.js +129 -0
- package/store.js +96 -0
package/client.js
ADDED
|
@@ -0,0 +1,3438 @@
|
|
|
1
|
+
// GENERATED FILE. Edit src/client/ and run `npm run build`; do not edit by hand.
|
|
2
|
+
(() => {
|
|
3
|
+
// src/client/constants.js
|
|
4
|
+
var STATE_URL = "/dsh-piggy/state";
|
|
5
|
+
var ART_URL = "/dsh-piggy/art/";
|
|
6
|
+
var ACT_URL = "/dsh-piggy/act";
|
|
7
|
+
var POLL_MS = 4e3;
|
|
8
|
+
var IDLE_CHAT_MINUTES = { min: 20, max: 40 };
|
|
9
|
+
var GREET_DELAY_MS = 1500;
|
|
10
|
+
var MOUNTED = "data-dsh-pig";
|
|
11
|
+
var OPEN_KEY = "dsh-piggy:open";
|
|
12
|
+
var POSITION_KEY = "dsh-piggy:position";
|
|
13
|
+
var PANEL_WIDTH = 292;
|
|
14
|
+
var PANEL_GAP = 8;
|
|
15
|
+
var PANEL_MARGIN = 10;
|
|
16
|
+
var PANEL_MIN_HEIGHT = 120;
|
|
17
|
+
var SCENE_RESERVE = 132;
|
|
18
|
+
var PIG_PADDING_X = 6;
|
|
19
|
+
var TABS = [
|
|
20
|
+
{ key: "status", label: "\u72B6\u6001", emoji: "\u{1F4CB}" },
|
|
21
|
+
{ key: "card", label: "\u5C45\u6C11\u5361", emoji: "\u{1FAAA}" },
|
|
22
|
+
{ key: "crown", label: "\u52A0\u5195", emoji: "\u{1F451}" },
|
|
23
|
+
{ key: "study", label: "\u5B66\u4E60", emoji: "\u{1F4DA}" },
|
|
24
|
+
{ key: "work", label: "\u6253\u5DE5", emoji: "\u{1F4BC}" },
|
|
25
|
+
{ key: "shop", label: "\u5546\u5E97", emoji: "\u{1F6D2}" },
|
|
26
|
+
{ key: "travel", label: "\u65C5\u884C", emoji: "\u{1F9F3}" },
|
|
27
|
+
{ key: "bag", label: "\u80CC\u5305", emoji: "\u{1F392}" }
|
|
28
|
+
];
|
|
29
|
+
var DEV_KEY = "dsh-piggy:dev";
|
|
30
|
+
var DEV_TAB = { key: "dev", label: "\u8C03\u8BD5", emoji: "\u{1F527}" };
|
|
31
|
+
var UPDATE_TAB = { key: "update", label: "\u66F4\u65B0", emoji: "\u{1F504}" };
|
|
32
|
+
var PET_LINES = [
|
|
33
|
+
"\u597D\u8212\u670D\u2026",
|
|
34
|
+
"\u518D\u6478\u6478\uFF5E",
|
|
35
|
+
"\u563F\u563F",
|
|
36
|
+
"\u547C\u565C\u547C\u565C\u2026",
|
|
37
|
+
"\u8FD9\u91CC\u8FD9\u91CC\uFF01",
|
|
38
|
+
"\uFF08\u772F\u8D77\u773C\u775B\uFF09",
|
|
39
|
+
"\u4ECA\u5929\u5FC3\u60C5\u4E0D\u9519",
|
|
40
|
+
"\u5514\u2026\u597D\u75D2",
|
|
41
|
+
"\u4F60\u5728\u5FD9\u4EC0\u4E48\u5440",
|
|
42
|
+
"\u518D\u591A\u5F85\u4E00\u4F1A\u513F"
|
|
43
|
+
];
|
|
44
|
+
var MODES = ["feed", "bathe", "play", "pet"];
|
|
45
|
+
var CARE_LABEL = { feed: ["\u5582\u98DF", "\u{1F34E}"], bathe: ["\u6D17\u6FA1", "\u{1F6C1}"], play: ["\u73A9\u800D", "\u{1F3BE}"], pet: ["\u6478\u6478", "\u2764\uFE0F"] };
|
|
46
|
+
var BOX_POKES_TO_OPEN = 3;
|
|
47
|
+
var BOX_POKE_LINES = [
|
|
48
|
+
"\u91CC\u9762\u597D\u50CF\u6709\u4E1C\u897F\u2026",
|
|
49
|
+
"\u52A8\u4E86\uFF01\u518D\u6233\u4E00\u4E0B\uFF01"
|
|
50
|
+
];
|
|
51
|
+
var NO_ITEM_LINE = {
|
|
52
|
+
food: "\u6CA1\u6709\u5403\u7684\u5566\uFF0C\u5FEB\u53BB\u4E70\u4E00\u70B9 \u{1F34E}",
|
|
53
|
+
bath: "\u6CA1\u6709\u6D17\u6D74\u7528\u54C1\u4E86\uFF0C\u53BB\u4E70\u70B9\u5427 \u{1F9FC}",
|
|
54
|
+
toy: "\u6CA1\u6709\u73A9\u5177\u4E86\uFF0C\u53BB\u5546\u5E97\u770B\u770B \u{1FA80}"
|
|
55
|
+
};
|
|
56
|
+
var KIND_TITLE = { food: "\u{1F34E} \u98DF\u7269", bath: "\u{1F9FC} \u6D17\u6D74", toy: "\u{1FA80} \u73A9\u5177", dress: "\u{1F455} \u88C5\u626E", medicine: "\u{1F48A} \u836F\u54C1", revive: "\u2728 \u590D\u6D3B" };
|
|
57
|
+
var KIND_ORDER = ["food", "bath", "toy", "dress", "medicine", "revive"];
|
|
58
|
+
var STAGES = [
|
|
59
|
+
{ key: "preschool", label: "\u5E7C\u513F\u56ED" },
|
|
60
|
+
{ key: "extracurricular", label: "\u8BFE\u5916" },
|
|
61
|
+
{ key: "primary", label: "\u5C0F\u5B66" },
|
|
62
|
+
{ key: "middle", label: "\u4E2D\u5B66" },
|
|
63
|
+
{ key: "high", label: "\u9AD8\u4E2D" },
|
|
64
|
+
{ key: "college", label: "\u5927\u5B66" },
|
|
65
|
+
{ key: "graduate", label: "\u7814\u7A76\u751F" }
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
// src/client/values.js
|
|
69
|
+
var isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
70
|
+
var obj = (v) => isObj(v) ? v : {};
|
|
71
|
+
var arr = (v) => Array.isArray(v) ? v : [];
|
|
72
|
+
var num = (v, dflt) => typeof v === "number" && isFinite(v) ? v : dflt;
|
|
73
|
+
var str = (v, dflt) => typeof v === "string" && v !== "" ? v : dflt;
|
|
74
|
+
|
|
75
|
+
// src/client/dom.js
|
|
76
|
+
function el(tag, className, text) {
|
|
77
|
+
var node = document.createElement(tag);
|
|
78
|
+
if (className) node.className = className;
|
|
79
|
+
if (text !== void 0) node.textContent = text;
|
|
80
|
+
return node;
|
|
81
|
+
}
|
|
82
|
+
function button(className, attrs, onClick) {
|
|
83
|
+
var node = el("button", className);
|
|
84
|
+
node.type = "button";
|
|
85
|
+
for (var key in attrs) node.setAttribute(key, attrs[key]);
|
|
86
|
+
node.addEventListener("click", function(event) {
|
|
87
|
+
event.stopPropagation();
|
|
88
|
+
onClick();
|
|
89
|
+
});
|
|
90
|
+
return node;
|
|
91
|
+
}
|
|
92
|
+
function meter(value, variant) {
|
|
93
|
+
var wrap = el("div", "dp-meter" + (variant ? " " + variant : ""));
|
|
94
|
+
var fill = document.createElement("i");
|
|
95
|
+
fill.style.width = Math.max(0, Math.min(100, num(value, 0))) + "%";
|
|
96
|
+
wrap.appendChild(fill);
|
|
97
|
+
return wrap;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// src/client/widgets.js
|
|
101
|
+
function labelledBar(ui, label, value, valueText, variant) {
|
|
102
|
+
var row = el("div", "dp-row");
|
|
103
|
+
row.appendChild(el("span", null, label));
|
|
104
|
+
row.appendChild(el("b", null, valueText));
|
|
105
|
+
ui.content.appendChild(row);
|
|
106
|
+
ui.content.appendChild(meter(value, variant));
|
|
107
|
+
}
|
|
108
|
+
function pickerPanel(ui, action) {
|
|
109
|
+
var wrap = el("div", "dp-pick");
|
|
110
|
+
var asks = { feed: "\u5582\u70B9\u4EC0\u4E48\uFF1F", bathe: "\u7528\u54EA\u4E2A\u6D17\u6FA1\uFF1F", play: "\u62FF\u54EA\u4E2A\u73A9\u5177\uFF1F" };
|
|
111
|
+
wrap.appendChild(el("div", "dp-pick-head", asks[action] ?? "\u7528\u54EA\u4E2A\uFF1F"));
|
|
112
|
+
var list = el("div", "dp-list");
|
|
113
|
+
var shelf = ui.view.care[action] ?? [];
|
|
114
|
+
for (var i = 0; i < shelf.length; i += 1) {
|
|
115
|
+
(function(item) {
|
|
116
|
+
var row = el("div", "dp-item");
|
|
117
|
+
row.appendChild(el("span", null, item.emoji));
|
|
118
|
+
var grow = el("div", "dp-grow");
|
|
119
|
+
grow.appendChild(el("div", null, item.label + (item.default ? "\uFF08\u81EA\u5E26\uFF09" : " \xD7" + num(item.count, 0))));
|
|
120
|
+
grow.appendChild(el("div", "dp-dim", careEffectLine(action, item)));
|
|
121
|
+
row.appendChild(grow);
|
|
122
|
+
var use = button("dp-mini", { "data-care": action + ":" + item.key }, function() {
|
|
123
|
+
ui.picker = null;
|
|
124
|
+
ui.send(action, { item: item.key });
|
|
125
|
+
});
|
|
126
|
+
use.textContent = "\u7528";
|
|
127
|
+
row.appendChild(use);
|
|
128
|
+
list.appendChild(row);
|
|
129
|
+
})(shelf[i]);
|
|
130
|
+
}
|
|
131
|
+
wrap.appendChild(list);
|
|
132
|
+
var cancel = button("dp-cancel", {}, function() {
|
|
133
|
+
ui.picker = null;
|
|
134
|
+
ui.renderContent();
|
|
135
|
+
});
|
|
136
|
+
cancel.textContent = "\u7B97\u4E86";
|
|
137
|
+
wrap.appendChild(cancel);
|
|
138
|
+
return wrap;
|
|
139
|
+
}
|
|
140
|
+
function careEffectLine(action, item) {
|
|
141
|
+
var parts = [];
|
|
142
|
+
if (action === "feed") {
|
|
143
|
+
parts.push("\u9971\u98DF +" + item.satiety);
|
|
144
|
+
if (item.happiness) parts.push("\u5FC3\u60C5 +" + item.happiness);
|
|
145
|
+
} else if (action === "bathe") {
|
|
146
|
+
parts.push("\u6E05\u6D01 +" + item.cleanliness);
|
|
147
|
+
if (item.happiness) parts.push("\u5FC3\u60C5 +" + item.happiness);
|
|
148
|
+
} else {
|
|
149
|
+
parts.push("\u5FC3\u60C5 +" + item.happiness);
|
|
150
|
+
if (item.satiety) parts.push("\u9971\u98DF " + item.satiety);
|
|
151
|
+
}
|
|
152
|
+
return parts.join(" \xB7 ");
|
|
153
|
+
}
|
|
154
|
+
function tileGrid() {
|
|
155
|
+
return el("div", "dp-tiles");
|
|
156
|
+
}
|
|
157
|
+
function tile(spec) {
|
|
158
|
+
var node = button("dp-tile" + (spec.soft ? " dp-tile-soft" : ""), spec.data ?? {}, function() {
|
|
159
|
+
spec.onPick();
|
|
160
|
+
});
|
|
161
|
+
node.setAttribute("data-color", spec.color);
|
|
162
|
+
if (spec.locked) node.setAttribute("data-locked", "true");
|
|
163
|
+
if (spec.dim) node.setAttribute("data-dim", "true");
|
|
164
|
+
if (spec.active) node.setAttribute("data-active", "true");
|
|
165
|
+
if (spec.disabled === true) node.disabled = true;
|
|
166
|
+
var icon = el("span", "dp-tile-icon");
|
|
167
|
+
icon.appendChild(el("span", "dp-tile-e", spec.emoji));
|
|
168
|
+
if (spec.badge) icon.appendChild(el("b", "dp-tile-badge", spec.badge));
|
|
169
|
+
if (spec.tag) icon.appendChild(el("b", "dp-tile-tag", spec.tag));
|
|
170
|
+
node.appendChild(icon);
|
|
171
|
+
node.appendChild(el("span", "dp-tile-n", spec.label));
|
|
172
|
+
if (spec.note) node.appendChild(el("span", "dp-tile-note", spec.note));
|
|
173
|
+
return node;
|
|
174
|
+
}
|
|
175
|
+
function drillTo(ui, tab, key) {
|
|
176
|
+
ui.drill[tab] = key;
|
|
177
|
+
ui.drill.pick = null;
|
|
178
|
+
ui.renderContent();
|
|
179
|
+
ui.content.scrollTop = 0;
|
|
180
|
+
}
|
|
181
|
+
function drillHeader(ui, tab, title, info) {
|
|
182
|
+
var row = el("div", "dp-drill");
|
|
183
|
+
var back = button("dp-drill-back", { "data-back": tab }, function() {
|
|
184
|
+
drillTo(ui, tab, null);
|
|
185
|
+
});
|
|
186
|
+
back.textContent = "\u2039";
|
|
187
|
+
row.appendChild(back);
|
|
188
|
+
row.appendChild(el("b", "dp-drill-title", title));
|
|
189
|
+
if (info) row.appendChild(el("span", "dp-drill-info", info));
|
|
190
|
+
ui.content.appendChild(row);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/client/tabs/shop.js
|
|
194
|
+
var SHELF_COLOR = { food: "red", bath: "teal", toy: "yellow", dress: "pink", medicine: "green", revive: "purple" };
|
|
195
|
+
function shelfParts(kind) {
|
|
196
|
+
var title = KIND_TITLE[kind] ?? kind;
|
|
197
|
+
var space = title.indexOf(" ");
|
|
198
|
+
return space < 0 ? ["\u{1F6D2}", title] : [title.slice(0, space), title.slice(space + 1)];
|
|
199
|
+
}
|
|
200
|
+
function renderShopTab(ui) {
|
|
201
|
+
if (ui.view.shop.length === 0) {
|
|
202
|
+
ui.content.appendChild(el("div", "dp-empty", "\u5BBF\u4E3B\u8FD8\u6CA1\u63D0\u4F9B\u8D27\u67B6\u3002"));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
var coins = "\u{1FA99} " + ui.view.pig.coins;
|
|
206
|
+
var shelf = ui.drill.shop;
|
|
207
|
+
if (shelf === null || KIND_ORDER.indexOf(shelf) < 0) {
|
|
208
|
+
renderShelves(ui);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
var parts = shelfParts(shelf);
|
|
212
|
+
drillHeader(ui, "shop", parts[0] + " " + parts[1], coins);
|
|
213
|
+
var grid = tileGrid();
|
|
214
|
+
var items = ui.view.shop.filter(function(item) {
|
|
215
|
+
return item.kind === shelf;
|
|
216
|
+
});
|
|
217
|
+
for (var i = 0; i < items.length; i += 1) grid.appendChild(itemTile(ui, items[i], SHELF_COLOR[shelf]));
|
|
218
|
+
ui.content.appendChild(grid);
|
|
219
|
+
}
|
|
220
|
+
function renderShelves(ui) {
|
|
221
|
+
var grid = tileGrid();
|
|
222
|
+
for (var k = 0; k < KIND_ORDER.length; k += 1) {
|
|
223
|
+
(function(kind) {
|
|
224
|
+
var items = ui.view.shop.filter(function(item) {
|
|
225
|
+
return item.kind === kind;
|
|
226
|
+
});
|
|
227
|
+
if (items.length === 0) return;
|
|
228
|
+
var parts = shelfParts(kind);
|
|
229
|
+
var needed = items.some(function(item) {
|
|
230
|
+
return item.needed;
|
|
231
|
+
});
|
|
232
|
+
grid.appendChild(tile({
|
|
233
|
+
emoji: parts[0],
|
|
234
|
+
label: parts[1],
|
|
235
|
+
color: SHELF_COLOR[kind] ?? "blue",
|
|
236
|
+
tag: needed ? "\u9700\u8981" : "",
|
|
237
|
+
data: { "data-shelf": kind },
|
|
238
|
+
onPick: function() {
|
|
239
|
+
drillTo(ui, "shop", kind);
|
|
240
|
+
}
|
|
241
|
+
}));
|
|
242
|
+
})(KIND_ORDER[k]);
|
|
243
|
+
}
|
|
244
|
+
ui.content.appendChild(grid);
|
|
245
|
+
}
|
|
246
|
+
function itemTile(ui, item, color) {
|
|
247
|
+
var owned = num(ui.view.inventory[item.key], 0);
|
|
248
|
+
var note = item.price + " \u{1FA99}";
|
|
249
|
+
if (item.owned) note = "\u5DF2\u62E5\u6709";
|
|
250
|
+
else if (item.kind === "dress" && item.unlocked === false) note = "\u{1F512} Lv." + item.level;
|
|
251
|
+
return tile({
|
|
252
|
+
emoji: item.emoji,
|
|
253
|
+
label: item.label,
|
|
254
|
+
color,
|
|
255
|
+
soft: true,
|
|
256
|
+
note,
|
|
257
|
+
badge: owned > 0 ? "\xD7" + owned : "",
|
|
258
|
+
tag: item.needed ? "\u9700\u8981" : item.owned && item.worn ? "\u7A7F\u7740" : "",
|
|
259
|
+
dim: !item.owned && (!item.affordable || item.kind === "dress" && item.unlocked === false),
|
|
260
|
+
disabled: item.owned === true,
|
|
261
|
+
data: { "data-buy": item.key },
|
|
262
|
+
onPick: function() {
|
|
263
|
+
ui.send("buy", { item: item.key });
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// src/client/tabs/bag.js
|
|
269
|
+
var CONSUMABLES = KIND_ORDER.filter(function(kind) {
|
|
270
|
+
return kind !== "dress";
|
|
271
|
+
});
|
|
272
|
+
var EXTRA = {
|
|
273
|
+
dress: { emoji: "\u{1F455}", label: "\u88C5\u626E", color: "pink" },
|
|
274
|
+
diary: { emoji: "\u{1F4D4}", label: "\u65E5\u8BB0", color: "brown" },
|
|
275
|
+
souvenir: { emoji: "\u{1F381}", label: "\u7EAA\u5FF5\u54C1", color: "blue" }
|
|
276
|
+
};
|
|
277
|
+
function shortDay(day) {
|
|
278
|
+
return day.length >= 10 ? day.slice(5) : day;
|
|
279
|
+
}
|
|
280
|
+
function renderBagTab(ui) {
|
|
281
|
+
var open = ui.drill.bag;
|
|
282
|
+
if (open === "dress") renderDress(ui);
|
|
283
|
+
else if (open === "diary") renderDiary(ui);
|
|
284
|
+
else if (open === "souvenir") renderSouvenirs(ui);
|
|
285
|
+
else if (open !== null && CONSUMABLES.indexOf(open) >= 0) renderItems(ui, open);
|
|
286
|
+
else renderCategories(ui);
|
|
287
|
+
}
|
|
288
|
+
function ownedOf(ui, kind) {
|
|
289
|
+
return ui.view.shop.filter(function(item) {
|
|
290
|
+
return item.kind === kind && num(ui.view.inventory[item.key], 0) > 0;
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function renderCategories(ui) {
|
|
294
|
+
var grid = tileGrid();
|
|
295
|
+
for (var k = 0; k < CONSUMABLES.length; k += 1) {
|
|
296
|
+
(function(kind) {
|
|
297
|
+
var items = ownedOf(ui, kind);
|
|
298
|
+
var count = items.reduce(function(sum, item) {
|
|
299
|
+
return sum + num(ui.view.inventory[item.key], 0);
|
|
300
|
+
}, 0);
|
|
301
|
+
var parts = shelfParts(kind);
|
|
302
|
+
grid.appendChild(tile({
|
|
303
|
+
emoji: parts[0],
|
|
304
|
+
label: parts[1],
|
|
305
|
+
color: SHELF_COLOR[kind] ?? "blue",
|
|
306
|
+
badge: count > 0 ? String(count) : "",
|
|
307
|
+
dim: count === 0,
|
|
308
|
+
tag: items.some(function(item) {
|
|
309
|
+
return item.needed;
|
|
310
|
+
}) ? "\u9700\u8981" : "",
|
|
311
|
+
data: { "data-bag": kind },
|
|
312
|
+
onPick: function() {
|
|
313
|
+
drillTo(ui, "bag", kind);
|
|
314
|
+
}
|
|
315
|
+
}));
|
|
316
|
+
})(CONSUMABLES[k]);
|
|
317
|
+
}
|
|
318
|
+
var counts = {
|
|
319
|
+
dress: ui.view.dress.filter(function(item) {
|
|
320
|
+
return item.owned;
|
|
321
|
+
}).length,
|
|
322
|
+
diary: ui.view.diary.length,
|
|
323
|
+
souvenir: ui.view.pig.souvenirs.length
|
|
324
|
+
};
|
|
325
|
+
for (var key in EXTRA) {
|
|
326
|
+
(function(category) {
|
|
327
|
+
var spec = EXTRA[category];
|
|
328
|
+
grid.appendChild(tile({
|
|
329
|
+
emoji: spec.emoji,
|
|
330
|
+
label: spec.label,
|
|
331
|
+
color: spec.color,
|
|
332
|
+
badge: counts[category] > 0 ? String(counts[category]) : "",
|
|
333
|
+
dim: counts[category] === 0,
|
|
334
|
+
data: { "data-bag": category },
|
|
335
|
+
onPick: function() {
|
|
336
|
+
drillTo(ui, "bag", category);
|
|
337
|
+
}
|
|
338
|
+
}));
|
|
339
|
+
})(key);
|
|
340
|
+
}
|
|
341
|
+
ui.content.appendChild(grid);
|
|
342
|
+
}
|
|
343
|
+
function renderItems(ui, kind) {
|
|
344
|
+
var parts = shelfParts(kind);
|
|
345
|
+
drillHeader(ui, "bag", parts[0] + " " + parts[1], "\u70B9\u4E00\u4E0B\u5C31\u7528");
|
|
346
|
+
var items = ownedOf(ui, kind);
|
|
347
|
+
if (items.length === 0) {
|
|
348
|
+
ui.content.appendChild(el("div", "dp-empty", "\u7A7A\u7684"));
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
var grid = tileGrid();
|
|
352
|
+
for (var i = 0; i < items.length; i += 1) {
|
|
353
|
+
(function(item) {
|
|
354
|
+
grid.appendChild(tile({
|
|
355
|
+
emoji: item.emoji,
|
|
356
|
+
label: item.label,
|
|
357
|
+
color: SHELF_COLOR[kind] ?? "blue",
|
|
358
|
+
soft: true,
|
|
359
|
+
badge: "\xD7" + num(ui.view.inventory[item.key], 0),
|
|
360
|
+
tag: item.needed ? "\u9700\u8981" : "",
|
|
361
|
+
data: { "data-use": item.key },
|
|
362
|
+
onPick: function() {
|
|
363
|
+
ui.send("use", { item: item.key });
|
|
364
|
+
}
|
|
365
|
+
}));
|
|
366
|
+
})(items[i]);
|
|
367
|
+
}
|
|
368
|
+
ui.content.appendChild(grid);
|
|
369
|
+
}
|
|
370
|
+
function renderDress(ui) {
|
|
371
|
+
var owned = ui.view.dress.filter(function(item) {
|
|
372
|
+
return item.owned;
|
|
373
|
+
});
|
|
374
|
+
var worn = owned.filter(function(item) {
|
|
375
|
+
return item.worn;
|
|
376
|
+
}).length;
|
|
377
|
+
drillHeader(ui, "bag", "\u{1F455} \u88C5\u626E", worn + " \u4EF6\u7A7F\u7740");
|
|
378
|
+
if (owned.length === 0) {
|
|
379
|
+
ui.content.appendChild(el("div", "dp-empty", "\u8FD8\u6CA1\u6709\u88C5\u626E"));
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
var grid = tileGrid();
|
|
383
|
+
for (var i = 0; i < owned.length; i += 1) {
|
|
384
|
+
(function(item) {
|
|
385
|
+
grid.appendChild(tile({
|
|
386
|
+
emoji: item.emoji,
|
|
387
|
+
label: item.label,
|
|
388
|
+
color: EXTRA.dress.color,
|
|
389
|
+
soft: true,
|
|
390
|
+
note: item.slotLabel,
|
|
391
|
+
tag: item.worn ? "\u7A7F\u7740" : "",
|
|
392
|
+
active: item.worn,
|
|
393
|
+
data: { "data-wear": item.key },
|
|
394
|
+
onPick: function() {
|
|
395
|
+
ui.send("wear", { item: item.key, on: !item.worn });
|
|
396
|
+
}
|
|
397
|
+
}));
|
|
398
|
+
})(owned[i]);
|
|
399
|
+
}
|
|
400
|
+
ui.content.appendChild(grid);
|
|
401
|
+
}
|
|
402
|
+
function renderDiary(ui) {
|
|
403
|
+
drillHeader(ui, "bag", "\u{1F4D4} \u65E5\u8BB0", ui.view.diary.length + " \u7BC7");
|
|
404
|
+
if (ui.view.diary.length === 0) {
|
|
405
|
+
ui.content.appendChild(el("div", "dp-empty", "\u8FD8\u6CA1\u6709\u65E5\u8BB0"));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
var grid = tileGrid();
|
|
409
|
+
var picked = null;
|
|
410
|
+
for (var d = 0; d < ui.view.diary.length; d += 1) {
|
|
411
|
+
(function(entry) {
|
|
412
|
+
var active = ui.drill.pick === entry.day;
|
|
413
|
+
if (active) picked = entry;
|
|
414
|
+
grid.appendChild(tile({
|
|
415
|
+
emoji: "\u{1F4D4}",
|
|
416
|
+
label: shortDay(entry.day),
|
|
417
|
+
color: EXTRA.diary.color,
|
|
418
|
+
soft: true,
|
|
419
|
+
active,
|
|
420
|
+
data: { "data-diary": entry.day },
|
|
421
|
+
onPick: function() {
|
|
422
|
+
ui.drill.pick = active ? null : entry.day;
|
|
423
|
+
ui.renderContent();
|
|
424
|
+
}
|
|
425
|
+
}));
|
|
426
|
+
})(ui.view.diary[d]);
|
|
427
|
+
}
|
|
428
|
+
ui.content.appendChild(grid);
|
|
429
|
+
if (picked !== null) {
|
|
430
|
+
var page = el("div", "dp-pick dp-tile-card dp-diary-page");
|
|
431
|
+
page.appendChild(el("div", "dp-pick-head", picked.day));
|
|
432
|
+
page.appendChild(el("div", null, picked.text));
|
|
433
|
+
ui.content.appendChild(page);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
function renderSouvenirs(ui) {
|
|
437
|
+
var list = ui.view.pig.souvenirs;
|
|
438
|
+
drillHeader(ui, "bag", "\u{1F381} \u7EAA\u5FF5\u54C1", list.length + " \u4EF6");
|
|
439
|
+
if (list.length === 0) {
|
|
440
|
+
ui.content.appendChild(el("div", "dp-empty", "\u6536\u85CF\u518C\u8FD8\u7A7A\u7740"));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
var grid = tileGrid();
|
|
444
|
+
var picked = null;
|
|
445
|
+
for (var s = 0; s < list.length; s += 1) {
|
|
446
|
+
(function(entry, index) {
|
|
447
|
+
var id = entry.key + "#" + index;
|
|
448
|
+
var active = ui.drill.pick === id;
|
|
449
|
+
if (active) picked = entry;
|
|
450
|
+
grid.appendChild(tile({
|
|
451
|
+
emoji: entry.emoji,
|
|
452
|
+
label: entry.label,
|
|
453
|
+
color: EXTRA.souvenir.color,
|
|
454
|
+
soft: true,
|
|
455
|
+
active,
|
|
456
|
+
note: entry.rarityEmoji + entry.rarityLabel,
|
|
457
|
+
data: { "data-souvenir": id },
|
|
458
|
+
onPick: function() {
|
|
459
|
+
ui.drill.pick = active ? null : id;
|
|
460
|
+
ui.renderContent();
|
|
461
|
+
}
|
|
462
|
+
}));
|
|
463
|
+
})(list[s], s);
|
|
464
|
+
}
|
|
465
|
+
ui.content.appendChild(grid);
|
|
466
|
+
if (picked === null) return;
|
|
467
|
+
var story = el("div", "dp-pick dp-tile-card");
|
|
468
|
+
story.appendChild(el("div", "dp-pick-head", picked.emoji + " " + picked.label + (picked.fromLabel === "" ? "" : " \xB7 " + picked.fromLabel)));
|
|
469
|
+
story.appendChild(el("div", null, picked.story === "" ? "\uFF08\u65E7\u7248\u672C\u5E26\u56DE\u6765\u7684\uFF0C\u6CA1\u6709\u6545\u4E8B\uFF09" : "\u300C" + picked.story + "\u300D"));
|
|
470
|
+
if (picked.price > 0) {
|
|
471
|
+
var sold = picked;
|
|
472
|
+
var sell = button("dp-mini", { "data-sell": sold.key }, function() {
|
|
473
|
+
ui.drill.pick = null;
|
|
474
|
+
ui.send("sell", { souvenir: sold.key });
|
|
475
|
+
});
|
|
476
|
+
sell.textContent = "\u5356\u6389 +" + sold.price + " \u{1FA99}";
|
|
477
|
+
sell.style.marginTop = "6px";
|
|
478
|
+
story.appendChild(sell);
|
|
479
|
+
}
|
|
480
|
+
ui.content.appendChild(story);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/client/tabs/dev.js
|
|
484
|
+
function renderDevTab(ui) {
|
|
485
|
+
ui.content.appendChild(el("div", "dp-dev-note", "\u{1F527} \u5F00\u53D1\u8005\u6A21\u5F0F \xB7 \u6784\u5EFA v" + (ui.view.version === "" ? "\u672A\u77E5" : ui.view.version) + " \xB7 Ctrl+Shift+D \u5173\u95ED"));
|
|
486
|
+
if (ui.view.pig !== null && ui.view.pig.ageForced) {
|
|
487
|
+
ui.content.appendChild(el(
|
|
488
|
+
"div",
|
|
489
|
+
"dp-dev-note",
|
|
490
|
+
"\u26A0\uFE0F \u5E74\u9F84\u662F\u8C03\u8BD5\u6539\u7684\uFF08HUD \u4E0A\u6709 \u{1F527}\uFF09\u2014\u2014 \u6309\u300C\u23EA \u5E74\u9F84\u5F52\u96F6\u300D\u624D\u4F1A\u91CD\u65B0\u6309\u771F\u5B9E\u65F6\u95F4\u7B97"
|
|
491
|
+
));
|
|
492
|
+
}
|
|
493
|
+
var p = ui.view.pig;
|
|
494
|
+
if (p === null) {
|
|
495
|
+
ui.content.appendChild(el("div", "dp-empty", "\u8FD8\u6CA1\u6709\u732A\u3002\u5148\u300C\u62C6\u5F00\u7EB8\u76D2\u300D\u518D\u8C03\u3002"));
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
function group(title, entries) {
|
|
499
|
+
var head = el("div", "dp-title");
|
|
500
|
+
head.appendChild(el("b", null, title));
|
|
501
|
+
ui.content.appendChild(head);
|
|
502
|
+
var wrap = el("div", "dp-dev-row");
|
|
503
|
+
for (var i = 0; i < entries.length; i += 1) {
|
|
504
|
+
(function(entry) {
|
|
505
|
+
var btn = button("dp-mini dp-dev-btn", { "data-dev": entry.key }, function() {
|
|
506
|
+
entry.run();
|
|
507
|
+
});
|
|
508
|
+
btn.textContent = entry.label;
|
|
509
|
+
wrap.appendChild(btn);
|
|
510
|
+
})(entries[i]);
|
|
511
|
+
}
|
|
512
|
+
ui.content.appendChild(wrap);
|
|
513
|
+
}
|
|
514
|
+
var patch = function(body) {
|
|
515
|
+
ui.send("dev", { patch: body });
|
|
516
|
+
};
|
|
517
|
+
group("\u72B6\u6001", [
|
|
518
|
+
{ key: "full", label: "\u{1F60A} \u6EE1\u72B6\u6001", run: function() {
|
|
519
|
+
patch({ satiety: 100, happiness: 100, cleanliness: 100, health: 5 });
|
|
520
|
+
} },
|
|
521
|
+
{ key: "hungry", label: "\u{1F34E} \u997F", run: function() {
|
|
522
|
+
patch({ satiety: 10 });
|
|
523
|
+
} },
|
|
524
|
+
{ key: "dirty", label: "\u{1FAE7} \u810F", run: function() {
|
|
525
|
+
patch({ cleanliness: 10 });
|
|
526
|
+
} },
|
|
527
|
+
{ key: "lonely", label: "\u{1F97A} \u5B64\u5355", run: function() {
|
|
528
|
+
patch({ happiness: 10 });
|
|
529
|
+
} },
|
|
530
|
+
{ key: "sleepy", label: "\u{1F4A4} \u56F0", run: function() {
|
|
531
|
+
patch({ satiety: 90, happiness: 90, cleanliness: 90 });
|
|
532
|
+
} }
|
|
533
|
+
]);
|
|
534
|
+
group("\u751F\u75C5", [
|
|
535
|
+
{ key: "cold1", label: "\u{1F927} \u611F\u5192", run: function() {
|
|
536
|
+
patch({ illness: { chain: 0, stage: 1 }, health: 4 });
|
|
537
|
+
} },
|
|
538
|
+
{ key: "cough1", label: "\u{1F637} \u54B3\u55FD", run: function() {
|
|
539
|
+
patch({ illness: { chain: 1, stage: 1 }, health: 4 });
|
|
540
|
+
} },
|
|
541
|
+
{ key: "belly1", label: "\u{1F922} \u809A\u5B50\u80C0", run: function() {
|
|
542
|
+
patch({ illness: { chain: 2, stage: 1 }, health: 4 });
|
|
543
|
+
} },
|
|
544
|
+
{ key: "dizzy1", label: "\u{1F635} \u5934\u6655", run: function() {
|
|
545
|
+
patch({ illness: { chain: 3, stage: 1 }, health: 4 });
|
|
546
|
+
} },
|
|
547
|
+
{ key: "skin1", label: "\u{1FA79} \u7619\u75D2", run: function() {
|
|
548
|
+
patch({ illness: { chain: 4, stage: 1 }, health: 4 });
|
|
549
|
+
} },
|
|
550
|
+
{ key: "cold4", label: "\u2620\uFE0F \u80BA\u708E", run: function() {
|
|
551
|
+
patch({ illness: { chain: 0, stage: 4 }, health: 1 });
|
|
552
|
+
} },
|
|
553
|
+
{ key: "cure", label: "\u{1F49A} \u6CBB\u597D", run: function() {
|
|
554
|
+
patch({ illness: null, health: 5 });
|
|
555
|
+
} }
|
|
556
|
+
]);
|
|
557
|
+
group("\u7B49\u7EA7", [
|
|
558
|
+
{ key: "box", label: "\u{1F4E6} \u7EB8\u76D2", run: function() {
|
|
559
|
+
patch({ hatched: false });
|
|
560
|
+
} },
|
|
561
|
+
{ key: "lv1", label: "\u5E7C\u5E74 Lv1", run: function() {
|
|
562
|
+
patch({ hatched: true, level: 1 });
|
|
563
|
+
} },
|
|
564
|
+
{ key: "lv10", label: "\u9752\u5E74 Lv10", run: function() {
|
|
565
|
+
patch({ level: 10 });
|
|
566
|
+
} },
|
|
567
|
+
{ key: "lv40", label: "\u6210\u5E74 Lv40", run: function() {
|
|
568
|
+
patch({ level: 40 });
|
|
569
|
+
} },
|
|
570
|
+
{ key: "lv60", label: "\u6EE1\u7EA7 Lv60", run: function() {
|
|
571
|
+
patch({ level: 60 });
|
|
572
|
+
} },
|
|
573
|
+
{ key: "real", label: "\u23EA \u5929\u6570\u5F52\u96F6", run: function() {
|
|
574
|
+
ui.send("ageFromNow");
|
|
575
|
+
} }
|
|
576
|
+
]);
|
|
577
|
+
group("\u8D44\u6E90", [
|
|
578
|
+
{ key: "coin100", label: "\u{1FA99} +100", run: function() {
|
|
579
|
+
patch({ coins: p.coins + 100 });
|
|
580
|
+
} },
|
|
581
|
+
{ key: "coin999", label: "\u{1FA99} 9999", run: function() {
|
|
582
|
+
patch({ coins: 9999 });
|
|
583
|
+
} },
|
|
584
|
+
{ key: "traits", label: "\u{1F9E0}+5 \u2728+5 \u{1F4AA}+5", run: function() {
|
|
585
|
+
patch({ traits: { intel: 5, charm: 5, strong: 5 } });
|
|
586
|
+
} },
|
|
587
|
+
{ key: "all", label: "\u{1F381} \u4E00\u952E\u62FF\u9F50", run: function() {
|
|
588
|
+
ui.send("giveAll");
|
|
589
|
+
} }
|
|
590
|
+
]);
|
|
591
|
+
group("\u65F6\u95F4", [
|
|
592
|
+
{ key: "real", label: "\xD71 \u771F\u5B9E", run: function() {
|
|
593
|
+
ui.send("timeScale", { scale: 1 });
|
|
594
|
+
} },
|
|
595
|
+
{ key: "fast12", label: "\xD712", run: function() {
|
|
596
|
+
ui.send("timeScale", { scale: 12 });
|
|
597
|
+
} },
|
|
598
|
+
{ key: "fast30", label: "\xD730", run: function() {
|
|
599
|
+
ui.send("timeScale", { scale: 30 });
|
|
600
|
+
} },
|
|
601
|
+
{ key: "fast60", label: "\xD760", run: function() {
|
|
602
|
+
ui.send("timeScale", { scale: 60 });
|
|
603
|
+
} }
|
|
604
|
+
]);
|
|
605
|
+
group("\u751F\u6B7B", [
|
|
606
|
+
{ key: "kill", label: "\u{1F480} \u5F04\u6B7B", run: function() {
|
|
607
|
+
patch({ dead: true });
|
|
608
|
+
} },
|
|
609
|
+
{ key: "revive", label: "\u2728 \u590D\u6D3B", run: function() {
|
|
610
|
+
patch({ dead: false, health: 5 });
|
|
611
|
+
} },
|
|
612
|
+
{ key: "adopt", label: "\u{1F4E6} \u9886\u517B", run: function() {
|
|
613
|
+
ui.send("adopt");
|
|
614
|
+
} },
|
|
615
|
+
{ key: "reset", label: "\u{1F504} \u91CD\u7F6E", run: function() {
|
|
616
|
+
ui.send("reset");
|
|
617
|
+
} }
|
|
618
|
+
]);
|
|
619
|
+
group("\u9762\u677F", [
|
|
620
|
+
{ key: "open", label: "\u5C55\u5F00/\u6536\u8D77", run: function() {
|
|
621
|
+
ui.setOpen(ui.host.getAttribute("data-open") !== "true");
|
|
622
|
+
} },
|
|
623
|
+
{ key: "away1", label: "\u23E9 +1 \u5C0F\u65F6", run: function() {
|
|
624
|
+
patch({ __advanceMs: 36e5 });
|
|
625
|
+
} },
|
|
626
|
+
{ key: "away24", label: "\u23E9 +1 \u5929", run: function() {
|
|
627
|
+
patch({ __advanceMs: 864e5 });
|
|
628
|
+
} }
|
|
629
|
+
]);
|
|
630
|
+
ui.content.appendChild(el(
|
|
631
|
+
"div",
|
|
632
|
+
"dp-dev-note",
|
|
633
|
+
"\u5F53\u524D\uFF1A" + p.stage.label + " \xB7 \u5065\u5EB7 " + p.health + " \xB7 \u{1FA99} " + p.coins + (p.illness === null ? "" : " \xB7 " + p.illness.name)
|
|
634
|
+
));
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
// src/client/tabs/status.js
|
|
638
|
+
function renderStatusTab(ui) {
|
|
639
|
+
var p = ui.view.pig;
|
|
640
|
+
if (p === null) return;
|
|
641
|
+
renderBanners(ui);
|
|
642
|
+
labelledBar(ui, "\u{1F35A} \u9971\u98DF", p.satiety, p.satiety + "%");
|
|
643
|
+
labelledBar(ui, "\u2764\uFE0F \u5FC3\u60C5", p.happiness, p.happiness + "%", "dp-mood");
|
|
644
|
+
labelledBar(ui, "\u{1FAE7} \u6E05\u6D01", p.cleanliness, p.cleanliness + "%", "dp-clean");
|
|
645
|
+
labelledBar(ui, "\u{1F49A} \u5065\u5EB7", p.healthPercent, p.health + "/" + ui.view.maxHealth, "dp-health");
|
|
646
|
+
var traits = el("div", "dp-traits");
|
|
647
|
+
traits.appendChild(el("span", null, "\u{1F9E0} \u667A\u529B " + p.traits.intel));
|
|
648
|
+
traits.appendChild(el("span", null, "\u2728 \u9B45\u529B " + p.traits.charm));
|
|
649
|
+
traits.appendChild(el("span", null, "\u{1F4AA} \u6B66\u529B " + p.traits.strong));
|
|
650
|
+
ui.content.appendChild(traits);
|
|
651
|
+
var info = el("div", "dp-row");
|
|
652
|
+
info.appendChild(el("span", null, "\u2696\uFE0F \u4F53\u91CD " + p.weight));
|
|
653
|
+
info.appendChild(el("b", null, "\u{1FA99} " + p.coins));
|
|
654
|
+
ui.content.appendChild(info);
|
|
655
|
+
var daily = ui.view.daily;
|
|
656
|
+
var dailyLine = el("div", "dp-row");
|
|
657
|
+
dailyLine.appendChild(el("span", null, "\u{1F4C5} \u7B7E\u5230"));
|
|
658
|
+
dailyLine.appendChild(el("b", null, "\u7B2C " + daily.signInDay + "/" + daily.cycle + " \u5929" + (daily.canSignIn ? " \xB7 \u4ECA\u5929\u8FD8\u6CA1\u7B7E" : "") + (daily.unclaimed > 0 ? " \xB7 \u{1F381} " + daily.unclaimed : "")));
|
|
659
|
+
ui.content.appendChild(dailyLine);
|
|
660
|
+
var lvl = el("div", "dp-row");
|
|
661
|
+
lvl.appendChild(el("span", null, "\u2B50 \u7B49\u7EA7"));
|
|
662
|
+
lvl.appendChild(el("b", null, "Lv." + p.level.level + " " + p.level.titleEmoji + p.level.titleLabel + (p.level.maxed ? " \xB7 \u6EE1\u7EA7" : " \xB7 \u8FD8\u5DEE " + Math.ceil(p.level.toNext) + " \u6210\u957F")));
|
|
663
|
+
ui.content.appendChild(lvl);
|
|
664
|
+
var age = el("div", "dp-row");
|
|
665
|
+
age.appendChild(el("span", null, "\u{1F3E0} \u966A\u4F34"));
|
|
666
|
+
age.appendChild(el("b", null, p.ageLabel + (p.ageForced ? " \u{1F527}" : "") + (p.daysToNextStage === null ? " \xB7 \u5DF2\u957F\u6210" : "")));
|
|
667
|
+
ui.content.appendChild(age);
|
|
668
|
+
var grid = el("div", "dp-actions");
|
|
669
|
+
for (var i = 0; i < MODES.length; i += 1) {
|
|
670
|
+
(function(key) {
|
|
671
|
+
var info2 = ui.view.actions[key];
|
|
672
|
+
var shelf = ui.view.care[key] ?? [];
|
|
673
|
+
var needsItem = shelf.length > 0;
|
|
674
|
+
var btn = button("dp-btn", { "data-action": key }, function() {
|
|
675
|
+
if (needsItem) {
|
|
676
|
+
ui.picker = ui.picker === key ? null : key;
|
|
677
|
+
ui.renderContent();
|
|
678
|
+
} else {
|
|
679
|
+
ui.send(key);
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
btn.setAttribute("data-open-picker", ui.picker === key ? "true" : "false");
|
|
683
|
+
btn.appendChild(el("span", null, CARE_LABEL[key][1]));
|
|
684
|
+
btn.appendChild(el("span", null, CARE_LABEL[key][0]));
|
|
685
|
+
if (needsItem) btn.appendChild(el("span", "dp-count", String(shelf.length)));
|
|
686
|
+
if (!info2.ready || ui.view.dead) {
|
|
687
|
+
btn.disabled = true;
|
|
688
|
+
if (ui.view.dead) btn.appendChild(el("span", "dp-wait", "\u2014"));
|
|
689
|
+
else if (info2.waitSeconds > 0) btn.appendChild(el("span", "dp-wait", info2.waitSeconds + "s"));
|
|
690
|
+
else if (info2.blocked === "away") btn.appendChild(el("span", "dp-wait", "\u4E0D\u5728\u5BB6"));
|
|
691
|
+
}
|
|
692
|
+
grid.appendChild(btn);
|
|
693
|
+
})(MODES[i]);
|
|
694
|
+
}
|
|
695
|
+
ui.content.appendChild(grid);
|
|
696
|
+
if (ui.picker !== null && (ui.view.care[ui.picker] ?? []).length > 0) ui.content.appendChild(pickerPanel(ui, ui.picker));
|
|
697
|
+
ui.content.appendChild(talkRow(ui));
|
|
698
|
+
if (p.memories.length > 0) {
|
|
699
|
+
ui.content.appendChild(el("div", "dp-memo", p.memories.slice(-3).join("\n")));
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
function talkRow(ui) {
|
|
703
|
+
var row = el("div", "dp-row dp-talk");
|
|
704
|
+
if (ui.ownerEdit !== null || ui.pigNameEdit !== null) {
|
|
705
|
+
var forPig = ui.pigNameEdit !== null;
|
|
706
|
+
var input = (
|
|
707
|
+
/** @type {HTMLInputElement} */
|
|
708
|
+
el("input", "dp-input")
|
|
709
|
+
);
|
|
710
|
+
input.value = forPig ? ui.pigNameEdit : ui.ownerEdit;
|
|
711
|
+
input.maxLength = 16;
|
|
712
|
+
input.setAttribute(forPig ? "data-pig-input" : "data-owner-input", "true");
|
|
713
|
+
input.addEventListener("input", function() {
|
|
714
|
+
if (forPig) ui.pigNameEdit = input.value;
|
|
715
|
+
else ui.ownerEdit = input.value;
|
|
716
|
+
});
|
|
717
|
+
var save = button("dp-mini", { "data-name-save": forPig ? "pig" : "owner" }, function() {
|
|
718
|
+
var name = ((forPig ? ui.pigNameEdit : ui.ownerEdit) || "").trim();
|
|
719
|
+
if (forPig) ui.pigNameEdit = null;
|
|
720
|
+
else ui.ownerEdit = null;
|
|
721
|
+
if (name !== "") ui.send(forPig ? "name" : "owner", { name });
|
|
722
|
+
ui.renderContent();
|
|
723
|
+
});
|
|
724
|
+
save.textContent = "\u597D";
|
|
725
|
+
var cancel = button("dp-mini dp-mini-plain", { "data-name-cancel": "true" }, function() {
|
|
726
|
+
ui.ownerEdit = null;
|
|
727
|
+
ui.pigNameEdit = null;
|
|
728
|
+
ui.renderContent();
|
|
729
|
+
});
|
|
730
|
+
cancel.textContent = "\u7B97\u4E86";
|
|
731
|
+
row.appendChild(input);
|
|
732
|
+
row.appendChild(save);
|
|
733
|
+
row.appendChild(cancel);
|
|
734
|
+
return row;
|
|
735
|
+
}
|
|
736
|
+
var renameOwner = button("dp-mini dp-mini-plain", { "data-owner-edit": "true" }, function() {
|
|
737
|
+
ui.ownerEdit = ui.view.dialogue.ownerName;
|
|
738
|
+
ui.renderContent();
|
|
739
|
+
});
|
|
740
|
+
renameOwner.textContent = "\u270F\uFE0F \u79F0\u547C";
|
|
741
|
+
renameOwner.title = "\u73B0\u5728\u53EB\u300C" + ui.view.dialogue.ownerName + "\u300D";
|
|
742
|
+
var renamePig = button("dp-mini dp-mini-plain", { "data-pig-edit": "true" }, function() {
|
|
743
|
+
ui.pigNameEdit = ui.view.pig === null ? "" : ui.view.pig.name;
|
|
744
|
+
ui.renderContent();
|
|
745
|
+
});
|
|
746
|
+
renamePig.textContent = "\u270F\uFE0F \u540D\u5B57";
|
|
747
|
+
renamePig.title = ui.view.pig === null ? "\u732A\u8FD8\u6CA1\u6765" : "\u73B0\u5728\u53EB\u300C" + ui.view.pig.name + "\u300D";
|
|
748
|
+
var quiet = button("dp-mini dp-mini-plain", { "data-quiet": ui.view.dialogue.quiet ? "on" : "off" }, function() {
|
|
749
|
+
ui.send("quiet", { on: !ui.view.dialogue.quiet });
|
|
750
|
+
});
|
|
751
|
+
quiet.textContent = ui.view.dialogue.quiet ? "\u{1F515} \u514D\u6253\u6270\u4E2D" : "\u{1F514} \u514D\u6253\u6270";
|
|
752
|
+
row.appendChild(renameOwner);
|
|
753
|
+
row.appendChild(renamePig);
|
|
754
|
+
row.appendChild(quiet);
|
|
755
|
+
return row;
|
|
756
|
+
}
|
|
757
|
+
function renderBanners(ui) {
|
|
758
|
+
if (ui.view.pig !== null && ui.view.dead) {
|
|
759
|
+
var dead = el("div", "dp-alert dp-dead");
|
|
760
|
+
dead.appendChild(el("b", null, "\u{1FAA6} " + ui.view.pig.name + " \u8D70\u4E86" + (ui.view.pig.soul ? "\uFF0C\u7075\u9B42\u8FD8\u7559\u5728\u5893\u7891\u4E0A \u{1F47B}" : "")));
|
|
761
|
+
dead.appendChild(el("div", null, ui.view.pig.soul ? "\u7528\u8FD8\u9B42\u4E39\u53EF\u4EE5\u628A\u5B83\u53EB\u56DE\u6765\uFF0C\u4E5F\u53EF\u4EE5\u9886\u517B\u65B0\u7684" : "\u80CC\u5305\u91CC\u7684\u8FD8\u9B42\u4E39\u5C31\u80FD\u6551\u56DE\u6765"));
|
|
762
|
+
ui.content.appendChild(dead);
|
|
763
|
+
var adoptWrap = el("div", "dp-actions");
|
|
764
|
+
var adopt = button("dp-btn dp-btn-wide", { "data-action": "adopt" }, function() {
|
|
765
|
+
ui.send("adopt");
|
|
766
|
+
});
|
|
767
|
+
adopt.appendChild(el("span", null, "\u{1F4E6}"));
|
|
768
|
+
adopt.appendChild(el("span", null, "\u9886\u517B\u65B0\u732A"));
|
|
769
|
+
adoptWrap.appendChild(adopt);
|
|
770
|
+
ui.content.appendChild(adoptWrap);
|
|
771
|
+
} else if (ui.view.pig !== null && ui.view.pig.illness !== null) {
|
|
772
|
+
var illness = ui.view.pig.illness;
|
|
773
|
+
var sick = el("div", "dp-alert dp-sick");
|
|
774
|
+
sick.appendChild(el("b", null, "\u{1F912} " + illness.name + "\uFF08\u7B2C " + illness.stage + "/4 \u671F\uFF09"));
|
|
775
|
+
sick.appendChild(el("div", null, "\u9700\u8981\u300C" + illness.cureEmoji + illness.cure + "\u300D\u2014\u2014 \u5403\u9519\u836F\u4F1A\u52A0\u91CD"));
|
|
776
|
+
var needed = null;
|
|
777
|
+
var shelf = ui.view.shop || [];
|
|
778
|
+
for (var c = 0; c < shelf.length; c += 1) {
|
|
779
|
+
if (shelf[c].needed) needed = shelf[c];
|
|
780
|
+
}
|
|
781
|
+
for (var t = 0; needed === null && t < shelf.length; t += 1) {
|
|
782
|
+
if (shelf[t].kind === "medicine" && shelf[t].tier === illness.stage) needed = shelf[t];
|
|
783
|
+
}
|
|
784
|
+
for (var n = 0; needed === null && n < shelf.length; n += 1) {
|
|
785
|
+
if (shelf[n].label === illness.cure) needed = shelf[n];
|
|
786
|
+
}
|
|
787
|
+
if (ui.view.canGoOut) {
|
|
788
|
+
sick.appendChild(el("div", "dp-dim", "\u5E26\u75C5\u51FA\u95E8\u62A5\u916C\u51CF\u534A\u3001\u75C5\u60C5\u66F4\u5FEB"));
|
|
789
|
+
}
|
|
790
|
+
if (needed !== null && ui.view.canGoOut && ui.view.pig.coins < needed.price) {
|
|
791
|
+
sick.appendChild(el("div", "dp-dim", "\u8FD8\u5DEE " + needed.price + " \u{1FA99} \u4E70\u300C" + needed.label + "\u300D\uFF0C\u5148\u53BB\u6253\u5DE5"));
|
|
792
|
+
}
|
|
793
|
+
ui.content.appendChild(sick);
|
|
794
|
+
if (illness.doctorFee !== null) {
|
|
795
|
+
var clinic = el("div", "dp-actions");
|
|
796
|
+
var doctor = button("dp-btn dp-btn-wide", { "data-action": "doctor" }, function() {
|
|
797
|
+
ui.send("doctor");
|
|
798
|
+
});
|
|
799
|
+
doctor.appendChild(el("span", null, "\u{1F3E5}"));
|
|
800
|
+
doctor.appendChild(el("span", null, "\u770B\u533B\u751F\uFF08" + illness.doctorFee + " \u{1FA99}\uFF09"));
|
|
801
|
+
clinic.appendChild(doctor);
|
|
802
|
+
ui.content.appendChild(clinic);
|
|
803
|
+
}
|
|
804
|
+
} else if (ui.view.pig !== null && ui.view.activity !== null) {
|
|
805
|
+
var away = el("div", "dp-alert dp-work");
|
|
806
|
+
away.appendChild(el("b", null, ui.view.activity.emoji + " \u5728\u5916\u9762\uFF1A" + ui.view.activity.label));
|
|
807
|
+
away.appendChild(el("div", null, "\u8FD8\u6709 " + ui.view.activity.secondsLeft + " \u79D2"));
|
|
808
|
+
ui.content.appendChild(away);
|
|
809
|
+
var wrap = el("div", "dp-actions");
|
|
810
|
+
var call = button("dp-btn dp-btn-wide", { "data-action": "calloff" }, function() {
|
|
811
|
+
ui.send("calloff");
|
|
812
|
+
});
|
|
813
|
+
call.appendChild(el("span", null, "\u21A9\uFE0F"));
|
|
814
|
+
call.appendChild(el("span", null, "\u53EB\u5B83\u56DE\u6765"));
|
|
815
|
+
wrap.appendChild(call);
|
|
816
|
+
ui.content.appendChild(wrap);
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// src/client/tabs/study.js
|
|
821
|
+
var INTEREST_TAB = "interest";
|
|
822
|
+
var STAGE_COLOR = { primary: "yellow", middle: "teal", college: "blue", graduate: "purple", beyond: "pink" };
|
|
823
|
+
var INTEREST_COLOR = "orange";
|
|
824
|
+
var FALLBACK_COLORS = ["yellow", "teal", "blue", "purple", "pink", "green", "lime"];
|
|
825
|
+
function standing(sub, stage) {
|
|
826
|
+
if (stage === null || sub.stageKey === "") return "current";
|
|
827
|
+
if (sub.stageKey === stage.key) return "current";
|
|
828
|
+
if (stage.upTo !== null && sub.lessons >= stage.upTo) return "done";
|
|
829
|
+
return "ahead";
|
|
830
|
+
}
|
|
831
|
+
function renderStudyTab(ui) {
|
|
832
|
+
if (ui.view.subjects.length === 0) {
|
|
833
|
+
ui.content.appendChild(el("div", "dp-empty", "\u5BBF\u4E3B\u8FD8\u6CA1\u63D0\u4F9B\u8BFE\u7A0B\u8868\u3002"));
|
|
834
|
+
return;
|
|
835
|
+
}
|
|
836
|
+
var open = ui.drill.study;
|
|
837
|
+
if (open === INTEREST_TAB && ui.view.interests.length > 0) {
|
|
838
|
+
renderInterests(ui);
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
var stage = null;
|
|
842
|
+
for (var d = 0; d < ui.view.stages.length; d += 1) if (ui.view.stages[d].key === open) stage = ui.view.stages[d];
|
|
843
|
+
if (stage === null) {
|
|
844
|
+
renderStages(ui);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
renderSubjects(ui, stage);
|
|
848
|
+
}
|
|
849
|
+
function renderStages(ui) {
|
|
850
|
+
var stageList = ui.view.stages.length > 0 ? ui.view.stages : STAGES;
|
|
851
|
+
var grid = tileGrid();
|
|
852
|
+
for (var s = 0; s < stageList.length; s += 1) {
|
|
853
|
+
(function(entry, index) {
|
|
854
|
+
var locked = entry.unlocked === false;
|
|
855
|
+
var finished = entry.upTo === null || entry.upTo === void 0 ? 0 : ui.view.subjects.filter(function(sub) {
|
|
856
|
+
return sub.lessons >= entry.upTo;
|
|
857
|
+
}).length;
|
|
858
|
+
grid.appendChild(tile({
|
|
859
|
+
emoji: entry.emoji || "\u{1F4DA}",
|
|
860
|
+
label: entry.label,
|
|
861
|
+
color: STAGE_COLOR[entry.key] ?? FALLBACK_COLORS[index % FALLBACK_COLORS.length],
|
|
862
|
+
locked,
|
|
863
|
+
tag: locked ? "\u{1F512}" : "",
|
|
864
|
+
badge: finished > 0 ? "\u2713" + finished : "",
|
|
865
|
+
data: { "data-stage": entry.key },
|
|
866
|
+
onPick: function() {
|
|
867
|
+
drillTo(ui, "study", entry.key);
|
|
868
|
+
}
|
|
869
|
+
}));
|
|
870
|
+
})(stageList[s], s);
|
|
871
|
+
}
|
|
872
|
+
if (ui.view.interests.length > 0) {
|
|
873
|
+
var certified = ui.view.interests.filter(function(entry) {
|
|
874
|
+
return entry.certified;
|
|
875
|
+
}).length;
|
|
876
|
+
grid.appendChild(tile({
|
|
877
|
+
emoji: "\u{1F3AF}",
|
|
878
|
+
label: "\u5174\u8DA3",
|
|
879
|
+
color: INTEREST_COLOR,
|
|
880
|
+
badge: certified > 0 ? "\u{1F4DC}" + certified : "",
|
|
881
|
+
data: { "data-stage": INTEREST_TAB },
|
|
882
|
+
onPick: function() {
|
|
883
|
+
drillTo(ui, "study", INTEREST_TAB);
|
|
884
|
+
}
|
|
885
|
+
}));
|
|
886
|
+
}
|
|
887
|
+
ui.content.appendChild(grid);
|
|
888
|
+
}
|
|
889
|
+
function renderSubjects(ui, stage) {
|
|
890
|
+
drillHeader(
|
|
891
|
+
ui,
|
|
892
|
+
"study",
|
|
893
|
+
stage.emoji + " " + stage.label,
|
|
894
|
+
stage.minutes + " \u5206\u949F \xB7 " + stage.tuition + " \u{1FA99} \xB7 +" + stage.gain
|
|
895
|
+
);
|
|
896
|
+
var color = STAGE_COLOR[stage.key] ?? FALLBACK_COLORS[Math.max(0, ui.view.stages.indexOf(stage)) % FALLBACK_COLORS.length];
|
|
897
|
+
var grid = tileGrid();
|
|
898
|
+
for (var i = 0; i < ui.view.subjects.length; i += 1) {
|
|
899
|
+
(function(sub) {
|
|
900
|
+
var where = standing(sub, stage);
|
|
901
|
+
var note;
|
|
902
|
+
if (where === "done") note = "\u2713 \u6BD5\u4E1A";
|
|
903
|
+
else if (where === "ahead") note = "\u{1F512} " + (sub.stageLabel || "\u6CA1\u5230");
|
|
904
|
+
else if (stage.upTo !== null) note = sub.lessons - stage.from + "/" + (stage.upTo - stage.from) + " \u8282";
|
|
905
|
+
else note = sub.lessons + " \u8282";
|
|
906
|
+
grid.appendChild(tile({
|
|
907
|
+
emoji: sub.emoji,
|
|
908
|
+
label: sub.label,
|
|
909
|
+
color,
|
|
910
|
+
soft: true,
|
|
911
|
+
note,
|
|
912
|
+
disabled: where !== "current" || !ui.view.canGoOut,
|
|
913
|
+
dim: where === "current" && !sub.affordable,
|
|
914
|
+
data: { "data-subject": sub.key },
|
|
915
|
+
onPick: function() {
|
|
916
|
+
ui.send("study", { subject: sub.key });
|
|
917
|
+
}
|
|
918
|
+
}));
|
|
919
|
+
})(ui.view.subjects[i]);
|
|
920
|
+
}
|
|
921
|
+
ui.content.appendChild(grid);
|
|
922
|
+
}
|
|
923
|
+
function renderInterests(ui) {
|
|
924
|
+
var after = ui.view.interests[0].certificateAfter;
|
|
925
|
+
drillHeader(ui, "study", "\u{1F3AF} \u5174\u8DA3", after > 0 ? "\u4E0A\u6EE1 " + after + " \u6B21\u62FF\u8BC1" : "");
|
|
926
|
+
var grid = tileGrid();
|
|
927
|
+
for (var n = 0; n < ui.view.interests.length; n += 1) {
|
|
928
|
+
(function(entry) {
|
|
929
|
+
var note = entry.certificate === "" ? entry.cost + " \u{1FA99}" : entry.certified ? "\u{1F4DC} \u6709\u8BC1" : "\u{1F4DC} " + entry.times + "/" + entry.certificateAfter;
|
|
930
|
+
grid.appendChild(tile({
|
|
931
|
+
emoji: entry.emoji,
|
|
932
|
+
label: entry.label,
|
|
933
|
+
color: INTEREST_COLOR,
|
|
934
|
+
soft: true,
|
|
935
|
+
note,
|
|
936
|
+
disabled: !ui.view.canGoOut,
|
|
937
|
+
dim: !entry.affordable,
|
|
938
|
+
data: { "data-interest": entry.key },
|
|
939
|
+
onPick: function() {
|
|
940
|
+
ui.send("interest", { interest: entry.key });
|
|
941
|
+
}
|
|
942
|
+
}));
|
|
943
|
+
})(ui.view.interests[n]);
|
|
944
|
+
}
|
|
945
|
+
ui.content.appendChild(grid);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// src/client/format.js
|
|
949
|
+
function formatMinutes(minutes) {
|
|
950
|
+
if (minutes < 60) return minutes + " \u5206\u949F";
|
|
951
|
+
const hours = Math.floor(minutes / 60);
|
|
952
|
+
const rest = minutes % 60;
|
|
953
|
+
return rest === 0 ? hours + " \u5C0F\u65F6" : hours + " \u5C0F\u65F6" + rest + " \u5206";
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// src/client/css-base.js
|
|
957
|
+
var CSS_BASE = [
|
|
958
|
+
// ---------------------------------------------------------------------
|
|
959
|
+
// Animal Crossing design language, transcribed from
|
|
960
|
+
// guokaigdg/animal-island-ui docs/design-system (design-tokens.md and the
|
|
961
|
+
// standalone css-variables.md template).
|
|
962
|
+
//
|
|
963
|
+
// The tokens are declared on the widget root rather than :root: the host
|
|
964
|
+
// page must not inherit them, and they must not be clobbered by it.
|
|
965
|
+
//
|
|
966
|
+
// The rules that shape everything below:
|
|
967
|
+
// · warm earth-brown text on cream parchment, never pure black or grey
|
|
968
|
+
// · 12px minimum radius; buttons and inputs are 50px pills
|
|
969
|
+
// · the thick 3D bottom shadow belongs to primary buttons only
|
|
970
|
+
// · cards carry a border, not an elevation shadow
|
|
971
|
+
// · motion is 0.15-0.35s on cubic-bezier(.4,0,.2,1)
|
|
972
|
+
// · focus rings are yellow or teal, never blue
|
|
973
|
+
// ---------------------------------------------------------------------
|
|
974
|
+
"[data-dsh-pig]{",
|
|
975
|
+
'--ac-font:Nunito,"Noto Sans SC",-apple-system,"PingFang SC","Hiragino Sans GB",sans-serif;',
|
|
976
|
+
"--ac-primary:#19c8b9;--ac-primary-hover:#3dd4c6;--ac-primary-active:#11a89b;",
|
|
977
|
+
"--ac-primary-bg:#e6f9f6;",
|
|
978
|
+
"--ac-text:#794f27;--ac-text-body:#725d42;--ac-text-2:#9f927d;--ac-text-muted:#8a7b66;",
|
|
979
|
+
"--ac-text-disabled:#c4b89e;",
|
|
980
|
+
"--ac-bg:#f8f8f0;--ac-bg-content:rgb(247,243,223);--ac-bg-input:#fffbe7;",
|
|
981
|
+
"--ac-bg-disabled:#f0ece2;",
|
|
982
|
+
"--ac-border:#c4b89e;--ac-border-light:#e5dcc6;--ac-border-hover:#a89878;",
|
|
983
|
+
"--ac-radius-sm:12px;--ac-radius-card:20px;--ac-pill:50px;",
|
|
984
|
+
"--ac-shadow-sm:0 2px 4px 0 rgba(61,52,40,.06);",
|
|
985
|
+
"--ac-shadow:0 3px 10px 0 rgba(61,52,40,.1);",
|
|
986
|
+
"--ac-shadow-lg:0 8px 24px 0 rgba(61,52,40,.16);",
|
|
987
|
+
"--ac-inset:inset 0 2px 4px rgba(114,93,66,.15);",
|
|
988
|
+
// sidebar tokens: the library uses these for the selected menu row, which
|
|
989
|
+
// is exactly the role the icon bar plays here.
|
|
990
|
+
"--ac-active:#b7c6e5;--ac-hover:#d6dff0;",
|
|
991
|
+
"--ac-success:#6fba2c;--ac-warning:#f5c31c;--ac-error:#e05a5a;",
|
|
992
|
+
"--ac-ease:cubic-bezier(.4,0,.2,1);",
|
|
993
|
+
// One place to size the pig; the scene and the panel cap derive from it.
|
|
994
|
+
"--pig-size:56px;--pig-gap-below:12px;--scene-open:132px;--panel-width:292px;",
|
|
995
|
+
"position:fixed;right:18px;bottom:18px;z-index:2147483000;",
|
|
996
|
+
"font-family:var(--ac-font);font-weight:500;letter-spacing:.01em;",
|
|
997
|
+
"-webkit-user-select:none;user-select:none;touch-action:none;",
|
|
998
|
+
// The wrapper spans a column wider and taller than what it paints (the
|
|
999
|
+
// scene's padding, the gap above the panel). Without this it swallows
|
|
1000
|
+
// clicks aimed at the page underneath — which once looked like "sending a
|
|
1001
|
+
// message does nothing" while the whole stack was healthy.
|
|
1002
|
+
"pointer-events:none;",
|
|
1003
|
+
// The pig is the only in-flow child, so the wrapper's box is exactly the
|
|
1004
|
+
// pig's box and the panel can be parked anywhere around it without ever
|
|
1005
|
+
// nudging the pig. `fitPanel` places the panel.
|
|
1006
|
+
"display:block}",
|
|
1007
|
+
"[data-dsh-pig] *{box-sizing:border-box}",
|
|
1008
|
+
"[data-dsh-pig]>*{pointer-events:auto}",
|
|
1009
|
+
// `hidden` MUST win. The UA sheet's `[hidden]{display:none}` ties on
|
|
1010
|
+
// specificity with a single class, so any `.dp-x{display:grid|flex}` rule
|
|
1011
|
+
// below silently beats it and the element keeps rendering. That is exactly
|
|
1012
|
+
// how a collapsed panel ended up showing the icon bar and the hud while
|
|
1013
|
+
// every `el.hidden === true` assertion still passed.
|
|
1014
|
+
"[data-dsh-pig] .dp-card[hidden],[data-dsh-pig] .dp-bar[hidden],",
|
|
1015
|
+
"[data-dsh-pig] .dp-content[hidden],[data-dsh-pig] .dp-hud[hidden],",
|
|
1016
|
+
"[data-dsh-pig] .dp-bubble[hidden],[data-dsh-pig] .dp-scene[hidden],",
|
|
1017
|
+
"[data-dsh-pig] .dp-work[hidden],[data-dsh-pig] .dp-soul[hidden],",
|
|
1018
|
+
"[data-dsh-pig] .dp-poke-hint[hidden],[data-dsh-pig] .dp-daily[hidden],",
|
|
1019
|
+
"[data-dsh-pig] .dp-pig-img[hidden],[data-dsh-pig] .dp-pig-emoji[hidden]{display:none}",
|
|
1020
|
+
/* ---------- the panel: cream parchment, border not shadow ---------- */
|
|
1021
|
+
// Taken out of flow on purpose. In flow it would widen the wrapper, and a
|
|
1022
|
+
// wider wrapper moves the pig — the exact thing this layout exists to
|
|
1023
|
+
// prevent. Absolutely positioned, the wrapper's box stays the pig's box
|
|
1024
|
+
// and `fitPanel` can put the panel on whichever side has room.
|
|
1025
|
+
".dp-card{position:absolute;right:0;bottom:calc(100% + 8px);width:var(--panel-width);",
|
|
1026
|
+
"border-radius:var(--ac-radius-card);overflow:hidden;",
|
|
1027
|
+
"display:flex;flex-direction:column;",
|
|
1028
|
+
"background:var(--ac-bg);border:2px solid var(--ac-border-light);",
|
|
1029
|
+
// 面板自己钉住基准字号与字体:不钉就会继承宿主页面的 16px,
|
|
1030
|
+
// 详情框那种「没写 font-size 的容器」就会比周围大一倍(用户反馈 #2)。
|
|
1031
|
+
"box-shadow:var(--ac-shadow-lg);color:var(--ac-text-body);font-family:var(--ac-font);font-size:11px}",
|
|
1032
|
+
/* ---------- the pig: never moved, never boxed ---------- */
|
|
1033
|
+
".dp-scene{position:relative;height:var(--scene-open);background:none;cursor:grab;",
|
|
1034
|
+
"overflow:visible;display:flex;align-items:flex-end;justify-content:flex-end;",
|
|
1035
|
+
"padding:0 6px var(--pig-gap-below);width:max-content}",
|
|
1036
|
+
'.dp-scene[data-dragging="true"]{cursor:grabbing}',
|
|
1037
|
+
// Collapsed the scene is exactly the pig, so the wrapper paints nothing
|
|
1038
|
+
// extra to click through. Open it widens to the panel so the hud and the
|
|
1039
|
+
// speech bubble have somewhere to sit — the pig is right-aligned either
|
|
1040
|
+
// way, so widening costs it no movement.
|
|
1041
|
+
'[data-dsh-pig][data-open="true"] .dp-scene{width:var(--panel-width)}',
|
|
1042
|
+
// Collapsed the scene shrinks to just the pig. An explicit height rather
|
|
1043
|
+
// than `auto` keeps the pig's line box identical in both states, so
|
|
1044
|
+
// opening moves it by exactly zero pixels.
|
|
1045
|
+
'[data-dsh-pig][data-open="false"] .dp-scene{height:calc(var(--pig-size) + var(--pig-gap-below));',
|
|
1046
|
+
"cursor:pointer}",
|
|
1047
|
+
".dp-pig{line-height:1;transform-origin:50% 85%;cursor:pointer;position:relative;",
|
|
1048
|
+
"filter:drop-shadow(0 4px 6px rgba(61,52,40,.28));animation:dp-bob 1.8s ease-in-out infinite}",
|
|
1049
|
+
// 装扮点位:猪身上固定的几个锚点,每个点位挂一件。
|
|
1050
|
+
// 以后换真立绘时,只改这里的偏移/尺寸,逻辑和存档都不用动。
|
|
1051
|
+
".dp-dress{position:absolute;inset:0;pointer-events:none;z-index:3}",
|
|
1052
|
+
".dp-slot{position:absolute;line-height:1;font-size:15px;transform:translate(-50%,-50%)}",
|
|
1053
|
+
'.dp-slot[data-slot="head"]{left:50%;top:2%}',
|
|
1054
|
+
'.dp-slot[data-slot="face"]{left:50%;top:32%}',
|
|
1055
|
+
'.dp-slot[data-slot="neck"]{left:50%;top:60%}',
|
|
1056
|
+
'.dp-slot[data-slot="body"]{left:50%;top:78%;font-size:19px}',
|
|
1057
|
+
'.dp-slot[data-slot="back"]{left:14%;top:42%;font-size:19px}',
|
|
1058
|
+
'.dp-slot[data-slot="feet"]{left:50%;top:99%}',
|
|
1059
|
+
'[data-dsh-pig][data-open="false"] .dp-pig{filter:drop-shadow(0 5px 9px rgba(61,52,40,.26))}',
|
|
1060
|
+
// A petting hand rather than an arrow. Drawn inline as an SVG data URI so
|
|
1061
|
+
// it needs no asset and can carry the palette's warm outline; the hotspot
|
|
1062
|
+
// sits in the palm, which is where a pat actually lands. The `pointer`
|
|
1063
|
+
// after it is the fallback for browsers that refuse a custom cursor.
|
|
1064
|
+
`.dp-pig{cursor:url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="30" height="30" viewBox="0 0 30 30"><g fill="%23F7C9B6" stroke="%23794F27" stroke-width="1.7" stroke-linejoin="round"><rect x="10" y="13.5" width="14" height="12" rx="4.8"/><rect x="10.6" y="6.6" width="3.6" height="10" rx="1.8"/><rect x="14.9" y="5.1" width="3.6" height="11.5" rx="1.8"/><rect x="19.2" y="6.6" width="3.6" height="10" rx="1.8"/><rect x="5.7" y="12.4" width="3.4" height="7.8" rx="1.7" transform="rotate(-27 7.4 16.3)"/></g></svg>') 16 24, pointer}`,
|
|
1065
|
+
// Transform-only keyframes: the pig is an ordinary flex item, so there is
|
|
1066
|
+
// no translateX(-50%) centring to preserve.
|
|
1067
|
+
"@keyframes dp-bob{0%,100%{transform:translateY(0) rotate(0deg)}50%{transform:translateY(-7px) rotate(-2.5deg)}}",
|
|
1068
|
+
"@keyframes dp-breathe{0%,100%{transform:translateY(0) scale(1)}50%{transform:translateY(1px) scale(1.09)}}",
|
|
1069
|
+
"@keyframes dp-shake{0%,100%{transform:translateX(0) rotate(0)}20%{transform:translateX(-4px) rotate(-5deg)}60%{transform:translateX(4px) rotate(5deg)}}",
|
|
1070
|
+
"@keyframes dp-spin{0%{transform:rotate(0)}50%{transform:rotate(180deg) scale(1.2)}100%{transform:rotate(360deg)}}",
|
|
1071
|
+
"@keyframes dp-jump{0%{transform:translateY(0)}30%{transform:translateY(-26px) scale(1.12)}60%{transform:translateY(0) scale(.92)}100%{transform:translateY(0)}}",
|
|
1072
|
+
"@keyframes dp-wobble{0%,100%{transform:rotate(0)}20%{transform:rotate(-14deg)}55%{transform:rotate(14deg)}}",
|
|
1073
|
+
"@keyframes dp-cough{0%,100%{transform:translateX(0)}30%{transform:translateX(-4px) rotate(-7deg)}70%{transform:translateX(4px) rotate(6deg)}}",
|
|
1074
|
+
'.dp-pig[data-mood="happy"]{animation-duration:1.15s}',
|
|
1075
|
+
'.dp-pig[data-mood="sleepy"]{animation-name:dp-breathe;animation-duration:3.6s}',
|
|
1076
|
+
'.dp-pig[data-mood="hungry"]{animation-name:dp-shake;animation-duration:2.4s}',
|
|
1077
|
+
'.dp-pig[data-mood="dirty"]{animation-name:dp-breathe;animation-duration:2.6s;filter:sepia(.4) drop-shadow(0 4px 6px rgba(61,52,40,.28))}',
|
|
1078
|
+
'.dp-pig[data-mood="sick"]{animation-name:dp-cough;animation-duration:2.2s;filter:hue-rotate(-28deg) saturate(.75) drop-shadow(0 4px 6px rgba(61,52,40,.28))}',
|
|
1079
|
+
// One pose per activity, so being away reads as a thing the pig is doing.
|
|
1080
|
+
"@keyframes dp-typing{0%,100%{transform:translateY(0) rotate(0)}25%{transform:translateY(-2px) rotate(-1.5deg)}50%{transform:translateY(0) rotate(0)}75%{transform:translateY(-2px) rotate(1.5deg)}}",
|
|
1081
|
+
"@keyframes dp-reading{0%,100%{transform:translateY(0) rotate(0)}35%{transform:translateY(1px) rotate(-5deg)}70%{transform:translateY(1px) rotate(-2deg)}}",
|
|
1082
|
+
"@keyframes dp-walking{0%,100%{transform:translateY(0) rotate(0)}25%{transform:translateY(-6px) rotate(-4deg)}50%{transform:translateY(0) rotate(0)}75%{transform:translateY(-6px) rotate(4deg)}}",
|
|
1083
|
+
'.dp-pig[data-mood="working"]{animation-name:dp-typing;animation-duration:.7s}',
|
|
1084
|
+
'.dp-pig[data-mood="studying"]{animation-name:dp-reading;animation-duration:2.4s}',
|
|
1085
|
+
'.dp-pig[data-mood="traveling"]{animation-name:dp-walking;animation-duration:1s}',
|
|
1086
|
+
'.dp-pig[data-mood="dead"]{animation:none;filter:grayscale(1)}',
|
|
1087
|
+
".dp-pig[data-react]{animation-duration:.85s;animation-iteration-count:1}",
|
|
1088
|
+
'.dp-pig[data-react="feed"]{animation-name:dp-jump}',
|
|
1089
|
+
'.dp-pig[data-react="bathe"]{animation-name:dp-wobble;animation-duration:1.05s}',
|
|
1090
|
+
'.dp-pig[data-react="play"]{animation-name:dp-spin;animation-duration:.9s}',
|
|
1091
|
+
'.dp-pig[data-react="away"]{animation-name:dp-jump;animation-duration:.9s}',
|
|
1092
|
+
'.dp-pig[data-react="cure"]{animation-name:dp-spin;animation-duration:.9s}',
|
|
1093
|
+
'.dp-pig[data-react="levelup"]{animation-name:dp-jump;animation-duration:.95s}',
|
|
1094
|
+
'.dp-pig[data-react="refuse"]{animation-name:dp-shake;animation-duration:.5s}',
|
|
1095
|
+
/* ---------- what the pig is off doing ---------- */
|
|
1096
|
+
"[data-dsh-pig] .dp-work{display:flex;flex-direction:column;align-items:center;gap:4px;",
|
|
1097
|
+
"margin:0 2px 6px 0}",
|
|
1098
|
+
".dp-prop{font-size:26px;line-height:1;filter:drop-shadow(0 3px 5px rgba(61,52,40,.22));",
|
|
1099
|
+
"animation:dp-prop-bob 2.4s ease-in-out infinite}",
|
|
1100
|
+
'[data-dsh-pig][data-away="study"] .dp-prop{animation-duration:3.4s}',
|
|
1101
|
+
'[data-dsh-pig][data-away="trip"] .dp-prop{animation-name:dp-prop-swing;animation-duration:1.6s}',
|
|
1102
|
+
'[data-dsh-pig][data-away="interest"] .dp-prop{animation-duration:3.4s}',
|
|
1103
|
+
"@keyframes dp-prop-bob{0%,100%{transform:translateY(0) rotate(-3deg)}50%{transform:translateY(-3px) rotate(3deg)}}",
|
|
1104
|
+
"@keyframes dp-prop-swing{0%,100%{transform:translateY(0) rotate(-8deg)}50%{transform:translateY(-4px) rotate(8deg)}}",
|
|
1105
|
+
".dp-progress{width:42px;height:7px;border-radius:var(--ac-pill);background:var(--ac-bg-disabled);",
|
|
1106
|
+
"box-shadow:var(--ac-inset);overflow:hidden}",
|
|
1107
|
+
".dp-progress i{display:block;height:100%;border-radius:var(--ac-pill);",
|
|
1108
|
+
"background:var(--ac-primary);transition:width .5s var(--ac-ease)}",
|
|
1109
|
+
// The scene needs room for the prop; it grows leftward, so the pig stays put.
|
|
1110
|
+
'[data-dsh-pig][data-away="work"] .dp-scene,[data-dsh-pig][data-away="study"] .dp-scene,',
|
|
1111
|
+
'[data-dsh-pig][data-away="interest"] .dp-scene,',
|
|
1112
|
+
'[data-dsh-pig][data-away="trip"] .dp-scene{width:max-content;min-width:132px}',
|
|
1113
|
+
// 加冕后的形态有动作立绘(桌子、书、行李都画在图里):不再摆 emoji 道具,
|
|
1114
|
+
// 动作也收小,免得把画里的东西甩来甩去(立绘与动作来自 PR #2)。
|
|
1115
|
+
'[data-dsh-pig][data-art-actions="true"] .dp-prop{display:none}',
|
|
1116
|
+
'.dp-pig[data-art-actions="true"][data-mood="working"]:not([data-react]){animation:dp-king-work 1.4s ease-in-out infinite}',
|
|
1117
|
+
'.dp-pig[data-art-actions="true"][data-mood="studying"]:not([data-react]){animation:dp-king-study 2.4s ease-in-out infinite}',
|
|
1118
|
+
'.dp-pig[data-art-actions="true"][data-mood="traveling"]:not([data-react]){animation:dp-king-walk .8s ease-in-out infinite}',
|
|
1119
|
+
"@keyframes dp-king-work{0%,100%{transform:translateY(0)}50%{transform:translateY(1px) rotate(1deg)}}",
|
|
1120
|
+
"@keyframes dp-king-study{0%,100%{transform:rotate(-2deg)}50%{transform:rotate(2deg)}}",
|
|
1121
|
+
"@keyframes dp-king-walk{0%,100%{transform:translateY(0) rotate(-2deg)}50%{transform:translateY(-3px) rotate(2deg)}}",
|
|
1122
|
+
/* ---------- hud: a cream tag beside the pig ---------- */
|
|
1123
|
+
".dp-hud{position:absolute;left:9px;top:7px;display:flex;flex-direction:column;gap:1px;",
|
|
1124
|
+
"font-size:10.5px;font-weight:600;line-height:1.45;color:var(--ac-text);",
|
|
1125
|
+
"background:var(--ac-bg);border:2px solid var(--ac-border-light);padding:5px 10px;",
|
|
1126
|
+
"border-radius:var(--ac-radius-sm);box-shadow:var(--ac-shadow-sm)}",
|
|
1127
|
+
".dp-hud b{font-weight:700}",
|
|
1128
|
+
// A drawn sprite is sized by the same variable as the emoji, so growing up
|
|
1129
|
+
// works identically either way.
|
|
1130
|
+
".dp-pig-img{width:var(--pig-size);height:var(--pig-size);display:block;",
|
|
1131
|
+
"-webkit-user-drag:none;user-select:none}",
|
|
1132
|
+
".dp-pig-emoji{font-size:var(--pig-size);line-height:1}",
|
|
1133
|
+
// No drawings yet — every stage is the same pig, so age reads as size plus
|
|
1134
|
+
// a faded coat on the last one.
|
|
1135
|
+
'[data-dsh-pig][data-faded="true"] .dp-pig-emoji{filter:grayscale(.5) opacity(.72)}',
|
|
1136
|
+
// The box advertises itself: a slow breathing glow plus a label, so it
|
|
1137
|
+
// does not read as scenery.
|
|
1138
|
+
'[data-dsh-pig][data-unhatched="true"] .dp-pig{cursor:pointer;',
|
|
1139
|
+
"animation:dp-box-breathe 2.4s ease-in-out infinite}",
|
|
1140
|
+
'[data-dsh-pig][data-unhatched="true"] .dp-pig-emoji{',
|
|
1141
|
+
"filter:drop-shadow(0 0 0 rgba(255,214,102,0)) drop-shadow(0 4px 6px rgba(61,52,40,.28))}",
|
|
1142
|
+
"@keyframes dp-box-breathe{0%,100%{transform:translateY(0) scale(1)}",
|
|
1143
|
+
"50%{transform:translateY(-3px) scale(1.06)}}",
|
|
1144
|
+
".dp-poke-hint{position:absolute;right:2px;bottom:-2px;display:flex;align-items:center;gap:3px;",
|
|
1145
|
+
"font-size:9.5px;font-weight:700;color:var(--ac-text);background:var(--ac-bg);",
|
|
1146
|
+
"border:1.5px solid var(--ac-border-light);border-radius:var(--ac-pill);padding:1px 7px;",
|
|
1147
|
+
"box-shadow:0 2px 0 rgba(61,52,40,.12);pointer-events:none;white-space:nowrap;z-index:3;",
|
|
1148
|
+
"animation:dp-hint-bob 1.6s ease-in-out infinite}",
|
|
1149
|
+
"@keyframes dp-hint-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}",
|
|
1150
|
+
// Each poke shakes it harder; the third one opens it instead.
|
|
1151
|
+
'[data-dsh-pig] .dp-pig[data-mood="poke"],',
|
|
1152
|
+
"[data-dsh-pig][data-poke] .dp-pig{animation-name:dp-poke-shake}",
|
|
1153
|
+
'[data-dsh-pig][data-poke="2"] .dp-pig{animation-duration:.28s}',
|
|
1154
|
+
"@keyframes dp-poke-shake{0%,100%{transform:rotate(0)}25%{transform:rotate(-7deg)}",
|
|
1155
|
+
"50%{transform:rotate(6deg)}75%{transform:rotate(-4deg)}}"
|
|
1156
|
+
// The shop's tiles live in css-tiles.js since B8.
|
|
1157
|
+
].join("");
|
|
1158
|
+
|
|
1159
|
+
// src/client/css-tabs.js
|
|
1160
|
+
var CSS_TABS = [
|
|
1161
|
+
/* ---------- developer tab ---------- */
|
|
1162
|
+
".dp-dev-note{font-size:10px;color:var(--ac-text-2);margin:4px 0 2px;line-height:1.5}",
|
|
1163
|
+
".dp-dev-row{display:flex;flex-wrap:wrap;gap:5px;margin:0 0 2px}",
|
|
1164
|
+
".dp-dev-btn{flex:0 0 auto;font-size:10px;padding:3px 8px}",
|
|
1165
|
+
".dp-on{background:var(--ac-primary);color:#fff;border-color:var(--ac-primary)}",
|
|
1166
|
+
'[data-dsh-pig][data-dev="true"] .dp-ico[data-tab="dev"]{color:var(--ac-primary)}',
|
|
1167
|
+
/* ---------- the soul that settles on an unclaimed grave ---------- */
|
|
1168
|
+
".dp-soul{position:absolute;left:50%;transform:translateX(-50%);top:-4px;font-size:22px;",
|
|
1169
|
+
"line-height:1;opacity:.9;pointer-events:none;z-index:1;",
|
|
1170
|
+
"animation:dp-haunt 3.4s ease-in-out infinite}",
|
|
1171
|
+
"@keyframes dp-haunt{0%,100%{transform:translate(-50%,0) scale(1);opacity:.75}",
|
|
1172
|
+
"50%{transform:translate(-50%,-9px) scale(1.08);opacity:1}}",
|
|
1173
|
+
// A grave does not bob about like a living pig.
|
|
1174
|
+
'.dp-pig[data-stage="grave"]{animation:none;filter:grayscale(.35) drop-shadow(0 4px 6px rgba(61,52,40,.3))}',
|
|
1175
|
+
'.dp-pig[data-stage="box"]{animation:dp-box-wobble 3.2s ease-in-out infinite}',
|
|
1176
|
+
"@keyframes dp-box-wobble{0%,100%{transform:rotate(0)}30%{transform:rotate(-4deg)}",
|
|
1177
|
+
"45%{transform:rotate(3deg)}60%{transform:rotate(-2deg)}}",
|
|
1178
|
+
// Patting squashes the pig flat. Short, so rapid clicking keeps up.
|
|
1179
|
+
'[data-dsh-pig] .dp-pig[data-react="pet"]{animation-name:dp-squash;animation-duration:.42s}',
|
|
1180
|
+
"@keyframes dp-squash{0%{transform:scale(1,1)}35%{transform:scale(1.16,.74) translateY(2px)}",
|
|
1181
|
+
"60%{transform:scale(.94,1.08) translateY(-3px)}100%{transform:scale(1,1)}}",
|
|
1182
|
+
/* ---------- speech bubble ---------- */
|
|
1183
|
+
// `z-index` matters: the pig comes later in the DOM, so without it the pig
|
|
1184
|
+
// paints over the bubble whenever the two boxes overlap — which is exactly
|
|
1185
|
+
// what happened when collapsed and the scene was only as wide as the pig.
|
|
1186
|
+
".dp-bubble{position:absolute;right:8px;top:7px;z-index:2;max-width:162px;padding:6px 10px;",
|
|
1187
|
+
"border-radius:var(--ac-radius-sm);font-size:10.5px;font-weight:600;line-height:1.45;",
|
|
1188
|
+
"color:var(--ac-text-body);background:var(--ac-bg-input);",
|
|
1189
|
+
"border:2px solid var(--ac-border-light);box-shadow:var(--ac-shadow-sm)}",
|
|
1190
|
+
// Tail drawn as a small rotated square so the 2px border stays continuous.
|
|
1191
|
+
'.dp-bubble::after{content:"";position:absolute;left:14px;bottom:-6px;width:8px;height:8px;',
|
|
1192
|
+
"background:var(--ac-bg-input);border-right:2px solid var(--ac-border-light);",
|
|
1193
|
+
"border-bottom:2px solid var(--ac-border-light);transform:rotate(45deg)}",
|
|
1194
|
+
// Reply buttons under a line: small pills, the mint of the primary colour
|
|
1195
|
+
// without the 3D base, which the spec keeps for real primary buttons.
|
|
1196
|
+
".dp-bubble-replies{display:flex;flex-wrap:wrap;gap:4px;margin-top:5px}",
|
|
1197
|
+
// 猪头上的日常气泡(签到 / 礼包):不用新颜色,沿用主色与卡片底色。
|
|
1198
|
+
// 挂在场景**上方**(不是 top 边缘):折叠时场景就是猪本身,用 top:-6px
|
|
1199
|
+
// 会让气泡叠在猪头上(用户反馈 #6)。
|
|
1200
|
+
".dp-daily{position:absolute;bottom:calc(100% + 7px);left:50%;width:36px;margin-left:-18px;",
|
|
1201
|
+
"font:inherit;font-size:15px;line-height:1;padding:3px 0;cursor:pointer;text-align:center;",
|
|
1202
|
+
"border:2px solid var(--ac-border);border-radius:50px;background:var(--ac-bg-input);",
|
|
1203
|
+
"box-shadow:0 3px 0 rgba(61,52,40,.14);animation:dp-daily-bob 2.4s var(--ac-ease) infinite}",
|
|
1204
|
+
// 折叠时场景就剩猪本身(而且它还在上下浮动 ±7px),再多让开一点。
|
|
1205
|
+
'[data-dsh-pig][data-open="false"] .dp-daily{bottom:calc(100% + 16px)}',
|
|
1206
|
+
// 展开时场景有面板那么宽、那么高,挂在场景上方会压到图标栏(B8 截图里压在「商店」上):
|
|
1207
|
+
// 改成蹲在猪左边、贴着猪身子(再高会碰到左边的名字框)。
|
|
1208
|
+
'[data-dsh-pig][data-open="true"] .dp-daily{left:auto;margin-left:0;',
|
|
1209
|
+
"right:calc(6px + var(--pig-size) + 10px);bottom:calc(var(--pig-gap-below) + 4px)}",
|
|
1210
|
+
".dp-daily:hover{border-color:var(--ac-border-hover)}",
|
|
1211
|
+
".dp-daily:focus-visible{outline:2px solid var(--ac-primary);outline-offset:1px}",
|
|
1212
|
+
// 名字必须独占:叫 dp-bob 会覆盖猪的待机动画(css-base.js),
|
|
1213
|
+
// 而那个动画的 transform 一被替掉,猪就会横跳半个身位。
|
|
1214
|
+
// 只上下浮:横向居中改用 margin,展开时才能挪到猪旁边。
|
|
1215
|
+
"@keyframes dp-daily-bob{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px)}}",
|
|
1216
|
+
// 日记:折叠时只有首句,展开是全文。
|
|
1217
|
+
".dp-diary{cursor:pointer}",
|
|
1218
|
+
'.dp-diary[data-open="true"] .dp-diary-full{display:block}',
|
|
1219
|
+
".dp-diary-full{margin-top:4px;line-height:1.5}",
|
|
1220
|
+
".dp-reply{font:inherit;font-size:10px;font-weight:700;padding:2px 9px;cursor:pointer;",
|
|
1221
|
+
"border-radius:var(--ac-pill);border:2px solid var(--ac-border-light);background:var(--ac-bg);",
|
|
1222
|
+
"color:var(--ac-text);transition:border-color .15s var(--ac-ease)}",
|
|
1223
|
+
".dp-reply:hover{border-color:var(--ac-border-hover)}",
|
|
1224
|
+
".dp-reply:focus-visible{outline:2px solid var(--ac-primary);outline-offset:1px}",
|
|
1225
|
+
// Collapsed, the scene is exactly the pig, so a bubble drawn inside it
|
|
1226
|
+
// would sit on the pig's face. Float it above the head with the tail
|
|
1227
|
+
// pointing down, anchored to the right edge so it can never run off the
|
|
1228
|
+
// window. The hearts rise from behind it.
|
|
1229
|
+
'[data-dsh-pig][data-open="false"] .dp-bubble{top:auto;bottom:calc(100% + 8px);',
|
|
1230
|
+
"left:auto;right:0;max-width:230px}",
|
|
1231
|
+
'[data-dsh-pig][data-open="false"] .dp-bubble::after{left:auto;right:26px;',
|
|
1232
|
+
"top:100%;bottom:auto;margin:0;transform:rotate(45deg);",
|
|
1233
|
+
"border:0;border-right:2px solid var(--ac-border-light);",
|
|
1234
|
+
"border-bottom:2px solid var(--ac-border-light)}",
|
|
1235
|
+
/* ---------- icon bar: the library sidebar, laid on its side ---------- */
|
|
1236
|
+
".dp-bar{display:grid;grid-template-columns:repeat(6,1fr);gap:4px;padding:8px;",
|
|
1237
|
+
"background:var(--ac-bg-content);border-top:2px solid var(--ac-border-light);",
|
|
1238
|
+
"border-bottom:2px solid var(--ac-border-light)}",
|
|
1239
|
+
".dp-ico{display:flex;flex-direction:column;align-items:center;gap:2px;cursor:pointer;",
|
|
1240
|
+
"font:inherit;font-size:9.5px;font-weight:600;color:var(--ac-text-muted);background:none;",
|
|
1241
|
+
"border:2px solid transparent;border-radius:var(--ac-radius-sm);padding:5px 1px;",
|
|
1242
|
+
"transition:all .2s var(--ac-ease)}",
|
|
1243
|
+
".dp-ico span.dp-ico-e{font-size:18px;line-height:1}",
|
|
1244
|
+
".dp-ico:hover{background:var(--ac-hover)}",
|
|
1245
|
+
'.dp-ico[data-active="true"]{background:var(--ac-active);border-color:#9db0d6;',
|
|
1246
|
+
"color:var(--ac-text);font-weight:700}",
|
|
1247
|
+
".dp-ico:focus-visible{outline:2px solid var(--ac-primary);outline-offset:1px}",
|
|
1248
|
+
"@keyframes dp-pulse{0%,100%{transform:scale(1)}50%{transform:scale(1.18)}}",
|
|
1249
|
+
'.dp-ico[data-alert="true"] span.dp-ico-e{animation:dp-pulse 1.4s ease-in-out infinite}',
|
|
1250
|
+
/* ---------- content ---------- */
|
|
1251
|
+
".dp-content{padding:12px 13px 13px;overflow-y:auto;flex:1 1 auto;min-height:0}",
|
|
1252
|
+
".dp-content::-webkit-scrollbar{width:8px}",
|
|
1253
|
+
".dp-content::-webkit-scrollbar-thumb{background:var(--ac-border-light);border-radius:4px}",
|
|
1254
|
+
".dp-content::-webkit-scrollbar-track{background:transparent}",
|
|
1255
|
+
".dp-title{display:flex;justify-content:space-between;align-items:baseline;font-size:11px;",
|
|
1256
|
+
"margin-bottom:8px}",
|
|
1257
|
+
".dp-title b{font-weight:700;color:var(--ac-text)}",
|
|
1258
|
+
".dp-title span{color:var(--ac-text-2);font-size:10.5px;font-weight:600}",
|
|
1259
|
+
".dp-row{display:flex;justify-content:space-between;font-size:11px;font-weight:600;",
|
|
1260
|
+
"color:var(--ac-text-body);margin:2px 0}",
|
|
1261
|
+
".dp-row b{font-weight:700;color:var(--ac-text)}",
|
|
1262
|
+
/* ---------- attribute bars: pill track with an inset well ---------- */
|
|
1263
|
+
".dp-meter{height:9px;border-radius:var(--ac-pill);background:var(--ac-bg-disabled);",
|
|
1264
|
+
"box-shadow:var(--ac-inset);overflow:hidden;margin:3px 0 8px}",
|
|
1265
|
+
".dp-meter i{display:block;height:100%;border-radius:var(--ac-pill);",
|
|
1266
|
+
"background:var(--ac-warning);transition:width .35s var(--ac-ease)}",
|
|
1267
|
+
".dp-meter.dp-mood i{background:#f8a6b2}",
|
|
1268
|
+
".dp-meter.dp-clean i{background:#82d5bb}",
|
|
1269
|
+
".dp-meter.dp-health i{background:#8ac68a}",
|
|
1270
|
+
".dp-traits{display:flex;gap:10px;font-size:10.5px;font-weight:600;color:var(--ac-text-2);",
|
|
1271
|
+
"margin:8px 0 3px}",
|
|
1272
|
+
/* ---------- banners ---------- */
|
|
1273
|
+
".dp-alert{margin:0 0 9px;padding:8px 10px;border-radius:var(--ac-radius-sm);",
|
|
1274
|
+
"font-size:10.5px;font-weight:600;line-height:1.55;border:2px solid}",
|
|
1275
|
+
".dp-alert b{font-weight:700;color:var(--ac-text)}",
|
|
1276
|
+
".dp-alert.dp-sick{background:#fdeeee;border-color:#f2c2c2}",
|
|
1277
|
+
".dp-alert.dp-work{background:#eef1fb;border-color:#c3cdf0}",
|
|
1278
|
+
".dp-alert.dp-dead{background:var(--ac-bg-disabled);border-color:var(--ac-border-light)}",
|
|
1279
|
+
".dp-alert.dp-legacy{background:#fdf7e2;border-color:#f0dfa8}",
|
|
1280
|
+
/* ---------- buttons: secondary is a cream pill with soft elevation ---- */
|
|
1281
|
+
".dp-actions{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:10px}",
|
|
1282
|
+
".dp-btn{display:flex;align-items:center;justify-content:center;gap:5px;font:inherit;",
|
|
1283
|
+
"font-size:11px;font-weight:700;letter-spacing:.02em;color:var(--ac-text-body);",
|
|
1284
|
+
"cursor:pointer;padding:8px 6px;border-radius:var(--ac-pill);",
|
|
1285
|
+
"border:2px solid var(--ac-border);background:var(--ac-bg-input);",
|
|
1286
|
+
"box-shadow:var(--ac-shadow-sm);transition:all .2s var(--ac-ease)}",
|
|
1287
|
+
".dp-btn:hover:not(:disabled){transform:translateY(-1px);box-shadow:var(--ac-shadow);",
|
|
1288
|
+
"border-color:var(--ac-border-hover)}",
|
|
1289
|
+
".dp-btn:active:not(:disabled){transform:translateY(2px);box-shadow:var(--ac-shadow-sm)}",
|
|
1290
|
+
".dp-btn:focus-visible{outline:2px solid var(--ac-primary);outline-offset:1px}",
|
|
1291
|
+
".dp-btn:disabled{background:var(--ac-bg-disabled);color:var(--ac-text-disabled);",
|
|
1292
|
+
"border-color:var(--ac-border-light);box-shadow:none;cursor:not-allowed}",
|
|
1293
|
+
".dp-btn-wide{grid-column:1/-1}",
|
|
1294
|
+
".dp-btn .dp-wait{color:var(--ac-text-2);font-size:10px;font-weight:600}",
|
|
1295
|
+
/* ---------- segmented control ---------- */
|
|
1296
|
+
".dp-seg{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin-bottom:9px}",
|
|
1297
|
+
".dp-seg button{font:inherit;font-size:10.5px;font-weight:600;color:var(--ac-text-muted);",
|
|
1298
|
+
"cursor:pointer;padding:6px 2px;border-radius:var(--ac-pill);",
|
|
1299
|
+
"border:2px solid var(--ac-border-light);background:var(--ac-bg-input);",
|
|
1300
|
+
// One line, always: a label that wraps makes its button taller than the rest.
|
|
1301
|
+
"white-space:nowrap;overflow:hidden;text-overflow:ellipsis;",
|
|
1302
|
+
"transition:all .2s var(--ac-ease)}",
|
|
1303
|
+
".dp-seg button:hover{background:var(--ac-hover)}",
|
|
1304
|
+
// The work tab has three skills, not four stages.
|
|
1305
|
+
".dp-seg.dp-seg-3{grid-template-columns:repeat(3,minmax(0,1fr))}",
|
|
1306
|
+
// Work rows: two small buttons on the right, 详情 opens the checklist below.
|
|
1307
|
+
".dp-job-locked{opacity:.75}",
|
|
1308
|
+
".dp-job-detail{margin-top:-2px}",
|
|
1309
|
+
".dp-req{font-size:10.5px;font-weight:600;color:var(--ac-error);line-height:1.6}",
|
|
1310
|
+
".dp-req.dp-req-ok{color:var(--ac-success)}",
|
|
1311
|
+
'.dp-seg button[data-active="true"]{background:var(--ac-active);border-color:#9db0d6;',
|
|
1312
|
+
"color:var(--ac-text);font-weight:700}",
|
|
1313
|
+
/* ---------- list rows ---------- */
|
|
1314
|
+
// minmax(0,1fr): a long nowrap line must ellipsize, not widen the panel.
|
|
1315
|
+
".dp-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:7px}",
|
|
1316
|
+
".dp-list{display:flex;flex-direction:column;gap:7px}",
|
|
1317
|
+
".dp-shelf{margin:9px 0 1px;font-size:10px;font-weight:700;color:var(--ac-text-2);",
|
|
1318
|
+
"letter-spacing:.04em}",
|
|
1319
|
+
".dp-shelf:first-child{margin-top:0}",
|
|
1320
|
+
".dp-item{display:flex;align-items:center;gap:8px;font-size:11px;font-weight:600;",
|
|
1321
|
+
"color:var(--ac-text-body);padding:7px 9px;border-radius:var(--ac-radius-sm);",
|
|
1322
|
+
"background:var(--ac-bg-content);border:2px solid var(--ac-border-light)}",
|
|
1323
|
+
".dp-item .dp-grow{flex:1;min-width:0}",
|
|
1324
|
+
".dp-item .dp-dim{color:var(--ac-text-2);font-size:10px;font-weight:500;overflow:hidden;",
|
|
1325
|
+
"text-overflow:ellipsis;white-space:nowrap}",
|
|
1326
|
+
".dp-item.dp-wanted{background:#fdf7e2;border-color:var(--ac-warning)}",
|
|
1327
|
+
// B6 talk row: name + 改 + 免打扰, and the inline name input.
|
|
1328
|
+
".dp-talk{gap:6px;margin-top:8px}",
|
|
1329
|
+
".dp-talk>span{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
1330
|
+
".dp-mini-plain{background:var(--ac-bg-input);color:var(--ac-text);border:2px solid var(--ac-border-light);box-shadow:none}",
|
|
1331
|
+
".dp-mini-plain:hover:not(:disabled){background:var(--ac-hover)}",
|
|
1332
|
+
".dp-input{flex:1;min-width:0;font:inherit;font-size:11px;padding:3px 8px;border-radius:var(--ac-pill);",
|
|
1333
|
+
"border:2px solid var(--ac-border);background:var(--ac-bg-input);color:var(--ac-text)}",
|
|
1334
|
+
".dp-input:focus{outline:2px solid var(--ac-primary);outline-offset:1px}",
|
|
1335
|
+
/* ---------- primary buttons: teal pill with the game 3D bottom edge --- */
|
|
1336
|
+
".dp-mini{font:inherit;font-size:10.5px;font-weight:700;letter-spacing:.02em;color:#fff;",
|
|
1337
|
+
"cursor:pointer;padding:6px 13px;border-radius:var(--ac-pill);",
|
|
1338
|
+
"border:2px solid var(--ac-primary-active);background:var(--ac-primary);",
|
|
1339
|
+
"box-shadow:0 3px 0 0 var(--ac-primary-active);transition:all .15s var(--ac-ease)}",
|
|
1340
|
+
".dp-mini:hover:not(:disabled){background:var(--ac-primary-hover);transform:translateY(-1px);",
|
|
1341
|
+
"box-shadow:0 4px 0 0 var(--ac-primary-active)}",
|
|
1342
|
+
".dp-mini:active:not(:disabled){transform:translateY(2px);",
|
|
1343
|
+
"box-shadow:0 1px 0 0 var(--ac-primary-active)}",
|
|
1344
|
+
".dp-mini:focus-visible{outline:2px solid var(--ac-primary);outline-offset:2px}",
|
|
1345
|
+
".dp-mini:disabled{background:var(--ac-bg-disabled);color:var(--ac-text-disabled);",
|
|
1346
|
+
"border-color:var(--ac-border-light);box-shadow:none;cursor:not-allowed}",
|
|
1347
|
+
/* ---------- the care item picker ---------- */
|
|
1348
|
+
".dp-pick{margin-top:9px;padding:9px 10px;border-radius:var(--ac-radius-sm);font-size:10.5px;",
|
|
1349
|
+
"background:var(--ac-bg-content);border:2px solid var(--ac-border-light)}",
|
|
1350
|
+
".dp-pick-head{font-size:10.5px;font-weight:700;color:var(--ac-text);margin-bottom:7px}",
|
|
1351
|
+
".dp-cancel{display:block;width:100%;margin-top:8px;font:inherit;font-size:10.5px;",
|
|
1352
|
+
"font-weight:600;color:var(--ac-text-2);cursor:pointer;padding:5px;",
|
|
1353
|
+
"border-radius:var(--ac-pill);border:2px solid var(--ac-border-light);",
|
|
1354
|
+
"background:var(--ac-bg-input);transition:all .2s var(--ac-ease)}",
|
|
1355
|
+
".dp-cancel:hover{background:var(--ac-hover);color:var(--ac-text)}",
|
|
1356
|
+
".dp-count{margin-left:2px;font-size:9px;font-weight:700;color:var(--ac-text-2);",
|
|
1357
|
+
"background:var(--ac-bg-content);border-radius:var(--ac-pill);padding:0 5px}",
|
|
1358
|
+
'.dp-btn[data-open-picker="true"]{background:var(--ac-active);border-color:#9db0d6}',
|
|
1359
|
+
'.dp-seg button[data-locked="true"]{color:var(--ac-text-disabled);',
|
|
1360
|
+
"border-style:dashed;background:var(--ac-bg-disabled)}",
|
|
1361
|
+
'.dp-seg button[data-locked="true"]:hover{background:var(--ac-bg-disabled)}',
|
|
1362
|
+
".dp-locked{margin:0 0 8px;font-size:10.5px;font-weight:600;line-height:1.5;",
|
|
1363
|
+
"color:var(--ac-text-body);background:#fdf7e2;border:2px solid #f0dfa8;",
|
|
1364
|
+
"border-radius:var(--ac-radius-sm);padding:6px 9px}",
|
|
1365
|
+
// The per-job gate reads as a lock, not as another grey stat line: a
|
|
1366
|
+
// threshold the pig cannot see is indistinguishable from a broken button.
|
|
1367
|
+
".dp-lock{font-size:10px;font-weight:700;line-height:1.5;color:#9a6b1f}",
|
|
1368
|
+
".dp-empty{color:var(--ac-text-2);font-size:10.5px;font-weight:500;line-height:1.65;",
|
|
1369
|
+
"margin-top:4px}",
|
|
1370
|
+
".dp-memo{margin-top:9px;padding-top:8px;border-top:2px solid var(--ac-border-light);",
|
|
1371
|
+
"color:var(--ac-text-muted);font-size:10px;font-weight:500;line-height:1.55;",
|
|
1372
|
+
"white-space:pre-wrap;word-break:break-word}",
|
|
1373
|
+
/* ---------- particles and toast ---------- */
|
|
1374
|
+
".dp-fx{position:absolute;z-index:1;pointer-events:none;font-size:17px;",
|
|
1375
|
+
"animation:dp-rise 1.1s ease-out forwards}",
|
|
1376
|
+
"@keyframes dp-rise{0%{opacity:0;transform:translate(var(--dx0,0),4px) scale(.5)}18%{opacity:1}",
|
|
1377
|
+
"100%{opacity:0;transform:translate(var(--dx,0),-56px) scale(1.15)}}",
|
|
1378
|
+
".dp-toast{position:absolute;left:9px;right:9px;top:8px;padding:8px 11px;",
|
|
1379
|
+
"border-radius:var(--ac-radius-sm);font-size:10.5px;font-weight:600;line-height:1.5;",
|
|
1380
|
+
"color:var(--ac-text);background:var(--ac-bg-input);border:2px solid var(--ac-border);",
|
|
1381
|
+
"box-shadow:var(--ac-shadow);pointer-events:none;white-space:normal;",
|
|
1382
|
+
"animation:dp-toast 4.6s var(--ac-ease) forwards}",
|
|
1383
|
+
"@keyframes dp-toast{0%{opacity:0;transform:translateY(-8px)}8%{opacity:1;transform:translateY(0)}",
|
|
1384
|
+
"82%{opacity:1}100%{opacity:0;transform:translateY(-6px)}}"
|
|
1385
|
+
].join("");
|
|
1386
|
+
|
|
1387
|
+
// src/client/css-tiles.js
|
|
1388
|
+
var CSS_TILES = [
|
|
1389
|
+
"[data-dsh-pig]{--tile-pink:#f8a6b2;--tile-purple:#b77dee;--tile-blue:#889df0;",
|
|
1390
|
+
"--tile-yellow:#f7cd67;--tile-orange:#e59266;--tile-teal:#82d5bb;--tile-green:#8ac68a;",
|
|
1391
|
+
"--tile-red:#fc736d;--tile-lime:#d1da49;--tile-peach:#e18c6f;--tile-brown:#9a835a}",
|
|
1392
|
+
'.dp-tile[data-color="pink"]{--tile-c:var(--tile-pink)}',
|
|
1393
|
+
'.dp-tile[data-color="purple"]{--tile-c:var(--tile-purple)}',
|
|
1394
|
+
'.dp-tile[data-color="blue"]{--tile-c:var(--tile-blue)}',
|
|
1395
|
+
'.dp-tile[data-color="yellow"]{--tile-c:var(--tile-yellow)}',
|
|
1396
|
+
'.dp-tile[data-color="orange"]{--tile-c:var(--tile-orange)}',
|
|
1397
|
+
'.dp-tile[data-color="teal"]{--tile-c:var(--tile-teal)}',
|
|
1398
|
+
'.dp-tile[data-color="green"]{--tile-c:var(--tile-green)}',
|
|
1399
|
+
'.dp-tile[data-color="red"]{--tile-c:var(--tile-red)}',
|
|
1400
|
+
'.dp-tile[data-color="lime"]{--tile-c:var(--tile-lime)}',
|
|
1401
|
+
'.dp-tile[data-color="peach"]{--tile-c:var(--tile-peach)}',
|
|
1402
|
+
'.dp-tile[data-color="brown"]{--tile-c:var(--tile-brown)}',
|
|
1403
|
+
// The grid: three columns that can never be widened by their content.
|
|
1404
|
+
".dp-tiles{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:10px 8px;padding:4px 2px 2px}",
|
|
1405
|
+
// A tile is a column: the coloured square, then its name, then a note.
|
|
1406
|
+
".dp-tile{font:inherit;display:flex;flex-direction:column;align-items:center;gap:4px;min-width:0;",
|
|
1407
|
+
"padding:0;margin:0;border:0;background:none;cursor:pointer;color:var(--ac-text)}",
|
|
1408
|
+
".dp-tile-icon{position:relative;display:flex;align-items:center;justify-content:center;",
|
|
1409
|
+
"width:50px;height:50px;border-radius:15px;background:var(--tile-c,var(--ac-bg-content));",
|
|
1410
|
+
"box-shadow:0 3px 0 rgba(61,52,40,.16);transition:transform .15s var(--ac-ease),box-shadow .15s var(--ac-ease)}",
|
|
1411
|
+
".dp-tile-e{font-size:24px;line-height:1;filter:drop-shadow(0 1px 1px rgba(61,52,40,.18))}",
|
|
1412
|
+
".dp-tile:hover:not(:disabled) .dp-tile-icon{transform:translateY(-2px);box-shadow:0 5px 0 rgba(61,52,40,.16)}",
|
|
1413
|
+
".dp-tile:active:not(:disabled) .dp-tile-icon{transform:translateY(2px);box-shadow:0 1px 0 rgba(61,52,40,.16)}",
|
|
1414
|
+
".dp-tile:focus-visible{outline:none}",
|
|
1415
|
+
".dp-tile:focus-visible .dp-tile-icon{outline:2px solid var(--ac-primary);outline-offset:2px}",
|
|
1416
|
+
// One line each, never wrapping: every tile in a row stays the same height.
|
|
1417
|
+
".dp-tile-n,.dp-tile-note{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.25}",
|
|
1418
|
+
".dp-tile-n{font-size:10.5px;font-weight:700}",
|
|
1419
|
+
".dp-tile-note{font-size:9.5px;font-weight:600;color:var(--ac-text-2);margin-top:-2px}",
|
|
1420
|
+
// Corner marks on the square: a count top-right, a word top-left.
|
|
1421
|
+
".dp-tile-badge,.dp-tile-tag{position:absolute;top:-5px;font-size:9px;font-weight:800;line-height:1;",
|
|
1422
|
+
"padding:3px 5px;border-radius:var(--ac-pill);white-space:nowrap;border:2px solid var(--ac-bg)}",
|
|
1423
|
+
".dp-tile-badge{right:-6px;background:var(--ac-primary);color:#fff}",
|
|
1424
|
+
".dp-tile-tag{left:-6px;background:var(--ac-warning);color:var(--ac-text)}",
|
|
1425
|
+
// Second layer: the same colour, a shade paler and a little smaller.
|
|
1426
|
+
// Sizes trimmed on 2026-10-01 (owner: the tiles were too big): 50px / 44px.
|
|
1427
|
+
".dp-tile-soft .dp-tile-icon{width:44px;height:44px;border-radius:13px;",
|
|
1428
|
+
"background:color-mix(in srgb,var(--tile-c) 42%,#fffbe7)}",
|
|
1429
|
+
".dp-tile-soft .dp-tile-e{font-size:21px}",
|
|
1430
|
+
// Locked: greyed but still openable (a stage can be looked into before it opens).
|
|
1431
|
+
'.dp-tile[data-locked="true"] .dp-tile-icon{filter:grayscale(.75);opacity:.6}',
|
|
1432
|
+
'.dp-tile[data-dim="true"] .dp-tile-icon,.dp-tile:disabled .dp-tile-icon{opacity:.45;box-shadow:none}',
|
|
1433
|
+
'.dp-tile[data-dim="true"] .dp-tile-n,.dp-tile:disabled .dp-tile-n{color:var(--ac-text-2)}',
|
|
1434
|
+
".dp-tile:disabled{cursor:default}",
|
|
1435
|
+
'.dp-tile[data-active="true"] .dp-tile-icon{outline:3px solid var(--ac-active);outline-offset:2px}',
|
|
1436
|
+
// The second layer's top row: back, title, one grey line.
|
|
1437
|
+
".dp-drill{display:flex;align-items:center;gap:7px;margin:0 0 10px}",
|
|
1438
|
+
".dp-drill-back{font:inherit;font-size:16px;font-weight:800;line-height:1;width:26px;height:26px;",
|
|
1439
|
+
"flex:none;cursor:pointer;color:var(--ac-text);border-radius:50%;",
|
|
1440
|
+
"border:2px solid var(--ac-border-light);background:var(--ac-bg-input)}",
|
|
1441
|
+
".dp-drill-back:hover{border-color:var(--ac-border-hover)}",
|
|
1442
|
+
".dp-drill-title{font-size:12px;font-weight:800;color:var(--ac-text);white-space:nowrap}",
|
|
1443
|
+
".dp-drill-info{flex:1;min-width:0;text-align:right;font-size:10px;font-weight:600;",
|
|
1444
|
+
"color:var(--ac-text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
1445
|
+
// B9: the home screen replaces the bottom icon bar. The bar still exists
|
|
1446
|
+
// (its icons carry the alert state the home tiles read) but is not shown.
|
|
1447
|
+
"[data-dsh-pig] .dp-card .dp-bar{display:none}",
|
|
1448
|
+
".dp-app-head{margin-bottom:12px}",
|
|
1449
|
+
// Out working, the collapsed scene shrinks to pig + prop; open, it must stay
|
|
1450
|
+
// as wide as the panel, or the name plate is squeezed onto the pig.
|
|
1451
|
+
'[data-dsh-pig][data-open="true"][data-away] .dp-scene{width:var(--panel-width)}',
|
|
1452
|
+
// Banners only live on the status tab now, with room to breathe below.
|
|
1453
|
+
"[data-dsh-pig] .dp-alert{margin-bottom:14px}",
|
|
1454
|
+
"[data-dsh-pig] .dp-alert + .dp-actions{margin-bottom:14px}",
|
|
1455
|
+
".dp-job-go{display:block;width:100%;margin-top:9px}",
|
|
1456
|
+
// A picked tile's details (a diary page, a souvenir's story) sit under the grid.
|
|
1457
|
+
".dp-tile-card{margin-top:12px}",
|
|
1458
|
+
// 更新 App: the release notes keep their line breaks but stay short.
|
|
1459
|
+
".dp-update-notes{white-space:pre-wrap;font-size:10.5px;line-height:1.5;color:var(--ac-text-2);max-height:120px;overflow:auto;margin:4px 0 6px}",
|
|
1460
|
+
".dp-update-back{margin-top:10px;width:100%}",
|
|
1461
|
+
".dp-update-now{margin-bottom:12px}",
|
|
1462
|
+
".dp-update-now .dp-btn,.dp-update-detail .dp-btn{width:100%;margin-top:8px}"
|
|
1463
|
+
].join("");
|
|
1464
|
+
|
|
1465
|
+
// src/client/css-card.js
|
|
1466
|
+
var CSS_CARD = [
|
|
1467
|
+
".dp-vcard{position:relative;padding:14px 14px 12px;border-radius:20px;color:var(--ac-text-body);",
|
|
1468
|
+
"--vc-dot:rgba(196,184,158,.15);--vc-dot2:rgba(196,184,158,.1);--vc-bg:rgb(247,243,223);--vc-line:#d4c4a8;",
|
|
1469
|
+
"background:radial-gradient(circle,var(--vc-dot) 1.5px,transparent 1.5px),",
|
|
1470
|
+
"radial-gradient(circle,var(--vc-dot2) 1px,transparent 1px),var(--vc-bg);",
|
|
1471
|
+
"background-size:28px 28px,14px 14px;background-position:0 0,7px 7px;border:1.5px solid var(--vc-line)}",
|
|
1472
|
+
'.dp-vcard[data-sex="girl"]{--vc-dot:rgba(248,166,178,.18);--vc-dot2:rgba(255,200,210,.12);--vc-bg:#fde4e8;--vc-line:#f8a6b2}',
|
|
1473
|
+
'.dp-vcard[data-sex="boy"]{--vc-dot:rgba(136,157,240,.18);--vc-dot2:rgba(180,195,255,.12);--vc-bg:#e8edff;--vc-line:#889df0}',
|
|
1474
|
+
// Top: the photo and who it is.
|
|
1475
|
+
".dp-vcard-top{display:flex;align-items:center;gap:12px;margin-bottom:12px}",
|
|
1476
|
+
".dp-vcard-avatar{flex:none;width:72px;height:72px;border-radius:18px;display:flex;align-items:center;",
|
|
1477
|
+
"justify-content:center;background:#fffbe7;border:2px solid var(--vc-line);box-shadow:0 3px 0 rgba(61,52,40,.12)}",
|
|
1478
|
+
".dp-vcard-img{width:56px;height:56px;display:block}",
|
|
1479
|
+
".dp-vcard-e{font-size:40px;line-height:1}",
|
|
1480
|
+
".dp-vcard-who{display:flex;flex-direction:column;gap:3px;min-width:0}",
|
|
1481
|
+
".dp-vcard-name{font-size:15px;font-weight:800;color:var(--ac-text);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
1482
|
+
".dp-vcard-sub{font-size:10.5px;font-weight:600;color:var(--ac-text-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
1483
|
+
// 「标签:值」 rows.
|
|
1484
|
+
".dp-vcard-row{display:flex;align-items:center;gap:6px;margin-top:7px;min-width:0}",
|
|
1485
|
+
".dp-vcard-label{flex:none;width:44px;font-size:10.5px;font-weight:700;color:var(--ac-text)}",
|
|
1486
|
+
".dp-vcard-value{flex:1;min-width:0;padding:5px 11px;border-radius:var(--ac-pill);background:#faf8f2;",
|
|
1487
|
+
"font-size:11px;font-weight:600;color:var(--ac-text-body);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
1488
|
+
// The motto is the pig talking: a bubble that may take two lines.
|
|
1489
|
+
".dp-vcard-motto-row{align-items:flex-start}",
|
|
1490
|
+
".dp-vcard-motto-row .dp-vcard-label{margin-top:6px}",
|
|
1491
|
+
".dp-vcard-value.dp-vcard-motto{border-radius:12px;white-space:normal;line-height:1.45}",
|
|
1492
|
+
".dp-vcard-edit{flex:none;font:inherit;font-size:12px;line-height:1;width:24px;height:24px;padding:0;cursor:pointer;",
|
|
1493
|
+
"border-radius:50%;border:2px solid var(--vc-line);background:#fffbe7}",
|
|
1494
|
+
".dp-vcard-edit:hover{background:var(--ac-hover)}",
|
|
1495
|
+
".dp-vcard-input{flex:1;min-width:0;padding:3px 9px;font-size:11px}",
|
|
1496
|
+
// In-place editing: the two buttons stay as small as the pencil they replace.
|
|
1497
|
+
".dp-vcard-row .dp-mini{flex:none;padding:3px 9px;font-size:10px;box-shadow:none}",
|
|
1498
|
+
"[data-dsh-pig] .dp-vcard-row .dp-mini.dp-mini-plain{background:#fffbe7;color:var(--ac-text);border:2px solid var(--vc-line);box-shadow:none}",
|
|
1499
|
+
".dp-vcard-foot{margin-top:12px;padding-top:9px;border-top:1.5px dashed var(--vc-line);",
|
|
1500
|
+
"font-size:10px;font-weight:600;color:var(--ac-text-2);text-align:center;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
|
|
1501
|
+
// 加冕 App: one cream box per form, its picture on the left, conditions as small chips.
|
|
1502
|
+
".dp-crown{margin-bottom:10px;padding:10px 12px;border-radius:16px;background:#fffbe7;border:2px dashed #e8c66a}",
|
|
1503
|
+
".dp-crown.dp-crown-now{border-style:solid;background:#fdf3d0}",
|
|
1504
|
+
".dp-crown-top{display:flex;gap:10px;align-items:flex-start}",
|
|
1505
|
+
".dp-crown-pic{flex:none;width:58px;height:58px;border-radius:14px;display:flex;align-items:center;justify-content:center;",
|
|
1506
|
+
"background:#fff;border:2px solid #f0dca0;font-size:30px}",
|
|
1507
|
+
".dp-crown-img{width:50px;height:50px;display:block}",
|
|
1508
|
+
".dp-crown-side{flex:1;min-width:0}",
|
|
1509
|
+
".dp-crown-head{font-size:12px;font-weight:800;color:var(--ac-text);margin-bottom:6px}",
|
|
1510
|
+
".dp-crown-done{font-size:11px;font-weight:700;color:#3f8a62}",
|
|
1511
|
+
".dp-crown-reqs{display:flex;flex-wrap:wrap;gap:4px}",
|
|
1512
|
+
".dp-crown-req{padding:2px 7px;border-radius:var(--ac-pill);font-size:10px;font-weight:700;white-space:nowrap;",
|
|
1513
|
+
"background:#f3ece0;color:var(--ac-text-2)}",
|
|
1514
|
+
".dp-crown-req.dp-crown-ok{background:#dff3e8;color:#3f8a62}",
|
|
1515
|
+
".dp-crown .dp-btn{width:100%;margin-top:9px}"
|
|
1516
|
+
].join("");
|
|
1517
|
+
|
|
1518
|
+
// src/client/styles.js
|
|
1519
|
+
var CSS = CSS_BASE + CSS_TABS + CSS_TILES + CSS_CARD;
|
|
1520
|
+
|
|
1521
|
+
// src/client/tabs/travel.js
|
|
1522
|
+
function renderTravelTab(ui) {
|
|
1523
|
+
if (ui.view.trips.length === 0) {
|
|
1524
|
+
ui.content.appendChild(el("div", "dp-empty", "\u5BBF\u4E3B\u8FD8\u6CA1\u63D0\u4F9B\u76EE\u7684\u5730\u3002"));
|
|
1525
|
+
return;
|
|
1526
|
+
}
|
|
1527
|
+
var list = el("div", "dp-list");
|
|
1528
|
+
for (var i = 0; i < ui.view.trips.length; i += 1) {
|
|
1529
|
+
(function(trip) {
|
|
1530
|
+
var row = el("div", "dp-item");
|
|
1531
|
+
row.appendChild(el("span", null, trip.emoji));
|
|
1532
|
+
var grow = el("div", "dp-grow");
|
|
1533
|
+
grow.appendChild(el("div", null, trip.label));
|
|
1534
|
+
grow.appendChild(el("div", "dp-dim", formatMinutes(trip.minutes) + " \xB7 " + trip.cost + " \u{1FA99}" + (trip.bestRarity ? " \xB7 \u53EF\u5E26\u56DE " + trip.bestRarityEmoji + trip.bestRarity : "")));
|
|
1535
|
+
row.appendChild(grow);
|
|
1536
|
+
var go = button("dp-mini", { "data-trip": trip.key }, function() {
|
|
1537
|
+
ui.send("trip", { trip: trip.key });
|
|
1538
|
+
});
|
|
1539
|
+
go.textContent = "\u51FA\u53D1";
|
|
1540
|
+
go.disabled = !ui.view.canGoOut || !trip.affordable;
|
|
1541
|
+
row.appendChild(go);
|
|
1542
|
+
list.appendChild(row);
|
|
1543
|
+
})(ui.view.trips[i]);
|
|
1544
|
+
}
|
|
1545
|
+
ui.content.appendChild(list);
|
|
1546
|
+
var souvenirs = ui.view.pig.souvenirs;
|
|
1547
|
+
var head = el("div", "dp-title");
|
|
1548
|
+
head.style.marginTop = "10px";
|
|
1549
|
+
head.appendChild(el("b", null, "\u{1F381} \u7EAA\u5FF5\u54C1 " + souvenirs.length));
|
|
1550
|
+
ui.content.appendChild(head);
|
|
1551
|
+
if (souvenirs.length === 0) {
|
|
1552
|
+
ui.content.appendChild(el("div", "dp-empty", "\u8FD8\u6CA1\u51FA\u8FC7\u8FDC\u95E8\u3002"));
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
var chips = el("div", "dp-grid");
|
|
1556
|
+
for (var s = 0; s < souvenirs.length; s += 1) {
|
|
1557
|
+
(function(entry) {
|
|
1558
|
+
var chip = button("dp-item", { "data-souvenir": entry.key }, function() {
|
|
1559
|
+
ui.souvenirPick = ui.souvenirPick === entry.key ? null : entry.key;
|
|
1560
|
+
ui.renderContent();
|
|
1561
|
+
});
|
|
1562
|
+
chip.appendChild(el("span", null, entry.emoji));
|
|
1563
|
+
var grow = el("div", "dp-grow");
|
|
1564
|
+
grow.appendChild(el("div", null, entry.label));
|
|
1565
|
+
grow.appendChild(el("div", "dp-dim", entry.rarityEmoji + entry.rarityLabel + (entry.price > 0 ? " \xB7 \u503C " + entry.price + " \u{1FA99}" : "")));
|
|
1566
|
+
chip.appendChild(grow);
|
|
1567
|
+
chips.appendChild(chip);
|
|
1568
|
+
})(souvenirs[s]);
|
|
1569
|
+
}
|
|
1570
|
+
ui.content.appendChild(chips);
|
|
1571
|
+
var picked = null;
|
|
1572
|
+
for (var q = 0; q < souvenirs.length; q += 1) if (souvenirs[q].key === ui.souvenirPick) picked = souvenirs[q];
|
|
1573
|
+
if (picked !== null) {
|
|
1574
|
+
var souvenirCard = el("div", "dp-locked");
|
|
1575
|
+
souvenirCard.appendChild(el("div", null, picked.emoji + " " + picked.label + " \xB7 " + picked.rarityEmoji + picked.rarityLabel + (picked.fromLabel === "" ? "" : " \xB7 \u6765\u81EA" + picked.fromLabel)));
|
|
1576
|
+
souvenirCard.appendChild(el("div", null, picked.story === "" ? "\uFF08\u8FD9\u53EA\u7EAA\u5FF5\u54C1\u662F\u65E7\u7248\u672C\u5E26\u56DE\u6765\u7684\uFF0C\u6CA1\u6709\u7559\u4E0B\u6545\u4E8B\u3002\uFF09" : "\u300C" + picked.story + "\u300D"));
|
|
1577
|
+
if (picked.price > 0) {
|
|
1578
|
+
var sell = button("dp-mini", { "data-sell": picked.key }, function() {
|
|
1579
|
+
ui.souvenirPick = null;
|
|
1580
|
+
ui.send("sell", { souvenir: picked.key });
|
|
1581
|
+
});
|
|
1582
|
+
sell.textContent = "\u5356\u6389 +" + picked.price + " \u{1FA99}";
|
|
1583
|
+
sell.style.marginTop = "6px";
|
|
1584
|
+
souvenirCard.appendChild(sell);
|
|
1585
|
+
}
|
|
1586
|
+
ui.content.appendChild(souvenirCard);
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
// src/client/tabs/work.js
|
|
1591
|
+
var SKILLS = [
|
|
1592
|
+
{ key: "strong", emoji: "\u{1F4AA}", label: "\u6B66\u529B", color: "orange" },
|
|
1593
|
+
{ key: "charm", emoji: "\u2728", label: "\u9B45\u529B", color: "pink" },
|
|
1594
|
+
{ key: "intel", emoji: "\u{1F9E0}", label: "\u667A\u529B", color: "blue" }
|
|
1595
|
+
];
|
|
1596
|
+
function renderWorkTab(ui) {
|
|
1597
|
+
if (ui.view.jobs.length === 0) {
|
|
1598
|
+
ui.content.appendChild(el("div", "dp-empty", "\u5BBF\u4E3B\u8FD8\u6CA1\u63D0\u4F9B\u5DE5\u4F5C\u5217\u8868\u3002"));
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
var bySkill = ui.view.jobs.some(function(job) {
|
|
1602
|
+
return job.trait !== "";
|
|
1603
|
+
});
|
|
1604
|
+
if (!bySkill) {
|
|
1605
|
+
renderJobs(ui, ui.view.jobs, "orange");
|
|
1606
|
+
return;
|
|
1607
|
+
}
|
|
1608
|
+
var skill = SKILLS.find(function(entry) {
|
|
1609
|
+
return entry.key === ui.drill.work;
|
|
1610
|
+
});
|
|
1611
|
+
if (skill === void 0) {
|
|
1612
|
+
renderSkills(ui);
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
var chosen = skill;
|
|
1616
|
+
drillHeader(ui, "work", chosen.emoji + " " + chosen.label, "");
|
|
1617
|
+
renderJobs(ui, ui.view.jobs.filter(function(job) {
|
|
1618
|
+
return job.trait === chosen.key;
|
|
1619
|
+
}), chosen.color);
|
|
1620
|
+
}
|
|
1621
|
+
function renderSkills(ui) {
|
|
1622
|
+
var grid = tileGrid();
|
|
1623
|
+
for (var s = 0; s < SKILLS.length; s += 1) {
|
|
1624
|
+
(function(skill) {
|
|
1625
|
+
var open = ui.view.jobs.filter(function(job) {
|
|
1626
|
+
return job.trait === skill.key && job.qualified;
|
|
1627
|
+
}).length;
|
|
1628
|
+
grid.appendChild(tile({
|
|
1629
|
+
emoji: skill.emoji,
|
|
1630
|
+
label: skill.label,
|
|
1631
|
+
color: skill.color,
|
|
1632
|
+
badge: open > 0 ? String(open) : "",
|
|
1633
|
+
data: { "data-skill": skill.key },
|
|
1634
|
+
onPick: function() {
|
|
1635
|
+
drillTo(ui, "work", skill.key);
|
|
1636
|
+
}
|
|
1637
|
+
}));
|
|
1638
|
+
})(SKILLS[s]);
|
|
1639
|
+
}
|
|
1640
|
+
ui.content.appendChild(grid);
|
|
1641
|
+
}
|
|
1642
|
+
function renderJobs(ui, jobs, color) {
|
|
1643
|
+
var grid = tileGrid();
|
|
1644
|
+
var picked = null;
|
|
1645
|
+
for (var i = 0; i < jobs.length; i += 1) {
|
|
1646
|
+
(function(job) {
|
|
1647
|
+
var active = ui.drill.pick === job.key;
|
|
1648
|
+
if (active) picked = job;
|
|
1649
|
+
var locked = job.qualified === false;
|
|
1650
|
+
grid.appendChild(tile({
|
|
1651
|
+
emoji: job.emoji,
|
|
1652
|
+
label: job.label,
|
|
1653
|
+
color,
|
|
1654
|
+
soft: true,
|
|
1655
|
+
active,
|
|
1656
|
+
note: job.minutes + "\u5206\xB7" + job.coins + "\u{1FA99}",
|
|
1657
|
+
tag: locked ? "\u{1F512}" : "",
|
|
1658
|
+
dim: locked,
|
|
1659
|
+
data: { "data-job-tile": job.key },
|
|
1660
|
+
onPick: function() {
|
|
1661
|
+
ui.drill.pick = active ? null : job.key;
|
|
1662
|
+
ui.renderContent();
|
|
1663
|
+
}
|
|
1664
|
+
}));
|
|
1665
|
+
})(jobs[i]);
|
|
1666
|
+
}
|
|
1667
|
+
ui.content.appendChild(grid);
|
|
1668
|
+
if (picked !== null) ui.content.appendChild(jobDetails(ui, picked));
|
|
1669
|
+
}
|
|
1670
|
+
function jobDetails(ui, job) {
|
|
1671
|
+
var box = el("div", "dp-pick dp-tile-card dp-job-detail");
|
|
1672
|
+
box.appendChild(el("div", "dp-pick-head", job.emoji + " " + job.label + " \xB7 " + (job.qualified ? "\u6761\u4EF6\u90FD\u591F\u4E86" : "\u8FD8\u5DEE\u8FD9\u4E9B")));
|
|
1673
|
+
for (var r = 0; r < job.requirements.length; r += 1) {
|
|
1674
|
+
var need = job.requirements[r];
|
|
1675
|
+
var have = need.kind === "level" ? "\uFF08\u73B0\u5728 Lv." + need.have + "\uFF09" : need.kind === "certificate" ? "\uFF08" + need.have + "/" + need.need + " \u6B21\uFF09" : need.kind === "every" || need.kind === "anyOf" ? "\uFF08" + need.have + "/" + need.need + " \u95E8\uFF09" : "\uFF08\u73B0\u5728 " + need.have + " \u8282\uFF09";
|
|
1676
|
+
box.appendChild(el("div", need.met ? "dp-req dp-req-ok" : "dp-req", (need.met ? "\u2713 " : "\u2717 ") + need.text + (need.met ? "" : " " + have)));
|
|
1677
|
+
}
|
|
1678
|
+
if (job.requirements.length === 0 && job.lockText) box.appendChild(el("div", "dp-req", "\u2717 " + job.lockText));
|
|
1679
|
+
box.appendChild(el("div", "dp-dim", job.minutes + " \u5206\u949F \xB7 " + job.coins + " \u{1FA99} \xB7 " + job.traitEmoji + job.traitLabel + " " + job.traitPoints + (job.payPercent > 0 ? "\uFF08+" + job.payPercent + "%\uFF09" : "") + " \xB7 \u9971\u98DF " + job.satiety + " \xB7 \u6E05\u6D01 " + job.cleanliness));
|
|
1680
|
+
var go = button("dp-btn dp-btn-wide dp-job-go", { "data-job": job.key }, function() {
|
|
1681
|
+
ui.send("work", { job: job.key });
|
|
1682
|
+
});
|
|
1683
|
+
go.textContent = "\u{1F4BC} \u51FA\u53D1";
|
|
1684
|
+
go.disabled = !ui.view.canGoOut || job.qualified === false;
|
|
1685
|
+
box.appendChild(go);
|
|
1686
|
+
return box;
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
// src/client/art.js
|
|
1690
|
+
var REACTION_ART = { feed: "eat", bathe: "bathe", play: "play", pet: "pet", cure: "relaxed", levelup: "relaxed" };
|
|
1691
|
+
var ACTIVITY_ART = { work: "work", study: "study", interest: "study", trip: "trip" };
|
|
1692
|
+
function syncPigArt(pig, image) {
|
|
1693
|
+
var base = pig.getAttribute("data-art");
|
|
1694
|
+
if (!base) return;
|
|
1695
|
+
var art = base;
|
|
1696
|
+
if (pig.getAttribute("data-art-actions") === "true") {
|
|
1697
|
+
var action = REACTION_ART[pig.getAttribute("data-react")] || ACTIVITY_ART[pig.getAttribute("data-activity")];
|
|
1698
|
+
if (action) art += "-" + action;
|
|
1699
|
+
}
|
|
1700
|
+
var src = ART_URL + art + ".svg";
|
|
1701
|
+
if (image.getAttribute("src") !== src) image.src = src;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
// src/client/effects.js
|
|
1705
|
+
function createEffects(deps) {
|
|
1706
|
+
var scene = deps.scene;
|
|
1707
|
+
var pig = deps.pig;
|
|
1708
|
+
var card = deps.card;
|
|
1709
|
+
var bubble = deps.bubble;
|
|
1710
|
+
var isStopped = deps.isStopped;
|
|
1711
|
+
var reactTimer = null;
|
|
1712
|
+
var bubbleTimer = null;
|
|
1713
|
+
function react(kind, ms) {
|
|
1714
|
+
if (reactTimer !== null) window.clearTimeout(reactTimer);
|
|
1715
|
+
pig.removeAttribute("data-react");
|
|
1716
|
+
void pig.offsetWidth;
|
|
1717
|
+
pig.setAttribute("data-react", kind);
|
|
1718
|
+
syncPigArt(pig, deps.pigArt);
|
|
1719
|
+
reactTimer = window.setTimeout(function() {
|
|
1720
|
+
pig.removeAttribute("data-react");
|
|
1721
|
+
syncPigArt(pig, deps.pigArt);
|
|
1722
|
+
reactTimer = null;
|
|
1723
|
+
}, ms || 900);
|
|
1724
|
+
}
|
|
1725
|
+
function burst(emojis, count) {
|
|
1726
|
+
for (var i = 0; i < (count || 1); i += 1) {
|
|
1727
|
+
(function(index) {
|
|
1728
|
+
window.setTimeout(function() {
|
|
1729
|
+
if (isStopped()) return;
|
|
1730
|
+
var node = el("span", "dp-fx", emojis[index % emojis.length]);
|
|
1731
|
+
node.style.setProperty("--dx", Math.round((Math.random() - 0.5) * 46) + "px");
|
|
1732
|
+
var spot = headSpot();
|
|
1733
|
+
node.style.left = spot.x + Math.round((Math.random() - 0.5) * 22) + "px";
|
|
1734
|
+
node.style.top = spot.y + "px";
|
|
1735
|
+
scene.appendChild(node);
|
|
1736
|
+
window.setTimeout(function() {
|
|
1737
|
+
node.remove();
|
|
1738
|
+
}, 1200);
|
|
1739
|
+
}, index * 110);
|
|
1740
|
+
})(i);
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
function headSpot() {
|
|
1744
|
+
var fallback = { x: 24, y: 8 };
|
|
1745
|
+
if (typeof pig.getBoundingClientRect !== "function" || typeof scene.getBoundingClientRect !== "function") return fallback;
|
|
1746
|
+
var p = pig.getBoundingClientRect();
|
|
1747
|
+
var s = scene.getBoundingClientRect();
|
|
1748
|
+
if (p.width === 0 && p.height === 0) return fallback;
|
|
1749
|
+
return { x: p.left - s.left + p.width / 2, y: p.top - s.top - 20 };
|
|
1750
|
+
}
|
|
1751
|
+
var REACTIONS = {
|
|
1752
|
+
hatch: { kind: "levelup", ms: 980, fx: ["\u{1F95A}", "\u2728", "\u{1F416}", "\u{1F389}"], count: 4, say: "\u5B75\u51FA\u6765\u5566\uFF01" },
|
|
1753
|
+
feed: { kind: "feed", ms: 900, fx: ["\u{1F34E}", "\u{1F60B}", "\u2728"], count: 3, say: "\u5403\u6389\u4E86\uFF01" },
|
|
1754
|
+
bathe: { kind: "bathe", ms: 1050, fx: ["\u{1FAE7}", "\u{1FAE7}", "\u{1F4A7}", "\u2728"], count: 4, say: "\u6D17\u5E72\u51C0\u5566\uFF5E" },
|
|
1755
|
+
play: { kind: "play", ms: 900, fx: ["\u{1F3BE}", "\u2B50", "\u{1F4A8}"], count: 3, say: "\u597D\u5F00\u5FC3\uFF01" },
|
|
1756
|
+
pet: { kind: "pet", ms: 420, fx: ["\u2764\uFE0F"], count: 1, say: "\u597D\u8212\u670D\u2026" },
|
|
1757
|
+
work: { kind: "away", ms: 900, fx: ["\u{1F4BC}", "\u{1F9F1}", "\u{1FA99}"], count: 3, say: "\u51FA\u95E8\u6253\u5DE5\uFF01" },
|
|
1758
|
+
study: { kind: "away", ms: 900, fx: ["\u{1F4DA}", "\u270F\uFE0F", "\u{1F9E0}"], count: 3, say: "\u4E0A\u5B66\u53BB\uFF01" },
|
|
1759
|
+
trip: { kind: "away", ms: 900, fx: ["\u{1F9F3}", "\u{1F5FA}", "\u2728"], count: 3, say: "\u51FA\u53D1\u65C5\u884C\uFF01" },
|
|
1760
|
+
calloff: { kind: "refuse", ms: 520, fx: ["\u{1F4A8}"], count: 1, say: "\u63D0\u524D\u56DE\u6765\u4E86\u2026" },
|
|
1761
|
+
buy: { kind: "pet", ms: 620, fx: ["\u{1FA99}", "\u{1F6D2}"], count: 2, say: "\u4E70\u5230\u4E86\uFF01" },
|
|
1762
|
+
use: { kind: "pet", ms: 620, fx: ["\u2728"], count: 2, say: "\u7528\u6389\u4E86\u3002" }
|
|
1763
|
+
};
|
|
1764
|
+
function flash(action) {
|
|
1765
|
+
var spec = REACTIONS[action];
|
|
1766
|
+
if (spec === void 0) return;
|
|
1767
|
+
react(spec.kind, spec.ms);
|
|
1768
|
+
burst(spec.fx, spec.count);
|
|
1769
|
+
var lines = action === "pet" ? PET_LINES : null;
|
|
1770
|
+
showBubble(lines === null ? spec.say : lines[Math.floor(Math.random() * lines.length)], 1600);
|
|
1771
|
+
}
|
|
1772
|
+
var bubbleTimer = null;
|
|
1773
|
+
function showBubble(text, ms) {
|
|
1774
|
+
if (bubbleTimer !== null) window.clearTimeout(bubbleTimer);
|
|
1775
|
+
bubble.textContent = text;
|
|
1776
|
+
bubble.hidden = false;
|
|
1777
|
+
bubbleTimer = window.setTimeout(function() {
|
|
1778
|
+
bubble.hidden = true;
|
|
1779
|
+
bubbleTimer = null;
|
|
1780
|
+
}, ms || 2600);
|
|
1781
|
+
}
|
|
1782
|
+
function showLine(text, replies, onReply) {
|
|
1783
|
+
if (replies.length === 0) {
|
|
1784
|
+
showBubble(text, 2600);
|
|
1785
|
+
return;
|
|
1786
|
+
}
|
|
1787
|
+
if (bubbleTimer !== null) window.clearTimeout(bubbleTimer);
|
|
1788
|
+
bubble.textContent = text;
|
|
1789
|
+
var row = el("div", "dp-bubble-replies", "");
|
|
1790
|
+
replies.forEach(function(label, index) {
|
|
1791
|
+
var answer = el("button", "dp-reply", label);
|
|
1792
|
+
answer.type = "button";
|
|
1793
|
+
answer.addEventListener("click", function(event) {
|
|
1794
|
+
event.stopPropagation();
|
|
1795
|
+
bubble.hidden = true;
|
|
1796
|
+
if (bubbleTimer !== null) window.clearTimeout(bubbleTimer);
|
|
1797
|
+
bubbleTimer = null;
|
|
1798
|
+
onReply(index);
|
|
1799
|
+
});
|
|
1800
|
+
row.appendChild(answer);
|
|
1801
|
+
});
|
|
1802
|
+
bubble.appendChild(row);
|
|
1803
|
+
bubble.hidden = false;
|
|
1804
|
+
bubbleTimer = window.setTimeout(function() {
|
|
1805
|
+
bubble.hidden = true;
|
|
1806
|
+
bubbleTimer = null;
|
|
1807
|
+
}, 6e3);
|
|
1808
|
+
}
|
|
1809
|
+
function toast(text) {
|
|
1810
|
+
var node = el("div", "dp-toast", text);
|
|
1811
|
+
card.insertBefore(node, card.firstChild);
|
|
1812
|
+
window.setTimeout(function() {
|
|
1813
|
+
node.remove();
|
|
1814
|
+
}, 4800);
|
|
1815
|
+
}
|
|
1816
|
+
function dispose() {
|
|
1817
|
+
if (reactTimer !== null) window.clearTimeout(reactTimer);
|
|
1818
|
+
if (bubbleTimer !== null) window.clearTimeout(bubbleTimer);
|
|
1819
|
+
reactTimer = null;
|
|
1820
|
+
bubbleTimer = null;
|
|
1821
|
+
}
|
|
1822
|
+
return { react, burst, flash, showBubble, showLine, toast, dispose };
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
// src/client/io.js
|
|
1826
|
+
function createIo(ctx) {
|
|
1827
|
+
var actionSeq = 0;
|
|
1828
|
+
async function send(action, extra) {
|
|
1829
|
+
if (ctx.busy || ctx.stopped) return;
|
|
1830
|
+
if (ctx.view.pig === null && action !== "hatch") return;
|
|
1831
|
+
actionSeq += 1;
|
|
1832
|
+
ctx.busy = true;
|
|
1833
|
+
ctx.flash(action);
|
|
1834
|
+
try {
|
|
1835
|
+
var body = { action };
|
|
1836
|
+
if (extra) for (var k in extra) body[k] = extra[k];
|
|
1837
|
+
var res = await fetch(ACT_URL, {
|
|
1838
|
+
method: "POST",
|
|
1839
|
+
headers: { "content-type": "application/json" },
|
|
1840
|
+
body: JSON.stringify(body)
|
|
1841
|
+
});
|
|
1842
|
+
var next = await res.json();
|
|
1843
|
+
ctx.render(next);
|
|
1844
|
+
if (next && next.ok === false) {
|
|
1845
|
+
if (next.reason === "stale-line") return;
|
|
1846
|
+
if (next.reason === "silent") return;
|
|
1847
|
+
ctx.react("refuse", 520);
|
|
1848
|
+
if (next.reason === "no-item") {
|
|
1849
|
+
var emptyKind = str(next.kind, "");
|
|
1850
|
+
ctx.showBubble(NO_ITEM_LINE[emptyKind] ?? "\u80CC\u5305\u91CC\u6CA1\u6709\u80FD\u7528\u7684\u4E1C\u897F", 3200);
|
|
1851
|
+
return;
|
|
1852
|
+
}
|
|
1853
|
+
var reasons = {
|
|
1854
|
+
box: "\u5148\u628A\u7EB8\u76D2\u62C6\u5F00",
|
|
1855
|
+
"coronation-ineligible": "\u52A0\u5195\u6761\u4EF6\u8FD8\u6CA1\u9F50",
|
|
1856
|
+
cooldown: "\u8FD8\u8981\u7B49 " + num(next.wait, 0) + " \u79D2",
|
|
1857
|
+
poor: "\u94B1\u4E0D\u591F",
|
|
1858
|
+
away: "\u5B83\u5728\u5916\u9762",
|
|
1859
|
+
weak: "\u592A\u865A\u5F31\u4E86\uFF0C\u5148\u517B\u597D\u518D\u51FA\u95E8",
|
|
1860
|
+
hungry: "\u592A\u997F\u4E86",
|
|
1861
|
+
"wrong-medicine": "\u836F\u4E0D\u5BF9\u75C7\uFF0C\u75C5\u60C5\u52A0\u91CD\u4E86\u2026",
|
|
1862
|
+
empty: "\u80CC\u5305\u91CC\u6CA1\u6709",
|
|
1863
|
+
"not-sick": "\u5B83\u6CA1\u751F\u75C5",
|
|
1864
|
+
dead: "\u5B83\u5DF2\u7ECF\u8D70\u4E86\u2026",
|
|
1865
|
+
idle: "\u5B83\u6CA1\u5728\u5916\u9762",
|
|
1866
|
+
owned: "\u8FD9\u4EF6\u5DF2\u7ECF\u6709\u4E86",
|
|
1867
|
+
"low-level": "\u7B49\u7EA7\u4E0D\u591F\uFF08\u8981 Lv." + num(next.need, 0) + "\uFF0C\u73B0\u5728 Lv." + num(next.have, 0) + "\uFF09",
|
|
1868
|
+
"not-owned": "\u8FD8\u6CA1\u6709\u8FD9\u4EF6\u4E1C\u897F",
|
|
1869
|
+
"not-consumable": "\u8FD9\u4E2A\u662F\u7A7F\u7684\uFF0C\u4E0D\u662F\u7528\u7684",
|
|
1870
|
+
"wrong-stage": "\u8FD9\u4E2A\u5B66\u6BB5\u6CA1\u6709\u8FD9\u95E8\u8BFE",
|
|
1871
|
+
underqualified: "\u5B83\u8FD8\u6CA1\u8FD9\u4E2A\u672C\u4E8B\uFF0C\u5148\u53BB\u4E0A\u8BFE"
|
|
1872
|
+
};
|
|
1873
|
+
ctx.showBubble(reasons[next.reason] ?? "\u8FD9\u4E2A\u64CD\u4F5C\u6CA1\u6210", 2400);
|
|
1874
|
+
}
|
|
1875
|
+
} catch (error) {
|
|
1876
|
+
ctx.showBubble("\u64CD\u4F5C\u6CA1\u9001\u5230\u5BBF\u4E3B", 2600);
|
|
1877
|
+
ctx.react("refuse", 520);
|
|
1878
|
+
} finally {
|
|
1879
|
+
ctx.busy = false;
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
async function refresh2() {
|
|
1883
|
+
if (ctx.stopped) return;
|
|
1884
|
+
ctx.fitPanel();
|
|
1885
|
+
var startedAt = actionSeq;
|
|
1886
|
+
try {
|
|
1887
|
+
var res = await fetch(STATE_URL, { cache: "no-store" });
|
|
1888
|
+
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
1889
|
+
var next = await res.json();
|
|
1890
|
+
if (startedAt !== actionSeq) return;
|
|
1891
|
+
ctx.render(next);
|
|
1892
|
+
} catch (error) {
|
|
1893
|
+
if (ctx.stopped) return;
|
|
1894
|
+
ctx.showBubble("\u8FDE\u63A5\u4E0D\u4E0A\u5BBF\u4E3B", 4e3);
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
return { send, refresh: refresh2 };
|
|
1898
|
+
}
|
|
1899
|
+
|
|
1900
|
+
// src/client/layout.js
|
|
1901
|
+
function createLayout(ctx) {
|
|
1902
|
+
function clampPig() {
|
|
1903
|
+
var vw = window.innerWidth || 0;
|
|
1904
|
+
var vh = window.innerHeight || 0;
|
|
1905
|
+
if (vw <= 0 || vh <= 0) return;
|
|
1906
|
+
var pigRect = ctx.pig.getBoundingClientRect ? ctx.pig.getBoundingClientRect() : null;
|
|
1907
|
+
var w = (pigRect ? pigRect.width || 0 : 0) + 2 * PIG_PADDING_X;
|
|
1908
|
+
var sceneRect = ctx.scene.getBoundingClientRect ? ctx.scene.getBoundingClientRect() : null;
|
|
1909
|
+
var h = Math.max(sceneRect ? sceneRect.height || 0 : 0, SCENE_RESERVE);
|
|
1910
|
+
var right = Math.min(Math.max(4, ctx.userRight), Math.max(4, vw - w - 4));
|
|
1911
|
+
var bottom = Math.min(Math.max(4, ctx.userBottom), Math.max(4, vh - h - 4));
|
|
1912
|
+
ctx.host.style.right = Math.round(right) + "px";
|
|
1913
|
+
ctx.host.style.bottom = Math.round(bottom) + "px";
|
|
1914
|
+
}
|
|
1915
|
+
function fitPanel() {
|
|
1916
|
+
if (!ctx.isOpen) return;
|
|
1917
|
+
var vw = window.innerWidth || 0;
|
|
1918
|
+
var vh = window.innerHeight || 0;
|
|
1919
|
+
if (vw <= 0 || vh <= 0) return;
|
|
1920
|
+
var rect = ctx.scene.getBoundingClientRect();
|
|
1921
|
+
var roomAbove = rect.top - PANEL_GAP - PANEL_MARGIN;
|
|
1922
|
+
var roomBelow = vh - rect.bottom - PANEL_GAP - PANEL_MARGIN;
|
|
1923
|
+
if (roomAbove >= roomBelow) {
|
|
1924
|
+
ctx.card.style.top = "auto";
|
|
1925
|
+
ctx.card.style.bottom = "calc(100% + " + PANEL_GAP + "px)";
|
|
1926
|
+
ctx.card.style.maxHeight = Math.max(PANEL_MIN_HEIGHT, Math.round(roomAbove)) + "px";
|
|
1927
|
+
} else {
|
|
1928
|
+
ctx.card.style.bottom = "auto";
|
|
1929
|
+
ctx.card.style.top = "calc(100% + " + PANEL_GAP + "px)";
|
|
1930
|
+
ctx.card.style.maxHeight = Math.max(PANEL_MIN_HEIGHT, Math.round(roomBelow)) + "px";
|
|
1931
|
+
}
|
|
1932
|
+
var width = Math.min(PANEL_WIDTH, vw - 2 * PANEL_MARGIN);
|
|
1933
|
+
ctx.card.style.maxWidth = Math.round(width) + "px";
|
|
1934
|
+
var shift = PANEL_MARGIN - (rect.right - width);
|
|
1935
|
+
ctx.card.style.right = shift > 0 ? -Math.round(shift) + "px" : "0px";
|
|
1936
|
+
var cardLeft = Math.max(rect.right - width, PANEL_MARGIN);
|
|
1937
|
+
ctx.hud.style.left = Math.max(9, Math.round(cardLeft - rect.left)) + "px";
|
|
1938
|
+
}
|
|
1939
|
+
function visibleTabs() {
|
|
1940
|
+
return ctx.devMode ? TABS.concat([DEV_TAB]) : TABS;
|
|
1941
|
+
}
|
|
1942
|
+
function paintBar() {
|
|
1943
|
+
while (ctx.bar.firstChild) ctx.bar.removeChild(ctx.bar.firstChild);
|
|
1944
|
+
var list = visibleTabs();
|
|
1945
|
+
for (var t = 0; t < list.length; t += 1) ctx.buildIcon(list[t]);
|
|
1946
|
+
if (ctx.icons[ctx.tab] === void 0 && ctx.tab !== "home") ctx.tab = "home";
|
|
1947
|
+
for (var k in ctx.icons) ctx.icons[k].setAttribute("data-active", k === ctx.tab ? "true" : "false");
|
|
1948
|
+
}
|
|
1949
|
+
function buildIcon(entry) {
|
|
1950
|
+
(function(entry2) {
|
|
1951
|
+
var btn = button("dp-ico", { "data-tab": entry2.key }, function() {
|
|
1952
|
+
if (ctx.host.getAttribute("data-open") !== "true") ctx.setOpen(true);
|
|
1953
|
+
ctx.select(entry2.key);
|
|
1954
|
+
});
|
|
1955
|
+
btn.appendChild(el("span", "dp-ico-e", entry2.emoji));
|
|
1956
|
+
btn.appendChild(el("span", null, entry2.label));
|
|
1957
|
+
ctx.icons[entry2.key] = btn;
|
|
1958
|
+
ctx.bar.appendChild(btn);
|
|
1959
|
+
})(entry);
|
|
1960
|
+
}
|
|
1961
|
+
return { clampPig, fitPanel, visibleTabs, paintBar, buildIcon };
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// src/client/normalize.js
|
|
1965
|
+
function normalize(raw) {
|
|
1966
|
+
var d = obj(raw);
|
|
1967
|
+
var pig = isObj(d.pig) ? d.pig : null;
|
|
1968
|
+
var legacy = pig !== null && !("coins" in pig) && !("health" in pig);
|
|
1969
|
+
return {
|
|
1970
|
+
legacy,
|
|
1971
|
+
// Host build version, shown in the debug tab so a stale bundle is
|
|
1972
|
+
// visible instead of being guessed at.
|
|
1973
|
+
version: str(d.version, ""),
|
|
1974
|
+
// Trust the flag when the host sends one. Older hosts did not, and for
|
|
1975
|
+
// those "a pig exists" is still the right answer.
|
|
1976
|
+
hatched: d.hatched === true || d.hatched === void 0 && pig !== null,
|
|
1977
|
+
dead: d.dead === true || pig !== null && num(pig.health, 5) <= 0,
|
|
1978
|
+
pig: pig === null ? null : {
|
|
1979
|
+
name: str(pig.name, "\u732A\u732A"),
|
|
1980
|
+
// The pig is measured in days now; `stage` carries how big it is and
|
|
1981
|
+
// what it looks like.
|
|
1982
|
+
stage: {
|
|
1983
|
+
key: str(obj(pig.stage).key, "piglet"),
|
|
1984
|
+
label: str(obj(pig.stage).label, "\u5C0F\u732A"),
|
|
1985
|
+
emoji: str(obj(pig.stage).emoji, "\u{1F416}"),
|
|
1986
|
+
size: num(obj(pig.stage).size, 56),
|
|
1987
|
+
line: str(obj(pig.stage).line, ""),
|
|
1988
|
+
art: typeof obj(pig.stage).art === "string" && obj(pig.stage).art !== "" ? obj(pig.stage).art : null,
|
|
1989
|
+
faded: obj(pig.stage).faded === true,
|
|
1990
|
+
// 加冕后的形态:有没有动作立绘、盖住哪些装扮位置。
|
|
1991
|
+
actionArt: obj(pig.stage).actionArt === true,
|
|
1992
|
+
hides: arr(obj(pig.stage).hides).map(function(slot) {
|
|
1993
|
+
return str(slot, "");
|
|
1994
|
+
})
|
|
1995
|
+
},
|
|
1996
|
+
// Older hosts send no sex; the HUD then simply shows none.
|
|
1997
|
+
sex: isObj(pig.sex) ? { key: str(pig.sex.key, ""), label: str(pig.sex.label, ""), symbol: str(pig.sex.symbol, "") } : null,
|
|
1998
|
+
ageLabel: str(pig.ageLabel, ""),
|
|
1999
|
+
ageForced: pig.ageForced === true,
|
|
2000
|
+
daysToNextStage: typeof pig.daysToNextStage === "number" ? pig.daysToNextStage : null,
|
|
2001
|
+
soul: pig.soul === true,
|
|
2002
|
+
mood: str(pig.mood, "fine"),
|
|
2003
|
+
moodEmoji: str(pig.moodEmoji, "\u{1F60A}"),
|
|
2004
|
+
moodLabel: str(pig.moodLabel, "\u8FD8\u4E0D\u9519"),
|
|
2005
|
+
satiety: Math.round(num(pig.satiety, 0)),
|
|
2006
|
+
happiness: Math.round(num(pig.happiness, 0)),
|
|
2007
|
+
cleanliness: Math.round(num(pig.cleanliness, 0)),
|
|
2008
|
+
health: num(pig.health, 5),
|
|
2009
|
+
healthPercent: num(pig.healthPercent, 100),
|
|
2010
|
+
coins: num(pig.coins, 0),
|
|
2011
|
+
weight: str(pig.weight, "\u2014"),
|
|
2012
|
+
xp: num(pig.xp, 0),
|
|
2013
|
+
// Level is driven by growth and decides the body (B2).
|
|
2014
|
+
level: (function(info) {
|
|
2015
|
+
var i = obj(info);
|
|
2016
|
+
var t = obj(i.title);
|
|
2017
|
+
return {
|
|
2018
|
+
level: num(i.level, 1),
|
|
2019
|
+
percent: num(i.percent, 0),
|
|
2020
|
+
toNext: num(i.toNext, 0),
|
|
2021
|
+
maxed: i.maxed === true,
|
|
2022
|
+
titleLabel: str(t.label, "\u65B0\u6765\u7684"),
|
|
2023
|
+
titleEmoji: str(t.emoji, "\u{1F331}")
|
|
2024
|
+
};
|
|
2025
|
+
})(pig.levelInfo),
|
|
2026
|
+
stageLine: str(pig.stageLine, ""),
|
|
2027
|
+
illness: isObj(pig.illness) ? {
|
|
2028
|
+
name: str(pig.illness.name, "\u751F\u75C5"),
|
|
2029
|
+
cure: str(pig.illness.cure, "\u836F"),
|
|
2030
|
+
cureEmoji: str(pig.illness.cureEmoji, "\u{1F48A}"),
|
|
2031
|
+
stage: num(pig.illness.stage, 1),
|
|
2032
|
+
doctorFee: typeof pig.illness.doctorFee === "number" ? pig.illness.doctorFee : null
|
|
2033
|
+
} : null,
|
|
2034
|
+
traits: {
|
|
2035
|
+
intel: num(obj(pig.traits).intel, 0),
|
|
2036
|
+
charm: num(obj(pig.traits).charm, 0),
|
|
2037
|
+
strong: num(obj(pig.traits).strong, 0)
|
|
2038
|
+
},
|
|
2039
|
+
courses: obj(pig.courses),
|
|
2040
|
+
// Souvenirs are objects now (rarity + story). An old host sent bare
|
|
2041
|
+
// strings, and those must still list rather than turn into [object
|
|
2042
|
+
// Object] or vanish.
|
|
2043
|
+
souvenirs: arr(pig.souvenirs).map((entry) => {
|
|
2044
|
+
if (typeof entry === "string") {
|
|
2045
|
+
return { key: entry, emoji: "\u{1F381}", label: entry, rarityLabel: "\u666E\u901A", rarityEmoji: "\u26AA", price: 0, story: "", fromLabel: "" };
|
|
2046
|
+
}
|
|
2047
|
+
return {
|
|
2048
|
+
key: str(obj(entry).key, ""),
|
|
2049
|
+
emoji: str(obj(entry).emoji, "\u{1F381}"),
|
|
2050
|
+
label: str(obj(entry).label, "\u7EAA\u5FF5\u54C1"),
|
|
2051
|
+
rarityLabel: str(obj(entry).rarityLabel, "\u666E\u901A"),
|
|
2052
|
+
rarityEmoji: str(obj(entry).rarityEmoji, "\u26AA"),
|
|
2053
|
+
price: num(obj(entry).price, 0),
|
|
2054
|
+
story: str(obj(entry).story, ""),
|
|
2055
|
+
fromLabel: str(obj(entry).fromLabel, "")
|
|
2056
|
+
};
|
|
2057
|
+
}).filter((entry) => entry.key !== ""),
|
|
2058
|
+
memories: arr(pig.memories).filter((m) => typeof m === "string")
|
|
2059
|
+
},
|
|
2060
|
+
actions: normalizeActions(d.actions),
|
|
2061
|
+
jobs: arr(d.jobs).map((job) => ({
|
|
2062
|
+
key: str(obj(job).key, ""),
|
|
2063
|
+
label: str(obj(job).label, "\u5DE5\u4F5C"),
|
|
2064
|
+
emoji: str(obj(job).emoji, "\u{1F4BC}"),
|
|
2065
|
+
minutes: num(obj(job).minutes, 0),
|
|
2066
|
+
coins: num(obj(job).coins, 0),
|
|
2067
|
+
available: obj(job).available === true,
|
|
2068
|
+
// What schooling has bought this job.
|
|
2069
|
+
traitLabel: str(obj(job).traitLabel, ""),
|
|
2070
|
+
traitEmoji: str(obj(job).traitEmoji, ""),
|
|
2071
|
+
traitPoints: num(obj(job).traitPoints, 0),
|
|
2072
|
+
baseMinutes: num(obj(job).baseMinutes, 0),
|
|
2073
|
+
baseCoins: num(obj(job).baseCoins, 0),
|
|
2074
|
+
payPercent: num(obj(job).payPercent, 0),
|
|
2075
|
+
speedPercent: num(obj(job).speedPercent, 0),
|
|
2076
|
+
// An old host has no gate at all, so a missing flag must read as
|
|
2077
|
+
// "qualified" — the opposite default would lock every job on upgrade.
|
|
2078
|
+
qualified: obj(job).qualified !== false,
|
|
2079
|
+
lockText: str(obj(job).lockText, ""),
|
|
2080
|
+
level: num(obj(job).level, 1),
|
|
2081
|
+
trait: str(obj(job).trait, ""),
|
|
2082
|
+
satiety: num(obj(job).satiety, 0),
|
|
2083
|
+
cleanliness: num(obj(job).cleanliness, 0),
|
|
2084
|
+
requirements: arr(obj(job).requirements).filter(isObj).map((entry) => ({
|
|
2085
|
+
text: str(entry.text, ""),
|
|
2086
|
+
need: num(entry.need, 0),
|
|
2087
|
+
have: num(entry.have, 0),
|
|
2088
|
+
kind: str(entry.kind, ""),
|
|
2089
|
+
met: entry.met === true
|
|
2090
|
+
}))
|
|
2091
|
+
})).filter((job) => job.key !== ""),
|
|
2092
|
+
// B4: nine subjects, each with its own lesson count and stage.
|
|
2093
|
+
subjects: arr(d.subjects).map((sub) => ({
|
|
2094
|
+
key: str(obj(sub).key, ""),
|
|
2095
|
+
label: str(obj(sub).label, "\u8BFE"),
|
|
2096
|
+
emoji: str(obj(sub).emoji, "\u{1F4D8}"),
|
|
2097
|
+
traitLabel: str(obj(sub).traitLabel, ""),
|
|
2098
|
+
traitEmoji: str(obj(sub).traitEmoji, ""),
|
|
2099
|
+
lessons: num(obj(sub).lessons, num(obj(sub).level, 0)),
|
|
2100
|
+
stageKey: str(obj(obj(sub).stage).key, ""),
|
|
2101
|
+
stageLabel: str(obj(obj(sub).stage).label, ""),
|
|
2102
|
+
graduatedLabel: isObj(obj(sub).graduated) ? str(obj(sub).graduated.label, "") : "",
|
|
2103
|
+
nextGraduation: typeof obj(sub).nextGraduation === "number" ? obj(sub).nextGraduation : null,
|
|
2104
|
+
minutes: num(obj(sub).minutes, 0),
|
|
2105
|
+
tuition: num(obj(sub).tuition, 0),
|
|
2106
|
+
gain: num(obj(sub).gain, 0),
|
|
2107
|
+
secondaryGain: num(obj(sub).secondaryGain, 0),
|
|
2108
|
+
available: obj(sub).available === true,
|
|
2109
|
+
affordable: obj(sub).affordable !== false
|
|
2110
|
+
})).filter((sub) => sub.key !== ""),
|
|
2111
|
+
// 兴趣课:学习页里随时能学的一栏,学完加的是既有的三条属性。
|
|
2112
|
+
interests: arr(d.interests).map((entry) => ({
|
|
2113
|
+
key: str(obj(entry).key, ""),
|
|
2114
|
+
label: str(obj(entry).label, "\u5174\u8DA3"),
|
|
2115
|
+
emoji: str(obj(entry).emoji, "\u{1F3AF}"),
|
|
2116
|
+
traitLabel: str(obj(entry).traitLabel, ""),
|
|
2117
|
+
traitEmoji: str(obj(entry).traitEmoji, ""),
|
|
2118
|
+
minutes: num(obj(entry).minutes, 0),
|
|
2119
|
+
cost: num(obj(entry).cost, 0),
|
|
2120
|
+
gain: num(obj(entry).gain, 0),
|
|
2121
|
+
blurb: str(obj(entry).blurb, ""),
|
|
2122
|
+
times: num(obj(entry).times, 0),
|
|
2123
|
+
certificate: str(obj(entry).certificate, ""),
|
|
2124
|
+
certificateAfter: num(obj(entry).certificateAfter, 0),
|
|
2125
|
+
certified: obj(entry).certified === true,
|
|
2126
|
+
available: obj(entry).available === true,
|
|
2127
|
+
affordable: obj(entry).affordable === true
|
|
2128
|
+
})).filter((entry) => entry.key !== ""),
|
|
2129
|
+
stages: arr(d.stages).map((stage) => ({
|
|
2130
|
+
key: str(obj(stage).key, ""),
|
|
2131
|
+
label: str(obj(stage).label, "\u5B66\u6BB5"),
|
|
2132
|
+
emoji: str(obj(stage).emoji, "\u{1F4DA}"),
|
|
2133
|
+
minutes: num(obj(stage).minutes, 0),
|
|
2134
|
+
tuition: num(obj(stage).tuition, 0),
|
|
2135
|
+
gain: num(obj(stage).gain, 0),
|
|
2136
|
+
// B4: the lesson numbers this stage covers (upTo null = no end).
|
|
2137
|
+
from: num(obj(stage).from, 0),
|
|
2138
|
+
upTo: typeof obj(stage).upTo === "number" ? obj(stage).upTo : null,
|
|
2139
|
+
// Which courses this stage teaches — empty on an old host, in which
|
|
2140
|
+
// case the panel shows every subject rather than none.
|
|
2141
|
+
subjects: arr(obj(stage).subjects).filter((key) => typeof key === "string"),
|
|
2142
|
+
// The school ladder: a stage with `unlocked === false` is gated behind
|
|
2143
|
+
// finishing the previous one, and says by how much.
|
|
2144
|
+
unlocked: obj(stage).unlocked !== false,
|
|
2145
|
+
progress: isObj(obj(stage).progress) ? {
|
|
2146
|
+
done: num(obj(stage).progress.done, 0),
|
|
2147
|
+
need: num(obj(stage).progress.need, 0),
|
|
2148
|
+
label: str(obj(stage).progress.label, "")
|
|
2149
|
+
} : null
|
|
2150
|
+
})).filter((stage) => stage.key !== ""),
|
|
2151
|
+
trips: arr(d.trips).map((trip) => ({
|
|
2152
|
+
key: str(obj(trip).key, ""),
|
|
2153
|
+
label: str(obj(trip).label, "\u76EE\u7684\u5730"),
|
|
2154
|
+
emoji: str(obj(trip).emoji, "\u{1F9F3}"),
|
|
2155
|
+
minutes: num(obj(trip).minutes, 0),
|
|
2156
|
+
cost: num(obj(trip).cost, 0),
|
|
2157
|
+
happiness: num(obj(trip).happiness, 0),
|
|
2158
|
+
// What the destination can bring back — the far trips advertise it.
|
|
2159
|
+
souvenirCount: num(obj(trip).souvenirCount, 0),
|
|
2160
|
+
bestRarity: str(obj(trip).bestRarity, ""),
|
|
2161
|
+
bestRarityEmoji: str(obj(trip).bestRarityEmoji, ""),
|
|
2162
|
+
affordable: obj(trip).affordable === true,
|
|
2163
|
+
available: obj(trip).available === true
|
|
2164
|
+
})).filter((trip) => trip.key !== ""),
|
|
2165
|
+
// 家当: owned and worn, never counted. An old host sends none.
|
|
2166
|
+
dress: arr(d.dress).map((entry) => ({
|
|
2167
|
+
key: str(obj(entry).key, ""),
|
|
2168
|
+
label: str(obj(entry).label, "\u88C5\u626E"),
|
|
2169
|
+
emoji: str(obj(entry).emoji, "\u{1F455}"),
|
|
2170
|
+
price: num(obj(entry).price, 0),
|
|
2171
|
+
level: num(obj(entry).level, 1),
|
|
2172
|
+
slot: str(obj(entry).slot, ""),
|
|
2173
|
+
slotLabel: str(obj(entry).slotLabel, ""),
|
|
2174
|
+
blurb: str(obj(entry).blurb, ""),
|
|
2175
|
+
owned: obj(entry).owned === true,
|
|
2176
|
+
worn: obj(entry).worn === true,
|
|
2177
|
+
unlocked: obj(entry).unlocked !== false
|
|
2178
|
+
})).filter((entry) => entry.key !== ""),
|
|
2179
|
+
shop: arr(d.shop).map((item) => ({
|
|
2180
|
+
key: str(obj(item).key, ""),
|
|
2181
|
+
label: str(obj(item).label, "\u7269\u54C1"),
|
|
2182
|
+
emoji: str(obj(item).emoji, "\u{1F4E6}"),
|
|
2183
|
+
price: num(obj(item).price, 0),
|
|
2184
|
+
kind: str(obj(item).kind, "food"),
|
|
2185
|
+
tier: typeof obj(item).tier === "number" ? obj(item).tier : null,
|
|
2186
|
+
// 家当 fields: a dress item is owned (not counted) or waits for a level.
|
|
2187
|
+
level: typeof obj(item).level === "number" ? obj(item).level : null,
|
|
2188
|
+
owned: obj(item).owned === true,
|
|
2189
|
+
worn: obj(item).worn === true,
|
|
2190
|
+
unlocked: obj(item).unlocked !== false,
|
|
2191
|
+
blurb: str(obj(item).blurb, ""),
|
|
2192
|
+
affordable: obj(item).affordable === true,
|
|
2193
|
+
needed: obj(item).needed === true
|
|
2194
|
+
})).filter((item) => item.key !== ""),
|
|
2195
|
+
inventory: obj(d.inventory),
|
|
2196
|
+
daily: {
|
|
2197
|
+
canSignIn: obj(d.daily).canSignIn === true,
|
|
2198
|
+
signInDay: num(obj(d.daily).signInDay, 1),
|
|
2199
|
+
signInTotal: num(obj(d.daily).signInTotal, 0),
|
|
2200
|
+
cycle: num(obj(d.daily).cycle, 12),
|
|
2201
|
+
unclaimed: num(obj(d.daily).unclaimed, 0),
|
|
2202
|
+
onlineMinutes: num(obj(d.daily).onlineMinutes, 0)
|
|
2203
|
+
},
|
|
2204
|
+
// 新到旧;老宿主没有 diary 时是空数组,面板不显示这一栏。
|
|
2205
|
+
diary: arr(d.diary).map((entry) => ({
|
|
2206
|
+
day: str(obj(entry).day, ""),
|
|
2207
|
+
text: str(obj(entry).text, "")
|
|
2208
|
+
})).filter((entry) => entry.day !== "" && entry.text !== ""),
|
|
2209
|
+
// Which items each care action could spend right now.
|
|
2210
|
+
care: (() => {
|
|
2211
|
+
const out = {};
|
|
2212
|
+
const source = obj(d.care);
|
|
2213
|
+
for (const action of ["feed", "bathe", "play"]) {
|
|
2214
|
+
out[action] = arr(source[action]).map((entry) => ({
|
|
2215
|
+
key: str(obj(entry).key, ""),
|
|
2216
|
+
label: str(obj(entry).label, "\u7269\u54C1"),
|
|
2217
|
+
emoji: str(obj(entry).emoji, "\u{1F4E6}"),
|
|
2218
|
+
default: obj(entry).default === true,
|
|
2219
|
+
count: typeof obj(entry).count === "number" ? obj(entry).count : null,
|
|
2220
|
+
satiety: num(obj(entry).satiety, 0),
|
|
2221
|
+
happiness: num(obj(entry).happiness, 0),
|
|
2222
|
+
cleanliness: num(obj(entry).cleanliness, 0)
|
|
2223
|
+
})).filter((entry) => entry.key !== "");
|
|
2224
|
+
}
|
|
2225
|
+
return out;
|
|
2226
|
+
})(),
|
|
2227
|
+
activity: isObj(d.activity) ? {
|
|
2228
|
+
kind: str(d.activity.kind, "work"),
|
|
2229
|
+
key: str(d.activity.key, ""),
|
|
2230
|
+
label: str(d.activity.label, "\u5916\u9762"),
|
|
2231
|
+
emoji: str(d.activity.emoji, "\u{1F4BC}"),
|
|
2232
|
+
secondsLeft: num(d.activity.secondsLeft, 0),
|
|
2233
|
+
progress: num(d.activity.progress, 0)
|
|
2234
|
+
} : null,
|
|
2235
|
+
canGoOut: d.canGoOut === true,
|
|
2236
|
+
timeScale: num(d.timeScale, 1),
|
|
2237
|
+
boxStage: isObj(d.boxStage) ? {
|
|
2238
|
+
key: str(d.boxStage.key, "box"),
|
|
2239
|
+
label: str(d.boxStage.label, "\u7EB8\u76D2"),
|
|
2240
|
+
emoji: str(d.boxStage.emoji, "\u{1F4E6}"),
|
|
2241
|
+
size: num(d.boxStage.size, 58)
|
|
2242
|
+
} : { key: "box", label: "\u7EB8\u76D2", emoji: "\u{1F4E6}", size: 58 },
|
|
2243
|
+
awayBlocked: typeof d.awayBlocked === "string" ? d.awayBlocked : null,
|
|
2244
|
+
// B9: the villager card. Older hosts send none, and the card says so.
|
|
2245
|
+
profile: isObj(d.profile) ? {
|
|
2246
|
+
personality: isObj(d.profile.personality) ? { label: str(d.profile.personality.label, ""), emoji: str(d.profile.personality.emoji, "") } : null,
|
|
2247
|
+
catchphrase: str(d.profile.catchphrase, ""),
|
|
2248
|
+
motto: str(d.profile.motto, ""),
|
|
2249
|
+
birthday: str(d.profile.birthday, ""),
|
|
2250
|
+
zodiac: isObj(d.profile.zodiac) ? { label: str(d.profile.zodiac.label, ""), emoji: str(d.profile.zodiac.emoji, "") } : null,
|
|
2251
|
+
counts: {
|
|
2252
|
+
days: num(obj(d.profile.counts).days, 0),
|
|
2253
|
+
certificates: num(obj(d.profile.counts).certificates, 0),
|
|
2254
|
+
souvenirs: num(obj(d.profile.counts).souvenirs, 0),
|
|
2255
|
+
graduations: num(obj(d.profile.counts).graduations, 0)
|
|
2256
|
+
}
|
|
2257
|
+
} : null,
|
|
2258
|
+
// 加冕: the forms and how close the pig is. Older hosts send none.
|
|
2259
|
+
forms: isObj(d.forms) ? {
|
|
2260
|
+
current: typeof d.forms.current === "string" ? d.forms.current : null,
|
|
2261
|
+
forms: arr(d.forms.forms).map(function(raw2) {
|
|
2262
|
+
var f = obj(raw2);
|
|
2263
|
+
return {
|
|
2264
|
+
key: str(f.key, ""),
|
|
2265
|
+
label: str(f.label, ""),
|
|
2266
|
+
emoji: str(f.emoji, "\u{1F451}"),
|
|
2267
|
+
art: str(f.art, ""),
|
|
2268
|
+
current: f.current === true,
|
|
2269
|
+
ready: f.ready === true,
|
|
2270
|
+
requirements: arr(f.requirements).map(function(row) {
|
|
2271
|
+
var r = obj(row);
|
|
2272
|
+
return { key: str(r.key, ""), label: str(r.label, ""), have: num(r.have, 0), need: num(r.need, 0), met: r.met === true };
|
|
2273
|
+
})
|
|
2274
|
+
};
|
|
2275
|
+
})
|
|
2276
|
+
} : null,
|
|
2277
|
+
// B6: what the pig calls its owner, and 免打扰. Older hosts send neither.
|
|
2278
|
+
dialogue: {
|
|
2279
|
+
ownerName: str(obj(d.dialogue).ownerName, "\u4E3B\u4EBA"),
|
|
2280
|
+
quiet: obj(d.dialogue).quiet === true
|
|
2281
|
+
},
|
|
2282
|
+
pending: arr(d.pending).filter((e) => isObj(e) && typeof e.at === "number").map((e) => ({
|
|
2283
|
+
id: num(e.id, 0),
|
|
2284
|
+
kind: str(e.kind, ""),
|
|
2285
|
+
text: str(e.text, ""),
|
|
2286
|
+
at: e.at,
|
|
2287
|
+
replies: arr(e.replies).filter((label) => typeof label === "string")
|
|
2288
|
+
})),
|
|
2289
|
+
maxHealth: num(d.maxHealth, 5)
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
function normalizeActions(raw) {
|
|
2293
|
+
var source = obj(raw);
|
|
2294
|
+
var out = {};
|
|
2295
|
+
for (var i = 0; i < MODES.length; i += 1) {
|
|
2296
|
+
var key = MODES[i];
|
|
2297
|
+
var entry = obj(source[key]);
|
|
2298
|
+
out[key] = {
|
|
2299
|
+
ready: entry.ready !== false,
|
|
2300
|
+
waitSeconds: num(entry.waitSeconds, 0),
|
|
2301
|
+
blocked: typeof entry.blocked === "string" ? entry.blocked : null
|
|
2302
|
+
};
|
|
2303
|
+
}
|
|
2304
|
+
return out;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
// src/client/storage.js
|
|
2308
|
+
function readStore(key) {
|
|
2309
|
+
try {
|
|
2310
|
+
var value = window.localStorage.getItem(key);
|
|
2311
|
+
return value !== null ? value : window.localStorage.getItem(key.replace(/^dsh-piggy:/, "dsh-pig:"));
|
|
2312
|
+
} catch (error) {
|
|
2313
|
+
return null;
|
|
2314
|
+
}
|
|
2315
|
+
}
|
|
2316
|
+
function writeStore(key, value) {
|
|
2317
|
+
try {
|
|
2318
|
+
window.localStorage.setItem(key, value);
|
|
2319
|
+
} catch (error) {
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
|
|
2323
|
+
// src/client/tabs/card.js
|
|
2324
|
+
var LIMITS = { catchphrase: 6, motto: 24 };
|
|
2325
|
+
function renderCardTab(ui) {
|
|
2326
|
+
var p = ui.view.pig;
|
|
2327
|
+
var profile = ui.view.profile;
|
|
2328
|
+
if (p === null) return;
|
|
2329
|
+
if (profile === null) {
|
|
2330
|
+
ui.content.appendChild(el("div", "dp-empty", "\u91CD\u542F dsh \u4E4B\u540E\u624D\u6709\u5C45\u6C11\u5361"));
|
|
2331
|
+
return;
|
|
2332
|
+
}
|
|
2333
|
+
var card = el("div", "dp-vcard");
|
|
2334
|
+
card.setAttribute("data-sex", p.sex !== null ? p.sex.key : "none");
|
|
2335
|
+
var top = el("div", "dp-vcard-top");
|
|
2336
|
+
var avatar = el("div", "dp-vcard-avatar");
|
|
2337
|
+
if (p.stage.art !== null) {
|
|
2338
|
+
var img = (
|
|
2339
|
+
/** @type {HTMLImageElement} */
|
|
2340
|
+
el("img", "dp-vcard-img")
|
|
2341
|
+
);
|
|
2342
|
+
img.src = ART_URL + p.stage.art + ".svg";
|
|
2343
|
+
img.alt = "";
|
|
2344
|
+
avatar.appendChild(img);
|
|
2345
|
+
} else {
|
|
2346
|
+
avatar.appendChild(el("span", "dp-vcard-e", p.stage.emoji));
|
|
2347
|
+
}
|
|
2348
|
+
top.appendChild(avatar);
|
|
2349
|
+
var who = el("div", "dp-vcard-who");
|
|
2350
|
+
who.appendChild(el("b", "dp-vcard-name", p.name + (p.sex !== null ? " " + p.sex.symbol : "")));
|
|
2351
|
+
who.appendChild(el("span", "dp-vcard-sub", "Lv." + p.level.level + " " + p.level.titleEmoji + p.level.titleLabel));
|
|
2352
|
+
who.appendChild(el("span", "dp-vcard-sub", p.stage.label));
|
|
2353
|
+
top.appendChild(who);
|
|
2354
|
+
card.appendChild(top);
|
|
2355
|
+
card.appendChild(field("\u751F\u65E5", profile.birthday));
|
|
2356
|
+
if (profile.zodiac !== null) card.appendChild(field("\u661F\u5EA7", profile.zodiac.emoji + " " + profile.zodiac.label));
|
|
2357
|
+
if (profile.personality !== null) card.appendChild(field("\u6027\u683C", profile.personality.emoji + " " + profile.personality.label));
|
|
2358
|
+
var forms = ui.view.forms;
|
|
2359
|
+
var worn = forms === null ? null : forms.forms.find(function(f) {
|
|
2360
|
+
return f.current;
|
|
2361
|
+
}) || null;
|
|
2362
|
+
if (worn !== null) card.appendChild(field("\u5F62\u6001", worn.emoji + " " + worn.label));
|
|
2363
|
+
card.appendChild(editableField(ui, "catchphrase", "\u53E3\u5934\u7985", profile.catchphrase));
|
|
2364
|
+
card.appendChild(editableField(ui, "motto", "\u7B7E\u540D", profile.motto));
|
|
2365
|
+
var c = profile.counts;
|
|
2366
|
+
card.appendChild(el(
|
|
2367
|
+
"div",
|
|
2368
|
+
"dp-vcard-foot",
|
|
2369
|
+
"\u517B\u4E86 " + c.days + " \u5929 \xB7 \u8BC1\u4E66 " + c.certificates + " \xB7 \u7EAA\u5FF5\u54C1 " + c.souvenirs + " \xB7 \u6BD5\u4E1A " + c.graduations
|
|
2370
|
+
));
|
|
2371
|
+
ui.content.appendChild(card);
|
|
2372
|
+
}
|
|
2373
|
+
function field(label, value) {
|
|
2374
|
+
var row = el("div", "dp-vcard-row");
|
|
2375
|
+
row.appendChild(el("span", "dp-vcard-label", label + "\uFF1A"));
|
|
2376
|
+
row.appendChild(el("span", "dp-vcard-value", value));
|
|
2377
|
+
return row;
|
|
2378
|
+
}
|
|
2379
|
+
function editableField(ui, key, label, value) {
|
|
2380
|
+
var editing = ui.cardEdit !== null && ui.cardEdit.field === key;
|
|
2381
|
+
var row = el("div", "dp-vcard-row" + (key === "motto" ? " dp-vcard-motto-row" : ""));
|
|
2382
|
+
row.appendChild(el("span", "dp-vcard-label", label + "\uFF1A"));
|
|
2383
|
+
if (editing) {
|
|
2384
|
+
var input = (
|
|
2385
|
+
/** @type {HTMLInputElement} */
|
|
2386
|
+
el("input", "dp-input dp-vcard-input")
|
|
2387
|
+
);
|
|
2388
|
+
input.value = ui.cardEdit.draft;
|
|
2389
|
+
input.maxLength = LIMITS[key];
|
|
2390
|
+
input.setAttribute("data-card-input", key);
|
|
2391
|
+
input.addEventListener("input", function() {
|
|
2392
|
+
ui.cardEdit = { field: key, draft: input.value };
|
|
2393
|
+
});
|
|
2394
|
+
var save = button("dp-mini", { "data-card-save": key }, function() {
|
|
2395
|
+
var text = (ui.cardEdit === null ? "" : ui.cardEdit.draft).trim();
|
|
2396
|
+
ui.cardEdit = null;
|
|
2397
|
+
if (text !== "") ui.send(key, { text });
|
|
2398
|
+
ui.renderContent();
|
|
2399
|
+
});
|
|
2400
|
+
save.textContent = "\u597D";
|
|
2401
|
+
var cancel = button("dp-mini dp-mini-plain", { "data-card-cancel": key }, function() {
|
|
2402
|
+
ui.cardEdit = null;
|
|
2403
|
+
ui.renderContent();
|
|
2404
|
+
});
|
|
2405
|
+
cancel.textContent = "\u7B97\u4E86";
|
|
2406
|
+
row.appendChild(input);
|
|
2407
|
+
row.appendChild(save);
|
|
2408
|
+
row.appendChild(cancel);
|
|
2409
|
+
return row;
|
|
2410
|
+
}
|
|
2411
|
+
row.appendChild(el("span", key === "motto" ? "dp-vcard-value dp-vcard-motto" : "dp-vcard-value", key === "motto" ? "\u300C" + value + "\u300D" : value));
|
|
2412
|
+
var pencil = button("dp-vcard-edit", { "data-card-edit": key }, function() {
|
|
2413
|
+
ui.cardEdit = { field: key, draft: value };
|
|
2414
|
+
ui.renderContent();
|
|
2415
|
+
});
|
|
2416
|
+
pencil.textContent = "\u270F\uFE0F";
|
|
2417
|
+
pencil.title = "\u6539" + label;
|
|
2418
|
+
row.appendChild(pencil);
|
|
2419
|
+
return row;
|
|
2420
|
+
}
|
|
2421
|
+
|
|
2422
|
+
// src/client/tabs/crown.js
|
|
2423
|
+
function renderCrownTab(ui) {
|
|
2424
|
+
var forms = ui.view.forms;
|
|
2425
|
+
if (forms === null) {
|
|
2426
|
+
ui.content.appendChild(el("div", "dp-empty", "\u91CD\u542F dsh \u4E4B\u540E\u624D\u6709\u52A0\u5195"));
|
|
2427
|
+
return;
|
|
2428
|
+
}
|
|
2429
|
+
if (ui.view.dead) ui.content.appendChild(el("div", "dp-empty", "\u5B83\u8D70\u4E86\uFF0C\u6551\u56DE\u6765\u624D\u80FD\u52A0\u5195"));
|
|
2430
|
+
for (var f = 0; f < forms.forms.length; f += 1) ui.content.appendChild(formBlock(ui, forms.forms[f]));
|
|
2431
|
+
}
|
|
2432
|
+
function formBlock(ui, form) {
|
|
2433
|
+
var box = el("div", form.current ? "dp-crown dp-crown-now" : "dp-crown");
|
|
2434
|
+
box.setAttribute("data-form", form.key);
|
|
2435
|
+
var top = el("div", "dp-crown-top");
|
|
2436
|
+
var pic = el("div", "dp-crown-pic");
|
|
2437
|
+
if (form.art !== "") {
|
|
2438
|
+
var img = (
|
|
2439
|
+
/** @type {HTMLImageElement} */
|
|
2440
|
+
el("img", "dp-crown-img")
|
|
2441
|
+
);
|
|
2442
|
+
img.src = ART_URL + form.art + ".svg";
|
|
2443
|
+
img.alt = "";
|
|
2444
|
+
pic.appendChild(img);
|
|
2445
|
+
} else {
|
|
2446
|
+
pic.appendChild(el("span", null, form.emoji));
|
|
2447
|
+
}
|
|
2448
|
+
top.appendChild(pic);
|
|
2449
|
+
var side = el("div", "dp-crown-side");
|
|
2450
|
+
side.appendChild(el("div", "dp-crown-head", form.emoji + " " + form.label));
|
|
2451
|
+
if (form.current) {
|
|
2452
|
+
side.appendChild(el("div", "dp-crown-done", "\u2713 \u5F53\u524D\u5F62\u6001"));
|
|
2453
|
+
} else {
|
|
2454
|
+
var chips = el("div", "dp-crown-reqs");
|
|
2455
|
+
for (var r = 0; r < form.requirements.length; r += 1) {
|
|
2456
|
+
var row = form.requirements[r];
|
|
2457
|
+
chips.appendChild(el(
|
|
2458
|
+
"span",
|
|
2459
|
+
row.met ? "dp-crown-req dp-crown-ok" : "dp-crown-req",
|
|
2460
|
+
(row.met ? "\u2713 " : "\u2717 ") + row.label + " " + Math.min(row.have, row.need) + "/" + row.need
|
|
2461
|
+
));
|
|
2462
|
+
}
|
|
2463
|
+
side.appendChild(chips);
|
|
2464
|
+
}
|
|
2465
|
+
top.appendChild(side);
|
|
2466
|
+
box.appendChild(top);
|
|
2467
|
+
if (!form.current) {
|
|
2468
|
+
var go = button("dp-btn dp-btn-wide", { "data-crown": form.key }, function() {
|
|
2469
|
+
ui.send("crown", { form: form.key });
|
|
2470
|
+
});
|
|
2471
|
+
go.textContent = form.ready ? "\u{1F451} \u52A0\u5195" : "\u6761\u4EF6\u9F50\u4E86\u5C31\u80FD\u52A0\u5195";
|
|
2472
|
+
go.disabled = !form.ready;
|
|
2473
|
+
box.appendChild(go);
|
|
2474
|
+
}
|
|
2475
|
+
return box;
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// src/client/tabs/home.js
|
|
2479
|
+
var APP_COLOR = {
|
|
2480
|
+
status: "green",
|
|
2481
|
+
card: "pink",
|
|
2482
|
+
crown: "purple",
|
|
2483
|
+
study: "yellow",
|
|
2484
|
+
work: "orange",
|
|
2485
|
+
shop: "red",
|
|
2486
|
+
travel: "blue",
|
|
2487
|
+
bag: "teal",
|
|
2488
|
+
update: "lime",
|
|
2489
|
+
dev: "brown"
|
|
2490
|
+
};
|
|
2491
|
+
function renderHome(ui, apps) {
|
|
2492
|
+
var p = ui.view.pig;
|
|
2493
|
+
var head = el("div", "dp-title");
|
|
2494
|
+
head.appendChild(el("b", null, p.name + (p.sex !== null ? " " + p.sex.symbol : "") + " Lv." + p.level.level));
|
|
2495
|
+
head.appendChild(el("span", null, "\u{1FA99} " + p.coins));
|
|
2496
|
+
ui.content.appendChild(head);
|
|
2497
|
+
var grid = tileGrid();
|
|
2498
|
+
for (var i = 0; i < apps.length; i += 1) {
|
|
2499
|
+
(function(app) {
|
|
2500
|
+
grid.appendChild(tile({
|
|
2501
|
+
emoji: app.emoji,
|
|
2502
|
+
label: app.label,
|
|
2503
|
+
color: APP_COLOR[app.key] ?? "blue",
|
|
2504
|
+
tag: alertFor(ui, app.key),
|
|
2505
|
+
data: { "data-app": app.key },
|
|
2506
|
+
onPick: function() {
|
|
2507
|
+
ui.select(app.key);
|
|
2508
|
+
}
|
|
2509
|
+
}));
|
|
2510
|
+
})(apps[i]);
|
|
2511
|
+
}
|
|
2512
|
+
ui.content.appendChild(grid);
|
|
2513
|
+
}
|
|
2514
|
+
function alertFor(ui, key) {
|
|
2515
|
+
var p = ui.view.pig;
|
|
2516
|
+
if (key === "status") {
|
|
2517
|
+
if (ui.view.dead) return "\u8D70\u4E86";
|
|
2518
|
+
if (p.illness !== null) return "\u751F\u75C5";
|
|
2519
|
+
if (ui.view.activity !== null) return "\u5728\u5916\u9762";
|
|
2520
|
+
return "";
|
|
2521
|
+
}
|
|
2522
|
+
var icon = ui.icons[key];
|
|
2523
|
+
return icon !== void 0 && icon.getAttribute("data-alert") === "true" ? "!" : "";
|
|
2524
|
+
}
|
|
2525
|
+
function appHeader(ui, app, info) {
|
|
2526
|
+
var row = el("div", "dp-drill dp-app-head");
|
|
2527
|
+
var back = el("button", "dp-drill-back");
|
|
2528
|
+
back.setAttribute("data-home", "true");
|
|
2529
|
+
back.textContent = "\u2039";
|
|
2530
|
+
back.addEventListener("click", function(event) {
|
|
2531
|
+
if (event && typeof event.stopPropagation === "function") event.stopPropagation();
|
|
2532
|
+
ui.select("home");
|
|
2533
|
+
});
|
|
2534
|
+
row.appendChild(back);
|
|
2535
|
+
row.appendChild(el("b", "dp-drill-title", app.emoji + " " + app.label));
|
|
2536
|
+
if (info) row.appendChild(el("span", "dp-drill-info", info));
|
|
2537
|
+
ui.content.appendChild(row);
|
|
2538
|
+
}
|
|
2539
|
+
|
|
2540
|
+
// src/client/tabs/update.js
|
|
2541
|
+
var state = {
|
|
2542
|
+
current: null,
|
|
2543
|
+
list: null,
|
|
2544
|
+
error: null,
|
|
2545
|
+
loading: false,
|
|
2546
|
+
pick: null,
|
|
2547
|
+
busy: null,
|
|
2548
|
+
fraction: 0,
|
|
2549
|
+
message: null,
|
|
2550
|
+
listening: false
|
|
2551
|
+
};
|
|
2552
|
+
function updatesBridge() {
|
|
2553
|
+
var shell = (
|
|
2554
|
+
/** @type {any} */
|
|
2555
|
+
window.piggyShell
|
|
2556
|
+
);
|
|
2557
|
+
return shell && shell.updates ? shell : null;
|
|
2558
|
+
}
|
|
2559
|
+
function refresh(ui) {
|
|
2560
|
+
var shell = updatesBridge();
|
|
2561
|
+
if (shell === null || state.loading) return;
|
|
2562
|
+
state.loading = true;
|
|
2563
|
+
state.error = null;
|
|
2564
|
+
if (!state.listening) {
|
|
2565
|
+
state.listening = true;
|
|
2566
|
+
shell.updates.onProgress(function(fraction) {
|
|
2567
|
+
state.fraction = fraction;
|
|
2568
|
+
ui.renderContent();
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
Promise.all([shell.updates.current(), shell.updates.list()]).then(function(got) {
|
|
2572
|
+
state.current = got[0];
|
|
2573
|
+
if (got[1] && got[1].ok) state.list = got[1].releases;
|
|
2574
|
+
else state.error = got[1] && got[1].reason || "\u6CA1\u95EE\u5230";
|
|
2575
|
+
}, function() {
|
|
2576
|
+
state.error = "\u6CA1\u95EE\u5230";
|
|
2577
|
+
}).then(function() {
|
|
2578
|
+
state.loading = false;
|
|
2579
|
+
ui.renderContent();
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
function install(ui, version) {
|
|
2583
|
+
var shell = updatesBridge();
|
|
2584
|
+
if (shell === null) return;
|
|
2585
|
+
state.busy = version;
|
|
2586
|
+
state.fraction = 0;
|
|
2587
|
+
state.message = null;
|
|
2588
|
+
ui.renderContent();
|
|
2589
|
+
shell.updates.install(version).then(function(result) {
|
|
2590
|
+
state.message = result.ok ? "\u6362\u597D\u4E86\uFF0C\u732A\u9A6C\u4E0A\u56DE\u6765\u2026" : result.reason;
|
|
2591
|
+
if (!result.ok) state.busy = null;
|
|
2592
|
+
ui.renderContent();
|
|
2593
|
+
});
|
|
2594
|
+
}
|
|
2595
|
+
function rollback(ui) {
|
|
2596
|
+
var shell = updatesBridge();
|
|
2597
|
+
if (shell === null) return;
|
|
2598
|
+
state.busy = "rollback";
|
|
2599
|
+
ui.renderContent();
|
|
2600
|
+
shell.updates.rollback().then(function(result) {
|
|
2601
|
+
state.message = result.ok ? "\u56DE\u53BB\u4E86\uFF0C\u732A\u9A6C\u4E0A\u56DE\u6765\u2026" : result.reason;
|
|
2602
|
+
if (!result.ok) state.busy = null;
|
|
2603
|
+
ui.renderContent();
|
|
2604
|
+
});
|
|
2605
|
+
}
|
|
2606
|
+
function renderUpdateTab(ui) {
|
|
2607
|
+
if (updatesBridge() === null) {
|
|
2608
|
+
ui.content.appendChild(el("div", "dp-empty", "\u684C\u9762\u7248\u624D\u6709\u8FD9\u4E2A"));
|
|
2609
|
+
return;
|
|
2610
|
+
}
|
|
2611
|
+
if (state.current === null && state.list === null && state.error === null) refresh(ui);
|
|
2612
|
+
var cur = state.current;
|
|
2613
|
+
var head = el("div", "dp-pick dp-tile-card dp-update-now");
|
|
2614
|
+
head.appendChild(el("div", "dp-pick-head", cur === null ? "\u6B63\u5728\u770B\u73B0\u5728\u7684\u7248\u672C\u2026" : "\u73B0\u5728 v" + cur.version + (cur.bundled ? "\uFF08\u5B89\u88C5\u5305\u81EA\u5E26\uFF09" : "")));
|
|
2615
|
+
if (cur !== null) head.appendChild(el("div", "dp-dim", "\u5B89\u88C5\u5305 " + cur.shell));
|
|
2616
|
+
if (state.message !== null) head.appendChild(el("div", "dp-req", state.message));
|
|
2617
|
+
if (state.busy !== null && state.message === null) {
|
|
2618
|
+
head.appendChild(el("div", "dp-dim", state.busy === "rollback" ? "\u6B63\u5728\u6362\u56DE\u53BB\u2026" : "\u4E0B\u8F7D\u4E2D " + Math.round(state.fraction * 100) + "%"));
|
|
2619
|
+
}
|
|
2620
|
+
var latest = state.list === null ? null : state.list.find(function(r) {
|
|
2621
|
+
return r.blocked === null && !r.prerelease;
|
|
2622
|
+
}) || null;
|
|
2623
|
+
if (latest !== null && cur !== null && !latest.current) {
|
|
2624
|
+
var up = button("dp-btn dp-btn-wide", { "data-update-latest": latest.version }, function() {
|
|
2625
|
+
install(ui, latest.version);
|
|
2626
|
+
});
|
|
2627
|
+
up.textContent = "\u2B06\uFE0F \u66F4\u65B0\u5230\u6700\u65B0 v" + latest.version;
|
|
2628
|
+
up.disabled = state.busy !== null;
|
|
2629
|
+
head.appendChild(up);
|
|
2630
|
+
} else if (latest !== null) {
|
|
2631
|
+
head.appendChild(el("div", "dp-req dp-req-ok", "\u2713 \u5DF2\u7ECF\u662F\u6700\u65B0"));
|
|
2632
|
+
}
|
|
2633
|
+
ui.content.appendChild(head);
|
|
2634
|
+
if (state.loading && state.list === null) ui.content.appendChild(el("div", "dp-empty", "\u6B63\u5728\u95EE GitHub\u2026"));
|
|
2635
|
+
if (state.error !== null) {
|
|
2636
|
+
ui.content.appendChild(el("div", "dp-empty", state.error));
|
|
2637
|
+
var again = button("dp-btn dp-btn-wide", { "data-update-retry": "" }, function() {
|
|
2638
|
+
refresh(ui);
|
|
2639
|
+
});
|
|
2640
|
+
again.textContent = "\u518D\u8BD5\u4E00\u6B21";
|
|
2641
|
+
ui.content.appendChild(again);
|
|
2642
|
+
}
|
|
2643
|
+
if (state.list !== null) renderList(ui, state.list);
|
|
2644
|
+
if (cur !== null && cur.previous !== null) {
|
|
2645
|
+
var back = button("dp-btn dp-btn-wide dp-update-back", { "data-update-rollback": "" }, function() {
|
|
2646
|
+
rollback(ui);
|
|
2647
|
+
});
|
|
2648
|
+
back.textContent = "\u21A9\uFE0F \u56DE\u5230\u4E0A\u4E00\u4E2A\u7248\u672C v" + cur.previous;
|
|
2649
|
+
back.disabled = state.busy !== null;
|
|
2650
|
+
ui.content.appendChild(back);
|
|
2651
|
+
}
|
|
2652
|
+
}
|
|
2653
|
+
function renderList(ui, list) {
|
|
2654
|
+
if (list.length === 0) {
|
|
2655
|
+
ui.content.appendChild(el("div", "dp-empty", "GitHub \u4E0A\u8FD8\u6CA1\u6709\u80FD\u70ED\u66F4\u65B0\u7684\u7248\u672C"));
|
|
2656
|
+
return;
|
|
2657
|
+
}
|
|
2658
|
+
var grid = tileGrid();
|
|
2659
|
+
var picked = null;
|
|
2660
|
+
for (var i = 0; i < list.length; i += 1) {
|
|
2661
|
+
(function(release, first) {
|
|
2662
|
+
var active = state.pick === release.version;
|
|
2663
|
+
if (active) picked = release;
|
|
2664
|
+
grid.appendChild(tile({
|
|
2665
|
+
emoji: release.current ? "\u{1F416}" : "\u{1F4E6}",
|
|
2666
|
+
label: "v" + release.version,
|
|
2667
|
+
color: "lime",
|
|
2668
|
+
soft: true,
|
|
2669
|
+
active,
|
|
2670
|
+
note: release.date,
|
|
2671
|
+
badge: first && release.blocked === null ? "\u65B0" : "",
|
|
2672
|
+
tag: release.current ? "\u5728\u7528" : release.blocked !== null ? "\u{1F512}" : "",
|
|
2673
|
+
dim: release.blocked !== null,
|
|
2674
|
+
data: { "data-release": release.version },
|
|
2675
|
+
onPick: function() {
|
|
2676
|
+
state.pick = active ? null : release.version;
|
|
2677
|
+
ui.renderContent();
|
|
2678
|
+
}
|
|
2679
|
+
}));
|
|
2680
|
+
})(list[i], i === 0);
|
|
2681
|
+
}
|
|
2682
|
+
ui.content.appendChild(grid);
|
|
2683
|
+
if (picked !== null) ui.content.appendChild(details(ui, picked));
|
|
2684
|
+
}
|
|
2685
|
+
function details(ui, release) {
|
|
2686
|
+
var box = el("div", "dp-pick dp-tile-card dp-update-detail");
|
|
2687
|
+
box.appendChild(el("div", "dp-pick-head", "v" + release.version + (release.date ? " \xB7 " + release.date : "") + (release.prerelease ? " \xB7 \u9884\u89C8\u7248" : "")));
|
|
2688
|
+
if (release.notes) box.appendChild(el("div", "dp-update-notes", release.notes));
|
|
2689
|
+
var go = button("dp-btn dp-btn-wide", { "data-update-install": release.version }, function() {
|
|
2690
|
+
if (release.blocked === "shell") updatesBridge().openPage(release.page);
|
|
2691
|
+
else install(ui, release.version);
|
|
2692
|
+
});
|
|
2693
|
+
if (release.current) {
|
|
2694
|
+
go.textContent = "\u6B63\u5728\u7528\u8FD9\u4E2A";
|
|
2695
|
+
go.disabled = true;
|
|
2696
|
+
} else if (release.blocked === "shell") {
|
|
2697
|
+
box.appendChild(el("div", "dp-req", "\u2717 \u8981\u5148\u88C5 " + release.minShell + " \u4EE5\u4E0A\u7684\u5B89\u88C5\u5305"));
|
|
2698
|
+
go.textContent = "\u53BB\u4E0B\u8F7D\u65B0\u5B89\u88C5\u5305";
|
|
2699
|
+
} else if (release.blocked === "save") {
|
|
2700
|
+
box.appendChild(el("div", "dp-req", "\u2717 \u5B58\u6863\u592A\u65B0\uFF0C\u8FD9\u4E2A\u7248\u672C\u8BFB\u4E0D\u4E86"));
|
|
2701
|
+
go.textContent = "\u6362\u4E0D\u4E86";
|
|
2702
|
+
go.disabled = true;
|
|
2703
|
+
} else {
|
|
2704
|
+
go.textContent = "\u6362\u5230\u8FD9\u4E2A\u7248\u672C";
|
|
2705
|
+
go.disabled = state.busy !== null;
|
|
2706
|
+
}
|
|
2707
|
+
box.appendChild(go);
|
|
2708
|
+
return box;
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
// src/client/panel.js
|
|
2712
|
+
var URGENT_KINDS = ["sick", "worse", "death", "cured", "revived"];
|
|
2713
|
+
function createPanel(ctx) {
|
|
2714
|
+
var AWAY_LINE = {
|
|
2715
|
+
work: "\u5728\u5FD9",
|
|
2716
|
+
study: "\u5728\u5FF5\u4E66",
|
|
2717
|
+
trip: "\u5728\u8DEF\u4E0A"
|
|
2718
|
+
};
|
|
2719
|
+
function setOpen(next) {
|
|
2720
|
+
ctx.isOpen = next;
|
|
2721
|
+
ctx.host.setAttribute("data-open", next ? "true" : "false");
|
|
2722
|
+
ctx.card.hidden = !next;
|
|
2723
|
+
ctx.hud.hidden = !next;
|
|
2724
|
+
if (!next) ctx.bubble.hidden = true;
|
|
2725
|
+
writeStore(OPEN_KEY, next ? "true" : "false");
|
|
2726
|
+
if (next) {
|
|
2727
|
+
renderContent();
|
|
2728
|
+
ctx.fitPanel();
|
|
2729
|
+
} else {
|
|
2730
|
+
ctx.card.style.right = "";
|
|
2731
|
+
ctx.card.style.top = "auto";
|
|
2732
|
+
ctx.card.style.bottom = "";
|
|
2733
|
+
ctx.card.style.maxHeight = "";
|
|
2734
|
+
ctx.card.style.maxWidth = "";
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
function select(next) {
|
|
2738
|
+
ctx.tab = next;
|
|
2739
|
+
ctx.picker = null;
|
|
2740
|
+
if (next in ctx.drill) {
|
|
2741
|
+
ctx.drill[next] = null;
|
|
2742
|
+
ctx.drill.pick = null;
|
|
2743
|
+
}
|
|
2744
|
+
renderContent();
|
|
2745
|
+
for (var k in ctx.icons) ctx.icons[k].setAttribute("data-active", k === ctx.tab ? "true" : "false");
|
|
2746
|
+
}
|
|
2747
|
+
function renderContent() {
|
|
2748
|
+
var scrollTop = ctx.content.scrollTop;
|
|
2749
|
+
paintContent();
|
|
2750
|
+
ctx.content.scrollTop = scrollTop;
|
|
2751
|
+
}
|
|
2752
|
+
function paintContent() {
|
|
2753
|
+
ctx.content.textContent = "";
|
|
2754
|
+
for (var k = 0; k < TABS.length; k += 1) {
|
|
2755
|
+
ctx.icons[TABS[k].key].setAttribute("data-active", TABS[k].key === ctx.tab ? "true" : "false");
|
|
2756
|
+
}
|
|
2757
|
+
if (ctx.host.getAttribute("data-open") !== "true") return;
|
|
2758
|
+
if (ctx.view.legacy) {
|
|
2759
|
+
var legacy = el("div", "dp-alert dp-legacy");
|
|
2760
|
+
legacy.appendChild(el("b", null, "\u26A0\uFE0F \u5BBF\u4E3B\u662F\u65E7\u7248\u672C"));
|
|
2761
|
+
legacy.appendChild(el("div", null, "\u91D1\u5E01\u3001\u5065\u5EB7\u3001\u6253\u5DE5\u3001\u5546\u5E97\u8FD9\u4E9B\u662F\u65B0\u589E\u7684\uFF0C\u91CD\u542F dsh\uFF08\u4E0D\u662F\u5237\u65B0\u9875\u9762\uFF09\u4E4B\u540E\u624D\u4F1A\u51FA\u73B0\u3002"));
|
|
2762
|
+
ctx.content.appendChild(legacy);
|
|
2763
|
+
}
|
|
2764
|
+
if (ctx.view.pig === null) {
|
|
2765
|
+
ctx.content.appendChild(el("div", "dp-empty", "\u95E8\u53E3\u653E\u7740\u4E00\u4E2A\u7EB8\u76D2\uFF0C\u91CC\u9762\u7AB8\u7AB8\u7AA3\u7AA3 \u{1F4E6}"));
|
|
2766
|
+
var grid = el("div", "dp-actions");
|
|
2767
|
+
var hatch = button("dp-btn dp-btn-wide", { "data-action": "hatch" }, function() {
|
|
2768
|
+
ctx.send("hatch");
|
|
2769
|
+
});
|
|
2770
|
+
hatch.appendChild(el("span", null, "\u{1F95A}"));
|
|
2771
|
+
hatch.appendChild(el("span", null, "\u62C6\u5F00\u7EB8\u76D2"));
|
|
2772
|
+
grid.appendChild(hatch);
|
|
2773
|
+
ctx.content.appendChild(grid);
|
|
2774
|
+
return;
|
|
2775
|
+
}
|
|
2776
|
+
var apps = TABS.concat(updatesBridge() !== null ? [UPDATE_TAB] : [], ctx.devMode ? [DEV_TAB] : []);
|
|
2777
|
+
if (ctx.tab === "home") {
|
|
2778
|
+
renderHome(ctx, apps);
|
|
2779
|
+
ctx.fitPanel();
|
|
2780
|
+
return;
|
|
2781
|
+
}
|
|
2782
|
+
var drilled = ctx.tab in ctx.drill && ctx.drill[ctx.tab] !== null;
|
|
2783
|
+
var app = apps.find(function(entry) {
|
|
2784
|
+
return entry.key === ctx.tab;
|
|
2785
|
+
});
|
|
2786
|
+
if (app !== void 0 && !drilled) appHeader(ctx, app, ctx.tab === "shop" ? "\u{1FA99} " + ctx.view.pig.coins : "");
|
|
2787
|
+
if (ctx.tab === "status") renderStatusTab(ctx);
|
|
2788
|
+
else if (ctx.tab === "card") renderCardTab(ctx);
|
|
2789
|
+
else if (ctx.tab === "crown") renderCrownTab(ctx);
|
|
2790
|
+
else if (ctx.tab === "study") renderStudyTab(ctx);
|
|
2791
|
+
else if (ctx.tab === "work") renderWorkTab(ctx);
|
|
2792
|
+
else if (ctx.tab === "shop") renderShopTab(ctx);
|
|
2793
|
+
else if (ctx.tab === "travel") renderTravelTab(ctx);
|
|
2794
|
+
else if (ctx.tab === "dev") renderDevTab(ctx);
|
|
2795
|
+
else if (ctx.tab === "update") renderUpdateTab(ctx);
|
|
2796
|
+
else renderBagTab(ctx);
|
|
2797
|
+
ctx.fitPanel();
|
|
2798
|
+
}
|
|
2799
|
+
function render(next) {
|
|
2800
|
+
ctx.view = normalize(next);
|
|
2801
|
+
var stageEntry = null;
|
|
2802
|
+
var firstOpen = null;
|
|
2803
|
+
for (var s = 0; s < ctx.view.stages.length; s += 1) {
|
|
2804
|
+
var entry = ctx.view.stages[s];
|
|
2805
|
+
if (entry.unlocked !== false && firstOpen === null) firstOpen = entry.key;
|
|
2806
|
+
if (entry.key === ctx.stage) stageEntry = entry;
|
|
2807
|
+
}
|
|
2808
|
+
if (!ctx.stagePicked && firstOpen !== null && (stageEntry === null || stageEntry.unlocked === false)) ctx.stage = firstOpen;
|
|
2809
|
+
ctx.host.setAttribute("data-dead", ctx.view.dead ? "true" : "false");
|
|
2810
|
+
ctx.host.setAttribute("data-open", ctx.isOpen ? "true" : "false");
|
|
2811
|
+
ctx.host.setAttribute("data-dev", ctx.devMode ? "true" : "false");
|
|
2812
|
+
ctx.host.setAttribute("data-away", ctx.view.activity === null ? "false" : ctx.view.activity.kind);
|
|
2813
|
+
if (ctx.view.activity === null) {
|
|
2814
|
+
ctx.work.hidden = true;
|
|
2815
|
+
} else {
|
|
2816
|
+
ctx.work.hidden = false;
|
|
2817
|
+
ctx.prop.textContent = ctx.view.activity.emoji;
|
|
2818
|
+
ctx.progressFill.style.width = ctx.view.activity.progress + "%";
|
|
2819
|
+
ctx.work.setAttribute("data-kind", ctx.view.activity.kind);
|
|
2820
|
+
ctx.work.title = (AWAY_LINE[ctx.view.activity.kind] ?? "\u5728\u5916\u9762") + "\uFF1A" + ctx.view.activity.label;
|
|
2821
|
+
}
|
|
2822
|
+
if (ctx.view.hatched !== true) {
|
|
2823
|
+
ctx.pigArt.hidden = true;
|
|
2824
|
+
ctx.pigArt.removeAttribute("src");
|
|
2825
|
+
ctx.pigEmoji.hidden = false;
|
|
2826
|
+
ctx.pigEmoji.textContent = ctx.view.boxStage.emoji;
|
|
2827
|
+
ctx.pig.removeAttribute("data-art");
|
|
2828
|
+
ctx.pig.setAttribute("data-mood", "box");
|
|
2829
|
+
ctx.host.style.setProperty("--pig-size", ctx.view.boxStage.size + "px");
|
|
2830
|
+
ctx.soul.hidden = true;
|
|
2831
|
+
ctx.host.setAttribute("data-soul", "false");
|
|
2832
|
+
ctx.host.setAttribute("data-faded", "false");
|
|
2833
|
+
ctx.host.setAttribute("data-unhatched", "true");
|
|
2834
|
+
ctx.pokeHint.hidden = false;
|
|
2835
|
+
ctx.hudName.textContent = "\u4E00\u4E2A" + ctx.view.boxStage.label;
|
|
2836
|
+
ctx.hudCoins.textContent = "\u70B9\u5F00\u62C6\u5F00\u5B83";
|
|
2837
|
+
ctx.hudHealth.textContent = "";
|
|
2838
|
+
ctx.lastStage = null;
|
|
2839
|
+
} else {
|
|
2840
|
+
const pigStage = ctx.view.pig.stage;
|
|
2841
|
+
if (pigStage.art !== null) {
|
|
2842
|
+
ctx.pigArt.hidden = false;
|
|
2843
|
+
ctx.pigEmoji.hidden = true;
|
|
2844
|
+
ctx.pig.setAttribute("data-art", pigStage.art);
|
|
2845
|
+
ctx.pig.setAttribute("data-art-actions", pigStage.actionArt ? "true" : "false");
|
|
2846
|
+
ctx.host.setAttribute("data-art-actions", pigStage.actionArt ? "true" : "false");
|
|
2847
|
+
ctx.pig.setAttribute("data-activity", ctx.view.activity === null ? "" : ctx.view.activity.kind);
|
|
2848
|
+
syncPigArt(ctx.pig, ctx.pigArt);
|
|
2849
|
+
} else {
|
|
2850
|
+
ctx.pigArt.hidden = true;
|
|
2851
|
+
ctx.pigArt.removeAttribute("src");
|
|
2852
|
+
ctx.pigEmoji.hidden = false;
|
|
2853
|
+
ctx.pigEmoji.textContent = pigStage.emoji;
|
|
2854
|
+
ctx.pig.removeAttribute("data-art");
|
|
2855
|
+
ctx.pig.removeAttribute("data-art-actions");
|
|
2856
|
+
ctx.host.removeAttribute("data-art-actions");
|
|
2857
|
+
}
|
|
2858
|
+
ctx.host.style.setProperty("--pig-size", pigStage.size + "px");
|
|
2859
|
+
ctx.pig.setAttribute("data-mood", ctx.view.pig.mood);
|
|
2860
|
+
ctx.host.setAttribute("data-soul", ctx.view.pig.soul ? "true" : "false");
|
|
2861
|
+
ctx.host.setAttribute("data-faded", pigStage.faded ? "true" : "false");
|
|
2862
|
+
ctx.host.setAttribute("data-unhatched", "false");
|
|
2863
|
+
ctx.pokeHint.hidden = true;
|
|
2864
|
+
ctx.soul.hidden = ctx.view.pig.soul !== true;
|
|
2865
|
+
ctx.pig.setAttribute("data-stage", pigStage.key);
|
|
2866
|
+
ctx.dressSlots.textContent = "";
|
|
2867
|
+
for (var wd = 0; wd < ctx.view.dress.length; wd += 1) {
|
|
2868
|
+
var piece = ctx.view.dress[wd];
|
|
2869
|
+
if (!piece.worn || piece.slot === "") continue;
|
|
2870
|
+
if (pigStage.hides.indexOf(piece.slot) >= 0) continue;
|
|
2871
|
+
var node = el("span", "dp-slot", piece.emoji);
|
|
2872
|
+
node.setAttribute("data-slot", piece.slot);
|
|
2873
|
+
ctx.dressSlots.appendChild(node);
|
|
2874
|
+
}
|
|
2875
|
+
ctx.hudName.textContent = ctx.view.pig.name + (ctx.view.pig.sex !== null ? " " + ctx.view.pig.sex.symbol : "") + " Lv." + ctx.view.pig.level.level + " \xB7 " + pigStage.label + (ctx.view.pig.ageLabel ? " \xB7 " + ctx.view.pig.ageLabel : "") + (ctx.view.pig.ageForced ? " \u{1F527}" : "");
|
|
2876
|
+
ctx.hudCoins.textContent = "\u{1FA99} " + ctx.view.pig.coins;
|
|
2877
|
+
ctx.hudHealth.textContent = "\u{1F49A} " + ctx.view.pig.health + "/" + ctx.view.maxHealth;
|
|
2878
|
+
if (ctx.lastStage !== null && pigStage.key !== ctx.lastStage) {
|
|
2879
|
+
ctx.react("levelup", 950);
|
|
2880
|
+
ctx.burst(["\u2728", "\u{1F389}", "\u2B50"], 4);
|
|
2881
|
+
ctx.showBubble("\u6211\u957F\u5927\u5566\uFF01" + pigStage.emoji, 2600);
|
|
2882
|
+
}
|
|
2883
|
+
ctx.lastStage = pigStage.key;
|
|
2884
|
+
}
|
|
2885
|
+
var daily = ctx.view.daily;
|
|
2886
|
+
var dailyAction = daily.canSignIn ? "signIn" : daily.unclaimed > 0 ? "openGift" : null;
|
|
2887
|
+
ctx.dailyHint.hidden = dailyAction === null || ctx.view.pig === null;
|
|
2888
|
+
if (dailyAction !== null) {
|
|
2889
|
+
ctx.dailyHint.textContent = dailyAction === "signIn" ? "\u{1F4C5}" : "\u{1F381}";
|
|
2890
|
+
ctx.dailyHint.title = dailyAction === "signIn" ? "\u7B7E\u5230\u7B2C " + daily.signInDay + "/" + daily.cycle + " \u5929" : "\u6709 " + daily.unclaimed + " \u4E2A\u5728\u7EBF\u793C\u5305";
|
|
2891
|
+
ctx.dailyHint.setAttribute("data-action", dailyAction);
|
|
2892
|
+
}
|
|
2893
|
+
var studyStage = null;
|
|
2894
|
+
for (var st = 0; st < ctx.view.stages.length; st += 1) {
|
|
2895
|
+
if (ctx.view.stages[st].key === ctx.stage) studyStage = ctx.view.stages[st];
|
|
2896
|
+
}
|
|
2897
|
+
var studyOpen = ctx.view.canGoOut && (studyStage === null || studyStage.unlocked !== false);
|
|
2898
|
+
var hasCourse = studyOpen && ctx.view.subjects.some(function(subject) {
|
|
2899
|
+
var onStage = studyStage === null || studyStage.subjects.length === 0 || studyStage.subjects.indexOf(subject.key) >= 0;
|
|
2900
|
+
return onStage && subject.affordable;
|
|
2901
|
+
});
|
|
2902
|
+
ctx.icons.study.setAttribute("data-alert", hasCourse ? "true" : "false");
|
|
2903
|
+
ctx.icons.shop.setAttribute("data-alert", ctx.view.pig !== null && ctx.view.pig.illness !== null ? "true" : "false");
|
|
2904
|
+
ctx.icons.crown.setAttribute("data-alert", ctx.view.forms !== null && ctx.view.forms.forms.some(function(f) {
|
|
2905
|
+
return f.ready;
|
|
2906
|
+
}) ? "true" : "false");
|
|
2907
|
+
ctx.icons.travel.setAttribute("data-alert", ctx.view.pig !== null && ctx.view.pig.coins >= 400 ? "true" : "false");
|
|
2908
|
+
for (var i = 0; i < ctx.view.pending.length; i += 1) {
|
|
2909
|
+
var event = ctx.view.pending[i];
|
|
2910
|
+
if (event.id > 0 ? event.id <= ctx.lastPendingId : event.at <= ctx.lastPendingAt) continue;
|
|
2911
|
+
if (event.id > 0) ctx.lastPendingId = event.id;
|
|
2912
|
+
ctx.lastPendingAt = Math.max(ctx.lastPendingAt, event.at);
|
|
2913
|
+
if (event.kind === "line") {
|
|
2914
|
+
showPigLine(event);
|
|
2915
|
+
continue;
|
|
2916
|
+
}
|
|
2917
|
+
if (ctx.view.dialogue.quiet && URGENT_KINDS.indexOf(event.kind) < 0) continue;
|
|
2918
|
+
ctx.toast(str(event.text, "\u732A\u6709\u65B0\u6D88\u606F"));
|
|
2919
|
+
if (event.kind === "coronation") {
|
|
2920
|
+
ctx.react("levelup", 950);
|
|
2921
|
+
ctx.burst(["\u{1F451}", "\u2728"], 3);
|
|
2922
|
+
} else if (event.kind === "levelup") {
|
|
2923
|
+
ctx.react("levelup", 950);
|
|
2924
|
+
ctx.burst(["\u2728", "\u{1F389}"], 3);
|
|
2925
|
+
} else if (event.kind === "cured") {
|
|
2926
|
+
ctx.react("cure", 900);
|
|
2927
|
+
ctx.burst(["\u{1F49A}", "\u2728"], 3);
|
|
2928
|
+
} else if (event.kind === "death") ctx.react("refuse", 700);
|
|
2929
|
+
else if (event.kind === "work") {
|
|
2930
|
+
ctx.react("away", 900);
|
|
2931
|
+
ctx.burst(["\u{1FA99}", "\u{1F4B0}"], 3);
|
|
2932
|
+
} else if (event.kind === "study") {
|
|
2933
|
+
ctx.react("away", 900);
|
|
2934
|
+
ctx.burst(["\u{1F4DA}", "\u2728"], 3);
|
|
2935
|
+
} else if (event.kind === "trip") {
|
|
2936
|
+
ctx.react("away", 900);
|
|
2937
|
+
ctx.burst(["\u{1F9F3}", "\u{1F381}"], 3);
|
|
2938
|
+
}
|
|
2939
|
+
}
|
|
2940
|
+
if ((ctx.ownerEdit !== null || ctx.pigNameEdit !== null) && ctx.tab === "status") return;
|
|
2941
|
+
if (ctx.cardEdit !== null && ctx.tab === "card") return;
|
|
2942
|
+
renderContent();
|
|
2943
|
+
}
|
|
2944
|
+
function showPigLine(event) {
|
|
2945
|
+
var lineId = event.id;
|
|
2946
|
+
ctx.showLine(event.text, event.replies, function(index) {
|
|
2947
|
+
ctx.send("reply", { line: lineId, index });
|
|
2948
|
+
});
|
|
2949
|
+
}
|
|
2950
|
+
return { setOpen, select, renderContent, render };
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
// src/client/scene.js
|
|
2954
|
+
function createScene() {
|
|
2955
|
+
var font = document.createElement("link");
|
|
2956
|
+
font.rel = "stylesheet";
|
|
2957
|
+
font.href = "https://fonts.googleapis.com/css2?family=Nunito:wght@400;500;600;700;800;900&family=Noto+Sans+SC:wght@400;500;700&display=swap";
|
|
2958
|
+
document.head.appendChild(font);
|
|
2959
|
+
var style = document.createElement("style");
|
|
2960
|
+
style.textContent = CSS;
|
|
2961
|
+
document.head.appendChild(style);
|
|
2962
|
+
var host = document.createElement("div");
|
|
2963
|
+
host.setAttribute(MOUNTED, "");
|
|
2964
|
+
var card = el("div", "dp-card");
|
|
2965
|
+
var scene = el("div", "dp-scene");
|
|
2966
|
+
var hud = el("div", "dp-hud");
|
|
2967
|
+
var hudName = el("div", null, "\u732A\u732A");
|
|
2968
|
+
var hudCoins = el("div", null, "\u{1FA99} 0");
|
|
2969
|
+
var hudHealth = el("div", null, "\u{1F49A} 5/5");
|
|
2970
|
+
hud.appendChild(hudName);
|
|
2971
|
+
hud.appendChild(hudCoins);
|
|
2972
|
+
hud.appendChild(hudHealth);
|
|
2973
|
+
scene.appendChild(hud);
|
|
2974
|
+
var bubble = el("div", "dp-bubble", "");
|
|
2975
|
+
bubble.hidden = true;
|
|
2976
|
+
scene.appendChild(bubble);
|
|
2977
|
+
var work = el("div", "dp-work");
|
|
2978
|
+
var prop = el("span", "dp-prop", "\u{1F4BC}");
|
|
2979
|
+
var progressWrap = el("div", "dp-progress");
|
|
2980
|
+
var progressFill = document.createElement("i");
|
|
2981
|
+
progressWrap.appendChild(progressFill);
|
|
2982
|
+
work.appendChild(prop);
|
|
2983
|
+
work.appendChild(progressWrap);
|
|
2984
|
+
work.hidden = true;
|
|
2985
|
+
scene.appendChild(work);
|
|
2986
|
+
var pokeHint = el("div", "dp-poke-hint");
|
|
2987
|
+
pokeHint.appendChild(el("span", null, "\u{1F446}"));
|
|
2988
|
+
pokeHint.appendChild(el("span", null, "\u6233\u4E09\u4E0B"));
|
|
2989
|
+
pokeHint.hidden = true;
|
|
2990
|
+
scene.appendChild(pokeHint);
|
|
2991
|
+
var dailyHint = el("button", "dp-daily");
|
|
2992
|
+
dailyHint.hidden = true;
|
|
2993
|
+
scene.appendChild(dailyHint);
|
|
2994
|
+
var soul = el("span", "dp-soul", "\u{1F47B}");
|
|
2995
|
+
soul.hidden = true;
|
|
2996
|
+
scene.appendChild(soul);
|
|
2997
|
+
var pigArt = document.createElement("img");
|
|
2998
|
+
pigArt.className = "dp-pig-img";
|
|
2999
|
+
pigArt.alt = "";
|
|
3000
|
+
pigArt.hidden = true;
|
|
3001
|
+
var pigEmoji = el("span", "dp-pig-emoji", "\u{1F416}");
|
|
3002
|
+
var pig = el("div", "dp-pig");
|
|
3003
|
+
pig.appendChild(pigArt);
|
|
3004
|
+
pig.appendChild(pigEmoji);
|
|
3005
|
+
var dressSlots = el("div", "dp-dress");
|
|
3006
|
+
pig.appendChild(dressSlots);
|
|
3007
|
+
scene.appendChild(pig);
|
|
3008
|
+
scene.title = "\u5DE6\u952E\u6478\u6478 \xB7 \u53F3\u952E\u6253\u5F00\u9762\u677F \xB7 \u62D6\u52A8\u53EF\u79FB\u52A8";
|
|
3009
|
+
var bar = el("div", "dp-bar");
|
|
3010
|
+
var content = el("div", "dp-content");
|
|
3011
|
+
card.appendChild(content);
|
|
3012
|
+
card.appendChild(bar);
|
|
3013
|
+
host.appendChild(card);
|
|
3014
|
+
host.appendChild(scene);
|
|
3015
|
+
if (document.body !== null && document.body !== void 0) {
|
|
3016
|
+
document.body.appendChild(host);
|
|
3017
|
+
} else {
|
|
3018
|
+
document.addEventListener("DOMContentLoaded", function() {
|
|
3019
|
+
try {
|
|
3020
|
+
document.body.appendChild(host);
|
|
3021
|
+
} catch (error) {
|
|
3022
|
+
}
|
|
3023
|
+
}, { once: true });
|
|
3024
|
+
}
|
|
3025
|
+
return { font, style, host, card, scene, hud, hudName, hudCoins, hudHealth, bubble, work, prop, progressWrap, progressFill, pokeHint, dailyHint, soul, pigArt, pigEmoji, pig, dressSlots, bar, content };
|
|
3026
|
+
}
|
|
3027
|
+
|
|
3028
|
+
// src/client/index.js
|
|
3029
|
+
window.__ModuleLoader__.load({
|
|
3030
|
+
id: "dsh-piggy",
|
|
3031
|
+
factory: (require2) => {
|
|
3032
|
+
var module = { exports: {} };
|
|
3033
|
+
var exports = module.exports;
|
|
3034
|
+
var devMode = false;
|
|
3035
|
+
function apply(ctx) {
|
|
3036
|
+
try {
|
|
3037
|
+
return mount();
|
|
3038
|
+
} catch (error) {
|
|
3039
|
+
console.warn("[dsh-piggy] \u6302\u8F7D\u5931\u8D25\uFF0C\u732A\u5148\u9000\u5230\u4E00\u8FB9", error);
|
|
3040
|
+
return () => {
|
|
3041
|
+
};
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
function mount() {
|
|
3045
|
+
if (document.querySelector("[" + MOUNTED + "]") !== null) {
|
|
3046
|
+
console.warn("[dsh-piggy] \u5DF2\u5B58\u5728\u5B9E\u4F8B\uFF0C\u8DF3\u8FC7\u91CD\u590D\u6302\u8F7D");
|
|
3047
|
+
return () => {
|
|
3048
|
+
};
|
|
3049
|
+
}
|
|
3050
|
+
var parts = createScene();
|
|
3051
|
+
var {
|
|
3052
|
+
font,
|
|
3053
|
+
style,
|
|
3054
|
+
host,
|
|
3055
|
+
card,
|
|
3056
|
+
scene,
|
|
3057
|
+
hud,
|
|
3058
|
+
hudName,
|
|
3059
|
+
hudCoins,
|
|
3060
|
+
hudHealth,
|
|
3061
|
+
bubble,
|
|
3062
|
+
work,
|
|
3063
|
+
prop,
|
|
3064
|
+
progressWrap,
|
|
3065
|
+
progressFill,
|
|
3066
|
+
pokeHint,
|
|
3067
|
+
dailyHint,
|
|
3068
|
+
soul,
|
|
3069
|
+
pigArt,
|
|
3070
|
+
pigEmoji,
|
|
3071
|
+
pig,
|
|
3072
|
+
dressSlots,
|
|
3073
|
+
bar,
|
|
3074
|
+
content
|
|
3075
|
+
} = parts;
|
|
3076
|
+
var savedPos = readStore(POSITION_KEY);
|
|
3077
|
+
var userRight = 18;
|
|
3078
|
+
var userBottom = 18;
|
|
3079
|
+
if (savedPos !== null) {
|
|
3080
|
+
try {
|
|
3081
|
+
var parsed = JSON.parse(savedPos);
|
|
3082
|
+
if (parsed && typeof parsed.right === "number") userRight = parsed.right;
|
|
3083
|
+
if (parsed && typeof parsed.bottom === "number") userBottom = parsed.bottom;
|
|
3084
|
+
} catch (error) {
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
host.style.right = userRight + "px";
|
|
3088
|
+
host.style.bottom = userBottom + "px";
|
|
3089
|
+
var icons = {};
|
|
3090
|
+
var view = normalize(null);
|
|
3091
|
+
var tab = "home";
|
|
3092
|
+
var stage = "primary";
|
|
3093
|
+
var stagePicked = false;
|
|
3094
|
+
var drill = { study: null, shop: null, bag: null, work: null, pick: null };
|
|
3095
|
+
var souvenirPick = null;
|
|
3096
|
+
var picker = null;
|
|
3097
|
+
var ownerEdit = null;
|
|
3098
|
+
var pigNameEdit = null;
|
|
3099
|
+
var cardEdit = null;
|
|
3100
|
+
var isOpen = readStore(OPEN_KEY) === "true";
|
|
3101
|
+
var lastStage = null;
|
|
3102
|
+
var lastPendingAt = 0;
|
|
3103
|
+
var lastPendingId = 0;
|
|
3104
|
+
var pollTimer = null;
|
|
3105
|
+
var fx = createEffects({
|
|
3106
|
+
scene,
|
|
3107
|
+
pig,
|
|
3108
|
+
pigArt,
|
|
3109
|
+
card,
|
|
3110
|
+
bubble,
|
|
3111
|
+
isStopped: function() {
|
|
3112
|
+
return stopped;
|
|
3113
|
+
}
|
|
3114
|
+
});
|
|
3115
|
+
var react = fx.react, burst = fx.burst, flash = fx.flash;
|
|
3116
|
+
var showBubble = fx.showBubble, showLine = fx.showLine, toast = fx.toast;
|
|
3117
|
+
var stopped = false;
|
|
3118
|
+
var busy = false;
|
|
3119
|
+
var ctx = {
|
|
3120
|
+
host,
|
|
3121
|
+
card,
|
|
3122
|
+
content,
|
|
3123
|
+
scene,
|
|
3124
|
+
hud,
|
|
3125
|
+
hudName,
|
|
3126
|
+
hudCoins,
|
|
3127
|
+
hudHealth,
|
|
3128
|
+
bubble,
|
|
3129
|
+
work,
|
|
3130
|
+
prop,
|
|
3131
|
+
progressWrap,
|
|
3132
|
+
progressFill,
|
|
3133
|
+
pokeHint,
|
|
3134
|
+
dailyHint,
|
|
3135
|
+
soul,
|
|
3136
|
+
pigArt,
|
|
3137
|
+
pigEmoji,
|
|
3138
|
+
pig,
|
|
3139
|
+
dressSlots,
|
|
3140
|
+
bar,
|
|
3141
|
+
icons,
|
|
3142
|
+
flash,
|
|
3143
|
+
react,
|
|
3144
|
+
burst,
|
|
3145
|
+
showBubble,
|
|
3146
|
+
showLine,
|
|
3147
|
+
toast,
|
|
3148
|
+
get view() {
|
|
3149
|
+
return view;
|
|
3150
|
+
},
|
|
3151
|
+
set view(next) {
|
|
3152
|
+
view = next;
|
|
3153
|
+
},
|
|
3154
|
+
get tab() {
|
|
3155
|
+
return tab;
|
|
3156
|
+
},
|
|
3157
|
+
set tab(next) {
|
|
3158
|
+
tab = next;
|
|
3159
|
+
},
|
|
3160
|
+
get stage() {
|
|
3161
|
+
return stage;
|
|
3162
|
+
},
|
|
3163
|
+
set stage(next) {
|
|
3164
|
+
stage = next;
|
|
3165
|
+
},
|
|
3166
|
+
get stagePicked() {
|
|
3167
|
+
return stagePicked;
|
|
3168
|
+
},
|
|
3169
|
+
set stagePicked(next) {
|
|
3170
|
+
stagePicked = next;
|
|
3171
|
+
},
|
|
3172
|
+
get drill() {
|
|
3173
|
+
return drill;
|
|
3174
|
+
},
|
|
3175
|
+
get picker() {
|
|
3176
|
+
return picker;
|
|
3177
|
+
},
|
|
3178
|
+
set picker(next) {
|
|
3179
|
+
picker = next;
|
|
3180
|
+
},
|
|
3181
|
+
get souvenirPick() {
|
|
3182
|
+
return souvenirPick;
|
|
3183
|
+
},
|
|
3184
|
+
set souvenirPick(next) {
|
|
3185
|
+
souvenirPick = next;
|
|
3186
|
+
},
|
|
3187
|
+
get ownerEdit() {
|
|
3188
|
+
return ownerEdit;
|
|
3189
|
+
},
|
|
3190
|
+
set ownerEdit(next) {
|
|
3191
|
+
ownerEdit = next;
|
|
3192
|
+
},
|
|
3193
|
+
get pigNameEdit() {
|
|
3194
|
+
return pigNameEdit;
|
|
3195
|
+
},
|
|
3196
|
+
set pigNameEdit(next) {
|
|
3197
|
+
pigNameEdit = next;
|
|
3198
|
+
},
|
|
3199
|
+
get cardEdit() {
|
|
3200
|
+
return cardEdit;
|
|
3201
|
+
},
|
|
3202
|
+
set cardEdit(next) {
|
|
3203
|
+
cardEdit = next;
|
|
3204
|
+
},
|
|
3205
|
+
get isOpen() {
|
|
3206
|
+
return isOpen;
|
|
3207
|
+
},
|
|
3208
|
+
set isOpen(next) {
|
|
3209
|
+
isOpen = next;
|
|
3210
|
+
},
|
|
3211
|
+
get lastStage() {
|
|
3212
|
+
return lastStage;
|
|
3213
|
+
},
|
|
3214
|
+
set lastStage(next) {
|
|
3215
|
+
lastStage = next;
|
|
3216
|
+
},
|
|
3217
|
+
get lastPendingAt() {
|
|
3218
|
+
return lastPendingAt;
|
|
3219
|
+
},
|
|
3220
|
+
set lastPendingAt(next) {
|
|
3221
|
+
lastPendingAt = next;
|
|
3222
|
+
},
|
|
3223
|
+
get lastPendingId() {
|
|
3224
|
+
return lastPendingId;
|
|
3225
|
+
},
|
|
3226
|
+
set lastPendingId(next) {
|
|
3227
|
+
lastPendingId = next;
|
|
3228
|
+
},
|
|
3229
|
+
get userRight() {
|
|
3230
|
+
return userRight;
|
|
3231
|
+
},
|
|
3232
|
+
set userRight(next) {
|
|
3233
|
+
userRight = next;
|
|
3234
|
+
},
|
|
3235
|
+
get userBottom() {
|
|
3236
|
+
return userBottom;
|
|
3237
|
+
},
|
|
3238
|
+
set userBottom(next) {
|
|
3239
|
+
userBottom = next;
|
|
3240
|
+
},
|
|
3241
|
+
get busy() {
|
|
3242
|
+
return busy;
|
|
3243
|
+
},
|
|
3244
|
+
set busy(next) {
|
|
3245
|
+
busy = next;
|
|
3246
|
+
},
|
|
3247
|
+
get stopped() {
|
|
3248
|
+
return stopped;
|
|
3249
|
+
},
|
|
3250
|
+
set stopped(next) {
|
|
3251
|
+
stopped = next;
|
|
3252
|
+
},
|
|
3253
|
+
get devMode() {
|
|
3254
|
+
return devMode;
|
|
3255
|
+
}
|
|
3256
|
+
};
|
|
3257
|
+
var layout = createLayout(ctx);
|
|
3258
|
+
var panel = createPanel(ctx);
|
|
3259
|
+
ctx.select = panel.select;
|
|
3260
|
+
ctx.setOpen = panel.setOpen;
|
|
3261
|
+
ctx.fitPanel = layout.fitPanel;
|
|
3262
|
+
ctx.paintBar = layout.paintBar;
|
|
3263
|
+
ctx.buildIcon = layout.buildIcon;
|
|
3264
|
+
ctx.clampPig = layout.clampPig;
|
|
3265
|
+
var render = panel.render, renderContent = panel.renderContent;
|
|
3266
|
+
var setOpen = panel.setOpen, select = panel.select;
|
|
3267
|
+
var fitPanel = layout.fitPanel, clampPig = layout.clampPig;
|
|
3268
|
+
var paintBar = layout.paintBar, buildIcon = layout.buildIcon;
|
|
3269
|
+
for (var t = 0; t < TABS.length; t += 1) buildIcon(TABS[t]);
|
|
3270
|
+
var io = createIo(ctx);
|
|
3271
|
+
var send = io.send, refresh2 = io.refresh;
|
|
3272
|
+
ctx.send = send;
|
|
3273
|
+
ctx.render = render;
|
|
3274
|
+
ctx.renderContent = renderContent;
|
|
3275
|
+
ctx.send = send;
|
|
3276
|
+
ctx.renderContent = renderContent;
|
|
3277
|
+
ctx.setOpen = setOpen;
|
|
3278
|
+
ctx.fitPanel = fitPanel;
|
|
3279
|
+
ctx.flash = flash;
|
|
3280
|
+
dailyHint.addEventListener("pointerdown", function(event) {
|
|
3281
|
+
event.stopPropagation();
|
|
3282
|
+
});
|
|
3283
|
+
dailyHint.addEventListener("click", function(event) {
|
|
3284
|
+
event.stopPropagation();
|
|
3285
|
+
var action = dailyHint.getAttribute("data-action");
|
|
3286
|
+
if (action !== null && action !== "") send(action);
|
|
3287
|
+
});
|
|
3288
|
+
var drag = null;
|
|
3289
|
+
scene.addEventListener("pointerdown", function(event) {
|
|
3290
|
+
if (event.button !== 0) return;
|
|
3291
|
+
drag = {
|
|
3292
|
+
x: event.clientX,
|
|
3293
|
+
y: event.clientY,
|
|
3294
|
+
right: parseFloat(getComputedStyle(host).right) || 18,
|
|
3295
|
+
bottom: parseFloat(getComputedStyle(host).bottom) || 18,
|
|
3296
|
+
moved: false
|
|
3297
|
+
};
|
|
3298
|
+
scene.setAttribute("data-dragging", "true");
|
|
3299
|
+
scene.setPointerCapture?.(event.pointerId);
|
|
3300
|
+
});
|
|
3301
|
+
scene.addEventListener("pointermove", function(event) {
|
|
3302
|
+
if (drag === null) return;
|
|
3303
|
+
var dx = event.clientX - drag.x;
|
|
3304
|
+
var dy = event.clientY - drag.y;
|
|
3305
|
+
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) drag.moved = true;
|
|
3306
|
+
userRight = drag.right - dx;
|
|
3307
|
+
userBottom = drag.bottom - dy;
|
|
3308
|
+
clampPig();
|
|
3309
|
+
fitPanel();
|
|
3310
|
+
});
|
|
3311
|
+
function endDrag() {
|
|
3312
|
+
if (drag === null) return false;
|
|
3313
|
+
var moved = drag.moved;
|
|
3314
|
+
drag = null;
|
|
3315
|
+
scene.removeAttribute("data-dragging");
|
|
3316
|
+
clampPig();
|
|
3317
|
+
writeStore(POSITION_KEY, JSON.stringify({ right: userRight, bottom: userBottom }));
|
|
3318
|
+
fitPanel();
|
|
3319
|
+
return moved;
|
|
3320
|
+
}
|
|
3321
|
+
var boxPokes = 0;
|
|
3322
|
+
function pokeBox() {
|
|
3323
|
+
boxPokes += 1;
|
|
3324
|
+
react("poke", 560);
|
|
3325
|
+
if (boxPokes >= BOX_POKES_TO_OPEN) {
|
|
3326
|
+
boxPokes = 0;
|
|
3327
|
+
host.removeAttribute("data-poke");
|
|
3328
|
+
showBubble("\u54C7\u2014\u2014\uFF01", 1200);
|
|
3329
|
+
burst(["\u2728", "\u{1F389}", "\u{1F4A8}"], 6);
|
|
3330
|
+
send("hatch");
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
3333
|
+
host.setAttribute("data-poke", String(boxPokes));
|
|
3334
|
+
burst(["\u{1F4A8}"], 2);
|
|
3335
|
+
showBubble(BOX_POKE_LINES[boxPokes - 1], 2200);
|
|
3336
|
+
}
|
|
3337
|
+
scene.addEventListener("pointerup", function() {
|
|
3338
|
+
if (endDrag()) return;
|
|
3339
|
+
if (view.hatched !== true) {
|
|
3340
|
+
pokeBox();
|
|
3341
|
+
return;
|
|
3342
|
+
}
|
|
3343
|
+
if (!view.dead) flash("pet");
|
|
3344
|
+
});
|
|
3345
|
+
scene.addEventListener("pointercancel", function() {
|
|
3346
|
+
endDrag();
|
|
3347
|
+
});
|
|
3348
|
+
scene.addEventListener("contextmenu", function(event) {
|
|
3349
|
+
event.preventDefault();
|
|
3350
|
+
setOpen(!isOpen);
|
|
3351
|
+
if (isOpen && view.pig !== null) flash("pet");
|
|
3352
|
+
});
|
|
3353
|
+
clampPig();
|
|
3354
|
+
setOpen(isOpen);
|
|
3355
|
+
render(view);
|
|
3356
|
+
refresh2();
|
|
3357
|
+
pollTimer = window.setInterval(refresh2, POLL_MS);
|
|
3358
|
+
var chatTimer = null;
|
|
3359
|
+
function scheduleChat() {
|
|
3360
|
+
var minutes = IDLE_CHAT_MINUTES.min + Math.random() * (IDLE_CHAT_MINUTES.max - IDLE_CHAT_MINUTES.min);
|
|
3361
|
+
chatTimer = window.setTimeout(function() {
|
|
3362
|
+
if (!stopped && !busy && view.pig !== null) send("chat", { reason: "idle" });
|
|
3363
|
+
scheduleChat();
|
|
3364
|
+
}, minutes * 6e4);
|
|
3365
|
+
}
|
|
3366
|
+
var greetTimer = window.setTimeout(function() {
|
|
3367
|
+
if (!stopped && view.pig !== null) send("chat", { reason: "enter" });
|
|
3368
|
+
}, GREET_DELAY_MS);
|
|
3369
|
+
scheduleChat();
|
|
3370
|
+
function onResize() {
|
|
3371
|
+
clampPig();
|
|
3372
|
+
fitPanel();
|
|
3373
|
+
}
|
|
3374
|
+
window.addEventListener?.("resize", onResize);
|
|
3375
|
+
function setDevMode(on) {
|
|
3376
|
+
devMode = on === true;
|
|
3377
|
+
writeStore(DEV_KEY, devMode ? "1" : "0");
|
|
3378
|
+
host.setAttribute("data-dev", devMode ? "true" : "false");
|
|
3379
|
+
paintBar();
|
|
3380
|
+
if (devMode) {
|
|
3381
|
+
setOpen(true);
|
|
3382
|
+
select("dev");
|
|
3383
|
+
showBubble("\u{1F527} \u5F00\u53D1\u8005\u6A21\u5F0F\u5DF2\u5F00", 2e3);
|
|
3384
|
+
} else {
|
|
3385
|
+
if (tab === "dev") select("status");
|
|
3386
|
+
showBubble("\u5F00\u53D1\u8005\u6A21\u5F0F\u5DF2\u5173", 1600);
|
|
3387
|
+
}
|
|
3388
|
+
}
|
|
3389
|
+
devMode = readStore(DEV_KEY) === "1";
|
|
3390
|
+
host.setAttribute("data-dev", devMode ? "true" : "false");
|
|
3391
|
+
if (devMode) paintBar();
|
|
3392
|
+
function onKeyDown(event) {
|
|
3393
|
+
if (event.ctrlKey && event.shiftKey && (event.key === "D" || event.key === "d")) {
|
|
3394
|
+
event.preventDefault();
|
|
3395
|
+
setDevMode(!devMode);
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
window.addEventListener?.("keydown", onKeyDown);
|
|
3399
|
+
try {
|
|
3400
|
+
window.dshPigDev = {
|
|
3401
|
+
on: function() {
|
|
3402
|
+
setDevMode(true);
|
|
3403
|
+
},
|
|
3404
|
+
off: function() {
|
|
3405
|
+
setDevMode(false);
|
|
3406
|
+
},
|
|
3407
|
+
toggle: function() {
|
|
3408
|
+
setDevMode(!devMode);
|
|
3409
|
+
}
|
|
3410
|
+
};
|
|
3411
|
+
} catch (error) {
|
|
3412
|
+
}
|
|
3413
|
+
function dispose() {
|
|
3414
|
+
stopped = true;
|
|
3415
|
+
window.removeEventListener?.("resize", onResize);
|
|
3416
|
+
window.removeEventListener?.("keydown", onKeyDown);
|
|
3417
|
+
try {
|
|
3418
|
+
delete /** @type {any} */
|
|
3419
|
+
window.dshPigDev;
|
|
3420
|
+
} catch (error) {
|
|
3421
|
+
}
|
|
3422
|
+
if (pollTimer !== null) window.clearInterval(pollTimer);
|
|
3423
|
+
if (chatTimer !== null) window.clearTimeout(chatTimer);
|
|
3424
|
+
window.clearTimeout(greetTimer);
|
|
3425
|
+
fx.dispose();
|
|
3426
|
+
pollTimer = null;
|
|
3427
|
+
host.remove();
|
|
3428
|
+
style.remove();
|
|
3429
|
+
font.remove();
|
|
3430
|
+
}
|
|
3431
|
+
return dispose;
|
|
3432
|
+
}
|
|
3433
|
+
exports.name = "dsh-piggy";
|
|
3434
|
+
exports.apply = apply;
|
|
3435
|
+
return module.exports;
|
|
3436
|
+
}
|
|
3437
|
+
});
|
|
3438
|
+
})();
|