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
d95708ec
Commit
d95708ec
authored
May 25, 2022
by
Akka
Committed by
Melledy
May 24, 2022
Browse files
Support spawn NPC
parent
5a4a7089
Changes
12
Show whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/data/GameData.java
View file @
d95708ec
...
...
@@ -7,12 +7,8 @@ import java.util.List;
import
java.util.Map
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.custom.*
;
import
emu.grasscutter.utils.Utils
;
import
emu.grasscutter.data.custom.AbilityEmbryoEntry
;
import
emu.grasscutter.data.custom.AbilityModifierEntry
;
import
emu.grasscutter.data.custom.OpenConfigEntry
;
import
emu.grasscutter.data.custom.MainQuestData
;
import
emu.grasscutter.data.custom.ScenePointEntry
;
import
emu.grasscutter.data.def.*
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
...
...
@@ -28,6 +24,7 @@ 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
<
SceneNpcBornData
>
npcBornData
=
new
Int2ObjectOpenHashMap
<>();
// ExcelConfigs
private
static
final
Int2ObjectMap
<
PlayerLevelData
>
playerLevelDataMap
=
new
Int2ObjectOpenHashMap
<>();
...
...
@@ -131,7 +128,9 @@ public class GameData {
public
static
Int2ObjectMap
<
MainQuestData
>
getMainQuestDataMap
()
{
return
mainQuestData
;
}
public
static
Int2ObjectMap
<
SceneNpcBornData
>
getSceneNpcBornData
()
{
return
npcBornData
;
}
public
static
Int2ObjectMap
<
AvatarData
>
getAvatarDataMap
()
{
return
avatarDataMap
;
}
...
...
src/main/java/emu/grasscutter/data/ResourceLoader.java
View file @
d95708ec
package
emu.grasscutter.data
;
import
java.io.*
;
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
ch.ethz.globis.phtree.PhTree
;
import
ch.ethz.globis.phtree.v16.PhTree16
;
import
com.google.gson.Gson
;
import
emu.grasscutter.data.custom.*
;
import
emu.grasscutter.utils.Utils
;
import
lombok.SneakyThrows
;
import
org.reflections.Reflections
;
import
com.google.gson.JsonElement
;
...
...
@@ -16,15 +22,9 @@ import com.google.gson.reflect.TypeToken;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.common.PointData
;
import
emu.grasscutter.data.common.ScenePointConfig
;
import
emu.grasscutter.data.custom.AbilityEmbryoEntry
;
import
emu.grasscutter.data.custom.AbilityModifier
;
import
emu.grasscutter.data.custom.AbilityModifier.AbilityConfigData
;
import
emu.grasscutter.data.custom.AbilityModifier.AbilityModifierAction
;
import
emu.grasscutter.data.custom.AbilityModifier.AbilityModifierActionType
;
import
emu.grasscutter.data.custom.AbilityModifierEntry
;
import
emu.grasscutter.data.custom.OpenConfigEntry
;
import
emu.grasscutter.data.custom.MainQuestData
;
import
emu.grasscutter.data.custom.ScenePointEntry
;
import
emu.grasscutter.game.world.SpawnDataEntry.*
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
...
...
@@ -65,6 +65,7 @@ public class ResourceLoader {
loadQuests
();
// Load scene points - must be done AFTER resources are loaded
loadScenePoints
();
loadNpcBornData
();
// Custom - TODO move this somewhere else
try
{
GameData
.
getAvatarSkillDepotDataMap
().
get
(
504
).
setAbilities
(
...
...
@@ -418,6 +419,29 @@ public class ResourceLoader {
Grasscutter
.
getLogger
().
info
(
"Loaded "
+
GameData
.
getMainQuestDataMap
().
size
()
+
" MainQuestDatas."
);
}
@SneakyThrows
private
static
void
loadNpcBornData
(){
var
folder
=
Files
.
list
(
Path
.
of
(
RESOURCE
(
"BinOutput/Scene/SceneNpcBorn"
))).
toList
();
for
(
var
file
:
folder
){
if
(
file
.
toFile
().
isDirectory
()){
continue
;
}
PhTree
<
SceneNpcBornEntry
>
index
=
new
PhTree16
<>(
3
);
var
data
=
Grasscutter
.
getGsonFactory
().
fromJson
(
Files
.
readString
(
file
),
SceneNpcBornData
.
class
);
if
(
data
.
getBornPosList
()
==
null
||
data
.
getBornPosList
().
size
()
==
0
){
continue
;
}
data
.
getBornPosList
().
forEach
(
item
->
index
.
put
(
item
.
getPos
().
toLongArray
(),
item
));
data
.
setIndex
(
index
);
GameData
.
getSceneNpcBornData
().
put
(
data
.
getSceneId
(),
data
);
}
Grasscutter
.
getLogger
().
info
(
"Loaded "
+
GameData
.
getSceneNpcBornData
().
size
()
+
" SceneNpcBornDatas."
);
}
// BinOutput configs
private
static
class
AvatarConfig
{
...
...
src/main/java/emu/grasscutter/data/custom/SceneNpcBornData.java
0 → 100644
View file @
d95708ec
package
emu.grasscutter.data.custom
;
import
ch.ethz.globis.phtree.PhTree
;
import
emu.grasscutter.scripts.data.SceneGroup
;
import
lombok.AccessLevel
;
import
lombok.Data
;
import
lombok.experimental.FieldDefaults
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.concurrent.ConcurrentHashMap
;
@Data
@FieldDefaults
(
level
=
AccessLevel
.
PRIVATE
)
public
class
SceneNpcBornData
{
int
sceneId
;
List
<
SceneNpcBornEntry
>
bornPosList
;
/**
* Spatial Index For NPC
*/
transient
PhTree
<
SceneNpcBornEntry
>
index
;
/**
* npc groups
*/
transient
Map
<
Integer
,
SceneGroup
>
groups
=
new
ConcurrentHashMap
<>();
}
src/main/java/emu/grasscutter/data/custom/SceneNpcBornEntry.java
0 → 100644
View file @
d95708ec
package
emu.grasscutter.data.custom
;
import
emu.grasscutter.utils.Position
;
import
lombok.AccessLevel
;
import
lombok.Data
;
import
lombok.experimental.FieldDefaults
;
import
java.util.List
;
@Data
@FieldDefaults
(
level
=
AccessLevel
.
PRIVATE
)
public
class
SceneNpcBornEntry
{
int
id
;
int
configId
;
Position
pos
;
Position
rot
;
int
groupId
;
List
<
Integer
>
suiteIdList
;
}
src/main/java/emu/grasscutter/game/entity/EntityNPC.java
0 → 100644
View file @
d95708ec
package
emu.grasscutter.game.entity
;
import
emu.grasscutter.game.props.EntityIdType
;
import
emu.grasscutter.game.world.Scene
;
import
emu.grasscutter.net.proto.*
;
import
emu.grasscutter.scripts.data.SceneNPC
;
import
emu.grasscutter.utils.Position
;
import
it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap
;
public
class
EntityNPC
extends
GameEntity
{
private
final
Position
position
;
private
final
Position
rotation
;
private
final
SceneNPC
metaNpc
;
private
final
int
suiteId
;
public
EntityNPC
(
Scene
scene
,
SceneNPC
metaNPC
,
int
blockId
,
int
suiteId
)
{
super
(
scene
);
this
.
id
=
getScene
().
getWorld
().
getNextEntityId
(
EntityIdType
.
NPC
);
setConfigId
(
metaNPC
.
config_id
);
setGroupId
(
metaNPC
.
group
.
id
);
setBlockId
(
blockId
);
this
.
suiteId
=
suiteId
;
this
.
position
=
metaNPC
.
pos
.
clone
();
this
.
rotation
=
metaNPC
.
rot
.
clone
();
this
.
metaNpc
=
metaNPC
;
}
@Override
public
Int2FloatOpenHashMap
getFightProperties
()
{
return
null
;
}
@Override
public
Position
getPosition
()
{
return
position
;
}
@Override
public
Position
getRotation
()
{
return
rotation
;
}
public
int
getSuiteId
()
{
return
suiteId
;
}
@Override
public
SceneEntityInfoOuterClass
.
SceneEntityInfo
toProto
()
{
EntityAuthorityInfoOuterClass
.
EntityAuthorityInfo
authority
=
EntityAuthorityInfoOuterClass
.
EntityAuthorityInfo
.
newBuilder
()
.
setAbilityInfo
(
AbilitySyncStateInfoOuterClass
.
AbilitySyncStateInfo
.
newBuilder
())
.
setRendererChangedInfo
(
EntityRendererChangedInfoOuterClass
.
EntityRendererChangedInfo
.
newBuilder
())
.
setAiInfo
(
SceneEntityAiInfoOuterClass
.
SceneEntityAiInfo
.
newBuilder
()
.
setIsAiOpen
(
true
)
.
setBornPos
(
getPosition
().
toProto
()))
.
setBornPos
(
getPosition
().
toProto
())
.
build
();
SceneEntityInfoOuterClass
.
SceneEntityInfo
.
Builder
entityInfo
=
SceneEntityInfoOuterClass
.
SceneEntityInfo
.
newBuilder
()
.
setEntityId
(
getId
())
.
setEntityType
(
ProtEntityTypeOuterClass
.
ProtEntityType
.
PROT_ENTITY_NPC
)
.
setMotionInfo
(
MotionInfoOuterClass
.
MotionInfo
.
newBuilder
()
.
setPos
(
getPosition
().
toProto
())
.
setRot
(
getRotation
().
toProto
())
.
setSpeed
(
VectorOuterClass
.
Vector
.
newBuilder
()))
.
addAnimatorParaList
(
AnimatorParameterValueInfoPairOuterClass
.
AnimatorParameterValueInfoPair
.
newBuilder
())
.
setEntityClientData
(
EntityClientDataOuterClass
.
EntityClientData
.
newBuilder
())
.
setEntityAuthorityInfo
(
authority
)
.
setLifeState
(
1
);
entityInfo
.
setNpc
(
SceneNpcInfoOuterClass
.
SceneNpcInfo
.
newBuilder
()
.
setNpcId
(
metaNpc
.
npc_id
)
.
setBlockId
(
getBlockId
())
.
build
());
return
entityInfo
.
build
();
}
}
src/main/java/emu/grasscutter/game/world/Scene.java
View file @
d95708ec
...
...
@@ -535,6 +535,9 @@ public class Scene {
.
toList
();
onLoadGroup
(
toLoad
);
}
for
(
Player
player
:
this
.
getPlayers
())
{
getScriptManager
().
meetEntities
(
loadNpcForPlayer
(
player
,
block
));
}
}
}
...
...
@@ -590,7 +593,9 @@ public class Scene {
List
<
SceneGadget
>
garbageGadgets
=
group
.
getGarbageGadgets
();
if
(
garbageGadgets
!=
null
)
{
garbageGadgets
.
forEach
(
g
->
scriptManager
.
createGadget
(
group
.
id
,
group
.
block_id
,
g
));
entities
.
addAll
(
garbageGadgets
.
stream
().
map
(
g
->
scriptManager
.
createGadget
(
group
.
id
,
group
.
block_id
,
g
))
.
filter
(
Objects:
:
nonNull
)
.
toList
());
}
// Load suites
...
...
@@ -605,9 +610,13 @@ public class Scene {
suiteData
.
sceneTriggers
.
forEach
(
getScriptManager
()::
registerTrigger
);
entities
.
addAll
(
suiteData
.
sceneGadgets
.
stream
()
.
map
(
g
->
scriptManager
.
createGadget
(
group
.
id
,
group
.
block_id
,
g
)).
toList
());
.
map
(
g
->
scriptManager
.
createGadget
(
group
.
id
,
group
.
block_id
,
g
))
.
filter
(
Objects:
:
nonNull
)
.
toList
());
entities
.
addAll
(
suiteData
.
sceneMonsters
.
stream
()
.
map
(
mob
->
scriptManager
.
createMonster
(
group
.
id
,
group
.
block_id
,
mob
)).
toList
());
.
map
(
mob
->
scriptManager
.
createMonster
(
group
.
id
,
group
.
block_id
,
mob
))
.
filter
(
Objects:
:
nonNull
)
.
toList
());
}
...
...
@@ -626,7 +635,7 @@ public class Scene {
this
.
broadcastPacket
(
new
PacketSceneEntityDisappearNotify
(
toRemove
,
VisionType
.
VISION_REMOVE
));
}
for
(
SceneGroup
group
:
block
.
groups
)
{
for
(
SceneGroup
group
:
block
.
groups
.
values
()
)
{
if
(
group
.
triggers
!=
null
){
group
.
triggers
.
values
().
forEach
(
getScriptManager
()::
deregisterTrigger
);
}
...
...
@@ -718,4 +727,47 @@ public class Scene {
addEntity
(
entity
);
}
}
public
List
<
EntityNPC
>
loadNpcForPlayer
(
Player
player
,
SceneBlock
block
){
if
(!
block
.
contains
(
player
.
getPos
())){
return
List
.
of
();
}
int
RANGE
=
100
;
var
pos
=
player
.
getPos
();
var
data
=
GameData
.
getSceneNpcBornData
().
get
(
getId
());
if
(
data
==
null
){
return
List
.
of
();
}
var
npcs
=
SceneIndexManager
.
queryNeighbors
(
data
.
getIndex
(),
pos
.
toLongArray
(),
RANGE
);
var
entityNPCS
=
npcs
.
stream
().
map
(
item
->
{
var
group
=
data
.
getGroups
().
get
(
item
.
getGroupId
());
if
(
group
==
null
){
group
=
SceneGroup
.
of
(
item
.
getGroupId
());
data
.
getGroups
().
putIfAbsent
(
item
.
getGroupId
(),
group
);
group
.
load
(
getId
());
}
if
(
group
.
npc
==
null
){
return
null
;
}
var
npc
=
group
.
npc
.
get
(
item
.
getConfigId
());
if
(
npc
==
null
){
return
null
;
}
return
getScriptManager
().
createNPC
(
npc
,
block
.
id
,
item
.
getSuiteIdList
().
get
(
0
));
})
.
filter
(
Objects:
:
nonNull
)
.
filter
(
item
->
getEntities
().
values
().
stream
()
.
filter
(
e
->
e
instanceof
EntityNPC
)
.
noneMatch
(
e
->
e
.
getConfigId
()
==
item
.
getConfigId
()))
.
toList
();
if
(
entityNPCS
.
size
()
>
0
){
broadcastPacket
(
new
PacketGroupSuiteNotify
(
entityNPCS
));
}
return
entityNPCS
;
}
}
src/main/java/emu/grasscutter/scripts/SceneIndexManager.java
View file @
d95708ec
...
...
@@ -4,12 +4,13 @@ import ch.ethz.globis.phtree.PhTree;
import
emu.grasscutter.utils.Position
;
import
java.util.ArrayList
;
import
java.util.Collection
;
import
java.util.List
;
import
java.util.function.Function
;
public
class
SceneIndexManager
{
public
static
<
T
>
void
buildIndex
(
PhTree
<
T
>
tree
,
List
<
T
>
elements
,
Function
<
T
,
long
[]>
extractor
){
public
static
<
T
>
void
buildIndex
(
PhTree
<
T
>
tree
,
Collection
<
T
>
elements
,
Function
<
T
,
long
[]>
extractor
){
elements
.
forEach
(
e
->
tree
.
put
(
extractor
.
apply
(
e
),
e
));
}
public
static
<
T
>
List
<
T
>
queryNeighbors
(
PhTree
<
T
>
tree
,
Position
position
,
int
range
){
...
...
src/main/java/emu/grasscutter/scripts/SceneScriptManager.java
View file @
d95708ec
...
...
@@ -7,6 +7,7 @@ import emu.grasscutter.data.def.MonsterData;
import
emu.grasscutter.data.def.WorldLevelData
;
import
emu.grasscutter.game.entity.EntityGadget
;
import
emu.grasscutter.game.entity.EntityMonster
;
import
emu.grasscutter.game.entity.EntityNPC
;
import
emu.grasscutter.game.entity.GameEntity
;
import
emu.grasscutter.game.world.Scene
;
import
emu.grasscutter.net.proto.VisionTypeOuterClass
;
...
...
@@ -140,15 +141,16 @@ public class SceneScriptManager {
// TODO optimize
public
SceneGroup
getGroupById
(
int
groupId
)
{
for
(
SceneBlock
block
:
this
.
getScene
().
getLoadedBlocks
())
{
for
(
SceneGroup
group
:
block
.
groups
)
{
if
(
group
.
id
==
groupId
)
{
var
group
=
block
.
groups
.
get
(
groupId
);
if
(
group
==
null
){
continue
;
}
if
(!
group
.
isLoaded
()){
getScene
().
onLoadGroup
(
List
.
of
(
group
));
}
return
group
;
}
}
}
return
null
;
}
...
...
@@ -365,7 +367,9 @@ public class SceneScriptManager {
return
entity
;
}
public
EntityNPC
createNPC
(
SceneNPC
npc
,
int
blockId
,
int
suiteId
)
{
return
new
EntityNPC
(
getScene
(),
npc
,
blockId
,
suiteId
);
}
public
EntityMonster
createMonster
(
int
groupId
,
int
blockId
,
SceneMonster
monster
)
{
if
(
monster
==
null
){
return
null
;
...
...
src/main/java/emu/grasscutter/scripts/data/SceneBlock.java
View file @
d95708ec
...
...
@@ -13,6 +13,8 @@ import javax.script.Bindings;
import
javax.script.CompiledScript
;
import
javax.script.ScriptException
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.stream.Collectors
;
import
static
emu
.
grasscutter
.
Configuration
.
SCRIPT
;
...
...
@@ -24,7 +26,7 @@ public class SceneBlock {
public
Position
min
;
public
int
sceneId
;
public
List
<
SceneGroup
>
groups
;
public
Map
<
Integer
,
SceneGroup
>
groups
;
public
PhTree
<
SceneGroup
>
sceneGroupIndex
=
new
PhTree16
<>(
3
);
private
transient
boolean
loaded
;
// Not an actual variable in the scripts either
...
...
@@ -61,9 +63,11 @@ public class SceneBlock {
cs
.
eval
(
bindings
);
// Set groups
groups
=
ScriptLoader
.
getSerializer
().
toList
(
SceneGroup
.
class
,
bindings
.
get
(
"groups"
));
groups
.
forEach
(
g
->
g
.
block_id
=
id
);
SceneIndexManager
.
buildIndex
(
this
.
sceneGroupIndex
,
groups
,
g
->
g
.
pos
.
toLongArray
());
groups
=
ScriptLoader
.
getSerializer
().
toList
(
SceneGroup
.
class
,
bindings
.
get
(
"groups"
)).
stream
()
.
collect
(
Collectors
.
toMap
(
x
->
x
.
id
,
y
->
y
));
groups
.
values
().
forEach
(
g
->
g
.
block_id
=
id
);
SceneIndexManager
.
buildIndex
(
this
.
sceneGroupIndex
,
groups
.
values
(),
g
->
g
.
pos
.
toLongArray
());
}
catch
(
ScriptException
e
)
{
Grasscutter
.
getLogger
().
error
(
"Error loading block "
+
id
+
" in scene "
+
sceneId
,
e
);
}
...
...
src/main/java/emu/grasscutter/scripts/data/SceneGroup.java
View file @
d95708ec
...
...
@@ -32,7 +32,7 @@ public class SceneGroup {
public
Map
<
Integer
,
SceneMonster
>
monsters
;
// <ConfigId, Monster>
public
Map
<
Integer
,
SceneGadget
>
gadgets
;
// <ConfigId, Gadgets>
public
Map
<
String
,
SceneTrigger
>
triggers
;
public
Map
<
Integer
,
SceneNPC
>
npc
;
// <NpcId, NPC>
public
List
<
SceneRegion
>
regions
;
public
List
<
SceneSuite
>
suites
;
public
List
<
SceneVar
>
variables
;
...
...
@@ -44,6 +44,11 @@ public class SceneGroup {
private
transient
boolean
loaded
;
// Not an actual variable in the scripts either
private
transient
CompiledScript
script
;
private
transient
Bindings
bindings
;
public
static
SceneGroup
of
(
int
groupId
)
{
var
group
=
new
SceneGroup
();
group
.
id
=
groupId
;
return
group
;
}
public
boolean
isLoaded
()
{
return
loaded
;
...
...
@@ -124,6 +129,10 @@ public class SceneGroup {
// Add variables to suite
variables
=
ScriptLoader
.
getSerializer
().
toList
(
SceneVar
.
class
,
bindings
.
get
(
"variables"
));
// NPC in groups
npc
=
ScriptLoader
.
getSerializer
().
toList
(
SceneNPC
.
class
,
bindings
.
get
(
"npcs"
)).
stream
()
.
collect
(
Collectors
.
toMap
(
x
->
x
.
npc_id
,
y
->
y
));
npc
.
values
().
forEach
(
n
->
n
.
group
=
this
);
// Add monsters and gadgets to suite
for
(
SceneSuite
suite
:
suites
)
{
...
...
src/main/java/emu/grasscutter/scripts/data/SceneNPC.java
0 → 100644
View file @
d95708ec
package
emu.grasscutter.scripts.data
;
import
lombok.Setter
;
import
lombok.ToString
;
@ToString
@Setter
public
class
SceneNPC
extends
SceneObject
{
public
int
npc_id
;
}
\ No newline at end of file
src/main/java/emu/grasscutter/server/packet/send/PacketGroupSuiteNotify.java
0 → 100644
View file @
d95708ec
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.game.entity.EntityNPC
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.GroupSuiteNotifyOuterClass
;
import
java.util.List
;
public
class
PacketGroupSuiteNotify
extends
BasePacket
{
/**
* control which npc suite is loaded
*/
public
PacketGroupSuiteNotify
(
List
<
EntityNPC
>
list
)
{
super
(
PacketOpcodes
.
GroupSuiteNotify
);
var
proto
=
GroupSuiteNotifyOuterClass
.
GroupSuiteNotify
.
newBuilder
();
list
.
forEach
(
item
->
proto
.
putGroupMap
(
item
.
getGroupId
(),
item
.
getSuiteId
()));
this
.
setData
(
proto
);
}
}
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