First Machine Age's Mods (Combined repo.)
Revisão | 3195994595d48d697d116e78f1e42b17169a3214 (tree) |
---|---|
Hora | 2019-11-18 08:28:10 |
Autor | melchior <melchior@user...> |
Commiter | melchior |
W.I.P. #8; Nearly there
@@ -381,7 +381,7 @@ namespace FirstMachineAge | ||
381 | 381 | IServerPlayer serverPlayer = player as IServerPlayer; |
382 | 382 | Vec3i chunkPos = ServerAPI.World.BlockAccessor.ToChunkPos(blockPos); |
383 | 383 | |
384 | - Mod.Logger.VerboseDebug("Applying lock; {0} T{1} @ {2} by {3}", theLock.LockStyle, theLock.LockTier, blockSel.Position, player.PlayerName); | |
384 | + Mod.Logger.VerboseDebug("Applying lock; {0} T{1} @ {2} by {3}", theLock.LockStyle, theLock.LockTier, blockPos, player.PlayerName); | |
385 | 385 | |
386 | 386 | AccessControlNode newLockACN = new AccessControlNode(player.PlayerUID, theLock.LockStyle ); |
387 | 387 |
@@ -399,7 +399,7 @@ namespace FirstMachineAge | ||
399 | 399 | } |
400 | 400 | |
401 | 401 | if (theLock.LockStyle == LockKinds.Key) { |
402 | - newLockACN.KeyID = theLock.KeyID(itemSlot).GetValueOrDefault(PersistedState.KeyId_Sequence); | |
402 | + newLockACN.KeyID = theLock.KeyID(itemSlot).GetValueOrDefault(-1); | |
403 | 403 | |
404 | 404 | //Perform inventory item swap to true Key from lock (create key first...) |
405 | 405 | GenericKey matchingKey = ServerAPI.World.GetItem(new AssetLocation(_domain,_keyCodeName+"-"+material)) as GenericKey; |
@@ -408,7 +408,8 @@ namespace FirstMachineAge | ||
408 | 408 | GenericKey.WriteACL_ItemStack(ref itemStackForKey, newLockACN, blockPos); |
409 | 409 | |
410 | 410 | serverPlayer.InventoryManager.TryGiveItemstack(itemStackForKey, true); |
411 | - //Mark slot dirty? | |
411 | + //Mark slot dirty? | |
412 | + Mod.Logger.VerboseDebug("Created matching Key #{0} for lock@{1}", newLockACN.KeyID, blockPos); | |
412 | 413 | } |
413 | 414 | |
414 | 415 | if (commitACN) |
@@ -540,7 +541,10 @@ namespace FirstMachineAge | ||
540 | 541 | return null; |
541 | 542 | } |
542 | 543 | |
543 | - | |
544 | + public ReadOnlyDictionary<int, KeyValuePair<BlockPos, AccessControlNode>> RetrieveKnownKeys( ) | |
545 | + { | |
546 | + return new ReadOnlyDictionary<int, KeyValuePair<BlockPos, AccessControlNode>>(ACNs_byKeyID); | |
547 | + } | |
544 | 548 | |
545 | 549 | protected bool AttemptAccess(IPlayer byPlayer, BlockPos atPosition, byte[] guess = null) |
546 | 550 | { |
@@ -113,6 +113,9 @@ | ||
113 | 113 | <None Include="assets\fma\shapes\item\access_controls\barrel_lock.json"> |
114 | 114 | <CopyToOutputDirectory>Always</CopyToOutputDirectory> |
115 | 115 | </None> |
116 | + <None Include="assets\fma\itemtypes\locks\keylock.json"> | |
117 | + <CopyToOutputDirectory>Always</CopyToOutputDirectory> | |
118 | + </None> | |
116 | 119 | </ItemGroup> |
117 | 120 | <ItemGroup> |
118 | 121 | <ProjectReference Include="..\FirstMachineAge_Common\Common.csproj"> |
@@ -24,7 +24,7 @@ namespace FirstMachineAge | ||
24 | 24 | /// </summary> |
25 | 25 | public partial class AccessControlsMod |
26 | 26 | { |
27 | - private const string _domain = "FMA"; | |
27 | + private const string _domain = @"fma"; | |
28 | 28 | private const string _AccessControlNodesKey = @"ACCESS_CONTROL_NODES"; |
29 | 29 | private const string _channel_name = @"AccessControl"; |
30 | 30 | internal const string _KeyIDKey = @"key_id";//for JSON attribute, DB key sequence |
@@ -40,12 +40,17 @@ namespace FirstMachineAge | ||
40 | 40 | private ICoreClientAPI ClientAPI; |
41 | 41 | |
42 | 42 | //private ModSystemBlockReinforcement brs; |
43 | - | |
43 | + private ACLPersisted PersistedState;//Holds; Sequence counters... | |
44 | 44 | private Dictionary<Vec3i, ChunkACNodes> Server_ACN;//Track changes - and commit every ## minutes, in addition to server shutdown data-storage, chunk unloads |
45 | 45 | private Dictionary<BlockPos, LockCacheNode> Client_LockLookup;//By BlockPos - for fast local lookup. pre-computed by server... |
46 | - private ACLPersisted PersistedState;//Holds; Sequence counters... | |
46 | + | |
47 | 47 | private SortedDictionary<string, HashSet<Vec3i>> previousChunkSet_byPlayerUID; |
48 | - private SortedDictionary<string, HashSet<int>> playerKeyIDs_byPlayerUID;//Future thread access collision? | |
48 | + private SortedDictionary<int, KeyValuePair<BlockPos, AccessControlNode>> ACNs_byKeyID;//Built on extraction of ACN when loading chunks [1:1] | |
49 | + | |
50 | + private SortedDictionary<string, HashSet<int>> playerKeyIDs_byPlayerUID;//All Keys ~current | |
51 | + private SortedDictionary<string, HashSet<int>> playerLostKeyIDs_byPlayerUID;//Kept only for single cycle | |
52 | + private SortedDictionary<string, HashSet<int>> playerGainKeyIDs_byPlayerUID;//Kept only for single cycle | |
53 | + | |
49 | 54 | |
50 | 55 | //Comm. Channels |
51 | 56 | private IClientNetworkChannel accessControl_ClientChannel; |
@@ -78,6 +83,9 @@ namespace FirstMachineAge | ||
78 | 83 | Server_ACN = new Dictionary<Vec3i, ChunkACNodes>(); |
79 | 84 | previousChunkSet_byPlayerUID = new SortedDictionary<string, HashSet<Vec3i>>( ); |
80 | 85 | playerKeyIDs_byPlayerUID = new SortedDictionary<string, HashSet<int>>( ); |
86 | + playerLostKeyIDs_byPlayerUID = new SortedDictionary<string, HashSet<int>>( ); | |
87 | + playerGainKeyIDs_byPlayerUID = new SortedDictionary<string, HashSet<int>>( ); | |
88 | + ACNs_byKeyID = new SortedDictionary<int, KeyValuePair<BlockPos, AccessControlNode>>( ); | |
81 | 89 | |
82 | 90 | //Await lock-GUI events, send cache updates via NW channel... |
83 | 91 | accessControl_ServerChannel = ServerAPI.Network.RegisterChannel(_channel_name); |
@@ -101,6 +109,7 @@ namespace FirstMachineAge | ||
101 | 109 | ServerAPI.Event.ServerRunPhase(EnumServerRunPhase.Shutdown, PersistACLData); |
102 | 110 | //TODO: Also chunk unload events??? (ideally - should be moot since data would mabey be already saved?) |
103 | 111 | ServerAPI.Event.DidBreakBlock += RemoveACN_byBlockBreakage; |
112 | + ServerAPI.RegisterCommand(new LocksmithCmd(this.ServerAPI)); | |
104 | 113 | |
105 | 114 | Mod.Logger.StoryEvent("...a tumbler turns, and opens\t*click*"); |
106 | 115 | Mod.Logger.VerboseDebug("ACN done server-side Init"); |
@@ -112,12 +121,12 @@ namespace FirstMachineAge | ||
112 | 121 | api.RegisterItemClass("ItemCombolock", typeof(ItemCombolock)); |
113 | 122 | api.RegisterItemClass("ItemKeylock", typeof(ItemKeylock)); |
114 | 123 | api.RegisterItemClass("ItemKey", typeof(GenericKey)); |
115 | - api.RegisterBlockBehaviorClass("Lockable", typeof(BlockBehaviorComplexLockable)); | |
124 | + api.RegisterBlockBehaviorClass("Lockable", typeof(BlockBehaviorComplexLockable)); | |
116 | 125 | } |
117 | 126 | |
118 | 127 | private void InitializeClientSide( ) |
119 | 128 | { |
120 | - if (ClientAPI.Network.DidReceiveChannelId(_channel_name)) Mod.Logger.VerboseDebug("Server side channel: \"{0}\" existant", _channel_name); | |
129 | + Mod.Logger.VerboseDebug("Server side channel: \"{0}\" is {1} ", _channel_name,ClientAPI.Network.GetChannelState(_channel_name)); | |
121 | 130 | accessControl_ClientChannel = ClientAPI.Network.RegisterChannel(_channel_name); |
122 | 131 | accessControl_ClientChannel = accessControl_ClientChannel.RegisterMessageType<LockGUIMessage>( ); |
123 | 132 | accessControl_ClientChannel = accessControl_ClientChannel.RegisterMessageType<LockStatusList>( ); |
@@ -145,9 +154,9 @@ namespace FirstMachineAge | ||
145 | 154 | return true; |
146 | 155 | } |
147 | 156 | |
148 | - internal void LoadACN_fromChunk(Vec3i chunkPos) | |
157 | + internal void LoadACN_fromChunk(Vec3i chunkPos, bool verboseMsg = false) | |
149 | 158 | { |
150 | - //ATTEMPT to Retrieve and add to local cache... | |
159 | + //ATTEMPT to Retrieve and add to local cache: ACN, KeyID lookups... | |
151 | 160 | IServerChunk targetChunk; |
152 | 161 | byte[ ] data = null; |
153 | 162 | long chunkIndex = ServerAPI.World.BulkBlockAccessor.ToChunkIndex3D(chunkPos); |
@@ -173,9 +182,24 @@ namespace FirstMachineAge | ||
173 | 182 | #endif |
174 | 183 | |
175 | 184 | Server_ACN.Add(chunkPos.Clone( ), existingNodes); |
185 | + | |
186 | + var keyCounter = existingNodes.Entries.Count(acn => acn.Value.LockStyle == LockKinds.Key); | |
187 | + if (keyCounter > 0) | |
188 | + { | |
189 | + Mod.Logger.VerboseDebug("ACN has {0} key-lock entries to track", keyCounter); | |
190 | + | |
191 | + foreach (KeyValuePair<BlockPos, AccessControlNode> kvp in existingNodes.Entries.Where(acn => acn.Value.LockStyle == LockKinds.Key)) | |
192 | + { | |
193 | + if (kvp.Value.KeyID.HasValue) { | |
194 | + ACNs_byKeyID.Add(kvp.Value.KeyID.Value, kvp); | |
195 | + } | |
196 | + } | |
197 | + | |
198 | + } | |
199 | + | |
176 | 200 | } else { |
177 | 201 | #if DEBUG |
178 | - Mod.Logger.VerboseDebug("Absent ACN data for chunk: {0}! (placeholder added)", chunkPos); | |
202 | + if (verboseMsg) Mod.Logger.VerboseDebug("Absent ACN data for chunk: {0}! (placeholder added)", chunkPos); | |
179 | 203 | #endif |
180 | 204 | //Setup new AC Node list for this chunk. |
181 | 205 | ChunkACNodes placeHolderNodes = new ChunkACNodes(); |
@@ -188,11 +212,35 @@ namespace FirstMachineAge | ||
188 | 212 | internal void AddACN_ToServerACNs(BlockPos blockPos, AccessControlNode node ) |
189 | 213 | { |
190 | 214 | Vec3i chunkPos = ServerAPI.World.BlockAccessor.ToChunkPos(blockPos); |
215 | + bool success = false; | |
191 | 216 | |
192 | 217 | if (Server_ACN.ContainsKey(chunkPos) ) { |
193 | - Mod.Logger.Debug("Appending to ChunkACNodes at {0}", chunkPos); | |
194 | - Server_ACN[chunkPos].Entries.Add(blockPos, node); | |
195 | - Server_ACN[chunkPos].Altered = true; | |
218 | + | |
219 | + if (Server_ACN[chunkPos].Entries.ContainsKey(blockPos)) | |
220 | + { | |
221 | + var existingACN = Server_ACN[chunkPos].Entries[blockPos]; | |
222 | + if (existingACN.LockStyle == LockKinds.None) { | |
223 | + Server_ACN[chunkPos].Entries[blockPos] = node; | |
224 | + Server_ACN[chunkPos].Altered = true; | |
225 | + Mod.Logger.Debug("Overwrote 'None' -> '{0}', Chunk at {1}", node.LockStyle, chunkPos); | |
226 | + success = true; | |
227 | + } | |
228 | + else { | |
229 | + Mod.Logger.Error("Rejecting overwrite of ACN @{0} was: ({1}){2} to ({3}){4}", blockPos, | |
230 | + existingACN.OwnerPlayerUID, | |
231 | + existingACN.LockStyle, | |
232 | + node.OwnerPlayerUID, | |
233 | + node.LockStyle | |
234 | + ); | |
235 | + } | |
236 | + } | |
237 | + else { | |
238 | + Mod.Logger.Debug("Appending New ACN Chunk at {0}", chunkPos); | |
239 | + Server_ACN[chunkPos].Entries.Add(blockPos, node); | |
240 | + Server_ACN[chunkPos].Altered = true; | |
241 | + success = true; | |
242 | + } | |
243 | + | |
196 | 244 | } |
197 | 245 | else { |
198 | 246 | Mod.Logger.Debug("Created ChunkACNodes for {0}", chunkPos); |
@@ -200,8 +248,19 @@ namespace FirstMachineAge | ||
200 | 248 | Server_ACN[chunkPos].Entries.Add(blockPos, node); |
201 | 249 | Server_ACN[chunkPos].Altered = true; |
202 | 250 | Server_ACN[chunkPos].OriginChunk = chunkPos.Clone( ); |
251 | + success = true; | |
203 | 252 | } |
204 | 253 | |
254 | + if (success && node.LockStyle == LockKinds.Key) { | |
255 | + if (ACNs_byKeyID.ContainsKey(node.KeyID.Value)) { | |
256 | + //Duplicate?? | |
257 | + Mod.Logger.Error("Duplicate ACN_byKeyID ?! #{0} @{1}", node.KeyID.Value, blockPos); | |
258 | + } | |
259 | + else { | |
260 | + ACNs_byKeyID.Add(node.KeyID.Value, new KeyValuePair<BlockPos, AccessControlNode>(blockPos, node)); | |
261 | + } | |
262 | + | |
263 | + } | |
205 | 264 | } |
206 | 265 | |
207 | 266 | private void PreloadACLData( ) |
@@ -357,6 +416,9 @@ namespace FirstMachineAge | ||
357 | 416 | |
358 | 417 | foreach (var update in networkMessage.LockStatesByBlockPos) |
359 | 418 | { |
419 | + #if DEBUG | |
420 | + if (update.Value.LockState != LockStatus.None) Mod.Logger.VerboseDebug("pos {0} LS: {1}", update.Key, update.Value.LockState); | |
421 | + #endif | |
360 | 422 | if (Client_LockLookup.ContainsKey(update.Key)) |
361 | 423 | { |
362 | 424 | //Replace |
@@ -453,7 +515,8 @@ namespace FirstMachineAge | ||
453 | 515 | wake: |
454 | 516 | Mod.Logger.VerboseDebug("Portunus thread awoken"); |
455 | 517 | try { |
456 | - //####################### Fetch ACN's for *INTRODUCED* (to A.C. system) Chunks... ###################### | |
518 | + //####################### Fetch ACN's for *INTRODUCED* (to A.C. system) Chunks... ###################### | |
519 | + uint newChunkCount = 0; | |
457 | 520 | foreach (var chunkEntry in ServerAPI.WorldManager.AllLoadedChunks) { |
458 | 521 | |
459 | 522 | Vec3i loadedPos = ServerMAIN.WorldMap.ChunkPosFromChunkIndex3D(chunkEntry.Key); |
@@ -461,12 +524,13 @@ namespace FirstMachineAge | ||
461 | 524 | if (Server_ACN.ContainsKey(loadedPos) == false) |
462 | 525 | { |
463 | 526 | LoadACN_fromChunk(loadedPos); |
527 | + newChunkCount++; | |
464 | 528 | } |
465 | 529 | } |
530 | + if (newChunkCount > 0) Mod.Logger.Debug("Noticed {0} new chunks", newChunkCount); | |
466 | 531 | |
467 | 532 | //####################### For all online players - ACN's for *new* chunks entered/in ########################### |
468 | 533 | foreach (var player in ServerAPI.World.AllOnlinePlayers) {//TODO: Parallel.ForEach |
469 | - | |
470 | 534 | //var client = this.ServerMAIN.GetConnectedClient(player.PlayerUID); |
471 | 535 | |
472 | 536 | var center = ServerAPI.World.BlockAccessor.ToChunkPos(player.Entity.ServerPos.AsBlockPos.Copy( )); |
@@ -474,7 +538,6 @@ namespace FirstMachineAge | ||
474 | 538 | HashSet<Vec3i> alreadyUpdatedSet = null; |
475 | 539 | if (previousChunkSet_byPlayerUID.TryGetValue(player.PlayerUID, out alreadyUpdatedSet)) |
476 | 540 | { |
477 | - | |
478 | 541 | //All of them for nearest 27 CHUNKs, contacting ['new' Chunk ] |
479 | 542 | var fresh_chunks = Helpers.ComputeChunkBubble(center).Except(alreadyUpdatedSet).ToList( ); |
480 | 543 |
@@ -487,21 +550,51 @@ namespace FirstMachineAge | ||
487 | 550 | introducedNodes.AddRange(ACNs_byChunk); |
488 | 551 | } |
489 | 552 | } |
553 | + if (introducedNodes.Count > 0) | |
554 | + { | |
555 | + #if DEBUG | |
556 | + Mod.Logger.VerboseDebug("Player {0} will get {1} ACNs about chunk {2}", player.PlayerName, introducedNodes.Count, center); | |
557 | + #endif | |
490 | 558 | |
491 | - if (introducedNodes.Count > 0) { | |
492 | - #if DEBUG | |
493 | - Mod.Logger.VerboseDebug("Player {0} will get {1} ACNs about chunk {2}", player.PlayerName, introducedNodes.Count, center); | |
494 | - #endif | |
495 | - | |
496 | - SendClientACNMultiUpdates(player as IServerPlayer, introducedNodes); | |
559 | + SendClientACNMultiUpdates(player as IServerPlayer, introducedNodes); | |
497 | 560 | |
498 | - previousChunkSet_byPlayerUID[player.PlayerUID].AddRange(fresh_chunks); | |
561 | + previousChunkSet_byPlayerUID[player.PlayerUID].AddRange(fresh_chunks); | |
562 | + } | |
563 | + } | |
499 | 564 | } |
565 | + | |
566 | + //################### Key holding status changes ######################## | |
567 | + if (playerLostKeyIDs_byPlayerUID.Count > 0 || playerGainKeyIDs_byPlayerUID.Count > 0) { | |
568 | + Stack<string> playerKeyChanges = new Stack<string>( playerGainKeyIDs_byPlayerUID.Keys.Union(playerLostKeyIDs_byPlayerUID.Keys)); | |
569 | + | |
570 | + while (playerKeyChanges.Count > 0) { | |
571 | + string pid = playerKeyChanges.Pop( ); | |
572 | + | |
573 | + IServerPlayer tgtPlayer = ServerAPI.World.PlayerByUid(pid) as IServerPlayer; | |
574 | + | |
575 | + Mod.Logger.VerboseDebug("Player {0} held key(s) changed ~ re-evaluating ACNs", tgtPlayer.PlayerName); | |
576 | + //Extract ACN from Key's stored position data... | |
577 | + | |
578 | + var ACNs_comboKeys = new List<KeyValuePair<BlockPos, AccessControlNode>>( ); | |
579 | + | |
580 | + if (playerLostKeyIDs_byPlayerUID.ContainsKey(pid)) ACNs_comboKeys = GetACNs_byKeyID(playerLostKeyIDs_byPlayerUID[pid]); | |
581 | + | |
582 | + if (playerGainKeyIDs_byPlayerUID.ContainsKey(pid)) ACNs_comboKeys = ACNs_comboKeys.Concat(GetACNs_byKeyID(playerGainKeyIDs_byPlayerUID[pid])).ToList(); | |
583 | + | |
584 | + if ( ACNs_comboKeys.Count > 0) { | |
585 | + var keyCount = playerKeyIDs_byPlayerUID.ContainsKey(pid) ? playerKeyIDs_byPlayerUID [pid].Count: 0; | |
586 | + Mod.Logger.VerboseDebug("Player {0} - {1} key(s) updates for {2} ACNs", tgtPlayer.PlayerName, keyCount ,ACNs_comboKeys.Count); | |
587 | + SendClientACNMultiUpdates(tgtPlayer, ACNs_comboKeys); | |
588 | + | |
589 | + //Done set - cleanup; | |
590 | + if (playerGainKeyIDs_byPlayerUID.ContainsKey(pid)) playerGainKeyIDs_byPlayerUID[pid].Clear( ); | |
591 | + if (playerLostKeyIDs_byPlayerUID.ContainsKey(pid)) playerLostKeyIDs_byPlayerUID[pid].Clear( ); | |
500 | 592 | } |
501 | - //Keys should already be marking their previous/current owner (called externally) - ACN | |
502 | 593 | |
503 | 594 | } |
504 | 595 | |
596 | + } | |
597 | + | |
505 | 598 | //########################### Persist & SAVE-COMMIT Altered ACNs ! ######################### |
506 | 599 | var alteredCount = Server_ACN.Count(ac => ac.Value.Altered == true); |
507 | 600 | if (alteredCount > 0) Mod.Logger.Debug("There are {0} altered chunk Nodes to persist", alteredCount); |
@@ -536,7 +629,6 @@ namespace FirstMachineAge | ||
536 | 629 | } |
537 | 630 | } |
538 | 631 | |
539 | - | |
540 | 632 | private LockStatusList ComputeLSLFromACNs(ICollection<KeyValuePair<BlockPos, AccessControlNode>> nodesByPos, IServerPlayer byPlayer) |
541 | 633 | { |
542 | 634 | var stati = new Dictionary<BlockPos, LockCacheNode>( ); |
@@ -676,7 +768,7 @@ namespace FirstMachineAge | ||
676 | 768 | { |
677 | 769 | var keyId = GenericKey.KeyID(slot.Itemstack); |
678 | 770 | //PlayerUID ? |
679 | - if (keyId > 0) keyIds.Add(keyId); | |
771 | + if (keyId > 0) keyIds.Add(keyId.Value); | |
680 | 772 | } |
681 | 773 | } |
682 | 774 |
@@ -716,32 +808,40 @@ namespace FirstMachineAge | ||
716 | 808 | IInventory theInv = player.InventoryManager.GetOwnInventory(inventoryClass); |
717 | 809 | |
718 | 810 | ItemSlot alteredSlot = theInv[slotNum]; |
719 | - | |
811 | + | |
720 | 812 | if (alteredSlot.Empty) return; |
721 | - | |
722 | - if (alteredSlot.StorageType == EnumItemStorageFlags.General && | |
813 | + | |
814 | + if ( | |
723 | 815 | alteredSlot.Itemstack.Class == EnumItemClass.Item && |
724 | 816 | alteredSlot.Itemstack.Item.Code.BeginsWith(_domain, _keyCodeName) |
725 | 817 | ) |
726 | 818 | { |
727 | - #if DEBUG | |
728 | - Mod.Logger.VerboseDebug("Key appears on slot# {0} in Inv: {1} for {2}", slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName); | |
819 | + #if DEBUG | |
820 | + Mod.Logger.VerboseDebug("AttainKeyBySlotChange: {0} {1} {2}", player.PlayerName, inventoryClass, slotNum); | |
821 | + Mod.Logger.VerboseDebug("Itemstack(code): {0}", alteredSlot.Itemstack.Collectible.Code); | |
729 | 822 | #endif |
730 | - | |
731 | - var ivbp = alteredSlot.Inventory as InventoryBasePlayer; | |
823 | + | |
824 | + var ivbp = alteredSlot.Inventory as InventoryBasePlayer; | |
732 | 825 | |
733 | 826 | keyID = GenericKey.KeyID(alteredSlot.Itemstack); |
734 | 827 | lockLocation = GenericKey.LockLocation(alteredSlot.Itemstack); |
735 | 828 | |
829 | + #if DEBUG | |
830 | + Mod.Logger.VerboseDebug("Key #{0} gained slot# {1} in Inv: {2} for {3}", keyID, slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName); | |
831 | + #endif | |
832 | + | |
736 | 833 | if (keyID.HasValue && playerKeyIDs_byPlayerUID.ContainsKey(playerUID)) |
737 | 834 | { |
738 | 835 | playerKeyIDs_byPlayerUID[playerUID].Add(keyID.Value); |
836 | + if (playerGainKeyIDs_byPlayerUID.ContainsKey(playerUID)) { playerGainKeyIDs_byPlayerUID[playerUID].Add(keyID.Value); } | |
837 | + else | |
838 | + { | |
839 | + playerGainKeyIDs_byPlayerUID.Add(playerUID, new HashSet<int>()); | |
840 | + playerGainKeyIDs_byPlayerUID[playerUID].Add(keyID.Value); | |
841 | + } | |
739 | 842 | } |
740 | - | |
741 | - | |
742 | - } | |
743 | - | |
744 | 843 | |
844 | + } | |
745 | 845 | } |
746 | 846 | |
747 | 847 | private void RemoveACN_byBlockBreakage(IServerPlayer byPlayer, int oldblockId, BlockSelection blockSel) |
@@ -758,8 +858,7 @@ namespace FirstMachineAge | ||
758 | 858 | Server_ACN[chunkPos].Entries.Remove(adjPos); |
759 | 859 | |
760 | 860 | UpdateBroadcast(byPlayer, adjPos, toRemoveACN); |
761 | - | |
762 | - Mod.Logger.Notification("player {0} broke @({1}) with ACN Owned by [{2}]", byPlayer.PlayerName, adjPos, toRemoveACN.OwnerPlayerUID); | |
861 | + if (toRemoveACN.LockStyle != LockKinds.None) Mod.Logger.Notification("player {0} broke @({1}) with ACN Owned by [{2}]", byPlayer.PlayerName, adjPos, toRemoveACN.OwnerPlayerUID); | |
763 | 862 | } |
764 | 863 | } |
765 | 864 | } |
@@ -773,30 +872,92 @@ namespace FirstMachineAge | ||
773 | 872 | IInventory theInv = player.InventoryManager.GetOwnInventory(inventoryClass); |
774 | 873 | |
775 | 874 | ItemSlot alteredSlot = theInv[slotNum]; |
776 | - | |
875 | + | |
777 | 876 | if (alteredSlot.Empty) return; |
778 | 877 | |
779 | - if (alteredSlot.StorageType == EnumItemStorageFlags.General && | |
878 | + if ( | |
780 | 879 | alteredSlot.Itemstack.Class == EnumItemClass.Item && |
781 | 880 | alteredSlot.Itemstack.Item.Code.BeginsWith(_domain, _keyCodeName) |
782 | - ) { | |
881 | + ) | |
882 | + { | |
783 | 883 | #if DEBUG |
784 | - Mod.Logger.VerboseDebug("Key lost on slot# {0} in Inv: {1} for {2}", slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName); | |
884 | + Mod.Logger.VerboseDebug("LoseKeyBySlotChange: {0} {1} {2}", player.PlayerName, inventoryClass, slotNum); | |
885 | + Mod.Logger.VerboseDebug("Itemstack(code): {0}", alteredSlot.Itemstack.Collectible.Code); | |
785 | 886 | #endif |
786 | 887 | |
787 | - var ivbp = alteredSlot.Inventory as InventoryBasePlayer; | |
888 | + var ivbp = alteredSlot.Inventory as InventoryBasePlayer; | |
788 | 889 | |
789 | 890 | keyID = GenericKey.KeyID(alteredSlot.Itemstack); |
790 | 891 | lockLocation = GenericKey.LockLocation(alteredSlot.Itemstack); |
791 | 892 | |
792 | - if (keyID.HasValue && playerKeyIDs_byPlayerUID.ContainsKey(playerUID)) | |
893 | + #if DEBUG | |
894 | + Mod.Logger.VerboseDebug("Key #{0} lost on slot# {1} in Inv: {2} for {3}", keyID, slotNum, alteredSlot.Inventory.InventoryID, player.PlayerName); | |
895 | + #endif | |
896 | + | |
897 | + if (keyID.HasValue && playerKeyIDs_byPlayerUID.ContainsKey(playerUID)) | |
793 | 898 | { |
794 | - playerKeyIDs_byPlayerUID[playerUID].Remove(keyID.Value); | |
899 | + playerKeyIDs_byPlayerUID[playerUID].Remove(keyID.Value);//Having duplicate keys - is a problem still. | |
900 | + if (playerLostKeyIDs_byPlayerUID.ContainsKey(playerUID)) { playerLostKeyIDs_byPlayerUID[playerUID].Add(keyID.Value); } | |
901 | + else | |
902 | + { | |
903 | + playerLostKeyIDs_byPlayerUID.Add(playerUID, new HashSet<int>( )); | |
904 | + playerLostKeyIDs_byPlayerUID[playerUID].Add(keyID.Value); | |
905 | + } | |
795 | 906 | } |
796 | 907 | } |
797 | 908 | } |
798 | 909 | |
910 | + private List<KeyValuePair<BlockPos, AccessControlNode>> GetACNs_byKeyID(HashSet<int> setKeyIDs) | |
911 | + { | |
912 | + List<KeyValuePair<BlockPos, AccessControlNode>> acnList = new List<KeyValuePair<BlockPos, AccessControlNode>>( ); | |
913 | + | |
914 | + foreach (int keyID in setKeyIDs) | |
915 | + { | |
916 | + if (ACNs_byKeyID.ContainsKey(keyID)) { | |
917 | + acnList.Add(ACNs_byKeyID[keyID]); | |
918 | + }//else: log error? missing ACN in Keys# | |
919 | + } | |
920 | + | |
921 | + return acnList; | |
922 | + } | |
923 | + | |
924 | + private List<AccessControlNode> ExtractACNsFromKeysFromPlayerInventory(string playerID) | |
925 | + { | |
926 | + var targetPlayer = ServerAPI.World.PlayerByUid(playerID); | |
927 | + List<AccessControlNode> associatedACNs = new List<AccessControlNode>( ); | |
928 | + | |
929 | + foreach (var inventory in targetPlayer.InventoryManager.Inventories) { | |
930 | + foreach (ItemSlot slot in inventory.Value) { | |
931 | + if (slot.Empty) continue; | |
932 | + | |
933 | + if (slot.Itemstack.Collectible.ItemClass == EnumItemClass.Item && | |
934 | + slot.Itemstack.Item.Code.BeginsWith(_domain, _keyCodeName)) | |
935 | + { | |
936 | + var keyId = GenericKey.KeyID(slot.Itemstack); | |
937 | + var ACN_location = GenericKey.LockLocation(slot.Itemstack); | |
799 | 938 | |
939 | + if (keyId == null || ACN_location == null) { | |
940 | + Mod.Logger.Error("Corrupt '{3}' Item in {0}'s {2} Slot#{1}", targetPlayer.PlayerName,inventory.Value.GetSlotId(slot), inventory.Value.InventoryID,slot.Itemstack.Item.Code); | |
941 | + continue; | |
942 | + } | |
943 | + | |
944 | + Vec3i locksChunk = ServerAPI.World.BlockAccessor.ToChunkPos(ACN_location); | |
945 | + | |
946 | + if (Server_ACN.ContainsKey(locksChunk) && Server_ACN[locksChunk].Entries.ContainsKey(ACN_location)) { | |
947 | + var controlNode = Server_ACN[locksChunk].Entries[ACN_location];//Instead by KEY# | |
948 | + | |
949 | + associatedACNs.Add(controlNode); | |
950 | + } | |
951 | + else { | |
952 | + Mod.Logger.Warning("Key-lock without ACN match K#{0} @{1}", keyId, ACN_location); | |
953 | + } | |
954 | + } | |
955 | + } | |
956 | + | |
957 | + } | |
958 | + | |
959 | + return associatedACNs; | |
960 | + } | |
800 | 961 | |
801 | 962 | #endregion |
802 | 963 | } |
@@ -17,7 +17,7 @@ namespace FirstMachineAge | ||
17 | 17 | this.Description = "Change lock permissions and assigend groupIDs."; |
18 | 18 | //this.handler += LocksmithParser; |
19 | 19 | this.Syntax = "grant [group/player] [player-name/group-name] / revoke [group/player] [player-name/group-name]"; |
20 | - //this.RequiredPrivilege = "locksmith"; | |
20 | + //this.RequiredPrivilege = "grouplocks"; | |
21 | 21 | |
22 | 22 | } |
23 | 23 | } |
@@ -1,6 +1,7 @@ | ||
1 | 1 | using System; |
2 | 2 | |
3 | 3 | using Vintagestory.API.Common; |
4 | +using Vintagestory.API.Config; | |
4 | 5 | using Vintagestory.API.MathTools; |
5 | 6 | using Vintagestory.API.Server; |
6 | 7 | using Vintagestory.API.Util; |
@@ -8,25 +9,103 @@ using Vintagestory.GameContent; | ||
8 | 9 | |
9 | 10 | namespace FirstMachineAge |
10 | 11 | { |
11 | - public class LocksmithCmd : ServerChatCommand | |
12 | + internal sealed class LocksmithCmd : ServerChatCommand | |
12 | 13 | { |
13 | 14 | private ICoreServerAPI ServerAPI; |
15 | + private ILogger Logger { get; set; } | |
16 | + private AccessControlsMod AccessControlsMod { get; set; } | |
14 | 17 | |
15 | - public LocksmithCmd( ) | |
18 | + public LocksmithCmd(ICoreServerAPI _coreAPI) | |
16 | 19 | { |
17 | - this.Command = "locksmith"; | |
18 | - this.Description = "ALTER LOCKS: Remove or Change keys and combos."; | |
19 | - this.handler += LocksmithParser; | |
20 | - this.Syntax = "remove / change / downgrade / info "; | |
21 | - this.RequiredPrivilege = "locksmith"; | |
20 | + this.Command = "locksmith"; | |
21 | + this.Description = "ALTER LOCKS: Remove or Change keys and combos."; | |
22 | + this.handler += LocksmithParser; | |
23 | + this.Syntax = "remove / change / downgrade / info "; | |
24 | + this.RequiredPrivilege = "locksmith"; | |
22 | 25 | |
26 | + if (_coreAPI.Side.IsServer( )) | |
27 | + { | |
28 | + this.ServerAPI = _coreAPI; | |
29 | + this.Logger = this.ServerAPI.World.Logger; | |
30 | + AccessControlsMod = ServerAPI.World.Api.ModLoader.GetModSystem<AccessControlsMod>( ); | |
31 | + } | |
23 | 32 | |
33 | + } | |
34 | + | |
35 | + private LocksmithCmd( ) | |
36 | + { | |
24 | 37 | |
25 | 38 | } |
26 | 39 | |
27 | 40 | private void LocksmithParser(IServerPlayer player, int groupId, CmdArgs args) |
28 | 41 | { |
29 | - throw new NotImplementedException( ); | |
42 | + if (args.Length > 0) { | |
43 | + string command = args.PopWord( ); | |
44 | + switch (command) { | |
45 | + case "nodes": | |
46 | + PrintNodes(player, groupId, args); | |
47 | + break; | |
48 | + | |
49 | + case "keys": | |
50 | + PrintKeys(player, groupId, args); | |
51 | + break; | |
52 | + | |
53 | + case "remove": | |
54 | + | |
55 | + break; | |
56 | + | |
57 | + case "destroy": | |
58 | + | |
59 | + break; | |
60 | + | |
61 | + default: | |
62 | + player.SendMessage(GlobalConstants.CurrentChatGroup, "unecognised command", EnumChatType.CommandError); | |
63 | + break; | |
64 | + | |
30 | 65 | } |
31 | -} | |
66 | + | |
67 | + //List All ACN in a chunk (current) | |
68 | + //List All ACN in a chunk {param} | |
69 | + //Remove ALL ACN in a chunk (current) | |
70 | + //Remove All ACN in a chunk {param} | |
71 | + //Destroy pointed Lock (and ACN for it) | |
72 | + } | |
73 | + else { | |
74 | + player.SendMessage(GlobalConstants.CurrentChatGroup, "no help yet", EnumChatType.CommandSuccess); | |
75 | + | |
76 | + } | |
77 | + | |
78 | + } | |
79 | + | |
80 | + private void PrintNodes(IServerPlayer player, int groupId, CmdArgs args ) | |
81 | + { | |
82 | + if (args.Length > 0) { | |
83 | + var chunkPos = args.PopVec3i(null); | |
84 | + } | |
85 | + else { | |
86 | + BlockPos location = player.Entity.ServerPos.AsBlockPos; ; | |
87 | + Vec3i chunkPos = ServerAPI.World.BlockAccessor.ToChunkPos(location); | |
88 | + | |
89 | + var acn_List = AccessControlsMod.RetrieveACNs_ByChunk(chunkPos); | |
90 | + | |
91 | + foreach (var acn in acn_List) { | |
92 | + string name = ServerAPI.World.PlayerByUid(acn.Value.OwnerPlayerUID).PlayerName; | |
93 | + player.SendMessage(GlobalConstants.InfoLogChatGroup, $"Node@{acn.Key}:{acn.Value.LockStyle} own:{name} '{acn.Value.NameOfLock}'\n", EnumChatType.CommandSuccess); | |
94 | + } | |
95 | + | |
96 | + } | |
97 | + } | |
98 | + | |
99 | + private void PrintKeys(IServerPlayer player, int groupId, CmdArgs args ) | |
100 | + { | |
101 | + var key_List = AccessControlsMod.RetrieveKnownKeys( ); | |
102 | + | |
103 | + foreach (var acn in key_List) { | |
104 | + string name = ServerAPI.World.PlayerByUid(acn.Value.Value.OwnerPlayerUID).PlayerName; | |
105 | + player.SendMessage(GlobalConstants.InfoLogChatGroup, $"Key#{acn.Key} @{acn.Value.Key} {acn.Value.Value.LockStyle} own:{name} '{acn.Value.Value.NameOfLock}'\n", EnumChatType.CommandSuccess); | |
106 | + } | |
107 | + } | |
108 | + | |
109 | + | |
110 | + } | |
32 | 111 | } |
@@ -2,6 +2,7 @@ | ||
2 | 2 | code: "combolock", |
3 | 3 | class: "ItemCombolock", |
4 | 4 | maxstacksize: 1, |
5 | + storageFlags: 1, | |
5 | 6 | variantgroups: [ |
6 | 7 | { code: "material", states: ["tinbronze", "blackbronze", "bismuthbronze", "iron", "steel" ] }, |
7 | 8 | ], |
@@ -2,19 +2,20 @@ | ||
2 | 2 | code: "key", |
3 | 3 | class: "ItemKey", |
4 | 4 | maxstacksize: 1, |
5 | + storageFlags: 1, | |
5 | 6 | variantgroups: [ |
6 | 7 | { code: "material", states: ["tinbronze", "blackbronze", "bismuthbronze", "iron", "steel" ] }, |
7 | 8 | ], |
8 | 9 | textures: { |
9 | - "material": { base: "item/tool/material/{material}" } | |
10 | + "metal": { base: "game:block/metal/plate/{material}" } | |
10 | 11 | }, |
11 | 12 | shape: { base: "item/access_controls/key1" }, |
12 | 13 | creativeinventory: { "general": ["*"], "gadgets": ["*"] }, |
13 | 14 | guiTransform: { |
14 | - translation: { x: 4, y: 0, z: 0 }, | |
15 | - rotation: { x: 20, y: 16, z: -152 }, | |
15 | + translation: { x: -6, y: 27, z: 0 }, | |
16 | + rotation: { x: 0, y: 21, z: -151 }, | |
16 | 17 | origin: { x: 0.5, y: 0.12, z: 0.5 }, |
17 | - scale: 6 | |
18 | + scale: 2.99 | |
18 | 19 | }, |
19 | 20 | groundTransform: { |
20 | 21 | translation: { x: 0, y: 0, z: 0 }, |
@@ -24,9 +25,9 @@ | ||
24 | 25 | }, |
25 | 26 | fpHandTransform: { |
26 | 27 | translation: { x: 0, y: 0, z: 0 }, |
27 | - rotation: { x: -14, y: -78, z: 17 }, | |
28 | + rotation: { x: -81, y: -78, z: 17 }, | |
28 | 29 | origin: { x: 0.5, y: 0.1, z: 0.5 }, |
29 | - scale: 2.41 | |
30 | + scale: 1.5 | |
30 | 31 | }, |
31 | 32 | tpHandTransform: { |
32 | 33 | translation: { x: -0.83, y: -0.36, z: -0.7 }, |
@@ -0,0 +1,66 @@ | ||
1 | +{ | |
2 | + code: "lockkey", | |
3 | + class: "ItemKeylock", | |
4 | + maxstacksize: 1, | |
5 | + storageFlags: 1, | |
6 | + variantgroups: [ | |
7 | + { code: "material", states: ["tinbronze", "blackbronze", "bismuthbronze", "iron", "steel" ] }, | |
8 | + ], | |
9 | + textures: { | |
10 | + "keymetal": { base: "game:block/metal/plate/{material}" }, | |
11 | + "slotmetal": { base: "game:item/tool/material/copper" }, | |
12 | + "body": { base: "game:item/tool/material/iron" }, | |
13 | + }, | |
14 | + shape: { base: "item/access_controls/barrel_lock" }, | |
15 | + attributesByType: | |
16 | + { | |
17 | + "*-tinbronze": { | |
18 | + lockTier:1, | |
19 | + lockStyle:"Key", | |
20 | + }, | |
21 | + "*-blackbronze": { | |
22 | + lockTier:1, | |
23 | + lockStyle:"Key", | |
24 | + }, | |
25 | + "*-bismuthbronze": { | |
26 | + lockTier:1, | |
27 | + lockStyle:"Key", | |
28 | + }, | |
29 | + "*-iron": { | |
30 | + lockTier:2, | |
31 | + lockStyle:"Key", | |
32 | + }, | |
33 | + "*-steel": { | |
34 | + lockTier:3, | |
35 | + lockStyle:"Key", | |
36 | + }, | |
37 | + "*": { | |
38 | + lockStyle:"Key" | |
39 | + } | |
40 | + }, | |
41 | + creativeinventory: { "general": ["*"], "gadgets": ["*"] }, | |
42 | + guiTransform: { | |
43 | + translation: { x: -8, y: 20, z: 1 }, | |
44 | + rotation: { x: -1, y: 16, z: -152 }, | |
45 | + origin: { x: 0.5, y: 0.12, z: 0.5 }, | |
46 | + scale: 3.3 | |
47 | + }, | |
48 | + groundTransform: { | |
49 | + translation: { x: 0, y: 0, z: 0 }, | |
50 | + rotation: { x: -90, y: 0, z: 0 }, | |
51 | + origin: { x: 0.5, y: 0.2, z: 0.46 }, | |
52 | + scale: 3.5 | |
53 | + }, | |
54 | + fpHandTransform: { | |
55 | + translation: { x: 0, y: 0.09, z: 0 }, | |
56 | + rotation: { x: 19, y: -39, z: 28 }, | |
57 | + origin: { x: 0.5, y: 0.1, z: 0.5 }, | |
58 | + scale: 1.25 | |
59 | + }, | |
60 | + tpHandTransform: { | |
61 | + translation: { x: -0.83, y: -0.36, z: -0.7 }, | |
62 | + rotation: { x: 1, y: 57, z: 18 }, | |
63 | + origin: { x: 0.5, y: 0.15, z: 0.5 }, | |
64 | + scale: 0.65 | |
65 | + } | |
66 | +} |
@@ -1,247 +1,222 @@ | ||
1 | -{ | |
2 | - "editor": { | |
3 | - "allAngles": false, | |
4 | - "singleTexture": false | |
5 | - }, | |
6 | - "textureWidth": 16, | |
7 | - "textureHeight": 16, | |
8 | - "textures": { | |
9 | - "iron2": "item/tool/material/iron", | |
10 | - "iron": "iron", | |
11 | - "copper": "item/tool/material/copper" | |
12 | - }, | |
13 | - "elements": [ | |
14 | - { | |
15 | - "name": "root", | |
16 | - "from": [ 8.0, 11.0, 0.0 ], | |
17 | - "to": [ 9.0, 12.0, 1.0 ], | |
18 | - "rotationOrigin": [ 8.0, 11.0, 0.0 ], | |
19 | - "faces": { | |
20 | - "north": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
21 | - "east": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
22 | - "south": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
23 | - "west": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
24 | - "up": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
25 | - "down": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false } | |
26 | - }, | |
27 | - "children": [ | |
28 | - { | |
29 | - "name": "ubolt_root", | |
30 | - "from": [ -1.0, -2.0, 0.5 ], | |
31 | - "to": [ 1.0, -1.2, 1.3 ], | |
32 | - "rotationOrigin": [ 0.0, 0.0, 0.5 ], | |
33 | - "faces": { | |
34 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
35 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
36 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
37 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
38 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
39 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false } | |
40 | - }, | |
41 | - "children": [ | |
42 | - { | |
43 | - "name": "uboltA", | |
44 | - "from": [ -0.2, -0.1, 0.1 ], | |
45 | - "to": [ 2.2, 0.5, 0.7 ], | |
46 | - "rotationOrigin": [ -0.2, -0.1, 0.1 ], | |
47 | - "faces": { | |
48 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
49 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
50 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
51 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
52 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
53 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false } | |
54 | - } | |
55 | - }, | |
56 | - { | |
57 | - "name": "uboltB", | |
58 | - "from": [ -0.2, -1.1, 0.1 ], | |
59 | - "to": [ 0.5, -0.1, 0.7 ], | |
60 | - "rotationOrigin": [ -0.2, -1.1, 0.1 ], | |
61 | - "faces": { | |
62 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
63 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
64 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
65 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
66 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
67 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false } | |
68 | - } | |
69 | - }, | |
70 | - { | |
71 | - "name": "uboltC", | |
72 | - "from": [ 1.5, -1.1, 0.1 ], | |
73 | - "to": [ 2.2, -0.1, 0.7 ], | |
74 | - "rotationOrigin": [ 1.5, -1.1, 0.1 ], | |
75 | - "faces": { | |
76 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
77 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
78 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
79 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
80 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
81 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false } | |
82 | - } | |
83 | - } | |
84 | - ] | |
85 | - }, | |
86 | - { | |
87 | - "name": "uboltD", | |
88 | - "from": [ -1.0, -3.0, 0.5 ], | |
89 | - "to": [ -0.6, -2.0, 1.3 ], | |
90 | - "rotationOrigin": [ 0.0, -1.0, 0.5 ], | |
91 | - "faces": { | |
92 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
93 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
94 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
95 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
96 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
97 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false } | |
98 | - } | |
99 | - }, | |
100 | - { | |
101 | - "name": "uboltE", | |
102 | - "from": [ 0.6, -3.0, 0.5 ], | |
103 | - "to": [ 1.0, -2.0, 1.3 ], | |
104 | - "rotationOrigin": [ 1.6, -1.0, 0.5 ], | |
105 | - "faces": { | |
106 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
107 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
108 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
109 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
110 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
111 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false } | |
112 | - } | |
113 | - }, | |
114 | - { | |
115 | - "name": "Cube1", | |
116 | - "from": [ -2.0, -5.0, 0.1 ], | |
117 | - "to": [ 1.5, -3.4, 1.7 ], | |
118 | - "rotationOrigin": [ -2.0, -5.0, 0.1 ], | |
119 | - "faces": { | |
120 | - "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }, | |
121 | - "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.5, 1.5 ] }, | |
122 | - "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }, | |
123 | - "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.5, 1.5 ] }, | |
124 | - "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }, | |
125 | - "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.5 ] } | |
126 | - }, | |
127 | - "children": [ | |
128 | - { | |
129 | - "name": "Cube9", | |
130 | - "from": [ 0.0, 1.6, 0.3 ], | |
131 | - "to": [ 3.5, 1.9, 1.3 ], | |
132 | - "rotationOrigin": [ 0.0, 1.6, 0.3 ], | |
133 | - "faces": { | |
134 | - "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
135 | - "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
136 | - "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
137 | - "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
138 | - "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }, | |
139 | - "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false } | |
140 | - } | |
141 | - }, | |
142 | - { | |
143 | - "name": "Cube10", | |
144 | - "from": [ 0.0, -0.3, 0.3 ], | |
145 | - "to": [ 3.5, 0.0, 1.3 ], | |
146 | - "rotationOrigin": [ 0.0, -0.3, 0.3 ], | |
147 | - "faces": { | |
148 | - "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
149 | - "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
150 | - "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
151 | - "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
152 | - "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }, | |
153 | - "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] } | |
154 | - } | |
155 | - }, | |
156 | - { | |
157 | - "name": "Cube11", | |
158 | - "from": [ 0.0, 0.2, -0.3 ], | |
159 | - "to": [ 3.5, 1.4, 0.0 ], | |
160 | - "rotationOrigin": [ 0.0, 0.2, -0.3 ], | |
161 | - "faces": { | |
162 | - "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }, | |
163 | - "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
164 | - "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }, | |
165 | - "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
166 | - "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
167 | - "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] } | |
168 | - } | |
169 | - }, | |
170 | - { | |
171 | - "name": "Cube12", | |
172 | - "from": [ 0.0, 0.2, 1.6 ], | |
173 | - "to": [ 3.5, 1.4, 1.9 ], | |
174 | - "rotationOrigin": [ 0.0, 0.2, 1.6 ], | |
175 | - "faces": { | |
176 | - "north": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }, | |
177 | - "east": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
178 | - "south": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }, | |
179 | - "west": { "texture": "#iron", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
180 | - "up": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
181 | - "down": { "texture": "#iron", "uv": [ 0.0, 0.0, 3.5, 0.5 ] } | |
182 | - } | |
183 | - }, | |
184 | - { | |
185 | - "name": "Cube14", | |
186 | - "from": [ -1.1, 0.5, 0.6 ], | |
187 | - "to": [ -0.1, 1.1, 1.0 ], | |
188 | - "rotationOrigin": [ -1.1, 0.5, 0.6 ], | |
189 | - "faces": { | |
190 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
191 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
192 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
193 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
194 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
195 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 1.0, 0.5 ] } | |
196 | - }, | |
197 | - "children": [ | |
198 | - { | |
199 | - "name": "Cube15", | |
200 | - "from": [ -0.2, 0.4, 0.1 ], | |
201 | - "to": [ 0.6, 1.1, 0.3 ], | |
202 | - "rotationOrigin": [ -0.2, 0.4, 0.1 ], | |
203 | - "faces": { | |
204 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
205 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
206 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
207 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
208 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
209 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
210 | - } | |
211 | - }, | |
212 | - { | |
213 | - "name": "Cube16", | |
214 | - "from": [ -0.2, -0.5, 0.1 ], | |
215 | - "to": [ 0.6, 0.2, 0.3 ], | |
216 | - "rotationOrigin": [ -0.2, -0.5, 0.1 ], | |
217 | - "faces": { | |
218 | - "north": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
219 | - "east": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
220 | - "south": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
221 | - "west": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
222 | - "up": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
223 | - "down": { "texture": "#iron2", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
224 | - } | |
225 | - } | |
226 | - ] | |
227 | - } | |
228 | - ] | |
229 | - }, | |
230 | - { | |
231 | - "name": "Cube13", | |
232 | - "from": [ -2.1, -4.9, 0.2 ], | |
233 | - "to": [ -2.0, -3.5, 1.6 ], | |
234 | - "rotationOrigin": [ -2.1, -4.2, 0.9 ], | |
235 | - "rotationX": 45.0, | |
236 | - "faces": { | |
237 | - "north": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
238 | - "east": { "texture": "#copper", "uv": [ 0.0, 0.0, 1.0, 1.0 ] }, | |
239 | - "south": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
240 | - "west": { "texture": "#copper", "uv": [ 0.0, 0.0, 1.0, 1.0 ] }, | |
241 | - "up": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
242 | - "down": { "texture": "#copper", "uv": [ 0.0, 0.0, 0.5, 1.0 ] } | |
243 | - } | |
244 | - } | |
245 | - ] | |
246 | - } | |
1 | +{ | |
2 | + "editor": { | |
3 | + "allAngles": false, | |
4 | + "singleTexture": false | |
5 | + }, | |
6 | + "textureWidth": 16, | |
7 | + "textureHeight": 16, | |
8 | + "textures": { | |
9 | + "keymetal": "item/tool/material/copper", | |
10 | + "slotmetal": "item/tool/material/copper", | |
11 | + "body": "item/tool/material/iron" | |
12 | + }, | |
13 | + "elements": [ | |
14 | + { | |
15 | + "name": "root", | |
16 | + "from": [ 8.0, 11.0, 0.0 ], | |
17 | + "to": [ 9.0, 12.0, 1.0 ], | |
18 | + "rotationOrigin": [ 8.0, 11.0, 0.0 ], | |
19 | + "faces": { | |
20 | + "north": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
21 | + "east": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
22 | + "south": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
23 | + "west": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
24 | + "up": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false }, | |
25 | + "down": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 1.0 ], "enabled": false } | |
26 | + }, | |
27 | + "children": [ | |
28 | + { | |
29 | + "name": "ubolt", | |
30 | + "from": [ -1.0, -2.0, 0.5 ], | |
31 | + "to": [ 1.0, -1.2, 1.3 ], | |
32 | + "rotationOrigin": [ 0.0, 0.0, 0.5 ], | |
33 | + "faces": { | |
34 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
35 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
36 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
37 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
38 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
39 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false } | |
40 | + }, | |
41 | + "children": [ | |
42 | + { | |
43 | + "name": "uboltA", | |
44 | + "from": [ -0.2, -0.1, 0.1 ], | |
45 | + "to": [ 2.2, 0.5, 0.7 ], | |
46 | + "rotationOrigin": [ -0.2, -0.1, 0.1 ], | |
47 | + "faces": { | |
48 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
49 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
50 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
51 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
52 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ] }, | |
53 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 2.0, 0.5 ], "enabled": false } | |
54 | + } | |
55 | + }, | |
56 | + { | |
57 | + "name": "uboltB", | |
58 | + "from": [ -0.2, -1.1, 0.1 ], | |
59 | + "to": [ 0.5, -0.1, 0.7 ], | |
60 | + "rotationOrigin": [ -0.2, -1.1, 0.1 ], | |
61 | + "faces": { | |
62 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
63 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
64 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
65 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
66 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
67 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false } | |
68 | + } | |
69 | + }, | |
70 | + { | |
71 | + "name": "uboltC", | |
72 | + "from": [ 1.5, -1.1, 0.1 ], | |
73 | + "to": [ 2.2, -0.1, 0.7 ], | |
74 | + "rotationOrigin": [ 1.5, -1.1, 0.1 ], | |
75 | + "faces": { | |
76 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
77 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
78 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
79 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
80 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
81 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 0.5, 0.5 ], "enabled": false } | |
82 | + } | |
83 | + } | |
84 | + ] | |
85 | + }, | |
86 | + { | |
87 | + "name": "Body", | |
88 | + "from": [ -2.0, -5.0, 0.1 ], | |
89 | + "to": [ 1.5, -3.4, 1.7 ], | |
90 | + "rotationOrigin": [ -2.0, -5.0, 0.1 ], | |
91 | + "faces": { | |
92 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }, | |
93 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 1.5, 1.5 ] }, | |
94 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }, | |
95 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 1.5, 1.5 ] }, | |
96 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] }, | |
97 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.5 ] } | |
98 | + }, | |
99 | + "children": [ | |
100 | + { | |
101 | + "name": "Cube9", | |
102 | + "from": [ 0.0, 1.6, 0.3 ], | |
103 | + "to": [ 3.5, 1.9, 1.3 ], | |
104 | + "rotationOrigin": [ 0.0, 1.6, 0.3 ], | |
105 | + "faces": { | |
106 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
107 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
108 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
109 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
110 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }, | |
111 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false } | |
112 | + } | |
113 | + }, | |
114 | + { | |
115 | + "name": "Cube10", | |
116 | + "from": [ 0.0, -0.3, 0.3 ], | |
117 | + "to": [ 3.5, 0.0, 1.3 ], | |
118 | + "rotationOrigin": [ 0.0, -0.3, 0.3 ], | |
119 | + "faces": { | |
120 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
121 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
122 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
123 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
124 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }, | |
125 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] } | |
126 | + } | |
127 | + }, | |
128 | + { | |
129 | + "name": "Cube11", | |
130 | + "from": [ 0.0, 0.2, -0.3 ], | |
131 | + "to": [ 3.5, 1.4, 0.0 ], | |
132 | + "rotationOrigin": [ 0.0, 0.2, -0.3 ], | |
133 | + "faces": { | |
134 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }, | |
135 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] }, | |
136 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }, | |
137 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] }, | |
138 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
139 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] } | |
140 | + } | |
141 | + }, | |
142 | + { | |
143 | + "name": "Cube12", | |
144 | + "from": [ 0.0, 0.2, 1.6 ], | |
145 | + "to": [ 3.5, 1.4, 1.9 ], | |
146 | + "rotationOrigin": [ 0.0, 0.2, 1.6 ], | |
147 | + "faces": { | |
148 | + "north": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ], "enabled": false }, | |
149 | + "east": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] }, | |
150 | + "south": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 1.0 ] }, | |
151 | + "west": { "texture": "#body", "uv": [ 0.0, 0.0, 0.25, 1.0 ] }, | |
152 | + "up": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] }, | |
153 | + "down": { "texture": "#body", "uv": [ 0.0, 0.0, 3.5, 0.5 ] } | |
154 | + } | |
155 | + } | |
156 | + ] | |
157 | + }, | |
158 | + { | |
159 | + "name": "SlotFace", | |
160 | + "from": [ -2.1, -4.9, 0.2 ], | |
161 | + "to": [ -2.0, -3.5, 1.6 ], | |
162 | + "rotationOrigin": [ -2.1, -4.2, 0.9 ], | |
163 | + "rotationX": 45.0, | |
164 | + "faces": { | |
165 | + "north": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
166 | + "east": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 1.0, 1.0 ] }, | |
167 | + "south": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
168 | + "west": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 1.0, 1.0 ] }, | |
169 | + "up": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
170 | + "down": { "texture": "#slotmetal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] } | |
171 | + }, | |
172 | + "children": [ | |
173 | + { | |
174 | + "name": "KeyStem", | |
175 | + "from": [ -1.0, 0.3, 0.65 ], | |
176 | + "to": [ 0.0, 0.9, 1.05 ], | |
177 | + "rotationOrigin": [ -1.1, 0.5, 0.6 ], | |
178 | + "rotationX": -45.0, | |
179 | + "faces": { | |
180 | + "north": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
181 | + "east": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.25, 0.5 ] }, | |
182 | + "south": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
183 | + "west": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.25, 0.5 ] }, | |
184 | + "up": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] }, | |
185 | + "down": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 1.0, 0.5 ] } | |
186 | + }, | |
187 | + "children": [ | |
188 | + { | |
189 | + "name": "KeyBit1", | |
190 | + "from": [ -0.2, 0.4, 0.1 ], | |
191 | + "to": [ 0.6, 1.1, 0.3 ], | |
192 | + "rotationOrigin": [ -0.2, 0.4, 0.1 ], | |
193 | + "faces": { | |
194 | + "north": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
195 | + "east": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
196 | + "south": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
197 | + "west": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
198 | + "up": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
199 | + "down": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
200 | + } | |
201 | + }, | |
202 | + { | |
203 | + "name": "KeyBit2", | |
204 | + "from": [ -0.2, -0.5, 0.1 ], | |
205 | + "to": [ 0.6, 0.2, 0.3 ], | |
206 | + "rotationOrigin": [ -0.2, -0.5, 0.1 ], | |
207 | + "faces": { | |
208 | + "north": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
209 | + "east": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
210 | + "south": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
211 | + "west": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
212 | + "up": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
213 | + "down": { "texture": "#keymetal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
214 | + } | |
215 | + } | |
216 | + ] | |
217 | + } | |
218 | + ] | |
219 | + } | |
220 | + ] | |
221 | + } | |
247 | 222 | ]} |
\ No newline at end of file |
@@ -1,82 +1,88 @@ | ||
1 | 1 | { |
2 | 2 | "editor": { |
3 | 3 | "allAngles": false, |
4 | - "singleTexture": false | |
4 | + "singleTexture": true | |
5 | 5 | }, |
6 | 6 | "textureWidth": 16, |
7 | 7 | "textureHeight": 16, |
8 | 8 | "textures": { |
9 | + "metal": "block/metal/plate/tin" | |
9 | 10 | }, |
10 | 11 | "elements": [ |
11 | 12 | { |
12 | 13 | "name": "Stem", |
13 | 14 | "from": [ 6.0, 7.0, 8.0 ], |
14 | 15 | "to": [ 11.0, 7.75, 8.75 ], |
16 | + "uv": [ 0.0, 0.0 ], | |
15 | 17 | "rotationOrigin": [ 6.0, 7.0, 8.0 ], |
16 | 18 | "faces": { |
17 | - "north": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }, | |
18 | - "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
19 | - "south": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }, | |
20 | - "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
21 | - "up": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }, | |
22 | - "down": { "texture": "#null", "uv": [ 0.0, 0.0, 5.0, 0.5 ] } | |
19 | + "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }, | |
20 | + "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
21 | + "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }, | |
22 | + "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
23 | + "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] }, | |
24 | + "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 5.0, 0.5 ] } | |
23 | 25 | }, |
24 | 26 | "children": [ |
25 | 27 | { |
26 | 28 | "name": "Hole", |
27 | 29 | "from": [ -1.5, 0.75, 0.0 ], |
28 | 30 | "to": [ 0.5, 2.75, 1.0 ], |
31 | + "uv": [ 0.0, 0.0 ], | |
29 | 32 | "rotationOrigin": [ -2.0, 1.0, 0.0 ], |
30 | 33 | "rotationZ": -45.0, |
31 | 34 | "faces": { |
32 | - "north": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }, | |
33 | - "east": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 2.0 ] }, | |
34 | - "south": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }, | |
35 | - "west": { "texture": "#null", "uv": [ 0.0, 0.0, 1.0, 2.0 ] }, | |
36 | - "up": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 1.0 ] }, | |
37 | - "down": { "texture": "#null", "uv": [ 0.0, 0.0, 2.0, 1.0 ] } | |
35 | + "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }, | |
36 | + "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 1.0, 2.0 ] }, | |
37 | + "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 2.0 ] }, | |
38 | + "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 1.0, 2.0 ] }, | |
39 | + "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 1.0 ] }, | |
40 | + "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 2.0, 1.0 ] } | |
38 | 41 | } |
39 | 42 | }, |
40 | 43 | { |
41 | 44 | "name": "Tooth1", |
42 | 45 | "from": [ 5.0, 0.0, 0.0 ], |
43 | 46 | "to": [ 5.5, 2.0, 0.75 ], |
47 | + "uv": [ 0.0, 0.0 ], | |
44 | 48 | "rotationOrigin": [ 5.0, 0.0, 0.0 ], |
45 | 49 | "faces": { |
46 | - "north": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
47 | - "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
48 | - "south": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
49 | - "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
50 | - "up": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
51 | - "down": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
50 | + "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
51 | + "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
52 | + "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
53 | + "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
54 | + "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
55 | + "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
52 | 56 | } |
53 | 57 | }, |
54 | 58 | { |
55 | 59 | "name": "Tooth2", |
56 | 60 | "from": [ 5.5, 0.0, 0.0 ], |
57 | 61 | "to": [ 6.25, 1.0, 0.75 ], |
62 | + "uv": [ 0.0, 0.0 ], | |
58 | 63 | "rotationOrigin": [ 5.0, 0.0, 0.0 ], |
59 | 64 | "faces": { |
60 | - "north": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
61 | - "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
62 | - "south": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
63 | - "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
64 | - "up": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
65 | - "down": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
65 | + "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
66 | + "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
67 | + "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
68 | + "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 1.0 ] }, | |
69 | + "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
70 | + "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
66 | 71 | } |
67 | 72 | }, |
68 | 73 | { |
69 | 74 | "name": "Tooth3", |
70 | 75 | "from": [ 6.25, 0.0, 0.0 ], |
71 | 76 | "to": [ 6.75, 2.0, 0.75 ], |
77 | + "uv": [ 0.0, 0.0 ], | |
72 | 78 | "rotationOrigin": [ 6.0, 0.0, 0.0 ], |
73 | 79 | "faces": { |
74 | - "north": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
75 | - "east": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
76 | - "south": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
77 | - "west": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
78 | - "up": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
79 | - "down": { "texture": "#null", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
80 | + "north": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
81 | + "east": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
82 | + "south": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
83 | + "west": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 2.0 ] }, | |
84 | + "up": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] }, | |
85 | + "down": { "texture": "#metal", "uv": [ 0.0, 0.0, 0.5, 0.5 ] } | |
80 | 86 | } |
81 | 87 | } |
82 | 88 | ] |
@@ -11,6 +11,11 @@ namespace FirstMachineAge | ||
11 | 11 | [ProtoContract] |
12 | 12 | public class ACLPersisted |
13 | 13 | { |
14 | + public ACLPersisted( ) | |
15 | + { | |
16 | + KeyId_Sequence = 1; | |
17 | + } | |
18 | + | |
14 | 19 | [ProtoMember(1)] |
15 | 20 | public int KeyId_Sequence; |
16 | 21 |
@@ -1,24 +1,25 @@ | ||
1 | 1 | using System; |
2 | 2 | using System.Text; |
3 | - | |
3 | +using Vintagestory.API.Client; | |
4 | 4 | using Vintagestory.API.Common; |
5 | 5 | using Vintagestory.API.MathTools; |
6 | +using Vintagestory.API.Server; | |
6 | 7 | |
7 | 8 | namespace FirstMachineAge |
8 | 9 | { |
9 | 10 | public class GenericKey : Item |
10 | - { | |
11 | + { | |
11 | 12 | |
12 | - public static int KeyID(ItemStack sourceStack) | |
13 | + public static int? KeyID(ItemStack sourceStack) | |
13 | 14 | { |
14 | 15 | if (sourceStack.Attributes.HasAttribute(AccessControlsMod._KeyIDKey)) { |
15 | 16 | return sourceStack.Attributes.GetInt(AccessControlsMod._KeyIDKey); |
16 | 17 | } else { |
17 | - return new int( ); | |
18 | + return null; | |
18 | 19 | } |
19 | 20 | } |
20 | 21 | |
21 | - public static BlockPos LockLocation(ItemStack sourceStack) | |
22 | + public static BlockPos LockLocation(ItemStack sourceStack)//Location of matching ACN | |
22 | 23 | { |
23 | 24 | if (sourceStack.Attributes.HasAttribute(AccessControlsMod._LockLocationKey)) { |
24 | 25 | return sourceStack.Attributes.GetBlockPos(AccessControlsMod._LockLocationKey); |
@@ -41,7 +42,7 @@ namespace FirstMachineAge | ||
41 | 42 | internal static void WriteACL_ItemStack(ref ItemStack targetStack, AccessControlNode acn, BlockPos location) |
42 | 43 | { |
43 | 44 | targetStack.Attributes.SetInt(AccessControlsMod._KeyIDKey, acn.KeyID.Value); |
44 | - targetStack.Attributes.SetBlockPos(AccessControlsMod._LockLocationKey, location); | |
45 | + targetStack.Attributes.SetBlockPos(AccessControlsMod._LockLocationKey, location.Copy()); | |
45 | 46 | targetStack.Attributes.SetString(AccessControlsMod._itemDescription, acn.NameOfLock); |
46 | 47 | } |
47 | 48 |
@@ -51,13 +52,16 @@ namespace FirstMachineAge | ||
51 | 52 | base.GetHeldItemInfo(inSlot, dsc, world, withDebugInfo); |
52 | 53 | |
53 | 54 | #if DEBUG |
54 | - int keyId = KeyID(inSlot.Itemstack); | |
55 | + int? keyId = KeyID(inSlot.Itemstack); | |
55 | 56 | dsc.AppendFormat("\nKey #{0:D},", keyId); |
56 | 57 | #endif |
57 | 58 | |
58 | 59 | |
59 | 60 | string desc = Description(inSlot.Itemstack); |
60 | - dsc.AppendFormat("\n' {0} '", desc); | |
61 | + if (!string.IsNullOrWhiteSpace(desc)) { | |
62 | + dsc.AppendFormat("\n'{0}'", desc); | |
63 | + } | |
64 | + | |
61 | 65 | } |
62 | 66 | |
63 | 67 |
@@ -60,8 +60,6 @@ namespace FirstMachineAge | ||
60 | 60 | this.Logger = this.ClientAPI.World.Logger; |
61 | 61 | AccessControlsMod = ClientAPI.World.Api.ModLoader.GetModSystem<AccessControlsMod>( ); |
62 | 62 | } |
63 | - | |
64 | - Logger.VerboseDebug("{0} ~ OnLoaded", base.Code.ToString()); | |
65 | 63 | } |
66 | 64 | |
67 | 65 |
@@ -104,12 +102,12 @@ namespace FirstMachineAge | ||
104 | 102 | handling = EnumHandHandling.NotHandled; |
105 | 103 | } |
106 | 104 | |
107 | - //or? OnCreatedByCrafting -- generate keyID and/or combo (also when getting inv. from a packet) [CLIENTSIDE?!] | |
105 | + //or? OnCreatedByCrafting -- generate keyID and/or combo (also when getting inv. from a packet) | |
108 | 106 | public override void OnModifiedInInventorySlot(IWorldAccessor world, ItemSlot slot, ItemStack extractedStack = null) |
109 | 107 | { |
110 | 108 | if (world.Side.IsServer( )) |
111 | 109 | { |
112 | - //Set keyid,combo if unset... | |
110 | + //should capture creat.inv. cases & trade...TODO: check crafting | |
113 | 111 | #if DEBUG |
114 | 112 | Logger.VerboseDebug("GenericLock: OnModifiedInInventorySlot ID:{0}", slot.Itemstack.Id); |
115 | 113 | #endif |
@@ -124,12 +122,9 @@ namespace FirstMachineAge | ||
124 | 122 | } |
125 | 123 | else if (this.LockStyle == LockKinds.Key) |
126 | 124 | { |
127 | - //var keyId = KeyID(slot); | |
128 | - | |
129 | - //if (keyId.HasValue == false) | |
130 | - //{ | |
131 | - //GenerateKeyId(slot, this); | |
132 | - //} | |
125 | + if (this.KeyID(slot).HasValue == false) { | |
126 | + GenerateKeyId(slot, this); | |
127 | + } | |
133 | 128 | } |
134 | 129 | |
135 | 130 | } |
@@ -200,7 +195,7 @@ namespace FirstMachineAge | ||
200 | 195 | if (sourceSlot.Itemstack.Attributes.HasAttribute(AccessControlsMod._KeyIDKey)) { |
201 | 196 | return sourceSlot.Itemstack.Attributes.GetInt(AccessControlsMod._KeyIDKey); |
202 | 197 | } else { |
203 | - return new int( ); | |
198 | + return null; | |
204 | 199 | } |
205 | 200 | } |
206 | 201 | } |
@@ -40,8 +40,6 @@ namespace FirstMachineAge | ||
40 | 40 | handling = EnumHandHandling.PreventDefault; |
41 | 41 | return; |
42 | 42 | } |
43 | - | |
44 | - base.OnHeldInteractStart(slot, byEntity, blockSel, entitySel, firstEvent, ref handling); | |
45 | 43 | } |
46 | 44 | } |
47 | 45 | } |
@@ -13,16 +13,17 @@ namespace FirstMachineAge | ||
13 | 13 | |
14 | 14 | public override void OnHeldInteractStart(ItemSlot slot, EntityAgent byEntity, BlockSelection blockSel, EntitySelection entitySel, bool firstEvent, ref EnumHandHandling handling) |
15 | 15 | { |
16 | + | |
16 | 17 | if (byEntity.World.Side.IsClient( )) { |
17 | 18 | ClientAPI = (byEntity.World.Api as ICoreClientAPI); |
18 | 19 | } |
19 | 20 | |
20 | - if (blockSel != null && byEntity.World.BlockAccessor.GetBlock(blockSel.Position).HasBehavior<BlockBehaviorLockable>( )) { | |
21 | + if (blockSel != null && byEntity.World.BlockAccessor.GetBlock(blockSel.Position).HasBehavior<BlockBehaviorComplexLockable>( )) { | |
21 | 22 | IPlayer player = (byEntity as EntityPlayer).Player; |
22 | 23 | |
23 | 24 | |
24 | - if (AccessControlsMod.LockState(blockSel.Position, player) != LockStatus.None)//already has a lock...? | |
25 | - { | |
25 | + if (AccessControlsMod.LockState(blockSel.Position, player) != LockStatus.None)//already has a lock... | |
26 | + {//TODO: Add Lock owner text | |
26 | 27 | ClientAPI?.TriggerIngameError(this, "cannotlock", Lang.Get("ingameerror-cannotlock")); |
27 | 28 | } |
28 | 29 | else { |
@@ -32,15 +33,12 @@ namespace FirstMachineAge | ||
32 | 33 | ClientAPI?.ShowChatMessage(Lang.Get("lockapplied")); |
33 | 34 | slot.TakeOut(1); |
34 | 35 | slot.MarkDirty( ); |
35 | - //TODO: after create matching KEY for lock - make this slot highlighted for that key?? | |
36 | - | |
37 | 36 | } |
38 | 37 | |
39 | 38 | handling = EnumHandHandling.PreventDefault; |
40 | 39 | return; |
41 | 40 | } |
42 | - | |
43 | - base.OnHeldInteractStart(slot, byEntity, blockSel, entitySel, firstEvent, ref handling); | |
41 | + | |
44 | 42 | } |
45 | 43 | } |
46 | 44 | } |
@@ -191,7 +191,7 @@ namespace FirstMachineAge | ||
191 | 191 | |
192 | 192 | public static void SetBlockPos(this ITreeAttribute source, string key, BlockPos value) |
193 | 193 | { |
194 | - byte[] buffer = new byte[3]; | |
194 | + byte[] buffer = new byte[12]; | |
195 | 195 | |
196 | 196 | using (MemoryStream bytesStream = new MemoryStream(buffer,true)) |
197 | 197 | { |