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
a2ff8c84
Commit
a2ff8c84
authored
May 14, 2022
by
KingRainbow44
Browse files
Merge `development` into `plugin-auth`
parents
3adf0d44
a751e71d
Changes
111
Expand all
Show whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/data/custom/MainQuestData.java
0 → 100644
View file @
a2ff8c84
package
emu.grasscutter.data.custom
;
import
emu.grasscutter.game.quest.enums.LogicType
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.game.quest.enums.QuestType
;
public
class
MainQuestData
{
private
int
id
;
private
int
series
;
private
QuestType
type
;
private
long
titleTextMapHash
;
private
int
[]
suggestTrackMainQuestList
;
private
int
[]
rewardIdList
;
private
SubQuestData
[]
subQuests
;
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
;
}
public
int
[]
getRewardIdList
()
{
return
rewardIdList
;
}
public
SubQuestData
[]
getSubQuests
()
{
return
subQuests
;
}
public
static
class
SubQuestData
{
private
int
subId
;
public
int
getSubId
()
{
return
subId
;
}
}
}
src/main/java/emu/grasscutter/data/def/CodexQuest.java
0 → 100644
View file @
a2ff8c84
package
emu.grasscutter.data.def
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.GameResource
;
import
emu.grasscutter.data.ResourceType
;
@ResourceType
(
name
=
{
"QuestCodexExcelConfigData.json"
},
loadPriority
=
ResourceType
.
LoadPriority
.
HIGH
)
public
class
CodexQuest
extends
GameResource
{
private
int
Id
;
private
int
ParentQuestId
;
private
int
ChapterId
;
private
int
SortOrder
;
private
boolean
IsDisuse
;
public
int
getParentQuestId
()
{
return
ParentQuestId
;
}
public
int
getId
()
{
return
Id
;
}
public
int
getChapterId
()
{
return
ChapterId
;
}
public
int
getSortOrder
()
{
return
SortOrder
;
}
public
boolean
getIsDisuse
()
{
return
IsDisuse
;
}
@Override
public
void
onLoad
()
{
if
(!
this
.
getIsDisuse
())
{
GameData
.
getCodexQuestIdMap
().
put
(
this
.
getParentQuestId
(),
this
);
}
}
}
src/main/java/emu/grasscutter/data/def/QuestData.java
0 → 100644
View file @
a2ff8c84
package
emu.grasscutter.data.def
;
import
java.util.Arrays
;
import
java.util.List
;
import
emu.grasscutter.data.GameResource
;
import
emu.grasscutter.data.ResourceType
;
import
emu.grasscutter.game.quest.enums.LogicType
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
@ResourceType
(
name
=
"QuestExcelConfigData.json"
)
public
class
QuestData
extends
GameResource
{
private
int
SubId
;
private
int
MainId
;
private
int
Order
;
private
long
DescTextMapHash
;
private
boolean
FinishParent
;
private
boolean
IsRewind
;
private
LogicType
AcceptCondComb
;
private
QuestCondition
[]
acceptConditons
;
private
LogicType
FinishCondComb
;
private
QuestCondition
[]
finishConditons
;
private
LogicType
FailCondComb
;
private
QuestCondition
[]
failConditons
;
private
List
<
QuestParam
>
AcceptCond
;
private
List
<
QuestParam
>
FinishCond
;
private
List
<
QuestParam
>
FailCond
;
private
List
<
QuestExecParam
>
BeginExec
;
private
List
<
QuestExecParam
>
FinishExec
;
private
List
<
QuestExecParam
>
FailExec
;
public
int
getId
()
{
return
SubId
;
}
public
int
getMainId
()
{
return
MainId
;
}
public
int
getOrder
()
{
return
Order
;
}
public
long
getDescTextMapHash
()
{
return
DescTextMapHash
;
}
public
boolean
finishParent
()
{
return
FinishParent
;
}
public
boolean
isRewind
()
{
return
IsRewind
;
}
public
LogicType
getAcceptCondComb
()
{
return
AcceptCondComb
;
}
public
QuestCondition
[]
getAcceptCond
()
{
return
acceptConditons
;
}
public
LogicType
getFinishCondComb
()
{
return
FinishCondComb
;
}
public
QuestCondition
[]
getFinishCond
()
{
return
finishConditons
;
}
public
LogicType
getFailCondComb
()
{
return
FailCondComb
;
}
public
QuestCondition
[]
getFailCond
()
{
return
failConditons
;
}
public
void
onLoad
()
{
this
.
acceptConditons
=
AcceptCond
.
stream
().
filter
(
p
->
p
.
Type
!=
null
).
map
(
QuestCondition:
:
new
).
toArray
(
QuestCondition
[]::
new
);
AcceptCond
=
null
;
this
.
finishConditons
=
FinishCond
.
stream
().
filter
(
p
->
p
.
Type
!=
null
).
map
(
QuestCondition:
:
new
).
toArray
(
QuestCondition
[]::
new
);
FinishCond
=
null
;
this
.
failConditons
=
FailCond
.
stream
().
filter
(
p
->
p
.
Type
!=
null
).
map
(
QuestCondition:
:
new
).
toArray
(
QuestCondition
[]::
new
);
FailCond
=
null
;
}
public
class
QuestParam
{
QuestTrigger
Type
;
int
[]
Param
;
String
count
;
}
public
class
QuestExecParam
{
QuestTrigger
Type
;
String
[]
Param
;
String
count
;
}
public
static
class
QuestCondition
{
private
QuestTrigger
type
;
private
int
[]
param
;
private
String
count
;
public
QuestCondition
(
QuestParam
param
)
{
this
.
type
=
param
.
Type
;
this
.
param
=
param
.
Param
;
}
public
QuestTrigger
getType
()
{
return
type
;
}
public
int
[]
getParam
()
{
return
param
;
}
public
String
getCount
()
{
return
count
;
}
}
}
src/main/java/emu/grasscutter/database/DatabaseHelper.java
View file @
a2ff8c84
...
...
@@ -15,6 +15,7 @@ import emu.grasscutter.game.gacha.GachaRecord;
import
emu.grasscutter.game.inventory.GameItem
;
import
emu.grasscutter.game.mail.Mail
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.quest.GameMainQuest
;
import
static
com
.
mongodb
.
client
.
model
.
Filters
.
eq
;
...
...
@@ -111,6 +112,8 @@ public final class DatabaseHelper {
DatabaseManager
.
getGameDatabase
().
getCollection
(
"gachas"
).
deleteMany
(
eq
(
"ownerId"
,
target
.
getPlayerUid
()));
// Delete GameItem.class data
DatabaseManager
.
getGameDatabase
().
getCollection
(
"items"
).
deleteMany
(
eq
(
"ownerId"
,
target
.
getPlayerUid
()));
// Delete GameMainQuest.class data
DatabaseManager
.
getGameDatabase
().
getCollection
(
"quests"
).
deleteMany
(
eq
(
"ownerUid"
,
target
.
getPlayerUid
()));
// Delete friendships.
// Here, we need to make sure to not only delete the deleted account's friendships,
...
...
@@ -260,4 +263,16 @@ public final class DatabaseHelper {
DeleteResult
result
=
DatabaseManager
.
getGameDatastore
().
delete
(
mail
);
return
result
.
wasAcknowledged
();
}
public
static
List
<
GameMainQuest
>
getAllQuests
(
Player
player
)
{
return
DatabaseManager
.
getGameDatastore
().
find
(
GameMainQuest
.
class
).
filter
(
Filters
.
eq
(
"ownerUid"
,
player
.
getUid
())).
stream
().
toList
();
}
public
static
void
saveQuest
(
GameMainQuest
quest
)
{
DatabaseManager
.
getGameDatastore
().
save
(
quest
);
}
public
static
boolean
deleteQuest
(
GameMainQuest
quest
)
{
return
DatabaseManager
.
getGameDatastore
().
delete
(
quest
).
wasAcknowledged
();
}
}
src/main/java/emu/grasscutter/database/DatabaseManager.java
View file @
a2ff8c84
...
...
@@ -19,6 +19,8 @@ import emu.grasscutter.game.gacha.GachaRecord;
import
emu.grasscutter.game.inventory.GameItem
;
import
emu.grasscutter.game.mail.Mail
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.quest.GameMainQuest
;
import
emu.grasscutter.game.quest.GameQuest
;
import
static
emu
.
grasscutter
.
Configuration
.*;
...
...
@@ -27,7 +29,8 @@ public final class DatabaseManager {
private
static
Datastore
dispatchDatastore
;
private
static
final
Class
<?>[]
mappedClasses
=
new
Class
<?>[]
{
DatabaseCounter
.
class
,
Account
.
class
,
Player
.
class
,
Avatar
.
class
,
GameItem
.
class
,
Friendship
.
class
,
GachaRecord
.
class
,
Mail
.
class
DatabaseCounter
.
class
,
Account
.
class
,
Player
.
class
,
Avatar
.
class
,
GameItem
.
class
,
Friendship
.
class
,
GachaRecord
.
class
,
Mail
.
class
,
GameMainQuest
.
class
};
public
static
Datastore
getGameDatastore
()
{
...
...
src/main/java/emu/grasscutter/game/Account.java
View file @
a2ff8c84
...
...
@@ -144,16 +144,17 @@ public class Account {
}
public
boolean
hasPermission
(
String
permission
)
{
if
(
this
.
permissions
.
contains
(
permission
)
||
this
.
permissions
.
contains
(
"*"
))
{
return
true
;
}
if
(
this
.
permissions
.
contains
(
permission
))
return
true
;
if
(
this
.
permissions
.
contains
(
"*"
)
&&
this
.
permissions
.
size
()
==
1
)
return
true
;
String
[]
permissionParts
=
permission
.
split
(
"\\."
);
for
(
String
p
:
this
.
permissions
)
{
if
(
permissionMatchesWildcard
(
p
,
permissionParts
))
{
return
true
;
}
if
(
p
.
startsWith
(
"-"
)
&&
permissionMatchesWildcard
(
p
.
substring
(
1
),
permissionParts
))
return
false
;
if
(
permissionMatchesWildcard
(
p
,
permissionParts
))
return
true
;
}
return
false
;
return
this
.
permissions
.
contains
(
"*"
);
}
public
boolean
removePermission
(
String
permission
)
{
...
...
src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java
View file @
a2ff8c84
...
...
@@ -7,6 +7,7 @@ import emu.grasscutter.data.custom.ScenePointEntry;
import
emu.grasscutter.data.def.DungeonData
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.game.props.SceneType
;
import
emu.grasscutter.game.quest.enums.QuestTrigger
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.server.game.GameServer
;
...
...
@@ -51,8 +52,9 @@ public class DungeonManager {
int
sceneId
=
data
.
getSceneId
();
player
.
getScene
().
setPrevScene
(
sceneId
);
if
(
player
.
getWorld
().
transferPlayerToScene
(
player
,
sceneId
,
data
)){
if
(
player
.
getWorld
().
transferPlayerToScene
(
player
,
sceneId
,
data
))
{
player
.
getScene
().
addDungeonSettleObserver
(
basicDungeonSettleObserver
);
player
.
getQuestManager
().
triggerEvent
(
QuestTrigger
.
QUEST_CONTENT_ENTER_DUNGEON
,
data
.
getId
());
}
player
.
getScene
().
setPrevScenePoint
(
pointId
);
...
...
src/main/java/emu/grasscutter/game/gacha/GachaBanner.java
View file @
a2ff8c84
...
...
@@ -2,28 +2,49 @@ package emu.grasscutter.game.gacha;
import
emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo
;
import
emu.grasscutter.net.proto.GachaUpInfoOuterClass.GachaUpInfo
;
import
emu.grasscutter.utils.Utils
;
import
static
emu
.
grasscutter
.
Configuration
.*;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.common.ItemParamData
;
public
class
GachaBanner
{
private
int
gachaType
;
private
int
scheduleId
;
private
String
prefabPath
;
private
String
previewPrefabPath
;
private
String
titlePath
;
private
int
costItem
;
private
int
costItemId
=
0
;
private
int
costItemAmount
=
1
;
private
int
costItemId10
=
0
;
private
int
costItemAmount10
=
10
;
private
int
beginTime
;
private
int
endTime
;
private
int
sortId
;
private
int
[]
rateUpItems1
;
private
int
[]
rateUpItems2
;
private
int
baseYellowWeight
=
60
;
// Max 10000
private
int
basePurpleWeight
=
510
;
// Max 10000
private
int
eventChance
=
50
;
// Chance to win a featured event item
private
int
softPity
=
75
;
private
int
hardPity
=
90
;
private
int
[]
rateUpItems4
=
{};
private
int
[]
rateUpItems5
=
{};
private
int
[]
fallbackItems3
=
{
11301
,
11302
,
11306
,
12301
,
12302
,
12305
,
13303
,
14301
,
14302
,
14304
,
15301
,
15302
,
15304
};
private
int
[]
fallbackItems4Pool1
=
{
1014
,
1020
,
1023
,
1024
,
1025
,
1027
,
1031
,
1032
,
1034
,
1036
,
1039
,
1043
,
1044
,
1045
,
1048
,
1053
,
1055
,
1056
,
1064
};
private
int
[]
fallbackItems4Pool2
=
{
11401
,
11402
,
11403
,
11405
,
12401
,
12402
,
12403
,
12405
,
13401
,
13407
,
14401
,
14402
,
14403
,
14409
,
15401
,
15402
,
15403
,
15405
};
private
int
[]
fallbackItems5Pool1
=
{
1003
,
1016
,
1042
,
1035
,
1041
};
private
int
[]
fallbackItems5Pool2
=
{
11501
,
11502
,
12501
,
12502
,
13502
,
13505
,
14501
,
14502
,
15501
,
15502
};
private
boolean
removeC6FromPool
=
false
;
private
boolean
autoStripRateUpFromFallback
=
true
;
private
int
[][]
weights4
=
{{
1
,
510
},
{
8
,
510
},
{
10
,
10000
}};
private
int
[][]
weights5
=
{{
1
,
75
},
{
73
,
150
},
{
90
,
10000
}};
private
int
[][]
poolBalanceWeights4
=
{{
1
,
255
},
{
17
,
255
},
{
21
,
10455
}};
private
int
[][]
poolBalanceWeights5
=
{{
1
,
30
},
{
147
,
150
},
{
181
,
10230
}};
private
int
eventChance4
=
50
;
// Chance to win a featured event item
private
int
eventChance5
=
50
;
// Chance to win a featured event item
private
BannerType
bannerType
=
BannerType
.
STANDARD
;
// Kinda wanna deprecate these but they're in people's configs
private
int
[]
rateUpItems1
=
{};
private
int
[]
rateUpItems2
=
{};
private
int
eventChance
=
-
1
;
private
int
costItem
=
0
;
public
int
getGachaType
()
{
return
gachaType
;
}
...
...
@@ -48,8 +69,15 @@ public class GachaBanner {
return
titlePath
;
}
public
ItemParamData
getCost
(
int
numRolls
)
{
return
switch
(
numRolls
)
{
case
10
->
new
ItemParamData
((
costItemId10
>
0
)
?
costItemId10
:
getCostItem
(),
costItemAmount10
);
default
->
new
ItemParamData
(
getCostItem
(),
costItemAmount
*
numRolls
);
};
}
public
int
getCostItem
()
{
return
costItem
;
return
(
costItem
>
0
)
?
costItem
:
costItemId
;
}
public
int
getBeginTime
()
{
...
...
@@ -64,32 +92,42 @@ public class GachaBanner {
return
sortId
;
}
public
int
get
BaseYellowWeight
()
{
return
baseYellowWeight
;
public
int
[]
get
RateUpItems4
()
{
return
(
rateUpItems2
.
length
>
0
)
?
rateUpItems2
:
rateUpItems4
;
}
public
int
getBasePurpleWeight
()
{
return
basePurpleWeight
;
public
int
[]
getRateUpItems5
()
{
return
(
rateUpItems1
.
length
>
0
)
?
rateUpItems1
:
rateUpItems5
;
}
public
int
[]
getRateUpItems1
()
{
return
rateUpItems1
;
}
public
int
[]
getFallbackItems3
()
{
return
fallbackItems3
;}
public
int
[]
getFallbackItems4Pool1
()
{
return
fallbackItems4Pool1
;}
public
int
[]
getFallbackItems4Pool2
()
{
return
fallbackItems4Pool2
;}
public
int
[]
getFallbackItems5Pool1
()
{
return
fallbackItems5Pool1
;}
public
int
[]
getFallbackItems5Pool2
()
{
return
fallbackItems5Pool2
;}
public
boolean
getRemoveC6FromPool
()
{
return
removeC6FromPool
;}
public
boolean
getAutoStripRateUpFromFallback
()
{
return
autoStripRateUpFromFallback
;}
public
int
[]
getRateUpItems2
()
{
return
rateUpItems2
;
}
public
int
getSoftPity
()
{
return
softPity
-
1
;
public
int
getWeight
(
int
rarity
,
int
pity
)
{
return
switch
(
rarity
)
{
case
4
->
Utils
.
lerp
(
pity
,
weights4
);
default
->
Utils
.
lerp
(
pity
,
weights5
);
};
}
public
int
getHardPity
()
{
return
hardPity
-
1
;
public
int
getPoolBalanceWeight
(
int
rarity
,
int
pity
)
{
return
switch
(
rarity
)
{
case
4
->
Utils
.
lerp
(
pity
,
poolBalanceWeights4
);
default
->
Utils
.
lerp
(
pity
,
poolBalanceWeights5
);
};
}
public
int
getEventChance
()
{
return
eventChance
;
public
int
getEventChance
(
int
rarity
)
{
return
switch
(
rarity
)
{
case
4
->
eventChance4
;
default
->
(
eventChance
>
-
1
)
?
eventChance
:
eventChance5
;
};
}
@Deprecated
...
...
@@ -102,34 +140,40 @@ public class GachaBanner {
+
lr
(
HTTP_INFO
.
accessAddress
,
HTTP_INFO
.
bindAddress
)
+
":"
+
lr
(
HTTP_INFO
.
accessPort
,
HTTP_INFO
.
bindPort
)
+
"/gacha?s="
+
sessionKey
+
"&gachaType="
+
gachaType
;
String
details
=
"http"
+
(
DISPATCH_INFO
.
encryption
.
useInRouting
?
"s"
:
""
)
+
"://"
+
lr
(
DISPATCH_INFO
.
accessAddress
,
DISPATCH_INFO
.
bindAddress
)
+
":"
+
lr
(
DISPATCH_INFO
.
accessPort
,
DISPATCH_INFO
.
bindPort
)
+
"/gacha/details?s="
+
sessionKey
+
"&gachaType="
+
gachaType
;
// Grasscutter.getLogger().info("record = " + record);
ItemParamData
costItem1
=
this
.
getCost
(
1
);
ItemParamData
costItem10
=
this
.
getCost
(
10
);
GachaInfo
.
Builder
info
=
GachaInfo
.
newBuilder
()
.
setGachaType
(
this
.
getGachaType
())
.
setScheduleId
(
this
.
getScheduleId
())
.
setBeginTime
(
this
.
getBeginTime
())
.
setEndTime
(
this
.
getEndTime
())
.
setCostItemId
(
this
.
getCostItem
())
.
setCostItemNum
(
1
)
.
setCostItemId
(
costItem1
.
getId
())
.
setCostItemNum
(
costItem1
.
getCount
())
.
setTenCostItemId
(
costItem10
.
getId
())
.
setTenCostItemNum
(
costItem10
.
getCount
())
.
setGachaPrefabPath
(
this
.
getPrefabPath
())
.
setGachaPreviewPrefabPath
(
this
.
getPreviewPrefabPath
())
.
setGachaProbUrl
(
record
)
.
setGachaProbUrlOversea
(
record
)
.
setGachaProbUrl
(
details
)
.
setGachaProbUrlOversea
(
details
)
.
setGachaRecordUrl
(
record
)
.
setGachaRecordUrlOversea
(
record
)
.
setTenCostItemId
(
this
.
getCostItem
())
.
setTenCostItemNum
(
10
)
.
setLeftGachaTimes
(
Integer
.
MAX_VALUE
)
.
setGachaTimesLimit
(
Integer
.
MAX_VALUE
)
.
setGachaSortId
(
this
.
getSortId
());
if
(
this
.
getTitlePath
()
!=
null
)
{
info
.
setGachaTitlePath
(
this
.
getTitlePath
());
}
if
(
this
.
getRateUpItems
1
().
length
>
0
)
{
if
(
this
.
getRateUpItems
5
().
length
>
0
)
{
GachaUpInfo
.
Builder
upInfo
=
GachaUpInfo
.
newBuilder
().
setItemParentType
(
1
);
for
(
int
id
:
getRateUpItems
1
())
{
for
(
int
id
:
getRateUpItems
5
())
{
upInfo
.
addItemIdList
(
id
);
info
.
addMainNameId
(
id
);
}
...
...
@@ -137,10 +181,10 @@ public class GachaBanner {
info
.
addGachaUpInfoList
(
upInfo
);
}
if
(
this
.
getRateUpItems
2
().
length
>
0
)
{
if
(
this
.
getRateUpItems
4
().
length
>
0
)
{
GachaUpInfo
.
Builder
upInfo
=
GachaUpInfo
.
newBuilder
().
setItemParentType
(
2
);
for
(
int
id
:
getRateUpItems
2
())
{
for
(
int
id
:
getRateUpItems
4
())
{
upInfo
.
addItemIdList
(
id
);
if
(
info
.
getSubNameIdCount
()
==
0
)
{
info
.
addSubNameId
(
id
);
...
...
src/main/java/emu/grasscutter/game/gacha/GachaManager.java
View file @
a2ff8c84
...
...
@@ -4,6 +4,7 @@ import java.io.File;
import
java.io.FileReader
;
import
java.nio.file.*
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.List
;
import
java.util.concurrent.ThreadLocalRandom
;
...
...
@@ -13,11 +14,12 @@ import com.google.gson.reflect.TypeToken;
import
com.sun.nio.file.SensitivityWatchEventModifier
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.common.ItemParamData
;
import
emu.grasscutter.data.def.ItemData
;
import
emu.grasscutter.database.DatabaseHelper
;
import
emu.grasscutter.game.avatar.Avatar
;
import
emu.grasscutter.game.gacha.GachaBanner.BannerType
;
import
emu.grasscutter.game.inventory.GameItem
;
import
emu.grasscutter.game.inventory.Inventory
;
import
emu.grasscutter.game.inventory.ItemType
;
import
emu.grasscutter.game.inventory.MaterialType
;
import
emu.grasscutter.game.player.Player
;
...
...
@@ -28,6 +30,7 @@ import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
import
emu.grasscutter.server.game.GameServer
;
import
emu.grasscutter.server.game.GameServerTickEvent
;
import
emu.grasscutter.server.packet.send.PacketDoGachaRsp
;
import
emu.grasscutter.utils.Utils
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectMap
;
import
it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap
;
import
it.unimi.dsi.fastutil.ints.IntArrayList
;
...
...
@@ -42,14 +45,10 @@ public class GachaManager {
private
GetGachaInfoRsp
cachedProto
;
WatchService
watchService
;
private
final
int
[]
yellowAvatars
=
new
int
[]
{
1003
,
1016
,
1042
,
1035
,
1041
};
private
final
int
[]
yellowWeapons
=
new
int
[]
{
11501
,
11502
,
12501
,
12502
,
13502
,
13505
,
14501
,
14502
,
15501
,
15502
};
private
final
int
[]
purpleAvatars
=
new
int
[]
{
1006
,
1014
,
1015
,
1020
,
1021
,
1023
,
1024
,
1025
,
1027
,
1031
,
1032
,
1034
,
1036
,
1039
,
1043
,
1044
,
1045
,
1048
,
1053
,
1055
,
1056
,
1064
};
private
final
int
[]
purpleWeapons
=
new
int
[]
{
11401
,
11402
,
11403
,
11405
,
12401
,
12402
,
12403
,
12405
,
13401
,
13407
,
14401
,
14402
,
14403
,
14409
,
15401
,
15402
,
15403
,
15405
};
private
final
int
[]
blueWeapons
=
new
int
[]
{
11301
,
11302
,
11306
,
12301
,
12302
,
12305
,
13303
,
14301
,
14302
,
14304
,
15301
,
15302
,
15304
};
private
static
final
int
starglitterId
=
221
;
private
static
final
int
stardustId
=
222
;
private
int
[]
fallbackItems4Pool2Default
=
{
11401
,
11402
,
11403
,
11405
,
12401
,
12402
,
12403
,
12405
,
13401
,
13407
,
14401
,
14402
,
14403
,
14409
,
15401
,
15402
,
15403
,
15405
};
private
int
[]
fallbackItems5Pool2Default
=
{
11501
,
11502
,
12501
,
12502
,
13502
,
13505
,
14501
,
14502
,
15501
,
15502
};
public
GachaManager
(
GameServer
server
)
{
this
.
server
=
server
;
...
...
@@ -66,7 +65,7 @@ public class GachaManager {
return
gachaBanners
;
}
public
int
randomRange
(
int
min
,
int
max
)
{
public
int
randomRange
(
int
min
,
int
max
)
{
// Both are inclusive
return
ThreadLocalRandom
.
current
().
nextInt
(
max
-
min
+
1
)
+
min
;
}
...
...
@@ -83,6 +82,8 @@ public class GachaManager {
getGachaBanners
().
put
(
banner
.
getGachaType
(),
banner
);
}
Grasscutter
.
getLogger
().
info
(
"Banners successfully loaded."
);
this
.
cachedProto
=
createProto
();
}
else
{
Grasscutter
.
getLogger
().
error
(
"Unable to load banners. Banners size is 0."
);
...
...
@@ -93,111 +94,191 @@ public class GachaManager {
}
}
public
synchronized
void
doPulls
(
Player
player
,
int
gachaType
,
int
times
)
{
// Sanity check
if
(
times
!=
10
&&
times
!=
1
)
{
return
;
private
class
BannerPools
{
public
int
[]
rateUpItems4
;
public
int
[]
rateUpItems5
;
public
int
[]
fallbackItems4Pool1
;
public
int
[]
fallbackItems4Pool2
;
public
int
[]
fallbackItems5Pool1
;
public
int
[]
fallbackItems5Pool2
;
public
BannerPools
(
GachaBanner
banner
)
{
rateUpItems4
=
banner
.
getRateUpItems4
();
rateUpItems5
=
banner
.
getRateUpItems5
();
fallbackItems4Pool1
=
banner
.
getFallbackItems4Pool1
();
fallbackItems4Pool2
=
banner
.
getFallbackItems4Pool2
();
fallbackItems5Pool1
=
banner
.
getFallbackItems5Pool1
();
fallbackItems5Pool2
=
banner
.
getFallbackItems5Pool2
();
if
(
banner
.
getAutoStripRateUpFromFallback
())
{
fallbackItems4Pool1
=
Utils
.
setSubtract
(
fallbackItems4Pool1
,
rateUpItems4
);
fallbackItems4Pool2
=
Utils
.
setSubtract
(
fallbackItems4Pool2
,
rateUpItems4
);
fallbackItems5Pool1
=
Utils
.
setSubtract
(
fallbackItems5Pool1
,
rateUpItems5
);
fallbackItems5Pool2
=
Utils
.
setSubtract
(
fallbackItems5Pool2
,
rateUpItems5
);
}
if
(
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_WEAPON
).
getSize
()
+
times
>
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_WEAPON
).
getMaxCapacity
())
{
player
.
sendPacket
(
new
PacketDoGachaRsp
());
return
;
}
// Get banner
GachaBanner
banner
=
this
.
getGachaBanners
().
get
(
gachaType
);
if
(
banner
==
null
)
{
player
.
sendPacket
(
new
PacketDoGachaRsp
());
return
;
public
void
removeFromAllPools
(
int
[]
itemIds
)
{
rateUpItems4
=
Utils
.
setSubtract
(
rateUpItems4
,
itemIds
);
rateUpItems5
=
Utils
.
setSubtract
(
rateUpItems5
,
itemIds
);
fallbackItems4Pool1
=
Utils
.
setSubtract
(
fallbackItems4Pool1
,
itemIds
);
fallbackItems4Pool2
=
Utils
.
setSubtract
(
fallbackItems4Pool2
,
itemIds
);
fallbackItems5Pool1
=
Utils
.
setSubtract
(
fallbackItems5Pool1
,
itemIds
);
fallbackItems5Pool2
=
Utils
.
setSubtract
(
fallbackItems5Pool2
,
itemIds
);
}
// Spend currency
if
(
banner
.
getCostItem
()
>
0
)
{
GameItem
costItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
banner
.
getCostItem
());
if
(
costItem
==
null
||
costItem
.
getCount
()
<
times
)
{
return
;
}
player
.
getInventory
().
removeItem
(
costItem
,
times
);
private
synchronized
int
checkPlayerAvatarConstellationLevel
(
Player
player
,
int
itemId
)
{
// Maybe this would be useful in the Player class?
ItemData
itemData
=
GameData
.
getItemDataMap
().
get
(
itemId
);
if
((
itemData
==
null
)
||
(
itemData
.
getMaterialType
()
!=
MaterialType
.
MATERIAL_AVATAR
)){
return
-
2
;
// Not an Avatar
}
// Roll
PlayerGachaBannerInfo
gachaInfo
=
player
.
getGachaInfo
().
getBannerInfo
(
banner
);
IntList
wonItems
=
new
IntArrayList
(
times
);
for
(
int
i
=
0
;
i
<
times
;
i
++)
{
int
random
=
this
.
randomRange
(
1
,
10000
);
int
itemId
=
0
;
int
bonusYellowChance
=
gachaInfo
.
getPity5
()
>=
banner
.
getSoftPity
()
?
100
*
(
gachaInfo
.
getPity5
()
-
banner
.
getSoftPity
()
-
1
):
0
;
int
yellowChance
=
banner
.
getBaseYellowWeight
()
+
(
int
)
Math
.
floor
(
100
f
*
(
gachaInfo
.
getPity5
()
/
(
banner
.
getSoftPity
()
-
1
D
)))
+
bonusYellowChance
;
int
purpleChance
=
10000
-
(
banner
.
getBasePurpleWeight
()
+
(
int
)
Math
.
floor
(
790
f
*
(
gachaInfo
.
getPity4
()
/
8
f
)));
if
(
random
<=
yellowChance
||
gachaInfo
.
getPity5
()
>=
banner
.
getHardPity
())
{
if
(
banner
.
getRateUpItems1
().
length
>
0
)
{
int
eventChance
=
this
.
randomRange
(
1
,
100
);
if
(
eventChance
<=
banner
.
getEventChance
()
||
gachaInfo
.
getFailedFeaturedItemPulls
()
>=
1
)
{
itemId
=
getRandom
(
banner
.
getRateUpItems1
());
gachaInfo
.
setFailedFeaturedItemPulls
(
0
);
}
else
{
// Lost the 50/50... rip
gachaInfo
.
addFailedFeaturedItemPulls
(
1
);
Avatar
avatar
=
player
.
getAvatars
().
getAvatarById
((
itemId
%
1000
)
+
10000000
);
if
(
avatar
==
null
)
{
return
-
1
;
// Doesn't have
}
// Constellation
int
constLevel
=
avatar
.
getCoreProudSkillLevel
();
GameItem
constItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
itemId
+
100
);
constLevel
+=
(
constItem
==
null
)?
0
:
constItem
.
getCount
();
return
constLevel
;
}
if
(
itemId
==
0
)
{
int
typeChance
=
this
.
randomRange
(
banner
.
getBannerType
()
==
BannerType
.
WEAPON
?
2
:
1
,
banner
.
getBannerType
()
==
BannerType
.
EVENT
?
1
:
2
);
if
(
typeChance
==
1
)
{
itemId
=
getRandom
(
this
.
yellowAvatars
);
}
else
{
itemId
=
getRandom
(
this
.
yellowWeapons
);
private
synchronized
int
[]
removeC6FromPool
(
int
[]
itemPool
,
Player
player
)
{
IntList
temp
=
new
IntArrayList
();
for
(
int
itemId
:
itemPool
)
{
if
(
checkPlayerAvatarConstellationLevel
(
player
,
itemId
)
<
6
)
{
temp
.
add
(
itemId
);
}
}
return
temp
.
toIntArray
();
}
// Pity
gachaInfo
.
addPity4
(
1
);
gachaInfo
.
setPity5
(
0
);
}
else
if
(
random
>=
purpleChance
||
gachaInfo
.
getPity4
()
>=
9
)
{
if
(
banner
.
getRateUpItems2
().
length
>
0
)
{
int
eventChance
=
this
.
randomRange
(
1
,
100
);
if
(
eventChance
>=
50
)
{
itemId
=
getRandom
(
banner
.
getRateUpItems2
());
private
synchronized
int
drawRoulette
(
int
[]
weights
,
int
cutoff
)
{
// This follows the logic laid out in issue #183
// Simple weighted selection with an upper bound for the roll that cuts off trailing entries
// All weights must be >= 0
int
total
=
0
;
for
(
int
weight
:
weights
)
{
if
(
weight
<
0
)
{
throw
new
IllegalArgumentException
(
"Weights must be non-negative!"
);
}
total
+=
weight
;
}
int
roll
=
ThreadLocalRandom
.
current
().
nextInt
((
total
<
cutoff
)?
total
:
cutoff
);
int
subTotal
=
0
;
for
(
int
i
=
0
;
i
<
weights
.
length
;
i
++)
{
subTotal
+=
weights
[
i
];
if
(
roll
<
subTotal
)
{
return
i
;
}
if
(
itemId
==
0
)
{
int
typeChance
=
this
.
randomRange
(
banner
.
getBannerType
()
==
BannerType
.
WEAPON
?
2
:
1
,
banner
.
getBannerType
()
==
BannerType
.
EVENT
?
1
:
2
);
if
(
typeChance
==
1
)
{
itemId
=
getRandom
(
this
.
purpleAvatars
);
}
else
{
itemId
=
getRandom
(
this
.
purpleWeapons
);
}
// throw new IllegalStateException();
return
0
;
// This should only be reachable if total==0
}
// Pity
gachaInfo
.
addPity5
(
1
);
gachaInfo
.
setPity4
(
0
);
private
synchronized
int
doRarePull
(
int
[]
featured
,
int
[]
fallback1
,
int
[]
fallback2
,
int
rarity
,
GachaBanner
banner
,
PlayerGachaBannerInfo
gachaInfo
)
{
int
itemId
=
0
;
boolean
pullFeatured
=
(
gachaInfo
.
getFailedFeaturedItemPulls
(
rarity
)
>=
1
)
// Lost previous coinflip
||
(
this
.
randomRange
(
1
,
100
)
<=
banner
.
getEventChance
(
rarity
));
// Won this coinflip
if
(
pullFeatured
&&
(
featured
.
length
>
0
))
{
itemId
=
getRandom
(
featured
);
gachaInfo
.
setFailedFeaturedItemPulls
(
rarity
,
0
);
}
else
{
gachaInfo
.
addFailedFeaturedItemPulls
(
rarity
,
1
);
if
(
fallback1
.
length
<
1
)
{
if
(
fallback2
.
length
<
1
)
{
itemId
=
getRandom
((
rarity
==
5
)?
fallbackItems5Pool2Default
:
fallbackItems4Pool2Default
);
}
else
{
itemId
=
getRandom
(
this
.
blueWeapons
);
itemId
=
getRandom
(
fallback2
);
}
}
else
if
(
fallback2
.
length
<
1
)
{
itemId
=
getRandom
(
fallback1
);
}
else
{
// Both pools are possible, use the pool balancer
int
pityPool1
=
banner
.
getPoolBalanceWeight
(
rarity
,
gachaInfo
.
getPityPool
(
rarity
,
1
));
int
pityPool2
=
banner
.
getPoolBalanceWeight
(
rarity
,
gachaInfo
.
getPityPool
(
rarity
,
2
));
int
chosenPool
=
switch
((
pityPool1
>=
pityPool2
)?
1
:
0
)
{
// Larger weight must come first for the hard cutoff to function correctly
case
1
->
1
+
drawRoulette
(
new
int
[]
{
pityPool1
,
pityPool2
},
10000
);
default
->
2
-
drawRoulette
(
new
int
[]
{
pityPool2
,
pityPool1
},
10000
);
};
itemId
=
switch
(
chosenPool
)
{
case
1
:
gachaInfo
.
setPityPool
(
rarity
,
1
,
0
);
yield
getRandom
(
fallback1
);
default
:
gachaInfo
.
setPityPool
(
rarity
,
2
,
0
);
yield
getRandom
(
fallback2
);
};
}
}
return
itemId
;
}
private
synchronized
int
doPull
(
GachaBanner
banner
,
PlayerGachaBannerInfo
gachaInfo
,
BannerPools
pools
)
{
// Pre-increment all pity pools (yes this makes all calculations assume 1-indexed pity)
gachaInfo
.
incPityAll
();
int
[]
weights
=
{
banner
.
getWeight
(
5
,
gachaInfo
.
getPity5
()),
banner
.
getWeight
(
4
,
gachaInfo
.
getPity4
()),
10000
};
int
levelWon
=
5
-
drawRoulette
(
weights
,
10000
);
return
switch
(
levelWon
)
{
case
5
:
gachaInfo
.
setPity5
(
0
);
yield
doRarePull
(
pools
.
rateUpItems5
,
pools
.
fallbackItems5Pool1
,
pools
.
fallbackItems5Pool2
,
5
,
banner
,
gachaInfo
);
case
4
:
gachaInfo
.
setPity4
(
0
);
yield
doRarePull
(
pools
.
rateUpItems4
,
pools
.
fallbackItems4Pool1
,
pools
.
fallbackItems4Pool2
,
4
,
banner
,
gachaInfo
);
default
:
yield
getRandom
(
banner
.
getFallbackItems3
());
};
}
// Pity
gachaInfo
.
addPity4
(
1
);
gachaInfo
.
addPity5
(
1
);
public
synchronized
void
doPulls
(
Player
player
,
int
gachaType
,
int
times
)
{
// Sanity check
if
(
times
!=
10
&&
times
!=
1
)
{
return
;
}
Inventory
inventory
=
player
.
getInventory
();
if
(
inventory
.
getInventoryTab
(
ItemType
.
ITEM_WEAPON
).
getSize
()
+
times
>
inventory
.
getInventoryTab
(
ItemType
.
ITEM_WEAPON
).
getMaxCapacity
())
{
player
.
sendPacket
(
new
PacketDoGachaRsp
());
return
;
}
// Get banner
GachaBanner
banner
=
this
.
getGachaBanners
().
get
(
gachaType
);
if
(
banner
==
null
)
{
player
.
sendPacket
(
new
PacketDoGachaRsp
());
return
;
}
// Add winning item
wonItems
.
add
(
itemId
);
// Spend currency
ItemParamData
cost
=
banner
.
getCost
(
times
);
if
(
cost
.
getCount
()
>
0
&&
!
inventory
.
payItem
(
cost
))
{
player
.
sendPacket
(
new
PacketDoGachaRsp
());
return
;
}
// Add to character
PlayerGachaBannerInfo
gachaInfo
=
player
.
getGachaInfo
().
getBannerInfo
(
banner
);
BannerPools
pools
=
new
BannerPools
(
banner
);
List
<
GachaItem
>
list
=
new
ArrayList
<>();
int
stardust
=
0
,
starglitter
=
0
;
for
(
int
itemId
:
wonItems
)
{
if
(
banner
.
getRemoveC6FromPool
())
{
// The ultimate form of pity (non-vanilla)
pools
.
rateUpItems4
=
removeC6FromPool
(
pools
.
rateUpItems4
,
player
);
pools
.
rateUpItems5
=
removeC6FromPool
(
pools
.
rateUpItems5
,
player
);
pools
.
fallbackItems4Pool1
=
removeC6FromPool
(
pools
.
fallbackItems4Pool1
,
player
);
pools
.
fallbackItems4Pool2
=
removeC6FromPool
(
pools
.
fallbackItems4Pool2
,
player
);
pools
.
fallbackItems5Pool1
=
removeC6FromPool
(
pools
.
fallbackItems5Pool1
,
player
);
pools
.
fallbackItems5Pool2
=
removeC6FromPool
(
pools
.
fallbackItems5Pool2
,
player
);
}
for
(
int
i
=
0
;
i
<
times
;
i
++)
{
// Roll
int
itemId
=
doPull
(
banner
,
gachaInfo
,
pools
);
ItemData
itemData
=
GameData
.
getItemDataMap
().
get
(
itemId
);
if
(
itemData
==
null
)
{
continue
;
continue
;
// Maybe we should bail out if an item fails instead of rolling the rest?
}
// Write gacha record
...
...
@@ -210,57 +291,47 @@ public class GachaManager {
boolean
isTransferItem
=
false
;
// Const check
if
(
itemData
.
getMaterialType
()
==
MaterialType
.
MATERIAL_AVATAR
)
{
int
avatarId
=
(
itemData
.
getId
()
%
1000
)
+
10000000
;
Avatar
avatar
=
player
.
getAvatars
().
getAvatarById
(
avatarId
);
if
(
avatar
!=
null
)
{
int
constLevel
=
avatar
.
getCoreProudSkillLevel
();
int
constItemId
=
itemData
.
getId
()
+
100
;
GameItem
constItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
constItemId
);
if
(
constItem
!=
null
)
{
constLevel
+=
constItem
.
getCount
();
}
if
(
constLevel
<
6
)
{
// Not max const
addStarglitter
=
2
;
// Add 1 const
gachaItem
.
addTransferItems
(
GachaTransferItem
.
newBuilder
().
setItem
(
ItemParam
.
newBuilder
().
setItemId
(
constItemId
).
setCount
(
1
)).
setIsTransferItemNew
(
constItem
==
null
));
player
.
getInventory
().
addItem
(
constItemId
,
1
);
}
else
{
// Is max const
addStarglitter
=
5
;
}
if
(
itemData
.
getRankLevel
()
==
5
)
{
addStarglitter
*=
5
;
}
isTransferItem
=
true
;
}
else
{
// New
gachaItem
.
setIsGachaItemNew
(
true
);
}
}
else
{
// Is weapon
int
constellation
=
checkPlayerAvatarConstellationLevel
(
player
,
itemId
);
switch
(
constellation
)
{
case
-
2
:
// Is weapon
switch
(
itemData
.
getRankLevel
())
{
case
5
->
addStarglitter
=
10
;
case
4
->
addStarglitter
=
2
;
case
3
->
addStardust
=
15
;
default
->
addStardust
=
15
;
}
break
;
case
-
1
:
// New character
gachaItem
.
setIsGachaItemNew
(
true
);
break
;
default
:
if
(
constellation
>=
6
)
{
// C6, give consolation starglitter
addStarglitter
=
(
itemData
.
getRankLevel
()==
5
)?
25
:
5
;
}
else
{
// C0-C5, give constellation item
if
(
banner
.
getRemoveC6FromPool
()
&&
constellation
==
5
)
{
// New C6, remove it from the pools so we don't get C7 in a 10pull
pools
.
removeFromAllPools
(
new
int
[]
{
itemId
});
}
addStarglitter
=
(
itemData
.
getRankLevel
()==
5
)?
10
:
2
;
int
constItemId
=
itemId
+
100
;
GameItem
constItem
=
inventory
.
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
constItemId
);
gachaItem
.
addTransferItems
(
GachaTransferItem
.
newBuilder
().
setItem
(
ItemParam
.
newBuilder
().
setItemId
(
constItemId
).
setCount
(
1
)).
setIsTransferItemNew
(
constItem
==
null
));
inventory
.
addItem
(
constItemId
,
1
);
}
isTransferItem
=
true
;
break
;
}
// Create item
GameItem
item
=
new
GameItem
(
itemData
);
gachaItem
.
setGachaItem
(
item
.
toItemParam
());
player
.
getI
nventory
()
.
addItem
(
item
);
i
nventory
.
addItem
(
item
);
stardust
+=
addStardust
;
starglitter
+=
addStarglitter
;
if
(
addStardust
>
0
)
{
gachaItem
.
addTokenItemList
(
ItemParam
.
newBuilder
().
setItemId
(
stardustId
).
setCount
(
addStardust
));
}
if
(
addStarglitter
>
0
)
{
}
if
(
addStarglitter
>
0
)
{
ItemParam
starglitterParam
=
ItemParam
.
newBuilder
().
setItemId
(
starglitterId
).
setCount
(
addStarglitter
).
build
();
if
(
isTransferItem
)
{
gachaItem
.
addTransferItems
(
GachaTransferItem
.
newBuilder
().
setItem
(
starglitterParam
));
...
...
@@ -273,9 +344,10 @@ public class GachaManager {
// Add stardust/starglitter
if
(
stardust
>
0
)
{
player
.
getInventory
().
addItem
(
stardustId
,
stardust
);
}
if
(
starglitter
>
0
)
{
player
.
getInventory
().
addItem
(
starglitterId
,
starglitter
);
inventory
.
addItem
(
stardustId
,
stardust
);
}
if
(
starglitter
>
0
)
{
inventory
.
addItem
(
starglitterId
,
starglitter
);
}
// Packets
...
...
src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java
View file @
a2ff8c84
...
...
@@ -7,6 +7,11 @@ public class PlayerGachaBannerInfo {
private
int
pity5
=
0
;
private
int
pity4
=
0
;
private
int
failedFeaturedItemPulls
=
0
;
private
int
failedFeatured4ItemPulls
=
0
;
private
int
pity5Pool1
=
0
;
private
int
pity5Pool2
=
0
;
private
int
pity4Pool1
=
0
;
private
int
pity4Pool2
=
0
;
public
int
getPity5
()
{
return
pity5
;
...
...
@@ -32,15 +37,82 @@ public class PlayerGachaBannerInfo {
this
.
pity4
+=
amount
;
}
public
int
getFailedFeaturedItemPulls
()
{
return
failedFeaturedItemPulls
;
public
int
getFailedFeaturedItemPulls
(
int
rarity
)
{
return
switch
(
rarity
)
{
case
4
->
failedFeatured4ItemPulls
;
default
->
failedFeaturedItemPulls
;
// 5
};
}
public
void
setFailedFeaturedItemPulls
(
int
failedEventCharacterPulls
)
{
this
.
failedFeaturedItemPulls
=
failedEventCharacterPulls
;
public
void
setFailedFeaturedItemPulls
(
int
rarity
,
int
amount
)
{
switch
(
rarity
)
{
case
4
->
failedFeatured4ItemPulls
=
amount
;
default
->
failedFeaturedItemPulls
=
amount
;
// 5
};
}
public
void
addFailedFeaturedItemPulls
(
int
amount
)
{
failedFeaturedItemPulls
+=
amount
;
public
void
addFailedFeaturedItemPulls
(
int
rarity
,
int
amount
)
{
switch
(
rarity
)
{
case
4
->
failedFeatured4ItemPulls
+=
amount
;
default
->
failedFeaturedItemPulls
+=
amount
;
// 5
};
}
public
int
getPityPool
(
int
rarity
,
int
pool
)
{
return
switch
(
rarity
)
{
case
4
->
switch
(
pool
)
{
case
1
->
pity4Pool1
;
default
->
pity4Pool2
;
};
default
->
switch
(
pool
)
{
case
1
->
pity5Pool1
;
default
->
pity5Pool2
;
};
};
}
public
void
setPityPool
(
int
rarity
,
int
pool
,
int
amount
)
{
switch
(
rarity
)
{
case
4
:
switch
(
pool
)
{
case
1
->
pity4Pool1
=
amount
;
default
->
pity4Pool2
=
amount
;
};
break
;
case
5
:
default
:
switch
(
pool
)
{
case
1
->
pity5Pool1
=
amount
;
default
->
pity5Pool2
=
amount
;
};
break
;
};
}
public
void
addPityPool
(
int
rarity
,
int
pool
,
int
amount
)
{
switch
(
rarity
)
{
case
4
:
switch
(
pool
)
{
case
1
->
pity4Pool1
+=
amount
;
default
->
pity4Pool2
+=
amount
;
};
break
;
case
5
:
default
:
switch
(
pool
)
{
case
1
->
pity5Pool1
+=
amount
;
default
->
pity5Pool2
+=
amount
;
};
break
;
};
}
public
void
incPityAll
()
{
pity4
++;
pity5
++;
pity4Pool1
++;
pity4Pool2
++;
pity5Pool1
++;
pity5Pool2
++;
}
}
src/main/java/emu/grasscutter/game/inventory/Inventory.java
View file @
a2ff8c84
...
...
@@ -7,6 +7,7 @@ import java.util.List;
import
emu.grasscutter.GameConstants
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.data.common.ItemParamData
;
import
emu.grasscutter.data.def.AvatarCostumeData
;
import
emu.grasscutter.data.def.AvatarData
;
import
emu.grasscutter.data.def.AvatarFlycloakData
;
...
...
@@ -257,6 +258,64 @@ public class Inventory implements Iterable<GameItem> {
}
}
private
int
getVirtualItemCount
(
int
itemId
)
{
switch
(
itemId
)
{
case
201
:
// Primogem
return
player
.
getPrimogems
();
case
202
:
// Mora
return
player
.
getMora
();
case
203
:
// Genesis Crystals
return
player
.
getCrystals
();
default
:
GameItem
item
=
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
itemId
);
// What if we ever want to operate on weapons/relics/furniture? :S
return
(
item
==
null
)
?
0
:
item
.
getCount
();
}
}
public
boolean
payItem
(
int
id
,
int
count
)
{
return
payItem
(
new
ItemParamData
(
id
,
count
));
}
public
boolean
payItem
(
ItemParamData
costItem
)
{
return
payItems
(
new
ItemParamData
[]
{
costItem
},
1
,
null
);
}
public
boolean
payItems
(
ItemParamData
[]
costItems
)
{
return
payItems
(
costItems
,
1
,
null
);
}
public
boolean
payItems
(
ItemParamData
[]
costItems
,
int
quantity
)
{
return
payItems
(
costItems
,
quantity
,
null
);
}
public
synchronized
boolean
payItems
(
ItemParamData
[]
costItems
,
int
quantity
,
ActionReason
reason
)
{
// Make sure player has requisite items
for
(
ItemParamData
cost
:
costItems
)
{
if
(
getVirtualItemCount
(
cost
.
getId
())
<
(
cost
.
getCount
()
*
quantity
))
{
return
false
;
}
}
// All costs are satisfied, now remove them all
for
(
ItemParamData
cost
:
costItems
)
{
switch
(
cost
.
getId
())
{
case
201
->
// Primogem
player
.
setPrimogems
(
player
.
getPrimogems
()
-
(
cost
.
getCount
()
*
quantity
));
case
202
->
// Mora
player
.
setMora
(
player
.
getMora
()
-
(
cost
.
getCount
()
*
quantity
));
case
203
->
// Genesis Crystals
player
.
setCrystals
(
player
.
getCrystals
()
-
(
cost
.
getCount
()
*
quantity
));
default
->
removeItem
(
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
cost
.
getId
()),
cost
.
getCount
()
*
quantity
);
}
}
if
(
reason
!=
null
)
{
// Do we need these?
// getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason));
}
// getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems));
return
true
;
}
public
void
removeItems
(
List
<
GameItem
>
items
)
{
// TODO Bulk delete
for
(
GameItem
item
:
items
)
{
...
...
src/main/java/emu/grasscutter/game/managers/InventoryManager.java
View file @
a2ff8c84
package
emu.grasscutter.game.managers
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.stream.Collectors
;
...
...
@@ -38,6 +39,8 @@ public class InventoryManager {
private
final
static
int
RELIC_MATERIAL_1
=
105002
;
// Sanctifying Unction
private
final
static
int
RELIC_MATERIAL_2
=
105003
;
// Sanctifying Essence
private
final
static
int
RELIC_MATERIAL_EXP_1
=
2500
;
// Sanctifying Unction
private
final
static
int
RELIC_MATERIAL_EXP_2
=
10000
;
// Sanctifying Essence
private
final
static
int
WEAPON_ORE_1
=
104011
;
// Enhancement Ore
private
final
static
int
WEAPON_ORE_2
=
104012
;
// Fine Enhancement Ore
...
...
@@ -85,6 +88,7 @@ public class InventoryManager {
int
moraCost
=
0
;
int
expGain
=
0
;
List
<
GameItem
>
foodRelics
=
new
ArrayList
<
GameItem
>();
for
(
long
guid
:
foodRelicList
)
{
// Add to delete queue
GameItem
food
=
player
.
getInventory
().
getItemByGuid
(
guid
);
...
...
@@ -96,23 +100,21 @@ public class InventoryManager {
expGain
+=
food
.
getItemData
().
getBaseConvExp
();
// Feeding artifact with exp already
if
(
food
.
getTotalExp
()
>
0
)
{
expGain
+=
(
int
)
Math
.
floor
(
food
.
getTotalExp
()
*
.
8
f
)
;
expGain
+=
(
food
.
getTotalExp
()
*
4
)
/
5
;
}
foodRelics
.
add
(
food
);
}
List
<
ItemParamData
>
payList
=
new
ArrayList
<
ItemParamData
>();
for
(
ItemParam
itemParam
:
list
)
{
GameItem
food
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
itemParam
.
getItemId
());
if
(
food
==
null
||
food
.
getItemData
().
getMaterialType
()
!=
MaterialType
.
MATERIAL_RELIQUARY_MATERIAL
)
{
continue
;
}
int
amount
=
Math
.
min
(
food
.
getCount
(),
itemParam
.
getCount
());
int
gain
=
0
;
if
(
food
.
getItemId
()
==
RELIC_MATERIAL_2
)
{
gain
=
10000
*
amount
;
}
else
if
(
food
.
getItemId
()
==
RELIC_MATERIAL_1
)
{
gain
=
2500
*
amount
;
}
int
amount
=
itemParam
.
getCount
();
// Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
int
gain
=
amount
*
switch
(
itemParam
.
getItemId
())
{
case
RELIC_MATERIAL_1
->
RELIC_MATERIAL_EXP_1
;
case
RELIC_MATERIAL_2
->
RELIC_MATERIAL_EXP_2
;
default
->
0
;
};
expGain
+=
gain
;
moraCost
+=
gain
;
payList
.
add
(
new
ItemParamData
(
itemParam
.
getItemId
(),
itemParam
.
getCount
()));
}
// Make sure exp gain is valid
...
...
@@ -120,28 +122,14 @@ public class InventoryManager {
return
;
}
// Check mora
if
(
player
.
getMora
()
<
moraCost
)
{
// Confirm payment of materials and mora (assume food relics are payable afterwards)
payList
.
add
(
new
ItemParamData
(
202
,
moraCost
));
if
(!
player
.
getInventory
().
payItems
(
payList
.
toArray
(
new
ItemParamData
[
0
])))
{
return
;
}
player
.
setMora
(
player
.
getMora
()
-
moraCost
);
// Consume food items
for
(
long
guid
:
foodRelicList
)
{
GameItem
food
=
player
.
getInventory
().
getItemByGuid
(
guid
);
if
(
food
==
null
||
!
food
.
isDestroyable
())
{
continue
;
}
player
.
getInventory
().
removeItem
(
food
);
}
for
(
ItemParam
itemParam
:
list
)
{
GameItem
food
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
itemParam
.
getItemId
());
if
(
food
==
null
||
food
.
getItemData
().
getMaterialType
()
!=
MaterialType
.
MATERIAL_RELIQUARY_MATERIAL
)
{
continue
;
}
int
amount
=
Math
.
min
(
food
.
getCount
(),
itemParam
.
getCount
());
player
.
getInventory
().
removeItem
(
food
,
amount
);
}
// Consume food relics
player
.
getInventory
().
removeItems
(
foodRelics
);
// Implement random rate boost
int
rate
=
1
;
...
...
@@ -231,22 +219,16 @@ public class InventoryManager {
}
expGain
+=
food
.
getItemData
().
getWeaponBaseExp
();
if
(
food
.
getTotalExp
()
>
0
)
{
expGain
+=
(
int
)
Math
.
floor
(
food
.
getTotalExp
()
*
.
8
f
)
;
expGain
+=
(
food
.
getTotalExp
()
*
4
)
/
5
;
}
}
for
(
ItemParam
param
:
itemParamList
)
{
GameItem
food
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
param
.
getItemId
());
if
(
food
==
null
||
food
.
getItemData
().
getMaterialType
()
!=
MaterialType
.
MATERIAL_WEAPON_EXP_STONE
)
{
continue
;
}
int
amount
=
Math
.
min
(
param
.
getCount
(),
food
.
getCount
());
if
(
food
.
getItemId
()
==
WEAPON_ORE_3
)
{
expGain
+=
10000
*
amount
;
}
else
if
(
food
.
getItemId
()
==
WEAPON_ORE_2
)
{
expGain
+=
2000
*
amount
;
}
else
if
(
food
.
getItemId
()
==
WEAPON_ORE_1
)
{
expGain
+=
400
*
amount
;
}
expGain
+=
param
.
getCount
()
*
switch
(
param
.
getItemId
())
{
case
WEAPON_ORE_1
->
WEAPON_ORE_EXP_1
;
case
WEAPON_ORE_2
->
WEAPON_ORE_EXP_2
;
case
WEAPON_ORE_3
->
WEAPON_ORE_EXP_3
;
default
->
0
;
};
}
// Try
...
...
@@ -288,65 +270,45 @@ public class InventoryManager {
}
// Get exp gain
int
expGain
=
0
,
moraCost
=
0
;
int
expGain
=
0
,
expGainFree
=
0
;
List
<
GameItem
>
foodWeapons
=
new
ArrayList
<
GameItem
>();
for
(
long
guid
:
foodWeaponGuidList
)
{
GameItem
food
=
player
.
getInventory
().
getItemByGuid
(
guid
);
if
(
food
==
null
||
!
food
.
isDestroyable
())
{
continue
;
}
expGain
+=
food
.
getItemData
().
getWeaponBaseExp
();
moraCost
+=
(
int
)
Math
.
floor
(
food
.
getItemData
().
getWeaponBaseExp
()
*
.
1
f
);
if
(
food
.
getTotalExp
()
>
0
)
{
expGain
+=
(
int
)
Math
.
floor
(
food
.
getTotalExp
()
*
.
8
f
);
expGain
Free
+=
(
food
.
getTotalExp
()
*
4
)
/
5
;
// No tax :D
}
foodWeapons
.
add
(
food
);
}
List
<
ItemParamData
>
payList
=
new
ArrayList
<
ItemParamData
>();
for
(
ItemParam
param
:
itemParamList
)
{
GameItem
food
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
param
.
getItemId
());
if
(
food
==
null
||
food
.
getItemData
().
getMaterialType
()
!=
MaterialType
.
MATERIAL_WEAPON_EXP_STONE
)
{
continue
;
}
int
amount
=
Math
.
min
(
param
.
getCount
(),
food
.
getCount
());
int
gain
=
0
;
if
(
food
.
getItemId
()
==
WEAPON_ORE_3
)
{
gain
=
10000
*
amount
;
}
else
if
(
food
.
getItemId
()
==
WEAPON_ORE_2
)
{
gain
=
2000
*
amount
;
}
else
if
(
food
.
getItemId
()
==
WEAPON_ORE_1
)
{
gain
=
400
*
amount
;
}
int
amount
=
param
.
getCount
();
// Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order
int
gain
=
amount
*
switch
(
param
.
getItemId
())
{
case
WEAPON_ORE_1
->
WEAPON_ORE_EXP_1
;
case
WEAPON_ORE_2
->
WEAPON_ORE_EXP_2
;
case
WEAPON_ORE_3
->
WEAPON_ORE_EXP_3
;
default
->
0
;
};
expGain
+=
gain
;
moraCost
+=
(
int
)
Math
.
floor
(
gain
*
.
1
f
);
payList
.
add
(
new
ItemParamData
(
param
.
getItemId
(),
amount
)
);
}
// Make sure exp gain is valid
int
moraCost
=
expGain
/
10
;
expGain
+=
expGainFree
;
if
(
expGain
<=
0
)
{
return
;
}
// Mora check
if
(
player
.
getMora
()
>=
moraCost
)
{
player
.
setMora
(
player
.
getMora
()
-
moraCost
);
}
else
{
// Confirm payment of materials and mora (assume food weapons are payable afterwards)
payList
.
add
(
new
ItemParamData
(
202
,
moraCost
));
if
(!
player
.
getInventory
().
payItems
(
payList
.
toArray
(
new
ItemParamData
[
0
])))
{
return
;
}
// Consume weapon/items used to feed
for
(
long
guid
:
foodWeaponGuidList
)
{
GameItem
food
=
player
.
getInventory
().
getItemByGuid
(
guid
);
if
(
food
==
null
||
!
food
.
isDestroyable
())
{
continue
;
}
player
.
getInventory
().
removeItem
(
food
);
}
for
(
ItemParam
param
:
itemParamList
)
{
GameItem
food
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
param
.
getItemId
());
if
(
food
==
null
||
food
.
getItemData
().
getMaterialType
()
!=
MaterialType
.
MATERIAL_WEAPON_EXP_STONE
)
{
continue
;
}
int
amount
=
Math
.
min
(
param
.
getCount
(),
food
.
getCount
());
player
.
getInventory
().
removeItem
(
food
,
amount
);
}
player
.
getInventory
().
removeItems
(
foodWeapons
);
// Level up
int
maxLevel
=
promoteData
.
getUnlockMaxLevel
();
...
...
@@ -393,7 +355,7 @@ public class InventoryManager {
player
.
sendPacket
(
new
PacketWeaponUpgradeRsp
(
weapon
,
oldLevel
,
leftovers
));
}
private
List
<
ItemParam
>
getLeftoverOres
(
floa
t
leftover
)
{
private
List
<
ItemParam
>
getLeftoverOres
(
in
t
leftover
)
{
List
<
ItemParam
>
leftoverOreList
=
new
ArrayList
<>(
3
);
if
(
leftover
<
WEAPON_ORE_EXP_1
)
{
...
...
@@ -401,11 +363,11 @@ public class InventoryManager {
}
// Get leftovers
int
ore3
=
(
int
)
Math
.
floor
(
leftover
/
WEAPON_ORE_EXP_3
)
;
int
ore3
=
leftover
/
WEAPON_ORE_EXP_3
;
leftover
=
leftover
%
WEAPON_ORE_EXP_3
;
int
ore2
=
(
int
)
Math
.
floor
(
leftover
/
WEAPON_ORE_EXP_2
)
;
int
ore2
=
leftover
/
WEAPON_ORE_EXP_2
;
leftover
=
leftover
%
WEAPON_ORE_EXP_2
;
int
ore1
=
(
int
)
Math
.
floor
(
leftover
/
WEAPON_ORE_EXP_1
)
;
int
ore1
=
leftover
/
WEAPON_ORE_EXP_1
;
if
(
ore3
>
0
)
{
leftoverOreList
.
add
(
ItemParam
.
newBuilder
().
setItemId
(
WEAPON_ORE_3
).
setCount
(
ore3
).
build
());
...
...
@@ -496,27 +458,16 @@ public class InventoryManager {
return
;
}
//
Make sure player has promote items
for
(
ItemParamData
cost
:
nextPromoteData
.
getCostItems
()
)
{
GameItem
feedItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
cost
.
getId
());
if
(
feedItem
==
null
||
feedItem
.
getCount
()
<
cost
.
getCount
())
{
return
;
//
Pay materials and mora if possible
ItemParamData
[]
cost
s
=
nextPromoteData
.
getCostItems
()
;
// Can this be null?
if
(
nextPromoteData
.
getCoinCost
()
>
0
)
{
costs
=
Arrays
.
copyOf
(
costs
,
costs
.
length
+
1
);
costs
[
costs
.
length
-
1
]
=
new
ItemParamData
(
202
,
nextPromoteData
.
getCoinCost
())
;
}
}
// Mora check
if
(
player
.
getMora
()
>=
nextPromoteData
.
getCoinCost
())
{
player
.
setMora
(
player
.
getMora
()
-
nextPromoteData
.
getCoinCost
());
}
else
{
if
(!
player
.
getInventory
().
payItems
(
costs
))
{
return
;
}
// Consume promote filler items
for
(
ItemParamData
cost
:
nextPromoteData
.
getCostItems
())
{
GameItem
feedItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
cost
.
getId
());
player
.
getInventory
().
removeItem
(
feedItem
,
cost
.
getCount
());
}
int
oldPromoteLevel
=
weapon
.
getPromoteLevel
();
weapon
.
setPromoteLevel
(
nextPromoteLevel
);
weapon
.
save
();
...
...
@@ -552,27 +503,16 @@ public class InventoryManager {
return
;
}
// Make sure player has cost items
for
(
ItemParamData
cost
:
nextPromoteData
.
getCostItems
())
{
GameItem
feedItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
cost
.
getId
());
if
(
feedItem
==
null
||
feedItem
.
getCount
()
<
cost
.
getCount
())
{
return
;
}
// Pay materials and mora if possible
ItemParamData
[]
costs
=
nextPromoteData
.
getCostItems
();
// Can this be null?
if
(
nextPromoteData
.
getCoinCost
()
>
0
)
{
costs
=
Arrays
.
copyOf
(
costs
,
costs
.
length
+
1
);
costs
[
costs
.
length
-
1
]
=
new
ItemParamData
(
202
,
nextPromoteData
.
getCoinCost
());
}
// Mora check
if
(
player
.
getMora
()
>=
nextPromoteData
.
getCoinCost
())
{
player
.
setMora
(
player
.
getMora
()
-
nextPromoteData
.
getCoinCost
());
}
else
{
if
(!
player
.
getInventory
().
payItems
(
costs
))
{
return
;
}
// Consume promote filler items
for
(
ItemParamData
cost
:
nextPromoteData
.
getCostItems
())
{
GameItem
feedItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
cost
.
getId
());
player
.
getInventory
().
removeItem
(
feedItem
,
cost
.
getCount
());
}
// Update promote level
avatar
.
setPromoteLevel
(
nextPromoteLevel
);
...
...
@@ -616,35 +556,26 @@ public class InventoryManager {
return
;
}
GameItem
feedItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
itemId
);
if
(
feedItem
==
null
||
feedItem
.
getItemData
().
getMaterialType
()
!=
MaterialType
.
MATERIAL_EXP_FRUIT
||
feedItem
.
getCount
()
<
count
)
{
return
;
}
// Calc exp
int
expGain
=
0
,
moraCost
=
0
;
int
expGain
=
switch
(
itemId
)
{
case
AVATAR_BOOK_1
->
AVATAR_BOOK_EXP_1
*
count
;
case
AVATAR_BOOK_2
->
AVATAR_BOOK_EXP_2
*
count
;
case
AVATAR_BOOK_3
->
AVATAR_BOOK_EXP_3
*
count
;
default
->
0
;
};
// TODO clean up
if
(
itemId
==
AVATAR_BOOK_3
)
{
expGain
=
AVATAR_BOOK_EXP_3
*
count
;
}
else
if
(
itemId
==
AVATAR_BOOK_2
)
{
expGain
=
AVATAR_BOOK_EXP_2
*
count
;
}
else
if
(
itemId
==
AVATAR_BOOK_1
)
{
expGain
=
AVATAR_BOOK_EXP_1
*
count
;
// Sanity check
if
(
expGain
<=
0
)
{
return
;
}
moraCost
=
(
int
)
Math
.
floor
(
expGain
*
.
2
f
);
//
Mora
check
i
f
(
player
.
getMora
()
>=
moraCost
)
{
player
.
setMora
(
player
.
getMora
()
-
moraCost
);
}
else
{
//
Payment
check
i
nt
moraCost
=
expGain
/
5
;
ItemParamData
[]
costItems
=
new
ItemParamData
[]
{
new
ItemParamData
(
itemId
,
count
),
new
ItemParamData
(
202
,
moraCost
)
}
;
if
(!
player
.
getInventory
().
payItems
(
costItems
))
{
return
;
}
// Consume items
player
.
getInventory
().
removeItem
(
feedItem
,
count
);
// Level up
upgradeAvatar
(
player
,
avatar
,
promoteData
,
expGain
);
}
...
...
@@ -764,33 +695,15 @@ public class InventoryManager {
return
;
}
// Make sure player has cost items
for
(
ItemParamData
cost
:
proudSkill
.
getCostItems
())
{
if
(
cost
.
getId
()
==
0
)
{
continue
;
}
GameItem
feedItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
cost
.
getId
());
if
(
feedItem
==
null
||
feedItem
.
getCount
()
<
cost
.
getCount
())
{
return
;
// Pay materials and mora if possible
List
<
ItemParamData
>
costs
=
new
ArrayList
<
ItemParamData
>(
proudSkill
.
getCostItems
());
// Can this be null?
if
(
proudSkill
.
getCoinCost
()
>
0
)
{
costs
.
add
(
new
ItemParamData
(
202
,
proudSkill
.
getCoinCost
()));
}
}
// Mora check
if
(
player
.
getMora
()
>=
proudSkill
.
getCoinCost
())
{
player
.
setMora
(
player
.
getMora
()
-
proudSkill
.
getCoinCost
());
}
else
{
if
(!
player
.
getInventory
().
payItems
(
costs
.
toArray
(
new
ItemParamData
[
0
])))
{
return
;
}
// Consume promote filler items
for
(
ItemParamData
cost
:
proudSkill
.
getCostItems
())
{
if
(
cost
.
getId
()
==
0
)
{
continue
;
}
GameItem
feedItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
cost
.
getId
());
player
.
getInventory
().
removeItem
(
feedItem
,
cost
.
getCount
());
}
// Upgrade skill
avatar
.
getSkillLevelMap
().
put
(
skillId
,
nextLevel
);
avatar
.
save
();
...
...
@@ -822,14 +735,11 @@ public class InventoryManager {
return
;
}
GameItem
cost
Item
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
talentData
.
getMainCostItemId
());
if
(
costItem
==
null
||
costItem
.
getCount
()
<
talentData
.
getMainCostItem
Count
(
))
{
// Pay
co
n
st
ellation item if possible
if
(
!
player
.
getInventory
().
payItem
(
talentData
.
getMainCostItem
Id
(),
1
))
{
return
;
}
// Consume item
player
.
getInventory
().
removeItem
(
costItem
,
talentData
.
getMainCostItemCount
());
// Apply + recalc
avatar
.
getTalentIdList
().
add
(
talentData
.
getId
());
avatar
.
setCoreProudSkillLevel
(
currentTalentLevel
+
1
);
...
...
src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMark.java
View file @
a2ff8c84
package
emu.grasscutter.game.managers.MapMarkManager
;
import
dev.morphia.annotations.Entity
;
import
emu.grasscutter.net.proto.MapMarkFromTypeOuterClass
;
import
emu.grasscutter.net.proto.MapMarkPointOuterClass
;
import
emu.grasscutter.net.proto.MapMarkPointTypeOuterClass
;
import
emu.grasscutter.net.proto.MapMarkFromTypeOuterClass
.MapMarkFromType
;
import
emu.grasscutter.net.proto.MapMarkPointOuterClass
.MapMarkPoint
;
import
emu.grasscutter.net.proto.MapMarkPointTypeOuterClass
.MapMarkPointType
;
import
emu.grasscutter.utils.Position
;
@Entity
public
class
MapMark
{
private
int
sceneId
;
private
String
name
;
private
Position
position
;
private
MapMarkPointTypeOuterClass
.
MapMarkPointType
pointType
;
private
int
monsterId
=
0
;
private
MapMarkFromTypeOuterClass
.
MapMarkFromType
fromType
;
private
int
questId
=
7
;
public
MapMark
(
Position
position
,
MapMarkPointTypeOuterClass
.
MapMarkPointType
type
)
{
this
.
position
=
position
;
}
public
MapMark
(
MapMarkPointOuterClass
.
MapMarkPoint
mapMarkPoint
)
{
private
final
int
sceneId
;
private
final
String
name
;
private
final
Position
position
;
private
final
MapMarkPointType
pointType
;
private
final
int
monsterId
;
private
final
MapMarkFromType
fromType
;
private
final
int
questId
;
public
MapMark
(
MapMarkPoint
mapMarkPoint
)
{
this
.
sceneId
=
mapMarkPoint
.
getSceneId
();
this
.
name
=
mapMarkPoint
.
getName
();
this
.
position
=
new
Position
(
mapMarkPoint
.
getPos
().
getX
(),
mapMarkPoint
.
getPos
().
getY
(),
mapMarkPoint
.
getPos
().
getZ
());
this
.
position
=
new
Position
(
mapMarkPoint
.
getPos
().
getX
(),
mapMarkPoint
.
getPos
().
getY
(),
mapMarkPoint
.
getPos
().
getZ
()
);
this
.
pointType
=
mapMarkPoint
.
getPointType
();
this
.
monsterId
=
mapMarkPoint
.
getMonsterId
();
this
.
fromType
=
mapMarkPoint
.
getFromType
();
...
...
@@ -33,41 +33,22 @@ public class MapMark {
public
int
getSceneId
()
{
return
this
.
sceneId
;
}
public
String
getName
()
{
return
this
.
name
;
}
public
Position
getPosition
()
{
return
this
.
position
;
}
public
MapMarkPointTypeOuterClass
.
MapMarkPointType
getMapMarkPointType
()
{
public
MapMarkPointType
getMapMarkPointType
()
{
return
this
.
pointType
;
}
public
void
setMapMarkPointType
(
MapMarkPointTypeOuterClass
.
MapMarkPointType
pointType
)
{
this
.
pointType
=
pointType
;
}
public
int
getMonsterId
()
{
return
this
.
monsterId
;
}
public
void
setMonsterId
(
int
monsterId
)
{
this
.
monsterId
=
monsterId
;
}
public
MapMarkFromTypeOuterClass
.
MapMarkFromType
getMapMarkFromType
()
{
public
MapMarkFromType
getMapMarkFromType
()
{
return
this
.
fromType
;
}
public
int
getQuestId
()
{
return
this
.
questId
;
}
public
void
setQuestId
(
int
questId
)
{
this
.
questId
=
questId
;
}
}
\ No newline at end of file
src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMarksManager.java
View file @
a2ff8c84
package
emu.grasscutter.game.managers.MapMarkManager
;
import
dev.morphia.annotations.Entity
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType
;
import
emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq
;
import
emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq.Operation
;
import
emu.grasscutter.server.packet.send.PacketMarkMapRsp
;
import
emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify
;
import
emu.grasscutter.utils.Position
;
import
java.util.HashMap
;
@Entity
public
class
MapMarksManager
{
static
final
int
mapMarkMaxCount
=
150
;
public
static
final
int
mapMarkMaxCount
=
150
;
private
HashMap
<
String
,
MapMark
>
mapMarks
;
private
final
Player
player
;
public
MapMarksManager
()
{
mapMarks
=
new
HashMap
<
String
,
MapMark
>();
public
MapMarksManager
(
Player
player
)
{
this
.
player
=
player
;
this
.
mapMarks
=
player
.
getMapMarks
();
if
(
this
.
mapMarks
==
null
)
{
this
.
mapMarks
=
new
HashMap
<>();
}
}
public
MapMarksManager
(
HashMap
<
String
,
MapMark
>
mapMarks
)
{
this
.
mapMarks
=
mapMarks
;
public
void
handleMapMarkReq
(
MarkMapReq
req
)
{
Operation
op
=
req
.
getOp
();
switch
(
op
)
{
case
ADD
->
{
MapMark
createMark
=
new
MapMark
(
req
.
getMark
());
// keep teleporting functionality on fishhook mark.
if
(
createMark
.
getMapMarkPointType
()
==
MapMarkPointType
.
MAP_MARK_POINT_TYPE_FISH_POOL
)
{
teleport
(
player
,
createMark
);
return
;
}
public
HashMap
<
String
,
MapMark
>
getAllMapMarks
()
{
return
mapMarks
;
addMapMark
(
createMark
);
}
public
MapMark
getMapMark
(
Position
position
)
{
String
key
=
getMapMarkKey
(
position
);
if
(
mapMarks
.
containsKey
(
key
))
{
return
mapMarks
.
get
(
key
);
}
else
{
return
null
;
case
MOD
->
{
MapMark
oldMark
=
new
MapMark
(
req
.
getOld
());
removeMapMark
(
oldMark
.
getPosition
());
MapMark
newMark
=
new
MapMark
(
req
.
getMark
());
addMapMark
(
newMark
);
}
case
DEL
->
{
MapMark
deleteMark
=
new
MapMark
(
req
.
getMark
());
removeMapMark
(
deleteMark
.
getPosition
());
}
}
if
(
op
!=
Operation
.
GET
)
{
saveMapMarks
();
}
player
.
getSession
().
send
(
new
PacketMarkMapRsp
(
getMapMarks
()));
}
public
HashMap
<
String
,
MapMark
>
getMapMarks
()
{
return
mapMarks
;
}
public
String
getMapMarkKey
(
Position
position
)
{
return
"x"
+
(
int
)
position
.
getX
()+
"z"
+
(
int
)
position
.
getZ
();
}
public
boolean
removeMapMark
(
Position
position
)
{
String
key
=
getMapMarkKey
(
position
);
if
(
mapMarks
.
containsKey
(
key
))
{
mapMarks
.
remove
(
key
);
return
true
;
}
return
false
;
public
void
removeMapMark
(
Position
position
)
{
mapMarks
.
remove
(
getMapMarkKey
(
position
));
}
public
boolean
addMapMark
(
MapMark
mapMark
)
{
public
void
addMapMark
(
MapMark
mapMark
)
{
if
(
mapMarks
.
size
()
<
mapMarkMaxCount
)
{
if
(!
mapMarks
.
containsKey
(
getMapMarkKey
(
mapMark
.
getPosition
())))
{
mapMarks
.
put
(
getMapMarkKey
(
mapMark
.
getPosition
()),
mapMark
);
return
true
;
}
}
return
false
;
}
public
void
setMapMarks
(
HashMap
<
String
,
MapMark
>
mapMarks
)
{
this
.
mapMarks
=
mapMarks
;
private
void
saveMapMarks
()
{
player
.
setMapMarks
(
mapMarks
);
player
.
save
();
}
private
void
teleport
(
Player
player
,
MapMark
mapMark
)
{
float
y
;
try
{
y
=
(
float
)
Integer
.
parseInt
(
mapMark
.
getName
());
}
catch
(
Exception
e
)
{
y
=
300
;
}
Position
pos
=
mapMark
.
getPosition
();
player
.
getPos
().
set
(
pos
.
getX
(),
y
,
pos
.
getZ
());
if
(
mapMark
.
getSceneId
()
!=
player
.
getSceneId
())
{
player
.
getWorld
().
transferPlayerToScene
(
player
,
mapMark
.
getSceneId
(),
player
.
getPos
());
}
player
.
getScene
().
broadcastPacket
(
new
PacketSceneEntityAppearNotify
(
player
));
}
}
src/main/java/emu/grasscutter/game/managers/SotSManager.java
View file @
a2ff8c84
package
emu.grasscutter.game.managers
;
import
ch.qos.logback.classic.Logger
;
import
emu.grasscutter.Grasscutter
;
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.game.props.PlayerProperty
;
import
emu.grasscutter.net.proto.ChangeHpReasonOuterClass
;
import
emu.grasscutter.net.proto.PropChangeReasonOuterClass
;
import
emu.grasscutter.net.proto.ChangeHpReasonOuterClass
.ChangeHpReason
;
import
emu.grasscutter.net.proto.PropChangeReasonOuterClass
.PropChangeReason
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify
;
import
emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify
;
import
emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify
;
import
emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify
;
...
...
@@ -24,7 +22,9 @@ public class SotSManager {
// NOTE: Spring volume balance *1 = fight prop HP *100
private
final
Player
player
;
private
final
Logger
logger
=
Grasscutter
.
getLogger
();
private
Timer
autoRecoverTimer
;
private
final
boolean
enablePriorityHealing
=
false
;
public
final
static
int
GlobalMaximumSpringVolume
=
8500000
;
...
...
@@ -38,6 +38,7 @@ public class SotSManager {
public
void
setIsAutoRecoveryEnabled
(
boolean
enabled
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_IS_SPRING_AUTO_USE
,
enabled
?
1
:
0
);
player
.
save
();
}
public
int
getAutoRecoveryPercentage
()
{
...
...
@@ -46,134 +47,146 @@ public class SotSManager {
public
void
setAutoRecoveryPercentage
(
int
percentage
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_SPRING_AUTO_USE_PERCENT
,
percentage
);
player
.
save
();
}
// autoRevive automatically revives all team members.
public
void
autoRevive
(
GameSession
session
)
{
player
.
getTeamManager
().
getActiveTeam
().
forEach
(
entity
->
{
boolean
isAlive
=
entity
.
isAlive
();
float
currentHP
=
entity
.
getAvatar
().
getFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
);
float
maxHP
=
entity
.
getAvatar
().
getFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
);
// Grasscutter.getLogger().debug("" + entity.getAvatar().getAvatarData().getName() + "\t" + currentHP + "/" + maxHP + "\t" + (isAlive ? "ALIVE":"DEAD"));
float
newHP
=
(
float
)(
maxHP
*
0.3
);
if
(
currentHP
<
newHP
)
{
updateAvatarCurHP
(
session
,
entity
,
newHP
);
public
long
getLastUsed
()
{
return
player
.
getSpringLastUsed
();
}
if
(!
isAlive
)
{
entity
.
getWorld
().
broadcastPacket
(
new
PacketAvatarLifeStateChangeNotify
(
entity
.
getAvatar
()));
public
void
setLastUsed
()
{
player
.
setSpringLastUsed
(
System
.
currentTimeMillis
()
/
1000
);
player
.
save
();
}
});
public
int
getMaxVolume
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
);
}
public
void
setMaxVolume
(
int
volume
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
,
volume
);
player
.
save
();
}
public
int
getCurrentVolume
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
);
}
public
void
scheduleAutoRecover
(
GameSession
session
)
{
public
void
setCurrentVolume
(
int
volume
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
,
volume
);
setLastUsed
();
player
.
save
();
}
public
void
handleEnterTransPointRegionNotify
()
{
logger
.
trace
(
"Player entered statue region"
);
autoRevive
();
if
(
autoRecoverTimer
==
null
)
{
autoRecoverTimer
=
new
Timer
();
autoRecoverTimer
.
schedule
(
new
AutoRecoverTimerTick
(
session
),
25
00
);
autoRecoverTimer
.
schedule
(
new
AutoRecoverTimerTick
(
),
2500
,
150
00
);
}
}
public
void
cancelAutoRecover
()
{
public
void
handleExitTransPointRegionNotify
()
{
logger
.
trace
(
"Player left statue region"
);
if
(
autoRecoverTimer
!=
null
)
{
autoRecoverTimer
.
cancel
();
autoRecoverTimer
=
null
;
}
}
private
class
AutoRecoverTimerTick
extends
TimerTask
{
private
GameSession
session
;
public
AutoRecoverTimerTick
(
GameSession
session
)
{
this
.
session
=
session
;
}
public
void
run
()
{
autoRecover
(
session
);
cancelAutoRecover
();
// autoRevive automatically revives all team members.
public
void
autoRevive
()
{
player
.
getTeamManager
().
getActiveTeam
().
forEach
(
entity
->
{
boolean
isAlive
=
entity
.
isAlive
();
if
(
isAlive
)
{
return
;
}
logger
.
trace
(
"Reviving avatar "
+
entity
.
getAvatar
().
getAvatarData
().
getName
());
player
.
getTeamManager
().
reviveAvatar
(
entity
.
getAvatar
());
player
.
getTeamManager
().
healAvatar
(
entity
.
getAvatar
(),
30
,
0
);
});
}
public
void
refillSpringVolume
()
{
// Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level.
// TODO: remove
// https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
player
.
setProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
,
8500000
);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
player
.
setProperty
(
PlayerProperty
.
PROP_SPRING_AUTO_USE_PERCENT
,
100
);
player
.
setProperty
(
PlayerProperty
.
PROP_IS_SPRING_AUTO_USE
,
1
);
private
class
AutoRecoverTimerTick
extends
TimerTask
{
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public
void
run
()
{
refillSpringVolume
();
long
now
=
System
.
currentTimeMillis
()
/
1000
;
long
secondsSinceLastUsed
=
now
-
player
.
getSpringLastUsed
();
float
percentageRefilled
=
(
float
)
secondsSinceLastUsed
/
15
/
100
;
// 15s = 1% max volume
int
maxVolume
=
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_SPRING_VOLUME
);
int
currentVolume
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
);
if
(
currentVolume
<
maxVolume
)
{
int
volumeRefilled
=
(
int
)(
percentageRefilled
*
maxVolume
);
int
newVolume
=
currentVolume
+
volumeRefilled
;
if
(
currentVolume
+
volumeRefilled
>
maxVolume
)
{
newVolume
=
maxVolume
;
logger
.
trace
(
"isAutoRecoveryEnabled: "
+
getIsAutoRecoveryEnabled
()
+
"\tautoRecoverPercentage: "
+
getAutoRecoveryPercentage
());
if
(
getIsAutoRecoveryEnabled
())
{
List
<
EntityAvatar
>
activeTeam
=
player
.
getTeamManager
().
getActiveTeam
();
// When the statue does not have enough remaining volume:
// Enhanced experience: Enable priority healing
// The current active character will get healed first, then sequential.
// Vanilla experience: Disable priority healing
// Sequential healing based on character index.
int
priorityIndex
=
enablePriorityHealing
?
player
.
getTeamManager
().
getCurrentCharacterIndex
()
:
-
1
;
if
(
priorityIndex
>=
0
)
{
checkAndHealAvatar
(
activeTeam
.
get
(
priorityIndex
));
}
for
(
int
i
=
0
;
i
<
activeTeam
.
size
();
i
++)
{
if
(
i
!=
priorityIndex
)
{
checkAndHealAvatar
(
activeTeam
.
get
(
i
));
}
}
}
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
,
newVolume
);
}
player
.
setSpringLastUsed
(
now
);
player
.
save
();
}
// autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level.
public
void
autoRecover
(
GameSession
session
)
{
// TODO: In MP, respect SotS settings from the HOST.
boolean
isAutoRecoveryEnabled
=
getIsAutoRecoveryEnabled
();
int
autoRecoverPercentage
=
getAutoRecoveryPercentage
();
Grasscutter
.
getLogger
().
debug
(
"isAutoRecoveryEnabled: "
+
isAutoRecoveryEnabled
+
"\tautoRecoverPercentage: "
+
autoRecoverPercentage
);
if
(
isAutoRecoveryEnabled
)
{
player
.
getTeamManager
().
getActiveTeam
().
forEach
(
entity
->
{
float
maxHP
=
entity
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
);
float
currentHP
=
entity
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
);
public
void
checkAndHealAvatar
(
EntityAvatar
entity
)
{
int
maxHP
=
(
int
)
(
entity
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_MAX_HP
)
*
100
);
int
currentHP
=
(
int
)
(
entity
.
getFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
)
*
100
);
if
(
currentHP
==
maxHP
)
{
return
;
}
floa
t
targetHP
=
maxHP
*
a
utoRecoverPercentage
/
100
;
in
t
targetHP
=
maxHP
*
getA
utoRecover
y
Percentage
()
/
100
;
if
(
targetHP
>
currentHP
)
{
float
needHP
=
targetHP
-
currentHP
;
float
needSV
=
needHP
*
100
;
// convert HP needed to Spring Volume needed
int
sotsSVBalance
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
);
if
(
sotsSVBalance
>=
needSV
)
{
int
needHP
=
targetHP
-
currentHP
;
int
currentVolume
=
getCurrentVolume
();
if
(
currentVolume
>=
needHP
)
{
// sufficient
sotsSVBalanc
e
-
=
need
SV
;
setCurrentVolume
(
currentVolum
e
-
need
HP
)
;
}
else
{
// insufficient balance
need
SV
=
sotsSVBalanc
e
;
sotsSVBalance
=
0
;
need
HP
=
currentVolum
e
;
setCurrentVolume
(
0
)
;
}
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_SPRING_VOLUME
,
sotsSVBalance
);
player
.
setSpringLastUsed
(
System
.
currentTimeMillis
()
/
1000
);
float
newHP
=
currentHP
+
needSV
/
100
;
// convert SV to HP
if
(
needHP
>
0
)
{
logger
.
trace
(
"Healing avatar "
+
entity
.
getAvatar
().
getAvatarData
().
getName
()
+
" +"
+
needHP
);
player
.
getTeamManager
().
healAvatar
(
entity
.
getAvatar
(),
0
,
needHP
);
player
.
getSession
().
send
(
new
PacketEntityFightPropChangeReasonNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
,
((
float
)
needHP
/
100
),
List
.
of
(
3
),
PropChangeReason
.
PROP_CHANGE_STATUE_RECOVER
,
ChangeHpReason
.
ChangeHpAddStatue
));
player
.
getSession
().
send
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
));
updateAvatarCurHP
(
session
,
entity
,
newHP
);
}
});
}
}
p
rivate
void
updateAvatarCurHP
(
GameSession
session
,
EntityAvatar
entity
,
float
newHP
)
{
// T
ODO: Figure out why client shows current HP instead of added HP
.
//
Say an avatar had 12000 and now has 14000, it should show "2000".
//
The client always show "+14000" which is incorrect.
entity
.
setFightProperty
(
FightProperty
.
FIGHT_PROP_CUR_HP
,
newHP
);
session
.
send
(
new
PacketEntityFightPropChangeReasonNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
,
newHP
,
List
.
of
(
3
),
PropChangeReasonOuterClass
.
PropChangeReason
.
PROP_CHANGE_STATUE_RECOVER
,
ChangeHpReasonOuterClass
.
ChangeHpReason
.
ChangeHpAddStatue
)
);
se
ssion
.
send
(
new
PacketEntityFightPropUpdateNotify
(
entity
,
FightProperty
.
FIGHT_PROP_CUR_HP
)
);
p
ublic
void
refillSpringVolume
(
)
{
// T
emporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level
.
//
TODO: remove
//
https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking
setMaxVolume
(
8500000
);
// Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game
// TODO: remove
setAutoRecoveryPercentage
(
100
);
se
tIsAutoRecoveryEnabled
(
true
);
Avatar
avatar
=
entity
.
getAvatar
();
avatar
.
setCurrentHp
(
newHP
);
session
.
send
(
new
PacketAvatarFightPropUpdateNotify
(
avatar
,
FightProperty
.
FIGHT_PROP_CUR_HP
));
player
.
save
();
int
maxVolume
=
getMaxVolume
();
int
currentVolume
=
getCurrentVolume
();
if
(
currentVolume
<
maxVolume
)
{
long
now
=
System
.
currentTimeMillis
()
/
1000
;
int
secondsSinceLastUsed
=
(
int
)
(
now
-
getLastUsed
());
// 15s = 1% max volume
int
volumeRefilled
=
secondsSinceLastUsed
*
maxVolume
/
15
/
100
;
logger
.
trace
(
"Statue has refilled HP volume: "
+
volumeRefilled
);
currentVolume
=
Math
.
min
(
currentVolume
+
volumeRefilled
,
maxVolume
);
logger
.
trace
(
"Statue remaining HP volume: "
+
currentVolume
);
setCurrentVolume
(
currentVolume
);
}
}
}
src/main/java/emu/grasscutter/game/managers/StaminaManager/AfterUpdateStaminaListener.java
View file @
a2ff8c84
...
...
@@ -8,5 +8,5 @@ public interface AfterUpdateStaminaListener {
* @param reason Why updating stamina.
* @param newStamina New Stamina value.
*/
void
onAfterUpdateStamina
(
String
reason
,
int
newStamina
);
void
onAfterUpdateStamina
(
String
reason
,
int
newStamina
,
boolean
isCharacterStamina
);
}
src/main/java/emu/grasscutter/game/managers/StaminaManager/BeforeUpdateStaminaListener.java
View file @
a2ff8c84
...
...
@@ -8,7 +8,7 @@ public interface BeforeUpdateStaminaListener {
* @param newStamina New ABSOLUTE stamina value.
* @return true if you want to cancel this update, otherwise false.
*/
int
onBeforeUpdateStamina
(
String
reason
,
int
newStamina
);
int
onBeforeUpdateStamina
(
String
reason
,
int
newStamina
,
boolean
isCharacterStamina
);
/**
* onBeforeUpdateStamina() will be called before StaminaManager attempt to update the player's current stamina.
* This gives listeners a chance to intercept this update.
...
...
@@ -16,5 +16,5 @@ public interface BeforeUpdateStaminaListener {
* @param consumption ConsumptionType and RELATIVE stamina change amount.
* @return true if you want to cancel this update, otherwise false.
*/
Consumption
onBeforeUpdateStamina
(
String
reason
,
Consumption
consumption
);
Consumption
onBeforeUpdateStamina
(
String
reason
,
Consumption
consumption
,
boolean
isCharacterStamina
);
}
\ No newline at end of file
src/main/java/emu/grasscutter/game/managers/StaminaManager/ConsumptionType.java
View file @
a2ff8c84
...
...
@@ -13,18 +13,19 @@ public enum ConsumptionType {
// Slow swimming is handled per movement, not per second.
// Arm movement frequency depends on gender/age/height.
// TODO: Instead of cost -80 per tick, find a proper way to calculate cost.
SKIFF
(-
300
),
// TODO: Get real value
SKIFF
_DASH
(-
204
),
SPRINT
(-
1800
),
SWIM_DASH_START
(-
20
),
SWIM_DASH_START
(-
20
00
),
SWIM_DASH
(-
204
),
// -10.2 per second, 5Hz = -204 each tick
SWIMMING
(-
80
),
TALENT_DASH
(-
300
),
// -1500 per second, 5Hz = -300 each tick
TALENT_DASH_START
(-
1000
),
// restore
POWERED_FLY
(
500
),
// TODO: Get real value
POWERED_SKIFF
(
20
00
),
// TODO: Get real value
POWERED_FLY
(
500
),
POWERED_SKIFF
(
5
00
),
RUN
(
500
),
SKIFF
(
500
),
STANDBY
(
500
),
WALK
(
500
);
...
...
src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java
View file @
a2ff8c84
This diff is collapsed.
Click to expand it.
src/main/java/emu/grasscutter/game/player/Player.java
View file @
a2ff8c84
...
...
@@ -29,6 +29,9 @@ import emu.grasscutter.game.props.ActionReason;
import
emu.grasscutter.game.props.EntityType
;
import
emu.grasscutter.game.props.PlayerProperty
;
import
emu.grasscutter.game.props.SceneType
;
import
emu.grasscutter.game.quest.GameMainQuest
;
import
emu.grasscutter.game.quest.GameQuest
;
import
emu.grasscutter.game.quest.QuestManager
;
import
emu.grasscutter.game.shop.ShopLimit
;
import
emu.grasscutter.game.managers.MapMarkManager.*
;
import
emu.grasscutter.game.tower.TowerManager
;
...
...
@@ -82,6 +85,11 @@ public class Player {
private
Set
<
Integer
>
flyCloakList
;
private
Set
<
Integer
>
costumeList
;
private
Integer
widgetId
;
private
Set
<
Integer
>
realmList
;
private
Integer
currentRealmId
;
@Transient
private
long
nextGuid
=
0
;
@Transient
private
int
peerId
;
@Transient
private
World
world
;
...
...
@@ -93,6 +101,7 @@ public class Player {
@Transient
private
MailHandler
mailHandler
;
@Transient
private
MessageHandler
messageHandler
;
@Transient
private
AbilityManager
abilityManager
;
@Transient
private
QuestManager
questManager
;
@Transient
private
SotSManager
sotsManager
;
...
...
@@ -132,10 +141,11 @@ public class Player {
@Transient
private
final
InvokeHandler
<
AbilityInvokeEntry
>
abilityInvokeHandler
;
@Transient
private
final
InvokeHandler
<
AbilityInvokeEntry
>
clientAbilityInitFinishHandler
;
private
MapMarksManager
mapMarksManager
;
@Transient
private
MapMarksManager
mapMarksManager
;
@Transient
private
StaminaManager
staminaManager
;
private
long
springLastUsed
;
private
HashMap
<
String
,
MapMark
>
mapMarks
;
@Deprecated
...
...
@@ -147,6 +157,7 @@ public class Player {
this
.
mailHandler
=
new
MailHandler
(
this
);
this
.
towerManager
=
new
TowerManager
(
this
);
this
.
abilityManager
=
new
AbilityManager
(
this
);
this
.
setQuestManager
(
new
QuestManager
(
this
));
this
.
pos
=
new
Position
();
this
.
rotation
=
new
Position
();
this
.
properties
=
new
HashMap
<>();
...
...
@@ -179,7 +190,7 @@ public class Player {
this
.
shopLimit
=
new
ArrayList
<>();
this
.
expeditionInfo
=
new
HashMap
<>();
this
.
messageHandler
=
null
;
this
.
mapMarksManager
=
new
MapMarksManager
();
this
.
mapMarksManager
=
new
MapMarksManager
(
this
);
this
.
staminaManager
=
new
StaminaManager
(
this
);
this
.
sotsManager
=
new
SotSManager
(
this
);
}
...
...
@@ -207,7 +218,7 @@ public class Player {
this
.
getPos
().
set
(
GameConstants
.
START_POSITION
);
this
.
getRotation
().
set
(
0
,
307
,
0
);
this
.
messageHandler
=
null
;
this
.
mapMarksManager
=
new
MapMarksManager
();
this
.
mapMarksManager
=
new
MapMarksManager
(
this
);
this
.
staminaManager
=
new
StaminaManager
(
this
);
this
.
sotsManager
=
new
SotSManager
(
this
);
}
...
...
@@ -297,6 +308,39 @@ public class Player {
this
.
updateProfile
();
}
public
Integer
getWidgetId
()
{
return
widgetId
;
}
public
void
setWidgetId
(
Integer
widgetId
)
{
this
.
widgetId
=
widgetId
;
}
public
Set
<
Integer
>
getRealmList
()
{
return
realmList
;
}
public
void
setRealmList
(
Set
<
Integer
>
realmList
)
{
this
.
realmList
=
realmList
;
}
public
void
addRealmList
(
int
realmId
)
{
if
(
this
.
realmList
==
null
)
{
this
.
realmList
=
new
HashSet
<>();
}
else
if
(
this
.
realmList
.
contains
(
realmId
))
{
return
;
}
this
.
realmList
.
add
(
realmId
);
}
public
Integer
getCurrentRealmId
()
{
return
currentRealmId
;
}
public
void
setCurrentRealmId
(
Integer
currentRealmId
)
{
this
.
currentRealmId
=
currentRealmId
;
}
public
Position
getPos
()
{
return
pos
;
}
...
...
@@ -411,6 +455,14 @@ public class Player {
return
towerManager
;
}
public
QuestManager
getQuestManager
()
{
return
questManager
;
}
public
void
setQuestManager
(
QuestManager
questManager
)
{
this
.
questManager
=
questManager
;
}
public
PlayerGachaInfo
getGachaInfo
()
{
return
gachaInfo
;
}
...
...
@@ -885,10 +937,8 @@ public class Player {
}
public
void
sendPacket
(
BasePacket
packet
)
{
if
(
this
.
hasSentAvatarDataNotify
)
{
this
.
getSession
().
send
(
packet
);
}
}
public
OnlinePlayerInfo
getOnlinePlayerInfo
()
{
OnlinePlayerInfo
.
Builder
onlineInfo
=
OnlinePlayerInfo
.
newBuilder
()
...
...
@@ -1034,6 +1084,10 @@ public class Player {
return
abilityManager
;
}
public
HashMap
<
String
,
MapMark
>
getMapMarks
()
{
return
mapMarks
;
}
public
void
setMapMarks
(
HashMap
<
String
,
MapMark
>
newMarks
)
{
mapMarks
=
newMarks
;
}
public
synchronized
void
onTick
()
{
// Check ping
if
(
this
.
getLastPingTime
()
>
System
.
currentTimeMillis
()
+
60000
)
{
...
...
@@ -1120,6 +1174,22 @@ public class Player {
this
.
getFriendsList
().
loadFromDatabase
();
this
.
getMailHandler
().
loadFromDatabase
();
this
.
getQuestManager
().
loadFromDatabase
();
// Quest - Commented out because a problem is caused if you log out while this quest is active
/*
if (getQuestManager().getMainQuestById(351) == null) {
GameQuest quest = getQuestManager().addQuest(35104);
if (quest != null) {
quest.finish();
}
getQuestManager().addQuest(35101);
this.setSceneId(3);
this.getPos().set(GameConstants.START_POSITION);
}
*/
// Create world
World
world
=
new
World
(
this
);
...
...
@@ -1140,6 +1210,14 @@ public class Player {
session
.
send
(
new
PacketStoreWeightLimitNotify
());
session
.
send
(
new
PacketPlayerStoreNotify
(
this
));
session
.
send
(
new
PacketAvatarDataNotify
(
this
));
session
.
send
(
new
PacketFinishedParentQuestNotify
(
this
));
session
.
send
(
new
PacketQuestListNotify
(
this
));
session
.
send
(
new
PacketCodexDataFullNotify
(
this
));
session
.
send
(
new
PacketServerCondMeetQuestListUpdateNotify
(
this
));
session
.
send
(
new
PacketAllWidgetDataNotify
(
this
));
session
.
send
(
new
PacketWidgetGadgetAllDataNotify
());
session
.
send
(
new
PacketPlayerHomeCompInfoNotify
(
this
));
session
.
send
(
new
PacketHomeComfortInfoNotify
(
this
));
getTodayMoonCard
();
// The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward.
...
...
@@ -1237,7 +1315,7 @@ public class Player {
}
else
if
(
prop
==
PlayerProperty
.
PROP_IS_TRANSFERABLE
)
{
// 10009
if
(!(
0
<=
value
&&
value
<=
1
))
{
return
false
;
}
}
else
if
(
prop
==
PlayerProperty
.
PROP_MAX_STAMINA
)
{
// 10010
if
(!(
value
>=
0
&&
value
<=
StaminaManager
.
GlobalMaximumStamina
))
{
return
false
;
}
if
(!(
value
>=
0
&&
value
<=
StaminaManager
.
Global
Character
MaximumStamina
))
{
return
false
;
}
}
else
if
(
prop
==
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
)
{
// 10011
int
playerMaximumStamina
=
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
if
(!(
value
>=
0
&&
value
<=
playerMaximumStamina
))
{
return
false
;
}
...
...
Prev
1
2
3
4
5
6
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