diff --git a/model/epmodel.sbt b/model/epmodel.sbt index df7131d..c37c3cb 100644 --- a/model/epmodel.sbt +++ b/model/epmodel.sbt @@ -5,7 +5,7 @@ name := "EP Model Root" organization in ThisBuild := "com.lkroll.ep" -version in ThisBuild := "1.9.3" +version in ThisBuild := "1.10.0" scalaVersion in ThisBuild := "2.12.6" diff --git a/model/shared/src/main/scala/com/lkroll/ep/model/DamageType.scala b/model/shared/src/main/scala/com/lkroll/ep/model/DamageType.scala index 4fa258c..4220ee1 100644 --- a/model/shared/src/main/scala/com/lkroll/ep/model/DamageType.scala +++ b/model/shared/src/main/scala/com/lkroll/ep/model/DamageType.scala @@ -35,3 +35,15 @@ object DamageType extends Enumeration { def dynamicLabel(v: Value): String = s"${labelPrefix}-${v.toString()}"; def dynamicLabelShort(v: Value): String = s"${labelShortPrefix}-${v.toString()}"; } + +object DamageArea extends Enumeration { + type DamageArea = Value; + + val Point, Blast, UniformBlast, Cone = Value; + + val labelPrefix = "damage-areaopt"; + val labelShortPrefix = "damage-areaopt-short"; + + def dynamicLabel(v: Value): String = s"${labelPrefix}-${v.toString()}"; + def dynamicLabelShort(v: Value): String = s"${labelShortPrefix}-${v.toString()}"; +} diff --git a/model/shared/src/main/scala/com/lkroll/ep/model/EPTranslation.scala b/model/shared/src/main/scala/com/lkroll/ep/model/EPTranslation.scala index f3abea5..c74903c 100644 --- a/model/shared/src/main/scala/com/lkroll/ep/model/EPTranslation.scala +++ b/model/shared/src/main/scala/com/lkroll/ep/model/EPTranslation.scala @@ -240,6 +240,7 @@ object EPTranslation extends SheetI18N { val burstFireDescription = text("burst-fire-description"); val fullAutomaticDescription = text("full-automatic-description"); val weaponRanges = text("weapon-ranges"); + val thrown = text("thrown"); val shortRange = abbr("s-range", "short-range"); val mediumRange = abbr("m-range", "medium-range"); val longRange = abbr("l-range", "long-range"); @@ -247,6 +248,8 @@ object EPTranslation extends SheetI18N { val magazine = text("magazine"); val size = text("size"); val ammoType = text("ammo-type"); + val damageArea = text("damage-area"); + val uniformRadius = text("uniform-radius"); val damageInflicts = text("damage-inflicts"); val damageValue = abbr("dv", "damage-value"); val concentrateFire = text("concentrate-fire"); @@ -381,6 +384,14 @@ object EPTranslation extends SheetI18N { val opts = DamageType.values.map(v => (v -> v.toString)).toMap; enum[DamageType.type](DamageType.labelShortPrefix, opts) } + val dmgArea = { + val opts = DamageArea.values.map(v => (v -> v.toString)).toMap; + enum[DamageArea.type](DamageArea.labelPrefix, opts) + } + val dmgAreaShort = { + val opts = DamageArea.values.map(v => (v -> v.toString)).toMap; + enum[DamageArea.type](DamageArea.labelShortPrefix, opts) + } val psiType = { val opts = PsiType.values.map(v => (v -> v.toString)).toMap; enum[PsiType.type](PsiType.labelPrefix, opts) diff --git a/model/shared/src/main/scala/com/lkroll/ep/model/EquipmentSections.scala b/model/shared/src/main/scala/com/lkroll/ep/model/EquipmentSections.scala index ea90f00..d54d995 100644 --- a/model/shared/src/main/scala/com/lkroll/ep/model/EquipmentSections.scala +++ b/model/shared/src/main/scala/com/lkroll/ep/model/EquipmentSections.scala @@ -82,17 +82,26 @@ object RangedWeaponSection extends RepeatingSection { val semiAutomatic = "semi_automatic".default(false); val burstFire = "burst_fire".default(false); val fullAutomatic = "full_automatic".default(false); + val thrown = flag("thrown").default(false); + val rangeUnitSymbol = "range_unit_symbol".editable(false).default("m"); val shortRangeLower = "short_range_lower".default(2).editable(false); - val shortRangeUpper = "short_range_upper".default(0).validIn(0, 99999, 1); + val shortRangeUpperInput = "short_range_upper_input".default(0.0).validIn(0.0, 99999.0, 0.1); + val shortRangeUpper = "short_range_upper".default(0).editable(false); val mediumRangeLower = "medium_range_lower".default(0).editable(false); - val mediumRangeUpper = "medium_range_upper".default(0).validIn(0, 99999, 1); + val mediumRangeUpperInput = "medium_range_upper_input".default(0.0).validIn(0.0, 99999.0, 0.1); + val mediumRangeUpper = "medium_range_upper".default(0).editable(false); val longRangeLower = "long_range_lower".default(0).editable(false); - val longRangeUpper = "long_range_upper".default(0).validIn(0, 99999, 1); + val longRangeUpperInput = "long_range_upper_input".default(0.0).validIn(0.0, 99999.0, 0.1); + val longRangeUpper = "long_range_upper".default(0).editable(false); val extremeRangeLower = "extreme_range_lower".default(0).editable(false); - val extremeRangeUpper = "extreme_range_upper".default(0).validIn(0, 99999, 1); + val extremeRangeUpperInput = "extreme_range_upper_input".default(0.0).validIn(0.0, 99999.0, 0.1); + val extremeRangeUpper = "extreme_range_upper".default(0).editable(false); val magazineSize = "ammo_max".default(0).validIn(0, 999, 1); val magazineCurrent = "ammo".default(0).validIn(0, 999, 1); val magazineType = "ammo_type".default("standard"); + val damageArea = "damage_area".options(DamageArea).default(DamageArea.Point); + val damageAreaShort = text("damage_area_short").editable(false).default(DamageArea.dynamicLabelShort(DamageArea.Point)); + val uniformBlastArea = "uniform_blast_area".default(1).validIn(0, 99999, 1); val showDescription = flag("show_description").default(false); val description = text("description"); } diff --git a/script/epapiscript.sbt b/script/epapiscript.sbt index 7dbedbf..7bd2a0e 100644 --- a/script/epapiscript.sbt +++ b/script/epapiscript.sbt @@ -6,13 +6,13 @@ name := "EP API Script" organization := "com.lkroll.ep" -version := "0.7.7" +version := "0.8.0" scalaVersion := "2.12.6" libraryDependencies += "com.lkroll.roll20" %%% "roll20-api-framework" % "0.8.+" -libraryDependencies += "com.lkroll.ep" %%% "epcompendium-core" % "3.0.0" -libraryDependencies += "com.lkroll.ep" %%% "ep-model" % "1.9.3" +libraryDependencies += "com.lkroll.ep" %%% "epcompendium-core" % "3.0.1" +libraryDependencies += "com.lkroll.ep" %%% "ep-model" % "1.10.0" libraryDependencies += "com.lihaoyi" %%% "fastparse" % "1.+" libraryDependencies += "org.rogach" %%% "scallop" % "3.1.+" libraryDependencies += "org.scalactic" %%% "scalactic" % "3.0.4" % "test" diff --git a/script/src/main/scala/com/lkroll/ep/api/compendium/WeaponImport.scala b/script/src/main/scala/com/lkroll/ep/api/compendium/WeaponImport.scala index d610a8e..d943192 100644 --- a/script/src/main/scala/com/lkroll/ep/api/compendium/WeaponImport.scala +++ b/script/src/main/scala/com/lkroll/ep/api/compendium/WeaponImport.scala @@ -27,8 +27,9 @@ package com.lkroll.ep.api.compendium import com.lkroll.roll20.core._ import com.lkroll.roll20.api._ import com.lkroll.ep.compendium._ -import com.lkroll.ep.model.{ EPCharModel => epmodel, MeleeWeaponSection, RangedWeaponSection, DamageType => ModelDamageType } +import com.lkroll.ep.model.{ EPCharModel => epmodel, MeleeWeaponSection, RangedWeaponSection, DamageType => ModelDamageType, DamageArea => ModelDamageArea } import APIImplicits._; +//import scala.util.{ Try, Success, Failure } object WeaponDamageTypeConverter { def convert(dt: DamageType): Either[ModelDamageType.DamageType, String] = { @@ -41,6 +42,17 @@ object WeaponDamageTypeConverter { } } +object WeaponDamageAreaConverter { + def convert(da: DamageArea): Either[ModelDamageArea.DamageArea, String] = { + da match { + case DamageArea.Point => Left(ModelDamageArea.Point) + case DamageArea.Blast => Left(ModelDamageArea.Blast) + case DamageArea.Cone => Left(ModelDamageArea.Cone) + case DamageArea.UniformBlast(_) => Left(ModelDamageArea.UniformBlast) + } + } +} + case class WeaponImport(weapon: Weapon) extends Importable { override def updateLabel: String = s"weapon ${weapon.name}"; @@ -50,6 +62,10 @@ case class WeaponImport(weapon: Weapon) extends Importable { case Left(dt) => dt case Right(msg) => return Right(msg) }; + val damageArea = WeaponDamageAreaConverter.convert(weapon.area) match { + case Left(da) => da + case Right(msg) => return Right(msg) + } weapon.`type` match { case _: WeaponType.Melee => { char.createRepeating(MeleeWeaponSection.weapon, rowId) <<= weapon.name; @@ -75,7 +91,7 @@ case class WeaponImport(weapon: Weapon) extends Importable { } } } - case _: WeaponType.Ranged => { + case _: WeaponType.Ranged | _: WeaponType.Thrown => { char.createRepeating(RangedWeaponSection.weapon, rowId) <<= weapon.name; char.createRepeating(RangedWeaponSection.skillSearch, rowId) <<= weapon.`type`.skill; char.createRepeating(RangedWeaponSection.miscMod, rowId) <<= weapon.attackBonus; @@ -88,19 +104,33 @@ case class WeaponImport(weapon: Weapon) extends Importable { char.createRepeating(RangedWeaponSection.damageBonus, rowId) <<= weapon.damage.dmgConst; char.createRepeating(RangedWeaponSection.damageType, rowId) <<= damageType.toString; char.createRepeating(RangedWeaponSection.damageTypeShort, rowId) <<= ModelDamageType.dynamicLabelShort(damageType); + char.createRepeating(RangedWeaponSection.damageArea, rowId) <<= damageArea.toString; + char.createRepeating(RangedWeaponSection.damageAreaShort, rowId) <<= ModelDamageArea.dynamicLabelShort(damageArea); + weapon.area match { + case DamageArea.UniformBlast(r) => { + char.createRepeating(RangedWeaponSection.uniformBlastArea, rowId) <<= r; + } + case _ => () // leave default + } + val thrownField = char.createRepeating(RangedWeaponSection.thrown, rowId); weapon.range match { - case Range.Melee => return Right("Ranged weapons should have range Ranged") - case _: Range.Thrown => return Right("Thrown weapons are currently not supported") + case Range.Melee => return Right("Ranged weapons should have range Ranged") + case r: Range.Thrown => { + char.createRepeating(RangedWeaponSection.shortRangeUpperInput, rowId) <<= r.shortFactor; + char.createRepeating(RangedWeaponSection.mediumRangeUpperInput, rowId) <<= r.mediumFactor; + char.createRepeating(RangedWeaponSection.longRangeUpperInput, rowId) <<= r.longFactor; + char.createRepeating(RangedWeaponSection.extremeRangeUpperInput, rowId) <<= r.extremeFactor; + + thrownField.setWithWorker(true); // this will cause dependent ranged to be calculated + } case Range.Ranged(s, m, l, x) => { - //char.createRepeating(RangedWeaponSection.shortRangeLower, rowId) <<= 2; // below this is point blank but don't write default value - char.createRepeating(RangedWeaponSection.shortRangeUpper, rowId) <<= s; - char.createRepeating(RangedWeaponSection.mediumRangeLower, rowId) <<= s + 1; - char.createRepeating(RangedWeaponSection.mediumRangeUpper, rowId) <<= m; - char.createRepeating(RangedWeaponSection.longRangeLower, rowId) <<= m + 1; - char.createRepeating(RangedWeaponSection.longRangeUpper, rowId) <<= l; - char.createRepeating(RangedWeaponSection.extremeRangeLower, rowId) <<= l + 1; - char.createRepeating(RangedWeaponSection.extremeRangeUpper, rowId) <<= x; + char.createRepeating(RangedWeaponSection.shortRangeUpperInput, rowId) <<= s; + char.createRepeating(RangedWeaponSection.mediumRangeUpperInput, rowId) <<= m; + char.createRepeating(RangedWeaponSection.longRangeUpperInput, rowId) <<= l; + char.createRepeating(RangedWeaponSection.extremeRangeUpperInput, rowId) <<= x; + + thrownField.setWithWorker(false); // this will cause dependent ranged to be calculated } } @@ -115,7 +145,17 @@ case class WeaponImport(weapon: Weapon) extends Importable { magazine <<= magazineSize; magazine.max = magazineSize.toString(); // TODO make max handling nicer } - case None => APILogger.warn(s"Weapon ${weapon.name} does not seems to be gun, despite being of ranged type.") + case None => { + char.createRepeating(RangedWeaponSection.singleShot, rowId) <<= true; + char.createRepeating(RangedWeaponSection.semiAutomatic, rowId) <<= false; + char.createRepeating(RangedWeaponSection.burstFire, rowId) <<= false; + char.createRepeating(RangedWeaponSection.fullAutomatic, rowId) <<= false; + + val magazine = char.createRepeating(RangedWeaponSection.magazineCurrent, rowId); + magazine <<= 1; + magazine.max = "1"; // TODO make max handling nicer + char.createRepeating(RangedWeaponSection.magazineType, rowId) <<= "NA"; + } } char.createRepeating(RangedWeaponSection.description, rowId) <<= weapon.descr; @@ -144,6 +184,10 @@ case class WeaponWithAmmoImport(weapon: WeaponWithAmmo) extends Importable { case Left(dt) => dt case Right(msg) => return Right(msg) }; + val damageArea = WeaponDamageAreaConverter.convert(weapon.area) match { + case Left(da) => da + case Right(msg) => return Right(msg) + } weapon.weapon.`type` match { case _: WeaponType.Melee => { char.createRepeating(MeleeWeaponSection.weapon, rowId) <<= weapon.name; @@ -182,19 +226,33 @@ case class WeaponWithAmmoImport(weapon: WeaponWithAmmo) extends Importable { char.createRepeating(RangedWeaponSection.damageBonus, rowId) <<= weapon.damage.dmgConst; char.createRepeating(RangedWeaponSection.damageType, rowId) <<= damageType.toString; char.createRepeating(RangedWeaponSection.damageTypeShort, rowId) <<= ModelDamageType.dynamicLabelShort(damageType); + char.createRepeating(RangedWeaponSection.damageArea, rowId) <<= damageArea.toString; + char.createRepeating(RangedWeaponSection.damageAreaShort, rowId) <<= ModelDamageArea.dynamicLabelShort(damageArea); + weapon.area match { + case DamageArea.UniformBlast(r) => { + char.createRepeating(RangedWeaponSection.uniformBlastArea, rowId) <<= r; + } + case _ => () // leave default + } + val thrownField = char.createRepeating(RangedWeaponSection.thrown, rowId); weapon.weapon.range match { - case Range.Melee => return Right("Ranged weapons should have range Ranged") - case _: Range.Thrown => return Right("Thrown weapons are currently not supported") + case Range.Melee => return Right("Ranged weapons should have range Ranged") + case r: Range.Thrown => { + char.createRepeating(RangedWeaponSection.shortRangeUpperInput, rowId) <<= r.shortFactor; + char.createRepeating(RangedWeaponSection.mediumRangeUpperInput, rowId) <<= r.mediumFactor; + char.createRepeating(RangedWeaponSection.longRangeUpperInput, rowId) <<= r.longFactor; + char.createRepeating(RangedWeaponSection.extremeRangeUpperInput, rowId) <<= r.extremeFactor; + + thrownField.setWithWorker(true); // this will cause dependent ranged to be calculated + } case Range.Ranged(s, m, l, x) => { - //char.createRepeating(RangedWeaponSection.shortRangeLower, rowId) <<= 2; // below this is point blank but don't write default value - char.createRepeating(RangedWeaponSection.shortRangeUpper, rowId) <<= s; - char.createRepeating(RangedWeaponSection.mediumRangeLower, rowId) <<= s + 1; - char.createRepeating(RangedWeaponSection.mediumRangeUpper, rowId) <<= m; - char.createRepeating(RangedWeaponSection.longRangeLower, rowId) <<= m + 1; - char.createRepeating(RangedWeaponSection.longRangeUpper, rowId) <<= l; - char.createRepeating(RangedWeaponSection.extremeRangeLower, rowId) <<= l + 1; - char.createRepeating(RangedWeaponSection.extremeRangeUpper, rowId) <<= x; + char.createRepeating(RangedWeaponSection.shortRangeUpperInput, rowId) <<= s; + char.createRepeating(RangedWeaponSection.mediumRangeUpperInput, rowId) <<= m; + char.createRepeating(RangedWeaponSection.longRangeUpperInput, rowId) <<= l; + char.createRepeating(RangedWeaponSection.extremeRangeUpperInput, rowId) <<= x; + + thrownField.setWithWorker(false); // this will cause dependent ranged to be calculated } } @@ -210,7 +268,17 @@ case class WeaponWithAmmoImport(weapon: WeaponWithAmmo) extends Importable { magazine.max = magazineSize.toString(); // TODO make max handling nicer char.createRepeating(RangedWeaponSection.magazineType, rowId) <<= weapon.ammo.name; } - case None => APILogger.warn(s"Weapon ${weapon.name} does not seems to be gun, despite being of ranged type.") + case None => { + char.createRepeating(RangedWeaponSection.singleShot, rowId) <<= true; + char.createRepeating(RangedWeaponSection.semiAutomatic, rowId) <<= false; + char.createRepeating(RangedWeaponSection.burstFire, rowId) <<= false; + char.createRepeating(RangedWeaponSection.fullAutomatic, rowId) <<= false; + + val magazine = char.createRepeating(RangedWeaponSection.magazineCurrent, rowId); + magazine <<= 1; + magazine.max = "1"; // TODO make max handling nicer + char.createRepeating(RangedWeaponSection.magazineType, rowId) <<= weapon.ammo.name; + } } char.createRepeating(RangedWeaponSection.description, rowId) <<= weapon.descr; diff --git a/sheet/epsheet.sbt b/sheet/epsheet.sbt index fce6330..793c123 100644 --- a/sheet/epsheet.sbt +++ b/sheet/epsheet.sbt @@ -6,7 +6,7 @@ name := "EP Sheet Root" organization in ThisBuild := "com.lkroll.ep" -version in ThisBuild := "1.9.3" +version in ThisBuild := "1.10.0" scalaVersion in ThisBuild := "2.12.6" diff --git a/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPUpdates.scala b/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPUpdates.scala index b437632..7bf6f0b 100644 --- a/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPUpdates.scala +++ b/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPUpdates.scala @@ -78,4 +78,21 @@ object EPUpdates extends MinorVersionUpdateManager { val assignSMax = op(model.lucidity).update(luc => Seq(model.stressMax <<= luc)); List(speedUpdate, moaUpdate, assignSMax) } + forVersion("1.9.0") { + val rangeUpdate = op( + RangedWeaponSection.shortRangeUpper, + RangedWeaponSection.mediumRangeUpper, + RangedWeaponSection.longRangeUpper, + RangedWeaponSection.extremeRangeUpper).update { + case (short, medium, long, extreme) => { + Seq( + RangedWeaponSection.shortRangeUpperInput <<= short.toDouble, + RangedWeaponSection.mediumRangeUpperInput <<= medium.toDouble, + RangedWeaponSection.longRangeUpperInput <<= long.toDouble, + RangedWeaponSection.extremeRangeUpperInput <<= extreme.toDouble) + } + }; + val rangeUpdates = rangeUpdate.all(RangedWeaponSection); + List(rangeUpdates) + } } diff --git a/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPWorkers.scala b/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPWorkers.scala index e7f5245..7d1634f 100644 --- a/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPWorkers.scala +++ b/sheet/js/src/main/scala/com/lkroll/ep/sheet/EPWorkers.scala @@ -171,13 +171,13 @@ object EPWorkers extends SheetWorkerRoot { val savTotalCalc = bind(op(savBase, savTemp, savMorph, savMorphMax)) update (aptTotalCalc(savTotal), SkillWorkers.skillTotalCalc); - val somTotalCalc = bind(op(somBase, somTemp, somMorph, somMorphMax)) update (aptTotalCalc(somTotal), dbCalc.andThen(SkillWorkers.skillTotalCalc)); + val somTotalCalc = bind(op(somBase, somTemp, somMorph, somMorphMax)) update (aptTotalCalc(somTotal), dbCalc.andThen(GearWorkers.weaponRangeLimits.all(RangedWeaponSection)).andThen(SkillWorkers.skillTotalCalc)); val wilTotalCalc = bind(op(wilBase, wilTemp, wilMorph, wilMorphMax)) update (aptTotalCalc(wilTotal), willStatsCalc.andThen(traumaThresholdCalc).andThen(SkillWorkers.skillTotalCalc)); val aptTotals = cogTotalCalc ++ List(cooTotalCalc, intTotalCalc, refTotalCalc, savTotalCalc, somTotalCalc, wilTotalCalc); - val aptTotalsAll = aptTotals ++ List(initCalc, dbCalc, willStatsCalc, traumaThresholdCalc, SkillWorkers.skillTotalCalc); + val aptTotalsAll = aptTotals ++ List(initCalc, dbCalc, willStatsCalc, traumaThresholdCalc, GearWorkers.weaponRangeLimits.all(RangedWeaponSection), SkillWorkers.skillTotalCalc); val durStatsCalc = op(durabilityBonus, morphDurability, morphType) update { case (bonus, morphDur, mt) => { diff --git a/sheet/js/src/main/scala/com/lkroll/ep/sheet/GearWorkers.scala b/sheet/js/src/main/scala/com/lkroll/ep/sheet/GearWorkers.scala index 70bb3c7..60fa7bd 100644 --- a/sheet/js/src/main/scala/com/lkroll/ep/sheet/GearWorkers.scala +++ b/sheet/js/src/main/scala/com/lkroll/ep/sheet/GearWorkers.scala @@ -80,16 +80,55 @@ object GearWorkers extends SheetWorker { } }; - val weaponRangeLimits = bind(op(rangedWeapons.shortRangeUpper, rangedWeapons.mediumRangeUpper, rangedWeapons.longRangeUpper)) update { - case (sru, mru, lru) => { + val weaponRangeLimitsFields = op(somTotal, rangedWeapons.thrown, rangedWeapons.shortRangeUpperInput, rangedWeapons.mediumRangeUpperInput, rangedWeapons.longRangeUpperInput, rangedWeapons.extremeRangeUpperInput); + val weaponRangeLimits = weaponRangeLimitsFields update { + case (som, somFactor, srui, mrui, lrui, xrui) => { + val (sru, mru, lru, xru) = if (somFactor) { + val somD = som.toDouble; + val multSom: Double => Int = (in) => Math.ceil(in * somD).toInt + (multSom(srui), multSom(mrui), multSom(lrui), multSom(xrui)) + } else { + (srui.toInt, mrui.toInt, lrui.toInt, xrui.toInt) + } + val srl = if (sru <= 2) Math.max(sru - 1, 0) else 2; val mrl = sru + 1; val lrl = mru + 1; val xrl = lru + 1; + val rus = if (somFactor) "×SOM" else "m"; Seq( + rangedWeapons.rangeUnitSymbol <<= rus, + rangedWeapons.shortRangeUpper <<= sru, + rangedWeapons.mediumRangeUpper <<= mru, + rangedWeapons.longRangeUpper <<= lru, + rangedWeapons.extremeRangeUpper <<= xru, + rangedWeapons.shortRangeLower <<= srl, rangedWeapons.mediumRangeLower <<= mrl, rangedWeapons.longRangeLower <<= lrl, rangedWeapons.extremeRangeLower <<= xrl) } + }; + val weaponRangeLimitsBinding = on(weaponRangeLimitsFields.getFields.map(f => s"change:${f.selector}").mkString(" "), (ei: EventInfo) => { + val repIdS = Roll20.getActiveRepeatingField(); + debug(s"Got repid = $repIdS"); + val f = if (repIdS == "-1") { + val op = weaponRangeLimits.all(RangedWeaponSection); + op() + } else { + weaponRangeLimits() + }; + f.onFailure { + case e: Throwable => sheet.error(e) + }; + }); + + val damageAreaCalc = bind(op(rangedWeapons.damageArea)) update { + case (daName) => { + import DamageArea._ + + val da = withName(daName); + val daLabel = dynamicLabelShort(da); + Seq(rangedWeapons.damageAreaShort <<= daLabel) + } } val meleeDamageTypeCalc = bind(op(meleeWeapons.damageType)) update { diff --git a/sheet/jvm/src/main/resources/WEB-INF/defaults.css b/sheet/jvm/src/main/resources/WEB-INF/defaults.css index 17aa697..ebc9554 100644 --- a/sheet/jvm/src/main/resources/WEB-INF/defaults.css +++ b/sheet/jvm/src/main/resources/WEB-INF/defaults.css @@ -443,6 +443,18 @@ span.sheet-cat-tag-field { top: -0.5em; } +span.sheet-area-field { + color: ${cat-tag-colour}; +} + +span.sheet-uniform-radius-field { + display: none; +} + +input.sheet-area-field[value="UniformBlast"]+span.sheet-uniform-radius-field { + display: inline; +} + span.sheet-trait-tag-field { font-family: monospace; font-size: 1em; diff --git a/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPStyle.scala b/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPStyle.scala index cf5c00e..354747f 100644 --- a/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPStyle.scala +++ b/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPStyle.scala @@ -411,6 +411,8 @@ object EPStyle extends SheetStyle { val identities = cls(); val `api-only` = cls(); val `using-api` = cls(); + val `area-field` = cls(); + val `uniform-radius-field` = cls(); // *** Templates *** diff --git a/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPTranslation.scala b/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPTranslation.scala index eaef3c2..2d912ba 100644 --- a/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPTranslation.scala +++ b/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/EPTranslation.scala @@ -234,6 +234,7 @@ A burst may be shot against a single target (concentrated fire) or against two t Only one full-auto attack may be made with each Complex Action. This attack may be made on a single target or against up to three separate targets within one meter of another. Against a single individual, the attacker can choose either a +30 modifier to hit or increase the DV by +3d10. Firing in full automatic mode uses up 10 shots. """.trim; val weaponRanges = keys.weaponRanges <~ "Ranges"; + val thrown = keys.thrown <~ "Thrown"; val shortRange = keys.shortRange <~ ("S", "Short"); val mediumRange = keys.mediumRange <~ ("M", "Medium"); val longRange = keys.longRange <~ ("L", "Long"); @@ -241,6 +242,8 @@ Only one full-auto attack may be made with each Complex Action. This attack may val magazine = keys.magazine <~ "Magazine"; val size = keys.size <~ "Size"; val ammoType = keys.ammoType <~ "Ammo Type"; + val damageArea = keys.damageArea <~ "Effect Area"; + val uniformRadius = keys.uniformRadius <~ "Uniform Radius"; val damageInflicts = keys.damageInflicts <~ "Inflicts"; val damageValue = keys.damageValue <~ ("DV", "Damage Value"); val concentrateFire = keys.concentrateFire <~ "Concentrate Fire"; @@ -388,6 +391,19 @@ Only one full-auto attack may be made with each Complex Action. This attack may case DamageType.Untyped => "" }; + val dmgArea = keys.dmgArea <~ { + case DamageArea.Point => "Point" + case DamageArea.Blast => "Blast" + case DamageArea.UniformBlast => "Uniform Blast" + case DamageArea.Cone => "Cone" + }; + val dmgAreaShort = keys.dmgAreaShort <~ { + case DamageArea.Point => "•" + case DamageArea.Blast => "⦾" + case DamageArea.UniformBlast => "⦿" + case DamageArea.Cone => "⋁" + }; + val psiType = keys.psiType <~ { case PsiType.Active => "Active" case PsiType.Passive => "Passive" diff --git a/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/GearTab.scala b/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/GearTab.scala index 54a0b4c..3efdf32 100644 --- a/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/GearTab.scala +++ b/sheet/jvm/src/main/scala/com/lkroll/ep/sheet/GearTab.scala @@ -66,7 +66,30 @@ object GearTab extends FieldGroup { sup(span(EPStyle.`cat-tag-field`, name := f.name, SheetI18NAttrs.datai18nDynamic)) } - case class RangeGroup(rangeLabel: LabelsI18N, rangeStart: FieldLike[_], rangeEnd: FieldLike[_], unit: String) extends FieldGroup { + // val daRenderer: GroupRenderer.FieldDualRenderer = (f, mode) => { + // span(sty.`area-field`, name := f.name, SheetI18NAttrs.datai18nDynamic) + // } + // + // val ubaRenderer: GroupRenderer.FieldDualRenderer = (f, mode) => { + // span(sty.`uniform-radius-field`, raw("("), span(name := f.name), raw("m)")) + // } + + case class AreaGroup(area: FieldLike[_], areaShort: FieldLike[_], radius: FieldLike[_]) extends FieldGroup { + val fieldRenderer = CoreTabRenderer.fieldRenderers; + + override def render(mode: RenderMode = RenderMode.Normal): Tag = { + span( + span(sty.`area-field`, name := areaShort.name, SheetI18NAttrs.datai18nDynamic), + input(sty.`area-field`, tpe := "hidden", name := area.name), + span(sty.`uniform-radius-field`, raw("("), span(name := radius.name), raw("m)"))) + } + override def renderer(): GroupRenderer = null; + override def members(): Seq[SheetElement] = null; + } + + def area(area: FieldLike[_], areaShort: FieldLike[_], radius: FieldLike[_]) = AreaGroup(area, areaShort, radius); + + case class RangeGroup(rangeLabel: LabelsI18N, rangeStart: FieldLike[_], rangeEnd: FieldLike[_], unit: FieldLike[_]) extends FieldGroup { val fieldRenderer = CoreTabRenderer.fieldRenderers; @@ -77,13 +100,13 @@ object GearTab extends FieldGroup { fieldRenderer(rangeStart, mode), span(raw(" - ")), fieldRenderer(rangeEnd, mode), - span(raw(unit))) + span(name := unit.name)) } override def renderer(): GroupRenderer = null; override def members(): Seq[SheetElement] = null; } - def range(rangeLabel: LabelsI18N, rangeStart: FieldLike[_], rangeEnd: FieldLike[_], unit: String): RangeGroup = RangeGroup(rangeLabel, rangeStart, rangeEnd, unit); + def range(rangeLabel: LabelsI18N, rangeStart: FieldLike[_], rangeEnd: FieldLike[_], unit: FieldLike[_]): RangeGroup = RangeGroup(rangeLabel, rangeStart, rangeEnd, unit); def checklabel(label: LabelsI18N, innerSep: Option[String] = None): GroupRenderer.FieldSingleRenderer = (f) => f match { case ff: FlagField => { @@ -315,6 +338,8 @@ object GearTab extends FieldGroup { span(raw(" } ")), span(raw(" ~ ")), rangedDamageRollQuery, + span(raw(" ")), + area(char.rangedWeapons.damageArea, char.rangedWeapons.damageAreaShort, char.rangedWeapons.uniformBlastArea), rangedDamageRoll.hidden, rangedDamageRollExcellent30.hidden, rangedDamageRollExcellent60.hidden, @@ -352,6 +377,8 @@ object GearTab extends FieldGroup { char.rangedWeapons.damageBonus, span(" / "), (t.ap -> char.rangedWeapons.armourPenetration), + (t.damageArea -> char.rangedWeapons.damageArea), + (t.uniformRadius -> char.rangedWeapons.uniformBlastArea), flexFill), tightfrow( span(EPStyle.lineLabel, t.firingModes), @@ -362,11 +389,13 @@ object GearTab extends FieldGroup { flexFill), tightfrow( span(EPStyle.lineLabel, t.weaponRanges), + char.rangedWeapons.rangeUnitSymbol.hidden, span(raw(" ")), - range(t.shortRange, char.rangedWeapons.shortRangeLower, char.rangedWeapons.shortRangeUpper, "m"), - range(t.mediumRange, char.rangedWeapons.mediumRangeLower, char.rangedWeapons.mediumRangeUpper, "m"), - range(t.longRange, char.rangedWeapons.longRangeLower, char.rangedWeapons.longRangeUpper, "m"), - range(t.extremeRange, char.rangedWeapons.extremeRangeLower, char.rangedWeapons.extremeRangeUpper, "m"), + (t.thrown -> char.rangedWeapons.thrown), + range(t.shortRange, char.rangedWeapons.shortRangeLower, char.rangedWeapons.shortRangeUpperInput, char.rangedWeapons.rangeUnitSymbol), + range(t.mediumRange, char.rangedWeapons.mediumRangeLower, char.rangedWeapons.mediumRangeUpperInput, char.rangedWeapons.rangeUnitSymbol), + range(t.longRange, char.rangedWeapons.longRangeLower, char.rangedWeapons.longRangeUpperInput, char.rangedWeapons.rangeUnitSymbol), + range(t.extremeRange, char.rangedWeapons.extremeRangeLower, char.rangedWeapons.extremeRangeUpperInput, char.rangedWeapons.rangeUnitSymbol), flexFill), tightfrow( span(EPStyle.lineLabel, t.magazine),