The MACRO / Token Action
!torpedo --selected @{selected|token_id} --target @{target|token_id}
__________________
The SCRIPT
//spawn torpedo
on('chat:message', function(msg) {
if (msg.type !== 'api' || !msg.content.startsWith('!torpedo')) return;
const args = msg.content.split(' ');
if (args.length !== 3) {
sendChat('System', '/w ' + msg.who + ' Invalid arguments. Usage: !torpedo casterTokenId targetTokenId');
return;
}
const casterTokenId = args[1];
const targetTokenId = args[2];
const casterToken = getObj('graphic', casterTokenId);
const targetToken = getObj('graphic', targetTokenId);
if (!casterToken || !targetToken) {
sendChat('System', 'Invalid token IDs provided.');
return;
}
const casterCharacter = getObj('character', casterToken.get('represents'));
if (!casterCharacter) {
sendChat('System', 'This token does not represent a character.');
return;
}
// Check current player page
const currentPageId = Campaign().get('playerpageid');
const currentPage = getObj('page', currentPageId);
const pageName = currentPage.get('name');
if (!['GHUD', 'PlanetaryNav', 'SystemNav'].includes(pageName)) {
sendChat('System', 'Ship to ship weapons cannot be fired from this screen.');
return;
}
// Check if character has "Torpedo Launcher"
const torpedoWeapon = findObjs({
_type: 'attribute',
_characterid: casterCharacter.id,
name: 'repeating_inventory_$X_itemname',
current: 'Torpedo Launcher'
})[0];
if (!torpedoWeapon) {
sendChat('System', 'This token does not have a torpedo launcher.');
return;
}
// Check ammo
const ammoAttribute = findObjs({
_type: 'attribute',
_characterid: casterCharacter.id,
name: 'repeating_inventory_$X_resource'
}).find(attr => attr.get('current') > 0);
if (!ammoAttribute || ammoAttribute.get('current') <= 0) {
sendChat('System', 'This token is out of torpedoes.');
return;
}
// Deduct ammo
ammoAttribute.set('current', ammoAttribute.get('current') - 1);
// Get locations
const casterLocation = {
left: casterToken.get('left'),
top: casterToken.get('top')
};
const targetLocation = {
left: targetToken.get('left'),
top: targetToken.get('top')
};
// Instantiate a token at caster location representing the torpedo
const torpedoToken = createObj('graphic', {
_pageid: casterToken.get('_pageid'),
imgsrc: 'https://s3.amazonaws.com/files.d20.io/images/366403456/xvVdL5KBgB6TMpyNb6-jTQ/med.webm?1699199637',
layer: 'objects',
left: casterLocation.left,
top: casterLocation.top,
width: 24, // Assuming the hex size corresponds to 24
height: 24,
name: 'weapon_torpedo',
bar2_value: targetTokenId,
bar2_max: JSON.stringify(targetLocation), // Store the target location in bar2_max
gmnotes: '20' // Adds “20” to gmnotes
});
// Send a red ping to the caster location
sendPing(casterLocation.left, casterLocation.top, casterToken.get('_pageid'), null, true, 'red');
// Play sound using !roll20AM
//sendChat('System', `!roll20AM --audio,play|${randomTrackFromPlaylist('GloriaTorpedoIncoming')}`);
});
// Helper function to randomly select a track from a playlist
function randomTrackFromPlaylist(playlistName) {
const playlist = findObjs({
_type: 'jukeboxtrack',
playlist: playlistName
});
if (playlist.length > 0) {
const track = playlist[Math.floor(Math.random() * playlist.length)];
return track.get('title');
}
return '';
}