2022-08-21 19:33:54 +08:00
package emu.grasscutter.command.commands ;
import java.util.HashMap ;
import java.util.List ;
import java.util.Map ;
import emu.grasscutter.command.Command ;
import emu.grasscutter.command.CommandHandler ;
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.server.packet.send.PacketEntityFightPropUpdateNotify ;
@Command (
label = " setStats " ,
aliases = { " stats " , " stat " } ,
usage = {
" [set] <stat> <value> " ,
" (lock|freeze) <stat> [<value>] " , // Can lock to current value
" (unlock|unfreeze) <stat> " } ,
permission = " player.setstats " ,
permissionTargeted = " player.setstats.others " )
public final class SetStatsCommand implements CommandHandler {
private static class Stat {
String name ;
FightProperty prop ;
public Stat ( FightProperty prop ) {
this . name = prop . toString ( ) ;
this . prop = prop ;
}
public Stat ( String name , FightProperty prop ) {
this . name = name ;
this . prop = prop ;
}
}
private static enum Action {
ACTION_SET ( " commands.generic.set_to " , " commands.generic.set_for_to " ) ,
ACTION_LOCK ( " commands.setStats.locked_to " , " commands.setStats.locked_for_to " ) ,
ACTION_UNLOCK ( " commands.setStats.unlocked " , " commands.setStats.unlocked_for " ) ;
public final String messageKeySelf ;
public final String messageKeyOther ;
private Action ( String messageKeySelf , String messageKeyOther ) {
this . messageKeySelf = messageKeySelf ;
this . messageKeyOther = messageKeyOther ;
}
}
private Map < String , Stat > stats ;
public SetStatsCommand ( ) {
this . stats = new HashMap < > ( ) ;
for ( String key : FightProperty . getShortNames ( ) ) {
this . stats . put ( key , new Stat ( FightProperty . getPropByShortName ( key ) ) ) ;
}
// Full FightProperty enum that won't be advertised but can be used by devs
// They have a prefix to avoid the "hp" clash
for ( FightProperty prop : FightProperty . values ( ) ) {
String name = prop . toString ( ) . substring ( 10 ) ; // FIGHT_PROP_BASE_HP -> _BASE_HP
String key = name . toLowerCase ( ) ; // _BASE_HP -> _base_hp
name = name . substring ( 1 ) ; // _BASE_HP -> BASE_HP
this . stats . put ( key , new Stat ( name , prop ) ) ;
}
// Compatibility aliases
this . stats . put ( " mhp " , this . stats . get ( " maxhp " ) ) ;
this . stats . put ( " hp " , new Stat ( FightProperty . FIGHT_PROP_CUR_HP ) ) ; // Overrides FIGHT_PROP_HP
this . stats . put ( " atk " , new Stat ( FightProperty . FIGHT_PROP_CUR_ATTACK ) ) ; // Overrides FIGHT_PROP_ATTACK
this . stats . put ( " atkb " , new Stat ( FightProperty . FIGHT_PROP_BASE_ATTACK ) ) ; // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff.
this . stats . put ( " eanemo " , this . stats . get ( " anemo% " ) ) ;
this . stats . put ( " ecryo " , this . stats . get ( " cryo% " ) ) ;
this . stats . put ( " edendro " , this . stats . get ( " dendro% " ) ) ;
this . stats . put ( " edend " , this . stats . get ( " dendro% " ) ) ;
this . stats . put ( " eelectro " , this . stats . get ( " electro% " ) ) ;
this . stats . put ( " eelec " , this . stats . get ( " electro% " ) ) ;
this . stats . put ( " ethunder " , this . stats . get ( " electro% " ) ) ;
this . stats . put ( " egeo " , this . stats . get ( " geo% " ) ) ;
this . stats . put ( " ehydro " , this . stats . get ( " hydro% " ) ) ;
this . stats . put ( " epyro " , this . stats . get ( " pyro% " ) ) ;
this . stats . put ( " ephys " , this . stats . get ( " phys% " ) ) ;
}
public static float parsePercent ( String input ) throws NumberFormatException {
if ( input . endsWith ( " % " ) ) {
return Float . parseFloat ( input . substring ( 0 , input . length ( ) - 1 ) ) / 100f ;
} else {
return Float . parseFloat ( input ) ;
}
}
@Override
public void execute ( Player sender , Player targetPlayer , List < String > args ) {
String statStr = null ;
String valueStr ;
float value = 0f ;
if ( args . size ( ) < 2 ) {
sendUsageMessage ( sender ) ;
return ;
}
// Get the action and stat
String arg0 = args . remove ( 0 ) . toLowerCase ( ) ;
Action action = switch ( arg0 ) {
default - > { statStr = arg0 ; yield Action . ACTION_SET ; } // Implicit set command
case " set " - > Action . ACTION_SET ; // Explicit set command
case " lock " , " freeze " - > Action . ACTION_LOCK ;
case " unlock " , " unfreeze " - > Action . ACTION_UNLOCK ;
} ;
if ( statStr = = null ) {
statStr = args . remove ( 0 ) . toLowerCase ( ) ;
}
if ( ! stats . containsKey ( statStr ) ) {
sendUsageMessage ( sender ) ; // Invalid stat or action
return ;
}
Stat stat = stats . get ( statStr ) ;
EntityAvatar entity = targetPlayer . getTeamManager ( ) . getCurrentAvatarEntity ( ) ;
Avatar avatar = entity . getAvatar ( ) ;
// Get the value if the action requires it
try {
switch ( action ) {
case ACTION_LOCK :
if ( args . isEmpty ( ) ) { // Lock to current value
value = avatar . getFightProperty ( stat . prop ) ;
break ;
} // Else fall-through and lock to supplied value
case ACTION_SET :
value = parsePercent ( args . remove ( 0 ) ) ;
break ;
case ACTION_UNLOCK :
break ;
}
} catch ( NumberFormatException ignored ) {
CommandHandler . sendTranslatedMessage ( sender , " commands.generic.invalid.statValue " ) ;
return ;
} catch ( IndexOutOfBoundsException ignored ) {
sendUsageMessage ( sender ) ;
return ;
}
if ( ! args . isEmpty ( ) ) { // Leftover arguments!
sendUsageMessage ( sender ) ;
return ;
}
switch ( action ) {
case ACTION_SET :
entity . setFightProperty ( stat . prop , value ) ;
entity . getWorld ( ) . broadcastPacket ( new PacketEntityFightPropUpdateNotify ( entity , stat . prop ) ) ;
break ;
case ACTION_LOCK :
avatar . getFightPropOverrides ( ) . put ( stat . prop . getId ( ) , value ) ;
avatar . recalcStats ( ) ;
break ;
case ACTION_UNLOCK :
avatar . getFightPropOverrides ( ) . remove ( stat . prop . getId ( ) ) ;
avatar . recalcStats ( ) ;
break ;
}
// Report action
if ( FightProperty . isPercentage ( stat . prop ) ) {
valueStr = String . format ( " %.1f%% " , value * 100f ) ;
} else {
valueStr = String . format ( " %.0f " , value ) ;
}
if ( targetPlayer = = sender ) {
CommandHandler . sendTranslatedMessage ( sender , action . messageKeySelf , stat . name , valueStr ) ;
} else {
String uidStr = targetPlayer . getAccount ( ) . getId ( ) ;
CommandHandler . sendTranslatedMessage ( sender , action . messageKeyOther , stat . name , uidStr , valueStr ) ;
}
return ;
}
}