Compare commits
3
Commits
main
..
tamagotchi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e17acc4cf | ||
|
|
8fbd3bd527 | ||
|
|
9dad3c6176 |
@@ -1,4 +1,4 @@
|
|||||||
This branch is the BP and RP pack for **The BLANK DLC Map or ADDON**.
|
This branch is the BP and RP pack for **The Tamagotchi DLC Map**.
|
||||||
Each Branch of this repository Should be it's own addon/piece of content.
|
Each Branch of this repository Should be it's own addon/piece of content.
|
||||||
|
|
||||||
If you need to ask any questions regarding any code: Discord momoko_koigakubo
|
If you need to ask any questions regarding any code: Discord momoko_koigakubo
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
pack.name=The Tamagotchi Resource Pack
|
|
||||||
pack.description=A Resource pack for the Tamagotchi Map
|
|
||||||
@@ -3,26 +3,32 @@
|
|||||||
"header": {
|
"header": {
|
||||||
"name": "pack.name",
|
"name": "pack.name",
|
||||||
"description": "pack.description",
|
"description": "pack.description",
|
||||||
"uuid": "***",
|
"uuid": "4390e189-ec4a-4f43-bc80-555b6c986147",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"min_engine_version": "1.26.50"
|
"min_engine_version": "1.26.50"
|
||||||
},
|
},
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
||||||
"type": "data",
|
"type": "data",
|
||||||
"uuid": "***",
|
"uuid": "a06b47ef-e0d9-4154-94c1-9a6bd86e506f",
|
||||||
"version": "1.0.0"
|
"version": "1.0.0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"description": "Test Scripting",
|
"description": "Tamagotchi Scripting",
|
||||||
"type": "script",
|
"type": "script",
|
||||||
"uuid": "***",
|
"uuid": "3dec0970-c9b8-4e64-886a-cdc237cf938d",
|
||||||
"version": [1, 0, 0],
|
"version": "1.0.0",
|
||||||
"entry": "scripts/main.js"
|
"entry": "scripts/main.js"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"module_name":"@minecraft/server",
|
||||||
|
"version":"2.10.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
"metadata": {
|
"metadata": {
|
||||||
"authors": ["blah"],
|
"authors": ["Momoko"],
|
||||||
"product_type": "addon"
|
"product_type": "addon"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {object} Region
|
||||||
|
* @property {string} nameKey
|
||||||
|
* @property {number} order
|
||||||
|
* @property {boolean} unlockedAtStart
|
||||||
|
* @property {number} unlockedCost
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Record<string, Region>}
|
||||||
|
*/
|
||||||
|
export const REGIONS = {
|
||||||
|
lagoon: {nameKey: "tama.region.lagoon.name",
|
||||||
|
order: 0, unlockedAtStart: true, unlockedCost: 0},
|
||||||
|
cropcountry: {nameKey: "tama.region.cropcountry.name",
|
||||||
|
order: 1, unlockedAtStart: false, unlockedCost: 100},
|
||||||
|
seaside: {nameKey: "tama.region.seaside.name",
|
||||||
|
order: 2, unlockedAtStart: false, unlockedCost: 500},
|
||||||
|
skyland: {nameKey: "tama.region.skyland.name",
|
||||||
|
order: 3, unlockedAtStart: false, unlockedCost: 1000}
|
||||||
|
};
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
import {
|
||||||
|
CommandPermissionLevel,
|
||||||
|
CustomCommandStatus,
|
||||||
|
CustomCommandParamType,
|
||||||
|
system,
|
||||||
|
} from "@minecraft/server";
|
||||||
|
import { REGIONS } from "../config/regions.js";
|
||||||
|
import { resetGates, resetGate, unlockGate, unlockGates,
|
||||||
|
updateBalance, returnBalance, printGateState} from "../save/state.js";
|
||||||
|
import { unlockRegion } from "../systems/progression.js";
|
||||||
|
|
||||||
|
//the system calls this function we do not
|
||||||
|
//our import into main runs the first line of this etc
|
||||||
|
system.beforeEvents.startup.subscribe(({ customCommandRegistry }) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
customCommandRegistry.registerEnum("rzb_tamagotchi:region", Object.keys(REGIONS));
|
||||||
|
|
||||||
|
customCommandRegistry.registerCommand(
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:print_gates_states",
|
||||||
|
description: "This command prints the gate states",
|
||||||
|
permissionLevel: CommandPermissionLevel.Host,
|
||||||
|
cheatsRequired: true,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
system.run(() => {
|
||||||
|
printGateState();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: CustomCommandStatus.Success
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
customCommandRegistry.registerCommand(
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:reset_gates_all",
|
||||||
|
description: "This command resets all gates to default",
|
||||||
|
permissionLevel: CommandPermissionLevel.Host,
|
||||||
|
cheatsRequired: true,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
system.run(() => {
|
||||||
|
resetGates();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: CustomCommandStatus.Success,
|
||||||
|
message: "Gates reset to default",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
customCommandRegistry.registerCommand(
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:reset_gate",
|
||||||
|
description: "This command resets a single gate",
|
||||||
|
permissionLevel: CommandPermissionLevel.Host,
|
||||||
|
cheatsRequired: true,
|
||||||
|
mandatoryParameters: [
|
||||||
|
{
|
||||||
|
// Use the enum by setting the name to the enum name
|
||||||
|
name: "rzb_tamagotchi:region",
|
||||||
|
type: CustomCommandParamType.Enum,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
(origin, region) => {
|
||||||
|
system.run(() => {
|
||||||
|
resetGate(region);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: CustomCommandStatus.Success,
|
||||||
|
message: "Gate ${REGION[id]} reset to default",
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
customCommandRegistry.registerCommand(
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:unlocks_gates_all",
|
||||||
|
description: "This command unlocks all gates",
|
||||||
|
permissionLevel: CommandPermissionLevel.Host,
|
||||||
|
cheatsRequired: true,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
system.run(() => {
|
||||||
|
unlockGates();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: CustomCommandStatus.Success,
|
||||||
|
message: "All gates Unlocked",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
customCommandRegistry.registerCommand(
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:unlock_gate",
|
||||||
|
description: "This command unlocks a single gate",
|
||||||
|
permissionLevel: CommandPermissionLevel.Host,
|
||||||
|
cheatsRequired: true,
|
||||||
|
mandatoryParameters: [
|
||||||
|
{
|
||||||
|
// Use the enum by setting the name to the enum name
|
||||||
|
name: "rzb_tamagotchi:region",
|
||||||
|
type: CustomCommandParamType.Enum,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
(origin, region) => {
|
||||||
|
system.run(() => {
|
||||||
|
unlockGate(region, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: CustomCommandStatus.Success,
|
||||||
|
message: `Gate ${REGIONS[id]} unlocked`,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
customCommandRegistry.registerCommand(
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:request_unlock",
|
||||||
|
description: "This command calls unlockRegion(), for entities",
|
||||||
|
permissionLevel: CommandPermissionLevel.Any,
|
||||||
|
cheatsRequired: false,
|
||||||
|
mandatoryParameters: [
|
||||||
|
{
|
||||||
|
// Use the enum by setting the name to the enum name
|
||||||
|
name: "rzb_tamagotchi:region",
|
||||||
|
type: CustomCommandParamType.Enum,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
(origin, region) => {
|
||||||
|
system.run(() => {
|
||||||
|
const result = unlockRegion(region);
|
||||||
|
|
||||||
|
// no entity to talk to when run from a command block or console
|
||||||
|
if (!origin.sourceEntity) { return; }
|
||||||
|
|
||||||
|
if (result.ok) {
|
||||||
|
origin.sourceEntity.sendMessage({
|
||||||
|
translate: "tama.unlock.success",
|
||||||
|
with: { rawtext: [{ translate: REGIONS[region].nameKey }] }
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// reason codes are turned into player-facing text here.
|
||||||
|
// Every branch produces a RawMessage - a plain string would be
|
||||||
|
// sent literally instead of being looked up in the lang file.
|
||||||
|
let why;
|
||||||
|
switch (result.reason) {
|
||||||
|
case "already_unlocked":
|
||||||
|
why = {
|
||||||
|
translate: "tama.unlock.already_unlocked",
|
||||||
|
with: { rawtext: [{ translate: REGIONS[region].nameKey }] }
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "missing_prior_region":
|
||||||
|
why = result.requires
|
||||||
|
? {
|
||||||
|
translate: "tama.unlock.missing_prior",
|
||||||
|
with: { rawtext: [{ translate: REGIONS[result.requires].nameKey }] }
|
||||||
|
}
|
||||||
|
: { translate: "tama.unlock.first_region" };
|
||||||
|
break;
|
||||||
|
case "insufficient_funds":
|
||||||
|
why = {
|
||||||
|
translate: "tama.unlock.insufficient_funds",
|
||||||
|
with: [String(result.cost), String(result.balance)]
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case "unknown_region":
|
||||||
|
why = { translate: "tama.unlock.unknown_region" };
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
why = {
|
||||||
|
translate: "tama.unlock.default",
|
||||||
|
with: { rawtext: [{ translate: REGIONS[region].nameKey }] }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
origin.sourceEntity.sendMessage(why);
|
||||||
|
});
|
||||||
|
|
||||||
|
// note: CustomCommandResult.message is typed `string`, so it cannot
|
||||||
|
// carry a RawMessage and can never be translated. All player-facing
|
||||||
|
// text for this command goes through sendMessage above instead.
|
||||||
|
return {
|
||||||
|
status: CustomCommandStatus.Success,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
customCommandRegistry.registerCommand(
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:balance_update",
|
||||||
|
description: "This command updates the player balance",
|
||||||
|
permissionLevel: CommandPermissionLevel.Host,
|
||||||
|
cheatsRequired: true,
|
||||||
|
mandatoryParameters: [
|
||||||
|
{
|
||||||
|
name: "rzb_tamagotchi:cost",
|
||||||
|
type: CustomCommandParamType.Integer,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
(origin, cost) => {
|
||||||
|
system.run(() => { //deferred tick so we get return message first
|
||||||
|
updateBalance(cost);
|
||||||
|
|
||||||
|
// the new balance only exists after the deferred update runs,
|
||||||
|
// so report it here rather than in the return value below
|
||||||
|
if (origin.sourceEntity) {
|
||||||
|
origin.sourceEntity.sendMessage(`Your balance is ${returnBalance()}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: CustomCommandStatus.Success,
|
||||||
|
message: `Balance changed by ${cost}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)})
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
const DEBUG = true;;
|
||||||
|
const PREFIX = "[TAMA]";
|
||||||
|
|
||||||
|
export function log(message) {
|
||||||
|
if(!DEBUG) return;
|
||||||
|
console.warn(`${PREFIX} : ${message}`);
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { log } from "./core/log.js";
|
||||||
|
import { load } from "./save/state.js";
|
||||||
|
import { getState } from "./save/state.js";
|
||||||
|
import "./core/commands.js"
|
||||||
|
import {world} from "@minecraft/server";
|
||||||
|
log("main.js loaded)");
|
||||||
|
|
||||||
|
/*
|
||||||
|
Dynamic properties have to exist post worldLoad
|
||||||
|
|
||||||
|
1.9 -> 2.0 scripts now load before world load
|
||||||
|
Grabbed data will throw an exception
|
||||||
|
if we don't do things after worldLoad
|
||||||
|
You can't serve a pizza if you don't have a pizza
|
||||||
|
|
||||||
|
Players, entities, blocks
|
||||||
|
Time of day, Gamemode, difficulty
|
||||||
|
*/
|
||||||
|
world.afterEvents.worldLoad.subscribe(() => {
|
||||||
|
load();
|
||||||
|
console.log(JSON.stringify(getState()));
|
||||||
|
});
|
||||||
|
|
||||||
|
//good for debugging gate values
|
||||||
|
//console.log(JSON.stringify(getState()));
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
import { REGIONS } from "../config/regions.js";
|
||||||
|
import { log } from "../core/log.js";
|
||||||
|
import { world } from "@minecraft/server";
|
||||||
|
|
||||||
|
const KEY = "tama.progress";
|
||||||
|
const VERSION = 1; //For updating the json in the save
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The in-memory copy of the whole save document, so we don't re-read the
|
||||||
|
* world's dynamic properties every time something needs a value.
|
||||||
|
* Null until load() runs. saveState() writes it back out to the world save.
|
||||||
|
*
|
||||||
|
* cache = {
|
||||||
|
* version: 1,
|
||||||
|
* coins: 0,
|
||||||
|
* gates: {
|
||||||
|
* lagoon: true,
|
||||||
|
* farmland: false,
|
||||||
|
* seaside: false,
|
||||||
|
* skyland: false
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
let cache = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the save document out of the world and puts it in `cache`.
|
||||||
|
*
|
||||||
|
* Nothing stored under KEY yet means a brand-new world - getDynamicProperty
|
||||||
|
* returns undefined - so build the defaults and write them straight back,
|
||||||
|
* which is what creates the save.
|
||||||
|
*
|
||||||
|
* Otherwise the stored value is JSON text, so parse it back into an object.
|
||||||
|
*/
|
||||||
|
export function load(){
|
||||||
|
const raw = world.getDynamicProperty(KEY);
|
||||||
|
//let raw = undefined;
|
||||||
|
//good for debugging before the value is updated
|
||||||
|
//console.log(raw);
|
||||||
|
|
||||||
|
if(raw === undefined) {
|
||||||
|
cache = buildDefaults();
|
||||||
|
saveState();
|
||||||
|
} else {
|
||||||
|
//good for debugging post update
|
||||||
|
//console.log(raw);
|
||||||
|
cache = JSON.parse(raw);
|
||||||
|
}
|
||||||
|
if(cache.version < VERSION) {
|
||||||
|
//if(version < (VERSION) -> add attribute etc))
|
||||||
|
log("==================================");
|
||||||
|
log(`version: ${cache.version}, code expects ${VERSION}`);
|
||||||
|
log("==================================");
|
||||||
|
//if attribute === undefined -> set default value
|
||||||
|
cache.version = VERSION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a fresh save document for a world that has never been played.
|
||||||
|
*
|
||||||
|
* `gates` is an object (not an array) filled by looping REGIONS and copying
|
||||||
|
* each region's unlockedAtStart value, so adding a region to the config
|
||||||
|
* automatically adds its gate here with no change to this function.
|
||||||
|
*/
|
||||||
|
function buildDefaults() {
|
||||||
|
const gates = {};
|
||||||
|
const coins = 0;
|
||||||
|
for(const id in REGIONS)
|
||||||
|
{
|
||||||
|
gates[id] = REGIONS[id].unlockedAtStart;
|
||||||
|
}
|
||||||
|
return{version: VERSION, gates: gates, coins: coins};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cached save document.
|
||||||
|
* This is the live object, not a copy - anything that mutates what comes back
|
||||||
|
* must call saveState() afterwards, or the change is lost on world close.
|
||||||
|
*/
|
||||||
|
export function getState() {
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* buildDefaults returns a value,
|
||||||
|
* using .gates returns just the
|
||||||
|
* gates value rather than coins
|
||||||
|
* and other default data
|
||||||
|
*/
|
||||||
|
export function resetGates() {
|
||||||
|
cache.gates = buildDefaults().gates;
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
export function resetGate(id) {
|
||||||
|
//resets the gate from the config/region config
|
||||||
|
cache.gates[id] = REGIONS[id].unlockedAtStart;
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a region's gate to T/F and persists the change.
|
||||||
|
* e.g. updateGate("farmland", state) ->
|
||||||
|
* cache.gates["farmland"] = state
|
||||||
|
*
|
||||||
|
* undefined will remove a gate from the cache
|
||||||
|
*/
|
||||||
|
export function unlockGate(nKey, state) {
|
||||||
|
cache.gates[nKey] = state;
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unlockGates() {
|
||||||
|
for(const id in REGIONS) {
|
||||||
|
cache.gates[id] = true;
|
||||||
|
}
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateBalance(cost) {
|
||||||
|
cache.coins += cost;
|
||||||
|
saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function returnBalance(){
|
||||||
|
const value = cache.coins;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function printGateState(){
|
||||||
|
log(JSON.stringify(getState().gates));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the cache out to the world save, where it survives the world closing.
|
||||||
|
*
|
||||||
|
* Returns early if nothing has been loaded yet - writing a null cache would
|
||||||
|
* store the text "null" and corrupt the save.
|
||||||
|
*
|
||||||
|
* Objects are not a permitted dynamic property value (only boolean, number,
|
||||||
|
* string and Vector3), so the document is converted to JSON text first.
|
||||||
|
*/
|
||||||
|
export function saveState() {
|
||||||
|
if(cache === null) return;
|
||||||
|
const text = JSON.stringify(cache);
|
||||||
|
world.setDynamicProperty(KEY, text);
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import { REGIONS } from "../config/regions.js"
|
||||||
|
import { getState, updateBalance, unlockGate, returnBalance } from "../save/state.js"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can this region be unlocked right now, and if not, why not?
|
||||||
|
*
|
||||||
|
* Returns an outcome object rather than a boolean so callers can explain the
|
||||||
|
* refusal to the player. `reason` is a code, not display text - whoever shows
|
||||||
|
* it is responsible for turning it into a translated message.
|
||||||
|
*
|
||||||
|
* { ok: true }
|
||||||
|
* { ok: false, reason: "unknown_region" } developer error
|
||||||
|
* { ok: false, reason: "already_unlocked" }
|
||||||
|
* { ok: false, reason: "missing_prior_region", requires: "farmland" }
|
||||||
|
* { ok: false, reason: "insufficient_funds", cost: 500, balance: 120 }
|
||||||
|
*
|
||||||
|
* @param {string} regionID - a REGIONS key, e.g. "farmland"
|
||||||
|
*/
|
||||||
|
export function canUnlock(regionID) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
the region's whole config record:
|
||||||
|
{ nameKey, order, unlockedAtStart, unlockedCost}
|
||||||
|
*/
|
||||||
|
const region = REGIONS[regionID];
|
||||||
|
if (region === undefined) {
|
||||||
|
return { ok: false, reason: "unknown_region" };
|
||||||
|
}
|
||||||
|
// order number from the REGIONS section we keyed
|
||||||
|
const order = region.order;
|
||||||
|
// we return the full record.
|
||||||
|
const gates = getState().gates;
|
||||||
|
/*
|
||||||
|
if the records value for that key's order is
|
||||||
|
true: unlocked
|
||||||
|
*/
|
||||||
|
if (gates[regionID] === true) {
|
||||||
|
return { ok: false, reason: "already_unlocked" };
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
WE ARE KEYING FOR THE KEY VALUE'S ORDER PRIOR TO PASSED REGION{RegionID}
|
||||||
|
the prior value has to be true for the passed region to unlock
|
||||||
|
*/
|
||||||
|
let priorRegion = null;
|
||||||
|
for (const id in REGIONS) {
|
||||||
|
// never pass lagoon otherwise all conditions fail
|
||||||
|
if (REGIONS[id].order === order - 1) {
|
||||||
|
/*
|
||||||
|
again tdlr we get the key prior to the past key
|
||||||
|
so for example command canUnlock("seaside");
|
||||||
|
we are retrieving farm land for comparison
|
||||||
|
it's sequential you can't unlock zones non
|
||||||
|
out of order
|
||||||
|
*/
|
||||||
|
priorRegion = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if first region is auto unlocked we will never meet this condition
|
||||||
|
if (priorRegion === null) {
|
||||||
|
return { ok: false, reason: "missing_prior_region", requires: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
the prior region before this one must be open first
|
||||||
|
*/
|
||||||
|
if (gates[priorRegion] !== true) {
|
||||||
|
return { ok: false, reason: "missing_prior_region", requires: priorRegion };
|
||||||
|
}
|
||||||
|
|
||||||
|
//they have to be able to pay for it.
|
||||||
|
const cost = region.unlockedCost;
|
||||||
|
const balance = returnBalance();
|
||||||
|
if (balance < cost) {
|
||||||
|
return { ok: false, reason: "insufficient_funds", cost: cost, balance: balance };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks a region if the rules allow it, charges the cost, and saves.
|
||||||
|
* object canUnlock produced and can explain what went wrong.
|
||||||
|
*
|
||||||
|
* @param {string} regionID - a REGIONS key, e.g. "farmland"
|
||||||
|
*/
|
||||||
|
export function unlockRegion(regionID) {
|
||||||
|
|
||||||
|
const result = canUnlock(regionID);
|
||||||
|
if (!result.ok) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlockGate(regionID, true);
|
||||||
|
|
||||||
|
/*
|
||||||
|
always make sure things that cost money are passed negative,
|
||||||
|
because otherwise it would add to the balance
|
||||||
|
*/
|
||||||
|
updateBalance(-REGIONS[regionID].unlockedCost);
|
||||||
|
|
||||||
|
return { ok: true };
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
pack.name=The Tamagotchi Resource Pack
|
||||||
|
pack.description=A Resource pack for the Tamagotchi Map
|
||||||
|
|
||||||
|
tama.region.lagoon.name=Lagoon Town
|
||||||
|
tama.region.cropcountry.name=Crop Country
|
||||||
|
tama.region.seaside.name=Seaside
|
||||||
|
tama.region.skyland.name=Skyland
|
||||||
|
|
||||||
|
tama.unlock.already_unlocked=%1 is already unlocked
|
||||||
|
tama.unlock.missing_prior=You must unlock %1 first
|
||||||
|
tama.unlock.insufficient_funds=You need %1 coins, you have %2
|
||||||
|
tama.unlock.first_region=First region is already unlocked
|
||||||
|
tama.unlock.unknown_region=Requested region not found
|
||||||
|
tama.unlock.default=Cannot unlock Region: %1
|
||||||
|
tama.unlock.success=%1 unlocked
|
||||||
Reference in New Issue
Block a user