Attempt to implement bargaining (untested)

This commit is contained in:
KingRainbow44
2023-08-13 12:28:56 -04:00
parent 40bbfd90e1
commit 597574ddda
20 changed files with 514 additions and 7 deletions

View File

@@ -2,7 +2,7 @@ package emu.grasscutter.game.player;
import dev.morphia.annotations.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.quest.ItemGiveRecord;
import emu.grasscutter.game.quest.*;
import emu.grasscutter.game.quest.enums.QuestContent;
import it.unimi.dsi.fastutil.ints.*;
import lombok.*;
@@ -29,12 +29,14 @@ public class PlayerProgress {
private Map<String, Integer> questProgressCountMap;
private Map<Integer, ItemGiveRecord> itemGivings;
private Map<Integer, BargainRecord> bargains;
public PlayerProgress() {
this.questProgressCountMap = new ConcurrentHashMap<>();
this.completedDungeons = new IntArrayList();
this.itemHistory = new Int2ObjectOpenHashMap<>();
this.itemGivings = new Int2ObjectOpenHashMap<>();
this.bargains = new Int2ObjectOpenHashMap<>();
}
/**

View File

@@ -0,0 +1,109 @@
package emu.grasscutter.game.quest;
import dev.morphia.annotations.Entity;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.BargainData;
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
import emu.grasscutter.net.proto.BargainSnapshotOuterClass.BargainSnapshot;
import emu.grasscutter.utils.Utils;
import lombok.*;
@Data
@Entity
@Builder
public final class BargainRecord {
/**
* Provides an instance of a bargain record.
* Uses information from game resources.
*
* @param bargainId The ID of the bargain.
* @return An instance of a bargain record.
*/
public static BargainRecord resolve(int bargainId) {
var bargainData = GameData.getBargainDataMap().get(bargainId);
if (bargainData == null) throw new RuntimeException("No bargain data found for " + bargainId + ".");
return BargainRecord.builder()
.bargainId(bargainId)
.build()
.determineBase(bargainData);
}
private int bargainId;
private int lowestPrice;
private int expectedPrice;
private int currentMood;
private boolean finished;
private BargainResultType result;
/**
* Determines the price of the bargain.
*/
public BargainRecord determineBase(BargainData data) {
// Set the expected price.
var price = data.getExpectedValue();
this.setExpectedPrice(Utils.randomRange(
price.get(0), price.get(1)));
// Set the lowest price.
this.setLowestPrice(price.get(0));
// Set the base mood.
var mood = data.getRandomMood();
this.setCurrentMood(Utils.randomRange(
mood.get(0), mood.get(1)));
return this;
}
/**
* Computes an offer's validity.
*
* @param offer The offer to compute.
* @return The result of the offer.
*/
public BargainResultType applyOffer(int offer) {
if (offer < this.getLowestPrice()) {
// Decrease the mood.
this.currentMood -= Utils.randomRange(1, 3);
// Return a failure.
return this.result = BargainResultType.BARGAIN_SINGLE_FAIL;
}
if (offer > this.getExpectedPrice()) {
// Complete the bargain.
this.setFinished(true);
// Return a success.
return this.result = BargainResultType.BARGAIN_COMPLETE_SUCC;
}
// Compare the offer against the mood & expected price.
// The mood is out of 100; 1 mood should decrease the price by 100.
var moodAdjustment = (int) Math.floor(this.getCurrentMood() / 100.0);
var expectedPrice = this.getExpectedPrice() - moodAdjustment;
if (offer < expectedPrice) {
// Decrease the mood.
this.currentMood -= Utils.randomRange(1, 3);
// Return a failure.
return this.result = BargainResultType.BARGAIN_SINGLE_FAIL;
} else {
// Complete the bargain.
this.setFinished(true);
// Return a success.
return this.result = BargainResultType.BARGAIN_COMPLETE_SUCC;
}
}
/**
* @return A snapshot of this bargain record.
*/
public BargainSnapshot toSnapshot() {
return BargainSnapshot.newBuilder()
.setBargainId(this.getBargainId())
.setCurMood(this.getCurrentMood())
.setPJHMEHGELGC(this.getExpectedPrice())
.setHADMOPEJFIC(this.getLowestPrice())
.build();
}
}

View File

@@ -170,7 +170,54 @@ public final class QuestManager extends BasePlayerManager {
}
/**
* Attempts to remove the giving action.
* Attempts to start the bargain.
*
* @param bargainId The bargain ID.
*/
public void startBargain(int bargainId) {
var progress = this.player.getPlayerProgress();
var bargains = progress.getBargains();
// Check if the bargain is already present.
if (bargains.containsKey(bargainId)) {
throw new IllegalStateException("Bargain " + bargainId + " is already active.");
}
// Add the action.
var bargain = BargainRecord.resolve(bargainId);
bargains.put(bargainId, bargain);
// Save the bargains.
this.player.save();
// Send the player the start packet.
this.player.sendPacket(new PacketBargainStartNotify(bargain));
}
/**
* Attempts to stop the bargain.
*
* @param bargainId The bargain ID.
*/
public void stopBargain(int bargainId) {
var progress = this.player.getPlayerProgress();
var bargains = progress.getBargains();
// Check if the bargain is already present.
if (!bargains.containsKey(bargainId)) {
throw new IllegalStateException("Bargain " + bargainId + " is not active.");
}
// Remove the action.
bargains.remove(bargainId);
// Save the bargains.
this.player.save();
// Send the player the stop packet.
this.player.sendPacket(new PacketBargainTerminateNotify(bargainId));
}
/**
* Sends the giving records to the player.
*/
public void sendGivingRecords() {
// Send the record to the player.

View File

@@ -0,0 +1,20 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.*;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
@QuestValueContent(QuestContent.QUEST_CONTENT_BARGAIN_FAIL)
public final class ContentBargainFail extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
var bargain = quest.getOwner()
.getPlayerProgress()
.getBargains()
.get(condition.getParam()[0]);
if (bargain == null) return false;
return bargain.getResult() == BargainResultType.BARGAIN_COMPLETE_FAIL;
}
}

