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
67ac0d70
Commit
67ac0d70
authored
Jun 23, 2022
by
Akka
Committed by
Melledy
Jun 23, 2022
Browse files
add region entity
parent
1c6c5813
Changes
8
Show whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/game/entity/EntityRegion.java
0 → 100644
View file @
67ac0d70
package
emu.grasscutter.game.entity
;
import
emu.grasscutter.game.props.EntityIdType
;
import
emu.grasscutter.game.world.Scene
;
import
emu.grasscutter.net.proto.SceneEntityInfoOuterClass
;
import
emu.grasscutter.scripts.data.SceneRegion
;
import
emu.grasscutter.utils.Position
;
import
it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap
;
import
lombok.Getter
;
import
java.util.Set
;
import
java.util.concurrent.ConcurrentHashMap
;
@Getter
public
class
EntityRegion
extends
GameEntity
{
private
final
Position
position
;
private
boolean
hasNewEntities
;
private
final
Set
<
Integer
>
entities
;
// Ids of entities inside this region
private
final
SceneRegion
metaRegion
;
public
EntityRegion
(
Scene
scene
,
SceneRegion
region
)
{
super
(
scene
);
this
.
id
=
getScene
().
getWorld
().
getNextEntityId
(
EntityIdType
.
REGION
);
setGroupId
(
region
.
group
.
id
);
setBlockId
(
region
.
group
.
block_id
);
setConfigId
(
region
.
config_id
);
this
.
position
=
region
.
pos
.
clone
();
this
.
entities
=
ConcurrentHashMap
.
newKeySet
();
this
.
metaRegion
=
region
;
}
public
void
addEntity
(
GameEntity
entity
)
{
if
(
this
.
getEntities
().
contains
(
entity
.
getId
()))
{
return
;
}
this
.
getEntities
().
add
(
entity
.
getId
());
this
.
hasNewEntities
=
true
;
}
public
boolean
hasNewEntities
()
{
return
hasNewEntities
;
}
public
void
resetNewEntities
()
{
hasNewEntities
=
false
;
}
public
void
removeEntity
(
GameEntity
entity
)
{
this
.
getEntities
().
remove
(
entity
.
getId
());
}
@Override
public
Int2FloatOpenHashMap
getFightProperties
()
{
return
null
;
}
@Override
public
Position
getPosition
()
{
return
position
;
}
@Override
public
Position
getRotation
()
{
return
null
;
}
@Override
public
SceneEntityInfoOuterClass
.
SceneEntityInfo
toProto
()
{
/**
* The Region Entity would not be sent to client.
*/
return
null
;
}
}
src/main/java/emu/grasscutter/game/props/EntityIdType.java
View file @
67ac0d70
...
...
@@ -5,6 +5,7 @@ public enum EntityIdType {
MONSTER
(
0x02
),
NPC
(
0x03
),
GADGET
(
0x04
),
REGION
(
0x05
),
WEAPON
(
0x06
),
TEAM
(
0x09
),
MPLEVEL
(
0x0b
);
...
...
src/main/java/emu/grasscutter/game/world/Scene.java
View file @
67ac0d70
...
...
@@ -30,14 +30,15 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import
org.danilopianini.util.SpatialIndex
;
import
java.util.*
;
import
java.util.concurrent.ConcurrentHashMap
;
import
java.util.concurrent.CopyOnWriteArrayList
;
import
java.util.stream.Collectors
;
public
class
Scene
{
private
final
World
world
;
private
final
SceneData
sceneData
;
private
final
List
<
Player
>
players
;
private
final
Int2ObjectMap
<
GameEntity
>
entities
;
private
final
Map
<
Integer
,
GameEntity
>
entities
;
private
final
Set
<
SpawnDataEntry
>
spawnedEntities
;
private
final
Set
<
SpawnDataEntry
>
deadSpawnedEntities
;
private
final
Set
<
SceneBlock
>
loadedBlocks
;
...
...
@@ -57,16 +58,16 @@ public class Scene {
public
Scene
(
World
world
,
SceneData
sceneData
)
{
this
.
world
=
world
;
this
.
sceneData
=
sceneData
;
this
.
players
=
Collections
.
synchronizedList
(
new
ArrayList
<>()
)
;
this
.
entities
=
Int2ObjectMaps
.
synchronize
(
new
Int2ObjectOp
enHashMap
<>()
)
;
this
.
players
=
new
CopyOnWrite
ArrayList
<>();
this
.
entities
=
new
Concurr
en
t
HashMap
<>();
this
.
time
=
8
*
60
;
this
.
climate
=
ClimateType
.
CLIMATE_SUNNY
;
this
.
prevScene
=
3
;
this
.
spawnedEntities
=
new
Hash
Set
<>
();
this
.
deadSpawnedEntities
=
new
Hash
Set
<>
();
this
.
loadedBlocks
=
new
Hash
Set
<>
();
this
.
spawnedEntities
=
ConcurrentHashMap
.
newKey
Set
();
this
.
deadSpawnedEntities
=
ConcurrentHashMap
.
newKey
Set
();
this
.
loadedBlocks
=
ConcurrentHashMap
.
newKey
Set
();
this
.
scriptManager
=
new
SceneScriptManager
(
this
);
}
...
...
@@ -94,7 +95,7 @@ public class Scene {
return
this
.
getPlayers
().
size
();
}
public
Int2ObjectMap
<
GameEntity
>
getEntities
()
{
public
Map
<
Integer
,
GameEntity
>
getEntities
()
{
return
entities
;
}
...
...
@@ -629,15 +630,10 @@ public class Scene {
var
suiteData
=
group
.
getSuiteByIndex
(
suite
);
suiteData
.
sceneTriggers
.
forEach
(
getScriptManager
()::
registerTrigger
);
entities
.
addAll
(
suiteData
.
sceneGadgets
.
stream
()
.
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
))
.
filter
(
Objects:
:
nonNull
)
.
toList
());
entities
.
addAll
(
scriptManager
.
getGadgetsInGroupSuite
(
group
,
suiteData
));
entities
.
addAll
(
scriptManager
.
getMonstersInGroupSuite
(
group
,
suiteData
));
scriptManager
.
registerRegionInGroupSuite
(
group
,
suiteData
);
}
scriptManager
.
meetEntities
(
entities
);
...
...
@@ -660,13 +656,12 @@ public class Scene {
group
.
triggers
.
values
().
forEach
(
getScriptManager
()::
deregisterTrigger
);
}
if
(
group
.
regions
!=
null
){
group
.
regions
.
forEach
(
getScriptManager
()::
deregisterRegion
);
group
.
regions
.
values
().
forEach
(
getScriptManager
()::
deregisterRegion
);
}
}
scriptManager
.
getLoadedGroupSetPerBlock
().
remove
(
block
.
id
);
Grasscutter
.
getLogger
().
info
(
"Scene {} Block {} is unloaded."
,
this
.
getId
(),
block
.
id
);
}
// Gadgets
public
void
onPlayerCreateGadget
(
EntityClientGadget
gadget
)
{
...
...
src/main/java/emu/grasscutter/scripts/SceneScriptManager.java
View file @
67ac0d70
...
...
@@ -6,10 +6,7 @@ import emu.grasscutter.Grasscutter;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.excels.MonsterData
;
import
emu.grasscutter.data.excels.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.entity.*
;
import
emu.grasscutter.game.world.Scene
;
import
emu.grasscutter.net.proto.VisionTypeOuterClass
;
import
emu.grasscutter.scripts.constants.EventType
;
...
...
@@ -17,17 +14,12 @@ import emu.grasscutter.scripts.data.*;
import
emu.grasscutter.scripts.service.ScriptMonsterSpawnService
;
import
emu.grasscutter.scripts.service.ScriptMonsterTideService
;
import
io.netty.util.concurrent.FastThreadLocalThread
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
import
org.luaj.vm2.LuaError
;
import
org.luaj.vm2.LuaValue
;
import
org.luaj.vm2.lib.jse.CoerceJavaToLua
;
import
java.util.*
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.LinkedBlockingDeque
;
import
java.util.concurrent.ThreadPoolExecutor
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.*
;
import
java.util.stream.Collectors
;
public
class
SceneScriptManager
{
...
...
@@ -38,15 +30,15 @@ public class SceneScriptManager {
/**
* current triggers controlled by RefreshGroup
*/
private
final
Int2ObjectOpenHashMap
<
Set
<
SceneTrigger
>>
currentTriggers
;
private
final
Int2ObjectOpenHashMap
<
SceneRegion
>
regions
;
private
Map
<
Integer
,
SceneGroup
>
sceneGroups
;
private
final
Map
<
Integer
,
Set
<
SceneTrigger
>>
currentTriggers
;
private
final
Map
<
Integer
,
EntityRegion
>
regions
;
// <EntityId-Region>
private
final
Map
<
Integer
,
SceneGroup
>
sceneGroups
;
private
ScriptMonsterTideService
scriptMonsterTideService
;
private
ScriptMonsterSpawnService
scriptMonsterSpawnService
;
/**
* blockid - loaded groupSet
*/
private
Int2ObjectMap
<
Set
<
SceneGroup
>>
loadedGroupSetPerBlock
;
private
final
Map
<
Integer
,
Set
<
SceneGroup
>>
loadedGroupSetPerBlock
;
public
static
final
ExecutorService
eventExecutor
;
static
{
eventExecutor
=
new
ThreadPoolExecutor
(
4
,
4
,
...
...
@@ -55,13 +47,13 @@ public class SceneScriptManager {
}
public
SceneScriptManager
(
Scene
scene
)
{
this
.
scene
=
scene
;
this
.
currentTriggers
=
new
Int2ObjectOp
enHashMap
<>();
this
.
currentTriggers
=
new
Concurr
en
t
HashMap
<>();
this
.
regions
=
new
Int2ObjectOp
enHashMap
<>();
this
.
variables
=
new
HashMap
<>();
this
.
sceneGroups
=
new
HashMap
<>();
this
.
regions
=
new
Concurr
en
t
HashMap
<>();
this
.
variables
=
new
Concurrent
HashMap
<>();
this
.
sceneGroups
=
new
Concurrent
HashMap
<>();
this
.
scriptMonsterSpawnService
=
new
ScriptMonsterSpawnService
(
this
);
this
.
loadedGroupSetPerBlock
=
new
Int2ObjectOp
enHashMap
<>();
this
.
loadedGroupSetPerBlock
=
new
Concurr
en
t
HashMap
<>();
// TEMPORARY
if
(
this
.
getScene
().
getId
()
<
10
&&
!
Grasscutter
.
getConfig
().
server
.
game
.
enableScriptInBigWorld
)
{
...
...
@@ -123,19 +115,25 @@ public class SceneScriptManager {
spawnMonstersInGroup
(
group
,
suite
);
spawnGadgetsInGroup
(
group
,
suite
);
}
public
Scene
Region
getRegionById
(
int
id
)
{
public
Entity
Region
getRegionById
(
int
id
)
{
return
regions
.
get
(
id
);
}
public
void
registerRegion
(
Scene
Region
region
)
{
regions
.
put
(
region
.
config_id
,
region
);
public
void
registerRegion
(
Entity
Region
region
)
{
regions
.
put
(
region
.
getId
()
,
region
);
}
public
void
deregisterRegion
(
SceneRegion
region
)
{
regions
.
remove
(
region
.
config_id
);
public
void
registerRegionInGroupSuite
(
SceneGroup
group
,
SceneSuite
suite
){
suite
.
sceneRegions
.
stream
().
map
(
region
->
new
EntityRegion
(
this
.
getScene
(),
region
))
.
forEach
(
this
::
registerRegion
);
}
public
synchronized
void
deregisterRegion
(
SceneRegion
region
)
{
var
instance
=
regions
.
values
().
stream
()
.
filter
(
r
->
r
.
getConfigId
()
==
region
.
config_id
)
.
findFirst
();
instance
.
ifPresent
(
entityRegion
->
regions
.
remove
(
entityRegion
.
getId
()));
}
public
Int2ObjectMap
<
Set
<
SceneGroup
>>
getLoadedGroupSetPerBlock
()
{
public
Map
<
Integer
,
Set
<
SceneGroup
>>
getLoadedGroupSetPerBlock
()
{
return
loadedGroupSetPerBlock
;
}
...
...
@@ -182,10 +180,6 @@ public class SceneScriptManager {
}
this
.
sceneGroups
.
put
(
group
.
id
,
group
);
if
(
group
.
regions
!=
null
){
group
.
regions
.
forEach
(
this
::
registerRegion
);
}
}
public
void
checkRegions
()
{
...
...
@@ -193,37 +187,49 @@ public class SceneScriptManager {
return
;
}
for
(
SceneRegion
region
:
this
.
regions
.
values
())
{
for
(
var
region
:
this
.
regions
.
values
())
{
getScene
().
getEntities
().
values
()
.
stream
()
.
filter
(
e
->
e
.
getEntityType
()
<=
2
&&
region
.
contains
(
e
.
getPosition
()))
.
filter
(
e
->
e
.
getEntityType
()
<=
2
&&
region
.
getMetaRegion
().
contains
(
e
.
getPosition
()))
.
forEach
(
region:
:
addEntity
);
if
(
region
.
hasNewEntities
())
{
// This is not how it works, source_eid should be region entity id, but we dont have an entity for regions yet
callEvent
(
EventType
.
EVENT_ENTER_REGION
,
new
ScriptArgs
(
region
.
config_id
).
setSourceEntityId
(
region
.
config_id
));
callEvent
(
EventType
.
EVENT_ENTER_REGION
,
new
ScriptArgs
(
region
.
getConfigId
()).
setSourceEntityId
(
region
.
getId
()));
region
.
resetNewEntities
();
}
}
}
public
List
<
EntityGadget
>
getGadgetsInGroupSuite
(
SceneGroup
group
,
SceneSuite
suite
){
return
suite
.
sceneGadgets
.
stream
()
.
map
(
g
->
createGadget
(
group
.
id
,
group
.
block_id
,
g
))
.
filter
(
Objects:
:
nonNull
)
.
toList
();
}
public
List
<
EntityMonster
>
getMonstersInGroupSuite
(
SceneGroup
group
,
SceneSuite
suite
){
return
suite
.
sceneMonsters
.
stream
()
.
map
(
mob
->
createMonster
(
group
.
id
,
group
.
block_id
,
mob
))
.
filter
(
Objects:
:
nonNull
)
.
toList
();
}
public
void
addGroupSuite
(
SceneGroup
group
,
SceneSuite
suite
){
spawnMonstersInGroup
(
group
,
suite
);
spawnGadgetsInGroup
(
group
,
suite
);
// we added trigger first
registerTrigger
(
suite
.
sceneTriggers
);
var
toCreate
=
new
ArrayList
<
GameEntity
>();
toCreate
.
addAll
(
getGadgetsInGroupSuite
(
group
,
suite
));
toCreate
.
addAll
(
getMonstersInGroupSuite
(
group
,
suite
));
addEntities
(
toCreate
);
registerRegionInGroupSuite
(
group
,
suite
);
}
public
void
removeGroupSuite
(
SceneGroup
group
,
SceneSuite
suite
){
deregisterTrigger
(
suite
.
sceneTriggers
);
removeMonstersInGroup
(
group
,
suite
);
removeGadgetsInGroup
(
group
,
suite
);
deregisterTrigger
(
suite
.
sceneTriggers
);
}
public
void
spawnGadgetsInGroup
(
SceneGroup
group
,
int
suiteIndex
)
{
spawnGadgetsInGroup
(
group
,
group
.
getSuiteByIndex
(
suiteIndex
));
}
public
void
spawnGadgetsInGroup
(
SceneGroup
group
)
{
spawnGadgetsInGroup
(
group
,
null
);
suite
.
sceneRegions
.
forEach
(
this
::
deregisterRegion
);
}
public
void
spawnGadgetsInGroup
(
SceneGroup
group
,
SceneSuite
suite
)
{
...
...
@@ -240,13 +246,6 @@ public class SceneScriptManager {
this
.
addEntities
(
toCreate
);
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
,
int
suiteIndex
)
{
var
suite
=
group
.
getSuiteByIndex
(
suiteIndex
);
if
(
suite
==
null
){
return
;
}
spawnMonstersInGroup
(
group
,
suite
);
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
,
SceneSuite
suite
)
{
if
(
suite
==
null
||
suite
.
sceneMonsters
.
size
()
<=
0
){
return
;
...
...
@@ -255,11 +254,6 @@ public class SceneScriptManager {
.
map
(
mob
->
createMonster
(
group
.
id
,
group
.
block_id
,
mob
)).
toList
());
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
)
{
this
.
addEntities
(
group
.
monsters
.
values
().
stream
()
.
map
(
mob
->
createMonster
(
group
.
id
,
group
.
block_id
,
mob
)).
toList
());
}
public
void
startMonsterTideInGroup
(
SceneGroup
group
,
Integer
[]
ordersConfigId
,
int
tideCount
,
int
sceneLimit
)
{
this
.
scriptMonsterTideService
=
new
ScriptMonsterTideService
(
this
,
group
,
tideCount
,
sceneLimit
,
ordersConfigId
);
...
...
src/main/java/emu/grasscutter/scripts/ScriptLib.java
View file @
67ac0d70
...
...
@@ -316,13 +316,13 @@ public class ScriptLib {
int
regionId
=
table
.
get
(
"region_eid"
).
toint
();
int
entityType
=
table
.
get
(
"entity_type"
).
toint
();
SceneRegion
region
=
this
.
getSceneScriptManager
().
getRegionById
(
regionId
);
var
region
=
this
.
getSceneScriptManager
().
getRegionById
(
regionId
);
if
(
region
==
null
)
{
return
0
;
}
return
(
int
)
region
.
getEntities
().
intS
tream
().
filter
(
e
->
e
>>
24
==
entityType
).
count
();
return
(
int
)
region
.
getEntities
().
s
tream
().
filter
(
e
->
e
>>
24
==
entityType
).
count
();
}
public
void
PrintContextLog
(
String
msg
)
{
...
...
src/main/java/emu/grasscutter/scripts/data/SceneGroup.java
View file @
67ac0d70
...
...
@@ -34,7 +34,7 @@ public class SceneGroup {
public
Map
<
Integer
,
SceneGadget
>
gadgets
;
// <ConfigId, Gadgets>
public
Map
<
String
,
SceneTrigger
>
triggers
;
public
Map
<
Integer
,
SceneNPC
>
npc
;
// <NpcId, NPC>
public
List
<
SceneRegion
>
regions
;
public
Map
<
Integer
,
SceneRegion
>
regions
;
public
List
<
SceneSuite
>
suites
;
public
List
<
SceneVar
>
variables
;
...
...
@@ -115,7 +115,10 @@ public class SceneGroup {
triggers
.
values
().
forEach
(
t
->
t
.
currentGroup
=
this
);
suites
=
ScriptLoader
.
getSerializer
().
toList
(
SceneSuite
.
class
,
bindings
.
get
(
"suites"
));
regions
=
ScriptLoader
.
getSerializer
().
toList
(
SceneRegion
.
class
,
bindings
.
get
(
"regions"
));
regions
=
ScriptLoader
.
getSerializer
().
toList
(
SceneRegion
.
class
,
bindings
.
get
(
"regions"
)).
stream
()
.
collect
(
Collectors
.
toMap
(
x
->
x
.
config_id
,
y
->
y
));
regions
.
values
().
forEach
(
m
->
m
.
group
=
this
);
init_config
=
ScriptLoader
.
getSerializer
().
toObject
(
SceneInitConfig
.
class
,
bindings
.
get
(
"init_config"
));
// Garbages TODO fix properly later
...
...
@@ -157,6 +160,13 @@ public class SceneGroup {
.
map
(
triggers:
:
get
)
.
toList
()
);
suite
.
sceneRegions
=
new
ArrayList
<>(
suite
.
regions
.
stream
()
.
filter
(
regions:
:
containsKey
)
.
map
(
regions:
:
get
)
.
toList
()
);
}
}
catch
(
ScriptException
e
)
{
...
...
src/main/java/emu/grasscutter/scripts/data/SceneRegion.java
View file @
67ac0d70
package
emu.grasscutter.scripts.data
;
import
emu.grasscutter.game.entity.GameEntity
;
import
emu.grasscutter.scripts.constants.ScriptRegionShape
;
import
emu.grasscutter.utils.Position
;
import
it.unimi.dsi.fastutil.ints.IntOpenHashSet
;
import
it.unimi.dsi.fastutil.ints.IntSet
;
import
lombok.Data
;
import
lombok.Setter
;
import
lombok.ToString
;
@ToString
@Setter
public
class
SceneRegion
{
public
int
config_id
;
public
int
shape
;
public
Position
pos
;
// for CUBIC
public
Position
size
;
// for SPHERE
public
int
radius
;
private
boolean
hasNewEntities
;
private
final
IntSet
entities
;
// Ids of entities inside this region
public
SceneRegion
()
{
this
.
entities
=
new
IntOpenHashSet
();
}
public
IntSet
getEntities
()
{
return
entities
;
}
public
void
addEntity
(
GameEntity
entity
)
{
if
(
this
.
getEntities
().
contains
(
entity
.
getId
()))
{
return
;
}
this
.
getEntities
().
add
(
entity
.
getId
());
this
.
hasNewEntities
=
true
;
}
public
void
removeEntity
(
GameEntity
entity
)
{
this
.
getEntities
().
remove
(
entity
.
getId
());
}
public
boolean
contains
(
Position
p
)
{
public
transient
SceneGroup
group
;
public
boolean
contains
(
Position
position
)
{
switch
(
shape
)
{
case
ScriptRegionShape
.
CUBIC
:
return
(
Math
.
abs
(
pos
.
getX
()
-
p
.
getX
())
<=
size
.
getX
())
&&
(
Math
.
abs
(
pos
.
getZ
()
-
p
.
getZ
())
<=
size
.
getZ
());
return
(
Math
.
abs
(
pos
.
getX
()
-
position
.
getX
())
<=
size
.
getX
())
&&
(
Math
.
abs
(
pos
.
getY
()
-
position
.
getY
())
<=
size
.
getY
())
&&
(
Math
.
abs
(
pos
.
getZ
()
-
position
.
getZ
())
<=
size
.
getZ
());
case
ScriptRegionShape
.
SPHERE
:
return
false
;
var
x
=
Math
.
pow
(
pos
.
getX
()
-
position
.
getX
(),
2
);
var
y
=
Math
.
pow
(
pos
.
getY
()
-
position
.
getY
(),
2
);
var
z
=
Math
.
pow
(
pos
.
getZ
()
-
position
.
getZ
(),
2
);
return
x
+
y
+
z
<=
(
radius
^
2
);
}
return
false
;
}
public
boolean
hasNewEntities
()
{
return
hasNewEntities
;
}
public
void
resetNewEntities
()
{
hasNewEntities
=
false
;
}
}
src/main/java/emu/grasscutter/scripts/data/SceneSuite.java
View file @
67ac0d70
...
...
@@ -11,9 +11,12 @@ public class SceneSuite {
public
List
<
Integer
>
monsters
;
public
List
<
Integer
>
gadgets
;
public
List
<
String
>
triggers
;
public
List
<
Integer
>
regions
;
public
int
rand_weight
;
public
transient
List
<
SceneMonster
>
sceneMonsters
;
public
transient
List
<
SceneGadget
>
sceneGadgets
;
public
transient
List
<
SceneTrigger
>
sceneTriggers
;
public
transient
List
<
SceneRegion
>
sceneRegions
;
}
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