Files
Campofinale/Campofinale/Game/Factory/FactoryManager.cs
2025-08-07 01:05:16 +02:00

900 lines
31 KiB
C#

using Campofinale.Database;
using Campofinale.Game.Entities;
using Campofinale.Game.Factory.Components;
using Campofinale.Packets.Sc;
using Campofinale.Protocol;
using Campofinale.Resource;
using Campofinale.Resource.Table;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using System.Linq;
using System.Numerics;
using System.Xml.Linq;
using static Campofinale.Resource.ResourceManager;
namespace Campofinale.Game.Factory
{
public class FactoryManager
{
public Player player;
public List<FactoryChapter> chapters = new();
public ObjectId _id;
public class FactoryData
{
public ulong roleId;
public ObjectId _id;
public List<FactoryChapter> chapters = new();
}
public FactoryManager(Player player)
{
this.player = player;
}
public void Load()
{
FactoryData data = DatabaseManager.db.LoadFactoryData(player.roleId);
if (data != null)
{
_id=data._id;
chapters = data.chapters;
}
if(!ChapterExist("domain_1")) chapters.Add(new FactoryChapter("domain_1", player.roleId));
if(!ChapterExist("domain_2")) chapters.Add(new FactoryChapter("domain_2", player.roleId));
}
public bool ChapterExist(string id)
{
return chapters.Find(c=>c.chapterId==id)!=null;
}
public void Save()
{
DatabaseManager.db.UpsertFactoryData(new FactoryData()
{
_id= _id,
roleId=player.roleId,
chapters=chapters
});
}
public void ExecOp(CsFactoryOp op, ulong seq)
{
FactoryChapter chapter = GetChapter(op.ChapterId);
if (chapter != null)
{
chapter.ExecOp(op, seq);
}
else
{
ScFactoryOpRet ret = new()
{
RetCode = FactoryOpRetCode.Fail,
};
player.Send(ScMsgId.ScFactoryOpRet, ret, seq);
}
}
public void Update()
{
foreach (FactoryChapter chapter in chapters)
{
chapter.Update();
}
}
public FactoryChapter GetChapter(string id)
{
return chapters.Find(c=>c.chapterId==id);
}
}
public class FactoryChapter
{
public string chapterId;
public ulong ownerId;
public List<FactoryNode> nodes=new();
public uint v = 1;
public uint compV = 0;
public int bandwidth = 200;
public ScFactorySyncChapter ToProto()
{
ScFactorySyncChapter chapter = new()
{
ChapterId = chapterId,
Tms = DateTime.UtcNow.ToUnixTimestampMilliseconds()/1000,
Blackboard = new()
{
Power = new()
{
PowerGen = 0,
PowerSaveMax = 0,
PowerSaveCurrent = 0,
PowerCost = 0
},
InventoryNodeId = 1
},
Statistic = new()
{
LastDay = new()
{
},
Other = new()
{
InPowerBuilding = 1
}
},
PinBoard = new()
{
Cards =
{
},
},
};
chapter.Blackboard.Power.PowerSaveCurrent = bandwidth;
domainDataTable[chapterId].levelGroup.ForEach(levelGroup =>
{
int grade = GetOwner().sceneManager.GetScene(GetSceneNumIdFromLevelData(levelGroup)).grade;
LevelGradeInfo sceneGrade = ResourceManager.levelGradeTable[levelGroup].grades.Find(g=>g.grade==grade);
if (sceneGrade != null)
{
chapter.Blackboard.Power.PowerGen += sceneGrade.bandwidth;
chapter.Blackboard.Power.PowerSaveMax += sceneGrade.bandwidth;
var scene = new ScdFactorySyncScene()
{
SceneId = GetSceneNumIdFromLevelData(levelGroup),
Bandwidth = new()
{
Current = 0,
Max = sceneGrade.bandwidth,
TravelPoleMax = sceneGrade.travelPoleLimit,
BattleCurrent = 0,
BattleMax = sceneGrade.battleBuildingLimit,
},
Settlements =
{
},
Panels =
{
}
};
int index = 0;
LevelScene scen = GetLevelData(GetSceneNumIdFromLevelData(levelGroup));
foreach (var reg in scen.levelData.factoryRegions)
{
foreach (var area in reg.areas)
{
var lvData = area.levelData.Find(l=>l.level==grade);
if (lvData == null)
{
lvData = area.levelData.Last();
}
if (lvData.levelBounds.Count > 0)
{
var bounds = lvData.levelBounds[0];
scene.Panels.Add(new ScdFactorySyncScenePanel()
{
Index = index,
Level = lvData.level,
MainMesh =
{
new ScdRectInt()
{
X=(int)bounds.start.x,
Z=(int)bounds.start.z,
Y=(int)bounds.start.y,
W=(int)bounds.size.x,
H=(int)bounds.size.y,
L=(int)bounds.size.z,
}
}
});
index++;
}
}
}
chapter.Scenes.Add(scene);
}
});
nodes.ForEach(node =>
{
chapter.Nodes.Add(node.ToProto());
});
chapter.Maps.AddRange(GetMaps());
return chapter;
}
public List<ScdFactorySyncMap> GetMaps()
{
List<ScdFactorySyncMap> maps = new();
string levelId = domainDataTable[chapterId].levelGroup[0];
string mapId = GetLevelData(GetSceneNumIdFromLevelData(levelId)).mapIdStr;
maps.Add(new ScdFactorySyncMap()
{
MapId = ResourceManager.strIdNumTable.chapter_map_id.dic[mapId],
Wires =
{
GetWires()
}
});
return maps;
}
public List<ScdFactorySyncMapWire> GetWires()
{
List<ScdFactorySyncMapWire> wires = new();
HashSet<(ulong, ulong)> addedConnections = new(); // evita doppioni esatti
ulong i = 0;
foreach (FactoryNode node in nodes)
{
foreach (var conn in node.connectedComps)
{
ulong compA = conn.Key;
ulong compB = conn.Value;
var key = (compA, compB);
if (!addedConnections.Contains(key))
{
wires.Add(new ScdFactorySyncMapWire()
{
Index = i,
FromComId = compA,
ToComId = compB
});
addedConnections.Add(key);
i++;
}
}
}
return wires;
}
public void Update()
{
try
{
UpdatePowerGrid(nodes);
foreach (FactoryNode node in nodes)
{
node.Update(this);
}
}
catch (Exception e)
{
}
}
public List<FactoryNode> GetNodesInRange(Vector3f pos,float range)
{
return nodes.FindAll(n => n.position.Distance(pos) <= range);
}
public void ExecOp(CsFactoryOp op, ulong seq)
{
switch (op.OpType)
{
case FactoryOpType.Place:
CreateNode(op, seq);
break;
case FactoryOpType.MoveNode:
MoveNode(op, seq);
break;
case FactoryOpType.Dismantle:
DismantleNode(op, seq);
break;
case FactoryOpType.AddConnection:
AddConnection(op, seq);
break;
case FactoryOpType.SetTravelPoleDefaultNext:
FactoryNode travelNode = GetNodeByCompId(op.SetTravelPoleDefaultNext.ComponentId);
travelNode.GetComponent<FComponentTravelPole>().defaultNext = op.SetTravelPoleDefaultNext.DefaultNext;
GetOwner().Send(new PacketScFactoryModifyChapterNodes(GetOwner(), chapterId, travelNode));
GetOwner().Send(new PacketScFactoryOpRet(GetOwner(), 0, op), seq);
break;
default:
break;
}
}
public void MoveNode(CsFactoryOp op, ulong seq)
{
var move = op.MoveNode;
FactoryNode node = nodes.Find(n => n.nodeId == move.NodeId);
if (node != null)
{
node.direction = new Vector3f(move.Direction);
node.position = new Vector3f(move.Position);
node.worldPosition = new Vector3f(move.InteractiveParam.Position);
GetOwner().Send(new PacketScFactoryModifyChapterNodes(GetOwner(), chapterId, node));
GetOwner().Send(new PacketScFactoryOpRet(GetOwner(), node.nodeId, op), seq);
node.SendEntity(GetOwner(), chapterId);
}
else
{
ScFactoryOpRet ret = new()
{
RetCode = FactoryOpRetCode.Fail,
};
GetOwner().Send(ScMsgId.ScFactoryOpRet, ret, seq);
}
}
public void DismantleNode(CsFactoryOp op, ulong seq)
{
var dismantle = op.Dismantle;
FactoryNode nodeRem = nodes.Find(n => n.nodeId == dismantle.NodeId);
if (nodeRem != null)
{
RemoveConnectionsToNode(nodeRem, nodes);
nodes.Remove(nodeRem);
GetOwner().Send(ScMsgId.ScFactoryModifyChapterMap, new ScFactoryModifyChapterMap()
{
ChapterId = chapterId,
MapId = nodeRem.mapId,
Tms = DateTime.UtcNow.ToUnixTimestampMilliseconds() / 1000,
Wires =
{
GetWires()
}
});
GetOwner().Send(new PacketScFactoryModifyChapterNodes(GetOwner(), chapterId, nodeRem.nodeId));
GetOwner().Send(new PacketScFactoryOpRet(GetOwner(), nodeRem.nodeId, op), seq);
}
else
{
ScFactoryOpRet ret = new()
{
RetCode = FactoryOpRetCode.Fail,
};
GetOwner().Send(ScMsgId.ScFactoryOpRet, ret, seq);
}
}
public void RemoveConnectionsToNode(FactoryNode nodeRem, List<FactoryNode> allNodes)
{
// Ottieni tutti i compId del nodo da rimuovere
HashSet<ulong> remCompIds = nodeRem.components.Select(c => (ulong)c.compId).ToHashSet();
foreach (var node in allNodes)
{
node.connectedComps.RemoveAll(conn =>
remCompIds.Contains(conn.Key) || remCompIds.Contains(conn.Value));
}
}
public uint nextCompV()
{
compV++;
return compV;
}
public FactoryNode.FComponent GetCompById(ulong compId)
{
foreach(FactoryNode node in nodes)
{
if (node.components.Find(c => c.compId == compId) != null)
{
return node.components.Find(c => c.compId == compId);
}
}
return null;
}
public FactoryNode GetNodeByCompId(ulong compId)
{
foreach (FactoryNode node in nodes)
{
if (node.components.Find(c => c.compId == compId) != null)
{
return node;
}
}
return null;
}
private void AddConnection(CsFactoryOp op,ulong seq)
{
FactoryNode.FComponent nodeFrom = GetCompById(op.AddConnection.FromComId);
FactoryNode.FComponent nodeTo = GetCompById(op.AddConnection.ToComId);
if(nodeFrom!=null && nodeTo != null)
{
GetNodeByCompId(nodeFrom.compId).connectedComps.Add(new(nodeFrom.compId, nodeTo.compId));
GetNodeByCompId(nodeTo.compId).connectedComps.Add(new(nodeTo.compId, nodeFrom.compId));
GetOwner().Send(ScMsgId.ScFactoryModifyChapterMap, new ScFactoryModifyChapterMap()
{
ChapterId = chapterId,
MapId = GetNodeByCompId(nodeFrom.compId).mapId,
Tms = DateTime.UtcNow.ToUnixTimestampMilliseconds()/1000,
Wires =
{
GetWires()
}
});
var wire = GetWires().Find(w =>
(w.FromComId == op.AddConnection.FromComId && w.ToComId == op.AddConnection.ToComId) ||
(w.FromComId == op.AddConnection.ToComId && w.ToComId == op.AddConnection.FromComId));
if (wire != null)
{
GetOwner().Send(new PacketScFactoryOpRet(GetOwner(), (uint)wire.Index, op), seq);
}
else
{
// Facoltativo: log di errore
Console.WriteLine($"[WARN] Connessione non trovata tra {op.AddConnection.FromComId} e {op.AddConnection.ToComId}");
}
}
}
void ResetAllPower(List<FactoryNode> allNodes)
{
foreach (var node in allNodes)
node.powered = false;
}
void UpdatePowerGrid(List<FactoryNode> allNodes)
{
ResetAllPower(allNodes);
HashSet<uint> visited = new();
foreach (var node in allNodes)
{
if (node.templateId.Contains("hub") || node.templateId == "power_diffuser_1")
{
//if(node.forcePowerOn)
if(node.templateId== "power_diffuser_1")
{
//Check inside factory region
}
else
{
PropagatePowerFrom(node, visited);
}
}
}
}
void PropagatePowerFrom(FactoryNode node, HashSet<uint> visited)
{
if (visited.Contains(node.nodeId))
return;
visited.Add(node.nodeId);
node.powered = true;
if(node.templateId == "power_diffuser_1")
{
//get builds in area test
List<FactoryNode> nodes=GetNodesInRange(node.position, 15);
foreach(FactoryNode propagateNode in nodes)
{
if (propagateNode.GetComponent<FComponentPowerPole>() == null)
{
propagateNode.powered = true;
}
}
}
if (node.GetComponent<FComponentPowerPole>() != null)
foreach (var connectedCompId in node.connectedComps)
{
FactoryNode connectedNode = GetNodeByCompId(connectedCompId.Value);
if (connectedNode != null)
{
PropagatePowerFrom(connectedNode, visited);
}
}
}
private void CreateNode(CsFactoryOp op, ulong seq)
{
v++;
uint nodeId = v;
CsdFactoryOpPlace place = op.Place;
FactoryBuildingTable table = ResourceManager.factoryBuildingTable[place.TemplateId];
FactoryNode node = new()
{
nodeId = nodeId,
templateId = place.TemplateId,
mapId = place.MapId,
sceneNumId = GetOwner().sceneManager.GetCurScene().sceneNumId,
nodeType = table.GetNodeType(),
position = new Vector3f(place.Position),
direction = new Vector3f(place.Direction),
worldPosition = new Vector3f(place.InteractiveParam.Position),
guid = GetOwner().random.NextRand(),
};
node.InitComponents(this);
GetOwner().Send(new PacketScFactoryModifyChapterNodes(GetOwner(), chapterId, node));
nodes.Add(node);
node.SendEntity(GetOwner(), chapterId);
GetOwner().Send(new PacketScFactoryOpRet(GetOwner(), node.nodeId,op),seq);
}
public FactoryChapter(string chapterId,ulong ownerId)
{
this.ownerId = ownerId;
this.chapterId = chapterId;
FactoryNode node = new()
{
nodeId = v,
templateId= "__inventory__",
nodeType=FCNodeType.Inventory,
mapId=0,
deactive=true,
guid = GetOwner().random.NextRand()
};
node.InitComponents(this);
nodes.Add(node);
}
public Player GetOwner()
{
return Server.clients.Find(c => c.roleId == ownerId);
}
}
public class FactoryNode
{
public uint nodeId;
public FCNodeType nodeType;
public string templateId;
public Vector3f position=new();
public Vector3f direction = new();
public Vector3f worldPosition = new();
public string instKey="";
public bool deactive = false;
public int mapId;
public int sceneNumId;
public bool forcePowerOn = false;
public List<FComponent> components = new();
[BsonIgnore]
public bool powered = false;
[BsonIgnore]
public bool lastPowered = false;
public List<ConnectedComp> connectedComps = new();
public ulong guid;
public class ConnectedComp
{
public ulong Key;
public ulong Value;
public ConnectedComp(ulong key, ulong value)
{
this.Key = key;
this.Value = value;
}
}
public void Update(FactoryChapter chapter)
{
LevelScene scen = GetLevelData(sceneNumId);
if (lastPowered != powered)
{
lastPowered = powered;
chapter.GetOwner().Send(new PacketScFactoryModifyChapterNodes(chapter.GetOwner(), chapter.chapterId, this));
}
}
public bool InPower()
{
if (forcePowerOn)
{
return true;
}
return powered;
}
public FComponent GetComponent<FComponent>() where FComponent : class
{
return components.Find(c => c is FComponent) as FComponent;
}
public FMesh GetMesh()
{
FMesh mesh = new FMesh();
if (ResourceManager.factoryBuildingTable.TryGetValue(templateId, out FactoryBuildingTable table))
{
float width = table.range.width - 1;
float height = table.range.height - 1;
float depth = table.range.depth-1;
Vector3f p1_final = new Vector3f();
Vector3f p2_final = new Vector3f();
switch (direction.y)
{
case 0f:
case 360f:
default:
p1_final = position + new Vector3f(table.range.x, table.range.y, table.range.z);
p2_final = p1_final + new Vector3f(width, height, depth);
break;
case 90f:
// Rotazione 90°: Larghezza e profondità si scambiano.
// Il mesh parte da 'position' ma si estende su assi diversi.
p1_final = position + new Vector3f(table.range.x, table.range.y, table.range.z - width);
p2_final = p1_final + new Vector3f(depth, height, width);
break;
case 180f:
// Rotazione 180°: Larghezza e profondità mantengono i loro valori
// ma il mesh si estende in direzioni negative.
p1_final = position + new Vector3f(table.range.x - width, table.range.y, table.range.z - depth);
p2_final = p1_final + new Vector3f(width, height, depth);
break;
case 270f:
// Rotazione 270°: Larghezza e profondità si scambiano.
// Il mesh si estende in direzioni diverse rispetto alla rotazione di 90°.
p1_final = position + new Vector3f(table.range.x - depth, table.range.y, table.range.z);
p2_final = p1_final + new Vector3f(depth, height, width);
break;
}
mesh.points.Add(p1_final);
mesh.points.Add(p2_final);
}
return mesh;
}
public ScdFacNode ToProto()
{
ScdFacNode node = new ScdFacNode()
{
InstKey = instKey,
NodeId=nodeId,
TemplateId=templateId,
StableId= GetStableId(),
IsDeactive= deactive,
Power = new()
{
InPower= InPower(),
NeedInPower=true,
},
NodeType=(int)nodeType,
Transform = new()
{
Position = position.ToProtoScd(),
Direction=direction.ToProtoScd(),
MapId=mapId,
}
};
if(templateId!="__inventory__")
{
node.Transform.Mesh = GetMesh().ToProto();
node.Transform.Position = position.ToProtoScd();
node.Transform.WorldPosition = worldPosition.ToProto();
node.Transform.WorldRotation = direction.ToProto();
node.InteractiveObject = new()
{
ObjectId=guid,
};
node.Flag = 0;
node.InstKey = "";
}
foreach(FComponent comp in components)
{
node.Components.Add(comp.ToProto());
node.ComponentPos.Add((int)comp.GetComPos(), comp.compId);
}
return node;
}
public uint GetStableId()
{
return 10000+nodeId;
}
public FCComponentType GetMainCompType()
{
string nodeTypeName = nodeType.ToString();
if (Enum.TryParse(nodeTypeName, out FCComponentType fromName))
{
return fromName;
}
return FCComponentType.Invalid;
}
public void InitComponents(FactoryChapter chapter)
{
switch (nodeType)
{
case FCNodeType.PowerPole:
components.Add(new FComponentPowerPole(chapter.nextCompV()).Init());
break;
case FCNodeType.PowerDiffuser:
components.Add(new FComponentPowerPole(chapter.nextCompV()).Init());
break;
case FCNodeType.TravelPole:
components.Add(new FComponentTravelPole(chapter.nextCompV()).Init());
break;
case FCNodeType.Hub:
components.Add(new FComponentSelector(chapter.nextCompV()).Init());
components.Add(new FComponentPowerPole(chapter.nextCompV()).Init());
components.Add(new FComponentPowerSave(chapter.nextCompV()).Init());
components.Add(new FComponentStablePower(chapter.nextCompV()).Init());
components.Add(new FComponentBusLoader(chapter.nextCompV()).Init());
components.Add(new FComponentPortManager(chapter.nextCompV(),GetComponent<FComponentBusLoader>().compId).Init());
forcePowerOn = true;
break;
case FCNodeType.SubHub:
components.Add(new FComponentSubHub(chapter.nextCompV()).Init());
components.Add(new FComponentSelector(chapter.nextCompV()).Init());
components.Add(new FComponentPowerPole(chapter.nextCompV()).Init());
components.Add(new FComponentPowerSave(chapter.nextCompV()).Init());
components.Add(new FComponentStablePower(chapter.nextCompV()).Init());
components.Add(new FComponentBusLoader(chapter.nextCompV()).Init());
components.Add(new FComponentPortManager(chapter.nextCompV(), GetComponent<FComponentBusLoader>().compId).Init());
forcePowerOn = true;
break;
default:
components.Add(new FComponent(chapter.nextCompV(), GetMainCompType()).Init());
break;
}
}
public void SendEntity(Player player, string chapterId)
{
Entity exist = player.sceneManager.GetCurScene().entities.Find(e => e.guid == guid);
if (exist != null)
{
exist.Position = worldPosition;
exist.Rotation = direction;
ScMoveObjectMove move = new()
{
ServerNotify = true,
MoveInfo =
{
new MoveObjectMoveInfo()
{
Objid = guid,
SceneNumId=sceneNumId,
MotionInfo = new()
{
Position=exist.Position.ToProto(),
Rotation=exist.Rotation.ToProto(),
Speed=new Vector()
{
},
State=MotionState.MotionNone
}
}
}
};
player.Send(ScMsgId.ScMoveObjectMove, move);
}
else
{
EntityInteractive e = new(interactiveFacWrapperTable[templateId].interactiveTemplateId, player.roleId, worldPosition, direction, sceneNumId, guid);
e.InitDefaultProperties();
e.SetPropValue(nodeId, "factory_inst_id");
player.sceneManager.GetCurScene().entities.Add(e);
player.sceneManager.GetCurScene().SpawnEntity(e);
}
}
[BsonDiscriminator(Required = true)]
[BsonKnownTypes(typeof(FComponentSelector))]
public class FComponent
{
public class FCompInventory
{
public ScdFacComInventory ToProto()
{
return new ScdFacComInventory()
{
};
}
}
public uint compId;
public FCComponentType type;
public FCompInventory inventory;
public FComponent(uint id, FCComponentType t)
{
this.compId = id;
this.type = t;
}
public FCComponentPos GetComPos()
{
string compTypeName = type.ToString();
if (Enum.TryParse(compTypeName, out FCComponentPos fromName))
{
return fromName;
}
switch (type)
{
case FCComponentType.PowerPole:
return FCComponentPos.PowerPole;
}
return FCComponentPos.Invalid;
}
public ScdFacCom ToProto()
{
ScdFacCom proto = new ScdFacCom()
{
ComponentType = (int)type,
ComponentId = compId,
};
SetComponentInfo(proto);
return proto;
}
public virtual void SetComponentInfo(ScdFacCom proto)
{
if (inventory != null)
{
proto.Inventory = inventory.ToProto();
}
else if (type == FCComponentType.PowerPole)
{
proto.PowerPole = new()
{
};
}
}
public virtual FComponent Init()
{
switch (type)
{
case FCComponentType.Inventory:
inventory = new();
break;
default:
break;
}
return this;
}
}
public class FMesh
{
public FCMeshType type;
public List<Vector3f> points=new();
public ScdFacMesh ToProto()
{
ScdFacMesh m = new ScdFacMesh()
{
MeshType = (int)type
};
foreach (Vector3f p in points)
{
m.Points.Add(new ScdVec3Int()
{
X = (int)p.x,
Y = (int)p.y,
Z = (int)p.z
});
}
return m;
}
}
}
}