View File

@@ -0,0 +1,20 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.*;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
@QuestValueContent(QuestContent.QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN)
public final class ContentBargainLessThan extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
var bargain = quest.getOwner()
.getPlayerProgress()
.getBargains()
.get(condition.getParam()[0]);
if (bargain == null) return false;
return bargain.getResult() == BargainResultType.BARGAIN_SINGLE_FAIL;
}
}

View File

@@ -0,0 +1,20 @@
package emu.grasscutter.game.quest.content;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.*;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.net.proto.BargainResultTypeOuterClass.BargainResultType;
@QuestValueContent(QuestContent.QUEST_CONTENT_BARGAIN_SUCC)
public final class ContentBargainSuccess extends BaseContent {
@Override
public boolean execute(GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
var bargain = quest.getOwner()
.getPlayerProgress()
.getBargains()
.get(condition.getParam()[0]);
if (bargain == null) return false;
return bargain.getResult() == BargainResultType.BARGAIN_COMPLETE_SUCC;
}
}

View File

@@ -51,9 +51,9 @@ public enum QuestContent implements QuestTrigger {
QUEST_CONTENT_QUEST_VAR_LESS(121),
QUEST_CONTENT_OBTAIN_VARIOUS_ITEM(122), // missing, finish
QUEST_CONTENT_FINISH_TOWER_LEVEL(123), // missing, currently unused
QUEST_CONTENT_BARGAIN_SUCC(124), // missing, finish
QUEST_CONTENT_BARGAIN_FAIL(125), // missing, fail
QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN(126), // missing, fail
QUEST_CONTENT_BARGAIN_SUCC(124),
QUEST_CONTENT_BARGAIN_FAIL(125),
QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN(126),
QUEST_CONTENT_ACTIVITY_TRIGGER_FAILED(127), // missing, fail
QUEST_CONTENT_MAIN_COOP_ENTER_SAVE_POINT(128), // missing, finish
QUEST_CONTENT_ANY_MANUAL_TRANSPORT(129),

View File

@@ -47,8 +47,8 @@ public enum QuestExec implements QuestTrigger {
QUEST_EXEC_ACTIVE_ACTIVITY_COND_STATE(37), // missing
QUEST_EXEC_INACTIVE_ACTIVITY_COND_STATE(38), // missing
QUEST_EXEC_ADD_CUR_AVATAR_ENERGY(39),
QUEST_EXEC_START_BARGAIN(41), // missing
QUEST_EXEC_STOP_BARGAIN(42), // missing
QUEST_EXEC_START_BARGAIN(41),
QUEST_EXEC_STOP_BARGAIN(42),
QUEST_EXEC_SET_QUEST_GLOBAL_VAR(43),
QUEST_EXEC_INC_QUEST_GLOBAL_VAR(44),
QUEST_EXEC_DEC_QUEST_GLOBAL_VAR(45),

View File

@@ -0,0 +1,27 @@
package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.*;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@QuestValueExec(QuestExec.QUEST_EXEC_START_BARGAIN)
public final class ExecStartBargain extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
// Get the bargain data from the quest parameters.
var bargainId = Integer.parseInt(condition.getParam()[0]);
try {
// Start the bargain.
quest.getOwner().getQuestManager()
.startBargain(bargainId);
Grasscutter.getLogger().debug("Bargain {} started.", bargainId);
return true;
} catch (RuntimeException ignored) {
Grasscutter.getLogger().debug("Bargain {} does not exist.", bargainId);
return false;
}
}
}

View File

@@ -0,0 +1,27 @@
package emu.grasscutter.game.quest.exec;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.excels.quest.QuestData;
import emu.grasscutter.game.quest.*;
import emu.grasscutter.game.quest.enums.QuestExec;
import emu.grasscutter.game.quest.handlers.QuestExecHandler;
@QuestValueExec(QuestExec.QUEST_EXEC_STOP_BARGAIN)
public final class ExecStopBargain extends QuestExecHandler {
@Override
public boolean execute(GameQuest quest, QuestData.QuestExecParam condition, String... paramStr) {
// Get the bargain data from the quest parameters.
var bargainId = Integer.parseInt(condition.getParam()[0]);
try {
// Start the bargain.
quest.getOwner().getQuestManager()
.stopBargain(bargainId);
Grasscutter.getLogger().debug("Bargain {} stopped.", bargainId);
return true;
} catch (RuntimeException ignored) {
Grasscutter.getLogger().debug("Bargain {} does not exist.", bargainId);
return false;
}
}
}