Adds abilities to monster cards.

Adds CommonMark dependency and CommonMarkHelper to render it to html.
This commit is contained in:
2020-09-01 23:42:09 -07:00
parent 4bb4bcd230
commit db670f3340
7 changed files with 200 additions and 7 deletions

View File

@@ -0,0 +1,38 @@
package com.majinnaibu.monstercards.helpers;
import org.commonmark.node.Document;
import org.commonmark.node.Node;
import org.commonmark.node.Paragraph;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.NodeRenderer;
import org.commonmark.renderer.html.HtmlNodeRendererContext;
import org.commonmark.renderer.html.HtmlNodeRendererFactory;
import org.commonmark.renderer.html.HtmlRenderer;
public final class CommonMarkHelper {
private static final class MyNodeRendererFactory implements HtmlNodeRendererFactory {
@Override
public NodeRenderer create(HtmlNodeRendererContext context) {
return null;
}
}
public static String toHtml(String rawCommonMark) {
Parser parser = Parser.builder().build();
Node document = parser.parse(rawCommonMark);
Node parent1 = document.getFirstChild();
Node parent2 = document.getLastChild();
if (parent1 == parent2 && parent1 instanceof Paragraph) {
document = new Document();
Node child = parent1.getFirstChild();
while(child != null) {
Node nextChild = child.getNext();
document.appendChild(child);
child = nextChild;//child.getNext();
}
}
HtmlRenderer renderer = HtmlRenderer.builder().build();
return renderer.render(document);
}
}

View File

@@ -0,0 +1,25 @@
package com.majinnaibu.monstercards.models;
public class Ability {
public Ability(String name, String description) {
mName = name;
mDescription = description;
}
private String mName;
public String getName() {
return mName;
}
public void setName(String name) {
mName = name;
}
private String mDescription;
public String getDescription() {
return mDescription;
}
public void setDescription(String description) {
mDescription = description;
}
}

View File

@@ -13,11 +13,12 @@ import java.util.Set;
public class Monster {
public Monster() {
mAbilities = new ArrayList<>();
mConditionImmunities = new HashSet<>();
mDamageTypes = new HashSet<>();
mLanguages = new HashSet<>();
mSavingThrows = new HashSet<>();
mSkills = new HashSet<>();
mDamageTypes = new HashSet<>();
mConditionImmunities = new HashSet<>();
mLanguages = new HashSet<>();
}
private String mName;
@@ -917,4 +918,65 @@ public class Monster {
return getCustomChallengeRating();
}
}
private ArrayList<Ability> mAbilities;
public List<Ability> getAbilities() {
return mAbilities;
}
public void addAbility(Ability ability) {
mAbilities.add(ability);
}
public void removeAbility(Ability ability) {
mAbilities.remove(ability);
}
public void clearAbilities() {
mAbilities.clear();
}
public List<String> getAbilityDescriptions() {
ArrayList<String> abilities = new ArrayList<>();
for (Ability ability : getAbilities()) {
abilities.add(getPlaceholderReplacedText(String.format("__%s__ %s", ability.getName(), ability.getDescription())));
}
return abilities;
}
public String getPlaceholderReplacedText(String rawText) {
return rawText
.replaceAll("\\[STR SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("strength")))
.replaceAll("\\[STR ATK]", String.format(Locale.US, "%+d", getAttackBonus("strength")))
.replaceAll("\\[DEX SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("dexterity")))
.replaceAll("\\[DEX ATK]", String.format(Locale.US, "%+d", getAttackBonus("dexterity")))
.replaceAll("\\[CON SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("constitution")))
.replaceAll("\\[CON ATK]", String.format(Locale.US, "%+d", getAttackBonus("constitution")))
.replaceAll("\\[INT SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("intelligence")))
.replaceAll("\\[INT ATK]", String.format(Locale.US, "%+d", getAttackBonus("intelligence")))
.replaceAll("\\[WIS SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("wisdom")))
.replaceAll("\\[WIS ATK]", String.format(Locale.US, "%+d", getAttackBonus("wisdom")))
.replaceAll("\\[CHA SAVE]", String.format(Locale.US, "%+d", getSpellSaveDC("charisma")))
.replaceAll("\\[CHA ATK]", String.format(Locale.US, "%+d", getAttackBonus("charisma")));
}
public int getSavingThrow(String name) {
Set<SavingThrow> sts = getSavingThrows();
for(SavingThrow st : sts) {
if (name.equals(st.getName())) {
return getAbilityModifier(name) + getProficiencyBonus();
}
}
return getAbilityModifier(name);
}
public String getWisdomSave() {
return String.format(Locale.US, "%+d", getSavingThrow("wis"));
}
public int getSpellSaveDC(String abilityScoreName) {
return 8 + getProficiencyBonus() + getAbilityModifier(abilityScoreName);
}
public int getAttackBonus(String abilityScoreName) {
return getProficiencyBonus() + getAbilityModifier(abilityScoreName);
}
}

View File

@@ -1,10 +1,16 @@
package com.majinnaibu.monstercards.ui.monster;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.Html;
import android.text.Spanned;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -14,13 +20,17 @@ import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import com.majinnaibu.monstercards.R;
import com.majinnaibu.monstercards.helpers.CommonMarkHelper;
import com.majinnaibu.monstercards.helpers.StringHelper;
import com.majinnaibu.monstercards.models.Ability;
import com.majinnaibu.monstercards.models.DamageType;
import com.majinnaibu.monstercards.models.Language;
import com.majinnaibu.monstercards.models.Monster;
import com.majinnaibu.monstercards.models.SavingThrow;
import com.majinnaibu.monstercards.models.Skill;
import java.util.List;
@SuppressWarnings("FieldCanBeLocal")
public class MonsterFragment extends Fragment {
@@ -99,11 +109,14 @@ public class MonsterFragment extends Fragment {
monster.addLanguage(new Language("French", true));
monster.addLanguage(new Language("Mermataur", false));
monster.addLanguage(new Language("Goldfish", false));
// Challenge Rating
monster.setChallengeRating("*");
monster.setCustomChallengeRating("Infinite (0XP)");
monster.setCustomProficiencyBonus(4);
// Abilities
monster.addAbility(new Ability("Spellcasting", "The acolyte is a 1st-level spellcaster. Its spellcasting ability is Wisdom (spell save DC [WIS SAVE], [WIS ATK] to hit with spell attacks). The acolyte has following cleric spells prepared:\n\n\n> Cantrips (at will): _light, sacred flame, thaumaturgy_\n> 1st level (3 slots): _bless, cure wounds, sanctuary_"));
monster.addAbility(new Ability("Amphibious", "The dragon can breathe air and water."));
monster.addAbility(new Ability("Legendary Resistance (3/Day)", "If the dragon fails a saving throw, it can choose to succeed instead."));
// END remove block
monsterViewModel = new ViewModelProvider(this).get(MonsterViewModel.class);
View root = inflater.inflate(R.layout.fragment_monster, container, false);
@@ -309,6 +322,35 @@ public class MonsterFragment extends Fragment {
}
});
final LinearLayout monsterAbilities = root.findViewById(R.id.abilities);
monsterViewModel.getAbilities().observe(getViewLifecycleOwner(), new Observer<List<String>>() {
@Override
public void onChanged(List<String> abilities) {
Context context = getContext();
DisplayMetrics displayMetrics = null;
if (context != null) {
Resources resources = context.getResources();
if (resources != null) {
displayMetrics = resources.getDisplayMetrics();
}
}
monsterAbilities.removeAllViews();
if (abilities != null) {
for (String ability : abilities) {
TextView tvAbility = new TextView(context);
// TODO: Handle multiline block quotes specially so they stay multiline.
// TODO: Replace QuoteSpans in the result of fromHtml with something like this https://stackoverflow.com/questions/7717567/how-to-style-blockquotes-in-android-textviews to make them indent as expected
Spanned spannedText = Html.fromHtml(CommonMarkHelper.toHtml(ability));
tvAbility.setText(spannedText);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.topMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, displayMetrics);
tvAbility.setLayoutParams(layoutParams);
monsterAbilities.addView(tvAbility);
}
}
}
});
return root;
}
}

View File

@@ -6,6 +6,9 @@ import androidx.lifecycle.ViewModel;
import com.majinnaibu.monstercards.models.Monster;
import java.util.ArrayList;
import java.util.List;
public class MonsterViewModel extends ViewModel {
public MonsterViewModel() {
@@ -50,6 +53,8 @@ public class MonsterViewModel extends ViewModel {
mLanguages.setValue("");
mChallenge = new MutableLiveData<>();
mChallenge.setValue("");
mAbilities = new MutableLiveData<>();
mAbilities.setValue(new ArrayList<String>());
}
private MutableLiveData<String> mName;
@@ -132,6 +137,10 @@ public class MonsterViewModel extends ViewModel {
public LiveData<String> getChallenge() {
return mChallenge;
}
private MutableLiveData<List<String>> mAbilities;
public LiveData<List<String>> getAbilities() {
return mAbilities;
}
private Monster mMonster;
public void setMonster(Monster monster) {
@@ -156,5 +165,6 @@ public class MonsterViewModel extends ViewModel {
mSenses.setValue(monster.getSensesDescription());
mLanguages.setValue(mMonster.getLanguagesDescription());
mChallenge.setValue(mMonster.getChallengeRatingDescription());
mAbilities.setValue(mMonster.getAbilityDescriptions());
}
}