Quellcode durchsuchen

Merge branch 'eval_level_spinfo'

Sam Jaffe vor 8 Jahren
Ursprung
Commit
e62f2a5b6c

+ 7 - 2
resources/Potato.json

@@ -23,8 +23,13 @@
           },
           "1":{
             "spellsPerDay":2,
-            "spellsPrepared":[],
-            "spellsPreparedPreviously":[]
+            "spellsPrepared":[
+              "Cure Light Wounds"
+            ],
+            "spellsPreparedPreviously":[
+              "Cure Light Wounds",
+              "Cure Light Wounds"
+            ]
           },
           "2":{
             "spellsPerDay":1,

+ 2 - 1
resources/classes/Cleric.json

@@ -51,7 +51,8 @@
       [6, 5, 5, 5, 5, 5, 4, 4, 4, 4]
     ],
     "spellList":[
-      ["Create Water"]
+      ["Create Water"],
+      ["Cure Light Wounds"]
     ]
   }
 }

+ 34 - 2
resources/spells/default.json

@@ -12,12 +12,44 @@
     "components":["V","S"],
     "castingTime":"Standard",
     "range":"Medium",
-    "effect":"Up to 2 gallons of water/level",
+    "effect":{
+      "format":"Up to {perlevel} gallons of water/level",
+      "resolved":"Up to {atlevel} gallons of water",
+      "per":2
+    },
     "duration":"Instantaneous",
     "savingThrow":"None",
-    "spellResistence":false,
+    "allowsSpellResistance":false,
     "description":"This spell generates wholesome, drinkable water, just like clean rain water. Water can be created in an area as small as will actually contain the liquid, or in an area three times as large—possibly creating a downpour or filling many small receptacles. Note: Conjuration spells can't create substances or objects within a creature. Water weighs about 8 pounds per gallon. One cubic foot of water contains roughly 8 gallons and weighs about 60 pounds."
   },
