+ Parece que você não mexe no jogo por mais de 2 minutos! Tempo ocioso está sendo armazenado num botão acima da tabela de recursos
+
+ Você pode permanentemente disabilitar isso no menu de configurações.
+ clique para voltar.
+
+
+
+
+
+
+
+
diff --git a/To Do.md b/To Do.md
index 68e36a7c..43a54370 100644
--- a/To Do.md
+++ b/To Do.md
@@ -4,15 +4,15 @@
"Reverse Engineering" *The components spin and whirr and click together, but their purpose eludes us. What secrets are you hiding in your mechanisms?*
### FRIGID
-"Village" A small village of squid greet you respectfully. The water in this place is a little warmer, and you hear a quiet, ambient hum.,
+~~"Village" A small village of squid greet you respectfully. The water in this place is a little warmer, and you hear a quiet, ambient hum.,~~
"Teamwork" The squid champion the value of teamwork and the necessity of cooperation. They say they follow by example.,
"Squid" The squid speak of an ancient visitor who saved their world. They ask if you too, have seen this visitor.,
"Suspicion" The squid describe the machine with fascination. They ask if we feel the same. They see something we do not.,
~~"Battery" Buried deep within the complex lies a massive, dimly glowing battery. The squid say replacing it will get the machine running at full power.,~~ (courtesy of Glowkate)
"Heat Returns" A wave of heat washes over you, and the dingy complex comes back to life. The gate turns on.
-### HAVEN
-"Done" The great song booms across the open water, carrying itself to all corners of the ocean. The gate reacts.
+### ~~HAVEN~~
+~~"Done" The great song booms across the open water, carrying itself to all corners of the ocean. The gate reacts.~~
### MARINE
"Sentience" All of us have boards now. Children are born half-machine. The lobsters call it effective.
@@ -24,8 +24,8 @@
"Truth" A team of eels get your attention. They have something from the caverns: it's a book. You can't read a thing inside it, but...that looks like arcana...and that looks like...a warning.
### Tempestuous
-"Billfish" "The so-called 'billfish' apologize for their behavior. They say no shark has entered this cave in a long, long time.",
-"Sandbags" "Stormgoers tredge slowly across the seabed. They dig through the sand extracting buried seagrass, but are too heavy to bring back crystals.",
+~~"Billfish" "The so-called 'billfish' apologize for their behavior. They say no shark has entered this cave in a long, long time.",~~
+~~"Sandbags" "Stormgoers tredge slowly across the seabed. They dig through the sand extracting buried seagrass, but are too heavy to bring back crystals.",~~ (courtesy of Glowkate)
"Stories" "The billfish tell stories of life before the storm and an ancient visitor who brought them prosperity. They ask if you can bring them prosperity, too.",
"Expeditions" "The billfish line up in droves to volunteer for expeditions, danger or otherwise. They don't need convincing, just equipment.",
"Map" "A grand map lays stretched out against the cavern wall. In the top right corner is a strange shape with a door.",
@@ -37,7 +37,7 @@
~~"Shrimp Threat" You are approached by an army of shrimp. They relay a very clear message to you: cooperate, or be destroyed. You decide to stop harvesting sponges.,~~
"Shrimp Communication" The homes (sponges) left behind by shrimp joining the frenzy may now be taken for ourselves.,
~~"Monarchy" The shrimps follow a caste system with the king of shrimps on top. They ask who your king is.,~~
-"Smithing" Porite: glassy hunks sealed on the outside but porous on the inside: it's lightweight, yet it stays strong.,
+~~"Smithing" Porite: glassy hunks sealed on the outside but porous on the inside: it's lightweight, yet it stays strong.,~~
"Acolytes" The acolytes gather. They pray for their king. They pray for their world. They pray for you.,
"Beauty" The king is speechless. As he views the great industrial city, his subjects gather and cheer, celebrating his arrival.,
~~"Curious Crabs" Sort of just off to the side, a group of curious crabs congregate and discuss stuff that we don't understand.,~~
@@ -120,7 +120,7 @@ Billfish Biology unlock billfish pairs,~~
~~Heavy Sifting seperate light and heavier sand to make all uses of sand cheaper,~~ (courtesy of Glowkate)
Sense of Direction actually navigate the storm properly,
~~Laser Lenses use glass bottles to focus lasers to make laser rays cheaper,~~
-Crystal Clippers tool for stormgoers,
+~~Crystal Clippers tool for stormgoers,~~
Routing use charts we have to make efficient routes,
Heat Harnesses move heat using special crystal harnesses,
Superclippers BIG CLIPPER,
diff --git a/img/events/home/haven-done.png b/img/events/home/haven-done.png
new file mode 100644
index 00000000..a472c4bf
Binary files /dev/null and b/img/events/home/haven-done.png differ
diff --git a/img/events/home/tempestuous-billfish.png b/img/events/home/tempestuous-billfish.png
new file mode 100644
index 00000000..08d40846
Binary files /dev/null and b/img/events/home/tempestuous-billfish.png differ
diff --git a/img/events/home/tempestuous-crab-stormgoers.png b/img/events/home/tempestuous-crab-stormgoers.png
new file mode 100644
index 00000000..fb9d417c
Binary files /dev/null and b/img/events/home/tempestuous-crab-stormgoers.png differ
diff --git a/img/events/home/volcanic-smithing.png b/img/events/home/volcanic-smithing.png
index dc193d79..c557d900 100644
Binary files a/img/events/home/volcanic-smithing.png and b/img/events/home/volcanic-smithing.png differ
diff --git a/img/small/technologies/crystalClippers.png b/img/small/technologies/crystalClippers.png
new file mode 100644
index 00000000..6e430554
Binary files /dev/null and b/img/small/technologies/crystalClippers.png differ
diff --git a/js/BRaspecttree.js b/js/BRaspecttree.js
new file mode 100644
index 00000000..89261e81
--- /dev/null
+++ b/js/BRaspecttree.js
@@ -0,0 +1,958 @@
+"use strict";
+
+const BUTTON_BORDER_RADIUS = 5;
+
+const CANVAS_WIDTH = 800;
+const CANVAS_HEIGHT = 600;
+
+/*
+ * +y
+ * ^
+ * |
+ * |
+ * |
+ * +x<--------+-------> -x
+ * |
+ * |
+ * |
+ * V
+ * -y
+ */
+const LEFT_EDGE = CANVAS_WIDTH + 400;
+const TOP_EDGE = CANVAS_HEIGHT + 650;
+const RIGHT_EDGE = -400;
+const BOTTOM_EDGE = -150;
+
+const SPRITE_SHEET = new Image();
+const EVENT_SPRITE_SHEET = new Image();
+
+SPRITE_SHEET.src = "img/sprites.png";
+EVENT_SPRITE_SHEET.src = "img/homemessagesprites.png";
+
+SharkGame.AspectTree = {
+ /** @type {CanvasRenderingContext2D} */
+ context: undefined,
+ pointerType: "mouse",
+ previousButton: undefined,
+ staticButtons: {
+ respec: {
+ posX: 10,
+ get posY() {
+ return 10;
+ },
+ width: 30,
+ height: 30,
+
+ name: "Reembolso",
+ description: "Liga/desliga o modo reembolso.",
+ getEffect() {
+ if (tree.refundMode) {
+ return "Desliga o modo reembolso.";
+ } else {
+ return "Ativa o modo reembolso para vender aspectos (se possível).";
+ }
+ },
+ clicked() {
+ tree.toggleRefundMode();
+ tree.updateTooltip(this);
+ },
+ getUnlocked() {
+ return SharkGame.Aspects.cleanSlate.level > 0;
+ },
+ getOn() {
+ return tree.refundMode;
+ },
+ },
+ respecAll: {
+ posX: 10,
+ get posY() {
+ return tree.staticButtons.respec.posY + tree.staticButtons.respec.height + 10;
+ },
+ width: 30,
+ height: 30,
+
+ name: "Reinicio",
+ description: "Reembolsa todos o aspectos.",
+ getEffect() {
+ return "Vende TODOS os aspectos reembolsáveis.";
+ },
+ clicked() {
+ if (confirm("Tem certeza que queres vender TODOS os aspectos reembolsáveis?")) {
+ tree.respecTree();
+ }
+ },
+ getUnlocked() {
+ return SharkGame.Aspects.cleanSlate.level > 0;
+ },
+ getOn() {
+ return false;
+ },
+ },
+ debug: {
+ posX: 760,
+ posY: 10,
+ width: 30,
+ height: 30,
+
+ name: "Debug",
+ description: "Liga/desliga modo de debug.",
+ getEffect() {
+ if (tree.debugMode) {
+ return "Desliga modo de debug.";
+ } else {
+ return "Ativa modo debug para mudar os níveis dos aspectos livremente.";
+ }
+ },
+ clicked() {
+ tree.toggleDebugMode();
+ tree.updateTooltip(this);
+ },
+ getUnlocked() {
+ return SharkGame.persistentFlags.debug;
+ },
+ getOn() {
+ return tree.debugMode;
+ },
+ },
+ },
+ requirementReference: {},
+
+ init() {
+ $.each(SharkGame.Aspects, (aspectId, aspectData) => {
+ _.each(aspectData.prerequisites, (prerequisite) => {
+ if (!_.has(aspectData, "requiredBy")) {
+ aspectData.requiredBy = [];
+ }
+ if (!_.has(SharkGame.Aspects, prerequisite)) return;
+ if (!_.has(SharkGame.Aspects[prerequisite], "requiredBy")) {
+ SharkGame.Aspects[prerequisite].requiredBy = [];
+ }
+ SharkGame.Aspects[prerequisite].requiredBy.push(aspectId);
+ });
+ // wipe all levels
+ aspectData.level = 0;
+
+ // redundant removal of persistent flags
+ if (SharkGame.persistentFlags.destinyRolls) {
+ SharkGame.persistentFlags.destinyRolls = 0;
+ }
+ if (SharkGame.persistentFlags.patience) {
+ SharkGame.persistentFlags.patience = 0;
+ }
+ });
+
+ // turn off refund mode
+ tree.refundMode = false;
+ tree.debugMode = false;
+ },
+
+ setup() {
+ if (SharkGame.missingAspects) {
+ res.setResource("essence", res.getTotalResource("essence"));
+ _.each(SharkGame.Aspects, (aspectData) => {
+ // wipe all levels
+ aspectData.level = 0;
+ });
+ SharkGame.PaneHandler.showAspectWarning();
+ } else {
+ // try to refund deprecated aspects
+ _.each(SharkGame.Aspects, (aspectData) => {
+ if (aspectData.deprecated) {
+ tree.refundLevels(aspectData);
+ }
+ });
+ }
+
+ tree.resetScoutingRestrictions();
+ tree.applyScoutingRestrictionsIfNeeded();
+
+ tree.applyAspects();
+
+ if (SharkGame.persistentFlags.patience) {
+ if (SharkGame.Aspects.patience.level) {
+ res.changeResource("essence", 2 * (SharkGame.Aspects.patience.level + 1) ** 2);
+ }
+ SharkGame.persistentFlags.patience = undefined;
+ }
+
+ res.setResource("aspectAffect", 1);
+ res.setTotalResource("aspectAffect", 1);
+
+ tree.generateRequirementReference();
+ },
+
+ drawTree(disableCanvas = true) {
+ if (disableCanvas) {
+ return tree.drawTable();
+ } else {
+ return tree.drawCanvas();
+ }
+ },
+
+ drawTable(table = $("
").html("NEXT: Already at maximum level."));
+ aspectTableRowNext.append($("
").html("N/A"));
+ }
+
+ _.each([aspectTableDescriptionRow, aspectTableRowNext, aspectTableRowCurrent], (row) => {
+ row.attr("data-aspectId", aspectId)
+ .on("click", clickCallback)
+ .attr("aria-role", "button")
+ .attr("disabled", reqref.prereqsMet.toString());
+ });
+
+ tableBody.append(aspectTableDescriptionRow);
+ tableBody.append(aspectTableRowCurrent);
+ tableBody.append(aspectTableRowNext);
+ });
+ table.append(tableBody);
+ return table;
+ },
+
+ drawCanvas() {
+ const canvas = document.createElement("canvas");
+ canvas.id = "treeCanvas";
+ canvas.setAttribute("width", "800px");
+ canvas.setAttribute("height", "600px");
+
+ $(canvas).on("mouseenter mousemove mouseleave wheel click touchstart touchmove touchend touchcancel", tree.updateMouse);
+ $(canvas).on("click", tree.click);
+
+ tree.context = canvas.getContext("2d", { alpha: false, desynchronized: true });
+
+ $("body").css("overscroll-behavior-x", "none");
+
+ return canvas;
+ },
+
+ initTree() {
+ this.panzoom = panzoom($("canvas")[0], {
+ maxZoom: 2,
+ minZoom: 0.8,
+ bounds: {
+ top: BOTTOM_EDGE + CANVAS_HEIGHT,
+ right: LEFT_EDGE - CANVAS_WIDTH,
+ bottom: TOP_EDGE - CANVAS_HEIGHT,
+ left: RIGHT_EDGE + CANVAS_WIDTH,
+ },
+ boundsDisabledForZoom: true,
+ smoothScroll: {
+ amplitude: 0.05,
+ },
+ onTouch: () => false,
+ beforeMouseDown: (event) => event.target.id !== "treeCanvas" || this.getButtonUnderMouse(event) !== undefined,
+ beforeWheel: (event) => event.target.id !== "treeCanvas",
+ });
+ this.panzoom.on("transform", () => {
+ requestAnimationFrame(tree.render);
+ });
+ tree.render();
+ },
+
+ /**
+ * @param {HTMLCanvasElement} canvas
+ * @param {MouseEvent} event
+ */
+ getCursorPositionInCanvas(canvas, event) {
+ const rect = canvas.getBoundingClientRect();
+ const posX = (event.clientX || event.targetTouches[0]?.clientX || event.changedTouches[0].clientX) - rect.left;
+ const posY = (event.clientY || event.targetTouches[0]?.clientY || event.changedTouches[0].clientY) - rect.top;
+ const result = { posX, posY };
+ return result;
+ },
+
+ /** @param {MouseEvent} event */
+ getButtonUnderMouse(event) {
+ const context = tree.context;
+ const mousePos = tree.getCursorPositionInCanvas(context.canvas, event);
+ const transform = this.panzoom.getTransform();
+
+ // this fixes one piece of the sticky tooltip bug on the tree
+ if (gateway.transitioning) {
+ return;
+ }
+
+ const staticButton = _.find(tree.staticButtons, ({ posX, posY, width, height }) => {
+ return mousePos.posX - posX >= 0 && mousePos.posY - posY >= 0 && mousePos.posX - posX <= width && mousePos.posY - posY <= height;
+ });
+ if (staticButton !== undefined && (!staticButton.getUnlocked || staticButton.getUnlocked())) {
+ return staticButton;
+ }
+
+ const aspect = _.find(SharkGame.Aspects, ({ posX, posY, width, height, prerequisites, level }) => {
+ if (
+ _.some(prerequisites, (prerequisite) => SharkGame.Aspects[prerequisite].level === 0) &&
+ !SharkGame.Aspects.infinityVision.level &&
+ !level
+ ) {
+ return;
+ }
+
+ const computedY = (mousePos.posY - transform.y) / transform.scale;
+ const computedX = (mousePos.posX - transform.x) / transform.scale;
+
+ return computedX >= posX && computedY >= posY && computedX <= posX + width && computedY <= posY + height;
+ });
+ return aspect;
+ },
+
+ /** @param {MouseEvent} event */
+ updateMouse(event) {
+ const button = event.type === "mouseleave" ? undefined : tree.getButtonUnderMouse(event);
+ tree.updateTooltip(button);
+ if (button === undefined) {
+ tree.previousButton = button;
+ }
+ },
+
+ /** @param {MouseEvent} event */
+ click(event) {
+ const button = tree.getButtonUnderMouse(event);
+ if (button === undefined) {
+ return;
+ }
+
+ // If it was clicked using touch, first touch on a button opens the tooltip and second touch purchases
+ // if unsure, treat it as a touch
+ const isMouseClick = event.pointerType === "mouse" || event.originalEvent.mozInputSource === 1 || event.originalEvent.mozInputSource === 2;
+ if (typeof button.clicked === "function" && (isMouseClick || tree.previousButton?.name === button?.name)) {
+ button.clicked(event);
+ }
+ tree.previousButton = button;
+ requestAnimationFrame(tree.render);
+ },
+ render() {
+ const context = tree.context;
+ if (context === undefined) return;
+ const transform = tree.panzoom.getTransform();
+
+ // For some reason, it scrolls indefinitely if you don't set this every frame
+ // I have no idea how or why
+ context.canvas.width = CANVAS_WIDTH;
+ context.canvas.height = CANVAS_HEIGHT;
+
+ // setting font for later
+ context.font = "12px Verdana";
+
+ // Only one call to getComputedStyle for two properties, otherwise we'd use sharkcolor.getElementColor
+ // Also, beware that these values change if the button is pressed, but that should never happen in the same frame
+ // as the aspect tree gets redrawn
+ const buttonStyle = getComputedStyle(document.getElementById("backToGateway"));
+ const buttonColor = buttonStyle.backgroundColor;
+ const borderColor = buttonStyle.borderTopColor;
+
+ context.save();
+ // FIXME: Hard-coded color
+ context.fillStyle = "#155c4b";
+ context.fillRect(0, 0, context.canvas.width, context.canvas.height);
+ context.restore();
+
+ context.translate(transform.x, transform.y);
+ context.scale(transform.scale, transform.scale);
+
+ if (gateway.completedWorlds.length >= 3) {
+ context.save();
+ context.lineWidth = 5;
+ context.strokeStyle = buttonColor;
+ context.setLineDash([15, 15]);
+ context.beginPath();
+ context.moveTo(420, -1000);
+ context.lineTo(420, 2000);
+ context.stroke();
+ context.restore();
+
+ context.save();
+ context.fillStyle = getComputedStyle(document.getElementById("backToGateway")).color;
+ context.fillText("em missões de descoberta", 440, 10);
+ context.fillText("apenas aspectos centrais são ativos", 440, 25);
+ context.fillText("aspectos superficiais ->", 440, 60);
+ context.fillText("<- aspectos centrais", 300, 60);
+ context.fillText("em missões de descoberta", 440, 710);
+ context.fillText("apenas aspectos centrais são ativos", 440, 725);
+ context.fillText("aspectos superficiais ->", 440, 680);
+ context.fillText("<- aspectos centrais", 300, 680);
+ context.restore();
+ }
+
+ // Lines between aspects
+ context.save();
+ context.lineWidth = 5;
+ _.each(SharkGame.Aspects, ({ level, posX, posY, width, height, requiredBy, deprecated }) => {
+ if (deprecated) return;
+
+ if (level) {
+ // requiredBy: array of aspectId that depend on this aspect
+ _.each(requiredBy, (requiringId) => {
+ const requiring = SharkGame.Aspects[requiringId];
+ if (requiring.deprecated) return;
+
+ context.save();
+
+ const startX = posX + width / 2;
+ const startY = posY + height / 2;
+
+ const endX = requiring.posX + requiring.width / 2;
+ const endY = requiring.posY + requiring.height / 2;
+
+ const gradient = context.createLinearGradient(startX, startY, endX, endY);
+ gradient.addColorStop(0, buttonColor);
+ gradient.addColorStop(1, borderColor);
+ context.strokeStyle = gradient;
+
+ if (requiring.level === 0) {
+ if (requiring.getUnlocked()) {
+ context.filter = "brightness(40%)";
+ } else {
+ context.filter = "brightness(65%)";
+ }
+ }
+
+ context.beginPath();
+ context.moveTo(startX, startY);
+ context.lineTo(endX, endY);
+
+ context.stroke();
+ context.restore();
+ });
+ }
+ });
+ context.restore();
+
+ // Aspects
+ context.save();
+ context.lineWidth = 1;
+ context.fillStyle = buttonColor;
+ context.strokeStyle = borderColor;
+ _.each(SharkGame.Aspects, ({ posX, posY, width, height, icon, eventSprite, level, deprecated }, name) => {
+ if (deprecated) return;
+
+ context.save();
+ const reqref = tree.requirementReference[name];
+ if (!reqref.revealed) {
+ // if any prerequisite is unmet and we dont have infinity vision, and it's level 0, don't render
+ return;
+ } else if (level === 0) {
+ if (reqref.locked) {
+ // if not unlocked, render even darker and more saturated
+ context.filter = "brightness(25%) saturate(160%)";
+ } else if (reqref.prereqsMet) {
+ // if not bought but can be bought, render a little darker and more saturated
+ context.filter = "brightness(70%) saturate(150%)";
+ } else {
+ // if we reach this statement then it is revealed by infinity vision
+ // render darker and more saturated
+ context.filter = "brightness(40%) saturate(150%)";
+ }
+ }
+
+ tree.renderButton(context, posX, posY, width, height, icon, eventSprite, name);
+
+ context.restore();
+ });
+ context.restore();
+
+ // Static buttons
+ context.save();
+ // revert zooming
+
+ context.scale(1 / transform.scale, 1 / transform.scale);
+ context.translate(-transform.x, -transform.y);
+
+ context.lineWidth = 1;
+ context.fillStyle = buttonColor;
+ context.strokeStyle = borderColor;
+ _.each(tree.staticButtons, ({ posX, posY, width, height, icon, eventSprite }, name) => {
+ const button = tree.staticButtons[name];
+ if (!button.getUnlocked || button.getUnlocked()) {
+ if (button.getOn && button.getOn()) {
+ context.fillStyle = borderColor;
+ }
+ tree.renderButton(context, posX, posY, width, height, icon || "aspects/static/" + name, eventSprite, name);
+ context.fillStyle = buttonColor;
+ }
+ });
+ context.restore();
+
+ // update essence count
+ tree.updateEssenceCounter();
+ },
+
+ /**
+ * Draws a rounded rectangle using the current state of the canvas
+ * @param {CanvasRenderingContext2D} context
+ * @param {number} posX The top left x coordinate
+ * @param {number} posY The top left y coordinate
+ * @param {number} width The width of the rectangle
+ * @param {number} height The height of the rectangle
+ * @param {string} icon The icon to draw in the rectangle
+ * @param {string} name The name of the button
+ */
+ renderButton(context, posX, posY, width, height, icon = "general/missing-action", eventIcon = false, name) {
+ context.beginPath();
+ context.moveTo(posX + BUTTON_BORDER_RADIUS, posY);
+ context.lineTo(posX + width - BUTTON_BORDER_RADIUS, posY);
+ context.quadraticCurveTo(posX + width, posY, posX + width, posY + BUTTON_BORDER_RADIUS);
+ context.lineTo(posX + width, posY + height - BUTTON_BORDER_RADIUS);
+ context.quadraticCurveTo(posX + width, posY + height, posX + width - BUTTON_BORDER_RADIUS, posY + height);
+ context.lineTo(posX + BUTTON_BORDER_RADIUS, posY + height);
+ context.quadraticCurveTo(posX, posY + height, posX, posY + height - BUTTON_BORDER_RADIUS);
+ context.lineTo(posX, posY + BUTTON_BORDER_RADIUS);
+ context.quadraticCurveTo(posX, posY, posX + BUTTON_BORDER_RADIUS, posY);
+ context.closePath();
+ context.fill();
+ context.stroke();
+ if (icon !== null) {
+ if (icon === "general/missing-action" && SharkGame.Sprites["aspects/" + name]) {
+ icon = "aspects/" + name;
+ }
+ let sprite = SharkGame.Sprites[icon];
+ if (sprite === undefined) {
+ sprite = SharkGame.Sprites["general/missing-action"];
+ }
+ context.drawImage(
+ eventIcon ? EVENT_SPRITE_SHEET : SPRITE_SHEET,
+ sprite.frame.x,
+ sprite.frame.y,
+ sprite.frame.w,
+ sprite.frame.h,
+ posX,
+ posY,
+ width,
+ height,
+ );
+ }
+ const textToDisplay = tree.getLittleLevelText(name);
+ if (textToDisplay) {
+ context.fillStyle = getComputedStyle(document.getElementById("backToGateway")).color;
+ context.fillText(textToDisplay, posX + width + 5, posY + height / 2);
+ // revert back to the previous fillStyle right afterward
+ context.fillStyle = getComputedStyle(document.getElementById("backToGateway")).backgroundColor;
+ }
+ },
+
+ getLittleLevelText(aspectName) {
+ const reqref = tree.requirementReference[aspectName];
+ if (!reqref) return;
+
+ if (!reqref.locked && reqref.prereqsMet) {
+ if (!reqref.max) {
+ return SharkGame.Aspects[aspectName].level + " / " + SharkGame.Aspects[aspectName].max;
+ }
+ return "MAX";
+ }
+ },
+
+ handleClickedAspect(aspect) {
+ if (tree.refundMode) {
+ if (!aspect.noRefunds) tree.refundLevels(aspect);
+ tree.updateRequirementReference();
+ tree.render();
+ } else if (tree.debugMode) {
+ tree.setLevel(aspect, prompt("Set to what level?"));
+ } else {
+ tree.increaseLevel(aspect);
+ }
+ tree.updateTooltip(aspect);
+ },
+
+ increaseLevel(aspect, ignoreRestrictions) {
+ let cost = 0;
+ if (!ignoreRestrictions) {
+ if (
+ aspect.level >= aspect.max ||
+ aspect.getUnlocked() ||
+ _.some(aspect.prerequisites, (prereq) => SharkGame.Aspects[prereq].level === 0)
+ ) {
+ return;
+ }
+
+ cost = aspect.getCost(aspect.level);
+ if (cost > res.getResource("essence")) {
+ return;
+ }
+ }
+
+ res.changeResource("essence", -cost);
+ aspect.level++;
+ if (typeof aspect.apply === "function") {
+ aspect.apply("levelUp");
+ }
+ tree.updateRequirementReference();
+ },
+
+ updateEssenceCounter() {
+ if (document.getElementById("essenceCount")) {
+ document.getElementById("essenceCount").innerHTML = sharktext.beautify(res.getResource("essence"), false, 2);
+ }
+ },
+
+ applyAspects() {
+ _.each(SharkGame.Aspects, (aspectData) => {
+ if (aspectData.level && typeof aspectData.apply === "function") {
+ aspectData.apply("init");
+ }
+ });
+ },
+
+ respecTree(totalWipe) {
+ if (!totalWipe) {
+ _.each(SharkGame.Aspects, (aspect) => {
+ if (!aspect.noRefunds) {
+ this.refundLevels(aspect);
+ }
+ });
+ } else {
+ _.each(SharkGame.Aspects, (aspect) => {
+ this.refundLevels(aspect);
+ });
+ }
+ tree.updateRequirementReference();
+ if (SharkGame.Settings.current.doAspectTable === "planilha") {
+ this.drawTable(document.getElementById("aspectTable"));
+ this.updateEssenceCounter();
+ } else {
+ requestAnimationFrame(tree.render);
+ }
+ },
+
+ refundLevels(aspectData) {
+ let cost = 0;
+ while (aspectData.level) {
+ cost = aspectData.getCost(aspectData.level - 1);
+ if (_.isUndefined(cost)) cost = 0;
+ res.changeResource("essence", cost);
+ aspectData.level -= 1;
+ }
+ },
+
+ applyScoutingRestrictionsIfNeeded() {
+ if (gateway.currentlyOnScoutingMission()) {
+ if (!SharkGame.persistentFlags.aspectStorage) {
+ SharkGame.persistentFlags.aspectStorage = {};
+ }
+ $.each(SharkGame.Aspects, (aspectName, aspectData) => {
+ if (aspectData.core) {
+ return true;
+ }
+ SharkGame.persistentFlags.aspectStorage[aspectName] = aspectData.level;
+ SharkGame.Aspects[aspectName].level = 0;
+ });
+ }
+ },
+
+ resetScoutingRestrictions() {
+ if (!SharkGame.persistentFlags.aspectStorage) {
+ SharkGame.persistentFlags.aspectStorage = {};
+ }
+ $.each(SharkGame.Aspects, (aspectName) => {
+ if (!_.isUndefined(SharkGame.persistentFlags.aspectStorage[aspectName])) {
+ SharkGame.Aspects[aspectName].level = SharkGame.persistentFlags.aspectStorage[aspectName];
+ SharkGame.persistentFlags.aspectStorage[aspectName] = undefined;
+ }
+ });
+ },
+
+ updateTooltip(button) {
+ const tooltipBox = $("#tooltipbox");
+ const context = tree.context;
+ // tooltips aren't needed on the aspect table
+ if (!context) return;
+ if (button === undefined) {
+ context.canvas.style.cursor = "grab";
+ tooltipBox.empty().removeClass("forAspectTree forAspectTreeUnpurchased forAspectTreeAffordable");
+ } else {
+ let name;
+ _.forEach(tree.staticButtons, (buttonData, buttonName) => {
+ if (buttonData.name === button.name) {
+ name = buttonName;
+ return false;
+ }
+ });
+
+ if (!name) {
+ _.forEach(SharkGame.Aspects, (aspectData, aspectName) => {
+ if (aspectData.name === button.name) {
+ name = aspectName;
+ return false;
+ }
+ });
+ } else {
+ // we have a static button
+ if (!button.getUnlocked || button.getUnlocked()) {
+ tooltipBox.html(button.getEffect()).removeClass("forAspectTree forAspectTreeAffordable").addClass("forAspectTreeUnpurchased");
+ context.canvas.style.cursor = "pointer";
+ }
+ return;
+ }
+
+ if (!name) {
+ tooltipBox.empty().removeClass("forAspectTree forAspectTreeUnpurchased forAspectTreeAffordable");
+ return;
+ }
+
+ const reqref = tree.requirementReference[name];
+
+ if (!reqref) {
+ tooltipBox.empty().removeClass("forAspectTree forAspectTreeUnpurchased forAspectTreeAffordable");
+ return;
+ }
+
+ const cost = button.getCost(button.level);
+
+ if (!reqref.prereqsMet) {
+ if (button.level) {
+ context.canvas.style.cursor = "not-allowed";
+ } else {
+ context.canvas.style.cursor = "grab";
+ }
+ } else {
+ context.canvas.style.cursor = "pointer";
+ }
+
+ tooltipBox.addClass("forAspectTree").removeClass("forAspectTreeUnpurchased").removeClass("forAspectTreeAffordable");
+ if (reqref.locked) {
+ tooltipBox.addClass("forAspectTreeUnpurchased").html(sharktext.boldString(button.getUnlocked()));
+ return;
+ }
+
+ const refundValue = tree.getTheoreticalRefundValue(button);
+ if (tree.refundMode) {
+ if (refundValue && !button.noRefunds) {
+ tooltipBox.addClass("forAspectTreeAffordable");
+ }
+ } else if (reqref.affordable && !reqref.max && reqref.prereqsMet) {
+ tooltipBox.addClass("forAspectTreeAffordable");
+ }
+
+ let tooltipText = "";
+ if (button.level === 0) {
+ let costText = "";
+ if (tree.refundMode) {
+ if (button.noRefunds) {
+ costText = "NO REFUNDS";
+ }
+ } else {
+ costText = `COST: ${cost} ESSENCE`;
+ }
+
+ const levelText =
+ (button.core ? " core aspect" : "") + (button.core && button.noRefunds ? ", " : "") + (button.noRefunds ? "no refunds" : "");
+
+ tooltipText =
+ sharktext.boldString(button.name) +
+ ` ${levelText}` +
+ ` ${button.getEffect(1)} ` +
+ `${button.description} ` +
+ (tree.debugMode ? "" : "" + `${costText}`);
+ tooltipBox.addClass("forAspectTreeUnpurchased");
+ } else if (button.level < button.max) {
+ let costText = "";
+ if (tree.refundMode) {
+ if (button.noRefunds) {
+ costText = "SEM REEMBOLSO";
+ } else {
+ costText = `VALOR DO REEMBOLSO: ${refundValue}`;
+ }
+ } else {
+ costText = `CUSTO: ${cost} ESSÊNCIA`;
+ }
+
+ const levelText =
+ "level " +
+ button.level +
+ " " +
+ (button.core ? " core aspect" : " aspect") +
+ (button.noRefunds ? ", no refunds" : "");
+
+ tooltipText =
+ sharktext.boldString(button.name) +
+ ` ${levelText} ` +
+ button.getEffect(button.level) +
+ ` ${button.description}` +
+ "" +
+ "NEXT LEVEL: " +
+ button.getEffect(button.level + 1) +
+ (tree.debugMode ? "" : "" + `${costText}`);
+ } else if (button.level === undefined) {
+ const levelText =
+ (button.core ? " core aspect" : "") + (button.core && button.noRefunds ? ", " : "") + (button.noRefunds ? "no refunds" : "");
+ tooltipText =
+ sharktext.boldString(button.name) +
+ ` ${levelText}` +
+ ` ${button.getEffect(button.level)}` +
+ ` ${button.description}`;
+ } else {
+ let costText = "";
+ if (tree.refundMode) {
+ if (button.noRefunds) {
+ costText = "SEM REEMBOLSO";
+ } else {
+ costText = `VALOR DO REEMBOLSO: ${refundValue}`;
+ }
+ } else {
+ costText = "NÍVEL MÁXIMOS.";
+ }
+
+ const levelText =
+ "level " +
+ button.level +
+ " " +
+ (button.core ? " core aspect" : " aspect") +
+ (button.noRefunds ? ", no refunds" : "");
+ tooltipText =
+ sharktext.boldString(button.name) +
+ ` ${levelText}` +
+ ` ${button.getEffect(button.level)}` +
+ ` ${button.description}` +
+ (tree.debugMode ? "" : "" + `${costText}`);
+ }
+ tooltipBox.html(tooltipText);
+ }
+ },
+
+ generateRequirementReference() {
+ tree.requirementReference = {};
+ _.forEach(SharkGame.Aspects, (aspectData, aspectName) => {
+ if (aspectData.deprecated) return;
+ tree.requirementReference[aspectName] = {};
+ });
+ tree.updateRequirementReference();
+ },
+
+ updateRequirementReference() {
+ const reqref = tree.requirementReference;
+
+ _.forEach(SharkGame.Aspects, (aspectData, aspectName) => {
+ if (aspectData.deprecated) return;
+ reqref[aspectName].affordable = aspectData.getCost(aspectData.level) <= res.getResource("essence");
+ reqref[aspectName].locked = aspectData.getUnlocked() || false;
+ reqref[aspectName].prereqsMet = !_.some(aspectData.prerequisites, (prereq) => SharkGame.Aspects[prereq].level === 0);
+ reqref[aspectName].isolated = aspectData.level && !reqref[aspectName].prereqsMet;
+ reqref[aspectName].revealed = reqref[aspectName].prereqsMet || SharkGame.Aspects.infinityVision.level || aspectData.level;
+ reqref[aspectName].max = aspectData.level >= aspectData.max;
+ });
+ },
+
+ toggleRefundMode() {
+ if (tree.refundMode) {
+ tree.refundMode = false;
+ $("#respecModeButton").removeClass("respecMode");
+ } else {
+ tree.refundMode = true;
+ $("#respecModeButton").addClass("respecMode");
+ if (tree.debugMode) tree.toggleDebugMode();
+ }
+ },
+
+ toggleDebugMode() {
+ if (tree.debugMode) {
+ tree.debugMode = false;
+ $("#debugModeButton").removeClass("respecMode");
+ } else {
+ tree.debugMode = true;
+ $("#debugModeButton").addClass("respecMode");
+ if (tree.refundMode) tree.toggleRefundMode();
+ }
+ },
+
+ getTheoreticalRefundValue(aspect) {
+ if (!aspect.level || aspect.noRefunds) return 0;
+
+ let value = 0;
+ let level = aspect.level - 1;
+ while (level >= 0) {
+ value += aspect.getCost(level);
+ level -= 1;
+ }
+ return value;
+ },
+
+ // will loop increase and decrease levels
+ setLevel(aspect, targetLevel) {
+ if (isNaN(targetLevel)) return;
+ targetLevel = Math.ceil(targetLevel);
+ if (targetLevel < 0) return;
+
+ if (targetLevel - aspect.level < 0) {
+ aspect.level = 0;
+ }
+
+ while (targetLevel - aspect.level > 0) {
+ tree.increaseLevel(aspect, true);
+ }
+ },
+};
diff --git a/js/BRfacts.js b/js/BRfacts.js
new file mode 100644
index 00000000..7136f190
--- /dev/null
+++ b/js/BRfacts.js
@@ -0,0 +1,318 @@
+SharkGame.FunFacts = {
+ dilutedResources: ["shark", "ray", "crab", "fish", "science"], // dilute these while not in starter to keep the fun facts fresher
+
+ showFact() {
+ log.addMessage(this.getFact());
+ },
+
+ getFact() {
+ const pool = this.getPool();
+ return SharkGame.choose(pool);
+ },
+
+ getPool() {
+ const pool = [];
+ const currentWorld = world.worldType;
+ if (
+ this.worldBased[currentWorld] &&
+ (!this.worldBased[currentWorld].areRequirementsMet || this.worldBased[currentWorld].areRequirementsMet())
+ ) {
+ _.each(this.worldBased[currentWorld].messages, (fact) => {
+ pool.push(sharktext.boldString("Fato sobre o mundo: ") + `${fact}`);
+ });
+ }
+
+ let anyAvailableResource = false;
+ $.each(this.resourceBased, (resource, facts) => {
+ // purposefully dilute some facts if we are not on the starter world
+ // I want these facts to be more likely relevant than not
+ if (world.doesResourceExist(resource) && res.getTotalResource(resource)) {
+ anyAvailableResource = true;
+ if (!this.dilutedResources.includes(resource) || currentWorld === "start" || Math.random() < 0.25) {
+ _.each(facts, (fact) => {
+ pool.push(
+ sharktext.boldString(
+ `Fato de
+ ${sharktext.getResourceName(
+ resource,
+ false,
+ 1,
+ SharkGame.Log.isNextMessageEven()
+ ? sharkcolor.getVariableColor("--color-dark")
+ : sharkcolor.getVariableColor("--color-med"),
+ )}: `,
+ ) + `${fact}`,
+ );
+ });
+ }
+ }
+ });
+
+ if (anyAvailableResource) {
+ // only 10% chance to include the 'default' facts
+ // this is because those facts are seen all over the place
+ // they would end up diluting the world-specific and resource-specific facts
+ //
+ // also acts as a failsafe in case there are no other facts to display
+ if (Math.random() < 0.1 || pool.length === 0) {
+ _.each(this.default, (fact) => {
+ pool.push(sharktext.boldString("Fun fact: ") + `${fact}`);
+ });
+ }
+ return pool;
+ } else {
+ return ["Fato: Novos fatos são desbloqueados com você vendo. Keep playing to unlock some!"];
+ }
+ },
+
+ worldBased: {
+ frigid: {
+ messages: ["Água expande no processo de congelamento. É por isso que garrafas cheias de água quebram ou estouram se forem postas no congelador."],
+ },
+ volcanic: {
+ messages: [
+ "Esse mundo originalmente se chamava 'Violento' antes de virar Vulcânico. Jogadores de teste ficaram confusos acharam que o mundo tinha violência quando, na verdade, era apenas a ameaça de violência.",
+ "Fontes hidrotermais cospem fogo na vida real. Apenas fumaça.",
+ "Fontes hidrotermais mantêm uma grande parte da vida marinha porque liberam grandes quantidades de minerais. Bactérias se alimentam desses minerais e elas servem como a base dale várias cadeias alimentares.",
+ "Fontes hidrotermais são encontradas em quebras na crosta terrestre, onde a água fica superaquecida por causa do calor do magma mais perto do solo oceânico que o normal.",
+ ],
+ areRequirementsMet() {
+ return SharkGame.Upgrades.purchased.includes("thermalVents");
+ },
+ },
+ shrouded: {},
+ abandoned: {
+ messages: ["'Abandonado' foi o primeiro mundo a ser refeito para Novas Fronteiras."],
+ },
+ haven: {
+ messages: ["Papel de alga é real. Mas não é possível escrever nele."],
+ areRequirementsMet() {
+ return SharkGame.Upgrades.purchased.includes("sunObservation");
+ },
+ },
+ marine: {},
+ tempestuous: {
+ messages: ["Diferente da língua inglesa, o português faz sentido. Portanto 'tempestuoso' significa 'algo que tem tempestades'."],
+ },
+ },
+
+ resourceBased: {
+ // add fish facts at some point
+ shark: [
+ "Muitas espécies de tubarão investigam coisas usando a boca. Isso tende a não ser bom para quem está sendo investigado.",
+ "Comportamentos sociais foram registrados entre tubarões-limão, e toda evidência aponta a eles preferirem estar uns com os outros do que estarem sozinhos.",
+ "Algumas espécies de tubarão tem 'imobilidade tônica' quando seu nariz é esfregado. Eles param de se mexer, aparentam extremamente relaxados e podem ficar assim por até 15 minutos antes de voltar ao estado normal.",
+ "Em algumas espécies, os ovos se chocam dentro das próprias mães, e entre esses espécies, os tubarõezinhos chocados comem os ovos não fertilizados e até mesmo irmãos dentro do ovo.",
+ "Mais pessoas morrem por ano ao serem alvejadas por um raio do que por ataques de tubarão.",
+ "Tubarões brancos já foram observados usando linguagem corporal para demonstrar submissão e dominância entre si sem usar violência.",
+ "Uma bitoca de um tubarão pode te tornar imortal. Mas apenas se o tubarão quiser.",
+ "Mais vale um tubarão na mão do que dois a voar. Isso se dá porque tubarões não conseguem voar.",
+ "Em termos evolutivos, tubarões são muito velhos. Os primeiros tubarões surgiram há mais ou menos 400 milhões de anos.",
+ "Tubarões tem pele muito grossa, parecendo uma lixa. Tanto que pele de tubarão era usada antigamente para lixar coisas.",
+ "Tubarões não tem ossos. Arraias também não.",
+ ],
+ crystal: ["Cristais mágicos provavelmente não são reais."],
+ ray: [
+ "Pode-se pensar em arraias como tubarões achatados. Ambos são muito parecidos geneticamente.",
+ "Arraias são as tapiocas do oceano. (nota de rodapé: citação necessária)",
+ "Arraias não tem ossos. Tubarões também não.",
+ "Recentemente, uma terceira espécie de jamanta foi descoberta na costa brasileira. Faz o L.",
+ "Algumas arraias tem um ferrão venenoso. Então mesmo que queiramos muito, não deveríamos abraçá-las.",
+ ],
+ crab: [
+ "Ao longo da história, muitas espéciesde crustáceos independentemente se desenvolveram até virarem caranguejos. Deram até o nome de 'carcinização' a esse fenômeno.",
+ "Muitas espécies de caranguejos têm algum tipo de assimetria de garra. Eles têm tamanhos e formatos diferentes que dão para cada garra um trabalho especializado.",
+ ],
+ octopus: [
+ "Se tem 8 tentáculos, é um polvo.",
+ "Polvos são capazes de se camuflar muito bem. Eles podem mudar de cor, padrãoe textura para combinar com o ambiente, é o suficiente para confundir qualquer animal, até mesmo humanos.",
+ "Em condições específicas, polvos conseguem resolverem problemas simples. Eles até ficam confusos com problemas difícieis e tiram tempo de seu dia para contemplar soluções possíveis.",
+ "Polvos ficam entediados em cativeiro. Para se distrair, eles podem brincar com objetos ou interagir com humanos em volta.",
+ "Polvos são extremamente hábeis. Eles conseguem usar seus tentáculos em uma grande variedade de jeitos para mexer objetos.",
+ "Polvos não tem ossos algum.",
+ "Cada tentáculo de um polvo é considerado ter um cérebro próprio. Podemos pensar neles como soldados (pequenos cérebros) sendo comandados por um chefe no meio (um cérebro grande).",
+ ],
+ dolphin: [
+ "Golfinhos são considerados um dos animais mais inteligentes de múltiplas formas, semelhantemente a macacos, elefantes e papagaios.",
+ "Golfinhos não são tão cheios de si na vida real. Provavelmente. Talvez.",
+ "Golfinhos são criativos e capazes de pensamento abstrato. Em cativeiro, eles podem ser pedidos para inventar novos truques e muitas vezes o farão.",
+ "Já se observou golfinhos comunicando diretamente uns com os outros. Tanto que, alguns acreditam que eles podem ter conversas coerentes entre si.",
+ ],
+ whale: [
+ "As 10 maiores espécies conhecidas no mundo são todas baleias.",
+ "Se uma baleia um dia conseguisse uma arma e atirasse em outra, os jornais diriam: Baleia baleia baleia.",
+ "Enquanto algumas baleias caçam ativamente, outras apenas filtram a água em busca de plankton. Nós não especificamos qual tipo são as baleias deste mundo.",
+ "A maioria das baleias são criaturas sociais. A maioria das baleias andam juntas em baleais, que podem formar clãn, e então comunidades. (porém, também existem baleias solitárias)",
+ "Não se sabe exatamente o porquê do canto das baleias, mas cientistas concordam que tem algum propósito social." /* Whales are observed to react to each other's songs and come to */,
+ ],
+ urchin: [
+ "Ouriços comem principalmente algas. Muita alga.",
+ "Já foi observadoque ouriços vão vestir diferentes items em cima de si mesmos, como pedras. Se você os der um chapeuzinho, eles vão vesti-lo também. Ainda se debate do porquê deles fazerem isso.",
+ "A maioria dos ouriços não são venenosos.",
+ "Os espinhos da maioria dos ouriços não são afiados. Tanto que muitas espécies podem ser seguradas na mão.",
+ ],
+ squid: [
+ "Lulas comem caranguejos. Elas só não comem os seus por respeito.",
+ "Lulas gigantes são reais. Elas vivem nas produndezas do oceano.",
+ "Lulas não tem ossos algum.",
+ "Se tem 8 tentáculos, não é uma lula.",
+ "Lula da Silva é o 35° e 39° presidente do Brasil, e não tem nenhuma relação com o animal.",
+ "Algumas espécies de lula têm sacos de tinta na sua pele que expandem ao ser puxados por músculos específicos, possibilitando sua camuflagem.",
+ // Based on https://www.youtube.com/watch?v=0wtLrlIKvJE
+ ],
+ lobster: [
+ "Lagostas realmente comem mexilhões. Eles instintivamente os quebram para abri-los.",
+ "Por causa de uma pequena peculiaridade biológica, lagostar são muito resistentes ao envelhecimento e podem viver por muito tempo. Alguns vão até viver mais que você.",
+ "Lagostas tem dentes dentro de seu estômago, não na boca, e eles mastigam com esse dentes.",
+ "Lagostas têm garras assimétricas. A maior, chamada de esmagadora, é usada para esmagar. A outra, chamada de estripadora, é usada para estripar. Biólogos marinhos estavam inspirados naquele dia.",
+ ],
+ shrimp: [
+ "Realmente existem camarões sociais que vivem em comunidades ao redor de esponjas de recifes, eles têm até rainhas lá.",
+ "Camarões são primos próximos de lagostas. Eles têm muitas similaridades, em algumas maneiras eles são apenas lagostas, só que menores e magrinhas..",
+ ],
+ eel: [
+ "Enguias podem ter tamanhos muito diferentes, desde poucos centímetros até vários metros.",
+ "O maior choque já registrado por uma enguia foi de 860 volts, mais do que qualquer outro animal!",
+ "Enguias europeias migram uma distância de 5,000 até 10,000 km pelo Oceano Atlântico para chegar no Mar dos Cargaços.",
+ ],
+ chimaera: [
+ "Quimeras têm uma ancestralidade comum próxima a tubarões e arraias.",
+ "Quimeras são animais de oceano profundo, normalmente são achados abaixo de 500 metros (~1.5 estádios do Maracanã) da superfície da água.",
+ "A maioria das espécies de quimera têm um espinho venenoso na frente de sua barbatana superior.",
+ "Quimeras não são roxas, elas são pálidas. Elas não se importam com cores bonitas porque animais das profudezas do mar não conseguem ver nada mesmo.",
+ "Quimeras não têm ossos. Da mesma maneira que tubarões e arraias.",
+ "Em muitas espécies de quimera, o focinho contém um órgão que detecta campos elétricos, como aqueles presentes em um batimento cardíaco, por exemplo.",
+ ],
+ billfish: [
+ "Billfish do indeed have bones, unlike sharks and rays.",
+ "Swordfish and marlins are large, predatory fish. At adulthood, their only natural predators are sharks (oh no) and whales.",
+ "Normalmente é noticiado que a velocidade máxima de marlins chega a quase 100 km/h, mas isso está errado. É mais próximo de 50 km/h.",
+ "The bill of a billfish is used to slash like a sword, not stab like a spear.",
+ "Swordfish are not a group of fish, they are a single species: Xiphias gladius.",
+ "Swordfish, spearfish, and marlins are part of a larger group of fish called billfish (the group featured in this game), of which there are only 12 species.",
+ ],
+ seaApple: [
+ "Holotúrias são um tipo de pepino-do-mar. Eles se alimentam de restos e migalhas no solo.",
+ "Holotúrias reais não são atraídas de forma alguma a algas. É só as desse jogo que são estranhas.",
+ ],
+ jellyfish: [
+ // "Sharks would definitely not have a way of acquiring most kinds of jellyfish in real life.",
+ "Águas-vivas podem ser extremamente perigosas. A picada de algumas vespas-do-mar consegue matar um adulto.",
+ "Turritopsis dohrnii is a species of jellyfish that can restart its lifecycle at will. In theory, this grants it an infinite lifespan.",
+ "Jellyfish are very old, evolutionarily speaking. A few jellyfish fossils have been dated to approximately 500 million years ago.",
+ "O sistema digestivo de águas-vivas só tem um buraco, o que quer dizer que comida sai pelo mesmo lugar que entra. Eca.",
+ "Águas-vivas são do filo 'cnidaria', o mesmo filo de anêmonas.",
+ "Apesar de seus nomes e aparência, águas-vivas-de-pente não tem nada haver com águas-vivas. Elas são de filos diferentes.",
+ // do more research into jellies
+ // On it, boss -Biggest Brian
+ ],
+ sharkonium: [
+ "Não tem nada suspeito nas máquinas.",
+ "Máquinas de tubarônio de pequeno e médio porte não precisam de uma fonte de energia externa, já que tubarônio é feito com cristais que emana magia de dentro de si.",
+ "Para uma pessoa, tubarônio lembra ouro roxo. Para um tubarão, parece como um troço brilhante.",
+ "Tubarônio não vai ter gosto algum de uva. Não, eu não vou te deixar testar.",
+ ],
+ // I just decided to put something in to complete the sentence, it was driving me nuts -Biggest Brian
+ porite: [
+ "A ideia para porita veio da estrutura de ossos, cuja medula é esponjosa para reduzir o peso enquanto mantém sua força.",
+ "Porita é mais forte que vidro, mas quebra sob pressão constante, então só deve ser usado para fazer ferramentas, não máquinas.",
+ "Porita é um tipo de vidro. Derrete a uma temperatura relativamente baixa e pode ser moldado facilmente, então ferramentas quebradas podem ser rapidamente recicladas.",
+ "Não, você não pode comer.",
+ ],
+ calcinium: [
+ "Calcinício foi inspirado pela aparência e textura de calcário e conchas.",
+ "Toma muita energia para fazer e, após formado, calcinício não derrete fácil. Cada fornada tem que ser moldada rápido, se não vai ter que ir pro lixo.",
+ "Calcinício é um material bem versátil. Se esfriar rápido, é uma cerâmica frágil - mas esfriado lentamente, é um plástico forte. E as lagostas usam ambas versões.",
+ "Enquanto que calcinício pareça com merengue, o gosto não tem nada haver.",
+ ],
+ laser: [
+ "Juntar tubarões com lasers é muito 2010, sabe? 'Arraia laser' é um trocadilho, então é muito melhor.",
+ "Areia provavelmente não se funde em crystais mágicos. A não ser que você conte vidro.",
+ "Nós não sabemos como que as arraias conseguem amarrar lasers em si mesmas. Apenas os tubarões sabem.",
+ "Arraias laser pegam a energia diretamente do calor de fontes hidrotermais, então elas estão presas a uma área relativamente pequena.",
+ "Normalmente, a energia do laser de uma arraia não é muito quente. Por isso toma muito tempo (e areia) para fundir qualquer coisa direito.",
+ ],
+ coral: [
+ "Alguns corais conseguem caçar peixes pequenos.",
+ "Corais não são plantas, mas sim animais. Um estranho animal estacionário.",
+ "Corais são principalmente carnívoros. Eles comem plankton (coisas muito pequenas que não sabem nadar) grudando neles com os tentáculos e os puxando para suas bocas.",
+ "Muitos corais têm uma relação mutualística com espécies de algas, que produzem nutrientes em troca de gás carbônico e abrigo.",
+ "Mesmo sendo paradão como uma esponja, coral é mais próximo de águas-vivas.",
+ ],
+ sponge: [
+ "Sponges are incredibly distinct from all other animals. They are asymmetric, have no organs, and their cells can change specialization at will.",
+ "Sponges are incredibly, incredibly old, evolutionarily speaking. They probably date back at least 600 million years.",
+ "Sponge is not a plant, it is an animal. A weird, amorphous animal.",
+ "The pores in sponges are designed to help them filter water for food at maximum efficiency.",
+ "Many species of sponge have a mualistic realitionship with species of algae. The algaes use photosynthesis to produce food for the sponges.",
+ "Sponges have bacteria inside their own cells that help with the metabolism of many substances. This is called 'endosymbiosis'.",
+ "The first animal formed, the 'urmetazoa', was something akin to a sponge.",
+ ],
+ algae: [
+ "Algas podem ter todos tipo de formato e tamanho. Como a espécie 'valonia ventricosa,' que possui células com seus interiores interligados, de forma que eles podem ser considerados uma única célula enorme.",
+ "Algas não são nem plantas nem animais. Mas ou bactérias (reino monera) ou protozoários (reino protista).",
+ "Microalgas, assim como todas as outras algas, não são plantas. O mar tem poucas plantas de verdade.",
+ "Em português é difícil escrever os fatos sobre algas, porque não temos palavra diferente para algas unicelulares e multicelulares, como em inglês.",
+ ],
+ kelp: ["Algas marinhas não são plantas. Elas são um tipo de alga, que é uma coisa diferente."],
+ seagrass: [
+ "Differente de algas marinhas, ervas marinhas não plantas de verdade. Uma das poucas do fundo do mar.",
+ "Flores de ervas marinhas não parecem de forma alguma com as flores que estamos acostumados a ver.",
+ ],
+ arcana: [
+ "Cristais mágicos super-poderosos definitivamente não existem na real.",
+ "Essas pedras estouram que nem estalinho de festa junina quando quebrados. Que daora!",
+ ],
+ sacrifice: ["Ninguém sabe como a energia dos cristais passa para o cardume."],
+ science: [
+ "Tubarões reais não sabem como fazer ciência. Provavelmente.",
+ "A maior parte da ciência no cardume é feita por tubarões cientistas de instituições públicas.",
+ "Os cientistas tubarões que negam a efetividade de vacinas têm um nome: Charlaturões.",
+ ],
+ sand: [
+ "No mundo real, o solo oceânico nem sempre é feito de areia. O sedimento do fundo do mar muitas vezes é bem mais fino.",
+ "Correntezas oceânicas podem carregar areia por distâncias enormes até alguma praia. E quanto mais longe é levada, mais fina a areia fica.",
+ ],
+ ancientPart: [
+ "O que eles fazem? Ainda não temos certeza.",
+ "Uma pessoa se lembraria de partes de trem olhando para esses pedaços. Mas ao olhar para ela, os tubarões se lembram de nada.",
+ "Do que é que são feitos? E eu sei lá!",
+ "As partes antigas tem uma textura de concreto pintado. E quando batemos eles, faz um barulho parecido com cerâmica.",
+ ],
+ investigator: ["Não temos certeza de onde os polvos conseguem seus chapéus de investigação. Acreditamos que eles só acham por aí."],
+ eggBrooder: ["Isso é nojento."],
+ collector: [
+ "A Dromia personata é uma espécie de caranguejo que pega esponjas e as grudam em suas costas como um jeito de se camuflar.",
+ "Não encoste na esponja em suas costas. Eles são superprotetores dela.",
+ ],
+ delphinium: [
+ "Para uma pessoa, golfínio parece glitter em cima de ouro azul. Para um tubarão, apenas parece como dor nos olhos.",
+ "A receita para fazer golfínio é uma receita anciã. Levaram gerações inteiras para aperfeiçoar o processo, pelo menos é o que eles dizem.",
+ "Os golfinhos são muito apegados ao golfínio eles o adora. Porém eles apreciam dd leve a praticidade do tubarônio.",
+ "Golfínio é pesado, e entorta sob pressão - mas é maleável o suficiente para ser moldado em formatos complexos.",
+ ],
+ ice: ["No jogo original, o gelo apenas consumia seus recursos passivamente ao invés de diminuir sua produção."],
+ tar: ["No jogo original, graxa se produzia sozinha. Máquinas quase não produziam graxa alguma."],
+ calciniumConverter: [
+ "Interfaces cérebro-máquina, como as usadas pelas lagostas, já existem desde antes de 2014.",
+ "Enquanto que a primeira interface cérebro-máquina tenha sido criada por tubarões cientistas em colaboração com as lagostas, no mundo real, quem desenvolveu foi o time do cientista brasileiro 'Miguel Niconelis'.",
+ "Interfaces cérebro-máquina podem ser facilmente feitas sem cirurgia, mas as lagostas acho que seria muito maneiro ter fios enfiados no crânio.",
+ ],
+
+ },
+
+ default: [
+ "O código original de 'Shark Game' veio de um jogo idle abandonado sobre abelhas. Agora quase não resta nenhum traço de abelhas!",
+ "A existênciade recursos que produzem recursos neste jogo foi inspirado pelo jogo 'Derivative Clicker'!",
+ "'Kitten Game' foi uma inspiração para esse jogo! Para a surpresa de 0 pessoas. A primeira mensagem do jogo é uma referência.",
+ "Tem uma surpreendente falta de biscoitos para um jogo clicker aqui.",
+ "Rêmoras foram banidas do oceano há anos. Os tubarões esperam que eles nunca mais voltem.",
+ "'Fatos' só vai falar sobre coisas que você tem desbloqueado no jogo.",
+ "O sistema de 'fatos' sempre esteve no código do jogo, mas não eram acessíveis até botarem esse botão de fatos.",
+ "Novas Fronteiras, esse mod de 'Shark Game', foi inspirado pelo estilo de descoberta de mecânicas dos jogos da série 'Candy Box' e 'A Dark Room'.",
+ "Quaisquer barreiras de progressão neste jogo podem ser perpassados com uma boa estratégia. Do latim estrategí.",
+ "Esse jogo tem atalhos. Eles podem ser bem úteis. Procure-os no menu de opções.",
+ "'Shark Game: Novas Fronteiras' é um mod do jogo feito por Cirrial 'Untitled Shark Game'. Começou como uma melhoria, mas virou um remake completo.",
+ "A tradução que você está vendo agora foi feita por uma pessoa sem permissão dos criadores do mod.",
+ ],
+};
diff --git a/js/BRgateway.js b/js/BRgateway.js
new file mode 100644
index 00000000..6bd856ab
--- /dev/null
+++ b/js/BRgateway.js
@@ -0,0 +1,1371 @@
+"use strict";
+
+SharkGame.Gateway = {
+ NUM_PLANETS_TO_SHOW: 3,
+
+ transitioning: false,
+ selectedWorld: "",
+
+ allowedWorlds: ["abandoned", "haven", "frigid", "shrouded", "marine", "volcanic", "tempestuous"],
+
+ completedWorlds: [],
+
+ planetPool: [],
+
+ init() {
+ this.completedWorlds = [];
+ this.planetPool = [];
+ SharkGame.wonGame = false;
+ SharkGame.gameOver = false;
+ },
+
+ setup() {
+ this.completedWorlds = this.completedWorlds.filter((worldType) => {
+ return Object.keys(SharkGame.WorldTypes).includes(worldType);
+ });
+ if (SharkGame.gameOver || gateway.badWorld) {
+ main.endGame(true);
+ gateway.badWorld = false;
+ } else {
+ gateway.updateScoutingStatus();
+ SharkGame.persistentFlags.wasOnScoutingMission = undefined;
+ }
+ },
+
+ enterGate(loadingFromSave) {
+ // To help diagnose negative time bug
+ // Can remove if/when that gets fixed
+ SharkGame.Save.createTaggedSave("PreGateway");
+ SharkGame.PaneHandler.wipeStack();
+
+ SharkGame.OverlayHandler.enterGateway();
+
+ // ensure buy buttons will be revealed
+ SharkGame.persistentFlags.revealedBuyButtons = true;
+
+ // be sure minute hand is off
+ res.minuteHand.toggleOff();
+ // be sure we're not paused
+ if (cad.pause) {
+ res.pause.togglePause();
+ }
+
+ tree.resetScoutingRestrictions();
+ gateway.updateWasScoutingStatus();
+
+ if (!loadingFromSave) {
+ SharkGame.persistentFlags.lastRunTime = sharktime.getRunTime();
+ if (SharkGame.wonGame) {
+ gateway.markWorldCompleted(world.worldType);
+ SharkGame.persistentFlags.destinyRolls = SharkGame.Aspects.destinyGamble.level;
+ gateway.preparePlanetSelection(gateway.NUM_PLANETS_TO_SHOW);
+ }
+ }
+
+ if (this.planetPool.length === 0) {
+ gateway.preparePlanetSelection(gateway.NUM_PLANETS_TO_SHOW);
+ }
+
+ if (!SharkGame.persistentFlags.minuteStorage) SharkGame.persistentFlags.minuteStorage = 0;
+ if (!SharkGame.flags.minuteHandTimer) SharkGame.flags.minuteHandTimer = 0;
+ if (!SharkGame.flags.requestedTimeLeft) SharkGame.flags.requestedTimeLeft = 0;
+ if (!SharkGame.flags.hourHandLeft) SharkGame.flags.hourHandLeft = 0;
+ const storedTime = SharkGame.flags.minuteHandTimer - SharkGame.flags.requestedTimeLeft - SharkGame.flags.hourHandLeft;
+ SharkGame.persistentFlags.minuteStorage += storedTime;
+
+ // make sure the player is flagged as having idled so the minute hand shows up from now on
+ res.minuteHand.allowMinuteHand();
+
+ const baseReward = gateway.getBaseReward(loadingFromSave);
+ const patienceReward = gateway.getPatienceReward(loadingFromSave);
+ const speedReward = gateway.getSpeedReward(loadingFromSave);
+ const gumptionBonus = gateway.getGumptionBonus(loadingFromSave);
+
+ gateway.ui.prepareBasePane(baseReward, patienceReward, speedReward, gumptionBonus, storedTime);
+ gateway.grantEssenceReward(baseReward, patienceReward, speedReward);
+
+ // store memories
+ SharkGame.Memories.elevateMemories();
+
+ // RESET COMPLETED GATE REQUIREMENTS
+ SharkGame.Gate.completedRequirements = {};
+ // clear non-persistent flags just in case
+ SharkGame.flags = {};
+
+ // SAVE
+ SharkGame.Save.saveGame();
+
+ $("#game").addClass("inGateway");
+ },
+
+ cleanUp() {
+ // empty out the game stuff behind
+ main.purgeGame();
+ },
+
+ rerollWorlds() {
+ if (SharkGame.persistentFlags.destinyRolls && SharkGame.persistentFlags.destinyRolls > 0) {
+ SharkGame.persistentFlags.destinyRolls -= 1;
+ gateway.preparePlanetSelection(gateway.NUM_PLANETS_TO_SHOW);
+ gateway.ui.showPlanets(true);
+ SharkGame.Save.saveGame();
+ }
+ },
+
+ preparePlanetSelection(numPlanets) {
+ // empty existing pool
+ gateway.planetPool = [];
+
+ // create pool of qualified types
+ const qualifiedPlanetTypes = gateway.allowedWorlds.slice(0);
+
+ // look for uncompleted planet types
+ const uncompletedPlanetTypes = gateway.allowedWorlds.slice(0);
+ _.each(gateway.completedWorlds, (worldtype) => {
+ const typeIndex = uncompletedPlanetTypes.indexOf(worldtype);
+ if (typeIndex > -1) {
+ uncompletedPlanetTypes.splice(typeIndex, 1);
+ }
+ });
+
+ // are there any? if so, set a random index out of the number of planets we're choosing
+ // the choice with this index is guaranteed to be an uncompleted planet
+ let guaranteeWhichWorld;
+ if (uncompletedPlanetTypes.length > 0) {
+ guaranteeWhichWorld = Math.floor(Math.random() * numPlanets);
+ }
+
+ // pull random types from the pool
+ // for each type pulled, generated a random level for the planet
+ // then add to the planet pool
+ for (let i = 0; i < numPlanets; i++) {
+ let choice;
+ if (uncompletedPlanetTypes.length > 0 && guaranteeWhichWorld === i) {
+ choice = SharkGame.choose(uncompletedPlanetTypes);
+ } else {
+ choice = SharkGame.choose(qualifiedPlanetTypes);
+ }
+ const index = qualifiedPlanetTypes.indexOf(choice);
+ // take it out of the qualified pool (avoid duplicates)
+ qualifiedPlanetTypes.splice(index, 1);
+
+ if (uncompletedPlanetTypes.indexOf(choice) > -1) {
+ uncompletedPlanetTypes.splice(uncompletedPlanetTypes.indexOf(choice), 1);
+ }
+
+ // add choice to pool
+ gateway.planetPool.push({
+ type: choice,
+ });
+ }
+ },
+
+ getVoiceMessage(wonGame, forceWorldBased) {
+ // the point of this function is to add to the message pool all available qualifying messages and then pick one
+ const messagePool = [];
+ const totalEssence = res.getTotalResource("essence");
+
+ // if the game wasn't won, add loss messages
+ if (!wonGame) {
+ messagePool.push(...gateway.Messages.loss);
+ } else if (forceWorldBased) {
+ const planetPool = gateway.Messages.lastPlanetBased[world.worldType];
+ if (planetPool) {
+ messagePool.push(...planetPool);
+ }
+ } else {
+ // determine which essence based messages should go into the pool
+ _.each(gateway.Messages.essenceBased, (message) => {
+ const min = message.min || 0;
+ const max = message.max || Number.MAX_VALUE;
+
+ if (totalEssence >= min && totalEssence <= max) {
+ messagePool.push(...message.messages);
+ }
+ });
+
+ // determine which planet based messages should go into the pool
+ const planetPool = gateway.Messages.lastPlanetBased[world.worldType];
+ if (planetPool) {
+ messagePool.push(...planetPool);
+ }
+
+ // finally just add all the generics into the pool
+ messagePool.push(...gateway.Messages.generic);
+ }
+
+ return '"' + SharkGame.choose(messagePool) + '"';
+ },
+
+ playerHasSeenResource(resource) {
+ if (res.isCategory(resource)) {
+ return true;
+ }
+ return _.some(gateway.completedWorlds, (completedWorld) =>
+ _.some(SharkGame.WorldTypes[completedWorld].foresight.present, (seenResource) => seenResource === resource),
+ );
+ },
+
+ markWorldCompleted(worldType) {
+ if (!gateway.completedWorlds.includes(worldType)) {
+ gateway.completedWorlds.push(worldType);
+ }
+ },
+
+ getTimeInLastWorld(formatLess) {
+ if (SharkGame.persistentFlags.lastRunTime) {
+ return formatLess ? SharkGame.persistentFlags.lastRunTime : sharktext.formatTime(SharkGame.persistentFlags.lastRunTime);
+ } else {
+ if (!SharkGame.persistentFlags.totalPausedTime) {
+ SharkGame.persistentFlags.totalPausedTime = 0;
+ }
+ if (!SharkGame.persistentFlags.currentPausedTime) {
+ SharkGame.persistentFlags.currentPausedTime = 0;
+ }
+ const time =
+ SharkGame.timestampRunEnd -
+ SharkGame.timestampRunStart -
+ SharkGame.persistentFlags.totalPausedTime -
+ SharkGame.persistentFlags.currentPausedTime;
+ return formatLess ? time : sharktext.formatTime(time);
+ }
+ },
+
+ updateWasScoutingStatus() {
+ if (!_.isUndefined(SharkGame.persistentFlags.scouting)) {
+ SharkGame.persistentFlags.wasScouting = SharkGame.persistentFlags.scouting;
+ SharkGame.persistentFlags.scouting = undefined;
+ } else if (_.isUndefined(SharkGame.persistentFlags.wasScouting)) {
+ // failsafe, assume we were indeed scouting
+ SharkGame.persistentFlags.wasScouting = true;
+ }
+ },
+
+ updateScoutingStatus() {
+ SharkGame.persistentFlags.scouting = !gateway.completedWorlds.includes(world.worldType);
+ },
+
+ wasOnScoutingMission() {
+ if (!_.isUndefined(SharkGame.persistentFlags.scouting)) {
+ gateway.updateWasScoutingStatus();
+ }
+ return SharkGame.persistentFlags.wasScouting;
+ },
+
+ currentlyOnScoutingMission() {
+ if (!SharkGame.gameOver && _.isUndefined(SharkGame.persistentFlags.scouting)) {
+ gateway.updateScoutingStatus();
+ }
+ return SharkGame.persistentFlags.scouting;
+ },
+
+ getMinutesBelowPar() {
+ const time = gateway.getPar() - gateway.getTimeInLastWorld(true) / 60000;
+ if (time < 0) {
+ return 0;
+ }
+ return time;
+ },
+
+ getPar(type = world.worldType) {
+ return SharkGame.WorldTypes[type].par;
+ },
+
+ getSpeedReward(loadingFromSave) {
+ let reward = 0;
+ if (gateway.getPar() && !loadingFromSave && SharkGame.wonGame) {
+ let timeBelowPar = gateway.getMinutesBelowPar();
+ if (timeBelowPar > 0) {
+ while (timeBelowPar > 0) {
+ timeBelowPar -= 5;
+ reward += 1;
+ }
+
+ let timeBelowThreshold;
+ const rawTime = gateway.getTimeInLastWorld(true) / 60000;
+ if (rawTime < 5) {
+ timeBelowThreshold = 5 - rawTime;
+
+ while (timeBelowThreshold > 0) {
+ timeBelowThreshold -= 1;
+ reward += 1;
+ }
+ }
+
+ if (rawTime < 1) {
+ timeBelowThreshold = 1 - rawTime;
+
+ while (timeBelowThreshold > 0) {
+ timeBelowThreshold -= 1 / 6;
+ reward += 1;
+ }
+ }
+
+ if (rawTime < 1 / 6) {
+ timeBelowThreshold = 1 / 6 - rawTime;
+
+ while (timeBelowThreshold > 0) {
+ timeBelowThreshold -= 1 / 60;
+ reward += 1;
+ }
+ }
+ }
+ }
+ return reward;
+ },
+
+ getBaseReward(loadingFromSave, whichWorld = world.worldType) {
+ let reward = 0;
+ if (!loadingFromSave && SharkGame.wonGame) {
+ reward = gateway.wasOnScoutingMission() ? 4 : 2;
+
+ const bonus = SharkGame.WorldTypes[whichWorld].bonus;
+ if (bonus) {
+ reward += bonus;
+ }
+ }
+ return reward;
+ },
+
+ getPatienceReward(loadingFromSave) {
+ if (!loadingFromSave && SharkGame.wonGame) {
+ if (SharkGame.persistentFlags.dialSetting > 1) {
+ return (SharkGame.Aspects.patience.level * 2 * Math.log(SharkGame.persistentFlags.dialSetting)) / Math.log(4);
+ } else {
+ return SharkGame.Aspects.patience.level;
+ }
+ }
+ return 0;
+ },
+
+ getGumptionBonus(loadingFromSave) {
+ if (!loadingFromSave && SharkGame.wonGame) {
+ const bonus = SharkGame.Aspects.gumption.level * 0.01 * res.getResource("essence");
+ return Math.min(1, bonus);
+ }
+ return 0;
+ },
+
+ grantEssenceReward(essenceReward, patienceReward, speedReward) {
+ const gumptionBonus = gateway.getGumptionBonus();
+ res.changeResource("essence", Math.ceil((1 + gumptionBonus) * (essenceReward + speedReward) + patienceReward));
+ },
+
+ isWorldBeaten(worldType = "") {
+ return gateway.completedWorlds.indexOf(worldType) > -1;
+ },
+
+ shouldCheatsBeUnlocked() {
+ return res.getTotalResource("essence") >= 1000 && !SharkGame.persistentFlags.unlockedDebug;
+ },
+
+ unlockCheats() {
+ if (!SharkGame.persistentFlags.debug && !SharkGame.persistentFlags.unlockedDebug) {
+ SharkGame.PaneHandler.showUnlockedCheatsMessage();
+ SharkGame.Save.createTaggedSave("BackupCheats");
+ cad.debug();
+ }
+ SharkGame.persistentFlags.unlockedDebug = true;
+ },
+
+ ui: {
+ showGateway(baseReward, patienceReward, speedReward, gumptionRatio = gateway.getGumptionBonus(), forceWorldBased = false, storedTime = 0) {
+ const gumptionBonus = Math.ceil(gumptionRatio * (baseReward + speedReward));
+
+ // get some useful numbers
+ const essenceHeld = res.getResource("essence");
+ const numenHeld = res.getResource("numen");
+
+ // construct the gateway content
+ const gatewayContent = $("
");
+ gatewayContent.append($("
").html("Você é um tubarão num espaço entre mundos."));
+ if (!SharkGame.wonGame) {
+ gatewayContent.append(
+ $("
").html("Não lhe é claro como você veio para aqui, mas você se lembra de uma triste derrota.").addClass("medDesc"),
+ );
+ }
+ gatewayContent.append($("
").html(
+ "Entrar aqui te mudou, aumentando a sua essência por " +
+ sharktext.beautify(baseReward) +
+ ".",
+ ),
+ );
+ }
+ if (speedReward > 0) {
+ gatewayContent.append(
+ $("
").html(
+ "Você completou este mundo " +
+ sharktext.beautify(gateway.getMinutesBelowPar()) +
+ ` minuto${gateway.getMinutesBelowPar() === 1 ? "" : "s"} mais rápido que o normal, e você conseguiu ` +
+ sharktext.beautify(speedReward) +
+ " de essência adicional.",
+ ),
+ );
+ } else if (SharkGame.wonGame && !gateway.wasOnScoutingMission() && !gateway.getMinutesBelowPar()) {
+ gatewayContent.append(
+ $("
").html("Você não venceu esse oceano rápido o suficiente. Se tivesse o feito, você teria mais essência."),
+ );
+ }
+ if (gumptionBonus) {
+ gatewayContent.append(
+ $("
").html(
+ "Seus culhões te dão mais " +
+ sharktext.beautify(gumptionBonus, false, 2) +
+ " de essência extra.",
+ ),
+ );
+ }
+ if (patienceReward > 0) {
+ gatewayContent.append(
+ $("
").html(
+ "Sua paciência é recompensada, te proporcionando " +
+ sharktext.beautify(patienceReward) +
+ " de essência adicional.",
+ ),
+ );
+ }
+ if (speedReward || gumptionBonus || patienceReward) {
+ gatewayContent.append(
+ $("
").html(
+ "You appear to have experienced a major bug that causes negative world-times. The source of this bug is unknown. " +
+ "Por favor, mande uma cópia do seu jogo (o sharkGameSavePreGateway no seu Armazenamento Local) para nós, ou pelo canal #bugs-and-issues do nossos servidor do Discord ou por email para
timebug@shark.tobot.dev
. E, sei lá, aproveite a essência grátis? " +
+ "(Para remover a essência em excesso, digite a seguinte mensagem no console do seu navegador res.changeResource(\"essence\", -1000), substituindo 1000 com o número de essência em excesso que você ganhou; desculpa pela inconveniência) " +
+ `tempo de começo: ${SharkGame.timestampRunStart} tempo verdadeiramente pausado: ${SharkGame.persistentFlags.totalPausedTime} tempo atual pausado: ${SharkGame.persistentFlags.currentPausedTime} ` +
+ `ponteiro dos minutos: ${SharkGame.flags.minuteHandTimer} ponteiro das horas: ${SharkGame.flags.hourHandLeft} bônus: ${SharkGame.flags.bonusTime} ` +
+ `tempo calculado no mundo: ${gateway.getTimeInLastWorld(true)} o provável tempo verdadeiro: ${
+ _.now() - SharkGame.timestampRunStart
+ } `,
+ ),
+ );
+ } else {
+ containerDiv.append($("
").html("NOTA: Quando você entra num mundo pela primeira vez, você está o EXPLORANDO. Se você entrar de novo nele, você NÃO está mais EXPLORANDO."),
+ );
+
+ if (SharkGame.Aspects.destinyGamble.level > 0) {
+ SharkGame.Button.makeButton("destinyGamble", "foobar", planetSelectionContent, gateway.rerollWorlds);
+ }
+
+ if (SharkGame.persistentFlags.debug) {
+ SharkGame.Button.makeButton("visitButton", "visitar qualquer mundo", planetSelectionContent, gateway.ui.showWorldVisitMenu);
+ }
+
+ // add return to gateway button
+ const returnButtonDiv = $("
");
+ SharkGame.Button.makeButton("backToGateway", "voltar ao entre-mundos", returnButtonDiv, () => {
+ gateway.ui.switchViews(gateway.ui.showGateway);
+ });
+ planetSelectionContent.append(returnButtonDiv);
+
+ SharkGame.PaneHandler.swapCurrentPane("WORLDS", planetSelectionContent, true, foregoAnimation ? 0 : 500, true);
+ gateway.transitioning = false;
+ gateway.ui.updatePlanetButtons();
+ gateway.ui.formatDestinyGamble();
+ },
+
+ formatDestinyGamble() {
+ if (!_.isUndefined(SharkGame.persistentFlags.destinyRolls)) {
+ switch (SharkGame.persistentFlags.destinyRolls) {
+ case 0:
+ $("#destinyGamble").html("Não há mais mudança. Entre em um mundo para recarregar.").addClass("disabled");
+ break;
+ case 1:
+ $("#destinyGamble").html("Mudar Mundos (1 mudança resta)");
+ break;
+ default:
+ $("#destinyGamble").html("Mudar Mundos (" + SharkGame.persistentFlags.destinyRolls + " mudanças restam)");
+ }
+ }
+ },
+
+ confirmWorld() {
+ const selectedWorldData = SharkGame.WorldTypes[gateway.selectedWorld];
+ const seenWorldYet = gateway.completedWorlds.includes(gateway.selectedWorld);
+
+ // construct the gateway content
+ const gatewayContent = $("
").append(
+ $("
").html(seenWorldYet ? "Re-entrar no Mundo " + selectedWorldData.name + "?" : "Explorar este mundo?"),
+ );
+
+ gatewayContent.append(
+ $("
").html("Tempo normal: " + selectedWorldData.par + " minutes Passe por esse mundo mais rápido para ganhar mais essência."),
+ );
+ }
+
+ if (SharkGame.Aspects.theDial.level) {
+ gatewayContent.append($("