mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-12-23 12:24:43 +01:00
implement the music game
This commit is contained in:
@@ -1,17 +1,85 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.activity.ActivityHandler;
|
||||
import emu.grasscutter.game.activity.ActivityType;
|
||||
import emu.grasscutter.game.activity.GameActivity;
|
||||
import emu.grasscutter.game.activity.PlayerActivityData;
|
||||
import emu.grasscutter.game.props.ActivityType;
|
||||
import emu.grasscutter.net.proto.ActivityInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicBriefInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicGameActivityDetailInfoOuterClass;
|
||||
|
||||
@ActivityType("NEW_ACTIVITY_MUSIC_GAME")
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@GameActivity(ActivityType.NEW_ACTIVITY_MUSIC_GAME)
|
||||
public class MusicGameActivityHandler extends ActivityHandler {
|
||||
|
||||
@Override
|
||||
public void buildProto(PlayerActivityData playerActivityData, ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {
|
||||
super.buildProto(playerActivityData, activityInfo);
|
||||
public void onInitPlayerActivityData(PlayerActivityData playerActivityData) {
|
||||
var musicGamePlayerData = MusicGamePlayerData.create();
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProtoBuild(PlayerActivityData playerActivityData, ActivityInfoOuterClass.ActivityInfo.Builder activityInfo) {
|
||||
MusicGamePlayerData musicGamePlayerData = getMusicGameRecord(playerActivityData);
|
||||
|
||||
activityInfo.setMusicGameInfo(MusicGameActivityDetailInfoOuterClass.MusicGameActivityDetailInfo.newBuilder()
|
||||
.putAllMusicGameRecordMap(
|
||||
musicGamePlayerData.getMusicGameRecord().values().stream()
|
||||
.collect(Collectors.toMap(MusicGamePlayerData.MusicGameRecord::getMusicId, MusicGamePlayerData.MusicGameRecord::toProto)))
|
||||
|
||||
.addAllPersonCustomBeatmap(musicGamePlayerData.getPersonalCustomBeatmapRecord().values().stream()
|
||||
.map(MusicGamePlayerData.CustomBeatmapRecord::toProto)
|
||||
.map(MusicBriefInfoOuterClass.MusicBriefInfo.Builder::build)
|
||||
.toList())
|
||||
|
||||
.addAllPersonCustomBeatmap(musicGamePlayerData.getOthersCustomBeatmapRecord().values().stream()
|
||||
.map(MusicGamePlayerData.CustomBeatmapRecord::toProto)
|
||||
.map(MusicBriefInfoOuterClass.MusicBriefInfo.Builder::build)
|
||||
.toList())
|
||||
.build());
|
||||
}
|
||||
|
||||
public MusicGamePlayerData getMusicGameRecord(PlayerActivityData playerActivityData){
|
||||
if(playerActivityData.getDetail() == null || playerActivityData.getDetail().isBlank()){
|
||||
onInitPlayerActivityData(playerActivityData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
return Grasscutter.getGsonFactory().fromJson(playerActivityData.getDetail(),
|
||||
MusicGamePlayerData.class);
|
||||
}
|
||||
|
||||
public boolean setMusicGameRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.MusicGameRecord newRecord){
|
||||
var musicGamePlayerData = getMusicGameRecord(playerActivityData);
|
||||
var saveRecord = musicGamePlayerData.getMusicGameRecord().get(newRecord.getMusicId());
|
||||
|
||||
saveRecord.setMaxCombo(Math.max(newRecord.getMaxCombo(), saveRecord.getMaxCombo()));
|
||||
saveRecord.setMaxScore(Math.max(newRecord.getMaxScore(), saveRecord.getMaxScore()));
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
|
||||
return newRecord.getMaxScore() > saveRecord.getMaxScore();
|
||||
}
|
||||
public void setMusicGameCustomBeatmapRecord(PlayerActivityData playerActivityData, MusicGamePlayerData.CustomBeatmapRecord newRecord){
|
||||
var musicGamePlayerData = getMusicGameRecord(playerActivityData);
|
||||
musicGamePlayerData.getOthersCustomBeatmapRecord().put(newRecord.getMusicShareId(), newRecord);
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
|
||||
public void addPersonalBeatmap(PlayerActivityData playerActivityData, MusicGameBeatmap musicGameBeatmap) {
|
||||
var musicGamePlayerData = getMusicGameRecord(playerActivityData);
|
||||
musicGamePlayerData.getPersonalCustomBeatmapRecord().put(musicGameBeatmap.getMusicShareId(),
|
||||
MusicGamePlayerData.CustomBeatmapRecord.of()
|
||||
.musicShareId(musicGameBeatmap.getMusicShareId())
|
||||
.build());
|
||||
|
||||
playerActivityData.setDetail(musicGamePlayerData);
|
||||
playerActivityData.save();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import dev.morphia.annotations.Entity;
|
||||
import dev.morphia.annotations.Id;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.net.proto.MusicBeatmapListOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicBeatmapNoteOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicBeatmapOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicBriefInfoOuterClass;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
@Entity("music_game_beatmaps")
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class MusicGameBeatmap {
|
||||
|
||||
@Id
|
||||
long musicShareId;
|
||||
int authorUid;
|
||||
int musicId;
|
||||
int musicNoteCount;
|
||||
int savePosition;
|
||||
int maxScore;
|
||||
int createTime;
|
||||
|
||||
List<List<BeatmapNote>> beatmap;
|
||||
|
||||
public static MusicGameBeatmap getByShareId(long musicShareId){
|
||||
return DatabaseHelper.getMusicGameBeatmap(musicShareId);
|
||||
}
|
||||
|
||||
public void save(){
|
||||
if(musicShareId == 0){
|
||||
musicShareId = new Random().nextLong(100000000000000L,999999999999999L);
|
||||
}
|
||||
DatabaseHelper.saveMusicGameBeatmap(this);
|
||||
}
|
||||
|
||||
public static List<List<BeatmapNote>> parse(List<MusicBeatmapListOuterClass.MusicBeatmapList> beatmapItemListList) {
|
||||
return beatmapItemListList.stream()
|
||||
.map(item -> item.getBeatmapNoteListList().stream()
|
||||
.map(BeatmapNote::parse)
|
||||
.toList())
|
||||
.toList();
|
||||
}
|
||||
|
||||
public MusicBeatmapOuterClass.MusicBeatmap toProto(){
|
||||
return MusicBeatmapOuterClass.MusicBeatmap.newBuilder()
|
||||
.setMusicId(musicId)
|
||||
.addAllBeatmapItemList(beatmap.stream()
|
||||
.map(this::musicBeatmapListToProto)
|
||||
.toList())
|
||||
.build();
|
||||
}
|
||||
|
||||
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toBriefProto(){
|
||||
var player = DatabaseHelper.getPlayerByUid(authorUid);
|
||||
|
||||
return MusicBriefInfoOuterClass.MusicBriefInfo.newBuilder()
|
||||
.setCanShare(true)
|
||||
.setMusicId(musicId)
|
||||
.setMusicNoteCount(musicNoteCount)
|
||||
.setMusicShareId(musicShareId)
|
||||
.setMaxScore(maxScore)
|
||||
.setCreateTime(createTime)
|
||||
.setShareTime(createTime)
|
||||
.setAuthorNickname(player.getNickname())
|
||||
.setVersion(1)
|
||||
;
|
||||
}
|
||||
|
||||
private MusicBeatmapListOuterClass.MusicBeatmapList musicBeatmapListToProto(List<BeatmapNote> beatmapNoteList){
|
||||
return MusicBeatmapListOuterClass.MusicBeatmapList.newBuilder()
|
||||
.addAllBeatmapNoteList(beatmapNoteList.stream()
|
||||
.map(BeatmapNote::toProto)
|
||||
.toList())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
@Entity
|
||||
public static class BeatmapNote{
|
||||
int startTime;
|
||||
int endTime;
|
||||
|
||||
public static BeatmapNote parse(MusicBeatmapNoteOuterClass.MusicBeatmapNote note){
|
||||
return BeatmapNote.of()
|
||||
.startTime(note.getStartTime())
|
||||
.endTime(note.getEndTime())
|
||||
.build();
|
||||
}
|
||||
|
||||
public MusicBeatmapNoteOuterClass.MusicBeatmapNote toProto(){
|
||||
return MusicBeatmapNoteOuterClass.MusicBeatmapNote.newBuilder()
|
||||
.setStartTime(startTime)
|
||||
.setEndTime(endTime)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.excels.MusicGameBasicData;
|
||||
import emu.grasscutter.net.proto.MusicBriefInfoOuterClass;
|
||||
import emu.grasscutter.net.proto.MusicGameRecordOuterClass;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.FieldDefaults;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public class MusicGamePlayerData {
|
||||
Map<Integer, MusicGameRecord> musicGameRecord;
|
||||
Map<Long, CustomBeatmapRecord> personalCustomBeatmapRecord;
|
||||
Map<Long, CustomBeatmapRecord> othersCustomBeatmapRecord;
|
||||
|
||||
public static MusicGamePlayerData create(){
|
||||
return MusicGamePlayerData.of()
|
||||
.musicGameRecord(GameData.getMusicGameBasicDataMap().values().stream()
|
||||
.collect(Collectors.toMap(MusicGameBasicData::getId, MusicGamePlayerData.MusicGameRecord::create)))
|
||||
.personalCustomBeatmapRecord(new HashMap<>())
|
||||
.othersCustomBeatmapRecord(new HashMap<>())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public static class MusicGameRecord {
|
||||
int musicId;
|
||||
int maxCombo;
|
||||
int maxScore;
|
||||
|
||||
public static MusicGameRecord create(MusicGameBasicData musicGameBasicData){
|
||||
return MusicGameRecord.of()
|
||||
.musicId(musicGameBasicData.getId())
|
||||
.build();
|
||||
}
|
||||
|
||||
public MusicGameRecordOuterClass.MusicGameRecord toProto(){
|
||||
return MusicGameRecordOuterClass.MusicGameRecord.newBuilder()
|
||||
.setIsUnlock(true)
|
||||
.setMaxCombo(maxCombo)
|
||||
.setMaxScore(maxScore)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@Data
|
||||
@FieldDefaults(level = AccessLevel.PRIVATE)
|
||||
@Builder(builderMethodName = "of")
|
||||
public static class CustomBeatmapRecord {
|
||||
long musicShareId;
|
||||
int score;
|
||||
boolean settle;
|
||||
|
||||
public MusicBriefInfoOuterClass.MusicBriefInfo.Builder toProto(){
|
||||
var musicGameBeatmap = MusicGameBeatmap.getByShareId(musicShareId);
|
||||
|
||||
return musicGameBeatmap.toBriefProto()
|
||||
.setScore(score)
|
||||
.setSettle(settle)
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package emu.grasscutter.game.activity.musicgame;
|
||||
|
||||
import emu.grasscutter.game.activity.ActivityWatcher;
|
||||
import emu.grasscutter.game.activity.WatcherType;
|
||||
import emu.grasscutter.game.activity.ActivityWatcherType;
|
||||
import emu.grasscutter.game.props.WatcherTriggerType;
|
||||
|
||||
@WatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
|
||||
@ActivityWatcherType(WatcherTriggerType.TRIGGER_FLEUR_FAIR_MUSIC_GAME_REACH_SCORE)
|
||||
public class MusicGameScoreTrigger extends ActivityWatcher {
|
||||
@Override
|
||||
protected boolean isMeet(String... param) {
|
||||
|
||||
Reference in New Issue
Block a user