def minVersion(self, options, rootLayer): """ http://wiki.xmoto.tuxfamily.org/index.php?title=Others_tips_to_make_levels """ self.options = options if 'sky' in self.options: self.addVersion((0, 2, 5)) if getValue(self.options, 'level', 'tex', default='') != '': self.addVersion((0, 2, 5)) if getValue(self.options, 'level', 'music') not in NOTSET: self.addVersion((0, 2, 5)) if 'remplacement' in self.options: for value in self.options['remplacement'].values(): if value not in NOTSET: self.addVersion((0, 2, 5)) break if 'layer' in self.options: self.addVersion((0, 2, 7)) if getValue(self.options, 'level', 'lua') not in NOTSET: self.addVersion((0, 1, 10)) self.analyseScript(self.options['level']['lua']) self.analyseLevelElements(rootLayer) return (self.x, self.y, self.z)
def addGradient(self, label): self.getGradients() edge = getValue(label, 'edge') edges = getValue(label, 'edges') up = getValue(edge, 'texture') down = getValue(edge, 'downtexture') if up is None: # (color, offset, opacity) stop1 = ('00ff00', 0, 0) stop2 = ('00ff00', 1, 1) elif down is None: stop1 = ('00ff00', 0, 1) stop2 = ('00ff00', 1, 0) else: stop1 = ('00ff00', 0, 1) stop2 = ('ff0000', 1, 1) gradientId = 'linearGradient_%s_%d_%d' % stop2 if gradientId not in self.gradients.keys(): gradient = newGradientNode(gradientId, stop1, stop2) self.gradients[gradientId] = gradient self.defs.append(gradient) angle = float(getValue(edges, 'angle', default=270.0)) rotGradId = '%s_%.2f' % (gradientId, angle) if rotGradId not in self.gradients.keys(): rotGrad = newRotatedGradientNode(rotGradId, gradientId, angle) self.gradients[rotGradId] = rotGrad self.defs.append(rotGrad) return (True, rotGradId)
def get(self, dictValues, namespace, name=None, default=None): value = getValue(dictValues, namespace, name, None) if value is None: if self.useDefault == True: value = getValue(self.defaultValues, namespace, name, None) if value is None: return default else: return value else: return default else: return value
def calculateNewDimensionsForRemplacement(self, name): if name + 'Scale' not in self.level.options['remplacement']: return scale = float(self.level.options['remplacement'][name + 'Scale']) if scale == 1.0: return sprite = getValue(self.level.options, 'remplacement', name, default=name) self.setSize(sprite, scale)
def generateLvlContent(self, lvlfile): self.writeLevelHead() if getValue(self.options, 'level', 'lua') not in NOTSET: self.writeLevelScript(self.options['level']['lua']) self.writeLevelContent(self.rootLayer) self.content.append("</level>") if lvlfile == None: self.printContentToStdout() else: lvlfile.writelines([(line + '\n').encode("utf-8") for line in self.content]) lvlfile.close()
def __init__(self, **kwargs): self._id = kwargs['_id'] self.infos = kwargs['infos'] self.pathsInfos = kwargs['vertex'] self.matrix = getValue(kwargs, 'matrix') if isIdentity(self.matrix): self.matrix = None self.content = [] self.aabb = AABB() # added by writeContent self.ratio = 1.0 self.newWidth = 1.0 self.newHeight = 1.0
def getImageDimensions(texName, scale, aabb): infos = getValue(AvailableElements()['SPRITES'], texName) cx = float(getValue(infos, 'centerX', default='0.5')) / SVG2LVL_RATIO cy = float(getValue(infos, 'centerY', default='0.5')) / SVG2LVL_RATIO width = float(getValue(infos, 'width', default='1.0')) / SVG2LVL_RATIO height = float(getValue(infos, 'height', default='1.0')) / SVG2LVL_RATIO scaledWidth = width scaledHeight = height if scale != 1.0: scaledWidth = scale * width scaledHeight = scale * height cx += (scaledWidth - width) / 2.0 cy += (scaledHeight - height) / 2.0 x = aabb.cx() - cx # with the same coordinates, inkscape doesn't display # images at the same place as xmoto ... y = aabb.cy() - scaledHeight + cy return (x, y, aabb.cx(), aabb.cy(), scaledWidth, scaledHeight)
def __init__(self, options, rootLayer, document): self.options = options self.rootLayer = rootLayer self.elements = [] self.layersType = [] self.layerBlock2Level = [] self.content = [] self.svg = SvgDoc(document) # the xmoto width of the level is the width of svg in pixel # divided by 20.0 ratio = Conf()['SVG2LVL_RATIO'] lvlWidth = self.options['svg']['width'] * ratio lvlHeight = self.options['svg']['height'] * ratio createIfAbsent(self.options, 'lvl') self.options['lvl']['width'] = lvlWidth self.options['lvl']['height'] = lvlHeight self.options['lvl']['ratio'] = ratio self.limits = {} self.limits['left'] = -lvlWidth / 2.0 self.limits['right'] = lvlWidth / 2.0 self.limits['top'] = lvlHeight / 2.0 self.limits['bottom'] = -lvlHeight / 2.0 smooth = float(getValue(self.options, 'level', 'smooth', default='9')) # now, smooth is from 1 to 10 in the tkinter window, # but it is still from 1 to 100 in svg2lvl smooth += 90 self.options['lvl']['smooth'] = smooth numLayers = len(self.rootLayer.children) self.createLayerInfos(numLayers) numLayers = 0 self.rootLayer.elements = [] for child in self.rootLayer.children: if (len(self.layersType) > 0 and self.layersType[numLayers] != 'unused'): self.createEntitiesAndBlocksFromSvg(child, numLayers) else: child.unused = True numLayers += 1 self.options['lvl']['rversion'] = self.getRequiredXmotoVersion()
def getEdgeColorAndScale(prefix): r = int(getValue(self.infos, 'edge', '%s_r' % prefix, default=255)) g = int(getValue(self.infos, 'edge', '%s_g' % prefix, default=255)) b = int(getValue(self.infos, 'edge', '%s_b' % prefix, default=255)) a = int(getValue(self.infos, 'edge', '%s_a' % prefix, default=255)) scale = getValue(self.infos, 'edge', '%s_scale' % prefix) if scale is not None: scale = float(scale) depth = getValue(self.infos, 'edge', '%s_depth' % prefix) if depth is not None: depth = float(depth) # ugly hack because xmoto 0.5.2 non set value for depth is -1.0f if depth == -1.0: depth += 0.0001 return ((r, g, b, a), scale, depth)
def updateLayerInfos(self, layersInfos): """ when an user updates the svg ordering in inkscape, he has to open the layers properties window in order to the layers infos in the metadatas to be updated. put this updates into this function so that it can be called from the .lvl creation code and from the layerinfos window """ def extractIndexFromKey(key): return int(key[len('layer_'):-len('_id')]) # metadata layerId -> layerIndex oldLayersIdToIndex = {} maxLayerIndex = -1 for (key, layerId) in layersInfos.iteritems(): if key[-3:] != '_id': continue layerIndex = extractIndexFromKey(key) if layerIndex > maxLayerIndex: maxLayerIndex = layerIndex oldLayersIdToIndex[layerId] = layerIndex # svg layers layers = self.document.xpath('/svg:svg/svg:g', namespaces=NSS) nblayers = len(layers) # svg layerId -> layerLabel layersLabel = [] for layer in layers: layerId = layer.get('id') layerLabel = layer.get(addNS('label', 'inkscape'), '') layersLabel.append((layerId, layerLabel)) # existing layers in the right order layersIdToIndexToSave = [] for layerIndex in reversed(xrange(nblayers)): # get old layer index or create a new one if it's a new layer layerLabel = layersLabel[layerIndex][1] if layerLabel == "": layerLabel = '#' + layerId layerId = layersLabel[layerIndex][0] if layerId in oldLayersIdToIndex: oldLayerIndex = oldLayersIdToIndex[layerId] else: maxLayerIndex += 1 oldLayerIndex = maxLayerIndex oldLayersIdToIndex[layerId] = oldLayerIndex # keep only layers who are still there. reorder them in # the metadata in the same order as in the svg layersIdToIndexToSave.append( (layerId, layerLabel, layerIndex, oldLayerIndex)) # keep only the still existing layers layers = {} numberMainLayers = 0 for (layerId, layerLabel, layerIndex, oldLayerIndex) in layersIdToIndexToSave: prefix = 'layer_%d_' % layerIndex prefixOld = 'layer_%d_' % oldLayerIndex layers[prefix + 'id'] = layerId value = getValue(layersInfos, prefixOld + 'isused', default='true') layers[prefix + 'isused'] = value value = getValue(layersInfos, prefixOld + 'ismain', default='false') layers[prefix + 'ismain'] = value value = getValue(layersInfos, prefixOld + 'x', default=1.0) layers[prefix + 'x'] = value value = getValue(layersInfos, prefixOld + 'y', default=1.0) layers[prefix + 'y'] = value return (layers, layersIdToIndexToSave)
def writeContent(self, options, level): """ - block: * background * dynamic * usetexture=texture_name """ def removeNonNormal(posParams, keys): """ only static blocks in layers other than main """ for key in keys: if key in posParams: del posParams[key] def removeNonMain(posParams): removeNonNormal(posParams, ['background', 'physics']) def removeForLayer(posParams): removeNonNormal(posParams, ['background', 'dynamic', 'physics']) def getEdgeColorAndScale(prefix): r = int(getValue(self.infos, 'edge', '%s_r' % prefix, default=255)) g = int(getValue(self.infos, 'edge', '%s_g' % prefix, default=255)) b = int(getValue(self.infos, 'edge', '%s_b' % prefix, default=255)) a = int(getValue(self.infos, 'edge', '%s_a' % prefix, default=255)) scale = getValue(self.infos, 'edge', '%s_scale' % prefix) if scale is not None: scale = float(scale) depth = getValue(self.infos, 'edge', '%s_depth' % prefix) if depth is not None: depth = float(depth) # ugly hack because xmoto 0.5.2 non set value for depth is -1.0f if depth == -1.0: depth += 0.0001 return ((r, g, b, a), scale, depth) self.curBlockCounter = 0 self.curBlock = self._id self.ratio = options['ratio'] self.newWidth = options['width'] self.newHeight = options['height'] self.smooth = options['smooth'] useSmooth = getBoolValue(self.infos, 'position', '_usesmooth') if useSmooth == True: (present, smooth) = getIfPresent(self.infos, 'position', '_smooth') if present == True: self.smooth = 90.0 + float(smooth) createIfAbsent(self.infos, 'position') if ('x' not in self.infos['position'] or 'y' not in self.infos['position']): self.infos['position']['x'] = '%f' % (-self.newWidth / 2.0) self.infos['position']['y'] = '%f' % (self.newHeight / 2.0) layerNumber = self.infos['layerid'] del self.infos['layerid'] layerLevel = level.getLayersType()[layerNumber] if layerLevel == 'static': pass elif layerLevel == '2ndStatic': self.infos['position']['islayer'] = "true" removeNonMain(self.infos['position']) else: self.infos['position']['islayer'] = "true" lid = str(level.getLayerBlock2Level()[layerNumber]) self.infos['position']['layerid'] = lid removeForLayer(self.infos['position']) if 'usetexture' not in self.infos: self.infos['usetexture'] = {'id': 'default'} self.edgeTexture = getValue(self.infos, 'edge', 'texture', '') self.downEdgeTexture = getValue(self.infos, 'edge', 'downtexture', '') for prefix in ['u', 'd']: ((r, g, b, a), scale, depth) = getEdgeColorAndScale(prefix) if (r != 255 or g != 255 or b != 255 or scale is not None or depth is not None): setattr(self, '%s_material' % prefix, ((r, g, b, a), scale, depth)) else: setattr(self, '%s_material' % prefix, None) self.edgeAngle = float(getValue(self.infos, 'edges', 'angle', 270.0)) delWoExcept(self.infos, 'edge') if 'physics' in self.infos: if 'infinitemass' in self.infos['physics']: if self.infos['physics']['infinitemass'] == 'true': self.infos['physics']['mass'] = 'INFINITY' del self.infos['physics']['infinitemass'] self.preProcessVertex() for vertex in self.blocks: self.writeBlockHead() self.writeBlockVertex(vertex) self.content.append("\t</block>") self.curBlockCounter += 1 self.curBlock = self._id + str(self.curBlockCounter) return self.content
def updateNodeSvgAttributes(self, node, label, style): node = convertToXmNode(node, self.svg) labelValue = LabelParser().unparse(label) styleValue = StyleParser().unparse(style) # if the user select the an element in the sublayer using the # 'circle tool' for example, the selected node will be that # element, not the sublayer (the sublayer is selected when you # use the 'selection tool'). # in this case, use the sublayer instead of the selected child if node.belongsToSubLayer() == True: node = convertToXmNode(node.getparent()) # update node shape # ugly and clumsy but will be refactored with xmObjects later if 'typeid' in label: # entity or zone typeid = label['typeid'] if typeid in [ 'PlayerStart', 'EndOfLevel', 'Strawberry', 'Wrecker', 'Checkpoint' ]: if typeid == 'EndOfLevel': typeid = 'Flower' metadata = self.svg.getMetaDataValue() metadata = LabelParser().parse(metadata) if typeid == 'Checkpoint': # the checkpoint sprite is called with _1 createIfAbsent(metadata, 'remplacement') metadata['remplacement']['Checkpoint'] = 'Checkpoint_1' texName = getValue(metadata, 'remplacement', typeid, default=typeid) scale = float( getValue(metadata, 'remplacement', typeid + 'Scale', 1.0)) _reversed = getBoolValue(label, 'position', 'reversed') rotation = float(getValue(label, 'position', 'angle', 0.0)) radius = ENTITY_RADIUS[typeid] / SVG2LVL_RATIO node.setNodeAsBitmap(self.svg, texName, radius, SPRITES, labelValue, styleValue, scale, _reversed, rotation) elif typeid == 'ParticleSource': texName = getValue(label, 'param', 'type', '') radius = ENTITY_RADIUS[typeid] / SVG2LVL_RATIO node.setNodeAsBitmap(self.svg, texName, radius, PARTICLESOURCES, labelValue, styleValue) elif typeid == 'Sprite': texName = getValue(label, 'param', 'name', '') scale = float(getValue(label, 'size', 'scale', 1.0)) _reversed = getBoolValue(label, 'position', 'reversed') rotation = float(getValue(label, 'position', 'angle', 0.0)) radius = ENTITY_RADIUS['Sprite'] / SVG2LVL_RATIO node.setNodeAsBitmap(self.svg, texName, radius, SPRITES, labelValue, styleValue, scale, _reversed, rotation) elif typeid == 'Zone': (node, aabb) = node.subLayerElementToSingleNode() node.setNodeAsRectangle(aabb) # we may have set the label and still to a child of # 'g', and now node is the 'g', so we have to set it # to it too. node.setStyleLabel(labelValue, styleValue) elif typeid == 'Joint': # the addJoint extension already create the joints # with the right shape pass else: raise Exception("typeid=%s not handled by \ updateNodeSvgAttributes" % typeid) else: # block if node.isSubLayer(type=XmNode.BITMAP) == True: log.outMsg("Can't convert an entity to a block") return # elif (getValue(label, 'usetexture', 'color_r', 255) != 255 # or getValue(label, 'usetexture', 'color_g', 255) != 255 # or getValue(label, 'usetexture', 'color_b', 255) != 255): # # a color is not 255, we have to set two blocks, one # # textured and one colored # coloredStyle = self.generateStyle(label, coloredBlock=True) # coloredStyleValue = StyleParser().unparse(coloredStyle) # # g = node.getSubLayerNode() # g.addColoredChildren(node, labelValue, styleValue, coloredStyleValue) else: if node.isSubLayer(type=XmNode.BLOCK) == True: # remove sublayer and colored block node.removeColoredChildren(labelValue, styleValue) else: # nothing to do pass node.setStyleLabel(labelValue, styleValue)
def generateStyle(self, label, coloredBlock=False): def generateElementColor(color): """ randomly change the color to distinguish between adjacent elements """ from random import randint # r, g and b must not be 'f' before adding the random int # or it could became '0' r = (hex2dec(color[0]) + randint(0, 1)) % 16 g = (hex2dec(color[2]) + randint(0, 1)) % 16 b = (hex2dec(color[4]) + randint(0, 1)) % 16 return ('#' + dec2hex(r) + color[1] + dec2hex(g) + color[3] + dec2hex(b) + color[5]) style = {} if 'typeid' in label: # entity or zone typeid = label['typeid'] if typeid in [ 'PlayerStart', 'EndOfLevel', 'ParticleSource', 'Sprite', 'Strawberry', 'Wrecker', 'Checkpoint' ]: style['fill'] = 'none' style['stroke-width'] = '1px' style['stroke-linecap'] = 'butt' style['stroke-linejoin'] = 'miter' style['stroke-opacity'] = '1' if typeid == 'PlayerStart': # blue style['stroke'] = generateElementColor('0000ee') elif typeid == 'EndOfLevel': # yellow style['stroke'] = generateElementColor('eeee00') elif typeid == 'ParticleSource': # orange style['stroke'] = generateElementColor('eea500') elif typeid == 'Sprite': # purple style['stroke'] = generateElementColor('800080') elif typeid == 'Strawberry': # red style['stroke'] = generateElementColor('ee0000') elif typeid == 'Wrecker': # gray style['stroke'] = generateElementColor('808080') elif typeid == 'Checkpoint': # green style['stroke'] = generateElementColor('00ee00') elif typeid == 'Zone': # cyan style['fill'] = generateElementColor('00eeee') style['fill-opacity'] = 0.5 elif typeid == 'Joint': # green style['fill'] = generateElementColor('00ee00') if getValue(label, 'joint', 'type', '') == 'pin': style['stroke'] = '#000000' style['stroke-opacity'] = '1' else: # black style['fill'] = generateElementColor('000000') else: # block if coloredBlock == False: createIfAbsent(label, 'usetexture') if 'id' not in label['usetexture']: label['usetexture']['id'] = 'Dirt' # display the texture, if the texture is missing, display # the old colors try: scale = float(getValue(label, 'usetexture', 'scale', 1.0)) patternId = self.svg.addPattern(label['usetexture']['id'], TEXTURES, scale) style['fill'] = 'url(#%s)' % patternId except Exception, e: logging.info("Can't create pattern for texture %s.\n%s" % (label['usetexture']['id'], e)) style['fill-opacity'] = '1' if 'position' in label: if ('background' in label['position'] and 'dynamic' in label['position']): # d36b00 style['fill'] = generateElementColor('d36b00') elif 'background' in label['position']: # bdb76b = darkkhaki style['fill'] = generateElementColor('bdb76b') elif 'dynamic' in label['position']: # f08080 = lightcoral style['fill'] = generateElementColor('e08080') elif 'physics' in label['position']: style['fill'] = generateElementColor('ee00ee') else: # 66cdaa = mediumaquamarine style['fill'] = generateElementColor('66cdaa') else: # 66cdaa = mediumaquamarine style['fill'] = generateElementColor('66cdaa') else:
elif 'background' in label['position']: # bdb76b = darkkhaki style['fill'] = generateElementColor('bdb76b') elif 'dynamic' in label['position']: # f08080 = lightcoral style['fill'] = generateElementColor('e08080') elif 'physics' in label['position']: style['fill'] = generateElementColor('ee00ee') else: # 66cdaa = mediumaquamarine style['fill'] = generateElementColor('66cdaa') else: # 66cdaa = mediumaquamarine style['fill'] = generateElementColor('66cdaa') else: r = getValue(label, 'usetexture', 'color_r', default=255) g = getValue(label, 'usetexture', 'color_g', default=255) b = getValue(label, 'usetexture', 'color_b', default=255) style['fill'] = color2Hex(r, g, b) # mix in the color (alpha for the moment) alpha = float(getValue(label, 'usetexture', 'color_a', 255)) / 255.0 if alpha != 1.0: style['opacity'] = alpha if 'edge' in label: style['stroke-width'] = '1px' style['stroke-linecap'] = 'butt' style['stroke-linejoin'] = 'miter' style['stroke-opacity'] = '1'
def writeLevelHead(self): head = [] _id = getValue(self.options, 'level', 'id', default='defaultId') rversion = self.options['lvl']['rversion'] name = getValue(self.options, 'level', 'name', default='defaultName') desc = getValue(self.options, 'level', 'desc', default='') author = getValue(self.options, 'level', 'author', default='') today = str(date.today()) if _id == '': _id = 'defaultId' if name == '': name = 'defaultName' head.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>") head.append("<level id=\"%s\" rversion=\"%s\">" % (_id, rversion)) head.append("\t<info>") head.append("\t\t<name>%s</name>" % name) head.append("\t\t<description>%s</description>" % desc) head.append("\t\t<author>%s</author>" % author) head.append("\t\t<date>%s</date>" % today) # sky sky = "\t\t<sky" if 'sky' in self.options: # drifted is useless when it's put to false drifted = getValue(self.options, 'sky', 'drifted', default='false') if drifted == 'false': delWoExcept(self.options['sky'], 'drifted') tex = getValue(self.options, 'sky', 'tex') if tex in NOTSET_BITMAP: tex = '' delWoExcept(self.options['sky'], 'tex') for skyParam, value in self.options['sky'].iteritems(): if not skyParam.startswith('_') and value != '': sky += ' %s="%s"' % (skyParam, value) sky += ">%s</sky>" % tex head.append(sky) else: sky += ">%s</sky>" % 'sky1' head.append(sky) # border border = getValue(self.options, 'level', 'tex') if border not in NOTSET_BITMAP: head.append("\t\t<border texture=\"%s\"/>" % border) # music music = getValue(self.options, 'level', 'music') if music not in NOTSET: head.append("\t\t<music name=\"%s\" />" % music) head.append("\t</info>") # remplacement if 'remplacement' in self.options: # we want to add to the level the <theme_replacements> # tags only if there's some theme replacements. first = True line = "\t\t<sprite_replacement old_name=\"%s\" new_name=\"%s\"/>" for key, value in self.options['remplacement'].iteritems(): if (value not in NOTSET and key.find('Scale') == -1 and key != value): if first == True: head.append("\t<theme_replacements>") first = False head.append(line % (key, value)) if first == False: head.append("\t</theme_replacements>") # layer if 'layer' in self.options: # only add the <layeroffsets> tag if there's really some layers first = True line = "\t\t<layeroffset x=\"%s\" y=\"%s\" frontlayer=\"%s\"/>" layerInfos = self.options['layer'] for _id in self.layersType: if _id in ['static', '2ndStatic', 'unused']: continue if first == True: head.append("\t<layeroffsets>") first = False head.append(line % (layerInfos['layer_%d_x' % _id], layerInfos['layer_%d_y' % _id], layerInfos['layer_%d_isfront' % _id])) if first == False: head.append("\t</layeroffsets>") # limits head.append("\t<limits left=\"%f\" right=\"%f\" \ top=\"%f\" bottom=\"%f\"/>" % (self.limits['left'], self.limits['right'], self.limits['top'], self.limits['bottom'])) self.content.extend(head)
def getValue(self, ns, key, default): return getValue(self.label, ns, key, default)