Skip to content

Commit

Permalink
Add sidebar showing how many victory points have been obtained within…
Browse files Browse the repository at this point in the history
… a zone
  • Loading branch information
Gegy committed Nov 17, 2024
1 parent 66206f1 commit 174dc3b
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 15 deletions.
3 changes: 3 additions & 0 deletions src/generated/resources/assets/ltminigames/lang/en_ud.json
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@
"ltminigames.minigame.river_race.microgames_results": ":sʇןnsǝɹ ǝɥʇ ǝɹɐ ǝɹǝH ¡pǝʇǝןdɯoɔ ǝʌɐɥ sǝɯɐboɹɔıW",
"ltminigames.minigame.river_race.player_has_collectable": "¡ǝןqɐʇɔǝןןoɔ sıɥʇ sɐɥ ʎpɐǝɹןɐ %s",
"ltminigames.minigame.river_race.shop": "doɥS",
"ltminigames.minigame.river_race.sidebar.team_progress": "%s%% - %s ",
"ltminigames.minigame.river_race.sidebar.victory_points": "ǝuoZ ɹǝd sʇuıoԀ ʎɹoʇɔıΛ",
"ltminigames.minigame.river_race.sidebar.zone_header": ":%s",
"ltminigames.minigame.river_race.trivia.collectable_given": "¡ʇuǝɯnuoɯ ǝɥʇ oʇuı ǝɔɐןd oʇ ǝןqɐʇɔǝןןoɔ ɐ uǝʌıb uǝǝq ǝʌ,noʎ",
"ltminigames.minigame.river_race.trivia.collectable_placed.subtitle": "ǝuoz %ǝɯɐu% pǝʇǝןdɯoƆ",
"ltminigames.minigame.river_race.trivia.collectable_placed.title": "¡%ɯɐǝʇ% o⅁",
Expand Down
3 changes: 3 additions & 0 deletions src/generated/resources/assets/ltminigames/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,9 @@
"ltminigames.minigame.river_race.microgames_results": "Microgames have completed! Here are the results:",
"ltminigames.minigame.river_race.player_has_collectable": "%s already has this collectable!",
"ltminigames.minigame.river_race.shop": "Shop",
"ltminigames.minigame.river_race.sidebar.team_progress": " %s - %s%%",
"ltminigames.minigame.river_race.sidebar.victory_points": "Victory Points per Zone",
"ltminigames.minigame.river_race.sidebar.zone_header": "%s:",
"ltminigames.minigame.river_race.trivia.collectable_given": "You've been given a collectable to place into the monument!",
"ltminigames.minigame.river_race.trivia.collectable_placed.subtitle": "Completed %name% zone",
"ltminigames.minigame.river_race.trivia.collectable_placed.title": "Go %team%!",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
package com.lovetropics.minigames.common.content.river_race;

