Skip to content

Commit

Permalink
Merge pull request #118 from scionaltera/character-attributes
Browse files Browse the repository at this point in the history
Character Stats
  • Loading branch information
scionaltera authored Jun 5, 2023
2 parents b95b656 + 0a6bbe7 commit 6d931ab
Show file tree
Hide file tree
Showing 17 changed files with 267 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@

import static com.agonyforge.mud.core.config.SessionConfiguration.MUD_CHARACTER;

public abstract class AbstractQuestion extends com.agonyforge.mud.core.cli.AbstractQuestion {
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractQuestion.class);
public abstract class BaseQuestion extends com.agonyforge.mud.core.cli.AbstractQuestion {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseQuestion.class);

private final ApplicationContext applicationContext;
private final RepositoryBundle repositoryBundle;

public AbstractQuestion(ApplicationContext applicationContext,
RepositoryBundle repositoryBundle) {
public BaseQuestion(ApplicationContext applicationContext,
RepositoryBundle repositoryBundle) {
super();
this.applicationContext = applicationContext;
this.repositoryBundle = repositoryBundle;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import java.util.Optional;

@Component
public class CharacterDeleteQuestion extends AbstractQuestion {
public class CharacterDeleteQuestion extends BaseQuestion {
@Autowired
public CharacterDeleteQuestion(ApplicationContext applicationContext,
RepositoryBundle repositoryBundle) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import static com.agonyforge.mud.core.config.SessionConfiguration.MUD_CHARACTER;

@Component
public class CharacterMenuQuestion extends AbstractQuestion {
public class CharacterMenuQuestion extends BaseQuestion {
private final MenuPane menuPane = new MenuPane();

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import static com.agonyforge.mud.core.config.SessionConfiguration.MUD_CHARACTER;

@Component
public class CharacterNameQuestion extends AbstractQuestion {
public class CharacterNameQuestion extends BaseQuestion {
private static final Logger LOGGER = LoggerFactory.getLogger(CharacterNameQuestion.class);

@Autowired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.Optional;

@Component
public class CharacterPronounQuestion extends AbstractQuestion {
public class CharacterPronounQuestion extends BaseQuestion {
private static final Logger LOGGER = LoggerFactory.getLogger(CharacterPronounQuestion.class);

private final MenuPane menuPane = new MenuPane();
Expand Down Expand Up @@ -68,7 +68,7 @@ public Response answer(WebSocketContext webSocketContext, Input input) {
getRepositoryBundle().getCharacterRepository().save(ch);
}

nextQuestion = "characterMenuQuestion";
nextQuestion = "characterStatQuestion";
}

Question next = getQuestion(nextQuestion);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.agonyforge.mud.demo.cli;

import com.agonyforge.mud.core.cli.Color;
import com.agonyforge.mud.core.cli.Question;
import com.agonyforge.mud.core.cli.Response;
import com.agonyforge.mud.core.web.model.Input;
import com.agonyforge.mud.core.web.model.Output;
import com.agonyforge.mud.core.web.model.WebSocketContext;
import com.agonyforge.mud.demo.cli.menu.MenuItem;
import com.agonyforge.mud.demo.cli.menu.MenuPane;
import com.agonyforge.mud.demo.cli.menu.MenuPrompt;
import com.agonyforge.mud.demo.cli.menu.MenuTitle;
import com.agonyforge.mud.models.dynamodb.constant.Stat;
import com.agonyforge.mud.models.dynamodb.impl.MudCharacter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Locale;

@Component
public class CharacterStatQuestion extends BaseQuestion {
private static final Logger LOGGER = LoggerFactory.getLogger(CharacterStatQuestion.class);
private static final int STARTING_STATS = 6;

private final MenuPane menuPane = new MenuPane();

public CharacterStatQuestion(ApplicationContext applicationContext,
RepositoryBundle repositoryBundle) {
super(applicationContext, repositoryBundle);

menuPane.setTitle(new MenuTitle("Allocate Stat Points"));
menuPane.setPrompt(new MenuPrompt());
}

@Override
public Output prompt(WebSocketContext wsContext) {
Output output = new Output();
MudCharacter ch = getCharacter(wsContext, output).orElseThrow();

populateMenuItems(ch);

return menuPane.render(Color.WHITE, Color.BLACK);
}

@Override
public Response answer(WebSocketContext webSocketContext, Input input) {
String nextQuestion = "characterStatQuestion";
Output output = new Output();
MudCharacter ch = getCharacter(webSocketContext, output).orElseThrow();
String choice = input.getInput().toUpperCase(Locale.ENGLISH);
int totalPoints = computeStatPoints(ch);

if (choice.contains("+")) {
if (totalPoints >= STARTING_STATS) {
output.append("[red]You don't have any more points to allocate!");
} else {
try {
int i = Integer.parseInt(choice.substring(0, choice.length() - 1)) - 1;
ch.addStat(Stat.values()[i], 1);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
output.append("[red][red]Oops! Try a number with a plus or minus!");
}
}
} else if (choice.contains("-")) {
if (totalPoints <= 0) {
output.append("[red]You haven't assigned any of your points yet!");
} else {
try {
int i = Integer.parseInt(choice.substring(0, choice.length() - 1)) - 1;
ch.addStat(Stat.values()[i], -1);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
output.append("[red][red]Oops! Try a number with a plus or minus!");
}
}
} else {
if (choice.equals("S")) {
if (totalPoints == STARTING_STATS) {
output.append("[green]Character stats saved!");
nextQuestion = "characterMenuQuestion";
} else {
output.append("[red]Please allocate exactly 6 points for your stats.");
}
} else {
output.append("[red]Oops! Try a number with a plus or minus!");
}
}

getRepositoryBundle().getCharacterRepository().save(ch);

Question next = getQuestion(nextQuestion);

return new Response(next, output);
}

private int computeStatPoints(MudCharacter ch) {
return Arrays.stream(Stat.values())
.map(ch::getStat)
.reduce(0, Integer::sum);
}

private void populateMenuItems(MudCharacter ch) {
menuPane.getItems().clear();

int points = STARTING_STATS - computeStatPoints(ch);

menuPane.getItems().add(new MenuItem(" ", "[default]Enter the menu number and a plus (+) or minus (-) to add or subtract from a stat."));
menuPane.getItems().add(new MenuItem(" ", "[default]For example, '3+' to raise CON or '6-' to lower CHA."));
menuPane.getItems().add(new MenuItem(" ", String.format("[default]Please allocate [white]%d more points [default]for your stats.", points)));

Arrays.stream(Stat.values())
.forEachOrdered(stat -> menuPane.getItems().add(new MenuItem((stat.ordinal() + 1) + "[+/-]", String.format("%15s (%d)", stat.getName(), ch.getStat(stat)))));

menuPane.getItems().add(new MenuItem("S", "Save"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.agonyforge.mud.core.web.model.Output;
import com.agonyforge.mud.core.web.model.WebSocketContext;
import com.agonyforge.mud.demo.cli.command.LookCommand;
import com.agonyforge.mud.models.dynamodb.constant.Stat;
import com.agonyforge.mud.models.dynamodb.impl.MudCharacter;
import com.agonyforge.mud.models.dynamodb.impl.MudRoom;
import com.agonyforge.mud.models.dynamodb.service.CommService;
Expand All @@ -15,10 +16,11 @@
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Optional;

@Component
public class CharacterViewQuestion extends AbstractQuestion {
public class CharacterViewQuestion extends BaseQuestion {
static final Long START_ROOM = 100L;

private static final Logger LOGGER = LoggerFactory.getLogger(CharacterViewQuestion.class);
Expand All @@ -44,6 +46,11 @@ public Output prompt(WebSocketContext wsContext) {
output.append("[dcyan]Character Sheet");
output.append("[default]Name: [cyan]%s", ch.getName());
output.append("[default]Pronouns: [cyan]%s/%s", ch.getPronoun().getSubject(), ch.getPronoun().getObject());

Arrays.stream(Stat.values())
.forEachOrdered(stat -> output.append("[default]%s: [cyan]%d", stat.getAbbreviation(), ch.getStat(stat)));

output.append("[default]DEF: [cyan]%d", ch.getDefense());
output.append("");
output.append("[green]P[black]) Play as this character");
output.append("[red]D[black]) Delete this character");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import java.util.stream.Collectors;

@Component
public class CommandQuestion extends AbstractQuestion {
public class CommandQuestion extends BaseQuestion {
private static final Logger LOGGER = LoggerFactory.getLogger(CommandQuestion.class);

private final ApplicationContext applicationContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import com.agonyforge.mud.core.web.model.Output;
import com.agonyforge.mud.core.web.model.WebSocketContext;
import com.agonyforge.mud.demo.cli.RepositoryBundle;
import com.agonyforge.mud.models.dynamodb.constant.Stat;
import com.agonyforge.mud.models.dynamodb.impl.MudCharacter;
import com.agonyforge.mud.models.dynamodb.service.CommService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

@Component
Expand All @@ -26,6 +28,11 @@ public Question execute(Question question, WebSocketContext webSocketContext, Li
output.append("[default]Your name is [white]%s[default].", ch.getName());
output.append("[default]Your pronouns are [white]%s/%s[default].", ch.getPronoun().getSubject(), ch.getPronoun().getObject());

Arrays.stream(Stat.values())
.forEachOrdered(stat -> output.append("[default]%s: [white]%s", stat.getAbbreviation(), ch.getStat(stat)));

output.append("[default]DEF: [white]%s", ch.getDefense());

return question;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import com.agonyforge.mud.core.web.model.Output;

public class MenuPane extends AbstractMenuPane {
@Override
public Output render(Color... colors) {
Output menu = new Output();

public Output render(Output menu, Color... colors) {
if (getTitle() != null) {
menu.append(getTitle().render(colors));
}
Expand All @@ -21,4 +18,11 @@ public Output render(Color... colors) {

return menu;
}

@Override
public Output render(Color... colors) {
Output menu = new Output();

return render(menu, colors);
}
}
3 changes: 3 additions & 0 deletions agonyforge-mud-demo/src/main/resources/greeting.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@
[dred] ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░
[dred] ░ ░

[yellow]Copyright &copy; 2022 Scion Altera
[dyellow]All code is <a target="_blank" href="https://github.com/scionaltera/agonyforge">original</a> and MIT licensed.
[dyellow]Some game elements inspired by <a target="_blank" href="https://www.smaug.org/">Smaug</a> and <a target="_blank" href="https://www.runehammer.online/">Index Card RPG</a>.
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ void testAnswerInvalid() {
void testAnswerValid() {
UUID chId = UUID.randomUUID();

when(applicationContext.getBean(eq("characterMenuQuestion"), eq(Question.class))).thenReturn(question);
when(applicationContext.getBean(eq("characterStatQuestion"), eq(Question.class))).thenReturn(question);
when(webSocketContext.getAttributes()).thenReturn(Map.of(
MUD_CHARACTER, chId
));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.agonyforge.mud.core.web.model.Input;
import com.agonyforge.mud.core.web.model.Output;
import com.agonyforge.mud.core.web.model.WebSocketContext;
import com.agonyforge.mud.models.dynamodb.constant.Stat;
import com.agonyforge.mud.models.dynamodb.impl.MudCharacter;
import com.agonyforge.mud.models.dynamodb.impl.MudRoom;
import com.agonyforge.mud.models.dynamodb.constant.Pronoun;
Expand All @@ -21,6 +22,7 @@
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.context.ApplicationContext;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
Expand Down Expand Up @@ -103,10 +105,17 @@ void testPrompt() {
Output result = uut.prompt(wsContext);

int i = 0;
assertEquals(8, result.getOutput().size());
assertEquals(15, result.getOutput().size());
assertTrue(result.getOutput().get(i++).contains("Character Sheet"));
assertTrue(result.getOutput().get(i++).contains(characterName));
assertTrue(result.getOutput().get(i++).contains(Pronoun.SHE.getObject()));
assertTrue(result.getOutput().get(i++).contains("STR:"));
assertTrue(result.getOutput().get(i++).contains("DEX:"));
assertTrue(result.getOutput().get(i++).contains("CON:"));
assertTrue(result.getOutput().get(i++).contains("INT:"));
assertTrue(result.getOutput().get(i++).contains("WIS:"));
assertTrue(result.getOutput().get(i++).contains("CHA:"));
assertTrue(result.getOutput().get(i++).contains("DEF:"));
assertEquals("", result.getOutput().get(i++));
assertTrue(result.getOutput().get(i++).contains("Play"));
assertTrue(result.getOutput().get(i++).contains("Delete"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.agonyforge.mud.models.dynamodb.constant;

public enum Stat {
STR("Strength", "STR"),
DEX("Dexterity", "DEX"),
CON("Constitution", "CON"),
INT("Intelligence", "INT"),
WIS("Wisdom", "WIS"),
CHA("Charisma", "CHA");

private final String name;
private final String abbreviation;

Stat(String name, String abbreviation) {
this.name = name;
this.abbreviation = abbreviation;
}

public String getName() {
return name;
}

public String getAbbreviation() {
return abbreviation;
}
}
Loading

0 comments on commit 6d931ab

Please sign in to comment.