mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-17 09:25:06 +01:00
Merge branch 'dev-world-scripts' of https://github.com/Grasscutters/Grasscutter into development
This commit is contained in:
@@ -1,223 +0,0 @@
|
||||
package emu.grasscutter.game.dungeons;
|
||||
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.excels.DungeonData;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DungeonChallenge {
|
||||
private final Scene scene;
|
||||
private final SceneGroup group;
|
||||
|
||||
private int challengeIndex;
|
||||
private int challengeId;
|
||||
private boolean success;
|
||||
private boolean progress;
|
||||
/**
|
||||
* has more challenge
|
||||
*/
|
||||
private boolean stage;
|
||||
private int score;
|
||||
private int objective = 0;
|
||||
private IntSet rewardedPlayers;
|
||||
|
||||
public DungeonChallenge(Scene scene, SceneGroup group, int challengeId, int challengeIndex, int objective) {
|
||||
this.scene = scene;
|
||||
this.group = group;
|
||||
this.challengeId = challengeId;
|
||||
this.challengeIndex = challengeIndex;
|
||||
this.objective = objective;
|
||||
this.setRewardedPlayers(new IntOpenHashSet());
|
||||
}
|
||||
|
||||
public Scene getScene() {
|
||||
return scene;
|
||||
}
|
||||
|
||||
public SceneGroup getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public int getChallengeIndex() {
|
||||
return challengeIndex;
|
||||
}
|
||||
|
||||
public void setChallengeIndex(int challengeIndex) {
|
||||
this.challengeIndex = challengeIndex;
|
||||
}
|
||||
|
||||
public int getChallengeId() {
|
||||
return challengeId;
|
||||
}
|
||||
|
||||
public void setChallengeId(int challengeId) {
|
||||
this.challengeId = challengeId;
|
||||
}
|
||||
|
||||
public int getObjective() {
|
||||
return objective;
|
||||
}
|
||||
|
||||
public void setObjective(int objective) {
|
||||
this.objective = objective;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public void setSuccess(boolean isSuccess) {
|
||||
this.success = isSuccess;
|
||||
}
|
||||
|
||||
public boolean inProgress() {
|
||||
return progress;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
public boolean isStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
public void setStage(boolean stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
public int getTimeLimit() {
|
||||
return 600;
|
||||
}
|
||||
|
||||
public IntSet getRewardedPlayers() {
|
||||
return rewardedPlayers;
|
||||
}
|
||||
|
||||
public void setRewardedPlayers(IntSet rewardedPlayers) {
|
||||
this.rewardedPlayers = rewardedPlayers;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
this.progress = true;
|
||||
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
this.progress = false;
|
||||
|
||||
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
|
||||
|
||||
if (this.isSuccess()) {
|
||||
// Call success script event
|
||||
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null);
|
||||
|
||||
// Settle
|
||||
settle();
|
||||
} else {
|
||||
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void settle() {
|
||||
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
|
||||
|
||||
if(!stage){
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void onMonsterDie(EntityMonster entity) {
|
||||
score = getScore() + 1;
|
||||
|
||||
getScene().broadcastPacket(new PacketChallengeDataNotify(this, 1, getScore()));
|
||||
|
||||
if (getScore() >= getObjective() && this.progress) {
|
||||
this.setSuccess(true);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private List<GameItem> rollRewards() {
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
|
||||
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||
}
|
||||
|
||||
return rewards;
|
||||
}
|
||||
|
||||
public void getStatueDrops(Player player, GadgetInteractReq request) {
|
||||
DungeonData dungeonData = getScene().getDungeonData();
|
||||
int resinCost = dungeonData.getStatueCostCount() != 0 ? dungeonData.getStatueCostCount() : 20;
|
||||
|
||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already rewarded
|
||||
if (getRewardedPlayers().contains(player.getUid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get rewards.
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
|
||||
if (request.getIsUseCondenseResin()) {
|
||||
// Check if condensed resin is usable here.
|
||||
// For this, we use the following logic for now:
|
||||
// The normal resin cost of the dungeon has to be 20.
|
||||
if (resinCost != 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the player has condensed resin.
|
||||
GameItem condensedResin = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(220007);
|
||||
if (condensedResin == null || condensedResin.getCount() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduct.
|
||||
player.getInventory().removeItem(condensedResin, 1);
|
||||
|
||||
// Roll rewards, twice (because condensed).
|
||||
rewards.addAll(this.rollRewards());
|
||||
rewards.addAll(this.rollRewards());
|
||||
}
|
||||
else {
|
||||
// If the player used regular resin, try to deduct.
|
||||
// Stop if insufficient resin.
|
||||
boolean success = player.getResinManager().useResin(resinCost);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Roll rewards.
|
||||
rewards.addAll(this.rollRewards());
|
||||
}
|
||||
|
||||
// Add rewards to player and send notification.
|
||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||
|
||||
getRewardedPlayers().add(player.getUid());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package emu.grasscutter.game.dungeons.challenge;
|
||||
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.data.def.DungeonData;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class DungeonChallenge extends WorldChallenge {
|
||||
|
||||
/**
|
||||
* has more challenge
|
||||
*/
|
||||
private boolean stage;
|
||||
private IntSet rewardedPlayers;
|
||||
|
||||
public DungeonChallenge(Scene scene, SceneGroup group,
|
||||
int challengeId, int challengeIndex,
|
||||
List<Integer> paramList,
|
||||
int timeLimit, int goal,
|
||||
List<ChallengeTrigger> challengeTriggers) {
|
||||
super(scene, group, challengeId, challengeIndex, paramList, timeLimit, goal, challengeTriggers);
|
||||
this.setRewardedPlayers(new IntOpenHashSet());
|
||||
}
|
||||
|
||||
public boolean isStage() {
|
||||
return stage;
|
||||
}
|
||||
|
||||
public void setStage(boolean stage) {
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
public IntSet getRewardedPlayers() {
|
||||
return rewardedPlayers;
|
||||
}
|
||||
|
||||
public void setRewardedPlayers(IntSet rewardedPlayers) {
|
||||
this.rewardedPlayers = rewardedPlayers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void done() {
|
||||
super.done();
|
||||
if (this.isSuccess()) {
|
||||
// Settle
|
||||
settle();
|
||||
}
|
||||
}
|
||||
|
||||
private void settle() {
|
||||
if(!stage){
|
||||
getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene()));
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE,
|
||||
new ScriptArgs(this.isSuccess() ? 1 : 0));
|
||||
}
|
||||
}
|
||||
|
||||
public void getStatueDrops(Player player) {
|
||||
DungeonData dungeonData = getScene().getDungeonData();
|
||||
if (!isSuccess() || dungeonData == null || dungeonData.getRewardPreview() == null || dungeonData.getRewardPreview().getPreviewItems().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Already rewarded
|
||||
if (getRewardedPlayers().contains(player.getUid())) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
for (ItemParamData param : getScene().getDungeonData().getRewardPreview().getPreviewItems()) {
|
||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||
}
|
||||
|
||||
player.getInventory().addItems(rewards, ActionReason.DungeonStatueDrop);
|
||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||
|
||||
getRewardedPlayers().add(player.getUid());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package emu.grasscutter.game.dungeons.challenge;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.ChallengeTrigger;
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeBeginNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class WorldChallenge {
|
||||
private final Scene scene;
|
||||
private final SceneGroup group;
|
||||
private final int challengeId;
|
||||
private final int challengeIndex;
|
||||
private final List<Integer> paramList;
|
||||
private final int timeLimit;
|
||||
private final List<ChallengeTrigger> challengeTriggers;
|
||||
private boolean progress;
|
||||
private boolean success;
|
||||
private long startedAt;
|
||||
private int finishedTime;
|
||||
private final int goal;
|
||||
private final AtomicInteger score;
|
||||
|
||||
public WorldChallenge(Scene scene, SceneGroup group,
|
||||
int challengeId, int challengeIndex, List<Integer> paramList,
|
||||
int timeLimit, int goal,
|
||||
List<ChallengeTrigger> challengeTriggers){
|
||||
this.scene = scene;
|
||||
this.group = group;
|
||||
this.challengeId = challengeId;
|
||||
this.challengeIndex = challengeIndex;
|
||||
this.paramList = paramList;
|
||||
this.timeLimit = timeLimit;
|
||||
this.challengeTriggers = challengeTriggers;
|
||||
this.goal = goal;
|
||||
this.score = new AtomicInteger(0);
|
||||
}
|
||||
public boolean inProgress(){
|
||||
return this.progress;
|
||||
}
|
||||
public void onCheckTimeOut(){
|
||||
if(!inProgress()){
|
||||
return;
|
||||
}
|
||||
if(timeLimit <= 0){
|
||||
return;
|
||||
}
|
||||
challengeTriggers.forEach(t -> t.onCheckTimeout(this));
|
||||
}
|
||||
public void start(){
|
||||
if(inProgress()){
|
||||
Grasscutter.getLogger().info("Could not start a in progress challenge.");
|
||||
return;
|
||||
}
|
||||
this.progress = true;
|
||||
this.startedAt = System.currentTimeMillis();
|
||||
getScene().broadcastPacket(new PacketDungeonChallengeBeginNotify(this));
|
||||
challengeTriggers.forEach(t -> t.onBegin(this));
|
||||
}
|
||||
|
||||
public void done(){
|
||||
if(!inProgress()){
|
||||
return;
|
||||
}
|
||||
finish(true);
|
||||
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS,
|
||||
// TODO record the time in PARAM2 and used in action
|
||||
new ScriptArgs().setParam2(finishedTime));
|
||||
|
||||
challengeTriggers.forEach(t -> t.onFinish(this));
|
||||
}
|
||||
|
||||
public void fail(){
|
||||
if(!inProgress()){
|
||||
return;
|
||||
}
|
||||
finish(false);
|
||||
this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_FAIL, null);
|
||||
challengeTriggers.forEach(t -> t.onFinish(this));
|
||||
}
|
||||
|
||||
private void finish(boolean success){
|
||||
this.progress = false;
|
||||
this.success = success;
|
||||
this.finishedTime = (int)((System.currentTimeMillis() - this.startedAt) / 1000L);
|
||||
getScene().broadcastPacket(new PacketDungeonChallengeFinishNotify(this));
|
||||
}
|
||||
|
||||
public int increaseScore(){
|
||||
return score.incrementAndGet();
|
||||
}
|
||||
public void onMonsterDeath(EntityMonster monster){
|
||||
if(!inProgress()){
|
||||
return;
|
||||
}
|
||||
if(monster.getGroupId() != getGroup().id){
|
||||
return;
|
||||
}
|
||||
this.challengeTriggers.forEach(t -> t.onMonsterDeath(this, monster));
|
||||
}
|
||||
public void onGadgetDeath(EntityGadget gadget){
|
||||
if(!inProgress()){
|
||||
return;
|
||||
}
|
||||
if(gadget.getGroupId() != getGroup().id){
|
||||
return;
|
||||
}
|
||||
this.challengeTriggers.forEach(t -> t.onGadgetDeath(this, gadget));
|
||||
}
|
||||
|
||||
public void onGadgetDamage(EntityGadget gadget){
|
||||
if(!inProgress()){
|
||||
return;
|
||||
}
|
||||
if(gadget.getGroupId() != getGroup().id){
|
||||
return;
|
||||
}
|
||||
this.challengeTriggers.forEach(t -> t.onGadgetDamage(this, gadget));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChallengeFactory {
|
||||
|
||||
private static final List<ChallengeFactoryHandler> challengeFactoryHandlers = new ArrayList<>();
|
||||
|
||||
static {
|
||||
challengeFactoryHandlers.add(new DungeonChallengeFactoryHandler());
|
||||
challengeFactoryHandlers.add(new DungeonGuardChallengeFactoryHandler());
|
||||
challengeFactoryHandlers.add(new KillGadgetChallengeFactoryHandler());
|
||||
challengeFactoryHandlers.add(new KillMonsterChallengeFactoryHandler());
|
||||
}
|
||||
|
||||
public static WorldChallenge getChallenge(int param1, int param2, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group){
|
||||
for(var handler : challengeFactoryHandlers){
|
||||
if(!handler.isThisType(param1, param2, param3, param4, param5, param6, scene, group)){
|
||||
continue;
|
||||
}
|
||||
return handler.build(param1, param2, param3, param4, param5, param6, scene, group);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
|
||||
public interface ChallengeFactoryHandler {
|
||||
boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
|
||||
WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DungeonChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||
@Override
|
||||
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
// ActiveChallenge with 1,1000,300,233101003,15,0
|
||||
return scene.getSceneType() == SceneType.SCENE_DUNGEON
|
||||
&& param4 == group.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
var realGroup = scene.getScriptManager().getGroupById(param4);
|
||||
return new DungeonChallenge(
|
||||
scene, realGroup,
|
||||
challengeId, // Id
|
||||
challengeIndex, // Index
|
||||
List.of(param5, param3),
|
||||
param3, // Limit
|
||||
param5, // Goal
|
||||
List.of(new InTimeTrigger(), new KillMonsterTrigger()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.GuardTrigger;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DungeonGuardChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||
@Override
|
||||
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
// ActiveChallenge with 1,188,234101003,12,3030,0
|
||||
return scene.getSceneType() == SceneType.SCENE_DUNGEON
|
||||
&& param3 == group.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
var realGroup = scene.getScriptManager().getGroupById(param3);
|
||||
return new DungeonChallenge(
|
||||
scene, realGroup,
|
||||
challengeId, // Id
|
||||
challengeIndex, // Index
|
||||
List.of(param4, 0),
|
||||
0, // Limit
|
||||
param5, // Goal
|
||||
List.of(new GuardTrigger()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactoryHandler;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.KillGadgetTrigger;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KillGadgetChallengeFactoryHandler implements ChallengeFactoryHandler {
|
||||
@Override
|
||||
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
// kill gadgets(explosive barrel) in time
|
||||
// ActiveChallenge with 56,201,20,2,201,4
|
||||
// open chest in time
|
||||
// ActiveChallenge with 666,202,30,7,202,1
|
||||
return challengeId == 201 || challengeId == 202;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
return new WorldChallenge(
|
||||
scene, group,
|
||||
challengeId, // Id
|
||||
challengeIndex, // Index
|
||||
List.of(param3, param6, 0),
|
||||
param3, // Limit
|
||||
param6, // Goal
|
||||
List.of(new InTimeTrigger(), new KillGadgetTrigger())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.factory;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.InTimeTrigger;
|
||||
import emu.grasscutter.game.dungeons.challenge.trigger.KillMonsterTrigger;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class KillMonsterChallengeFactoryHandler implements ChallengeFactoryHandler{
|
||||
@Override
|
||||
public boolean isThisType(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
// ActiveChallenge with 180,180,45,133108061,1,0
|
||||
return challengeId == 180;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorldChallenge build(int challengeIndex, int challengeId, int param3, int param4, int param5, int param6, Scene scene, SceneGroup group) {
|
||||
var realGroup = scene.getScriptManager().getGroupById(param4);
|
||||
return new WorldChallenge(
|
||||
scene, realGroup,
|
||||
challengeId, // Id
|
||||
challengeIndex, // Index
|
||||
List.of(param5, param3),
|
||||
param3, // Limit
|
||||
param5, // Goal
|
||||
List.of(new KillMonsterTrigger(), new InTimeTrigger())
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
|
||||
public abstract class ChallengeTrigger {
|
||||
public void onBegin(WorldChallenge challenge){}
|
||||
public void onFinish(WorldChallenge challenge){}
|
||||
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster){}
|
||||
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget){}
|
||||
public void onCheckTimeout(WorldChallenge challenge){}
|
||||
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget){}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||
|
||||
public class GuardTrigger extends KillMonsterTrigger{
|
||||
@Override
|
||||
public void onBegin(WorldChallenge challenge) {
|
||||
super.onBegin(challenge);
|
||||
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, 100));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGadgetDamage(WorldChallenge challenge, EntityGadget gadget) {
|
||||
var curHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_CUR_HP.getId());
|
||||
var maxHp = gadget.getFightProperties().get(FightProperty.FIGHT_PROP_BASE_HP.getId());
|
||||
int percent = (int) (curHp / maxHp);
|
||||
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, percent));
|
||||
|
||||
if(percent <= 0){
|
||||
challenge.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
|
||||
public class InTimeTrigger extends ChallengeTrigger{
|
||||
@Override
|
||||
public void onCheckTimeout(WorldChallenge challenge) {
|
||||
var current = System.currentTimeMillis();
|
||||
if(current - challenge.getStartedAt() > challenge.getTimeLimit() * 1000L){
|
||||
challenge.fail();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||
|
||||
public class KillGadgetTrigger extends ChallengeTrigger{
|
||||
@Override
|
||||
public void onBegin(WorldChallenge challenge) {
|
||||
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, challenge.getScore().get()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGadgetDeath(WorldChallenge challenge, EntityGadget gadget) {
|
||||
var newScore = challenge.increaseScore();
|
||||
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 2, newScore));
|
||||
|
||||
if(newScore >= challenge.getGoal()){
|
||||
challenge.done();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package emu.grasscutter.game.dungeons.challenge.trigger;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.server.packet.send.PacketChallengeDataNotify;
|
||||
|
||||
public class KillMonsterTrigger extends ChallengeTrigger{
|
||||
@Override
|
||||
public void onBegin(WorldChallenge challenge) {
|
||||
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, challenge.getScore().get()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMonsterDeath(WorldChallenge challenge, EntityMonster monster) {
|
||||
var newScore = challenge.increaseScore();
|
||||
challenge.getScene().broadcastPacket(new PacketChallengeDataNotify(challenge, 1, newScore));
|
||||
|
||||
if(newScore >= challenge.getGoal()){
|
||||
challenge.done();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,13 @@
|
||||
package emu.grasscutter.game.entity;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.GadgetData;
|
||||
import emu.grasscutter.game.entity.gadget.*;
|
||||
import emu.grasscutter.game.props.EntityIdType;
|
||||
import emu.grasscutter.game.props.EntityType;
|
||||
import emu.grasscutter.game.props.LifeState;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.proto.ClientGadgetInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
||||
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||
@@ -23,15 +20,17 @@ import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo;
|
||||
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||
import emu.grasscutter.net.proto.VectorOuterClass.Vector;
|
||||
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.SceneGadget;
|
||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.ProtoHelper;
|
||||
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import lombok.ToString;
|
||||
|
||||
@ToString(callSuper = true)
|
||||
public class EntityGadget extends EntityBaseGadget {
|
||||
private final GadgetData data;
|
||||
private final Position pos;
|
||||
@@ -39,7 +38,9 @@ public class EntityGadget extends EntityBaseGadget {
|
||||
private int gadgetId;
|
||||
|
||||
private int state;
|
||||
private IntSet worktopOptions;
|
||||
private int pointType;
|
||||
private GadgetContent content;
|
||||
private SceneGadget metaGadget;
|
||||
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos) {
|
||||
super(scene);
|
||||
@@ -50,19 +51,22 @@ public class EntityGadget extends EntityBaseGadget {
|
||||
this.rot = new Position();
|
||||
}
|
||||
|
||||
public EntityGadget(Scene scene, int gadgetId, Position pos, GadgetContent content) {
|
||||
this(scene, gadgetId, pos);
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public GadgetData getGadgetData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
// TODO Auto-generated method stub
|
||||
return this.pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
// TODO Auto-generated method stub
|
||||
return this.rot;
|
||||
}
|
||||
|
||||
@@ -81,34 +85,73 @@ public class EntityGadget extends EntityBaseGadget {
|
||||
public void setState(int state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void updateState(int state){
|
||||
this.setState(state);
|
||||
this.getScene().broadcastPacket(new PacketGadgetStateNotify(this, state));
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
|
||||
}
|
||||
|
||||
public IntSet getWorktopOptions() {
|
||||
return worktopOptions;
|
||||
public int getPointType() {
|
||||
return pointType;
|
||||
}
|
||||
|
||||
public void addWorktopOptions(int[] options) {
|
||||
if (this.worktopOptions == null) {
|
||||
this.worktopOptions = new IntOpenHashSet();
|
||||
}
|
||||
Arrays.stream(options).forEach(this.worktopOptions::add);
|
||||
|
||||
public void setPointType(int pointType) {
|
||||
this.pointType = pointType;
|
||||
}
|
||||
|
||||
public void removeWorktopOption(int option) {
|
||||
if (this.worktopOptions == null) {
|
||||
|
||||
public GadgetContent getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Deprecated // Dont use!
|
||||
public void setContent(GadgetContent content) {
|
||||
this.content = this.content == null ? content : this.content;
|
||||
}
|
||||
|
||||
public SceneGadget getMetaGadget() {
|
||||
return metaGadget;
|
||||
}
|
||||
|
||||
public void setMetaGadget(SceneGadget metaGadget) {
|
||||
this.metaGadget = metaGadget;
|
||||
}
|
||||
|
||||
// TODO refactor
|
||||
public void buildContent() {
|
||||
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) {
|
||||
return;
|
||||
}
|
||||
this.worktopOptions.remove(option);
|
||||
|
||||
EntityType type = getGadgetData().getType();
|
||||
GadgetContent content = switch (type) {
|
||||
case GatherPoint -> new GadgetGatherPoint(this);
|
||||
case Worktop -> new GadgetWorktop(this);
|
||||
case RewardStatue -> new GadgetRewardStatue(this);
|
||||
case Chest -> new GadgetChest(this);
|
||||
default -> null;
|
||||
};
|
||||
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Lua event
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeath(int killerId) {
|
||||
|
||||
if(getScene().getChallenge() != null){
|
||||
getScene().getChallenge().onGadgetDeath(this);
|
||||
}
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_GADGET_DIE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -143,15 +186,16 @@ public class EntityGadget extends EntityBaseGadget {
|
||||
.setIsEnableInteract(true)
|
||||
.setAuthorityPeerId(this.getScene().getWorld().getHostPeerId());
|
||||
|
||||
if (this.getGadgetData().getType() == EntityType.Worktop && this.getWorktopOptions() != null) {
|
||||
WorktopInfo worktop = WorktopInfo.newBuilder()
|
||||
.addAllOptionList(this.getWorktopOptions())
|
||||
.build();
|
||||
gadgetInfo.setWorktop(worktop);
|
||||
if (this.getContent() != null) {
|
||||
this.getContent().onBuildProto(gadgetInfo);
|
||||
}
|
||||
|
||||
entityInfo.setGadget(gadgetInfo);
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
public void die() {
|
||||
getScene().broadcastPacket(new PacketLifeStateChangeNotify(this, LifeState.LIFE_DEAD));
|
||||
this.onDeath(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,11 @@ import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.common.PropGrowCurve;
|
||||
import emu.grasscutter.data.excels.MonsterCurveData;
|
||||
import emu.grasscutter.data.excels.MonsterData;
|
||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.EntityIdType;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.game.world.World;
|
||||
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
|
||||
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
|
||||
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
|
||||
@@ -25,6 +23,7 @@ import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo;
|
||||
import emu.grasscutter.net.proto.SceneMonsterInfoOuterClass.SceneMonsterInfo;
|
||||
import emu.grasscutter.net.proto.SceneWeaponInfoOuterClass.SceneWeaponInfo;
|
||||
import emu.grasscutter.scripts.constants.EventType;
|
||||
import emu.grasscutter.scripts.data.ScriptArgs;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.ProtoHelper;
|
||||
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
|
||||
@@ -111,6 +110,12 @@ public class EntityMonster extends GameEntity {
|
||||
public void setPoseId(int poseId) {
|
||||
this.poseId = poseId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// Lua event
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(this.getConfigId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(float amount, int killerId) {
|
||||
@@ -135,8 +140,8 @@ public class EntityMonster extends GameEntity {
|
||||
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
|
||||
}
|
||||
// first set the challenge data
|
||||
if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) {
|
||||
getScene().getChallenge().onMonsterDie(this);
|
||||
if (getScene().getChallenge() != null) {
|
||||
getScene().getChallenge().onMonsterDeath(this);
|
||||
}
|
||||
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) {
|
||||
if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){
|
||||
@@ -144,7 +149,9 @@ public class EntityMonster extends GameEntity {
|
||||
}
|
||||
// prevent spawn monster after success
|
||||
if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null);
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||
}else if(getScene().getChallenge() == null){
|
||||
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
81
src/main/java/emu/grasscutter/game/entity/EntityNPC.java
Normal file
81
src/main/java/emu/grasscutter/game/entity/EntityNPC.java
Normal file
@@ -0,0 +1,81 @@
|
||||
package emu.grasscutter.game.entity;
|
||||
|
||||
import emu.grasscutter.game.props.EntityIdType;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
import emu.grasscutter.net.proto.*;
|
||||
import emu.grasscutter.scripts.data.SceneNPC;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
|
||||
|
||||
public class EntityNPC extends GameEntity{
|
||||
|
||||
private final Position position;
|
||||
private final Position rotation;
|
||||
private final SceneNPC metaNpc;
|
||||
private final int suiteId;
|
||||
|
||||
public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) {
|
||||
super(scene);
|
||||
this.id = getScene().getWorld().getNextEntityId(EntityIdType.NPC);
|
||||
setConfigId(metaNPC.config_id);
|
||||
setGroupId(metaNPC.group.id);
|
||||
setBlockId(blockId);
|
||||
this.suiteId = suiteId;
|
||||
this.position = metaNPC.pos.clone();
|
||||
this.rotation = metaNPC.rot.clone();
|
||||
this.metaNpc = metaNPC;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Int2FloatOpenHashMap getFightProperties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getRotation() {
|
||||
return rotation;
|
||||
}
|
||||
|
||||
public int getSuiteId() {
|
||||
return suiteId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
|
||||
|
||||
EntityAuthorityInfoOuterClass.EntityAuthorityInfo authority = EntityAuthorityInfoOuterClass.EntityAuthorityInfo.newBuilder()
|
||||
.setAbilityInfo(AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo.newBuilder())
|
||||
.setRendererChangedInfo(EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo.newBuilder())
|
||||
.setAiInfo(SceneEntityAiInfoOuterClass.SceneEntityAiInfo.newBuilder()
|
||||
.setIsAiOpen(true)
|
||||
.setBornPos(getPosition().toProto()))
|
||||
.setBornPos(getPosition().toProto())
|
||||
.build();
|
||||
|
||||
SceneEntityInfoOuterClass.SceneEntityInfo.Builder entityInfo = SceneEntityInfoOuterClass.SceneEntityInfo.newBuilder()
|
||||
.setEntityId(getId())
|
||||
.setEntityType(ProtEntityTypeOuterClass.ProtEntityType.PROT_ENTITY_NPC)
|
||||
.setMotionInfo(MotionInfoOuterClass.MotionInfo.newBuilder()
|
||||
.setPos(getPosition().toProto())
|
||||
.setRot(getRotation().toProto())
|
||||
.setSpeed(VectorOuterClass.Vector.newBuilder()))
|
||||
.addAnimatorParaList(AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair.newBuilder())
|
||||
.setEntityClientData(EntityClientDataOuterClass.EntityClientData.newBuilder())
|
||||
.setEntityAuthorityInfo(authority)
|
||||
.setLifeState(1);
|
||||
|
||||
|
||||
entityInfo.setNpc(SceneNpcInfoOuterClass.SceneNpcInfo.newBuilder()
|
||||
.setNpcId(metaNpc.npc_id)
|
||||
.setBlockId(getBlockId())
|
||||
.build());
|
||||
|
||||
return entityInfo.build();
|
||||
}
|
||||
}
|
||||
@@ -107,10 +107,6 @@ public abstract class GameEntity {
|
||||
public void setLastMoveReliableSeq(int lastMoveReliableSeq) {
|
||||
this.lastMoveReliableSeq = lastMoveReliableSeq;
|
||||
}
|
||||
|
||||
public abstract SceneEntityInfo toProto();
|
||||
|
||||
public abstract void onDeath(int killerId);
|
||||
|
||||
public void setFightProperty(FightProperty prop, float value) {
|
||||
this.getFightProperties().put(prop.getId(), value);
|
||||
@@ -219,4 +215,21 @@ public abstract class GameEntity {
|
||||
getScene().killEntity(this, killerId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this entity is added to the world
|
||||
*/
|
||||
public void onCreate() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this entity dies
|
||||
* @param killerId Entity id of the entity that killed this entity
|
||||
*/
|
||||
public void onDeath(int killerId) {
|
||||
|
||||
}
|
||||
|
||||
public abstract SceneEntityInfo toProto();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package emu.grasscutter.game.entity.gadget;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.BossChestInfoOuterClass.BossChestInfo;
|
||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.InteractTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||
import emu.grasscutter.scripts.constants.ScriptGadgetState;
|
||||
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||
|
||||
import static emu.grasscutter.net.proto.InterOpTypeOuterClass.InterOpType.INTER_OP_START;
|
||||
|
||||
public class GadgetChest extends GadgetContent {
|
||||
|
||||
public GadgetChest(EntityGadget gadget) {
|
||||
super(gadget);
|
||||
}
|
||||
|
||||
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||
var chestInteractHandlerMap = getGadget().getScene().getWorld().getServer().getWorldDataManager().getChestInteractHandlerMap();
|
||||
var handler = chestInteractHandlerMap.get(getGadget().getGadgetData().getJsonName());
|
||||
if(handler == null){
|
||||
Grasscutter.getLogger().warn("Could not found the handler of this type of Chests {}", getGadget().getGadgetData().getJsonName());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(opType == INTER_OP_START && handler.isTwoStep()){
|
||||
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_CHEST, INTER_OP_START));
|
||||
return false;
|
||||
}else{
|
||||
var success = handler.onInteract(this, player);
|
||||
if (!success){
|
||||
return false;
|
||||
}
|
||||
|
||||
getGadget().updateState(ScriptGadgetState.ChestOpened);
|
||||
player.sendPacket(new PacketGadgetInteractRsp(this.getGadget(), InteractTypeOuterClass.InteractType.INTERACT_OPEN_CHEST));
|
||||
// let the chest disappear
|
||||
getGadget().die();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||
if(getGadget().getMetaGadget() == null){
|
||||
return;
|
||||
}
|
||||
|
||||
var bossChest = getGadget().getMetaGadget().boss_chest;
|
||||
if(bossChest != null){
|
||||
var players = getGadget().getScene().getPlayers().stream().map(Player::getUid).toList();
|
||||
|
||||
gadgetInfo.setBossChest(BossChestInfo.newBuilder()
|
||||
.setMonsterConfigId(bossChest.monster_config_id)
|
||||
.setResin(bossChest.resin)
|
||||
.addAllQualifyUidList(players)
|
||||
.addAllRemainUidList(players)
|
||||
.build());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package emu.grasscutter.game.entity.gadget;
|
||||
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||
|
||||
public abstract class GadgetContent {
|
||||
private final EntityGadget gadget;
|
||||
|
||||
public GadgetContent(EntityGadget gadget) {
|
||||
this.gadget = gadget;
|
||||
}
|
||||
|
||||
public EntityGadget getGadget() {
|
||||
return gadget;
|
||||
}
|
||||
|
||||
public abstract boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType);
|
||||
|
||||
public abstract void onBuildProto(SceneGadgetInfo.Builder gadgetInfo);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package emu.grasscutter.game.entity.gadget;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.GatherData;
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.net.proto.GatherGadgetInfoOuterClass.GatherGadgetInfo;
|
||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||
|
||||
public class GadgetGatherPoint extends GadgetContent {
|
||||
private GatherData gatherData;
|
||||
|
||||
public GadgetGatherPoint(EntityGadget gadget) {
|
||||
super(gadget);
|
||||
this.gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
|
||||
}
|
||||
|
||||
public GatherData getGatherData() {
|
||||
return gatherData;
|
||||
}
|
||||
|
||||
public int getItemId() {
|
||||
return getGatherData().getItemId();
|
||||
}
|
||||
|
||||
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||
GameItem item = new GameItem(gatherData.getItemId(), 1);
|
||||
|
||||
player.getInventory().addItem(item, ActionReason.Gather);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||
GatherGadgetInfo gatherGadgetInfo = GatherGadgetInfo.newBuilder()
|
||||
.setItemId(this.getItemId())
|
||||
.setIsForbidGuest(this.getGatherData().isForbidGuest())
|
||||
.build();
|
||||
|
||||
gadgetInfo.setGatherGadget(gatherGadgetInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package emu.grasscutter.game.entity.gadget;
|
||||
|
||||
import emu.grasscutter.game.dungeons.challenge.DungeonChallenge;
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
|
||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
|
||||
|
||||
public class GadgetRewardStatue extends GadgetContent {
|
||||
|
||||
public GadgetRewardStatue(EntityGadget gadget) {
|
||||
super(gadget);
|
||||
}
|
||||
|
||||
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||
if (player.getScene().getChallenge() != null && player.getScene().getChallenge() instanceof DungeonChallenge dungeonChallenge) {
|
||||
dungeonChallenge.getStatueDrops(player);
|
||||
}
|
||||
|
||||
player.sendPacket(new PacketGadgetInteractRsp(getGadget(), InteractType.INTERACT_OPEN_STATUE));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package emu.grasscutter.game.entity.gadget;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import emu.grasscutter.game.entity.EntityGadget;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.net.proto.InterOpTypeOuterClass;
|
||||
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
|
||||
import emu.grasscutter.net.proto.WorktopInfoOuterClass.WorktopInfo;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
public class GadgetWorktop extends GadgetContent {
|
||||
private IntSet worktopOptions;
|
||||
|
||||
public GadgetWorktop(EntityGadget gadget) {
|
||||
super(gadget);
|
||||
}
|
||||
|
||||
public IntSet getWorktopOptions() {
|
||||
return worktopOptions;
|
||||
}
|
||||
|
||||
public void addWorktopOptions(int[] options) {
|
||||
if (this.worktopOptions == null) {
|
||||
this.worktopOptions = new IntOpenHashSet();
|
||||
}
|
||||
Arrays.stream(options).forEach(this.worktopOptions::add);
|
||||
}
|
||||
|
||||
public void removeWorktopOption(int option) {
|
||||
if (this.worktopOptions == null) {
|
||||
return;
|
||||
}
|
||||
this.worktopOptions.remove(option);
|
||||
}
|
||||
|
||||
public boolean onInteract(Player player, InterOpTypeOuterClass.InterOpType opType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void onBuildProto(SceneGadgetInfo.Builder gadgetInfo) {
|
||||
if (this.worktopOptions == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
WorktopInfo worktop = WorktopInfo.newBuilder()
|
||||
.addAllOptionList(this.getWorktopOptions())
|
||||
.build();
|
||||
|
||||
gadgetInfo.setWorktop(worktop);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package emu.grasscutter.game.entity.gadget.chest;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.server.packet.send.PacketGadgetAutoPickDropInfoNotify;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BossChestInteractHandler implements ChestInteractHandler{
|
||||
@Override
|
||||
public boolean isTwoStep() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInteract(GadgetChest chest, Player player) {
|
||||
var worldDataManager = chest.getGadget().getScene().getWorld().getServer().getWorldDataManager();
|
||||
var monster = chest.getGadget().getMetaGadget().group.monsters.get(chest.getGadget().getMetaGadget().boss_chest.monster_config_id);
|
||||
var reward = worldDataManager.getRewardByBossId(monster.monster_id);
|
||||
|
||||
if(reward == null){
|
||||
Grasscutter.getLogger().warn("Could not found the reward of boss monster {}", monster.monster_id);
|
||||
return false;
|
||||
}
|
||||
List<GameItem> rewards = new ArrayList<>();
|
||||
for (ItemParamData param : reward.getPreviewItems()) {
|
||||
rewards.add(new GameItem(param.getId(), Math.max(param.getCount(), 1)));
|
||||
}
|
||||
|
||||
player.getInventory().addItems(rewards, ActionReason.OpenWorldBossChest);
|
||||
player.sendPacket(new PacketGadgetAutoPickDropInfoNotify(rewards));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package emu.grasscutter.game.entity.gadget.chest;
|
||||
|
||||
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
public interface ChestInteractHandler {
|
||||
|
||||
boolean isTwoStep();
|
||||
|
||||
boolean onInteract(GadgetChest chest, Player player);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package emu.grasscutter.game.entity.gadget.chest;
|
||||
|
||||
import emu.grasscutter.game.entity.gadget.GadgetChest;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.world.ChestReward;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class NormalChestInteractHandler implements ChestInteractHandler {
|
||||
private final ChestReward chestReward;
|
||||
|
||||
public NormalChestInteractHandler(ChestReward rewardData){
|
||||
this.chestReward = rewardData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTwoStep() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInteract(GadgetChest chest, Player player) {
|
||||
player.earnExp(chestReward.getAdvExp());
|
||||
player.getInventory().addItem(201, chestReward.getResin());
|
||||
|
||||
var mora = chestReward.getMora() * (1 + (player.getWorldLevel() - 1) * 0.5);
|
||||
player.getInventory().addItem(202, (int)mora);
|
||||
|
||||
for(int i=0;i<chestReward.getContent().size();i++){
|
||||
chest.getGadget().getScene().addItemEntity(chestReward.getContent().get(i).getItemId(), chestReward.getContent().get(i).getCount(), chest.getGadget());
|
||||
}
|
||||
|
||||
var random = new Random(System.currentTimeMillis());
|
||||
for(int i=0;i<chestReward.getRandomCount();i++){
|
||||
var index = random.nextInt(chestReward.getRandomContent().size());
|
||||
var item = chestReward.getRandomContent().get(index);
|
||||
chest.getGadget().getScene().addItemEntity(item.getItemId(), item.getCount(), chest.getGadget());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,6 @@ import emu.grasscutter.game.managers.mapmark.*;
|
||||
import emu.grasscutter.game.managers.stamina.StaminaManager;
|
||||
import emu.grasscutter.game.managers.SotSManager;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.game.props.EntityType;
|
||||
import emu.grasscutter.game.props.PlayerProperty;
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import emu.grasscutter.game.quest.QuestManager;
|
||||
@@ -988,7 +987,8 @@ public class Player {
|
||||
return this.getMailHandler().replaceMailByIndex(index, message);
|
||||
}
|
||||
|
||||
public void interactWith(int gadgetEntityId, GadgetInteractReq request) {
|
||||
|
||||
public void interactWith(int gadgetEntityId, InterOpTypeOuterClass.InterOpType opType) {
|
||||
GameEntity entity = getScene().getEntityById(gadgetEntityId);
|
||||
if (entity == null) {
|
||||
return;
|
||||
@@ -1016,11 +1016,15 @@ public class Player {
|
||||
}
|
||||
}
|
||||
} else if (entity instanceof EntityGadget gadget) {
|
||||
if (gadget.getGadgetData().getType() == EntityType.RewardStatue) {
|
||||
if (scene.getChallenge() != null) {
|
||||
scene.getChallenge().getStatueDrops(this, request);
|
||||
}
|
||||
this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_TYPE_OPEN_STATUE));
|
||||
|
||||
if (gadget.getContent() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean shouldDelete = gadget.getContent().onInteract(this, opType);
|
||||
|
||||
if (shouldDelete) {
|
||||
entity.getScene().removeEntity(entity);
|
||||
}
|
||||
} else if (entity instanceof EntityMonster monster) {
|
||||
insectCaptureManager.arrestSmallCreature(monster);
|
||||
|
||||
22
src/main/java/emu/grasscutter/game/world/ChestReward.java
Normal file
22
src/main/java/emu/grasscutter/game/world/ChestReward.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import emu.grasscutter.game.inventory.ItemDef;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
public class ChestReward {
|
||||
List<String> objNames;
|
||||
int advExp;
|
||||
int resin;
|
||||
int mora;
|
||||
int sigil;
|
||||
List<ItemDef> content;
|
||||
int randomCount;
|
||||
List<ItemDef> randomContent;
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameDepot;
|
||||
import emu.grasscutter.data.excels.*;
|
||||
import emu.grasscutter.game.dungeons.DungeonChallenge;
|
||||
import emu.grasscutter.game.dungeons.DungeonSettleListener;
|
||||
import emu.grasscutter.game.entity.*;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
@@ -13,24 +13,24 @@ import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.game.props.LifeState;
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
||||
import emu.grasscutter.game.dungeons.challenge.WorldChallenge;
|
||||
import emu.grasscutter.net.packet.BasePacket;
|
||||
import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult;
|
||||
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
|
||||
import emu.grasscutter.scripts.SceneIndexManager;
|
||||
import emu.grasscutter.scripts.SceneScriptManager;
|
||||
import emu.grasscutter.scripts.data.SceneBlock;
|
||||
import emu.grasscutter.scripts.data.SceneGadget;
|
||||
import emu.grasscutter.scripts.data.SceneGroup;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarSkillInfoNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketDungeonChallengeFinishNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify;
|
||||
import emu.grasscutter.server.packet.send.*;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.danilopianini.util.SpatialIndex;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Scene {
|
||||
private final World world;
|
||||
@@ -49,12 +49,11 @@ public class Scene {
|
||||
private int weather;
|
||||
|
||||
private SceneScriptManager scriptManager;
|
||||
private DungeonChallenge challenge;
|
||||
private WorldChallenge challenge;
|
||||
private List<DungeonSettleListener> dungeonSettleListeners;
|
||||
private DungeonData dungeonData;
|
||||
private int prevScene; // Id of the previous scene
|
||||
private int prevScenePoint;
|
||||
|
||||
public Scene(World world, SceneData sceneData) {
|
||||
this.world = world;
|
||||
this.sceneData = sceneData;
|
||||
@@ -198,11 +197,11 @@ public class Scene {
|
||||
this.dungeonData = dungeonData;
|
||||
}
|
||||
|
||||
public DungeonChallenge getChallenge() {
|
||||
public WorldChallenge getChallenge() {
|
||||
return challenge;
|
||||
}
|
||||
|
||||
public void setChallenge(DungeonChallenge challenge) {
|
||||
public void setChallenge(WorldChallenge challenge) {
|
||||
this.challenge = challenge;
|
||||
}
|
||||
|
||||
@@ -310,6 +309,7 @@ public class Scene {
|
||||
|
||||
private void addEntityDirectly(GameEntity entity) {
|
||||
getEntities().put(entity.getId(), entity);
|
||||
entity.onCreate(); // Call entity create event
|
||||
}
|
||||
|
||||
public synchronized void addEntity(GameEntity entity) {
|
||||
@@ -320,14 +320,21 @@ public class Scene {
|
||||
public synchronized void addEntityToSingleClient(Player player, GameEntity entity) {
|
||||
this.addEntityDirectly(entity);
|
||||
player.sendPacket(new PacketSceneEntityAppearNotify(entity));
|
||||
|
||||
}
|
||||
public void addEntities(Collection<? extends GameEntity> entities){
|
||||
addEntities(entities, VisionType.VISION_TYPE_BORN);
|
||||
}
|
||||
|
||||
public synchronized void addEntities(Collection<GameEntity> entities) {
|
||||
public synchronized void addEntities(Collection<? extends GameEntity> entities, VisionType visionType) {
|
||||
if(entities == null || entities.isEmpty()){
|
||||
return;
|
||||
}
|
||||
for (GameEntity entity : entities) {
|
||||
this.addEntityDirectly(entity);
|
||||
}
|
||||
|
||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_BORN));
|
||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, visionType));
|
||||
}
|
||||
|
||||
private GameEntity removeEntityDirectly(GameEntity entity) {
|
||||
@@ -344,14 +351,21 @@ public class Scene {
|
||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(removed, visionType));
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeEntities(List<GameEntity> entity, VisionType visionType) {
|
||||
var toRemove = entity.stream()
|
||||
.map(this::removeEntityDirectly)
|
||||
.toList();
|
||||
if (toRemove.size() > 0) {
|
||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, visionType));
|
||||
}
|
||||
}
|
||||
public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) {
|
||||
this.removeEntityDirectly(oldEntity);
|
||||
this.addEntityDirectly(newEntity);
|
||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_TYPE_REPLACE));
|
||||
this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_TYPE_REPLACE, oldEntity.getId()));
|
||||
}
|
||||
|
||||
|
||||
public void showOtherEntities(Player player) {
|
||||
List<GameEntity> entities = new LinkedList<>();
|
||||
GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity();
|
||||
@@ -362,7 +376,7 @@ public class Scene {
|
||||
}
|
||||
entities.add(entity);
|
||||
}
|
||||
|
||||
|
||||
player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET));
|
||||
}
|
||||
|
||||
@@ -412,7 +426,7 @@ public class Scene {
|
||||
// Death event
|
||||
target.onDeath(attackerId);
|
||||
}
|
||||
|
||||
|
||||
public void onTick() {
|
||||
// disable script for home
|
||||
if (this.getSceneType() == SceneType.SCENE_HOME_WORLD || this.getSceneType() == SceneType.SCENE_HOME_ROOM){
|
||||
@@ -424,9 +438,12 @@ public class Scene {
|
||||
// TEMPORARY
|
||||
this.checkSpawns();
|
||||
}
|
||||
|
||||
// Triggers
|
||||
this.getScriptManager().onTick();
|
||||
this.scriptManager.checkRegions();
|
||||
|
||||
if(challenge != null){
|
||||
challenge.onCheckTimeOut();
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - Test
|
||||
@@ -502,83 +519,152 @@ public class Scene {
|
||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public List<SceneBlock> getPlayerActiveBlocks(Player player){
|
||||
// consider the borders' entities of blocks, so we check if contains by index
|
||||
return SceneIndexManager.queryNeighbors(getScriptManager().getBlocksIndex(),
|
||||
player.getPos().toXZDoubleArray(), Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
||||
}
|
||||
public void checkBlocks() {
|
||||
Set<SceneBlock> visible = new HashSet<>();
|
||||
|
||||
for (Player player : this.getPlayers()) {
|
||||
for (SceneBlock block : getScriptManager().getBlocks()) {
|
||||
if (!block.contains(player.getPos())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
visible.add(block);
|
||||
}
|
||||
var blocks = getPlayerActiveBlocks(player);
|
||||
visible.addAll(blocks);
|
||||
}
|
||||
|
||||
|
||||
Iterator<SceneBlock> it = this.getLoadedBlocks().iterator();
|
||||
while (it.hasNext()) {
|
||||
SceneBlock block = it.next();
|
||||
|
||||
|
||||
if (!visible.contains(block)) {
|
||||
it.remove();
|
||||
|
||||
|
||||
onUnloadBlock(block);
|
||||
}
|
||||
}
|
||||
|
||||
for (SceneBlock block : visible) {
|
||||
|
||||
for(var block : visible){
|
||||
if (!this.getLoadedBlocks().contains(block)) {
|
||||
this.onLoadBlock(block);
|
||||
this.onLoadBlock(block, this.getPlayers());
|
||||
this.getLoadedBlocks().add(block);
|
||||
}else{
|
||||
// dynamic load the groups for players in a loaded block
|
||||
var toLoad = this.getPlayers().stream()
|
||||
.filter(p -> block.contains(p.getPos()))
|
||||
.map(p -> playerMeetGroups(p, block))
|
||||
.flatMap(Collection::stream)
|
||||
.toList();
|
||||
onLoadGroup(toLoad);
|
||||
}
|
||||
for (Player player : this.getPlayers()) {
|
||||
getScriptManager().meetEntities(loadNpcForPlayer(player, block));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
public void onLoadBlock(SceneBlock block) {
|
||||
for (SceneGroup group : block.groups) {
|
||||
// We load the script files for the groups here
|
||||
if (!group.isLoaded()) {
|
||||
this.getScriptManager().loadGroupFromScript(group);
|
||||
}
|
||||
|
||||
group.triggers.forEach(getScriptManager()::registerTrigger);
|
||||
group.regions.forEach(getScriptManager()::registerRegion);
|
||||
public List<SceneGroup> playerMeetGroups(Player player, SceneBlock block){
|
||||
List<SceneGroup> sceneGroups = SceneIndexManager.queryNeighbors(block.sceneGroupIndex, player.getPos().toDoubleArray(),
|
||||
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
||||
|
||||
List<SceneGroup> groups = sceneGroups.stream()
|
||||
.filter(group -> !scriptManager.getLoadedGroupSetPerBlock().get(block.id).contains(group))
|
||||
.peek(group -> scriptManager.getLoadedGroupSetPerBlock().get(block.id).add(group))
|
||||
.toList();
|
||||
|
||||
if (groups.size() == 0) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
|
||||
return groups;
|
||||
}
|
||||
public void onLoadBlock(SceneBlock block, List<Player> players) {
|
||||
this.getScriptManager().loadBlockFromScript(block);
|
||||
scriptManager.getLoadedGroupSetPerBlock().put(block.id , new HashSet<>());
|
||||
|
||||
// the groups form here is not added in current scene
|
||||
var groups = players.stream()
|
||||
.filter(player -> block.contains(player.getPos()))
|
||||
.map(p -> playerMeetGroups(p, block))
|
||||
.flatMap(Collection::stream)
|
||||
.toList();
|
||||
|
||||
onLoadGroup(groups);
|
||||
Grasscutter.getLogger().info("Scene {} Block {} loaded.", this.getId(), block.id);
|
||||
}
|
||||
|
||||
public void onLoadGroup(List<SceneGroup> groups){
|
||||
if(groups == null || groups.isEmpty()){
|
||||
return;
|
||||
}
|
||||
for (SceneGroup group : groups) {
|
||||
// We load the script files for the groups here
|
||||
this.getScriptManager().loadGroupFromScript(group);
|
||||
}
|
||||
|
||||
// Spawn gadgets AFTER triggers are added
|
||||
// TODO
|
||||
for (SceneGroup group : block.groups) {
|
||||
var entities = new ArrayList<GameEntity>();
|
||||
for (SceneGroup group : groups) {
|
||||
if (group.init_config == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int suite = group.init_config.suite;
|
||||
// Load garbages
|
||||
List<SceneGadget> garbageGadgets = group.getGarbageGadgets();
|
||||
|
||||
if (suite == 0) {
|
||||
if (garbageGadgets != null) {
|
||||
entities.addAll(garbageGadgets.stream().map(g -> scriptManager.createGadget(group.id, group.block_id, g))
|
||||
.filter(Objects::nonNull)
|
||||
.toList());
|
||||
}
|
||||
|
||||
// Load suites
|
||||
int suite = group.init_config.suite;
|
||||
|
||||
if (suite == 0 || group.suites == null || group.suites.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
do {
|
||||
this.getScriptManager().spawnGadgetsInGroup(group, suite);
|
||||
suite++;
|
||||
} while (suite < group.init_config.end_suite);
|
||||
|
||||
// just load the 'init' suite, avoid spawn the suite added by AddExtraGroupSuite etc.
|
||||
var suiteData = group.getSuiteByIndex(suite);
|
||||
suiteData.sceneTriggers.forEach(getScriptManager()::registerTrigger);
|
||||
|
||||
entities.addAll(suiteData.sceneGadgets.stream()
|
||||
.map(g -> scriptManager.createGadget(group.id, group.block_id, g))
|
||||
.filter(Objects::nonNull)
|
||||
.toList());
|
||||
entities.addAll(suiteData.sceneMonsters.stream()
|
||||
.map(mob -> scriptManager.createMonster(group.id, group.block_id, mob))
|
||||
.filter(Objects::nonNull)
|
||||
.toList());
|
||||
|
||||
}
|
||||
|
||||
scriptManager.meetEntities(entities);
|
||||
//scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null);
|
||||
//groups.forEach(g -> scriptManager.callEvent(EventType.EVENT_GROUP_LOAD, null));
|
||||
Grasscutter.getLogger().info("Scene {} loaded {} group(s)", this.getId(), groups.size());
|
||||
}
|
||||
|
||||
public void onUnloadBlock(SceneBlock block) {
|
||||
List<GameEntity> toRemove = this.getEntities().values().stream().filter(e -> e.getBlockId() == block.id).toList();
|
||||
List<GameEntity> toRemove = this.getEntities().values().stream()
|
||||
.filter(e -> e.getBlockId() == block.id).toList();
|
||||
|
||||
if (toRemove.size() > 0) {
|
||||
toRemove.stream().forEach(this::removeEntityDirectly);
|
||||
toRemove.forEach(this::removeEntityDirectly);
|
||||
this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE));
|
||||
}
|
||||
|
||||
for (SceneGroup group : block.groups) {
|
||||
group.triggers.forEach(getScriptManager()::deregisterTrigger);
|
||||
group.regions.forEach(getScriptManager()::deregisterRegion);
|
||||
for (SceneGroup group : block.groups.values()) {
|
||||
if(group.triggers != null){
|
||||
group.triggers.values().forEach(getScriptManager()::deregisterTrigger);
|
||||
}
|
||||
if(group.regions != null){
|
||||
group.regions.forEach(getScriptManager()::deregisterRegion);
|
||||
}
|
||||
}
|
||||
scriptManager.getLoadedGroupSetPerBlock().remove(block.id);
|
||||
Grasscutter.getLogger().info("Scene {} Block {} is unloaded.", this.getId(), block.id);
|
||||
}
|
||||
|
||||
// Gadgets
|
||||
@@ -643,4 +729,65 @@ public class Scene {
|
||||
player.getSession().send(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void addItemEntity(int itemId, int amount, GameEntity bornForm){
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if (itemData == null) {
|
||||
return;
|
||||
}
|
||||
if (itemData.isEquip()) {
|
||||
float range = (3f + (.1f * amount));
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Position pos = bornForm.getPosition().clone().addX((float) (Math.random() * range) - (range / 2)).addZ((float) (Math.random() * range) - (range / 2)).addZ(.9f);
|
||||
EntityItem entity = new EntityItem(this, null, itemData, pos, 1);
|
||||
addEntity(entity);
|
||||
}
|
||||
} else {
|
||||
EntityItem entity = new EntityItem(this, null, itemData, bornForm.getPosition().clone().addZ(.9f), amount);
|
||||
addEntity(entity);
|
||||
}
|
||||
}
|
||||
public List<EntityNPC> loadNpcForPlayer(Player player, SceneBlock block){
|
||||
if(!block.contains(player.getPos())){
|
||||
return List.of();
|
||||
}
|
||||
|
||||
var pos = player.getPos();
|
||||
var data = GameData.getSceneNpcBornData().get(getId());
|
||||
if(data == null){
|
||||
return List.of();
|
||||
}
|
||||
|
||||
var npcs = SceneIndexManager.queryNeighbors(data.getIndex(), pos.toDoubleArray(),
|
||||
Grasscutter.getConfig().server.game.loadEntitiesForPlayerRange);
|
||||
var entityNPCS = npcs.stream().map(item -> {
|
||||
var group = data.getGroups().get(item.getGroupId());
|
||||
if(group == null){
|
||||
group = SceneGroup.of(item.getGroupId());
|
||||
data.getGroups().putIfAbsent(item.getGroupId(), group);
|
||||
group.load(getId());
|
||||
}
|
||||
|
||||
if(group.npc == null){
|
||||
return null;
|
||||
}
|
||||
var npc = group.npc.get(item.getConfigId());
|
||||
if(npc == null){
|
||||
return null;
|
||||
}
|
||||
|
||||
return getScriptManager().createNPC(npc, block.id, item.getSuiteIdList().get(0));
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.filter(item -> getEntities().values().stream()
|
||||
.filter(e -> e instanceof EntityNPC)
|
||||
.noneMatch(e -> e.getConfigId() == item.getConfigId()))
|
||||
.toList();
|
||||
|
||||
if(entityNPCS.size() > 0){
|
||||
broadcastPacket(new PacketGroupSuiteNotify(entityNPCS));
|
||||
}
|
||||
|
||||
return entityNPCS;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package emu.grasscutter.game.world;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.DataLoader;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.RewardPreviewData;
|
||||
import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler;
|
||||
import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler;
|
||||
import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WorldDataManager {
|
||||
private final GameServer gameServer;
|
||||
private final Map<String, ChestInteractHandler> chestInteractHandlerMap; // chestType-Handler
|
||||
|
||||
public WorldDataManager(GameServer gameServer){
|
||||
this.gameServer = gameServer;
|
||||
this.chestInteractHandlerMap = new HashMap<>();
|
||||
loadChestConfig();
|
||||
}
|
||||
|
||||
public synchronized void loadChestConfig(){
|
||||
// set the special chest first
|
||||
chestInteractHandlerMap.put("SceneObj_Chest_Flora", new BossChestInteractHandler());
|
||||
|
||||
try(InputStream is = DataLoader.load("ChestReward.json"); InputStreamReader isr = new InputStreamReader(is)) {
|
||||
List<ChestReward> chestReward = Grasscutter.getGsonFactory().fromJson(
|
||||
isr,
|
||||
TypeToken.getParameterized(List.class, ChestReward.class).getType());
|
||||
|
||||
chestReward.forEach(reward ->
|
||||
reward.getObjNames().forEach(
|
||||
name -> chestInteractHandlerMap.putIfAbsent(name, new NormalChestInteractHandler(reward))));
|
||||
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Unable to load chest reward config.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, ChestInteractHandler> getChestInteractHandlerMap() {
|
||||
return chestInteractHandlerMap;
|
||||
}
|
||||
|
||||
public RewardPreviewData getRewardByBossId(int monsterId){
|
||||
var investigationMonsterData = GameData.getInvestigationMonsterDataMap().values().parallelStream()
|
||||
.filter(imd -> imd.getMonsterIdList() != null && !imd.getMonsterIdList().isEmpty())
|
||||
.filter(imd -> imd.getMonsterIdList().get(0) == monsterId)
|
||||
.findFirst();
|
||||
|
||||
if(investigationMonsterData.isEmpty()){
|
||||
return null;
|
||||
}
|
||||
return GameData.getRewardPreviewDataMap().get(investigationMonsterData.get().getRewardPreviewId());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user