2024-09-22 16:28:59 +02:00
// Import document classes.
2024-12-10 17:22:28 +01:00
import { NeverStopBlowingUpActor } from "./documents/actor.mjs" ;
2024-09-22 16:28:59 +02:00
// Import sheet classes.
2024-12-10 17:22:28 +01:00
import { NeverStopBlowingUpActorSheet } from "./sheets/actor-sheet.mjs" ;
2024-09-22 16:28:59 +02:00
// Import helper/utility classes and constants.
import { preloadHandlebarsTemplates } from "./helpers/templates.mjs" ;
2024-12-10 17:22:28 +01:00
import { NEVERSTOPBLOWINGUP } from "./helpers/config.mjs" ;
2024-09-22 16:28:59 +02:00
/* -------------------------------------------- */
/* Init Hook */
/* -------------------------------------------- */
Hooks . once ( 'init' , async function ( ) {
// Register the helper
Handlebars . registerHelper ( 'capitalizeFirst' , function ( string ) {
if ( typeof string === 'string' ) {
return string . charAt ( 0 ) . toUpperCase ( ) + string . slice ( 1 ) ;
}
return '' ;
} ) ;
// Add utility classes and functions to the global game object so that they're more easily
// accessible in global contexts.
game . kidsonbrooms = {
2024-12-10 17:22:28 +01:00
NeverStopBlowingUpActor ,
2024-09-22 16:28:59 +02:00
_onTakeAdversityToken : _onTakeAdversityToken , // Add the function to the global object
_onSpendAdversityTokens : _onSpendAdversityTokens // Add the function to the global object
} ;
// Add custom constants for configuration.
2024-12-10 17:22:28 +01:00
CONFIG . NEVERSTOPBLOWINGUP = NEVERSTOPBLOWINGUP ;
2024-09-22 16:28:59 +02:00
/ * *
* Set an initiative formula for the system
* @ type { String }
* /
CONFIG . Combat . initiative = {
formula : "1d20" ,
decimals : 2
} ;
2024-12-02 19:00:59 +01:00
2024-09-22 16:28:59 +02:00
// Define custom Document classes
2024-12-10 17:22:28 +01:00
CONFIG . Actor . documentClass = NeverStopBlowingUpActor ;
2024-09-22 16:28:59 +02:00
// Register sheet application classes
Actors . unregisterSheet ( "core" , ActorSheet ) ;
2024-12-10 17:22:28 +01:00
Actors . registerSheet ( "fvtt-never-stop-blowing-up" , NeverStopBlowingUpActorSheet , { makeDefault : true } ) ;
2024-09-22 16:28:59 +02:00
//If there is a new chat message that is a roll we add the adversity token controls
Hooks . on ( "renderChatMessage" , ( message , html , messageData ) => {
const adversityControls = html . find ( '.adversity-controls' ) ;
if ( adversityControls . length > 0 ) {
const messageToEdit = adversityControls . data ( "roll-id" ) ;
// Bind event listeners for the controls
adversityControls . find ( ".take-adversity" ) . off ( "click" ) . click ( ( event ) => {
const actorId = event . currentTarget . dataset . actorId ;
const actor = game . actors . get ( actorId ) ;
// Check if the current user owns the actor - They can not claim if they are not
if ( ! actor . testUserPermission ( game . user , "owner" ) ) {
ui . notifications . warn ( "You don't own this character and cannot take adversity tokens." ) ;
return ;
}
// Check if the token has already been claimed -- Contigency if the button somehow activates again
2024-09-26 02:35:05 +02:00
if ( message . getFlag ( "kidsonbrooms" , "tokenClaimed" ) ) {
2024-09-22 16:28:59 +02:00
ui . notifications . warn ( "This adversity token has already been claimed." ) ;
return ;
}
_onTakeAdversityToken ( event , actor ) ;
if ( game . user . isGM ) {
let tokenControls = game . messages . get ( message . id ) ;
console . log ( tokenControls ) ;
// Update the chat message content with the button disabled and text changed
const updatedContent = tokenControls . content . replace (
` <button class="take-adversity" data-actor-id=" ${ actor . id } ">Take Adversity Token</button> ` ,
` <button class="take-adversity" data-actor-id=" ${ actor . id } " disabled>Token claimed</button> `
) ;
console . log ( "Removing Button" ) ;
// Update the message content
tokenControls . update ( { content : updatedContent } ) ;
// Set the flag on the chat message to indicate that the token has been claimed
2024-12-10 17:22:28 +01:00
tokenControls . setFlag ( "fvtt-never-stop-blowing-up" , "tokenClaimed" , true ) ;
2024-09-22 16:28:59 +02:00
} else {
// Emit a socket request to update the message to show that the token has been claimed
2024-12-10 17:22:28 +01:00
game . socket . emit ( 'system.fvtt-never-stop-blowing-up' , {
2024-09-22 16:28:59 +02:00
action : "takeToken" ,
messageID : message . id ,
actorID : actor . id ,
} ) ;
}
console . log ( "Send socket message for taking a token" ) ;
} ) ;
adversityControls . find ( ".spend-adversity" ) . off ( "click" ) . click ( ( event ) => {
//This entails a lot more, so I offloaded it to a new function
_onSpendAdversityTokens ( event , messageToEdit ) ;
} ) ;
}
} ) ;
// Preload Handlebars templates.
return preloadHandlebarsTemplates ( ) ;
2024-12-02 19:00:59 +01:00
2024-09-22 16:28:59 +02:00
} ) ;
/ * * *
* This handles the incoming socket requests .
* If a player wants to spend tokens on another players roll the gm has to approve first
* if a player wants to claim a token we will update the message since they do not have the permissions
* /
Hooks . once ( 'ready' , function ( ) {
2024-12-10 17:22:28 +01:00
game . socket . on ( 'system.fvtt-never-stop-blowing-up' , async ( data ) => {
2024-09-22 16:28:59 +02:00
console . log ( "Socket data received:" , data ) ;
if ( data . action === "spendTokens" ) {
console . log ( ` Request to spend tokens: ${ data . tokensToSpend } tokens for ${ data . rollActorId } by ${ data . spendingActorId } ` ) ;
// Only handle the request if the GM is logged in
if ( ! game . user . isGM ) {
console . log ( "Not GM, ignoring the token spend request." ) ;
return ;
}
// The actor who made the roll
const rollActor = game . actors . get ( data . rollActorId ) ;
// The actor who is spending tokens
const spendingActor = game . actors . get ( data . spendingActorId ) ;
//If these for some reason do not exist
if ( ! rollActor || ! spendingActor ) {
console . warn ( "Actor not found:" , data . rollActorId , data . spendingActorId ) ;
return ;
}
// Create a confirmation dialog for the GM
new Dialog ( {
title : "Approve Adversity Token Spending?" ,
content : ` <p> ${ spendingActor . name } wants to spend ${ data . tokenCost } adversity tokens on ${ rollActor . name } 's roll to increase it by ${ data . tokensToSpend } . Approve?</p> ` ,
buttons : {
yes : {
label : "Yes" ,
callback : async ( ) => {
const currentTokens = spendingActor . system . adversityTokens || 0 ;
// Update the spending actor's adversity token count
await spendingActor . update ( { "system.adversityTokens" : currentTokens - data . tokenCost } ) ;
// Modify the roll message with the new total
await _updateRollMessage ( data . rollMessageId , data . tokensToSpend , false ) ;
console . log ( ` ${ spendingActor . name } spent ${ data . tokensToSpend } tokens, updated roll total to ${ roll . cumulativeTotal } ` ) ;
ui . notifications . info ( ` ${ spendingActor . name } successfully spent ${ data . tokensToSpend } tokens. ` ) ;
}
} ,
no : {
label : "No" ,
callback : ( ) => {
ui . notifications . info ( ` The GM denied ${ spendingActor . name } 's request to spend tokens. ` ) ;
}
}
} ,
default : "yes"
} ) . render ( true ) ;
} else if ( data . action === "takeToken" ) {
// Only handle the request if the GM is logged in
if ( ! game . user . isGM ) {
console . log ( "Not GM, ignoring the token spend request." ) ;
return ;
}
let tokenControls = game . messages . get ( data . messageID ) ;
console . log ( tokenControls ) ;
// Update the chat message content with the button disabled and text changed
const updatedContent = tokenControls . content . replace (
` <button class="take-adversity" data-actor-id=" ${ data . actorID } ">Take Adversity Token</button> ` ,
` <button class="take-adversity" data-actor-id=" ${ data . actorID } " disabled>Token claimed</button> `
) ;
console . log ( "Removing Button" ) ;
// Update the message content
tokenControls . update ( { content : updatedContent } ) ;
// Set the flag on the chat message to indicate that the token has been claimed
2024-12-10 17:22:28 +01:00
tokenControls . setFlag ( "fvtt-never-stop-blowing-up" , "tokenClaimed" , true ) ;
2024-09-22 16:28:59 +02:00
}
} ) ;
} ) ;
/ * * *
* This function adds the adversity token to the actor that made the roll and logs it
*
* @ param { Event } e - The button click event
* @ param { Actor } actor - The actor object that made the roll
* /
async function _onTakeAdversityToken ( e , actor ) {
e . preventDefault ( ) ;
// Get the chat message ID (assuming it's stored in the dataset)
const messageId = e . currentTarget . closest ( '.message' ) . dataset . messageId ;
const message = game . messages . get ( messageId ) ;
// Add an adversity token to the actor
const currentTokens = actor . system . adversityTokens || 0 ;
await actor . update ( { "system.adversityTokens" : currentTokens + 1 } ) ;
// Notify the user
ui . notifications . info ( ` You gained 1 adversity token. ` ) ;
console . log ( ` Gave one adversity token to ${ actor . id } ` )
}
/ * * *
* This function allows players to spend tokens to change a roll . This will automatically be calculated in their sheet
*
* /
async function _onSpendAdversityTokens ( e , rollMessageId ) {
e . preventDefault ( ) ;
// The actor who made the roll
const rollActorId = e . currentTarget . dataset . actorId ;
const rollActor = game . actors . get ( rollActorId ) ; //technically redundant since it is also done in the main hook, but perfomance is good enuff
// Get the actor of the player who is spending tokens
const spendingPlayerActor = game . actors . get ( game . user . character ? . id || game . actors . filter ( actor => actor . testUserPermission ( game . user , "owner" ) ) [ 0 ] ? . id ) ;
if ( ! spendingPlayerActor ) {
ui . notifications . warn ( "You don't control any actors." ) ;
return ;
}
//Get the tokens to be spend from the input field
const tokenInput = $ ( e . currentTarget ) . closest ( '.adversity-controls' ) . find ( '.token-input' ) . val ( ) ;
const tokensToSpend = parseInt ( tokenInput , 10 ) ;
if ( isNaN ( tokensToSpend ) || tokensToSpend <= 0 ) {
ui . notifications . warn ( "Please enter a valid number of tokens." ) ;
return ;
}
let tokenCost = tokensToSpend ;
// If the player spending tokens is not the owner of the actor who rolled, they spend double
//(note, this is a rule of mine, I have disabled it by default)
if ( ( ! spendingPlayerActor . testUserPermission ( game . user , "owner" ) || spendingPlayerActor . id !== rollActorId ) && false ) {
tokenCost = tokensToSpend * 2 ;
}
// Ensure the spending actor has enough adversity tokens
if ( spendingPlayerActor . system . adversityTokens < tokenCost ) {
ui . notifications . warn ( ` You do not have enough adversity tokens. ` ) ;
return ;
}
// Check if the player owns the actor who made the roll
if ( spendingPlayerActor . id === rollActorId ) {
// The player owns the actor, so they can spend tokens directly without GM approval
const currentTokens = spendingPlayerActor . system . adversityTokens || 0 ;
// Deduct the tokens from the player
await spendingPlayerActor . update ( { "system.adversityTokens" : currentTokens - tokenCost } ) ;
// Modify the roll message with the new total
await _updateRollMessage ( rollMessageId , tokensToSpend , true ) ;
} else {
// The player does not own the actor, so request GM approval to spend the tokens
console . log ( ` Requesting to spend ${ tokensToSpend } tokens for ${ rollActor . name } by ${ spendingPlayerActor . name } (cost: ${ tokenCost } ) ` ) ;
// Emit a socket request to spend tokens
2024-12-10 17:22:28 +01:00
game . socket . emit ( 'system.fvtt-never-stop-blowing-up' , {
2024-09-22 16:28:59 +02:00
action : "spendTokens" ,
rollActorId : rollActorId ,
spendingActorId : spendingPlayerActor . id , // Send the player's actor who is spending the tokens
tokensToSpend : tokensToSpend ,
tokenCost : tokenCost ,
rollMessageId : rollMessageId // Pass message ID to update the roll result
} ) ;
ui . notifications . info ( ` Requested to spend ${ tokenCost } tokens for ${ rollActor . name } ` ) ;
}
}
// Helper function to send a new message with the updated roll result
async function _updateRollMessage ( rollMessageId , tokensToSpend , isPlayerOfActor ) {
const message = game . messages . get ( rollMessageId ) ;
if ( ! message ) {
console . error ( "Message not found with ID:" , rollMessageId ) ;
return ;
}
// Retrieve current tokens spent from flags, or initialize to 0 if not found
2024-12-10 17:22:28 +01:00
let cumulativeTokensSpent = message . getFlag ( "fvtt-never-stop-blowing-up" , "tokensSpent" ) || 0 ;
let newTotal = message . getFlag ( "fvtt-never-stop-blowing-up" , "newRollTotal" ) || message . rolls [ 0 ] . total ;
2024-09-22 16:28:59 +02:00
/ * i f ( i s P l a y e r O f A c t o r )
{
// Add the new tokens to the cumulative total
cumulativeTokensSpent += tokensToSpend ;
} else {
cumulativeTokensSpent += 2 * tokensToSpend ;
} * /
cumulativeTokensSpent += tokensToSpend ;
newTotal += tokensToSpend ;
2024-12-10 17:22:28 +01:00
await message . setFlag ( "fvtt-never-stop-blowing-up" , "newRollTotal" , newTotal ) ;
2024-09-22 16:28:59 +02:00
// Update the message's flags to store the cumulative tokens spent
2024-12-10 17:22:28 +01:00
await message . setFlag ( "fvtt-never-stop-blowing-up" , "tokensSpent" , cumulativeTokensSpent ) ;
2024-09-22 16:28:59 +02:00
let newContent = "" ;
if ( cumulativeTokensSpent === 1 )
{
newContent = ` You have now spent ${ cumulativeTokensSpent } token. The new roll total is ${ newTotal } . ` ;
} else {
newContent = ` You have now spent ${ cumulativeTokensSpent } tokens. The new roll total is ${ newTotal } . ` ;
}
// Create a new chat message to display the updated total
await ChatMessage . create ( {
speaker : ChatMessage . getSpeaker ( { actor : message . speaker . actor } ) ,
content : newContent ,
type : CONST . CHAT _MESSAGE _STYLES . OTHER ,
} ) ;
}