2024-04-28 13:55:19 +00:00
using System ;
using System.Text ;
using ColdMint.scripts.damage ;
using ColdMint.scripts.utils ;
using ColdMint.scripts.weapon ;
2024-05-07 11:36:06 +00:00
using Godot ;
namespace ColdMint.scripts.character ;
2024-04-28 13:55:19 +00:00
/// <summary>
/// <para>玩家角色</para>
/// </summary>
public partial class Player : CharacterTemplate
{
2024-05-07 11:36:06 +00:00
private PackedScene ? _floatLabelPackedScene ;
2024-04-28 13:55:19 +00:00
2024-05-07 11:36:06 +00:00
private Control ? _floatLabel ;
2024-04-28 13:55:19 +00:00
2024-05-06 10:59:39 +00:00
//Empty object projectile
//空的物品抛射线
2024-05-07 11:36:06 +00:00
private readonly Vector2 [ ] _emptyVector2Array = new [ ] { Vector2 . Zero } ;
2024-04-28 13:55:19 +00:00
//抛物线
2024-05-07 11:36:06 +00:00
private Line2D ? _parabola ;
2024-04-28 13:55:19 +00:00
//用于检测玩家是否站在平台上的射线
2024-05-07 11:36:06 +00:00
private RayCast2D ? _platformDetectionRayCast2D ;
2024-04-28 13:55:19 +00:00
//在拾捡范围内,可拾起的物品数量
2024-05-07 11:36:06 +00:00
private int _totalNumberOfPickups ;
2024-04-28 13:55:19 +00:00
2024-05-07 11:36:06 +00:00
private const float PromptTextDistance = 50 ;
2024-04-28 13:55:19 +00:00
//玩家可拾捡的物品
2024-05-07 11:36:06 +00:00
private Node2D ? _pickAbleItem ;
2024-04-28 13:55:19 +00:00
//抛出物品的飞行速度
2024-05-07 11:36:06 +00:00
private float _throwingVelocity = Config . CellSize * 13 ;
2024-04-28 13:55:19 +00:00
//射线是否与平台碰撞
2024-05-07 11:36:06 +00:00
private bool _collidingWithPlatform ;
2024-04-28 13:55:19 +00:00
//How long does it take for the character to recover from a collision with the platform after jumping off the platform (in seconds)
//角色从平台上跳下后,多少时间后恢复与平台的碰撞(单位:秒)
2024-05-07 11:36:06 +00:00
private double _platformCollisionRecoveryTime = 0.2f ;
2024-04-28 13:55:19 +00:00
//物品被扔出后多长时间恢复与地面和平台的碰撞(单位:秒)
2024-05-07 11:36:06 +00:00
private readonly double _itemCollisionRecoveryTime = 0.045f ;
2024-04-28 13:55:19 +00:00
public override void _Ready ( )
{
base . _Ready ( ) ;
2024-05-04 14:59:46 +00:00
CharacterName = TranslationServer . Translate ( "default_player_name" ) ;
2024-05-07 11:36:06 +00:00
_floatLabelPackedScene = GD . Load < PackedScene > ( "res://prefab/ui/FloatLabel.tscn" ) ;
_parabola = GetNode < Line2D > ( "Parabola" ) ;
_platformDetectionRayCast2D = GetNode < RayCast2D > ( "PlatformDetectionRayCast" ) ;
2024-04-28 13:55:19 +00:00
UpdateOperationTip ( ) ;
GameSceneNodeHolder . HealthBarUi . MaxHp = MaxHp ;
GameSceneNodeHolder . HealthBarUi . CurrentHp = CurrentHp ;
}
/// <summary>
/// <para>Update operation prompt</para>
/// <para>更新操作提示</para>
/// </summary>
private void UpdateOperationTip ( )
{
var operationTipBuilder = new StringBuilder ( ) ;
2024-05-07 11:36:06 +00:00
if ( _totalNumberOfPickups > 0 )
2024-04-28 13:55:19 +00:00
{
//If there's anything around to pick up
//如果周围有能捡的东西
if ( CurrentItem = = null )
{
2024-05-07 11:36:06 +00:00
if ( _pickAbleItem ! = null )
2024-04-28 13:55:19 +00:00
{
2024-05-07 11:36:06 +00:00
string? name = null ;
if ( _pickAbleItem is WeaponTemplate weaponTemplate )
2024-04-28 13:55:19 +00:00
{
//When the weapon has no owner, a pick up prompt is displayed.
//当武器没有主人时,显示捡起提示。
if ( weaponTemplate . Owner = = null | | weaponTemplate . Owner = = this )
{
name = TranslationServer . Translate ( weaponTemplate . Name ) ;
}
}
if ( name ! = null )
{
operationTipBuilder . Append (
TranslationServer . Translate ( InputMap . ActionGetEvents ( "pick_up" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "pick_up" ) ) ;
operationTipBuilder . Append ( name ) ;
}
}
}
else
{
2024-05-07 11:36:06 +00:00
string? pickAbleItemName = null ;
string? currentItemName = null ;
2024-04-28 13:55:19 +00:00
string mustBeThrown = TranslationServer . Translate ( "must_be_thrown" ) ;
2024-05-07 11:36:06 +00:00
if ( _pickAbleItem ! = null )
2024-04-28 13:55:19 +00:00
{
//可捡的物品是武器
2024-05-07 11:36:06 +00:00
if ( _pickAbleItem is WeaponTemplate weaponTemplate )
2024-04-28 13:55:19 +00:00
{
pickAbleItemName = TranslationServer . Translate ( weaponTemplate . Name ) ;
}
}
if ( CurrentItem ! = null )
{
//当前持有的物品是武器
if ( CurrentItem is WeaponTemplate weaponTemplate )
{
currentItemName = TranslationServer . Translate ( weaponTemplate . Name ) ;
}
}
if ( pickAbleItemName ! = null & & currentItemName ! = null & & mustBeThrown ! = "must_be_thrown" )
{
operationTipBuilder . Append ( string . Format ( mustBeThrown , currentItemName , pickAbleItemName ) ) ;
operationTipBuilder . Append ( ' ' ) ;
operationTipBuilder . Append (
TranslationServer . Translate ( InputMap . ActionGetEvents ( "throw" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "throw" ) ) ;
operationTipBuilder . Append ( currentItemName ) ;
}
}
GameSceneNodeHolder . OperationTipLabel . Text = operationTipBuilder . ToString ( ) ;
return ;
}
operationTipBuilder . Append ( TranslationServer . Translate ( InputMap . ActionGetEvents ( "ui_left" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "move_left" ) ) ;
operationTipBuilder . Append ( ' ' ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( InputMap . ActionGetEvents ( "ui_right" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "move_right" ) ) ;
operationTipBuilder . Append ( ' ' ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( InputMap . ActionGetEvents ( "ui_up" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "jump" ) ) ;
2024-05-07 11:36:06 +00:00
if ( _collidingWithPlatform )
2024-04-28 13:55:19 +00:00
{
operationTipBuilder . Append ( ' ' ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( InputMap . ActionGetEvents ( "ui_down" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "jump_down" ) ) ;
}
if ( CurrentItem ! = null )
{
operationTipBuilder . Append ( ' ' ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( InputMap . ActionGetEvents ( "throw" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "throw" ) ) ;
if ( CurrentItem is WeaponTemplate weaponTemplate )
{
operationTipBuilder . Append ( TranslationServer . Translate ( weaponTemplate . Name ) ) ;
//提示武器攻击
operationTipBuilder . Append ( ' ' ) ;
operationTipBuilder . Append (
TranslationServer . Translate ( InputMap . ActionGetEvents ( "use_item" ) [ 0 ] . AsText ( ) ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( "use_item" ) ) ;
operationTipBuilder . Append ( TranslationServer . Translate ( weaponTemplate . Name ) ) ;
}
}
GameSceneNodeHolder . OperationTipLabel . Text = operationTipBuilder . ToString ( ) ;
}
protected override void HookPhysicsProcess ( ref Vector2 velocity , double delta )
{
2024-05-06 10:59:39 +00:00
//When the collision state between the platform detection ray and the platform changes
2024-04-28 13:55:19 +00:00
//在平台检测射线与平台碰撞状态改变时
2024-05-07 11:36:06 +00:00
if ( _platformDetectionRayCast2D ! = null & & _platformDetectionRayCast2D . IsColliding ( ) ! = _collidingWithPlatform )
2024-04-28 13:55:19 +00:00
{
2024-05-06 10:59:39 +00:00
//When the state changes, update the action hint
2024-04-28 13:55:19 +00:00
//当状态改变时,更新操作提示
2024-05-07 11:36:06 +00:00
_collidingWithPlatform = _platformDetectionRayCast2D . IsColliding ( ) ;
2024-04-28 13:55:19 +00:00
UpdateOperationTip ( ) ;
}
2024-05-06 10:59:39 +00:00
//If the character is on the ground, give an upward velocity when the jump button is pressed
2024-04-28 13:55:19 +00:00
//如果角色正在地面上,按下跳跃键时,给予一个向上的速度
if ( Input . IsActionJustPressed ( "ui_up" ) & & IsOnFloor ( ) )
velocity . Y = JumpVelocity ;
2024-05-06 10:59:39 +00:00
//Moving left and right
2024-04-28 13:55:19 +00:00
//左右移动
var axis = Input . GetAxis ( "ui_left" , "ui_right" ) ;
velocity . X = axis * Speed ;
2024-05-06 10:59:39 +00:00
//Use items
2024-04-28 13:55:19 +00:00
//使用物品
if ( Input . IsActionPressed ( "use_item" ) )
{
UseItem ( GetGlobalMousePosition ( ) ) ;
}
2024-05-06 10:59:39 +00:00
//Pick up an item
2024-04-28 13:55:19 +00:00
//捡起物品
if ( Input . IsActionJustPressed ( "pick_up" ) )
{
2024-05-07 11:36:06 +00:00
var success = PickItem ( _pickAbleItem ) ;
2024-04-29 15:25:03 +00:00
if ( success )
{
2024-05-07 11:36:06 +00:00
_pickAbleItem = null ;
_totalNumberOfPickups - - ;
if ( _floatLabel ! = null )
2024-04-29 15:25:03 +00:00
{
2024-05-07 11:36:06 +00:00
_floatLabel . QueueFree ( ) ;
_floatLabel = null ;
2024-04-29 15:25:03 +00:00
}
UpdateOperationTip ( ) ;
}
2024-04-28 13:55:19 +00:00
}
if ( Input . IsActionJustPressed ( "ui_down" ) )
{
2024-05-07 11:36:06 +00:00
if ( _collidingWithPlatform )
2024-04-28 13:55:19 +00:00
{
2024-05-06 10:59:39 +00:00
//When the character stands on the platform and presses the ui_down key, we cancel the collision between the character and the platform
2024-04-28 13:55:19 +00:00
//当角色站在平台上按下 ui_down 键时,我们取消角色与平台的碰撞
var timer = new Timer ( ) ;
AddChild ( timer ) ;
2024-05-07 11:36:06 +00:00
timer . WaitTime = _platformCollisionRecoveryTime ;
2024-04-28 13:55:19 +00:00
timer . OneShot = true ;
timer . Start ( ) ;
timer . Timeout + = ( ) = >
{
SetCollisionMaskValue ( Config . LayerNumber . Platform , true ) ;
timer . QueueFree ( ) ;
} ;
SetCollisionMaskValue ( Config . LayerNumber . Platform , false ) ;
}
}
2024-05-06 10:59:39 +00:00
//Display a parabola when an item is thrown
2024-04-28 13:55:19 +00:00
//抛出物品时,显示抛物线
if ( Input . IsActionPressed ( "throw" ) )
{
2024-05-07 11:36:06 +00:00
if ( _parabola = = null )
2024-05-06 10:59:39 +00:00
{
2024-05-07 11:36:06 +00:00
return ;
2024-05-06 10:59:39 +00:00
}
2024-05-07 11:36:06 +00:00
if ( ItemMarker2D = = null )
2024-04-28 13:55:19 +00:00
{
2024-05-07 11:36:06 +00:00
//Cannot get the marked location of the item, then do not draw a line
//无法获取物品的标记位置,那么不绘制线
return ;
2024-04-28 13:55:19 +00:00
}
2024-05-07 11:36:06 +00:00
_parabola . Points = CurrentItem = = null
? _emptyVector2Array
: ParabolicUtils . ComputeParabolic ( ItemMarker2D . Position , GetThrowVelocity ( ) , Gravity , 0.1f ) ;
2024-04-28 13:55:19 +00:00
}
2024-05-06 10:59:39 +00:00
//When you raise your hand, throw the object
2024-04-28 13:55:19 +00:00
//抬起手时,抛出物品
if ( Input . IsActionJustReleased ( "throw" ) )
{
2024-05-07 11:36:06 +00:00
if ( CurrentItem = = null ) return ;
if ( _parabola ! = null )
{
_parabola . Points = new [ ] { Vector2 . Zero } ;
}
CurrentItem . Reparent ( GameSceneNodeHolder . WeaponContainer ) ;
switch ( CurrentItem )
2024-04-28 13:55:19 +00:00
{
2024-05-07 11:36:06 +00:00
case WeaponTemplate weaponTemplate :
2024-04-28 13:55:19 +00:00
{
var timer = new Timer ( ) ;
weaponTemplate . AddChild ( timer ) ;
2024-05-07 11:36:06 +00:00
timer . WaitTime = _itemCollisionRecoveryTime ;
2024-04-28 13:55:19 +00:00
timer . OneShot = true ;
timer . Timeout + = ( ) = >
{
//We cannot immediately resume the physical collision when the weapon is discharged, which will cause the weapon to collide with the ground and platform earlier, preventing the weapon from flying.
//仍出武器时,我们不能立即恢复物理碰撞,立即恢复会导致武器更早的与地面和平台碰撞,阻止武器的飞行。
weaponTemplate . EnableContactInjury = true ;
weaponTemplate . SetCollisionMaskValue ( Config . LayerNumber . Ground , true ) ;
weaponTemplate . SetCollisionMaskValue ( Config . LayerNumber . Platform , true ) ;
timer . QueueFree ( ) ;
} ;
timer . Start ( ) ;
weaponTemplate . Sleeping = false ;
weaponTemplate . LinearVelocity = Vector2 . Zero ;
2024-05-07 11:36:06 +00:00
break ;
2024-04-28 13:55:19 +00:00
}
2024-05-07 11:36:06 +00:00
case CharacterBody2D characterBody2D :
2024-04-28 13:55:19 +00:00
characterBody2D . Velocity = GetThrowVelocity ( ) ;
2024-05-07 11:36:06 +00:00
break ;
case RigidBody2D rigidBody2D :
2024-04-28 13:55:19 +00:00
rigidBody2D . LinearVelocity = GetThrowVelocity ( ) ;
2024-05-07 11:36:06 +00:00
break ;
2024-04-28 13:55:19 +00:00
}
2024-05-07 11:36:06 +00:00
CurrentItem = null ;
_totalNumberOfPickups + + ;
GameSceneNodeHolder . HotBar . RemoveItemFromItemSlotBySelectIndex ( 1 ) ;
UpdateOperationTip ( ) ;
2024-04-28 13:55:19 +00:00
}
}
private Vector2 GetThrowVelocity ( )
{
2024-05-06 10:59:39 +00:00
//We take the mouse position, normalize it, and then multiply it by the distance the player can throw
2024-04-28 13:55:19 +00:00
//我们拿到鼠标的位置,将其归一化处理,然后乘以玩家可扔出的距离
2024-05-07 11:36:06 +00:00
return GetLocalMousePosition ( ) . Normalized ( ) * _throwingVelocity ;
2024-04-28 13:55:19 +00:00
}
public override void _Process ( double delta )
{
AimTheCurrentItemAtAPoint ( GetGlobalMousePosition ( ) ) ;
2024-05-07 11:36:06 +00:00
var itemMarker2DPosition = Vector2 . Zero ;
if ( ItemMarker2D ! = null )
{
itemMarker2DPosition = ItemMarker2D . Position ;
}
2024-04-28 13:55:19 +00:00
var axis = Input . GetAxis ( "ui_left" , "ui_right" ) ;
switch ( axis )
{
case - 1 :
2024-05-06 10:59:39 +00:00
//Minus 1, we move to the left
2024-04-28 13:55:19 +00:00
//-1, 向左移动
FacingLeft = true ;
2024-05-07 11:36:06 +00:00
if ( ItemMarker2D ! = null )
{
itemMarker2DPosition . X = - ReadOnlyItemMarkerOriginalX ;
}
2024-04-28 13:55:19 +00:00
Flip ( ) ;
break ;
case 1 :
2024-05-06 10:59:39 +00:00
//1, move to the right
2024-04-28 13:55:19 +00:00
//1, 向右移动
FacingLeft = false ;
2024-05-07 11:36:06 +00:00
if ( ItemMarker2D ! = null )
{
itemMarker2DPosition . X = ReadOnlyItemMarkerOriginalX ;
}
2024-04-28 13:55:19 +00:00
Flip ( ) ;
break ;
}
2024-05-07 11:36:06 +00:00
if ( ItemMarker2D ! = null )
{
ItemMarker2D . Position = itemMarker2DPosition ;
}
2024-04-28 13:55:19 +00:00
}
protected override void Flip ( )
{
base . Flip ( ) ;
2024-05-06 10:59:39 +00:00
//If there is a weapon, flip it too
2024-04-28 13:55:19 +00:00
//如果有武器的话,也要翻转
2024-05-07 11:36:06 +00:00
if ( CurrentItem is WeaponTemplate weapon )
2024-04-28 13:55:19 +00:00
{
2024-05-07 11:36:06 +00:00
weapon . Flip ( FacingLeft ) ;
2024-04-28 13:55:19 +00:00
}
}
protected override void EnterThePickingRangeBody ( Node node )
{
2024-05-06 10:59:39 +00:00
if ( CurrentItem = = node )
{
//If the node entering the pick range is the node held by the player, then return.
//如果说进入拾捡范围的节点是玩家所持有的节点,那么返回。
return ;
}
2024-05-07 11:36:06 +00:00
if ( node is not Node2D node2D )
2024-04-28 13:55:19 +00:00
{
return ;
}
2024-05-07 11:36:06 +00:00
_totalNumberOfPickups + + ;
_pickAbleItem = node2D ;
if ( _floatLabelPackedScene ! = null )
2024-04-28 13:55:19 +00:00
{
2024-05-07 11:36:06 +00:00
//If there is a scene of floating text, then we generate floating text.
//如果有悬浮文本的场景,那么我们生成悬浮文本。
_floatLabel ? . QueueFree ( ) ;
_floatLabel = ( Control ) _floatLabelPackedScene . Instantiate ( ) ;
var rotationDegreesNode2D = node2D . RotationDegrees ;
var rotationDegreesNode2DAbs = Math . Abs ( rotationDegreesNode2D ) ;
_floatLabel . Position = rotationDegreesNode2DAbs > 90
? new Vector2 ( 0 , PromptTextDistance )
: new Vector2 ( 0 , - PromptTextDistance ) ;
_floatLabel . RotationDegrees = 0 - rotationDegreesNode2D ;
var label = _floatLabel . GetNode < Label > ( "Label" ) ;
if ( node is WeaponTemplate weapon )
2024-04-28 13:55:19 +00:00
{
2024-05-07 11:36:06 +00:00
var stringBuilder = new StringBuilder ( ) ;
2024-04-28 13:55:19 +00:00
if ( weapon . Owner is CharacterTemplate characterTemplate )
{
2024-05-04 14:59:46 +00:00
stringBuilder . Append ( characterTemplate . ReadOnlyCharacterName ) ;
2024-04-28 13:55:19 +00:00
stringBuilder . Append ( TranslationServer . Translate ( "de" ) ) ;
}
2024-05-07 11:36:06 +00:00
stringBuilder . Append ( TranslationServer . Translate ( weapon . Name ) ) ;
label . Text = stringBuilder . ToString ( ) ;
2024-04-28 13:55:19 +00:00
}
2024-05-07 11:36:06 +00:00
node . AddChild ( _floatLabel ) ;
2024-04-28 13:55:19 +00:00
}
UpdateOperationTip ( ) ;
}
protected override void ExitThePickingRangeBody ( Node node )
{
if ( node is not Node2D )
{
return ;
}
2024-05-07 11:36:06 +00:00
_totalNumberOfPickups - - ;
if ( _totalNumberOfPickups = = 0 )
2024-04-28 13:55:19 +00:00
{
2024-05-06 10:59:39 +00:00
//Set to null if there are no more items to pick up
2024-04-28 13:55:19 +00:00
//如果没有可捡的物品了, 设置为null
2024-05-07 11:36:06 +00:00
_pickAbleItem = null ;
2024-04-28 13:55:19 +00:00
}
2024-05-07 11:36:06 +00:00
if ( _floatLabel ! = null )
2024-04-28 13:55:19 +00:00
{
2024-05-07 11:36:06 +00:00
_floatLabel . QueueFree ( ) ;
_floatLabel = null ;
2024-04-28 13:55:19 +00:00
}
UpdateOperationTip ( ) ;
}
protected override void OnHit ( DamageTemplate damageTemplate )
{
base . OnHit ( damageTemplate ) ;
GameSceneNodeHolder . HealthBarUi . CurrentHp = CurrentHp ;
}
}