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
c2a7d607
Commit
c2a7d607
authored
May 10, 2022
by
Akka
Committed by
GitHub
May 10, 2022
Browse files
Merge pull request #9 from Grasscutters/development
Development
parents
0bfa1fa0
9a7d78e0
Changes
43
Hide whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java
View file @
c2a7d607
...
...
@@ -10,7 +10,7 @@ import java.util.List;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
@Command
(
label
=
"setworldlevel"
,
usage
=
"setworldlevel <level>"
,
aliases
=
{
"setworldlvl"
},
permission
=
"player.setworldlevel"
,
description
=
"commands.setWorldLevel.description"
)
aliases
=
{
"setworldlvl"
},
permission
=
"player.setworldlevel"
,
permissionTargeted
=
"player.setworldlevel.others"
,
description
=
"commands.setWorldLevel.description"
)
public
final
class
SetWorldLevelCommand
implements
CommandHandler
{
@Override
...
...
src/main/java/emu/grasscutter/command/commands/SpawnCommand.java
View file @
c2a7d607
...
...
@@ -22,7 +22,7 @@ import java.util.Random;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
@Command
(
label
=
"spawn"
,
usage
=
"spawn <entityId> [amount] [level(monster only)]"
,
permission
=
"server.spawn"
,
description
=
"commands.spawn.description"
)
@Command
(
label
=
"spawn"
,
usage
=
"spawn <entityId> [amount] [level(monster only)]"
,
permission
=
"server.spawn"
,
permissionTargeted
=
"server.spawn.others"
,
description
=
"commands.spawn.description"
)
public
final
class
SpawnCommand
implements
CommandHandler
{
@Override
...
...
@@ -46,13 +46,13 @@ public final class SpawnCommand implements CommandHandler {
try
{
amount
=
Integer
.
parseInt
(
args
.
get
(
1
));
}
catch
(
NumberFormatException
ignored
)
{
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.generic.
error
.amount"
));
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.generic.
invalid
.amount"
));
}
// Fallthrough
case
1
:
try
{
id
=
Integer
.
parseInt
(
args
.
get
(
0
));
}
catch
(
NumberFormatException
ignored
)
{
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.generic.
error
.entityId"
));
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.generic.
invalid
.entityId"
));
}
break
;
default
:
...
...
@@ -64,7 +64,7 @@ public final class SpawnCommand implements CommandHandler {
GadgetData
gadgetData
=
GameData
.
getGadgetDataMap
().
get
(
id
);
ItemData
itemData
=
GameData
.
getItemDataMap
().
get
(
id
);
if
(
monsterData
==
null
&&
gadgetData
==
null
&&
itemData
==
null
)
{
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.generic.
error
.entityId"
));
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.generic.
invalid
.entityId"
));
return
;
}
Scene
scene
=
targetPlayer
.
getScene
();
...
...
src/main/java/emu/grasscutter/command/commands/TalentCommand.java
View file @
c2a7d607
...
...
@@ -14,7 +14,7 @@ import java.util.List;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
@Command
(
label
=
"talent"
,
usage
=
"talent <talentID> <value>"
,
permission
=
"player.settalent"
,
description
=
"commands.talent.description"
)
@Command
(
label
=
"talent"
,
usage
=
"talent <talentID> <value>"
,
permission
=
"player.settalent"
,
permissionTargeted
=
"player.settalent.others"
,
description
=
"commands.talent.description"
)
public
final
class
TalentCommand
implements
CommandHandler
{
private
void
setTalentLevel
(
Player
sender
,
Player
player
,
Avatar
avatar
,
int
talentId
,
int
talentLevel
)
{
int
oldLevel
=
avatar
.
getSkillLevelMap
().
get
(
talentId
);
...
...
@@ -24,7 +24,7 @@ public final class TalentCommand implements CommandHandler {
}
// Upgrade skill
avatar
.
getSkillLevelMap
().
put
(
talent
Level
,
talentLevel
);
avatar
.
getSkillLevelMap
().
put
(
talent
Id
,
talentLevel
);
avatar
.
save
();
// Packet
...
...
src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java
View file @
c2a7d607
...
...
@@ -10,7 +10,7 @@ import java.util.List;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
@Command
(
label
=
"tpall"
,
usage
=
"tpall"
,
permission
=
"player.tpall"
,
description
=
"commands.teleportAll.description"
)
@Command
(
label
=
"tpall"
,
usage
=
"tpall"
,
permission
=
"player.tpall"
,
permissionTargeted
=
"player.tpall.others"
,
description
=
"commands.teleportAll.description"
)
public
final
class
TeleportAllCommand
implements
CommandHandler
{
@Override
...
...
src/main/java/emu/grasscutter/command/commands/TeleportCommand.java
View file @
c2a7d607
...
...
@@ -10,7 +10,7 @@ import java.util.List;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
@Command
(
label
=
"teleport"
,
usage
=
"teleport <x> <y> <z> [scene id]"
,
aliases
=
{
"tp"
},
permission
=
"player.teleport"
,
description
=
"commands.teleport.description"
)
@Command
(
label
=
"teleport"
,
usage
=
"teleport <x> <y> <z> [scene id]"
,
aliases
=
{
"tp"
},
permission
=
"player.teleport"
,
permissionTargeted
=
"player.teleport.others"
,
description
=
"commands.teleport.description"
)
public
final
class
TeleportCommand
implements
CommandHandler
{
private
float
parseRelative
(
String
input
,
Float
current
)
{
// TODO: Maybe this will be useful elsewhere later
...
...
src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java
View file @
c2a7d607
...
...
@@ -10,7 +10,7 @@ import java.util.List;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
@Command
(
label
=
"unlocktower"
,
usage
=
"unlocktower"
,
aliases
=
{
"ut"
},
description
=
"
Unlock all levels of tower
"
,
permission
=
"player.tower"
)
description
=
"
commands.unlocktower.description
"
,
permission
=
"player.tower"
)
public
class
UnlockTowerCommand
implements
CommandHandler
{
@Override
...
...
@@ -21,7 +21,7 @@ public class UnlockTowerCommand implements CommandHandler {
unlockFloor
(
sender
,
sender
.
getServer
().
getTowerScheduleManager
()
.
getScheduleFloors
());
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.
tower.unlock_done
"
));
CommandHandler
.
sendMessage
(
sender
,
translate
(
"commands.
unlocktower.success
"
));
}
public
void
unlockFloor
(
Player
player
,
List
<
Integer
>
floors
){
...
...
src/main/java/emu/grasscutter/command/commands/WeatherCommand.java
View file @
c2a7d607
...
...
@@ -11,7 +11,7 @@ import java.util.List;
import
static
emu
.
grasscutter
.
utils
.
Language
.
translate
;
@Command
(
label
=
"weather"
,
usage
=
"weather <weatherId> [climateId]"
,
aliases
=
{
"w"
},
permission
=
"player.weather"
,
description
=
"commands.weather.description"
)
@Command
(
label
=
"weather"
,
usage
=
"weather <weatherId> [climateId]"
,
aliases
=
{
"w"
},
permission
=
"player.weather"
,
permissionTargeted
=
"player.weather.others"
,
description
=
"commands.weather.description"
)
public
final
class
WeatherCommand
implements
CommandHandler
{
@Override
...
...
src/main/java/emu/grasscutter/game/Account.java
View file @
c2a7d607
package
emu.grasscutter.game
;
import
dev.morphia.annotations.*
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.utils.Crypto
;
import
emu.grasscutter.utils.Utils
;
...
...
@@ -107,11 +108,41 @@ public class Account {
this
.
permissions
.
add
(
permission
);
return
true
;
}
public
static
boolean
permissionMatchesWildcard
(
String
wildcard
,
String
[]
permissionParts
)
{
String
[]
wildcardParts
=
wildcard
.
split
(
"\\."
);
if
(
permissionParts
.
length
<
wildcardParts
.
length
)
{
// A longer wildcard can never match a shorter permission
return
false
;
}
for
(
int
i
=
0
;
i
<
wildcardParts
.
length
;
i
++)
{
switch
(
wildcardParts
[
i
])
{
case
"**"
:
// Recursing match
return
true
;
case
"*"
:
// Match only one layer
if
(
i
>=
(
permissionParts
.
length
-
1
))
{
return
true
;
}
break
;
default
:
// This layer isn't a wildcard, it needs to match exactly
if
(!
wildcardParts
[
i
].
equals
(
permissionParts
[
i
]))
{
return
false
;
}
}
}
// At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **).
return
(
wildcardParts
.
length
==
permissionParts
.
length
);
}
public
boolean
hasPermission
(
String
permission
)
{
return
this
.
permissions
.
contains
(
permission
)
||
this
.
permissions
.
contains
(
"*"
)
||
(
this
.
permissions
.
contains
(
"player"
)
||
this
.
permissions
.
contains
(
"player.*"
))
&&
permission
.
startsWith
(
"player."
)
||
(
this
.
permissions
.
contains
(
"server"
)
||
this
.
permissions
.
contains
(
"server.*"
))
&&
permission
.
startsWith
(
"server."
);
if
(
this
.
permissions
.
contains
(
permission
)
||
this
.
permissions
.
contains
(
"*"
))
{
return
true
;
}
String
[]
permissionParts
=
permission
.
split
(
"\\."
);
for
(
String
p
:
this
.
permissions
)
{
if
(
permissionMatchesWildcard
(
p
,
permissionParts
))
{
return
true
;
}
}
return
false
;
}
public
boolean
removePermission
(
String
permission
)
{
...
...
src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java
View file @
c2a7d607
...
...
@@ -9,6 +9,10 @@ public class TowerDungeonSettleListener implements DungeonSettleListener {
@Override
public
void
onDungeonSettle
(
Scene
scene
)
{
if
(
scene
.
getScriptManager
().
getVariables
().
containsKey
(
"stage"
)
&&
scene
.
getScriptManager
().
getVariables
().
get
(
"stage"
)
==
1
){
return
;
}
scene
.
setAutoCloseTime
(
Utils
.
getCurrentSeconds
()
+
1000
);
var
towerManager
=
scene
.
getPlayers
().
get
(
0
).
getTowerManager
();
...
...
src/main/java/emu/grasscutter/game/entity/EntityMonster.java
View file @
c2a7d607
...
...
@@ -117,7 +117,9 @@ public class EntityMonster extends GameEntity {
this
.
getScene
().
getDeadSpawnedEntities
().
add
(
getSpawnEntry
());
}
if
(
getScene
().
getScriptManager
().
isInit
()
&&
this
.
getGroupId
()
>
0
)
{
getScene
().
getScriptManager
().
onMonsterDie
();
if
(
getScene
().
getScriptManager
().
getScriptMonsterSpawnService
()
!=
null
){
getScene
().
getScriptManager
().
getScriptMonsterSpawnService
().
onMonsterDead
(
this
);
}
getScene
().
getScriptManager
().
callEvent
(
EventType
.
EVENT_ANY_MONSTER_DIE
,
null
);
}
if
(
getScene
().
getChallenge
()
!=
null
&&
getScene
().
getChallenge
().
getGroup
().
id
==
this
.
getGroupId
())
{
...
...
src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java
View file @
c2a7d607
...
...
@@ -190,14 +190,17 @@ public class StaminaManager {
// Returns new stamina and sends PlayerPropNotify
public
int
setStamina
(
GameSession
session
,
String
reason
,
int
newStamina
)
{
// set stamina
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
,
newStamina
);
session
.
send
(
new
PacketPlayerPropNotify
(
player
,
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
));
// notify updated
for
(
Map
.
Entry
<
String
,
AfterUpdateStaminaListener
>
listener
:
afterUpdateStaminaListeners
.
entrySet
())
{
listener
.
getValue
().
onAfterUpdateStamina
(
reason
,
newStamina
);
if
(
Grasscutter
.
getConfig
().
OpenStamina
)
{
// set stamina
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
,
newStamina
);
session
.
send
(
new
PacketPlayerPropNotify
(
player
,
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
));
// notify updated
for
(
Map
.
Entry
<
String
,
AfterUpdateStaminaListener
>
listener
:
afterUpdateStaminaListeners
.
entrySet
())
{
listener
.
getValue
().
onAfterUpdateStamina
(
reason
,
newStamina
);
}
return
newStamina
;
}
return
newStamina
;
return
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
)
;
}
// Kills avatar, removes entity and sends notification.
...
...
@@ -243,15 +246,16 @@ public class StaminaManager {
cachedEntity
=
entity
;
MotionInfo
motionInfo
=
moveInfo
.
getMotionInfo
();
MotionState
motionState
=
motionInfo
.
getState
();
boolean
isReliable
=
moveInfo
.
getIsReliable
();
Grasscutter
.
getLogger
().
trace
(
""
+
motionState
+
"\t"
+
(
isReliable
?
"reliable"
:
""
));
if
(
isReliable
)
{
currentState
=
motionState
;
Vector
posVector
=
motionInfo
.
getPos
();
Position
newPos
=
new
Position
(
posVector
.
getX
(),
posVector
.
getY
(),
posVector
.
getZ
());
if
(
newPos
.
getX
()
!=
0
&&
newPos
.
getY
()
!=
0
&&
newPos
.
getZ
()
!=
0
)
{
currentCoordinates
=
newPos
;
}
int
notifyEntityId
=
entity
.
getId
();
int
currentAvatarEntityId
=
session
.
getPlayer
().
getTeamManager
().
getCurrentAvatarEntity
().
getId
();
if
(
notifyEntityId
!=
currentAvatarEntityId
)
{
return
;
}
currentState
=
motionState
;
Vector
posVector
=
motionInfo
.
getPos
();
Position
newPos
=
new
Position
(
posVector
.
getX
(),
posVector
.
getY
(),
posVector
.
getZ
());
if
(
newPos
.
getX
()
!=
0
&&
newPos
.
getY
()
!=
0
&&
newPos
.
getZ
()
!=
0
)
{
currentCoordinates
=
newPos
;
}
startSustainedStaminaHandler
();
handleImmediateStamina
(
session
,
motionInfo
,
motionState
,
entity
);
...
...
@@ -287,50 +291,48 @@ public class StaminaManager {
private
class
SustainedStaminaHandler
extends
TimerTask
{
public
void
run
()
{
if
(
Grasscutter
.
getConfig
().
OpenStamina
)
{
boolean
moving
=
isPlayerMoving
();
int
currentStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
int
maxStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
if
(
moving
||
(
currentStamina
<
maxStamina
))
{
Grasscutter
.
getLogger
().
trace
(
"Player moving: "
+
moving
+
", stamina full: "
+
(
currentStamina
>=
maxStamina
)
+
", recalculate stamina"
);
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
None
);
if
(
MotionStatesCategorized
.
get
(
"CLIMB"
).
contains
(
currentState
))
{
consumption
=
getClimbSustainedConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"SWIM"
).
contains
((
currentState
)))
{
consumption
=
getSwimSustainedConsumptions
();
}
else
if
(
MotionStatesCategorized
.
get
(
"RUN"
).
contains
(
currentState
))
{
consumption
=
getRunWalkDashSustainedConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"FLY"
).
contains
(
currentState
))
{
consumption
=
getFlySustainedConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"STANDBY"
).
contains
(
currentState
))
{
consumption
=
getStandSustainedConsumption
();
}
boolean
moving
=
isPlayerMoving
();
int
currentStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
int
maxStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
if
(
moving
||
(
currentStamina
<
maxStamina
))
{
Grasscutter
.
getLogger
().
trace
(
"Player moving: "
+
moving
+
", stamina full: "
+
(
currentStamina
>=
maxStamina
)
+
", recalculate stamina"
);
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
None
);
if
(
MotionStatesCategorized
.
get
(
"CLIMB"
).
contains
(
currentState
))
{
consumption
=
getClimbSustainedConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"SWIM"
).
contains
((
currentState
)))
{
consumption
=
getSwimSustainedConsumptions
();
}
else
if
(
MotionStatesCategorized
.
get
(
"RUN"
).
contains
(
currentState
))
{
consumption
=
getRunWalkDashSustainedConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"FLY"
).
contains
(
currentState
))
{
consumption
=
getFlySustainedConsumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"STANDBY"
).
contains
(
currentState
))
{
consumption
=
getStandSustainedConsumption
();
}
/*
TODO: Reductions that apply to all motion types:
Elemental Resonance
Wind: -15%
Skills
Diona E: -10% while shield lasts
Barbara E: -12% while lasts
*/
if
(
cachedSession
!=
null
)
{
if
(
consumption
.
amount
<
0
)
{
staminaRecoverDelay
=
0
;
}
if
(
consumption
.
amount
>
0
&&
consumption
.
consumptionType
!=
ConsumptionType
.
POWERED_FLY
)
{
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
if
(
staminaRecoverDelay
<
10
)
{
// For others recover after 2 seconds (10 ticks) - as official server does.
staminaRecoverDelay
++;
consumption
.
amount
=
0
;
Grasscutter
.
getLogger
().
trace
(
"[StaminaManager] Delaying recovery: "
+
staminaRecoverDelay
);
}
/*
TODO: Reductions that apply to all motion types:
Elemental Resonance
Wind: -15%
Skills
Diona E: -10% while shield lasts
Barbara E: -12% while lasts
*/
if
(
cachedSession
!=
null
)
{
if
(
consumption
.
amount
<
0
)
{
staminaRecoverDelay
=
0
;
}
if
(
consumption
.
amount
>
0
&&
consumption
.
consumptionType
!=
ConsumptionType
.
POWERED_FLY
)
{
// For POWERED_FLY recover immediately - things like Amber's gliding exam may require this.
if
(
staminaRecoverDelay
<
10
)
{
// For others recover after 2 seconds (10 ticks) - as official server does.
staminaRecoverDelay
++;
consumption
.
amount
=
0
;
Grasscutter
.
getLogger
().
trace
(
"[StaminaManager] Delaying recovery: "
+
staminaRecoverDelay
);
}
updateStaminaRelative
(
cachedSession
,
consumption
);
}
updateStaminaRelative
(
cachedSession
,
consumption
);
}
}
previousState
=
currentState
;
...
...
src/main/java/emu/grasscutter/game/tower/TowerManager.java
View file @
c2a7d607
...
...
@@ -7,10 +7,7 @@ import emu.grasscutter.data.def.TowerLevelData;
import
emu.grasscutter.game.dungeons.DungeonSettleListener
;
import
emu.grasscutter.game.dungeons.TowerDungeonSettleListener
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.server.packet.send.PacketCanUseSkillNotify
;
import
emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify
;
import
emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp
;
import
emu.grasscutter.server.packet.send.PacketTowerLevelStarCondNotify
;
import
emu.grasscutter.server.packet.send.*
;
import
java.util.HashMap
;
import
java.util.List
;
...
...
@@ -152,4 +149,10 @@ public class TowerManager {
return
recordMap
.
get
(
player
.
getServer
().
getTowerScheduleManager
().
getLastEntranceFloor
())
.
getStarCount
()
>=
6
;
}
public
void
mirrorTeamSetUp
(
int
teamId
)
{
// use team user choose
player
.
getTeamManager
().
useTemporaryTeam
(
teamId
);
player
.
sendPacket
(
new
PacketTowerMiddleLevelChangeTeamNotify
());
}
}
src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java
View file @
c2a7d607
...
...
@@ -49,6 +49,7 @@ public class TowerScheduleManager {
public
int
getNextFloorId
(
int
floorId
){
var
entranceFloors
=
getCurrentTowerScheduleData
().
getEntranceFloorId
();
var
scheduleFloors
=
getScheduleFloors
();
var
nextId
=
0
;
// find in entrance floors first
for
(
int
i
=
0
;
i
<
entranceFloors
.
size
()-
1
;
i
++){
...
...
@@ -56,10 +57,12 @@ public class TowerScheduleManager {
nextId
=
entranceFloors
.
get
(
i
+
1
);
}
}
if
(
floorId
==
entranceFloors
.
get
(
entranceFloors
.
size
()-
1
)){
nextId
=
scheduleFloors
.
get
(
0
);
}
if
(
nextId
!=
0
){
return
nextId
;
}
var
scheduleFloors
=
getScheduleFloors
();
// find in schedule floors
for
(
int
i
=
0
;
i
<
scheduleFloors
.
size
()-
1
;
i
++){
if
(
floorId
==
scheduleFloors
.
get
(
i
)){
...
...
src/main/java/emu/grasscutter/scripts/SceneScriptManager.java
View file @
c2a7d607
package
emu.grasscutter.scripts
;
import
java.util.*
;
import
java.util.concurrent.ConcurrentLinkedQueue
;
import
java.util.concurrent.atomic.AtomicInteger
;
import
java.util.stream.Collectors
;
import
javax.script.Bindings
;
import
javax.script.CompiledScript
;
import
javax.script.ScriptException
;
import
emu.grasscutter.scripts.service.ScriptMonsterSpawnService
;
import
emu.grasscutter.scripts.service.ScriptMonsterTideService
;
import
org.luaj.vm2.LuaError
;
import
org.luaj.vm2.LuaValue
;
import
org.luaj.vm2.lib.jse.CoerceJavaToLua
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
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.world.Scene
;
import
emu.grasscutter.scripts.constants.EventType
;
import
emu.grasscutter.scripts.data.SceneBlock
;
...
...
@@ -39,28 +36,36 @@ public class SceneScriptManager {
private
final
ScriptLib
scriptLib
;
private
final
LuaValue
scriptLibLua
;
private
final
Map
<
String
,
Integer
>
variables
;
private
Bindings
bindings
;
private
SceneConfig
config
;
private
List
<
SceneBlock
>
blocks
;
private
boolean
isInit
;
private
final
Int2ObjectOpenHashMap
<
Set
<
SceneTrigger
>>
triggers
;
/**
* SceneTrigger Set
*/
private
final
Map
<
String
,
SceneTrigger
>
triggers
;
/**
* current triggers controlled by RefreshGroup
*/
private
final
Int2ObjectOpenHashMap
<
Set
<
SceneTrigger
>>
currentTriggers
;
private
final
Int2ObjectOpenHashMap
<
SceneRegion
>
regions
;
private
Map
<
Integer
,
SceneGroup
>
sceneGroups
;
private
SceneGroup
currentGroup
;
private
AtomicInteger
monsterAlive
;
private
AtomicInteger
monsterTideCount
;
private
int
monsterSceneLimit
;
private
ConcurrentLinkedQueue
<
Integer
>
monsterOrders
;
private
ScriptMonsterTideService
scriptMonsterTideService
;
private
ScriptMonsterSpawnService
scriptMonsterSpawnService
;
public
SceneScriptManager
(
Scene
scene
)
{
this
.
scene
=
scene
;
this
.
scriptLib
=
new
ScriptLib
(
this
);
this
.
scriptLibLua
=
CoerceJavaToLua
.
coerce
(
this
.
scriptLib
);
this
.
triggers
=
new
Int2ObjectOpenHashMap
<>();
this
.
triggers
=
new
HashMap
<>();
this
.
currentTriggers
=
new
Int2ObjectOpenHashMap
<>();
this
.
regions
=
new
Int2ObjectOpenHashMap
<>();
this
.
variables
=
new
HashMap
<>();
this
.
sceneGroups
=
new
HashMap
<>();
this
.
scriptMonsterSpawnService
=
new
ScriptMonsterSpawnService
(
this
);
// TEMPORARY
if
(
this
.
getScene
().
getId
()
<
10
)
{
return
;
...
...
@@ -103,17 +108,35 @@ public class SceneScriptManager {
}
public
Set
<
SceneTrigger
>
getTriggersByEvent
(
int
eventId
)
{
return
t
riggers
.
computeIfAbsent
(
eventId
,
e
->
new
HashSet
<>());
return
currentT
riggers
.
computeIfAbsent
(
eventId
,
e
->
new
HashSet
<>());
}
public
void
registerTrigger
(
SceneTrigger
trigger
)
{
this
.
triggers
.
put
(
trigger
.
name
,
trigger
);
getTriggersByEvent
(
trigger
.
event
).
add
(
trigger
);
}
public
void
deregisterTrigger
(
SceneTrigger
trigger
)
{
this
.
triggers
.
remove
(
trigger
.
name
);
getTriggersByEvent
(
trigger
.
event
).
remove
(
trigger
);
}
public
void
resetTriggers
(
List
<
String
>
triggerNames
)
{
for
(
var
name
:
triggerNames
){
var
instance
=
triggers
.
get
(
name
);
this
.
currentTriggers
.
get
(
instance
.
event
).
clear
();
this
.
currentTriggers
.
get
(
instance
.
event
).
add
(
instance
);
}
}
public
void
refreshGroup
(
SceneGroup
group
,
int
suiteIndex
){
var
suite
=
group
.
getSuiteByIndex
(
suiteIndex
);
if
(
suite
==
null
){
return
;
}
if
(
suite
.
triggers
.
size
()
>
0
){
resetTriggers
(
suite
.
triggers
);
}
spawnMonstersInGroup
(
group
,
suite
);
spawnGadgetsInGroup
(
group
,
suite
);
}
public
SceneRegion
getRegionById
(
int
id
)
{
return
regions
.
get
(
id
);
}
...
...
@@ -263,6 +286,7 @@ public class SceneScriptManager {
}
}
}
this
.
sceneGroups
.
put
(
group
.
id
,
group
);
}
catch
(
ScriptException
e
)
{
Grasscutter
.
getLogger
().
error
(
"Error loading group "
+
group
.
id
+
" in scene "
+
getScene
().
getId
(),
e
);
}
...
...
@@ -322,96 +346,36 @@ public class SceneScriptManager {
this
.
callEvent
(
EventType
.
EVENT_GADGET_CREATE
,
new
ScriptArgs
(
entity
.
getConfigId
()));
}
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
,
int
suiteIndex
)
{
var
suite
=
group
.
getSuiteByIndex
(
suiteIndex
);
if
(
suite
==
null
){
return
;
}
if
(
suite
.
sceneMonsters
.
size
()
>
0
){
this
.
currentGroup
=
group
;
this
.
monsterSceneLimit
=
0
;
suite
.
sceneMonsters
.
forEach
(
mob
->
spawnMonstersInGroup
(
group
,
mob
));
spawnMonstersInGroup
(
group
,
suite
);
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
,
SceneSuite
suite
)
{
if
(
suite
==
null
||
suite
.
sceneMonsters
.
size
()
<=
0
){
return
;
}
this
.
currentGroup
=
group
;
suite
.
sceneMonsters
.
forEach
(
mob
->
this
.
scriptMonsterSpawnService
.
spawnMonster
(
group
.
id
,
mob
));
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
)
{
this
.
currentGroup
=
group
;
this
.
monsterSceneLimit
=
0
;
group
.
monsters
.
values
().
forEach
(
mob
->
spawnMonstersInGroup
(
group
,
mob
));
group
.
monsters
.
values
().
forEach
(
mob
->
this
.
scriptMonsterSpawnService
.
spawnMonster
(
group
.
id
,
mob
));
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
,
Integer
[]
ordersConfigId
,
int
tideCount
,
int
sceneLimit
)
{
this
.
currentGroup
=
group
;
this
.
monsterSceneLimit
=
sceneLimit
;
this
.
monsterTideCount
=
new
AtomicInteger
(
tideCount
);
this
.
monsterAlive
=
new
AtomicInteger
(
0
);
this
.
monsterOrders
=
new
ConcurrentLinkedQueue
<>(
List
.
of
(
ordersConfigId
));
// add the last turn
group
.
monsters
.
keySet
().
stream
()
.
filter
(
i
->
!
this
.
monsterOrders
.
contains
(
i
))
.
forEach
(
this
.
monsterOrders
::
add
);
for
(
int
i
=
0
;
i
<
sceneLimit
;
i
++)
{
spawnMonstersInGroup
(
group
,
group
.
monsters
.
get
(
this
.
monsterOrders
.
poll
()));
}
}
public
void
spawnMonstersInGroup
(
SceneGroup
group
,
SceneMonster
monster
)
{
if
(
monster
==
null
){
return
;
}
if
(
this
.
monsterSceneLimit
>
0
){
this
.
monsterTideCount
.
decrementAndGet
();
this
.
monsterAlive
.
incrementAndGet
();
}
MonsterData
data
=
GameData
.
getMonsterDataMap
().
get
(
monster
.
monster_id
);
if
(
data
==
null
)
{
return
;
}
// Calculate level
int
level
=
monster
.
level
;
if
(
getScene
().
getDungeonData
()
!=
null
)
{
level
=
getScene
().
getDungeonData
().
getShowLevel
();
}
else
if
(
getScene
().
getWorld
().
getWorldLevel
()
>
0
)
{
WorldLevelData
worldLevelData
=
GameData
.
getWorldLevelDataMap
().
get
(
getScene
().
getWorld
().
getWorldLevel
());
if
(
worldLevelData
!=
null
)
{
level
=
worldLevelData
.
getMonsterLevel
();
}
}
// Spawn mob
EntityMonster
entity
=
new
EntityMonster
(
getScene
(),
data
,
monster
.
pos
,
level
);
entity
.
getRotation
().
set
(
monster
.
rot
);
entity
.
setGroupId
(
group
.
id
);
entity
.
setConfigId
(
monster
.
config_id
);
getScene
().
addEntity
(
entity
);
public
void
startMonsterTideInGroup
(
SceneGroup
group
,
Integer
[]
ordersConfigId
,
int
tideCount
,
int
sceneLimit
)
{
this
.
currentGroup
=
group
;
this
.
scriptMonsterTideService
=
new
ScriptMonsterTideService
(
this
,
group
,
tideCount
,
sceneLimit
,
ordersConfigId
);
callEvent
(
EventType
.
EVENT_ANY_MONSTER_LIVE
,
new
ScriptArgs
(
entity
.
getConfigId
()));
}
public
void
onMonsterDie
(){
if
(
this
.
monsterSceneLimit
<=
0
){
return
;
}
if
(
this
.
monsterAlive
.
decrementAndGet
()
>=
this
.
monsterSceneLimit
)
{
// maybe not happen
return
;
}
if
(
this
.
monsterTideCount
.
get
()
>
0
){
// add more
spawnMonstersInGroup
(
this
.
currentGroup
,
this
.
currentGroup
.
monsters
.
get
(
this
.
monsterOrders
.
poll
()));
}
else
if
(
this
.
monsterAlive
.
get
()
==
0
){
// spawn the last turn of monsters
//callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs());
while
(!
this
.
monsterOrders
.
isEmpty
()){
spawnMonstersInGroup
(
this
.
currentGroup
,
this
.
currentGroup
.
monsters
.
get
(
this
.
monsterOrders
.
poll
()));
}
}
public
void
spawnMonstersByConfigId
(
int
configId
,
int
delayTime
)
{
// TODO delay
this
.
scriptMonsterSpawnService
.
spawnMonster
(
this
.
currentGroup
.
id
,
this
.
currentGroup
.
monsters
.
get
(
configId
));
}
// Events
...
...
@@ -432,17 +396,35 @@ public class SceneScriptManager {
args
=
CoerceJavaToLua
.
coerce
(
params
);
}
ret
=
condition
.
call
(
this
.
getScriptLibLua
()
,
args
);
ret
=
safetyCall
(
trigger
.
condition
,
condition
,
args
);
}
if
(
ret
.
checkboolean
()
==
true
)
{
if
(
ret
.
isboolean
()
&&
ret
.
checkboolean
())
{
LuaValue
action
=
(
LuaValue
)
this
.
getBindings
().
get
(
trigger
.
action
);
action
.
call
(
this
.
getScriptLibLua
(),
LuaValue
.
NIL
);
var
arg
=
new
ScriptArgs
();
arg
.
param2
=
100
;
var
args
=
CoerceJavaToLua
.
coerce
(
arg
);
safetyCall
(
trigger
.
action
,
action
,
args
);
}
//TODO some ret may not bool
}
}
// public LuaValue safetyCall(){
//
// }
public
LuaValue
safetyCall
(
String
name
,
LuaValue
func
,
LuaValue
args
){
try
{
return
func
.
call
(
this
.
getScriptLibLua
(),
args
);
}
catch
(
LuaError
error
){
ScriptLib
.
logger
.
error
(
"[LUA] call trigger failed {},{}"
,
name
,
args
,
error
);
return
LuaValue
.
valueOf
(-
1
);
}
}
public
ScriptMonsterTideService
getScriptMonsterTideService
()
{
return
scriptMonsterTideService
;
}
public
ScriptMonsterSpawnService
getScriptMonsterSpawnService
()
{
return
scriptMonsterSpawnService
;
}
}
src/main/java/emu/grasscutter/scripts/ScriptLib.java
View file @
c2a7d607
...
...
@@ -28,7 +28,17 @@ public class ScriptLib {
public
SceneScriptManager
getSceneScriptManager
()
{
return
sceneScriptManager
;
}
private
String
printTable
(
LuaTable
table
){
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"{"
);
for
(
var
meta
:
table
.
keys
()){
sb
.
append
(
meta
).
append
(
":"
).
append
(
table
.
get
(
meta
)).
append
(
","
);
}
sb
.
append
(
"}"
);
return
sb
.
toString
();
}
public
int
SetGadgetStateByConfigId
(
int
configId
,
int
gadgetState
)
{
logger
.
debug
(
"[LUA] Call SetGadgetStateByConfigId with {},{}"
,
configId
,
gadgetState
);
...
...
@@ -123,7 +133,7 @@ public class ScriptLib {
return
1
;
}
this
.
getSceneScriptManager
().
s
pawn
Monster
s
InGroup
(
group
,
ordersConfigId
,
tideCount
,
sceneLimit
);
this
.
getSceneScriptManager
().
s
tart
Monster
Tide
InGroup
(
group
,
ordersConfigId
,
tideCount
,
sceneLimit
);
return
0
;
}
...
...
@@ -204,10 +214,13 @@ public class ScriptLib {
getSceneScriptManager
().
getVariables
().
put
(
var
,
getSceneScriptManager
().
getVariables
().
get
(
var
)
+
value
);
return
LuaValue
.
ZERO
;
}
/**
* Set the actions and triggers to designated group
*/
public
int
RefreshGroup
(
LuaTable
table
)
{
logger
.
debug
(
"[LUA] Call RefreshGroup with {}"
,
table
);
printTable
(
table
)
)
;
// Kill and Respawn?
int
groupId
=
table
.
get
(
"group_id"
).
toint
();
int
suite
=
table
.
get
(
"suite"
).
toint
();
...
...
@@ -218,8 +231,7 @@ public class ScriptLib {
return
1
;
}
this
.
getSceneScriptManager
().
spawnMonstersInGroup
(
group
,
suite
);
this
.
getSceneScriptManager
().
spawnGadgetsInGroup
(
group
,
suite
);
getSceneScriptManager
().
refreshGroup
(
group
,
suite
);
return
0
;
}
...
...
@@ -260,7 +272,7 @@ public class ScriptLib {
public
int
SetMonsterBattleByGroup
(
int
var1
,
int
var2
,
int
var3
){
logger
.
debug
(
"[LUA] Call SetMonsterBattleByGroup with {},{},{}"
,
var1
,
var2
,
var3
);
// TODO
return
0
;
}
...
...
@@ -270,7 +282,7 @@ public class ScriptLib {
return
0
;
}
// 8-1
public
int
GetGroupVariableValueByGroup
(
String
name
,
int
groupId
){
logger
.
debug
(
"[LUA] Call GetGroupVariableValueByGroup with {},{}"
,
name
,
groupId
);
...
...
@@ -288,7 +300,7 @@ public class ScriptLib {
public
int
KillEntityByConfigId
(
LuaTable
table
){
logger
.
debug
(
"[LUA] Call KillEntityByConfigId with {}"
,
table
);
printTable
(
table
)
)
;
var
configId
=
table
.
get
(
"config_id"
);
if
(
configId
==
LuaValue
.
NIL
){
return
1
;
...
...
@@ -306,6 +318,26 @@ public class ScriptLib {
logger
.
debug
(
"[LUA] Call SetGroupVariableValueByGroup with {},{},{}"
,
key
,
value
,
groupId
);
getSceneScriptManager
().
getVariables
().
put
(
key
,
value
);
return
0
;
}
public
int
CreateMonster
(
LuaTable
table
){
logger
.
debug
(
"[LUA] Call CreateMonster with {}"
,
printTable
(
table
));
var
configId
=
table
.
get
(
"config_id"
).
toint
();
var
delayTime
=
table
.
get
(
"delay_time"
).
toint
();
getSceneScriptManager
().
spawnMonstersByConfigId
(
configId
,
delayTime
);
return
0
;
}
public
int
TowerMirrorTeamSetUp
(
int
team
,
int
var1
)
{
logger
.
debug
(
"[LUA] Call TowerMirrorTeamSetUp with {},{}"
,
team
,
var1
);
getSceneScriptManager
().
getScene
().
getPlayers
().
get
(
0
).
getTowerManager
().
mirrorTeamSetUp
(
team
-
1
);
return
0
;
}
...
...
src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java
View file @
c2a7d607
...
...
@@ -7,4 +7,18 @@ public class SceneTrigger {
public
String
source
;
public
String
condition
;
public
String
action
;
@Override
public
boolean
equals
(
Object
obj
)
{
if
(
obj
instanceof
SceneTrigger
sceneTrigger
){
return
this
.
name
.
equals
(
sceneTrigger
.
name
);
}
return
super
.
equals
(
obj
);
}
@Override
public
int
hashCode
()
{
return
name
.
hashCode
();
}
}
src/main/java/emu/grasscutter/scripts/service/ScriptMonsterSpawnService.java
0 → 100644
View file @
c2a7d607
package
emu.grasscutter.scripts.service
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.def.MonsterData
;
import
emu.grasscutter.data.def.WorldLevelData
;
import
emu.grasscutter.game.entity.EntityMonster
;
import
emu.grasscutter.scripts.SceneScriptManager
;
import
emu.grasscutter.scripts.constants.EventType
;
import
emu.grasscutter.scripts.data.SceneMonster
;
import
emu.grasscutter.scripts.data.ScriptArgs
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.function.Consumer
;
public
class
ScriptMonsterSpawnService
{
private
final
SceneScriptManager
sceneScriptManager
;
private
final
List
<
Consumer
<
EntityMonster
>>
onMonsterCreatedListener
=
new
ArrayList
<>();
private
final
List
<
Consumer
<
EntityMonster
>>
onMonsterDeadListener
=
new
ArrayList
<>();
public
ScriptMonsterSpawnService
(
SceneScriptManager
sceneScriptManager
){
this
.
sceneScriptManager
=
sceneScriptManager
;
}
public
void
addMonsterCreatedListener
(
Consumer
<
EntityMonster
>
consumer
){
onMonsterCreatedListener
.
add
(
consumer
);
}
public
void
addMonsterDeadListener
(
Consumer
<
EntityMonster
>
consumer
){
onMonsterDeadListener
.
add
(
consumer
);
}
public
void
onMonsterDead
(
EntityMonster
entityMonster
){
onMonsterDeadListener
.
forEach
(
l
->
l
.
accept
(
entityMonster
));
}
public
void
spawnMonster
(
int
groupId
,
SceneMonster
monster
)
{
if
(
monster
==
null
){
return
;
}
MonsterData
data
=
GameData
.
getMonsterDataMap
().
get
(
monster
.
monster_id
);
if
(
data
==
null
)
{
return
;
}
// Calculate level
int
level
=
monster
.
level
;
if
(
sceneScriptManager
.
getScene
().
getDungeonData
()
!=
null
)
{
level
=
sceneScriptManager
.
getScene
().
getDungeonData
().
getShowLevel
();
}
else
if
(
sceneScriptManager
.
getScene
().
getWorld
().
getWorldLevel
()
>
0
)
{
WorldLevelData
worldLevelData
=
GameData
.
getWorldLevelDataMap
().
get
(
sceneScriptManager
.
getScene
().
getWorld
().
getWorldLevel
());
if
(
worldLevelData
!=
null
)
{
level
=
worldLevelData
.
getMonsterLevel
();
}
}
// Spawn mob
EntityMonster
entity
=
new
EntityMonster
(
sceneScriptManager
.
getScene
(),
data
,
monster
.
pos
,
level
);
entity
.
getRotation
().
set
(
monster
.
rot
);
entity
.
setGroupId
(
groupId
);
entity
.
setConfigId
(
monster
.
config_id
);
onMonsterCreatedListener
.
forEach
(
action
->
action
.
accept
(
entity
));
sceneScriptManager
.
getScene
().
addEntity
(
entity
);
sceneScriptManager
.
callEvent
(
EventType
.
EVENT_ANY_MONSTER_LIVE
,
new
ScriptArgs
(
entity
.
getConfigId
()));
}
}
src/main/java/emu/grasscutter/scripts/service/ScriptMonsterTideService.java
0 → 100644
View file @
c2a7d607
package
emu.grasscutter.scripts.service
;
import
emu.grasscutter.game.entity.EntityMonster
;
import
emu.grasscutter.scripts.SceneScriptManager
;
import
emu.grasscutter.scripts.constants.EventType
;
import
emu.grasscutter.scripts.data.SceneGroup
;
import
emu.grasscutter.scripts.data.SceneMonster
;
import
emu.grasscutter.scripts.data.ScriptArgs
;
import
java.util.List
;
import
java.util.concurrent.ConcurrentLinkedQueue
;
import
java.util.concurrent.atomic.AtomicInteger
;
public
class
ScriptMonsterTideService
{
private
final
SceneScriptManager
sceneScriptManager
;
private
final
SceneGroup
currentGroup
;
private
final
AtomicInteger
monsterAlive
;
private
final
AtomicInteger
monsterTideCount
;
private
final
AtomicInteger
monsterKillCount
;
private
final
int
monsterSceneLimit
;
private
final
ConcurrentLinkedQueue
<
Integer
>
monsterConfigOrders
;
public
ScriptMonsterTideService
(
SceneScriptManager
sceneScriptManager
,
SceneGroup
group
,
int
tideCount
,
int
monsterSceneLimit
,
Integer
[]
ordersConfigId
){
this
.
sceneScriptManager
=
sceneScriptManager
;
this
.
currentGroup
=
group
;
this
.
monsterSceneLimit
=
monsterSceneLimit
;
this
.
monsterTideCount
=
new
AtomicInteger
(
tideCount
);
this
.
monsterKillCount
=
new
AtomicInteger
(
0
);
this
.
monsterAlive
=
new
AtomicInteger
(
0
);
this
.
monsterConfigOrders
=
new
ConcurrentLinkedQueue
<>(
List
.
of
(
ordersConfigId
));
this
.
sceneScriptManager
.
getScriptMonsterSpawnService
().
addMonsterCreatedListener
(
this
::
onMonsterCreated
);
this
.
sceneScriptManager
.
getScriptMonsterSpawnService
().
addMonsterDeadListener
(
this
::
onMonsterDead
);
// spawn the first turn
for
(
int
i
=
0
;
i
<
this
.
monsterSceneLimit
;
i
++)
{
this
.
sceneScriptManager
.
getScriptMonsterSpawnService
().
spawnMonster
(
group
.
id
,
getNextMonster
());
}
}
public
void
onMonsterCreated
(
EntityMonster
entityMonster
){
if
(
this
.
monsterSceneLimit
>
0
){
this
.
monsterTideCount
.
decrementAndGet
();
this
.
monsterAlive
.
incrementAndGet
();
}
}
public
SceneMonster
getNextMonster
(){
var
nextId
=
this
.
monsterConfigOrders
.
poll
();
if
(
currentGroup
.
monsters
.
containsKey
(
nextId
)){
return
currentGroup
.
monsters
.
get
(
nextId
);
}
// TODO some monster config_id do not exist in groups, so temporarily set it to the first
return
currentGroup
.
monsters
.
values
().
stream
().
findFirst
().
orElse
(
null
);
}
public
void
onMonsterDead
(
EntityMonster
entityMonster
){
if
(
this
.
monsterSceneLimit
<=
0
){
return
;
}
if
(
this
.
monsterAlive
.
decrementAndGet
()
>=
this
.
monsterSceneLimit
)
{
// maybe not happen
return
;
}
this
.
monsterKillCount
.
incrementAndGet
();
if
(
this
.
monsterTideCount
.
get
()
>
0
){
// add more
this
.
sceneScriptManager
.
getScriptMonsterSpawnService
().
spawnMonster
(
this
.
currentGroup
.
id
,
getNextMonster
());
}
// spawn the last turn of monsters
// fix the 5-2
this
.
sceneScriptManager
.
callEvent
(
EventType
.
EVENT_MONSTER_TIDE_DIE
,
new
ScriptArgs
(
this
.
monsterKillCount
.
get
()));
}
}
src/main/java/emu/grasscutter/server/packet/send/PacketTowerMiddleLevelChangeTeamNotify.java
0 → 100644
View file @
c2a7d607
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.TowerMiddleLevelChangeTeamNotifyOuterClass
;
public
class
PacketTowerMiddleLevelChangeTeamNotify
extends
BasePacket
{
public
PacketTowerMiddleLevelChangeTeamNotify
()
{
super
(
PacketOpcodes
.
TowerMiddleLevelChangeTeamNotify
);
TowerMiddleLevelChangeTeamNotifyOuterClass
.
TowerMiddleLevelChangeTeamNotify
proto
=
TowerMiddleLevelChangeTeamNotifyOuterClass
.
TowerMiddleLevelChangeTeamNotify
.
newBuilder
()
.
build
();
this
.
setData
(
proto
);
}
}
src/main/resources/languages/en-US.json
View file @
c2a7d607
...
...
@@ -334,6 +334,13 @@
},
"restart"
:
{
"description"
:
"Restarts the current session"
},
"unlocktower"
:
{
"success"
:
"unlock done"
,
"description"
:
"Unlock all levels of tower"
},
"resetshop"
:
{
"description"
:
"reset shop"
}
}
}
Prev
1
2
3
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