Skip to content

Commit 2a5c269

Browse files
committed
城镇已满时,禁用招募按扭
1 parent 2436aa2 commit 2a5c269

5 files changed

Lines changed: 112 additions & 10 deletions

File tree

src/main/java/com/teammoeg/frostedheart/content/town/TeamTown.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,40 @@ public boolean addResident(String firstName, String lastName) {
153153
return addResident(new Resident(firstName, lastName));
154154
}
155155

156+
/**
157+
* 客户端/服务端通用:判断城镇是否还能再容纳一名居民。
158+
* 逻辑镜像 {@link TeamTownData#allocateHouse()} 的空闲槽位判定:
159+
* 存在任一可工作的 {@code HouseBuilding} 仍有空余房屋槽位即可。
160+
* <p>
161+
* 容量按居民实际住房归属({@code Resident.housePos})计数而非
162+
* {@code HouseBuilding.getResidentsID()}:后者不在 CODEC 序列化范围,
163+
* 客户端快照 / 存档重载后恒为空集,不能作为容量依据;此口径与城镇 GUI
164+
* 显示、{@link TeamTownData#allocateHouse()} 的真实分配结果保持一致。
165+
* <p>
166+
* Client/server shared: whether the town can still accommodate one more resident.
167+
* Counts actual occupancy by {@code Resident.housePos} instead of
168+
* {@code HouseBuilding.getResidentsID()}, because the latter is not serialized
169+
* by the CODEC and is always empty on client snapshots / after save reloads;
170+
* this matches the town GUI display and the real allocation of
171+
* {@link TeamTownData#allocateHouse()}.
172+
*
173+
* @return 可容纳则返回 true / true if another resident can be accommodated
174+
*/
175+
public boolean canAddResident() {
176+
return data.buildings.values().stream()
177+
.filter(b -> b instanceof HouseBuilding)
178+
.map(b -> (HouseBuilding) b)
179+
.filter(HouseBuilding::isBuildingWorkable)
180+
.anyMatch(h -> countResidentsAt(h.getPos()) < h.getMaxResidents());
181+
}
182+
183+
/** 统计实际住在指定房屋的居民数(按 housePos 归属)。 */
184+
private long countResidentsAt(BlockPos housePos) {
185+
return data.residents.values().stream()
186+
.filter(resident -> housePos.equals(resident.getHousePos()))
187+
.count();
188+
}
189+
156190
public boolean removeResident(UUID id) {
157191
if(!data.residents.containsKey(id)){
158192
return false;

src/main/java/com/teammoeg/frostedheart/content/town/network/WanderingRefugeeRecruitMessage.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,16 @@ public void handle(Supplier<NetworkEvent.Context> ctx) {
6464
player.displayClientMessage(Component.translatable("message.frostedheart.wandering_refugee.too_far_to_recruit"), false);
6565
} else{
6666
TeamTown town = TeamTown.from(player);
67-
if (town.addResident(new Resident(refugee.getFirstName(), refugee.getLastName()))){
68-
//随便产生点粒子效果吧
69-
{
67+
if (town.addResident(new Resident(refugee.getFirstName(), refugee.getLastName()))) {
68+
// 随便产生点粒子效果吧
7069
for (int i = 0; i < 16; i++) {
7170
player.level().addParticle(ParticleTypes.EXPLOSION, refugee.getX(), refugee.getY(), refugee.getZ(), Math.random(), Math.random(), 0.01D);
7271
player.level().addParticle(ParticleTypes.END_ROD, refugee.getX(), refugee.getY(), refugee.getZ(), Math.random(), Math.random(), 0.01D);
7372
}
74-
}
75-
player.displayClientMessage(Component.translatable("message.frostedheart.wandering_refugee.recruited"), false);
76-
refugee.remove(Entity.RemovalReason.DISCARDED);
73+
player.displayClientMessage(Component.translatable("message.frostedheart.wandering_refugee.recruited"), false);
74+
refugee.remove(Entity.RemovalReason.DISCARDED);
75+
} else {
76+
player.displayClientMessage(Component.translatable("message.frostedheart.wandering_refugee.cannot_accommodate"), false);
7777
}
7878
}
7979
}
Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package com.teammoeg.frostedheart.content.town.resident;
22

3+
import com.teammoeg.chorda.client.cui.base.MouseButton;
4+
import com.teammoeg.chorda.client.cui.base.TooltipBuilder;
35
import com.teammoeg.chorda.client.cui.widgets.TextButton;
46
import com.teammoeg.chorda.client.icon.FlatIcon;
7+
import com.teammoeg.chorda.dataholders.team.CClientTeamDataManager;
58
import com.teammoeg.frostedheart.FHNetwork;
9+
import com.teammoeg.frostedheart.bootstrap.common.FHSpecialDataTypes;
10+
import com.teammoeg.frostedheart.content.town.TeamTownData;
11+
import com.teammoeg.frostedheart.content.town.event.ITownDataUpdateListener;
612
import com.teammoeg.frostedheart.content.town.network.WanderingRefugeeOpenTradeGUIMessage;
713
import com.teammoeg.frostedheart.content.town.network.WanderingRefugeeRecruitMessage;
814
import com.teammoeg.frostedheart.content.ui.dialogue.DialogueOverlay;
@@ -13,15 +19,73 @@
1319

1420
@OnlyIn(Dist.CLIENT)
1521
public class WanderingRefugeeClientHelper {
22+
/** 当前对话注册的城镇数据监听器,对话关闭时自我移除,避免跨对话累积。 */
23+
private static ITownDataUpdateListener refreshListener = null;
24+
1625
public static void openScreen(WanderingRefugee entity) {
1726
var trade = TextButton.create(DialogueOverlay.INSTANCE, Component.translatable("gui.frostedheart.wandering_refugee.trade_button"), FlatIcon.TRADE.toCIcon(), mb ->
1827
FHNetwork.INSTANCE.sendToServer(new WanderingRefugeeOpenTradeGUIMessage(entity.getId()))
1928
);
20-
var recruit = TextButton.create(DialogueOverlay.INSTANCE, Component.translatable("gui.frostedheart.wandering_refugee.recruit_button"), FlatIcon.GAIN.toCIcon(), mb ->
21-
FHNetwork.INSTANCE.sendToServer(new WanderingRefugeeRecruitMessage(entity.getId()))
22-
);
23-
// recruit.setEnabled(false); TODO 无法容纳更多居民时禁用按钮
29+
// 招募按钮:禁用时显示"无空余房屋"提示,状态翻转时提示自动跟随
30+
var recruit = new TextButton(DialogueOverlay.INSTANCE, Component.translatable("gui.frostedheart.wandering_refugee.recruit_button"), FlatIcon.GAIN.toCIcon()) {
31+
@Override
32+
public void onClicked(MouseButton button) {
33+
FHNetwork.INSTANCE.sendToServer(new WanderingRefugeeRecruitMessage(entity.getId()));
34+
}
35+
36+
@Override
37+
public void getTooltip(TooltipBuilder list) {
38+
if (!isEnabled()) {
39+
list.accept(Component.translatable("gui.frostedheart.wandering_refugee.recruit_disabled_no_housing"));
40+
}
41+
}
42+
};
43+
44+
boolean canRecruit = canAddResident();
45+
recruit.setEnabled(canRecruit);
46+
47+
// 注册城镇数据增量监听:建筑/居民变化(新建房屋、居民死亡释放槽位等)时刷新招募可用状态。
48+
// 复用与城镇 GUI 相同的 addClientListener/removeClientListener 机制;监听在对话关闭
49+
// (DialogueOverlay 被禁用)时自我移除。
50+
if (refreshListener != null) {
51+
TeamTownData.removeClientListener(refreshListener);
52+
}
53+
final TextButton recruitButton = recruit;
54+
refreshListener = new ITownDataUpdateListener() {
55+
@Override
56+
public void onBuildingsChanged() {
57+
refreshRecruit();
58+
}
59+
60+
@Override
61+
public void onResidentsChanged() {
62+
refreshRecruit();
63+
}
64+
65+
private void refreshRecruit() {
66+
if (!DialogueOverlay.INSTANCE.isEnabled()) {
67+
TeamTownData.removeClientListener(this);
68+
refreshListener = null;
69+
return;
70+
}
71+
recruitButton.setEnabled(canAddResident());
72+
}
73+
};
74+
TeamTownData.addClientListener(refreshListener);
75+
2476
DialogueScreen.open(true, trade, recruit);
2577
// Minecraft.getInstance().setScreen(new WanderingRefugeeScreen(entity));
2678
}
79+
80+
/**
81+
* 从客户端城镇数据快照判断能否再招募一名居民。
82+
* <p>
83+
* Whether another resident can be recruited, based on the client-side town data snapshot.
84+
*
85+
* @return 可招募则返回 true / true if another resident can be recruited
86+
*/
87+
private static boolean canAddResident() {
88+
TeamTownData townData = CClientTeamDataManager.INSTANCE.getInstance().getData(FHSpecialDataTypes.TOWN_DATA);
89+
return townData != null && townData.createTeamTown().canAddResident();
90+
}
2791
}

src/main/resources/assets/frostedheart/lang/en_us/en_us.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@
152152
"gui.frostedheart.update_recommended": "The Winter Rescue Update: ",
153153
"gui.frostedheart.wandering_refugee.trade_button": "Trade with refugee",
154154
"gui.frostedheart.wandering_refugee.recruit_button": "Recruit refugee to your town",
155+
"gui.frostedheart.wandering_refugee.recruit_disabled_no_housing": "Your town has no vacant houses to accommodate more residents",
156+
"message.frostedheart.wandering_refugee.cannot_accommodate": "Your town has no vacant house to accommodate this refugee",
155157
"gui.frostedheart.snow_sack.auto_pickup": "Auto Pickup: %s",
156158
"gui.frostedheart.snow_sack.delete_overflow": "Destroy Overflows: %s",
157159
"gui.frostedheart.snow_sack.stored_snow": "Stored Snow: %s",

src/main/resources/assets/frostedheart/lang/zh_cn/zh_cn.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@
152152
"gui.frostedheart.update_recommended": "建议更新《冬季救援》: ",
153153
"gui.frostedheart.wandering_refugee.trade_button": "与难民交易",
154154
"gui.frostedheart.wandering_refugee.recruit_button": "将难民招募至你的城镇",
155+
"gui.frostedheart.wandering_refugee.recruit_disabled_no_housing": "你的城镇没有空余的房屋,无法容纳更多居民",
156+
"message.frostedheart.wandering_refugee.cannot_accommodate": "你的城镇没有空余的房屋,无法容纳这位难民",
155157
"gui.frostedheart.snow_sack.auto_pickup": "自动拾取:%s",
156158
"gui.frostedheart.snow_sack.delete_overflow": "溢出销毁:%s",
157159
"gui.frostedheart.snow_sack.stored_snow": "已储存雪:%s",

0 commit comments

Comments
 (0)