import com.lovetropics.lib.BlockBox;
import com.lovetropics.minigames.common.content.river_race.block.HasTrivia;
import com.lovetropics.minigames.common.content.river_race.block.TriviaType;
import com.lovetropics.minigames.common.core.game.IGamePhase;
import com.lovetropics.minigames.common.core.game.state.GameStateKey;
import com.lovetropics.minigames.common.core.game.state.IGameState;
import it.unimi.dsi.fastutil.longs.LongIterator;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class RiverRaceState implements IGameState {
public static final GameStateKey.Defaulted<RiverRaceState> KEY = GameStateKey.create("River Race", RiverRaceState::new);
Expand All @@ -23,8 +32,27 @@ public void setForwardDirection(Direction forwardDirection) {
this.forwardDirection = forwardDirection;
}

public void addZone(String id, BlockBox box, Component displayName, DyeColor color) {
zones.add(new Zone(id, box, displayName, color));
private Map<BlockPos, TriviaType> findAllTriviaBlocks(IGamePhase game, BlockBox box) {
Map<BlockPos, TriviaType> triviaBlocks = new HashMap<>();

LongIterator chunkIterator = box.asChunks().longIterator();
while (chunkIterator.hasNext()) {
long chunkPos = chunkIterator.nextLong();
LevelChunk chunk = game.level().getChunk(ChunkPos.getX(chunkPos), ChunkPos.getZ(chunkPos));
for (Map.Entry<BlockPos, BlockEntity> entry : chunk.getBlockEntities().entrySet()) {
BlockPos pos = entry.getKey();
if (box.contains(pos) && entry.getValue() instanceof HasTrivia hasTrivia) {
triviaBlocks.put(pos, hasTrivia.getTriviaType());
}
}
}

return triviaBlocks;
}

public void addZone(IGamePhase game, String id, BlockBox box, Component displayName, DyeColor color) {
Map<BlockPos, TriviaType> triviaBlocks = findAllTriviaBlocks(game, box);
zones.add(new Zone(id, box, displayName, color, triviaBlocks));
}

public Zone getZoneById(String id) {
Expand All @@ -46,6 +74,14 @@ public Zone getZoneByPos(BlockPos pos) {
return null;
}

public Zone getFirstZone() {
return zones.getFirst();
}

public List<Zone> getZones() {
return zones;
}

// Mirrored for the opposite team side so that equivalent trivia blocks can reuse the same question
public ZoneLocalPos getZoneLocalPosKey(Zone zone, BlockPos pos) {
pos = pos.subtract(zone.box.min());
Expand Down Expand Up @@ -78,15 +114,17 @@ public static final class Zone {
private final BlockBox box;
private final Component displayName;
private final DyeColor color;
private final Map<BlockPos, TriviaType> triviaBlocks;

@Nullable
private ItemStack collectable;

public Zone(String id, BlockBox box, Component displayName, DyeColor color) {
public Zone(String id, BlockBox box, Component displayName, DyeColor color, Map<BlockPos, TriviaType> triviaBlocks) {
this.id = id;
this.box = box;
this.displayName = displayName;
this.color = color;
this.triviaBlocks = triviaBlocks;
}

public String id() {
Expand All @@ -105,6 +143,10 @@ public DyeColor color() {
return color;
}

public Map<BlockPos, TriviaType> triviaBlocks() {
return triviaBlocks;
}

@Nullable
public ItemStack collectable() {
return collectable;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ public final class RiverRaceTexts {
public static final Component MICROGAME_RESULTS = KEYS.add("microgames_results", "Microgames have completed! Here are the results:")
.withStyle(ChatFormatting.GOLD);

public static final Component SIDEBAR_VICTORY_POINTS = KEYS.add("sidebar.victory_points", "Victory Points per Zone").withStyle(ChatFormatting.GREEN);
public static final TranslationCollector.Fun1 SIDEBAR_ZONE_HEADER = KEYS.add1("sidebar.zone_header", "%s:");
public static final TranslationCollector.Fun2 SIDEBAR_TEAM_PROGRESS = KEYS.add2("sidebar.team_progress", " %s - %s%%");

public static final TranslationCollector.Fun1 COLLECTABLE_NAME = KEYS.add1("collectable_name", "Collectable - %s");

public static void collectTranslations(BiConsumer<String, String> consumer) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ private InteractionResult onCollectablePlaced(IGamePhase game, ServerPlayer play
if (firstTeamToCollect.putIfAbsent(collectableZone.id(), team.key()) == null) {
GameActionContext context = GameActionContext.builder()
.set(GameActionParameter.TEAM, team)
.set(GameActionParameter.NAME, collectableZone.displayName().copy().withColor(collectableZone.color().getTextureDiffuseColor()))
.set(GameActionParameter.NAME, collectableZone.displayName())
.build();
CollectableConfig config = collectablesByZone.get(collectableZone.id());
if (config != null) {
Expand All @@ -148,7 +148,7 @@ private InteractionResult onCollectablePlaced(IGamePhase game, ServerPlayer play

private ItemStack createItem(RiverRaceState.Zone zone, CollectableConfig collectable) {
ItemStack item = collectable.baseItem.copy();
item.set(DataComponents.ITEM_NAME, RiverRaceTexts.COLLECTABLE_NAME.apply(zone.displayName()).withColor(zone.color().getTextureDiffuseColor()));
item.set(DataComponents.ITEM_NAME, RiverRaceTexts.COLLECTABLE_NAME.apply(zone.displayName()));
item.set(RiverRace.COLLECTABLE_MARKER.get(), Unit.INSTANCE);
item.applyComponents(itemPatch);
return item;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ public record RiverRaceZoneBehavior(
public void register(IGamePhase game, EventRegistrar events) throws GameException {
RiverRaceState riverRace = game.state().get(RiverRaceState.KEY);
BlockBox box = game.mapRegions().getOrThrow(regionKey);
riverRace.addZone(id, box, displayName, color);
riverRace.addZone(game, id, box, displayName, color);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.lovetropics.lib.codec.MoreCodecs;
import com.lovetropics.minigames.SoundRegistry;
import com.lovetropics.minigames.common.content.river_race.RiverRaceState;
import com.lovetropics.minigames.common.content.river_race.RiverRaceTexts;
import com.lovetropics.minigames.common.content.river_race.block.TriviaType;
import com.lovetropics.minigames.common.content.river_race.event.RiverRaceEvents;
Expand All @@ -22,20 +23,28 @@
import com.lovetropics.minigames.common.core.game.state.team.GameTeam;
import com.lovetropics.minigames.common.core.game.state.team.GameTeamKey;
import com.lovetropics.minigames.common.core.game.state.team.TeamState;
import com.lovetropics.minigames.common.core.game.util.GameSidebar;
import com.lovetropics.minigames.common.core.game.util.GlobalGameWidgets;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.ChatFormatting;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.CommonComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.ExtraCodecs;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class VictoryPointsBehavior implements IGameBehavior {

Expand All @@ -48,7 +57,14 @@ public class VictoryPointsBehavior implements IGameBehavior {
ExtraCodecs.nonEmptyList(MoreCodecs.listOrUnit(Codec.INT)).optionalFieldOf("points_per_game_won", List.of(3, 2, 1)).forGetter(c -> c.pointsPerGameWon)
).apply(i, VictoryPointsBehavior::new));

private static final int SIDEBAR_INTERVAL = SharedConstants.TICKS_PER_SECOND / 2;

private IGamePhase game;
private TeamState teams;
private final Object2IntMap<String> availablePointsPerZone = new Object2IntOpenHashMap<>();
private final Map<GameTeamKey, Object2IntOpenHashMap<String>> acquiredPointsPerZone = new HashMap<>();

private RiverRaceState.Zone currentZone;

private final int triviaChestPoints;
private final int triviaGatePoints;
Expand All @@ -72,7 +88,20 @@ public VictoryPointsBehavior(int triviaChestPoints, int triviaGatePoints, int tr
@Override
public void register(IGamePhase game, EventRegistrar events) throws GameException {
this.game = game;
TeamState teams = game.instanceState().getOrThrow(TeamState.KEY);
teams = game.instanceState().getOrThrow(TeamState.KEY);

RiverRaceState riverRace = game.state().get(RiverRaceState.KEY);
for (RiverRaceState.Zone zone : riverRace.getZones()) {
availablePointsPerZone.put(zone.id(), computeAvailablePoints(zone));
}

for (GameTeam team : teams) {
acquiredPointsPerZone.put(team.key(), new Object2IntOpenHashMap<>());
}

currentZone = riverRace.getFirstZone();

GameSidebar sidebar = GlobalGameWidgets.registerTo(game, events).openSidebar(game.definition().name().copy().withStyle(ChatFormatting.AQUA));

events.listen(RiverRaceEvents.QUESTION_COMPLETED, this::onQuestionAnswered);
events.listen(RiverRaceEvents.COLLECTABLE_PLACED, this::onCollectablePlaced);
Expand All @@ -91,6 +120,10 @@ public void register(IGamePhase game, EventRegistrar events) throws GameExceptio
game.invoker(RiverRaceEvents.VICTORY_POINTS_CHANGED).onVictoryPointsChanged(teamKey, newPoints, oldPoints);
}
}

if (game.ticks() % SIDEBAR_INTERVAL == 0) {
sidebar.set(renderSidebar(teams));
}
});

events.listen(SubGameEvents.CREATE, (subGame, subEvents) -> {
Expand All @@ -106,6 +139,24 @@ public void register(IGamePhase game, EventRegistrar events) throws GameExceptio
microgameSegment = null;
}
});

events.listen(RiverRaceEvents.UNLOCK_ZONE, id ->
currentZone = riverRace.getZoneById(id)
);
}

private int computeAvailablePoints(RiverRaceState.Zone zone) {
int availablePoints = 0;
for (TriviaType type : zone.triviaBlocks().values()) {
availablePoints += getPointsForTriviaType(type);
}
if (zone.collectable() != null) {
availablePoints += collectablePlacedPoints * teams.size();
}
if (availablePoints % teams.size() != 0) {
throw new GameException(Component.literal("Uneven point balance between teams"));
}
return availablePoints / teams.size();
}

private void onMicrogameWinTriggered(GameWinner winner, MicrogameSegmentState segmentState) {
Expand All @@ -126,7 +177,7 @@ private void onMicrogamesCompleted(MicrogameSegmentState segmentState) {
GameStatistics segmentStatistics = new GameStatistics();

for (Object2IntMap.Entry<GameTeamKey> entry : segmentState.pointsByTeam.object2IntEntrySet()) {
addPoints(entry.getKey(), entry.getIntValue());
addPoints(entry.getKey(), entry.getIntValue(), false);
segmentStatistics.forTeam(entry.getKey()).set(StatisticKey.VICTORY_POINTS, entry.getIntValue());
}

Expand All @@ -141,30 +192,59 @@ private GameTeamKey getTeamFor(PlayerKey player) {
return teams != null ? teams.getTeamForPlayer(player) : null;
}

private void addPoints(final PlayerKey playerKey, final int points) {
private void addPoints(final PlayerKey playerKey, final int points, boolean inZone) {
TeamState teams = game.instanceState().getOrNull(TeamState.KEY);
GameTeamKey team = teams != null ? teams.getTeamForPlayer(playerKey) : null;
if (team != null) {
game.statistics().forTeam(team).incrementInt(StatisticKey.VICTORY_POINTS, points);
addPoints(team, points, inZone);
}
}

private void addPoints(final GameTeamKey team, final int points) {
private void addPoints(final GameTeamKey team, final int points, final boolean inZone) {
game.statistics().forTeam(team).incrementInt(StatisticKey.VICTORY_POINTS, points);
if (inZone) {
acquiredPointsPerZone.get(team).addTo(currentZone.id(), points);
}
}

private void onQuestionAnswered(ServerPlayer player, TriviaType triviaType, BlockPos triviaPos) {
int points = switch (triviaType) {
addPoints(PlayerKey.from(player), getPointsForTriviaType(triviaType), true);
}

private int getPointsForTriviaType(TriviaType triviaType) {
return switch (triviaType) {
case COLLECTABLE -> collectableCollectedPoints;
case VICTORY -> triviaChallengePoints;
case REWARD -> triviaChestPoints;
case GATE -> triviaGatePoints;
};
addPoints(PlayerKey.from(player), points);
}

private void onCollectablePlaced(ServerPlayer player, GameTeam team, BlockPos pos) {
addPoints(PlayerKey.from(player), collectablePlacedPoints);
addPoints(PlayerKey.from(player), collectablePlacedPoints, true);
}

private Component[] renderSidebar(TeamState teams) {
RiverRaceState riverRace = game.state().get(RiverRaceState.KEY);

List<Component> sidebar = new ArrayList<>(10);
sidebar.add(RiverRaceTexts.SIDEBAR_VICTORY_POINTS);

for (RiverRaceState.Zone zone : riverRace.getZones()) {
int pointsInZone = availablePointsPerZone.getInt(zone.id());
if (pointsInZone == 0) {
continue;
}
sidebar.add(CommonComponents.EMPTY);
sidebar.add(RiverRaceTexts.SIDEBAR_ZONE_HEADER.apply(zone.displayName()));
for (GameTeam team : teams) {
int acquiredPoints = acquiredPointsPerZone.get(team.key()).getInt(zone.id());
int percent = acquiredPoints * 100 / pointsInZone;
sidebar.add(RiverRaceTexts.SIDEBAR_TEAM_PROGRESS.apply(team.config().styledName(), percent));
}
}

return sidebar.toArray(new Component[0]);
}

private static final class MicrogameSegmentState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ GameResult<Unit> start(final boolean savePlayerDataToMemory) {
} catch (GameException e) {
return GameResult.error(e.getTextMessage());
} catch (Exception e) {
return GameResult.error(Component.literal(e.getMessage()));
return GameResult.error(Component.literal(e.getClass() + ": " + e.getMessage()));
}

final String mapName = map.name();
Expand Down

0 comments on commit 174dc3b

Please sign in to comment.