Unstable 0.17.1.0
This commit is contained in:
@@ -42,7 +42,7 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
IsAlive = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
@@ -112,395 +112,395 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientWriteInput(IWriteMessage msg)
|
||||
{
|
||||
if (extraData != null)
|
||||
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
|
||||
|
||||
if (memInput.Count > 60)
|
||||
{
|
||||
switch ((NetEntityEvent.Type)extraData[0])
|
||||
memInput.RemoveRange(60, memInput.Count - 60);
|
||||
}
|
||||
|
||||
msg.Write(LastNetworkUpdateID);
|
||||
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
|
||||
msg.Write(inputCount);
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
|
||||
msg.Write(memInput[i].intAim);
|
||||
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Grab))
|
||||
{
|
||||
case NetEntityEvent.Type.InventoryState:
|
||||
msg.WriteRangedInteger(0, 0, 4);
|
||||
Inventory.ClientWrite(msg, extraData);
|
||||
break;
|
||||
case NetEntityEvent.Type.Treatment:
|
||||
msg.WriteRangedInteger(1, 0, 4);
|
||||
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
|
||||
break;
|
||||
case NetEntityEvent.Type.Status:
|
||||
msg.WriteRangedInteger(2, 0, 4);
|
||||
break;
|
||||
case NetEntityEvent.Type.UpdateTalents:
|
||||
msg.WriteRangedInteger(3, 0, 4);
|
||||
msg.Write((ushort)characterTalents.Count);
|
||||
foreach (var unlockedTalent in characterTalents)
|
||||
{
|
||||
msg.Write(unlockedTalent.Prefab.UintIdentifier);
|
||||
}
|
||||
break;
|
||||
msg.Write(memInput[i].interact);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!(extraData is IEventData eventData)) { throw new Exception($"Malformed character event: expected {nameof(Character)}.{nameof(IEventData)}"); }
|
||||
|
||||
msg.WriteRangedInteger((int)eventData.EventType, (int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
switch (eventData)
|
||||
{
|
||||
case InventoryStateEventData inventoryStateEventData:
|
||||
Inventory.ClientEventWrite(msg, inventoryStateEventData);
|
||||
break;
|
||||
case TreatmentEventData _:
|
||||
msg.Write(AnimController.Anim == AnimController.Animation.CPR);
|
||||
break;
|
||||
case StatusEventData _:
|
||||
//do nothing
|
||||
break;
|
||||
case UpdateTalentsEventData _:
|
||||
msg.Write((ushort)characterTalents.Count);
|
||||
foreach (var unlockedTalent in characterTalents)
|
||||
{
|
||||
msg.Write(unlockedTalent.Prefab.UintIdentifier);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Malformed character event: did not expect {eventData.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientReadPosition(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool facingRight = AnimController.Dir > 0.0f;
|
||||
|
||||
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
|
||||
|
||||
AnimController.Frozen = false;
|
||||
Enabled = true;
|
||||
|
||||
UInt16 networkUpdateID = 0;
|
||||
if (msg.ReadBoolean())
|
||||
{
|
||||
networkUpdateID = msg.ReadUInt16();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write((byte)ClientNetObject.CHARACTER_INPUT);
|
||||
bool aimInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Aim].Held = aimInput;
|
||||
keys[(int)InputType.Aim].SetState(false, aimInput);
|
||||
|
||||
if (memInput.Count > 60)
|
||||
bool shootInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Shoot].Held = shootInput;
|
||||
keys[(int)InputType.Shoot].SetState(false, shootInput);
|
||||
|
||||
bool useInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Use].Held = useInput;
|
||||
keys[(int)InputType.Use].SetState(false, useInput);
|
||||
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
memInput.RemoveRange(60, memInput.Count - 60);
|
||||
bool crouching = msg.ReadBoolean();
|
||||
keys[(int)InputType.Crouch].Held = crouching;
|
||||
keys[(int)InputType.Crouch].SetState(false, crouching);
|
||||
}
|
||||
|
||||
msg.Write(LastNetworkUpdateID);
|
||||
byte inputCount = Math.Min((byte)memInput.Count, (byte)60);
|
||||
msg.Write(inputCount);
|
||||
for (int i = 0; i < inputCount; i++)
|
||||
{
|
||||
msg.WriteRangedInteger((int)memInput[i].states, 0, (int)InputNetFlags.MaxVal);
|
||||
msg.Write(memInput[i].intAim);
|
||||
if (memInput[i].states.HasFlag(InputNetFlags.Select) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Deselect) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Use) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Health) ||
|
||||
memInput[i].states.HasFlag(InputNetFlags.Grab))
|
||||
{
|
||||
msg.Write(memInput[i].interact);
|
||||
}
|
||||
}
|
||||
bool attackInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Attack].Held = attackInput;
|
||||
keys[(int)InputType.Attack].SetState(false, attackInput);
|
||||
|
||||
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
||||
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
||||
TransformCursorPos();
|
||||
|
||||
bool ragdollInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
||||
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
|
||||
|
||||
facingRight = msg.ReadBoolean();
|
||||
}
|
||||
msg.WritePadBits();
|
||||
}
|
||||
|
||||
public virtual void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
{
|
||||
switch (type)
|
||||
bool entitySelected = msg.ReadBoolean();
|
||||
Character selectedCharacter = null;
|
||||
Item selectedItem = null;
|
||||
|
||||
AnimController.Animation animation = AnimController.Animation.None;
|
||||
if (entitySelected)
|
||||
{
|
||||
case ServerNetObject.ENTITY_POSITION:
|
||||
bool facingRight = AnimController.Dir > 0.0f;
|
||||
|
||||
lastRecvPositionUpdateTime = (float)Lidgren.Network.NetTime.Now;
|
||||
|
||||
AnimController.Frozen = false;
|
||||
Enabled = true;
|
||||
|
||||
UInt16 networkUpdateID = 0;
|
||||
if (msg.ReadBoolean())
|
||||
ushort characterID = msg.ReadUInt16();
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
selectedCharacter = FindEntityByID(characterID) as Character;
|
||||
selectedItem = FindEntityByID(itemID) as Item;
|
||||
if (characterID != NullEntityID)
|
||||
{
|
||||
bool doingCpr = msg.ReadBoolean();
|
||||
if (doingCpr && SelectedCharacter != null)
|
||||
{
|
||||
networkUpdateID = msg.ReadUInt16();
|
||||
animation = AnimController.Animation.CPR;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool aimInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Aim].Held = aimInput;
|
||||
keys[(int)InputType.Aim].SetState(false, aimInput);
|
||||
|
||||
bool shootInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Shoot].Held = shootInput;
|
||||
keys[(int)InputType.Shoot].SetState(false, shootInput);
|
||||
|
||||
bool useInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Use].Held = useInput;
|
||||
keys[(int)InputType.Use].SetState(false, useInput);
|
||||
|
||||
if (AnimController is HumanoidAnimController)
|
||||
{
|
||||
bool crouching = msg.ReadBoolean();
|
||||
keys[(int)InputType.Crouch].Held = crouching;
|
||||
keys[(int)InputType.Crouch].SetState(false, crouching);
|
||||
}
|
||||
|
||||
bool attackInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Attack].Held = attackInput;
|
||||
keys[(int)InputType.Attack].SetState(false, attackInput);
|
||||
|
||||
double aimAngle = msg.ReadUInt16() / 65535.0 * 2.0 * Math.PI;
|
||||
cursorPosition = AimRefPosition + new Vector2((float)Math.Cos(aimAngle), (float)Math.Sin(aimAngle)) * 500.0f;
|
||||
TransformCursorPos();
|
||||
|
||||
bool ragdollInput = msg.ReadBoolean();
|
||||
keys[(int)InputType.Ragdoll].Held = ragdollInput;
|
||||
keys[(int)InputType.Ragdoll].SetState(false, ragdollInput);
|
||||
|
||||
facingRight = msg.ReadBoolean();
|
||||
}
|
||||
|
||||
bool entitySelected = msg.ReadBoolean();
|
||||
Character selectedCharacter = null;
|
||||
Item selectedItem = null;
|
||||
|
||||
AnimController.Animation animation = AnimController.Animation.None;
|
||||
if (entitySelected)
|
||||
{
|
||||
ushort characterID = msg.ReadUInt16();
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
selectedCharacter = FindEntityByID(characterID) as Character;
|
||||
selectedItem = FindEntityByID(itemID) as Item;
|
||||
if (characterID != NullEntityID)
|
||||
{
|
||||
bool doingCpr = msg.ReadBoolean();
|
||||
if (doingCpr && SelectedCharacter != null)
|
||||
{
|
||||
animation = AnimController.Animation.CPR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 pos = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
|
||||
Vector2 linearVelocity = new Vector2(
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
|
||||
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
|
||||
|
||||
bool fixedRotation = msg.ReadBoolean();
|
||||
float? rotation = null;
|
||||
float? angularVelocity = null;
|
||||
if (!fixedRotation)
|
||||
{
|
||||
rotation = msg.ReadSingle();
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
|
||||
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
|
||||
}
|
||||
|
||||
bool readStatus = msg.ReadBoolean();
|
||||
if (readStatus)
|
||||
{
|
||||
ReadStatus(msg);
|
||||
AIController?.ClientRead(msg);
|
||||
}
|
||||
|
||||
msg.ReadPadBits();
|
||||
|
||||
int index = 0;
|
||||
if (GameMain.Client.Character == this && CanMove)
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
networkUpdateID,
|
||||
facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
linearVelocity, angularVelocity,
|
||||
sendingTime, facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
int eventType = msg.ReadRangedInteger(0, 13);
|
||||
switch (eventType)
|
||||
{
|
||||
case 0: //NetEntityEvent.Type.InventoryState
|
||||
if (Inventory == null)
|
||||
{
|
||||
string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
|
||||
DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", SpeciesName.Value));
|
||||
|
||||
//read anyway to prevent messing up reading the rest of the message
|
||||
_ = msg.ReadUInt16();
|
||||
byte inventoryItemCount = msg.ReadByte();
|
||||
for (int i = 0; i < inventoryItemCount; i++)
|
||||
{
|
||||
msg.ReadUInt16();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory.ClientRead(type, msg, sendingTime);
|
||||
}
|
||||
break;
|
||||
case 1: //NetEntityEvent.Type.Control
|
||||
bool myCharacter = msg.ReadBoolean();
|
||||
byte ownerID = msg.ReadByte();
|
||||
ResetNetState();
|
||||
if (myCharacter)
|
||||
{
|
||||
if (controlled != null)
|
||||
{
|
||||
LastNetworkUpdateID = controlled.LastNetworkUpdateID;
|
||||
}
|
||||
|
||||
if (!IsDead) { Controlled = this; }
|
||||
IsRemotePlayer = false;
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = this;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
GameMain.Client.WaitForNextRoundRespawn = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (controlled == this)
|
||||
{
|
||||
Controlled = null;
|
||||
IsRemotePlayer = ownerID > 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2: //NetEntityEvent.Type.Status
|
||||
ReadStatus(msg);
|
||||
break;
|
||||
case 3: //NetEntityEvent.Type.UpdateSkills
|
||||
int skillCount = msg.ReadByte();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
{
|
||||
Identifier skillIdentifier = msg.ReadIdentifier();
|
||||
float skillLevel = msg.ReadSingle();
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel);
|
||||
}
|
||||
break;
|
||||
case 4: // NetEntityEvent.Type.SetAttackTarget
|
||||
case 5: //NetEntityEvent.Type.ExecuteAttack
|
||||
int attackLimbIndex = msg.ReadByte();
|
||||
UInt16 targetEntityID = msg.ReadUInt16();
|
||||
int targetLimbIndex = msg.ReadByte();
|
||||
Vector2 targetSimPos = new Vector2(msg.ReadSingle(), msg.ReadSingle());
|
||||
//255 = entity already removed, no need to do anything
|
||||
if (attackLimbIndex == 255 || Removed) { break; }
|
||||
if (attackLimbIndex >= AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
|
||||
Limb targetLimb = null;
|
||||
IDamageable targetEntity = FindEntityByID(targetEntityID) as IDamageable;
|
||||
if (targetEntity == null && eventType == 4)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid SetAttackTarget message. Target entity not found (ID {targetEntityID})");
|
||||
break;
|
||||
}
|
||||
if (targetEntity is Character targetCharacter)
|
||||
{
|
||||
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == 4 ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
|
||||
}
|
||||
if (attackLimb?.attack != null && Controlled != this)
|
||||
{
|
||||
if (eventType == 4)
|
||||
{
|
||||
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
|
||||
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 6: //NetEntityEvent.Type.AssignCampaignInteraction
|
||||
byte campaignInteractionType = msg.ReadByte();
|
||||
bool requireConsciousness = msg.ReadBoolean();
|
||||
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
|
||||
RequireConsciousnessForCustomInteract = requireConsciousness;
|
||||
break;
|
||||
case 7: //NetEntityEvent.Type.ObjectiveManagerState
|
||||
// 1 = order, 2 = objective
|
||||
int msgType = msg.ReadRangedInteger(0, 2);
|
||||
if (msgType == 0) { break; }
|
||||
bool validData = msg.ReadBoolean();
|
||||
if (!validData) { break; }
|
||||
if (msgType == 1)
|
||||
{
|
||||
UInt32 orderPrefabUintIdentifier = msg.ReadUInt32();
|
||||
var orderPrefab = OrderPrefab.Prefabs.Find(p => p.UintIdentifier == orderPrefabUintIdentifier);
|
||||
Identifier option = Identifier.Empty;
|
||||
if (orderPrefab.HasOptions)
|
||||
{
|
||||
int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
|
||||
if (optionIndex > -1)
|
||||
{
|
||||
option = orderPrefab.AllOptions[optionIndex];
|
||||
}
|
||||
}
|
||||
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
|
||||
}
|
||||
else if (msgType == 2)
|
||||
{
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
Identifier option = msg.ReadIdentifier();
|
||||
ushort objectiveTargetEntityId = msg.ReadUInt16();
|
||||
var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId);
|
||||
GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
|
||||
}
|
||||
break;
|
||||
case 8: //NetEntityEvent.Type.TeamChange
|
||||
byte newTeamId = msg.ReadByte();
|
||||
ChangeTeam((CharacterTeamType)newTeamId);
|
||||
break;
|
||||
case 9: //NetEntityEvent.Type.AddToCrew
|
||||
GameMain.GameSession.CrewManager.AddCharacter(this);
|
||||
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
|
||||
item.AllowStealing = true;
|
||||
var wifiComponent = item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null)
|
||||
{
|
||||
wifiComponent.TeamID = teamID;
|
||||
}
|
||||
var idCard = item.GetComponent<IdCard>();
|
||||
if (idCard != null)
|
||||
{
|
||||
idCard.TeamID = teamID;
|
||||
idCard.SubmarineSpecificID = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 10: //NetEntityEvent.Type.UpdateExperience
|
||||
int experienceAmount = msg.ReadInt32();
|
||||
info?.SetExperience(experienceAmount);
|
||||
break;
|
||||
case 11: //NetEntityEvent.Type.UpdateTalents:
|
||||
ushort talentCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < talentCount; i++)
|
||||
{
|
||||
bool addedThisRound = msg.ReadBoolean();
|
||||
UInt32 talentIdentifier = msg.ReadUInt32();
|
||||
GiveTalent(talentIdentifier, addedThisRound);
|
||||
}
|
||||
break;
|
||||
case 12: //NetEntityEvent.Type.UpdateMoney:
|
||||
int moneyAmount = msg.ReadInt32();
|
||||
SetMoney(moneyAmount);
|
||||
break;
|
||||
case 13: //NetEntityEvent.Type.UpdatePermanentStats:
|
||||
byte savedStatValueCount = msg.ReadByte();
|
||||
StatTypes statType = (StatTypes)msg.ReadByte();
|
||||
info?.ClearSavedStatValues(statType);
|
||||
for (int i = 0; i < savedStatValueCount; i++)
|
||||
{
|
||||
string statIdentifier = msg.ReadString();
|
||||
float statValue = msg.ReadSingle();
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 pos = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
|
||||
Vector2 linearVelocity = new Vector2(
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12),
|
||||
msg.ReadRangedSingle(-MaxVel, MaxVel, 12));
|
||||
linearVelocity = NetConfig.Quantize(linearVelocity, -MaxVel, MaxVel, 12);
|
||||
|
||||
bool fixedRotation = msg.ReadBoolean();
|
||||
float? rotation = null;
|
||||
float? angularVelocity = null;
|
||||
if (!fixedRotation)
|
||||
{
|
||||
rotation = msg.ReadSingle();
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
angularVelocity = msg.ReadRangedSingle(-MaxAngularVel, MaxAngularVel, 8);
|
||||
angularVelocity = NetConfig.Quantize(angularVelocity.Value, -MaxAngularVel, MaxAngularVel, 8);
|
||||
}
|
||||
|
||||
bool readStatus = msg.ReadBoolean();
|
||||
if (readStatus)
|
||||
{
|
||||
ReadStatus(msg);
|
||||
AIController?.ClientRead(msg);
|
||||
}
|
||||
|
||||
msg.ReadPadBits();
|
||||
|
||||
int index = 0;
|
||||
if (GameMain.Client.Character == this && CanMove)
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
networkUpdateID,
|
||||
facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && NetIdUtils.IdMoreRecent(posInfo.ID, memState[index].ID))
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
var posInfo = new CharacterStateInfo(
|
||||
pos, rotation,
|
||||
linearVelocity, angularVelocity,
|
||||
sendingTime, facingRight ? Direction.Right : Direction.Left,
|
||||
selectedCharacter, selectedItem, animation);
|
||||
|
||||
while (index < memState.Count && posInfo.Timestamp > memState[index].Timestamp)
|
||||
index++;
|
||||
memState.Insert(index, posInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
EventType eventType = (EventType)msg.ReadRangedInteger((int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
switch (eventType)
|
||||
{
|
||||
case EventType.InventoryState:
|
||||
if (Inventory == null)
|
||||
{
|
||||
string errorMsg = "Received an inventory update message for an entity with no inventory ([name], removed: " + Removed + ")";
|
||||
DebugConsole.ThrowError(errorMsg.Replace("[name]", Name));
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterNetworking.ClientRead:NoInventory" + ID, GameAnalyticsManager.ErrorSeverity.Error, errorMsg.Replace("[name]", SpeciesName.Value));
|
||||
|
||||
//read anyway to prevent messing up reading the rest of the message
|
||||
_ = msg.ReadUInt16();
|
||||
byte inventoryItemCount = msg.ReadByte();
|
||||
for (int i = 0; i < inventoryItemCount; i++)
|
||||
{
|
||||
msg.ReadUInt16();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Inventory.ClientEventRead(msg, sendingTime);
|
||||
}
|
||||
break;
|
||||
case EventType.Control:
|
||||
bool myCharacter = msg.ReadBoolean();
|
||||
byte ownerID = msg.ReadByte();
|
||||
ResetNetState();
|
||||
if (myCharacter)
|
||||
{
|
||||
if (controlled != null)
|
||||
{
|
||||
LastNetworkUpdateID = controlled.LastNetworkUpdateID;
|
||||
}
|
||||
|
||||
if (!IsDead) { Controlled = this; }
|
||||
IsRemotePlayer = false;
|
||||
GameMain.Client.HasSpawned = true;
|
||||
GameMain.Client.Character = this;
|
||||
GameMain.LightManager.LosEnabled = true;
|
||||
GameMain.LightManager.LosAlpha = 1f;
|
||||
GameMain.Client.WaitForNextRoundRespawn = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (controlled == this)
|
||||
{
|
||||
Controlled = null;
|
||||
IsRemotePlayer = ownerID > 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.Status:
|
||||
ReadStatus(msg);
|
||||
break;
|
||||
case EventType.UpdateSkills:
|
||||
int skillCount = msg.ReadByte();
|
||||
for (int i = 0; i < skillCount; i++)
|
||||
{
|
||||
Identifier skillIdentifier = msg.ReadIdentifier();
|
||||
float skillLevel = msg.ReadSingle();
|
||||
info?.SetSkillLevel(skillIdentifier, skillLevel);
|
||||
}
|
||||
break;
|
||||
case EventType.SetAttackTarget:
|
||||
case EventType.ExecuteAttack:
|
||||
int attackLimbIndex = msg.ReadByte();
|
||||
UInt16 targetEntityID = msg.ReadUInt16();
|
||||
int targetLimbIndex = msg.ReadByte();
|
||||
float targetX = msg.ReadSingle();
|
||||
float targetY = msg.ReadSingle();
|
||||
Vector2 targetSimPos = new Vector2(targetX, targetY);
|
||||
//255 = entity already removed, no need to do anything
|
||||
if (attackLimbIndex == 255 || Removed) { break; }
|
||||
if (attackLimbIndex >= AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Limb index out of bounds (character: {Name}, limb index: {attackLimbIndex}, limb count: {AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
Limb attackLimb = AnimController.Limbs[attackLimbIndex];
|
||||
Limb targetLimb = null;
|
||||
IDamageable targetEntity = FindEntityByID(targetEntityID) as IDamageable;
|
||||
if (targetEntity == null && eventType == EventType.SetAttackTarget)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid SetAttackTarget message. Target entity not found (ID {targetEntityID})");
|
||||
break;
|
||||
}
|
||||
if (targetEntity is Character targetCharacter)
|
||||
{
|
||||
if (targetLimbIndex >= targetCharacter.AnimController.Limbs.Length)
|
||||
{
|
||||
DebugConsole.ThrowError($"Received invalid {(eventType == EventType.SetAttackTarget ? "SetAttackTarget" : "ExecuteAttack")} message. Target limb index out of bounds (target character: {targetCharacter.Name}, limb index: {targetLimbIndex}, limb count: {targetCharacter.AnimController.Limbs.Length})");
|
||||
break;
|
||||
}
|
||||
targetLimb = targetCharacter.AnimController.Limbs[targetLimbIndex];
|
||||
}
|
||||
if (attackLimb?.attack != null && Controlled != this)
|
||||
{
|
||||
if (eventType == EventType.SetAttackTarget)
|
||||
{
|
||||
SetAttackTarget(attackLimb, targetEntity, targetSimPos);
|
||||
PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
attackLimb.ExecuteAttack(targetEntity, targetLimb, out _);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.AssignCampaignInteraction:
|
||||
byte campaignInteractionType = msg.ReadByte();
|
||||
bool requireConsciousness = msg.ReadBoolean();
|
||||
(GameMain.GameSession?.GameMode as CampaignMode)?.AssignNPCMenuInteraction(this, (CampaignMode.InteractionType)campaignInteractionType);
|
||||
RequireConsciousnessForCustomInteract = requireConsciousness;
|
||||
break;
|
||||
case EventType.ObjectiveManagerState:
|
||||
// 1 = order, 2 = objective
|
||||
AIObjectiveManager.ObjectiveType msgType
|
||||
= (AIObjectiveManager.ObjectiveType)msg.ReadRangedInteger(
|
||||
(int)AIObjectiveManager.ObjectiveType.MinValue,
|
||||
(int)AIObjectiveManager.ObjectiveType.MaxValue);
|
||||
if (msgType == 0) { break; }
|
||||
bool validData = msg.ReadBoolean();
|
||||
if (!validData) { break; }
|
||||
if (msgType == AIObjectiveManager.ObjectiveType.Order)
|
||||
{
|
||||
UInt32 orderPrefabUintIdentifier = msg.ReadUInt32();
|
||||
var orderPrefab = OrderPrefab.Prefabs.Find(p => p.UintIdentifier == orderPrefabUintIdentifier);
|
||||
Identifier option = Identifier.Empty;
|
||||
if (orderPrefab.HasOptions)
|
||||
{
|
||||
int optionIndex = msg.ReadRangedInteger(-1, orderPrefab.AllOptions.Length);
|
||||
if (optionIndex > -1)
|
||||
{
|
||||
option = orderPrefab.AllOptions[optionIndex];
|
||||
}
|
||||
}
|
||||
GameMain.GameSession?.CrewManager?.SetOrderHighlight(this, orderPrefab.Identifier, option);
|
||||
}
|
||||
else if (msgType == AIObjectiveManager.ObjectiveType.Objective)
|
||||
{
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
Identifier option = msg.ReadIdentifier();
|
||||
ushort objectiveTargetEntityId = msg.ReadUInt16();
|
||||
var objectiveTargetEntity = FindEntityByID(objectiveTargetEntityId);
|
||||
GameMain.GameSession?.CrewManager?.CreateObjectiveIcon(this, identifier, option, objectiveTargetEntity);
|
||||
}
|
||||
break;
|
||||
case EventType.TeamChange:
|
||||
byte newTeamId = msg.ReadByte();
|
||||
ChangeTeam((CharacterTeamType)newTeamId);
|
||||
break;
|
||||
case EventType.AddToCrew:
|
||||
GameMain.GameSession.CrewManager.AddCharacter(this);
|
||||
CharacterTeamType teamID = (CharacterTeamType)msg.ReadByte();
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < itemCount; i++)
|
||||
{
|
||||
ushort itemID = msg.ReadUInt16();
|
||||
if (!(Entity.FindEntityByID(itemID) is Item item)) { continue; }
|
||||
item.AllowStealing = true;
|
||||
var wifiComponent = item.GetComponent<WifiComponent>();
|
||||
if (wifiComponent != null)
|
||||
{
|
||||
wifiComponent.TeamID = teamID;
|
||||
}
|
||||
var idCard = item.GetComponent<IdCard>();
|
||||
if (idCard != null)
|
||||
{
|
||||
idCard.TeamID = teamID;
|
||||
idCard.SubmarineSpecificID = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.UpdateExperience:
|
||||
int experienceAmount = msg.ReadInt32();
|
||||
info?.SetExperience(experienceAmount);
|
||||
break;
|
||||
case EventType.UpdateTalents:
|
||||
ushort talentCount = msg.ReadUInt16();
|
||||
for (int i = 0; i < talentCount; i++)
|
||||
{
|
||||
bool addedThisRound = msg.ReadBoolean();
|
||||
UInt32 talentIdentifier = msg.ReadUInt32();
|
||||
GiveTalent(talentIdentifier, addedThisRound);
|
||||
}
|
||||
break;
|
||||
case EventType.UpdateMoney:
|
||||
int moneyAmount = msg.ReadInt32();
|
||||
SetMoney(moneyAmount);
|
||||
break;
|
||||
case EventType.UpdatePermanentStats:
|
||||
byte savedStatValueCount = msg.ReadByte();
|
||||
StatTypes statType = (StatTypes)msg.ReadByte();
|
||||
info?.ClearSavedStatValues(statType);
|
||||
for (int i = 0; i < savedStatValueCount; i++)
|
||||
{
|
||||
string statIdentifier = msg.ReadString();
|
||||
float statValue = msg.ReadSingle();
|
||||
bool removeOnDeath = msg.ReadBoolean();
|
||||
info?.ChangeSavedStatValue(statType, statValue, statIdentifier, removeOnDeath, setValue: true);
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
|
||||
public static Character ReadSpawnData(IReadMessage inc)
|
||||
@@ -542,6 +542,8 @@ namespace Barotrauma
|
||||
{
|
||||
bool hasOwner = inc.ReadBoolean();
|
||||
int ownerId = hasOwner ? inc.ReadByte() : -1;
|
||||
int balance = hasOwner ? inc.ReadInt32() : -1;
|
||||
int rewardDistribution = hasOwner ? inc.ReadRangedInteger(0, 100) : -1;
|
||||
byte teamID = inc.ReadByte();
|
||||
bool hasAi = inc.ReadBoolean();
|
||||
Identifier infoSpeciesName = inc.ReadIdentifier();
|
||||
@@ -558,6 +560,8 @@ namespace Barotrauma
|
||||
}
|
||||
character.TeamID = (CharacterTeamType)teamID;
|
||||
character.CampaignInteractionType = (CampaignMode.InteractionType)inc.ReadByte();
|
||||
character.Wallet.Balance = balance;
|
||||
character.Wallet.RewardDistribution = rewardDistribution;
|
||||
if (character.CampaignInteractionType != CampaignMode.InteractionType.None)
|
||||
{
|
||||
(GameMain.GameSession.GameMode as CampaignMode)?.AssignNPCMenuInteraction(character, character.CampaignInteractionType);
|
||||
@@ -597,7 +601,7 @@ namespace Barotrauma
|
||||
: Identifier.Empty)
|
||||
.WithManualPriority(orderPriority)
|
||||
.WithOrderGiver(orderGiver);
|
||||
character.SetOrder(order, speak: false, force: true);
|
||||
character.SetOrder(order, isNewOrder: true, speak: false, force: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AfflictionHusk : Affliction
|
||||
{
|
||||
private InfectionState? prevDisplayedMessage;
|
||||
partial void UpdateMessages()
|
||||
{
|
||||
if (character != Character.Controlled) { return; }
|
||||
if (Prefab is AfflictionPrefabHusk { SendMessages: false }) { return; }
|
||||
if (prevDisplayedMessage.HasValue && prevDisplayedMessage.Value == State) { return; }
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case InfectionState.Dormant:
|
||||
if (Strength < DormantThreshold * 0.5f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
GUI.AddMessage(TextManager.Get("HuskDormant"), GUIStyle.Red);
|
||||
break;
|
||||
case InfectionState.Transition:
|
||||
GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUIStyle.Red);
|
||||
break;
|
||||
case InfectionState.Active:
|
||||
if (character.Params.UseHuskAppendage)
|
||||
{
|
||||
GUI.AddMessage(TextManager.GetWithVariable("HuskActivate", "[Attack]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Attack)), GUIStyle.Red);
|
||||
}
|
||||
break;
|
||||
case InfectionState.Final:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
prevDisplayedMessage = State;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +300,7 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Treatment });
|
||||
GameMain.Client.CreateEntityEvent(Character.Controlled, new Character.TreatmentEventData());
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -400,7 +400,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(Character.Controlled, new object[] { NetEntityEvent.Type.Status });
|
||||
GameMain.Client.CreateEntityEvent(Character.Controlled, new Character.StatusEventData());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -573,10 +573,10 @@ namespace Barotrauma
|
||||
inWater ? Character.Params.BleedParticleWater : Character.Params.BleedParticleAir,
|
||||
limb.WorldPosition, velocity, 0.0f, Character.AnimController.CurrentHull);
|
||||
|
||||
if (blood != null && !inWater)
|
||||
if (blood != null)
|
||||
{
|
||||
blood.Size *= bloodParticleSize;
|
||||
if (!string.IsNullOrEmpty(Character.BloodDecalName) && Rand.Range(0.0f, 1.0f) < 0.05f)
|
||||
if (!inWater && !string.IsNullOrEmpty(Character.BloodDecalName) && Rand.Range(0.0f, 1.0f) < 0.05f)
|
||||
{
|
||||
blood.OnCollision += (Vector2 pos, Hull hull) =>
|
||||
{
|
||||
|
||||
@@ -523,7 +523,7 @@ namespace Barotrauma
|
||||
private string GetSpritePath(ContentPath texturePath)
|
||||
{
|
||||
if (!character.IsHumanoid) { return texturePath.Value; }
|
||||
return GetSpritePath(texturePath, character?.Info);
|
||||
return GetSpritePath(texturePath, character.Info);
|
||||
}
|
||||
|
||||
partial void LoadParamsProjSpecific()
|
||||
|
||||
@@ -559,7 +559,7 @@ namespace Barotrauma
|
||||
|
||||
GameMain.MainMenuScreen.QuickStart(fixedSeed: false, subName, difficulty, levelGenerationParams);
|
||||
|
||||
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().ToArray() }));
|
||||
}, getValidArgs: () => new[] { SubmarineInfo.SavedSubmarines.Select(s => s.Name).Distinct().OrderBy(s => s).ToArray() }));
|
||||
|
||||
commands.Add(new Command("steamnetdebug", "steamnetdebug: Toggles Steamworks networking debug logging.", (string[] args) =>
|
||||
{
|
||||
@@ -628,7 +628,7 @@ namespace Barotrauma
|
||||
DebugConsoleMapping.Instance.Remove(key);
|
||||
NewMessage("Keybind unbound.", GUIStyle.Green);
|
||||
return;
|
||||
}, isCheat: false, getValidArgs: () => new[] { DebugConsoleMapping.Instance.Bindings.Keys.Select(keys => keys.ToString()).Distinct().ToArray() }));
|
||||
}, isCheat: false, getValidArgs: () => new[] { DebugConsoleMapping.Instance.Bindings.Keys.Select(keys => keys.ToString()).Distinct().OrderBy(k => k).ToArray() }));
|
||||
|
||||
commands.Add(new Command("savebinds", "savebinds: Writes current keybinds into the config file.", (string[] args) =>
|
||||
{
|
||||
@@ -710,6 +710,7 @@ namespace Barotrauma
|
||||
commands.Add(new Command("traitorlist", "", (string[] args) => { }));
|
||||
AssignRelayToServer("traitorlist", true);
|
||||
AssignRelayToServer("money", true);
|
||||
AssignRelayToServer("showmoney", true);
|
||||
AssignRelayToServer("setskill", true);
|
||||
AssignRelayToServer("readycheck", true);
|
||||
|
||||
@@ -1734,7 +1735,7 @@ namespace Barotrauma
|
||||
|
||||
return new string[][]
|
||||
{
|
||||
propertyList.Distinct().Select(i => i.Value).ToArray(),
|
||||
propertyList.Distinct().Select(i => i.Value).OrderBy(n => n).ToArray(),
|
||||
Array.Empty<string>()
|
||||
};
|
||||
}));
|
||||
@@ -1763,17 +1764,26 @@ namespace Barotrauma
|
||||
foreach (var missionPrefab in MissionPrefab.Prefabs)
|
||||
{
|
||||
Identifier missionId = (missionPrefab.ConfigElement.Attribute("textidentifier") == null ? missionPrefab.Identifier : missionPrefab.ConfigElement.GetAttributeIdentifier("textidentifier", Identifier.Empty));
|
||||
Identifier nameIdentifier = $"missionname.{missionId}".ToIdentifier();
|
||||
if (!tags[language].Contains(nameIdentifier))
|
||||
addIfMissing($"missionname.{missionId}".ToIdentifier(), language);
|
||||
addIfMissing($"missiondescription.{missionId}".ToIdentifier(), language);
|
||||
}
|
||||
|
||||
foreach (Type itemComponentType in typeof(ItemComponent).Assembly.GetTypes().Where(type => type.IsSubclassOf(typeof(ItemComponent))))
|
||||
{
|
||||
foreach (var property in itemComponentType.GetProperties())
|
||||
{
|
||||
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[nameIdentifier].Add(language);
|
||||
}
|
||||
Identifier descriptionIdentifier = $"missiondescription.{missionId}".ToIdentifier();
|
||||
if (!tags[language].Contains(descriptionIdentifier))
|
||||
{
|
||||
if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[descriptionIdentifier].Add(language);
|
||||
if (!property.IsDefined(typeof(InGameEditable), false)) { continue; }
|
||||
|
||||
string propertyTag = $"{property.DeclaringType.Name}.{property.Name}";
|
||||
|
||||
addIfMissingAll(language,
|
||||
propertyTag.ToIdentifier(),
|
||||
property.Name.ToIdentifier(),
|
||||
$"sp.{propertyTag}.name".ToIdentifier());
|
||||
|
||||
addIfMissingAll(language,
|
||||
$"sp.{propertyTag}.description".ToIdentifier(),
|
||||
$"{property.Name.ToIdentifier()}.description".ToIdentifier());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1781,18 +1791,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (sub.Type != SubmarineType.Player || !sub.IsVanillaSubmarine()) { continue; }
|
||||
|
||||
Identifier nameIdentifier = $"submarine.name.{sub.Name}".ToIdentifier();
|
||||
if (!tags[language].Contains(nameIdentifier))
|
||||
{
|
||||
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[nameIdentifier].Add(language);
|
||||
}
|
||||
Identifier descriptionIdentifier = ("submarine.description." + sub.Name).ToIdentifier();
|
||||
if (!tags[language].Contains(descriptionIdentifier))
|
||||
{
|
||||
if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[descriptionIdentifier].Add(language);
|
||||
}
|
||||
addIfMissing($"submarine.name.{sub.Name}".ToIdentifier(), language);
|
||||
addIfMissing(("submarine.description." + sub.Name).ToIdentifier(), language);
|
||||
}
|
||||
|
||||
foreach (AfflictionPrefab affliction in AfflictionPrefab.List)
|
||||
@@ -1806,42 +1806,21 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
Identifier afflictionId = affliction.TranslationIdentifier;
|
||||
Identifier nameIdentifier = $"afflictionname.{afflictionId}".ToIdentifier();
|
||||
if (!tags[language].Contains(nameIdentifier))
|
||||
{
|
||||
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[nameIdentifier].Add(language);
|
||||
}
|
||||
|
||||
Identifier descriptionIdentifier = $"afflictiondescription.{afflictionId}".ToIdentifier();
|
||||
if (!tags[language].Contains(descriptionIdentifier))
|
||||
{
|
||||
if (!missingTags.ContainsKey(descriptionIdentifier)) { missingTags[descriptionIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[descriptionIdentifier].Add(language);
|
||||
}
|
||||
addIfMissing($"afflictionname.{afflictionId}".ToIdentifier(), language);
|
||||
addIfMissing($"afflictiondescription.{afflictionId}".ToIdentifier(), language);
|
||||
}
|
||||
|
||||
foreach (var talentTree in TalentTree.JobTalentTrees)
|
||||
{
|
||||
foreach (var talentSubTree in talentTree.TalentSubTrees)
|
||||
{
|
||||
Identifier nameIdentifier = $"talenttree.{talentSubTree.Identifier}".ToIdentifier();
|
||||
if (!tags[language].Contains(nameIdentifier))
|
||||
{
|
||||
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[nameIdentifier].Add(language);
|
||||
}
|
||||
addIfMissing($"talenttree.{talentSubTree.Identifier}".ToIdentifier(), language);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var talent in TalentPrefab.TalentPrefabs)
|
||||
{
|
||||
Identifier nameIdentifier = $"talentname.{talent.Identifier}".ToIdentifier();
|
||||
if (!tags[language].Contains(nameIdentifier))
|
||||
{
|
||||
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[nameIdentifier].Add(language);
|
||||
}
|
||||
addIfMissing($"talentname.{talent.Identifier}".ToIdentifier(), language);
|
||||
}
|
||||
|
||||
//check missing entity names
|
||||
@@ -1849,17 +1828,25 @@ namespace Barotrauma
|
||||
{
|
||||
Identifier nameIdentifier = ("entityname." + me.Identifier).ToIdentifier();
|
||||
if (tags[language].Contains(nameIdentifier)) { continue; }
|
||||
if (me.HideInMenus) { continue; }
|
||||
|
||||
ContentXElement configElement = null;
|
||||
|
||||
if (me is ItemPrefab itemPrefab)
|
||||
{
|
||||
nameIdentifier = itemPrefab.ConfigElement?.GetAttributeIdentifier("nameidentifier", nameIdentifier) ?? nameIdentifier;
|
||||
if (nameIdentifier != null)
|
||||
{
|
||||
if (tags[language].Contains("entityname." + nameIdentifier)) { continue; }
|
||||
}
|
||||
configElement = itemPrefab.ConfigElement;
|
||||
}
|
||||
else if (me is StructurePrefab structurePrefab)
|
||||
{
|
||||
configElement = structurePrefab.ConfigElement;
|
||||
}
|
||||
if (configElement != null)
|
||||
{
|
||||
var overrideIdentifier = configElement.GetAttributeIdentifier("nameidentifier", null);
|
||||
if (overrideIdentifier != null && tags[language].Contains("entityname." + overrideIdentifier)) { continue; }
|
||||
}
|
||||
|
||||
if (!missingTags.ContainsKey(nameIdentifier)) { missingTags[nameIdentifier] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[nameIdentifier].Add(language);
|
||||
addIfMissing(nameIdentifier, language);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1868,11 +1855,7 @@ namespace Barotrauma
|
||||
foreach (LanguageIdentifier language in TextManager.AvailableLanguages)
|
||||
{
|
||||
if (language == TextManager.DefaultLanguage) { continue; }
|
||||
if (!tags[language].Contains(englishTag))
|
||||
{
|
||||
if (!missingTags.ContainsKey(englishTag)) { missingTags[englishTag] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[englishTag].Add(language);
|
||||
}
|
||||
addIfMissing(englishTag, language);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1920,6 +1903,24 @@ namespace Barotrauma
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = false;
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
SwapLanguage(TextManager.DefaultLanguage);
|
||||
|
||||
void addIfMissing(Identifier tag, LanguageIdentifier language)
|
||||
{
|
||||
if (!tags[language].Contains(tag))
|
||||
{
|
||||
if (!missingTags.ContainsKey(tag)) { missingTags[tag] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[tag].Add(language);
|
||||
}
|
||||
}
|
||||
void addIfMissingAll(LanguageIdentifier language, params Identifier[] potentialTags)
|
||||
{
|
||||
if (!potentialTags.Any(t => tags[language].Contains(t)))
|
||||
{
|
||||
var tag = potentialTags.First();
|
||||
if (!missingTags.ContainsKey(tag)) { missingTags[tag] = new HashSet<LanguageIdentifier>(); }
|
||||
missingTags[tag].Add(language);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
commands.Add(new Command("comparelocafiles", "comparelocafiles [file1] [file2]", (string[] args) =>
|
||||
@@ -1944,7 +1945,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var content1 = getContent(doc1.Root);
|
||||
var language1 = doc1.Root.GetAttributeIdentifier("language", string.Empty);
|
||||
|
||||
var content2 = getContent(doc2.Root);
|
||||
var language2 = doc2.Root.GetAttributeIdentifier("language", string.Empty);
|
||||
|
||||
foreach (KeyValuePair<string, string> kvp in content1)
|
||||
{
|
||||
@@ -1952,12 +1956,9 @@ namespace Barotrauma
|
||||
{
|
||||
ThrowError($"File 2 doesn't contain the text tag \"{kvp.Key}\"");
|
||||
}
|
||||
else
|
||||
else if (language1 == language2 && content2[kvp.Key] != kvp.Value)
|
||||
{
|
||||
if (content2[kvp.Key] != kvp.Value)
|
||||
{
|
||||
ThrowError($"Texts for the tag \"{kvp.Key}\" don't match:\n1. {kvp.Value}\n2. {content2[kvp.Key]}");
|
||||
}
|
||||
ThrowError($"Texts for the tag \"{kvp.Key}\" don't match:\n1. {kvp.Value}\n2. {content2[kvp.Key]}");
|
||||
}
|
||||
}
|
||||
foreach (KeyValuePair<string, string> kvp in content2)
|
||||
@@ -2319,7 +2320,14 @@ namespace Barotrauma
|
||||
}
|
||||
Barotrauma.IO.Validation.SkipValidationInDebugBuilds = true;
|
||||
File.WriteAllLines(filePath, lines);
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
try
|
||||
{
|
||||
ToolBox.OpenFileWithShell(Path.GetFullPath(filePath));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ThrowError($"Failed to open the file \"{filePath}\".", e);
|
||||
}
|
||||
|
||||
System.Xml.XmlWriterSettings settings = new System.Xml.XmlWriterSettings
|
||||
{
|
||||
|
||||
@@ -671,7 +671,7 @@ namespace Barotrauma
|
||||
if (Character.Controlled != null && ChatMessage.CanUseRadio(Character.Controlled, out WifiComponent radio))
|
||||
{
|
||||
radio.Channel = channel;
|
||||
GameMain.Client?.CreateEntityEvent(radio.Item, new object[] { NetEntityEvent.Type.ChangeProperty, radio.SerializableProperties["channel".ToIdentifier()] });
|
||||
GameMain.Client?.CreateEntityEvent(radio.Item, new Item.ChangePropertyEventData(radio.SerializableProperties["channel".ToIdentifier()]));
|
||||
|
||||
if (setText)
|
||||
{
|
||||
|
||||
@@ -172,7 +172,7 @@ namespace Barotrauma
|
||||
{
|
||||
AutoScaleVertical = true,
|
||||
TextScale = 1.1f,
|
||||
TextGetter = () => FormatCurrency(campaign.Money)
|
||||
TextGetter = () => FormatCurrency(campaign.Wallet.Balance)
|
||||
};
|
||||
|
||||
var pendingAndCrewGroup = new GUILayoutGroup(new RectTransform(new Vector2(0.9f, 0.95f), anchor: Anchor.Center,
|
||||
@@ -630,7 +630,7 @@ namespace Barotrauma
|
||||
total += ((InfoSkill)c.UserData).CharacterInfo.Salary;
|
||||
});
|
||||
totalBlock.Text = FormatCurrency(total);
|
||||
bool enoughMoney = campaign != null ? total <= campaign.Money : true;
|
||||
bool enoughMoney = campaign == null || campaign.Wallet.CanAfford(total);
|
||||
totalBlock.TextColor = enoughMoney ? Color.White : Color.Red;
|
||||
validateHiresButton.Enabled = enoughMoney && pendingList.Content.RectTransform.Children.Any();
|
||||
}
|
||||
@@ -652,7 +652,7 @@ namespace Barotrauma
|
||||
|
||||
int total = nonDuplicateHires.Aggregate(0, (total, info) => total + info.Salary);
|
||||
|
||||
if (total > campaign.Money) { return false; }
|
||||
if (!campaign.Wallet.CanAfford(total)) { return false; }
|
||||
|
||||
bool atLeastOneHired = false;
|
||||
foreach (CharacterInfo ci in nonDuplicateHires)
|
||||
|
||||
@@ -299,14 +299,16 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.ShowFPS || GameMain.DebugDraw)
|
||||
if (GameMain.ShowFPS || GameMain.DebugDraw || GameMain.ShowPerf)
|
||||
{
|
||||
DrawString(spriteBatch, new Vector2(10, 10),
|
||||
float y = 10.0f;
|
||||
DrawString(spriteBatch, new Vector2(10, y),
|
||||
"FPS: " + Math.Round(GameMain.PerformanceCounter.AverageFramesPerSecond),
|
||||
Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
|
||||
if (GameMain.GameSession != null && Timing.TotalTime > GameMain.GameSession.RoundStartTime + 1.0)
|
||||
{
|
||||
DrawString(spriteBatch, new Vector2(10, 25),
|
||||
y += GameSettings.CurrentConfig.Graphics.TextScale * 15.0f;
|
||||
DrawString(spriteBatch, new Vector2(10, y),
|
||||
$"Physics: {GameMain.CurrentUpdateRate}",
|
||||
(GameMain.CurrentUpdateRate < Timing.FixedUpdateRate) ? Color.Red : Color.White, Color.Black * 0.5f, 0, GUIStyle.SmallFont);
|
||||
}
|
||||
@@ -336,8 +338,15 @@ namespace Barotrauma
|
||||
DrawString(spriteBatch, new Vector2(300, y),
|
||||
key + ": " + elapsedMillisecs.ToString("0.00"),
|
||||
Color.Lerp(Color.LightGreen, GUIStyle.Red, elapsedMillisecs / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
|
||||
|
||||
y += 15;
|
||||
foreach (string childKey in GameMain.PerformanceCounter.GetSavedPartialIdentifiers(key))
|
||||
{
|
||||
elapsedMillisecs = GameMain.PerformanceCounter.GetPartialAverageElapsedMillisecs(key, childKey);
|
||||
DrawString(spriteBatch, new Vector2(315, y),
|
||||
childKey + ": " + elapsedMillisecs.ToString("0.00"),
|
||||
Color.Lerp(Color.LightGreen, GUIStyle.Red, elapsedMillisecs / 10.0f), Color.Black * 0.5f, 0, GUIStyle.SmallFont);
|
||||
y += 15;
|
||||
}
|
||||
}
|
||||
|
||||
if (Powered.Grids != null)
|
||||
@@ -1453,7 +1462,7 @@ namespace Barotrauma
|
||||
3 => radii.Start,
|
||||
_ => throw new InvalidOperationException()
|
||||
};
|
||||
int getDirectionIndex(int vertexIndex)
|
||||
static int getDirectionIndex(int vertexIndex)
|
||||
=> (vertexIndex % 4) switch
|
||||
{
|
||||
0 => (vertexIndex / 4) + 0,
|
||||
|
||||
@@ -5,7 +5,7 @@ using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class GUIColorPicker : GUIComponent
|
||||
public class GUIColorPicker : GUIComponent, IDisposable
|
||||
{
|
||||
public delegate bool OnColorSelectedHandler(GUIColorPicker component, Color color);
|
||||
public OnColorSelectedHandler? OnColorSelected;
|
||||
@@ -34,11 +34,6 @@ namespace Barotrauma
|
||||
|
||||
public GUIColorPicker(RectTransform rectT, string? style = null) : base(style, rectT) { }
|
||||
|
||||
~GUIColorPicker()
|
||||
{
|
||||
DisposeTextures();
|
||||
}
|
||||
|
||||
private void Init()
|
||||
{
|
||||
int tWidth = Rect.Width;
|
||||
@@ -170,10 +165,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void DisposeTextures()
|
||||
public void Dispose()
|
||||
{
|
||||
mainTexture?.Dispose();
|
||||
mainTexture = null;
|
||||
hueTexture?.Dispose();
|
||||
hueTexture = null;
|
||||
}
|
||||
|
||||
public void RefreshHue()
|
||||
|
||||
@@ -161,7 +161,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false) : base(style, rectT)
|
||||
public GUIDropDown(RectTransform rectT, LocalizedString text = null, int elementCount = 4, string style = "", bool selectMultiple = false, bool dropAbove = false, Alignment textAlignment = Alignment.CenterLeft) : base(style, rectT)
|
||||
{
|
||||
text ??= new RawLString("");
|
||||
|
||||
@@ -170,9 +170,10 @@ namespace Barotrauma
|
||||
|
||||
this.selectMultiple = selectMultiple;
|
||||
|
||||
button = new GUIButton(new RectTransform(Vector2.One, rectT), text, Alignment.CenterLeft, style: "GUIDropDown")
|
||||
button = new GUIButton(new RectTransform(Vector2.One, rectT), text, textAlignment, style: "GUIDropDown")
|
||||
{
|
||||
OnClicked = OnClicked
|
||||
OnClicked = OnClicked,
|
||||
TextBlock = { OverflowClip = true }
|
||||
};
|
||||
GUIStyle.Apply(button, "", this);
|
||||
button.TextBlock.SetTextPos();
|
||||
|
||||
@@ -96,11 +96,15 @@ namespace Barotrauma
|
||||
switch (child.ScaleBasis)
|
||||
{
|
||||
case ScaleBasis.BothHeight:
|
||||
child.MinSize = new Point(child.Rect.Height, child.MinSize.Y);
|
||||
break;
|
||||
case ScaleBasis.Smallest when Rect.Height <= Rect.Width:
|
||||
case ScaleBasis.Largest when Rect.Height > Rect.Width:
|
||||
child.MinSize = new Point((int)((child.Rect.Height * child.RelativeSize.X) / child.RelativeSize.Y), child.MinSize.Y);
|
||||
break;
|
||||
case ScaleBasis.BothWidth:
|
||||
child.MinSize = new Point(child.MinSize.X, child.Rect.Width);
|
||||
break;
|
||||
case ScaleBasis.Smallest when Rect.Width <= Rect.Height:
|
||||
case ScaleBasis.Largest when Rect.Width > Rect.Height:
|
||||
child.MinSize = new Point(child.MinSize.X, (int)((child.Rect.Width * child.RelativeSize.Y) / child.RelativeSize.X));
|
||||
|
||||
@@ -12,9 +12,12 @@ namespace Barotrauma
|
||||
Int, Float
|
||||
}
|
||||
|
||||
public delegate void OnValueEnteredHandler(GUINumberInput numberInput);
|
||||
public OnValueEnteredHandler OnValueEntered;
|
||||
|
||||
public delegate void OnValueChangedHandler(GUINumberInput numberInput);
|
||||
public OnValueChangedHandler OnValueChanged;
|
||||
|
||||
|
||||
public GUITextBox TextBox { get; private set; }
|
||||
|
||||
public GUIButton PlusButton { get; private set; }
|
||||
@@ -209,6 +212,8 @@ namespace Barotrauma
|
||||
{
|
||||
ClampFloatValue();
|
||||
}
|
||||
|
||||
OnValueEntered?.Invoke(this);
|
||||
};
|
||||
TextBox.OnEnterPressed += (textBox, text) =>
|
||||
{
|
||||
@@ -220,6 +225,8 @@ namespace Barotrauma
|
||||
{
|
||||
ClampFloatValue();
|
||||
}
|
||||
|
||||
OnValueEntered?.Invoke(this);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
@@ -312,6 +312,7 @@ namespace Barotrauma
|
||||
MoveButton(new Vector2(
|
||||
Math.Sign(PlayerInput.MousePosition.X - Bar.Rect.Center.X) * Bar.Rect.Width * barScale,
|
||||
Math.Sign(PlayerInput.MousePosition.Y - Bar.Rect.Center.Y) * Bar.Rect.Height * barScale));
|
||||
OnReleased?.Invoke(this, BarScroll);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ namespace Barotrauma
|
||||
healList.PriceBlock.Text = UpgradeStore.FormatCurrency(totalCost);
|
||||
healList.PriceBlock.TextColor = GUIStyle.Red;
|
||||
healList.HealButton.Enabled = false;
|
||||
if (medicalClinic.GetMoney() > totalCost)
|
||||
if (medicalClinic.GetWallet().CanAfford(totalCost))
|
||||
{
|
||||
healList.PriceBlock.TextColor = GUIStyle.TextColorNormal;
|
||||
if (medicalClinic.PendingHeals.Any())
|
||||
@@ -467,7 +467,7 @@ namespace Barotrauma
|
||||
|
||||
GUITextBlock moneyLabel = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), balanceLayout.RectTransform), string.Empty, textAlignment: Alignment.TopRight, font: GUIStyle.SubHeadingFont)
|
||||
{
|
||||
TextGetter = () => UpgradeStore.FormatCurrency(medicalClinic.GetMoney()),
|
||||
TextGetter = () => UpgradeStore.FormatCurrency(medicalClinic.GetWallet().Balance),
|
||||
AutoScaleVertical = true,
|
||||
TextScale = 1.1f
|
||||
};
|
||||
@@ -577,7 +577,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), footerLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterRight);
|
||||
GUIButton healButton = new GUIButton(new RectTransform(new Vector2(0.33f, 1f), buttonLayout.RectTransform), TextManager.Get("medicalclinic.heal"))
|
||||
{
|
||||
Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetTotalCost() < medicalClinic.GetMoney(),
|
||||
Enabled = medicalClinic.PendingHeals.Any() && medicalClinic.GetWallet().CanAfford(medicalClinic.GetTotalCost()),
|
||||
OnClicked = (button, _) =>
|
||||
{
|
||||
button.Enabled = false;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
@@ -67,8 +68,7 @@ namespace Barotrauma
|
||||
|
||||
private CargoManager CargoManager => campaignUI.Campaign.CargoManager;
|
||||
private Location CurrentLocation => campaignUI.Campaign.Map?.CurrentLocation;
|
||||
private int PlayerMoney => campaignUI.Campaign.Money;
|
||||
|
||||
private Wallet PlayerWallet => campaignUI.Campaign.Wallet;
|
||||
private bool IsBuying => activeTab switch
|
||||
{
|
||||
StoreTab.Buy => true,
|
||||
@@ -715,24 +715,30 @@ namespace Barotrauma
|
||||
|
||||
private LocalizedString GetMerchantBalanceText() => GetCurrencyFormatted(CurrentLocation?.StoreCurrentBalance ?? 0);
|
||||
|
||||
private LocalizedString GetPlayerBalanceText() => GetCurrencyFormatted(PlayerMoney);
|
||||
private LocalizedString GetPlayerBalanceText() => GetCurrencyFormatted(PlayerWallet.Balance);
|
||||
|
||||
private GUILayoutGroup CreateDealsGroup(GUIListBox parentList, int elementCount = 4)
|
||||
{
|
||||
var elementHeight = (int)(GUI.yScale * 80);
|
||||
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, elementCount * elementHeight + 3), parent: parentList.Content.RectTransform), style: null);
|
||||
frame.UserData = "deals";
|
||||
var frame = new GUIFrame(new RectTransform(new Point(parentList.Content.Rect.Width, elementCount * elementHeight + 3), parent: parentList.Content.RectTransform), style: null)
|
||||
{
|
||||
UserData = "deals"
|
||||
};
|
||||
var dealsGroup = new GUILayoutGroup(new RectTransform(Vector2.One, frame.RectTransform, anchor: Anchor.Center), childAnchor: Anchor.TopCenter);
|
||||
var dealsHeader = new GUILayoutGroup(new RectTransform(new Point((int)(0.95f * parentList.Content.Rect.Width), elementHeight), parent: dealsGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
dealsHeader.UserData = "header";
|
||||
var dealsHeader = new GUILayoutGroup(new RectTransform(new Point((int)(0.95f * parentList.Content.Rect.Width), elementHeight), parent: dealsGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft)
|
||||
{
|
||||
UserData = "header"
|
||||
};
|
||||
var iconWidth = (0.9f * dealsHeader.Rect.Height) / dealsHeader.Rect.Width;
|
||||
var dealsIcon = new GUIImage(new RectTransform(new Vector2(iconWidth, 0.9f), dealsHeader.RectTransform), "StoreDealIcon", scaleToFit: true);
|
||||
var text = TextManager.Get(parentList == storeBuyList ? "campaignstore.dailyspecials" : "campaignstore.requestedgoods");
|
||||
var dealsText = new GUITextBlock(new RectTransform(new Vector2(1.0f - iconWidth, 0.9f), dealsHeader.RectTransform), text, font: GUIStyle.LargeFont);
|
||||
storeSpecialColor = dealsIcon.Color;
|
||||
dealsText.TextColor = storeSpecialColor;
|
||||
var divider = new GUIImage(new RectTransform(new Point(dealsGroup.Rect.Width, 3), dealsGroup.RectTransform), "HorizontalLine");
|
||||
divider.UserData = "divider";
|
||||
var divider = new GUIImage(new RectTransform(new Point(dealsGroup.Rect.Width, 3), dealsGroup.RectTransform), "HorizontalLine")
|
||||
{
|
||||
UserData = "divider"
|
||||
};
|
||||
frame.CanBeFocused = dealsGroup.CanBeFocused = dealsHeader.CanBeFocused = dealsIcon.CanBeFocused = dealsText.CanBeFocused = divider.CanBeFocused = false;
|
||||
return dealsGroup;
|
||||
}
|
||||
@@ -1801,7 +1807,7 @@ namespace Barotrauma
|
||||
private void SetOwnedText(GUIComponent itemComponent, GUITextBlock ownedLabel = null)
|
||||
{
|
||||
ownedLabel ??= itemComponent?.FindChild("owned", recursive: true) as GUITextBlock;
|
||||
if (itemComponent == null && ownedLabel == null) { return; }
|
||||
if (itemComponent == null && ownedLabel == null) { return; }
|
||||
PurchasedItem purchasedItem = itemComponent?.UserData as PurchasedItem;
|
||||
ItemQuantity itemQuantity = null;
|
||||
LocalizedString ownedLabelText = string.Empty;
|
||||
@@ -1970,7 +1976,7 @@ namespace Barotrauma
|
||||
DebugConsole.ShowError($"Error clearing the shopping crate: Uknown store tab type. {e.StackTrace.CleanupStackTrace()}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool BuyItems()
|
||||
{
|
||||
@@ -1990,7 +1996,7 @@ namespace Barotrauma
|
||||
}
|
||||
itemsToRemove.ForEach(i => itemsToPurchase.Remove(i));
|
||||
|
||||
if (itemsToPurchase.None() || totalPrice > PlayerMoney) { return false; }
|
||||
if (itemsToPurchase.None() || !PlayerWallet.CanAfford(totalPrice)) { return false; }
|
||||
|
||||
CargoManager.PurchaseItems(itemsToPurchase, true);
|
||||
GameMain.Client?.SendCampaignState();
|
||||
@@ -2047,7 +2053,7 @@ namespace Barotrauma
|
||||
if (IsBuying)
|
||||
{
|
||||
shoppingCrateTotal.Text = GetCurrencyFormatted(buyTotal);
|
||||
shoppingCrateTotal.TextColor = buyTotal > PlayerMoney ? Color.Red : Color.White;
|
||||
shoppingCrateTotal.TextColor = !PlayerWallet.CanAfford(buyTotal) ? Color.Red : Color.White;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2093,7 +2099,7 @@ namespace Barotrauma
|
||||
ActiveShoppingCrateList.Content.RectTransform.Children.Any() &&
|
||||
activeTab switch
|
||||
{
|
||||
StoreTab.Buy => buyTotal <= PlayerMoney,
|
||||
StoreTab.Buy => PlayerWallet.CanAfford(buyTotal),
|
||||
StoreTab.Sell => CurrentLocation != null && sellTotal <= CurrentLocation.StoreCurrentBalance,
|
||||
StoreTab.SellSub => CurrentLocation != null && sellFromSubTotal <= CurrentLocation.StoreCurrentBalance,
|
||||
_ => false
|
||||
@@ -2109,9 +2115,12 @@ namespace Barotrauma
|
||||
|
||||
private float ownedItemsUpdateTimer = 0.0f, sellableItemsFromSubUpdateTimer = 0.0f;
|
||||
private const float timerUpdateInterval = 1.5f;
|
||||
private readonly Stopwatch updateStopwatch = new Stopwatch();
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
updateStopwatch.Restart();
|
||||
|
||||
if (GameMain.GraphicsWidth != resolutionWhenCreated.X || GameMain.GraphicsHeight != resolutionWhenCreated.Y)
|
||||
{
|
||||
CreateUI();
|
||||
@@ -2124,10 +2133,10 @@ namespace Barotrauma
|
||||
{
|
||||
var prevOwnedItems = new Dictionary<ItemPrefab, ItemQuantity>(OwnedItems);
|
||||
UpdateOwnedItems();
|
||||
var refresh = (prevOwnedItems.Count != OwnedItems.Count) ||
|
||||
(prevOwnedItems.Select(kvp => kvp.Value.Total).Sum() != OwnedItems.Select(kvp => kvp.Value.Total).Sum()) ||
|
||||
(OwnedItems.Any(kvp => kvp.Value.Total > 0 && !prevOwnedItems.ContainsKey(kvp.Key)) ||
|
||||
prevOwnedItems.Any(kvp => !OwnedItems.TryGetValue(kvp.Key, out ItemQuantity itemQuantity) || kvp.Value.Total != itemQuantity.Total));
|
||||
bool refresh = OwnedItems.Count != prevOwnedItems.Count ||
|
||||
OwnedItems.Values.Sum(v => v.Total) != prevOwnedItems.Values.Sum(v => v.Total) ||
|
||||
OwnedItems.Any(kvp => !prevOwnedItems.TryGetValue(kvp.Key, out ItemQuantity v) || kvp.Value.Total != v.Total) ||
|
||||
prevOwnedItems.Any(kvp => !OwnedItems.ContainsKey(kvp.Key));
|
||||
if (refresh)
|
||||
{
|
||||
needsItemsToSellRefresh = true;
|
||||
@@ -2138,8 +2147,13 @@ namespace Barotrauma
|
||||
sellableItemsFromSubUpdateTimer += deltaTime;
|
||||
if (sellableItemsFromSubUpdateTimer >= timerUpdateInterval)
|
||||
{
|
||||
needsItemsToSellFromSubRefresh = true;
|
||||
needsRefresh = true;
|
||||
var prevSubItems = new List<PurchasedItem>(itemsToSellFromSub);
|
||||
RefreshItemsToSellFromSub();
|
||||
needsRefresh = needsRefresh ||
|
||||
itemsToSellFromSub.Count != prevSubItems.Count ||
|
||||
itemsToSellFromSub.Sum(i => i.Quantity) != prevSubItems.Sum(i => i.Quantity) ||
|
||||
itemsToSellFromSub.Any(i => !(prevSubItems.FirstOrDefault(prev => prev.ItemPrefab == i.ItemPrefab) is PurchasedItem prev) || i.Quantity != prev.Quantity) ||
|
||||
prevSubItems.Any(prev => itemsToSellFromSub.None(i => i.ItemPrefab == prev.ItemPrefab));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2148,7 +2162,10 @@ namespace Barotrauma
|
||||
if (needsRefresh || HavePermissionsChanged()) { Refresh(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
||||
if (needsBuyingRefresh || HavePermissionsChanged(StoreTab.Buy)) { RefreshBuying(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
||||
if (needsSellingRefresh || HavePermissionsChanged(StoreTab.Sell)) { RefreshSelling(updateOwned: ownedItemsUpdateTimer > 0.0f); }
|
||||
if (needsSellingFromSubRefresh || HavePermissionsChanged(StoreTab.SellSub)) { RefreshSellingFromSub(updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
|
||||
if (needsSellingFromSubRefresh || HavePermissionsChanged(StoreTab.SellSub)) { RefreshSellingFromSub(updateOwned: ownedItemsUpdateTimer > 0.0f, updateItemsToSellFromSub: sellableItemsFromSubUpdateTimer > 0.0f); }
|
||||
|
||||
updateStopwatch.Stop();
|
||||
GameMain.PerformanceCounter.AddPartialElapsedTicks("GameSessionUpdate", "StoreUpdate", updateStopwatch.ElapsedTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,7 +581,7 @@ namespace Barotrauma
|
||||
|
||||
private void ShowTransferPrompt()
|
||||
{
|
||||
if (GameMain.GameSession.Campaign.Money < deliveryFee && deliveryFee > 0)
|
||||
if (!GameMain.GameSession.Campaign.Wallet.CanAfford(deliveryFee) && deliveryFee > 0)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("deliveryrequestheader"), TextManager.GetWithVariables("notenoughmoneyfordeliverytext",
|
||||
("[currencyname]", currencyLongText),
|
||||
@@ -629,7 +629,7 @@ namespace Barotrauma
|
||||
|
||||
private void ShowBuyPrompt(bool purchaseOnly)
|
||||
{
|
||||
if (GameMain.GameSession.Campaign.Money < selectedSubmarine.Price)
|
||||
if (!GameMain.GameSession.Campaign.Wallet.CanAfford(selectedSubmarine.Price))
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("purchasesubmarineheader"), TextManager.GetWithVariables("notenoughmoneyforpurchasetext",
|
||||
("[currencyname]", currencyLongText),
|
||||
|
||||
@@ -37,6 +37,12 @@ namespace Barotrauma
|
||||
private GUIFrame pendingChangesFrame = null;
|
||||
|
||||
public static Color OwnCharacterBGColor = Color.Gold * 0.7f;
|
||||
private bool isTransferMenuOpen;
|
||||
private bool isSending;
|
||||
private GUIComponent transferMenu;
|
||||
private GUIButton transferMenuButton;
|
||||
private float transferMenuOpenState;
|
||||
private bool transferMenuStateCompleted;
|
||||
|
||||
private class LinkedGUI
|
||||
{
|
||||
@@ -133,8 +139,43 @@ namespace Barotrauma
|
||||
SelectInfoFrameTab(SelectedTab);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
float menuOpenSpeed = deltaTime * 10f;
|
||||
if (isTransferMenuOpen)
|
||||
{
|
||||
if (transferMenuStateCompleted)
|
||||
{
|
||||
transferMenuOpenState = transferMenuOpenState < 0.25f ? Math.Min(0.25f, transferMenuOpenState + (menuOpenSpeed / 2f)) : 0.25f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (transferMenuOpenState > 0.15f)
|
||||
{
|
||||
transferMenuStateCompleted = false;
|
||||
transferMenuOpenState = Math.Max(0.15f, transferMenuOpenState - menuOpenSpeed);
|
||||
}
|
||||
else
|
||||
{
|
||||
transferMenuStateCompleted = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
transferMenuStateCompleted = false;
|
||||
if (transferMenuOpenState < 1f)
|
||||
{
|
||||
transferMenuOpenState = Math.Min(1f, transferMenuOpenState + menuOpenSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
if (transferMenu != null && transferMenuButton != null)
|
||||
{
|
||||
int pos = (int)(transferMenuOpenState * -transferMenu.Rect.Height);
|
||||
transferMenu.RectTransform.AbsoluteOffset = new Point(0, pos);
|
||||
transferMenuButton.RectTransform.AbsoluteOffset = new Point(0, -pos - transferMenu.Rect.Height);
|
||||
}
|
||||
GameSession.UpdateTalentNotificationIndicator(talentPointNotification);
|
||||
if (Character.Controlled is { } controlled && talentResetButton != null && talentApplyButton != null)
|
||||
{
|
||||
@@ -243,10 +284,7 @@ namespace Barotrauma
|
||||
var reputationButton = createTabButton(InfoFrameTab.Reputation, "reputation");
|
||||
|
||||
var balanceFrame = new GUIFrame(new RectTransform(new Point(innerLayoutGroup.Rect.Width, innerLayoutGroup.Rect.Height - infoFrameHolderHeight), parent: innerLayoutGroup.RectTransform), style: "InnerFrame");
|
||||
new GUITextBlock(new RectTransform(Vector2.One, balanceFrame.RectTransform), "", textAlignment: Alignment.Right)
|
||||
{
|
||||
TextGetter = () => TextManager.GetWithVariable("campaignmoney", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", campaignMode.Money))
|
||||
};
|
||||
GUITextBlock balanceText = new GUITextBlock(new RectTransform(Vector2.One, balanceFrame.RectTransform), string.Empty, textAlignment: Alignment.Right);
|
||||
GUIFrame bottomDisclaimerFrame = new GUIFrame(new RectTransform(new Vector2(contentFrameSize.X, 0.1f), infoFrame.RectTransform)
|
||||
{
|
||||
AbsoluteOffset = new Point(contentFrame.Rect.X, contentFrame.Rect.Bottom + GUI.IntScale(8))
|
||||
@@ -258,6 +296,18 @@ namespace Barotrauma
|
||||
{
|
||||
NetLobbyScreen.CreateChangesPendingFrame(pendingChangesFrame);
|
||||
}
|
||||
|
||||
SetBalanceText(balanceText, campaignMode.Bank.Balance);
|
||||
campaignMode.OnMoneyChanged.RegisterOverwriteExisting(nameof(CreateInfoFrame).ToIdentifier(), e =>
|
||||
{
|
||||
if (e.Wallet != campaignMode.Bank) { return; }
|
||||
SetBalanceText(balanceText, e.Wallet.Balance);
|
||||
});
|
||||
|
||||
static void SetBalanceText(GUITextBlock text, int balance)
|
||||
{
|
||||
text.Text = TextManager.GetWithVariable("bankbalanceformat", "[money]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", balance));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -329,7 +379,8 @@ namespace Barotrauma
|
||||
|
||||
private void CreateCrewListFrame(GUIFrame crewFrame)
|
||||
{
|
||||
crew = GameMain.GameSession.CrewManager.GetCharacters();
|
||||
// FIXME remove TestScreen stuff
|
||||
crew = GameMain.GameSession?.CrewManager?.GetCharacters() ?? new []{ TestScreen.dummyCharacter };
|
||||
teamIDs = crew.Select(c => c.TeamID).Distinct().ToList();
|
||||
|
||||
// Show own team first when there's more than one team
|
||||
@@ -743,7 +794,7 @@ namespace Barotrauma
|
||||
Client client = userData as Client;
|
||||
|
||||
GUIComponent existingPreview = infoFrameHolder.FindChild("SelectedCharacter");
|
||||
if (existingPreview != null) infoFrameHolder.RemoveChild(existingPreview);
|
||||
if (existingPreview != null) { infoFrameHolder.RemoveChild(existingPreview); }
|
||||
|
||||
GUIFrame background = new GUIFrame(new RectTransform(new Vector2(0.543f, 0.717f), infoFrameHolder.RectTransform, Anchor.TopLeft, Pivot.TopRight) { RelativeOffset = new Vector2(-0.145f, 0) })
|
||||
{
|
||||
@@ -760,17 +811,291 @@ namespace Barotrauma
|
||||
{
|
||||
GUIComponent preview = character.Info.CreateInfoFrame(background, false, GetPermissionIcon(GameMain.Client.ConnectedClients.Find(c => c.Character == character)));
|
||||
GameMain.Client.SelectCrewCharacter(character, preview);
|
||||
CreateWalletFrame(background, character);
|
||||
}
|
||||
}
|
||||
else if (client != null)
|
||||
{
|
||||
GUIComponent preview = CreateClientInfoFrame(background, client, GetPermissionIcon(client));
|
||||
if (GameMain.NetworkMember != null) GameMain.Client.SelectCrewClient(client, preview);
|
||||
if (GameMain.NetworkMember != null) { GameMain.Client.SelectCrewClient(client, preview); }
|
||||
CreateWalletFrame(background, client.Character);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CreateWalletFrame(GUIComponent parent, Character character)
|
||||
{
|
||||
if (character is null) { throw new ArgumentNullException(nameof(character), "Tried to create a wallet frame for a null character");}
|
||||
isTransferMenuOpen = false;
|
||||
transferMenuOpenState = 1f;
|
||||
ImmutableArray<Character> salaryCrew = Mission.GetSalaryEligibleCrew().Where(c => c != character).ToImmutableArray();
|
||||
|
||||
Wallet targetWallet = character.Wallet;
|
||||
|
||||
GUIFrame walletFrame = new GUIFrame(new RectTransform(new Vector2(1f, 0.35f), parent.RectTransform, anchor: Anchor.TopLeft)
|
||||
{
|
||||
RelativeOffset = new Vector2(0, 1.02f)
|
||||
});
|
||||
|
||||
GUILayoutGroup walletLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(walletFrame.RectTransform, 0.9f), walletFrame.RectTransform, anchor: Anchor.Center));
|
||||
|
||||
GUILayoutGroup headerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.33f), walletLayout.RectTransform), isHorizontal: true);
|
||||
GUIImage icon = new GUIImage(new RectTransform(Vector2.One, headerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight), style: "StoreTradingIcon", scaleToFit: true);
|
||||
float relativeX = icon.RectTransform.NonScaledSize.X / (float)icon.Parent.RectTransform.NonScaledSize.X;
|
||||
GUILayoutGroup headerTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1.0f - relativeX, 1f), headerLayout.RectTransform), isHorizontal: true) { Stretch = true };
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), headerTextLayout.RectTransform), TextManager.Get("crewwallet.wallet"), font: GUIStyle.LargeFont);
|
||||
GUITextBlock moneyBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), headerTextLayout.RectTransform), UpgradeStore.FormatCurrency(targetWallet.Balance), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right);
|
||||
|
||||
GUILayoutGroup middleLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.66f), walletLayout.RectTransform));
|
||||
GUILayoutGroup salaryTextLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), middleLayout.RectTransform), isHorizontal: true);
|
||||
GUITextBlock salaryTitle = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), salaryTextLayout.RectTransform), TextManager.Get("crewwallet.salary"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.BottomLeft);
|
||||
GUITextBlock rewardBlock = new GUITextBlock(new RectTransform(new Vector2(0.5f, 1f), salaryTextLayout.RectTransform), $"{Mission.GetRewardShare(targetWallet.RewardDistribution, salaryCrew, Option<int>.None()).Percentage}%", textAlignment: Alignment.BottomRight);
|
||||
GUILayoutGroup sliderLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), middleLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.Center);
|
||||
GUIScrollBar salarySlider = new GUIScrollBar(new RectTransform(new Vector2(0.9f, 1f), sliderLayout.RectTransform), style: "GUISlider", barSize: 0.03f)
|
||||
{
|
||||
Range = Vector2.UnitY,
|
||||
BarScrollValue = targetWallet.RewardDistribution / 100f,
|
||||
Step = 0.01f,
|
||||
BarSize = 0.1f,
|
||||
OnMoved = (bar, scroll) =>
|
||||
{
|
||||
rewardBlock.Text = $"{Mission.GetRewardShare((int)(scroll * 100f), salaryCrew, Option<int>.None()).Percentage}%";
|
||||
return true;
|
||||
},
|
||||
OnReleased = (bar, scroll) =>
|
||||
{
|
||||
int newRewardDistribution = (int)(scroll * 100);
|
||||
if (newRewardDistribution == targetWallet.RewardDistribution) { return false; }
|
||||
SetRewardDistribution(character, newRewardDistribution);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
// @formatter:off
|
||||
GUIScissorComponent scissorComponent = new GUIScissorComponent(new RectTransform(new Vector2(0.85f, 1.25f), walletFrame.RectTransform, Anchor.BottomCenter, Pivot.TopCenter))
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
transferMenu = new GUIFrame(new RectTransform(Vector2.One, scissorComponent.Content.RectTransform));
|
||||
|
||||
GUILayoutGroup transferMenuLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.8f), transferMenu.RectTransform, Anchor.BottomLeft), childAnchor: Anchor.Center);
|
||||
GUILayoutGroup paddedTransferMenuLayout = new GUILayoutGroup(new RectTransform(ToolBox.PaddingSizeParentRelative(transferMenuLayout.RectTransform, 0.85f), transferMenuLayout.RectTransform));
|
||||
GUILayoutGroup mainLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.5f), paddedTransferMenuLayout.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft);
|
||||
GUILayoutGroup leftLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1f), mainLayout.RectTransform));
|
||||
GUITextBlock leftName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), leftLayout.RectTransform), character.Name, textAlignment: Alignment.CenterLeft, font: GUIStyle.SubHeadingFont);
|
||||
GUITextBlock leftBalance = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), leftLayout.RectTransform), UpgradeStore.FormatCurrency(targetWallet.Balance), textAlignment: Alignment.Left) { TextColor = GUIStyle.Blue };
|
||||
GUILayoutGroup rightLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.5f, 1f), mainLayout.RectTransform), childAnchor: Anchor.TopRight);
|
||||
GUITextBlock rightName = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), rightLayout.RectTransform), string.Empty, font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterRight);
|
||||
GUITextBlock rightBalance = new GUITextBlock(new RectTransform(new Vector2(1f, 0.5f), rightLayout.RectTransform), string.Empty, textAlignment: Alignment.Right) { TextColor = GUIStyle.Red };
|
||||
GUILayoutGroup centerLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.9f), mainLayout.RectTransform, Anchor.Center), childAnchor: Anchor.Center) { IgnoreLayoutGroups = true };
|
||||
new GUIFrame(new RectTransform(new Vector2(0f, 1f), centerLayout.RectTransform, Anchor.Center), style: "VerticalLine") { IgnoreLayoutGroups = true };
|
||||
GUIButton centerButton = new GUIButton(new RectTransform(new Vector2(0.6f), centerLayout.RectTransform, scaleBasis: ScaleBasis.BothHeight, anchor: Anchor.Center), style: "GUIButtonTransferArrow");
|
||||
|
||||
GUILayoutGroup inputLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedTransferMenuLayout.RectTransform), childAnchor: Anchor.Center);
|
||||
GUINumberInput transferAmountInput = new GUINumberInput(new RectTransform(new Vector2(0.5f, 1f), inputLayout.RectTransform), GUINumberInput.NumberType.Int, hidePlusMinusButtons: true)
|
||||
{
|
||||
MinValueInt = 0
|
||||
};
|
||||
|
||||
GUILayoutGroup buttonLayout = new GUILayoutGroup(new RectTransform(new Vector2(1f, 0.25f), paddedTransferMenuLayout.RectTransform), childAnchor: Anchor.Center);
|
||||
GUILayoutGroup centerButtonLayout = new GUILayoutGroup(new RectTransform(new Vector2(0.75f, 1f), buttonLayout.RectTransform), isHorizontal: true);
|
||||
GUIButton confirmButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), centerButtonLayout.RectTransform), TextManager.Get("confirm"), style: "GUIButtonFreeScale") { Enabled = false };
|
||||
GUIButton resetButton = new GUIButton(new RectTransform(new Vector2(0.5f, 1f), centerButtonLayout.RectTransform), TextManager.Get("reset"), style: "GUIButtonFreeScale") { Enabled = false };
|
||||
// @formatter:on
|
||||
transferMenuButton = new GUIButton(new RectTransform(new Vector2(0.5f, 0.2f), walletFrame.RectTransform, Anchor.BottomCenter, Pivot.TopCenter), style: "UIToggleButtonVertical")
|
||||
{
|
||||
OnClicked = (button, o) =>
|
||||
{
|
||||
isTransferMenuOpen = !isTransferMenuOpen;
|
||||
if (!isTransferMenuOpen)
|
||||
{
|
||||
transferAmountInput.IntValue = 0;
|
||||
}
|
||||
ToggleTransferMenuIcon(button, open: isTransferMenuOpen);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
ToggleTransferMenuIcon(transferMenuButton, open: isTransferMenuOpen);
|
||||
ToggleCenterButton(centerButton, isSending);
|
||||
|
||||
if (GameMain.GameSession?.Campaign is MultiPlayerCampaign campaign)
|
||||
{
|
||||
if (!(Character.Controlled is { } myCharacter))
|
||||
{
|
||||
salarySlider.Enabled = false;
|
||||
transferAmountInput.Enabled = false;
|
||||
centerButton.Enabled = false;
|
||||
confirmButton.Enabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasPermissions = campaign.AllowedToManageCampaign();
|
||||
salarySlider.Enabled = hasPermissions;
|
||||
Wallet otherWallet;
|
||||
|
||||
switch (hasPermissions)
|
||||
{
|
||||
case true:
|
||||
rightName.Text = TextManager.Get("crewwallet.bank");
|
||||
otherWallet = campaign.Bank;
|
||||
break;
|
||||
case false when character == myCharacter:
|
||||
rightName.Text = TextManager.Get("crewwallet.bank");
|
||||
otherWallet = campaign.Bank;
|
||||
isSending = true;
|
||||
ToggleCenterButton(centerButton, isSending);
|
||||
break;
|
||||
default:
|
||||
rightName.Text = myCharacter.Name;
|
||||
otherWallet = campaign.PersonalWallet;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasPermissions)
|
||||
{
|
||||
centerButton.Enabled = centerButton.CanBeFocused = false;
|
||||
salarySlider.Enabled = salarySlider.CanBeFocused = false;
|
||||
}
|
||||
|
||||
leftBalance.Text = UpgradeStore.FormatCurrency(otherWallet.Balance);
|
||||
|
||||
UpdateAllInputs();
|
||||
|
||||
centerButton.OnClicked = (btn, o) =>
|
||||
{
|
||||
isSending = !isSending;
|
||||
ToggleCenterButton(btn, isSending);
|
||||
UpdateAllInputs();
|
||||
return true;
|
||||
};
|
||||
|
||||
transferAmountInput.OnValueChanged = input =>
|
||||
{
|
||||
UpdateInputs();
|
||||
};
|
||||
|
||||
transferAmountInput.OnValueEntered = input =>
|
||||
{
|
||||
UpdateAllInputs();
|
||||
};
|
||||
|
||||
campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(CreateWalletFrame).ToIdentifier(), e =>
|
||||
{
|
||||
if (e.Wallet == targetWallet)
|
||||
{
|
||||
moneyBlock.Text = UpgradeStore.FormatCurrency(e.Info.Balance);
|
||||
salarySlider.BarScrollValue = e.Info.RewardDistribution / 100f;
|
||||
}
|
||||
UpdateAllInputs();
|
||||
});
|
||||
|
||||
resetButton.OnClicked = (button, o) =>
|
||||
{
|
||||
transferAmountInput.IntValue = 0;
|
||||
UpdateAllInputs();
|
||||
return true;
|
||||
};
|
||||
|
||||
confirmButton.OnClicked = (button, o) =>
|
||||
{
|
||||
int amount = transferAmountInput.IntValue;
|
||||
if (amount == 0) { return false; }
|
||||
|
||||
Option<Character> target1 = Option<Character>.Some(character),
|
||||
target2 = otherWallet == campaign.Bank ? Option<Character>.None() : Option<Character>.Some(myCharacter);
|
||||
if (isSending) { (target1, target2) = (target2, target1); }
|
||||
|
||||
SendTransaction(target1, target2, amount);
|
||||
isTransferMenuOpen = false;
|
||||
ToggleTransferMenuIcon(transferMenuButton, isTransferMenuOpen);
|
||||
return true;
|
||||
};
|
||||
|
||||
void UpdateAllInputs()
|
||||
{
|
||||
UpdateInputs();
|
||||
UpdateMaxInput();
|
||||
}
|
||||
|
||||
void UpdateInputs()
|
||||
{
|
||||
confirmButton.Enabled = resetButton.Enabled = transferAmountInput.IntValue > 0;
|
||||
if (transferAmountInput.IntValue == 0)
|
||||
{
|
||||
rightBalance.Text = UpgradeStore.FormatCurrency(otherWallet.Balance);
|
||||
rightBalance.TextColor = GUIStyle.TextColorNormal;
|
||||
leftBalance.Text = UpgradeStore.FormatCurrency(targetWallet.Balance);
|
||||
leftBalance.TextColor = GUIStyle.TextColorNormal;
|
||||
}
|
||||
else if (isSending)
|
||||
{
|
||||
rightBalance.Text = UpgradeStore.FormatCurrency(otherWallet.Balance + transferAmountInput.IntValue);
|
||||
rightBalance.TextColor = GUIStyle.Blue;
|
||||
leftBalance.Text = UpgradeStore.FormatCurrency(targetWallet.Balance - transferAmountInput.IntValue);
|
||||
leftBalance.TextColor = GUIStyle.Red;
|
||||
}
|
||||
else
|
||||
{
|
||||
rightBalance.Text = UpgradeStore.FormatCurrency(otherWallet.Balance - transferAmountInput.IntValue);
|
||||
rightBalance.TextColor = GUIStyle.Red;
|
||||
leftBalance.Text = UpgradeStore.FormatCurrency(targetWallet.Balance + transferAmountInput.IntValue);
|
||||
leftBalance.TextColor = GUIStyle.Blue;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateMaxInput()
|
||||
{
|
||||
transferAmountInput.MaxValueInt = isSending ? targetWallet.Balance : otherWallet.Balance;
|
||||
}
|
||||
}
|
||||
|
||||
static void ToggleTransferMenuIcon(GUIButton btn, bool open)
|
||||
{
|
||||
foreach (GUIComponent child in btn.Children)
|
||||
{
|
||||
child.SpriteEffects = open ? SpriteEffects.None : SpriteEffects.FlipVertically;
|
||||
}
|
||||
}
|
||||
|
||||
static void ToggleCenterButton(GUIButton btn, bool isSending)
|
||||
{
|
||||
foreach (GUIComponent child in btn.Children)
|
||||
{
|
||||
child.SpriteEffects = isSending ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
|
||||
}
|
||||
}
|
||||
|
||||
static void SendTransaction(Option<Character> to, Option<Character> from, int amount)
|
||||
{
|
||||
INetSerializableStruct transfer = new NetWalletTransfer
|
||||
{
|
||||
Sender = from.Select(option => option.ID),
|
||||
Receiver = to.Select(option => option.ID),
|
||||
Amount = amount
|
||||
};
|
||||
IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.MONEY);
|
||||
transfer.Write(msg);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
static void SetRewardDistribution(Character character, int newValue)
|
||||
{
|
||||
INetSerializableStruct transfer = new NetWalletSalaryUpdate
|
||||
{
|
||||
Target = character.ID,
|
||||
NewRewardDistribution = newValue
|
||||
};
|
||||
IWriteMessage msg = new WriteOnlyMessage().WithHeader(ClientPacketHeader.REWARD_DISTRIBUTION);
|
||||
transfer.Write(msg);
|
||||
GameMain.Client?.ClientPeer?.Send(msg, DeliveryMethod.Reliable);
|
||||
}
|
||||
|
||||
static int GetRewardDistributionPercentage(int distribution, ImmutableArray<Character> crew)
|
||||
{
|
||||
return Mission.GetRewardShare(distribution, crew, Option<int>.None()).Percentage;
|
||||
}
|
||||
}
|
||||
|
||||
private GUIComponent CreateClientInfoFrame(GUIFrame frame, Client client, Sprite permissionIcon = null)
|
||||
{
|
||||
GUIComponent paddedFrame;
|
||||
@@ -1209,8 +1534,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
private Color unselectedColor = new Color(240, 255, 255, 225);
|
||||
private Color selectedColor = new Color(220, 255, 220, 225);
|
||||
private Color ownedColor = new Color(140, 180, 140, 225);
|
||||
private Color unselectableColor = new Color(100, 100, 100, 225);
|
||||
private Color pressedColor = new Color(60, 60, 60, 225);
|
||||
|
||||
@@ -1427,7 +1750,7 @@ namespace Barotrauma
|
||||
|
||||
GUILayoutGroup talentOptionLayoutGroup = new GUILayoutGroup(new RectTransform(Vector2.One, talentOptionCenterGroup.RectTransform), isHorizontal: true, childAnchor: Anchor.CenterLeft) { Stretch = true };
|
||||
|
||||
foreach (TalentPrefab talent in talentOption.Talents)
|
||||
foreach (TalentPrefab talent in talentOption.Talents.OrderBy(t => t.Identifier))
|
||||
{
|
||||
GUIFrame talentFrame = new GUIFrame(new RectTransform(Vector2.One, talentOptionLayoutGroup.RectTransform), style: null)
|
||||
{
|
||||
@@ -1652,7 +1975,7 @@ namespace Barotrauma
|
||||
controlledCharacter.GiveTalent(talent);
|
||||
if (GameMain.Client != null)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(controlledCharacter, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
GameMain.Client.CreateEntityEvent(controlledCharacter, new Character.UpdateTalentsEventData());
|
||||
}
|
||||
}
|
||||
selectedTalents = controlledCharacter.Info.GetUnlockedTalentsInTree().ToList();
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly CampaignUI campaignUI;
|
||||
private CampaignMode? Campaign => campaignUI.Campaign;
|
||||
private int AvailableMoney => Campaign?.Money ?? 0;
|
||||
private Wallet PlayerWallet => Campaign?.Wallet ?? Wallet.Invalid;
|
||||
private UpgradeTab selectedUpgradeTab = UpgradeTab.Upgrade;
|
||||
|
||||
private GUIMessageBox? currectConfirmation;
|
||||
@@ -61,7 +61,7 @@ namespace Barotrauma
|
||||
private Vector2[][] subHullVertices = new Vector2[0][];
|
||||
private List<Structure> submarineWalls = new List<Structure>();
|
||||
|
||||
public MapEntity? HoveredItem;
|
||||
public MapEntity? HoveredEntity;
|
||||
private bool highlightWalls;
|
||||
|
||||
private UpgradeCategory? currentUpgradeCategory;
|
||||
@@ -105,6 +105,7 @@ namespace Barotrauma
|
||||
Campaign.UpgradeManager.OnUpgradesChanged += RefreshAll;
|
||||
Campaign.CargoManager.OnPurchasedItemsChanged += RefreshAll;
|
||||
Campaign.CargoManager.OnSoldItemsChanged += RefreshAll;
|
||||
Campaign.OnMoneyChanged.RegisterOverwriteExisting(nameof(UpgradeStore).ToIdentifier(), e => { RefreshAll(); } );
|
||||
}
|
||||
|
||||
public void RefreshAll()
|
||||
@@ -184,7 +185,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// reset the order first
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories.OrderBy(c => c.Name))
|
||||
{
|
||||
GUIComponent component = categoryList.Content.FindChild(c => c.UserData is CategoryData categoryData && categoryData.Category == category);
|
||||
component?.SetAsLastChild();
|
||||
@@ -286,7 +287,7 @@ namespace Barotrauma
|
||||
GUILayoutGroup rightLayout = new GUILayoutGroup(rectT(0.5f, 1, topHeaderLayout), childAnchor: Anchor.TopRight);
|
||||
GUILayoutGroup priceLayout = new GUILayoutGroup(rectT(1, 0.8f, rightLayout), childAnchor: Anchor.Center) { RelativeSpacing = 0.08f };
|
||||
new GUITextBlock(rectT(1f, 0f, priceLayout), TextManager.Get("CampaignStore.Balance"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right);
|
||||
new GUITextBlock(rectT(1f, 0f, priceLayout), FormatCurrency(AvailableMoney, format: true), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right) { TextGetter = () => FormatCurrency(AvailableMoney, format: true) };
|
||||
new GUITextBlock(rectT(1f, 0f, priceLayout), FormatCurrency(PlayerWallet.Balance, format: true), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.Right) { TextGetter = () => FormatCurrency(PlayerWallet.Balance, format: true) };
|
||||
new GUIFrame(rectT(0.5f, 0.1f, rightLayout, Anchor.BottomRight), style: "HorizontalLine") { IgnoreLayoutGroups = true };
|
||||
|
||||
repairButton.OnClicked = upgradeButton.OnClicked = (button, o) =>
|
||||
@@ -343,15 +344,15 @@ namespace Barotrauma
|
||||
private void DrawItemSwapPreview(SpriteBatch spriteBatch, GUICustomComponent component)
|
||||
{
|
||||
var selectedItem = customizeTabOpen ?
|
||||
activeItemSwapSlideDown?.UserData as Item ?? HoveredItem as Item :
|
||||
HoveredItem as Item;
|
||||
activeItemSwapSlideDown?.UserData as Item ?? HoveredEntity as Item :
|
||||
HoveredEntity as Item;
|
||||
if (selectedItem?.Prefab.SwappableItem == null) { return; }
|
||||
|
||||
Sprite schematicsSprite = selectedItem.Prefab.SwappableItem.SchematicSprite;
|
||||
if (schematicsSprite == null) { return; }
|
||||
float schematicsScale = Math.Min(component.Rect.Width / 2 / schematicsSprite.size.X, component.Rect.Height / schematicsSprite.size.Y);
|
||||
Vector2 center = new Vector2(component.Rect.Center.X, component.Rect.Center.Y);
|
||||
schematicsSprite.Draw(spriteBatch, new Vector2(component.Rect.X, center.Y), GUIStyle.Green, new Vector2(0, schematicsSprite.size.Y / 2),
|
||||
schematicsSprite.Draw(spriteBatch, new Vector2(component.Rect.X, center.Y), GUIStyle.Green, new Vector2(0, schematicsSprite.size.Y / 2),
|
||||
scale: schematicsScale);
|
||||
|
||||
var swappableItemList = selectedUpgradeCategoryLayout?.FindChild("prefablist", true) as GUIListBox;
|
||||
@@ -426,14 +427,14 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AvailableMoney >= hullRepairCost)
|
||||
if (PlayerWallet.CanAfford(hullRepairCost))
|
||||
{
|
||||
LocalizedString body = TextManager.GetWithVariable("WallRepairs.PurchasePromptBody", "[amount]", hullRepairCost.ToString());
|
||||
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
|
||||
{
|
||||
if (AvailableMoney >= hullRepairCost)
|
||||
if (PlayerWallet.Balance >= hullRepairCost)
|
||||
{
|
||||
Campaign.Money -= hullRepairCost;
|
||||
PlayerWallet.TryDeduct(hullRepairCost);
|
||||
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
|
||||
Campaign.PurchasedHullRepairs = true;
|
||||
button.Enabled = false;
|
||||
@@ -461,14 +462,14 @@ namespace Barotrauma
|
||||
|
||||
CreateRepairEntry(currentStoreLayout.Content, TextManager.Get("repairallitems"), "RepairItemsButton", itemRepairCost, (button, o) =>
|
||||
{
|
||||
if (AvailableMoney >= itemRepairCost && !Campaign.PurchasedItemRepairs)
|
||||
if (PlayerWallet.Balance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
|
||||
{
|
||||
LocalizedString body = TextManager.GetWithVariable("ItemRepairs.PurchasePromptBody", "[amount]", itemRepairCost.ToString());
|
||||
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
|
||||
{
|
||||
if (AvailableMoney >= itemRepairCost && !Campaign.PurchasedItemRepairs)
|
||||
if (PlayerWallet.Balance >= itemRepairCost && !Campaign.PurchasedItemRepairs)
|
||||
{
|
||||
Campaign.Money -= itemRepairCost;
|
||||
PlayerWallet.TryDeduct(itemRepairCost);
|
||||
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
|
||||
Campaign.PurchasedItemRepairs = true;
|
||||
button.Enabled = false;
|
||||
@@ -507,14 +508,14 @@ namespace Barotrauma
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AvailableMoney >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
|
||||
if (PlayerWallet.CanAfford(shuttleRetrieveCost) && !Campaign.PurchasedLostShuttles)
|
||||
{
|
||||
LocalizedString body = TextManager.GetWithVariable("ReplaceLostShuttles.PurchasePromptBody", "[amount]", shuttleRetrieveCost.ToString());
|
||||
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), body, () =>
|
||||
{
|
||||
if (AvailableMoney >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
|
||||
if (PlayerWallet.Balance >= shuttleRetrieveCost && !Campaign.PurchasedLostShuttles)
|
||||
{
|
||||
Campaign.Money -= shuttleRetrieveCost;
|
||||
PlayerWallet.TryDeduct(shuttleRetrieveCost);
|
||||
GameAnalyticsManager.AddMoneySpentEvent(hullRepairCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
|
||||
Campaign.PurchasedLostShuttles = true;
|
||||
button.Enabled = false;
|
||||
@@ -572,13 +573,13 @@ namespace Barotrauma
|
||||
new GUITextBlock(rectT(1, 0, textLayout), title, font: GUIStyle.SubHeadingFont) { CanBeFocused = false, AutoScaleHorizontal = true };
|
||||
new GUITextBlock(rectT(1, 0, textLayout), FormatCurrency(price));
|
||||
GUILayoutGroup buyButtonLayout = new GUILayoutGroup(rectT(0.2f, 1, contentLayout), childAnchor: Anchor.Center) { UserData = "buybutton" };
|
||||
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = AvailableMoney >= price && !isDisabled, OnClicked = onPressed };
|
||||
new GUIButton(rectT(0.7f, 0.5f, buyButtonLayout), string.Empty, style: "RepairBuyButton") { ClickSound = GUISoundType.HireRepairClick, Enabled = PlayerWallet.Balance >= price && !isDisabled, OnClicked = onPressed };
|
||||
contentLayout.Recalculate();
|
||||
buyButtonLayout.Recalculate();
|
||||
|
||||
if (disableElement)
|
||||
{
|
||||
frameChild.Enabled = AvailableMoney >= price && !isDisabled;
|
||||
frameChild.Enabled = PlayerWallet.Balance >= price && !isDisabled;
|
||||
}
|
||||
|
||||
if (!HasPermission)
|
||||
@@ -610,9 +611,9 @@ namespace Barotrauma
|
||||
|
||||
Dictionary<UpgradeCategory, List<UpgradePrefab>> upgrades = new Dictionary<UpgradeCategory, List<UpgradePrefab>>();
|
||||
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories)
|
||||
foreach (UpgradeCategory category in UpgradeCategory.Categories.OrderBy(c => c.Name))
|
||||
{
|
||||
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs)
|
||||
foreach (UpgradePrefab prefab in UpgradePrefab.Prefabs.OrderBy(p => p.Name))
|
||||
{
|
||||
if (prefab.UpgradeCategories.Contains(category))
|
||||
{
|
||||
@@ -963,12 +964,12 @@ namespace Barotrauma
|
||||
buttonStyle: isPurchased ? "WeaponInstallButton" : "StoreAddToCrateButton"));
|
||||
|
||||
if (!(frames.Last().FindChild(c => c is GUIButton, recursive: true) is GUIButton buyButton)) { continue; }
|
||||
if (Campaign.Money >= price)
|
||||
if (PlayerWallet.CanAfford(price))
|
||||
{
|
||||
buyButton.Enabled = true;
|
||||
buyButton.OnClicked += (button, o) =>
|
||||
{
|
||||
LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
|
||||
LocalizedString promptBody = TextManager.GetWithVariables(isPurchased ? "upgrades.itemswappromptbody" : "upgrades.purchaseitemswappromptbody",
|
||||
("[itemtoinstall]", replacement.Name),
|
||||
("[amount]", (replacement.SwappableItem.GetPrice(Campaign?.Map?.CurrentLocation) * linkedItems.Count).ToString()));
|
||||
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
|
||||
@@ -1183,7 +1184,7 @@ namespace Barotrauma
|
||||
|
||||
buyButton.OnClicked += (button, o) =>
|
||||
{
|
||||
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
|
||||
LocalizedString promptBody = TextManager.GetWithVariables("Upgrades.PurchasePromptBody",
|
||||
("[upgradename]", prefab.Name),
|
||||
("[amount]", prefab.Price.GetBuyprice(Campaign.UpgradeManager.GetUpgradeLevel(prefab, category), Campaign.Map?.CurrentLocation).ToString()));
|
||||
currectConfirmation = EventEditorScreen.AskForConfirmation(TextManager.Get("Upgrades.PurchasePromptTitle"), promptBody, () =>
|
||||
@@ -1334,16 +1335,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (GUI.MouseOn == frame)
|
||||
{
|
||||
if (HoveredItem != item) { CreateItemTooltip(item); }
|
||||
HoveredItem = item;
|
||||
if (HoveredEntity != item) { CreateItemTooltip(item); }
|
||||
HoveredEntity = item;
|
||||
if (PlayerInput.PrimaryMouseButtonClicked() && selectedUpgradeTab == UpgradeTab.Upgrade && currentStoreLayout != null)
|
||||
{
|
||||
if (customizeTabOpen)
|
||||
{
|
||||
if (selectedUpgradeCategoryLayout != null)
|
||||
{
|
||||
var linkedItems = HoveredItem is Item ? Campaign.UpgradeManager.GetLinkedItemsToSwap((Item)HoveredItem) : new List<Item>();
|
||||
if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData as Item == HoveredItem || linkedItems.Contains((Item)c.UserData), recursive: true) is GUIButton itemElement)
|
||||
var linkedItems = HoveredEntity is Item hoveredItem ? Campaign.UpgradeManager.GetLinkedItemsToSwap(hoveredItem) : new List<Item>();
|
||||
if (selectedUpgradeCategoryLayout.FindChild(c => c.UserData is Item item && (item == HoveredEntity || linkedItems.Contains(item)), recursive: true) is GUIButton itemElement)
|
||||
{
|
||||
if (!itemElement.Selected) { itemElement.OnClicked(itemElement, itemElement.UserData); }
|
||||
(itemElement.Parent?.Parent?.Parent as GUIListBox)?.ScrollToElement(itemElement);
|
||||
@@ -1370,8 +1371,8 @@ namespace Barotrauma
|
||||
// use pnpoly algorithm to detect if our mouse is within any of the hull polygons
|
||||
if (subHullVertices.Any(hullVertex => ToolBox.PointIntersectsWithPolygon(PlayerInput.MousePosition, hullVertex)))
|
||||
{
|
||||
if (HoveredItem != firstStructure && !(firstStructure is null)) { CreateItemTooltip(firstStructure); }
|
||||
HoveredItem = firstStructure;
|
||||
if (HoveredEntity != firstStructure && !(firstStructure is null)) { CreateItemTooltip(firstStructure); }
|
||||
HoveredEntity = firstStructure;
|
||||
isMouseOnStructure = true;
|
||||
GUI.MouseCursor = CursorState.Hand;
|
||||
|
||||
@@ -1382,7 +1383,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (!isMouseOnStructure) { HoveredItem = null; }
|
||||
if (!isMouseOnStructure) { HoveredEntity = null; }
|
||||
}
|
||||
|
||||
// flip the tooltip if it is outside of the screen
|
||||
@@ -1582,12 +1583,12 @@ namespace Barotrauma
|
||||
priceLabel.Text = TextManager.Get("Upgrade.MaxedUpgrade");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GUIButton button = buttonParent.GetChild<GUIButton>();
|
||||
if (button != null)
|
||||
{
|
||||
button.Enabled = currentLevel < prefab.MaxLevel;
|
||||
if (WaitForServerUpdate || !campaign.AllowedToManageCampaign() || price > campaign.Money)
|
||||
if (WaitForServerUpdate || !campaign.AllowedToManageCampaign() || !campaign.Wallet.CanAfford(price))
|
||||
{
|
||||
button.Enabled = false;
|
||||
}
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Barotrauma
|
||||
}
|
||||
// Exchange money
|
||||
Location.StoreCurrentBalance -= itemValue;
|
||||
campaign.Money += itemValue;
|
||||
campaign.Wallet.TryDeduct(itemValue);
|
||||
GameAnalyticsManager.AddMoneyGainedEvent(itemValue, GameAnalyticsManager.MoneySource.Store, item.ItemPrefab.Identifier.Value);
|
||||
|
||||
// Remove from the sell crate
|
||||
|
||||
@@ -236,7 +236,7 @@ namespace Barotrauma
|
||||
|
||||
if (crewManager != null)
|
||||
{
|
||||
Order order = new Order(orderPrefab, Identifier.Empty, CharacterInfo.HighestManualOrderPriority, Order.OrderType.Current, null, null, orderGiver: Character.Controlled);
|
||||
Order order = orderPrefab.CreateInstance(OrderPrefab.OrderTargetType.Entity, orderGiver: Character.Controlled);
|
||||
crewManager.SetCharacterOrder(null, order);
|
||||
if (crewManager.IsSinglePlayer) { HumanAIController.ReportProblem(Character.Controlled, order); }
|
||||
}
|
||||
@@ -290,16 +290,6 @@ namespace Barotrauma
|
||||
return crewArea.Rect;
|
||||
}
|
||||
|
||||
public IEnumerable<Character> GetCharacters()
|
||||
{
|
||||
return characters;
|
||||
}
|
||||
|
||||
public IEnumerable<CharacterInfo> GetCharacterInfos()
|
||||
{
|
||||
return characterInfos;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the character from the crew (and crew menus).
|
||||
/// </summary>
|
||||
@@ -708,7 +698,8 @@ namespace Barotrauma
|
||||
List<Character> availableSpeakers = Character.CharacterList.FindAll(c =>
|
||||
c.AIController is HumanAIController &&
|
||||
!c.IsDead &&
|
||||
c.SpeechImpediment <= 100.0f);
|
||||
c.SpeechImpediment <= 100.0f &&
|
||||
c.CharacterHealth.GetAllAfflictions(a => a is AfflictionHusk huskInfection && huskInfection.Prefab is AfflictionPrefabHusk { CauseSpeechImpediment: true }).None());
|
||||
pendingConversationLines.AddRange(NPCConversation.CreateRandom(availableSpeakers));
|
||||
}
|
||||
|
||||
@@ -837,7 +828,7 @@ namespace Barotrauma
|
||||
var orderGiver = order?.OrderGiver;
|
||||
if (IsSinglePlayer)
|
||||
{
|
||||
character.SetOrder(order, speak: orderGiver != character);
|
||||
character.SetOrder(order, isNewOrder, speak: orderGiver != character);
|
||||
string message = order?.GetChatMessage(character.Name, orderGiver?.CurrentHull?.DisplayName?.Value, givingOrderToSelf: character == orderGiver, orderOption: order?.Option ?? Identifier.Empty, isNewOrder: isNewOrder);
|
||||
orderGiver?.Speak(message);
|
||||
}
|
||||
@@ -2437,26 +2428,26 @@ namespace Barotrauma
|
||||
optionNodes.Add(new OptionNode(node, Keys.D0 + hotkey % 10));
|
||||
}
|
||||
|
||||
/// <remarks>
|
||||
/// The order giver doesn't need to be set for the Order instances as it will be set when the node button is clicked.
|
||||
/// </remarks>
|
||||
private void CreateShortcutNodes()
|
||||
{
|
||||
bool HasAppropriateJobId(Character c, Identifier jobId) => c.Info?.Job != null && c.Info.Job.Prefab.AppropriateOrders.Contains(jobId);
|
||||
bool HasAppropriateJob(Character c, string jobId) => HasAppropriateJobId(c, jobId.ToIdentifier());
|
||||
|
||||
var sub = GetTargetSubmarine();
|
||||
if (sub == null) { return; }
|
||||
if (!(GetTargetSubmarine() is { } sub)) { return; }
|
||||
shortcutNodes.Clear();
|
||||
if (CanFitMoreNodes() && sub.GetItems(false).Find(i => i.HasTag("reactor") && i.IsPlayerTeamInteractable)?.GetComponent<Reactor>() is Reactor reactor)
|
||||
var subItems = sub.GetItems(false);
|
||||
if (CanFitMoreNodes() && subItems.Find(i => i.HasTag("reactor") && i.IsPlayerTeamInteractable)?.GetComponent<Reactor>() is Reactor reactor)
|
||||
{
|
||||
float reactorOutput = -reactor.CurrPowerConsumption;
|
||||
// If player is not an engineer AND the reactor is not powered up AND nobody is using the reactor
|
||||
// ---> Create shortcut node for "Operate Reactor" order's "Power Up" option
|
||||
// --> Create shortcut node for "Operate Reactor" order's "Power Up" option
|
||||
if (ShouldDelegateOrder("operatereactor") && reactorOutput < float.Epsilon && characters.None(c => c.SelectedConstruction == reactor.Item))
|
||||
{
|
||||
var orderPrefab = OrderPrefab.Prefabs["operatereactor"];
|
||||
var order = new Order(orderPrefab, orderPrefab.Options[0], reactor.Item, reactor, Character.Controlled);
|
||||
var order = new Order(orderPrefab, orderPrefab.Options[0], reactor.Item, reactor);
|
||||
if (IsNonDuplicateOrder(order))
|
||||
{
|
||||
shortcutNodes.Add(CreateOrderOptionNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
AddOrderNode(order);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2464,11 +2455,11 @@ namespace Barotrauma
|
||||
// If player is not a captain AND nobody is using the nav terminal AND the nav terminal is powered up
|
||||
// --> Create shortcut node for Steer order
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("steer") && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["steer"]) &&
|
||||
sub.GetItems(false).Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
|
||||
subItems.Find(i => i.HasTag("navterminal") && i.IsPlayerTeamInteractable) is Item nav && characters.None(c => c.SelectedConstruction == nav) &&
|
||||
nav.GetComponent<Steering>() is Steering steering && steering.Voltage > steering.MinVoltage)
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering, Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
var order = new Order(OrderPrefab.Prefabs["steer"], steering.Item, steering);
|
||||
AddOrderNode(order);
|
||||
}
|
||||
// If player is not a security officer AND invaders are reported
|
||||
// --> Create shorcut node for Fight Intruders order
|
||||
@@ -2476,8 +2467,7 @@ namespace Barotrauma
|
||||
ActiveOrders.Any(o => o.Order.Identifier == "reportintruders") &&
|
||||
IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["fightintruders"]))
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["fightintruders"], null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
AddOrderNodeWithIdentifier("fightintruders");
|
||||
}
|
||||
// If player is not a mechanic AND a breach has been reported
|
||||
// --> Create shorcut node for Fix Leaks order
|
||||
@@ -2485,8 +2475,7 @@ namespace Barotrauma
|
||||
IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["fixleaks"]) &&
|
||||
ActiveOrders.Any(o => o.Order.Identifier == "reportbreach"))
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["fixleaks"], null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
AddOrderNodeWithIdentifier("fixleaks");
|
||||
}
|
||||
// --> Create shortcut nodes for the Repair orders
|
||||
if (CanFitMoreNodes() && ActiveOrders.Any(o => o.Order.Identifier == "reportbrokendevices"))
|
||||
@@ -2494,33 +2483,27 @@ namespace Barotrauma
|
||||
var reportBrokenDevices = OrderPrefab.Prefabs["reportbrokendevices"];
|
||||
// TODO: Doesn't work for player issued reports, because they don't have a target.
|
||||
bool useSpecificRepairOrder = false;
|
||||
string tag = "repairelectrical";
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder(tag) &&
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("repairelectrical") &&
|
||||
ActiveOrders.Any(o => o.Order.Prefab == reportBrokenDevices && o.Order.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "electrical")))
|
||||
{
|
||||
if (IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs[tag]))
|
||||
if (IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["repairelectrical"]))
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs[tag], null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
AddOrderNodeWithIdentifier("repairelectrical");
|
||||
}
|
||||
useSpecificRepairOrder = true;
|
||||
}
|
||||
tag = "repairmechanical";
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder(tag) &&
|
||||
if (CanFitMoreNodes() && ShouldDelegateOrder("repairmechanical") &&
|
||||
ActiveOrders.Any(o => o.Order.Prefab == reportBrokenDevices && o.Order.TargetItemComponent is Repairable r && r.requiredSkills.Any(s => s.Identifier == "mechanical")))
|
||||
{
|
||||
if (IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs[tag]))
|
||||
if (IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["repairmechanical"]))
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs[tag], null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
AddOrderNodeWithIdentifier("repairmechanical");
|
||||
}
|
||||
useSpecificRepairOrder = true;
|
||||
}
|
||||
tag = "repairsystems";
|
||||
if (!useSpecificRepairOrder && CanFitMoreNodes() && ShouldDelegateOrder(tag) && OrderPrefab.Prefabs[tag] is OrderPrefab repairOrder && IsNonDuplicateOrderPrefab(repairOrder))
|
||||
if (!useSpecificRepairOrder && CanFitMoreNodes() && ShouldDelegateOrder("repairsystems") && OrderPrefab.Prefabs["repairsystems"] is OrderPrefab repairOrder && IsNonDuplicateOrderPrefab(repairOrder))
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs[tag], null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
AddOrderNodeWithIdentifier("repairsystems");
|
||||
}
|
||||
}
|
||||
// If fire is reported
|
||||
@@ -2528,8 +2511,7 @@ namespace Barotrauma
|
||||
if (CanFitMoreNodes() && IsNonDuplicateOrderPrefab(OrderPrefab.Prefabs["extinguishfires"]) &&
|
||||
ActiveOrders.Any(o => o.Order.Identifier == "reportfire"))
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["extinguishfires"], null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
AddOrderNodeWithIdentifier("extinguishfires");
|
||||
}
|
||||
if (CanFitMoreNodes() && characterContext?.Info?.Job?.Prefab?.AppropriateOrders != null)
|
||||
{
|
||||
@@ -2541,8 +2523,8 @@ namespace Barotrauma
|
||||
{
|
||||
if (!orderPrefab.MustSetTarget || orderPrefab.GetMatchingItems(sub, true, interactableFor: characterContext ?? Character.Controlled).Any())
|
||||
{
|
||||
var order = new Order(orderPrefab, null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
var order = orderPrefab.CreateInstance(OrderPrefab.OrderTargetType.Entity);
|
||||
AddOrderNode(order);
|
||||
}
|
||||
if (!CanFitMoreNodes()) { break; }
|
||||
}
|
||||
@@ -2550,9 +2532,8 @@ namespace Barotrauma
|
||||
}
|
||||
if (CanFitMoreNodes() && characterContext != null && !characterContext.IsDismissed)
|
||||
{
|
||||
var order = new Order(OrderPrefab.Dismissal, null, orderGiver: Character.Controlled);
|
||||
shortcutNodes.Add(
|
||||
CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1));
|
||||
var order = OrderPrefab.Dismissal.CreateInstance(OrderPrefab.OrderTargetType.Entity);
|
||||
AddOrderNode(order);
|
||||
}
|
||||
shortcutNodes.RemoveAll(n => n.UserData is Order o && !IsOrderAvailable(o));
|
||||
if (shortcutNodes.Count < 1) { return; }
|
||||
@@ -2593,18 +2574,30 @@ namespace Barotrauma
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier) :
|
||||
characterContext.CurrentOrders.None(oi => oi?.Identifier == orderPrefab?.Identifier && oi.Option == option));
|
||||
}
|
||||
void AddOrderNodeWithIdentifier(string identifier)
|
||||
{
|
||||
var order = OrderPrefab.Prefabs[identifier].CreateInstance(OrderPrefab.OrderTargetType.Entity);
|
||||
AddOrderNode(order);
|
||||
}
|
||||
void AddOrderNode(Order order)
|
||||
{
|
||||
var node = order.Option.IsEmpty ?
|
||||
CreateOrderNode(shortcutNodeSize, null, Point.Zero, order, -1) :
|
||||
CreateOrderOptionNode(shortcutNodeSize, null, Point.Zero, order, -1);
|
||||
shortcutNodes.Add(node);
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateOrderNodes(OrderCategory orderCategory)
|
||||
{
|
||||
var orderPrefabs = OrderPrefab.Prefabs.Where(o => o.Category == orderCategory && !o.IsReport && IsOrderAvailable(o)).ToArray();
|
||||
var orderPrefabs = OrderPrefab.Prefabs.Where(o => o.Category == orderCategory && !o.IsReport && IsOrderAvailable(o)).OrderBy(o => o.Identifier).ToArray();
|
||||
Order order;
|
||||
bool disableNode;
|
||||
var offsets = MathUtils.GetPointsOnCircumference(Vector2.Zero, nodeDistance,
|
||||
GetCircumferencePointCount(orderPrefabs.Length), GetFirstNodeAngle(orderPrefabs.Length));
|
||||
for (int i = 0; i < orderPrefabs.Length; i++)
|
||||
{
|
||||
order = new Order(orderPrefabs[i], null, orderGiver: Character.Controlled);
|
||||
order = orderPrefabs[i].CreateInstance(OrderPrefab.OrderTargetType.Entity);
|
||||
disableNode = !CanCharacterBeHeard() ||
|
||||
(order.MustSetTarget && (order.ItemComponentType != null || order.GetTargetItems().Any() || order.RequireItems.Any()) &&
|
||||
order.GetMatchingItems(true, interactableFor: characterContext ?? Character.Controlled).None());
|
||||
@@ -2617,11 +2610,13 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Create order nodes based on the item context
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The order giver doesn't need to be set for the Order instances as it will be set when the node button is clicked.
|
||||
/// </remarks>
|
||||
private void CreateContextualOrderNodes()
|
||||
{
|
||||
if (contextualOrders.None())
|
||||
{
|
||||
string orderIdentifier;
|
||||
// Check if targeting an item or a hull
|
||||
if (itemContext != null && itemContext.IsPlayerTeamInteractable)
|
||||
{
|
||||
@@ -2636,66 +2631,62 @@ namespace Barotrauma
|
||||
{
|
||||
if (p.TargetItemsMatchItem(itemContext, option))
|
||||
{
|
||||
contextualOrders.Add(new Order(p, itemContext, targetComponent, Character.Controlled).WithOption(option));
|
||||
contextualOrders.Add(new Order(p, option, itemContext, targetComponent));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (p.TargetItemsMatchItem(itemContext) || p.TryGetTargetItemComponent(itemContext, out targetComponent))
|
||||
{
|
||||
contextualOrders.Add(p.HasOptions ?
|
||||
new Order(p, null, orderGiver: Character.Controlled) :
|
||||
new Order(p, itemContext, targetComponent, Character.Controlled));
|
||||
p.CreateInstance(OrderPrefab.OrderTargetType.Entity) :
|
||||
new Order(p, itemContext, targetComponent));
|
||||
}
|
||||
}
|
||||
// If targeting a periscope connected to a turret, show the 'operateweapons' order
|
||||
orderIdentifier = "operateweapons";
|
||||
var operateWeaponsPrefab = OrderPrefab.Prefabs[orderIdentifier];
|
||||
if (contextualOrders.None(o => o.Identifier == orderIdentifier) && itemContext.Components.Any(c => c is Controller))
|
||||
var operateWeaponsPrefab = OrderPrefab.Prefabs["operateweapons"];
|
||||
if (contextualOrders.None(o => o.Identifier == "operateweapons") && itemContext.Components.Any(c => c is Controller))
|
||||
{
|
||||
var turret = itemContext.GetConnectedComponents<Turret>().FirstOrDefault(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item)) ??
|
||||
itemContext.GetConnectedComponents<Turret>(recursive: true).FirstOrDefault(c => operateWeaponsPrefab.TargetItemsMatchItem(c.Item));
|
||||
if (turret != null)
|
||||
{
|
||||
contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret, Character.Controlled));
|
||||
contextualOrders.Add(new Order(operateWeaponsPrefab, turret.Item, turret));
|
||||
}
|
||||
}
|
||||
// If targeting a repairable item with condition below the repair threshold, show the 'repairsystems' order
|
||||
orderIdentifier = "repairsystems";
|
||||
if (contextualOrders.None(order => order.Identifier == orderIdentifier) && itemContext.Repairables.Any(r => r.IsBelowRepairThreshold))
|
||||
if (contextualOrders.None(order => order.Identifier == "repairsystems") && itemContext.Repairables.Any(r => r.IsBelowRepairThreshold))
|
||||
{
|
||||
if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical"))))
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["repairelectrical"], itemContext, targetItem: null, Character.Controlled));
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["repairelectrical"], itemContext, targetItem: null));
|
||||
}
|
||||
else if (itemContext.Repairables.Any(r => r != null && r.requiredSkills.Any(s => s != null && s.Identifier.Equals("mechanical"))))
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["repairmechanical"], itemContext, targetItem: null, Character.Controlled));
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["repairmechanical"], itemContext, targetItem: null));
|
||||
}
|
||||
else
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs[orderIdentifier], itemContext, targetItem: null, Character.Controlled));
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["repairsystems"], itemContext, targetItem: null));
|
||||
}
|
||||
}
|
||||
// Remove the 'pumpwater' order if the target pump is auto-controlled (as it will immediately overwrite the work done by the bot)
|
||||
orderIdentifier = "pumpwater";
|
||||
if (contextualOrders.FirstOrDefault(order => order.Identifier.Equals(orderIdentifier)) is Order pumpOrder &&
|
||||
if (contextualOrders.FirstOrDefault(order => order.Identifier.Equals("pumpwater")) is Order pumpOrder &&
|
||||
itemContext.Components.FirstOrDefault(c => c.GetType() == pumpOrder.ItemComponentType) is Pump pump && pump.IsAutoControlled)
|
||||
{
|
||||
contextualOrders.Remove(pumpOrder);
|
||||
}
|
||||
orderIdentifier = "cleanupitems";
|
||||
if (contextualOrders.None(info => info.Identifier.Equals(orderIdentifier)))
|
||||
if (contextualOrders.None(info => info.Identifier.Equals("cleanupitems")))
|
||||
{
|
||||
if (AIObjectiveCleanupItems.IsValidTarget(itemContext, Character.Controlled, checkInventory: false) || AIObjectiveCleanupItems.IsValidContainer(itemContext, Character.Controlled))
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs[orderIdentifier], itemContext, targetItem: null, Character.Controlled));
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["cleanupitems"], itemContext, targetItem: null));
|
||||
}
|
||||
}
|
||||
AddIgnoreOrder(itemContext);
|
||||
}
|
||||
else if (hullContext != null)
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["fixleaks"], hullContext, targetItem: null, Character.Controlled));
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["fixleaks"], hullContext, targetItem: null));
|
||||
if (wallContext != null)
|
||||
{
|
||||
AddIgnoreOrder(wallContext);
|
||||
@@ -2729,26 +2720,23 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
orderIdentifier = "wait";
|
||||
if (contextualOrders.None(order => order.Identifier.Equals(orderIdentifier)))
|
||||
if (contextualOrders.None(order => order.Identifier.Equals("wait")))
|
||||
{
|
||||
Vector2 position = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition);
|
||||
Hull hull = Hull.FindHull(position, guess: Character.Controlled?.CurrentHull);
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs[orderIdentifier], new OrderTarget(position, hull), Character.Controlled));
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs["wait"], new OrderTarget(position, hull)));
|
||||
}
|
||||
if (contextualOrders.None(order => order.Category != OrderCategory.Movement) && characters.Any(c => c != Character.Controlled))
|
||||
{
|
||||
orderIdentifier = "follow";
|
||||
if (contextualOrders.None(order => order.Identifier.Equals(orderIdentifier)))
|
||||
if (contextualOrders.None(order => order.Identifier.Equals("follow")))
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs[orderIdentifier], null, orderGiver: Character.Controlled));
|
||||
contextualOrders.Add(OrderPrefab.Prefabs["follow"].CreateInstance(OrderPrefab.OrderTargetType.Entity));
|
||||
}
|
||||
}
|
||||
// Show 'dismiss' order only when there are crew members with active orders
|
||||
orderIdentifier = "dismissed";
|
||||
if (contextualOrders.None(order => order.Identifier.Equals(orderIdentifier)) && characters.Any(c => !c.IsDismissed))
|
||||
if (contextualOrders.None(order => order.IsDismissal) && characters.Any(c => !c.IsDismissed))
|
||||
{
|
||||
contextualOrders.Add(new Order(OrderPrefab.Prefabs[orderIdentifier], null, orderGiver: Character.Controlled));
|
||||
contextualOrders.Add(OrderPrefab.Dismissal.CreateInstance(OrderPrefab.OrderTargetType.Entity));
|
||||
}
|
||||
}
|
||||
contextualOrders.RemoveAll(o => !IsOrderAvailable(o));
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
internal partial class Wallet
|
||||
{
|
||||
public bool IsOwnWallet =>
|
||||
GameMain.GameSession?.Campaign switch
|
||||
{
|
||||
null => false,
|
||||
SinglePlayerCampaign spCampaign => this == spCampaign.Bank,
|
||||
MultiPlayerCampaign mpCampaign => this == mpCampaign.PersonalWallet,
|
||||
_ => false
|
||||
};
|
||||
|
||||
partial void SettingsChanged(Option<int> balanceChanged, Option<int> rewardChanged)
|
||||
{
|
||||
CampaignMode campaign = GameMain.GameSession?.Campaign;
|
||||
WalletChangedData data = new WalletChangedData
|
||||
{
|
||||
BalanceChanged = balanceChanged,
|
||||
RewardDistributionChanged = rewardChanged,
|
||||
};
|
||||
|
||||
campaign?.OnMoneyChanged.Invoke(new WalletChangedEvent(this, data, CreateWalletInfo()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Wallet Wallet => GetWallet();
|
||||
|
||||
public override void ShowStartMessage()
|
||||
{
|
||||
foreach (Mission mission in Missions.ToList())
|
||||
@@ -310,7 +312,7 @@ namespace Barotrauma
|
||||
if (ShowCampaignUI || ForceMapUI)
|
||||
{
|
||||
campaignUIContainer?.AddToGUIUpdateList();
|
||||
if (CampaignUI?.UpgradeStore?.HoveredItem != null)
|
||||
if (CampaignUI?.UpgradeStore?.HoveredEntity != null)
|
||||
{
|
||||
if (CampaignUI.SelectedTab != InteractionType.Upgrade) { return; }
|
||||
CampaignUI?.UpgradeStore?.ItemInfoFrame.AddToGUIUpdateList(order: 1);
|
||||
|
||||
+54
-7
@@ -21,7 +21,7 @@ namespace Barotrauma
|
||||
private UInt16 pendingSaveID = 1;
|
||||
public UInt16 PendingSaveID
|
||||
{
|
||||
get
|
||||
get
|
||||
{
|
||||
return pendingSaveID;
|
||||
}
|
||||
@@ -34,6 +34,14 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Wallet PersonalWallet => Character.Controlled?.Wallet ?? Wallet.Invalid;
|
||||
public override Wallet Wallet => GetWallet();
|
||||
|
||||
public override Wallet GetWallet(Client client = null)
|
||||
{
|
||||
return PersonalWallet;
|
||||
}
|
||||
|
||||
public static void StartCampaignSetup(IEnumerable<string> saveFiles)
|
||||
{
|
||||
var parent = GameMain.NetLobbyScreen.CampaignSetupFrame;
|
||||
@@ -195,6 +203,11 @@ namespace Barotrauma
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
yield return CoroutineStatus.Failure;
|
||||
}
|
||||
|
||||
if (GameMain.Client.LateCampaignJoin)
|
||||
{
|
||||
GameMain.Client.LateCampaignJoin = false;
|
||||
@@ -618,7 +631,6 @@ namespace Barotrauma
|
||||
|
||||
bool forceMapUI = msg.ReadBoolean();
|
||||
|
||||
int money = msg.ReadInt32();
|
||||
bool purchasedHullRepairs = msg.ReadBoolean();
|
||||
bool purchasedItemRepairs = msg.ReadBoolean();
|
||||
bool purchasedLostShuttles = msg.ReadBoolean();
|
||||
@@ -812,12 +824,10 @@ namespace Barotrauma
|
||||
GameMain.NetLobbyScreen.ToggleCampaignMode(true);
|
||||
}
|
||||
|
||||
bool shouldRefresh = campaign.Money != money ||
|
||||
campaign.PurchasedHullRepairs != purchasedHullRepairs ||
|
||||
bool shouldRefresh = campaign.PurchasedHullRepairs != purchasedHullRepairs ||
|
||||
campaign.PurchasedItemRepairs != purchasedItemRepairs ||
|
||||
campaign.PurchasedLostShuttles != purchasedLostShuttles;
|
||||
|
||||
campaign.Money = money;
|
||||
campaign.PurchasedHullRepairs = purchasedHullRepairs;
|
||||
campaign.PurchasedItemRepairs = purchasedItemRepairs;
|
||||
campaign.PurchasedLostShuttles = purchasedLostShuttles;
|
||||
@@ -896,6 +906,43 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientReadMoney(IReadMessage inc)
|
||||
{
|
||||
NetWalletUpdate update = INetSerializableStruct.Read<NetWalletUpdate>(inc);
|
||||
foreach (NetWalletTransaction transaction in update.Transactions)
|
||||
{
|
||||
WalletInfo info = transaction.Info;
|
||||
switch (transaction.CharacterID)
|
||||
{
|
||||
case Some<ushort> { Value: var charID}:
|
||||
{
|
||||
Character targetCharacter = Character.CharacterList?.FirstOrDefault(c => c.ID == charID);
|
||||
if (targetCharacter is null) { break; }
|
||||
Wallet wallet = targetCharacter.Wallet;
|
||||
|
||||
wallet.Balance = info.Balance;
|
||||
wallet.RewardDistribution = info.RewardDistribution;
|
||||
TryInvokeEvent(wallet, transaction.ChangedData, info);
|
||||
break;
|
||||
}
|
||||
case None<ushort> _:
|
||||
{
|
||||
Bank.Balance = info.Balance;
|
||||
TryInvokeEvent(Bank, transaction.ChangedData, info);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TryInvokeEvent(Wallet wallet, WalletChangedData data, WalletInfo info)
|
||||
{
|
||||
if (data.BalanceChanged.IsSome() || data.RewardDistributionChanged.IsSome())
|
||||
{
|
||||
OnMoneyChanged.Invoke(new WalletChangedEvent(wallet, data, info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
//do nothing, the clients get the save files from the server
|
||||
@@ -908,10 +955,10 @@ namespace Barotrauma
|
||||
|
||||
string gamesessionDocPath = Path.Combine(SaveUtil.TempPath, "gamesession.xml");
|
||||
XDocument doc = XMLExtensions.TryLoadXml(gamesessionDocPath);
|
||||
if (doc == null)
|
||||
if (doc == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to load the state of a multiplayer campaign. Could not open the file \"{gamesessionDocPath}\".");
|
||||
return;
|
||||
return;
|
||||
}
|
||||
Load(doc.Root.Element("MultiPlayerCampaign"));
|
||||
GameMain.GameSession.OwnedSubmarines = SaveUtil.LoadOwnedSubmarines(doc, out SubmarineInfo selectedSub);
|
||||
|
||||
+15
-3
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -108,6 +109,9 @@ namespace Barotrauma
|
||||
case "pets":
|
||||
petsElement = subElement;
|
||||
break;
|
||||
case Wallet.LowerCaseSaveElementName:
|
||||
Bank = new Wallet(subElement);
|
||||
break;
|
||||
case "stats":
|
||||
LoadStats(subElement);
|
||||
break;
|
||||
@@ -121,7 +125,15 @@ namespace Barotrauma
|
||||
|
||||
InitUI();
|
||||
|
||||
Money = element.GetAttributeInt("money", 0);
|
||||
int oldMoney = element.GetAttributeInt("money", 0);
|
||||
if (oldMoney > 0)
|
||||
{
|
||||
Bank = new Wallet
|
||||
{
|
||||
Balance = oldMoney
|
||||
};
|
||||
}
|
||||
|
||||
PurchasedLostShuttles = element.GetAttributeBool("purchasedlostshuttles", false);
|
||||
PurchasedHullRepairs = element.GetAttributeBool("purchasedhullrepairs", false);
|
||||
PurchasedItemRepairs = element.GetAttributeBool("purchaseditemrepairs", false);
|
||||
@@ -729,7 +741,6 @@ namespace Barotrauma
|
||||
public override void Save(XElement element)
|
||||
{
|
||||
XElement modeElement = new XElement("SinglePlayerCampaign",
|
||||
new XAttribute("money", Money),
|
||||
new XAttribute("purchasedlostshuttles", PurchasedLostShuttles),
|
||||
new XAttribute("purchasedhullrepairs", PurchasedHullRepairs),
|
||||
new XAttribute("purchaseditemrepairs", PurchasedItemRepairs),
|
||||
@@ -756,13 +767,14 @@ namespace Barotrauma
|
||||
|
||||
petsElement = new XElement("pets");
|
||||
PetBehavior.SavePets(petsElement);
|
||||
modeElement.Add(petsElement);
|
||||
modeElement.Add(petsElement);
|
||||
|
||||
CrewManager.Save(modeElement);
|
||||
CampaignMetadata.Save(modeElement);
|
||||
Map.Save(modeElement);
|
||||
CargoManager?.SavePurchasedItems(modeElement);
|
||||
UpgradeManager?.Save(modeElement);
|
||||
modeElement.Add(Bank.Save());
|
||||
element.Add(modeElement);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Barotrauma
|
||||
|
||||
public TestGameMode(GameModePreset preset) : base(preset)
|
||||
{
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs.OrderBy(p => p.Identifier))
|
||||
{
|
||||
for (int i = 0; i < jobPrefab.InitialCount; i++)
|
||||
{
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
tabMenu.Update();
|
||||
tabMenu.Update(deltaTime);
|
||||
if ((PlayerInput.KeyHit(InputType.InfoTab) || PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) &&
|
||||
!(GUI.KeyboardDispatcher.Subscriber is GUITextBox))
|
||||
{
|
||||
|
||||
@@ -181,6 +181,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMoneyChanged(WalletChangedEvent e)
|
||||
{
|
||||
if (e.Wallet.IsOwnWallet) { OnUpdate?.Invoke(); }
|
||||
}
|
||||
|
||||
// if you have more than 5000 ping there are probably more important things to worry about but hey just in case
|
||||
private static DateTimeOffset GetTimeout() => DateTimeOffset.Now.AddSeconds(5).AddMilliseconds(GetPing());
|
||||
|
||||
@@ -241,6 +246,7 @@ namespace Barotrauma
|
||||
private void HealRequestReceived(IReadMessage inc)
|
||||
{
|
||||
NetHealRequest request = INetSerializableStruct.Read<NetHealRequest>(inc);
|
||||
|
||||
if (request.Result == HealRequestResult.Success)
|
||||
{
|
||||
HealAllPending(force: true);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
@@ -15,7 +16,6 @@ namespace Barotrauma
|
||||
|
||||
private int jobColumnWidth, characterColumnWidth, statusColumnWidth;
|
||||
|
||||
private readonly SubmarineInfo sub;
|
||||
private readonly List<Mission> selectedMissions;
|
||||
private readonly Location startLocation, endLocation;
|
||||
|
||||
@@ -30,11 +30,8 @@ namespace Barotrauma
|
||||
|
||||
public GUIComponent Frame { get; private set; }
|
||||
|
||||
|
||||
|
||||
public RoundSummary(SubmarineInfo sub, GameMode gameMode, IEnumerable<Mission> selectedMissions, Location startLocation, Location endLocation)
|
||||
public RoundSummary(GameMode gameMode, IEnumerable<Mission> selectedMissions, Location startLocation, Location endLocation)
|
||||
{
|
||||
this.sub = sub;
|
||||
this.gameMode = gameMode;
|
||||
this.selectedMissions = selectedMissions.ToList();
|
||||
this.startLocation = startLocation;
|
||||
@@ -320,6 +317,16 @@ namespace Barotrauma
|
||||
{
|
||||
LocalizedString rewardText = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", reward));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), RichString.Rich(displayedMission.GetMissionRewardText(Submarine.MainSub)));
|
||||
if (Character.Controlled is { } controlled)
|
||||
{
|
||||
var (share, percentage) = Mission.GetRewardShare(controlled.Wallet.RewardDistribution, Mission.GetSalaryEligibleCrew(), Option<int>.Some(reward));
|
||||
if (share > 0)
|
||||
{
|
||||
string shareFormatted = string.Format(CultureInfo.InvariantCulture, "{0:N0}", share);
|
||||
RichString yourShareString = RichString.Rich(TextManager.GetWithVariables("crewwallet.missionreward.get", ("[money]", $"{shareFormatted}"), ("[share]", $"{percentage}")));
|
||||
new GUITextBlock(new RectTransform(new Vector2(1.0f, 0.0f), missionTextContent.RectTransform), yourShareString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (displayedMission != missionsToDisplay.Last())
|
||||
@@ -407,7 +414,7 @@ namespace Barotrauma
|
||||
CreatePathUnlockElement(locationFrame, null, startLocation);
|
||||
}
|
||||
|
||||
foreach (Faction faction in campaignMode.Factions)
|
||||
foreach (Faction faction in campaignMode.Factions.OrderBy(f => f.Prefab.MenuOrder).ThenBy(f => f.Prefab.Name))
|
||||
{
|
||||
float initialReputation = faction.Reputation.Value;
|
||||
if (initialFactionReputations.ContainsKey(faction))
|
||||
@@ -728,7 +735,7 @@ namespace Barotrauma
|
||||
if (Math.Abs(reputationChange) > 0)
|
||||
{
|
||||
string changeText = $"{(reputationChange > 0 ? "+" : "") + reputationChange}";
|
||||
string colorStr = XMLExtensions.ColorToString(reputationChange > 0 ? GUIStyle.Green : GUIStyle.Red);
|
||||
string colorStr = XMLExtensions.ToStringHex(reputationChange > 0 ? GUIStyle.Green : GUIStyle.Red);
|
||||
var richText = RichString.Rich($"{reputationText} (‖color:{colorStr}‖{changeText}‖color:end‖)");
|
||||
new GUITextBlock(new RectTransform(new Vector2(0.5f, 1.0f), sliderHolder.RectTransform),
|
||||
richText,
|
||||
|
||||
@@ -283,9 +283,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public override void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
base.ClientRead(type, msg, sendingTime);
|
||||
base.ClientEventRead(msg, sendingTime);
|
||||
|
||||
bool open = msg.ReadBoolean();
|
||||
bool broken = msg.ReadBoolean();
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
CurrPowerConsumption = powerConsumption;
|
||||
charging = true;
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Tainted = msg.ReadBoolean();
|
||||
if (Tainted)
|
||||
|
||||
@@ -156,7 +156,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private readonly object mutex = new object();
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Health = msg.ReadRangedSingle(0, MaxHealth, 8);
|
||||
int startOffset = msg.ReadRangedInteger(-1, MaximumVines);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Diagnostics.Tracing;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -58,18 +59,23 @@ namespace Barotrauma.Items.Components
|
||||
GUI.DrawRectangle(spriteBatch, new Vector2(attachPos.X - 2, -attachPos.Y - 2), Vector2.One * 5, GUIStyle.Red, thickness: 3);
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public override bool ValidateEventData(NetEntityEvent.IData data)
|
||||
=> TryExtractEventData<EventData>(data, out _);
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!attachable || body == null) { return; }
|
||||
|
||||
var eventData = ExtractEventData<EventData>(extraData);
|
||||
|
||||
Vector2 attachPos = (Vector2)extraData[2];
|
||||
Vector2 attachPos = eventData.AttachPos;
|
||||
msg.Write(attachPos.X);
|
||||
msg.Write(attachPos.Y);
|
||||
}
|
||||
|
||||
public override void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public override void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
base.ClientRead(type, msg, sendingTime);
|
||||
base.ClientEventRead(msg, sendingTime);
|
||||
|
||||
bool readAttachData = msg.ReadBoolean();
|
||||
if (!readAttachData) { return; }
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Barotrauma.Items.Components
|
||||
ContentXElement getElementFromList(List<ContentXElement> list, int index)
|
||||
=> CharacterInfo.IsValidIndex(index, list)
|
||||
? list[index]
|
||||
: characterInfo.GetRandomElement(list);
|
||||
: null;
|
||||
|
||||
var disguisedHairElement = getElementFromList(disguisedHairs, disguisedHairIndex);
|
||||
var disguisedBeardElement = getElementFromList(disguisedBeards, disguisedBeardIndex);
|
||||
|
||||
@@ -566,14 +566,14 @@ namespace Barotrauma.Items.Components
|
||||
protected virtual void CreateGUI() { }
|
||||
|
||||
//Starts a coroutine that will read the correct state of the component from the NetBuffer when correctionTimer reaches zero.
|
||||
protected void StartDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync = false)
|
||||
protected void StartDelayedCorrection(IReadMessage buffer, float sendingTime, bool waitForMidRoundSync = false)
|
||||
{
|
||||
if (delayedCorrectionCoroutine != null) CoroutineManager.StopCoroutines(delayedCorrectionCoroutine);
|
||||
if (delayedCorrectionCoroutine != null) { CoroutineManager.StopCoroutines(delayedCorrectionCoroutine); }
|
||||
|
||||
delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(type, buffer, sendingTime, waitForMidRoundSync));
|
||||
delayedCorrectionCoroutine = CoroutineManager.StartCoroutine(DoDelayedCorrection(buffer, sendingTime, waitForMidRoundSync));
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> DoDelayedCorrection(ServerNetObject type, IReadMessage buffer, float sendingTime, bool waitForMidRoundSync)
|
||||
private IEnumerable<CoroutineStatus> DoDelayedCorrection(IReadMessage buffer, float sendingTime, bool waitForMidRoundSync)
|
||||
{
|
||||
while (GameMain.Client != null &&
|
||||
(correctionTimer > 0.0f || (waitForMidRoundSync && GameMain.Client.MidRoundSyncing)))
|
||||
@@ -587,7 +587,7 @@ namespace Barotrauma.Items.Components
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
((IServerSerializable)this).ClientRead(type, buffer, sendingTime);
|
||||
((IServerSerializable)this).ClientEventRead(buffer, sendingTime);
|
||||
|
||||
correctionTimer = 0.0f;
|
||||
delayedCorrectionCoroutine = null;
|
||||
|
||||
@@ -282,7 +282,7 @@ namespace Barotrauma.Items.Components
|
||||
textBlock.DrawManually(spriteBatch);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Text = msg.ReadString();
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class LevelResource : ItemComponent, IServerSerializable
|
||||
{
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
deattachTimer = msg.ReadSingle();
|
||||
if (deattachTimer >= DeattachDuration)
|
||||
|
||||
@@ -56,8 +56,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Light.Position = item.DrawPosition;
|
||||
if (item.Submarine != null) { Light.Position -= item.Submarine.DrawPosition; }
|
||||
Vector2 pos = item.DrawPosition;
|
||||
if (item.Submarine != null) { pos -= item.Submarine.DrawPosition; }
|
||||
Light.Position = pos;
|
||||
}
|
||||
PhysicsBody body = Light.ParentBody;
|
||||
if (body != null)
|
||||
@@ -120,7 +121,7 @@ namespace Barotrauma.Items.Components
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
IsActive = msg.ReadBoolean();
|
||||
lastReceivedState = IsActive;
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
State = msg.ReadBoolean();
|
||||
ushort userID = msg.ReadUInt16();
|
||||
|
||||
@@ -251,12 +251,12 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(pendingState);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
ushort userID = msg.ReadUInt16();
|
||||
Character user = userID == Entity.NullEntityID ? null : Entity.FindEntityByID(userID) as Character;
|
||||
|
||||
@@ -156,17 +156,17 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
//targetForce can only be adjusted at 10% intervals -> no need for more accuracy than this
|
||||
msg.WriteRangedInteger((int)(targetForce / 10.0f), -10, 10);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
StartDelayedCorrection(type, msg.ExtractBits(5 + 16), sendingTime);
|
||||
StartDelayedCorrection(msg.ExtractBits(5 + 16), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -705,13 +705,13 @@ namespace Barotrauma.Items.Components
|
||||
requiredTimeBlock.Text = ToolBox.SecondsToReadableTime(timeUntilReady > 0.0f ? timeUntilReady : requiredTime);
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
uint recipeHash = pendingFabricatedItem?.RecipeHash ?? 0;
|
||||
msg.Write(recipeHash);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
FabricatorState newState = (FabricatorState)msg.ReadByte();
|
||||
float newTimeUntilReady = msg.ReadSingle();
|
||||
|
||||
@@ -216,14 +216,14 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
//flowpercentage can only be adjusted at 10% intervals -> no need for more accuracy than this
|
||||
msg.WriteRangedInteger((int)(flowPercentage / 10.0f), -10, 10);
|
||||
msg.Write(IsActive);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
int msgStartPos = msg.BitPosition;
|
||||
|
||||
@@ -244,7 +244,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
int msgLength = msg.BitPosition - msgStartPos;
|
||||
msg.BitPosition = msgStartPos;
|
||||
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
|
||||
StartDelayedCorrection(msg.ExtractBits(msgLength), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -608,9 +608,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Vector2 pointerPos = pos - new Vector2(0, 30) * scale;
|
||||
|
||||
float scaleMultiplier = 0.95f;
|
||||
if (optimalRangeNormalized.X == optimalRangeNormalized.Y)
|
||||
{
|
||||
sectorSprite.Draw(spriteBatch, pointerPos, GUIStyle.Red, MathHelper.PiOver2, scale);
|
||||
sectorSprite.Draw(spriteBatch, pointerPos, GUIStyle.Red, MathHelper.PiOver2, scale * scaleMultiplier);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -619,7 +620,6 @@ namespace Barotrauma.Items.Components
|
||||
spriteBatch.GraphicsDevice.ScissorRectangle = new Rectangle(0, 0, GameMain.GraphicsWidth, (int)(pointerPos.Y + (meterSprite.size.Y - meterSprite.Origin.Y) * scale) - 3);
|
||||
spriteBatch.Begin(SpriteSortMode.Deferred, rasterizerState: GameMain.ScissorTestEnable);
|
||||
|
||||
float scaleMultiplier = 0.95f;
|
||||
sectorSprite.Draw(spriteBatch, pointerPos, optimalRangeColor, MathHelper.PiOver2 + (allowedSectorRad.X + allowedSectorRad.Y) / 2.0f, scale * scaleMultiplier);
|
||||
sectorSprite.Draw(spriteBatch, pointerPos, offRangeColor, optimalSectorRad.X, scale * scaleMultiplier);
|
||||
sectorSprite.Draw(spriteBatch, pointerPos, warningColor, allowedSectorRad.X, scale * scaleMultiplier);
|
||||
@@ -711,7 +711,7 @@ namespace Barotrauma.Items.Components
|
||||
tempRangeIndicator?.Remove();
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(autoTemp);
|
||||
msg.Write(PowerOn);
|
||||
@@ -721,11 +721,11 @@ namespace Barotrauma.Items.Components
|
||||
correctionTimer = CorrectionDelay;
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
StartDelayedCorrection(type, msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime);
|
||||
StartDelayedCorrection(msg.ExtractBits(1 + 1 + 8 + 8 + 8 + 8), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -766,7 +766,7 @@ namespace Barotrauma.Items.Components
|
||||
if (activePingsCount == 0) { disruptedDirections.Clear(); }
|
||||
foreach (AITarget t in AITarget.List)
|
||||
{
|
||||
if (t.Entity is Character c && c.Params.HideInSonar) { continue; }
|
||||
if (t.Entity is Character c && !c.IsUnconscious && c.Params.HideInSonar) { continue; }
|
||||
if (t.SoundRange <= 0.0f || float.IsNaN(t.SoundRange) || float.IsInfinity(t.SoundRange)) { continue; }
|
||||
|
||||
float distSqr = Vector2.DistanceSquared(t.WorldPosition, transducerCenter);
|
||||
@@ -1264,7 +1264,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
foreach (AITarget aiTarget in AITarget.List)
|
||||
{
|
||||
float disruption = aiTarget.Entity is Character c ? c.Params.SonarDisruption : aiTarget.SonarDisruption;
|
||||
float disruption = aiTarget.Entity is Character c && !c.IsUnconscious ? c.Params.SonarDisruption : aiTarget.SonarDisruption;
|
||||
if (disruption <= 0.0f || aiTarget.InDetectable) { continue; }
|
||||
float distSqr = Vector2.DistanceSquared(aiTarget.WorldPosition, pingSource);
|
||||
if (distSqr > worldPingRadiusSqr) { continue; }
|
||||
@@ -1413,7 +1413,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.AnimController.CurrentHull != null || !c.Enabled) { continue; }
|
||||
if (c.Params.HideInSonar) { continue; }
|
||||
if (!c.IsUnconscious && c.Params.HideInSonar) { continue; }
|
||||
if (DetectSubmarineWalls && c.AnimController.CurrentHull == null && item.CurrentHull != null) { continue; }
|
||||
|
||||
if (c.AnimController.SimplePhysicsEnabled)
|
||||
@@ -1742,7 +1742,7 @@ namespace Barotrauma.Items.Components
|
||||
MineralClusters = null;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(currentMode == Mode.Active);
|
||||
if (currentMode == Mode.Active)
|
||||
@@ -1758,7 +1758,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
int msgStartPos = msg.BitPosition;
|
||||
|
||||
@@ -1782,7 +1782,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
int msgLength = msg.BitPosition - msgStartPos;
|
||||
msg.BitPosition = msgStartPos;
|
||||
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
|
||||
StartDelayedCorrection(msg.ExtractBits(msgLength), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -822,7 +822,7 @@ namespace Barotrauma.Items.Components
|
||||
Connection dockingConnection = item.Connections?.FirstOrDefault(c => c.Name == "toggle_docking");
|
||||
if (dockingConnection != null)
|
||||
{
|
||||
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection, ignoreInactiveRelays: true);
|
||||
connectedPorts = item.GetConnectedComponentsRecursive<DockingPort>(dockingConnection, ignoreInactiveRelays: true, allowTraversingBackwards: false);
|
||||
}
|
||||
checkConnectedPortsTimer = CheckConnectedPortsInterval;
|
||||
}
|
||||
@@ -894,7 +894,7 @@ namespace Barotrauma.Items.Components
|
||||
pathFinder = null;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(AutoPilot);
|
||||
msg.Write(dockingNetworkMessagePending);
|
||||
@@ -921,7 +921,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
int msgStartPos = msg.BitPosition;
|
||||
|
||||
@@ -962,7 +962,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
int msgLength = (int)(msg.BitPosition - msgStartPos);
|
||||
msg.BitPosition = msgStartPos;
|
||||
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime);
|
||||
StartDelayedCorrection(msg.ExtractBits(msgLength), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -163,16 +163,16 @@ namespace Barotrauma.Items.Components
|
||||
indicatorSize * item.Scale, Color.Black, depth: item.SpriteDepth - 0.000015f);
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData)
|
||||
{
|
||||
msg.WriteRangedInteger((int)(rechargeSpeed / MaxRechargeSpeed * 10), 0, 10);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
if (correctionTimer > 0.0f)
|
||||
{
|
||||
StartDelayedCorrection(type, msg.ExtractBits(4 + 8), sendingTime);
|
||||
StartDelayedCorrection(msg.ExtractBits(4 + 8), sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
private readonly List<ParticleEmitter> particleEmitters = new List<ParticleEmitter>();
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool launch = msg.ReadBoolean();
|
||||
if (launch)
|
||||
|
||||
@@ -427,7 +427,7 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateClientEvent(this);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
deteriorationTimer = msg.ReadSingle();
|
||||
deteriorateAlwaysResetTimer = msg.ReadSingle();
|
||||
@@ -446,7 +446,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.WriteRangedInteger((int)requestStartFixAction, 0, 2);
|
||||
msg.Write(qteSuccess);
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
snapped = msg.ReadBoolean();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool wasScanCompletedPreviously = IsScanCompleted;
|
||||
scanTimer = msg.ReadSingle();
|
||||
|
||||
@@ -76,13 +76,14 @@ namespace Barotrauma.Items.Components
|
||||
UserData = i,
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
int signalIndex = (int)userData;
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
SendSignal((int)userData, Character.Controlled);
|
||||
SendSignal(signalIndex, Character.Controlled);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this, new object[] { userData });
|
||||
item.CreateClientEvent(this, new EventData(signalIndex));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -104,12 +105,12 @@ namespace Barotrauma.Items.Components
|
||||
Container.Inventory.RectTransform = containerHolder.RectTransform;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
Write(msg, extraData);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
SendSignal(msg.ReadRangedInteger(0, Signals.Length - 1), sender: null, isServerMessage: true);
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
if (GameMain.Client.MidRoundSyncing)
|
||||
{
|
||||
@@ -161,7 +161,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
int msgLength = (int)(msg.BitPosition - msgStartPos);
|
||||
msg.BitPosition = (int)msgStartPos;
|
||||
StartDelayedCorrection(type, msg.ExtractBits(msgLength), sendingTime, waitForMidRoundSync: true);
|
||||
StartDelayedCorrection(msg.ExtractBits(msgLength), sendingTime, waitForMidRoundSync: true);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -137,13 +137,14 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
btn.OnClicked += (_, userdata) =>
|
||||
{
|
||||
CustomInterfaceElement btnElement = userdata as CustomInterfaceElement;;
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
ButtonClicked(userdata as CustomInterfaceElement);
|
||||
ButtonClicked(btnElement);
|
||||
}
|
||||
else
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, item.GetComponentIndex(this), userdata as CustomInterfaceElement });
|
||||
item.CreateClientEvent(this, new EventData(btnElement));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -301,7 +302,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
//extradata contains an array of buttons clicked by the player (or nothing if the player didn't click anything)
|
||||
for (int i = 0; i < customInterfaceElementList.Count; i++)
|
||||
@@ -323,12 +324,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.Write(extraData != null && extraData.Any(d => d as CustomInterfaceElement == customInterfaceElementList[i]));
|
||||
msg.Write(extraData is Item.ComponentStateEventData { ComponentData: EventData eventData } && eventData.BtnElement == customInterfaceElementList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
for (int i = 0; i < customInterfaceElementList.Count; i++)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class MemoryComponent : ItemComponent
|
||||
{
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Value = msg.ReadString();
|
||||
}
|
||||
|
||||
@@ -7,6 +7,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Terminal : ItemComponent, IClientSerializable, IServerSerializable
|
||||
{
|
||||
private readonly struct ClientEventData : IEventData
|
||||
{
|
||||
public readonly string Text;
|
||||
|
||||
public ClientEventData(string text)
|
||||
{
|
||||
Text = text;
|
||||
}
|
||||
}
|
||||
|
||||
private GUIListBox historyBox;
|
||||
private GUITextBlock fillerBlock;
|
||||
private GUITextBox inputBox;
|
||||
@@ -42,7 +52,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
item.CreateClientEvent(this, new object[] { text });
|
||||
item.CreateClientEvent(this, new ClientEventData(text));
|
||||
}
|
||||
textBox.Text = string.Empty;
|
||||
return true;
|
||||
@@ -133,17 +143,15 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (extraData is null) { return; }
|
||||
|
||||
if (extraData[2] is string str)
|
||||
if (TryExtractEventData(extraData, out ClientEventData eventData))
|
||||
{
|
||||
msg.Write(str);
|
||||
msg.Write(eventData.Text);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
SendOutput(msg.ReadString());
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Barotrauma.Items.Components
|
||||
ShapeExtensions.DrawCircle(spriteBatch, pos, range, 32, Color.Cyan * 0.5f, 3);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
Channel = msg.ReadRangedInteger(MinChannel, MaxChannel);
|
||||
}
|
||||
|
||||
@@ -11,6 +11,16 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class Wire : ItemComponent, IDrawableComponent, IServerSerializable, IClientSerializable
|
||||
{
|
||||
private readonly struct ClientEventData : IEventData
|
||||
{
|
||||
public readonly int NodeCount;
|
||||
|
||||
public ClientEventData(int nodeCount)
|
||||
{
|
||||
NodeCount = nodeCount;
|
||||
}
|
||||
}
|
||||
|
||||
public static Color higlightColor = Color.LightGreen;
|
||||
public static Color editorHighlightColor = Color.Yellow;
|
||||
public static Color editorSelectedColor = Color.Red;
|
||||
@@ -555,7 +565,7 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
int eventIndex = msg.ReadRangedInteger(0, (int)Math.Ceiling(MaxNodeCount / (float)MaxNodesPerNetworkEvent));
|
||||
int nodeCount = msg.ReadRangedInteger(0, MaxNodesPerNetworkEvent);
|
||||
@@ -586,9 +596,13 @@ namespace Barotrauma.Items.Components
|
||||
(item.ParentInventory is CharacterInventory characterInventory && ((characterInventory.Owner as Character)?.HasEquippedItem(item) ?? false));
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public override bool ValidateEventData(NetEntityEvent.IData data)
|
||||
=> TryExtractEventData<ClientEventData>(data, out _);
|
||||
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
int nodeCount = (int)extraData[2];
|
||||
var eventData = ExtractEventData<ClientEventData>(extraData);
|
||||
int nodeCount = eventData.NodeCount;
|
||||
msg.Write((byte)nodeCount);
|
||||
if (nodeCount > 0)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class TriggerComponent : ItemComponent, IServerSerializable
|
||||
{
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
CurrentForceFluctuation = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
}
|
||||
|
||||
@@ -44,7 +44,9 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private readonly Dictionary<string, Widget> widgets = new Dictionary<string, Widget>();
|
||||
private float prevAngle;
|
||||
|
||||
|
||||
private float currentBarrelSpin = 0f;
|
||||
|
||||
private bool flashLowPower;
|
||||
private bool flashNoAmmo, flashLoaderBroken;
|
||||
private float flashTimer;
|
||||
@@ -716,7 +718,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
UInt16 projectileID = msg.ReadUInt16();
|
||||
float newTargetRotation = msg.ReadRangedSingle(minRotation, maxRotation, 16);
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool isDocked = msg.ReadBoolean();
|
||||
|
||||
|
||||
@@ -1598,7 +1598,7 @@ namespace Barotrauma
|
||||
int maxStackSize = Math.Min(containedItem.Prefab.MaxStackSize, itemContainer.GetMaxStackSize(0));
|
||||
if (maxStackSize > 1 || containedItem.Prefab.HideConditionBar)
|
||||
{
|
||||
containedState = itemContainer.Inventory.slots[0].ItemCount / (float)maxStackSize;
|
||||
containedState = itemContainer.Inventory.slots[0].Items.Count / (float)maxStackSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1702,7 +1702,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (maxStackSize > 1 && inventory != null)
|
||||
{
|
||||
int itemCount = slot.MouseOn() ? inventory.slots[slotIndex].ItemCount : inventory.slots[slotIndex].Items.Where(it => !DraggingItems.Contains(it)).Count();
|
||||
int itemCount = slot.MouseOn() ? inventory.slots[slotIndex].Items.Count : inventory.slots[slotIndex].Items.Where(it => !DraggingItems.Contains(it)).Count();
|
||||
if (item.IsFullCondition || MathUtils.NearlyEqual(item.Condition, 0.0f) || itemCount > 1)
|
||||
{
|
||||
Vector2 stackCountPos = new Vector2(rect.Right, rect.Bottom);
|
||||
@@ -1795,20 +1795,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
UInt16 lastEventID = msg.ReadUInt16();
|
||||
byte slotCount = msg.ReadByte();
|
||||
receivedItemIDs = new List<ushort>[slotCount];
|
||||
for (int i = 0; i < slotCount; i++)
|
||||
{
|
||||
receivedItemIDs[i] = new List<ushort>();
|
||||
int itemCount = msg.ReadRangedInteger(0, MaxStackSize);
|
||||
for (int j = 0; j < itemCount; j++)
|
||||
{
|
||||
receivedItemIDs[i].Add(msg.ReadUInt16());
|
||||
}
|
||||
}
|
||||
SharedRead(msg, out receivedItemIDs);
|
||||
|
||||
//delay applying the new state if less than 1 second has passed since this client last sent a state to the server
|
||||
//prevents the inventory from briefly reverting to an old state if items are moved around in quick succession
|
||||
@@ -1895,7 +1885,7 @@ namespace Barotrauma
|
||||
receivedItemIDs = null;
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
SharedWrite(msg, extraData);
|
||||
syncItemsDelay = 1.0f;
|
||||
|
||||
@@ -6,6 +6,7 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.MapCreatures.Behavior;
|
||||
@@ -1262,25 +1263,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
if (type == ServerNetObject.ENTITY_POSITION)
|
||||
{
|
||||
ClientReadPosition(type, msg, sendingTime);
|
||||
return;
|
||||
}
|
||||
|
||||
NetEntityEvent.Type eventType =
|
||||
(NetEntityEvent.Type)msg.ReadRangedInteger(0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
|
||||
EventType eventType =
|
||||
(EventType)msg.ReadRangedInteger((int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case NetEntityEvent.Type.ComponentState:
|
||||
case EventType.ComponentState:
|
||||
{
|
||||
int componentIndex = msg.ReadRangedInteger(0, components.Count - 1);
|
||||
if (components[componentIndex] is IServerSerializable serverSerializable)
|
||||
{
|
||||
serverSerializable.ClientRead(type, msg, sendingTime);
|
||||
serverSerializable.ClientEventRead(msg, sendingTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1288,12 +1283,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetEntityEvent.Type.InventoryState:
|
||||
case EventType.InventoryState:
|
||||
{
|
||||
int containerIndex = msg.ReadRangedInteger(0, components.Count - 1);
|
||||
if (components[containerIndex] is ItemContainer container)
|
||||
{
|
||||
container.Inventory.ClientRead(type, msg, sendingTime);
|
||||
container.Inventory.ClientEventRead(msg, sendingTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1301,7 +1296,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetEntityEvent.Type.Status:
|
||||
case EventType.Status:
|
||||
float prevCondition = condition;
|
||||
condition = msg.ReadSingle();
|
||||
if (prevCondition > 0.0f && condition <= 0.0f)
|
||||
@@ -1314,10 +1309,10 @@ namespace Barotrauma
|
||||
}
|
||||
SetActiveSprite();
|
||||
break;
|
||||
case NetEntityEvent.Type.AssignCampaignInteraction:
|
||||
case EventType.AssignCampaignInteraction:
|
||||
CampaignInteractionType = (CampaignMode.InteractionType)msg.ReadByte();
|
||||
break;
|
||||
case NetEntityEvent.Type.ApplyStatusEffect:
|
||||
case EventType.ApplyStatusEffect:
|
||||
{
|
||||
ActionType actionType = (ActionType)msg.ReadRangedInteger(0, Enum.GetValues(typeof(ActionType)).Length - 1);
|
||||
byte componentIndex = msg.ReadByte();
|
||||
@@ -1347,10 +1342,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NetEntityEvent.Type.ChangeProperty:
|
||||
case EventType.ChangeProperty:
|
||||
ReadPropertyChange(msg, false);
|
||||
break;
|
||||
case NetEntityEvent.Type.Upgrade:
|
||||
case EventType.Upgrade:
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
byte level = msg.ReadByte();
|
||||
if (UpgradePrefab.Find(identifier) is { } upgradePrefab)
|
||||
@@ -1371,51 +1366,65 @@ namespace Barotrauma
|
||||
AddUpgrade(upgrade, false);
|
||||
}
|
||||
break;
|
||||
case NetEntityEvent.Type.Invalid:
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Malformed incoming item event: unsupported event type {eventType}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (extraData == null || extraData.Length == 0 || !(extraData[0] is NetEntityEvent.Type))
|
||||
Exception error(string reason)
|
||||
{
|
||||
return;
|
||||
string errorMsg = $"Failed to write a network event for the item \"{Name}\" - {reason}";
|
||||
GameAnalyticsManager.AddErrorEventOnce($"Item.ClientWrite:{Name}", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
return new Exception(errorMsg);
|
||||
}
|
||||
|
||||
if (extraData is null) { throw error("event data was null"); }
|
||||
if (!(extraData is IEventData eventData)) { throw error($"event data was of the wrong type (\"{extraData.GetType().Name}\")"); }
|
||||
|
||||
NetEntityEvent.Type eventType = (NetEntityEvent.Type)extraData[0];
|
||||
msg.WriteRangedInteger((int)eventType, 0, Enum.GetValues(typeof(NetEntityEvent.Type)).Length - 1);
|
||||
switch (eventType)
|
||||
EventType eventType = eventData.EventType;
|
||||
msg.WriteRangedInteger((int)eventType, (int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
switch (eventData)
|
||||
{
|
||||
case NetEntityEvent.Type.ComponentState:
|
||||
int componentIndex = (int)extraData[1];
|
||||
case ComponentStateEventData componentStateEventData:
|
||||
{
|
||||
var component = componentStateEventData.Component;
|
||||
if (component is null) { throw error("component was null"); }
|
||||
if (!(component is IClientSerializable clientSerializable)) { throw error($"component was not {nameof(IClientSerializable)}"); }
|
||||
int componentIndex = components.IndexOf(component);
|
||||
if (componentIndex < 0) { throw error("component did not belong to item"); }
|
||||
msg.WriteRangedInteger(componentIndex, 0, components.Count - 1);
|
||||
(components[componentIndex] as IClientSerializable).ClientWrite(msg, extraData);
|
||||
break;
|
||||
case NetEntityEvent.Type.InventoryState:
|
||||
int containerIndex = (int)extraData[1];
|
||||
clientSerializable.ClientEventWrite(msg, extraData);
|
||||
}
|
||||
break;
|
||||
case InventoryStateEventData inventoryStateEventData:
|
||||
{
|
||||
var container = inventoryStateEventData.Component;
|
||||
if (container is null) { throw error("container was null"); }
|
||||
int containerIndex = components.IndexOf(container);
|
||||
if (containerIndex < 0) { throw error("container did not belong to item"); }
|
||||
msg.WriteRangedInteger(containerIndex, 0, components.Count - 1);
|
||||
(components[containerIndex] as ItemContainer).Inventory.ClientWrite(msg, extraData);
|
||||
break;
|
||||
case NetEntityEvent.Type.Treatment:
|
||||
UInt16 characterID = (UInt16)extraData[1];
|
||||
Limb targetLimb = (Limb)extraData[2];
|
||||
container.Inventory.ClientEventWrite(msg, extraData);
|
||||
}
|
||||
break;
|
||||
case TreatmentEventData treatmentEventData:
|
||||
Character targetCharacter = treatmentEventData.TargetCharacter;
|
||||
|
||||
Character targetCharacter = FindEntityByID(characterID) as Character;
|
||||
|
||||
msg.Write(characterID);
|
||||
msg.Write(targetCharacter == null ? (byte)255 : (byte)Array.IndexOf(targetCharacter.AnimController.Limbs, targetLimb));
|
||||
msg.Write(targetCharacter.ID);
|
||||
msg.Write(treatmentEventData.LimbIndex);
|
||||
break;
|
||||
case NetEntityEvent.Type.ChangeProperty:
|
||||
WritePropertyChange(msg, extraData, true);
|
||||
case ChangePropertyEventData changePropertyEventData:
|
||||
WritePropertyChange(msg, changePropertyEventData, inGameEditableOnly: true);
|
||||
editingHUDRefreshTimer = 1.0f;
|
||||
break;
|
||||
case NetEntityEvent.Type.Combine:
|
||||
UInt16 combineTargetID = (UInt16)extraData[1];
|
||||
msg.Write(combineTargetID);
|
||||
case CombineEventData combineEventData:
|
||||
Item combineTarget = combineEventData.CombineTarget;
|
||||
msg.Write(combineTarget.ID);
|
||||
break;
|
||||
default:
|
||||
throw error($"Unsupported event type {eventData.GetType().Name}");
|
||||
}
|
||||
msg.WritePadBits();
|
||||
}
|
||||
|
||||
partial void UpdateNetPosition(float deltaTime)
|
||||
@@ -1451,7 +1460,7 @@ namespace Barotrauma
|
||||
rect.Y = (int)(displayPos.Y + rect.Height / 2.0f);
|
||||
}
|
||||
|
||||
public void ClientReadPosition(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientReadPosition(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
if (body == null)
|
||||
{
|
||||
@@ -1465,7 +1474,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
var posInfo = body.ClientRead(type, msg, sendingTime, parentDebugName: Name);
|
||||
var posInfo = body.ClientRead(msg, sendingTime, parentDebugName: Name);
|
||||
msg.ReadPadBits();
|
||||
if (posInfo != null)
|
||||
{
|
||||
@@ -1510,24 +1519,18 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public void CreateClientEvent<T>(T ic) where T : ItemComponent, IClientSerializable
|
||||
=> CreateClientEvent(ic, null);
|
||||
|
||||
public void CreateClientEvent<T>(T ic, ItemComponent.IEventData extraData) where T : ItemComponent, IClientSerializable
|
||||
{
|
||||
if (GameMain.Client == null) return;
|
||||
if (GameMain.Client == null) { return; }
|
||||
|
||||
int index = components.IndexOf(ic);
|
||||
if (index == -1) return;
|
||||
#warning TODO: this should throw an exception
|
||||
if (!components.Contains(ic)) { return; }
|
||||
|
||||
GameMain.Client.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.ComponentState, index });
|
||||
}
|
||||
|
||||
public void CreateClientEvent<T>(T ic, object[] extraData) where T : ItemComponent, IClientSerializable
|
||||
{
|
||||
if (GameMain.Client == null) return;
|
||||
|
||||
int index = components.IndexOf(ic);
|
||||
if (index == -1) return;
|
||||
|
||||
object[] data = new object[] { NetEntityEvent.Type.ComponentState, index }.Concat(extraData).ToArray();
|
||||
GameMain.Client.CreateEntityEvent(this, data);
|
||||
var eventData = new ComponentStateEventData(ic, extraData);
|
||||
if (!ic.ValidateEventData(eventData)) { throw new Exception($"Component event creation failed: {typeof(T).Name}.{nameof(ItemComponent.ValidateEventData)} returned false"); }
|
||||
GameMain.Client.CreateEntityEvent(this, eventData);
|
||||
}
|
||||
|
||||
public static Item ReadSpawnData(IReadMessage msg, bool spawn = true)
|
||||
@@ -1576,6 +1579,36 @@ namespace Barotrauma
|
||||
bool allowStealing = msg.ReadBoolean();
|
||||
int quality = msg.ReadRangedInteger(0, Items.Components.Quality.MaxQuality);
|
||||
byte teamID = msg.ReadByte();
|
||||
|
||||
bool hasIdCard = msg.ReadBoolean();
|
||||
string ownerName = "", ownerTags = "";
|
||||
int ownerBeardIndex = -1, ownerHairIndex = -1, ownerMoustacheIndex = -1, ownerFaceAttachmentIndex = -1;
|
||||
Color ownerHairColor = Microsoft.Xna.Framework.Color.White,
|
||||
ownerFacialHairColor = Microsoft.Xna.Framework.Color.White,
|
||||
ownerSkinColor = Microsoft.Xna.Framework.Color.White;
|
||||
Identifier ownerJobId = Identifier.Empty;
|
||||
Vector2 ownerSheetIndex = Vector2.Zero;
|
||||
if (hasIdCard)
|
||||
{
|
||||
ownerName = msg.ReadString();
|
||||
ownerTags = msg.ReadString();
|
||||
|
||||
ownerBeardIndex = msg.ReadByte() - 1;
|
||||
ownerHairIndex = msg.ReadByte() - 1;
|
||||
ownerMoustacheIndex = msg.ReadByte() - 1;
|
||||
ownerFaceAttachmentIndex = msg.ReadByte() - 1;
|
||||
|
||||
ownerHairColor = msg.ReadColorR8G8B8();
|
||||
ownerFacialHairColor = msg.ReadColorR8G8B8();
|
||||
ownerSkinColor = msg.ReadColorR8G8B8();
|
||||
|
||||
ownerJobId = msg.ReadIdentifier();
|
||||
|
||||
int x = msg.ReadByte();
|
||||
int y = msg.ReadByte();
|
||||
ownerSheetIndex = (x, y);
|
||||
}
|
||||
|
||||
bool tagsChanged = msg.ReadBoolean();
|
||||
string tags = "";
|
||||
if (tagsChanged)
|
||||
@@ -1587,6 +1620,7 @@ namespace Barotrauma
|
||||
tags = string.Join(',',itemPrefab.Tags.Where(t => !removedTags.Contains(t)).Concat(addedTags));
|
||||
}
|
||||
}
|
||||
|
||||
bool isNameTag = msg.ReadBoolean();
|
||||
string writtenName = "";
|
||||
if (isNameTag)
|
||||
@@ -1672,6 +1706,17 @@ namespace Barotrauma
|
||||
foreach (IdCard idCard in item.GetComponents<IdCard>())
|
||||
{
|
||||
idCard.TeamID = (CharacterTeamType)teamID;
|
||||
idCard.OwnerName = ownerName;
|
||||
idCard.OwnerTags = ownerTags;
|
||||
idCard.OwnerBeardIndex = ownerBeardIndex;
|
||||
idCard.OwnerHairIndex = ownerHairIndex;
|
||||
idCard.OwnerMoustacheIndex = ownerMoustacheIndex;
|
||||
idCard.OwnerFaceAttachmentIndex = ownerFaceAttachmentIndex;
|
||||
idCard.OwnerHairColor = ownerHairColor;
|
||||
idCard.OwnerFacialHairColor = ownerFacialHairColor;
|
||||
idCard.OwnerSkinColor = ownerSkinColor;
|
||||
idCard.OwnerJobId = ownerJobId;
|
||||
idCard.OwnerSheetIndex = ownerSheetIndex;
|
||||
}
|
||||
if (descriptionChanged) { item.Description = itemDesc; }
|
||||
if (tagsChanged) { item.Tags = tags; }
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Item
|
||||
{
|
||||
private readonly struct CombineEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.Combine;
|
||||
public readonly Item CombineTarget;
|
||||
|
||||
public CombineEventData(Item combineTarget)
|
||||
{
|
||||
CombineTarget = combineTarget;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct TreatmentEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.Treatment;
|
||||
public readonly Character TargetCharacter;
|
||||
public readonly Limb TargetLimb;
|
||||
public byte LimbIndex
|
||||
=> TargetCharacter?.AnimController?.Limbs is { } limbs
|
||||
? (byte)Array.IndexOf(limbs, TargetLimb)
|
||||
: byte.MaxValue;
|
||||
|
||||
public TreatmentEventData(Character targetCharacter, Limb targetLimb)
|
||||
{
|
||||
TargetCharacter = targetCharacter;
|
||||
TargetLimb = targetLimb;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ namespace Barotrauma
|
||||
|
||||
private float serverUpdateDelay;
|
||||
private float remoteWaterVolume, remoteOxygenPercentage;
|
||||
private List<Vector3> remoteFireSources;
|
||||
private NetworkFireSource[] remoteFireSources = null;
|
||||
private readonly List<BackgroundSection> remoteBackgroundSections = new List<BackgroundSection>();
|
||||
private readonly List<RemoteDecal> remoteDecals = new List<RemoteDecal>();
|
||||
|
||||
@@ -175,15 +175,16 @@ namespace Barotrauma
|
||||
{
|
||||
if (!pendingSectionUpdates.Any() && !pendingDecalUpdates.Any())
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this);
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new StatusEventData());
|
||||
}
|
||||
foreach (Decal decal in pendingDecalUpdates)
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { decal });
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new DecalEventData(decal));
|
||||
}
|
||||
pendingDecalUpdates.Clear();
|
||||
foreach (int pendingSectionUpdate in pendingSectionUpdates)
|
||||
{
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new object[] { pendingSectionUpdate });
|
||||
GameMain.NetworkMember?.CreateEntityEvent(this, new BackgroundSectionsEventData(pendingSectionUpdate));
|
||||
}
|
||||
pendingSectionUpdates.Clear();
|
||||
networkUpdatePending = false;
|
||||
@@ -595,132 +596,101 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientWrite(IWriteMessage msg, object[] extraData = null)
|
||||
public void ClientEventWrite(IWriteMessage msg, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (extraData == null)
|
||||
{
|
||||
msg.WriteRangedInteger(0, 0, 2);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(waterVolume / Volume, 0.0f, 1.5f), 0.0f, 1.5f, 8);
|
||||
if (!(extraData is IEventData eventData)) { throw new Exception($"Malformed hull event: expected {nameof(Hull)}.{nameof(IEventData)}"); }
|
||||
|
||||
msg.Write(FireSources.Count > 0);
|
||||
if (FireSources.Count > 0)
|
||||
{
|
||||
msg.WriteRangedInteger(Math.Min(FireSources.Count, 16), 0, 16);
|
||||
for (int i = 0; i < Math.Min(FireSources.Count, 16); i++)
|
||||
{
|
||||
var fireSource = FireSources[i];
|
||||
Vector2 normalizedPos = new Vector2(
|
||||
(fireSource.Position.X - rect.X) / rect.Width,
|
||||
(fireSource.Position.Y - (rect.Y - rect.Height)) / rect.Height);
|
||||
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(normalizedPos.X, 0.0f, 1.0f), 0.0f, 1.0f, 8);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(normalizedPos.Y, 0.0f, 1.0f), 0.0f, 1.0f, 8);
|
||||
msg.WriteRangedSingle(MathHelper.Clamp(fireSource.Size.X / rect.Width, 0.0f, 1.0f), 0, 1.0f, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (extraData[0] is Decal decal)
|
||||
msg.WriteRangedInteger((int)eventData.EventType, (int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
switch (eventData)
|
||||
{
|
||||
msg.WriteRangedInteger(1, 0, 2);
|
||||
int decalIndex = decals.IndexOf(decal);
|
||||
msg.Write((byte)(decalIndex < 0 ? 255 : decalIndex));
|
||||
msg.WriteRangedSingle(decal.BaseAlpha, 0.0f, 1.0f, 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.WriteRangedInteger(2, 0, 2);
|
||||
int sectorToUpdate = (int)extraData[0];
|
||||
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
|
||||
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
|
||||
msg.WriteRangedInteger(sectorToUpdate, 0, BackgroundSections.Count - 1);
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
msg.WriteRangedSingle(BackgroundSections[i].ColorStrength, 0.0f, 1.0f, 8);
|
||||
msg.Write(BackgroundSections[i].Color.PackedValue);
|
||||
}
|
||||
case StatusEventData statusEventData:
|
||||
SharedStatusWrite(msg);
|
||||
break;
|
||||
case BackgroundSectionsEventData backgroundSectionsEventData:
|
||||
SharedBackgroundSectionsWrite(msg, backgroundSectionsEventData);
|
||||
break;
|
||||
case DecalEventData decalEventData:
|
||||
var decal = decalEventData.Decal;
|
||||
int decalIndex = decals.IndexOf(decal);
|
||||
msg.Write((byte)(decalIndex < 0 ? 255 : decalIndex));
|
||||
msg.WriteRangedSingle(decal.BaseAlpha, 0.0f, 1.0f, 8);
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Malformed hull event: did not expect {eventData.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool isBallastFloraUpdate = message.ReadBoolean();
|
||||
if (isBallastFloraUpdate)
|
||||
EventType eventType = (EventType)msg.ReadRangedInteger((int)EventType.MinValue, (int)EventType.MaxValue);
|
||||
switch (eventType)
|
||||
{
|
||||
BallastFloraBehavior.NetworkHeader header = (BallastFloraBehavior.NetworkHeader) message.ReadByte();
|
||||
if (header == BallastFloraBehavior.NetworkHeader.Spawn)
|
||||
{
|
||||
Identifier identifier = message.ReadIdentifier();
|
||||
float x = message.ReadSingle();
|
||||
float y = message.ReadSingle();
|
||||
BallastFlora = new BallastFloraBehavior(this, BallastFloraPrefab.Find(identifier), new Vector2(x, y), firstGrowth: true)
|
||||
{
|
||||
PowerConsumptionTimer = message.ReadSingle()
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
BallastFlora?.ClientRead(message, header);
|
||||
}
|
||||
return;
|
||||
}
|
||||
remoteWaterVolume = message.ReadRangedSingle(0.0f, 1.5f, 8) * Volume;
|
||||
remoteOxygenPercentage = message.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
|
||||
bool hasFireSources = message.ReadBoolean();
|
||||
remoteFireSources = new List<Vector3>();
|
||||
if (hasFireSources)
|
||||
{
|
||||
int fireSourceCount = message.ReadRangedInteger(0, 16);
|
||||
for (int i = 0; i < fireSourceCount; i++)
|
||||
{
|
||||
remoteFireSources.Add(new Vector3(
|
||||
MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
MathHelper.Clamp(message.ReadRangedSingle(0.0f, 1.0f, 8), 0.05f, 0.95f),
|
||||
message.ReadRangedSingle(0.0f, 1.0f, 8)));
|
||||
}
|
||||
}
|
||||
|
||||
bool hasExtraData = message.ReadBoolean();
|
||||
if (hasExtraData)
|
||||
{
|
||||
bool hasSectionUpdate = message.ReadBoolean();
|
||||
if (hasSectionUpdate)
|
||||
{
|
||||
int sectorToUpdate = message.ReadRangedInteger(0, BackgroundSections.Count - 1);
|
||||
int start = sectorToUpdate * BackgroundSectionsPerNetworkEvent;
|
||||
int end = Math.Min((sectorToUpdate + 1) * BackgroundSectionsPerNetworkEvent, BackgroundSections.Count - 1);
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
float colorStrength = message.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
Color color = new Color(message.ReadUInt32());
|
||||
var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i);
|
||||
if (remoteBackgroundSection != null)
|
||||
case EventType.Status:
|
||||
remoteOxygenPercentage = msg.ReadRangedSingle(0.0f, 100.0f, 8);
|
||||
|
||||
SharedStatusRead(
|
||||
msg,
|
||||
out float newWaterVolume,
|
||||
out NetworkFireSource[] newFireSources);
|
||||
|
||||
remoteWaterVolume = newWaterVolume;
|
||||
remoteFireSources = newFireSources;
|
||||
break;
|
||||
case EventType.BackgroundSections:
|
||||
SharedBackgroundSectionRead(
|
||||
msg,
|
||||
bsnu =>
|
||||
{
|
||||
remoteBackgroundSection.SetColorStrength(colorStrength);
|
||||
remoteBackgroundSection.SetColor(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), (ushort)i, colorStrength, color, 0));
|
||||
}
|
||||
}
|
||||
int i = bsnu.SectionIndex;
|
||||
Color color = bsnu.Color;
|
||||
float colorStrength = bsnu.ColorStrength;
|
||||
|
||||
var remoteBackgroundSection = remoteBackgroundSections.Find(s => s.Index == i);
|
||||
if (remoteBackgroundSection != null)
|
||||
{
|
||||
remoteBackgroundSection.SetColorStrength(colorStrength);
|
||||
remoteBackgroundSection.SetColor(color);
|
||||
}
|
||||
else
|
||||
{
|
||||
remoteBackgroundSections.Add(new BackgroundSection(new Rectangle(0, 0, 1, 1), (ushort)i, colorStrength, color, 0));
|
||||
}
|
||||
}, out _);
|
||||
paintAmount = BackgroundSections.Sum(s => s.ColorStrength);
|
||||
}
|
||||
else
|
||||
{
|
||||
int decalCount = message.ReadRangedInteger(0, MaxDecalsPerHull);
|
||||
break;
|
||||
case EventType.Decal:
|
||||
int decalCount = msg.ReadRangedInteger(0, MaxDecalsPerHull);
|
||||
if (decalCount == 0) { decals.Clear(); }
|
||||
remoteDecals.Clear();
|
||||
for (int i = 0; i < decalCount; i++)
|
||||
{
|
||||
UInt32 decalId = message.ReadUInt32();
|
||||
int spriteIndex = message.ReadByte();
|
||||
float normalizedXPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
float normalizedYPos = message.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
float decalScale = message.ReadRangedSingle(0.0f, 2.0f, 12);
|
||||
UInt32 decalId = msg.ReadUInt32();
|
||||
int spriteIndex = msg.ReadByte();
|
||||
float normalizedXPos = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
float normalizedYPos = msg.ReadRangedSingle(0.0f, 1.0f, 8);
|
||||
float decalScale = msg.ReadRangedSingle(0.0f, 2.0f, 12);
|
||||
remoteDecals.Add(new RemoteDecal(decalId, spriteIndex, new Vector2(normalizedXPos, normalizedYPos), decalScale));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EventType.BallastFlora:
|
||||
BallastFloraBehavior.NetworkHeader header = (BallastFloraBehavior.NetworkHeader) msg.ReadByte();
|
||||
if (header == BallastFloraBehavior.NetworkHeader.Spawn)
|
||||
{
|
||||
Identifier identifier = msg.ReadIdentifier();
|
||||
float x = msg.ReadSingle();
|
||||
float y = msg.ReadSingle();
|
||||
BallastFlora = new BallastFloraBehavior(this, BallastFloraPrefab.Find(identifier), new Vector2(x, y), firstGrowth: true)
|
||||
{
|
||||
PowerConsumptionTimer = msg.ReadSingle()
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
BallastFlora?.ClientRead(msg, header);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Malformed incoming hull event: {eventType} is not a supported event type");
|
||||
}
|
||||
|
||||
if (serverUpdateDelay > 0.0f) { return; }
|
||||
@@ -756,17 +726,15 @@ namespace Barotrauma
|
||||
remoteDecals.Clear();
|
||||
}
|
||||
|
||||
if (remoteFireSources == null) { return; }
|
||||
|
||||
if (remoteFireSources is null) { return; }
|
||||
|
||||
WaterVolume = remoteWaterVolume;
|
||||
OxygenPercentage = remoteOxygenPercentage;
|
||||
|
||||
for (int i = 0; i < remoteFireSources.Count; i++)
|
||||
for (int i = 0; i < remoteFireSources.Length; i++)
|
||||
{
|
||||
Vector2 pos = new Vector2(
|
||||
rect.X + rect.Width * remoteFireSources[i].X,
|
||||
rect.Y - rect.Height + (rect.Height * remoteFireSources[i].Y));
|
||||
float size = remoteFireSources[i].Z * rect.Width;
|
||||
Vector2 pos = remoteFireSources[i].Position;
|
||||
float size = remoteFireSources[i].Size;
|
||||
|
||||
var newFire = i < FireSources.Count ?
|
||||
FireSources[i] :
|
||||
@@ -782,7 +750,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = FireSources.Count - 1; i >= remoteFireSources.Count; i--)
|
||||
for (int i = FireSources.Count - 1; i >= remoteFireSources.Length; i--)
|
||||
{
|
||||
FireSources[i].Remove();
|
||||
if (i < FireSources.Count)
|
||||
|
||||
@@ -121,34 +121,42 @@ namespace Barotrauma
|
||||
{
|
||||
renderer?.DrawForeground(spriteBatch, cam, LevelObjectManager);
|
||||
}
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool isGlobalUpdate = msg.ReadBoolean();
|
||||
if (isGlobalUpdate)
|
||||
EventType eventType = (EventType)msg.ReadByte();
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
foreach (LevelWall levelWall in ExtraWalls)
|
||||
case EventType.GlobalDestructibleWall:
|
||||
{
|
||||
if (levelWall.Body.BodyType == BodyType.Static) { continue; }
|
||||
|
||||
Vector2 bodyPos = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16);
|
||||
DestructibleLevelWall destructibleWall = levelWall as DestructibleLevelWall;
|
||||
if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f && (destructibleWall == null || !destructibleWall.Destroyed))
|
||||
foreach (LevelWall levelWall in ExtraWalls)
|
||||
{
|
||||
levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation);
|
||||
if (levelWall.Body.BodyType == BodyType.Static) { continue; }
|
||||
|
||||
Vector2 bodyPos = new Vector2(
|
||||
msg.ReadSingle(),
|
||||
msg.ReadSingle());
|
||||
levelWall.MoveState = msg.ReadRangedSingle(0.0f, MathHelper.TwoPi, 16);
|
||||
if (Vector2.DistanceSquared(bodyPos, levelWall.Body.Position) > 0.5f
|
||||
&& !(levelWall is DestructibleLevelWall { Destroyed: true }))
|
||||
{
|
||||
levelWall.Body.SetTransformIgnoreContacts(ref bodyPos, levelWall.Body.Rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int index = msg.ReadUInt16();
|
||||
byte damageByte = msg.ReadByte();
|
||||
if (index < ExtraWalls.Count && ExtraWalls[index] is DestructibleLevelWall destructibleWall)
|
||||
break;
|
||||
case EventType.SingleDestructibleWall:
|
||||
{
|
||||
destructibleWall.SetDamage(destructibleWall.MaxHealth * damageByte / 255.0f);
|
||||
int index = msg.ReadUInt16();
|
||||
float damageByte = msg.ReadByte();
|
||||
if (index < ExtraWalls.Count && ExtraWalls[index] is DestructibleLevelWall destructibleWall)
|
||||
{
|
||||
destructibleWall.SetDamage(destructibleWall.MaxHealth * damageByte / 255.0f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Malformed incoming level event: {eventType} is not a supported event type");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -229,7 +229,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
int objIndex = msg.ReadRangedInteger(0, objects.Count);
|
||||
objects[objIndex].ClientRead(msg);
|
||||
|
||||
@@ -106,7 +106,8 @@ namespace Barotrauma
|
||||
WaterEffect.Parameters["xBlurDistance"].SetValue(BlurAmount / 100.0f);
|
||||
}
|
||||
else
|
||||
{ WaterEffect.CurrentTechnique = WaterEffect.Techniques["WaterShader"];
|
||||
{
|
||||
WaterEffect.CurrentTechnique = WaterEffect.Techniques["WaterShader"];
|
||||
}
|
||||
|
||||
Vector2 offset = WavePos;
|
||||
|
||||
@@ -188,8 +188,10 @@ namespace Barotrauma.Lights
|
||||
if (light.ParentBody != null)
|
||||
{
|
||||
light.ParentBody.UpdateDrawPosition();
|
||||
light.Position = light.ParentBody.DrawPosition;
|
||||
if (light.ParentSub != null) { light.Position -= light.ParentSub.DrawPosition; }
|
||||
|
||||
Vector2 pos = light.ParentBody.DrawPosition;
|
||||
if (light.ParentSub != null) { pos -= light.ParentSub.DrawPosition; }
|
||||
light.Position = pos;
|
||||
}
|
||||
|
||||
float range = light.LightSourceParams.TextureRange;
|
||||
|
||||
@@ -70,6 +70,8 @@ namespace Barotrauma
|
||||
|
||||
private (SubmarineInfo pendingSub, float realWorldCrushDepth) pendingSubInfo;
|
||||
|
||||
private RichString beaconStationActiveText, beaconStationInactiveText;
|
||||
|
||||
/*private (Rectangle targetArea, string tip)? connectionTooltip;
|
||||
private string sanitizedConnectionTooltip;
|
||||
private List<RichTextData> connectionTooltipRichTextData;
|
||||
@@ -153,6 +155,9 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError($"Could not find campaign map sprites for the biome \"{missingBiome.Identifier}\". Using the sprites of the first biome instead...");
|
||||
}
|
||||
|
||||
beaconStationActiveText = RichString.Rich(TextManager.Get("BeaconStationActiveTooltip"));
|
||||
beaconStationInactiveText = RichString.Rich(TextManager.Get("BeaconStationInactiveTooltip"));
|
||||
|
||||
RemoveFogOfWar(StartLocation);
|
||||
|
||||
GenerateLocationConnectionVisuals();
|
||||
@@ -619,7 +624,7 @@ namespace Barotrauma
|
||||
if (Vector2.Distance(PlayerInput.MousePosition, typeChangeIconPos) < generationParams.TypeChangeIcon.SourceRect.Width * zoom &&
|
||||
(tooltip == null || IsPreferredTooltip(typeChangeIconPos)))
|
||||
{
|
||||
tooltip = (new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)), location.LastTypeChangeMessage);
|
||||
tooltip = (new Rectangle(typeChangeIconPos.ToPoint(), new Point(30)), RichString.Rich(location.LastTypeChangeMessage));
|
||||
}
|
||||
}
|
||||
if (location != CurrentLocation && generationParams.MissionIcon != null)
|
||||
@@ -920,7 +925,7 @@ namespace Barotrauma
|
||||
if (connection.LevelData.HasBeaconStation)
|
||||
{
|
||||
var beaconStationIconStyle = connection.LevelData.IsBeaconActive ? "BeaconStationActive" : "BeaconStationInactive";
|
||||
DrawIcon(beaconStationIconStyle, (int)(28 * zoom), TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip"));
|
||||
DrawIcon(beaconStationIconStyle, (int)(28 * zoom), connection.LevelData.IsBeaconActive ? beaconStationActiveText : beaconStationInactiveText);
|
||||
}
|
||||
|
||||
if (connection.Locked)
|
||||
@@ -942,9 +947,9 @@ namespace Barotrauma
|
||||
|
||||
DrawIcon(
|
||||
"LockedLocationConnection", (int)(28 * zoom),
|
||||
TextManager.GetWithVariables(unlockEvent.UnlockPathTooltip ?? "LockedPathTooltip",
|
||||
RichString.Rich(TextManager.GetWithVariables(unlockEvent.UnlockPathTooltip ?? "LockedPathTooltip",
|
||||
("[requiredreputation]", Reputation.GetFormattedReputationText(MathUtils.InverseLerp(unlockReputation.MinReputation, unlockReputation.MaxReputation, unlockEvent.UnlockPathReputation), unlockEvent.UnlockPathReputation, addColorTags: true)),
|
||||
("[currentreputation]", unlockReputation.GetFormattedReputationText(addColorTags: true))));
|
||||
("[currentreputation]", unlockReputation.GetFormattedReputationText(addColorTags: true)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -955,7 +960,7 @@ namespace Barotrauma
|
||||
|
||||
if (connection.LevelData.HasHuntingGrounds)
|
||||
{
|
||||
DrawIcon("HuntingGrounds", (int)(28 * zoom), TextManager.Get("HuntingGroundsTooltip"));
|
||||
DrawIcon("HuntingGrounds", (int)(28 * zoom), RichString.Rich(TextManager.Get("HuntingGroundsTooltip")));
|
||||
}
|
||||
|
||||
if (crushDepthWarningIconStyle != null)
|
||||
@@ -976,7 +981,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
void DrawIcon(string iconStyle, int iconSize, LocalizedString tooltipText)
|
||||
void DrawIcon(string iconStyle, int iconSize, RichString tooltipText)
|
||||
{
|
||||
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
|
||||
Vector2 iconDiff = Vector2.Normalize(connectionEnd.Value - connectionStart.Value) * iconSize;
|
||||
|
||||
@@ -526,22 +526,17 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
byte sectionCount = msg.ReadByte();
|
||||
|
||||
bool invalidMessage = false;
|
||||
if (type != ServerNetObject.ENTITY_EVENT && type != ServerNetObject.ENTITY_EVENT_INITIAL)
|
||||
{
|
||||
DebugConsole.NewMessage($"Error while reading a network event for the structure \"{Name} ({ID})\". Invalid event type ({type}).", Color.Red);
|
||||
return;
|
||||
}
|
||||
else if (sectionCount != Sections.Length)
|
||||
if (sectionCount != Sections.Length)
|
||||
{
|
||||
invalidMessage = true;
|
||||
string errorMsg = $"Error while reading a network event for the structure \"{Name} ({ID})\". Section count does not match (server: {sectionCount} client: {Sections.Length})";
|
||||
DebugConsole.NewMessage(errorMsg, Color.Red);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Structure.ClientRead:SectionCountMismatch", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
throw new Exception(errorMsg);
|
||||
}
|
||||
|
||||
for (int i = 0; i < sectionCount; i++)
|
||||
|
||||
@@ -15,7 +15,7 @@ using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Submarine : Entity, IServerSerializable
|
||||
partial class Submarine : Entity, IServerPositionSync
|
||||
{
|
||||
public static Vector2 MouseToWorldGrid(Camera cam, Submarine sub)
|
||||
{
|
||||
@@ -547,19 +547,50 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
int disabledItemLightCount = 0;
|
||||
foreach (Item item in Item.ItemList)
|
||||
float entityCountWarningThreshold = 0.75f;
|
||||
|
||||
if (Item.ItemList.Count > SubEditorScreen.MaxItems * entityCountWarningThreshold)
|
||||
{
|
||||
if (item.ParentInventory == null) { continue; }
|
||||
disabledItemLightCount += item.GetComponents<Items.Components.LightComponent>().Count();
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.ItemCount))
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("subeditor.itemcountwarning").Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.ItemCount);
|
||||
}
|
||||
}
|
||||
int count = GameMain.LightManager.Lights.Count(l => l.CastShadows) - disabledItemLightCount;
|
||||
if (count > 45)
|
||||
|
||||
if ((MapEntity.mapEntityList.Count - Item.ItemList.Count - Hull.HullList.Count - WayPoint.WayPointList.Count - Gap.GapList.Count) > SubEditorScreen.MaxStructures * entityCountWarningThreshold)
|
||||
{
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.TooManyLights))
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.StructureCount))
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("subeditor.structurecountwarning").Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.StructureCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (Structure.WallList.Count > SubEditorScreen.MaxStructures * entityCountWarningThreshold)
|
||||
{
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.WallCount))
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("subeditor.wallcountwarning").Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.WallCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetLightCount() > SubEditorScreen.MaxLights * entityCountWarningThreshold)
|
||||
{
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.LightCount))
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("subeditor.lightcountwarning").Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.LightCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetShadowCastingLightCount() > SubEditorScreen.MaxShadowCastingLights * entityCountWarningThreshold)
|
||||
{
|
||||
if (!IsWarningSuppressed(SubEditorScreen.WarningType.ShadowCastingLightCount))
|
||||
{
|
||||
errorMsgs.Add(TextManager.Get("subeditor.shadowcastinglightswarning").Value);
|
||||
warnings.Add(SubEditorScreen.WarningType.TooManyLights);
|
||||
warnings.Add(SubEditorScreen.WarningType.ShadowCastingLightCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,14 +658,31 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public static int GetLightCount()
|
||||
{
|
||||
if (type != ServerNetObject.ENTITY_POSITION)
|
||||
int disabledItemLightCount = 0;
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
DebugConsole.NewMessage($"Error while reading a network event for the submarine \"{Info.Name} ({ID})\". Invalid event type ({type}).", Color.Red);
|
||||
if (item.ParentInventory == null) { continue; }
|
||||
disabledItemLightCount += item.GetComponents<Items.Components.LightComponent>().Count();
|
||||
}
|
||||
return GameMain.LightManager.Lights.Count() - disabledItemLightCount;
|
||||
}
|
||||
|
||||
var posInfo = PhysicsBody.ClientRead(type, msg, sendingTime, parentDebugName: Info.Name);
|
||||
public static int GetShadowCastingLightCount()
|
||||
{
|
||||
int disabledItemLightCount = 0;
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.ParentInventory == null) { continue; }
|
||||
disabledItemLightCount += item.GetComponents<Items.Components.LightComponent>().Count();
|
||||
}
|
||||
return GameMain.LightManager.Lights.Count(l => l.CastShadows) - disabledItemLightCount;
|
||||
}
|
||||
|
||||
public void ClientReadPosition(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
var posInfo = PhysicsBody.ClientRead(msg, sendingTime, parentDebugName: Info.Name);
|
||||
msg.ReadPadBits();
|
||||
|
||||
if (posInfo != null)
|
||||
@@ -648,5 +696,10 @@ namespace Barotrauma
|
||||
subBody.PositionBuffer.Insert(index, posInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
throw new Exception($"Error while reading a network event for the submarine \"{Info.Name} ({ID})\". Submarines are not even supposed to receive events!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
orderMessageInfo.TargetCharacter?.SetOrder(order);
|
||||
orderMessageInfo.TargetCharacter?.SetOrder(order, orderMessageInfo.IsNewOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,8 @@ namespace Barotrauma.Networking
|
||||
Order order = null;
|
||||
if (orderMessageInfo.TargetPosition != null)
|
||||
{
|
||||
order = new Order(orderPrefab, orderOption, orderMessageInfo.Priority, Order.OrderType.Current, null, orderMessageInfo.TargetPosition, orderGiver: senderCharacter);
|
||||
order = new Order(orderPrefab, orderOption, orderMessageInfo.TargetPosition, orderGiver: senderCharacter)
|
||||
.WithManualPriority(orderMessageInfo.Priority);
|
||||
}
|
||||
else if (orderMessageInfo.WallSectionIndex != null)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System.Diagnostics;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -12,6 +13,9 @@ namespace Barotrauma.Networking
|
||||
|
||||
public static void Start(ProcessStartInfo processInfo)
|
||||
{
|
||||
CrashString = null;
|
||||
CrashReportFilePath = null;
|
||||
|
||||
writePipe = new AnonymousPipeServerStream(PipeDirection.Out, System.IO.HandleInheritability.Inheritable);
|
||||
readPipe = new AnonymousPipeServerStream(PipeDirection.In, System.IO.HandleInheritability.Inheritable);
|
||||
|
||||
@@ -42,8 +46,8 @@ namespace Barotrauma.Networking
|
||||
|
||||
public static void ClosePipes()
|
||||
{
|
||||
writePipe?.Close();
|
||||
readPipe?.Close();
|
||||
writePipe?.Dispose(); writePipe = null;
|
||||
readPipe?.Dispose(); readPipe = null;
|
||||
shutDown = true;
|
||||
}
|
||||
|
||||
@@ -54,5 +58,20 @@ namespace Barotrauma.Networking
|
||||
|
||||
PrivateShutDown();
|
||||
}
|
||||
|
||||
public static string CrashString { get; private set; }
|
||||
public static string CrashReportFilePath { get; private set; }
|
||||
|
||||
public static LocalizedString CrashMessage
|
||||
=> string.IsNullOrEmpty(CrashReportFilePath)
|
||||
? TextManager.Get("ServerProcessClosed")
|
||||
: TextManager.GetWithVariable("ServerProcessCrashed", "[reportfilepath]", CrashReportFilePath);
|
||||
|
||||
static partial void HandleCrashString(string str)
|
||||
{
|
||||
DebugConsole.ThrowError($"The server has crashed: {str}");
|
||||
CrashReportFilePath = str.Split("||").FirstOrDefault() ?? "servercrashreport.log";
|
||||
CrashString = str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace Barotrauma
|
||||
{
|
||||
partial class EntitySpawner : Entity, IServerSerializable
|
||||
{
|
||||
public void ClientRead(ServerNetObject type, IReadMessage message, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage message, float sendingTime)
|
||||
{
|
||||
bool remove = message.ReadBoolean();
|
||||
|
||||
|
||||
@@ -666,7 +666,7 @@ namespace Barotrauma.Networking
|
||||
if (ChildServerRelay.Process?.HasExited ?? true)
|
||||
{
|
||||
Disconnect();
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), TextManager.Get("ServerProcessClosed"));
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += ReturnToPreviousMenu;
|
||||
}
|
||||
}
|
||||
@@ -937,6 +937,9 @@ namespace Barotrauma.Networking
|
||||
case ServerPacketHeader.MEDICAL:
|
||||
campaign?.MedicalClinic?.ClientRead(inc);
|
||||
break;
|
||||
case ServerPacketHeader.MONEY:
|
||||
campaign?.ClientReadMoney(inc);
|
||||
break;
|
||||
case ServerPacketHeader.READY_CHECK:
|
||||
ReadyCheck.ClientRead(inc);
|
||||
break;
|
||||
@@ -1203,7 +1206,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
if (disconnectReason == DisconnectReason.ServerCrashed && IsServerOwner)
|
||||
{
|
||||
msg = TextManager.Get("ServerProcessCrashed");
|
||||
msg = TextManager.GetWithVariable("ServerProcessCrashed", "[reportfilepath]", ChildServerRelay.CrashReportFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2240,10 +2243,11 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
readonly List<IServerSerializable> debugEntityList = new List<IServerSerializable>();
|
||||
private void ReadIngameUpdate(IReadMessage inc)
|
||||
{
|
||||
List<IServerSerializable> entities = new List<IServerSerializable>();
|
||||
|
||||
debugEntityList.Clear();
|
||||
|
||||
float sendingTime = inc.ReadSingle() - 0.0f;//TODO: reimplement inc.SenderConnection.RemoteTimeOffset;
|
||||
|
||||
ServerNetObject? prevObjHeader = null;
|
||||
@@ -2284,15 +2288,15 @@ namespace Barotrauma.Networking
|
||||
uint msgLength = inc.ReadVariableUInt32();
|
||||
int msgEndPos = (int)(inc.BitPosition + msgLength * 8);
|
||||
|
||||
var entity = Entity.FindEntityByID(id) as IServerSerializable;
|
||||
var entity = Entity.FindEntityByID(id) as IServerPositionSync;
|
||||
if (msgEndPos > inc.LengthBits)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error while reading a position update for the entity \"({entity?.ToString() ?? "null"})\". Message length exceeds the size of the buffer.");
|
||||
return;
|
||||
}
|
||||
|
||||
entities.Add(entity);
|
||||
if (entity != null && (entity is Item || entity is Character || entity is Submarine))
|
||||
debugEntityList.Add(entity);
|
||||
if (entity != null)
|
||||
{
|
||||
if (entity is Item != isItem)
|
||||
{
|
||||
@@ -2307,7 +2311,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
else
|
||||
{
|
||||
entity.ClientRead(objHeader.Value, inc, sendingTime);
|
||||
entity.ClientReadPosition(inc, sendingTime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2321,7 +2325,7 @@ namespace Barotrauma.Networking
|
||||
break;
|
||||
case ServerNetObject.ENTITY_EVENT:
|
||||
case ServerNetObject.ENTITY_EVENT_INITIAL:
|
||||
if (!entityEventManager.Read(objHeader.Value, inc, sendingTime, entities))
|
||||
if (!entityEventManager.Read(objHeader.Value, inc, sendingTime, debugEntityList))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -2340,7 +2344,6 @@ namespace Barotrauma.Networking
|
||||
prevBytePos = inc.BytePosition;
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
List<string> errorLines = new List<string>
|
||||
@@ -2359,7 +2362,7 @@ namespace Barotrauma.Networking
|
||||
objHeader == ServerNetObject.ENTITY_EVENT || objHeader == ServerNetObject.ENTITY_EVENT_INITIAL ||
|
||||
objHeader == ServerNetObject.ENTITY_POSITION || prevObjHeader == ServerNetObject.ENTITY_POSITION)
|
||||
{
|
||||
foreach (IServerSerializable ent in entities)
|
||||
foreach (IServerSerializable ent in debugEntityList)
|
||||
{
|
||||
if (ent == null)
|
||||
{
|
||||
@@ -2480,7 +2483,7 @@ namespace Barotrauma.Networking
|
||||
outmsg.Write(GameMain.NetLobbyScreen.CampaignCharacterDiscarded);
|
||||
}
|
||||
|
||||
Character.Controlled?.ClientWrite(outmsg);
|
||||
Character.Controlled?.ClientWriteInput(outmsg);
|
||||
GameMain.GameScreen.Cam?.ClientWrite(outmsg);
|
||||
|
||||
entityEventManager.Write(outmsg, clientPeer?.ServerConnection);
|
||||
@@ -2711,7 +2714,7 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
}
|
||||
|
||||
public override void CreateEntityEvent(INetSerializable entity, object[] extraData)
|
||||
public override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (!(entity is IClientSerializable clientSerializable))
|
||||
{
|
||||
@@ -2770,7 +2773,7 @@ namespace Barotrauma.Networking
|
||||
if (ChildServerRelay.Process != null)
|
||||
{
|
||||
int checks = 0;
|
||||
while (ChildServerRelay.Process != null && !ChildServerRelay.Process.HasExited)
|
||||
while (ChildServerRelay.Process is { HasExited: false })
|
||||
{
|
||||
if (checks > 10)
|
||||
{
|
||||
|
||||
+13
-13
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
@@ -41,16 +42,16 @@ namespace Barotrauma.Networking
|
||||
thisClient = client;
|
||||
}
|
||||
|
||||
public void CreateEvent(IClientSerializable entity, object[] extraData = null)
|
||||
public void CreateEvent(IClientSerializable entity, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
if (GameMain.Client?.Character == null) { return; }
|
||||
|
||||
if (!ValidateEntity(entity)) { return; }
|
||||
|
||||
var newEvent = new ClientEntityEvent(entity, (UInt16)(ID + 1))
|
||||
{
|
||||
CharacterStateID = GameMain.Client.Character.LastNetworkUpdateID
|
||||
};
|
||||
var newEvent = new ClientEntityEvent(
|
||||
entity,
|
||||
eventId: (UInt16)(ID + 1),
|
||||
characterStateId: GameMain.Client.Character.LastNetworkUpdateID);
|
||||
if (extraData != null) { newEvent.SetData(extraData); }
|
||||
|
||||
for (int i = events.Count - 1; i >= 0; i--)
|
||||
@@ -144,6 +145,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
entities.Clear();
|
||||
|
||||
msg.ReadPadBits();
|
||||
UInt16 firstEventID = msg.ReadUInt16();
|
||||
int eventCount = msg.ReadByte();
|
||||
|
||||
@@ -172,7 +174,6 @@ namespace Barotrauma.Networking
|
||||
DebugConsole.NewMessage("received msg " + thisEventID + " (null entity)",
|
||||
Microsoft.Xna.Framework.Color.Orange);
|
||||
}
|
||||
msg.ReadPadBits();
|
||||
entities.Add(null);
|
||||
if (thisEventID == (UInt16)(lastReceivedID + 1)) { lastReceivedID++; }
|
||||
continue;
|
||||
@@ -207,7 +208,6 @@ namespace Barotrauma.Networking
|
||||
}
|
||||
|
||||
msg.BitPosition += msgLength * 8;
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -239,22 +239,22 @@ namespace Barotrauma.Networking
|
||||
//msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
}
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
string errorMsg = "Failed to read event for entity \"" + entity.ToString() + "\" (" + e.Message + ")! (MidRoundSyncing: " + thisClient.MidRoundSyncing + ")\n" + e.StackTrace.CleanupStackTrace();
|
||||
string errorMsg = $"Failed to read event {thisEventID} for entity \"{entity}\"" +
|
||||
$"{(entity is Entity { ID: var entityId } ? $", id {entityId}" : "")} ";
|
||||
DebugConsole.ThrowError(errorMsg, e);
|
||||
|
||||
errorMsg += $"({e.Message})! (MidRoundSyncing: {thisClient.MidRoundSyncing})\n{e.StackTrace.CleanupStackTrace()}";
|
||||
errorMsg += "\nPrevious entities:";
|
||||
for (int j = entities.Count - 2; j >= 0; j--)
|
||||
{
|
||||
errorMsg += "\n" + (entities[j] == null ? "NULL" : entities[j].ToString());
|
||||
}
|
||||
|
||||
DebugConsole.ThrowError("Failed to read event for entity \"" + entity.ToString() + "\"!", e);
|
||||
|
||||
GameAnalyticsManager.AddErrorEventOnce("ClientEntityEventManager.Read:ReadFailed" + entity.ToString(),
|
||||
GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
msg.BitPosition = (int)(msgPosition + msgLength * 8);
|
||||
msg.ReadPadBits();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -272,7 +272,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
protected void ReadEvent(IReadMessage buffer, IServerSerializable entity, float sendingTime)
|
||||
{
|
||||
entity.ClientRead(ServerNetObject.ENTITY_EVENT, buffer, sendingTime);
|
||||
entity.ClientEventRead(buffer, sendingTime);
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
|
||||
+6
-5
@@ -4,20 +4,21 @@ namespace Barotrauma.Networking
|
||||
{
|
||||
class ClientEntityEvent : NetEntityEvent
|
||||
{
|
||||
private IClientSerializable serializable;
|
||||
private readonly IClientSerializable serializable;
|
||||
|
||||
public UInt16 CharacterStateID;
|
||||
public readonly UInt16 CharacterStateID;
|
||||
|
||||
public ClientEntityEvent(IClientSerializable entity, UInt16 id)
|
||||
: base(entity, id)
|
||||
public ClientEntityEvent(IClientSerializable entity, UInt16 eventId, UInt16 characterStateId)
|
||||
: base(entity, eventId)
|
||||
{
|
||||
serializable = entity;
|
||||
CharacterStateID = characterStateId;
|
||||
}
|
||||
|
||||
public void Write(IWriteMessage msg)
|
||||
{
|
||||
msg.Write(CharacterStateID);
|
||||
serializable.ClientWrite(msg, Data);
|
||||
serializable.ClientEventWrite(msg, Data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ namespace Barotrauma.Networking
|
||||
if (ownerKey != 0 && (ChildServerRelay.Process?.HasExited ?? true))
|
||||
{
|
||||
Close();
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), TextManager.Get("ServerProcessClosed"));
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) => { GameMain.MainMenuScreen.Select(); return false; };
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-1
@@ -193,7 +193,7 @@ namespace Barotrauma.Networking
|
||||
if (ChildServerRelay.HasShutDown || (ChildServerRelay.Process?.HasExited ?? true))
|
||||
{
|
||||
Close();
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), TextManager.Get("ServerProcessClosed"));
|
||||
var msgBox = new GUIMessageBox(TextManager.Get("ConnectionLost"), ChildServerRelay.CrashMessage);
|
||||
msgBox.Buttons[0].OnClicked += (btn, obj) => { GameMain.MainMenuScreen.Select(); return false; };
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ namespace Barotrauma.Networking
|
||||
}, delay: delay);
|
||||
}
|
||||
|
||||
public void ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime)
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
bool respawnPromptPending = false;
|
||||
var newState = (State)msg.ReadRangedInteger(0, Enum.GetNames(typeof(State)).Length);
|
||||
|
||||
@@ -153,7 +153,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public PosInfo ClientRead(ServerNetObject type, IReadMessage msg, float sendingTime, string parentDebugName)
|
||||
public PosInfo ClientRead(IReadMessage msg, float sendingTime, string parentDebugName)
|
||||
{
|
||||
float MaxVel = NetConfig.MaxPhysicsBodyVelocity;
|
||||
float MaxAngularVel = NetConfig.MaxPhysicsBodyAngularVelocity;
|
||||
|
||||
+11
@@ -278,6 +278,17 @@ namespace Barotrauma
|
||||
characterInfos.Add((new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant), jobPrefab));
|
||||
}
|
||||
}
|
||||
if (characterInfos.Count == 0)
|
||||
{
|
||||
DebugConsole.ThrowError($"No starting crew found! If you're using mods, it may be that the mods have overridden the vanilla jobs without specifying which types of characters the starting crew should consist of. If you're the developer of the mod, ensure that you've set the {nameof(JobPrefab.InitialCount)} properties for the custom jobs.");
|
||||
DebugConsole.AddWarning("Choosing the first available jobs as the starting crew...");
|
||||
foreach (JobPrefab jobPrefab in JobPrefab.Prefabs)
|
||||
{
|
||||
var variant = Rand.Range(0, jobPrefab.Variants);
|
||||
characterInfos.Add((new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: jobPrefab, variant: variant), jobPrefab));
|
||||
if (characterInfos.Count >= 3) { break; }
|
||||
}
|
||||
}
|
||||
characterInfos.Sort((a, b) => Math.Sign(b.Job.MinKarma - a.Job.MinKarma));
|
||||
|
||||
characterInfoColumns.ClearChildren();
|
||||
|
||||
@@ -143,14 +143,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (Campaign.PurchasedHullRepairs)
|
||||
{
|
||||
Campaign.Money += CampaignMode.HullRepairCost;
|
||||
Campaign.Wallet.Refund(CampaignMode.HullRepairCost);
|
||||
Campaign.PurchasedHullRepairs = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Campaign.Money >= CampaignMode.HullRepairCost)
|
||||
if (Campaign.Wallet.TryDeduct(CampaignMode.HullRepairCost))
|
||||
{
|
||||
Campaign.Money -= CampaignMode.HullRepairCost;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.HullRepairCost, GameAnalyticsManager.MoneySink.Service, "hullrepairs");
|
||||
Campaign.PurchasedHullRepairs = true;
|
||||
}
|
||||
@@ -189,14 +188,13 @@ namespace Barotrauma
|
||||
{
|
||||
if (Campaign.PurchasedItemRepairs)
|
||||
{
|
||||
Campaign.Money += CampaignMode.ItemRepairCost;
|
||||
Campaign.Wallet.Refund(CampaignMode.ItemRepairCost);
|
||||
Campaign.PurchasedItemRepairs = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Campaign.Money >= CampaignMode.ItemRepairCost)
|
||||
if (Campaign.Wallet.TryDeduct(CampaignMode.ItemRepairCost))
|
||||
{
|
||||
Campaign.Money -= CampaignMode.ItemRepairCost;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ItemRepairCost, GameAnalyticsManager.MoneySink.Service, "devicerepairs");
|
||||
Campaign.PurchasedItemRepairs = true;
|
||||
}
|
||||
@@ -242,14 +240,13 @@ namespace Barotrauma
|
||||
|
||||
if (Campaign.PurchasedLostShuttles)
|
||||
{
|
||||
Campaign.Money += CampaignMode.ShuttleReplaceCost;
|
||||
Campaign.Wallet.Refund(CampaignMode.ShuttleReplaceCost);
|
||||
Campaign.PurchasedLostShuttles = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Campaign.Money >= CampaignMode.ShuttleReplaceCost)
|
||||
if (Campaign.Wallet.TryDeduct(CampaignMode.ShuttleReplaceCost))
|
||||
{
|
||||
Campaign.Money -= CampaignMode.ShuttleReplaceCost;
|
||||
GameAnalyticsManager.AddMoneySpentEvent(CampaignMode.ShuttleReplaceCost, GameAnalyticsManager.MoneySink.Service, "retrieveshuttle");
|
||||
Campaign.PurchasedLostShuttles = true;
|
||||
}
|
||||
@@ -445,7 +442,7 @@ namespace Barotrauma
|
||||
{
|
||||
Color = MapGenerationParams.Instance.IndicatorColor,
|
||||
HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f),
|
||||
ToolTip = TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip")
|
||||
ToolTip = RichString.Rich(TextManager.Get(connection.LevelData.IsBeaconActive ? "BeaconStationActiveTooltip" : "BeaconStationInactiveTooltip"))
|
||||
};
|
||||
new GUITextBlock(new RectTransform(Vector2.One, beaconStationContent.RectTransform),
|
||||
TextManager.Get("submarinetype.beaconstation", "beaconstationsonarlabel"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
@@ -462,7 +459,7 @@ namespace Barotrauma
|
||||
{
|
||||
Color = MapGenerationParams.Instance.IndicatorColor,
|
||||
HoverColor = Color.Lerp(MapGenerationParams.Instance.IndicatorColor, Color.White, 0.5f),
|
||||
ToolTip = TextManager.Get("HuntingGroundsTooltip")
|
||||
ToolTip = RichString.Rich(TextManager.Get("HuntingGroundsTooltip"))
|
||||
};
|
||||
new GUITextBlock(new RectTransform(Vector2.One, huntingGroundsContent.RectTransform),
|
||||
TextManager.Get("missionname.huntinggrounds"), font: GUIStyle.SubHeadingFont, textAlignment: Alignment.CenterLeft)
|
||||
@@ -705,11 +702,11 @@ namespace Barotrauma
|
||||
{
|
||||
case CampaignMode.InteractionType.Repair:
|
||||
repairHullsButton.Enabled =
|
||||
(Campaign.PurchasedHullRepairs || Campaign.Money >= CampaignMode.HullRepairCost) &&
|
||||
(Campaign.PurchasedHullRepairs || Campaign.Wallet.CanAfford(CampaignMode.HullRepairCost)) &&
|
||||
Campaign.AllowedToManageCampaign();
|
||||
repairHullsButton.GetChild<GUITickBox>().Selected = Campaign.PurchasedHullRepairs;
|
||||
repairItemsButton.Enabled =
|
||||
(Campaign.PurchasedItemRepairs || Campaign.Money >= CampaignMode.ItemRepairCost) &&
|
||||
(Campaign.PurchasedItemRepairs || Campaign.Wallet.CanAfford(CampaignMode.ItemRepairCost)) &&
|
||||
Campaign.AllowedToManageCampaign();
|
||||
repairItemsButton.GetChild<GUITickBox>().Selected = Campaign.PurchasedItemRepairs;
|
||||
|
||||
@@ -721,7 +718,7 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
replaceShuttlesButton.Enabled =
|
||||
(Campaign.PurchasedLostShuttles || Campaign.Money >= CampaignMode.ShuttleReplaceCost) &&
|
||||
(Campaign.PurchasedLostShuttles || Campaign.Wallet.CanAfford(CampaignMode.ShuttleReplaceCost)) &&
|
||||
Campaign.AllowedToManageCampaign();
|
||||
replaceShuttlesButton.GetChild<GUITickBox>().Selected = Campaign.PurchasedLostShuttles;
|
||||
}
|
||||
@@ -742,7 +739,7 @@ namespace Barotrauma
|
||||
|
||||
public static LocalizedString GetMoney()
|
||||
{
|
||||
return TextManager.GetWithVariable("PlayerCredits", "[credits]", (GameMain.GameSession?.Campaign == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", GameMain.GameSession.Campaign.Money));
|
||||
return TextManager.GetWithVariable("PlayerCredits", "[credits]", (GameMain.GameSession?.Campaign == null) ? "0" : string.Format(CultureInfo.InvariantCulture, "{0:N0}", GameMain.GameSession.Campaign.Wallet.Balance));
|
||||
}
|
||||
|
||||
private void UpdateMaxMissions(Location location)
|
||||
|
||||
+4
-10
@@ -1668,11 +1668,7 @@ namespace Barotrauma.CharacterEditor
|
||||
|
||||
if (contentPackage == null)
|
||||
{
|
||||
#if DEBUG
|
||||
contentPackage = ContentPackageManager.EnabledPackages.All.LastOrDefault();
|
||||
#else
|
||||
contentPackage = ContentPackageManager.EnabledPackages.All.LastOrDefault(cp => cp != vanilla);
|
||||
#endif
|
||||
}
|
||||
if (contentPackage == null)
|
||||
{
|
||||
@@ -1680,13 +1676,11 @@ namespace Barotrauma.CharacterEditor
|
||||
DebugConsole.ThrowError(GetCharacterEditorTranslation("NoContentPackageSelected"));
|
||||
return false;
|
||||
}
|
||||
#if !DEBUG
|
||||
if (vanilla != null && contentPackage == vanilla)
|
||||
{
|
||||
GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), GUIStyle.Red, font: GUIStyle.LargeFont);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
// Content package
|
||||
if (contentPackage is RegularPackage regular && !ContentPackageManager.EnabledPackages.Regular.Contains(regular))
|
||||
{
|
||||
@@ -1721,9 +1715,9 @@ namespace Barotrauma.CharacterEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
config.SetAttributeValue("speciesname", name);
|
||||
config.SetAttributeValue("humanoid", isHumanoid);
|
||||
var ragdollElement = config.Element("ragdolls");
|
||||
config.SetAttributeValue("speciesname", name, StringComparison.OrdinalIgnoreCase);
|
||||
config.SetAttributeValue("humanoid", isHumanoid, StringComparison.OrdinalIgnoreCase);
|
||||
var ragdollElement = config.GetChildElement("ragdolls");
|
||||
if (ragdollElement == null)
|
||||
{
|
||||
config.Add(new XElement("ragdolls", CreateRagdollPath()));
|
||||
@@ -1736,7 +1730,7 @@ namespace Barotrauma.CharacterEditor
|
||||
ragdollElement.ReplaceWith(new XElement("ragdolls", CreateRagdollPath()));
|
||||
}
|
||||
}
|
||||
var animationElement = config.Element("animations");
|
||||
var animationElement = config.GetChildElement("animations");
|
||||
if (animationElement == null)
|
||||
{
|
||||
config.Add(new XElement("animations", CreateAnimationPath()));
|
||||
|
||||
@@ -160,8 +160,7 @@ namespace Barotrauma.CharacterEditor
|
||||
bool isTextureSelected = false;
|
||||
void UpdatePaths()
|
||||
{
|
||||
string pathBase = ContentPackage == GameMain.VanillaContent ? $"Content/Characters/{Name}/{Name}"
|
||||
: $"{ContentPath.ModDirStr}/Characters/{Name}/{Name}";
|
||||
string pathBase = $"{ContentPath.ModDirStr}/Characters/{Name}/{Name}";
|
||||
XMLPath = $"{pathBase}.xml";
|
||||
xmlPathElement.Text = XMLPath;
|
||||
if (updateTexturePath)
|
||||
@@ -307,10 +306,10 @@ namespace Barotrauma.CharacterEditor
|
||||
contentPackageDropDown = new GUIDropDown(new RectTransform(new Vector2(1.0f, 0.5f), rightContainer.RectTransform, Anchor.TopRight));
|
||||
foreach (ContentPackage contentPackage in ContentPackageManager.EnabledPackages.All)
|
||||
{
|
||||
#if !DEBUG
|
||||
if (contentPackage == GameMain.VanillaContent) { continue; }
|
||||
#endif
|
||||
contentPackageDropDown.AddItem(contentPackage.Name, userData: contentPackage, toolTip: contentPackage.Path);
|
||||
if (contentPackage != GameMain.VanillaContent)
|
||||
{
|
||||
contentPackageDropDown.AddItem(contentPackage.Name, userData: contentPackage, toolTip: contentPackage.Path);
|
||||
}
|
||||
}
|
||||
contentPackageDropDown.OnSelected = (obj, userdata) =>
|
||||
{
|
||||
|
||||
@@ -1329,6 +1329,11 @@ namespace Barotrauma
|
||||
}
|
||||
SeedBox.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
levelDifficultyScrollBar.Enabled = !CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
levelDifficultyScrollBar.ToolTip = string.Empty;
|
||||
if (!levelDifficultyScrollBar.Enabled)
|
||||
{
|
||||
levelDifficultyScrollBar.ToolTip = TextManager.Get("campaigndifficultydisabled");
|
||||
}
|
||||
|
||||
traitorProbabilityButtons[0].Enabled = traitorProbabilityButtons[1].Enabled = traitorProbabilityText.Enabled =
|
||||
!CampaignFrame.Visible && !CampaignSetupFrame.Visible && GameMain.Client.HasPermission(ClientPermissions.ManageSettings);
|
||||
|
||||
@@ -32,7 +32,10 @@ namespace Barotrauma
|
||||
private GUIScrollBar zoomBar;
|
||||
private readonly List<Sprite> selectedSprites = new List<Sprite>();
|
||||
private readonly List<Sprite> dirtySprites = new List<Sprite>();
|
||||
private Sprite selectedTexture;
|
||||
private Texture2D SelectedTexture => lastSprite?.Texture;
|
||||
private Sprite lastSprite;
|
||||
private string selectedTexturePath;
|
||||
|
||||
private Rectangle textureRect;
|
||||
private float zoom = 1;
|
||||
private const float MinZoom = 0.25f, MaxZoom = 10.0f;
|
||||
@@ -82,12 +85,11 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (!(textureList.SelectedData is Texture2D selectedTexture)) { return false; }
|
||||
var selected = selectedSprites;
|
||||
Sprite firstSelected = selected.First();
|
||||
selected.ForEach(s => s.ReloadTexture());
|
||||
RefreshLists();
|
||||
textureList.Select(firstSelected.Texture, autoScroll: false);
|
||||
textureList.Select(firstSelected.FullPath, autoScroll: false);
|
||||
selected.ForEachMod(s => spriteList.Select(s, autoScroll: false));
|
||||
texturePathText.Text = TextManager.GetWithVariable("spriteeditor.texturesreloaded", "[filepath]", firstSelected.FilePath.Value);
|
||||
texturePathText.TextColor = GUIStyle.Green;
|
||||
@@ -101,10 +103,10 @@ namespace Barotrauma
|
||||
{
|
||||
OnClicked = (button, userData) =>
|
||||
{
|
||||
if (selectedTexture == null) { return false; }
|
||||
if (SelectedTexture == null) { return false; }
|
||||
foreach (Sprite sprite in loadedSprites)
|
||||
{
|
||||
if (sprite.FullPath != selectedTexture.FullPath) { continue; }
|
||||
if (sprite.FullPath != selectedTexturePath) { continue; }
|
||||
var element = sprite.SourceElement;
|
||||
if (element == null) { continue; }
|
||||
// Not all sprites have a sourcerect defined, in which case we'll want to use the current source rect instead of an empty rect.
|
||||
@@ -206,23 +208,20 @@ namespace Barotrauma
|
||||
{
|
||||
OnSelected = (listBox, userData) =>
|
||||
{
|
||||
var previousSprite = selectedTexture;
|
||||
selectedTexture = userData as Sprite;
|
||||
if (previousSprite != selectedTexture)
|
||||
var newTexturePath = userData as string;
|
||||
if (selectedTexturePath == null || selectedTexturePath != newTexturePath)
|
||||
{
|
||||
selectedTexturePath = newTexturePath;
|
||||
ResetZoom();
|
||||
spriteList.Select(loadedSprites.First(s => s.FilePath == selectedTexturePath), autoScroll: false);
|
||||
UpdateScrollBar(spriteList);
|
||||
}
|
||||
foreach (GUIComponent child in spriteList.Content.Children)
|
||||
{
|
||||
var textBlock = (GUITextBlock)child;
|
||||
var sprite = (Sprite)textBlock.UserData;
|
||||
textBlock.TextColor = new Color(textBlock.TextColor, sprite.FilePath == selectedTexture.FilePath ? 1.0f : 0.4f);
|
||||
if (sprite.FilePath == selectedTexture.FilePath) { textBlock.Visible = true; }
|
||||
}
|
||||
if (selectedSprites.None(s => s.FilePath == selectedTexture.FilePath))
|
||||
{
|
||||
spriteList.Select(loadedSprites.First(s => s.FilePath == selectedTexture.FilePath), autoScroll: false);
|
||||
UpdateScrollBar(spriteList);
|
||||
textBlock.TextColor = new Color(textBlock.TextColor, sprite.FilePath == selectedTexturePath ? 1.0f : 0.4f);
|
||||
if (sprite.FilePath == selectedTexturePath) { textBlock.Visible = true; }
|
||||
}
|
||||
texturePathText.TextColor = Color.LightGray;
|
||||
topPanelContents.Visible = true;
|
||||
@@ -251,9 +250,12 @@ namespace Barotrauma
|
||||
{
|
||||
OnSelected = (listBox, userData) =>
|
||||
{
|
||||
if (!(userData is Sprite sprite)) return false;
|
||||
SelectSprite(sprite);
|
||||
return true;
|
||||
if (userData is Sprite sprite)
|
||||
{
|
||||
SelectSprite(sprite);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -410,12 +412,12 @@ namespace Barotrauma
|
||||
|
||||
private bool SaveSprites(IEnumerable<Sprite> sprites)
|
||||
{
|
||||
if (selectedTexture == null) { return false; }
|
||||
if (SelectedTexture == null) { return false; }
|
||||
if (sprites.None()) { return false; }
|
||||
HashSet<XDocument> docsToSave = new HashSet<XDocument>();
|
||||
foreach (Sprite sprite in sprites)
|
||||
{
|
||||
if (sprite.FullPath != selectedTexture.FullPath) { continue; }
|
||||
if (sprite.FullPath != selectedTexturePath) { continue; }
|
||||
var element = sprite.SourceElement;
|
||||
if (element == null) { continue; }
|
||||
element.SetAttributeValue("sourcerect", XMLExtensions.RectToString(sprite.SourceRect));
|
||||
@@ -469,11 +471,11 @@ namespace Barotrauma
|
||||
// Select rects with the mouse
|
||||
if (Widget.selectedWidgets.None() || Widget.EnableMultiSelect)
|
||||
{
|
||||
if (selectedTexture != null && GUI.MouseOn == null)
|
||||
if (SelectedTexture != null && GUI.MouseOn == null)
|
||||
{
|
||||
foreach (Sprite sprite in loadedSprites)
|
||||
{
|
||||
if (sprite.FullPath != selectedTexture.FullPath) { continue; }
|
||||
if (sprite.FullPath != selectedTexturePath) { continue; }
|
||||
if (PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
var scaledRect = new Rectangle(textureRect.Location + sprite.SourceRect.Location.Multiply(zoom), sprite.SourceRect.Size.Multiply(zoom));
|
||||
@@ -637,20 +639,20 @@ namespace Barotrauma
|
||||
|
||||
var viewArea = GetViewArea;
|
||||
|
||||
if (selectedTexture != null)
|
||||
if (SelectedTexture != null)
|
||||
{
|
||||
textureRect = new Rectangle(
|
||||
(int)(viewArea.Center.X - selectedTexture.Texture.Bounds.Width / 2f * zoom),
|
||||
(int)(viewArea.Center.Y - selectedTexture.Texture.Bounds.Height / 2f * zoom),
|
||||
(int)(selectedTexture.Texture.Bounds.Width * zoom),
|
||||
(int)(selectedTexture.Texture.Bounds.Height * zoom));
|
||||
(int)(viewArea.Center.X - SelectedTexture.Bounds.Width / 2f * zoom),
|
||||
(int)(viewArea.Center.Y - SelectedTexture.Bounds.Height / 2f * zoom),
|
||||
(int)(SelectedTexture.Bounds.Width * zoom),
|
||||
(int)(SelectedTexture.Bounds.Height * zoom));
|
||||
|
||||
spriteBatch.Draw(selectedTexture.Texture,
|
||||
spriteBatch.Draw(SelectedTexture,
|
||||
viewArea.Center.ToVector2(),
|
||||
sourceRectangle: null,
|
||||
color: Color.White,
|
||||
rotation: 0.0f,
|
||||
origin: new Vector2(selectedTexture.Texture.Bounds.Width / 2.0f, selectedTexture.Texture.Bounds.Height / 2.0f),
|
||||
origin: new Vector2(SelectedTexture.Bounds.Width / 2.0f, SelectedTexture.Bounds.Height / 2.0f),
|
||||
scale: zoom,
|
||||
effects: SpriteEffects.None,
|
||||
layerDepth: 0);
|
||||
@@ -666,7 +668,7 @@ namespace Barotrauma
|
||||
foreach (GUIComponent element in spriteList.Content.Children)
|
||||
{
|
||||
if (!(element.UserData is Sprite sprite)) { continue; }
|
||||
if (sprite.FullPath != selectedTexture.FullPath) { continue; }
|
||||
if (sprite.FullPath != selectedTexturePath) { continue; }
|
||||
|
||||
Rectangle sourceRect = new Rectangle(
|
||||
textureRect.X + (int)(sprite.SourceRect.X * zoom),
|
||||
@@ -874,13 +876,13 @@ namespace Barotrauma
|
||||
|
||||
public void SelectSprite(Sprite sprite)
|
||||
{
|
||||
lastSprite = sprite;
|
||||
if (!loadedSprites.Contains(sprite))
|
||||
{
|
||||
loadedSprites.Add(sprite);
|
||||
RefreshLists();
|
||||
}
|
||||
|
||||
if (selectedSprites.Any(s => s.FullPath != selectedTexture.FullPath))
|
||||
if (selectedSprites.Any(s => s.FullPath != selectedTexturePath))
|
||||
{
|
||||
ResetWidgets();
|
||||
}
|
||||
@@ -902,9 +904,9 @@ namespace Barotrauma
|
||||
selectedSprites.Add(sprite);
|
||||
dirtySprites.Add(sprite);
|
||||
}
|
||||
if (selectedTexture?.FullPath != sprite.FullPath)
|
||||
if (sprite.FullPath != selectedTexturePath)
|
||||
{
|
||||
textureList.Select(sprite.Texture, autoScroll: false);
|
||||
textureList.Select(sprite.FullPath, autoScroll: false);
|
||||
UpdateScrollBar(textureList);
|
||||
}
|
||||
xmlPathText.Text = string.Empty;
|
||||
@@ -926,7 +928,6 @@ namespace Barotrauma
|
||||
|
||||
public void RefreshLists()
|
||||
{
|
||||
//selectedTexture = null;
|
||||
selectedSprites.Clear();
|
||||
textureList.ClearChildren();
|
||||
spriteList.ClearChildren();
|
||||
@@ -936,7 +937,7 @@ namespace Barotrauma
|
||||
foreach (Sprite sprite in loadedSprites.OrderBy(s => Path.GetFileNameWithoutExtension(s.FilePath.Value)))
|
||||
{
|
||||
//ignore sprites that don't have a file path (e.g. submarine pics)
|
||||
if (sprite.FilePath.IsNullOrEmpty()) continue;
|
||||
if (sprite.FilePath.IsNullOrEmpty()) { continue; }
|
||||
string normalizedFilePath = sprite.FilePath.FullPath;
|
||||
if (!textures.Contains(normalizedFilePath))
|
||||
{
|
||||
@@ -944,7 +945,7 @@ namespace Barotrauma
|
||||
Path.GetFileName(sprite.FilePath.Value))
|
||||
{
|
||||
ToolTip = sprite.FilePath.Value,
|
||||
UserData = sprite
|
||||
UserData = sprite.FullPath
|
||||
};
|
||||
textures.Add(normalizedFilePath);
|
||||
}
|
||||
@@ -965,10 +966,10 @@ namespace Barotrauma
|
||||
|
||||
public void ResetZoom()
|
||||
{
|
||||
if (selectedTexture == null) { return; }
|
||||
if (SelectedTexture == null) { return; }
|
||||
var viewArea = GetViewArea;
|
||||
float width = viewArea.Width / (float)selectedTexture.Texture.Width;
|
||||
float height = viewArea.Height / (float)selectedTexture.Texture.Height;
|
||||
float width = viewArea.Width / (float)SelectedTexture.Width;
|
||||
float height = viewArea.Height / (float)SelectedTexture.Height;
|
||||
zoom = Math.Min(1, Math.Min(width, height));
|
||||
zoomBar.BarScroll = GetBarScrollValue();
|
||||
viewAreaOffset = Point.Zero;
|
||||
|
||||
@@ -5,12 +5,10 @@ using Microsoft.Xna.Framework.Graphics;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using Microsoft.Xna.Framework.Input;
|
||||
using System.Threading.Tasks;
|
||||
#if DEBUG
|
||||
using System.IO;
|
||||
#else
|
||||
@@ -21,6 +19,12 @@ namespace Barotrauma
|
||||
{
|
||||
class SubEditorScreen : EditorScreen
|
||||
{
|
||||
public const int MaxStructures = 2000;
|
||||
public const int MaxWalls = 500;
|
||||
public const int MaxItems = 5000;
|
||||
public const int MaxLights = 300;
|
||||
public const int MaxShadowCastingLights = 60;
|
||||
|
||||
private static Submarine MainSub
|
||||
{
|
||||
get => Submarine.MainSub;
|
||||
@@ -83,7 +87,11 @@ namespace Barotrauma
|
||||
NoCargoSpawnpoints,
|
||||
NoBallastTag,
|
||||
NonLinkedGaps,
|
||||
TooManyLights
|
||||
StructureCount,
|
||||
WallCount,
|
||||
ItemCount,
|
||||
LightCount,
|
||||
ShadowCastingLightCount
|
||||
}
|
||||
|
||||
public static Vector2 MouseDragStart = Vector2.Zero;
|
||||
@@ -102,7 +110,7 @@ namespace Barotrauma
|
||||
private bool wasSelectedBefore;
|
||||
|
||||
public GUIComponent TopPanel;
|
||||
private GUIComponent showEntitiesPanel, entityCountPanel;
|
||||
public GUIComponent showEntitiesPanel, entityCountPanel;
|
||||
private readonly List<GUITickBox> showEntitiesTickBoxes = new List<GUITickBox>();
|
||||
private readonly Dictionary<string, bool> hiddenSubCategories = new Dictionary<string, bool>();
|
||||
|
||||
@@ -809,7 +817,7 @@ namespace Barotrauma
|
||||
var itemCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), itemCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
|
||||
itemCount.TextGetter = () =>
|
||||
{
|
||||
itemCount.TextColor = ToolBox.GradientLerp(Item.ItemList.Count / 5000.0f, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
|
||||
itemCount.TextColor = Item.ItemList.Count > MaxItems ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, Item.ItemList.Count / (float)MaxItems);
|
||||
return Item.ItemList.Count.ToString();
|
||||
};
|
||||
|
||||
@@ -818,8 +826,8 @@ namespace Barotrauma
|
||||
var structureCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), structureCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
|
||||
structureCount.TextGetter = () =>
|
||||
{
|
||||
int count = (MapEntity.mapEntityList.Count - Item.ItemList.Count - Hull.HullList.Count - WayPoint.WayPointList.Count - Gap.GapList.Count);
|
||||
structureCount.TextColor = ToolBox.GradientLerp(count / 1000.0f, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
|
||||
int count = MapEntity.mapEntityList.Count - Item.ItemList.Count - Hull.HullList.Count - WayPoint.WayPointList.Count - Gap.GapList.Count;
|
||||
structureCount.TextColor = count > MaxStructures ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, count / (float)MaxStructures);
|
||||
return count.ToString();
|
||||
};
|
||||
|
||||
@@ -828,7 +836,7 @@ namespace Barotrauma
|
||||
var wallCount = new GUITextBlock(new RectTransform(new Vector2(0.33f, 1.0f), wallCountText.RectTransform, Anchor.TopRight, Pivot.TopLeft), "", textAlignment: Alignment.CenterRight);
|
||||
wallCount.TextGetter = () =>
|
||||
{
|
||||
wallCount.TextColor = ToolBox.GradientLerp(Structure.WallList.Count / 500.0f, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
|
||||
wallCount.TextColor = Structure.WallList.Count > MaxWalls ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, Structure.WallList.Count / (float)MaxWalls);
|
||||
return Structure.WallList.Count.ToString();
|
||||
};
|
||||
|
||||
@@ -843,7 +851,7 @@ namespace Barotrauma
|
||||
if (item.ParentInventory != null) { continue; }
|
||||
lightCount += item.GetComponents<LightComponent>().Count();
|
||||
}
|
||||
lightCountText.TextColor = ToolBox.GradientLerp(lightCount / 250.0f, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
|
||||
lightCountText.TextColor = lightCount > MaxLights ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, lightCount / (float)MaxLights);
|
||||
return lightCount.ToString();
|
||||
};
|
||||
var shadowCastingLightCountLabel = new GUITextBlock(new RectTransform(new Vector2(0.75f, 0.0f), paddedEntityCountPanel.RectTransform), TextManager.Get("SubEditorShadowCastingLights"),
|
||||
@@ -857,7 +865,7 @@ namespace Barotrauma
|
||||
if (item.ParentInventory != null) { continue; }
|
||||
lightCount += item.GetComponents<LightComponent>().Count(l => l.CastShadows);
|
||||
}
|
||||
shadowCastingLightCountText.TextColor = ToolBox.GradientLerp(lightCount / 60.0f, GUIStyle.Green, GUIStyle.Orange, GUIStyle.Red);
|
||||
shadowCastingLightCountText.TextColor = lightCount > MaxShadowCastingLights ? GUIStyle.Red : Color.Lerp(GUIStyle.Green, GUIStyle.Orange, lightCount / (float)MaxShadowCastingLights);
|
||||
return lightCount.ToString();
|
||||
};
|
||||
entityCountPanel.RectTransform.NonScaledSize =
|
||||
@@ -1448,7 +1456,7 @@ namespace Barotrauma
|
||||
case ".jpeg":
|
||||
if (saveFrame == null) { break; }
|
||||
|
||||
Texture2D texture = Sprite.LoadTexture(filePath);
|
||||
Texture2D texture = Sprite.LoadTexture(filePath, compress: false);
|
||||
previewImage.Sprite = new Sprite(texture, null, null);
|
||||
if (MainSub != null)
|
||||
{
|
||||
@@ -1548,7 +1556,7 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (GUIColorPicker colorPicker in msgBox.GetAllChildren<GUIColorPicker>())
|
||||
{
|
||||
colorPicker.DisposeTextures();
|
||||
colorPicker.Dispose();
|
||||
}
|
||||
|
||||
msgBox.Close();
|
||||
@@ -1827,6 +1835,21 @@ namespace Barotrauma
|
||||
modProject.Save(packagePath);
|
||||
}
|
||||
|
||||
if (!GameMain.DebugDraw)
|
||||
{
|
||||
if (Submarine.GetLightCount() > MaxLights)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("subeditor.lightcounterror", "[max]", MaxShadowCastingLights.ToString()));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Submarine.GetShadowCastingLightCount() > MaxShadowCastingLights)
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("subeditor.shadowcastinglightcounterror", "[max]", MaxShadowCastingLights.ToString()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(name))
|
||||
{
|
||||
GUI.AddMessage(TextManager.Get("SubNameMissingWarning"), GUIStyle.Red);
|
||||
@@ -3634,7 +3657,7 @@ namespace Barotrauma
|
||||
|
||||
closeButton.OnClicked = (button, o) =>
|
||||
{
|
||||
colorPicker.DisposeTextures();
|
||||
colorPicker.Dispose();
|
||||
msgBox.Close();
|
||||
|
||||
Color newColor = SetColor(null);
|
||||
@@ -3678,7 +3701,7 @@ namespace Barotrauma
|
||||
|
||||
cancelButton.OnClicked = (button, o) =>
|
||||
{
|
||||
colorPicker.DisposeTextures();
|
||||
colorPicker.Dispose();
|
||||
msgBox.Close();
|
||||
|
||||
foreach (var (e, color, prop) in entities)
|
||||
|
||||
@@ -21,16 +21,16 @@ namespace Barotrauma
|
||||
private Item? miniMapItem;
|
||||
|
||||
private Submarine? submarine;
|
||||
private Character? dummyCharacter;
|
||||
public static Effect BlueprintEffect = null!;
|
||||
private GUIFrame container = null!;
|
||||
public static Character? dummyCharacter;
|
||||
public static Effect? BlueprintEffect;
|
||||
private GUIFrame? container;
|
||||
|
||||
private TabMenu? tabMenu;
|
||||
|
||||
public TestScreen()
|
||||
{
|
||||
Cam = new Camera();
|
||||
BlueprintEffect = GameMain.GameScreen.BlueprintEffect!;
|
||||
BlueprintEffect = GameMain.GameScreen.BlueprintEffect;
|
||||
|
||||
new GUIButton(new RectTransform(new Point(256, 256), Frame.RectTransform), "Reload shader")
|
||||
{
|
||||
@@ -38,7 +38,7 @@ namespace Barotrauma
|
||||
{
|
||||
BlueprintEffect.Dispose();
|
||||
GameMain.Instance.Content.Unload();
|
||||
BlueprintEffect = GameMain.Instance.Content.Load<Effect>("Effects/blueprintshader_opengl")!;
|
||||
BlueprintEffect = GameMain.Instance.Content.Load<Effect>("Effects/blueprintshader_opengl");
|
||||
GameMain.GameScreen.BlueprintEffect = BlueprintEffect;
|
||||
return true;
|
||||
}
|
||||
@@ -47,21 +47,19 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public override void Select()
|
||||
{
|
||||
{
|
||||
base.Select();
|
||||
container = new GUIFrame(new RectTransform(Vector2.One, GUI.Canvas, Anchor.Center), style: "InnerGlow", color: Color.Black);
|
||||
var tab = new GUIFrame(new RectTransform(Vector2.One, container.RectTransform), color: Color.Black * 0.9f);
|
||||
MedicalClinicUI clinic = new MedicalClinicUI(new MedicalClinic(null!), tab);
|
||||
clinic.RequestLatestPending();
|
||||
if (dummyCharacter is { Removed: false })
|
||||
{
|
||||
dummyCharacter?.Remove();
|
||||
}
|
||||
|
||||
// dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
|
||||
// dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.Where(jp => TalentTree.JobTalentTrees.ContainsKey(jp.Identifier)).GetRandom());
|
||||
// dummyCharacter.Info.Name = "Galldren";
|
||||
// dummyCharacter.Inventory.CreateSlots();
|
||||
dummyCharacter = Character.Create(CharacterPrefab.HumanSpeciesName, Vector2.Zero, "", id: Entity.DummyID, hasAi: false);
|
||||
dummyCharacter.Info.Job = new Job(JobPrefab.Prefabs.Where(jp => TalentTree.JobTalentTrees.ContainsKey(jp.Identifier)).GetRandom(Rand.RandSync.Unsynced));
|
||||
dummyCharacter.Info.Name = "Galldren";
|
||||
dummyCharacter.Inventory.CreateSlots();
|
||||
|
||||
Character.Controlled = dummyCharacter;
|
||||
GameMain.World.ProcessChanges();
|
||||
@@ -71,7 +69,8 @@ namespace Barotrauma
|
||||
public override void AddToGUIUpdateList()
|
||||
{
|
||||
Frame.AddToGUIUpdateList();
|
||||
container.AddToGUIUpdateList();
|
||||
container?.AddToGUIUpdateList();
|
||||
tabMenu?.AddToGUIUpdateList();
|
||||
// CharacterHUD.AddToGUIUpdateList(dummyCharacter);
|
||||
// dummyCharacter?.SelectedConstruction?.AddToGUIUpdateList();
|
||||
}
|
||||
@@ -79,15 +78,13 @@ namespace Barotrauma
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
base.Update(deltaTime);
|
||||
tabMenu!.Update();
|
||||
|
||||
if (dummyCharacter is { } dummy)
|
||||
{
|
||||
dummy.ControlLocalPlayer((float)deltaTime, Cam, false);
|
||||
dummy.Control((float)deltaTime, Cam);
|
||||
}
|
||||
|
||||
GUI.Update((float)deltaTime);
|
||||
tabMenu?.Update((float)deltaTime);
|
||||
}
|
||||
|
||||
public override void Draw(double deltaTime, GraphicsDevice graphics, SpriteBatch spriteBatch)
|
||||
|
||||
@@ -342,9 +342,9 @@ namespace Barotrauma
|
||||
if (property.PropertyType == typeof(string) && value == null)
|
||||
{
|
||||
value = "";
|
||||
}
|
||||
}
|
||||
|
||||
Identifier propertyTag = $"{entity.GetType().Name}.{property.PropertyInfo.Name}".ToIdentifier();
|
||||
Identifier propertyTag = $"{property.PropertyInfo.DeclaringType.Name}.{property.PropertyInfo.Name}".ToIdentifier();
|
||||
Identifier fallbackTag = property.PropertyInfo.Name.ToIdentifier();
|
||||
LocalizedString displayName =
|
||||
TextManager.Get(propertyTag, $"sp.{propertyTag}.name".ToIdentifier());
|
||||
@@ -365,7 +365,7 @@ namespace Barotrauma
|
||||
{
|
||||
displayName = property.Name.FormatCamelCaseWithSpaces();
|
||||
#if DEBUG
|
||||
Editable editable = property.GetAttribute<Editable>();
|
||||
InGameEditable editable = property.GetAttribute<InGameEditable>();
|
||||
if (editable != null)
|
||||
{
|
||||
if (!MissingLocalizations.Contains($"sp.{propertyTag}.name|{displayName}"))
|
||||
@@ -378,7 +378,11 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
|
||||
LocalizedString toolTip = TextManager.Get($"sp.{propertyTag}.description", $"sp.{fallbackTag}.description");
|
||||
LocalizedString toolTip = TextManager.Get($"sp.{propertyTag}.description");
|
||||
if (toolTip.IsNullOrEmpty())
|
||||
{
|
||||
toolTip = TextManager.Get($"{propertyTag}.description", $"sp.{fallbackTag}.description");
|
||||
}
|
||||
|
||||
if (toolTip == null)
|
||||
{
|
||||
@@ -1312,12 +1316,9 @@ namespace Barotrauma
|
||||
entity = e.Item;
|
||||
}
|
||||
|
||||
if (GameMain.Client != null)
|
||||
if (GameMain.Client != null && entity is Item item)
|
||||
{
|
||||
if (entity is IClientSerializable clientSerializable)
|
||||
{
|
||||
GameMain.Client.CreateEntityEvent(clientSerializable, new object[] { NetEntityEvent.Type.ChangeProperty, property });
|
||||
}
|
||||
GameMain.Client.CreateEntityEvent(item, new Item.ChangePropertyEventData(property));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -21,8 +21,8 @@ namespace Barotrauma.SpriteDeformations
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize("", IsPropertySaveable.Yes)]
|
||||
public string TypeName
|
||||
[Serialize("", IsPropertySaveable.No)]
|
||||
public string Type
|
||||
{
|
||||
get;
|
||||
set;
|
||||
@@ -35,7 +35,7 @@ namespace Barotrauma.SpriteDeformations
|
||||
set;
|
||||
}
|
||||
|
||||
public string Name => $"Deformation ({TypeName})";
|
||||
public string Name => $"Deformation ({Type})";
|
||||
|
||||
[Serialize(1.0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2, ValueStep = 0.01f)]
|
||||
public float Strength { get; private set; }
|
||||
@@ -85,11 +85,11 @@ namespace Barotrauma.SpriteDeformations
|
||||
|
||||
public SpriteDeformationParams(XElement element)
|
||||
{
|
||||
if (element != null)
|
||||
{
|
||||
TypeName = element.GetAttributeString("type", "").ToLowerInvariant();
|
||||
}
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
if (element != null && string.IsNullOrEmpty(Type))
|
||||
{
|
||||
Type = element.GetAttributeString("typename", string.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace Barotrauma.SpriteDeformations
|
||||
set { SetResolution(value); }
|
||||
}
|
||||
|
||||
public string TypeName => Params.TypeName;
|
||||
public string TypeName => Params.Type;
|
||||
|
||||
public int Sync => Params.Sync;
|
||||
|
||||
@@ -177,7 +177,7 @@ namespace Barotrauma.SpriteDeformations
|
||||
|
||||
if (newDeformation != null)
|
||||
{
|
||||
newDeformation.Params.TypeName = typeName;
|
||||
newDeformation.Params.Type = typeName;
|
||||
}
|
||||
return newDeformation;
|
||||
}
|
||||
|
||||
@@ -522,7 +522,7 @@ namespace Barotrauma.Steam
|
||||
UserData = tag
|
||||
};
|
||||
tagBtn.RectTransform.NonScaledSize
|
||||
= tagBtn.Font.MeasureString(tagBtn.Text).ToPoint() + new Point(GUI.IntScale(5));
|
||||
= tagBtn.Font.MeasureString(tagBtn.Text).ToPoint() + new Point(GUI.IntScale(15), GUI.IntScale(5));
|
||||
tagBtn.RectTransform.IsFixedSize = true;
|
||||
tagBtn.ClampMouseRectToParent = false;
|
||||
}
|
||||
@@ -625,7 +625,7 @@ namespace Barotrauma.Steam
|
||||
#region Stats box
|
||||
var statsHorizontalLayout = new GUILayoutGroup(new RectTransform(Vector2.One, statsBox.RectTransform), isHorizontal: true);
|
||||
var statsVertical0
|
||||
= new GUILayoutGroup(new RectTransform((1.0f, 1.0f), statsHorizontalLayout.RectTransform));
|
||||
= new GUILayoutGroup(new RectTransform((1.0f, 1.0f), statsHorizontalLayout.RectTransform), childAnchor: Anchor.TopCenter);
|
||||
|
||||
statFrame("", ""); //padding
|
||||
|
||||
@@ -680,7 +680,7 @@ namespace Barotrauma.Steam
|
||||
|
||||
var tagsLabel = new GUITextBlock(new RectTransform((1.0f, 0.12f), statsVertical0.RectTransform),
|
||||
TextManager.Get("WorkshopItemTags"), font: GUIStyle.SubHeadingFont);
|
||||
CreateTagsList(workshopItem.Tags.ToIdentifiers(), new RectTransform((1.0f, 0.3f), statsVertical0.RectTransform), canBeFocused: false);
|
||||
CreateTagsList(workshopItem.Tags.ToIdentifiers(), new RectTransform((0.97f, 0.3f), statsVertical0.RectTransform), canBeFocused: false);
|
||||
#endregion
|
||||
|
||||
var descriptionListBox = new GUIListBox(new RectTransform((1.0f, 0.38f), verticalLayout.RectTransform));
|
||||
|
||||
@@ -71,6 +71,7 @@ namespace Barotrauma.Steam
|
||||
if (GameMain.Client == null)
|
||||
{
|
||||
LeaveLobby();
|
||||
return;
|
||||
}
|
||||
|
||||
if (lobbyState == LobbyState.NotConnected)
|
||||
@@ -83,7 +84,7 @@ namespace Barotrauma.Steam
|
||||
return;
|
||||
}
|
||||
|
||||
var contentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerIncompatibleContent);
|
||||
var contentPackages = ContentPackageManager.EnabledPackages.All.Where(cp => cp.HasMultiplayerSyncedContent);
|
||||
|
||||
currentLobby?.SetData("name", serverSettings.ServerName);
|
||||
currentLobby?.SetData("playercount", (GameMain.Client?.ConnectedClients?.Count ?? 0).ToString());
|
||||
|
||||
@@ -357,21 +357,31 @@ namespace Barotrauma.Steam
|
||||
|
||||
private IEnumerable<CoroutineStatus> MessageBoxCoroutine(Func<GUITextBlock, GUIMessageBox, IEnumerable<CoroutineStatus>> subcoroutine)
|
||||
{
|
||||
var messageBox = new GUIMessageBox("", "", relativeSize: (0.4f, 0.4f), buttons: new [] { TextManager.Get("Cancel") });
|
||||
var messageBox = new GUIMessageBox("", "...", buttons: new [] { TextManager.Get("Cancel") });
|
||||
messageBox.Buttons[0].OnClicked = (button, o) =>
|
||||
{
|
||||
messageBox.Close();
|
||||
return false;
|
||||
};
|
||||
|
||||
var currentStepText = new GUITextBlock(new RectTransform((1.0f, 0.8f), messageBox.InnerFrame.RectTransform),
|
||||
"...", font: GUIStyle.Font)
|
||||
{
|
||||
CanBeFocused = false
|
||||
};
|
||||
|
||||
foreach (var status in subcoroutine(currentStepText, messageBox))
|
||||
var coroutineEval = subcoroutine(messageBox.Text, messageBox);
|
||||
while (true)
|
||||
{
|
||||
bool moveNext = true;
|
||||
try
|
||||
{
|
||||
moveNext = coroutineEval.GetEnumerator().MoveNext();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError($"{e.Message} {e.StackTrace.CleanupStackTrace()}");
|
||||
messageBox.Close();
|
||||
}
|
||||
if (!moveNext)
|
||||
{
|
||||
messageBox.Close();
|
||||
}
|
||||
var status = coroutineEval.GetEnumerator().Current;
|
||||
if (messageBox.Closed)
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
@@ -410,7 +420,7 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
SteamManager.Workshop.ForceRedownload(workshopItem);
|
||||
}
|
||||
currentStepText.Text = $"Downloading {Percentage(workshopItem.DownloadAmount)}";
|
||||
currentStepText.Text = TextManager.GetWithVariable("PublishPopupDownload", "[percentage]", Percentage(workshopItem.DownloadAmount));
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
}
|
||||
@@ -426,7 +436,7 @@ namespace Barotrauma.Steam
|
||||
});
|
||||
while (!ContentPackageManager.WorkshopPackages.Any(p => p.SteamWorkshopId == workshopItem.Id))
|
||||
{
|
||||
currentStepText.Text = $"Installing";
|
||||
currentStepText.Text = TextManager.Get("PublishPopupInstall");
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
@@ -444,7 +454,7 @@ namespace Barotrauma.Steam
|
||||
});
|
||||
while (!localCopyMade)
|
||||
{
|
||||
currentStepText.Text = $"Creating local copy";
|
||||
currentStepText.Text = TextManager.Get("PublishPopupCreateLocal");
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
@@ -457,47 +467,62 @@ namespace Barotrauma.Steam
|
||||
GUITextBlock currentStepText, GUIMessageBox messageBox,
|
||||
string modVersion, Steamworks.Ugc.Editor editor, ContentPackage localPackage)
|
||||
{
|
||||
if (!SteamManager.IsInitialized)
|
||||
{
|
||||
yield return CoroutineStatus.Failure;
|
||||
}
|
||||
|
||||
bool stagingReady = false;
|
||||
Exception? stagingException = null;
|
||||
TaskPool.Add("CreatePublishStagingCopy",
|
||||
SteamManager.Workshop.CreatePublishStagingCopy(modVersion, localPackage),
|
||||
(t) =>
|
||||
{
|
||||
Exception? exception = t.Exception?.InnerException ?? t.Exception;
|
||||
if (exception != null)
|
||||
{
|
||||
throw new Exception($"Failed to create staging copy: {exception.Message} {exception.StackTrace}");
|
||||
}
|
||||
stagingReady = true;
|
||||
stagingException = t.Exception?.GetInnermost();
|
||||
});
|
||||
currentStepText.Text = "Copying item to staging folder...";
|
||||
currentStepText.Text = TextManager.Get("PublishPopupStaging");
|
||||
while (!stagingReady) { yield return new WaitForSeconds(0.5f); }
|
||||
|
||||
if (stagingException != null)
|
||||
{
|
||||
throw new Exception($"Failed to create staging copy: {stagingException.Message} {stagingException.StackTrace.CleanupStackTrace()}");
|
||||
}
|
||||
|
||||
editor = editor
|
||||
.WithContent(SteamManager.Workshop.PublishStagingDir)
|
||||
.ForAppId(SteamManager.AppID);
|
||||
|
||||
messageBox.Buttons[0].Enabled = false;
|
||||
Steamworks.Ugc.PublishResult? result = null;
|
||||
Exception? resultException = null;
|
||||
TaskPool.Add($"Publishing {localPackage.Name} ({localPackage.SteamWorkshopId})",
|
||||
editor.SubmitAsync(),
|
||||
(t) =>
|
||||
t =>
|
||||
{
|
||||
result = ((Task<Steamworks.Ugc.PublishResult>)t).Result;
|
||||
t.TryGetResult(out result);
|
||||
resultException = t.Exception?.GetInnermost();
|
||||
});
|
||||
currentStepText.Text = "Submitting item to the Workshop...";
|
||||
while (!result.HasValue) { yield return new WaitForSeconds(0.5f); }
|
||||
currentStepText.Text = TextManager.Get("PublishPopupSubmit");
|
||||
while (!result.HasValue && resultException is null) { yield return new WaitForSeconds(0.5f); }
|
||||
|
||||
if (result.Value.Success)
|
||||
if (result is { Success: true })
|
||||
{
|
||||
var resultId = result.Value.FileId;
|
||||
Steamworks.Ugc.Item resultItem = new Steamworks.Ugc.Item(resultId);
|
||||
SteamManager.Workshop.ForceRedownload(resultItem);
|
||||
while (!resultItem.IsInstalled)
|
||||
Task downloadTask = SteamManager.Workshop.ForceRedownload(resultItem);
|
||||
while (!resultItem.IsInstalled && !downloadTask.IsCompleted)
|
||||
{
|
||||
currentStepText.Text = $"Downloading {Percentage(resultItem.DownloadAmount)}";
|
||||
currentStepText.Text = TextManager.GetWithVariable("PublishPopupDownload", "[percentage]", Percentage(resultItem.DownloadAmount));
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
if (!resultItem.IsInstalled)
|
||||
{
|
||||
throw new Exception($"Failed to install item: download task ended with status {downloadTask.Status}, " +
|
||||
$"exception was {downloadTask.Exception?.GetInnermost()?.ToString().CleanupStackTrace() ?? "[NULL]"}");
|
||||
}
|
||||
|
||||
bool installed = false;
|
||||
TaskPool.Add(
|
||||
"InstallNewlyPublished",
|
||||
@@ -508,7 +533,7 @@ namespace Barotrauma.Steam
|
||||
});
|
||||
while (!installed)
|
||||
{
|
||||
currentStepText.Text = $"Installing";
|
||||
currentStepText.Text = TextManager.Get("PublishPopupInstall");
|
||||
yield return new WaitForSeconds(0.5f);
|
||||
}
|
||||
|
||||
@@ -524,8 +549,19 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
SteamManager.OverlayCustomURL(resultItem.Url);
|
||||
}
|
||||
new GUIMessageBox(string.Empty, TextManager.GetWithVariable("workshopitempublished", "[itemname]", localPackage.Name));
|
||||
}
|
||||
else if (resultException != null)
|
||||
{
|
||||
throw new Exception($"Failed to publish item: {resultException.Message} {resultException.StackTrace.CleanupStackTrace()}");
|
||||
}
|
||||
else
|
||||
{
|
||||
new GUIMessageBox(TextManager.Get("error"), TextManager.GetWithVariable("workshopitempublishfailed", "[itemname]", localPackage.Name));
|
||||
}
|
||||
|
||||
SteamManager.Workshop.DeletePublishStagingCopy();
|
||||
messageBox.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace Barotrauma.Steam
|
||||
}
|
||||
|
||||
private static int Round(float v) => (int)MathF.Round(v);
|
||||
private static string Percentage(float v) => $"{Round(v * 100)}%";
|
||||
private static string Percentage(float v) => $"{Round(v * 100)}";
|
||||
|
||||
private struct ActionCarrier
|
||||
{
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
#nullable enable
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using RestSharp;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.IO;
|
||||
|
||||
namespace Barotrauma.Steam
|
||||
{
|
||||
@@ -180,8 +179,10 @@ namespace Barotrauma.Steam
|
||||
await CopyDirectory(contentPackage.Dir, contentPackage.Name, Path.GetDirectoryName(contentPackage.Path)!, PublishStagingDir);
|
||||
|
||||
//Load filelist.xml and write the hash into it so anyone downloading this mod knows what it should be
|
||||
ModProject modProject = new ModProject(contentPackage);
|
||||
modProject.ModVersion = modVersion;
|
||||
ModProject modProject = new ModProject(contentPackage)
|
||||
{
|
||||
ModVersion = modVersion
|
||||
};
|
||||
modProject.Save(Path.Combine(PublishStagingDir, ContentPackage.FileListFileName));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.IO;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using ItemOrPackage = Barotrauma.Either<Steamworks.Ugc.Item, Barotrauma.ContentPackage>;
|
||||
|
||||
namespace Barotrauma.Steam
|
||||
@@ -25,12 +20,12 @@ namespace Barotrauma.Steam
|
||||
Publish
|
||||
}
|
||||
|
||||
private GUILayoutGroup tabber;
|
||||
private Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
|
||||
private readonly GUILayoutGroup tabber;
|
||||
private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
|
||||
|
||||
private GUIFrame contentFrame;
|
||||
private readonly GUIFrame contentFrame;
|
||||
|
||||
private CorePackage enabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected");
|
||||
private CorePackage EnabledCorePackage => enabledCoreDropdown.SelectedData as CorePackage ?? throw new Exception("Valid core package not selected");
|
||||
|
||||
private readonly GUIDropDown enabledCoreDropdown;
|
||||
private readonly GUIListBox enabledRegularModsList;
|
||||
@@ -173,7 +168,7 @@ namespace Barotrauma.Steam
|
||||
|
||||
to.DraggedElement = draggedElement;
|
||||
|
||||
to.BarScroll = to.BarScroll * (oldCount / newCount);
|
||||
to.BarScroll *= (oldCount / newCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -367,7 +362,8 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
ToolBox.OpenFileWithShell(mod.Dir);
|
||||
return false;
|
||||
}
|
||||
},
|
||||
ToolTip = TextManager.Get("OpenLocalModInExplorer")
|
||||
};
|
||||
}
|
||||
else if (ContentPackageManager.WorkshopPackages.Contains(mod))
|
||||
@@ -386,8 +382,13 @@ namespace Barotrauma.Steam
|
||||
onInstalledInfoButtonHit(item.Value);
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
ToolTip = TextManager.Get("ViewModDetails")
|
||||
};
|
||||
if (!SteamManager.IsInitialized)
|
||||
{
|
||||
infoButton.Enabled = false;
|
||||
}
|
||||
TaskPool.Add(
|
||||
$"DetermineUpdateRequired{mod.SteamWorkshopId}",
|
||||
mod.IsUpToDate(),
|
||||
@@ -398,6 +399,7 @@ namespace Barotrauma.Steam
|
||||
if (!isUpToDate)
|
||||
{
|
||||
infoButton.ApplyStyle(GUIStyle.ComponentStyles["WorkshopMenu.InfoButtonUpdate"]);
|
||||
infoButton.ToolTip = TextManager.Get("ViewModDetailsUpdateAvailable");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -421,20 +423,26 @@ namespace Barotrauma.Steam
|
||||
private void CreatePopularModsTab(out GUIListBox popularModsList)
|
||||
{
|
||||
GUIFrame content = CreateNewContentFrame(Tab.PopularMods);
|
||||
|
||||
if (!SteamManager.IsInitialized)
|
||||
{
|
||||
tabContents[Tab.PopularMods].Button.Enabled = false;
|
||||
}
|
||||
CreateWorkshopItemList(content, out _, out popularModsList, onSelected: PopulateFrameWithItemInfo);
|
||||
}
|
||||
|
||||
private void CreatePublishTab(out GUIListBox selfModsList)
|
||||
{
|
||||
GUIFrame content = CreateNewContentFrame(Tab.Publish);
|
||||
|
||||
if (!SteamManager.IsInitialized)
|
||||
{
|
||||
tabContents[Tab.Publish].Button.Enabled = false;
|
||||
}
|
||||
CreateWorkshopItemOrPackageList(content, out _, out selfModsList, onSelected: PopulatePublishTab);
|
||||
}
|
||||
|
||||
public void Apply()
|
||||
{
|
||||
ContentPackageManager.EnabledPackages.SetCore(enabledCorePackage);
|
||||
ContentPackageManager.EnabledPackages.SetCore(EnabledCorePackage);
|
||||
ContentPackageManager.EnabledPackages.SetRegular(enabledRegularModsList.Content.Children
|
||||
.Where(c => c.UserData is RegularPackage).Select(c => (RegularPackage)c.UserData).ToArray());
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user