@magikcraft/mct1
Version:
Minecraft for Type 1 Diabetes
443 lines (384 loc) • 13.5 kB
text/typescript
import { questCommand } from '@magikcraft/mct1/quests'
import { Logger } from '@magikcraft/mct1/log'
import { worldly } from '@magikcraft/mct1/world'
import { user } from '@magikcraft/mct1/user'
import { Inventories } from '@magikcraft/mct1/quests/mct1/inventories'
import { ChestItems } from '@magikcraft/mct1/quests/mct1/chest-items'
import * as tools from '@magikcraft/mct1/tools'
import * as events from 'events'
import * as questTools from '@magikcraft/mct1/quests/quest-tools'
import DB from './db'
export type QuestMode = 'single' | 'multi'
export interface QuestOptions {
mode: QuestMode
/** When set to true, this.debug will print to log */
verbose?: boolean
}
export interface QuestConfig {
name: string
nextQuestName: string | undefined
player: any
world: any
options: QuestOptions
}
export class QuestBase {
name: string
nextQuestName?: string
player: any
db: DB // player database
world: any
state: any = {}
options: any
log: any
/** When set to true, this.debug will print messages to log */
verbose: boolean = false
Locs: any = {}
inventory: any[] = []
waypoints?: any[]
endPortalRegion?: any[]
private events: any = {}
private intervals: any = {}
private timers: any = {}
constructor(conf: QuestConfig) {
this.name = conf.name
this.nextQuestName = conf.nextQuestName
this.player = conf.player
this.world = conf.world
this.db = new DB(this.world.name)
this.options = conf.options || {}
this.log = Logger(`mct1/quests/${this.name}--${this.player.name}`)
this.verbose = (conf.options && conf.options.verbose) || false
}
start() {
const { player, world } = this
this.stop() // stop and restart, in case already running.
this.registerEvents()
this.setupWayPoints()
this.setupEndPortal()
this.doTracking()
worldly(world).setDestroyWorldIfEmpty(true, 5000)
}
doTracking() {
this.setTimeout(() => {
this.track()
this.setInterval(() => {
this.track()
}, 10000) // track every 10 secs
}, 2000) // delay first track by 2 secs
}
track() {
// user(this.player).db.
this.log(`track quest ${this.world.name}`)
const { player, world, name } = this
const inventoryJSON = user(player).inventory.exportToJSON(
user(player).inventory.getAllitemStacks()
)
const inventory = inventoryJSON
.map((item, i) => (item ? { ...item, slot: i } : null))
.filter(item => item)
const mct1 = user(player).mct1.isStarted
? {
bgl: user(player).mct1.bgl,
insulin: user(player).mct1.insulin,
digestionQueue: user(player).mct1.digestionQueue.map(
item => ({ ...item })
), // Clone instead of object reference
isStarted: user(player).mct1.isStarted,
isUSA: user(player).mct1.isUSA,
hasInfiniteInsulin: user(player).mct1.hasInfiniteInsulin,
hasLightningSnowballs: user(player).mct1
.hasLightningSnowballs,
hasSuperJump: user(player).mct1.hasSuperJump,
hasSuperSpeed: user(player).mct1.hasSuperSpeed,
hasNightVision: user(player).mct1.hasNightVision,
isSuperCharged: user(player).mct1.isSuperCharged,
}
: false
const state = {
quest: name,
player: player.name,
isDead: player.isDead(),
world: world.name,
session: user(player).sessionId,
timestamp: new Date(),
health: player.health,
foodLevel: player.foodLevel,
location: tools.locationToJSON(player.location),
inventory: inventory,
mct1: mct1,
}
// Log to console.
const logs: any[] = []
logs.push(`PLAYER: ${player.name}`)
logs.push(`WORLD: ${world.name}`)
logs.push(`HEALTH: ${player.health}`)
logs.push(`FOOD_LEVEL: ${player.foodLevel}`)
if (mct1) {
logs.push(`BGL: ${mct1.bgl}`)
logs.push(`INSULIN: ${mct1.insulin}`)
let digestionQueue: any[] = []
mct1.digestionQueue.forEach(item => {
if (item && item.food && item.food.type) {
digestionQueue.push(item.food.type)
}
})
logs.push(`DIGESTION_QUEUE: [${digestionQueue.join(', ')}]`)
}
this.log(logs.join(' | '))
}
stop() {
// Remove all mobs!
this.world.getEntities().forEach(e => {
if (e.type != 'PLAYER') {
e.remove()
}
})
this.unregisterAllEvents()
this.clearAllIntervals()
this.clearAllTimeouts()
}
registerEvents() {
const { player, world, options, log } = this
// playerChangedWorld
this.registerEvent('playerChangedWorld', event => {
if (
event.player.name == player.name &&
event.from.name == world.name
) {
this.stop()
}
})
// playerQuit
this.registerEvent('playerQuit', event => {
if (event.player.name == player.name) {
this.stop()
}
})
}
complete() {
const { player, options, nextQuestName } = this
this.stop()
if (this.nextQuestName) {
questCommand(nextQuestName, 'start', player, options)
}
}
setTimeout(callback: any, interval: number, key?: string) {
const k = key || tools.uuid()
this.timers[k] = setTimeout(callback, interval)
}
clearTimeout(key: string) {
clearTimeout(this.timers[key])
}
clearTimeoutsLike(wildcard: string) {
for (let key in this.timers) {
if (key.includes(wildcard)) {
this.clearTimeout(key)
}
}
}
clearAllTimeouts() {
for (let key in this.timers) {
this.clearTimeout(key)
}
}
setInterval = function(callback: any, interval: number, key?: string) {
const k = key || tools.uuid()
this.intervals[k] = setInterval(callback, interval)
}
clearInterval(key: string) {
clearInterval(this.intervals[key])
}
clearIntervalsLike(wildcard: string) {
for (let key in this.timers) {
if (key.includes(wildcard)) {
this.clearInterval(key)
}
}
}
clearAllIntervals() {
for (let key in this.intervals) {
this.clearInterval(key)
delete this.intervals[key]
}
}
/**
* Print debugging messages to the log when this.verbose is true
* @param msg - The message to log. Can be used for a label.
* @param toLog - An object or string to log.
*/
debug(msg, toLog?: any) {
if (this.verbose) {
this.log(msg, toLog)
}
}
registerEvent(type: string, callback: any, key?: string) {
const k = key || tools.uuid()
this.unregisterEvent(k)
this.events[k] = events[type](callback)
}
unregisterEvent(key: string) {
if (this.events[key]) this.events[key].unregister()
}
unregisterEventsLike(wildcard: string) {
for (let key in this.events) {
if (key.includes(wildcard)) {
this.unregisterEvent(key)
}
}
}
unregisterAllEvents() {
for (let key in this.events) {
this.unregisterEvent(key)
delete this.events[key]
}
}
setupEndPortal() {
const { player, world, endPortalRegion } = this
if (endPortalRegion) {
const name = 'endPortal'
worldly(world).registerRegion(
name,
endPortalRegion[0],
endPortalRegion[1]
)
worldly(world).registerPlayerEnterRegionEvent(name, event => {
this.complete()
})
}
}
setupWayPoints() {
const { player, world, Locs, log } = this
const { waypoints } = Locs
if (waypoints) {
for (const name in waypoints) {
const waypoint = waypoints[name]
const key = `waypoint-${name}`
worldly(world).registerRegion(
key,
waypoint.region[0],
waypoint.region[1]
)
worldly(world).registerPlayerEnterRegionEvent(key, event => {
user(player).saveSpawn(waypoint.saveLocation)
})
}
}
}
}
// #################### MCT1 Quest #######################
export class QuestMCT1 extends QuestBase {
mct1QuestName: string
endGateRegion?: any[]
endChestLocation?: any
endChestContents?: any[]
constructor(conf: QuestConfig) {
super(conf)
if (this.name === 'mct1') this.name = 'mct1-prologue'
this.mct1QuestName = this.name.replace('mct1-', '')
if (Inventories[this.mct1QuestName]) {
this.inventory = Inventories[this.mct1QuestName]
}
}
start() {
// Set defaults for MCT1 quests.
const { name, player, world, options, log, Locs } = this
const { locations, regions } = Locs
if (ChestItems[this.mct1QuestName])
this.endChestContents = ChestItems[this.mct1QuestName]
if (locations.endChest) this.endChestLocation = locations.endChest
if (regions.endGate) this.endGateRegion = regions.endGate
if (regions.endPortal) this.endPortalRegion = regions.endPortal
// Do this here ...
super.start()
user(player).teleport(locations.spawn)
user(player).saveSpawn(locations.spawn)
user(player).setRespawnAtSpawnLocation(true)
user(player).gma() // ADVENTURE!
user(player).effects.cancel('LEVITATION') // Just in case
if (this.inventory) {
user(player).inventory.set(this.inventory)
}
user(player).inventory.saveCurrent()
user(player).inventory.setReloadAtSpawn(true)
this.setMCT1SuperPowers(false)
user(player).mct1.start()
user(player).mct1.setInfiniteInsulin(true)
log('setInfiniteInsulin')
worldly(world).setNight()
worldly(world).setStorm()
worldly(world).preventMobSpawning(['HUSK'])
// worldly(world).setDestroyWorldIfEmpty(true, 3000)
// setup endchest contents
if (this.endChestLocation && this.endChestContents) {
questTools.putItemsInChest(
this.endChestLocation,
this.endChestContents
)
}
if (this.nextQuestName) {
// pre-import world to make quest start more snappy
// questCommand(this.nextQuestName, 'import', player, options)
}
}
stop() {
super.stop()
}
complete() {
super.complete()
}
setMCT1SuperPowers(bool) {
const { player } = this
if (bool) {
user(player).mct1.setSuperCharged(false)
user(player).mct1.setInfiniteSnowballs(true)
user(player).mct1.setSuperJump(true)
user(player).mct1.setSuperSpeed(true)
user(player).mct1.setNightVision(false)
// user(player).mct1.start()
} else {
user(player).mct1.setSuperCharged(false)
user(player).mct1.setInfiniteSnowballs(false)
user(player).mct1.setSuperJump(false)
user(player).mct1.setSuperSpeed(false)
user(player).mct1.setNightVision(false)
// user(player).mct1.start()
}
}
openEndGate() {
const { player, world, options, log, nextQuestName } = this
// if (this.nextQuestName) {
// // pre-import world to make quest start more snappy
// questCommand(nextQuestName, 'import', player, options)
// }
if (this.endGateRegion) {
// End gate effect
const reg = this.endGateRegion
questTools.replaceRegion(reg[0], reg[1], 'AIR')
questTools.playEffectInRegion(reg[0], reg[1], 'DRAGON_BREATH')
this.setInterval(() => {
questTools.playEffectInRegion(reg[0], reg[1], 'PORTAL')
}, 500)
}
}
registerEvents() {
super.registerEvents()
const { player } = this
if (this.endChestLocation) {
// inventoryClose
this.registerEvent('inventoryClose', event => {
if (event.player.name != player.name) return
if (event.inventory.type != 'CHEST') return
// end chest close...
const cLoc = event.inventory.location
const ecLoc = this.endChestLocation
if (
cLoc.x === ecLoc.x &&
cLoc.y === ecLoc.y &&
cLoc.z === ecLoc.z
) {
this.openEndGate()
}
})
}
}
}