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
9b26426e
Commit
9b26426e
authored
May 12, 2022
by
Melledy
Browse files
Merge branch 'development' into dev-quests
parents
12318021
8c32438b
Changes
19
Show whitespace changes
Inline
Side-by-side
src/main/java/emu/grasscutter/Grasscutter.java
View file @
9b26426e
...
...
@@ -4,6 +4,7 @@ import java.io.*;
import
java.util.Calendar
;
import
emu.grasscutter.command.CommandMap
;
import
emu.grasscutter.game.managers.StaminaManager.StaminaManager
;
import
emu.grasscutter.plugin.PluginManager
;
import
emu.grasscutter.plugin.api.ServerHook
;
import
emu.grasscutter.scripts.ScriptLoader
;
...
...
@@ -111,6 +112,9 @@ public final class Grasscutter {
// Create plugin manager instance.
pluginManager
=
new
PluginManager
();
// TODO: find a better place?
StaminaManager
.
initialize
();
// Start servers.
var
runMode
=
SERVER
.
runMode
;
if
(
runMode
==
ServerRunMode
.
HYBRID
)
{
...
...
src/main/java/emu/grasscutter/game/gacha/GachaManager.java
View file @
9b26426e
...
...
@@ -13,6 +13,7 @@ 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
;
...
...
@@ -127,15 +128,10 @@ public class GachaManager {
}
// Spend currency
if
(
banner
.
getCostItem
()
>
0
)
{
GameItem
costItem
=
player
.
getInventory
().
getInventoryTab
(
ItemType
.
ITEM_MATERIAL
).
getItemById
(
banner
.
getCostItem
());
if
(
costItem
==
null
||
costItem
.
getCount
()
<
times
)
{
if
(
banner
.
getCostItem
()
>
0
&&
!
player
.
getInventory
().
payItem
(
banner
.
getCostItem
(),
times
))
{
return
;
}
player
.
getInventory
().
removeItem
(
costItem
,
times
);
}
// Roll
PlayerGachaBannerInfo
gachaInfo
=
player
.
getGachaInfo
().
getBannerInfo
(
banner
);
IntList
wonItems
=
new
IntArrayList
(
times
);
...
...
src/main/java/emu/grasscutter/game/inventory/Inventory.java
View file @
9b26426e
...
...
@@ -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 @
9b26426e
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/StaminaManager/AfterUpdateStaminaListener.java
View file @
9b26426e
...
...
@@ -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 @
9b26426e
...
...
@@ -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 @
9b26426e
...
...
@@ -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 @
9b26426e
...
...
@@ -2,6 +2,7 @@ package emu.grasscutter.game.managers.StaminaManager;
import
ch.qos.logback.classic.Logger
;
import
emu.grasscutter.Grasscutter
;
import
emu.grasscutter.data.GameData
;
import
emu.grasscutter.game.entity.EntityAvatar
;
import
emu.grasscutter.game.entity.GameEntity
;
import
emu.grasscutter.game.player.Player
;
...
...
@@ -13,21 +14,21 @@ import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import
emu.grasscutter.net.proto.MotionStateOuterClass.MotionState
;
import
emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType
;
import
emu.grasscutter.net.proto.VectorOuterClass.Vector
;
import
emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.*
;
import
emu.grasscutter.utils.Position
;
import
org.jetbrains.annotations.NotNull
;
import
java.lang.Math
;
import
java.util.*
;
import
static
emu
.
grasscutter
.
Configuration
.
*
;
import
static
emu
.
grasscutter
.
Configuration
.
GAME_OPTIONS
;
public
class
StaminaManager
{
// TODO: Skiff state detection?
private
final
Player
player
;
private
final
HashMap
<
String
,
HashSet
<
MotionState
>>
MotionStatesCategorized
=
new
HashMap
<>()
{{
private
static
final
HashMap
<
String
,
HashSet
<
MotionState
>>
MotionStatesCategorized
=
new
HashMap
<>()
{{
put
(
"CLIMB"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_CLIMB
,
// sustained, when not moving no cost no recover
MotionState
.
MOTION_STANDBY_TO_CLIMB
// NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY
...
...
@@ -48,7 +49,7 @@ public class StaminaManager {
)));
put
(
"SKIFF"
,
new
HashSet
<>(
List
.
of
(
MotionState
.
MOTION_SKIFF_BOARDING
,
// NOT OBSERVED even when boarding
MotionState
.
MOTION_SKIFF_DASH
,
//
NOT OBSERVED e
ve
n
w
hen dashing
MotionState
.
MOTION_SKIFF_DASH
,
//
sustained, obser
ve
d
w
ith waverider entity ID.
MotionState
.
MOTION_SKIFF_NORMAL
,
// sustained, OBSERVED when both normal and dashing
MotionState
.
MOTION_SKIFF_POWERED_DASH
// sustained, recover
)));
...
...
@@ -108,7 +109,8 @@ public class StaminaManager {
}};
private
final
Logger
logger
=
Grasscutter
.
getLogger
();
public
final
static
int
GlobalMaximumStamina
=
24000
;
public
final
static
int
GlobalCharacterMaximumStamina
=
24000
;
public
final
static
int
GlobalVehicleMaxStamina
=
24000
;
private
Position
currentCoordinates
=
new
Position
(
0
,
0
,
0
);
private
Position
previousCoordinates
=
new
Position
(
0
,
0
,
0
);
private
MotionState
currentState
=
MotionState
.
MOTION_STANDBY
;
...
...
@@ -122,74 +124,58 @@ public class StaminaManager {
private
int
lastSkillId
=
0
;
private
int
lastSkillCasterId
=
0
;
private
boolean
lastSkillFirstTick
=
true
;
public
static
final
HashSet
<
Integer
>
TalentMovements
=
new
HashSet
<>(
List
.
of
(
10013
,
// Kamisato Ayaka
10413
// Mona
private
int
vehicleId
=
-
1
;
private
int
vehicleStamina
=
GlobalVehicleMaxStamina
;
private
static
final
HashSet
<
Integer
>
TalentMovements
=
new
HashSet
<>(
List
.
of
(
10013
,
10413
));
private
static
final
HashMap
<
Integer
,
Float
>
ClimbFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
DashFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
FlyFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
SwimFoodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real food id
put
(
0
,
0.8f
);
// Sample food
}};
private
static
final
HashMap
<
Integer
,
Float
>
ClimbTalentReductionMap
=
new
HashMap
<>()
{{
put
(
262301
,
0.8f
);
}};
private
static
final
HashMap
<
Integer
,
Float
>
FlyTalentReductionMap
=
new
HashMap
<>()
{{
put
(
212301
,
0.8f
);
put
(
222301
,
0.8f
);
}};
private
static
final
HashMap
<
Integer
,
Float
>
SwimTalentReductionMap
=
new
HashMap
<>()
{{
put
(
242301
,
0.8f
);
put
(
542301
,
0.8f
);
}};
// TODO: Get from somewhere else, instead of hard-coded here?
public
static
final
HashSet
<
Integer
>
ClaymoreSkills
=
new
HashSet
<>(
List
.
of
(
10160
,
// Diluc, /=2
10201
,
// Razor
10241
,
// Beidou
10341
,
// Noelle
10401
,
// Chongyun
10441
,
// Xinyan
10511
,
// Eula
10531
,
// Sayu
10571
// Arataki Itto, = 0
));
public
static
final
HashSet
<
Integer
>
CatalystSkills
=
new
HashSet
<>(
List
.
of
(
10060
,
// Lisa
10070
,
// Barbara
10271
,
// Ningguang
10291
,
// Klee
10411
,
// Mona
10431
,
// Sucrose
10481
,
// Yanfei
10541
,
// Sangonomoiya Kokomi
10581
// Yae Miko
));
public
static
final
HashSet
<
Integer
>
PolearmSkills
=
new
HashSet
<>(
List
.
of
(
10231
,
// Xiangling
10261
,
// Xiao
10301
,
// Zhongli
10451
,
// Rosaria
10461
,
// Hu Tao
10501
,
// Thoma
10521
,
// Raiden Shogun
10631
,
// Shenhe
10641
// Yunjin
));
public
static
final
HashSet
<
Integer
>
SwordSkills
=
new
HashSet
<>(
List
.
of
(
10024
,
// Kamisato Ayaka
10031
,
// Jean
10073
,
// Kaeya
10321
,
// Bennett
10337
,
// Tartaglia, melee stance (10332 switch to melee, 10336 switch to ranged stance)
10351
,
// Qiqi
10381
,
// Xingqiu
10386
,
// Albedo
10421
,
// Keqing, =-2500
10471
,
// Kaedehara Kazuha
10661
,
// Kamisato Ayato
100553
,
// Lumine
100540
// Aether
));
public
static
final
HashSet
<
Integer
>
BowSkills
=
new
HashSet
<>(
List
.
of
(
10041
,
10043
,
// Amber
10221
,
10223
,
// Venti
10311
,
10315
,
// Fischl
10331
,
10335
,
// Tartaglia, ranged stance
10371
,
// Ganyu
10391
,
10394
,
// Diona
10491
,
// Yoimiya
10551
,
10554
,
// Gorou
10561
,
10564
,
// Kojou Sara
10621
,
// Aloy
99998
,
99999
// Yelan // TODO: get real values
));
public
static
final
HashSet
<
Integer
>
BowAvatars
=
new
HashSet
<>();
public
static
final
HashSet
<
Integer
>
CatalystAvatars
=
new
HashSet
<>();
public
static
final
HashSet
<
Integer
>
ClaymoreAvatars
=
new
HashSet
<>();
public
static
final
HashSet
<
Integer
>
PolearmAvatars
=
new
HashSet
<>();
public
static
final
HashSet
<
Integer
>
SwordAvatars
=
new
HashSet
<>();
public
static
void
initialize
()
{
// Initialize skill categories
GameData
.
getAvatarDataMap
().
forEach
((
avatarId
,
avatarData
)
->
{
switch
(
avatarData
.
getWeaponType
())
{
case
"WEAPON_BOW"
->
BowAvatars
.
add
(
avatarId
);
case
"WEAPON_CLAYMORE"
->
ClaymoreAvatars
.
add
(
avatarId
);
case
"WEAPON_CATALYST"
->
CatalystAvatars
.
add
(
avatarId
);
case
"WEAPON_POLE"
->
PolearmAvatars
.
add
(
avatarId
);
case
"WEAPON_SWORD_ONE_HAND"
->
SwordAvatars
.
add
(
avatarId
);
}
});
// TODO: Initialize foods etc.
}
public
StaminaManager
(
Player
player
)
{
this
.
player
=
player
;
...
...
@@ -203,6 +189,22 @@ public class StaminaManager {
lastSkillCasterId
=
skillCasterId
;
}
public
int
getMaxCharacterStamina
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
}
public
int
getCurrentCharacterStamina
()
{
return
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
}
public
int
getMaxVehicleStamina
()
{
return
GlobalVehicleMaxStamina
;
}
public
int
getCurrentVehicleStamina
()
{
return
vehicleStamina
;
}
public
boolean
registerBeforeUpdateStaminaListener
(
String
listenerName
,
BeforeUpdateStaminaListener
listener
)
{
if
(
beforeUpdateStaminaListeners
.
containsKey
(
listenerName
))
{
return
false
;
...
...
@@ -244,67 +246,71 @@ public class StaminaManager {
return
Math
.
abs
(
diffX
)
>
0.3
||
Math
.
abs
(
diffY
)
>
0.2
||
Math
.
abs
(
diffZ
)
>
0.3
;
}
public
int
updateStaminaRelative
(
GameSession
session
,
Consumption
consumption
)
{
int
currentStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
public
int
updateStaminaRelative
(
GameSession
session
,
Consumption
consumption
,
boolean
isCharacterStamina
)
{
int
currentStamina
=
isCharacterStamina
?
getCurrentCharacterStamina
()
:
getCurrentVehicleStamina
(
);
if
(
consumption
.
amount
==
0
)
{
return
currentStamina
;
}
// notify will update
for
(
Map
.
Entry
<
String
,
BeforeUpdateStaminaListener
>
listener
:
beforeUpdateStaminaListeners
.
entrySet
())
{
Consumption
overriddenConsumption
=
listener
.
getValue
().
onBeforeUpdateStamina
(
consumption
.
type
.
toString
(),
consumption
);
Consumption
overriddenConsumption
=
listener
.
getValue
().
onBeforeUpdateStamina
(
consumption
.
type
.
toString
(),
consumption
,
isCharacterStamina
);
if
((
overriddenConsumption
.
type
!=
consumption
.
type
)
&&
(
overriddenConsumption
.
amount
!=
consumption
.
amount
))
{
logger
.
debug
(
"
[StaminaManager]
Stamina update relative("
+
logger
.
debug
(
"Stamina update relative("
+
consumption
.
type
.
toString
()
+
", "
+
consumption
.
amount
+
") overridden to relative("
+
consumption
.
type
.
toString
()
+
", "
+
consumption
.
amount
+
") by: "
+
listener
.
getKey
());
return
currentStamina
;
}
}
int
playerM
axStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
logger
.
trace
(
currentStamina
+
"/"
+
playerM
axStamina
+
"\t"
+
currentState
+
"\t"
+
int
m
axStamina
=
isCharacterStamina
?
getMaxCharacterStamina
()
:
getMaxVehicleStamina
(
);
logger
.
trace
(
(
isCharacterStamina
?
"C "
:
"V "
)
+
currentStamina
+
"/"
+
m
axStamina
+
"\t"
+
currentState
+
"\t"
+
(
isPlayerMoving
()
?
"moving"
:
" "
)
+
"\t("
+
consumption
.
type
+
","
+
consumption
.
amount
+
")"
);
int
newStamina
=
currentStamina
+
consumption
.
amount
;
if
(
newStamina
<
0
)
{
newStamina
=
0
;
}
else
if
(
newStamina
>
playerM
axStamina
)
{
newStamina
=
playerM
axStamina
;
}
else
if
(
newStamina
>
m
axStamina
)
{
newStamina
=
m
axStamina
;
}
return
setStamina
(
session
,
consumption
.
type
.
toString
(),
newStamina
);
return
setStamina
(
session
,
consumption
.
type
.
toString
(),
newStamina
,
isCharacterStamina
);
}
public
int
updateStaminaAbsolute
(
GameSession
session
,
String
reason
,
int
newStamina
)
{
int
currentStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
public
int
updateStaminaAbsolute
(
GameSession
session
,
String
reason
,
int
newStamina
,
boolean
isCharacterStamina
)
{
int
currentStamina
=
isCharacterStamina
?
getCurrentCharacterStamina
()
:
getCurrentVehicleStamina
(
);
// notify will update
for
(
Map
.
Entry
<
String
,
BeforeUpdateStaminaListener
>
listener
:
beforeUpdateStaminaListeners
.
entrySet
())
{
int
overriddenNewStamina
=
listener
.
getValue
().
onBeforeUpdateStamina
(
reason
,
newStamina
);
int
overriddenNewStamina
=
listener
.
getValue
().
onBeforeUpdateStamina
(
reason
,
newStamina
,
isCharacterStamina
);
if
(
overriddenNewStamina
!=
newStamina
)
{
logger
.
debug
(
"
[StaminaManager]
Stamina update absolute("
+
logger
.
debug
(
"Stamina update absolute("
+
reason
+
", "
+
newStamina
+
") overridden to absolute("
+
reason
+
", "
+
newStamina
+
") by: "
+
listener
.
getKey
());
return
currentStamina
;
}
}
int
playerM
axStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
int
m
axStamina
=
isCharacterStamina
?
getMaxCharacterStamina
()
:
getMaxVehicleStamina
(
);
if
(
newStamina
<
0
)
{
newStamina
=
0
;
}
else
if
(
newStamina
>
playerM
axStamina
)
{
newStamina
=
playerM
axStamina
;
}
else
if
(
newStamina
>
m
axStamina
)
{
newStamina
=
m
axStamina
;
}
return
setStamina
(
session
,
reason
,
newStamina
);
return
setStamina
(
session
,
reason
,
newStamina
,
isCharacterStamina
);
}
// Returns new stamina and sends PlayerPropNotify
public
int
setStamina
(
GameSession
session
,
String
reason
,
int
newStamina
)
{
// Returns new stamina and sends PlayerPropNotify
or VehicleStaminaNotify
public
int
setStamina
(
GameSession
session
,
String
reason
,
int
newStamina
,
boolean
isCharacterStamina
)
{
if
(!
GAME_OPTIONS
.
staminaUsage
)
{
newStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
newStamina
=
getMaxCharacterStamina
(
);
}
// set s
tamina
// set stamina if is character stamina
if
(
isCharacterS
tamina
)
{
player
.
setProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
,
newStamina
);
session
.
send
(
new
PacketPlayerPropNotify
(
player
,
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
));
}
else
{
vehicleStamina
=
newStamina
;
session
.
send
(
new
PacketVehicleStaminaNotify
(
vehicleId
,
((
float
)
newStamina
)
/
100
));
}
// notify updated
for
(
Map
.
Entry
<
String
,
AfterUpdateStaminaListener
>
listener
:
afterUpdateStaminaListeners
.
entrySet
())
{
listener
.
getValue
().
onAfterUpdateStamina
(
reason
,
newStamina
);
listener
.
getValue
().
onAfterUpdateStamina
(
reason
,
newStamina
,
isCharacterStamina
);
}
return
newStamina
;
}
...
...
@@ -343,22 +349,23 @@ public class StaminaManager {
// External trigger handler
public
void
handleEvtDoSkillSuccNotify
(
GameSession
session
,
int
skillId
,
int
casterId
)
{
// Ignore if skill not cast by not current active
// Ignore if skill not cast by not current active
avatar
if
(
casterId
!=
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getId
())
{
return
;
}
setSkillCast
(
skillId
,
casterId
);
// Handle immediate stamina cost
if
(
ClaymoreSkills
.
contains
(
skillId
))
{
int
currentAvatarId
=
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getAvatar
().
getAvatarId
();
if
(
ClaymoreAvatars
.
contains
(
currentAvatarId
))
{
// Exclude claymore as their stamina cost starts when MixinStaminaCost gets in
return
;
}
// TODO: Differentiate normal attacks from charged attacks and exclude
// TODO: Temporary: Exclude non-claymore attacks for now
if
(
Bow
Skill
s
.
contains
(
skill
Id
)
||
Sword
Skill
s
.
contains
(
skill
Id
)
||
Polearm
Skill
s
.
contains
(
skill
Id
)
||
Catalyst
Skill
s
.
contains
(
skill
Id
)
if
(
Bow
Avatar
s
.
contains
(
currentAvatar
Id
)
||
Sword
Avatar
s
.
contains
(
currentAvatar
Id
)
||
Polearm
Avatar
s
.
contains
(
currentAvatar
Id
)
||
Catalyst
Avatar
s
.
contains
(
currentAvatar
Id
)
)
{
return
;
}
...
...
@@ -367,7 +374,7 @@ public class StaminaManager {
public
void
handleMixinCostStamina
(
boolean
isSwim
)
{
// Talent moving and claymore avatar charged attack duration
// logger.trace("abilityMixinCostStamina: isSwim: " + isSwim);
// logger.trace("abilityMixinCostStamina: isSwim: " + isSwim
+ "\tlastSkill: " + lastSkillId
);
if
(
lastSkillCasterId
==
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getId
())
{
handleImmediateStamina
(
cachedSession
,
lastSkillId
);
}
...
...
@@ -381,11 +388,11 @@ public class StaminaManager {
MotionState
motionState
=
motionInfo
.
getState
();
int
notifyEntityId
=
entity
.
getId
();
int
currentAvatarEntityId
=
session
.
getPlayer
().
getTeamManager
().
getCurrentAvatarEntity
().
getId
();
if
(
notifyEntityId
!=
currentAvatarEntityId
)
{
if
(
notifyEntityId
!=
currentAvatarEntityId
&&
notifyEntityId
!=
vehicleId
)
{
return
;
}
currentState
=
motionState
;
// logger.trace(
"" +
currentState);
// logger.trace(currentState
+ "\t" + (notifyEntityId == currentAvatarEntityId ? "character" : "vehicle")
);
Vector
posVector
=
motionInfo
.
getPos
();
Position
newPos
=
new
Position
(
posVector
.
getX
(),
posVector
.
getY
(),
posVector
.
getZ
());
if
(
newPos
.
getX
()
!=
0
&&
newPos
.
getY
()
!=
0
&&
newPos
.
getZ
()
!=
0
)
{
...
...
@@ -395,28 +402,40 @@ public class StaminaManager {
handleImmediateStamina
(
session
,
motionState
);
}
public
void
handleVehicleInteractReq
(
GameSession
session
,
int
vehicleId
,
VehicleInteractType
vehicleInteractType
)
{
if
(
vehicleInteractType
==
VehicleInteractType
.
VEHICLE_INTERACT_IN
)
{
this
.
vehicleId
=
vehicleId
;
// Reset character stamina here to prevent falling into water immediately on ejection if char stamina is
// close to empty when boarding.
updateStaminaAbsolute
(
session
,
"board vehicle"
,
getMaxCharacterStamina
(),
true
);
updateStaminaAbsolute
(
session
,
"board vehicle"
,
getMaxVehicleStamina
(),
false
);
}
else
{
this
.
vehicleId
=
-
1
;
}
}
// Internal handler
private
void
handleImmediateStamina
(
GameSession
session
,
@NotNull
MotionState
motionState
)
{
switch
(
motionState
)
{
case
MOTION_CLIMB:
if
(
currentState
!=
MotionState
.
MOTION_CLIMB
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
CLIMB_START
));
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
CLIMB_START
)
,
true
);
}
break
;
case
MOTION_DASH_BEFORE_SHAKE:
if
(
previousState
!=
MotionState
.
MOTION_DASH_BEFORE_SHAKE
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
SPRINT
));
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
SPRINT
)
,
true
);
}
break
;
case
MOTION_CLIMB_JUMP:
if
(
previousState
!=
MotionState
.
MOTION_CLIMB_JUMP
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
CLIMB_JUMP
));
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
CLIMB_JUMP
)
,
true
);
}
break
;
case
MOTION_SWIM_DASH:
if
(
previousState
!=
MotionState
.
MOTION_SWIM_DASH
)
{
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
SWIM_DASH_START
));
updateStaminaRelative
(
session
,
new
Consumption
(
ConsumptionType
.
SWIM_DASH_START
)
,
true
);
}
break
;
}
...
...
@@ -424,18 +443,20 @@ public class StaminaManager {
private
void
handleImmediateStamina
(
GameSession
session
,
int
skillId
)
{
Consumption
consumption
=
getFightConsumption
(
skillId
);
updateStaminaRelative
(
session
,
consumption
);
updateStaminaRelative
(
session
,
consumption
,
true
);
}
private
class
SustainedStaminaHandler
extends
TimerTask
{
public
void
run
()
{
boolean
moving
=
isPlayerMoving
();
int
currentStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
int
maxStamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
);
if
(
moving
||
(
currentStamina
<
maxStamina
))
{
int
currentCharacterStamina
=
getCurrentCharacterStamina
();
int
maxCharacterStamina
=
getMaxCharacterStamina
();
int
currentVehicleStamina
=
getCurrentVehicleStamina
();
int
maxVehicleStamina
=
getMaxVehicleStamina
();
if
(
moving
||
(
currentCharacterStamina
<
maxCharacterStamina
)
||
(
currentVehicleStamina
<
maxVehicleStamina
))
{
logger
.
trace
(
"Player moving: "
+
moving
+
", stamina full: "
+
(
currentStamina
>=
maxStamina
)
+
", recalculate stamina"
);
(
current
Character
Stamina
>=
max
Character
Stamina
)
+
", recalculate stamina"
);
boolean
isCharacterStamina
=
true
;
Consumption
consumption
;
if
(
MotionStatesCategorized
.
get
(
"CLIMB"
).
contains
(
currentState
))
{
consumption
=
getClimbConsumption
();
...
...
@@ -447,43 +468,44 @@ public class StaminaManager {
consumption
=
new
Consumption
(
ConsumptionType
.
RUN
);
}
else
if
(
MotionStatesCategorized
.
get
(
"SKIFF"
).
contains
(
currentState
))
{
consumption
=
getSkiffConsumption
();
isCharacterStamina
=
false
;
}
else
if
(
MotionStatesCategorized
.
get
(
"STANDBY"
).
contains
(
currentState
))
{
consumption
=
new
Consumption
(
ConsumptionType
.
STANDBY
);
}
else
if
(
MotionStatesCategorized
.
get
(
"SWIM"
).
contains
(
(
currentState
))
)
{
}
else
if
(
MotionStatesCategorized
.
get
(
"SWIM"
).
contains
(
currentState
))
{
consumption
=
getSwimConsumptions
();
}
else
if
(
MotionStatesCategorized
.
get
(
"WALK"
).
contains
(
(
currentState
))
)
{
}
else
if
(
MotionStatesCategorized
.
get
(
"WALK"
).
contains
(
currentState
))
{
consumption
=
new
Consumption
(
ConsumptionType
.
WALK
);
}
else
if
(
MotionStatesCategorized
.
get
(
"OTHER"
).
contains
((
currentState
)))
{
}
else
if
(
MotionStatesCategorized
.
get
(
"NOCOST_NORECOVER"
).
contains
(
currentState
))
{
consumption
=
new
Consumption
();
}
else
if
(
MotionStatesCategorized
.
get
(
"OTHER"
).
contains
(
currentState
))
{
consumption
=
getOtherConsumptions
();
}
else
{
// ignore
}
else
{
// ignore
return
;
}
if
(
consumption
.
amount
<
0
)
{
/* Do not apply reduction factor when recovering stamina
TODO: Reductions that apply to all motion types:
Elemental Resonance
Wind: -15%
Skills
Diona E: -10% while shield lasts - applies to SP+MP
Barbara E: -12% while lasts - applies to SP+MP
*/
}
// Delay 2 seconds before starts recovering stamina
if
(
cachedSession
!=
null
)
{
if
(
consumption
.
amount
<
0
&&
isCharacterStamina
)
{
// Do not apply reduction factor when recovering stamina
if
(
player
.
getTeamManager
().
getTeamResonances
().
contains
(
10301
))
{
consumption
.
amount
*=
0.85f
;
}
}
// Delay 1 seconds before starts recovering stamina
if
(
consumption
.
amount
!=
0
&&
cachedSession
!=
null
)
{
if
(
consumption
.
amount
<
0
)
{
staminaRecoverDelay
=
0
;
}
if
(
consumption
.
amount
>
0
&&
consumption
.
type
!=
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.
if
(
consumption
.
amount
>
0
&&
consumption
.
type
!=
ConsumptionType
.
POWERED_FLY
&&
consumption
.
type
!=
ConsumptionType
.
POWERED_SKIFF
)
{
// For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this.
if
(
staminaRecoverDelay
<
5
)
{
// For others recover after 1 seconds (5 ticks) - as official server does.
staminaRecoverDelay
++;
consumption
.
amount
=
0
;
logger
.
trace
(
"
[StaminaManager]
Delaying recovery: "
+
staminaRecoverDelay
);
logger
.
trace
(
"Delaying recovery: "
+
staminaRecoverDelay
);
}
}
updateStaminaRelative
(
cachedSession
,
consumption
);
updateStaminaRelative
(
cachedSession
,
consumption
,
isCharacterStamina
);
}
}
previousState
=
currentState
;
...
...
@@ -496,10 +518,11 @@ public class StaminaManager {
}
private
void
handleDrowning
()
{
int
stamina
=
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
);
// TODO: fix drowning waverider entity
int
stamina
=
getCurrentCharacterStamina
();
if
(
stamina
<
10
)
{
logger
.
trace
(
player
.
getProperty
(
PlayerProperty
.
PROP_CUR_PERSIST_STAMINA
)
+
"/"
+
player
.
getProperty
(
PlayerProperty
.
PROP_MAX_STAMINA
)
+
"\t"
+
currentState
);
logger
.
trace
(
getCurrentCharacterStamina
(
)
+
"/"
+
getMaxCharacterStamina
(
)
+
"\t"
+
currentState
);
if
(
currentState
!=
MotionState
.
MOTION_SWIM_IDLE
)
{
killAvatar
(
cachedSession
,
cachedEntity
,
PlayerDieType
.
PLAYER_DIE_DRAWN
);
}
...
...
@@ -517,24 +540,25 @@ public class StaminaManager {
return
getTalentMovingSustainedCost
(
skillCasting
);
}
// Bow avatar charged attack
if
(
BowSkills
.
contains
(
skillCasting
))
{
int
currentAvatarId
=
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getAvatar
().
getAvatarId
();
if
(
BowAvatars
.
contains
(
currentAvatarId
))
{
return
getBowSustainedCost
(
skillCasting
);
}
// Claymore avatar charged attack
if
(
Claymore
Skill
s
.
contains
(
skillCasting
))
{
if
(
Claymore
Avatar
s
.
contains
(
currentAvatarId
))
{
return
getClaymoreSustainedCost
(
skillCasting
);
}
// Catalyst avatar charged attack
if
(
Catalyst
Skill
s
.
contains
(
skillCasting
))
{
return
getCatalyst
Sustained
Cost
(
skillCasting
);
if
(
Catalyst
Avatar
s
.
contains
(
currentAvatarId
))
{
return
getCatalystCost
(
skillCasting
);
}
// Polearm avatar charged attack
if
(
Polearm
Skill
s
.
contains
(
skillCasting
))
{
return
getPolearm
Sustained
Cost
(
skillCasting
);
if
(
Polearm
Avatar
s
.
contains
(
currentAvatarId
))
{
return
getPolearmCost
(
skillCasting
);
}
// Sword avatar charged attack
if
(
Sword
Skill
s
.
contains
(
skillCasting
))
{
return
getSword
Sustained
Cost
(
skillCasting
);
if
(
Sword
Avatar
s
.
contains
(
skillCasting
))
{
return
getSwordCost
(
skillCasting
);
}
return
new
Consumption
();
}
...
...
@@ -546,18 +570,8 @@ public class StaminaManager {
consumption
.
amount
=
ConsumptionType
.
CLIMBING
.
amount
;
}
// Climbing specific reductions
// TODO: create a food cost reduction map
HashMap
<
Integer
,
Float
>
foodReductionMap
=
new
HashMap
<>()
{{
// TODO: get real talent id
put
(
0
,
0.8f
);
// Sample food
}};
consumption
.
amount
*=
getFoodCostReductionFactor
(
foodReductionMap
);
HashMap
<
Integer
,
Float
>
talentReductionMap
=
new
HashMap
<>()
{{
// TODO: get real talent id
put
(
0
,
0.8f
);
// Xiao
}};
consumption
.
amount
*=
getTalentCostReductionFactor
(
talentReductionMap
);
consumption
.
amount
*=
getFoodCostReductionFactor
(
ClimbFoodReductionMap
);
consumption
.
amount
*=
getTalentCostReductionFactor
(
ClimbTalentReductionMap
);
return
consumption
;
}
...
...
@@ -572,13 +586,9 @@ public class StaminaManager {
consumption
.
type
=
ConsumptionType
.
SWIM_DASH
;
consumption
.
amount
=
ConsumptionType
.
SWIM_DASH
.
amount
;
}
// Reductions
HashMap
<
Integer
,
Float
>
talentReductionMap
=
new
HashMap
<>()
{{
// TODO: get real talent id
put
(
0
,
0.8f
);
// Beidou
put
(
1
,
0.8f
);
// Sangonomiya Kokomi
}};
consumption
.
amount
*=
getTalentCostReductionFactor
(
talentReductionMap
);
// Swimming specific reductions
consumption
.
amount
*=
getFoodCostReductionFactor
(
SwimFoodReductionMap
);
consumption
.
amount
*=
getTalentCostReductionFactor
(
SwimTalentReductionMap
);
return
consumption
;
}
...
...
@@ -587,8 +597,8 @@ public class StaminaManager {
if
(
currentState
==
MotionState
.
MOTION_DASH
)
{
consumption
.
type
=
ConsumptionType
.
DASH
;
consumption
.
amount
=
ConsumptionType
.
DASH
.
amount
;
//
TODO:
Dashing specific reductions
// Foods:
// Dashing specific reductions
consumption
.
amount
*=
getFoodCostReductionFactor
(
DashFoodReductionMap
);
}
return
consumption
;
}
...
...
@@ -599,32 +609,34 @@ public class StaminaManager {
return
new
Consumption
(
ConsumptionType
.
POWERED_FLY
);
}
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FLY
);
// Passive Talents
HashMap
<
Integer
,
Float
>
talentReductionMap
=
new
HashMap
<>()
{{
put
(
212301
,
0.8f
);
// Amber
put
(
222301
,
0.8f
);
// Venti
}};
consumption
.
amount
*=
getTalentCostReductionFactor
(
talentReductionMap
);
// TODO: Foods
// Flying specific reductions
consumption
.
amount
*=
getFoodCostReductionFactor
(
FlyFoodReductionMap
);
consumption
.
amount
*=
getTalentCostReductionFactor
(
FlyTalentReductionMap
);
return
consumption
;
}
private
Consumption
getSkiffConsumption
()
{
// POWERED_SKIFF, e.g. wind tunnel
if
(
currentState
==
MotionState
.
MOTION_SKIFF_POWERED_DASH
)
{
return
new
Consumption
(
ConsumptionType
.
POWERED_SKIFF
);
}
// No known reduction for skiffing.
return
new
Consumption
(
ConsumptionType
.
SKIFF
);
return
switch
(
currentState
)
{
case
MOTION_SKIFF_DASH
->
new
Consumption
(
ConsumptionType
.
SKIFF_DASH
);
case
MOTION_SKIFF_POWERED_DASH
->
new
Consumption
(
ConsumptionType
.
POWERED_SKIFF
);
case
MOTION_SKIFF_NORMAL
->
new
Consumption
(
ConsumptionType
.
SKIFF
);
default
->
new
Consumption
();
};
}
private
Consumption
getOtherConsumptions
()
{
if
(
currentState
==
MotionState
.
MOTION_NOTIFY
)
{
if
(
BowSkills
.
contains
(
lastSkillId
))
{
switch
(
currentState
)
{
case
MOTION_NOTIFY:
// if (BowSkills.contains(lastSkillId)) {
// return new Consumption(ConsumptionType.FIGHT, 500);
// }
break
;
case
MOTION_FIGHT:
// TODO: what if charged attack
return
new
Consumption
(
ConsumptionType
.
FIGHT
,
500
);
}
}
// TODO: Add other logic
return
new
Consumption
();
}
...
...
@@ -671,11 +683,11 @@ public class StaminaManager {
return
new
Consumption
(
ConsumptionType
.
FIGHT
,
+
500
);
}
private
Consumption
getCatalyst
Sustained
Cost
(
int
skillId
)
{
private
Consumption
getCatalystCost
(
int
skillId
)
{
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
5000
);
// Character specific handling
switch
(
skillId
)
{
// TODO:
Yanfei
// TODO:
}
return
consumption
;
}
...
...
@@ -684,18 +696,20 @@ public class StaminaManager {
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
1333
);
// 4000 / 3 = 1333
// Character specific handling
switch
(
skillId
)
{
case
10571
:
// Arataki Itto, does not consume stamina at all.
case
10571
:
case
10532
:
consumption
.
amount
=
0
;
break
;
case
10160
:
// Diluc, with talent "Relentless" stamina cost is decreased by 50%
// TODO: How to get talent status?
case
10160
:
if
(
player
.
getTeamManager
().
getCurrentAvatarEntity
().
getAvatar
().
getProudSkillList
().
contains
(
162101
))
{
consumption
.
amount
/=
2
;
}
break
;
}
return
consumption
;
}
private
Consumption
getPolearm
Sustained
Cost
(
int
skillId
)
{
private
Consumption
getPolearmCost
(
int
skillId
)
{
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
2500
);
// Character specific handling
switch
(
skillId
)
{
...
...
@@ -704,11 +718,11 @@ public class StaminaManager {
return
consumption
;
}
private
Consumption
getSword
Sustained
Cost
(
int
skillId
)
{
private
Consumption
getSwordCost
(
int
skillId
)
{
Consumption
consumption
=
new
Consumption
(
ConsumptionType
.
FIGHT
,
-
2000
);
// Character specific handling
switch
(
skillId
)
{
case
10421
:
// Keqing, -2500
case
10421
:
consumption
.
amount
=
-
2500
;
break
;
}
...
...
src/main/java/emu/grasscutter/game/player/Player.java
View file @
9b26426e
...
...
@@ -85,6 +85,8 @@ public class Player {
private
Set
<
Integer
>
flyCloakList
;
private
Set
<
Integer
>
costumeList
;
private
Integer
widgetId
;
@Transient
private
long
nextGuid
=
0
;
@Transient
private
int
peerId
;
@Transient
private
World
world
;
...
...
@@ -302,6 +304,14 @@ public class Player {
this
.
updateProfile
();
}
public
Integer
getWidgetId
()
{
return
widgetId
;
}
public
void
setWidgetId
(
Integer
widgetId
)
{
this
.
widgetId
=
widgetId
;
}
public
Position
getPos
()
{
return
pos
;
}
...
...
@@ -1170,6 +1180,8 @@ public class Player {
session
.
send
(
new
PacketFinishedParentQuestNotify
(
this
));
session
.
send
(
new
PacketQuestListNotify
(
this
));
session
.
send
(
new
PacketServerCondMeetQuestListUpdateNotify
(
this
));
session
.
send
(
new
PacketAllWidgetDataNotify
(
this
));
session
.
send
(
new
PacketWidgetGadgetAllDataNotify
());
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.
...
...
@@ -1267,7 +1279,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
;
}
...
...
src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java
View file @
9b26426e
...
...
@@ -18,7 +18,7 @@ import emu.grasscutter.server.packet.send.PacketBuyGoodsRsp;
import
emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify
;
import
emu.grasscutter.utils.Utils
;
import
java.util.
HashMap
;
import
java.util.
ArrayList
;
import
java.util.List
;
import
java.util.Optional
;
...
...
@@ -56,36 +56,13 @@ public class HandlerBuyGoodsReq extends PacketHandler {
return
;
}
if
(
sg
.
getScoin
()
>
0
&&
session
.
getPlayer
().
getMora
()
<
buyGoodsReq
.
getBoughtNum
()
*
sg
.
getScoin
())
{
List
<
ItemParamData
>
costs
=
new
ArrayList
<
ItemParamData
>(
sg
.
getCostItemList
());
// Can this even be null?
costs
.
add
(
new
ItemParamData
(
202
,
sg
.
getScoin
()));
costs
.
add
(
new
ItemParamData
(
201
,
sg
.
getHcoin
()));
costs
.
add
(
new
ItemParamData
(
203
,
sg
.
getMcoin
()));
if
(!
session
.
getPlayer
().
getInventory
().
payItems
(
costs
.
toArray
(
new
ItemParamData
[
0
]),
buyGoodsReq
.
getBoughtNum
()))
{
return
;
}
if
(
sg
.
getHcoin
()
>
0
&&
session
.
getPlayer
().
getPrimogems
()
<
buyGoodsReq
.
getBoughtNum
()
*
sg
.
getHcoin
())
{
return
;
}
if
(
sg
.
getMcoin
()
>
0
&&
session
.
getPlayer
().
getCrystals
()
<
buyGoodsReq
.
getBoughtNum
()
*
sg
.
getMcoin
())
{
return
;
}
HashMap
<
GameItem
,
Integer
>
itemsCache
=
new
HashMap
<>();
if
(
sg
.
getCostItemList
()
!=
null
)
{
for
(
ItemParamData
p
:
sg
.
getCostItemList
())
{
Optional
<
GameItem
>
invItem
=
session
.
getPlayer
().
getInventory
().
getItems
().
values
().
stream
().
filter
(
x
->
x
.
getItemId
()
==
p
.
getId
()).
findFirst
();
if
(
invItem
.
isEmpty
()
||
invItem
.
get
().
getCount
()
<
p
.
getCount
())
return
;
itemsCache
.
put
(
invItem
.
get
(),
p
.
getCount
()
*
buyGoodsReq
.
getBoughtNum
());
}
}
session
.
getPlayer
().
setMora
(
session
.
getPlayer
().
getMora
()
-
buyGoodsReq
.
getBoughtNum
()
*
sg
.
getScoin
());
session
.
getPlayer
().
setPrimogems
(
session
.
getPlayer
().
getPrimogems
()
-
buyGoodsReq
.
getBoughtNum
()
*
sg
.
getHcoin
());
session
.
getPlayer
().
setCrystals
(
session
.
getPlayer
().
getCrystals
()
-
buyGoodsReq
.
getBoughtNum
()
*
sg
.
getMcoin
());
if
(!
itemsCache
.
isEmpty
())
{
for
(
GameItem
gi
:
itemsCache
.
keySet
())
{
session
.
getPlayer
().
getInventory
().
removeItem
(
gi
,
itemsCache
.
get
(
gi
));
}
itemsCache
.
clear
();
}
session
.
getPlayer
().
addShopLimit
(
sg
.
getGoodsId
(),
buyGoodsReq
.
getBoughtNum
(),
ShopManager
.
getShopNextRefreshTime
(
sg
));
GameItem
item
=
new
GameItem
(
GameData
.
getItemDataMap
().
get
(
sg
.
getGoodsItem
().
getId
()));
...
...
src/main/java/emu/grasscutter/server/packet/recv/HandlerGetWidgetSlotReq.java
View file @
9b26426e
package
emu.grasscutter.server.packet.recv
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.net.packet.Opcodes
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.packet.PacketHandler
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.PacketGetShopRsp
;
import
emu.grasscutter.server.packet.send.PacketGetWidgetSlotRsp
;
@Opcodes
(
PacketOpcodes
.
GetWidgetSlotReq
)
public
class
HandlerGetWidgetSlotReq
extends
PacketHandler
{
@Override
public
void
handle
(
GameSession
session
,
byte
[]
header
,
byte
[]
payload
)
throws
Exception
{
// Unhandled
Player
player
=
session
.
getPlayer
();
session
.
send
(
new
PacketGetWidgetSlotRsp
(
player
));
}
}
src/main/java/emu/grasscutter/server/packet/recv/HandlerSetWidgetSlotReq.java
0 → 100644
View file @
9b26426e
package
emu.grasscutter.server.packet.recv
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.net.packet.Opcodes
;
import
emu.grasscutter.net.packet.PacketHandler
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.SetWidgetSlotReqOuterClass
;
import
emu.grasscutter.net.proto.WidgetSlotOpOuterClass
;
import
emu.grasscutter.server.game.GameSession
;
import
emu.grasscutter.server.packet.send.PacketSetWidgetSlotRsp
;
import
emu.grasscutter.server.packet.send.PacketWidgetSlotChangeNotify
;
@Opcodes
(
PacketOpcodes
.
SetWidgetSlotReq
)
public
class
HandlerSetWidgetSlotReq
extends
PacketHandler
{
@Override
public
void
handle
(
GameSession
session
,
byte
[]
header
,
byte
[]
payload
)
throws
Exception
{
SetWidgetSlotReqOuterClass
.
SetWidgetSlotReq
req
=
SetWidgetSlotReqOuterClass
.
SetWidgetSlotReq
.
parseFrom
(
payload
);
Player
player
=
session
.
getPlayer
();
player
.
setWidgetId
(
req
.
getMaterialId
());
// WidgetSlotChangeNotify op & slot key
session
.
send
(
new
PacketWidgetSlotChangeNotify
(
WidgetSlotOpOuterClass
.
WidgetSlotOp
.
DETACH
));
// WidgetSlotChangeNotify slot
session
.
send
(
new
PacketWidgetSlotChangeNotify
(
req
.
getMaterialId
()));
// SetWidgetSlotRsp
session
.
send
(
new
PacketSetWidgetSlotRsp
(
req
.
getMaterialId
()));
}
}
src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java
View file @
9b26426e
...
...
@@ -14,6 +14,7 @@ public class HandlerVehicleInteractReq extends PacketHandler {
@Override
public
void
handle
(
GameSession
session
,
byte
[]
header
,
byte
[]
payload
)
throws
Exception
{
VehicleInteractReqOuterClass
.
VehicleInteractReq
req
=
VehicleInteractReqOuterClass
.
VehicleInteractReq
.
parseFrom
(
payload
);
session
.
getPlayer
().
getStaminaManager
().
handleVehicleInteractReq
(
session
,
req
.
getEntityId
(),
req
.
getInteractType
());
session
.
send
(
new
PacketVehicleInteractRsp
(
session
.
getPlayer
(),
req
.
getEntityId
(),
req
.
getInteractType
()));
}
}
src/main/java/emu/grasscutter/server/packet/send/PacketAllWidgetDataNotify.java
0 → 100644
View file @
9b26426e
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.AllWidgetDataNotifyOuterClass.AllWidgetDataNotify
;
import
emu.grasscutter.net.proto.LunchBoxDataOuterClass
;
import
emu.grasscutter.net.proto.WidgetSlotDataOuterClass
;
import
emu.grasscutter.net.proto.WidgetSlotTagOuterClass
;
import
java.util.List
;
import
java.util.Map
;
public
class
PacketAllWidgetDataNotify
extends
BasePacket
{
public
PacketAllWidgetDataNotify
(
Player
player
)
{
super
(
PacketOpcodes
.
AllWidgetDataNotify
);
// TODO: Implement this
AllWidgetDataNotify
.
Builder
proto
=
AllWidgetDataNotify
.
newBuilder
()
// If you want to implement this, feel free to do so. :)
.
setLunchBoxData
(
LunchBoxDataOuterClass
.
LunchBoxData
.
newBuilder
().
build
()
)
// Maybe it's a little difficult, or it makes you upset :(
.
addAllOneoffGatherPointDetectorDataList
(
List
.
of
())
// So, goodbye, and hopefully sometime in the future o(* ̄▽ ̄*)ブ
.
addAllCoolDownGroupDataList
(
List
.
of
())
// I'll see your PR with a title that says (・∀・(・∀・(・∀・*)
.
addAllAnchorPointList
(
List
.
of
())
// "Complete implementation of widget functionality" b( ̄▽ ̄)d
.
addAllClientCollectorDataList
(
List
.
of
())
// Good luck, my boy.
.
addAllNormalCoolDownDataList
(
List
.
of
());
if
(
player
.
getWidgetId
()
==
null
)
{
proto
.
addAllSlotList
(
List
.
of
());
}
else
{
proto
.
addSlotList
(
WidgetSlotDataOuterClass
.
WidgetSlotData
.
newBuilder
()
.
setIsActive
(
true
)
.
setMaterialId
(
player
.
getWidgetId
())
.
build
()
);
proto
.
addSlotList
(
WidgetSlotDataOuterClass
.
WidgetSlotData
.
newBuilder
()
.
setTag
(
WidgetSlotTagOuterClass
.
WidgetSlotTag
.
WIDGET_SLOT_ATTACH_AVATAR
)
.
build
()
);
}
AllWidgetDataNotify
protoData
=
proto
.
build
();
this
.
setData
(
protoData
);
}
}
src/main/java/emu/grasscutter/server/packet/send/PacketGetWidgetSlotRsp.java
0 → 100644
View file @
9b26426e
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.game.player.Player
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.GetWidgetSlotRspOuterClass
;
import
emu.grasscutter.net.proto.WidgetSlotDataOuterClass
;
import
emu.grasscutter.net.proto.WidgetSlotTagOuterClass
;
import
java.util.List
;
public
class
PacketGetWidgetSlotRsp
extends
BasePacket
{
public
PacketGetWidgetSlotRsp
(
Player
player
)
{
super
(
PacketOpcodes
.
GetWidgetSlotRsp
);
GetWidgetSlotRspOuterClass
.
GetWidgetSlotRsp
.
Builder
proto
=
GetWidgetSlotRspOuterClass
.
GetWidgetSlotRsp
.
newBuilder
();
if
(
player
.
getWidgetId
()
==
null
)
{
proto
.
addAllSlotList
(
List
.
of
());
}
else
{
proto
.
addSlotList
(
WidgetSlotDataOuterClass
.
WidgetSlotData
.
newBuilder
()
.
setIsActive
(
true
)
.
setMaterialId
(
player
.
getWidgetId
())
.
build
()
);
proto
.
addSlotList
(
WidgetSlotDataOuterClass
.
WidgetSlotData
.
newBuilder
()
.
setTag
(
WidgetSlotTagOuterClass
.
WidgetSlotTag
.
WIDGET_SLOT_ATTACH_AVATAR
)
.
build
()
);
}
GetWidgetSlotRspOuterClass
.
GetWidgetSlotRsp
protoData
=
proto
.
build
();
this
.
setData
(
protoData
);
}
}
src/main/java/emu/grasscutter/server/packet/send/PacketSetWidgetSlotRsp.java
0 → 100644
View file @
9b26426e
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.SetWidgetSlotRspOuterClass
;
public
class
PacketSetWidgetSlotRsp
extends
BasePacket
{
public
PacketSetWidgetSlotRsp
(
int
materialId
)
{
super
(
PacketOpcodes
.
SetWidgetSlotRsp
);
SetWidgetSlotRspOuterClass
.
SetWidgetSlotRsp
proto
=
SetWidgetSlotRspOuterClass
.
SetWidgetSlotRsp
.
newBuilder
()
.
setMaterialId
(
materialId
)
.
build
();
this
.
setData
(
proto
);
}
}
src/main/java/emu/grasscutter/server/packet/send/PacketVehicleStaminaNotify.java
0 → 100644
View file @
9b26426e
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.VehicleStaminaNotifyOuterClass.VehicleStaminaNotify
;
public
class
PacketVehicleStaminaNotify
extends
BasePacket
{
public
PacketVehicleStaminaNotify
(
int
vehicleId
,
float
newStamina
)
{
super
(
PacketOpcodes
.
VehicleStaminaNotify
);
VehicleStaminaNotify
.
Builder
proto
=
VehicleStaminaNotify
.
newBuilder
();
proto
.
setEntityId
(
vehicleId
);
proto
.
setCurStamina
(
newStamina
);
this
.
setData
(
proto
.
build
());
}
}
src/main/java/emu/grasscutter/server/packet/send/PacketWidgetGadgetAllDataNotify.java
0 → 100644
View file @
9b26426e
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.WidgetGadgetAllDataNotifyOuterClass.WidgetGadgetAllDataNotify
;
public
class
PacketWidgetGadgetAllDataNotify
extends
BasePacket
{
public
PacketWidgetGadgetAllDataNotify
()
{
super
(
PacketOpcodes
.
AllWidgetDataNotify
);
WidgetGadgetAllDataNotify
proto
=
WidgetGadgetAllDataNotify
.
newBuilder
().
build
();
this
.
setData
(
proto
);
}
}
src/main/java/emu/grasscutter/server/packet/send/PacketWidgetSlotChangeNotify.java
0 → 100644
View file @
9b26426e
package
emu.grasscutter.server.packet.send
;
import
emu.grasscutter.net.packet.BasePacket
;
import
emu.grasscutter.net.packet.PacketOpcodes
;
import
emu.grasscutter.net.proto.WidgetSlotChangeNotifyOuterClass
;
import
emu.grasscutter.net.proto.WidgetSlotDataOuterClass
;
import
emu.grasscutter.net.proto.WidgetSlotOpOuterClass
;
public
class
PacketWidgetSlotChangeNotify
extends
BasePacket
{
public
PacketWidgetSlotChangeNotify
(
WidgetSlotChangeNotifyOuterClass
.
WidgetSlotChangeNotify
proto
)
{
super
(
PacketOpcodes
.
WidgetSlotChangeNotify
);
this
.
setData
(
proto
);
}
public
PacketWidgetSlotChangeNotify
(
WidgetSlotOpOuterClass
.
WidgetSlotOp
op
)
{
super
(
PacketOpcodes
.
WidgetSlotChangeNotify
);
WidgetSlotChangeNotifyOuterClass
.
WidgetSlotChangeNotify
proto
=
WidgetSlotChangeNotifyOuterClass
.
WidgetSlotChangeNotify
.
newBuilder
()
.
setOp
(
op
)
.
setSlot
(
WidgetSlotDataOuterClass
.
WidgetSlotData
.
newBuilder
()
.
setIsActive
(
true
)
.
build
()
)
.
build
();
this
.
setData
(
proto
);
}
public
PacketWidgetSlotChangeNotify
(
int
materialId
)
{
super
(
PacketOpcodes
.
WidgetSlotChangeNotify
);
WidgetSlotChangeNotifyOuterClass
.
WidgetSlotChangeNotify
proto
=
WidgetSlotChangeNotifyOuterClass
.
WidgetSlotChangeNotify
.
newBuilder
()
.
setSlot
(
WidgetSlotDataOuterClass
.
WidgetSlotData
.
newBuilder
()
.
setIsActive
(
true
)
.
setMaterialId
(
materialId
)
.
build
()
)
.
build
();
this
.
setData
(
proto
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment