예제 #1
0
	def macros(self):
		if self._macros: return self._macros
		# get optinal macros related to the token actions
		actions = (macros.ActionMacro(self, action) for action in self.actions if action["name"])
		lairs = (macros.LairMacro(self, action) for action in self.lair_actions if action["name"])
		reg = (macros.RegionalEffectMacro(self, action) for action in self.regional_effects if action["name"])
		legends= (macros.LegendaryMacro(self, leg) for leg in self.legends if leg["name"])
		attributes = self.scAttributes
		if self.sc and attributes is None:
			log.warning("Token %s has malformed spellcasting info: %s", self, self.sc)
		spellCast = []
		if attributes:
			attr, dc, attack = attributes
			groupName = 'Spells(%s) save DC%s attack %s' % (attr[:3], dc, attack)
			spellCast = (macros.SpellCastingMacro(self, spe, groupName) for spe in self.specials if spe['name'].lower()=="spellcasting")
		specials = (macros.SpecialMacro(self, spe) for spe in self.specials if spe['name'] and spe['name'].lower()!="spellcasting")
		spells = (macros.SpellMacro(self, spell) for spell in self.spells)
		commons = [
			macros.SheetMacro(self),
			macros.Macro(self, None, 'Init', '[macro("Init@Lib:Addon5e"):0]', **{'group': 'Rolls', 'colors': ('white', 'green'), 'tooltip': 'Roll and add to the init panel'}),
			macros.Macro(self, None, 'SaveMe', '[macro("SaveMe@Lib:Addon5e"):0]', **{'group': 'Rolls', 'colors': ('white', 'green'), 'tooltip': 'Roll Saving Throws'}),
			macros.Macro(self, None, 'CheckMe', '[macro("CheckMe@Lib:Addon5e"):0]', **{'group': 'Rolls', 'colors': ('white', 'green'), 'tooltip': 'Roll Skill Checks'}),
		]
		if not args.delivery: 
			commons.append(macros.Macro(self, None, 'Debug', '[r: a5e.debug()]', **{'group': 'zDebug', 'colors': ('white', 'black')}))
		self._macros = list(itertools.chain(actions, spellCast, specials, legends, lairs, reg, commons, spells))
		return self._macros
예제 #2
0
	def macros(self):
		if not self._macros:
			for name,asset in self.assets.iteritems():
				# add the name as comment so the macro are sorted by the name => increasing number
				label = '<!--%s--><img height=40 width=40 src="asset://%s"></img>' % (name, asset.md5)
				self._macros.append(macros.Macro(self, '', label, ''' [h: setTokenImage("asset://%s")] ''' % asset.md5, group='icons' if (not name.startswith('rn_')) else 'IDs', colors=('black', 'white')))
			self._macros.append(macros.Macro(self, '', 'bigger',  '''
			[h, if (getSize() == "large"), code : {[setSize("huge")]};{}]
			[h, if (getSize() == "medium"), code : {[setSize("large")]};{}]
			[h, if (getSize() == "small"), code : {[setSize("medium")]};{}]
			[h, if (getSize() == "tiny"), code : {[setSize("small")]};{}]
			''', group='aSettings',))
			self._macros.append(macros.Macro(self, '', 'smaller',  '''
			[h, if (getSize() == "small"), code : {[setSize("tiny")]};{}]
			[h, if (getSize() == "medium"), code : {[setSize("small")]};{}]
			[h, if (getSize() == "large"), code : {[setSize("medium")]};{}]
			[h, if (getSize() == "huge"), code : {[setSize("large")]};{}]
			''', group='aSettings',))
			self._macros.append(macros.Macro(self, '', "fromHandout", '''
[h: gmNotes = ""]
[h: notes = getNotes()]
[h: notes = notes +"This is a test"]
[h: pcNotes = ""]
[h: pcNotes = pcNotes + "<FONT COLOR=BLACK SIZE=4>" + notes + "</FONT><HR>"]
[h: pid = getTokenPortrait()]
[h: hid = getTokenHandout()]
[h, if (pid != ""), code: {
	[h: gmNotes = gmNotes + "<img src='" + pid + "'/><br>"]
};{}]
[h, if (hid != ""), code: {
	[h: gmNotes = gmNotes + "<img src='" + hid + "'/>"]
};{}]
[h: setNotes("")]
[h: setGMNotes(pcNotes + gmNotes)]
[h: setSize("large")]
[h: setLayer("GM")]
[h: setTokenSnapToGrid(0)]
''', group='aSettings'))
		return self._macros
예제 #3
0
def main():
	parser = argparse.ArgumentParser(description='DnD 5e token builder')
	parser.add_argument('--verbose', '-v', action='count')
	parser.add_argument('--max-token', '-m', type=int)
	parser.add_argument('--delivery', '-d', action="store_true", default=False)
	global args
	args = parser.parse_args()
	if not os.path.exists('build'): os.makedirs('build')
	localMonsters = []
	tob = '../open5e/legacy-source-content/monsters/tome-of-beasts/'
	sources = [
		r'../5e-database/5e-SRD-Monsters-volo.json',
		r'../5e-database/5e-SRD-Monsters.json',
	]
	sources += [os.path.join(dp, f) for dp, dn, filenames in os.walk(tob) for f in filenames if os.path.splitext(f)[1] == '.rst' and 'index' not in f]
	for f in sources:
		with codecs.open(f, 'r', encoding='utf8') as mfile:
			if f.endswith('json'):
				localMonsters += json.load(mfile)
			if f.endswith('rst'):
				localMonsters += [loadFromRst(mfile)]

	mLog = logging.getLogger()
	mLog.setLevel(logging.DEBUG)
	mLog.handlers[-1].setLevel(logging.WARNING-(args.verbose or 0)*10)
	fh = logging.FileHandler(os.path.join('build', 'tokens.log'), mode="w") # mode w will erase previous logs
	fh.setLevel(logging.DEBUG)
	fh.setFormatter(logging.Formatter('%(name)s : %(levelname)s : %(message)s'))
	mLog.addHandler(fh)

	# generate the lib addon token
	addon = LibToken('Lib:Addon5e')
	fromFile = lambda path: jenv().get_template(path).render().encode("utf-8")
	params = {'group': 'zLib', 'prefix': 'a5e'}
	addon.add(macros.Macro(addon, '', 'onCampaignLoad', '''
[h: defineFunction( "%(prefix)s.jget", "jget@this" )]
[h: defineFunction( "%(prefix)s.debug", "debug@this" )]
[h: defineFunction( "%(prefix)s.output", "output@this" )]
[h: defineFunction( "%(prefix)s.rollDice", "rollDice@this",0,0)]
''' % params, **params))
	addon.add(macros.Macro(addon, '', 'debug', '''[h: props = getPropertyNames()] [foreach(name, props, "<br>"), code: { [name]: [getProperty(name)]: [getRawProperty(name)]}] ''', **params))
	addon.add(macros.Macro(addon, '', 'output', fromFile("output.mtmacro"), **params))
	addon.add(macros.Macro(addon, '', 'rollDice', fromFile("rollDice.mtmacro"), **params))
	addon.add(macros.Macro(addon, '', 'jget', '''
[h: '<!-- Like json.get, but will adapt if the requested reference cannot be made.  By default, returns 0, or returns a default named (as a third parameter). -->']

[h: object = arg(0)]
[h: key = arg(1)]
[h, if( argCount() > 2 ): default = arg(2); default = 0]

[h, if( json.type( object ) == "OBJECT" ), code:
{
	[h: macro.return = if( json.contains( object, key ), json.get( object, key ), default )]
};{
	[if( json.type( object ) == "ARRAY" && isNumber( key ) ), code:
	{
		[h, if( json.length( object ) > key ): 
			macro.return = json.get( object, key ) ;
			macro.return = default )]
	};{
		[h: macro.return = default ]
	}]
}]
''', **params))
	params = {'group': 'dnd5e'}
	addon.add(macros.Macro(addon, '', 'Description', fromFile('description.mtmacro'), **params))
	addon.add(macros.Macro(addon, '', 'CastSpell', fromFile('castSpell.mtmacro'), **params))
	addon.add(macros.Macro(addon, '', 'NPCAttack', fromFile('npcAttack.mtmacro'), **params))
	addon.add(macros.Macro(addon, '', 'Init', fromFile('init.mtmacro'), **params))
	addon.add(macros.Macro(addon, '', 'getNPCInitBonus', '''[h, macro("getNPCSkills@Lib:Addon5e"):0]
[h: jskills = macro.return]
[h: initb = json.get(jskills, "Initiative")]
[h, if (initb==""), code: {[h: initb=getProperty("bdex")]}]
[h:macro.return=initb]''', **params))
	# "Perception +5, Initiative +3" => {"Perception": 5, "Initiative": 3}
	addon.add(macros.Macro(addon, '', 'getNPCSkills', r'''<!-- Fetch skill bonuses-->

<!-- Depending on the source (SRD, tome of the beast, MM) format of skill data may change-->
<!-- Method 1: fetch skill entries like "skills: Perception +5, Stealth +4"-->
[h: id = strfind(getProperty("skills"), "((\\w+) \\+(\\d+))")]
[h: jskills = "{}"]
[h: find = getFindCount(id)]
[h, while (find != 0), code: {
	[h: sname = getGroup(id, find, 2)]
	[h: svalue = getGroup(id, find, 3)]
	[h: jskills = json.set(jskills, sname, svalue)]
	[h: find = find - 1]
}]
[h: all_skills= getLibProperty("all_skills", "Lib:Addon5e")]
<!-- Most of the token don't specify a modifier for all skills-->
<!-- for all skills missing a modifier, use the default one which is the attribute modifier -->
[h, foreach(skill, all_skills), code: {
	[Attribute = json.get(all_skills, skill)]
	[att_ = lower(substring(Attribute, 0, 3))]
	[modifier = json.get(jskills, skill)]
	[default_mod = getProperty("b"+att_)]
	[no_mod = json.isEmpty(modifier) ]
	[if (no_mod): jskills = json.set(jskills, skill , default_mod)]
}]
[h: macro.return = jskills]''', **params))
	# "Wis +3, Con +2" => {"Wis": 2, "Con": 2}
	addon.add(macros.Macro(addon, '', 'getNPCSaves', r'''[h: id = strfind(getProperty("saves"), "((\\w+) \\+(\\d+))")]
[h: jsaves= "{}"]
[h: find = getFindCount(id)]
<!-- parse the prop "saves" which may contain some save modifiers-->
<!-- "Wis +3, Con +2" => "Wis": 2, "Con": 2 -->
[h, while (find != 0), code: {
	[h: sname = getGroup(id, find, 2)]
	[h: svalue = getGroup(id, find, 3)]
	[h: jsaves = json.set(jsaves, sname, svalue)]
	[h: find = find - 1]
}]
<!-- Most of the token don't specify a modifier for all saves -->
<!-- for all saves missing a modifier, use the default one which is the attribute modifier -->
[h, foreach(Attribute, getLibProperty("attributes", "Lib:Addon5e")), code: {
	[Att = substring(Attribute, 0, 3)]
	[att_ = lower(Att)]
	[modifier = json.get(jsaves, Att)]
    [default_mod = getProperty("b"+att_)]
    [no_mod = json.isEmpty(modifier) ]
	[if (no_mod): jsaves = json.set(jsaves, Att ,default_mod)]
}]
[h: macro.return = jsaves]''', **params))
	addon.add(macros.Macro(addon, '', 'SaveMe', fromFile('saveme.mtmacro'), **params))
	addon.add(macros.Macro(addon, '', 'CheckMe', fromFile('checkme.mtmacro'), **params))
	params = {'group': 'aMenu'}
	# TODO: control panel is currently empty but it is a customized panel where I can add whatever macro, it act as a campaign panel
	# but is fully customizable, it's a html form
	# see http://forums.rptools.net/viewtopic.php?f=20&t=23208&p=236662&hilit=amsave#p236662
	addon.add(macros.Macro(addon, '', 'ControlPanel', '''[dialog("A5e Panel", "width=215; height=700; temporary=0; input=1"): {[r,macro("cpanel@this"):0]}]''', **params))
	params = {'group': 'Format'}
	addon.add(macros.Macro(addon, '', 'cpanel', fromFile('cpanel.mtmacro'), **params))
	addon.add(macros.Macro(addon, '', 'HTMLMacroButton','''[h:bgColor	= arg(1)]
[h,if(argCount() > 5): shadow = arg(5); shadow = "")]
[h,if(argCount() > 6): toolTip = arg(6); toolTip = "")]
[h,if(argCount() > 7): args = arg(7); args = "[]")]
[h,if(argCount() > 8): libType = arg(8); libType = "@this")]
[h,if(argCount() > 9): output = arg(9); output = "none")]

[h:btnformat	= strformat("padding:1px; border-width:1pt; border-style:solid; border-color:black; text-align:center; white-space:nowrap; background-image:url(%{shadow}); background-color:%{bgColor};")]
 
<td width='[r:arg(0)]%'>
	<table width='100%' cellpadding='0' cellspacing='0'>
		<tr>
			<td style='[r:btnformat]'>
				<span title='[r:toolTip]' style='text-decoration:none; color:[r:arg(2)]'>
					[r:macroLink(arg(3),arg(4)+libType,output,args)]
				</span>
			</td>
		</tr>
	</table>
</td>''' , **params))
	filename = addon.zipme()
	log.warning("Done generating 1 library token: %s", addon)


	poi = POI("POI")
	# fetch the monsters(token) and spells from dnd5Api or get them from the serialized file
	#tokens = itertools.chain((Token(m) for m in monsters), Token.load('build'))
	# dont use online api, use the fectched local database instead
	tokens = itertools.chain([poi], (Token(m) for m in itertools.chain(localMonsters)))
	# 5e-database is probably a link
	with open(r'../5e-database/5e-SRD-Spells.json', 'r') as mfile:
		localSpells = json.load(mfile)

	Spell.spellDB = [Spell(spell) for spell in localSpells]

	sTokens = [] # used for further serialization, because tokens is a generator and will be consumed
	cnt = 0
	deliveryFilename = 'build/dnd5eTokens.zip'
	zfile = zipfile.ZipFile(deliveryFilename, "w", zipfile.ZIP_STORED) if args.delivery else None
	# add lib:addon5e to the zipfile
	if zfile:
		zfile.write(filename, os.path.relpath(filename, start='build'))
	for token in itertools.islice(tokens, args.max_token):
		log.info(token)
		log.debug(token.verbose())
		filename = token.zipme()
		if zfile:
			zfile.write(filename, os.path.relpath(filename, start='build'))
		sTokens.append(token)
		if 'dft.png' in token.img.name: log.warning(str(token))
		cnt += 1
	log.warning("Done generating %s tokens"%cnt)

	log.warning("building campaign file")
	zone = Zone('Library')
	zone.build(sTokens + [addon])
	cp = Campaign('demo5e')
	cp.build([zone], [PSet('Basic', [])], [])
	log.warning("Done building campaign file")

	if zfile:
		zfile.close()
		log.warning("Done writing delivery zip file '%s'" % deliveryFilename)

	Token.dump('build', sTokens)
	Spell.dump('build', Spell.spellDB)