110 changed files with 3542 additions and 1088 deletions
			
			
		| @ -1,7 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| @ -1,7 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| @ -1,7 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| @ -1,23 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| INVENTORY_TYPE_GOLD = 1 |  | ||||||
| INVENTORY_TYPE_ITEM = 2 |  | ||||||
| INVENTORY_TYPE_BOOTYTABLE = 3 |  | ||||||
| 
 |  | ||||||
| INVENTORY_TYPE_TO_STRING = { |  | ||||||
|     INVENTORY_TYPE_GOLD: 'GOLD', |  | ||||||
|     INVENTORY_TYPE_ITEM: 'ITEM', |  | ||||||
|     INVENTORY_TYPE_BOOTYTABLE: 'BOOTYTABLE', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_INVENTORY_TYPE = { |  | ||||||
|     'GOLD': INVENTORY_TYPE_GOLD, |  | ||||||
|     'ITEM': INVENTORY_TYPE_ITEM, |  | ||||||
|     'BOOTYTABLE': INVENTORY_TYPE_BOOTYTABLE, |  | ||||||
| } |  | ||||||
| @ -1,41 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| import glob |  | ||||||
| import os |  | ||||||
| 
 |  | ||||||
| from arcane.util.hasher import hash_string |  | ||||||
| 
 |  | ||||||
| _STRING_TO_HASH = {} |  | ||||||
| _HASH_TO_STRING = {} |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def load_files(): |  | ||||||
|     directory = os.path.dirname(__file__) |  | ||||||
| 
 |  | ||||||
|     for filepath in glob.glob(os.path.join(directory, '*.txt')): |  | ||||||
|         lines = list(map(lambda s: s.strip(), open(filepath).readlines())) |  | ||||||
| 
 |  | ||||||
|         _STRING_TO_HASH.update({ |  | ||||||
|             s: hash_string(s) for s in lines |  | ||||||
|         }) |  | ||||||
| 
 |  | ||||||
|         _HASH_TO_STRING.update({ |  | ||||||
|             hash_string(s): s for s in lines |  | ||||||
|         }) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def string_to_hash(s): |  | ||||||
|     return _STRING_TO_HASH.get(s, s) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def hash_to_string(h): |  | ||||||
|     return _HASH_TO_STRING.get(h, h) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| load_files() |  | ||||||
| @ -1,38 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| MOBILE = 1 |  | ||||||
| BANKER = 2 |  | ||||||
| SHOPKEEPER = 3 |  | ||||||
| TRAINER = 4 |  | ||||||
| MERCHANT = 8 |  | ||||||
| HIRELING = 9 |  | ||||||
| PET = 10 |  | ||||||
| MINION = 11 |  | ||||||
| 
 |  | ||||||
| MOBILE_TO_STRING = { |  | ||||||
|     MOBILE: 'MOBILE', |  | ||||||
|     BANKER: 'BANKER', |  | ||||||
|     SHOPKEEPER: 'SHOPKEEPER', |  | ||||||
|     TRAINER: 'TRAINER', |  | ||||||
|     MERCHANT: 'MERCHANT', |  | ||||||
|     HIRELING: 'HIRELING', |  | ||||||
|     PET: 'PET', |  | ||||||
|     MINION: 'MINION', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_MOBILE = { |  | ||||||
|     'MOBILE': MOBILE, |  | ||||||
|     'BANKER': BANKER, |  | ||||||
|     'SHOPKEEPER': SHOPKEEPER, |  | ||||||
|     'TRAINER': TRAINER, |  | ||||||
|     'MERCHANT': MERCHANT, |  | ||||||
|     'HIRELING': HIRELING, |  | ||||||
|     'PET': PET, |  | ||||||
|     'MINION': MINION, |  | ||||||
| } |  | ||||||
| @ -1,23 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| PROP = 5 |  | ||||||
| CITY = 6 |  | ||||||
| CONTAINER = 7 |  | ||||||
| 
 |  | ||||||
| PROP_TO_STRING = { |  | ||||||
|     PROP: 'PROP', |  | ||||||
|     CITY: 'CITY', |  | ||||||
|     CONTAINER: 'CONTAINER', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_PROP = { |  | ||||||
|     'PROP': PROP, |  | ||||||
|     'CITY': CITY, |  | ||||||
|     'CONTAINER': CONTAINER, |  | ||||||
| } |  | ||||||
| @ -1,35 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| PEAKED = 1 |  | ||||||
| RIDGED = 2 |  | ||||||
| ROLLING = 3 |  | ||||||
| MESA = 5 |  | ||||||
| PLANAR = 4 |  | ||||||
| MESH = 6 |  | ||||||
| TARGA = 7 |  | ||||||
| 
 |  | ||||||
| TERRAIN_TYPE_TO_STRING = { |  | ||||||
|     PEAKED: 'PEAKED', |  | ||||||
|     RIDGED: 'RIDGED', |  | ||||||
|     ROLLING: 'ROLLING', |  | ||||||
|     MESA: 'MESA', |  | ||||||
|     PLANAR: 'PLANAR', |  | ||||||
|     MESH: 'MESH', |  | ||||||
|     TARGA: 'TARGA', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_TERRAIN_TYPE = { |  | ||||||
|     'PEAKED': PEAKED, |  | ||||||
|     'RIDGED': RIDGED, |  | ||||||
|     'ROLLING': ROLLING, |  | ||||||
|     'MESA': MESA, |  | ||||||
|     'PLANAR': PLANAR, |  | ||||||
|     'MESH': MESH, |  | ||||||
|     'TARGA': TARGA, |  | ||||||
| } |  | ||||||
| @ -1,62 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| ELLIPTICAL = 0 |  | ||||||
| RECTANGULAR = 1 |  | ||||||
| 
 |  | ||||||
| ZONE_TO_STRING = { |  | ||||||
|     ELLIPTICAL: 'ELLIPTICAL', |  | ||||||
|     RECTANGULAR: 'RECTANGULAR', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_ZONE = { |  | ||||||
|     'ELLIPTICAL': ELLIPTICAL, |  | ||||||
|     'RECTANGULAR': RECTANGULAR, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| GLOBAL = 0 |  | ||||||
| LOCAL = 1 |  | ||||||
| 
 |  | ||||||
| TILECOORD_TO_STRING = { |  | ||||||
|     GLOBAL: 'GLOBAL', |  | ||||||
|     LOCAL: 'LOCAL', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_TILECOORD = { |  | ||||||
|     'GLOBAL': GLOBAL, |  | ||||||
|     'LOCAL': LOCAL, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| ADJACENT = 0 |  | ||||||
| RANDOM = 1 |  | ||||||
| 
 |  | ||||||
| PATTERN_TO_STRING = { |  | ||||||
|     ADJACENT: 'ADJACENT', |  | ||||||
|     RANDOM: 'RANDOM', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_PATTERN = { |  | ||||||
|     'ADJACENT': ADJACENT, |  | ||||||
|     'RANDOM': RANDOM, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| PARENT = 0 |  | ||||||
| WORLD = 1 |  | ||||||
| SELF = 2 |  | ||||||
| 
 |  | ||||||
| SEALEVEL_TO_STRING = { |  | ||||||
|     PARENT: 'PARENT', |  | ||||||
|     WORLD: 'WORLD', |  | ||||||
|     SELF: 'SELF', |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| STRING_TO_SEALEVEL = { |  | ||||||
|     'PARENT': PARENT, |  | ||||||
|     'WORLD': WORLD, |  | ||||||
|     'SELF': SELF, |  | ||||||
| } |  | ||||||
| @ -1,29 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| from arcane.util import ResStream |  | ||||||
| from .ArcStructureObject import ArcStructureObject |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ArcAssetStructureObject(ArcStructureObject): |  | ||||||
|     def load_binary(self, stream: ResStream): |  | ||||||
|         super().load_binary(stream) |  | ||||||
|         self.asset_structure_template_id = stream.read_qword() |  | ||||||
| 
 |  | ||||||
|     def save_binary(self, stream: ResStream): |  | ||||||
|         super().save_binary(stream) |  | ||||||
|         stream.write_qword(self.asset_structure_template_id) |  | ||||||
| 
 |  | ||||||
|     def load_json(self, data): |  | ||||||
|         super().load_json(data) |  | ||||||
|         self.asset_structure_template_id = data['asset_structure_template_id'] |  | ||||||
| 
 |  | ||||||
|     def save_json(self): |  | ||||||
|         data = super().save_json() |  | ||||||
|         data['asset_structure_template_id'] = self.asset_structure_template_id |  | ||||||
|         return data |  | ||||||
| @ -1,21 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| from .ArcAssetStructureObject import ArcAssetStructureObject |  | ||||||
| from .ArcCityAssetTemplate import ArcCityAssetTemplate |  | ||||||
| from .ArcCombatObj import ArcCombatObj, ArcCharacter |  | ||||||
| from .ArcContainerObject import ArcContainerObject |  | ||||||
| from .ArcDeed import ArcDeed |  | ||||||
| from .ArcDoorObject import ArcDoorObject |  | ||||||
| from .ArcDungeonUnitObject import ArcDungeonUnitObject, ArcDungeonExitObject, ArcDungeonStairObject |  | ||||||
| from .ArcItem import ArcItem |  | ||||||
| from .ArcKey import ArcKey |  | ||||||
| from .ArcObj import ArcObj |  | ||||||
| from .ArcRune import ArcRune |  | ||||||
| from .ArcStaticObject import ArcStaticObject |  | ||||||
| from .ArcStructureObject import ArcStructureObject |  | ||||||
| @ -1,40 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| from collections import OrderedDict |  | ||||||
| 
 |  | ||||||
| from arcane.enums.hashes import hash_to_string, string_to_hash |  | ||||||
| from arcane.util import ResStream |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class DiscRequired: |  | ||||||
|     def load_binary(self, stream: ResStream): |  | ||||||
|         discs = stream.read_dword() |  | ||||||
|         self.disc_restrict = stream.read_bool() |  | ||||||
|         self.disc_values = [ |  | ||||||
|             stream.read_dword() for _ in range(discs) |  | ||||||
|         ] |  | ||||||
| 
 |  | ||||||
|     def save_binary(self, stream: ResStream): |  | ||||||
|         stream.write_dword(len(self.disc_values)) |  | ||||||
|         stream.write_bool(self.disc_restrict) |  | ||||||
|         for disc in self.disc_values: |  | ||||||
|             stream.write_dword(disc) |  | ||||||
| 
 |  | ||||||
|     def load_json(self, data): |  | ||||||
|         self.disc_restrict = data['restrict'] |  | ||||||
|         self.disc_values = [] |  | ||||||
|         for disc in data['disces']: |  | ||||||
|             self.disc_values.append(string_to_hash(disc)) |  | ||||||
| 
 |  | ||||||
|     def save_json(self): |  | ||||||
|         data = OrderedDict() |  | ||||||
|         data['restrict'] = self.disc_restrict |  | ||||||
|         data['disces'] = [] |  | ||||||
|         for disc in self.disc_values: |  | ||||||
|             data['disces'].append(hash_to_string(disc)) |  | ||||||
|         return data |  | ||||||
| @ -1,40 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| from collections import OrderedDict |  | ||||||
| 
 |  | ||||||
| from arcane.enums.hashes import hash_to_string, string_to_hash |  | ||||||
| from arcane.util import ResStream |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class RaceRequired: |  | ||||||
|     def load_binary(self, stream: ResStream): |  | ||||||
|         races = stream.read_dword() |  | ||||||
|         self.race_restrict = stream.read_bool() |  | ||||||
|         self.race_values = [ |  | ||||||
|             stream.read_dword() for _ in range(races) |  | ||||||
|         ] |  | ||||||
| 
 |  | ||||||
|     def save_binary(self, stream: ResStream): |  | ||||||
|         stream.write_dword(len(self.race_values)) |  | ||||||
|         stream.write_bool(self.race_restrict) |  | ||||||
|         for race in self.race_values: |  | ||||||
|             stream.write_dword(race) |  | ||||||
| 
 |  | ||||||
|     def load_json(self, data): |  | ||||||
|         self.race_restrict = data['restrict'] |  | ||||||
|         self.race_values = [] |  | ||||||
|         for race in data['racees']: |  | ||||||
|             self.race_values.append(string_to_hash(race)) |  | ||||||
| 
 |  | ||||||
|     def save_json(self): |  | ||||||
|         data = OrderedDict() |  | ||||||
|         data['restrict'] = self.race_restrict |  | ||||||
|         data['racees'] = [] |  | ||||||
|         for race in self.race_values: |  | ||||||
|             data['racees'].append(hash_to_string(race)) |  | ||||||
|         return data |  | ||||||
| @ -1,7 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| @ -1,10 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| from .ArcFileCache import load_cache_file, save_cache_file |  | ||||||
| from .ResStream import ResStream |  | ||||||
| @ -1,39 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| from collections import OrderedDict |  | ||||||
| 
 |  | ||||||
| from arcane.util import ResStream |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class ArcDungeonInfo: |  | ||||||
|     def load_binary(self, stream: ResStream): |  | ||||||
|         self.dungeon_template_id = stream.read_qword() |  | ||||||
|         self.dungeon_unkown = stream.read_qword() |  | ||||||
|         self.dungeon_spawn_location = stream.read_tuple() |  | ||||||
|         self.dungeon_y_offset = stream.read_float() |  | ||||||
| 
 |  | ||||||
|     def save_binary(self, stream: ResStream): |  | ||||||
|         stream.write_qword(self.dungeon_template_id) |  | ||||||
|         stream.write_qword(self.dungeon_unkown) |  | ||||||
|         stream.write_tuple(self.dungeon_spawn_location) |  | ||||||
|         stream.write_float(self.dungeon_y_offset) |  | ||||||
| 
 |  | ||||||
|     def save_json(self): |  | ||||||
|         data = OrderedDict() |  | ||||||
|         data['dungeon_template_id'] = self.dungeon_template_id |  | ||||||
|         data['dungeon_unkown'] = self.dungeon_unkown |  | ||||||
|         data['dungeon_spawn_location'] = self.dungeon_spawn_location |  | ||||||
|         data['dungeon_y_offset'] = self.dungeon_y_offset |  | ||||||
|         return data |  | ||||||
| 
 |  | ||||||
|     def load_json(self, data): |  | ||||||
|         self.dungeon_template_id = data['dungeon_template_id'] |  | ||||||
|         self.dungeon_unkown = data['dungeon_unkown'] |  | ||||||
|         self.dungeon_spawn_location = data['dungeon_spawn_location'] |  | ||||||
|         self.dungeon_y_offset = data['dungeon_y_offset'] |  | ||||||
| @ -1,9 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| from .ArcZone import ArcZone |  | ||||||
| @ -1,20 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| from arcane.util import ResStream |  | ||||||
| from .HirelingInfo import HirelingInfo |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| class MinionInfo(HirelingInfo): |  | ||||||
|     def __init__(self): |  | ||||||
|         super().__init__() |  | ||||||
|         self.minion_info_u = None |  | ||||||
| 
 |  | ||||||
|     def load_binary(self, stream: ResStream): |  | ||||||
|         super().load_binary(stream) |  | ||||||
|         self.minion_info_u = stream.read_string() |  | ||||||
| @ -1,7 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| @ -1,223 +0,0 @@ | |||||||
| # • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄         |  | ||||||
| # ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪      |  | ||||||
| # ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄  |  | ||||||
| # ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ |  | ||||||
| # ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ |  | ||||||
| #               Magicbane Emulator Project © 2013 - 2022 |  | ||||||
| #                          www.magicbane.com |  | ||||||
| 
 |  | ||||||
| import glob |  | ||||||
| import json |  | ||||||
| import os |  | ||||||
| import sys |  | ||||||
| 
 |  | ||||||
| from arcane.enums.arc_object import * |  | ||||||
| from arcane.objects import * |  | ||||||
| from arcane.util import * |  | ||||||
| from arcane.zones import * |  | ||||||
| 
 |  | ||||||
| DUMP_DIRECTORY = 'ARCANE_DUMP' |  | ||||||
| WORKING_DIRECTORY = os.path.dirname(__file__) |  | ||||||
| TARGET_DIRECTORY = os.path.join(WORKING_DIRECTORY, DUMP_DIRECTORY) |  | ||||||
| COBJECTS_DIRECTORY = os.path.join(TARGET_DIRECTORY, 'COBJECTS') |  | ||||||
| CZONE_DIRECTORY = os.path.join(TARGET_DIRECTORY, 'CZONE') |  | ||||||
| COBJECTS_MAGIC = 0x434c4e54 |  | ||||||
| 
 |  | ||||||
| COBJECTS_MAP = { |  | ||||||
|     OBJECT_TYPE_LIGHT: ArcObj, |  | ||||||
|     OBJECT_TYPE_DOOR: ArcDoorObject, |  | ||||||
|     OBJECT_TYPE_STATIC: ArcStaticObject, |  | ||||||
|     OBJECT_TYPE_STRUCTURE: ArcStructureObject, |  | ||||||
|     OBJECT_TYPE_ASSETSTRUCTURE: ArcAssetStructureObject, |  | ||||||
|     OBJECT_TYPE_DUNGEONUNIT: ArcDungeonUnitObject, |  | ||||||
|     OBJECT_TYPE_DUNGEONEXIT: ArcDungeonExitObject, |  | ||||||
|     OBJECT_TYPE_DUNGEONSTAIR: ArcDungeonStairObject, |  | ||||||
|     OBJECT_TYPE_ITEM: ArcItem, |  | ||||||
|     OBJECT_TYPE_PLAYER: ArcCharacter, |  | ||||||
|     OBJECT_TYPE_MOBILE: ArcCharacter, |  | ||||||
|     OBJECT_TYPE_RUNE: ArcRune, |  | ||||||
|     OBJECT_TYPE_CONTAINER: ArcContainerObject, |  | ||||||
|     OBJECT_TYPE_DEED: ArcDeed, |  | ||||||
|     OBJECT_TYPE_KEY: ArcKey, |  | ||||||
|     OBJECT_TYPE_ASSET: ArcCityAssetTemplate, |  | ||||||
|     OBJECT_TYPE_OBJECT: ArcObj, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def init_cobjects(): |  | ||||||
|     for type in COBJECTS_MAP: |  | ||||||
|         directory_path = os.path.join(COBJECTS_DIRECTORY, OBJECT_TYPE_TO_STRING[type]) |  | ||||||
|         os.makedirs(directory_path, exist_ok=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def init_czones(): |  | ||||||
|     directory_path = CZONE_DIRECTORY |  | ||||||
|     os.makedirs(directory_path, exist_ok=True) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def unpack_cobjects(): |  | ||||||
|     init_cobjects() |  | ||||||
| 
 |  | ||||||
|     resources = load_cache_file('CObjects.cache') |  | ||||||
| 
 |  | ||||||
|     for res_id, data in resources: |  | ||||||
|         in_stream = ResStream(data) |  | ||||||
|         magic = in_stream.read_dword() |  | ||||||
|         obj_type = in_stream.read_dword() |  | ||||||
| 
 |  | ||||||
|         arc_object = COBJECTS_MAP[obj_type]() |  | ||||||
|         filepath = os.path.join( |  | ||||||
|             COBJECTS_DIRECTORY, |  | ||||||
|             OBJECT_TYPE_TO_STRING[obj_type], |  | ||||||
|             f'{res_id:020d}.json' |  | ||||||
|         ) |  | ||||||
|         arc_object.load_binary(in_stream) |  | ||||||
|         parsed = arc_object.save_json() |  | ||||||
|         with open(filepath, 'w') as fp: |  | ||||||
|             json.dump(parsed, fp, indent=2) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def pack_cobjects(): |  | ||||||
|     init_cobjects() |  | ||||||
| 
 |  | ||||||
|     resources = [] |  | ||||||
|     for obj_type in COBJECTS_MAP: |  | ||||||
|         directory = os.path.join( |  | ||||||
|             COBJECTS_DIRECTORY, |  | ||||||
|             OBJECT_TYPE_TO_STRING[obj_type] |  | ||||||
|         ) |  | ||||||
|         for filepath in glob.glob(os.path.join(directory, '*.json')): |  | ||||||
|             filename = os.path.basename(filepath) |  | ||||||
|             res_id = int(filename.split('.json')[0]) |  | ||||||
|             json_data = json.load(open(filepath)) |  | ||||||
| 
 |  | ||||||
|             out_stream = ResStream() |  | ||||||
|             out_stream.write_dword(COBJECTS_MAGIC) |  | ||||||
|             out_stream.write_dword(obj_type) |  | ||||||
| 
 |  | ||||||
|             arc_object = COBJECTS_MAP[obj_type]() |  | ||||||
|             try: |  | ||||||
|                 arc_object.load_json(json_data) |  | ||||||
|                 arc_object.save_binary(out_stream) |  | ||||||
|             except: |  | ||||||
|                 print(res_id) |  | ||||||
|                 raise |  | ||||||
| 
 |  | ||||||
|             resources.append([res_id, out_stream.get_bytes()]) |  | ||||||
| 
 |  | ||||||
|     save_cache_file('CObjects.cache.new', resources) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def test_cobjects(): |  | ||||||
|     resources = load_cache_file('CObjects.cache') |  | ||||||
| 
 |  | ||||||
|     for res_id, data in resources: |  | ||||||
|         in_stream = ResStream(data) |  | ||||||
|         out_stream = ResStream() |  | ||||||
| 
 |  | ||||||
|         magic = in_stream.read_dword() |  | ||||||
|         o_type = in_stream.read_dword() |  | ||||||
|         out_stream.write_dword(magic) |  | ||||||
|         out_stream.write_dword(o_type) |  | ||||||
| 
 |  | ||||||
|         arc_in = COBJECTS_MAP[o_type]() |  | ||||||
|         arc_out = COBJECTS_MAP[o_type]() |  | ||||||
| 
 |  | ||||||
|         arc_in.load_binary(in_stream) |  | ||||||
|         parsed = arc_in.save_json() |  | ||||||
|         arc_out.load_json(parsed) |  | ||||||
|         arc_out.save_binary(out_stream) |  | ||||||
|         try: |  | ||||||
|             assert in_stream.get_bytes() == out_stream.get_bytes() |  | ||||||
|         except: |  | ||||||
|             print(res_id, in_stream.buffer.tell(), out_stream.buffer.tell()) |  | ||||||
|             print() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def unpack_czones(): |  | ||||||
|     init_czones() |  | ||||||
| 
 |  | ||||||
|     resources = load_cache_file('CZone.cache') |  | ||||||
| 
 |  | ||||||
|     for res_id, data in resources: |  | ||||||
|         arc_zone = ArcZone() |  | ||||||
|         in_stream = ResStream(data) |  | ||||||
|         filepath = os.path.join( |  | ||||||
|             CZONE_DIRECTORY, |  | ||||||
|             f'{res_id:020d}.json' |  | ||||||
|         ) |  | ||||||
|         arc_zone.load_binary(in_stream) |  | ||||||
|         parsed = arc_zone.save_json() |  | ||||||
|         with open(filepath, 'w') as fp: |  | ||||||
|             json.dump(parsed, fp, indent=2) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def pack_czones(): |  | ||||||
|     init_czones() |  | ||||||
| 
 |  | ||||||
|     resources = [] |  | ||||||
|     directory = CZONE_DIRECTORY |  | ||||||
|     for filepath in glob.glob(os.path.join(directory, '*.json')): |  | ||||||
|         filename = os.path.basename(filepath) |  | ||||||
|         res_id = int(filename.split('.json')[0]) |  | ||||||
|         json_data = json.load(open(filepath)) |  | ||||||
| 
 |  | ||||||
|         out_stream = ResStream() |  | ||||||
|         arc_object = ArcZone() |  | ||||||
|         try: |  | ||||||
|             arc_object.load_json(json_data) |  | ||||||
|             arc_object.save_binary(out_stream) |  | ||||||
|         except: |  | ||||||
|             print(res_id) |  | ||||||
|             raise |  | ||||||
| 
 |  | ||||||
|         resources.append([res_id, out_stream.get_bytes()]) |  | ||||||
| 
 |  | ||||||
|     save_cache_file('CZone.cache.new', resources) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def test_czones(): |  | ||||||
|     resources = load_cache_file('CZone.cache') |  | ||||||
|     for res_id, data in resources: |  | ||||||
|         in_stream = ResStream(data) |  | ||||||
|         out_stream = ResStream() |  | ||||||
| 
 |  | ||||||
|         arc_in = ArcZone() |  | ||||||
|         arc_out = ArcZone() |  | ||||||
| 
 |  | ||||||
|         arc_in.load_binary(in_stream) |  | ||||||
|         parsed = arc_in.save_json() |  | ||||||
|         arc_out.load_json(parsed) |  | ||||||
|         arc_out.save_binary(out_stream) |  | ||||||
|         try: |  | ||||||
|             assert in_stream.get_bytes() == out_stream.get_bytes() |  | ||||||
|         except: |  | ||||||
|             print(res_id, in_stream.buffer.tell(), out_stream.buffer.tell()) |  | ||||||
|             print() |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def usage(): |  | ||||||
|     print('mbEditorPro commands:') |  | ||||||
|     print('usage: {} unpack cobjects'.format(sys.argv[0])) |  | ||||||
|     print('usage: {} pack cobjects'.format(sys.argv[0])) |  | ||||||
|     print('usage: {} unpack czones'.format(sys.argv[0])) |  | ||||||
|     print('usage: {} pack czones'.format(sys.argv[0])) |  | ||||||
|     exit(1) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| def main(): |  | ||||||
|     if not sys.argv[2:]: |  | ||||||
|         usage() |  | ||||||
| 
 |  | ||||||
|     if sys.argv[1] not in ['pack', 'unpack']: |  | ||||||
|         usage() |  | ||||||
| 
 |  | ||||||
|     if sys.argv[2] not in ['cobjects', 'czones']: |  | ||||||
|         usage() |  | ||||||
| 
 |  | ||||||
|     method = '_'.join(sys.argv[1:3]) + '()' |  | ||||||
|     print(method) |  | ||||||
|     exec(method) |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| main() |  | ||||||
| @ -0,0 +1,70 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from PIL import Image, ImageOps | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcTexture: | ||||||
|  |     VALUE_TO_MODE = { | ||||||
|  |         (4, 8, 2): 'RGBA', | ||||||
|  |         (3, 0, 0): 'RGB', | ||||||
|  |         (1, 0, 0): 'L', | ||||||
|  |         (1, 8, 2): 'P', | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MODE_TO_VALUE = {value: key for key, value in VALUE_TO_MODE.items()} | ||||||
|  | 
 | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.image_width = stream.read_dword() | ||||||
|  |         self.image_height = stream.read_dword() | ||||||
|  |         self.image_color_depth = stream.read_dword() | ||||||
|  |         self.image_alpha = stream.read_dword() | ||||||
|  |         self.image_type = stream.read_dword() | ||||||
|  |         self.image_compressed = stream.read_bool() | ||||||
|  |         self.image_linear = stream.read_bool() | ||||||
|  |         data_size = stream.read_dword() | ||||||
|  |         self.image_data = stream.read_bytes(data_size) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.image_width) | ||||||
|  |         stream.write_dword(self.image_height) | ||||||
|  |         stream.write_dword(self.image_color_depth) | ||||||
|  |         stream.write_dword(self.image_alpha) | ||||||
|  |         stream.write_dword(self.image_type) | ||||||
|  |         stream.write_bool(self.image_compressed) | ||||||
|  |         stream.write_bool(self.image_linear) | ||||||
|  |         stream.write_dword(len(self.image_data)) | ||||||
|  |         stream.write_bytes(self.image_data) | ||||||
|  | 
 | ||||||
|  |     def load_img(self, filepath): | ||||||
|  |         img = Image.open(filepath) | ||||||
|  |         img = ImageOps.mirror(img).rotate(180) | ||||||
|  |         self.image_width = img.width | ||||||
|  |         self.image_height = img.height | ||||||
|  |         self.image_color_depth, self.image_alpha, self.image_type = self.MODE_TO_VALUE[img.mode] | ||||||
|  |         self.image_compressed = True | ||||||
|  |         self.image_linear = True | ||||||
|  |         self.image_data = img.tobytes() | ||||||
|  | 
 | ||||||
|  |     def save_img(self, filepath): | ||||||
|  |         mode = self.VALUE_TO_MODE[ | ||||||
|  |             (self.image_color_depth, self.image_alpha, self.image_type) | ||||||
|  |         ] | ||||||
|  |         img = Image.frombytes(mode, (self.image_width, self.image_height), self.image_data) | ||||||
|  |         img = ImageOps.mirror(img.rotate(180)) | ||||||
|  |         img.save(filepath) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcTerrain(ArcTexture): | ||||||
|  |     VALUE_TO_MODE = { | ||||||
|  |         (1, 1, 0): 'P', | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     MODE_TO_VALUE = {value: key for key, value in VALUE_TO_MODE.items()} | ||||||
| @ -0,0 +1,113 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcMesh: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.mesh_name = stream.read_string() | ||||||
|  |         self.mesh_distance = stream.read_float() | ||||||
|  |         self.mesh_start_point = stream.read_tuple() | ||||||
|  |         self.mesh_end_point = stream.read_tuple() | ||||||
|  |         self.mesh_ref_point = stream.read_tuple() | ||||||
|  |         self.mesh_use_face_normals = stream.read_bool() | ||||||
|  |         self.mesh_use_tangent_basis = stream.read_bool() | ||||||
|  |         num_vertices = stream.read_dword() | ||||||
|  |         self.mesh_vertices = [stream.read_tuple() for _ in range(num_vertices)] | ||||||
|  |         num_other_vertices = stream.read_dword() | ||||||
|  |         self.mesh_normals = [stream.read_tuple() for _ in range(num_other_vertices)] | ||||||
|  |         num = stream.read_dword() | ||||||
|  |         self.mesh_uv = [ | ||||||
|  |             [ | ||||||
|  |                 stream.read_float(), | ||||||
|  |                 stream.read_float(), | ||||||
|  |             ] for _ in range(num) | ||||||
|  |         ] | ||||||
|  |         if self.mesh_use_tangent_basis: | ||||||
|  |             num_tangent_vertices = stream.read_dword() | ||||||
|  |             self.mesh_tanget_vertices = [stream.read_tuple() for _ in range(num_tangent_vertices)] | ||||||
|  |         num_indicies = stream.read_dword() | ||||||
|  |         self.mesh_indices = [stream.read_word() for _ in range(num_indicies)] | ||||||
|  |         num = stream.read_dword() | ||||||
|  |         self.mesh_extra_indices = [ | ||||||
|  |             [ | ||||||
|  |                 stream.read_dword(), | ||||||
|  |                 stream.read_dword(), | ||||||
|  |                 [stream.read_word() for _ in range(stream.read_dword())], | ||||||
|  |             ] for _ in range(num) | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_string(self.mesh_name) | ||||||
|  |         stream.write_float(self.mesh_distance) | ||||||
|  |         stream.write_tuple(self.mesh_start_point) | ||||||
|  |         stream.write_tuple(self.mesh_end_point) | ||||||
|  |         stream.write_tuple(self.mesh_ref_point) | ||||||
|  |         stream.write_bool(self.mesh_use_face_normals) | ||||||
|  |         stream.write_bool(self.mesh_use_tangent_basis) | ||||||
|  |         stream.write_dword(len(self.mesh_vertices)) | ||||||
|  |         for i in range(len(self.mesh_vertices)): | ||||||
|  |             stream.write_tuple(self.mesh_vertices[i]) | ||||||
|  |         stream.write_dword(len(self.mesh_normals)) | ||||||
|  |         for i in range(len(self.mesh_normals)): | ||||||
|  |             stream.write_tuple(self.mesh_normals[i]) | ||||||
|  |         stream.write_dword(len(self.mesh_uv)) | ||||||
|  |         for i in range(len(self.mesh_uv)): | ||||||
|  |             stream.write_float(self.mesh_uv[i][0]) | ||||||
|  |             stream.write_float(self.mesh_uv[i][1]) | ||||||
|  |         if self.mesh_use_tangent_basis: | ||||||
|  |             stream.write_dword(len(self.mesh_tanget_vertices)) | ||||||
|  |             for i in range(len(self.mesh_tanget_vertices)): | ||||||
|  |                 stream.write_tuple(self.mesh_tanget_vertices[i]) | ||||||
|  |         stream.write_dword(len(self.mesh_indices)) | ||||||
|  |         for i in range(len(self.mesh_indices)): | ||||||
|  |             stream.write_word(self.mesh_indices[i]) | ||||||
|  |         stream.write_dword(len(self.mesh_extra_indices)) | ||||||
|  |         for i in range(len(self.mesh_extra_indices)): | ||||||
|  |             stream.write_dword(self.mesh_extra_indices[i][0]) | ||||||
|  |             stream.write_dword(self.mesh_extra_indices[i][1]) | ||||||
|  |             stream.write_dword(len(self.mesh_extra_indices[i][2])) | ||||||
|  |             for j in range(len(self.mesh_extra_indices[i][2])): | ||||||
|  |                 stream.write_word(self.mesh_extra_indices[i][2][j]) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.mesh_name = data['mesh_name'] | ||||||
|  |         self.mesh_distance = data['mesh_distance'] | ||||||
|  |         self.mesh_start_point = data['mesh_start_point'] | ||||||
|  |         self.mesh_end_point = data['mesh_end_point'] | ||||||
|  |         self.mesh_ref_point = data['mesh_ref_point'] | ||||||
|  |         self.mesh_use_face_normals = data['mesh_use_face_normals'] | ||||||
|  |         self.mesh_use_tangent_basis = data['mesh_use_tangent_basis'] | ||||||
|  |         self.mesh_vertices = data['mesh_vertices'] | ||||||
|  |         self.mesh_normals = data['mesh_normals'] | ||||||
|  |         self.mesh_uv = data['mesh_uv'] | ||||||
|  |         if self.mesh_use_tangent_basis: | ||||||
|  |             self.mesh_tanget_vertices = data['mesh_tanget_vertices'] | ||||||
|  |         self.mesh_indices = data['mesh_indices'] | ||||||
|  |         self.mesh_extra_indices = data['mesh_extra_indices'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['mesh_name'] = self.mesh_name | ||||||
|  |         data['mesh_distance'] = self.mesh_distance | ||||||
|  |         data['mesh_start_point'] = self.mesh_start_point | ||||||
|  |         data['mesh_end_point'] = self.mesh_end_point | ||||||
|  |         data['mesh_ref_point'] = self.mesh_ref_point | ||||||
|  |         data['mesh_use_face_normals'] = self.mesh_use_face_normals | ||||||
|  |         data['mesh_use_tangent_basis'] = self.mesh_use_tangent_basis | ||||||
|  |         data['mesh_vertices'] = self.mesh_vertices | ||||||
|  |         data['mesh_normals'] = self.mesh_normals | ||||||
|  |         data['mesh_uv'] = self.mesh_uv | ||||||
|  |         if self.mesh_use_tangent_basis: | ||||||
|  |             data['mesh_tanget_vertices'] = self.mesh_tanget_vertices | ||||||
|  |         data['mesh_indices'] = self.mesh_indices | ||||||
|  |         data['mesh_extra_indices'] = self.mesh_extra_indices | ||||||
|  |         return data | ||||||
| @ -0,0 +1,89 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcMotion: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.motion_file = stream.read_string() | ||||||
|  |         self.motion_smoothed_count = stream.read_dword() | ||||||
|  |         self.motion_smoothed_value = stream.read_dword() | ||||||
|  |         self.motion_smoothed_factor = stream.read_float() | ||||||
|  |         self.motion_sound = stream.read_qword() | ||||||
|  |         self.motion_sheath = stream.read_dword() | ||||||
|  |         self.motion_reset_loc = stream.read_bool() | ||||||
|  |         self.motion_leave_ground = stream.read_bool() | ||||||
|  |         self.motion_force = stream.read_float() | ||||||
|  |         self.motion_disable_blend = stream.read_bool() | ||||||
|  |         num_parts = stream.read_dword() | ||||||
|  |         self.motion_parts = [stream.read_string() for _ in range(num_parts)] | ||||||
|  |         num_smoothing = stream.read_dword() | ||||||
|  |         self.motion_smoothing = [ | ||||||
|  |             [ | ||||||
|  |                 stream.read_float() for _ in range(10) | ||||||
|  |             ] for _ in range(num_smoothing) | ||||||
|  |         ] | ||||||
|  |         num_target_frames = stream.read_dword() | ||||||
|  |         self.motion_target_frames = [stream.read_dword() for _ in range(num_target_frames)] | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_string(self.motion_file) | ||||||
|  |         stream.write_dword(self.motion_smoothed_count) | ||||||
|  |         stream.write_dword(self.motion_smoothed_value) | ||||||
|  |         stream.write_float(self.motion_smoothed_factor) | ||||||
|  |         stream.write_qword(self.motion_sound) | ||||||
|  |         stream.write_dword(self.motion_sheath) | ||||||
|  |         stream.write_bool(self.motion_reset_loc) | ||||||
|  |         stream.write_bool(self.motion_leave_ground) | ||||||
|  |         stream.write_float(self.motion_force) | ||||||
|  |         stream.write_bool(self.motion_disable_blend) | ||||||
|  |         stream.write_dword(len(self.motion_parts)) | ||||||
|  |         for i in range(len(self.motion_parts)): | ||||||
|  |             stream.write_string(self.motion_parts[i]) | ||||||
|  |         stream.write_dword(len(self.motion_smoothing)) | ||||||
|  |         for i in range(len(self.motion_smoothing)): | ||||||
|  |             for j in range(10): | ||||||
|  |                 stream.write_float(self.motion_smoothing[i][j]) | ||||||
|  |         stream.write_dword(len(self.motion_target_frames)) | ||||||
|  |         for i in range(len(self.motion_target_frames)): | ||||||
|  |             stream.write_dword(self.motion_target_frames[i]) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.motion_file = data['motion_file'] | ||||||
|  |         self.motion_smoothed_count = data['motion_smoothed_count'] | ||||||
|  |         self.motion_smoothed_value = data['motion_smoothed_value'] | ||||||
|  |         self.motion_smoothed_factor = data['motion_smoothed_factor'] | ||||||
|  |         self.motion_sound = data['motion_sound'] | ||||||
|  |         self.motion_sheath = data['motion_sheath'] | ||||||
|  |         self.motion_reset_loc = data['motion_reset_loc'] | ||||||
|  |         self.motion_leave_ground = data['motion_leave_ground'] | ||||||
|  |         self.motion_force = data['motion_force'] | ||||||
|  |         self.motion_disable_blend = data['motion_disable_blend'] | ||||||
|  |         self.motion_parts = data['motion_parts'] | ||||||
|  |         self.motion_smoothing = data['motion_smoothing'] | ||||||
|  |         self.motion_target_frames = data['motion_target_frames'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['motion_file'] = self.motion_file | ||||||
|  |         data['motion_smoothed_count'] = self.motion_smoothed_count | ||||||
|  |         data['motion_smoothed_value'] = self.motion_smoothed_value | ||||||
|  |         data['motion_smoothed_factor'] = self.motion_smoothed_factor | ||||||
|  |         data['motion_sound'] = self.motion_sound | ||||||
|  |         data['motion_sheath'] = self.motion_sheath | ||||||
|  |         data['motion_reset_loc'] = self.motion_reset_loc | ||||||
|  |         data['motion_leave_ground'] = self.motion_leave_ground | ||||||
|  |         data['motion_force'] = self.motion_force | ||||||
|  |         data['motion_disable_blend'] = self.motion_disable_blend | ||||||
|  |         data['motion_parts'] = self.motion_parts | ||||||
|  |         data['motion_smoothing'] = self.motion_smoothing | ||||||
|  |         data['motion_target_frames'] = self.motion_target_frames | ||||||
|  |         return data | ||||||
| @ -0,0 +1,644 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | TRACKER_TO_STRING = { | ||||||
|  |     0: 'NONE', | ||||||
|  |     1: 'XY', | ||||||
|  |     2: 'Y', | ||||||
|  | } | ||||||
|  | STRING_TO_TRACKER = {value: key for key, value in TRACKER_TO_STRING.items()} | ||||||
|  | 
 | ||||||
|  | TRANSPARENT_TO_STRING = { | ||||||
|  |     0: 'NONE', | ||||||
|  |     1: 'PINK', | ||||||
|  |     2: 'BLACK', | ||||||
|  |     3: 'WHITE', | ||||||
|  |     4: 'SEMI', | ||||||
|  |     6: 'ALPHA', | ||||||
|  | } | ||||||
|  | STRING_TO_TRANSPARENT = {value: key for key, value in TRANSPARENT_TO_STRING.items()} | ||||||
|  | 
 | ||||||
|  | TEXTURE_TO_STRING = { | ||||||
|  |     0: 'SINGLE_TEXTURE', | ||||||
|  |     1: 'COLOR_TEXTURE', | ||||||
|  |     3: 'ANIMATED_TEXTURE', | ||||||
|  | } | ||||||
|  | STRING_TO_TEXTURE = {value: key for key, value in TEXTURE_TO_STRING.items()} | ||||||
|  | 
 | ||||||
|  | LIGHT_TYPE_TO_STRING = { | ||||||
|  |     0xb6787258: 'ArcLightPoint', | ||||||
|  |     0x54e8ff1d: 'ArcLightAffectorAttach', | ||||||
|  |     0xa73bd9d4: 'ArcLightAffectorFlicker', | ||||||
|  | } | ||||||
|  | STRING_TO_LIGHT_TYPE = {value: key for key, value in LIGHT_TYPE_TO_STRING.items()} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcSinglePolyMesh: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.polymesh_id = stream.read_qword() | ||||||
|  |         self.polymesh_decal = stream.read_bool() | ||||||
|  |         self.polymesh_double_sided = stream.read_bool() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_qword(self.polymesh_id) | ||||||
|  |         stream.write_bool(self.polymesh_decal) | ||||||
|  |         stream.write_bool(self.polymesh_double_sided) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.polymesh_id = data['polymesh_id'] | ||||||
|  |         self.polymesh_decal = data['polymesh_decal'] | ||||||
|  |         self.polymesh_double_sided = data['polymesh_double_sided'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['polymesh_id'] = self.polymesh_id | ||||||
|  |         data['polymesh_decal'] = self.polymesh_decal | ||||||
|  |         data['polymesh_double_sided'] = self.polymesh_double_sided | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcMeshSet: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         num = stream.read_dword() | ||||||
|  |         self.mesh_set = [ArcSinglePolyMesh() for _ in range(num)] | ||||||
|  |         for mesh in self.mesh_set: | ||||||
|  |             mesh.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(len(self.mesh_set)) | ||||||
|  |         for mesh in self.mesh_set: | ||||||
|  |             mesh.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.mesh_set = [] | ||||||
|  |         for mesh_data in data['mesh_set']: | ||||||
|  |             mesh = ArcSinglePolyMesh() | ||||||
|  |             mesh.load_json(mesh_data) | ||||||
|  |             self.mesh_set.append(mesh) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['mesh_set'] = [] | ||||||
|  |         for mesh in self.mesh_set: | ||||||
|  |             data['mesh_set'].append(mesh.save_json()) | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcRenderTemplate: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.template_object_can_fade = stream.read_bool() | ||||||
|  |         self.template_tracker = stream.read_dword() | ||||||
|  |         self.template_illuminated = stream.read_bool() | ||||||
|  |         self.template_bone_length = stream.read_float() | ||||||
|  |         self.template_clip_map = stream.read_dword() | ||||||
|  |         self.template_light_two_side = stream.read_dword() | ||||||
|  |         self.template_cull_face = stream.read_dword() | ||||||
|  |         self.template_specular_map = stream.read_qword() | ||||||
|  |         self.template_shininess = stream.read_float() | ||||||
|  |         self.template_has_mesh = stream.read_bool() | ||||||
|  |         if self.template_has_mesh: | ||||||
|  |             self.template_mesh = ArcMeshSet() | ||||||
|  |             self.template_mesh.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_bool(self.template_object_can_fade) | ||||||
|  |         stream.write_dword(self.template_tracker) | ||||||
|  |         stream.write_bool(self.template_illuminated) | ||||||
|  |         stream.write_float(self.template_bone_length) | ||||||
|  |         stream.write_dword(self.template_clip_map) | ||||||
|  |         stream.write_dword(self.template_light_two_side) | ||||||
|  |         stream.write_dword(self.template_cull_face) | ||||||
|  |         stream.write_qword(self.template_specular_map) | ||||||
|  |         stream.write_float(self.template_shininess) | ||||||
|  |         stream.write_bool(self.template_has_mesh) | ||||||
|  |         if self.template_has_mesh: | ||||||
|  |             self.template_mesh.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.template_object_can_fade = data['template_object_can_fade'] | ||||||
|  |         self.template_tracker = STRING_TO_TRACKER[data['template_tracker']] | ||||||
|  |         self.template_illuminated = data['template_illuminated'] | ||||||
|  |         self.template_bone_length = data['template_bone_length'] | ||||||
|  |         self.template_clip_map = data['template_clip_map'] | ||||||
|  |         self.template_light_two_side = data['template_light_two_side'] | ||||||
|  |         self.template_cull_face = data['template_cull_face'] | ||||||
|  |         self.template_specular_map = data['template_specular_map'] | ||||||
|  |         self.template_shininess = data['template_shininess'] | ||||||
|  |         self.template_has_mesh = data['template_has_mesh'] | ||||||
|  |         if self.template_has_mesh: | ||||||
|  |             self.template_mesh = ArcMeshSet() | ||||||
|  |             self.template_mesh.load_json(data['template_mesh']) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['template_object_can_fade'] = self.template_object_can_fade | ||||||
|  |         data['template_tracker'] = TRACKER_TO_STRING[self.template_tracker] | ||||||
|  |         data['template_illuminated'] = self.template_illuminated | ||||||
|  |         data['template_bone_length'] = self.template_bone_length | ||||||
|  |         data['template_clip_map'] = self.template_clip_map | ||||||
|  |         data['template_light_two_side'] = self.template_light_two_side | ||||||
|  |         data['template_cull_face'] = self.template_cull_face | ||||||
|  |         data['template_specular_map'] = self.template_specular_map | ||||||
|  |         data['template_shininess'] = self.template_shininess | ||||||
|  |         data['template_has_mesh'] = self.template_has_mesh | ||||||
|  |         if self.template_has_mesh: | ||||||
|  |             data['template_mesh'] = self.template_mesh.save_json() | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcSingleTexture: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.texture_id = stream.read_qword() | ||||||
|  |         self.texture_transparent = stream.read_dword() | ||||||
|  |         self.texture_compress = stream.read_bool() | ||||||
|  |         self.texture_normal_map = stream.read_bool() | ||||||
|  |         self.texture_detail_normal_map = stream.read_bool() | ||||||
|  |         self.texture_create_mip_maps = stream.read_bool() | ||||||
|  |         self.texture_x0 = stream.read_string() | ||||||
|  |         self.texture_x1 = stream.read_string() | ||||||
|  |         self.texture_x2 = stream.read_dword() | ||||||
|  |         self.texture_x3 = stream.read_dword() | ||||||
|  |         self.texture_x4 = stream.read_bool() | ||||||
|  |         self.texture_wrap = stream.read_bool() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_qword(self.texture_id) | ||||||
|  |         stream.write_dword(self.texture_transparent) | ||||||
|  |         stream.write_bool(self.texture_compress) | ||||||
|  |         stream.write_bool(self.texture_normal_map) | ||||||
|  |         stream.write_bool(self.texture_detail_normal_map) | ||||||
|  |         stream.write_bool(self.texture_create_mip_maps) | ||||||
|  |         stream.write_string(self.texture_x0) | ||||||
|  |         stream.write_string(self.texture_x1) | ||||||
|  |         stream.write_dword(self.texture_x2) | ||||||
|  |         stream.write_dword(self.texture_x3) | ||||||
|  |         stream.write_bool(self.texture_x4) | ||||||
|  |         stream.write_bool(self.texture_wrap) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.texture_id = data['texture_id'] | ||||||
|  |         self.texture_transparent = STRING_TO_TRANSPARENT[data['texture_transparent']] | ||||||
|  |         self.texture_compress = data['texture_compress'] | ||||||
|  |         self.texture_normal_map = data['texture_normal_map'] | ||||||
|  |         self.texture_detail_normal_map = data['texture_detail_normal_map'] | ||||||
|  |         self.texture_create_mip_maps = data['texture_create_mip_maps'] | ||||||
|  |         self.texture_x0 = '' | ||||||
|  |         self.texture_x1 = '' | ||||||
|  |         self.texture_x2 = 255 | ||||||
|  |         self.texture_x3 = 0 | ||||||
|  |         self.texture_x4 = False | ||||||
|  |         self.texture_wrap = data['texture_wrap'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['texture_id'] = self.texture_id | ||||||
|  |         data['texture_transparent'] = TRANSPARENT_TO_STRING[self.texture_transparent] | ||||||
|  |         data['texture_compress'] = self.texture_compress | ||||||
|  |         data['texture_normal_map'] = self.texture_normal_map | ||||||
|  |         data['texture_detail_normal_map'] = self.texture_detail_normal_map | ||||||
|  |         data['texture_create_mip_maps'] = self.texture_create_mip_maps | ||||||
|  |         data['texture_wrap'] = self.texture_wrap | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcColorTexture(ArcSingleTexture): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcAnimatedTexture: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.animated_texture_id = stream.read_qword() | ||||||
|  |         self.animated_texture_transparent = stream.read_dword() | ||||||
|  |         self.animated_texture_compress = stream.read_bool() | ||||||
|  |         self.animated_texture_normal_map = stream.read_bool() | ||||||
|  |         self.animated_texture_detail_normal_map = stream.read_bool() | ||||||
|  |         self.animated_texture_create_mip_maps = stream.read_bool() | ||||||
|  |         self.animated_texture_frame_timer = stream.read_float() | ||||||
|  |         self.animated_texture_x0 = stream.read_float() | ||||||
|  |         self.animated_texture_frame_rand = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |         num = stream.read_dword() | ||||||
|  |         self.animated_texture_sets = [ArcTextureSet() for _ in range(num)] | ||||||
|  |         for texture in self.animated_texture_sets: | ||||||
|  |             texture.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_qword(self.animated_texture_id) | ||||||
|  |         stream.write_dword(self.animated_texture_transparent) | ||||||
|  |         stream.write_bool(self.animated_texture_compress) | ||||||
|  |         stream.write_bool(self.animated_texture_normal_map) | ||||||
|  |         stream.write_bool(self.animated_texture_detail_normal_map) | ||||||
|  |         stream.write_bool(self.animated_texture_create_mip_maps) | ||||||
|  |         stream.write_float(self.animated_texture_frame_timer) | ||||||
|  |         stream.write_float(self.animated_texture_x0) | ||||||
|  |         stream.write_dword(self.animated_texture_frame_rand) | ||||||
|  | 
 | ||||||
|  |         stream.write_dword(len(self.animated_texture_sets)) | ||||||
|  |         for texture in self.animated_texture_sets: | ||||||
|  |             texture.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.animated_texture_id = data['animated_texture_id'] | ||||||
|  |         self.animated_texture_transparent = STRING_TO_TRANSPARENT[data['animated_texture_transparent']] | ||||||
|  |         self.animated_texture_compress = data['animated_texture_compress'] | ||||||
|  |         self.animated_texture_normal_map = data['animated_texture_normal_map'] | ||||||
|  |         self.animated_texture_detail_normal_map = data['animated_texture_detail_normal_map'] | ||||||
|  |         self.animated_texture_create_mip_maps = data['animated_texture_create_mip_maps'] | ||||||
|  |         self.animated_texture_frame_timer = data['animated_texture_frame_timer'] | ||||||
|  |         self.animated_texture_x0 = 0.0 | ||||||
|  |         self.animated_texture_frame_rand = data['animated_texture_frame_rand'] | ||||||
|  |         self.animated_texture_sets = [] | ||||||
|  |         for texture_data in data['animated_texture_sets']: | ||||||
|  |             texture = ArcTextureSet() | ||||||
|  |             texture.load_json(texture_data) | ||||||
|  |             self.animated_texture_sets.append(texture) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['animated_texture_id'] = self.animated_texture_id | ||||||
|  |         data['animated_texture_transparent'] = TRANSPARENT_TO_STRING[self.animated_texture_transparent] | ||||||
|  |         data['animated_texture_compress'] = self.animated_texture_compress | ||||||
|  |         data['animated_texture_normal_map'] = self.animated_texture_normal_map | ||||||
|  |         data['animated_texture_detail_normal_map'] = self.animated_texture_detail_normal_map | ||||||
|  |         data['animated_texture_create_mip_maps'] = self.animated_texture_create_mip_maps | ||||||
|  |         data['animated_texture_frame_timer'] = self.animated_texture_frame_timer | ||||||
|  |         data['animated_texture_frame_rand'] = self.animated_texture_frame_rand | ||||||
|  |         data['animated_texture_sets'] = [] | ||||||
|  |         for texture in self.animated_texture_sets: | ||||||
|  |             data['animated_texture_sets'].append(texture.save_json()) | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcTextureSet: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.texture_type = stream.read_dword() | ||||||
|  |         if self.texture_type == 0: | ||||||
|  |             self.texture_data = ArcSingleTexture() | ||||||
|  |         elif self.texture_type == 1: | ||||||
|  |             self.texture_data = ArcColorTexture() | ||||||
|  |         elif self.texture_type == 3: | ||||||
|  |             self.texture_data = ArcAnimatedTexture() | ||||||
|  |         self.texture_data.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.texture_type) | ||||||
|  |         self.texture_data.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.texture_type = STRING_TO_TEXTURE[data['texture_type']] | ||||||
|  |         if self.texture_type == 0: | ||||||
|  |             self.texture_data = ArcSingleTexture() | ||||||
|  |         elif self.texture_type == 1: | ||||||
|  |             self.texture_data = ArcColorTexture() | ||||||
|  |         elif self.texture_type == 3: | ||||||
|  |             self.texture_data = ArcAnimatedTexture() | ||||||
|  |         self.texture_data.load_json(data['texture_data']) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['texture_type'] = TEXTURE_TO_STRING[self.texture_type] | ||||||
|  |         data['texture_data'] = self.texture_data.save_json() | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcLightPoint: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.lightpoint_x0 = stream.read_dword() | ||||||
|  |         self.lightpoint_x1 = stream.read_bool() | ||||||
|  |         self.lightpoint_shader = stream.read_bool() | ||||||
|  |         self.lightpoint_update_offscreen = stream.read_bool() | ||||||
|  |         self.lightpoint_radius = stream.read_float() | ||||||
|  |         self.lightpoint_position = stream.read_tuple() | ||||||
|  |         self.lightpoint_diffuse_color = [stream.read_float() for _ in range(4)] | ||||||
|  |         self.lightpoint_x2 = stream.read_dword() | ||||||
|  |         self.lightpoint_orientation = [stream.read_float() for _ in range(4)] | ||||||
|  |         self.lightpoint_cubemap = stream.read_dword() | ||||||
|  |         self.lightpoint_x3 = stream.read_bool() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.lightpoint_x0) | ||||||
|  |         stream.write_bool(self.lightpoint_x1) | ||||||
|  |         stream.write_bool(self.lightpoint_shader) | ||||||
|  |         stream.write_bool(self.lightpoint_update_offscreen) | ||||||
|  |         stream.write_float(self.lightpoint_radius) | ||||||
|  |         stream.write_tuple(self.lightpoint_position) | ||||||
|  |         for i in range(4): | ||||||
|  |             stream.write_float(self.lightpoint_diffuse_color[i]) | ||||||
|  |         stream.write_dword(self.lightpoint_x2) | ||||||
|  |         for i in range(4): | ||||||
|  |             stream.write_float(self.lightpoint_orientation[i]) | ||||||
|  |         stream.write_dword(self.lightpoint_cubemap) | ||||||
|  |         stream.write_bool(self.lightpoint_x3) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.lightpoint_x0 = 1 | ||||||
|  |         self.lightpoint_x1 = True | ||||||
|  |         self.lightpoint_shader = data['lightpoint_shader'] | ||||||
|  |         self.lightpoint_update_offscreen = data['lightpoint_update_offscreen'] | ||||||
|  |         self.lightpoint_radius = data['lightpoint_radius'] | ||||||
|  |         self.lightpoint_position = data['lightpoint_position'] | ||||||
|  |         self.lightpoint_diffuse_color = data['lightpoint_diffuse_color'] | ||||||
|  |         self.lightpoint_x2 = 1 | ||||||
|  |         self.lightpoint_orientation = data['lightpoint_orientation'] | ||||||
|  |         self.lightpoint_cubemap = data['lightpoint_cubemap'] | ||||||
|  |         self.lightpoint_x3 = False | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['lightpoint_shader'] = self.lightpoint_shader | ||||||
|  |         data['lightpoint_update_offscreen'] = self.lightpoint_update_offscreen | ||||||
|  |         data['lightpoint_radius'] = self.lightpoint_radius | ||||||
|  |         data['lightpoint_position'] = self.lightpoint_position | ||||||
|  |         data['lightpoint_diffuse_color'] = self.lightpoint_diffuse_color | ||||||
|  |         data['lightpoint_orientation'] = self.lightpoint_orientation | ||||||
|  |         data['lightpoint_cubemap'] = self.lightpoint_cubemap | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcLightAffectorAttach: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.attach_x0 = stream.read_dword() | ||||||
|  |         self.attach_offset = stream.read_tuple() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.attach_x0) | ||||||
|  |         stream.write_tuple(self.attach_offset) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.attach_x0 = 1 | ||||||
|  |         self.attach_offset = data['attach_offset'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['attach_offset'] = self.attach_offset | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcLightAffectorFlicker: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.flicker_x0 = stream.read_dword() | ||||||
|  |         self.flicker_avg_period = stream.read_float() | ||||||
|  |         self.flicker_std_dev_radius = stream.read_float() | ||||||
|  |         self.flicker_std_dev_period = stream.read_float() | ||||||
|  |         self.flicker_falloff = stream.read_float() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.flicker_x0) | ||||||
|  |         stream.write_float(self.flicker_avg_period) | ||||||
|  |         stream.write_float(self.flicker_std_dev_radius) | ||||||
|  |         stream.write_float(self.flicker_std_dev_period) | ||||||
|  |         stream.write_float(self.flicker_falloff) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.flicker_x0 = 1 | ||||||
|  |         self.flicker_avg_period = data['flicker_avg_period'] | ||||||
|  |         self.flicker_std_dev_radius = data['flicker_std_dev_radius'] | ||||||
|  |         self.flicker_std_dev_period = data['flicker_std_dev_period'] | ||||||
|  |         self.flicker_falloff = data['flicker_falloff'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['flicker_avg_period'] = self.flicker_avg_period | ||||||
|  |         data['flicker_std_dev_radius'] = self.flicker_std_dev_radius | ||||||
|  |         data['flicker_std_dev_period'] = self.flicker_std_dev_period | ||||||
|  |         data['flicker_falloff'] = self.flicker_falloff | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcLightAffectors: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.light_affector_type = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |         if self.light_affector_type == 0x54e8ff1d: | ||||||
|  |             self.light_affector_data = ArcLightAffectorAttach() | ||||||
|  |         elif self.light_affector_type == 0xa73bd9d4: | ||||||
|  |             self.light_affector_data = ArcLightAffectorFlicker() | ||||||
|  |         self.light_affector_data.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |         self.light_affector_0xdaed = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.light_affector_type) | ||||||
|  |         self.light_affector_data.save_binary(stream) | ||||||
|  |         stream.write_dword(self.light_affector_0xdaed) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.light_affector_type = STRING_TO_LIGHT_TYPE[data['light_affector_type']] | ||||||
|  |         if self.light_affector_type == 0x54e8ff1d: | ||||||
|  |             self.light_affector_data = ArcLightAffectorAttach() | ||||||
|  |         elif self.light_affector_type == 0xa73bd9d4: | ||||||
|  |             self.light_affector_data = ArcLightAffectorFlicker() | ||||||
|  |         self.light_affector_data.load_json(data['light_affector_data']) | ||||||
|  |         self.light_affector_0xdaed = 0xddaaeedd | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['light_affector_type'] = LIGHT_TYPE_TO_STRING[self.light_affector_type] | ||||||
|  |         data['light_affector_data'] = self.light_affector_data.save_json() | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcLight: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.light_x0 = stream.read_dword() | ||||||
|  |         self.light_x1 = stream.read_bool() | ||||||
|  |         self.light_type = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |         if self.light_type == 0xb6787258: | ||||||
|  |             self.light_data = ArcLightPoint() | ||||||
|  |         self.light_data.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |         self.light_0xdaed = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |         num = stream.read_dword() | ||||||
|  |         self.light_affectors = [ArcLightAffectors() for _ in range(num)] | ||||||
|  |         for extra in self.light_affectors: | ||||||
|  |             extra.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.light_x0) | ||||||
|  |         stream.write_bool(self.light_x1) | ||||||
|  |         stream.write_dword(self.light_type) | ||||||
|  |         self.light_data.save_binary(stream) | ||||||
|  |         stream.write_dword(self.light_0xdaed) | ||||||
|  | 
 | ||||||
|  |         stream.write_dword(len(self.light_affectors)) | ||||||
|  |         for extra in self.light_affectors: | ||||||
|  |             extra.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.light_x0 = 1 | ||||||
|  |         self.light_x1 = True | ||||||
|  |         self.light_type = STRING_TO_LIGHT_TYPE[data['light_type']] | ||||||
|  |         if self.light_type == 0xb6787258: | ||||||
|  |             self.light_data = ArcLightPoint() | ||||||
|  |         self.light_data.load_json(data['light_data']) | ||||||
|  |         self.light_0xdaed = 0xddaaeedd | ||||||
|  | 
 | ||||||
|  |         self.light_affectors = [] | ||||||
|  |         for extra_data in data['light_affectors']: | ||||||
|  |             extra = ArcLightAffectors() | ||||||
|  |             extra.load_json(extra_data) | ||||||
|  |             self.light_affectors.append(extra) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['light_type'] = LIGHT_TYPE_TO_STRING[self.light_type] | ||||||
|  |         data['light_data'] = self.light_data.save_json() | ||||||
|  | 
 | ||||||
|  |         data['light_affectors'] = [] | ||||||
|  |         for extra in self.light_affectors: | ||||||
|  |             data['light_affectors'].append(extra.save_json()) | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcRender: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.render_template = ArcRenderTemplate() | ||||||
|  |         self.render_template.load_binary(stream) | ||||||
|  |         self.render_target_bone = stream.read_string() | ||||||
|  |         self.render_scale = stream.read_tuple() | ||||||
|  |         self.render_has_loc = stream.read_dword() | ||||||
|  |         if self.render_has_loc: | ||||||
|  |             self.render_loc = stream.read_tuple() | ||||||
|  |             num_children = stream.read_dword() | ||||||
|  |             self.render_children = [stream.read_qword() for _ in range(num_children)] | ||||||
|  |         self.render_has_texture_set = stream.read_bool() | ||||||
|  |         if self.render_has_texture_set: | ||||||
|  |             num = stream.read_dword() | ||||||
|  |             self.render_texture_set = [ArcTextureSet() for _ in range(num)] | ||||||
|  |             for texture in self.render_texture_set: | ||||||
|  |                 texture.load_binary(stream) | ||||||
|  |         self.render_collides = stream.read_bool() | ||||||
|  |         self.render_calculate_bounding_box = stream.read_bool() | ||||||
|  |         self.render_nation_crest = stream.read_bool() | ||||||
|  |         self.render_guild_crest = stream.read_bool() | ||||||
|  |         self.render_bumped = stream.read_bool() | ||||||
|  |         self.render_vp_active = stream.read_bool() | ||||||
|  |         if self.render_vp_active: | ||||||
|  |             self.render_vp_name = stream.read_string() | ||||||
|  |             num_params = stream.read_dword() | ||||||
|  |             self.render_vp_params = [ | ||||||
|  |                 [ | ||||||
|  |                     stream.read_dword(), | ||||||
|  |                     stream.read_float(), | ||||||
|  |                     stream.read_float(), | ||||||
|  |                     stream.read_float(), | ||||||
|  |                     stream.read_float(), | ||||||
|  |                 ] for _ in range(num_params) | ||||||
|  |             ] | ||||||
|  |         self.render_has_light_effects = stream.read_bool() | ||||||
|  |         if self.render_has_light_effects: | ||||||
|  |             num_effects = stream.read_dword() | ||||||
|  |             self.render_light_effects = [ArcLight() for _ in range(num_effects)] | ||||||
|  |             for effect in self.render_light_effects: | ||||||
|  |                 effect.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         self.render_template.save_binary(stream) | ||||||
|  |         stream.write_string(self.render_target_bone) | ||||||
|  |         stream.write_tuple(self.render_scale) | ||||||
|  |         stream.write_dword(self.render_has_loc) | ||||||
|  |         if self.render_has_loc: | ||||||
|  |             stream.write_tuple(self.render_loc) | ||||||
|  |             stream.write_dword(len(self.render_children)) | ||||||
|  |             for child in self.render_children: | ||||||
|  |                 stream.write_qword(child) | ||||||
|  |         stream.write_bool(self.render_has_texture_set) | ||||||
|  |         if self.render_has_texture_set: | ||||||
|  |             stream.write_dword(len(self.render_texture_set)) | ||||||
|  |             for texture in self.render_texture_set: | ||||||
|  |                 texture.save_binary(stream) | ||||||
|  |         stream.write_bool(self.render_collides) | ||||||
|  |         stream.write_bool(self.render_calculate_bounding_box) | ||||||
|  |         stream.write_bool(self.render_nation_crest) | ||||||
|  |         stream.write_bool(self.render_guild_crest) | ||||||
|  |         stream.write_bool(self.render_bumped) | ||||||
|  |         stream.write_bool(self.render_vp_active) | ||||||
|  |         if self.render_vp_active: | ||||||
|  |             stream.write_string(self.render_vp_name) | ||||||
|  | 
 | ||||||
|  |             stream.write_dword(len(self.render_vp_params)) | ||||||
|  |             for param in self.render_vp_params: | ||||||
|  |                 stream.write_dword(param[0]) | ||||||
|  |                 stream.write_float(param[1]) | ||||||
|  |                 stream.write_float(param[2]) | ||||||
|  |                 stream.write_float(param[3]) | ||||||
|  |                 stream.write_float(param[4]) | ||||||
|  |         stream.write_bool(self.render_has_light_effects) | ||||||
|  |         if self.render_has_light_effects: | ||||||
|  |             stream.write_dword(len(self.render_light_effects)) | ||||||
|  |             for effect in self.render_light_effects: | ||||||
|  |                 effect.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.render_template = ArcRenderTemplate() | ||||||
|  |         self.render_template.load_json(data['render_template']) | ||||||
|  |         self.render_target_bone = data['render_target_bone'] | ||||||
|  |         self.render_scale = data['render_scale'] | ||||||
|  |         self.render_has_loc = data['render_has_loc'] | ||||||
|  |         if self.render_has_loc: | ||||||
|  |             self.render_loc = data['render_loc'] | ||||||
|  |             self.render_children = data['render_children'] | ||||||
|  |         self.render_has_texture_set = data['render_has_texture_set'] | ||||||
|  |         if self.render_has_texture_set: | ||||||
|  |             self.render_texture_set = [] | ||||||
|  |             for texture_data in data['render_texture_set']: | ||||||
|  |                 texture = ArcTextureSet() | ||||||
|  |                 texture.load_json(texture_data) | ||||||
|  |                 self.render_texture_set.append(texture) | ||||||
|  |         self.render_collides = data['render_collides'] | ||||||
|  |         self.render_calculate_bounding_box = data['render_calculate_bounding_box'] | ||||||
|  |         self.render_nation_crest = data['render_nation_crest'] | ||||||
|  |         self.render_guild_crest = data['render_guild_crest'] | ||||||
|  |         self.render_bumped = data['render_bumped'] | ||||||
|  |         self.render_vp_active = data['render_vp_active'] | ||||||
|  |         if self.render_vp_active: | ||||||
|  |             self.render_vp_name = data['render_vp_name'] | ||||||
|  |             self.render_vp_params = data['render_vp_params'] | ||||||
|  |         self.render_has_light_effects = data['render_has_light_effects'] | ||||||
|  |         if self.render_has_light_effects: | ||||||
|  |             self.render_light_effects = [] | ||||||
|  |             for effect_data in data['render_light_effects']: | ||||||
|  |                 effect = ArcLight() | ||||||
|  |                 effect.load_json(effect_data) | ||||||
|  |                 self.render_light_effects.append(effect) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['render_template'] = self.render_template.save_json() | ||||||
|  |         data['render_target_bone'] = self.render_target_bone | ||||||
|  |         data['render_scale'] = self.render_scale | ||||||
|  |         data['render_has_loc'] = self.render_has_loc | ||||||
|  |         if self.render_has_loc: | ||||||
|  |             data['render_loc'] = self.render_loc | ||||||
|  |             data['render_children'] = self.render_children | ||||||
|  |         data['render_has_texture_set'] = self.render_has_texture_set | ||||||
|  |         if self.render_has_texture_set: | ||||||
|  |             data['render_texture_set'] = [] | ||||||
|  |             for texture in self.render_texture_set: | ||||||
|  |                 data['render_texture_set'].append(texture.save_json()) | ||||||
|  |         data['render_collides'] = self.render_collides | ||||||
|  |         data['render_calculate_bounding_box'] = self.render_calculate_bounding_box | ||||||
|  |         data['render_nation_crest'] = self.render_nation_crest | ||||||
|  |         data['render_guild_crest'] = self.render_guild_crest | ||||||
|  |         data['render_bumped'] = self.render_bumped | ||||||
|  |         data['render_vp_active'] = self.render_vp_active | ||||||
|  |         if self.render_vp_active: | ||||||
|  |             data['render_vp_name'] = self.render_vp_name | ||||||
|  |             data['render_vp_params'] = self.render_vp_params | ||||||
|  |         data['render_has_light_effects'] = self.render_has_light_effects | ||||||
|  |         if self.render_has_light_effects: | ||||||
|  |             data['render_light_effects'] = [] | ||||||
|  |             for effect in self.render_light_effects: | ||||||
|  |                 data['render_light_effects'].append(effect.save_json()) | ||||||
|  |         return data | ||||||
| @ -0,0 +1,125 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcBone: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.bone_id = stream.read_dword() | ||||||
|  |         self.bone_name = stream.read_string() | ||||||
|  |         self.bone_direction = stream.read_tuple() | ||||||
|  |         self.bone_length = stream.read_float() | ||||||
|  |         self.bone_axis = stream.read_tuple() | ||||||
|  |         self.bone_dof = stream.read_string() | ||||||
|  |         self.bone_order = stream.read_tuple() | ||||||
|  |         self.bone_position = stream.read_tuple() | ||||||
|  |         self.bone_orientation = stream.read_tuple() | ||||||
|  |         self.bone_u0 = stream.read_bool() | ||||||
|  |         self.bone_u1 = stream.read_bool() | ||||||
|  | 
 | ||||||
|  |         num = stream.read_dword() | ||||||
|  |         self.bone_hierarchy = [ArcBone() for _ in range(num)] | ||||||
|  |         for bone in self.bone_hierarchy: | ||||||
|  |             bone.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.bone_id) | ||||||
|  |         stream.write_string(self.bone_name) | ||||||
|  |         stream.write_tuple(self.bone_direction) | ||||||
|  |         stream.write_float(self.bone_length) | ||||||
|  |         stream.write_tuple(self.bone_axis) | ||||||
|  |         stream.write_string(self.bone_dof) | ||||||
|  |         stream.write_tuple(self.bone_order) | ||||||
|  |         stream.write_tuple(self.bone_position) | ||||||
|  |         stream.write_tuple(self.bone_orientation) | ||||||
|  |         stream.write_bool(self.bone_u0) | ||||||
|  |         stream.write_bool(self.bone_u1) | ||||||
|  | 
 | ||||||
|  |         stream.write_dword(len(self.bone_hierarchy)) | ||||||
|  |         for bone in self.bone_hierarchy: | ||||||
|  |             bone.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.bone_id = data['bone_id'] | ||||||
|  |         self.bone_name = data['bone_name'] | ||||||
|  |         self.bone_direction = data['bone_direction'] | ||||||
|  |         self.bone_length = data['bone_length'] | ||||||
|  |         self.bone_axis = data['bone_axis'] | ||||||
|  |         self.bone_dof = data['bone_dof'] | ||||||
|  |         self.bone_order = data['bone_order'] | ||||||
|  |         self.bone_position = data['bone_position'] | ||||||
|  |         self.bone_orientation = data['bone_orientation'] | ||||||
|  |         self.bone_u0 = data['bone_u0'] | ||||||
|  |         self.bone_u1 = data['bone_u1'] | ||||||
|  | 
 | ||||||
|  |         self.bone_hierarchy = [] | ||||||
|  |         for bone_data in data['bone_hierarchy']: | ||||||
|  |             bone = ArcBone() | ||||||
|  |             bone.load_json(bone_data) | ||||||
|  |             self.bone_hierarchy.append(bone) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['bone_id'] = self.bone_id | ||||||
|  |         data['bone_name'] = self.bone_name | ||||||
|  |         data['bone_direction'] = self.bone_direction | ||||||
|  |         data['bone_length'] = self.bone_length | ||||||
|  |         data['bone_axis'] = self.bone_axis | ||||||
|  |         data['bone_dof'] = self.bone_dof | ||||||
|  |         data['bone_order'] = self.bone_order | ||||||
|  |         data['bone_position'] = self.bone_position | ||||||
|  |         data['bone_orientation'] = self.bone_orientation | ||||||
|  |         data['bone_u0'] = self.bone_u0 | ||||||
|  |         data['bone_u1'] = self.bone_u1 | ||||||
|  | 
 | ||||||
|  |         data['bone_hierarchy'] = [] | ||||||
|  |         for bone in self.bone_hierarchy: | ||||||
|  |             data['bone_hierarchy'].append(bone.save_json()) | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcSkeleton: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.skeleton_name = stream.read_string() | ||||||
|  | 
 | ||||||
|  |         num = stream.read_dword() | ||||||
|  |         self.skeleton_motion = [ | ||||||
|  |             [ | ||||||
|  |                 stream.read_qword(), | ||||||
|  |                 stream.read_qword(), | ||||||
|  |             ] for _ in range(num) | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |         self.skeleton_root = ArcBone() | ||||||
|  |         self.skeleton_root.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_string(self.skeleton_name) | ||||||
|  | 
 | ||||||
|  |         stream.write_dword(len(self.skeleton_motion)) | ||||||
|  |         for i in range(len(self.skeleton_motion)): | ||||||
|  |             stream.write_qword(self.skeleton_motion[i][0]) | ||||||
|  |             stream.write_qword(self.skeleton_motion[i][1]) | ||||||
|  | 
 | ||||||
|  |         self.skeleton_root.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.skeleton_name = data['skeleton_name'] | ||||||
|  |         self.skeleton_motion = data['skeleton_motion'] | ||||||
|  |         self.skeleton_root = ArcBone() | ||||||
|  |         self.skeleton_root.load_json(data['skeleton_root']) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['skeleton_name'] = self.skeleton_name | ||||||
|  |         data['skeleton_motion'] = self.skeleton_motion | ||||||
|  |         data['skeleton_root'] = self.skeleton_root.save_json() | ||||||
|  |         return data | ||||||
| @ -0,0 +1,40 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from wave import Wave_read, Wave_write | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcSound: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         data_size = stream.read_dword() | ||||||
|  |         self.sound_frame_rate = stream.read_dword() | ||||||
|  |         self.sound_n_channels = stream.read_dword() | ||||||
|  |         self.sound_sample_width = stream.read_dword() | ||||||
|  |         self.sound_data = stream.read_bytes(data_size) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         data_size = len(self.sound_data) | ||||||
|  |         stream.write_dword(data_size) | ||||||
|  |         stream.write_dword(self.sound_frame_rate) | ||||||
|  |         stream.write_dword(self.sound_n_channels) | ||||||
|  |         stream.write_dword(self.sound_sample_width) | ||||||
|  |         stream.write_bytes(self.sound_data) | ||||||
|  | 
 | ||||||
|  |     def load_wav(self, stream: Wave_read): | ||||||
|  |         self.sound_frame_rate = stream.getframerate() | ||||||
|  |         self.sound_n_channels = stream.getnchannels() | ||||||
|  |         self.sound_sample_width = stream.getsampwidth() * 8 | ||||||
|  |         self.sound_data = stream.readframes(stream.getnframes()) | ||||||
|  | 
 | ||||||
|  |     def save_wav(self, stream: Wave_write): | ||||||
|  |         stream.setframerate(self.sound_frame_rate) | ||||||
|  |         stream.setnchannels(self.sound_n_channels) | ||||||
|  |         stream.setsampwidth(self.sound_sample_width // 8) | ||||||
|  |         stream.writeframes(self.sound_data) | ||||||
| @ -0,0 +1,242 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | TILE_TO_STRING = { | ||||||
|  |     0: "UNKNOWN", | ||||||
|  |     1: "Regular", | ||||||
|  |     2: "Slope", | ||||||
|  |     3: "Road", | ||||||
|  | } | ||||||
|  | STRING_TO_TILE = {value: key for key, value in TILE_TO_STRING.items()} | ||||||
|  | 
 | ||||||
|  | MASK_TO_STRING = { | ||||||
|  |     0: "UNKNOWN", | ||||||
|  |     1: "Transition", | ||||||
|  |     2: "Road", | ||||||
|  | } | ||||||
|  | STRING_TO_MASK = {value: key for key, value in MASK_TO_STRING.items()} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcTile: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.tile_row_0 = stream.read_dword() | ||||||
|  |         self.tile_row_1 = stream.read_dword() | ||||||
|  |         self.tile_row_2 = stream.read_dword() | ||||||
|  |         self.tile_row_3 = stream.read_dword() | ||||||
|  |         self.tile_prob = stream.read_float() | ||||||
|  |         self.tile_type = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |         num_alts = stream.read_dword() | ||||||
|  |         self.tile_alts = [stream.read_dword() for _ in range(num_alts)] | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.tile_row_0) | ||||||
|  |         stream.write_dword(self.tile_row_1) | ||||||
|  |         stream.write_dword(self.tile_row_2) | ||||||
|  |         stream.write_dword(self.tile_row_3) | ||||||
|  |         stream.write_float(self.tile_prob) | ||||||
|  |         stream.write_dword(self.tile_type) | ||||||
|  |         stream.write_dword(len(self.tile_alts)) | ||||||
|  |         for i in range(len(self.tile_alts)): | ||||||
|  |             stream.write_dword(self.tile_alts[i]) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.tile_row_0 = data['tile_row_0'] | ||||||
|  |         self.tile_row_1 = data['tile_row_1'] | ||||||
|  |         self.tile_row_2 = data['tile_row_2'] | ||||||
|  |         self.tile_row_3 = data['tile_row_3'] | ||||||
|  |         self.tile_prob = data['tile_prob'] | ||||||
|  |         self.tile_type = STRING_TO_TILE[data['tile_type']] | ||||||
|  |         self.tile_alts = data['tile_alts'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['tile_row_0'] = self.tile_row_0 | ||||||
|  |         data['tile_row_1'] = self.tile_row_1 | ||||||
|  |         data['tile_row_2'] = self.tile_row_2 | ||||||
|  |         data['tile_row_3'] = self.tile_row_3 | ||||||
|  |         data['tile_prob'] = self.tile_prob | ||||||
|  |         data['tile_type'] = TILE_TO_STRING[self.tile_type] | ||||||
|  |         data['tile_alts'] = self.tile_alts | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcTileMask: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.mask_row_0 = stream.read_dword() | ||||||
|  |         self.mask_row_1 = stream.read_dword() | ||||||
|  |         self.mask_row_2 = stream.read_dword() | ||||||
|  |         self.mask_row_3 = stream.read_dword() | ||||||
|  |         self.mask_prob = stream.read_float() | ||||||
|  |         self.mask_start = stream.read_dword() | ||||||
|  |         self.mask_end = stream.read_dword() | ||||||
|  |         self.mask_end1 = stream.read_dword() | ||||||
|  |         self.mask_end2 = stream.read_dword() | ||||||
|  |         self.mask_type = stream.read_dword() | ||||||
|  |         self.mask_width = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.mask_row_0) | ||||||
|  |         stream.write_dword(self.mask_row_1) | ||||||
|  |         stream.write_dword(self.mask_row_2) | ||||||
|  |         stream.write_dword(self.mask_row_3) | ||||||
|  |         stream.write_float(self.mask_prob) | ||||||
|  |         stream.write_dword(self.mask_start) | ||||||
|  |         stream.write_dword(self.mask_end) | ||||||
|  |         stream.write_dword(self.mask_end1) | ||||||
|  |         stream.write_dword(self.mask_end2) | ||||||
|  |         stream.write_dword(self.mask_type) | ||||||
|  |         stream.write_dword(self.mask_width) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.mask_row_0 = data['mask_row_0'] | ||||||
|  |         self.mask_row_1 = data['mask_row_1'] | ||||||
|  |         self.mask_row_2 = data['mask_row_2'] | ||||||
|  |         self.mask_row_3 = data['mask_row_3'] | ||||||
|  |         self.mask_prob = data['mask_prob'] | ||||||
|  |         self.mask_start = data['mask_start'] | ||||||
|  |         self.mask_end = data['mask_end'] | ||||||
|  |         self.mask_end1 = data['mask_end1'] | ||||||
|  |         self.mask_end2 = data['mask_end2'] | ||||||
|  |         self.mask_type = STRING_TO_MASK[data['mask_type']] | ||||||
|  |         self.mask_width = data['mask_width'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['mask_row_0'] = self.mask_row_0 | ||||||
|  |         data['mask_row_1'] = self.mask_row_1 | ||||||
|  |         data['mask_row_2'] = self.mask_row_2 | ||||||
|  |         data['mask_row_3'] = self.mask_row_3 | ||||||
|  |         data['mask_prob'] = self.mask_prob | ||||||
|  |         data['mask_start'] = self.mask_start | ||||||
|  |         data['mask_end'] = self.mask_end | ||||||
|  |         data['mask_end1'] = self.mask_end1 | ||||||
|  |         data['mask_end2'] = self.mask_end2 | ||||||
|  |         data['mask_type'] = MASK_TO_STRING[self.mask_type] | ||||||
|  |         data['mask_width'] = self.mask_width | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcTilePattern: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.pattern_row_0 = stream.read_dword() | ||||||
|  |         self.pattern_row_1 = stream.read_dword() | ||||||
|  |         self.pattern_row_2 = stream.read_dword() | ||||||
|  |         self.pattern_row_3 = stream.read_dword() | ||||||
|  |         self.pattern_prob = stream.read_float() | ||||||
|  |         self.pattern_patx = stream.read_dword() | ||||||
|  |         self.pattern_patz = stream.read_dword() | ||||||
|  |         num_tiles = stream.read_dword() | ||||||
|  |         self.pattern_tiles = [stream.read_dword() for _ in range(num_tiles)] | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.pattern_row_0) | ||||||
|  |         stream.write_dword(self.pattern_row_1) | ||||||
|  |         stream.write_dword(self.pattern_row_2) | ||||||
|  |         stream.write_dword(self.pattern_row_3) | ||||||
|  |         stream.write_float(self.pattern_prob) | ||||||
|  |         stream.write_dword(self.pattern_patx) | ||||||
|  |         stream.write_dword(self.pattern_patz) | ||||||
|  |         stream.write_dword(len(self.pattern_tiles)) | ||||||
|  |         for i in range(len(self.pattern_tiles)): | ||||||
|  |             stream.write_dword(self.pattern_tiles[i]) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.pattern_row_0 = data['pattern_row_0'] | ||||||
|  |         self.pattern_row_1 = data['pattern_row_1'] | ||||||
|  |         self.pattern_row_2 = data['pattern_row_2'] | ||||||
|  |         self.pattern_row_3 = data['pattern_row_3'] | ||||||
|  |         self.pattern_prob = data['pattern_prob'] | ||||||
|  |         self.pattern_patx = data['pattern_patx'] | ||||||
|  |         self.pattern_patz = data['pattern_patz'] | ||||||
|  |         self.pattern_tiles = data['pattern_tiles'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['pattern_row_0'] = self.pattern_row_0 | ||||||
|  |         data['pattern_row_1'] = self.pattern_row_1 | ||||||
|  |         data['pattern_row_2'] = self.pattern_row_2 | ||||||
|  |         data['pattern_row_3'] = self.pattern_row_3 | ||||||
|  |         data['pattern_prob'] = self.pattern_prob | ||||||
|  |         data['pattern_patx'] = self.pattern_patx | ||||||
|  |         data['pattern_patz'] = self.pattern_patz | ||||||
|  |         data['pattern_tiles'] = self.pattern_tiles | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcTileManager: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.manager_texture_width = stream.read_dword() | ||||||
|  |         self.manager_tile_width = stream.read_dword() | ||||||
|  |         self.manager_tile_texture = stream.read_qword() | ||||||
|  |         num_tiles = stream.read_dword() | ||||||
|  |         self.manager_tiles = [ArcTile() for _ in range(num_tiles)] | ||||||
|  |         for tile in self.manager_tiles: | ||||||
|  |             tile.load_binary(stream) | ||||||
|  |         num_masks = stream.read_dword() | ||||||
|  |         self.manager_masks = [ArcTileMask() for _ in range(num_masks)] | ||||||
|  |         for mask in self.manager_masks: | ||||||
|  |             mask.load_binary(stream) | ||||||
|  |         num_patterns = stream.read_dword() | ||||||
|  |         self.manager_patterns = [ArcTilePattern() for _ in range(num_patterns)] | ||||||
|  |         for pattern in self.manager_patterns: | ||||||
|  |             pattern.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.manager_texture_width) | ||||||
|  |         stream.write_dword(self.manager_tile_width) | ||||||
|  |         stream.write_qword(self.manager_tile_texture) | ||||||
|  |         stream.write_dword(len(self.manager_tiles)) | ||||||
|  |         for tile in self.manager_tiles: | ||||||
|  |             tile.save_binary(stream) | ||||||
|  |         stream.write_dword(len(self.manager_masks)) | ||||||
|  |         for mask in self.manager_masks: | ||||||
|  |             mask.save_binary(stream) | ||||||
|  |         stream.write_dword(len(self.manager_patterns)) | ||||||
|  |         for pattern in self.manager_patterns: | ||||||
|  |             pattern.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.manager_texture_width = data['manager_texture_width'] | ||||||
|  |         self.manager_tile_width = data['manager_tile_width'] | ||||||
|  |         self.manager_tile_texture = data['manager_tile_texture'] | ||||||
|  |         self.manager_tiles = [] | ||||||
|  |         for tile_data in data['manager_tiles']: | ||||||
|  |             tile = ArcTile() | ||||||
|  |             tile.load_json(tile_data) | ||||||
|  |             self.manager_tiles.append(tile) | ||||||
|  |         self.manager_masks = [] | ||||||
|  |         for mask_data in data['manager_masks']: | ||||||
|  |             mask = ArcTileMask() | ||||||
|  |             mask.load_json(mask_data) | ||||||
|  |             self.manager_masks.append(mask) | ||||||
|  |         self.manager_patterns = [] | ||||||
|  |         for pattern_data in data['manager_patterns']: | ||||||
|  |             pattern = ArcTilePattern() | ||||||
|  |             pattern.load_json(pattern_data) | ||||||
|  |             self.manager_patterns.append(pattern) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['manager_texture_width'] = self.manager_texture_width | ||||||
|  |         data['manager_tile_width'] = self.manager_tile_width | ||||||
|  |         data['manager_tile_texture'] = self.manager_tile_texture | ||||||
|  |         data['manager_tiles'] = [] | ||||||
|  |         for tile in self.manager_tiles: | ||||||
|  |             data['manager_tiles'].append(tile.save_json()) | ||||||
|  |         data['manager_masks'] = [] | ||||||
|  |         for mask in self.manager_masks: | ||||||
|  |             data['manager_masks'].append(mask.save_json()) | ||||||
|  |         data['manager_patterns'] = [] | ||||||
|  |         for pattern in self.manager_patterns: | ||||||
|  |             data['manager_patterns'].append(pattern.save_json()) | ||||||
|  |         return data | ||||||
| @ -0,0 +1,396 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄ | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪ | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | from base64 import b64decode, b64encode | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | EFFETCT_TO_STRING = { | ||||||
|  |     0: 'PARTICLE', | ||||||
|  |     1: 'LIGHTNING', | ||||||
|  |     2: 'GEOMETRY', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_EFFECT = {value: key for key, value in EFFETCT_TO_STRING.items()} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcParticle: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.particle_attached_bone = stream.read_dword() | ||||||
|  |         self.particle_count = stream.read_dword() | ||||||
|  |         self.particle_size = stream.read_float() | ||||||
|  |         self.particle_life = stream.read_float() | ||||||
|  |         self.particle_life_rand = stream.read_float() | ||||||
|  |         self.particle_shape_and_pos = stream.read_dword() | ||||||
|  |         self.particle_emitter_scale = stream.read_float() | ||||||
|  |         self.particle_pos_offset = stream.read_tuple() | ||||||
|  |         self.particle_ppos_reference = stream.read_tuple() | ||||||
|  |         self.particle_initial_velocities = stream.read_dword() | ||||||
|  |         self.particle_velocity_scale = stream.read_float() | ||||||
|  |         self.particle_vel_reference = stream.read_tuple() | ||||||
|  |         self.particle_dir_random = stream.read_float() | ||||||
|  |         self.particle_spd_random = stream.read_float() | ||||||
|  |         self.particle_initial_rot = stream.read_float() | ||||||
|  |         self.particle_initial_rot_random = stream.read_float() | ||||||
|  |         self.particle_incremental_rot = stream.read_float() | ||||||
|  |         self.particle_incremental_rot_random = stream.read_float() | ||||||
|  |         self.particle_color_keys = [ | ||||||
|  |             [ | ||||||
|  |                 stream.read_float(), | ||||||
|  |                 stream.read_float(), | ||||||
|  |                 stream.read_float(), | ||||||
|  |                 stream.read_float(), | ||||||
|  |             ] for _ in range(5) | ||||||
|  |         ] | ||||||
|  |         self.particle_color_keytimes = [stream.read_float() for _ in range(5)] | ||||||
|  |         self.particle_size_keys = [stream.read_float() for _ in range(5)] | ||||||
|  |         self.particle_targettype = stream.read_dword() | ||||||
|  |         self.particle_lifetime = stream.read_float() | ||||||
|  |         self.particle_texture = stream.read_dword() | ||||||
|  |         self.particle_blend_type = stream.read_dword() | ||||||
|  |         self.particle_attractor_bone = stream.read_dword() | ||||||
|  |         self.particle_directional_grav = stream.read_tuple() | ||||||
|  |         self.particle_field_function = stream.read_dword() | ||||||
|  |         self.particle_gravity_strength = stream.read_float() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.particle_attached_bone) | ||||||
|  |         stream.write_dword(self.particle_count) | ||||||
|  |         stream.write_float(self.particle_size) | ||||||
|  |         stream.write_float(self.particle_life) | ||||||
|  |         stream.write_float(self.particle_life_rand) | ||||||
|  |         stream.write_dword(self.particle_shape_and_pos) | ||||||
|  |         stream.write_float(self.particle_emitter_scale) | ||||||
|  |         stream.write_tuple(self.particle_pos_offset) | ||||||
|  |         stream.write_tuple(self.particle_ppos_reference) | ||||||
|  |         stream.write_dword(self.particle_initial_velocities) | ||||||
|  |         stream.write_float(self.particle_velocity_scale) | ||||||
|  |         stream.write_tuple(self.particle_vel_reference) | ||||||
|  |         stream.write_float(self.particle_dir_random) | ||||||
|  |         stream.write_float(self.particle_spd_random) | ||||||
|  |         stream.write_float(self.particle_initial_rot) | ||||||
|  |         stream.write_float(self.particle_initial_rot_random) | ||||||
|  |         stream.write_float(self.particle_incremental_rot) | ||||||
|  |         stream.write_float(self.particle_incremental_rot_random) | ||||||
|  |         for i in range(5): | ||||||
|  |             stream.write_float(self.particle_color_keys[i][0]) | ||||||
|  |             stream.write_float(self.particle_color_keys[i][1]) | ||||||
|  |             stream.write_float(self.particle_color_keys[i][2]) | ||||||
|  |             stream.write_float(self.particle_color_keys[i][3]) | ||||||
|  |         for i in range(5): | ||||||
|  |             stream.write_float(self.particle_color_keytimes[i]) | ||||||
|  |         for i in range(5): | ||||||
|  |             stream.write_float(self.particle_size_keys[i]) | ||||||
|  |         stream.write_dword(self.particle_targettype) | ||||||
|  |         stream.write_float(self.particle_lifetime) | ||||||
|  |         stream.write_dword(self.particle_texture) | ||||||
|  |         stream.write_dword(self.particle_blend_type) | ||||||
|  |         stream.write_dword(self.particle_attractor_bone) | ||||||
|  |         stream.write_tuple(self.particle_directional_grav) | ||||||
|  |         stream.write_dword(self.particle_field_function) | ||||||
|  |         stream.write_float(self.particle_gravity_strength) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.particle_attached_bone = data['particle_attached_bone'] | ||||||
|  |         self.particle_count = data['particle_count'] | ||||||
|  |         self.particle_size = data['particle_size'] | ||||||
|  |         self.particle_life = data['particle_life'] | ||||||
|  |         self.particle_life_rand = data['particle_life_rand'] | ||||||
|  |         self.particle_shape_and_pos = data['particle_shape_and_pos'] | ||||||
|  |         self.particle_emitter_scale = data['particle_emitter_scale'] | ||||||
|  |         self.particle_pos_offset = data['particle_pos_offset'] | ||||||
|  |         self.particle_ppos_reference = data['particle_ppos_reference'] | ||||||
|  |         self.particle_initial_velocities = data['particle_initial_velocities'] | ||||||
|  |         self.particle_velocity_scale = data['particle_velocity_scale'] | ||||||
|  |         self.particle_vel_reference = data['particle_vel_reference'] | ||||||
|  |         self.particle_dir_random = data['particle_dir_random'] | ||||||
|  |         self.particle_spd_random = data['particle_spd_random'] | ||||||
|  |         self.particle_initial_rot = data['particle_initial_rot'] | ||||||
|  |         self.particle_initial_rot_random = data['particle_initial_rot_random'] | ||||||
|  |         self.particle_incremental_rot = data['particle_incremental_rot'] | ||||||
|  |         self.particle_incremental_rot_random = data['particle_incremental_rot_random'] | ||||||
|  |         self.particle_color_keys = data['particle_color_keys'] | ||||||
|  |         self.particle_color_keytimes = data['particle_color_keytimes'] | ||||||
|  |         self.particle_size_keys = data['particle_size_keys'] | ||||||
|  |         self.particle_targettype = data['particle_targettype'] | ||||||
|  |         self.particle_lifetime = data['particle_lifetime'] | ||||||
|  |         self.particle_texture = data['particle_texture'] | ||||||
|  |         self.particle_blend_type = data['particle_blend_type'] | ||||||
|  |         self.particle_attractor_bone = data['particle_attractor_bone'] | ||||||
|  |         self.particle_directional_grav = data['particle_directional_grav'] | ||||||
|  |         self.particle_field_function = data['particle_field_function'] | ||||||
|  |         self.particle_gravity_strength = data['particle_gravity_strength'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['particle_attached_bone'] = self.particle_attached_bone | ||||||
|  |         data['particle_count'] = self.particle_count | ||||||
|  |         data['particle_size'] = self.particle_size | ||||||
|  |         data['particle_life'] = self.particle_life | ||||||
|  |         data['particle_life_rand'] = self.particle_life_rand | ||||||
|  |         data['particle_shape_and_pos'] = self.particle_shape_and_pos | ||||||
|  |         data['particle_emitter_scale'] = self.particle_emitter_scale | ||||||
|  |         data['particle_pos_offset'] = self.particle_pos_offset | ||||||
|  |         data['particle_ppos_reference'] = self.particle_ppos_reference | ||||||
|  |         data['particle_initial_velocities'] = self.particle_initial_velocities | ||||||
|  |         data['particle_velocity_scale'] = self.particle_velocity_scale | ||||||
|  |         data['particle_vel_reference'] = self.particle_vel_reference | ||||||
|  |         data['particle_dir_random'] = self.particle_dir_random | ||||||
|  |         data['particle_spd_random'] = self.particle_spd_random | ||||||
|  |         data['particle_initial_rot'] = self.particle_initial_rot | ||||||
|  |         data['particle_initial_rot_random'] = self.particle_initial_rot_random | ||||||
|  |         data['particle_incremental_rot'] = self.particle_incremental_rot | ||||||
|  |         data['particle_incremental_rot_random'] = self.particle_incremental_rot_random | ||||||
|  |         data['particle_color_keys'] = self.particle_color_keys | ||||||
|  |         data['particle_color_keytimes'] = self.particle_color_keytimes | ||||||
|  |         data['particle_size_keys'] = self.particle_size_keys | ||||||
|  |         data['particle_targettype'] = self.particle_targettype | ||||||
|  |         data['particle_lifetime'] = self.particle_lifetime | ||||||
|  |         data['particle_texture'] = self.particle_texture | ||||||
|  |         data['particle_blend_type'] = self.particle_blend_type | ||||||
|  |         data['particle_attractor_bone'] = self.particle_attractor_bone | ||||||
|  |         data['particle_directional_grav'] = self.particle_directional_grav | ||||||
|  |         data['particle_field_function'] = self.particle_field_function | ||||||
|  |         data['particle_gravity_strength'] = self.particle_gravity_strength | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcLightning: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.lightning_texture = stream.read_dword() | ||||||
|  |         self.lightning_src_bone = stream.read_dword() | ||||||
|  |         self.lightning_dst_bone = stream.read_dword() | ||||||
|  |         self.lightning_width = stream.read_float() | ||||||
|  |         self.lightning_random_factor = stream.read_float() | ||||||
|  |         self.lightning_sine_factor = stream.read_float() | ||||||
|  |         self.lightning_sine_phase = stream.read_float() | ||||||
|  |         self.lightning_sine_phase_rep = stream.read_float() | ||||||
|  |         self.lightning_length = stream.read_float() | ||||||
|  |         self.lightning_random_move_speed = stream.read_float() | ||||||
|  |         self.lightning_color = stream.read_tuple() | ||||||
|  |         self.lightning_lifetime = stream.read_float() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.lightning_texture) | ||||||
|  |         stream.write_dword(self.lightning_src_bone) | ||||||
|  |         stream.write_dword(self.lightning_dst_bone) | ||||||
|  |         stream.write_float(self.lightning_width) | ||||||
|  |         stream.write_float(self.lightning_random_factor) | ||||||
|  |         stream.write_float(self.lightning_sine_factor) | ||||||
|  |         stream.write_float(self.lightning_sine_phase) | ||||||
|  |         stream.write_float(self.lightning_sine_phase_rep) | ||||||
|  |         stream.write_float(self.lightning_length) | ||||||
|  |         stream.write_float(self.lightning_random_move_speed) | ||||||
|  |         stream.write_tuple(self.lightning_color) | ||||||
|  |         stream.write_float(self.lightning_lifetime) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.lightning_texture = data['lightning_texture'] | ||||||
|  |         self.lightning_src_bone = data['lightning_src_bone'] | ||||||
|  |         self.lightning_dst_bone = data['lightning_dst_bone'] | ||||||
|  |         self.lightning_width = data['lightning_width'] | ||||||
|  |         self.lightning_random_factor = data['lightning_random_factor'] | ||||||
|  |         self.lightning_sine_factor = data['lightning_sine_factor'] | ||||||
|  |         self.lightning_sine_phase = data['lightning_sine_phase'] | ||||||
|  |         self.lightning_sine_phase_rep = data['lightning_sine_phase_rep'] | ||||||
|  |         self.lightning_length = data['lightning_length'] | ||||||
|  |         self.lightning_random_move_speed = data['lightning_random_move_speed'] | ||||||
|  |         self.lightning_color = data['lightning_color'] | ||||||
|  |         self.lightning_lifetime = data['lightning_lifetime'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['lightning_texture'] = self.lightning_texture | ||||||
|  |         data['lightning_src_bone'] = self.lightning_src_bone | ||||||
|  |         data['lightning_dst_bone'] = self.lightning_dst_bone | ||||||
|  |         data['lightning_width'] = self.lightning_width | ||||||
|  |         data['lightning_random_factor'] = self.lightning_random_factor | ||||||
|  |         data['lightning_sine_factor'] = self.lightning_sine_factor | ||||||
|  |         data['lightning_sine_phase'] = self.lightning_sine_phase | ||||||
|  |         data['lightning_sine_phase_rep'] = self.lightning_sine_phase_rep | ||||||
|  |         data['lightning_length'] = self.lightning_length | ||||||
|  |         data['lightning_random_move_speed'] = self.lightning_random_move_speed | ||||||
|  |         data['lightning_color'] = self.lightning_color | ||||||
|  |         data['lightning_lifetime'] = self.lightning_lifetime | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcGeometry: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.geometry_texture = stream.read_dword() | ||||||
|  |         self.geometry_src_bone = stream.read_dword() | ||||||
|  |         self.geometry_lifetime = stream.read_float() | ||||||
|  |         self.geometry_tex_trans_x = stream.read_float() | ||||||
|  |         self.geometry_tex_trans_y = stream.read_float() | ||||||
|  |         self.geometry_tex_rot = stream.read_float() | ||||||
|  |         self.geometry_grow = stream.read_tuple() | ||||||
|  |         self.geometry_tesselation = stream.read_float() | ||||||
|  |         self.geometry_size = stream.read_tuple() | ||||||
|  |         self.geometry_geo_rot_x = stream.read_float() | ||||||
|  |         self.geometry_fade_falloff = stream.read_float() | ||||||
|  |         self.geometry_fadein = stream.read_float() | ||||||
|  |         self.geometry_fadeout = stream.read_float() | ||||||
|  |         self.geometry_color = [stream.read_float() for _ in range(4)] | ||||||
|  |         self.geometry_type = stream.read_dword() | ||||||
|  |         self.geometry_texture_proj = stream.read_dword() | ||||||
|  |         self.geometry_fade_dir = stream.read_dword() | ||||||
|  |         self.geometry_offset = stream.read_tuple() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.geometry_texture) | ||||||
|  |         stream.write_dword(self.geometry_src_bone) | ||||||
|  |         stream.write_float(self.geometry_lifetime) | ||||||
|  |         stream.write_float(self.geometry_tex_trans_x) | ||||||
|  |         stream.write_float(self.geometry_tex_trans_y) | ||||||
|  |         stream.write_float(self.geometry_tex_rot) | ||||||
|  |         stream.write_tuple(self.geometry_grow) | ||||||
|  |         stream.write_float(self.geometry_tesselation) | ||||||
|  |         stream.write_tuple(self.geometry_size) | ||||||
|  |         stream.write_float(self.geometry_geo_rot_x) | ||||||
|  |         stream.write_float(self.geometry_fade_falloff) | ||||||
|  |         stream.write_float(self.geometry_fadein) | ||||||
|  |         stream.write_float(self.geometry_fadeout) | ||||||
|  |         for i in range(4): | ||||||
|  |             stream.write_float(self.geometry_color[i]) | ||||||
|  |         stream.write_dword(self.geometry_type) | ||||||
|  |         stream.write_dword(self.geometry_texture_proj) | ||||||
|  |         stream.write_dword(self.geometry_fade_dir) | ||||||
|  |         stream.write_tuple(self.geometry_offset) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.geometry_texture = data['geometry_texture'] | ||||||
|  |         self.geometry_src_bone = data['geometry_src_bone'] | ||||||
|  |         self.geometry_lifetime = data['geometry_lifetime'] | ||||||
|  |         self.geometry_tex_trans_x = data['geometry_tex_trans_x'] | ||||||
|  |         self.geometry_tex_trans_y = data['geometry_tex_trans_y'] | ||||||
|  |         self.geometry_tex_rot = data['geometry_tex_rot'] | ||||||
|  |         self.geometry_grow = data['geometry_grow'] | ||||||
|  |         self.geometry_tesselation = data['geometry_tesselation'] | ||||||
|  |         self.geometry_size = data['geometry_size'] | ||||||
|  |         self.geometry_geo_rot_x = data['geometry_geo_rot_x'] | ||||||
|  |         self.geometry_fade_falloff = data['geometry_fade_falloff'] | ||||||
|  |         self.geometry_fadein = data['geometry_fadein'] | ||||||
|  |         self.geometry_fadeout = data['geometry_fadeout'] | ||||||
|  |         self.geometry_color = data['geometry_color'] | ||||||
|  |         self.geometry_type = data['geometry_type'] | ||||||
|  |         self.geometry_texture_proj = data['geometry_texture_proj'] | ||||||
|  |         self.geometry_fade_dir = data['geometry_fade_dir'] | ||||||
|  |         self.geometry_offset = data['geometry_offset'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['geometry_texture'] = self.geometry_texture | ||||||
|  |         data['geometry_src_bone'] = self.geometry_src_bone | ||||||
|  |         data['geometry_lifetime'] = self.geometry_lifetime | ||||||
|  |         data['geometry_tex_trans_x'] = self.geometry_tex_trans_x | ||||||
|  |         data['geometry_tex_trans_y'] = self.geometry_tex_trans_y | ||||||
|  |         data['geometry_tex_rot'] = self.geometry_tex_rot | ||||||
|  |         data['geometry_grow'] = self.geometry_grow | ||||||
|  |         data['geometry_tesselation'] = self.geometry_tesselation | ||||||
|  |         data['geometry_size'] = self.geometry_size | ||||||
|  |         data['geometry_geo_rot_x'] = self.geometry_geo_rot_x | ||||||
|  |         data['geometry_fade_falloff'] = self.geometry_fade_falloff | ||||||
|  |         data['geometry_fadein'] = self.geometry_fadein | ||||||
|  |         data['geometry_fadeout'] = self.geometry_fadeout | ||||||
|  |         data['geometry_color'] = self.geometry_color | ||||||
|  |         data['geometry_type'] = self.geometry_type | ||||||
|  |         data['geometry_texture_proj'] = self.geometry_texture_proj | ||||||
|  |         data['geometry_fade_dir'] = self.geometry_fade_dir | ||||||
|  |         data['geometry_offset'] = self.geometry_offset | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcVisualEffect: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.effect_type = stream.read_dword() | ||||||
|  |         self.effect_time = stream.read_float() | ||||||
|  | 
 | ||||||
|  |         if self.effect_type == 0: | ||||||
|  |             self.effect = ArcParticle() | ||||||
|  |         elif self.effect_type == 1: | ||||||
|  |             self.effect = ArcLightning() | ||||||
|  |         elif self.effect_type == 2: | ||||||
|  |             self.effect = ArcGeometry() | ||||||
|  |         self.effect.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(self.effect_type) | ||||||
|  |         stream.write_float(self.effect_time) | ||||||
|  |         self.effect.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.effect_type = STRING_TO_EFFECT[data['effect_type']] | ||||||
|  |         self.effect_time = data['effect_time'] | ||||||
|  |         if self.effect_type == 0: | ||||||
|  |             self.effect = ArcParticle() | ||||||
|  |         elif self.effect_type == 1: | ||||||
|  |             self.effect = ArcLightning() | ||||||
|  |         elif self.effect_type == 2: | ||||||
|  |             self.effect = ArcGeometry() | ||||||
|  |         self.effect.load_json(data['effect']) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['effect_type'] = EFFETCT_TO_STRING[self.effect_type] | ||||||
|  |         data['effect_time'] = self.effect_time | ||||||
|  |         data['effect'] = self.effect.save_json() | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcVisual: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         num_effects = stream.read_dword() | ||||||
|  | 
 | ||||||
|  |         self.vfx_fail = None | ||||||
|  |         if num_effects == 1617156728: | ||||||
|  |             stream.buffer.seek(0, 0) | ||||||
|  |             self.vfx_fail = stream.buffer.read() | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         self.vfx_duration = stream.read_float() | ||||||
|  |         self.vfx_effects = [ArcVisualEffect() for _ in range(num_effects)] | ||||||
|  |         for effect in self.vfx_effects: | ||||||
|  |             effect.load_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         if self.vfx_fail: | ||||||
|  |             stream.buffer.write(self.vfx_fail) | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         stream.write_dword(len(self.vfx_effects)) | ||||||
|  |         stream.write_float(self.vfx_duration) | ||||||
|  |         for effect in self.vfx_effects: | ||||||
|  |             effect.save_binary(stream) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.vfx_fail = data.get('vfx_fail') | ||||||
|  |         if self.vfx_fail: | ||||||
|  |             self.vfx_fail = b64decode(self.vfx_fail) | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         self.vfx_duration = data['vfx_duration'] | ||||||
|  |         self.vfx_effects = [] | ||||||
|  |         for effect_data in data['vfx_effects']: | ||||||
|  |             effect = ArcVisualEffect() | ||||||
|  |             effect.load_json(effect_data) | ||||||
|  |             self.vfx_effects.append(effect) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         if self.vfx_fail: | ||||||
|  |             data['vfx_fail'] = b64encode(self.vfx_fail).decode() | ||||||
|  |             return data | ||||||
|  | 
 | ||||||
|  |         data['vfx_duration'] = self.vfx_duration | ||||||
|  |         data['vfx_effects'] = [] | ||||||
|  |         for effect in self.vfx_effects: | ||||||
|  |             data['vfx_effects'].append(effect.save_json()) | ||||||
|  |         return data | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | INVENTORY_TYPE_GOLD = 1 | ||||||
|  | INVENTORY_TYPE_ITEM = 2 | ||||||
|  | INVENTORY_TYPE_BOOTYTABLE = 3 | ||||||
|  | 
 | ||||||
|  | INVENTORY_TYPE_TO_STRING = { | ||||||
|  |     INVENTORY_TYPE_GOLD: 'GOLD', | ||||||
|  |     INVENTORY_TYPE_ITEM: 'ITEM', | ||||||
|  |     INVENTORY_TYPE_BOOTYTABLE: 'BOOTYTABLE', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_INVENTORY_TYPE = { | ||||||
|  |     'GOLD': INVENTORY_TYPE_GOLD, | ||||||
|  |     'ITEM': INVENTORY_TYPE_ITEM, | ||||||
|  |     'BOOTYTABLE': INVENTORY_TYPE_BOOTYTABLE, | ||||||
|  | } | ||||||
| @ -0,0 +1,41 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | import glob | ||||||
|  | import os | ||||||
|  | 
 | ||||||
|  | from arcane.util.hasher import hash_string | ||||||
|  | 
 | ||||||
|  | _STRING_TO_HASH = {} | ||||||
|  | _HASH_TO_STRING = {} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def load_files(): | ||||||
|  |     directory = os.path.dirname(__file__) | ||||||
|  | 
 | ||||||
|  |     for filepath in glob.glob(os.path.join(directory, '*.txt')): | ||||||
|  |         lines = list(map(lambda s: s.strip(), open(filepath).readlines())) | ||||||
|  | 
 | ||||||
|  |         _STRING_TO_HASH.update({ | ||||||
|  |             s: hash_string(s) for s in lines | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         _HASH_TO_STRING.update({ | ||||||
|  |             hash_string(s): s for s in lines | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def string_to_hash(s): | ||||||
|  |     return _STRING_TO_HASH.get(s, s) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def hash_to_string(h): | ||||||
|  |     return _HASH_TO_STRING.get(h, h) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | load_files() | ||||||
| @ -0,0 +1,38 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | MOBILE = 1 | ||||||
|  | BANKER = 2 | ||||||
|  | SHOPKEEPER = 3 | ||||||
|  | TRAINER = 4 | ||||||
|  | MERCHANT = 8 | ||||||
|  | HIRELING = 9 | ||||||
|  | PET = 10 | ||||||
|  | MINION = 11 | ||||||
|  | 
 | ||||||
|  | MOBILE_TO_STRING = { | ||||||
|  |     MOBILE: 'MOBILE', | ||||||
|  |     BANKER: 'BANKER', | ||||||
|  |     SHOPKEEPER: 'SHOPKEEPER', | ||||||
|  |     TRAINER: 'TRAINER', | ||||||
|  |     MERCHANT: 'MERCHANT', | ||||||
|  |     HIRELING: 'HIRELING', | ||||||
|  |     PET: 'PET', | ||||||
|  |     MINION: 'MINION', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_MOBILE = { | ||||||
|  |     'MOBILE': MOBILE, | ||||||
|  |     'BANKER': BANKER, | ||||||
|  |     'SHOPKEEPER': SHOPKEEPER, | ||||||
|  |     'TRAINER': TRAINER, | ||||||
|  |     'MERCHANT': MERCHANT, | ||||||
|  |     'HIRELING': HIRELING, | ||||||
|  |     'PET': PET, | ||||||
|  |     'MINION': MINION, | ||||||
|  | } | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | PROP = 5 | ||||||
|  | CITY = 6 | ||||||
|  | CONTAINER = 7 | ||||||
|  | 
 | ||||||
|  | PROP_TO_STRING = { | ||||||
|  |     PROP: 'PROP', | ||||||
|  |     CITY: 'CITY', | ||||||
|  |     CONTAINER: 'CONTAINER', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_PROP = { | ||||||
|  |     'PROP': PROP, | ||||||
|  |     'CITY': CITY, | ||||||
|  |     'CONTAINER': CONTAINER, | ||||||
|  | } | ||||||
| @ -0,0 +1,35 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | PEAKED = 1 | ||||||
|  | RIDGED = 2 | ||||||
|  | ROLLING = 3 | ||||||
|  | MESA = 5 | ||||||
|  | PLANAR = 4 | ||||||
|  | MESH = 6 | ||||||
|  | TARGA = 7 | ||||||
|  | 
 | ||||||
|  | TERRAIN_TYPE_TO_STRING = { | ||||||
|  |     PEAKED: 'PEAKED', | ||||||
|  |     RIDGED: 'RIDGED', | ||||||
|  |     ROLLING: 'ROLLING', | ||||||
|  |     MESA: 'MESA', | ||||||
|  |     PLANAR: 'PLANAR', | ||||||
|  |     MESH: 'MESH', | ||||||
|  |     TARGA: 'TARGA', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_TERRAIN_TYPE = { | ||||||
|  |     'PEAKED': PEAKED, | ||||||
|  |     'RIDGED': RIDGED, | ||||||
|  |     'ROLLING': ROLLING, | ||||||
|  |     'MESA': MESA, | ||||||
|  |     'PLANAR': PLANAR, | ||||||
|  |     'MESH': MESH, | ||||||
|  |     'TARGA': TARGA, | ||||||
|  | } | ||||||
| @ -0,0 +1,62 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | ELLIPTICAL = 0 | ||||||
|  | RECTANGULAR = 1 | ||||||
|  | 
 | ||||||
|  | ZONE_TO_STRING = { | ||||||
|  |     ELLIPTICAL: 'ELLIPTICAL', | ||||||
|  |     RECTANGULAR: 'RECTANGULAR', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_ZONE = { | ||||||
|  |     'ELLIPTICAL': ELLIPTICAL, | ||||||
|  |     'RECTANGULAR': RECTANGULAR, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GLOBAL = 0 | ||||||
|  | LOCAL = 1 | ||||||
|  | 
 | ||||||
|  | TILECOORD_TO_STRING = { | ||||||
|  |     GLOBAL: 'GLOBAL', | ||||||
|  |     LOCAL: 'LOCAL', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_TILECOORD = { | ||||||
|  |     'GLOBAL': GLOBAL, | ||||||
|  |     'LOCAL': LOCAL, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ADJACENT = 0 | ||||||
|  | RANDOM = 1 | ||||||
|  | 
 | ||||||
|  | PATTERN_TO_STRING = { | ||||||
|  |     ADJACENT: 'ADJACENT', | ||||||
|  |     RANDOM: 'RANDOM', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_PATTERN = { | ||||||
|  |     'ADJACENT': ADJACENT, | ||||||
|  |     'RANDOM': RANDOM, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | PARENT = 0 | ||||||
|  | WORLD = 1 | ||||||
|  | SELF = 2 | ||||||
|  | 
 | ||||||
|  | SEALEVEL_TO_STRING = { | ||||||
|  |     PARENT: 'PARENT', | ||||||
|  |     WORLD: 'WORLD', | ||||||
|  |     SELF: 'SELF', | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | STRING_TO_SEALEVEL = { | ||||||
|  |     'PARENT': PARENT, | ||||||
|  |     'WORLD': WORLD, | ||||||
|  |     'SELF': SELF, | ||||||
|  | } | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | from .ArcStructureObject import ArcStructureObject | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcAssetStructureObject(ArcStructureObject): | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         super().load_binary(stream) | ||||||
|  |         self.asset_structure_template_id = stream.read_qword() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         super().save_binary(stream) | ||||||
|  |         stream.write_qword(self.asset_structure_template_id) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         super().load_json(data) | ||||||
|  |         self.asset_structure_template_id = data['asset_structure_template_id'] | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = super().save_json() | ||||||
|  |         data['asset_structure_template_id'] = self.asset_structure_template_id | ||||||
|  |         return data | ||||||
| @ -0,0 +1,21 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from .ArcAssetStructureObject import ArcAssetStructureObject | ||||||
|  | from .ArcCityAssetTemplate import ArcCityAssetTemplate | ||||||
|  | from .ArcCombatObj import ArcCombatObj, ArcCharacter | ||||||
|  | from .ArcContainerObject import ArcContainerObject | ||||||
|  | from .ArcDeed import ArcDeed | ||||||
|  | from .ArcDoorObject import ArcDoorObject | ||||||
|  | from .ArcDungeonUnitObject import ArcDungeonUnitObject, ArcDungeonExitObject, ArcDungeonStairObject | ||||||
|  | from .ArcItem import ArcItem | ||||||
|  | from .ArcKey import ArcKey | ||||||
|  | from .ArcObj import ArcObj | ||||||
|  | from .ArcRune import ArcRune | ||||||
|  | from .ArcStaticObject import ArcStaticObject | ||||||
|  | from .ArcStructureObject import ArcStructureObject | ||||||
| @ -0,0 +1,41 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.enums.hashes import hash_to_string, string_to_hash | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class DiscRequired: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         discs = stream.read_dword() | ||||||
|  |         self.disc_restrict = stream.read_bool() | ||||||
|  |         self.disc_values = [ | ||||||
|  |             stream.read_dword() for _ in range(discs) | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(len(self.disc_values)) | ||||||
|  |         stream.write_bool(self.disc_restrict) | ||||||
|  |         for disc in self.disc_values: | ||||||
|  |             stream.write_dword(disc) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.disc_restrict = data['restrict'] | ||||||
|  |         self.disc_values = [] | ||||||
|  |         for disc in data['discs']: | ||||||
|  |             self.disc_values.append(string_to_hash(disc)) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['restrict'] = self.disc_restrict | ||||||
|  |         data['discs'] = [] | ||||||
|  |         for disc in self.disc_values: | ||||||
|  |             data['discs'].append(hash_to_string(disc)) | ||||||
|  |         return data | ||||||
| @ -0,0 +1,41 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.enums.hashes import hash_to_string, string_to_hash | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class RaceRequired: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         races = stream.read_dword() | ||||||
|  |         self.race_restrict = stream.read_bool() | ||||||
|  |         self.race_values = [ | ||||||
|  |             stream.read_dword() for _ in range(races) | ||||||
|  |         ] | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_dword(len(self.race_values)) | ||||||
|  |         stream.write_bool(self.race_restrict) | ||||||
|  |         for race in self.race_values: | ||||||
|  |             stream.write_dword(race) | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.race_restrict = data['restrict'] | ||||||
|  |         self.race_values = [] | ||||||
|  |         for race in data['races']: | ||||||
|  |             self.race_values.append(string_to_hash(race)) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['restrict'] = self.race_restrict | ||||||
|  |         data['races'] = [] | ||||||
|  |         for race in self.race_values: | ||||||
|  |             data['races'].append(hash_to_string(race)) | ||||||
|  |         return data | ||||||
| @ -0,0 +1,7 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
| @ -0,0 +1,10 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from .ArcFileCache import load_cache_file, save_cache_file | ||||||
|  | from .ResStream import ResStream | ||||||
| @ -0,0 +1,19 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄ | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪ | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | # | ||||||
|  | 
 | ||||||
|  | def int32(x): | ||||||
|  |     if x > 0xFFFFFFFF: | ||||||
|  |         raise OverflowError | ||||||
|  |     if x > 0x7FFFFFFF: | ||||||
|  |         x = int(0x100000000 - x) | ||||||
|  |         if x < 2147483648: | ||||||
|  |             return -x | ||||||
|  |         else: | ||||||
|  |             return -2147483648 | ||||||
|  |     return x | ||||||
| @ -0,0 +1,39 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from collections import OrderedDict | ||||||
|  | 
 | ||||||
|  | from arcane.util import ResStream | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class ArcDungeonInfo: | ||||||
|  |     def load_binary(self, stream: ResStream): | ||||||
|  |         self.dungeon_template_id = stream.read_qword() | ||||||
|  |         self.dungeon_unknown = stream.read_qword() | ||||||
|  |         self.dungeon_spawn_location = stream.read_tuple() | ||||||
|  |         self.dungeon_y_offset = stream.read_float() | ||||||
|  | 
 | ||||||
|  |     def save_binary(self, stream: ResStream): | ||||||
|  |         stream.write_qword(self.dungeon_template_id) | ||||||
|  |         stream.write_qword(self.dungeon_unknown) | ||||||
|  |         stream.write_tuple(self.dungeon_spawn_location) | ||||||
|  |         stream.write_float(self.dungeon_y_offset) | ||||||
|  | 
 | ||||||
|  |     def save_json(self): | ||||||
|  |         data = OrderedDict() | ||||||
|  |         data['dungeon_template_id'] = self.dungeon_template_id | ||||||
|  |         data['dungeon_unknown'] = self.dungeon_unknown | ||||||
|  |         data['dungeon_spawn_location'] = self.dungeon_spawn_location | ||||||
|  |         data['dungeon_y_offset'] = self.dungeon_y_offset | ||||||
|  |         return data | ||||||
|  | 
 | ||||||
|  |     def load_json(self, data): | ||||||
|  |         self.dungeon_template_id = data['dungeon_template_id'] | ||||||
|  |         self.dungeon_unknown = data['dungeon_unknown'] | ||||||
|  |         self.dungeon_spawn_location = data['dungeon_spawn_location'] | ||||||
|  |         self.dungeon_y_offset = data['dungeon_y_offset'] | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | #  • ▌ ▄ ·. ▄▄▄▄·     ▄▄▄ .·▄▄▄▄  ▪  ▄▄▄▄▄      ▄▄▄       ▄▄▄·▄▄▄        | ||||||
|  | #  ·██ ▐███▪▐█ ▀█▪    ▀▄.▀·██▪ ██ ██ •██  ▪     ▀▄ █·    ▐█ ▄█▀▄ █·▪     | ||||||
|  | #  ▐█ ▌▐▌▐█·▐█▀▀█▄    ▐▀▀▪▄▐█· ▐█▌▐█· ▐█.▪ ▄█▀▄ ▐▀▀▄      ██▀·▐▀▀▄  ▄█▀▄ | ||||||
|  | #  ██ ██▌▐█▌██▄▪▐█    ▐█▄▄▌██. ██ ▐█▌ ▐█▌·▐█▌.▐▌▐█•█▌    ▐█▪·•▐█•█▌▐█▌.▐▌ | ||||||
|  | #  ▀▀  █▪▀▀▀·▀▀▀▀      ▀▀▀ ▀▀▀▀▀• ▀▀▀ ▀▀▀  ▀█▄▀▪.▀  ▀    .▀   .▀  ▀ ▀█▄▀▪ | ||||||
|  | #                Magicbane Emulator Project © 2013 - 2022 | ||||||
|  | #                           www.magicbane.com | ||||||
|  | 
 | ||||||
|  | from .ArcZone import ArcZone | ||||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue