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
1de402bd
Commit
1de402bd
authored
Aug 01, 2022
by
KingRainbow44
Browse files
Merge branch 'development' into more-events
parents
b9b4b6f4
f25fb629
Changes
81
Hide whitespace changes
Inline
Side-by-side
manage_languages.py
View file @
1de402bd
...
...
@@ -23,7 +23,7 @@ def ppprint(data):
class
JsonHelpers
:
@
staticmethod
def
load
(
filename
:
str
)
->
dict
:
with
open
(
filename
,
'r'
)
as
file
:
with
open
(
filename
,
'r'
,
encoding
=
'utf-8'
)
as
file
:
return
json
.
load
(
file
)
@
staticmethod
...
...
@@ -117,7 +117,7 @@ class LanguageManager:
for
file
in
files
:
if
file
.
rpartition
(
'.'
)[
-
1
]
in
SOURCE_EXTENSIONS
:
filename
=
os
.
path
.
join
(
root
,
file
)
with
open
(
filename
,
'r'
)
as
f
:
with
open
(
filename
,
'r'
,
encoding
=
'utf-8'
)
as
f
:
data
=
f
.
read
()
# Loads in entire file at once
for
k
in
self
.
TRANSLATION_KEY
.
findall
(
data
):
used
.
add
(
k
)
...
...
src/main/java/emu/grasscutter/command/CommandHandler.java
View file @
1de402bd
...
...
@@ -28,7 +28,7 @@ public interface CommandHandler {
if
(
player
==
null
)
{
Grasscutter
.
getLogger
().
info
(
event
.
getMessage
());
}
else
{
player
.
dropMessage
(
event
.
getMessage
());
player
.
dropMessage
(
event
.
getMessage
()
.
replace
(
"\n\t"
,
"\n\n"
)
);
}
}
...
...
@@ -44,6 +44,9 @@ public interface CommandHandler {
if
(
alias
.
length
()
<
command
.
length
())
command
=
alias
;
}
if
(
player
!=
null
)
{
command
=
"/"
+
command
;
}
String
target
=
switch
(
annotation
.
targetRequirement
())
{
case
NONE
->
""
;
case
OFFLINE
->
"@<UID> "
;
// TODO: make translation keys for offline and online players
...
...
src/main/java/emu/grasscutter/command/commands/GiveCommand.java
View file @
1de402bd
...
...
@@ -240,7 +240,7 @@ public final class GiveCommand implements CommandHandler {
}
private
static
Avatar
makeAvatar
(
GiveItemParameters
param
)
{
return
makeAvatar
(
param
.
avatarData
,
param
.
lvl
,
Avatar
.
getMinPromoteLevel
(
param
.
lvl
),
0
);
return
makeAvatar
(
param
.
avatarData
,
param
.
lvl
,
Avatar
.
getMinPromoteLevel
(
param
.
lvl
),
param
.
constellation
);
}
private
static
Avatar
makeAvatar
(
AvatarData
avatarData
,
int
level
,
int
promoteLevel
,
int
constellation
)
{
...
...
src/main/java/emu/grasscutter/command/commands/SetConstCommand.java
0 → 100644
View file @
1de402bd
package
emu.grasscutter.command.commands
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.command.Command
;
import
emu.grasscutter.command.CommandHandler
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.excels.AvatarTalentData
;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.entity.EntityAvatar
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.world.Scene
;
import
emu.grasscutter.game.world.World
;
import
emu.grasscutter.server.packet.send.*
;
import
emu.grasscutter.utils.Position
;
import
it.unimi.dsi.fastutil.ints.IntArrayList
;
import
java.util.List
;
import
java.util.Set
;
@Command
(
label
=
"setConst"
,
aliases
=
{
"setconstellation"
},
usage
=
{
"<constellation level>"
},
permission
=
"player.setconstellation"
,
permissionTargeted
=
"player.setconstellation.others"
)
public
final
class
SetConstCommand
implements
CommandHandler
{
@Override
public
void
execute
(
Player
sender
,
Player
targetPlayer
,
List
<
String
>
args
)
{
if
(
args
.
size
()
<
1
)
{
sendUsageMessage
(
sender
);
return
;
}
try
{
int
constLevel
=
Integer
.
parseInt
(
args
.
get
(
0
));
if
(
constLevel
<
0
||
constLevel
>
6
)
{
CommandHandler
.
sendTranslatedMessage
(
sender
,
"commands.setConst.range_error"
);
return
;
}
EntityAvatar
entity
=
targetPlayer
.
getTeamManager
().
getCurrentAvatarEntity
();
if
(
entity
==
null
)
return
;
Avatar
avatar
=
entity
.
getAvatar
();
this
.
setConstellation
(
targetPlayer
,
avatar
,
constLevel
);
CommandHandler
.
sendTranslatedMessage
(
sender
,
"commands.setConst.success"
,
avatar
.
getAvatarData
().
getName
(),
constLevel
);
}
catch
(
NumberFormatException
ignored
)
{
CommandHandler
.
sendTranslatedMessage
(
sender
,
"commands.setConst.level_error"
);
}
}
private
void
setConstellation
(
Player
player
,
Avatar
avatar
,
int
constLevel
)
{
int
currentConstLevel
=
avatar
.
getCoreProudSkillLevel
();
IntArrayList
talentIds
=
new
IntArrayList
(
avatar
.
getSkillDepot
().
getTalents
());
Set
<
Integer
>
talentIdList
=
avatar
.
getTalentIdList
();
talentIdList
.
clear
();
avatar
.
setCoreProudSkillLevel
(
0
);
for
(
int
talent
=
0
;
talent
<
constLevel
;
talent
++)
{
AvatarTalentData
talentData
=
GameData
.
getAvatarTalentDataMap
().
get
(
talentIds
.
getInt
(
talent
));
int
mainCostItemId
=
talentData
.
getMainCostItemId
();
player
.
getInventory
().
addItem
(
mainCostItemId
);
Grasscutter
.
getGameServer
().
getInventorySystem
().
unlockAvatarConstellation
(
player
,
avatar
.
getGuid
());
}
// force player to reload scene when necessary
if
(
constLevel
<
currentConstLevel
)
{
World
world
=
player
.
getWorld
();
Scene
scene
=
player
.
getScene
();
Position
pos
=
player
.
getPosition
();
world
.
transferPlayerToScene
(
player
,
1
,
pos
);
world
.
transferPlayerToScene
(
player
,
scene
.
getId
(),
pos
);
scene
.
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
player
));
}
// ensure that all changes are visible to the player
avatar
.
recalcConstellations
();
avatar
.
recalcStats
(
true
);
avatar
.
save
();
}
}
src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java
View file @
1de402bd
...
...
@@ -6,14 +6,23 @@ import java.util.Map;
import
emu.grasscutter.command.Command
;
import
emu.grasscutter.command.CommandHandler
;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.entity.EntityAvatar
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.props.FightProperty
;
import
emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify
;
@Command
(
label
=
"setStats"
,
aliases
=
{
"stats"
,
"stat"
},
usage
=
{
"<stat> <value>"
},
permission
=
"player.setstats"
,
permissionTargeted
=
"player.setstats.others"
)
@Command
(
label
=
"setStats"
,
aliases
=
{
"stats"
,
"stat"
},
usage
=
{
"[set] <stat> <value>"
,
"(lock|freeze) <stat> [<value>]"
,
// Can lock to current value
"(unlock|unfreeze) <stat>"
},
permission
=
"player.setstats"
,
permissionTargeted
=
"player.setstats.others"
)
public
final
class
SetStatsCommand
implements
CommandHandler
{
static
class
Stat
{
private
static
class
Stat
{
String
name
;
FightProperty
prop
;
...
...
@@ -27,9 +36,21 @@ public final class SetStatsCommand implements CommandHandler {
this
.
prop
=
prop
;
}
}
Map
<
String
,
Stat
>
stats
;
private
static
enum
Action
{
ACTION_SET
(
"commands.generic.set_to"
,
"commands.generic.set_for_to"
),
ACTION_LOCK
(
"commands.setStats.locked_to"
,
"commands.setStats.locked_for_to"
),
ACTION_UNLOCK
(
"commands.setStats.unlocked"
,
"commands.setStats.unlocked_for"
);
public
final
String
messageKeySelf
;
public
final
String
messageKeyOther
;
private
Action
(
String
messageKeySelf
,
String
messageKeyOther
)
{
this
.
messageKeySelf
=
messageKeySelf
;
this
.
messageKeyOther
=
messageKeyOther
;
}
}
private
Map
<
String
,
Stat
>
stats
;
public
SetStatsCommand
()
{
this
.
stats
=
new
HashMap
<>();
for
(
String
key
:
FightProperty
.
getShortNames
())
{
...
...
@@ -62,50 +83,97 @@ public final class SetStatsCommand implements CommandHandler {
this
.
stats
.
put
(
"ephys"
,
this
.
stats
.
get
(
"phys%"
));
}
public
static
float
parsePercent
(
String
input
)
throws
NumberFormatException
{
if
(
input
.
endsWith
(
"%"
))
{
return
Float
.
parseFloat
(
input
.
substring
(
0
,
input
.
length
()-
1
))/
100
f
;
}
else
{
return
Float
.
parseFloat
(
input
);
}
}
@Override
public
void
execute
(
Player
sender
,
Player
targetPlayer
,
List
<
String
>
args
)
{
String
statStr
;
String
statStr
=
null
;
String
valueStr
;
float
value
=
0
f
;
if
(
args
.
size
()
==
2
)
{
statStr
=
args
.
get
(
0
).
toLowerCase
();
valueStr
=
args
.
get
(
1
);
}
else
{
if
(
args
.
size
()
<
2
)
{
sendUsageMessage
(
sender
);
return
;
}
// Get the action and stat
String
arg0
=
args
.
remove
(
0
).
toLowerCase
();
Action
action
=
switch
(
arg0
)
{
default
->
{
statStr
=
arg0
;
yield
Action
.
ACTION_SET
;}
// Implicit set command
case
"set"
->
Action
.
ACTION_SET
;
// Explicit set command
case
"lock"
,
"freeze"
->
Action
.
ACTION_LOCK
;
case
"unlock"
,
"unfreeze"
->
Action
.
ACTION_UNLOCK
;
};
if
(
statStr
==
null
)
{
statStr
=
args
.
remove
(
0
).
toLowerCase
();
}
if
(!
stats
.
containsKey
(
statStr
))
{
sendUsageMessage
(
sender
);
// Invalid stat or action
return
;
}
Stat
stat
=
stats
.
get
(
statStr
);
EntityAvatar
entity
=
targetPlayer
.
getTeamManager
().
getCurrentAvatarEntity
();
Avatar
avatar
=
entity
.
getAvatar
();
float
value
;
// Get the value if the action requires it
try
{
if
(
valueStr
.
endsWith
(
"%"
))
{
value
=
Float
.
parseFloat
(
valueStr
.
substring
(
0
,
valueStr
.
length
()-
1
))/
100
f
;
}
else
{
value
=
Float
.
parseFloat
(
valueStr
);
switch
(
action
)
{
case
ACTION_LOCK:
if
(
args
.
isEmpty
())
{
// Lock to current value
value
=
avatar
.
getFightProperty
(
stat
.
prop
);
break
;
}
// Else fall-through and lock to supplied value
case
ACTION_SET:
value
=
parsePercent
(
args
.
remove
(
0
));
break
;
case
ACTION_UNLOCK:
break
;
}
}
catch
(
NumberFormatException
ignored
)
{
CommandHandler
.
sendTranslatedMessage
(
sender
,
"commands.generic.invalid.statValue"
);
return
;
}
catch
(
IndexOutOfBoundsException
ignored
)
{
sendUsageMessage
(
sender
);
return
;
}
if
(
stats
.
containsKey
(
statStr
))
{
Stat
stat
=
stats
.
get
(
statStr
);
entity
.
setFightProperty
(
stat
.
prop
,
value
);
entity
.
getWorld
().
broadcastPacket
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
stat
.
prop
));
if
(
FightProperty
.
isPercentage
(
stat
.
prop
))
{
valueStr
=
String
.
format
(
"%.1f%%"
,
value
*
100
f
);
}
else
{
valueStr
=
String
.
format
(
"%.0f"
,
value
);
}
if
(
targetPlayer
==
sender
)
{
CommandHandler
.
sendTranslatedMessage
(
sender
,
"commands.generic.set_to"
,
stat
.
name
,
valueStr
);
}
else
{
String
uidStr
=
targetPlayer
.
getAccount
().
getId
();
CommandHandler
.
sendTranslatedMessage
(
sender
,
"commands.generic.set_for_to"
,
stat
.
name
,
uidStr
,
valueStr
);
}
}
else
{
if
(!
args
.
isEmpty
())
{
// Leftover arguments!
sendUsageMessage
(
sender
);
return
;
}
switch
(
action
)
{
case
ACTION_SET:
entity
.
setFightProperty
(
stat
.
prop
,
value
);
entity
.
getWorld
().
broadcastPacket
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
stat
.
prop
));
break
;
case
ACTION_LOCK:
avatar
.
getFightPropOverrides
().
put
(
stat
.
prop
.
getId
(),
value
);
avatar
.
recalcStats
();
break
;
case
ACTION_UNLOCK:
avatar
.
getFightPropOverrides
().
remove
(
stat
.
prop
.
getId
());
avatar
.
recalcStats
();
break
;
}
// Report action
if
(
FightProperty
.
isPercentage
(
stat
.
prop
))
{
valueStr
=
String
.
format
(
"%.1f%%"
,
value
*
100
f
);
}
else
{
valueStr
=
String
.
format
(
"%.0f"
,
value
);
}
if
(
targetPlayer
==
sender
)
{
CommandHandler
.
sendTranslatedMessage
(
sender
,
action
.
messageKeySelf
,
stat
.
name
,
valueStr
);
}
else
{
String
uidStr
=
targetPlayer
.
getAccount
().
getId
();
CommandHandler
.
sendTranslatedMessage
(
sender
,
action
.
messageKeyOther
,
stat
.
name
,
uidStr
,
valueStr
);
}
return
;
}
...
...
src/main/java/emu/grasscutter/config/ConfigContainer.java
View file @
1de402bd
...
...
@@ -5,6 +5,7 @@ import emu.grasscutter.Grasscutter;
import
emu.grasscutter.Grasscutter.ServerDebugMode
;
import
emu.grasscutter.Grasscutter.ServerRunMode
;
import
java.nio.charset.StandardCharsets
;
import
java.util.Set
;
import
java.io.FileReader
;
import
java.lang.reflect.Field
;
...
...
@@ -27,7 +28,7 @@ public class ConfigContainer {
public
static
void
updateConfig
()
{
try
{
// Check if the server is using a legacy config.
JsonObject
configObject
=
Grasscutter
.
getGsonFactory
()
.
fromJson
(
new
FileReader
(
Grasscutter
.
configFile
),
JsonObject
.
class
);
.
fromJson
(
new
FileReader
(
Grasscutter
.
configFile
,
StandardCharsets
.
UTF_8
),
JsonObject
.
class
);
if
(!
configObject
.
has
(
"version"
))
{
Grasscutter
.
getLogger
().
info
(
"Updating legacy .."
);
Grasscutter
.
saveConfig
(
null
);
...
...
@@ -121,7 +122,7 @@ public class ConfigContainer {
public
static
class
HTTP
{
public
String
bindAddress
=
"0.0.0.0"
;
public
int
bindPort
=
443
;
/* This is the address used in URLs. */
public
String
accessAddress
=
"127.0.0.1"
;
/* This is the port used in URLs. */
...
...
@@ -145,7 +146,7 @@ public class ConfigContainer {
public
int
loadEntitiesForPlayerRange
=
100
;
public
boolean
enableScriptInBigWorld
=
false
;
public
boolean
enableConsole
=
true
;
/* Kcp internal work interval (milliseconds) */
public
int
kcpInterval
=
20
;
/* Controls whether packets should be logged in console or not */
...
...
src/main/java/emu/grasscutter/data/GameData.java
View file @
1de402bd
...
...
@@ -8,6 +8,7 @@ import java.util.Map;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.binout.*
;
import
emu.grasscutter.game.quest.QuestEncryptionKey
;
import
emu.grasscutter.utils.Utils
;
import
emu.grasscutter.data.excels.*
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap
;
...
...
@@ -25,12 +26,13 @@ public class GameData {
private
static
final
Map
<
String
,
OpenConfigEntry
>
openConfigEntries
=
new
HashMap
<>();
private
static
final
Map
<
String
,
ScenePointEntry
>
scenePointEntries
=
new
HashMap
<>();
private
static
final
Int2ObjectMap
<
MainQuestData
>
mainQuestData
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
QuestEncryptionKey
>
questsKeys
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
HomeworldDefaultSaveData
>
homeworldDefaultSaveData
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
SceneNpcBornData
>
npcBornData
=
new
Int2ObjectOpenHashMap
<>();
// ExcelConfigs
private
static
final
Int2ObjectMap
<
PlayerLevelData
>
playerLevelDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarData
>
avatarDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarLevelData
>
avatarLevelDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarSkillDepotData
>
avatarSkillDepotDataMap
=
new
Int2ObjectOpenHashMap
<>();
...
...
@@ -40,7 +42,7 @@ public class GameData {
private
static
final
Int2ObjectMap
<
AvatarPromoteData
>
avatarPromoteDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarTalentData
>
avatarTalentDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
ProudSkillData
>
proudSkillDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
ItemData
>
itemDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
ReliquaryLevelData
>
reliquaryLevelDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
ReliquaryAffixData
>
reliquaryAffixDataMap
=
new
Int2ObjectOpenHashMap
<>();
...
...
@@ -57,11 +59,11 @@ public class GameData {
private
static
final
Int2ObjectMap
<
GadgetData
>
gadgetDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
MonsterCurveData
>
monsterCurveDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
MonsterDescribeData
>
monsterDescribeDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarFlycloakData
>
avatarFlycloakDataMap
=
new
Int2ObjectLinkedOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarCostumeData
>
avatarCostumeDataMap
=
new
Int2ObjectLinkedOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
AvatarCostumeData
>
avatarCostumeDataItemIdMap
=
new
Int2ObjectLinkedOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
SceneData
>
sceneDataMap
=
new
Int2ObjectLinkedOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
FetterData
>
fetterDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
CodexQuestData
>
codexQuestDataMap
=
new
Int2ObjectOpenHashMap
<>();
...
...
@@ -76,7 +78,7 @@ public class GameData {
private
static
final
ArrayList
<
CodexReliquaryData
>
codexReliquaryArrayList
=
new
ArrayList
<>();
private
static
final
Int2ObjectMap
<
FetterCharacterCardData
>
fetterCharacterCardDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
RewardData
>
rewardDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
WorldAreaData
>
worldAreaDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
WorldLevelData
>
worldLevelDataMap
=
new
Int2ObjectOpenHashMap
<>();
private
static
final
Int2ObjectMap
<
DailyDungeonData
>
dailyDungeonDataMap
=
new
Int2ObjectOpenHashMap
<>();
...
...
@@ -108,6 +110,8 @@ public class GameData {
@Getter
private
static
final
Int2ObjectMap
<
MusicGameBasicData
>
musicGameBasicDataMap
=
new
Int2ObjectOpenHashMap
<>();
@Getter
private
static
final
Int2ObjectMap
<
PersonalLineData
>
personalLineDataMap
=
new
Int2ObjectOpenHashMap
<>();
@Getter
private
static
final
Int2ObjectMap
<
ChapterData
>
chapterDataMap
=
new
Int2ObjectOpenHashMap
<>();
@Getter
private
static
final
Int2ObjectMap
<
TriggerExcelConfigData
>
triggerExcelConfigDataMap
=
new
Int2ObjectOpenHashMap
<>();
@Getter
private
static
final
Map
<
String
,
ScriptSceneData
>
scriptSceneDataMap
=
new
HashMap
<>();
@Getter
private
static
final
Int2ObjectMap
<
OpenStateData
>
openStateDataMap
=
new
Int2ObjectOpenHashMap
<>();
...
...
@@ -115,29 +119,33 @@ public class GameData {
private
static
Map
<
Integer
,
List
<
Integer
>>
fetters
=
new
HashMap
<>();
private
static
Map
<
Integer
,
List
<
ShopGoodsData
>>
shopGoods
=
new
HashMap
<>();
private
static
final
IntList
scenePointIdList
=
new
IntArrayList
();
@Getter
private
static
final
List
<
OpenStateData
>
openStateList
=
new
ArrayList
<>();
public
static
Int2ObjectMap
<?>
getMapByResourceDef
(
Class
<?>
resourceDefinition
)
{
Int2ObjectMap
<?>
map
=
null
;
try
{
Field
field
=
GameData
.
class
.
getDeclaredField
(
Utils
.
lowerCaseFirstChar
(
resourceDefinition
.
getSimpleName
())
+
"Map"
);
field
.
setAccessible
(
true
);
map
=
(
Int2ObjectMap
<?>)
field
.
get
(
null
);
field
.
setAccessible
(
false
);
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"Error fetching resource map for "
+
resourceDefinition
.
getSimpleName
(),
e
);
}
return
map
;
}
public
static
Int2ObjectMap
<
String
>
getAbilityHashes
()
{
return
abilityHashes
;
}
public
static
Map
<
String
,
AbilityEmbryoEntry
>
getAbilityEmbryoInfo
()
{
return
abilityEmbryos
;
}
...
...
@@ -153,7 +161,7 @@ public class GameData {
public
static
Map
<
String
,
ScenePointEntry
>
getScenePointEntries
()
{
return
scenePointEntries
;
}
// TODO optimize
public
static
ScenePointEntry
getScenePointEntryById
(
int
sceneId
,
int
pointId
)
{
return
getScenePointEntries
().
get
(
sceneId
+
"_"
+
pointId
);
...
...
@@ -163,18 +171,22 @@ public class GameData {
return
mainQuestData
;
}
public
static
Int2ObjectMap
<
QuestEncryptionKey
>
getMainQuestEncryptionMap
()
{
return
questsKeys
;
}
public
static
Int2ObjectMap
<
HomeworldDefaultSaveData
>
getHomeworldDefaultSaveData
()
{
return
homeworldDefaultSaveData
;
}
public
static
Int2ObjectMap
<
SceneNpcBornData
>
getSceneNpcBornData
()
{
return
npcBornData
;
}
public
static
Int2ObjectMap
<
AvatarData
>
getAvatarDataMap
()
{
return
avatarDataMap
;
}
public
static
Int2ObjectMap
<
ItemData
>
getItemDataMap
()
{
return
itemDataMap
;
}
...
...
@@ -182,7 +194,7 @@ public class GameData {
public
static
Int2ObjectMap
<
AvatarSkillDepotData
>
getAvatarSkillDepotDataMap
()
{
return
avatarSkillDepotDataMap
;
}
public
static
Int2ObjectMap
<
AvatarSkillData
>
getAvatarSkillDataMap
()
{
return
avatarSkillDataMap
;
}
...
...
@@ -206,11 +218,11 @@ public class GameData {
public
static
Int2ObjectMap
<
WeaponLevelData
>
getWeaponLevelDataMap
()
{
return
weaponLevelDataMap
;
}
public
static
Int2ObjectMap
<
ReliquaryAffixData
>
getReliquaryAffixDataMap
()
{
return
reliquaryAffixDataMap
;
}
public
static
Int2ObjectMap
<
ReliquaryMainPropData
>
getReliquaryMainPropDataMap
()
{
return
reliquaryMainPropDataMap
;
}
...
...
@@ -222,7 +234,7 @@ public class GameData {
public
static
Int2ObjectMap
<
WeaponCurveData
>
getWeaponCurveDataMap
()
{
return
weaponCurveDataMap
;
}
public
static
Int2ObjectMap
<
AvatarCurveData
>
getAvatarCurveDataMap
()
{
return
avatarCurveDataMap
;
}
...
...
@@ -231,11 +243,11 @@ public class GameData {
ReliquaryLevelData
levelData
=
reliquaryLevelDataMap
.
get
((
rankLevel
<<
8
)
+
level
);
return
levelData
!=
null
?
levelData
.
getExp
()
:
0
;
}
public
static
ReliquaryLevelData
getRelicLevelData
(
int
rankLevel
,
int
level
)
{
return
reliquaryLevelDataMap
.
get
((
rankLevel
<<
8
)
+
level
);
}
public
static
WeaponPromoteData
getWeaponPromoteData
(
int
promoteId
,
int
promoteLevel
)
{
return
weaponPromoteDataMap
.
get
((
promoteId
<<
8
)
+
promoteLevel
);
}
...
...
@@ -251,7 +263,7 @@ public class GameData {
return
0
;
}
}
public
static
AvatarPromoteData
getAvatarPromoteData
(
int
promoteId
,
int
promoteLevel
)
{
return
avatarPromoteDataMap
.
get
((
promoteId
<<
8
)
+
promoteLevel
);
}
...
...
@@ -265,7 +277,7 @@ public class GameData {
AvatarFetterLevelData
levelData
=
avatarFetterLevelDataMap
.
get
(
level
);
return
levelData
!=
null
?
levelData
.
getExp
()
:
0
;
}
public
static
Int2ObjectMap
<
ProudSkillData
>
getProudSkillDataMap
()
{
return
proudSkillDataMap
;
}
...
...
@@ -312,7 +324,7 @@ public class GameData {
public
static
Int2ObjectMap
<
AvatarCostumeData
>
getAvatarCostumeDataMap
()
{
return
avatarCostumeDataMap
;
}
public
static
Int2ObjectMap
<
AvatarCostumeData
>
getAvatarCostumeDataItemIdMap
()
{
return
avatarCostumeDataItemIdMap
;
}
...
...
@@ -353,7 +365,7 @@ public class GameData {
public
static
Int2ObjectMap
<
WorldAreaData
>
getWorldAreaDataMap
()
{
return
worldAreaDataMap
;
}
public
static
Int2ObjectMap
<
WorldLevelData
>
getWorldLevelDataMap
()
{
return
worldLevelDataMap
;
}
...
...
@@ -361,7 +373,7 @@ public class GameData {
public
static
Int2ObjectMap
<
DungeonData
>
getDungeonDataMap
()
{
return
dungeonDataMap
;
}
public
static
Int2ObjectMap
<
DailyDungeonData
>
getDailyDungeonDataMap
()
{
return
dailyDungeonDataMap
;
}
...
...
@@ -397,11 +409,11 @@ public class GameData {
public
static
Int2ObjectMap
<
TowerFloorData
>
getTowerFloorDataMap
(){
return
towerFloorDataMap
;
}
public
static
Int2ObjectMap
<
TowerLevelData
>
getTowerLevelDataMap
(){
return
towerLevelDataMap
;
}
public
static
Int2ObjectMap
<
TowerScheduleData
>
getTowerScheduleDataMap
(){
return
towerScheduleDataMap
;
}
...
...
@@ -413,19 +425,19 @@ public class GameData {
public
static
Int2ObjectMap
<
ForgeData
>
getForgeDataMap
()
{
return
forgeDataMap
;
}
public
static
Int2ObjectMap
<
HomeWorldLevelData
>
getHomeWorldLevelDataMap
()
{
return
homeWorldLevelDataMap
;
}
public
static
Int2ObjectMap
<
FurnitureMakeConfigData
>
getFurnitureMakeConfigDataMap
()
{
return
furnitureMakeConfigDataMap
;
}
public
static
Int2ObjectMap
<
GatherData
>
getGatherDataMap
()
{
return
gatherDataMap
;
}
public
static
Int2ObjectMap
<
InvestigationMonsterData
>
getInvestigationMonsterDataMap
()
{
return
investigationMonsterDataMap
;
}
...
...
src/main/java/emu/grasscutter/data/ResourceLoader.java
View file @
1de402bd
package
emu.grasscutter.data
;
import
java.io.*
;
import
java.lang.reflect.Type
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.util.*
;
import
java.util.Map.Entry
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
emu.grasscutter.data.binout.*
;
import
emu.grasscutter.game.world.SpawnDataEntry
;
import
emu.grasscutter.scripts.SceneIndexManager
;
import
emu.grasscutter.utils.Utils
;
import
lombok.SneakyThrows
;
import
org.reflections.Reflections
;
import
com.google.gson.JsonElement
;
import
com.google.gson.annotations.SerializedName
;
import
com.google.gson.reflect.TypeToken
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.binout.*
;
import
emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData
;
import
emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction
;
import
emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType
;
import
emu.grasscutter.data.common.PointData
;
import
emu.grasscutter.data.common.ScenePointConfig
;
import
emu.grasscutter.game.world.SpawnDataEntry.*
;
import
emu.grasscutter.game.quest.QuestEncryptionKey
;
import
emu.grasscutter.game.world.SpawnDataEntry
;
import
emu.grasscutter.game.world.SpawnDataEntry.GridBlockId
;
import
emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry
;
import
emu.grasscutter.scripts.SceneIndexManager
;
import
emu.grasscutter.utils.Utils
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
lombok.SneakyThrows
;
import
org.reflections.Reflections
;
import
static
emu
.
grasscutter
.
config
.
Configuration
.*;
import
java.io.*
;
import
java.lang.reflect.Type
;
import
java.nio.file.Files
;
import
java.nio.file.Path
;
import
java.util.*
;
import
java.util.Map.Entry
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
static
emu
.
grasscutter
.
config
.
Configuration
.
RESOURCE
;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
public
class
ResourceLoader
{
...
...
@@ -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"
));
}
...
...
@@ -417,13 +418,49 @@ public class ResourceLoader {
GameData
.
getMainQuestDataMap
().
put
(
mainQuest
.
getId
(),
mainQuest
);
}
try
(
Reader
reader
=
new
FileReader
(
new
File
(
RESOURCE
(
"QuestEncryptionKeys.json"
))))
{
List
<
QuestEncryptionKey
>
keys
=
Grasscutter
.
getGsonFactory
().
fromJson
(
reader
,
TypeToken
.
getParameterized
(
List
.
class
,
QuestEncryptionKey
.
class
).
getType
());
Int2ObjectMap
<
QuestEncryptionKey
>
questEncryptionMap
=
GameData
.
getMainQuestEncryptionMap
();
keys
.
forEach
(
key
->
questEncryptionMap
.
put
(
key
.
getMainQuestId
(),
key
));
Grasscutter
.
getLogger
().
debug
(
"Loaded {} quest keys."
,
questEncryptionMap
.
size
());
}
catch
(
FileNotFoundException
ignored
)
{
Grasscutter
.
getLogger
().
error
(
"Unable to load quest keys - ./resources/QuestEncryptionKeys.json not found."
);
}
catch
(
Exception
e
)
{
Grasscutter
.
getLogger
().
error
(
"Unable to load quest keys."
,
e
);
}
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 @
1de402bd
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/binout/ScriptSceneData.java
0 → 100644
View file @
1de402bd
package
emu.grasscutter.data.binout
;
import
com.google.gson.annotations.SerializedName
;
import
emu.grasscutter.scripts.data.SceneGroup
;
import
emu.grasscutter.utils.Position
;
import
lombok.Data
;
import
java.util.List
;
import
java.util.Map
;
@Data
public
class
ScriptSceneData
{
Map
<
String
,
ScriptObject
>
scriptObjectList
;
@Data
public
static
class
ScriptObject
{
//private SceneGroup groups;
@SerializedName
(
"dummy_points"
)
private
Map
<
String
,
List
<
Float
>>
dummyPoints
;
}
}
src/main/java/emu/grasscutter/data/excels/QuestData.java
View file @
1de402bd
...
...
@@ -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/data/excels/TriggerExcelConfigData.java
0 → 100644
View file @
1de402bd
package
emu.grasscutter.data.excels
;
import
emu.grasscutter.data.GameResource
;
import
emu.grasscutter.data.ResourceType
;
import
lombok.Getter
;
@ResourceType
(
name
=
"TriggerExcelConfigData.json"
)
@Getter
public
class
TriggerExcelConfigData
extends
GameResource
{
@Getter
private
int
id
;
private
int
sceneId
;
private
int
groupId
;
private
String
triggerName
;
}
src/main/java/emu/grasscutter/game/avatar/Avatar.java
View file @
1de402bd
...
...
@@ -63,6 +63,7 @@ import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import
it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
import
lombok.Getter
;
@Entity
(
value
=
"avatars"
,
useDiscriminator
=
false
)
public
class
Avatar
{
...
...
@@ -85,6 +86,7 @@ public class Avatar {
@Transient
private
final
Int2ObjectMap
<
GameItem
>
equips
;
@Transient
private
final
Int2FloatOpenHashMap
fightProp
;
@Transient
@Getter
private
final
Int2FloatOpenHashMap
fightPropOverrides
;
@Transient
private
Set
<
String
>
extraAbilityEmbryos
;
private
List
<
Integer
>
fetters
;
...
...
@@ -111,6 +113,7 @@ public class Avatar {
public
Avatar
()
{
this
.
equips
=
new
Int2ObjectOpenHashMap
<>();
this
.
fightProp
=
new
Int2FloatOpenHashMap
();
this
.
fightPropOverrides
=
new
Int2FloatOpenHashMap
();
this
.
extraAbilityEmbryos
=
new
HashSet
<>();
this
.
proudSkillBonusMap
=
new
HashMap
<>();
this
.
fetters
=
new
ArrayList
<>();
// TODO Move to avatar
...
...
@@ -728,6 +731,9 @@ public class Avatar {
(
getFightProperty
(
FightProperty
.
FIGHT_PROP_BASE_DEFENSE
)
*
(
1
f
+
getFightProperty
(
FightProperty
.
FIGHT_PROP_DEFENSE_PERCENT
)))
+
getFightProperty
(
FightProperty
.
FIGHT_PROP_DEFENSE
)
);
// Reapply all overrides
this
.
fightProp
.
putAll
(
this
.
fightPropOverrides
);
// Set current hp
this
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
,
this
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
)
*
hpPercent
);
...
...
src/main/java/emu/grasscutter/game/entity/EntityGadget.java
View file @
1de402bd
...
...
@@ -223,6 +223,10 @@ public class EntityGadget extends EntityBaseGadget {
.
setIsEnableInteract
(
true
)
.
setAuthorityPeerId
(
this
.
getScene
().
getWorld
().
getHostPeerId
());
if
(
this
.
metaGadget
!=
null
)
{
gadgetInfo
.
setDraftId
(
this
.
metaGadget
.
draft_id
);
}
if
(
this
.
getContent
()
!=
null
)
{
this
.
getContent
().
onBuildProto
(
gadgetInfo
);
}
...
...
src/main/java/emu/grasscutter/game/entity/EntityRegion.java
View file @
1de402bd
...
...
@@ -15,6 +15,7 @@ import java.util.concurrent.ConcurrentHashMap;
public
class
EntityRegion
extends
GameEntity
{
private
final
Position
position
;
private
boolean
hasNewEntities
;
private
boolean
entityLeave
;
private
final
Set
<
Integer
>
entities
;
// Ids of entities inside this region
private
final
SceneRegion
metaRegion
;
...
...
@@ -45,10 +46,17 @@ public class EntityRegion extends GameEntity{
hasNewEntities
=
false
;
}
public
void
removeEntity
(
int
entityId
)
{
this
.
getEntities
().
remove
(
entityId
);
this
.
entityLeave
=
true
;
}
public
void
removeEntity
(
GameEntity
entity
)
{
this
.
getEntities
().
remove
(
entity
.
getId
());
this
.
entityLeave
=
true
;
}
public
boolean
entityLeave
()
{
return
this
.
entityLeave
;}
public
void
resetEntityLeave
()
{
this
.
entityLeave
=
false
;}
@Override
public
Int2FloatOpenHashMap
getFightProperties
()
{
return
null
;
...
...
src/main/java/emu/grasscutter/game/player/Player.java
View file @
1de402bd
...
...
@@ -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,6 +1356,11 @@ public class Player {
// Execute daily reset logic if this is a new day.
this
.
doDailyReset
();
// Rewind active quests, and put the player to a rewind position it finds (if any) of an active quest
getQuestManager
().
onLogin
();
// Packets
session
.
send
(
new
PacketPlayerDataNotify
(
this
));
// Player data
session
.
send
(
new
PacketStoreWeightLimitNotify
());
...
...
src/main/java/emu/grasscutter/game/quest/GameMainQuest.java
View file @
1de402bd
...
...
@@ -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,28 @@ public class GameMainQuest {
public
ParentQuest
toProto
()
{
ParentQuest
.
Builder
proto
=
ParentQuest
.
newBuilder
()
.
setParentQuestId
(
getParentQuestId
())
.
setIsFinished
(
isFinished
())
.
setParentQuestState
(
getState
().
getValue
());
.
setIsFinished
(
isFinished
());
for
(
GameQuest
quest
:
this
.
getChildQuests
().
values
())
{
ChildQuest
childQuest
=
ChildQuest
.
newBuilder
()
.
setQuestId
(
quest
.
getQuestId
())
.
setState
(
quest
.
getState
().
getValue
())
.
build
();
proto
.
addChildQuestList
(
childQuest
);
}
proto
.
setParentQuestState
(
getState
().
getValue
())
.
setCutsceneEncryptionKey
(
QuestManager
.
getQuestKey
(
parentQuestId
));
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
);
}
if
(
getQuestVars
()
!=
null
)
{
for
(
int
i
:
getQuestVars
())
{
proto
.
addQuestVar
(
i
);
}
}
return
proto
.
build
();
}
}
src/main/java/emu/grasscutter/game/quest/GameQuest.java
View file @
1de402bd
...
...
@@ -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/QuestEncryptionKey.java
0 → 100644
View file @
1de402bd
package
emu.grasscutter.game.quest
;
import
lombok.AccessLevel
;
import
lombok.Data
;
import
lombok.experimental.FieldDefaults
;
@Data
@FieldDefaults
(
level
=
AccessLevel
.
PRIVATE
)
public
class
QuestEncryptionKey
{
int
mainQuestId
;
long
encryptionKey
;
}
src/main/java/emu/grasscutter/game/quest/QuestManager.java
View file @
1de402bd
package
emu.grasscutter.game.quest
;
import
java.beans.Transient
;
import
java.util.*
;
import
java.util.function.Consumer
;
import
java.util.stream.Collectors
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.binout.MainQuestData
;
...
...
@@ -17,32 +16,134 @@ 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
static
long
getQuestKey
(
int
mainQuestId
){
QuestEncryptionKey
questEncryptionKey
=
GameData
.
getMainQuestEncryptionMap
().
get
(
mainQuestId
);
return
questEncryptionKey
!=
null
?
questEncryptionKey
.
getEncryptionKey
()
:
0L
;
}
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
Int2ObjectMap
<
GameMainQuest
>
getQuests
()
{
return
quests
;
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
));
}
}
}
public
GameMainQuest
getMainQuestById
(
int
mainQuestId
)
{
return
getQuests
().
get
(
mainQuestId
);
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
;
}
/*
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
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 +152,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 +203,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 +232,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 +320,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
();
}
}
Prev
1
2
3
4
5
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