using Preparation.Interface;
using Preparation.Utility;
using System;
using System.Collections.Generic;
using System.Threading;
namespace GameClass.GameObj
{
public partial class Character : Moveable, ICharacter // 负责人LHR摆烂终了
{
private readonly ReaderWriterLockSlim hpReaderWriterLock = new();
public ReaderWriterLockSlim HPReadWriterLock => hpReaderWriterLock;
private readonly object vampireLock = new();
public object VampireLock => vampire;
private readonly object inventoryLock = new();
public object InventoryLock => inventoryLock;
#region 装弹、攻击相关的基本属性及方法
///
/// 装弹冷却
///
protected int cd;
public int CD
{
get
{
lock (actionLock)
{
return cd;
}
}
}
public int OrgCD { get; protected set; }
public readonly BulletType OriBulletOfPlayer;
private BulletType bulletOfPlayer;
public BulletType BulletOfPlayer
{
get
{
lock (actionLock)
{
return bulletOfPlayer;
}
}
set
{
lock (actionLock)
{
bulletOfPlayer = value;
cd = OrgCD = (BulletFactory.BulletCD(value));
Debugger.Output(this, string.Format("'s CD has been set to: {0}.", cd));
maxBulletNum = bulletNum = (BulletFactory.BulletNum(value));
}
}
}
protected int maxBulletNum;
public int MaxBulletNum
{
get
{
lock (actionLock)
{
return maxBulletNum;
}
}
}
private int bulletNum;
private int updateTimeOfBulletNum = 0;
public int UpdateBulletNum(int time)
{
lock (actionLock)
{
if (bulletNum < maxBulletNum)
{
int add = Math.Min(maxBulletNum - bulletNum, (time - updateTimeOfBulletNum) / cd);
updateTimeOfBulletNum += add * cd;
return (bulletNum += add);
}
return maxBulletNum;
}
}
///
/// 进行一次攻击
///
/// 攻击操作发出的子弹
public Bullet? Attack(double angle, int time)
{
lock (actionLock)
{
if (bulletOfPlayer == BulletType.Null)
return null;
if (UpdateBulletNum(time) > 0)
{
if (bulletNum == maxBulletNum) updateTimeOfBulletNum = time;
--bulletNum;
XY res = Position + new XY // 子弹紧贴人物生成。
(
(int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Cos(angle))) * Math.Sign(Math.Cos(angle)),
(int)(Math.Abs((Radius + BulletFactory.BulletRadius(bulletOfPlayer)) * Math.Sin(angle))) * Math.Sign(Math.Sin(angle))
);
Bullet? bullet = BulletFactory.GetBullet(this, res, this.bulletOfPlayer);
if (bullet == null) return null;
if (TryAddAp()) bullet.AddAP(GameData.ApPropAdd);
facingDirection = new(angle, bullet.AttackDistance);
return bullet;
}
else
return null;
}
}
/*
///
/// 攻击被反弹,反弹伤害不会再被反弹
///
///
///
/// 反弹伤害者
/// 是否因反弹伤害而死
private bool BeBounced(int subHP, bool hasSpear, Character? bouncer)
{
lock (beAttackedLock)
{
if (hp <= 0)
return false;
if (!(bouncer?.TeamID == this.TeamID))
{
if (hasSpear || !HasShield)
_ = TrySubHp(subHP);
if (hp <= 0)
TryActivatingLIFE();
}
return hp <= 0;
}
}*/
#endregion
#region 感知相关的基本属性及方法
private Dictionary bgmDictionary = new();
public Dictionary BgmDictionary
{
get => bgmDictionary;
private set
{
lock (gameObjLock)
{
bgmDictionary = value;
}
}
}
public void AddBgm(BgmType bgm, double value)
{
if (BgmDictionary.ContainsKey(bgm))
BgmDictionary[bgm] = value;
else BgmDictionary.Add(bgm, value);
}
private int alertnessRadius;
public int AlertnessRadius
{
get => alertnessRadius;
set
{
lock (gameObjLock)
{
alertnessRadius = value;
}
}
}
private double concealment;
public double Concealment
{
get => concealment;
set
{
lock (gameObjLock)
{
concealment = value;
}
}
}
private int viewRange;
public int ViewRange
{
get => viewRange;
set
{
lock (gameObjLock)
{
viewRange = (value > 0) ? value : 0;
}
}
}
#endregion
#region 交互相关的基本属性及方法
private int speedOfOpeningOrLocking;
public int SpeedOfOpeningOrLocking
{
get => speedOfOpeningOrLocking;
set
{
lock (gameObjLock)
{
speedOfOpeningOrLocking = value;
}
}
}
private int speedOfClimbingThroughWindows;
public int SpeedOfClimbingThroughWindows
{
get => speedOfClimbingThroughWindows;
set
{
lock (gameObjLock)
{
speedOfClimbingThroughWindows = value;
}
}
}
private int speedOfOpenChest;
public int SpeedOfOpenChest
{
get => speedOfOpenChest;
set
{
lock (gameObjLock)
{
speedOfOpenChest = value;
}
}
}
#endregion
#region 血量相关的基本属性及方法
private int maxHp;
public int MaxHp
{
get
{
HPReadWriterLock.EnterReadLock();
try
{
return maxHp;
}
finally
{
HPReadWriterLock.ExitReadLock();
}
}
protected set
{
HPReadWriterLock.EnterWriteLock();
try
{
maxHp = value;
if (hp > maxHp) hp = maxHp;
}
finally
{
HPReadWriterLock.ExitWriteLock();
}
}
} // 最大血量
protected int hp;
public int HP
{
get
{
HPReadWriterLock.EnterReadLock();
try
{
return hp;
}
finally
{
HPReadWriterLock.ExitReadLock();
}
}
set
{
HPReadWriterLock.EnterWriteLock();
try
{
if (value > 0)
{
hp = value <= maxHp ? value : maxHp;
}
else
hp = 0;
}
finally
{
HPReadWriterLock.ExitWriteLock();
}
}
}
///
/// 尝试减血
///
/// 减血量
public int TrySubHp(int sub)
{
HPReadWriterLock.EnterWriteLock();
try
{
int previousHp = hp;
if (hp <= sub)
{
hp = 0;
return hp;
}
else
{
hp -= sub;
return sub;
}
}
finally
{
HPReadWriterLock.ExitWriteLock();
}
}
private double vampire = 0; // 回血率:0-1之间
public double Vampire
{
get
{
lock (vampireLock)
return vampire;
}
set
{
lock (vampireLock)
{
if (value > 1)
vampire = 1;
else if (value < 0)
vampire = 0;
else
vampire = value;
}
}
}
public double OriVampire { get; protected set; }
#endregion
#region 状态相关的基本属性与方法
private PlayerStateType playerState = PlayerStateType.Null;
public PlayerStateType PlayerState
{
get
{
lock (actionLock)
{
if (playerState == PlayerStateType.Moving)
if (IsMoving == 1) return PlayerStateType.Moving;
else return PlayerStateType.Null;
return playerState;
}
}
}
public bool NoHp()
{
lock (actionLock)
return (playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped || playerState == PlayerStateType.Addicted || playerState == PlayerStateType.Rescued);
}
public bool Commandable()
{
lock (actionLock)
{
return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Swinging && playerState != PlayerStateType.TryingToAttack
&& playerState != PlayerStateType.ClimbingThroughWindows
&& playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed);
}
}
public bool CanPinDown()
{
lock (actionLock)
{
return (playerState != PlayerStateType.Deceased && playerState != PlayerStateType.Escaped
&& playerState != PlayerStateType.Addicted && playerState != PlayerStateType.Rescued
&& playerState != PlayerStateType.Stunned && playerState != PlayerStateType.Charmed);
}
}
public bool InteractingWithMapWithoutMoving()
{
lock (actionLock)
{
return (playerState == PlayerStateType.LockingTheDoor || playerState == PlayerStateType.OpeningTheDoor
|| playerState == PlayerStateType.Fixing || playerState == PlayerStateType.OpeningTheChest);
}
}
public bool NullOrMoving()
{
lock (actionLock)
{
return (playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
}
}
public bool CanBeAwed()
{
lock (actionLock)
return !(playerState == PlayerStateType.Deceased || playerState == PlayerStateType.Escaped
|| playerState == PlayerStateType.Addicted
|| playerState == PlayerStateType.Rescued || playerState == PlayerStateType.Treated
|| playerState == PlayerStateType.Stunned || playerState == PlayerStateType.Charmed
|| playerState == PlayerStateType.Null || playerState == PlayerStateType.Moving);
}
private GameObj? whatInteractingWith = null;
public GameObj? WhatInteractingWith => whatInteractingWith;
private long ChangePlayerState(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{
//只能被SetPlayerState引用
whatInteractingWith = gameObj;
playerState = value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
return ++stateNum;
}
private long ChangePlayerStateInOneThread(PlayerStateType value = PlayerStateType.Null, GameObj? gameObj = null)
{
//只能被SetPlayerState引用
whatInteractingWith = gameObj;
playerState = value;
//Debugger.Output(this,playerState.ToString()+" "+IsMoving.ToString());
return stateNum;
}
public long SetPlayerState(PlayerStateType value = PlayerStateType.Null, IGameObj? obj = null)
{
GameObj? gameObj = (GameObj?)obj;
lock (actionLock)
{
PlayerStateType nowPlayerState = PlayerState;
if (nowPlayerState == value && value != PlayerStateType.UsingSkill) return -1;
switch (nowPlayerState)
{
case PlayerStateType.Escaped:
case PlayerStateType.Deceased:
return -1;
case PlayerStateType.Addicted:
if (value == PlayerStateType.Rescued)
return ChangePlayerStateInOneThread(value, gameObj);
else if (value == PlayerStateType.Null)
return ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.Rescued:
if (value == PlayerStateType.Addicted)
return ChangePlayerStateInOneThread(value, gameObj);
else if (value == PlayerStateType.Null)
return ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.TryingToAttack:
if (value == PlayerStateType.Addicted || value == PlayerStateType.Swinging
|| value == PlayerStateType.Deceased || value == PlayerStateType.Stunned
|| value == PlayerStateType.Charmed || value == PlayerStateType.Null)
return ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.Stunned:
case PlayerStateType.Charmed:
if (value == PlayerStateType.Addicted || value == PlayerStateType.Deceased
|| value == PlayerStateType.Null)
return ChangePlayerState(value, gameObj);
else return -1;
case PlayerStateType.Swinging:
if (value == PlayerStateType.Addicted
|| value == PlayerStateType.Deceased || value == PlayerStateType.Stunned
|| value == PlayerStateType.Charmed || value == PlayerStateType.Null)
{
try
{
return ChangePlayerState(value, gameObj);
}
finally
{
ThreadNum.Release();
}
}
else return -1;
case PlayerStateType.ClimbingThroughWindows:
if (value == PlayerStateType.Addicted
|| value == PlayerStateType.Deceased || value == PlayerStateType.Stunned
|| value == PlayerStateType.Charmed || value == PlayerStateType.Null)
{
Window window = (Window)WhatInteractingWith!;
try
{
window.FinishClimbing();
return ChangePlayerState(value, gameObj);
}
finally
{
if (window.Stage.x == 0)
ThreadNum.Release();
else ReSetPos(window.Stage);
}
}
else return -1;
case PlayerStateType.OpeningTheChest:
((Chest)whatInteractingWith!).StopOpen();
return ChangePlayerState(value, gameObj);
case PlayerStateType.OpeningTheDoorway:
Doorway doorway = (Doorway)whatInteractingWith!;
doorway.StopOpenning();
return ChangePlayerState(value, gameObj);
case PlayerStateType.OpeningTheDoor:
Door door = (Door)whatInteractingWith!;
try
{
door.StopOpen();
ReleaseTool(door.DoorNum switch
{
3 => PropType.Key3,
5 => PropType.Key5,
_ => PropType.Key6,
}
);
return ChangePlayerState(value, gameObj);
}
finally
{
ThreadNum.Release();
}
case PlayerStateType.UsingSkill:
if (CharacterType == CharacterType.TechOtaku)
{
if (typeof(CraftingBench).IsInstanceOfType(whatInteractingWith))
{
try
{
((CraftingBench)whatInteractingWith!).StopSkill();
return ChangePlayerState(value, gameObj);
}
finally
{
ThreadNum.Release();
}
}
else
{
if (value != PlayerStateType.UsingSkill)
((UseRobot)FindActiveSkill(ActiveSkillType.UseRobot)).NowPlayerID = (int)playerID;
}
}
return ChangePlayerState(value, gameObj);
default:
return ChangePlayerState(value, gameObj);
}
}
}
public long SetPlayerStateNaturally()
{
lock (actionLock)
{
whatInteractingWith = null;
playerState = PlayerStateType.Null;
return ++stateNum;
}
}
public void RemoveFromGame(PlayerStateType playerStateType)
{
lock (actionLock)
{
MoveReaderWriterLock.EnterWriteLock();
try
{
canMove = false;
isRemoved = true;
}
finally
{
MoveReaderWriterLock.ExitWriteLock();
}
playerState = playerStateType;
position = GameData.PosWhoDie;
}
}
#endregion
private long score = 0;
public long Score
{
get => Interlocked.Read(ref score);
}
///
/// 加分
///
/// 增加量
public virtual void AddScore(long add)
{
Interlocked.Add(ref score, add);
//Debugger.Output(this, " 's score has been added to: " + score.ToString());
}
///
/// 角色所属队伍ID
///
private long teamID = long.MaxValue;
public long TeamID
{
get => Interlocked.Read(ref teamID);
set => Interlocked.Exchange(ref teamID, value);
}
private long playerID = long.MaxValue;
public long PlayerID
{
get => Interlocked.Read(ref playerID);
set => Interlocked.Exchange(ref playerID, value);
}
#region 道具和buff相关属性、方法
private Gadget[] propInventory = new Gadget[GameData.maxNumOfPropInPropInventory]
{new NullProp(), new NullProp(),new NullProp() };
public Gadget[] PropInventory
{
get
{
lock (inventoryLock)
return propInventory;
}
set
{
lock (inventoryLock)
propInventory = value;
}
}
///
/// 使用物品栏中的道具
///
/// 被使用的道具
public Gadget UseProp(int indexing)
{
if (indexing < 0 || indexing >= GameData.maxNumOfPropInPropInventory)
return new NullProp();
lock (inventoryLock)
{
Gadget prop = propInventory[indexing];
if (!prop.IsUsable()) return new NullProp();
PropInventory[indexing] = new NullProp();
return prop;
}
}
public Gadget UseProp(PropType propType)
{
if (propType == PropType.Null)
{
lock (inventoryLock)
{
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{
if (PropInventory[indexing].IsUsable())
{
Gadget prop = PropInventory[indexing];
PropInventory[indexing] = new NullProp();
return prop;
}
}
}
}
else
{
lock (inventoryLock)
{
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{
if (PropInventory[indexing].GetPropType() == propType && PropInventory[indexing].IsUsable())
{
Gadget prop = PropInventory[indexing];
PropInventory[indexing] = new NullProp();
return prop;
}
}
}
}
return new NullProp();
}
public bool UseTool(PropType propType)
{
lock (inventoryLock)
{
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{
if (PropInventory[indexing].GetPropType() == propType && PropInventory[indexing].IsUsable())
{
return ((Tool)PropInventory[indexing]).IsUsed = true;
}
}
}
return false;
}
public void ReleaseTool(PropType propType)
{
lock (inventoryLock)
{
for (int indexing = 0; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
{
if (PropInventory[indexing].GetPropType() == propType && ((Tool)PropInventory[indexing]).IsUsed)
{
((Tool)PropInventory[indexing]).IsUsed = false;
break;
}
}
}
}
///
/// 如果indexing==GameData.maxNumOfPropInPropInventory表明道具栏为满
///
public int IndexingOfAddProp()
{
int indexing = 0;
lock (inventoryLock)
for (; indexing < GameData.maxNumOfPropInPropInventory; ++indexing)
if (propInventory[indexing].GetPropType() == PropType.Null)
break;
return indexing;
}
public void AddMoveSpeed(int buffTime, double add = 1.0) => buffManager.AddMoveSpeed(add, buffTime, newVal =>
{ MoveSpeed = newVal < GameData.characterMaxSpeed ? newVal : GameData.characterMaxSpeed; },
OrgMoveSpeed);
public bool HasFasterSpeed => buffManager.HasFasterSpeed;
public void AddShield(int shieldTime) => buffManager.AddShield(shieldTime);
public bool HasShield => buffManager.HasShield;
public void AddLife(int LIFETime) => buffManager.AddLife(LIFETime);
public bool HasLIFE => buffManager.HasLIFE;
public void AddAp(int time) => buffManager.AddAp(time);
public bool HasAp => buffManager.HasAp;
public void AddSpear(int spearTime) => buffManager.AddSpear(spearTime);
public bool HasSpear => buffManager.HasSpear;
public void AddClairaudience(int time) => buffManager.AddClairaudience(time);
public bool HasClairaudience => buffManager.HasClairaudience;
public void AddInvisible(int time) => buffManager.AddInvisible(time);
public bool HasInvisible => buffManager.HasInvisible;
private Array buffTypeArray = Enum.GetValues(typeof(BuffType));
public Dictionary Buff
{
get
{
Dictionary buff = new Dictionary();
foreach (BuffType type in buffTypeArray)
{
if (type != BuffType.Null)
buff.Add(type, GetBuffStatus(type));
}
return buff;
}
}
private bool GetBuffStatus(BuffType type)
{
switch (type)
{
case BuffType.Spear:
return this.HasSpear;
case BuffType.AddSpeed:
return this.HasFasterSpeed;
case BuffType.Shield:
return this.HasShield;
case BuffType.AddLife:
return this.HasLIFE;
case BuffType.AddAp:
return this.HasAp;
case BuffType.Clairaudience:
return this.HasClairaudience;
case BuffType.Invisible:
return this.HasInvisible;
default:
return false;
}
}
public void TryActivatingLIFE()
{
if (buffManager.TryActivatingLIFE())
{
AddScore(GameData.ScorePropRemainHp);
hp = GameData.RemainHpWhenAddLife;
}
}
public bool TryAddAp()
{
if (buffManager.TryAddAp())
{
AddScore(GameData.ScorePropAddAp);
return true;
}
return false;
}
public bool TryUseSpear()
{
return buffManager.TryUseSpear();
}
public bool TryDeleteInvisible()
{
return buffManager.TryDeleteInvisible();
}
public bool TryUseShield()
{
if (buffManager.TryUseShield())
{
AddScore(GameData.ScorePropUseShield);
return true;
}
return false;
}
#endregion
/* public override void Reset() // 要加锁吗?
{
lock (gameObjLock)
{
// _ = AddDeathCount();
base.Reset();
this.MoveSpeed = OrgMoveSpeed;
HP = MaxHp;
PropInventory = null;
BulletOfPlayer = OriBulletOfPlayer;
lock (gameObjLock)
bulletNum = maxBulletNum;
buffManager.ClearAll();
IsInvisible = false;
this.Vampire = this.OriVampire;
}
}*/
public override bool IsRigid => true;
public override ShapeType Shape => ShapeType.Circle;
public override bool IgnoreCollideExecutor(IGameObj targetObj)
{
if (IsRemoved)
return true;
if (targetObj.Type == GameObjType.Gadget)
{
return true;
}
if (targetObj.Type == GameObjType.Character && XY.DistanceCeil3(targetObj.Position, this.Position) < this.Radius + targetObj.Radius - GameData.adjustLength)
return true;
return false;
}
}
}