UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

587 lines (485 loc) 15.2 kB
import { world, Player, BlockPermutation } from "@minecraft/server"; import Challenge, { ChallengeFlavor, ChallengePhase } from "./Challenge.js"; import ChallengePlayer from "./ChallengePlayer.js"; import { PAD_SURROUND_X as PAD_SURROUND_X, PAD_SURROUND_Z as PAD_SURROUND_Z, PAD_SIZE_X, PAD_SIZE_Z, JOIN_TEAM_X, JOIN_TEAM_Y, JOIN_TEAM_Z, OPTIONS_AREA_TEAM_X, OPTIONS_AREA_TEAM_Y, OPTIONS_AREA_TEAM_Z, SPAWN_TEAM_X, SPAWN_TEAM_Y, SPAWN_TEAM_Z, } from "./Constants.js"; import Log from "./Log.js"; import Utilities from "./Utilities.js"; import { ModalFormData, MessageFormData } from "@minecraft/server-ui"; import { MinecraftBlockTypes, MinecraftDimensionTypes } from "@minecraft/vanilla-data"; export interface ITeamData { n: string; s: number; t: number; } export default class Team { index: number = -1; padX: number; padZ: number; #playerTocks: number; // one player tock = one player was active for 20 secs #name: string | undefined = undefined; #score: number = 0; nwbX: number = 0; nwbY: number = 0; nwbZ: number = 0; padNwbX: number = 0; padNwbY: number = 0; padNwbZ: number = 0; teamUsageQuartile: number = 0; votes: number = 0; rankByVote: number = -1; players: ChallengePlayer[] = []; challenge: Challenge; get active() { return this.players.length > 0; } get name() { if (!this.#name) { return "Team " + new String(this.index + 1); } return this.#name; } set name(newName: string) { if (newName !== this.#name) { if (this.isValidName(newName)) { this.#name = newName; this.addTeamName(); this.challenge.refreshTeamScores(); this.challenge.save(); } } } get effectiveScore() { let effectiveScore = this.score; if (effectiveScore < 0) { return 0; } if (this.challenge.teams.length >= 4) { if (this.teamUsageQuartile == 1) { effectiveScore += this.score / 4; } else if (this.teamUsageQuartile == 2) { effectiveScore += this.score / 2; } else if (this.teamUsageQuartile == 3) { effectiveScore += this.score; } } if ( (this.challenge.phase === ChallengePhase.vote || this.challenge.phase === ChallengePhase.post) && this.challenge.teams.length >= 3 ) { if (this.rankByVote == 0) { effectiveScore += this.score * 2; // 3x bonus for first vote winner. } else if (this.rankByVote == 1) { effectiveScore += this.score; } else if (this.rankByVote == 2) { effectiveScore += this.score / 2; } } return Math.floor(effectiveScore); } get playerTocks() { return this.#playerTocks; } set playerTocks(newTock: number) { if (this.#playerTocks !== newTock) { this.#playerTocks = newTock; } } get score() { return this.#score; } set score(newScore: number) { if (this.#score !== newScore) { this.#score = newScore; } } constructor(challenge: Challenge, index: number, x: number, z: number) { this.challenge = challenge; this.padX = x; this.padZ = z; this.#playerTocks = 0; this.updateLocation(); this.index = index; } removePlayer(playerToRemove: ChallengePlayer) { let newPlayerArr = []; for (let i = 0; i < this.players.length; i++) { if ( this.players[i] !== playerToRemove && this.challenge.canonicalizePlayerName(this.players[i].name) !== this.challenge.canonicalizePlayerName(playerToRemove.name) ) { newPlayerArr.push(this.players[i]); } } this.players = newPlayerArr; } applyScore() { let teamName = this.name; if (this.challenge.teams.length >= 4) { if (this.teamUsageQuartile == 0) { teamName = "█ " + teamName; } else if (this.teamUsageQuartile == 1) { teamName = "▓ " + teamName; } else if (this.teamUsageQuartile == 2) { teamName = "▒ " + teamName; } else if (this.teamUsageQuartile == 3) { teamName = "░ " + teamName; } } if ( (this.challenge.phase === ChallengePhase.vote || this.challenge.phase === ChallengePhase.post) && this.challenge.teams.length >= 3 ) { if (this.rankByVote == 0) { teamName = "§g" + teamName + " √√√ (" + this.votes + ")"; } else if (this.rankByVote == 1) { teamName = "§s" + teamName + " √√ (" + this.votes + ")"; } else if (this.rankByVote == 2) { teamName = "§6" + teamName + " √ (" + this.votes + ")"; } else if (this.votes > 0) { teamName += " (" + this.votes + ")"; } } teamName += " "; world.scoreboard.getObjective("main")?.setScore(teamName, this.effectiveScore); } getEffectiveTeamPlayerCount() { let count = 0; for (let i = 0; i < this.players.length; i++) { if (!this.players[i].allowTeamChangeAlways) { count++; } } return count; } ensurePlayerIsOnTeam(challPlayer: ChallengePlayer) { for (let i = 0; i < this.players.length; i++) { if ( this.players[i] === challPlayer || this.challenge.canonicalizePlayerName(this.players[i].name) === this.challenge.canonicalizePlayerName(challPlayer.name) ) { return; } } this.players.push(challPlayer); if (challPlayer.player) { if (challPlayer.player.isValid) { try { challPlayer.player.setSpawnPoint({ x: this.nwbX + SPAWN_TEAM_X, y: this.nwbY + SPAWN_TEAM_Y, z: this.nwbZ + SPAWN_TEAM_Z, dimension: world.getDimension(MinecraftDimensionTypes.Overworld), }); } catch (e) {} } } } updateLocation() { this.nwbX = this.challenge.nwbLocation.x + this.padX * (PAD_SIZE_X + PAD_SURROUND_X); this.nwbY = this.challenge.nwbLocation.y; this.nwbZ = this.challenge.nwbLocation.z + this.padZ * (PAD_SIZE_Z + PAD_SURROUND_Z); this.padNwbX = this.nwbX + PAD_SURROUND_X / 2 - 2; this.padNwbY = this.nwbY; this.padNwbZ = this.nwbZ + PAD_SURROUND_Z / 2 - 2; } addTeamName() { const airBlock = BlockPermutation.resolve(MinecraftBlockTypes.Air); const signBlock = BlockPermutation.resolve(MinecraftBlockTypes.BirchSlab); if (!airBlock || !signBlock) { return; } Utilities.fillBlock( airBlock, this.nwbX + 5, this.nwbY + 1, this.nwbZ + 5, this.nwbX + (PAD_SIZE_X + PAD_SURROUND_X) - 5, this.nwbY + 1, this.nwbZ + 10 ); Utilities.writeTextFlatX(this.name, { x: this.nwbX + 5, y: this.nwbY + 1, z: this.nwbZ + 5 }, signBlock, airBlock); } init() { // console.warn("Setting score for " + this.name + " to " + this.#score); world.scoreboard.getObjective("main")?.setScore(this.name, this.#score); } isValidName(newName: string) { if (newName.length < 2 || newName.length > 10) { return false; } for (let c of newName) { if ((c < "a" || c > "z") && (c < "A" || c > "Z") && (c < "0" || c > "9") && c !== " ") { return false; } } return true; } initPad() { this.ensurePad(); } async showOptions(player: Player) { let challPlayer = this.challenge.ensurePlayer(player); if (challPlayer) { if ( challPlayer.teamId === this.index && this.challenge.phase !== ChallengePhase.post && this.challenge.phase !== ChallengePhase.vote ) { let mdf = new ModalFormData(); let name = this.name; if (!name) { name = "Unknown team"; } mdf.title(name); mdf.textField("Name", "team name", { defaultValue: this.name }); let result = await mdf.show(player); if (result.formValues && result.formValues[0] !== undefined && typeof result.formValues[0] === "string") { if (!this.isValidName(result.formValues[0])) { player.sendMessage("New team name can only be letters or numbers, and less than 11 characters."); } else { this.name = result.formValues[0]; } } } this.showName(player); } } async showName(player: Player) { let challPlayer = this.challenge.ensurePlayer(player); if (challPlayer) { let mdf = new MessageFormData(); let name = this.name; if (!name) { name = "Unknown team"; } mdf.title(name); if (this.players.length === 0) { mdf.body("Nobody is on " + this.name + " yet."); } else { let teamMembers = ""; for (let i = 0; i < this.players.length; i++) { if (teamMembers.length > 0) { teamMembers += ", "; } teamMembers += this.players[i].name; } let bodyStr = "Team members: " + teamMembers + "\r\nScore (before bonuses): " + this.score + "\r\n"; if (this.challenge.phase === ChallengePhase.vote || this.challenge.phase === ChallengePhase.post) { if (this.rankByVote === 0) { bodyStr += "1st place (" + this.votes + ")\r\n"; } else if (this.rankByVote === 0) { bodyStr += "2st place (" + this.votes + ")\r\n"; } else if (this.rankByVote === 0) { bodyStr += "3rd place (" + this.votes + ")\r\n"; } else { bodyStr += this.votes + " votes\r\n"; } } if (this.teamUsageQuartile == 0) { bodyStr += "Most active group (no bonus)\r\n"; } else if (this.teamUsageQuartile == 0) { bodyStr += "2nd active group (25% bonus)\r\n"; } else if (this.teamUsageQuartile == 1) { bodyStr += "3rd active group (50% bonus)\r\n"; } else if (this.teamUsageQuartile == 2) { bodyStr += "Least active group (double bonus)\r\n"; } mdf.body(bodyStr); } mdf.button1("OK"); mdf.button2("Cancel"); let result = await mdf.show(player); } } ensurePad() { const foundationSurroundBlock = BlockPermutation.resolve(MinecraftBlockTypes.GrassBlock); const foundationBlock = BlockPermutation.resolve(MinecraftBlockTypes.Sandstone); const foundationLowerBlock = BlockPermutation.resolve(MinecraftBlockTypes.Bedrock); const roadBlock = BlockPermutation.resolve(MinecraftBlockTypes.RedSandstone); if (!foundationSurroundBlock || !foundationBlock || !roadBlock || !foundationLowerBlock) { return; } // east bar Utilities.fillBlock( foundationSurroundBlock, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X / 2 - 1, this.nwbY - 3, this.nwbZ, this.nwbX + PAD_SIZE_X + (PAD_SURROUND_X - 1), this.nwbY, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z - 1 ); // west bar Utilities.fillBlock( foundationSurroundBlock, this.nwbX, this.nwbY - 3, this.nwbZ, this.nwbX + PAD_SURROUND_X / 2, this.nwbY, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z - 1 ); // north and south are inset since the corners are covered by e/w bars // north bar? Utilities.fillBlock( foundationSurroundBlock, this.nwbX + PAD_SURROUND_X / 2, this.nwbY - 3, this.nwbZ, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X / 2, this.nwbY, this.nwbZ + PAD_SURROUND_Z / 2 ); // south bar Utilities.fillBlock( foundationSurroundBlock, this.nwbX + PAD_SURROUND_X / 2, this.nwbY - 3, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z / 2 - 1, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X / 2, this.nwbY, this.nwbZ + PAD_SIZE_Z + (PAD_SURROUND_Z - 1) ); Utilities.fillBlock( foundationBlock, this.nwbX + PAD_SURROUND_X / 2 - 2, this.nwbY, this.nwbZ + PAD_SURROUND_Z / 2 - 2, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X / 2 - 2, this.nwbY, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z / 2 - 2 ); Utilities.fillBlock( foundationLowerBlock, this.nwbX + PAD_SURROUND_X / 2 - 2, this.nwbY - 4, this.nwbZ + PAD_SURROUND_Z / 2 - 2, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X / 2 - 2, this.nwbY - 1, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z / 2 - 2 ); // east road Utilities.fillBlock( roadBlock, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X - 5, this.nwbY, this.nwbZ, this.nwbX + PAD_SIZE_X + (PAD_SURROUND_X - 1), this.nwbY, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z - 1 ); // west road running n/s = out of bounds but filler road Utilities.fillBlock( roadBlock, this.nwbX - 4, this.nwbY, this.nwbZ - 4, this.nwbX, this.nwbY, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z - 1 ); // south road Utilities.fillBlock( roadBlock, this.nwbX, this.nwbY, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z - 5, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X - 5, this.nwbY, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z - 1 ); // north road = out of bounds but filler road Utilities.fillBlock( roadBlock, this.nwbX, this.nwbY, this.nwbZ - 4, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X - 5, this.nwbY, this.nwbZ - 1 ); Utilities.fillBlock( roadBlock, this.nwbX, this.nwbY - 10, this.nwbZ, this.nwbX + PAD_SIZE_X, this.nwbY - 1, this.nwbZ + PAD_SIZE_Z ); let ow = world.getDimension(MinecraftDimensionTypes.Overworld); let block = ow.getBlock({ x: this.nwbX + JOIN_TEAM_X, y: this.nwbY + JOIN_TEAM_Y, z: this.nwbZ + JOIN_TEAM_Z }); let consoleType = "options"; if (this.challenge.phase === ChallengePhase.vote && this.challenge.flavor === ChallengeFlavor.regular) { consoleType = "vote"; } else if (this.challenge.phase === ChallengePhase.vote && this.challenge.flavor === ChallengeFlavor.goodVibes) { consoleType = "vote2"; } ow.runCommand( `/structure load challenge:${consoleType} ${this.nwbX + OPTIONS_AREA_TEAM_X} ${this.nwbY + OPTIONS_AREA_TEAM_Y} ${ this.nwbZ + OPTIONS_AREA_TEAM_Z } 0_degrees ` ); this.addTeamName(); } clearPad(index: number) { let airBlock = BlockPermutation.resolve(MinecraftBlockTypes.Air); if (airBlock) { Utilities.fillBlock( airBlock, this.nwbX - 4, this.nwbY + 1 + index * 4, this.nwbZ - 4, this.nwbX + PAD_SIZE_X + PAD_SURROUND_X, this.nwbY + 7 + index * 4, this.nwbZ + PAD_SIZE_Z + PAD_SURROUND_Z ); } } getSaveData() { let td = { n: this.name, s: this.score, t: this.#playerTocks, }; return td; } loadFromString(dataStr: string) { if (!dataStr) { return; } try { let data = JSON.parse(dataStr); this.loadFromData(data); } catch (e) { Log.debug("Could not parse incoming data: " + dataStr); } } loadFromData(data: ITeamData) { if (data.n) { this.#name = data.n; } if (data.s) { this.#score = data.s; } if (data.t) { this.#playerTocks = data.t; } } }