1
0
mirror of https://github.com/tobspr/shapez.io.git synced 2026-03-02 03:39:21 +00:00

Puzzle DLC (#1172)

* Puzzle mode (#1135)

* Add mode button to main menu

* [WIP] Add mode menu. Add factory-based gameMode creation

* Add savefile migration, serialize, deserialize

* Add hidden HUD elements, zone, and zoom, boundary constraints

* Clean up lint issues

* Add building, HUD exclusion, building exclusion, and refactor

- [WIP] Add ConstantProducer building that combines ConstantSignal
and ItemProducer functionality. Currently using temp assets.
- Add pre-placement check to the zone
- Use Rectangles for zone and boundary
- Simplify zone drawing
- Account for exclusion in savegame data
- [WIP] Add puzzle play and edit buttons in puzzle mode menu

* [WIP] Add building, component, and systems for producing and
accepting user-specified items and checking goal criteria

* Add ingame puzzle mode UI elements

- Add minimal menus in puzzle mode for back, next navigation
- Add lower menu for changing zone dimenensions

Co-authored-by: Greg Considine <gconsidine@users.noreply.github.com>

* Performance optimizations (#1154)

* 1.3.1 preparations

* Minor fixes, update translations

* Fix achievements not working

* Lots of belt optimizations, ~15% performance boost

* Puzzle mode, part 1

* Puzzle mode, part 2

* Fix missing import

* Puzzle mode, part 3

* Fix typo

* Puzzle mode, part 4

* Puzzle Mode fixes: Correct zone restrictions and more (#1155)

* Hide Puzzle Editor Controls in regular game mode, fix typo

* Disallow shrinking zone if there are buildings

* Fix multi-tile buildings for shrinking

* Puzzle mode, Refactor hud

* Puzzle mode

* Fixed typo in latest puzzle commit (#1156)

* Allow completing puzzles

* Puzzle mode, almost done

* Bump version to 1.4.0

* Fixes

* [puzzle] Prevent pipette cheats (miners, emitters) (#1158)

* Puzzle mode, almost done

* Allow clearing belts with 'B'

* Multiple users for the puzzle dlc

* Bump api key

* Minor adjustments

* Update

* Minor fixes

* Fix throughput

* Fix belts

* Minor puzzle adjustments

* New difficulty

* Minor puzzle improvements

* Fix belt path

* Update translations

* Added a button to return to the menu after a puzzle is completed (#1170)

* added another button to return to the menu

* improved menu return

* fixed continue button to not go back to menu

* [Puzzle] Added ability to lock buildings in the puzzle editor! (#1164)

* initial test

* tried to get it to work

* added icon

* added test exclusion

* reverted css

* completed flow for building locking

* added lock option

* finalized look and changed locked building to same sprite

* removed unused art

* added clearing every goal acceptor on lock to prevent creating impossible puzzles

* heavily improved validation and prevented autocompletion

* validation only checks every 100 ticks to improve performance

* validation only checks every 100 ticks to improve performance

* removed clearing goal acceptors as it isn't needed because of validation

* Add soundtrack, puzzle dlc fixes

Co-authored-by: Greg Considine <gconsidine@users.noreply.github.com>
Co-authored-by: dengr1065 <dengr1065@gmail.com>
Co-authored-by: Sense101 <67970865+Sense101@users.noreply.github.com>
This commit is contained in:
tobspr
2021-05-23 16:32:05 +02:00
committed by GitHub
parent 5f0a95ba11
commit 931c8a5821
167 changed files with 14001 additions and 8193 deletions

203
src/js/platform/api.js Normal file
View File

@@ -0,0 +1,203 @@
/* typehints:start */
import { Application } from "../application";
/* typehints:end */
import { createLogger } from "../core/logging";
import { compressX64 } from "../core/lzstring";
import { T } from "../translations";
const logger = createLogger("puzzle-api");
const rusha = require("rusha");
export class ClientAPI {
/**
*
* @param {Application} app
*/
constructor(app) {
this.app = app;
/**
* The current users session token
* @type {string|null}
*/
this.token = null;
this.syncToken = window.localStorage.getItem("tmp.syncToken");
if (!this.syncToken) {
this.syncToken = rusha
.createHash()
.update(new Date().getTime() + "=" + Math.random())
.digest("hex");
window.localStorage.setItem("tmp.syncToken", this.syncToken);
}
}
getEndpoint() {
if (G_IS_DEV) {
return "http://localhost:15001";
}
if (window.location.host === "beta.shapez.io") {
return "https://api-staging.shapez.io";
}
return "https://api.shapez.io";
}
isLoggedIn() {
return Boolean(this.token);
}
/**
*
* @param {string} endpoint
* @param {object} options
* @param {"GET"|"POST"=} options.method
* @param {any=} options.body
*/
_request(endpoint, options) {
const headers = {
"x-api-key": "d5c54aaa491f200709afff082c153ef2",
"Content-Type": "application/json",
};
if (this.token) {
headers["x-token"] = this.token;
}
return Promise.race([
fetch(this.getEndpoint() + endpoint, {
cache: "no-cache",
mode: "cors",
headers,
method: options.method || "GET",
body: options.body ? JSON.stringify(options.body) : undefined,
})
.then(res => {
if (res.status !== 200) {
throw "bad-status: " + res.status + " / " + res.statusText;
}
return res;
})
.then(res => res.json()),
new Promise((resolve, reject) => setTimeout(() => reject("timeout"), 15000)),
])
.then(data => {
if (data && data.error) {
logger.warn("Got error from api:", data);
throw T.backendErrors[data.error] || data.error;
}
return data;
})
.catch(err => {
logger.warn("Failure:", endpoint, ":", err);
throw err;
});
}
tryLogin() {
return this.apiTryLogin()
.then(({ token }) => {
this.token = token;
return true;
})
.catch(err => {
logger.warn("Failed to login:", err);
return false;
});
}
/**
* @returns {Promise<{token: string}>}
*/
apiTryLogin() {
return this._request("/v1/public/login", {
method: "POST",
body: {
token: this.syncToken,
},
});
}
/**
* @param {"new"|"top-rated"|"mine"} category
* @returns {Promise<import("../savegame/savegame_typedefs").PuzzleMetadata[]>}
*/
apiListPuzzles(category) {
if (!this.isLoggedIn()) {
return Promise.reject("not-logged-in");
}
return this._request("/v1/puzzles/list/" + category, {});
}
/**
* @param {number} puzzleId
* @returns {Promise<import("../savegame/savegame_typedefs").PuzzleFullData>}
*/
apiDownloadPuzzle(puzzleId) {
if (!this.isLoggedIn()) {
return Promise.reject("not-logged-in");
}
return this._request("/v1/puzzles/download/" + puzzleId, {});
}
/**
* @param {number} shortKey
* @returns {Promise<import("../savegame/savegame_typedefs").PuzzleFullData>}
*/
apiDownloadPuzzleByKey(shortKey) {
if (!this.isLoggedIn()) {
return Promise.reject("not-logged-in");
}
return this._request("/v1/puzzles/download/" + shortKey, {});
}
/**
* @param {number} puzzleId
* @returns {Promise<void>}
*/
apiReportPuzzle(puzzleId, reason) {
if (!this.isLoggedIn()) {
return Promise.reject("not-logged-in");
}
return this._request("/v1/puzzles/report/" + puzzleId, {
method: "POST",
body: { reason },
});
}
/**
* @param {number} puzzleId
* @param {object} payload
* @param {number} payload.time
* @param {boolean} payload.liked
* @returns {Promise<{ success: true }>}
*/
apiCompletePuzzle(puzzleId, payload) {
if (!this.isLoggedIn()) {
return Promise.reject("not-logged-in");
}
return this._request("/v1/puzzles/complete/" + puzzleId, {
method: "POST",
body: payload,
});
}
/**
* @param {object} payload
* @param {string} payload.title
* @param {string} payload.shortKey
* @param {import("../savegame/savegame_typedefs").PuzzleGameData} payload.data
* @returns {Promise<{ success: true }>}
*/
apiSubmitPuzzle(payload) {
if (!this.isLoggedIn()) {
return Promise.reject("not-logged-in");
}
return this._request("/v1/puzzles/submit", {
method: "POST",
body: {
...payload,
data: compressX64(JSON.stringify(payload.data)),
},
});
}
}

View File

@@ -3,6 +3,7 @@ import { createLogger } from "../../core/logging";
import { queryParamOptions } from "../../core/query_parameters";
import { BeltComponent } from "../../game/components/belt";
import { StaticMapEntityComponent } from "../../game/components/static_map_entity";
import { RegularGameMode } from "../../game/modes/regular";
import { GameRoot } from "../../game/root";
import { InGameState } from "../../states/ingame";
import { GameAnalyticsInterface } from "../game_analytics";
@@ -163,6 +164,10 @@ export class ShapezGameAnalytics extends GameAnalyticsInterface {
return;
}
if (!(root.gameMode instanceof RegularGameMode)) {
return;
}
logger.log("Sending event", category, value);
this.sendToApi("/v1/game-event", {

View File

@@ -35,6 +35,10 @@ export const MUSIC = {
menu: "menu",
};
if (G_IS_STANDALONE || G_IS_DEV) {
MUSIC.puzzle = "puzzle-full";
}
export class SoundInstanceInterface {
constructor(key, url) {
this.key = key;