Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
ziqian zhang
Grasscutter
Commits
8050f0cc
Commit
8050f0cc
authored
Jul 23, 2022
by
akatatsu27
Browse files
and misc bug fixes
parent
02a56fce
Changes
28
Hide whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/data/ResourceLoader.java
View file @
8050f0cc
...
...
@@ -56,22 +56,23 @@ public class ResourceLoader {
public
static
void
loadAll
()
{
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.status.resources.loading"
));
// Load ability lists
loadAbilityEmbryos
();
loadOpenConfig
();
loadAbilityModifiers
();
// Load resources
loadResources
();
// Process into depots
GameDepot
.
load
();
// Load spawn data and quests
loadSpawnData
();
loadQuests
();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints
();
// Load default home layout
loadHomeworldDefaultSaveData
();
loadNpcBornData
();
// Load ability lists
loadAbilityEmbryos
();
loadOpenConfig
();
loadAbilityModifiers
();
// Load resources
loadResources
();
// Process into depots
GameDepot
.
load
();
// Load spawn data and quests
loadSpawnData
();
loadQuests
();
loadScriptSceneData
();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints
();
// Load default home layout
loadHomeworldDefaultSaveData
();
loadNpcBornData
();
Grasscutter
.
getLogger
().
info
(
translate
(
"messages.status.resources.finish"
));
}
...
...
@@ -420,10 +421,32 @@ public class ResourceLoader {
Grasscutter
.
getLogger
().
debug
(
"Loaded "
+
GameData
.
getMainQuestDataMap
().
size
()
+
" MainQuestDatas."
);
}
@SneakyThrows
private
static
void
loadHomeworldDefaultSaveData
()
{
var
folder
=
Files
.
list
(
Path
.
of
(
RESOURCE
(
"BinOutput/HomeworldDefaultSave"
))).
toList
();
var
pattern
=
Pattern
.
compile
(
"scene(.*)_home_config.json"
);
public
static
void
loadScriptSceneData
()
{
File
folder
=
new
File
(
RESOURCE
(
"ScriptSceneData/"
));
if
(!
folder
.
exists
())
{
return
;
}
for
(
File
file
:
folder
.
listFiles
())
{
ScriptSceneData
sceneData
;
try
(
FileReader
fileReader
=
new
FileReader
(
file
))
{
sceneData
=
Grasscutter
.
getGsonFactory
().
fromJson
(
fileReader
,
ScriptSceneData
.
class
);
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
continue
;
}
GameData
.
getScriptSceneDataMap
().
put
(
file
.
getName
(),
sceneData
);
}
Grasscutter
.
getLogger
().
debug
(
"Loaded "
+
GameData
.
getScriptSceneDataMap
().
size
()
+
" ScriptSceneDatas."
);
}
@SneakyThrows
private
static
void
loadHomeworldDefaultSaveData
(){
var
folder
=
Files
.
list
(
Path
.
of
(
RESOURCE
(
"BinOutput/HomeworldDefaultSave"
))).
toList
();
var
pattern
=
Pattern
.
compile
(
"scene(.*)_home_config.json"
);
for
(
var
file
:
folder
)
{
var
matcher
=
pattern
.
matcher
(
file
.
getFileName
().
toString
());
...
...
src/main/java/emu/grasscutter/data/binout/MainQuestData.java
View file @
8050f0cc
package
emu.grasscutter.data.binout
;
import
dev.morphia.annotations.Entity
;
import
emu.grasscutter.game.quest.enums.QuestType
;
import
lombok.Data
;
import
java.util.List
;
import
java.util.Objects
;
public
class
MainQuestData
{
private
int
id
;
private
int
ICLLDPJFIMA
;
private
int
series
;
private
QuestType
type
;
private
long
titleTextMapHash
;
private
int
[]
suggestTrackMainQuestList
;
private
int
[]
rewardIdList
;
private
SubQuestData
[]
subQuests
;
public
int
getId
()
{
return
id
;
}
private
List
<
TalkData
>
talks
;
private
long
[]
preloadLuaList
;
public
int
getId
()
{
return
id
;
}
public
int
getSeries
()
{
return
series
;
}
public
QuestType
getType
()
{
return
type
;
}
public
long
getTitleTextMapHash
()
{
return
titleTextMapHash
;
}
public
int
[]
getSuggestTrackMainQuestList
()
{
return
suggestTrackMainQuestList
;
}
...
...
@@ -37,14 +43,28 @@ public class MainQuestData {
public
int
[]
getRewardIdList
()
{
return
rewardIdList
;
}
public
SubQuestData
[]
getSubQuests
()
{
return
subQuests
;
}
public
List
<
TalkData
>
getTalks
()
{
return
talks
;
}
public
void
onLoad
()
{
this
.
talks
=
talks
.
stream
().
filter
(
Objects:
:
nonNull
).
toList
();
}
@Data
public
static
class
SubQuestData
{
private
int
subId
;
private
int
order
;
}
@Data
@Entity
public
static
class
TalkData
{
private
int
id
;
private
String
heroTalk
;
}
}
src/main/java/emu/grasscutter/data/excels/QuestData.java
View file @
8050f0cc
...
...
@@ -36,10 +36,14 @@ public class QuestData extends GameResource {
private
List
<
QuestExecParam
>
beginExec
;
private
List
<
QuestExecParam
>
finishExec
;
private
List
<
QuestExecParam
>
failExec
;
private
Guide
guide
;
//ResourceLoader not happy if you remove getId() ~~
public
int
getId
()
{
return
subId
;
}
//Added getSubId() for clarity
public
int
getSubId
()
{
return
subId
;}
public
int
getMainId
()
{
return
mainId
;
...
...
@@ -62,7 +66,7 @@ public class QuestData extends GameResource {
}
public
LogicType
getAcceptCondComb
()
{
return
acceptCondComb
;
return
acceptCondComb
==
null
?
LogicType
.
LOGIC_NONE
:
acceptCondComb
;
}
public
List
<
QuestCondition
>
getAcceptCond
()
{
...
...
@@ -70,7 +74,7 @@ public class QuestData extends GameResource {
}
public
LogicType
getFinishCondComb
()
{
return
finishCondComb
;
return
finishCondComb
==
null
?
LogicType
.
LOGIC_NONE
:
finishCondComb
;
}
public
List
<
QuestCondition
>
getFinishCond
()
{
...
...
@@ -78,7 +82,7 @@ public class QuestData extends GameResource {
}
public
LogicType
getFailCondComb
()
{
return
failCondComb
;
return
failCondComb
==
null
?
LogicType
.
LOGIC_NONE
:
failCondComb
;
}
public
List
<
QuestCondition
>
getFailCond
()
{
...
...
@@ -118,4 +122,11 @@ public class QuestData extends GameResource {
private
String
count
;
}
@Data
public
static
class
Guide
{
private
String
type
;
private
List
<
String
>
param
;
private
int
guideScene
;
}
}
src/main/java/emu/grasscutter/game/player/Player.java
View file @
8050f0cc
...
...
@@ -14,12 +14,8 @@ import emu.grasscutter.game.activity.ActivityManager;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.avatar.AvatarStorage
;
import
emu.grasscutter.game.battlepass.BattlePassManager
;
import
emu.grasscutter.game.entity.EntityMonster
;
import
emu.grasscutter.game.entity.EntityVehicle
;
import
emu.grasscutter.game.entity.*
;
import
emu.grasscutter.game.home.GameHome
;
import
emu.grasscutter.game.entity.EntityGadget
;
import
emu.grasscutter.game.entity.EntityItem
;
import
emu.grasscutter.game.entity.GameEntity
;
import
emu.grasscutter.game.expedition.ExpeditionInfo
;
import
emu.grasscutter.game.friends.FriendsList
;
import
emu.grasscutter.game.friends.PlayerProfile
;
...
...
@@ -43,6 +39,7 @@ import emu.grasscutter.game.props.ClimateType;
import
emu.grasscutter.game.props.PlayerProperty
;
import
emu.grasscutter.game.props.WatcherTriggerType
;
import
emu.grasscutter.game.quest.QuestManager
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.game.shop.ShopLimit
;
import
emu.grasscutter.game.tower.TowerData
;
import
emu.grasscutter.game.tower.TowerManager
;
...
...
@@ -61,6 +58,7 @@ import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo
import
emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture
;
import
emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail
;
import
emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType
;
import
emu.grasscutter.scripts.data.SceneRegion
;
import
emu.grasscutter.server.event.player.PlayerJoinEvent
;
import
emu.grasscutter.server.event.player.PlayerQuitEvent
;
import
emu.grasscutter.server.game.GameServer
;
...
...
@@ -74,6 +72,7 @@ import emu.grasscutter.utils.Utils;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
import
lombok.Getter
;
import
lombok.Setter
;
import
static
emu
.
grasscutter
.
config
.
Configuration
.*;
...
...
@@ -122,6 +121,7 @@ public class Player {
@Getter
private
Map
<
Long
,
ExpeditionInfo
>
expeditionInfo
;
@Getter
private
Map
<
Integer
,
Integer
>
unlockedRecipies
;
@Getter
private
List
<
ActiveForgeData
>
activeForges
;
@Getter
private
Map
<
Integer
,
Integer
>
questGlobalVariables
;
@Transient
private
long
nextGuid
=
0
;
@Transient
private
int
peerId
;
...
...
@@ -151,7 +151,7 @@ public class Player {
@Getter
private
transient
CookingManager
cookingManager
;
@Getter
private
transient
ActivityManager
activityManager
;
@Getter
private
transient
PlayerBuffManager
buffManager
;
// Manager data (Save-able to the database)
private
PlayerProfile
playerProfile
;
private
TeamManager
teamManager
;
...
...
@@ -579,9 +579,38 @@ public class Player {
return
towerData
;
}
public
PlayerGachaInfo
getGachaInfo
()
{
return
gachaInfo
;
public
void
setQuestManager
(
QuestManager
questManager
)
{
this
.
questManager
=
questManager
;
}
public
void
onEnterRegion
(
SceneRegion
region
)
{
getQuestManager
().
forEachActiveQuest
(
quest
->
{
if
(
quest
.
getTriggers
().
containsKey
(
"ENTER_REGION_"
+
String
.
valueOf
(
region
.
config_id
)))
{
// If trigger hasn't been fired yet
if
(!
Boolean
.
TRUE
.
equals
(
quest
.
getTriggers
().
put
(
"ENTER_REGION_"
+
String
.
valueOf
(
region
.
config_id
),
true
)))
{
//getSession().send(new PacketServerCondMeetQuestListUpdateNotify());
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_TRIGGER_FIRE
,
quest
.
getTriggerData
().
get
(
"ENTER_REGION_"
+
String
.
valueOf
(
region
.
config_id
)).
getId
(),
0
);
}
}
});
}
public
void
onLeaveRegion
(
SceneRegion
region
)
{
getQuestManager
().
forEachActiveQuest
(
quest
->
{
if
(
quest
.
getTriggers
().
containsKey
(
"LEAVE_REGION_"
+
String
.
valueOf
(
region
.
config_id
)))
{
// If trigger hasn't been fired yet
if
(!
Boolean
.
TRUE
.
equals
(
quest
.
getTriggers
().
put
(
"LEAVE_REGION_"
+
String
.
valueOf
(
region
.
config_id
),
true
)))
{
getSession
().
send
(
new
PacketServerCondMeetQuestListUpdateNotify
());
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_TRIGGER_FIRE
,
quest
.
getTriggerData
().
get
(
"LEAVE_REGION_"
+
String
.
valueOf
(
region
.
config_id
)).
getId
(),
0
);
}
}
});
}
public
PlayerGachaInfo
getGachaInfo
()
{
return
gachaInfo
;
}
public
PlayerProfile
getProfile
()
{
if
(
this
.
playerProfile
==
null
)
{
...
...
@@ -892,7 +921,7 @@ public class Player {
public
boolean
hasSentLoginPackets
()
{
return
hasSentLoginPackets
;
}
public
void
addAvatar
(
Avatar
avatar
,
boolean
addToCurrentTeam
)
{
boolean
result
=
getAvatars
().
addAvatar
(
avatar
);
...
...
@@ -1327,21 +1356,25 @@ public class Player {
// Execute daily reset logic if this is a new day.
this
.
doDailyReset
();
// Packets
session
.
send
(
new
PacketPlayerDataNotify
(
this
));
// Player data
session
.
send
(
new
PacketStoreWeightLimitNotify
());
session
.
send
(
new
PacketPlayerStoreNotify
(
this
));
session
.
send
(
new
PacketAvatarDataNotify
(
this
));
session
.
send
(
new
PacketFinishedParentQuestNotify
(
this
));
session
.
send
(
new
PacketBattlePassAllDataNotify
(
this
));
session
.
send
(
new
PacketQuestListNotify
(
this
));
session
.
send
(
new
PacketCodexDataFullNotify
(
this
));
session
.
send
(
new
PacketAllWidgetDataNotify
(
this
));
session
.
send
(
new
PacketWidgetGadgetAllDataNotify
());
session
.
send
(
new
PacketCombineDataNotify
(
this
.
unlockedCombines
));
this
.
forgingManager
.
sendForgeDataNotify
();
this
.
resinManager
.
onPlayerLogin
();
this
.
cookingManager
.
sendCookDataNofity
();
// Rewind active quests, and put the player to the first rewind position it finds (if any) of an active quest
getQuestManager
().
onLogin
();
// Packets
session
.
send
(
new
PacketPlayerDataNotify
(
this
));
// Player data
session
.
send
(
new
PacketStoreWeightLimitNotify
());
session
.
send
(
new
PacketPlayerStoreNotify
(
this
));
session
.
send
(
new
PacketAvatarDataNotify
(
this
));
session
.
send
(
new
PacketFinishedParentQuestNotify
(
this
));
session
.
send
(
new
PacketBattlePassAllDataNotify
(
this
));
session
.
send
(
new
PacketQuestListNotify
(
this
));
session
.
send
(
new
PacketCodexDataFullNotify
(
this
));
session
.
send
(
new
PacketAllWidgetDataNotify
(
this
));
session
.
send
(
new
PacketWidgetGadgetAllDataNotify
());
session
.
send
(
new
PacketCombineDataNotify
(
this
.
unlockedCombines
));
this
.
forgingManager
.
sendForgeDataNotify
();
this
.
resinManager
.
onPlayerLogin
();
this
.
cookingManager
.
sendCookDataNofity
();
// Unlock in case this is an existing user that reached a level before we implemented unlocking.
this
.
getOpenStateManager
().
unlockLevelDependentStates
();
...
...
src/main/java/emu/grasscutter/game/quest/GameMainQuest.java
View file @
8050f0cc
...
...
@@ -2,7 +2,15 @@ package emu.grasscutter.game.quest;
import
java.util.*
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.binout.ScriptSceneData
;
import
emu.grasscutter.data.excels.QuestData
;
import
emu.grasscutter.game.quest.enums.LogicType
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.scripts.ScriptLoader
;
import
emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify
;
import
emu.grasscutter.utils.Position
;
import
lombok.Getter
;
import
org.bson.types.ObjectId
;
import
dev.morphia.annotations.Entity
;
...
...
@@ -11,6 +19,7 @@ import dev.morphia.annotations.Indexed;
import
dev.morphia.annotations.Transient
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.binout.MainQuestData
;
import
emu.grasscutter.data.binout.MainQuestData.*
;
import
emu.grasscutter.data.excels.RewardData
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.game.player.Player
;
...
...
@@ -23,24 +32,33 @@ import emu.grasscutter.net.proto.QuestOuterClass.Quest;
import
emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify
;
import
emu.grasscutter.utils.Utils
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
import
javax.script.Bindings
;
import
javax.script.CompiledScript
;
import
javax.script.ScriptException
;
import
static
emu
.
grasscutter
.
config
.
Configuration
.
SCRIPT
;
@Entity
(
value
=
"quests"
,
useDiscriminator
=
false
)
public
class
GameMainQuest
{
@Id
private
ObjectId
id
;
@Indexed
private
int
ownerUid
;
@Transient
private
Player
owner
;
private
Map
<
Integer
,
GameQuest
>
childQuests
;
private
int
parentQuestId
;
private
int
[]
questVars
;
private
ParentQuestState
state
;
private
boolean
isFinished
;
List
<
QuestGroupSuite
>
questGroupSuites
;
@Indexed
@Getter
private
int
ownerUid
;
@Transient
@Getter
private
Player
owner
;
@Transient
@Getter
private
QuestManager
questManager
;
@Getter
private
Map
<
Integer
,
GameQuest
>
childQuests
;
@Getter
private
int
parentQuestId
;
@Getter
private
int
[]
questVars
;
//QuestUpdateQuestVarReq is sent in two stages...
@Getter
private
List
<
Integer
>
questVarsUpdate
;
@Getter
private
ParentQuestState
state
;
@Getter
private
boolean
isFinished
;
@Getter
List
<
QuestGroupSuite
>
questGroupSuites
;
@Getter
int
[]
suggestTrackMainQuestList
;
@Getter
private
Map
<
Integer
,
TalkData
>
talks
;
//key is subId
private
Map
<
Integer
,
Position
>
rewindPositions
;
private
Map
<
Integer
,
Position
>
rewindRotations
;
@Deprecated
// Morphia only. Do not use.
public
GameMainQuest
()
{}
...
...
@@ -48,52 +66,60 @@ public class GameMainQuest {
public
GameMainQuest
(
Player
player
,
int
parentQuestId
)
{
this
.
owner
=
player
;
this
.
ownerUid
=
player
.
getUid
();
this
.
questManager
=
player
.
getQuestManager
();
this
.
parentQuestId
=
parentQuestId
;
this
.
childQuests
=
new
HashMap
<>();
this
.
questVars
=
new
int
[
5
];
this
.
talks
=
new
HashMap
<>();
//official server always has a list of 5 questVars, with default value 0
this
.
questVars
=
new
int
[]
{
0
,
0
,
0
,
0
,
0
};
this
.
state
=
ParentQuestState
.
PARENT_QUEST_STATE_NONE
;
this
.
questGroupSuites
=
new
ArrayList
<>();
this
.
rewindPositions
=
new
HashMap
<>();
this
.
rewindRotations
=
new
HashMap
<>();
addAllChildQuests
();
addRewindPoints
();
}
public
int
getParentQuestId
()
{
return
parentQuestId
;
}
public
int
getOwnerUid
()
{
return
ownerUid
;
}
public
Player
getOwner
()
{
return
owner
;
}
private
void
addAllChildQuests
()
{
List
<
Integer
>
subQuestIds
=
Arrays
.
stream
(
GameData
.
getMainQuestDataMap
().
get
(
this
.
parentQuestId
).
getSubQuests
()).
map
(
SubQuestData:
:
getSubId
).
toList
();
for
(
Integer
subQuestId
:
subQuestIds
)
{
QuestData
questConfig
=
GameData
.
getQuestDataMap
().
get
(
subQuestId
);
this
.
childQuests
.
put
(
subQuestId
,
new
GameQuest
(
this
,
questConfig
));
}
}
public
void
setOwner
(
Player
player
)
{
if
(
player
.
getUid
()
!=
this
.
getOwnerUid
())
return
;
this
.
owner
=
player
;
}
public
Map
<
Integer
,
GameQuest
>
getChildQuests
()
{
return
childQuests
;
}
public
int
getQuestVar
(
int
i
)
{
return
questVars
[
i
];
}
public
void
setQuestVar
(
int
i
,
int
value
)
{
int
previousValue
=
this
.
questVars
[
i
];
this
.
questVars
[
i
]
=
value
;
Grasscutter
.
getLogger
().
debug
(
"questVar {} value changed from {} to {}"
,
i
,
previousValue
,
value
);
}
public
GameQuest
getChildQuestById
(
int
id
)
{
return
this
.
getChildQuests
().
get
(
id
);
}
public
void
incQuestVar
(
int
i
,
int
inc
)
{
int
previousValue
=
this
.
questVars
[
i
];
this
.
questVars
[
i
]
+=
inc
;
Grasscutter
.
getLogger
().
debug
(
"questVar {} value incremented from {} to {}"
,
i
,
previousValue
,
previousValue
+
inc
);
}
public
int
[]
getQuestVars
()
{
return
questVars
;
}
public
void
decQuestVar
(
int
i
,
int
dec
)
{
int
previousValue
=
this
.
questVars
[
i
];
this
.
questVars
[
i
]
-=
dec
;
Grasscutter
.
getLogger
().
debug
(
"questVar {} value decremented from {} to {}"
,
i
,
previousValue
,
previousValue
-
dec
);
}
public
ParentQuestState
getState
()
{
return
state
;
}
public
boolean
isFinished
(
)
{
return
isFinished
;
public
GameQuest
getChildQuestById
(
int
id
)
{
return
this
.
getChildQuests
().
get
(
id
)
;
}
public
List
<
QuestGroupSuite
>
getQuestGroupSuites
()
{
return
questGroupSuites
;
public
GameQuest
getChildQuestByOrder
(
int
order
)
{
return
this
.
getChildQuests
().
values
().
stream
().
filter
(
p
->
p
.
getQuestData
().
getOrder
()
==
order
).
toList
().
get
(
0
);
}
public
void
finish
()
{
...
...
@@ -120,9 +146,193 @@ public class GameMainQuest {
// handoff main quest
if
(
mainQuestData
.
getSuggestTrackMainQuestList
()
!=
null
){
Arrays
.
stream
(
mainQuestData
.
getSuggestTrackMainQuestList
())
.
forEach
(
getOwner
().
getQuestManager
()::
startMainQuest
);
.
forEach
(
getQuestManager
()::
startMainQuest
);
}
}
//TODO
public
void
fail
()
{}
public
void
cancel
()
{}
// Rewinds to the last finished/unfinished rewind quest, and returns the avatar rewind position (if it exists)
public
List
<
Position
>
rewind
()
{
if
(
this
.
questManager
==
null
)
{
this
.
questManager
=
getOwner
().
getQuestManager
();
}
List
<
GameQuest
>
sortedByOrder
=
new
ArrayList
<>(
getChildQuests
().
values
().
stream
().
filter
(
q
->
q
.
getQuestData
().
isRewind
()).
toList
());
sortedByOrder
.
sort
((
a
,
b
)
->
{
if
(
a
==
b
){
return
0
;
}
return
a
.
getQuestData
().
getOrder
()
>
b
.
getQuestData
().
getOrder
()
?
1
:
-
1
;});
boolean
didRewind
=
false
;
for
(
GameQuest
quest
:
sortedByOrder
)
{
int
i
=
sortedByOrder
.
indexOf
(
quest
);
if
(
i
==
sortedByOrder
.
size
())
{
didRewind
=
quest
.
rewind
(
null
);
}
else
{
didRewind
=
quest
.
rewind
(
sortedByOrder
.
get
(
i
+
1
));
}
if
(
didRewind
)
{
break
;
}
}
List
<
GameQuest
>
rewindQuests
=
getChildQuests
().
values
().
stream
()
.
filter
(
p
->
(
p
.
getState
()
==
QuestState
.
QUEST_STATE_UNFINISHED
||
p
.
getState
()
==
QuestState
.
QUEST_STATE_FINISHED
)
&&
p
.
getQuestData
().
isRewind
()).
toList
();
for
(
GameQuest
quest
:
rewindQuests
)
{
if
(
rewindPositions
.
containsKey
(
quest
.
getSubQuestId
()))
{
List
<
Position
>
posAndRot
=
new
ArrayList
<>();
posAndRot
.
add
(
0
,
rewindPositions
.
get
(
quest
.
getSubQuestId
()));
posAndRot
.
add
(
1
,
rewindRotations
.
get
(
quest
.
getSubQuestId
()));
return
posAndRot
;
}
}
return
null
;
}
public
void
addRewindPoints
()
{
Bindings
bindings
=
ScriptLoader
.
getEngine
().
createBindings
();
CompiledScript
cs
=
ScriptLoader
.
getScriptByPath
(
SCRIPT
(
"Quest/Share/Q"
+
getParentQuestId
()
+
"ShareConfig."
+
ScriptLoader
.
getScriptType
()));
if
(
cs
==
null
)
{
Grasscutter
.
getLogger
().
error
(
"Couldn't find Q"
+
getParentQuestId
()
+
"ShareConfig."
+
ScriptLoader
.
getScriptType
());
return
;
}
// Eval script
try
{
cs
.
eval
(
bindings
);
var
rewindDataMap
=
ScriptLoader
.
getSerializer
().
toMap
(
RewindData
.
class
,
bindings
.
get
(
"rewind_data"
));
for
(
String
subId
:
rewindDataMap
.
keySet
())
{
RewindData
questRewind
=
rewindDataMap
.
get
(
subId
);
if
(
questRewind
!=
null
)
{
RewindData
.
AvatarData
avatarData
=
questRewind
.
getAvatar
();
if
(
avatarData
!=
null
)
{
String
avatarPos
=
avatarData
.
getPos
();
QuestData
.
Guide
guide
=
GameData
.
getQuestDataMap
().
get
(
Integer
.
valueOf
(
subId
)).
getGuide
();
if
(
guide
!=
null
)
{
int
sceneId
=
guide
.
getGuideScene
();
ScriptSceneData
fullGlobals
=
GameData
.
getScriptSceneDataMap
().
get
(
"flat.luas.scenes.full_globals.lua.json"
);
if
(
fullGlobals
!=
null
)
{
ScriptSceneData
.
ScriptObject
dummyPointScript
=
fullGlobals
.
getScriptObjectList
().
get
(
sceneId
+
"/scene"
+
sceneId
+
"_dummy_points.lua"
);
if
(
dummyPointScript
!=
null
)
{
Map
<
String
,
List
<
Float
>>
dummyPointMap
=
dummyPointScript
.
getDummyPoints
();
if
(
dummyPointMap
!=
null
)
{
List
<
Float
>
avatarPosPos
=
dummyPointMap
.
get
(
avatarPos
+
".pos"
);
if
(
avatarPosPos
!=
null
)
{
Position
pos
=
new
Position
(
avatarPosPos
.
get
(
0
),
avatarPosPos
.
get
(
1
),
avatarPosPos
.
get
(
2
));
List
<
Float
>
avatarPosRot
=
dummyPointMap
.
get
(
avatarPos
+
".rot"
);
Position
rot
=
new
Position
(
avatarPosRot
.
get
(
0
),
avatarPosRot
.
get
(
1
),
avatarPosRot
.
get
(
2
));
rewindPositions
.
put
(
Integer
.
valueOf
(
subId
),
pos
);
rewindRotations
.
put
(
Integer
.
valueOf
(
subId
),
rot
);
Grasscutter
.
getLogger
().
debug
(
"Succesfully loaded rewind position for subQuest {}"
,
subId
);
}
}
}
}
}
}
}
}
}
catch
(
ScriptException
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while loading rewind positions"
);
}
}
public
void
tryAcceptSubQuests
(
QuestTrigger
condType
,
String
paramStr
,
int
...
params
)
{
try
{
List
<
GameQuest
>
subQuestsWithCond
=
getChildQuests
().
values
().
stream
()
.
filter
(
p
->
p
.
getState
()
==
QuestState
.
QUEST_STATE_UNSTARTED
)
.
filter
(
p
->
p
.
getQuestData
().
getAcceptCond
().
stream
().
anyMatch
(
q
->
q
.
getType
()
==
condType
))
.
toList
();
for
(
GameQuest
subQuestWithCond
:
subQuestsWithCond
)
{
List
<
QuestData
.
QuestCondition
>
acceptCond
=
subQuestWithCond
.
getQuestData
().
getAcceptCond
();
int
[]
accept
=
new
int
[
acceptCond
.
size
()];
for
(
int
i
=
0
;
i
<
subQuestWithCond
.
getQuestData
().
getAcceptCond
().
size
();
i
++)
{
QuestData
.
QuestCondition
condition
=
acceptCond
.
get
(
i
);
boolean
result
=
this
.
getOwner
().
getServer
().
getQuestSystem
().
triggerCondition
(
subQuestWithCond
,
condition
,
paramStr
,
params
);
accept
[
i
]
=
result
?
1
:
0
;
}
boolean
shouldAccept
=
LogicType
.
calculate
(
subQuestWithCond
.
getQuestData
().
getAcceptCondComb
(),
accept
);
if
(
shouldAccept
)
{
subQuestWithCond
.
start
();
getQuestManager
().
getAddToQuestListUpdateNotify
().
add
(
subQuestWithCond
);
}
}
this
.
save
();
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while trying to accept quest."
,
e
);
}
}
public
void
tryFailSubQuests
(
QuestTrigger
condType
,
String
paramStr
,
int
...
params
)
{
try
{
List
<
GameQuest
>
subQuestsWithCond
=
getChildQuests
().
values
().
stream
()
.
filter
(
p
->
p
.
getState
()
==
QuestState
.
QUEST_STATE_UNFINISHED
)
.
filter
(
p
->
p
.
getQuestData
().
getFailCond
().
stream
().
anyMatch
(
q
->
q
.
getType
()
==
condType
))
.
toList
();
for
(
GameQuest
subQuestWithCond
:
subQuestsWithCond
)
{
List
<
QuestData
.
QuestCondition
>
failCond
=
subQuestWithCond
.
getQuestData
().
getFailCond
();
int
[]
fail
=
new
int
[
failCond
.
size
()];
for
(
int
i
=
0
;
i
<
subQuestWithCond
.
getQuestData
().
getFailCond
().
size
();
i
++)
{
QuestData
.
QuestCondition
condition
=
failCond
.
get
(
i
);
boolean
result
=
this
.
getOwner
().
getServer
().
getQuestSystem
().
triggerContent
(
subQuestWithCond
,
condition
,
paramStr
,
params
);
fail
[
i
]
=
result
?
1
:
0
;
}
boolean
shouldFail
=
LogicType
.
calculate
(
subQuestWithCond
.
getQuestData
().
getFailCondComb
(),
fail
);
if
(
shouldFail
)
{
subQuestWithCond
.
fail
();
getQuestManager
().
getAddToQuestListUpdateNotify
().
add
(
subQuestWithCond
);
}
}
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while trying to fail quest."
,
e
);
}
}
public
void
tryFinishSubQuests
(
QuestTrigger
condType
,
String
paramStr
,
int
...
params
)
{
try
{
List
<
GameQuest
>
subQuestsWithCond
=
getChildQuests
().
values
().
stream
()
//There are subQuests with no acceptCond, but can be finished (example: 35104)
.
filter
(
p
->
p
.
getState
()
==
QuestState
.
QUEST_STATE_UNFINISHED
&&
p
.
getQuestData
().
getAcceptCond
()
!=
null
)
.
filter
(
p
->
p
.
getQuestData
().
getFinishCond
().
stream
().
anyMatch
(
q
->
q
.
getType
()
==
condType
))
.
toList
();
for
(
GameQuest
subQuestWithCond
:
subQuestsWithCond
)
{
List
<
QuestData
.
QuestCondition
>
finishCond
=
subQuestWithCond
.
getQuestData
().
getFinishCond
();
int
[]
finish
=
new
int
[
finishCond
.
size
()];
for
(
int
i
=
0
;
i
<
subQuestWithCond
.
getQuestData
().
getFinishCond
().
size
();
i
++)
{
QuestData
.
QuestCondition
condition
=
finishCond
.
get
(
i
);
boolean
result
=
this
.
getOwner
().
getServer
().
getQuestSystem
().
triggerContent
(
subQuestWithCond
,
condition
,
paramStr
,
params
);
finish
[
i
]
=
result
?
1
:
0
;
}
boolean
shouldFinish
=
LogicType
.
calculate
(
subQuestWithCond
.
getQuestData
().
getFinishCondComb
(),
finish
);
if
(
shouldFinish
)
{
subQuestWithCond
.
finish
();
getQuestManager
().
getAddToQuestListUpdateNotify
().
add
(
subQuestWithCond
);
}
}
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
debug
(
"An error occurred while trying to finish quest."
,
e
);
}
}
public
void
save
()
{
DatabaseHelper
.
saveQuest
(
this
);
...
...
@@ -131,24 +341,30 @@ public class GameMainQuest {
public
ParentQuest
toProto
()
{
ParentQuest
.
Builder
proto
=
ParentQuest
.
newBuilder
()
.
setParentQuestId
(
getParentQuestId
())
.
setIsFinished
(
isFinished
())
.
setParentQuestState
(
getState
().
getValue
());
for
(
GameQuest
quest
:
this
.
getChildQuests
().
values
())
{
ChildQuest
childQuest
=
ChildQuest
.
newBuilder
()
.
setQuestId
(
quest
.
getQuestId
())
.
setState
(
quest
.
getState
().
getValue
())
.
build
();
proto
.
addChildQuestList
(
childQuest
);
}
if
(
getQuestVars
()
!=
null
)
{
for
(
int
i
:
getQuestVars
())
{
proto
.
addQuestVar
(
i
);
}
}
.
setIsFinished
(
isFinished
());
/**
if ParentQuestState is NONE, official server does not send ParentQuestState nor childQuestList!!!
might need more sniffing...
sending childQuestList without ParentQuestState set causes the game to hang on login
*/
if
(
getState
()
!=
ParentQuestState
.
PARENT_QUEST_STATE_NONE
)
{
proto
.
setParentQuestState
(
getState
().
getValue
());
for
(
GameQuest
quest
:
this
.
getChildQuests
().
values
())
{
if
(
quest
.
getState
()
!=
QuestState
.
QUEST_STATE_UNSTARTED
)
{
ChildQuest
childQuest
=
ChildQuest
.
newBuilder
()
.
setQuestId
(
quest
.
getSubQuestId
())
.
setState
(
quest
.
getState
().
getValue
())
.
build
();
proto
.
addChildQuestList
(
childQuest
);
}
}
}
for
(
int
i
:
getQuestVars
())
{
proto
.
addQuestVar
(
i
);
}
return
proto
.
build
();
}
}
src/main/java/emu/grasscutter/game/quest/GameQuest.java
View file @
8050f0cc
...
...
@@ -4,51 +4,84 @@ import dev.morphia.annotations.Entity;
import
dev.morphia.annotations.Transient
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.binout.MainQuestData
;
import
emu.grasscutter.data.binout.MainQuestData.SubQuestData
;
import
emu.grasscutter.data.excels.ChapterData
;
import
emu.grasscutter.data.excels.QuestData
;
import
emu.grasscutter.data.excels.
QuestData.QuestCondition
;
import
emu.grasscutter.data.excels.
TriggerExcelConfigData
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.quest.enums.LogicType
;
import
emu.grasscutter.game.quest.enums.QuestState
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.net.proto.ChapterStateOuterClass
;
import
emu.grasscutter.net.proto.QuestOuterClass.Quest
;
import
emu.grasscutter.scripts.data.SceneGroup
;
import
emu.grasscutter.server.packet.send.PacketChapterStateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify
;
import
emu.grasscutter.utils.Utils
;
import
lombok.Getter
;
import
lombok.Setter
;
import
javax.script.Bindings
;
import
java.util.Arrays
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
@Entity
public
class
GameQuest
{
@Transient
private
GameMainQuest
mainQuest
;
@Transient
private
QuestData
questData
;
@Transient
@Getter
@Setter
private
GameMainQuest
mainQuest
;
@Transient
@Getter
private
QuestData
questData
;
private
int
questId
;
private
int
mainQuestId
;
@Getter
private
int
subQuestId
;
@Getter
private
int
mainQuestId
;
@Getter
@Setter
private
QuestState
state
;
private
int
startTime
;
private
int
acceptTime
;
private
int
finishTime
;
@Getter
@Setter
private
int
startTime
;
@Getter
@Setter
private
int
acceptTime
;
@Getter
@Setter
private
int
finishTime
;
private
int
[]
finishProgressList
;
private
int
[]
failProgressList
;
@Getter
private
int
[]
finishProgressList
;
@Getter
private
int
[]
failProgressList
;
@Transient
@Getter
private
Map
<
String
,
TriggerExcelConfigData
>
triggerData
;
@Getter
private
Map
<
String
,
Boolean
>
triggers
;
private
transient
Bindings
bindings
;
@Deprecated
// Morphia only. Do not use.
public
GameQuest
()
{}
@Deprecated
// Morphia only. Do not use.
public
GameQuest
()
{}
public
GameQuest
(
GameMainQuest
mainQuest
,
QuestData
questData
)
{
this
.
mainQuest
=
mainQuest
;
this
.
questId
=
questData
.
getId
();
this
.
mainQuestId
=
questData
.
getMainId
();
this
.
questData
=
questData
;
public
GameQuest
(
GameMainQuest
mainQuest
,
QuestData
questData
)
{
this
.
mainQuest
=
mainQuest
;
this
.
subQuestId
=
questData
.
getId
();
this
.
mainQuestId
=
questData
.
getMainId
();
this
.
questData
=
questData
;
this
.
state
=
QuestState
.
QUEST_STATE_UNSTARTED
;
this
.
triggerData
=
new
HashMap
<>();
this
.
triggers
=
new
HashMap
<>();
}
public
void
start
()
{
this
.
acceptTime
=
Utils
.
getCurrentSeconds
();
this
.
startTime
=
this
.
acceptTime
;
this
.
state
=
QuestState
.
QUEST_STATE_UNFINISHED
;
List
<
QuestData
.
QuestCondition
>
triggerCond
=
questData
.
getFinishCond
().
stream
()
.
filter
(
p
->
p
.
getType
()
==
QuestTrigger
.
QUEST_CONTENT_TRIGGER_FIRE
).
toList
();
if
(
triggerCond
.
size
()
>
0
)
{
for
(
QuestData
.
QuestCondition
cond
:
triggerCond
)
{
TriggerExcelConfigData
newTrigger
=
GameData
.
getTriggerExcelConfigDataMap
().
get
(
cond
.
getParam
()[
0
]);
if
(
newTrigger
!=
null
)
{
if
(
this
.
triggerData
==
null
)
{
this
.
triggerData
=
new
HashMap
<>();
}
triggerData
.
put
(
newTrigger
.
getTriggerName
(),
newTrigger
);
triggers
.
put
(
newTrigger
.
getTriggerName
(),
false
);
SceneGroup
group
=
SceneGroup
.
of
(
newTrigger
.
getGroupId
()).
load
(
newTrigger
.
getSceneId
());
getOwner
().
getWorld
().
getSceneById
(
newTrigger
.
getSceneId
()).
loadTriggerFromGroup
(
group
,
newTrigger
.
getTriggerName
());
}
}
}
if
(
questData
.
getFinishCond
()
!=
null
&&
questData
.
get
Accept
Cond
().
size
()
!=
0
)
{
if
(
questData
.
getFinishCond
()
!=
null
&&
questData
.
get
Finish
Cond
().
size
()
!=
0
)
{
this
.
finishProgressList
=
new
int
[
questData
.
getFinishCond
().
size
()];
}
...
...
@@ -56,201 +89,136 @@ public class GameQuest {
this
.
failProgressList
=
new
int
[
questData
.
getFailCond
().
size
()];
}
this
.
mainQuest
.
getChildQuests
().
put
(
this
.
questId
,
this
);
this
.
getData
().
getBeginExec
().
forEach
(
e
->
getOwner
().
getServer
().
getQuestSystem
().
triggerExec
(
this
,
e
,
e
.
getParam
()));
getQuestData
().
getBeginExec
().
forEach
(
e
->
getOwner
().
getServer
().
getQuestSystem
().
triggerExec
(
this
,
e
,
e
.
getParam
()));
this
.
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_QUEST_STATE_EQUAL
,
this
.
questId
,
this
.
state
.
getValue
());
if
(
ChapterData
.
beginQuestChapterMap
.
containsKey
(
q
uestId
))
{
if
(
ChapterData
.
beginQuestChapterMap
.
containsKey
(
subQ
uestId
)){
mainQuest
.
getOwner
().
sendPacket
(
new
PacketChapterStateNotify
(
ChapterData
.
beginQuestChapterMap
.
get
(
q
uestId
).
getId
(),
ChapterData
.
beginQuestChapterMap
.
get
(
subQ
uestId
).
getId
(),
ChapterStateOuterClass
.
ChapterState
.
CHAPTER_STATE_BEGIN
));
}
Grasscutter
.
getLogger
().
debug
(
"Quest {} is started"
,
questId
);
}
public
GameMainQuest
getMainQuest
()
{
return
mainQuest
;
}
public
void
setMainQuest
(
GameMainQuest
mainQuest
)
{
this
.
mainQuest
=
mainQuest
;
}
public
Player
getOwner
()
{
return
getMainQuest
().
getOwner
();
}
public
int
getQuestId
()
{
return
questId
;
}
public
int
getMainQuestId
()
{
return
mainQuestId
;
}
public
QuestData
getData
()
{
return
questData
;
}
public
void
setConfig
(
QuestData
config
)
{
if
(
this
.
getQuestId
()
!=
config
.
getId
())
return
;
this
.
questData
=
config
;
}
public
QuestState
getState
()
{
return
state
;
}
public
void
setState
(
QuestState
state
)
{
this
.
state
=
state
;
}
public
int
getStartTime
()
{
return
startTime
;
}
public
void
setStartTime
(
int
startTime
)
{
this
.
startTime
=
startTime
;
}
public
int
getAcceptTime
()
{
return
acceptTime
;
}
public
void
setAcceptTime
(
int
acceptTime
)
{
this
.
acceptTime
=
acceptTime
;
}
//Some subQuests and talks become active when some other subQuests are unfinished (even from different MainQuests)
this
.
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_QUEST_STATE_EQUAL
,
this
.
getSubQuestId
(),
this
.
getState
().
getValue
(),
0
,
0
,
0
);
this
.
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_COND_STATE_EQUAL
,
this
.
getSubQuestId
(),
this
.
getState
().
getValue
(),
0
,
0
,
0
);
public
int
getFinishTime
()
{
return
finishTime
;
Grasscutter
.
getLogger
().
debug
(
"Quest {} is started"
,
subQuestId
);
}
public
void
setFinishTime
(
int
finishTime
)
{
this
.
finishTime
=
finishTime
;
public
String
getTriggerNameById
(
int
id
)
{
TriggerExcelConfigData
trigger
=
GameData
.
getTriggerExcelConfigDataMap
().
get
(
id
);
if
(
trigger
!=
null
)
{
String
triggerName
=
trigger
.
getTriggerName
();
return
triggerName
;
}
//return empty string if can't find trigger
return
""
;
}
public
int
[]
getFinishProgressList
()
{
return
finishProgressList
;
}
public
Player
getOwner
()
{
return
this
.
getMainQuest
().
getOwner
()
;
}
public
void
setFinishProgress
(
int
index
,
int
value
)
{
finishProgressList
[
index
]
=
value
;
}
public
void
setConfig
(
QuestData
config
)
{
if
(
getSubQuestId
()
!=
config
.
getId
())
return
;
this
.
questData
=
config
;
}
public
int
[]
getFailProgressList
(
)
{
return
failProgressList
;
}
public
void
setFinishProgress
(
int
index
,
int
value
)
{
finishProgressList
[
index
]
=
value
;
}
public
void
setFailProgress
(
int
index
,
int
value
)
{
failProgressList
[
index
]
=
value
;
}
public
void
setFailProgress
(
int
index
,
int
value
)
{
failProgressList
[
index
]
=
value
;
}
public
void
finish
()
{
this
.
state
=
QuestState
.
QUEST_STATE_FINISHED
;
this
.
finishTime
=
Utils
.
getCurrentSeconds
();
public
void
finish
()
{
this
.
state
=
QuestState
.
QUEST_STATE_FINISHED
;
this
.
finishTime
=
Utils
.
getCurrentSeconds
();
if
(
this
.
getFinishProgressList
()
!=
null
)
{
for
(
int
i
=
0
;
i
<
getFinishProgressList
().
length
;
i
++)
{
getFinishProgressList
()[
i
]
=
1
;
}
}
if
(
getFinishProgressList
()
!=
null
)
{
Arrays
.
fill
(
getFinishProgressList
(),
1
);
}
this
.
getOwner
().
getSession
().
send
(
new
PacketQuestProgressUpdateNotify
(
this
));
this
.
getOwner
().
getSession
().
send
(
new
PacketQuestListUpdateNotify
(
this
));
getOwner
().
getSession
().
send
(
new
PacketQuestProgressUpdateNotify
(
this
));
if
(
this
.
getData
().
finishParent
())
{
// This quest finishes the questline - the main quest will also save the quest to db so we dont have to call save() here
this
.
getMainQuest
().
finish
();
}
else
{
// Try and accept other quests if possible
this
.
tryAcceptQuestLine
();
this
.
save
();
}
this
.
getData
().
getFinishExec
().
forEach
(
e
->
getOwner
().
getServer
().
getQuestSystem
().
triggerExec
(
this
,
e
,
e
.
getParam
()));
if
(
getQuestData
().
finishParent
())
{
// This quest finishes the questline - the main quest will also save the quest to db, so we don't have to call save() here
getMainQuest
().
finish
();
}
this
.
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_QUEST_STATE_EQUAL
,
this
.
questId
,
this
.
state
.
getValue
());
getQuestData
().
getFinishExec
().
forEach
(
e
->
getOwner
().
getServer
().
getQuestSystem
().
triggerExec
(
this
,
e
,
e
.
getParam
()));
//Some subQuests have conditions that subQuests are finished (even from different MainQuests)
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_QUEST_STATE_EQUAL
,
this
.
subQuestId
,
this
.
state
.
getValue
(),
0
,
0
,
0
);
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_COND_STATE_EQUAL
,
this
.
subQuestId
,
this
.
state
.
getValue
(),
0
,
0
,
0
);
if
(
ChapterData
.
endQuestChapterMap
.
containsKey
(
q
uestId
))
{
if
(
ChapterData
.
endQuestChapterMap
.
containsKey
(
subQ
uestId
)){
mainQuest
.
getOwner
().
sendPacket
(
new
PacketChapterStateNotify
(
ChapterData
.
endQuestChapterMap
.
get
(
q
uestId
).
getId
(),
ChapterData
.
endQuestChapterMap
.
get
(
subQ
uestId
).
getId
(),
ChapterStateOuterClass
.
ChapterState
.
CHAPTER_STATE_END
));
}
Grasscutter
.
getLogger
().
debug
(
"Quest {} is finished"
,
questId
);
}
public
boolean
tryAcceptQuestLine
()
{
try
{
MainQuestData
questConfig
=
GameData
.
getMainQuestDataMap
().
get
(
this
.
getMainQuestId
());
for
(
SubQuestData
subQuest
:
questConfig
.
getSubQuests
())
{
GameQuest
quest
=
getMainQuest
().
getChildQuestById
(
subQuest
.
getSubId
());
if
(
quest
==
null
)
{
QuestData
questData
=
GameData
.
getQuestDataMap
().
get
(
subQuest
.
getSubId
());
if
(
questData
==
null
||
questData
.
getAcceptCond
()
==
null
||
questData
.
getAcceptCond
().
size
()
==
0
)
{
continue
;
}
int
[]
accept
=
new
int
[
questData
.
getAcceptCond
().
size
()];
// TODO
for
(
int
i
=
0
;
i
<
questData
.
getAcceptCond
().
size
();
i
++)
{
QuestCondition
condition
=
questData
.
getAcceptCond
().
get
(
i
);
boolean
result
=
getOwner
().
getServer
().
getQuestSystem
().
triggerCondition
(
this
,
condition
,
condition
.
getParamStr
(),
condition
.
getParam
());
Grasscutter
.
getLogger
().
debug
(
"Quest {} is finished"
,
subQuestId
);
}
accept
[
i
]
=
result
?
1
:
0
;
}
boolean
shouldAccept
=
LogicType
.
calculate
(
questData
.
getAcceptCondComb
(),
accept
);
//TODO
public
void
fail
()
{
this
.
state
=
QuestState
.
QUEST_STATE_FAILED
;
this
.
finishTime
=
Utils
.
getCurrentSeconds
(
);
if
(
shouldAccept
)
{
this
.
getOwner
().
getQuestManager
().
addQuest
(
questData
.
getId
());
}
}
}
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"An error occurred while trying to accept quest."
,
e
);
if
(
getFailProgressList
()
!=
null
)
{
Arrays
.
fill
(
getFailProgressList
(),
1
);
}
return
false
;
}
public
void
save
()
{
getMainQuest
().
save
();
}
getOwner
().
getSession
().
send
(
new
PacketQuestProgressUpdateNotify
(
this
));
public
Quest
toProto
()
{
Quest
.
Builder
proto
=
Quest
.
newBuilder
()
.
setQuestId
(
this
.
getQuestId
())
.
setState
(
this
.
getState
().
getValue
())
.
setParentQuestId
(
this
.
getMainQuestId
())
.
setStartTime
(
this
.
getStartTime
())
.
setStartGameTime
(
438
)
.
setAcceptTime
(
this
.
getAcceptTime
());
if
(
this
.
getFinishProgressList
()
!=
null
)
{
for
(
int
i
:
this
.
getFinishProgressList
())
{
proto
.
addFinishProgressList
(
i
);
}
}
getQuestData
().
getFailExec
().
forEach
(
e
->
getOwner
().
getServer
().
getQuestSystem
().
triggerExec
(
this
,
e
,
e
.
getParam
()));
//Some subQuests have conditions that subQuests fail (even from different MainQuests)
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_QUEST_STATE_EQUAL
,
this
.
subQuestId
,
this
.
state
.
getValue
(),
0
,
0
,
0
);
getOwner
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_COND_STATE_EQUAL
,
this
.
subQuestId
,
this
.
state
.
getValue
(),
0
,
0
,
0
);
if
(
this
.
getFailProgressList
()
!=
null
)
{
for
(
int
i
:
this
.
getFailProgressList
())
{
proto
.
addFailProgressList
(
i
);
}
// Return true if ParentQuest should rewind to this childQuest
public
boolean
rewind
(
GameQuest
nextRewind
)
{
if
(
questData
.
isRewind
())
{
if
(
nextRewind
==
null
)
{
return
true
;}
// if the next isRewind subQuest is none or unstarted, reset all subQuests with order higher than this one, and restart this quest
if
(
nextRewind
.
getState
()
==
QuestState
.
QUEST_STATE_NONE
||
nextRewind
.
getState
()
==
QuestState
.
QUEST_STATE_UNSTARTED
)
{
getMainQuest
().
getChildQuests
().
values
().
stream
().
filter
(
p
->
p
.
getQuestData
().
getOrder
()
>
this
.
getQuestData
().
getOrder
()).
forEach
(
q
->
q
.
setState
(
QuestState
.
QUEST_STATE_UNSTARTED
));
this
.
start
();
return
true
;
}
}
return
proto
.
build
();
return
false
;
}
public
void
save
()
{
getMainQuest
().
save
();
}
public
Quest
toProto
()
{
Quest
.
Builder
proto
=
Quest
.
newBuilder
()
.
setQuestId
(
getSubQuestId
())
.
setState
(
getState
().
getValue
())
.
setParentQuestId
(
getMainQuestId
())
.
setStartTime
(
getStartTime
())
.
setStartGameTime
(
438
)
.
setAcceptTime
(
getAcceptTime
());
if
(
getFinishProgressList
()
!=
null
)
{
for
(
int
i
:
getFinishProgressList
())
{
proto
.
addFinishProgressList
(
i
);
}
}
if
(
getFailProgressList
()
!=
null
)
{
for
(
int
i
:
getFailProgressList
())
{
proto
.
addFailProgressList
(
i
);
}
}
return
proto
.
build
();
}
}
src/main/java/emu/grasscutter/game/quest/QuestManager.java
View file @
8050f0cc
package
emu.grasscutter.game.quest
;
import
java.beans.Transient
;
import
java.util.*
;
import
java.util.function.Consumer
;
import
java.util.stream.Collectors
;
...
...
@@ -17,32 +18,127 @@ import emu.grasscutter.game.quest.enums.QuestTrigger;
import
emu.grasscutter.game.quest.enums.LogicType
;
import
emu.grasscutter.game.quest.enums.QuestState
;
import
emu.grasscutter.server.packet.send.*
;
import
emu.grasscutter.utils.Position
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
import
jdk.jshell.spi.ExecutionControl
;
import
lombok.Getter
;
public
class
QuestManager
extends
BasePlayerManager
{
private
final
Int2ObjectMap
<
GameMainQuest
>
quests
;
public
QuestManager
(
Player
player
)
{
@Getter
private
final
Player
player
;
@Getter
private
Map
<
Integer
,
Integer
>
questGlobalVariables
;
@Getter
private
final
Int2ObjectMap
<
GameMainQuest
>
mainQuests
;
@Getter
private
List
<
GameQuest
>
addToQuestListUpdateNotify
;
/*
On SetPlayerBornDataReq, the server sends FinishedParentQuestNotify, with this exact
parentQuestList. Captured on Game version 2.7
Note: quest 40063 is already set to finished, with childQuest 4006406's state set to 3
*/
private
static
Set
<
Integer
>
newPlayerMainQuests
=
Set
.
of
(
303
,
318
,
348
,
349
,
350
,
351
,
416
,
500
,
501
,
502
,
503
,
504
,
505
,
506
,
507
,
508
,
509
,
20000
,
20507
,
20509
,
21004
,
21005
,
21010
,
21011
,
21016
,
21017
,
21020
,
21021
,
21025
,
40063
,
70121
,
70124
,
70511
,
71010
,
71012
,
71013
,
71015
,
71016
,
71017
,
71555
);
/*
On SetPlayerBornDataReq, the server sends ServerCondMeetQuestListUpdateNotify, with this exact
addQuestIdList. Captured on Game version 2.7
Total of 161...
*/
/*
private static Set<Integer> newPlayerServerCondMeetQuestListUpdateNotify = Set.of(3100101, 7104405, 2201601,
7100801, 1907002, 7293301, 7193801, 7293401, 7193901, 7091001, 7190501, 7090901, 7190401, 7090801, 7190301,
7195301, 7294801, 7195201, 7293001, 7094001, 7193501, 7293501, 7194001, 7293701, 7194201, 7194301, 7293801,
7194901, 7194101, 7195001, 7294501, 7294101, 7194601, 7294301, 7194801, 7091301, 7290301, 2102401, 7216801,
7190201, 7090701, 7093801, 7193301, 7292801, 7227828, 7093901, 7193401, 7292901, 7093701, 7193201, 7292701,
7082402, 7093601, 7292601, 7193101, 2102301, 7093501, 7292501, 7193001, 7093401, 7292401, 7192901, 7093301,
7292301, 7192801, 7294201, 7194701, 2100301, 7093201, 7212402, 7292201, 7192701, 7280001, 7293901, 7194401,
7093101, 7212302, 7292101, 7192601, 7093001, 7292001, 7192501, 7216001, 7195101, 7294601, 2100900, 7092901,
7291901, 7192401, 7092801, 7291801, 7192301, 2101501, 7092701, 7291701, 7192201, 7106401, 2100716, 7091801,
7290801, 7191301, 7293201, 7193701, 7094201, 7294001, 7194501, 2102290, 7227829, 7193601, 7094101, 7091401,
7290401, 7190901, 7106605, 7291601, 7192101, 7092601, 7291501, 7192001, 7092501, 7291401, 7191901, 7092401,
7291301, 7191801, 7092301, 7211402, 7291201, 7191701, 7092201, 7291101, 7191601, 7092101, 7291001, 7191501,
7092001, 7290901, 7191401, 7091901, 7290701, 7191201, 7091701, 7290601, 7191101, 7091601, 7290501, 7191001,
7091501, 7290201, 7190701, 7091201, 7190601, 7091101, 7190101, 7090601, 7090501, 7090401, 7010701, 7090301,
7090201, 7010103, 7090101
);
*/
public
QuestManager
(
Player
player
)
{
super
(
player
);
this
.
quests
=
new
Int2ObjectOpenHashMap
<>();
this
.
player
=
player
;
this
.
questGlobalVariables
=
player
.
getQuestGlobalVariables
();
this
.
mainQuests
=
new
Int2ObjectOpenHashMap
<>();
this
.
addToQuestListUpdateNotify
=
new
ArrayList
<>();
}
public
void
onNewPlayerCreate
()
{
List
<
GameMainQuest
>
newQuests
=
this
.
addMultMainQuests
(
newPlayerMainQuests
);
//getPlayer().sendPacket(new PacketServerCondMeetQuestListUpdateNotify(newPlayerServerCondMeetQuestListUpdateNotify));
getPlayer
().
sendPacket
(
new
PacketFinishedParentQuestUpdateNotify
(
newQuests
));
}
public
void
onLogin
()
{
List
<
GameMainQuest
>
activeQuests
=
getActiveMainQuests
();
for
(
GameMainQuest
quest
:
activeQuests
)
{
List
<
Position
>
rewindPos
=
quest
.
rewind
();
// <pos, rotation>
if
(
rewindPos
!=
null
)
{
getPlayer
().
getPosition
().
set
(
rewindPos
.
get
(
0
));
getPlayer
().
getRotation
().
set
(
rewindPos
.
get
(
1
));
}
}
}
private
List
<
GameMainQuest
>
addMultMainQuests
(
Set
<
Integer
>
mainQuestIds
)
{
List
<
GameMainQuest
>
newQuests
=
new
ArrayList
<>();
for
(
Integer
id
:
mainQuestIds
)
{
getMainQuests
().
put
(
id
.
intValue
(),
new
GameMainQuest
(
this
.
player
,
id
));
getMainQuestById
(
id
).
save
();
newQuests
.
add
(
getMainQuestById
(
id
));
}
return
newQuests
;
}
public
Int2ObjectMap
<
GameMainQuest
>
getQuests
()
{
return
quests
;
/*
Looking through mainQuests 72201-72208 and 72174, we can infer that a questGlobalVar's default value is 0
*/
public
Integer
getQuestGlobalVarValue
(
Integer
variable
)
{
return
this
.
questGlobalVariables
.
getOrDefault
(
variable
,
0
);
}
public
GameMainQuest
getMainQuestById
(
int
mainQuestId
)
{
return
getQuests
().
get
(
mainQuestId
);
public
void
setQuestGlobalVarValue
(
Integer
variable
,
Integer
value
)
{
Integer
previousValue
=
this
.
questGlobalVariables
.
put
(
variable
,
value
);
Grasscutter
.
getLogger
().
debug
(
"Changed questGlobalVar {} value from {} to {}"
,
variable
,
previousValue
==
null
?
0
:
previousValue
,
value
);
}
public
void
incQuestGlobalVarValue
(
Integer
variable
,
Integer
inc
)
{
//
Integer
previousValue
=
this
.
questGlobalVariables
.
getOrDefault
(
variable
,
0
);
this
.
questGlobalVariables
.
put
(
variable
,
previousValue
+
inc
);
Grasscutter
.
getLogger
().
debug
(
"Incremented questGlobalVar {} value from {} to {}"
,
variable
,
previousValue
,
previousValue
+
inc
);
}
//In MainQuest 998, dec is passed as a positive integer
public
void
decQuestGlobalVarValue
(
Integer
variable
,
Integer
dec
)
{
//
Integer
previousValue
=
this
.
questGlobalVariables
.
getOrDefault
(
variable
,
0
);
this
.
questGlobalVariables
.
put
(
variable
,
previousValue
-
dec
);
Grasscutter
.
getLogger
().
debug
(
"Decremented questGlobalVar {} value from {} to {}"
,
variable
,
previousValue
,
previousValue
-
dec
);
}
public
GameMainQuest
getMainQuestById
(
int
mainQuestId
)
{
return
getMainQuests
().
get
(
mainQuestId
);
}
public
GameQuest
getQuestById
(
int
questId
)
{
QuestData
questConfig
=
GameData
.
getQuestDataMap
().
get
(
questId
);
if
(
questConfig
==
null
)
{
return
null
;
}
GameMainQuest
mainQuest
=
getQuests
().
get
(
questConfig
.
getMainId
());
GameMainQuest
mainQuest
=
get
Main
Quests
().
get
(
questConfig
.
getMainId
());
if
(
mainQuest
==
null
)
{
return
null
;
...
...
@@ -51,34 +147,34 @@ public class QuestManager extends BasePlayerManager {
return
mainQuest
.
getChildQuests
().
get
(
questId
);
}
public
void
forEachQuest
(
Consumer
<
GameQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
getQuests
().
values
())
{
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
callback
.
accept
(
quest
);
}
}
}
public
void
forEachMainQuest
(
Consumer
<
GameMainQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
getQuests
().
values
())
{
callback
.
accept
(
mainQuest
);
}
}
// TODO
public
void
forEachActiveQuest
(
Consumer
<
GameQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
getQuests
().
values
())
{
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
if
(
quest
.
getState
()
!=
QuestState
.
QUEST_STATE_FINISHED
)
{
callback
.
accept
(
quest
);
}
}
}
}
public
GameMainQuest
addMainQuest
(
QuestData
questConfig
)
{
GameMainQuest
mainQuest
=
new
GameMainQuest
(
getPlayer
(),
questConfig
.
getMainId
());
get
Quests
().
put
(
mainQuest
.
getParentQuestId
(),
mainQuest
);
public
void
forEachQuest
(
Consumer
<
GameQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
get
Main
Quests
().
values
())
{
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
callback
.
accept
(
quest
);
}
}
}
public
void
forEachMainQuest
(
Consumer
<
GameMainQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
get
Main
Quests
().
values
())
{
callback
.
accept
(
mainQuest
);
}
}
// TODO
public
void
forEachActiveQuest
(
Consumer
<
GameQuest
>
callback
)
{
for
(
GameMainQuest
mainQuest
:
get
Main
Quests
().
values
())
{
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
if
(
quest
.
getState
()
!=
QuestState
.
QUEST_STATE_FINISHED
)
{
callback
.
accept
(
quest
);
}
}
}
}
public
GameMainQuest
addMainQuest
(
QuestData
questConfig
)
{
GameMainQuest
mainQuest
=
new
GameMainQuest
(
getPlayer
(),
questConfig
.
getMainId
());
getMain
Quests
().
put
(
mainQuest
.
getParentQuestId
(),
mainQuest
);
getPlayer
().
sendPacket
(
new
PacketFinishedParentQuestUpdateNotify
(
mainQuest
));
...
...
@@ -102,18 +198,16 @@ public class QuestManager extends BasePlayerManager {
// Sub quest
GameQuest
quest
=
mainQuest
.
getChildQuestById
(
questId
);
if
(
quest
!=
null
)
{
return
null
;
}
// Create
quest
=
new
GameQuest
(
mainQuest
,
questConfig
);
// Forcefully start
quest
.
start
();
// Save main quest
mainQuest
.
save
();
// Send packet
getPlayer
().
sendPacket
(
new
PacketQuestListUpdateNotify
(
quest
));
// Send packet
getPlayer
().
sendPacket
(
new
PacketQuestListUpdateNotify
(
mainQuest
.
getChildQuests
().
values
().
stream
()
.
filter
(
p
->
p
.
getState
()
!=
QuestState
.
QUEST_STATE_UNSTARTED
)
.
toList
()));
return
quest
;
}
...
...
@@ -133,55 +227,81 @@ public class QuestManager extends BasePlayerManager {
triggerEvent
(
condType
,
""
,
params
);
}
public
void
triggerEvent
(
QuestTrigger
condType
,
String
paramStr
,
int
...
params
)
{
//TODO
public
void
triggerEvent
(
QuestTrigger
condType
,
String
paramStr
,
int
...
params
)
{
Grasscutter
.
getLogger
().
debug
(
"Trigger Event {}, {}, {}"
,
condType
,
paramStr
,
params
);
Set
<
GameQuest
>
changedQuests
=
new
HashSet
<>();
this
.
forEachActiveQuest
(
quest
->
{
QuestData
data
=
quest
.
getData
();
for
(
int
i
=
0
;
i
<
data
.
getFinishCond
().
size
();
i
++)
{
if
(
quest
.
getFinishProgressList
()
==
null
||
quest
.
getFinishProgressList
().
length
==
0
||
quest
.
getFinishProgressList
()[
i
]
==
1
)
{
continue
;
List
<
GameMainQuest
>
checkMainQuests
=
this
.
getMainQuests
().
values
().
stream
()
.
filter
(
i
->
i
.
getState
()
!=
ParentQuestState
.
PARENT_QUEST_STATE_FINISHED
)
.
toList
();
switch
(
condType
){
//accept Conds
case
QUEST_COND_STATE_EQUAL:
case
QUEST_COND_STATE_NOT_EQUAL:
case
QUEST_COND_COMPLETE_TALK:
case
QUEST_COND_LUA_NOTIFY:
case
QUEST_COND_QUEST_VAR_EQUAL:
case
QUEST_COND_QUEST_VAR_GREATER:
case
QUEST_COND_QUEST_VAR_LESS:
case
QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER:
case
QUEST_COND_QUEST_GLOBAL_VAR_EQUAL:
case
QUEST_COND_QUEST_GLOBAL_VAR_GREATER:
case
QUEST_COND_QUEST_GLOBAL_VAR_LESS:
for
(
GameMainQuest
mainquest
:
checkMainQuests
)
{
mainquest
.
tryAcceptSubQuests
(
condType
,
paramStr
,
params
);
}
break
;
QuestCondition
condition
=
data
.
getFinishCond
().
get
(
i
);
if
(
condition
.
getType
()
!=
condType
)
{
continue
;
//fail Conds
case
QUEST_CONTENT_NOT_FINISH_PLOT:
for
(
GameMainQuest
mainquest
:
checkMainQuests
)
{
mainquest
.
tryFailSubQuests
(
condType
,
paramStr
,
params
)
;
}
boolean
result
=
getPlayer
().
getServer
().
getQuestSystem
().
triggerContent
(
quest
,
condition
,
paramStr
,
params
);
if
(
result
)
{
quest
.
getFinishProgressList
()[
i
]
=
1
;
changedQuests
.
add
(
quest
);
break
;
//finish Conds
case
QUEST_CONTENT_COMPLETE_TALK:
case
QUEST_CONTENT_FINISH_PLOT:
case
QUEST_CONTENT_COMPLETE_ANY_TALK:
case
QUEST_CONTENT_LUA_NOTIFY:
case
QUEST_CONTENT_QUEST_VAR_EQUAL:
case
QUEST_CONTENT_QUEST_VAR_GREATER:
case
QUEST_CONTENT_QUEST_VAR_LESS:
case
QUEST_CONTENT_ENTER_DUNGEON:
case
QUEST_CONTENT_ENTER_ROOM:
case
QUEST_CONTENT_INTERACT_GADGET:
case
QUEST_CONTENT_TRIGGER_FIRE:
case
QUEST_CONTENT_UNLOCK_TRANS_POINT:
for
(
GameMainQuest
mainQuest
:
checkMainQuests
)
{
mainQuest
.
tryFinishSubQuests
(
condType
,
paramStr
,
params
);
}
}
});
for
(
GameQuest
quest
:
changedQuests
)
{
LogicType
logicType
=
quest
.
getData
().
getFailCondComb
();
int
[]
progress
=
quest
.
getFinishProgressList
();
// Handle logical comb
boolean
finish
=
LogicType
.
calculate
(
logicType
,
progress
);
// Finish
if
(
finish
)
{
quest
.
finish
();
}
else
{
getPlayer
().
sendPacket
(
new
PacketQuestProgressUpdateNotify
(
quest
));
quest
.
save
();
}
break
;
//finish Or Fail Conds
case
QUEST_CONTENT_GAME_TIME_TICK:
case
QUEST_CONTENT_QUEST_STATE_EQUAL:
case
QUEST_CONTENT_ADD_QUEST_PROGRESS:
case
QUEST_CONTENT_LEAVE_SCENE:
for
(
GameMainQuest
mainQuest
:
checkMainQuests
)
{
mainQuest
.
tryFailSubQuests
(
condType
,
paramStr
,
params
);
mainQuest
.
tryFinishSubQuests
(
condType
,
paramStr
,
params
);
}
break
;
//QUEST_EXEC are handled directly by each subQuest
//Unused
case
QUEST_CONTENT_QUEST_STATE_NOT_EQUAL:
case
QUEST_COND_PLAYER_CHOOSE_MALE:
default
:
Grasscutter
.
getLogger
().
error
(
"Unhandled QuestTrigger {}"
,
condType
);
}
}
if
(
this
.
addToQuestListUpdateNotify
.
size
()
!=
0
){
this
.
getPlayer
().
getSession
().
send
(
new
PacketQuestListUpdateNotify
(
this
.
addToQuestListUpdateNotify
));
this
.
addToQuestListUpdateNotify
.
clear
();
}
}
public
List
<
QuestGroupSuite
>
getSceneGroupSuite
(
int
sceneId
)
{
return
getQuests
().
values
().
stream
()
return
get
Main
Quests
().
values
().
stream
()
.
filter
(
i
->
i
.
getState
()
!=
ParentQuestState
.
PARENT_QUEST_STATE_FINISHED
)
.
map
(
GameMainQuest:
:
getQuestGroupSuites
)
.
filter
(
Objects:
:
nonNull
)
...
...
@@ -195,12 +315,16 @@ public class QuestManager extends BasePlayerManager {
for
(
GameMainQuest
mainQuest
:
quests
)
{
mainQuest
.
setOwner
(
this
.
getPlayer
());
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
quest
.
setMainQuest
(
mainQuest
);
quest
.
setConfig
(
GameData
.
getQuestDataMap
().
get
(
quest
.
getQuestId
()));
}
for
(
GameQuest
quest
:
mainQuest
.
getChildQuests
().
values
())
{
quest
.
setMainQuest
(
mainQuest
);
quest
.
setConfig
(
GameData
.
getQuestDataMap
().
get
(
quest
.
get
Sub
QuestId
()));
}
this
.
getQuests
().
put
(
mainQuest
.
getParentQuestId
(),
mainQuest
);
}
this
.
getMainQuests
().
put
(
mainQuest
.
getParentQuestId
(),
mainQuest
);
}
}
public
List
<
GameMainQuest
>
getActiveMainQuests
()
{
return
getMainQuests
().
values
().
stream
().
filter
(
p
->
!
p
.
isFinished
()).
toList
();
}
}
src/main/java/emu/grasscutter/game/quest/conditions/ConditionLuaNotify.java
View file @
8050f0cc
...
...
@@ -8,7 +8,7 @@ import emu.grasscutter.game.quest.handlers.QuestBaseHandler;
@QuestValue
(
QuestTrigger
.
QUEST_COND_LUA_NOTIFY
)
public
class
ConditionLuaNotify
extends
QuestBaseHandler
{
//Wrong implementation. Example: 7010226 has no paramStr
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
String
paramStr
,
int
...
params
)
{
return
condition
.
getParam
()[
0
]
==
Integer
.
parseInt
(
paramStr
);
...
...
src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java
View file @
8050f0cc
package
emu.grasscutter.game.quest.conditions
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.game.quest.QuestValue
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.quest.GameQuest
;
...
...
@@ -11,13 +12,16 @@ public class ConditionStateEqual extends QuestBaseHandler {
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
String
paramStr
,
int
...
params
)
{
GameQuest
checkQuest
=
quest
.
getOwner
().
getQuestManager
().
getQuestById
(
params
[
0
]);
GameQuest
checkQuest
=
quest
.
getOwner
().
getQuestManager
().
getQuestById
(
condition
.
getParam
()[
0
]);
if
(
checkQuest
==
null
)
{
/*
Will spam the console
//Grasscutter.getLogger().debug("Warning: quest {} hasn't been started yet!", condition.getParam()[0]);
if
(
checkQuest
!=
null
)
{
return
checkQuest
.
getState
().
getValue
()
==
params
[
1
];
}
return
false
;
*/
return
false
;
}
return
checkQuest
.
getState
().
getValue
()
==
condition
.
getParam
()[
1
];
}
}
src/main/java/emu/grasscutter/game/quest/content/ContentAddQuestProgress.java
View file @
8050f0cc
...
...
@@ -11,7 +11,12 @@ public class ContentAddQuestProgress extends QuestBaseHandler {
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
String
paramStr
,
int
...
params
)
{
return
condition
.
getParam
()[
0
]
==
params
[
0
];
/*
//paramStr is a lua group, params[0] may also be a lua group!
questid = xxxxxx lua group = xxxxxxyy
count seems relevant only for lua group
*/
return
condition
.
getParam
()[
0
]
==
params
[
0
];
//missing params[1], paramStr, and count
}
}
src/main/java/emu/grasscutter/game/quest/content/ContentCompleteTalk.java
View file @
8050f0cc
package
emu.grasscutter.game.quest.content
;
import
emu.grasscutter.data.binout.MainQuestData
;
import
emu.grasscutter.game.quest.GameMainQuest
;
import
emu.grasscutter.game.quest.GameQuest
;
import
emu.grasscutter.game.quest.QuestValue
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.quest.GameQuest
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.game.quest.handlers.QuestBaseHandler
;
...
...
@@ -11,7 +13,9 @@ public class ContentCompleteTalk extends QuestBaseHandler {
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
String
paramStr
,
int
...
params
)
{
return
condition
.
getParam
()[
0
]
==
params
[
0
];
GameMainQuest
checkMainQuest
=
quest
.
getOwner
().
getQuestManager
().
getMainQuestById
(
params
[
0
]/
100
);
if
(
checkMainQuest
==
null
)
{
return
false
;}
MainQuestData
.
TalkData
talkData
=
checkMainQuest
.
getTalks
().
get
(
Integer
.
valueOf
(
params
[
0
]));
return
talkData
==
null
||
condition
.
getParamStr
().
contains
(
paramStr
)
||
checkMainQuest
.
getChildQuestById
(
params
[
0
])
!=
null
;
}
}
src/main/java/emu/grasscutter/game/quest/content/ContentEnterDungeon.java
View file @
8050f0cc
...
...
@@ -11,7 +11,7 @@ public class ContentEnterDungeon extends QuestBaseHandler {
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
String
paramStr
,
int
...
params
)
{
return
condition
.
getParam
()[
0
]
==
params
[
0
];
return
condition
.
getParam
()[
0
]
==
params
[
0
];
//missing params[1]
}
}
src/main/java/emu/grasscutter/game/quest/content/ContentFinishPlot.java
View file @
8050f0cc
package
emu.grasscutter.game.quest.content
;
import
emu.grasscutter.data.binout.MainQuestData
;
import
emu.grasscutter.game.quest.GameMainQuest
;
import
emu.grasscutter.game.quest.QuestValue
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
emu.grasscutter.game.quest.GameQuest
;
...
...
@@ -11,7 +13,9 @@ public class ContentFinishPlot extends QuestBaseHandler {
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
String
paramStr
,
int
...
params
)
{
return
condition
.
getParam
()[
0
]
==
params
[
0
];
}
MainQuestData
.
TalkData
talkData
=
quest
.
getMainQuest
().
getTalks
().
get
(
Integer
.
valueOf
(
params
[
0
]));
GameQuest
subQuest
=
quest
.
getMainQuest
().
getChildQuestById
(
params
[
0
]);
return
talkData
!=
null
||
subQuest
!=
null
;
}
}
src/main/java/emu/grasscutter/game/quest/content/ContentQuestStateEqual.java
View file @
8050f0cc
...
...
@@ -11,13 +11,9 @@ public class ContentQuestStateEqual extends QuestBaseHandler {
@Override
public
boolean
execute
(
GameQuest
quest
,
QuestCondition
condition
,
String
paramStr
,
int
...
params
)
{
GameQuest
checkQuest
=
quest
.
getOwner
().
getQuestManager
().
getQuestById
(
params
[
0
]);
if
(
checkQuest
!=
null
)
{
return
checkQuest
.
getState
().
getValue
()
==
params
[
1
];
}
return
false
;
GameQuest
checkQuest
=
quest
.
getOwner
().
getQuestManager
().
getQuestById
(
condition
.
getParam
()[
0
]);
if
(
checkQuest
==
null
)
{
return
false
;}
return
checkQuest
.
getState
().
getValue
()
==
params
[
1
];
}
}
src/main/java/emu/grasscutter/game/quest/enums/QuestState.java
View file @
8050f0cc
...
...
@@ -5,10 +5,17 @@ public enum QuestState {
QUEST_STATE_UNSTARTED
(
1
),
QUEST_STATE_UNFINISHED
(
2
),
QUEST_STATE_FINISHED
(
3
),
QUEST_STATE_FAILED
(
4
);
QUEST_STATE_FAILED
(
4
),
// Used by lua
NONE
(
0
),
UNSTARTED
(
1
),
UNFINISHED
(
2
),
FINISHED
(
3
),
FAILED
(
4
);
private
final
int
value
;
QuestState
(
int
id
)
{
this
.
value
=
id
;
}
...
...
src/main/java/emu/grasscutter/scripts/SceneScriptManager.java
View file @
8050f0cc
...
...
@@ -92,6 +92,7 @@ public class SceneScriptManager {
}
public
void
registerTrigger
(
SceneTrigger
trigger
)
{
getTriggersByEvent
(
trigger
.
event
).
add
(
trigger
);
Grasscutter
.
getLogger
().
debug
(
"Registered trigger {}"
,
trigger
.
name
);
}
public
void
deregisterTrigger
(
List
<
SceneTrigger
>
triggers
)
{
triggers
.
forEach
(
this
::
deregisterTrigger
);
...
...
@@ -122,6 +123,7 @@ public class SceneScriptManager {
public
void
registerRegion
(
EntityRegion
region
)
{
regions
.
put
(
region
.
getId
(),
region
);
Grasscutter
.
getLogger
().
debug
(
"Registered region {} from group {}"
,
region
.
getMetaRegion
().
config_id
,
region
.
getGroupId
());
}
public
void
registerRegionInGroupSuite
(
SceneGroup
group
,
SceneSuite
suite
){
suite
.
sceneRegions
.
stream
().
map
(
region
->
new
EntityRegion
(
this
.
getScene
(),
region
))
...
...
@@ -195,9 +197,30 @@ public class SceneScriptManager {
.
filter
(
e
->
e
.
getEntityType
()
==
EntityType
.
Avatar
.
getValue
()
&&
region
.
getMetaRegion
().
contains
(
e
.
getPosition
()))
.
forEach
(
region:
:
addEntity
);
var
players
=
region
.
getScene
().
getPlayers
();
int
targetID
=
0
;
if
(
players
.
size
()
>
0
)
targetID
=
players
.
get
(
0
).
getUid
();
if
(
region
.
hasNewEntities
())
{
Grasscutter
.
getLogger
().
trace
(
"Call EVENT_ENTER_REGION_{}"
,
region
.
getMetaRegion
().
config_id
);
callEvent
(
EventType
.
EVENT_ENTER_REGION
,
new
ScriptArgs
(
region
.
getConfigId
())
.
setSourceEntityId
(
region
.
getId
())
.
setTargetEntityId
(
targetID
)
);
region
.
resetNewEntities
();
}
for
(
int
entityId
:
region
.
getEntities
())
{
if
(!
region
.
getMetaRegion
().
contains
(
getScene
().
getEntityById
(
entityId
).
getPosition
()))
{
region
.
removeEntity
(
entityId
);
}
}
if
(
region
.
entityLeave
())
{
callEvent
(
EventType
.
EVENT_LEAVE_REGION
,
new
ScriptArgs
(
region
.
getConfigId
())
.
setSourceEntityId
(
region
.
getId
())
.
setTargetEntityId
(
region
.
getFirstEntityId
())
);
...
...
@@ -286,27 +309,39 @@ public class SceneScriptManager {
}
private
void
realCallEvent
(
int
eventType
,
ScriptArgs
params
)
{
try
{
ScriptLoader
.
getScriptLib
().
setSceneScriptManager
(
this
);
for
(
SceneTrigger
trigger
:
this
.
getTriggersByEvent
(
eventType
))
{
try
{
ScriptLoader
.
getScriptLib
().
setCurrentGroup
(
trigger
.
currentGroup
);
LuaValue
ret
=
callScriptFunc
(
trigger
.
condition
,
trigger
.
currentGroup
,
params
);
Grasscutter
.
getLogger
().
trace
(
"Call Condition Trigger {}"
,
trigger
.
condition
);
if
(
ret
.
isboolean
()
&&
ret
.
checkboolean
())
{
// the SetGroupVariableValueByGroup in tower need the param to record the first stage time
callScriptFunc
(
trigger
.
action
,
trigger
.
currentGroup
,
params
);
Grasscutter
.
getLogger
().
trace
(
"Call Action Trigger {}"
,
trigger
.
action
);
}
//TODO some ret may not bool
}
finally
{
ScriptLoader
.
getScriptLib
().
removeCurrentGroup
();
}
}
}
finally
{
try
{
Set
<
SceneTrigger
>
relevantTriggers
=
new
HashSet
<>();
if
(
eventType
==
EventType
.
EVENT_ENTER_REGION
||
eventType
==
EventType
.
EVENT_LEAVE_REGION
)
{
List
<
SceneTrigger
>
relevantTriggersList
=
this
.
getTriggersByEvent
(
eventType
).
stream
()
.
filter
(
p
->
p
.
condition
.
contains
(
String
.
valueOf
(
params
.
param1
))).
toList
();
relevantTriggers
=
new
HashSet
<>(
relevantTriggersList
);
}
else
{
relevantTriggers
=
this
.
getTriggersByEvent
(
eventType
);}
for
(
SceneTrigger
trigger
:
relevantTriggers
)
{
try
{
ScriptLoader
.
getScriptLib
().
setCurrentGroup
(
trigger
.
currentGroup
);
LuaValue
ret
=
this
.
callScriptFunc
(
trigger
.
condition
,
trigger
.
currentGroup
,
params
);
Grasscutter
.
getLogger
().
trace
(
"Call Condition Trigger {}, [{},{},{}]"
,
trigger
.
condition
,
params
.
param1
,
params
.
source_eid
,
params
.
target_eid
);
if
(
ret
.
isboolean
()
&&
ret
.
checkboolean
())
{
// the SetGroupVariableValueByGroup in tower need the param to record the first stage time
this
.
callScriptFunc
(
trigger
.
action
,
trigger
.
currentGroup
,
params
);
Grasscutter
.
getLogger
().
trace
(
"Call Action Trigger {}"
,
trigger
.
action
);
if
(
trigger
.
event
==
EventType
.
EVENT_ENTER_REGION
)
{
EntityRegion
region
=
this
.
regions
.
values
().
stream
().
filter
(
p
->
p
.
getConfigId
()
==
params
.
param1
).
toList
().
get
(
0
);
getScene
().
getPlayers
().
forEach
(
p
->
p
.
onEnterRegion
(
region
.
getMetaRegion
()));
}
else
if
(
trigger
.
event
==
EventType
.
EVENT_LEAVE_REGION
)
{
EntityRegion
region
=
this
.
regions
.
values
().
stream
().
filter
(
p
->
p
.
getConfigId
()
==
params
.
param1
).
toList
().
get
(
0
);
getScene
().
getPlayers
().
forEach
(
p
->
p
.
onLeaveRegion
(
region
.
getMetaRegion
()));
}
deregisterTrigger
(
trigger
);
}
else
{
Grasscutter
.
getLogger
().
debug
(
"Condition Trigger {} returned {}"
,
trigger
.
condition
,
ret
);
}
//TODO some ret do not bool
}
finally
{
ScriptLoader
.
getScriptLib
().
removeCurrentGroup
();
}
}
}
finally
{
// make sure it is removed
ScriptLoader
.
getScriptLib
().
removeSceneScriptManager
();
}
...
...
src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
View file @
8050f0cc
...
...
@@ -26,7 +26,8 @@ public class SceneRegion {
var
x
=
Math
.
pow
(
pos
.
getX
()
-
position
.
getX
(),
2
);
var
y
=
Math
.
pow
(
pos
.
getY
()
-
position
.
getY
(),
2
);
var
z
=
Math
.
pow
(
pos
.
getZ
()
-
position
.
getZ
(),
2
);
return
x
+
y
+
z
<=
(
radius
^
2
);
// ^ means XOR in java!
return
x
+
y
+
z
<=
(
radius
*
radius
);
}
return
false
;
}
...
...
src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java
View file @
8050f0cc
...
...
@@ -29,22 +29,70 @@ public class LuaSerializer implements Serializer {
public
<
T
>
T
toObject
(
Class
<
T
>
type
,
Object
obj
)
{
return
serialize
(
type
,
(
LuaTable
)
obj
);
}
public
<
T
>
List
<
T
>
serializeList
(
Class
<
T
>
type
,
LuaTable
table
)
{
@Override
public
<
T
>
Map
<
String
,
T
>
toMap
(
Class
<
T
>
type
,
Object
obj
)
{
return
serializeMap
(
type
,
(
LuaTable
)
obj
);
}
private
<
T
>
Map
<
String
,
T
>
serializeMap
(
Class
<
T
>
type
,
LuaTable
table
)
{
Map
<
String
,
T
>
map
=
new
HashMap
<>();
if
(
table
==
null
)
{
return
map
;
}
try
{
LuaValue
[]
keys
=
table
.
keys
();
for
(
LuaValue
k
:
keys
)
{
try
{
LuaValue
keyValue
=
table
.
get
(
k
);
T
object
=
null
;
if
(
keyValue
.
istable
())
{
object
=
serialize
(
type
,
keyValue
.
checktable
());
}
else
if
(
keyValue
.
isint
())
{
object
=
(
T
)
(
Integer
)
keyValue
.
toint
();
}
else
if
(
keyValue
.
isnumber
())
{
object
=
(
T
)
(
Float
)
keyValue
.
tofloat
();
// terrible...
}
else
if
(
keyValue
.
isstring
())
{
object
=
(
T
)
keyValue
.
tojstring
();
}
else
if
(
keyValue
.
isboolean
())
{
object
=
(
T
)
(
Boolean
)
keyValue
.
toboolean
();
}
else
{
object
=
(
T
)
keyValue
;
}
if
(
object
!=
null
)
{
map
.
put
(
String
.
valueOf
(
k
),
object
);
}
}
catch
(
Exception
ex
)
{
}
}
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
return
map
;
}
public
<
T
>
List
<
T
>
serializeList
(
Class
<
T
>
type
,
LuaTable
table
)
{
List
<
T
>
list
=
new
ArrayList
<>();
if
(
table
==
null
)
{
return
list
;
}
try
{
LuaValue
[]
keys
=
table
.
keys
();
for
(
LuaValue
k
:
keys
)
{
try
{
LuaValue
keyValue
=
table
.
get
(
k
);
T
object
=
null
;
if
(
keyValue
.
istable
())
{
object
=
serialize
(
type
,
keyValue
.
checktable
());
}
else
if
(
keyValue
.
isint
())
{
...
...
@@ -75,7 +123,7 @@ public class LuaSerializer implements Serializer {
public
<
T
>
T
serialize
(
Class
<
T
>
type
,
LuaTable
table
)
{
T
object
=
null
;
if
(
type
==
List
.
class
)
{
try
{
Class
<
T
>
listType
=
(
Class
<
T
>)
type
.
getTypeParameters
()[
0
].
getClass
();
...
...
@@ -85,7 +133,7 @@ public class LuaSerializer implements Serializer {
return
null
;
}
}
try
{
if
(!
methodAccessCache
.
containsKey
(
type
))
{
cacheType
(
type
);
...
...
@@ -98,7 +146,7 @@ public class LuaSerializer implements Serializer {
if
(
table
==
null
)
{
return
object
;
}
LuaValue
[]
keys
=
table
.
keys
();
for
(
LuaValue
k
:
keys
)
{
try
{
...
...
@@ -131,7 +179,7 @@ public class LuaSerializer implements Serializer {
Grasscutter
.
getLogger
().
info
(
ScriptUtils
.
toMap
(
table
).
toString
());
e
.
printStackTrace
();
}
return
object
;
}
...
...
src/main/java/emu/grasscutter/scripts/serializer/Serializer.java
View file @
8050f0cc
package
emu.grasscutter.scripts.serializer
;
import
java.util.List
;
import
java.util.Map
;
import
org.luaj.vm2.LuaTable
;
public
interface
Serializer
{
public
<
T
>
List
<
T
>
toList
(
Class
<
T
>
type
,
Object
obj
);
public
<
T
>
T
toObject
(
Class
<
T
>
type
,
Object
obj
);
public
<
T
>
Map
<
String
,
T
>
toMap
(
Class
<
T
>
type
,
Object
obj
);
}
src/main/java/emu/grasscutter/server/packet/recv/HandlerAddQuestContentProgressReq.java
View file @
8050f0cc
package
emu.grasscutter.server.packet.recv
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.excels.QuestData
;
import
emu.grasscutter.game.quest.GameQuest
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.net.packet.Opcodes
;
import
emu.grasscutter.net.packet.PacketHandler
;
...
...
@@ -7,6 +10,9 @@ import emu.grasscutter.net.packet.PacketOpcodes;
import
emu.grasscutter.net.proto.AddQuestContentProgressReqOuterClass
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.PacketAddQuestContentProgressRsp
;
import
emu.grasscutter.data.excels.QuestData.QuestCondition
;
import
java.util.List
;
import
java.util.stream.Stream
;
@Opcodes
(
PacketOpcodes
.
AddQuestContentProgressReq
)
public
class
HandlerAddQuestContentProgressReq
extends
PacketHandler
{
...
...
@@ -14,9 +20,14 @@ public class HandlerAddQuestContentProgressReq extends PacketHandler {
@Override
public
void
handle
(
GameSession
session
,
byte
[]
header
,
byte
[]
payload
)
throws
Exception
{
var
req
=
AddQuestContentProgressReqOuterClass
.
AddQuestContentProgressReq
.
parseFrom
(
payload
);
session
.
getPlayer
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
getContentTriggerByValue
(
req
.
getContentType
()),
req
.
getParam
());
//Find all conditions in quest that are the same as the given one
Stream
<
QuestCondition
>
finishCond
=
GameData
.
getQuestDataMap
().
get
(
req
.
getParam
()).
getFinishCond
().
stream
();
Stream
<
QuestCondition
>
acceptCond
=
GameData
.
getQuestDataMap
().
get
(
req
.
getParam
()).
getAcceptCond
().
stream
();
Stream
<
QuestCondition
>
failCond
=
GameData
.
getQuestDataMap
().
get
(
req
.
getParam
()).
getFailCond
().
stream
();
List
<
QuestCondition
>
allCondMatch
=
Stream
.
concat
(
Stream
.
concat
(
acceptCond
,
failCond
),
finishCond
).
filter
(
p
->
p
.
getType
().
getValue
()
==
req
.
getContentType
()).
toList
();
for
(
QuestCondition
cond
:
allCondMatch
)
{
session
.
getPlayer
().
getQuestManager
().
triggerEvent
(
QuestTrigger
.
getContentTriggerByValue
(
req
.
getContentType
()),
cond
.
getParam
());
}
session
.
send
(
new
PacketAddQuestContentProgressRsp
(
req
.
getContentType
()));
}
...
...
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment