Adds abilities to monster cards.
Adds CommonMark dependency and CommonMarkHelper to render it to html.
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user