mirror of
https://github.com/Melledy/Grasscutter.git
synced 2024-11-22 06:31:26 +00:00
Fix mirror tower stages; fix tower time challenge and star scoring (#2406)
This commit is contained in:
parent
bc8e7c21ce
commit
f5703e5964
@ -1,33 +1,82 @@
|
|||||||
package emu.grasscutter.data.excels.tower;
|
package emu.grasscutter.data.excels.tower;
|
||||||
|
|
||||||
import emu.grasscutter.data.*;
|
import emu.grasscutter.data.*;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
@ResourceType(name = "TowerLevelExcelConfigData.json")
|
@ResourceType(name = "TowerLevelExcelConfigData.json")
|
||||||
|
@Getter
|
||||||
public class TowerLevelData extends GameResource {
|
public class TowerLevelData extends GameResource {
|
||||||
|
|
||||||
private int levelId;
|
private int levelId;
|
||||||
private int levelIndex;
|
private int levelIndex;
|
||||||
private int levelGroupId;
|
private int levelGroupId;
|
||||||
private int dungeonId;
|
private int dungeonId;
|
||||||
|
private List<TowerLevelCond> conds;
|
||||||
|
|
||||||
|
public static class TowerLevelCond {
|
||||||
|
private TowerCondType towerCondType;
|
||||||
|
private List<Integer> argumentList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TowerCondType {
|
||||||
|
TOWER_COND_NONE,
|
||||||
|
TOWER_COND_CHALLENGE_LEFT_TIME_MORE_THAN,
|
||||||
|
TOWER_COND_LEFT_HP_GREATER_THAN
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not actual data in TowerLevelExcelConfigData.
|
||||||
|
// Just packaging condition parameters for convenience.
|
||||||
|
@Getter
|
||||||
|
public class TowerCondTimeParams {
|
||||||
|
private int param1;
|
||||||
|
private int minimumTimeInSeconds;
|
||||||
|
|
||||||
|
public TowerCondTimeParams(int param1, int minimumTimeInSeconds) {
|
||||||
|
this.param1 = param1;
|
||||||
|
this.minimumTimeInSeconds = minimumTimeInSeconds;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class TowerCondHpParams {
|
||||||
|
private int sceneId;
|
||||||
|
private int configId;
|
||||||
|
private int minimumHpPercentage;
|
||||||
|
|
||||||
|
public TowerCondHpParams(int sceneId, int configId, int minimumHpPercentage) {
|
||||||
|
this.sceneId = sceneId;
|
||||||
|
this.configId = configId;
|
||||||
|
this.minimumHpPercentage = minimumHpPercentage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getId() {
|
public int getId() {
|
||||||
return this.getLevelId();
|
return this.getLevelId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLevelId() {
|
public TowerCondType getCondType(int star) {
|
||||||
return levelId;
|
if (star < 0 || conds == null || star >= conds.size()) {
|
||||||
|
return TowerCondType.TOWER_COND_NONE;
|
||||||
|
}
|
||||||
|
var condType = conds.get(star).towerCondType;
|
||||||
|
return condType == null ? TowerCondType.TOWER_COND_NONE : condType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLevelGroupId() {
|
public TowerCondTimeParams getTimeCond(int star) {
|
||||||
return levelGroupId;
|
if (star < 0 || conds == null || star >= conds.size()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var params = conds.get(star).argumentList;
|
||||||
|
return new TowerCondTimeParams(params.get(0), params.get(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getLevelIndex() {
|
public TowerCondHpParams getHpCond(int star) {
|
||||||
return levelIndex;
|
if (star < 0 || conds == null || star >= conds.size()) {
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
public int getDungeonId() {
|
var params = conds.get(star).argumentList;
|
||||||
return dungeonId;
|
return new TowerCondHpParams(params.get(0), params.get(1), params.get(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ public final class DungeonManager {
|
|||||||
private boolean ended = false;
|
private boolean ended = false;
|
||||||
private int newestWayPoint = 0;
|
private int newestWayPoint = 0;
|
||||||
@Getter private int startSceneTime = 0;
|
@Getter private int startSceneTime = 0;
|
||||||
|
@Setter @Getter private boolean towerDungeon = false;
|
||||||
|
|
||||||
DungeonTrialTeam trialTeam = null;
|
DungeonTrialTeam trialTeam = null;
|
||||||
|
|
||||||
@ -323,15 +324,30 @@ public final class DungeonManager {
|
|||||||
p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON);
|
p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_FINISH_DUNGEON);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
scene
|
var future = scene
|
||||||
.getScriptManager()
|
.getScriptManager()
|
||||||
.callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0));
|
.callEvent(new ScriptArgs(0, EventType.EVENT_DUNGEON_SETTLE, successfully ? 1 : 0));
|
||||||
|
// Note: There is a possible race condition with calling
|
||||||
|
// EVENT_DUNGEON_SETTLE here asynchronously:
|
||||||
|
// 1. EVENT_DUNGEON_SETTLE triggers some Lua-side logic,
|
||||||
|
// which may happen after 2 (below) finishes.
|
||||||
|
// 2. Some DungeonSettleListener could be comparing some
|
||||||
|
// Lua variable before its setting in 1 (above) finishes.
|
||||||
|
// For safety, ensure all events have finished before returning.
|
||||||
|
try {
|
||||||
|
future.get();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void endDungeon(BaseDungeonResult.DungeonEndReason endReason) {
|
public void endDungeon(BaseDungeonResult.DungeonEndReason endReason) {
|
||||||
if (scene.getDungeonSettleListeners() != null) {
|
if (scene.getDungeonSettleListeners() != null) {
|
||||||
scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(this, endReason));
|
scene.getDungeonSettleListeners().forEach(o -> o.onDungeonSettle(this, endReason));
|
||||||
}
|
}
|
||||||
|
if (isTowerDungeon()) {
|
||||||
|
scene.getPlayers().get(0).getTowerManager().onEnd();
|
||||||
|
}
|
||||||
ended = true;
|
ended = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +132,9 @@ public final class DungeonSystem extends BaseGameSystem {
|
|||||||
|
|
||||||
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
|
if (player.getWorld().transferPlayerToScene(player, data.getSceneId(), data)) {
|
||||||
var scene = player.getScene();
|
var scene = player.getScene();
|
||||||
scene.setDungeonManager(new DungeonManager(scene, data));
|
var dungeonManager = new DungeonManager(scene, data);
|
||||||
|
dungeonManager.setTowerDungeon(true);
|
||||||
|
scene.setDungeonManager(dungeonManager);
|
||||||
dungeonSettleListeners.forEach(scene::addDungeonSettleObserver);
|
dungeonSettleListeners.forEach(scene::addDungeonSettleObserver);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -168,6 +170,7 @@ public final class DungeonSystem extends BaseGameSystem {
|
|||||||
// clean temp team if it has
|
// clean temp team if it has
|
||||||
player.getTeamManager().cleanTemporaryTeam();
|
player.getTeamManager().cleanTemporaryTeam();
|
||||||
player.getTowerManager().clearEntry();
|
player.getTowerManager().clearEntry();
|
||||||
|
dungeonManager.setTowerDungeon(false);
|
||||||
|
|
||||||
// Transfer player back to world
|
// Transfer player back to world
|
||||||
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
|
player.getWorld().transferPlayerToScene(player, prevScene, prevPos);
|
||||||
|
@ -9,6 +9,7 @@ public class TowerDungeonSettleListener implements DungeonSettleListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onDungeonSettle(DungeonManager dungeonManager, DungeonEndReason endReason) {
|
public void onDungeonSettle(DungeonManager dungeonManager, DungeonEndReason endReason) {
|
||||||
var scene = dungeonManager.getScene();
|
var scene = dungeonManager.getScene();
|
||||||
|
|
||||||
var dungeonData = dungeonManager.getDungeonData();
|
var dungeonData = dungeonManager.getDungeonData();
|
||||||
if (scene.getLoadedGroups().stream()
|
if (scene.getLoadedGroups().stream()
|
||||||
.anyMatch(
|
.anyMatch(
|
||||||
@ -22,17 +23,18 @@ public class TowerDungeonSettleListener implements DungeonSettleListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var towerManager = scene.getPlayers().get(0).getTowerManager();
|
var towerManager = scene.getPlayers().get(0).getTowerManager();
|
||||||
|
var stars = towerManager.getCurLevelStars();
|
||||||
|
|
||||||
towerManager.notifyCurLevelRecordChangeWhenDone(3);
|
towerManager.notifyCurLevelRecordChangeWhenDone(stars);
|
||||||
scene.broadcastPacket(
|
scene.broadcastPacket(
|
||||||
new PacketTowerFloorRecordChangeNotify(
|
new PacketTowerFloorRecordChangeNotify(
|
||||||
towerManager.getCurrentFloorId(), 3, towerManager.canEnterScheduleFloor()));
|
towerManager.getCurrentFloorId(), stars, towerManager.canEnterScheduleFloor()));
|
||||||
|
|
||||||
var challenge = scene.getChallenge();
|
var challenge = scene.getChallenge();
|
||||||
var dungeonStats =
|
var dungeonStats =
|
||||||
new DungeonEndStats(
|
new DungeonEndStats(
|
||||||
scene.getKilledMonsterCount(), challenge.getFinishedTime(), 0, endReason);
|
scene.getKilledMonsterCount(), challenge.getFinishedTime(), 0, endReason);
|
||||||
var result = new TowerResult(dungeonData, dungeonStats, towerManager, challenge);
|
var result = new TowerResult(dungeonData, dungeonStats, towerManager, challenge, stars);
|
||||||
|
|
||||||
scene.broadcastPacket(new PacketDungeonSettleNotify(result));
|
scene.broadcastPacket(new PacketDungeonSettleNotify(result));
|
||||||
}
|
}
|
||||||
|
@ -80,9 +80,16 @@ public class WorldChallenge {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.progress = true;
|
this.progress = true;
|
||||||
this.startedAt = System.currentTimeMillis();
|
this.startedAt = getScene().getSceneTimeSeconds();
|
||||||
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
|
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
|
||||||
challengeTriggers.forEach(t -> t.onBegin(this));
|
challengeTriggers.forEach(t -> t.onBegin(this));
|
||||||
|
|
||||||
|
var player = scene.getPlayers().get(0);
|
||||||
|
var dungeonManager = scene.getDungeonManager();
|
||||||
|
var towerManager = player.getTowerManager();
|
||||||
|
if (dungeonManager != null && dungeonManager.isTowerDungeon() && towerManager != null) {
|
||||||
|
towerManager.onBegin();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void done() {
|
public void done() {
|
||||||
|
@ -1,10 +1,29 @@
|
|||||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||||
|
|
||||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||||
|
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||||
|
|
||||||
public class InTimeTrigger extends ChallengeTrigger {
|
public class InTimeTrigger extends ChallengeTrigger {
|
||||||
|
@Override
|
||||||
|
public void onBegin(WorldChallenge challenge) {
|
||||||
|
// Show time remaining UI
|
||||||
|
var scene = challenge.getScene();
|
||||||
|
scene.broadcastPacket(
|
||||||
|
new PacketChallengeDataNotify(
|
||||||
|
challenge,
|
||||||
|
2,
|
||||||
|
// Compensate for time passed so far in scene.
|
||||||
|
challenge.getTimeLimit() + scene.getSceneTimeSeconds()));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCheckTimeout(WorldChallenge challenge) {
|
public void onCheckTimeout(WorldChallenge challenge) {
|
||||||
|
// In Tower challenges, time can run out without
|
||||||
|
// causing the challenge to fail. (Player just
|
||||||
|
// gets 0 stars when they ultimately finish.)
|
||||||
|
var dungeonManager = challenge.getScene().getDungeonManager();
|
||||||
|
if (dungeonManager != null && dungeonManager.isTowerDungeon()) return;
|
||||||
|
|
||||||
var current = challenge.getScene().getSceneTimeSeconds();
|
var current = challenge.getScene().getSceneTimeSeconds();
|
||||||
if (current - challenge.getStartedAt() > challenge.getTimeLimit()) {
|
if (current - challenge.getStartedAt() > challenge.getTimeLimit()) {
|
||||||
challenge.fail();
|
challenge.fail();
|
||||||
|
@ -13,17 +13,20 @@ public class TowerResult extends BaseDungeonResult {
|
|||||||
boolean canJump;
|
boolean canJump;
|
||||||
boolean hasNextLevel;
|
boolean hasNextLevel;
|
||||||
int nextFloorId;
|
int nextFloorId;
|
||||||
|
int currentStars;
|
||||||
|
|
||||||
public TowerResult(
|
public TowerResult(
|
||||||
DungeonData dungeonData,
|
DungeonData dungeonData,
|
||||||
DungeonEndStats dungeonStats,
|
DungeonEndStats dungeonStats,
|
||||||
TowerManager towerManager,
|
TowerManager towerManager,
|
||||||
WorldChallenge challenge) {
|
WorldChallenge challenge,
|
||||||
|
int currentStars) {
|
||||||
super(dungeonData, dungeonStats);
|
super(dungeonData, dungeonStats);
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
this.canJump = towerManager.hasNextFloor();
|
this.canJump = towerManager.hasNextFloor();
|
||||||
this.hasNextLevel = towerManager.hasNextLevel();
|
this.hasNextLevel = towerManager.hasNextLevel();
|
||||||
this.nextFloorId = hasNextLevel ? 0 : towerManager.getNextFloorId();
|
this.nextFloorId = hasNextLevel ? 0 : towerManager.getNextFloorId();
|
||||||
|
this.currentStars = currentStars;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -40,14 +43,16 @@ public class TowerResult extends BaseDungeonResult {
|
|||||||
TowerLevelEndNotify.newBuilder()
|
TowerLevelEndNotify.newBuilder()
|
||||||
.setIsSuccess(challenge.isSuccess())
|
.setIsSuccess(challenge.isSuccess())
|
||||||
.setContinueState(continueStatus)
|
.setContinueState(continueStatus)
|
||||||
.addFinishedStarCondList(1)
|
|
||||||
.addFinishedStarCondList(2)
|
|
||||||
.addFinishedStarCondList(3)
|
|
||||||
.addRewardItemList(
|
.addRewardItemList(
|
||||||
ItemParamOuterClass.ItemParam.newBuilder().setItemId(201).setCount(1000).build());
|
ItemParamOuterClass.ItemParam.newBuilder().setItemId(201).setCount(1000));
|
||||||
|
|
||||||
|
for (int i = 1; i <= currentStars; i++) {
|
||||||
|
towerLevelEndNotify.addFinishedStarCondList(i);
|
||||||
|
}
|
||||||
|
|
||||||
if (nextFloorId > 0 && canJump) {
|
if (nextFloorId > 0 && canJump) {
|
||||||
towerLevelEndNotify.setNextFloorId(nextFloorId);
|
towerLevelEndNotify.setNextFloorId(nextFloorId);
|
||||||
}
|
}
|
||||||
builder.setTowerLevelEndNotify(towerLevelEndNotify);
|
builder.setTowerLevelEndNotify(towerLevelEndNotify.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,10 @@ public class TowerLevelRecord {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getLevelStars(int levelId) {
|
||||||
|
return passedLevelMap.get(levelId);
|
||||||
|
}
|
||||||
|
|
||||||
public int getStarCount() {
|
public int getStarCount() {
|
||||||
return passedLevelMap.values().stream().mapToInt(Integer::intValue).sum();
|
return passedLevelMap.values().stream().mapToInt(Integer::intValue).sum();
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
package emu.grasscutter.game.tower;
|
package emu.grasscutter.game.tower;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
import emu.grasscutter.data.GameData;
|
import emu.grasscutter.data.GameData;
|
||||||
import emu.grasscutter.data.excels.tower.TowerLevelData;
|
import emu.grasscutter.data.excels.tower.TowerLevelData;
|
||||||
import emu.grasscutter.game.dungeons.*;
|
import emu.grasscutter.game.dungeons.*;
|
||||||
import emu.grasscutter.game.player.*;
|
import emu.grasscutter.game.player.*;
|
||||||
import emu.grasscutter.server.packet.send.*;
|
import emu.grasscutter.server.packet.send.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import lombok.*;
|
||||||
|
|
||||||
public class TowerManager extends BasePlayerManager {
|
public class TowerManager extends BasePlayerManager {
|
||||||
private static final List<DungeonSettleListener> towerDungeonSettleListener =
|
private static final List<DungeonSettleListener> towerDungeonSettleListener =
|
||||||
List.of(new TowerDungeonSettleListener());
|
List.of(new TowerDungeonSettleListener());
|
||||||
|
|
||||||
|
private int currentPossibleStars = 0;
|
||||||
|
@Getter private boolean inProgress;
|
||||||
|
@Getter private int currentTimeLimit;
|
||||||
|
|
||||||
public TowerManager(Player player) {
|
public TowerManager(Player player) {
|
||||||
super(player);
|
super(player);
|
||||||
}
|
}
|
||||||
@ -32,6 +38,30 @@ public class TowerManager extends BasePlayerManager {
|
|||||||
return this.getTowerData().currentLevel + 1;
|
return this.getTowerData().currentLevel + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onTick() {
|
||||||
|
var challenge = player.getScene().getChallenge();
|
||||||
|
if (challenge == null || !challenge.inProgress()) return;
|
||||||
|
|
||||||
|
// Check star conditions and notify client if any failed.
|
||||||
|
int stars = getCurLevelStars();
|
||||||
|
while (stars < currentPossibleStars) {
|
||||||
|
player
|
||||||
|
.getSession()
|
||||||
|
.send(new PacketTowerLevelStarCondNotify(getTowerData().currentFloorId, getCurrentLevel(), currentPossibleStars));
|
||||||
|
currentPossibleStars--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onBegin() {
|
||||||
|
var challenge = player.getScene().getChallenge();
|
||||||
|
inProgress = true;
|
||||||
|
currentTimeLimit = challenge.getTimeLimit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onEnd() {
|
||||||
|
inProgress = false;
|
||||||
|
}
|
||||||
|
|
||||||
public Map<Integer, TowerLevelRecord> getRecordMap() {
|
public Map<Integer, TowerLevelRecord> getRecordMap() {
|
||||||
Map<Integer, TowerLevelRecord> recordMap = getTowerData().recordMap;
|
Map<Integer, TowerLevelRecord> recordMap = getTowerData().recordMap;
|
||||||
if (recordMap == null || recordMap.size() == 0) {
|
if (recordMap == null || recordMap.size() == 0) {
|
||||||
@ -84,9 +114,10 @@ public class TowerManager extends BasePlayerManager {
|
|||||||
// stop using skill
|
// stop using skill
|
||||||
player.getSession().send(new PacketCanUseSkillNotify(false));
|
player.getSession().send(new PacketCanUseSkillNotify(false));
|
||||||
// notify the cond of stars
|
// notify the cond of stars
|
||||||
|
currentPossibleStars = 3;
|
||||||
player
|
player
|
||||||
.getSession()
|
.getSession()
|
||||||
.send(new PacketTowerLevelStarCondNotify(getTowerData().currentFloorId, getCurrentLevel()));
|
.send(new PacketTowerLevelStarCondNotify(getTowerData().currentFloorId, getCurrentLevel(), currentPossibleStars + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void notifyCurLevelRecordChange() {
|
public void notifyCurLevelRecordChange() {
|
||||||
@ -97,6 +128,36 @@ public class TowerManager extends BasePlayerManager {
|
|||||||
getTowerData().currentFloorId, getCurrentLevel()));
|
getTowerData().currentFloorId, getCurrentLevel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getCurLevelStars() {
|
||||||
|
var scene = player.getScene();
|
||||||
|
var challenge = scene.getChallenge();
|
||||||
|
if (challenge == null) {
|
||||||
|
Grasscutter.getLogger().error("getCurLevelStars: no challenge registered!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var levelData = GameData.getTowerLevelDataMap().get(getCurrentLevelId());
|
||||||
|
// 0-based indexing. "star" = 0 means checking for 1-star conditions.
|
||||||
|
int star;
|
||||||
|
for (star = 2; star >= 0; star--) {
|
||||||
|
var cond = levelData.getCondType(star);
|
||||||
|
if (cond == TowerLevelData.TowerCondType.TOWER_COND_CHALLENGE_LEFT_TIME_MORE_THAN) {
|
||||||
|
var params = levelData.getTimeCond(star);
|
||||||
|
var timeRemaining = challenge.getTimeLimit() - (scene.getSceneTimeSeconds() - challenge.getStartedAt());
|
||||||
|
if (timeRemaining >= params.getMinimumTimeInSeconds()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (cond == TowerLevelData.TowerCondType.TOWER_COND_LEFT_HP_GREATER_THAN) {
|
||||||
|
// TODO: Check monolith health
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
Grasscutter.getLogger().error("getCurLevelStars: Tower level {} has no or unknown condition defined for {} stars", getCurrentLevelId(), star + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return star + 1;
|
||||||
|
}
|
||||||
|
|
||||||
public void notifyCurLevelRecordChangeWhenDone(int stars) {
|
public void notifyCurLevelRecordChangeWhenDone(int stars) {
|
||||||
Map<Integer, TowerLevelRecord> recordMap = this.getRecordMap();
|
Map<Integer, TowerLevelRecord> recordMap = this.getRecordMap();
|
||||||
int currentFloorId = getTowerData().currentFloorId;
|
int currentFloorId = getTowerData().currentFloorId;
|
||||||
@ -105,8 +166,17 @@ public class TowerManager extends BasePlayerManager {
|
|||||||
currentFloorId,
|
currentFloorId,
|
||||||
new TowerLevelRecord(currentFloorId).setLevelStars(getCurrentLevelId(), stars));
|
new TowerLevelRecord(currentFloorId).setLevelStars(getCurrentLevelId(), stars));
|
||||||
} else {
|
} else {
|
||||||
recordMap.put(
|
// Only update record if better than previous
|
||||||
currentFloorId, recordMap.get(currentFloorId).setLevelStars(getCurrentLevelId(), stars));
|
var prevRecord = recordMap.get(currentFloorId);
|
||||||
|
var passedLevelMap = prevRecord.getPassedLevelMap();
|
||||||
|
int prevStars = 0;
|
||||||
|
if (passedLevelMap.containsKey(getCurrentLevelId())) {
|
||||||
|
prevStars = prevRecord.getLevelStars(getCurrentLevelId());
|
||||||
|
}
|
||||||
|
if (stars > prevStars) {
|
||||||
|
recordMap.put(
|
||||||
|
currentFloorId, prevRecord.setLevelStars(getCurrentLevelId(), stars));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getTowerData().currentLevel++;
|
this.getTowerData().currentLevel++;
|
||||||
|
@ -597,6 +597,13 @@ public class Scene {
|
|||||||
|
|
||||||
blossomManager.onTick();
|
blossomManager.onTick();
|
||||||
|
|
||||||
|
// Should be OK to check only player 0,
|
||||||
|
// as no other players could enter Tower
|
||||||
|
var towerManager = getPlayers().get(0).getTowerManager();
|
||||||
|
if (towerManager != null) {
|
||||||
|
towerManager.onTick();
|
||||||
|
}
|
||||||
|
|
||||||
this.checkNpcGroup();
|
this.checkNpcGroup();
|
||||||
|
|
||||||
this.finishLoading();
|
this.finishLoading();
|
||||||
|
@ -807,11 +807,11 @@ public class SceneScriptManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Events
|
// Events
|
||||||
public void callEvent(int groupId, int eventType) {
|
public Future<?> callEvent(int groupId, int eventType) {
|
||||||
callEvent(new ScriptArgs(groupId, eventType));
|
return callEvent(new ScriptArgs(groupId, eventType));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void callEvent(@Nonnull ScriptArgs params) {
|
public Future<?> callEvent(@Nonnull ScriptArgs params) {
|
||||||
/**
|
/**
|
||||||
* We use ThreadLocal to trans SceneScriptManager context to ScriptLib, to avoid eval script for
|
* We use ThreadLocal to trans SceneScriptManager context to ScriptLib, to avoid eval script for
|
||||||
* every groups' trigger in every scene instances. But when callEvent is called in a ScriptLib
|
* every groups' trigger in every scene instances. But when callEvent is called in a ScriptLib
|
||||||
@ -819,7 +819,7 @@ public class SceneScriptManager {
|
|||||||
* not get it. e.g. CallEvent -> set -> ScriptLib.xxx -> CallEvent -> set -> remove -> NPE ->
|
* not get it. e.g. CallEvent -> set -> ScriptLib.xxx -> CallEvent -> set -> remove -> NPE ->
|
||||||
* (remove) So we use thread pool to clean the stack to avoid this new issue.
|
* (remove) So we use thread pool to clean the stack to avoid this new issue.
|
||||||
*/
|
*/
|
||||||
eventExecutor.submit(() -> this.realCallEvent(params));
|
return eventExecutor.submit(() -> this.realCallEvent(params));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void realCallEvent(@Nonnull ScriptArgs params) {
|
private void realCallEvent(@Nonnull ScriptArgs params) {
|
||||||
|
@ -190,8 +190,27 @@ public class ScriptLib {
|
|||||||
// TODO: ActivateGroupLinkBundle
|
// TODO: ActivateGroupLinkBundle
|
||||||
// TODO: ActivateGroupLinkBundleByBundleId
|
// TODO: ActivateGroupLinkBundleByBundleId
|
||||||
|
|
||||||
public int ActiveChallenge(int challengeId, int challengeIndex, int timeLimitOrGroupId, int groupId, int objectiveKills, int param5) {
|
public synchronized int ActiveChallenge(int challengeId, int challengeIndex, int timeLimitOrGroupId, int groupId, int objectiveKills, int param5) {
|
||||||
logger.debug("[LUA] Call ActiveChallenge with {},{},{},{},{},{}", challengeId, challengeIndex, timeLimitOrGroupId, groupId, objectiveKills, param5);
|
logger.debug("[LUA] Call ActiveChallenge with {},{},{},{},{},{}", challengeId, challengeIndex, timeLimitOrGroupId, groupId, objectiveKills, param5);
|
||||||
|
|
||||||
|
var scene = getSceneScriptManager().getScene();
|
||||||
|
var existingChallenge = scene.getChallenge();
|
||||||
|
if (existingChallenge != null && existingChallenge.inProgress()) {
|
||||||
|
logger.warn("ActiveChallenge: tried to create challenge while one is already in progress");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var towerManager = scene.getPlayers().get(0).getTowerManager();
|
||||||
|
if (towerManager.isInProgress()) {
|
||||||
|
// Tower scripts call ActiveChallenge twice in mirror stages.
|
||||||
|
// The second call provides the time _taken_ in the first stage,
|
||||||
|
// not the actual time limit for the challenge.
|
||||||
|
timeLimitOrGroupId = towerManager.getCurrentTimeLimit() - timeLimitOrGroupId;
|
||||||
|
if (timeLimitOrGroupId < 0) {
|
||||||
|
timeLimitOrGroupId = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var challenge = ChallengeFactory.getChallenge(
|
var challenge = ChallengeFactory.getChallenge(
|
||||||
challengeId,
|
challengeId,
|
||||||
challengeIndex,
|
challengeIndex,
|
||||||
@ -199,7 +218,7 @@ public class ScriptLib {
|
|||||||
groupId,
|
groupId,
|
||||||
objectiveKills,
|
objectiveKills,
|
||||||
param5,
|
param5,
|
||||||
getSceneScriptManager().getScene(),
|
scene,
|
||||||
getCurrentGroup().get()
|
getCurrentGroup().get()
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -212,7 +231,7 @@ public class ScriptLib {
|
|||||||
dungeonChallenge.setStage(getSceneScriptManager().getVariables(groupId).getOrDefault("stage", -1) == 0);
|
dungeonChallenge.setStage(getSceneScriptManager().getVariables(groupId).getOrDefault("stage", -1) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
getSceneScriptManager().getScene().setChallenge(challenge);
|
scene.setChallenge(challenge);
|
||||||
challenge.start();
|
challenge.start();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -6,27 +6,37 @@ import emu.grasscutter.net.proto.TowerLevelStarCondNotifyOuterClass.TowerLevelSt
|
|||||||
|
|
||||||
public class PacketTowerLevelStarCondNotify extends BasePacket {
|
public class PacketTowerLevelStarCondNotify extends BasePacket {
|
||||||
|
|
||||||
public PacketTowerLevelStarCondNotify(int floorId, int levelIndex) {
|
public PacketTowerLevelStarCondNotify(int floorId, int levelIndex, int lostStar) {
|
||||||
super(PacketOpcodes.TowerLevelStarCondNotify);
|
super(PacketOpcodes.TowerLevelStarCondNotify);
|
||||||
|
|
||||||
TowerLevelStarCondNotify proto =
|
var proto = TowerLevelStarCondNotify.newBuilder()
|
||||||
TowerLevelStarCondNotify.newBuilder()
|
.setFloorId(floorId)
|
||||||
.setFloorId(floorId)
|
.setLevelIndex(levelIndex);
|
||||||
.setLevelIndex(levelIndex)
|
|
||||||
.addCondDataList(
|
|
||||||
TowerLevelStarCondData.newBuilder()
|
|
||||||
// .setCondValue(1)
|
|
||||||
.build())
|
|
||||||
.addCondDataList(
|
|
||||||
TowerLevelStarCondData.newBuilder()
|
|
||||||
// .setCondValue(2)
|
|
||||||
.build())
|
|
||||||
.addCondDataList(
|
|
||||||
TowerLevelStarCondData.newBuilder()
|
|
||||||
// .setCondValue(3)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
this.setData(proto);
|
if (1 <= lostStar && lostStar <= 3) {
|
||||||
|
proto.addCondDataList(
|
||||||
|
TowerLevelStarCondData.newBuilder()
|
||||||
|
// If these are still obfuscated in the next client version,
|
||||||
|
// just set all int fields to the star (1 <= star <= 3)
|
||||||
|
// that failed and set all boolean fields to true.
|
||||||
|
.setNGHNFHCLFBH(lostStar)
|
||||||
|
.setIBGHBFANCBK(true)
|
||||||
|
.setOILLLBMMABH(true)
|
||||||
|
.setOMOECEGOALC(lostStar)
|
||||||
|
.build());
|
||||||
|
} else {
|
||||||
|
proto
|
||||||
|
.addCondDataList(
|
||||||
|
TowerLevelStarCondData.newBuilder()
|
||||||
|
.build())
|
||||||
|
.addCondDataList(
|
||||||
|
TowerLevelStarCondData.newBuilder()
|
||||||
|
.build())
|
||||||
|
.addCondDataList(
|
||||||
|
TowerLevelStarCondData.newBuilder()
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setData(proto.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user