dna.py (36287B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\devtools\script\dna.py 2 import dogma.data as dogma_data 3 from carbon.common.script.sys.serviceConst import ROLE_GML, ROLE_SPAWN, ROLE_WORLDMOD 4 from carbonui.primitives.container import Container 5 from carbonui.button.group import ButtonGroup 6 from carbonui.control.buttonIcon import ButtonIcon 7 from carbonui.control.window import Window 8 from carbonui.primitives.frame import Frame 9 from eve.client.script.ui.control.entries.generic import Generic 10 from eve.client.script.ui.control.entries.util import GetFromClass 11 from eve.client.script.ui.control.infoIcon import InfoIcon 12 from player_messaging.client.ui_message import prompt_player 13 from eve.common.script.sys import eveCfg 14 from eveexceptions import UserError 15 from inventorycommon.const import subsystemSlotFlags 16 from inventorycommon.util import IsShipFittable 17 import evetypes 18 import blue 19 import triui 20 from eve.client.script.ui.util import uix 21 from eve.client.script.ui.control import eveIcon, eveLabel, eveScroll 22 import uthread 23 import carbonui.const as uiconst 24 from player_messaging import IGNORE_TO_TOP 25 USE_SPAWN = True 26 27 def GetTypeName(typeID): 28 return evetypes.GetName(typeID) 29 30 31 Message = lambda title, body, icon = triui.INFO: prompt_player(body, title, options=uiconst.OK, icon=icon) 32 Progress = lambda title, text, current, total: (title, 33 text, 34 current, 35 total) 36 Cycle = lambda title, text: sm.GetService('loading').Cycle(title, text) 37 StopCycle = lambda : sm.GetService('loading').StopCycle() 38 Slash = lambda command: sm.RemoteSvc('slash').SlashCmd(command) 39 COPYCAT = 'copycat' 40 groupByFlag = {} 41 SLOTGROUPS = (const.flagHiSlot0, 42 const.flagMedSlot0, 43 const.flagLoSlot0, 44 const.flagRigSlot0, 45 const.flagSubSystemSlot0) 46 47 def InitStuff(): 48 global SLOTGROUPS 49 if groupByFlag: 50 return 51 SLOTGROUPS = (const.flagHiSlot0, 52 const.flagMedSlot0, 53 const.flagLoSlot0, 54 const.flagRigSlot0, 55 const.flagSubSystemSlot0) 56 grp = 0 57 for slot in SLOTGROUPS: 58 for flag in xrange(slot, slot + 8): 59 groupByFlag[flag] = grp 60 61 grp += 1 62 63 64 def GetSlotGroup(typeID): 65 try: 66 for effect in dogma_data.get_type_effects(typeID): 67 if effect.effectID == const.effectHiPower: 68 return 0 69 if effect.effectID == const.effectMedPower: 70 return 1 71 if effect.effectID == const.effectLoPower: 72 return 2 73 if effect.effectID == const.effectRigSlot: 74 return 3 75 if effect.effectID == const.effectSubSystem: 76 return 4 77 78 except KeyError: 79 return None 80 81 82 def Load(typeID, qty = 1, where = 'me'): 83 try: 84 return sm.RemoteSvc('slash').SlashCmd('/load %s %s %s' % (where, typeID, qty))[0] 85 except: 86 return None 87 88 89 def WaitAndSetLabel(itemID, name, timeout = 10): 90 timeout *= 4 91 bp = sm.GetService('michelle').GetBallpark() 92 while timeout: 93 if bp.GetBall(itemID): 94 sm.GetService('invCache').GetInventoryMgr().SetLabel(itemID, name) 95 break 96 timeout -= 1 97 blue.pyos.synchro.SleepSim(250) 98 99 100 def CreateShip_WORLDMOD(typeID, name = None, subsystems = None): 101 if eveCfg.IsDocked(): 102 itemID = sm.RemoteSvc('slash').SlashCmd('/createitem %d 1' % (typeID,)) 103 subSystemIDs = [] 104 if subsystems is not None: 105 for subSystemTypeID in subsystems: 106 subSystemID = sm.RemoteSvc('slash').SlashCmd('/createitem %d' % subSystemTypeID) 107 subSystemIDs.append(subSystemID) 108 109 sm.GetService('gameui').GetShipAccess().AssembleShip(itemID, name, subSystems=subSystemIDs) 110 else: 111 if USE_SPAWN: 112 return CreateShip_SPAWN(typeID, name) 113 itemID = sm.RemoteSvc('slash').SlashCmd('/createitem %d' % typeID) 114 sm.GetService('gameui').GetShipAccess().Jettison([itemID]) 115 if name: 116 WaitAndSetLabel(itemID, name) 117 return itemID 118 119 120 def CreateShip_SPAWN(typeID, name = None): 121 if eveCfg.IsDocked(): 122 return CreateShip_GML(typeID, name) 123 itemID = sm.RemoteSvc('slash').SlashCmd('/spawn %d' % typeID) 124 if name: 125 WaitAndSetLabel(itemID, name) 126 return itemID 127 128 129 def CreateShip_GML(typeID, name = None): 130 shipID = None 131 itemID = Load(typeID) 132 invCache = sm.GetService('invCache') 133 if not itemID: 134 if eveCfg.IsDocked(): 135 hangar = invCache.GetInventory(const.containerHangar) 136 cargo = invCache.GetInventoryFromId(eveCfg.GetActiveShip()).GetCapacity(const.flagCargo) 137 if cargo.capacity - cargo.used >= 100: 138 shipID = Load(11019) 139 if shipID: 140 hangar.Add(shipID, eveCfg.GetActiveShip(), qty=1) 141 sm.GetService('gameui').GetShipAccess().AssembleShip(shipID, 'Copycat Temporary') 142 itemID = Load(typeID, where=shipID) 143 if not itemID: 144 Message('what the?!', 'Something is hosed alright.') 145 return 146 if itemID: 147 if eveCfg.IsDocked(): 148 invCache.GetInventory(const.containerHangar).Add(itemID, eveCfg.GetActiveShip(), qty=1) 149 sm.GetService('gameui').GetShipAccess().AssembleShip(itemID, name) 150 else: 151 sm.GetService('gameui').GetShipAccess().Jettison([itemID]) 152 if name: 153 WaitAndSetLabel(itemID, name) 154 if shipID and eveCfg.IsDocked(): 155 invCache.GetInventoryMgr().TrashItems([shipID], session.stationid or session.structureid) 156 if not itemID: 157 Message('Planck field overload', "Sorry, you can't squeeze a <color=0xffffff00>%s<color=0xffffffff> into your current ship's cargohold.<br><br>Please make sure you have enough free cargo capacity to load this ship before trying again.<br>" % evetypes.GetName(typeID)) 158 return itemID 159 160 161 def CreateShip(typeID, name = None, subsystems = None): 162 if session.role & ROLE_WORLDMOD: 163 return CreateShip_WORLDMOD(typeID, name, subsystems) 164 elif session.role & ROLE_SPAWN and USE_SPAWN: 165 return CreateShip_SPAWN(typeID, name) 166 elif session.role & ROLE_GML: 167 return CreateShip_GML(typeID, name) 168 else: 169 return None 170 171 172 class Ship(): 173 174 def __init__(self, shipID = None, dnaKey = None, name = None): 175 self.source = None 176 self.dna = [] 177 self.typeID = 0 178 self.moduleByFlag = {} 179 self.name = None 180 self.valid = False 181 self.errorMessage = 'Template not initialized' 182 self.errorTypeID = 0 183 InitStuff() 184 if shipID: 185 self.ImportFromShip(shipID=shipID) 186 return 187 if dnaKey: 188 self.ImportFromDNA(dnaKey, name) 189 return 190 191 def itermodules(self, smart = False, banks = None): 192 if banks is None: 193 banks = SLOTGROUPS 194 get = self.moduleByFlag.get 195 if smart: 196 for slot in banks: 197 fit = [] 198 go = False 199 for flag in xrange(slot + 7, slot - 1, -1): 200 typeID = get(flag, 0) 201 if typeID: 202 go = True 203 if go: 204 fit.insert(0, (flag, typeID)) 205 206 for flag, typeID in fit: 207 yield (flag, typeID) 208 209 else: 210 for slot in banks: 211 for flag in xrange(slot, slot + 8): 212 yield (flag, get(flag, 0)) 213 214 def ImportFromShip(self, shipID = None, ownerID = None, deferred = False): 215 if deferred: 216 self.deferred = (shipID, ownerID) 217 return self 218 self.__init__() 219 if shipID is None: 220 shipID = eveCfg.GetActiveShip() 221 if ownerID is None: 222 ownerID = session.charid 223 loc = cfg.evelocations.GetIfExists(shipID) 224 if loc: 225 self.name = loc.name 226 else: 227 self.name = None 228 if ownerID != session.charid: 229 dna = sm.RemoteSvc('slash').SlashCmd('/getshipsetup %d' % shipID) 230 return self.ImportFromDNA(dna) 231 if shipID == eveCfg.GetActiveShip(): 232 ship = sm.GetService('clientDogmaIM').GetDogmaLocation().GetDogmaItem(shipID) 233 if ship is None: 234 self.errorMessage = 'Could not get shipID: %s' % shipID 235 return self 236 self.typeID = ship.typeID 237 for module in ship.GetFittedItems().itervalues(): 238 if IsShipFittable(module.categoryID): 239 self.moduleByFlag[module.flagID] = module.typeID 240 241 else: 242 try: 243 shipinv = sm.GetService('invCache').GetInventoryFromId(shipID) 244 self.typeID = shipinv.typeID 245 if not self.name: 246 self.name = evetypes.GetName(self.typeID) 247 mods = shipinv.ListHardwareModules() 248 except: 249 self.errorMessage = 'Could not get inv of shipID: %s' % shipID 250 return self 251 252 for rec in mods: 253 if IsShipFittable(evetypes.GetCategoryID(rec.typeID)): 254 self.moduleByFlag[rec.flagID] = rec.typeID 255 256 self.valid = True 257 self.Update() 258 return self 259 260 def ImportFromDNA(self, dna, name = None): 261 self.__init__() 262 if dna[:4] != 'DNA:': 263 self.errorMessage = 'Not a DNA key: %s' % (dna,) 264 return self 265 if name: 266 self.name = name 267 self.source = dna 268 self.dna = dna.split(':')[1:] 269 expectedCategoryIDs = [const.categoryShip] 270 flags = list(SLOTGROUPS) 271 try: 272 for frag in self.dna: 273 try: 274 parts = map(int, frag.split('*', 1)) 275 except: 276 raise RuntimeError(-1, "Invalid DNA fragment: '%s'" % frag) 277 278 if len(parts) == 2: 279 typeID, multi = parts 280 else: 281 typeID, = parts 282 multi = 1 283 self.errorTypeID = typeID 284 if multi < 1 or multi > 8: 285 raise RuntimeError(typeID, 'Invalid module count: %s' % multi) 286 if typeID > 3: 287 if not evetypes.Exists(typeID): 288 raise RuntimeError(typeID, 'Type not found') 289 categoryID = evetypes.GetCategoryID(typeID) 290 while multi > 0: 291 if typeID <= 3: 292 grp = typeID 293 self.moduleByFlag[flags[grp]] = 0 294 flags[grp] += 1 295 multi -= 1 296 continue 297 if categoryID not in expectedCategoryIDs: 298 raise RuntimeError(typeID, 'Expected %s type, got %s type' % (evetypes.GetCategoryNameByCategory(expectedCategoryIDs[0]), evetypes.GetCategoryNameByCategory(categoryID))) 299 if categoryID == const.categoryShip: 300 self.typeID = typeID 301 expectedCategoryIDs = [const.categoryModule, const.categorySubSystem] 302 elif IsShipFittable(categoryID): 303 grp = GetSlotGroup(typeID) 304 if grp is not None: 305 self.moduleByFlag[flags[grp]] = typeID 306 flags[grp] += 1 307 else: 308 raise RuntimeError(typeID, 'Type is not a module or rig') 309 else: 310 raise RuntimeError(typeID, 'Type is not a ship or module') 311 multi -= 1 312 313 for startFlag, endFlag in zip(SLOTGROUPS, flags): 314 if endFlag > startFlag + 8: 315 raise RuntimeError(-1, 'Malformed template') 316 317 self.valid = True 318 self.Update() 319 except RuntimeError as e: 320 self.errorTypeID, self.errorMessage = e 321 322 return self 323 324 def Update(self): 325 real = 0 326 fake = 0 327 for flag, typeID in self.itermodules(smart=True): 328 if typeID: 329 real += 1 330 else: 331 fake += 1 332 333 self.realmodulecount = real 334 self.fakemodulecount = fake 335 336 def AssembleMany(self, amount = None): 337 self.NoPodCheck(wantNewShip=True) 338 if amount is None: 339 result = uix.QtyPopup(maxvalue=50, minvalue=1, caption='Mass Manufacture', label='', hint='Specify amount of ships to assemble (Max. 50).<br>Note: this function cannot be aborted once running.') 340 if result: 341 amount = result['qty'] 342 else: 343 amount = 0 344 for x in xrange(amount): 345 self.Assemble() 346 347 def Assemble(self, clone = 0): 348 if not self.Valid(report=True): 349 return None 350 self.NoPodCheck(wantNewShip=True) 351 tname = GetTypeName(self.typeID) 352 title = ['Create %s', 'Clone %s'][clone] % tname 353 sm.GetService('loading').ProgressWnd(title, 'Spawning: %s ...' % tname, 0, 1) 354 subsystems = [] 355 for flagID in subsystemSlotFlags: 356 if flagID in self.moduleByFlag: 357 subsystems.append(self.moduleByFlag[flagID]) 358 359 if session.solarsystemid and len(subsystems) > 0: 360 raise UserError('CustomInfo', {'info': "You can't assemble ship with subsystems in space. Please try again in station"}) 361 shipID = CreateShip(self.typeID, self.name, subsystems) 362 if shipID: 363 self.Fit(shipID, fromAssemble=True) 364 sm.GetService('loading').ProgressWnd(title, 'Success', 1, 1) 365 return shipID 366 sm.GetService('loading').ProgressWnd(title, 'Failed', 1, 1) 367 368 def Fit(self, shipID = None, fromAssemble = False): 369 if not self.Valid(report=True): 370 return None 371 self.NoPodCheck(wantNewShip=fromAssemble) 372 if not shipID: 373 shipID = eveCfg.GetActiveShip() 374 if not fromAssemble: 375 try: 376 shipTypeID = sm.GetService('clientDogmaIM').GetDogmaLocation().GetDogmaItem(shipID).GetTypeID() 377 except Exception: 378 shipTypeID = sm.GetService('invCache').GetInventoryFromId(shipID).typeID 379 380 if shipTypeID != self.typeID: 381 Message('Unable to refit', 'Sorry, this setup is designed for a %s. You are currently piloting a %s.' % (GetTypeName(self.typeID), GetTypeName(shipTypeID))) 382 raise UserError(IGNORE_TO_TOP) 383 if session.role & ROLE_GML: 384 return self.FitUsingSlash(shipID) 385 if eveCfg.IsDocked(): 386 self.SkillCheck() 387 return self.FitUsingHangar(shipID) 388 self.Oops('Oops', 'Fitting operations cannot be done in space. Please dock at a station and try again.') 389 390 def FitUsingHangar(self, shipID): 391 if shipID != eveCfg.GetActiveShip(): 392 self.Oops('Oops', 'You cannot fit ships other than your active one.') 393 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 394 ship = dogmaLocation.GetDogmaItem(shipID) 395 if ship is None: 396 self.Oops('Huh?', 'Where is your ship?') 397 title = 'Fitting %s' % GetTypeName(self.typeID) 398 setup = self.GetModuleInfo() 399 Cycle(title, 'Locating required modules...') 400 source = {} 401 for typeID in setup['modulelist']: 402 source[typeID] = [] 403 404 fit = {} 405 for flag, typeID in self.moduleByFlag.iteritems(): 406 if typeID: 407 fit[flag] = typeID 408 409 for item in ship.GetFittedItems().itervalues(): 410 if item.typeID == self.moduleByFlag[item.flagID]: 411 if item.flagID in fit: 412 del fit[item.flagID] 413 else: 414 source[typeID].append(item) 415 if item.typeID in setup: 416 setup[item.typeID] -= 1 417 418 invCache = sm.GetService('invCache') 419 for item in invCache.GetInventory(const.containerHangar).List(const.flagHangar): 420 if IsShipFittable(item.categoryID): 421 if item.typeID in setup: 422 if item.singleton: 423 source[item.typeID].insert(0, item) 424 else: 425 source[item.typeID].append(item) 426 setup[item.typeID] -= item.stacksize 427 428 missing = [] 429 for typeID in setup['modulelist']: 430 if setup[typeID] > 0: 431 missing.append(' %s %s' % (setup[typeID], GetTypeName(typeID))) 432 433 if missing: 434 StopCycle() 435 msg = 'Sorry, you cannot fit your ship with this setup; The following modules are not in your possession:<br><br>' 436 msg += '<br>'.join(missing) 437 self.Oops('Unable to fit ship', msg) 438 Cycle(title, 'Unfitting modules...') 439 charges = {} 440 for item in ship.subLocations.itervalues(): 441 charges[item.flagID] = item 442 443 unfit = [] 444 for item in ship.GetFittedItems().itervalues(): 445 if item.categoryID == const.categoryCharge: 446 charges[item.flagID] = item 447 elif self.moduleByFlag[item.flagID] != item.typeID: 448 unfit.append(item) 449 450 ids = [] 451 for item in unfit: 452 charge = charges.get(item.flagID, None) 453 if charge: 454 ids.append(charge.itemID) 455 ids.append(item.itemID) 456 457 invCache.GetInventory(const.containerHangar).MultiAdd(ids, shipID, flag=const.flagHangar) 458 del charges 459 del unfit 460 del ids 461 del item 462 fitByTypeID = {} 463 for slot in (const.flagLoSlot0, const.flagMedSlot0, const.flagHiSlot0): 464 for flag in xrange(slot, slot + 8): 465 if flag in fit: 466 typeID = fit[flag] 467 if typeID in fitByTypeID: 468 fitByTypeID[typeID].append(flag) 469 else: 470 fitByTypeID[typeID] = [flag] 471 472 total = len(fit) 473 state = [1, False] 474 475 def _fit(typeID, flags): 476 for flag in flags: 477 sm.GetService('loading').ProgressWnd(title, 'Fitting modules...', state[0], total) 478 try: 479 while source[typeID]: 480 item = source[typeID][0] 481 try: 482 if item.stacksize == 1: 483 source[typeID].pop(0) 484 invCache.GetInventoryFromId(shipID).Add(item.itemID, item.locationID, qty=1, flag=flag) 485 state[0] += 1 486 break 487 except: 488 pass 489 490 except: 491 state[1] = True 492 break 493 494 parallelCalls = [] 495 for typeID, flags in fitByTypeID.iteritems(): 496 parallelCalls.append((_fit, (typeID, flags))) 497 498 StopCycle() 499 try: 500 uthread.parallel(parallelCalls) 501 except: 502 pass 503 504 if state[1]: 505 StopCycle() 506 self.Oops('There was a problem...', 'Your hangar inventory changed while fitting the ship, one or more required modules are locked or no longer within your reach.<br>The fitting operation has been aborted.') 507 sm.GetService('loading').ProgressWnd(title, 'Done', 1, 1) 508 509 def FitUsingSlash(self, shipID): 510 title = 'Fitting %s' % GetTypeName(self.typeID) 511 state = [0, 0] 512 513 def _fitgroup(slot): 514 w = sm.RemoteSvc('slash') 515 for flag, typeID in self.itermodules(banks=(slot,), smart=True): 516 if typeID: 517 state[0] += 1 518 modname = GetTypeName(typeID) 519 else: 520 continue 521 state[1] += 1 522 sm.GetService('loading').ProgressWnd(title, 'Fitting module %s/%s: %s' % (state[0], self.realmodulecount, modname), state[1] * 1000, (self.realmodulecount + self.fakemodulecount) * 1000 + 1) 523 w.SlashCmd('/fit %s %s flag=%d' % (shipID, typeID, flag)) 524 525 if shipID == eveCfg.GetActiveShip(): 526 sm.RemoteSvc('slash').SlashCmd('/unload me all') 527 parallelCalls = [] 528 for slot in SLOTGROUPS: 529 parallelCalls.append((_fitgroup, (slot,))) 530 531 uthread.parallel(parallelCalls) 532 sm.GetService('loading').ProgressWnd(title, 'Done', 1, 1) 533 534 def NoPodCheck(self, wantNewShip = False): 535 dogmaLocation = sm.GetService('clientDogmaIM').GetDogmaLocation() 536 inPod = not (eveCfg.GetActiveShip() is not None and dogmaLocation.GetDogmaItem(eveCfg.GetActiveShip()).groupID != 29) 537 if not inPod: 538 return 539 if inPod and wantNewShip and session.role & (ROLE_WORLDMOD | ROLE_SPAWN): 540 return 541 self.Oops('Allergic to eggs', 'This feature cannot be used when you are in a pod.<br>Get yourself in a ship and try again!') 542 543 def SkillCheck(self, setup = None): 544 if not setup: 545 setup = self.GetModuleInfo() 546 Cycle(' Preparing...', 'Checking skill requirements') 547 skills = {} 548 groups = sm.GetService('skills').GetSkillGroups() 549 for _each in groups: 550 for skill in _each[1]: 551 skills[skill.typeID] = skill.skillLevel 552 553 required = {} 554 for eachTypeID in setup['modulelist']: 555 c = 0 556 for typeID, requiredLevel in sm.RemoteSvc('dogmaIM').GetRequiredSkillLevels(eachTypeID).iteritems(): 557 c += 1 558 if not (typeID in skills and skills[typeID] >= requiredLevel): 559 required[typeID] = max(required.get(typeID, 0), requiredLevel) 560 561 StopCycle() 562 if required: 563 rs = [] 564 for typeID, level in required.iteritems(): 565 rs.append(' %s level %s' % (GetTypeName(typeID), level)) 566 567 msg = 'To fit this setup to your ship you require the following skills:<br><br>' 568 msg += '<br>'.join(rs) 569 self.Oops('Insufficient Skill', msg) 570 571 def GetModuleInfo(self): 572 setup = {} 573 setup['modulelist'] = [] 574 for flag, typeID in self.itermodules(): 575 if typeID: 576 if typeID in setup: 577 setup[typeID] += 1 578 else: 579 setup['modulelist'].append(typeID) 580 setup[typeID] = 1 581 582 return setup 583 584 def ExportAsDNA(self): 585 if not self.Valid(): 586 return self.ErrorReport() 587 dna = 'DNA:%s' % self.typeID 588 multi = 1 589 lastID = None 590 grp = 0 591 get = self.moduleByFlag.get 592 for flag, typeID in self.itermodules(smart=True): 593 if not typeID: 594 typeID = groupByFlag[flag] 595 if typeID == lastID: 596 multi += 1 597 else: 598 if multi > 1: 599 dna += '*%s' % multi 600 multi = 1 601 dna += ':%s' % typeID 602 lastID = typeID 603 604 if multi > 1: 605 dna += '*%s' % multi 606 return dna 607 608 def ExportAsTriText(self, modulesOnly = False, maxLen = 0): 609 if not self.Valid(): 610 return self.ErrorReport() 611 text = '' 612 if modulesOnly: 613 indent = '' 614 else: 615 indent = ' ' 616 if self.name: 617 text += '<color=0xffffff00>Name:<color=0xffffffff> %s<br>' % self.name 618 text += '<color=0xffffff00>Ship:<color=0xffffffff> %s<br>' % GetTypeName(self.typeID) 619 text += '<color=0xffffff00>Modules: <color=0xffc0c0c0><br>' 620 info = self.GetModuleInfo() 621 if not info['modulelist']: 622 text += indent + 'None<br>' 623 if maxLen: 624 for typeID in info['modulelist']: 625 name = GetTypeName(typeID) 626 if len(name) > maxLen: 627 name = name[:maxLen] + '...' 628 text += indent + '%s %s<br>' % (info[typeID], name) 629 630 else: 631 for typeID in info['modulelist']: 632 text += indent + '%s %s<br>' % (info[typeID], GetTypeName(typeID)) 633 634 if not modulesOnly: 635 text += '<br><color=0xffffff00>Key:<color=0xffc0c0c0><br>%s' % self.ExportAsDNA() 636 return text 637 638 def ExportAsText(self, modulesOnly = False, maxLen = 0): 639 r = self.ExportAsTriText(modulesOnly, maxLen).replace('<br>', '\r\n') + '\r\n' 640 while 1: 641 i = r.find('<color=0x') 642 if i == -1: 643 break 644 r = r[:i] + r[i + 18:] 645 646 return r 647 648 def ExportAsHTML(self): 649 if not self.Valid(): 650 msg = '<html><body><h1><font color="red">?TEMPLATE ERROR</font></h1><br>' 651 msg += self.ErrorReport() 652 msg += '</body></html>' 653 return msg 654 html = '<html><title>Copycat</title><body link="#4080FF">' 655 html += '<a href="showinfo:%s"><img style="margin: 0px 0px 4px 0px;" src="typeicon:%s" align="right" width="64" height="64"></a>' % (self.typeID, self.typeID) 656 html += '<font color="yellow">Ship:</font>' 657 html += '<font size="+2"> %s</font>' % GetTypeName(self.typeID) 658 if self.name: 659 html += '<br><font color="yellow">Name:</font> %s' % self.name 660 else: 661 html += '' 662 info = self.GetModuleInfo() 663 html += '<br><br><font color="yellow">Modules:</font><br>' 664 if info['modulelist']: 665 for typeID in info['modulelist']: 666 html += ' %s <a href="showinfo:%s">%s</a><br>' % (info[typeID], typeID, GetTypeName(typeID)) 667 668 html += '<br><font color="yellow">Fitting overview:</font><br>' 669 for grp in xrange(3): 670 if not self.modules[grp]: 671 continue 672 if grp == 1: 673 html += '<img src="icon:09_14" width="16" height="32">' 674 for typeID in self.modules[grp]: 675 if typeID != 0: 676 html += '<a href="showinfo:%s"><img alt="%s" src="typeicon:%s" width="32" height="32"></a>' % (typeID, GetTypeName(typeID), typeID) 677 else: 678 html += '<img src="icon:09_14" width="32" height="32">' 679 680 html += '<br>' 681 682 else: 683 html += 'No modules fitted<br><br>' 684 html += '</body></html>' 685 return html 686 687 def ExportAsIGBLink(self, label = None): 688 if label is None: 689 label = self.name 690 return '<a href="localsvc:method=ShowCopyCatIGB&action=open&key=%s&name=%s">%s</a>' % (self.ExportAsDNA(), self.name, label) 691 692 def ExportToClipboard(self, mode = 1): 693 if mode == 0: 694 blue.pyos.SetClipboardData(self.ExportAsText()) 695 elif mode == 1: 696 blue.pyos.SetClipboardData(self.ExportAsDNA()) 697 elif mode == 2: 698 blue.pyos.SetClipboardData(self.ExportAsIGBLink()) 699 700 def Valid(self, report = False): 701 if self.valid: 702 return True 703 if hasattr(self, 'deferred'): 704 self.ImportFromShip(shipID=self.deferred[0], ownerID=self.deferred[1]) 705 if self.valid: 706 return True 707 if report: 708 Message('Invalid Template', self.ErrorReport(), icon=triui.ERROR) 709 return False 710 711 def Oops(self, title, message): 712 Message(title, message) 713 raise UserError(IGNORE_TO_TOP) 714 715 def ErrorReport(self): 716 msg = 'The template does not contain valid data.<br><br>Maybe it is because of this error:<br> %s: %s' % (self.errorTypeID, self.errorMessage) 717 if self.source: 718 msg += '<br><br>Key: %s' % self.source 719 return msg 720 721 def ShowInfo(self, buttons = False): 722 Popup(self, buttons=buttons) 723 724 def ShowCargo(self, id = None, *args): 725 cargoContents = [] 726 try: 727 if id: 728 itemID = id 729 else: 730 itemID = self.deferred[0] 731 except: 732 return 733 734 for cargo in sm.GetService('invCache').GetInventoryFromId(itemID).ListCargo(): 735 if cargo.categoryID != const.categoryOwner: 736 cargoContents.append(('%s<t>%s' % (evetypes.GetName(cargo.typeID), cargo.stacksize), cargo)) 737 738 uix.ListWnd(cargo, listtype='generic', caption='Cargo loaded with %d items' % len(cargoContents), isModal=0, minChoices=0, scrollHeaders=[u'Type', u'Quantity']) 739 740 def Store(self): 741 sm.GetService(COPYCAT).DoStore(self.ExportAsDNA(), self.name) 742 743 def RefitMenu(self): 744 m = [] 745 746 def _Refit(dnaKey): 747 self.__class__().ImportFromDNA(dnaKey).Fit(eveCfg.GetActiveShip()) 748 749 for name, dnaKey in sm.GetService('copycat').GetSetupsByTypeID(self.typeID): 750 m.append((name, _Refit, (dnaKey,))) 751 752 if m: 753 m.insert(0, ('My %s setups' % evetypes.GetName(self.typeID), None)) 754 return m 755 756 def GetMenuInline(self, disabled = False, store = True, assemble = True, info = True, fit = True, refit = False, spiffy = False): 757 758 def d(func, condition = None): 759 if disabled: 760 return 761 if condition is None or condition: 762 return func 763 764 m = [] 765 if info: 766 m += [(('Show Info', 'res:/UI/Texture/Icons/38_16_208.png'), self.ShowInfo), None] 767 if store: 768 m += [(('Store Setup', 'res:/UI/Texture/Icons/41_64_1.png'), self.Store)] 769 if refit: 770 m += [(('Switch Setup', 'res:/ui/Texture/WindowIcons/fitting.png'), self.RefitMenu())] 771 if assemble: 772 if session.role & (ROLE_GML | ROLE_WORLDMOD): 773 m += [(('Assemble Ship', 'res:/UI/Texture/Icons/41_64_2.png'), d(self.Assemble, session.role & (ROLE_GML | ROLE_WORLDMOD))), (('Mass Manufacture', 'res:/UI/Texture/Icons/27_64_1.png'), d(self.AssembleMany, session.role & (ROLE_GML | ROLE_WORLDMOD)))] 774 if fit: 775 m += [(('Fit to active Ship', 'res:/UI/Texture/Icons/8_64_11.png'), d(self.Fit))] 776 if store or assemble or fit: 777 m.append(None) 778 if not disabled: 779 m += [(('Copy Manifest', 'res:/UI/Texture/Icons/10_64_16.png'), d(self.ExportToClipboard), (0,)), (('Copy DNA Key', 'res:/ui/Texture/WindowIcons/attributes.png'), d(self.ExportToClipboard), (1,)), (('Copy IGB Link', 'res:/ui/Texture/WindowIcons/browser.png'), d(self.ExportToClipboard), (2,))] 780 if spiffy is True: 781 return m 782 else: 783 m2 = [] 784 for x in m: 785 if x: 786 m2.append((x[0][0],) + x[1:]) 787 else: 788 m2.append(None) 789 790 return m2 791 792 793 TOOLSIZE = 34 794 ICONSIZE = 72 795 796 class InfoPanel(Container): 797 __guid__ = 'xtriui.DNAInfoPanel' 798 799 def Setup(self, template = None, readonly = False): 800 self.typeID = None 801 self.readonly = readonly 802 self.boven = Container(name='ccip_top', parent=self, height=ICONSIZE, align=uiconst.TOTOP) 803 self.toolbar = Container(name='ccip_bar', parent=self.boven, left=ICONSIZE + 2, top=ICONSIZE - TOOLSIZE, height=TOOLSIZE, align=uiconst.TOPLEFT, width=240) 804 Container(name='ccip_push', parent=self, height=3, align=uiconst.TOTOP) 805 cont = Container(name='mainicon', parent=self.boven, width=ICONSIZE, align=uiconst.TOLEFT) 806 self.infoframe = Frame(parent=cont, color=(1.0, 1.0, 1.0, 0.5)) 807 self.infoicon = infoicon = InfoIcon(left=1, top=1, parent=cont, idx=0, align=uiconst.TOPRIGHT, state=uiconst.UI_DISABLED) 808 infoicon.OnClick = lambda *x: self.ShowInfo() 809 self.icon = eveIcon.Icon(parent=cont) 810 self.icon.OnClick = self.ShowInfo 811 self.icon.LoadTexture('res:/UI/Texture/notavailable.dds') 812 self.icon.width = self.icon.height = ICONSIZE 813 Container(name='ccip_push', parent=self.boven, width=6, align=uiconst.TOLEFT) 814 cont = Container(name='ccip_icon', parent=self.boven, align=uiconst.TOTOP, height=24) 815 self.capt = eveLabel.CaptionLabel(text='', parent=cont) 816 cont = Container(name='ccip_textcont', parent=self.boven, align=uiconst.TOALL, pos=(0, 0, 0, 0)) 817 self.text = eveLabel.Label(text='', parent=cont, align=uiconst.TOTOP, height=self.boven.height, state=uiconst.UI_NORMAL) 818 self.scroll = eveScroll.Scroll(name='ccip_top', parent=self) 819 if template: 820 self.Load(template) 821 else: 822 self.typeID = None 823 self.capt.text = 'Copycat' 824 self.text.text = 'Select a ship setup from the list to display details in this panel' 825 self.scroll.Load(contentList=[], headers=['qty', 'name']) 826 827 def ShowInfo(self, *args): 828 if self.typeID: 829 sm.GetService('info').ShowInfo(typeID=self.typeID) 830 831 def Clicketyclick(self, item): 832 f = item[1] 833 if f: 834 if len(item) >= 3: 835 f(*item[2]) 836 else: 837 f() 838 839 def Load(self, template): 840 entries = [] 841 headers = ['qty', 'name'] 842 state = uiconst.UI_NORMAL 843 if template: 844 if template.Valid(): 845 self.typeID = template.typeID 846 info = template.GetModuleInfo() 847 capt = GetTypeName(template.typeID) 848 name = template.name 849 for typeID in info['modulelist']: 850 entries.append(GetFromClass(Generic, {'label': '%s<t>%s' % (info[typeID], GetTypeName(typeID)), 851 'typeID': typeID, 852 'showinfo': True})) 853 854 else: 855 state = uiconst.UI_HIDDEN 856 capt = 'TEMPLATE ERROR' 857 name = 'This template does not contain a valid ship setup' 858 entries.append(GetFromClass(Generic, {'label': '%s<t>%s' % (template.errorTypeID, template.errorMessage)})) 859 headers = ['typeID', 'error'] 860 else: 861 state = uiconst.UI_HIDDEN 862 capt = name = '' 863 self.toolbar.Flush() 864 if state == uiconst.UI_NORMAL: 865 self.icon.LoadIconByTypeID(template.typeID) 866 m = template.GetMenuInline(info=False, store=not self.readonly, spiffy=True) 867 x = 0 868 for item in m: 869 if item: 870 hint, icon = item[0] 871 ButtonIcon(parent=self.toolbar, align=uiconst.TOPLEFT, pos=(x, 872 0, 873 TOOLSIZE, 874 TOOLSIZE), texturePath=icon, iconSize=TOOLSIZE, hint=hint, func=lambda _item = item: self.Clicketyclick(_item)) 875 x += TOOLSIZE 876 877 else: 878 self.typeID = None 879 self.icon.LoadTexture('res:/UI/Texture/notavailable.dds') 880 self.capt.text = capt 881 self.text.text = name 882 self.infoframe.state = self.infoicon.state = self.toolbar.state = state 883 self.scroll.Load(contentList=entries, headers=headers) 884 885 886 class Popup(): 887 __wndname__ = 'DNA Popup' 888 889 def __init__(self, key_or_template, name = None, buttons = True, store = True): 890 if isinstance(key_or_template, Ship): 891 self.t = key_or_template 892 else: 893 self.t = Ship().ImportFromDNA(key_or_template, name) 894 _wndname = '%s_%s' % (Popup.__wndname__, blue.os.GetWallclockTime()) 895 wnd = Window.Open(windowID=_wndname) 896 wnd.SetCaption('Ship Setup Information') 897 wnd.sr.main = wnd.GetChild('main') 898 wnd.SetMinSize((340, 256)) 899 main = Container(name='main', parent=wnd.sr.main, pos=(const.defaultPadding, 900 const.defaultPadding, 901 const.defaultPadding, 902 const.defaultPadding), align=uiconst.TOALL) 903 info = InfoPanel(name='info', parent=main, pos=(0, 0, 0, 0)) 904 info.Setup(self.t) 905 if buttons: 906 buttons = [['Close', 907 self.Close, 908 None, 909 81]] 910 wnd.sr.main.children.insert(0, ButtonGroup(btns=buttons)) 911 912 def Spawn(self, *args): 913 self.wnd.Close() 914 self.t.Assemble() 915 916 def Store(self, *args): 917 if sm.GetService(COPYCAT).DoStore(self.t.ExportAsDNA(), self.t.name): 918 self.wnd.Close() 919 920 def Close(self, *args): 921 self.wnd.Close() 922 del self.wnd