Spaces:
Building
Building
const BlockType = require('../../extension-support/block-type'); | |
const ArgumentType = require('../../extension-support/argument-type'); | |
const Cast = require('../../util/cast'); | |
class TailgatingExtension { | |
constructor(runtime) { | |
/** | |
* The runtime instantiating this block package. | |
* @type {Runtime} | |
*/ | |
this.runtime = runtime; | |
this.trackers = Object.create(null); | |
this.maxSaving = Object.create(null); | |
this.positions = Object.create(null); | |
const shouldSaveNewPosition = (positionsList, tracker) => { | |
const firstPos = positionsList[0]; | |
if (typeof firstPos !== "object") return true; | |
if (firstPos.x !== tracker.x || firstPos.y !== tracker.y) { | |
return true; | |
} | |
return false; | |
}; | |
this.runtime.on('RUNTIME_STEP_START', () => { | |
for (const trackerName in this.trackers) { | |
const tracker = this.trackers[trackerName]; | |
// happens when sprite is deleted or clone is deleted | |
if (tracker.isDisposed) { | |
this.stopTrackingSprite({ NAME: trackerName }); | |
continue; | |
} | |
// 0 positions should be saved, so just dont make them at all | |
const positions = this.positions[trackerName]; | |
const maxPositions = this.maxSaving[trackerName]; | |
if (maxPositions <= 0) continue; | |
// only track new positions when they have changed | |
// we have no reason to track the same position multiple times (that would make this ext useless) | |
if (shouldSaveNewPosition(positions, tracker)) { | |
// console.log('saved new pos for', trackerName); | |
positions.unshift({ x: tracker.x, y: tracker.y }); | |
} | |
this.positions[trackerName] = positions.slice(0, maxPositions); | |
} | |
}); | |
} | |
getInfo() { | |
return { | |
id: "jgTailgating", | |
name: "Tailgating", | |
blocks: [ | |
{ | |
opcode: "startTrackingSprite", | |
blockType: BlockType.COMMAND, | |
text: "start tracking [SPRITE] as [NAME]", | |
arguments: { | |
SPRITE: { | |
type: ArgumentType.STRING, | |
menu: "spriteMenu", | |
}, | |
NAME: { | |
type: ArgumentType.STRING, | |
defaultValue: "leader", | |
} | |
}, | |
}, | |
{ | |
opcode: "stopTrackingSprite", | |
blockType: BlockType.COMMAND, | |
text: "stop tracking [NAME]", | |
arguments: { | |
NAME: { | |
type: ArgumentType.STRING, | |
defaultValue: "leader", | |
} | |
}, | |
}, | |
'---', | |
{ | |
opcode: "followSprite", | |
blockType: BlockType.COMMAND, | |
text: "follow [INDEX] positions behind [NAME]", | |
arguments: { | |
INDEX: { | |
type: ArgumentType.NUMBER, | |
defaultValue: 20, | |
}, | |
NAME: { | |
type: ArgumentType.STRING, | |
defaultValue: "leader", | |
} | |
}, | |
}, | |
{ | |
opcode: "savePositionsBehindSprite", | |
blockType: BlockType.COMMAND, | |
text: "set max saved positions behind [NAME] to [MAX]", | |
arguments: { | |
MAX: { | |
type: ArgumentType.NUMBER, | |
defaultValue: 20, | |
}, | |
NAME: { | |
type: ArgumentType.STRING, | |
defaultValue: "leader", | |
} | |
}, | |
}, | |
{ | |
opcode: "getSpriteFollowPos", | |
blockType: BlockType.REPORTER, | |
disableMonitor: true, | |
text: "get position [INDEX] behind [NAME]", | |
arguments: { | |
INDEX: { | |
type: ArgumentType.NUMBER, | |
defaultValue: 20, | |
}, | |
NAME: { | |
type: ArgumentType.STRING, | |
defaultValue: "leader", | |
} | |
}, | |
}, | |
], | |
menus: { | |
spriteMenu: '_getSpriteMenu' | |
}, | |
}; | |
} | |
// menus | |
_getSpriteMenu() { | |
const emptyMenu = [{ text: '', value: '' }]; | |
const sprites = []; | |
if (this.runtime.vm.editingTarget && !this.runtime.vm.editingTarget.isStage) { | |
sprites.push({ text: 'this sprite', value: '_myself_' }); | |
} | |
for (const target of this.runtime.targets) { | |
if (!target.isOriginal) continue; | |
if (target.isStage) continue; | |
if (this.runtime.vm.editingTarget && this.runtime.vm.editingTarget.id === target.id) continue; | |
const name = target.getName(); | |
sprites.push({ | |
text: name, | |
value: name | |
}); | |
} | |
return sprites.length > 0 ? sprites : emptyMenu; | |
} | |
// blocks | |
startTrackingSprite(args, util) { | |
const spriteName = Cast.toString(args.SPRITE); | |
const trackerName = Cast.toString(args.NAME); | |
const pickedSprite = spriteName === '_myself_' ? util.target : this.runtime.getSpriteTargetByName(spriteName); | |
if (!pickedSprite) return; | |
this.trackers[trackerName] = pickedSprite; | |
this.positions[trackerName] = []; | |
if (!(trackerName in this.maxSaving)) { | |
this.maxSaving[trackerName] = 20; | |
} | |
} | |
stopTrackingSprite(args) { | |
const trackerName = Cast.toString(args.NAME); | |
delete this.trackers[trackerName]; | |
this.positions[trackerName] = []; | |
} | |
followSprite(args, util) { | |
const trackerName = Cast.toString(args.NAME); | |
const index = Cast.toNumber(args.INDEX); | |
const spritePositions = this.positions[trackerName]; | |
if (!spritePositions) return; | |
let position = spritePositions[index]; | |
if (typeof position !== "object") { | |
// this index position was not found | |
// use the last one in the list instead | |
// if there is nothing in the list, dont do anything | |
if (spritePositions.length <= 0) return; | |
position = spritePositions[spritePositions.length - 1]; | |
} | |
util.target.setXY(position.x, position.y); | |
} | |
getSpriteFollowPos(args) { | |
const trackerName = Cast.toString(args.NAME); | |
const index = Cast.toNumber(args.INDEX); | |
const spritePositions = this.positions[trackerName]; | |
if (!spritePositions) return '{}'; | |
let position = spritePositions[index]; | |
if (typeof position !== "object") { | |
// this index position was not found | |
// use the last one in the list instead | |
// if there is nothing in the list, dont do anything | |
if (spritePositions.length <= 0) return '{}'; | |
position = spritePositions[spritePositions.length - 1]; | |
} | |
return JSON.stringify({ | |
x: position.x, | |
y: position.y | |
}); | |
} | |
savePositionsBehindSprite(args, util) { | |
const trackerName = Cast.toString(args.NAME); | |
const maxPositions = Cast.toNumber(args.MAX); | |
let max = Math.round(maxPositions); | |
if (max <= 0) { | |
max = 0; | |
} | |
if (max > 0) { | |
max++; | |
} | |
this.maxSaving[trackerName] = max; | |
} | |
} | |
module.exports = TailgatingExtension; |