+  "Cure Light Wounds":{
+    "name":"Cure Light Wounds",
+    "classToLevel":{
+      "Bard":1,
+      "Cleric":1,
+      "Druid":1,
+      "Healing":1,
+      "Paladin":1,
+      "Ranger":2
+    },
+    "school":"Conjuration",
+    "subSchool":"Healing",
+    "components":["V", "S"],
+    "keywords":[],
+    "castingTime":"Standard",
+    "range":"Touch",
+    "target":"Creature Touched",
+    "effect":{
+      "format":"1d8 + {perlevel} per level (maximum +{upto})",
+      "resolved":"1d8 + {atlevel}",
+      "per":1,
+      "upto":5
+    },
+    "duration":"Instantaneous",
+    "savingThrow":"Will half (harmless)",
+    "allowsSpellResistance":true,
+    "description":"When laying your hand upon a living creature, you channel positive energy that cures 1d8 points of damage +1 point per caster level (maximum +5). Since undead are powered by negative energy, this spell deals damage to them instead of curing their wounds. An undead creature can apply spell resistance, and can attempt a Will save to take half damage."
+  },
   "Know Direction":{
     "name":"Know Direction",
     "classToLevel":{

+ 9 - 2
src/org/leumasjaffe/charsheet/config/Constants.java

@@ -9,6 +9,13 @@ import lombok.experimental.UtilityClass;
 public final class Constants {
 	static String NO_FLAT_FOOTED = "Keeps Dexterity When Flat-footed";
 	
-	String DISTANCE = "Distance Measurement Unit";
-	static int DISTANCE_MEASUREMENT_FEET = 0x0, DISTANCE_MEASUREMENT_SQUARES = 0x1;
+	String K_DISTANCE = "Distance Measurement Unit";
+	public enum DistanceMeasurement {
+		FEET, SQUARES
+	}
+	
+	String K_DURATION = "Duration Display Unit";
+	public enum DurationMeasurement {
+		NATURAL, ROUNDS
+	}
 }

+ 10 - 3
src/org/leumasjaffe/charsheet/model/magic/DDSpell.java

@@ -2,8 +2,15 @@ package org.leumasjaffe.charsheet.model.magic;
 
 import java.util.EnumSet;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 
+import org.leumasjaffe.charsheet.model.magic.dimension.Area;
+import org.leumasjaffe.charsheet.model.magic.dimension.Duration;
+import org.leumasjaffe.charsheet.model.magic.dimension.Effect;
+import org.leumasjaffe.charsheet.model.magic.dimension.Range;
+import org.leumasjaffe.charsheet.model.magic.dimension.Target;
+
 import com.fasterxml.jackson.annotation.JsonCreator;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 
@@ -64,10 +71,10 @@ public class DDSpell {
 	@NonNull EnumSet<Component> components = EnumSet.noneOf(Component.class);
 	@NonNull DDActionType castingTime;
 	@NonNull Range range;
-	String effect; // TODO
+	Optional<Effect> effect;
 	Area area;
-	String target;
-	@NonNull String duration; // TODO
+	Target target;
+	@NonNull Duration duration;
 	@NonNull String savingThrow; // TODO
 	boolean allowsSpellResistance;
 	@NonNull String description;

+ 19 - 22
src/org/leumasjaffe/charsheet/model/magic/Area.java

@@ -1,7 +1,8 @@
-package org.leumasjaffe.charsheet.model.magic;
+package org.leumasjaffe.charsheet.model.magic.dimension;
+
+import org.leumasjaffe.charsheet.util.StringHelper;
 
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
 import com.fasterxml.jackson.annotation.JsonTypeInfo.Id;
 
 import lombok.AccessLevel;
@@ -9,7 +10,7 @@ import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.experimental.FieldDefaults;
 
-@JsonTypeInfo(use=Id.CLASS, include=As.PROPERTY, property="class")
+@JsonTypeInfo(use=Id.NAME)
 public interface Area {
 	public static enum Emission { BURST, EMANATION, SPREAD, NONE }
 	public static enum Shape { CONE, CYLINDER, LINE, SPHERE }
@@ -20,7 +21,8 @@ public interface Area {
 		int distance;
 
 		public String toString() {
-			return distance + " ft. line from you";
+			final Range.__Pair p = new Range.__Pair(distance);
+			return StringHelper.format("{} {} line from you", p.value[0], p.measure);
 		}
 	}
 
@@ -42,24 +44,20 @@ public interface Area {
 		boolean aroundYou;
 
 		public String toString() {
+			final Range.__Pair p = new Range.__Pair(Math.max(5, radius));
 			// TODO size/level spheres like Otiluke’s Resilient Sphere PH258
-			final StringBuilder str = new StringBuilder();
-			if ( radius == 0 ) { str.append("5-ft.-diameter "); }
-			else { str.append(radius).append("-ft.-radius "); }
+			return StringHelper.format(
+					"{}-{}-{?diameter:radius} {} {?, centered around you:}", 
+					p.value[0], p.measure, radius == 0,
+					getEminationString(), aroundYou);
+		}
 
+		private String getEminationString() {
 			switch (emit) {
-			case NONE:
-				str.append("sphere");
-				break;
-			case EMANATION:
-				str.append("spherical ");
-			default:
-				str.append(emit);
-				break;
+			case NONE: return "sphere";
+			case EMANATION:	return "spherical " + emit;
+			default: return emit.toString();
 			}
-			if ( aroundYou ) { str.append(", centered around you");	}
-
-			return str.toString();
 		}
 	}
 
@@ -71,10 +69,9 @@ public interface Area {
 		
 		public String toString() {
 			// TODO Grow by level cylinder (e.g. Control Winds PH214)
-			final StringBuilder str = new StringBuilder("Cylinder (");
-			str.append(radius).append("-ft. radius, ");
-			str.append(height).append("-ft. high)");
-			return str.toString();
+			final Range.__Pair p = new Range.__Pair(radius, height);
+			return StringHelper.format("Cylinder ({}-{2} radius, {}-{} high)", 
+					p.value[0], p.value[1], p.measure);
 		}
 	}
 }

+ 90 - 0
src/org/leumasjaffe/charsheet/model/magic/dimension/Duration.java

@@ -0,0 +1,90 @@
+package org.leumasjaffe.charsheet.model.magic.dimension;
+
+import static org.leumasjaffe.charsheet.config.Constants.K_DURATION;
+import static org.leumasjaffe.charsheet.config.Constants.DurationMeasurement.*;
+
+import org.leumasjaffe.charsheet.config.Config;
+import org.leumasjaffe.charsheet.util.StringHelper;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+
+public interface Duration {
+	@AllArgsConstructor
+	public static enum Measure {
+		round(1), minute(10), hour(600), day(14400);
+		int rounds;
+	}
+	
+	@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+	public static final class __Pair {
+		int[] value;
+		Measure measure;
+		
+		private __Pair(Measure def, int... times) {
+			value = new int[times.length];
+			final int div;
+			if (Config.get(K_DURATION, NATURAL) == ROUNDS) {
+				div = def.rounds;
+				measure = Measure.round;
+			} else {
+				div = 1;
+				measure = def;
+			}
+			for (int i = 0; i < times.length; ++i) {
+				value[i] = times[i] / div;
+			}
+		}
+	}
+
+	public default String getResolved(int level) { return toString(); }
+
+	@JsonCreator
+	public static Duration create(String name) {
+		return new Basic(name);
+	}
+	
+	@JsonCreator
+	public static Duration create(@JsonProperty("unit") Measure meas, 
+			@JsonProperty("duration") int min, 
+			@JsonProperty("per") int per, 
+			@JsonProperty("step") int step) {
+		return new WithLevelGrowth(meas, min, per, step);
+	}
+	
+	@RequiredArgsConstructor
+	@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+	public static class Basic implements Duration {
+		@NonNull String name;
+		
+		public String toString() {
+			return name;
+		}
+	}
+	
+	@RequiredArgsConstructor
+	@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+	public static class WithLevelGrowth implements Duration {
+		@NonNull Measure measure; 
+		int length, per, step;
+		
+		@Override
+		public String getResolved(int level) {
+			final __Pair p = new __Pair(measure, length + (per * (level / step)));
+			return StringHelper.format("{} {}{0>1?s:}", p.value[0], p.measure);
+		}
+		
+		public String toString() {
+			final __Pair p = new __Pair(measure, length, per);
+			final StringBuilder str = new StringBuilder(StringHelper.format("{} {3}{0>1?s:} + {} {}{1>1?s:}/{?level:{} levels}",
+					p.value[0], p.value[1], p.measure, step));
+			return str.toString();
+		}
+	}
+}

+ 33 - 0
src/org/leumasjaffe/charsheet/model/magic/dimension/Effect.java

@@ -0,0 +1,33 @@
+package org.leumasjaffe.charsheet.model.magic.dimension;
+
+import org.leumasjaffe.charsheet.util.StringHelper;
+import org.leumasjaffe.format.Named;
+
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.FieldDefaults;
+import lombok.experimental.NonFinal;
+
+@RequiredArgsConstructor
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+public class Effect {
+	@NonNull String format;
+	String resolved;
+	int count, per, beyond;
+	@NonFinal @Setter(AccessLevel.PRIVATE) @Getter(AccessLevel.PRIVATE) int step = 1, upto = Integer.MAX_VALUE;
+	
+	public String getResolved(int level) {
+		final int result = count + per * ((Math.min(level, upto)-beyond) / step);
+		return resolved == null ? toString() :
+			StringHelper.format(resolved, new Named("atlevel", result));
+	}
+	
+	public String toString() {
+		return StringHelper.format(format, new Named("count", count), 
+				new Named("per", per), new Named("step", step),
+				new Named("beyond", beyond), new Named("upto", upto));
+	}
+}

+ 9 - 34
src/org/leumasjaffe/charsheet/model/magic/Range.java

@@ -1,6 +1,4 @@
-package org.leumasjaffe.charsheet.model.magic;
-
-import java.util.Map;
+package org.leumasjaffe.charsheet.model.magic.dimension;
 
 import org.leumasjaffe.charsheet.config.Config;
 import org.leumasjaffe.charsheet.util.StringHelper;
@@ -13,7 +11,8 @@ import lombok.NonNull;
 import lombok.RequiredArgsConstructor;
 import lombok.experimental.FieldDefaults;
 
-import static org.leumasjaffe.charsheet.config.Constants.*;
+import static org.leumasjaffe.charsheet.config.Constants.K_DISTANCE;
+import static org.leumasjaffe.charsheet.config.Constants.DistanceMeasurement.*;
 
 public interface Range {	
 	@FieldDefaults(level=AccessLevel.PUBLIC, makeFinal=true)
@@ -21,10 +20,10 @@ public interface Range {
 		int[] value;
 		String measure;
 		
-		private __Pair(int... feet) {
+		public __Pair(int... feet) {
 			value = new int[feet.length];
 			final int div;
-			if (Config.<Integer>get(DISTANCE, DISTANCE_MEASUREMENT_FEET).equals(DISTANCE_MEASUREMENT_SQUARES)) {
+			if (Config.get(K_DISTANCE, FEET) == SQUARES) {
 				div = 5;
 				measure = "squares";
 			} else {
@@ -59,19 +58,7 @@ public interface Range {
 		}
 		throw new IllegalArgumentException("Unrecognized Range constant: " + name);
 	}
-	
-	@JsonCreator
-	public static Range create(final Map<String, Object> data) {
-		final String type = (String) data.get("type");
-		switch (type.toLowerCase()) {
-		case "basic": return new Basic((String) data.get("name"));
-		case "withdistance": return new WithDistance((String) data.get("name"), (Integer) data.get("range"));
-		case "withlevelgrowth": return new WithLevelGrowth((String) data.get("name"), (Integer) data.get("range"), 
-				(Integer) data.get("per"), (Integer) data.get("step"));
-		}
-		throw new IllegalArgumentException("Unrecognized Range type: " + type);
-	}
-	
+		
 	@RequiredArgsConstructor
 	@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 	public static class Basic implements Range {
@@ -82,18 +69,6 @@ public interface Range {
 		}
 	}
 	
-	@RequiredArgsConstructor
-	@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
-	public static class WithDistance implements Range {
-		@NonNull @Getter String name;
-		int range;
-		
-		public String toString() {
-			final __Pair p = new __Pair(range);
-			return StringHelper.format(" ({} {})", p.value[0], p.measure);
-		}
-	}
-	
 	@RequiredArgsConstructor
 	@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
 	public static class WithLevelGrowth implements Range {
@@ -102,14 +77,14 @@ public interface Range {
 		
 		@Override
 		public String getResolved(int level) {
-			final __Pair p = new __Pair(range + (per * (level / step)));
+			final __Pair p = new __Pair(range + (step == 0 ? 0 : (per * (level / step))));
 			return StringHelper.format("{} {}", p.value[0], p.measure);
 		}
 		
 		public String toString() {
 			final __Pair p = new __Pair(range, per);
-			final StringBuilder str = new StringBuilder(StringHelper.format("{} ({} {3} + {} {}/{?level:{} levels})",
-					name, p.value[0], p.value[1], p.measure, step == 1, step));
+			final StringBuilder str = new StringBuilder(StringHelper.format("{} ({} {3}{4>0? + {} {}/{=1?level:{4} levels}:})",
+					name, p.value[0], p.value[1], p.measure, step));
 			return str.toString();
 		}
 	}

+ 39 - 0
src/org/leumasjaffe/charsheet/model/magic/dimension/Target.java

@@ -0,0 +1,39 @@
+package org.leumasjaffe.charsheet.model.magic.dimension;
+
+import org.leumasjaffe.charsheet.util.StringHelper;
+import org.leumasjaffe.format.Named;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+import lombok.AccessLevel;
+import lombok.NonNull;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+import lombok.experimental.NonFinal;
+
+@RequiredArgsConstructor
+@FieldDefaults(level=AccessLevel.PRIVATE, makeFinal=true)
+public class Target {
+	@NonNull String format;
+	String resolved;
+	int count, perlevel;
+	@NonFinal int step = 1;
+	
+	@JsonCreator
+	public Target(final String descr) {
+		this.format = descr;
+		this.resolved = null;
+		this.count = this.perlevel = 0;
+	}
+	
+	public String getResolved(int level) {
+		final int result = count + perlevel * (level / step);
+		return resolved == null ? toString() :
+			StringHelper.format(resolved, new Named("atlevel", result));
+	}
+	
+	public String toString() {
+		return StringHelper.format(format, new Named("count", count), 
+				new Named("perlevel", perlevel), new Named("step", step));
+	}
+}

+ 14 - 2
src/org/leumasjaffe/charsheet/view/magic/SpellInfoPanel.java

@@ -4,7 +4,9 @@ import javax.swing.JPanel;
 import java.awt.GridBagLayout;
 import java.awt.GridBagConstraints;
 import java.awt.Insets;
+import java.util.Optional;
 import java.util.Set;
+import java.util.function.Function;
 
 import javax.swing.JTextArea;
 
@@ -228,7 +230,8 @@ class SpellInfoPanel extends JPanel {
 		gbc_lblEffect.gridy = 4;
 		panel.add(lblEffect, gbc_lblEffect);
 		
-		JTextField effect = new JTextField(asString(spell.getEffect()));
+		JTextField effect = new JTextField(asString(spell.getEffect(), e -> e.getResolved(dclass.getLevel())));
+		effect.setToolTipText(asString(spell.getEffect()));
 		effect.setEditable(false);
 		effect.setColumns(10);
 		GridBagConstraints gbc_effect = new GridBagConstraints();
@@ -266,7 +269,8 @@ class SpellInfoPanel extends JPanel {
 		gbc_lblDuration.gridy = 5;
 		panel.add(lblDuration, gbc_lblDuration);
 		
-		JTextField duration = new JTextField(spell.getDuration());
+		JTextField duration = new JTextField(spell.getDuration().getResolved(dclass.getLevel()));
+		duration.setToolTipText(asString(spell.getDuration()));
 		duration.setEditable(false);
 		duration.setColumns(10);
 		GridBagConstraints gbc_duration = new GridBagConstraints();
@@ -330,6 +334,14 @@ class SpellInfoPanel extends JPanel {
 		description.setLineWrap(true);
 	}
 
+	private <T> String asString(Optional<T> obj, Function<? super T, String> ts) {
+		return (!obj.isPresent()) ? "" : ts.apply(obj.get());
+	}
+	
+	private <T> String asString(Optional<T> obj) {
+		return (!obj.isPresent()) ? "" : obj.get().toString();
+	}
+
 	private String asString(Object obj) {
 		return obj == null ? "" : obj.toString();
 	}

+ 23 - 1
src/org/leumasjaffe/format/StringFormatter.java

@@ -2,6 +2,8 @@ package org.leumasjaffe.format;
 
 import java.util.HashMap;
 import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import lombok.AccessLevel;
 import lombok.experimental.FieldDefaults;
@@ -83,7 +85,7 @@ public class StringFormatter {
 			// If token starts with '%', make it a c-format expression
 			if (token.indexOf('?') != -1) {
 				final int b1 = fmt.indexOf('?', pos)+1;
-				boolean _if = (Boolean) getToken(token.substring(0, b1-pos-1));
+				boolean _if = getConditionToken(token, b1-pos-1);
 				FmtStateMachine then = new FmtStateMachine(fmt.substring(b1), currentIdx, args, named, ':').formatMain();
 				FmtStateMachine els = new FmtStateMachine(fmt.substring(then.endPos+b1), then.currentIdx, args, named, '}').formatMain();
 				currentIdx = els.currentIdx;
@@ -95,6 +97,26 @@ public class StringFormatter {
 			return epos;
 		}
 
+		private boolean getConditionToken(final String token, final int end) {
+			final String cond = token.substring(0, end);
+			Matcher matcher = Pattern.compile("^(\\d*|[a-zA-Z_]+)(>=?|<=?|!=|==?)(\\d+)$").matcher(cond);
+			if (matcher.matches()) {
+				final int cmp = ((Integer) getToken(matcher.group(1))).compareTo(Integer.valueOf(matcher.group(3)));
+				switch (matcher.group(2)) {
+				case "=" : return cmp == 0;
+				case "==": return cmp == 0;
+				case "!=": return cmp != 0;
+				case "<" : return cmp <  0;
+				case "<=": return cmp <= 0;
+				case ">" : return cmp >  0;
+				case ">=": return cmp >= 0;
+				default: throw new IllegalStateException();
+				}
+			} else {
+				return (Boolean) getToken(cond);
+			}
+		}
+
 		private Object getToken(final String token) {
 			if (token == null || token.isEmpty()) {
 				return args[currentIdx++];

+ 5 - 0
test/org/leumasjaffe/format/StringFormatterTest.java

@@ -26,5 +26,10 @@ public class StringFormatterTest {
 	public void testFormatNumberedCondition() {
 		assertEquals("0 word + 1 word/2 levels", StringHelper.format("{0} {2} + {1} {2}/{3?level:{4} levels}", 0, 1, "word", false, 2));
 	}
+	
+	@Test
+	public void testFormatIntegerCompareCondition() {
+		assertEquals("true", StringHelper.format("{>1?true:false}", 2));
+	}
 
 }