ENH: Rolls!
							parent
							
								
									7c8ca4ae1f
								
							
						
					
					
						commit
						7be97146c2
					
				|  | @ -71,6 +71,8 @@ | |||
|     "OSE.SpellDC": "Spell DC", | ||||
|     "OSE.SpellDCShort": "DC", | ||||
|     "OSE.Thac0": "THAC0", | ||||
|     "OSE.ABShort": "AB", | ||||
|     "OSE.AB": "Attack Bonus", | ||||
|     "OSE.MeleeShort": "MEL", | ||||
|     "OSE.Melee": "Melee", | ||||
|     "OSE.MeleeBonus": "Melee Bonus", | ||||
|  |  | |||
|  | @ -16,11 +16,18 @@ export class OseActor extends Actor { | |||
|     const label = game.i18n.localize(`OSE.saves.${save}.long`); | ||||
|     const rollParts = ['1d20']; | ||||
| 
 | ||||
|     const data = {...this.data, ...{ | ||||
|       rollData : { | ||||
|         type: 'Save', | ||||
|         stat: save | ||||
|       } | ||||
|     }}; | ||||
| 
 | ||||
|     // Roll and return
 | ||||
|     return OseDice.Roll({ | ||||
|       event: options.event, | ||||
|       parts: rollParts, | ||||
|       data: this.data, | ||||
|       data: data, | ||||
|       speaker: ChatMessage.getSpeaker({ actor: this }), | ||||
|       flavor: `${label} ${game.i18n.localize('OSE.SavingThrow')}`, | ||||
|       title: `${label} ${game.i18n.localize('OSE.SavingThrow')}`, | ||||
|  | @ -31,11 +38,18 @@ export class OseActor extends Actor { | |||
|     const label = game.i18n.localize(`OSE.scores.${score}.long`); | ||||
|     const rollParts = ['1d20']; | ||||
| 
 | ||||
|     const data = {...this.data, ...{ | ||||
|       rollData : { | ||||
|         type: 'Check', | ||||
|         stat: score | ||||
|       } | ||||
|     }}; | ||||
| 
 | ||||
|     // Roll and return
 | ||||
|     return OseDice.Roll({ | ||||
|       event: options.event, | ||||
|       parts: rollParts, | ||||
|       data: this.data, | ||||
|       data: data, | ||||
|       speaker: ChatMessage.getSpeaker({ actor: this }), | ||||
|       flavor: `${label} ${game.i18n.localize('OSE.AbilityCheck')}`, | ||||
|       title: `${label} ${game.i18n.localize('OSE.AbilityCheck')}`, | ||||
|  | @ -62,12 +76,23 @@ export class OseActor extends Actor { | |||
|         this.data.data.thac0.mod.melee.toString() | ||||
|       ); | ||||
|     } | ||||
|     if (game.settings.get('ose', 'ascendingAC')) { | ||||
|       rollParts.push('+', this.data.data.thac0.bba.toString()); | ||||
|     } | ||||
| 
 | ||||
|     const data = {...this.data, ...{ | ||||
|       rollData : { | ||||
|         type: 'Attack', | ||||
|         stat: attack, | ||||
|         mods: mods | ||||
|       } | ||||
|     }}; | ||||
| 
 | ||||
|     // Roll and return
 | ||||
|     return OseDice.Roll({ | ||||
|       event: options.event, | ||||
|       parts: rollParts, | ||||
|       data: this.data, | ||||
|       data: data, | ||||
|       speaker: ChatMessage.getSpeaker({ actor: this }), | ||||
|       flavor: `${label} ${game.i18n.localize('OSE.Attack')}`, | ||||
|       title: `${label} ${game.i18n.localize('OSE.Attack')}`, | ||||
|  |  | |||
|  | @ -1,4 +1,111 @@ | |||
| export class OseDice { | ||||
|   static digestResult(data, roll) { | ||||
|     let details = ""; | ||||
|      | ||||
|     // ATTACKS
 | ||||
|     let die = roll.parts[0].total; | ||||
|     if (data.rollData.type == "Attack") { | ||||
|       if (game.settings.get("ose", "ascendingAC")) { | ||||
|         let bba = data.data.thac0.bba; | ||||
|         bba += | ||||
|           data.rollData.stat == "Melee" | ||||
|             ? data.data.thac0.mod.melee + data.rollData.mods.str | ||||
|             : data.data.thac0.mod.missile + data.rollData.mods.dex; | ||||
|         details = `<div class='roll-result roll-fail'><b>Failure</b> (${bba})</div>`; | ||||
|         if (die == 1) { | ||||
|           return details; | ||||
|         } | ||||
|         details = `<div class='roll-result'><b>Hits AC ${roll.total}</b> (${bba})</div>`; | ||||
|       } else { | ||||
|         // B/X Historic THAC0 Calculation
 | ||||
|         let thac = data.data.thac0.value; | ||||
|         thac -= | ||||
|           data.rollData.stat == "Melee" | ||||
|             ? data.data.thac0.mod.melee + data.rollData.mods.str | ||||
|             : data.data.thac0.mod.missile + data.rollData.mods.dex; | ||||
| 
 | ||||
|         details = `<div class='roll-result roll-fail'><b>Failure</b> (${thac})</div>`; | ||||
|         if (thac - roll.total > 9) { | ||||
|           return details; | ||||
|         } | ||||
|         details = `<div class='roll-result'><b>Hits AC ${Math.clamped(thac - roll.total,-3,9)}</b> (${thac})</div>`; | ||||
|       } | ||||
|     } else if (data.rollData.type == "Save") { | ||||
|       // SAVING THROWS
 | ||||
|       let sv = data.data.saves[data.rollData.stat].value; | ||||
|       if (roll.total >= sv) { | ||||
|         details = `<div class='roll-result roll-success'><b>Success!</b> (${sv})</div>`; | ||||
|       } else { | ||||
|         details = `<div class='roll-result roll-fail'><b>Failure</b> (${sv})</div>`; | ||||
|       } | ||||
|     } else if (data.rollData.type == "Check") { | ||||
|       // SCORE CHECKS
 | ||||
|       let sc = data.data.scores[data.rollData.stat].value; | ||||
|       if (die == 1 || (roll.total <= sc && die < 20)) { | ||||
|         details = `<div class='roll-result roll-success'><b>Success!</b> (${sc})</div>`; | ||||
|       } else { | ||||
|         details = `<div class='roll-result roll-fail'><b>Failure</b> (${sc})</div>`; | ||||
|       } | ||||
|     } | ||||
|     return details; | ||||
|   } | ||||
| 
 | ||||
|   static async sendRoll( | ||||
|     parts = [], | ||||
|     data = {}, | ||||
|     title = null, | ||||
|     flavor = null, | ||||
|     speaker = null, | ||||
|     form = null | ||||
|   ) { | ||||
|     const template = "systems/ose/templates/chat/roll-attack.html"; | ||||
| 
 | ||||
|     let chatData = { | ||||
|       user: game.user._id, | ||||
|       speaker: speaker, | ||||
|     }; | ||||
| 
 | ||||
|     let templateData = { | ||||
|       title: title, | ||||
|       flavor: flavor, | ||||
|       data: data, | ||||
|     }; | ||||
| 
 | ||||
|     // Optionally include a situational bonus
 | ||||
|     if (form !== null) data["bonus"] = form.bonus.value; | ||||
|     if (data["bonus"]) parts.push(data["bonus"]); | ||||
| 
 | ||||
|     const roll = new Roll(parts.join(""), data).roll(); | ||||
| 
 | ||||
|     // Convert the roll to a chat message and return the roll
 | ||||
|     let rollMode = game.settings.get("core", "rollMode"); | ||||
|     rollMode = form ? form.rollMode.value : rollMode; | ||||
| 
 | ||||
|     templateData.details = OseDice.digestResult(data, roll); | ||||
|     roll.render().then((r) => { | ||||
|       templateData.rollOSE = r; | ||||
|       renderTemplate(template, templateData).then((content) => { | ||||
|         chatData.content = content; | ||||
|         chatData.sound = CONFIG.sounds.dice; | ||||
|         if (game.dice3d) { | ||||
|           game.dice3d | ||||
|             .showForRoll( | ||||
|               roll, | ||||
|               game.user, | ||||
|               true, | ||||
|               chatData.whisper, | ||||
|               chatData.blind | ||||
|             ) | ||||
|             .then((displayed) => ChatMessage.create(chatData)); | ||||
|         } else { | ||||
|           ChatMessage.create(chatData); | ||||
|         } | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     return roll; | ||||
|   } | ||||
| 
 | ||||
|   // eslint-disable-next-line no-unused-vars
 | ||||
|   static async Roll({ | ||||
|     parts = [], | ||||
|  | @ -12,32 +119,10 @@ export class OseDice { | |||
|   } = {}) { | ||||
|     let rollMode = game.settings.get("core", "rollMode"); | ||||
|     let rolled = false; | ||||
|     let filtered = parts.filter(function (el) { | ||||
|       return el != "" && el; | ||||
|     }); | ||||
| 
 | ||||
|     const _roll = (form = null, raise = false) => { | ||||
|       // Optionally include a situational bonus
 | ||||
|       if (form !== null) data["bonus"] = form.bonus.value; | ||||
|       if (data["bonus"]) filtered.push(data["bonus"]); | ||||
| 
 | ||||
|       const roll = new Roll(filtered.join(""), data).roll(); | ||||
|       // Convert the roll to a chat message and return the roll
 | ||||
|       rollMode = form ? form.rollMode.value : rollMode; | ||||
|       roll.toMessage( | ||||
|         { | ||||
|           speaker: speaker, | ||||
|           flavor: flavor, | ||||
|         }, | ||||
|         { rollMode } | ||||
|       ); | ||||
|       rolled = true; | ||||
|       return roll; | ||||
|     }; | ||||
| 
 | ||||
|     const template = "systems/ose/templates/chat/roll-dialog.html"; | ||||
|     let dialogData = { | ||||
|       formula: filtered.join(" "), | ||||
|       formula: parts.join(" "), | ||||
|       data: data, | ||||
|       rollMode: rollMode, | ||||
|       rollModes: CONFIG.Dice.rollModes, | ||||
|  | @ -48,7 +133,14 @@ export class OseDice { | |||
|         label: game.i18n.localize("OSE.Roll"), | ||||
|         icon: '<i class="fas fa-dice-d20"></i>', | ||||
|         callback: (html) => { | ||||
|           roll = _roll(html[0].children[0]); | ||||
|           roll = OseDice.sendRoll( | ||||
|             parts, | ||||
|             data, | ||||
|             title, | ||||
|             flavor, | ||||
|             speaker, | ||||
|             html[0].children[0] | ||||
|           ); | ||||
|         }, | ||||
|       }, | ||||
|       cancel: { | ||||
|  | @ -70,7 +162,7 @@ export class OseDice { | |||
|         buttons: buttons, | ||||
|         default: "ok", | ||||
|         close: () => { | ||||
|             resolve(rolled ? roll : false)     | ||||
|           resolve(rolled ? roll : false); | ||||
|         }, | ||||
|       }).render(true); | ||||
|     }); | ||||
|  |  | |||
|  | @ -188,7 +188,7 @@ | |||
|           text-indent: 8px; | ||||
|         } | ||||
|         font-weight: 300; | ||||
|         font-size: 12px; | ||||
|         font-size: 13px; | ||||
|         background: $darkBackground; | ||||
|         color: white; | ||||
|         input { | ||||
|  |  | |||
|  | @ -1,97 +1,124 @@ | |||
| 
 | ||||
| .ose.chat-card { | ||||
|     font-style: normal; | ||||
|     font-size: 12px; | ||||
|    | ||||
|     .card-header { | ||||
|       padding: 3px 0; | ||||
|       border-top: 2px groove #FFF; | ||||
|       border-bottom: 2px groove #FFF; | ||||
|    | ||||
|       img { | ||||
|         flex: 0 0 36px; | ||||
|         margin-right: 5px; | ||||
| .ose.chat-block { | ||||
|   margin: 0; | ||||
|   .chat-title { | ||||
|     background: $darkBackground; | ||||
|     border: 1px solid black; | ||||
|     border-radius: 3px; | ||||
|     color: white; | ||||
|     padding: 2px; | ||||
|     box-shadow: 0 0 2px #FFF inset; | ||||
|     text-align: center; | ||||
|     margin: 4px 0; | ||||
|     font-size: 16px; | ||||
|   } | ||||
|   .chat-details { | ||||
|     padding: 4px; | ||||
|     font-size: 13px; | ||||
|     .roll-result { | ||||
|       text-align: center; | ||||
|       &.roll-success { | ||||
|         color: #18520b; | ||||
|       } | ||||
|    | ||||
|       h3 { | ||||
|         flex: 1; | ||||
|         margin: 0; | ||||
|         line-height: 36px; | ||||
|         color: $colorOlive; | ||||
|         &:hover { | ||||
|           color: #111; | ||||
|           text-shadow: 0 0 10px red; | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|    | ||||
|     .card-content { | ||||
|       margin: 5px 0; | ||||
|    | ||||
|       h3 { | ||||
|         font-size: 12px; | ||||
|         margin: 0; | ||||
|         font-weight: bold; | ||||
|       } | ||||
|    | ||||
|       > * { | ||||
|         -webkit-user-select: text; | ||||
|         -moz-user-select: text; | ||||
|         -ms-user-select: text; | ||||
|         user-select: text; | ||||
|       } | ||||
|     } | ||||
|    | ||||
|     .card-buttons { | ||||
|       margin: 5px 0; | ||||
|    | ||||
|       span { | ||||
|         display: block; | ||||
|         line-height: 28px; | ||||
|         text-align: center; | ||||
|         border: 1px solid $colorTan; | ||||
|       } | ||||
|    | ||||
|       button { | ||||
|         font-size: 12px; | ||||
|         height: 24px; | ||||
|         line-height: 20px; | ||||
|         margin: 2px 0; | ||||
|       } | ||||
|     } | ||||
|    | ||||
|     .card-footer { | ||||
|       padding: 3px 0 0; | ||||
|       border-top: 2px groove #FFF; | ||||
|    | ||||
|       span { | ||||
|         border-right: 2px groove #FFF; | ||||
|         padding: 0 5px 0 0; | ||||
|         font-size: 10px; | ||||
|    | ||||
|         &:last-child { | ||||
|           border-right: none; | ||||
|           padding-right: 0; | ||||
|         } | ||||
|       &.roll-fail { | ||||
|         color: #aa0200; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|    | ||||
|   .dice-roll .dice-total { | ||||
|     &.success { | ||||
|       color: inherit; | ||||
|       background: #c7d0c0; | ||||
|       border: 1px solid #006c00; | ||||
| } | ||||
| 
 | ||||
| .ose.chat-card { | ||||
|   font-style: normal; | ||||
|   font-size: 12px; | ||||
| 
 | ||||
|   .card-header { | ||||
|     padding: 3px 0; | ||||
|     border-top: 2px groove #fff; | ||||
|     border-bottom: 2px groove #fff; | ||||
| 
 | ||||
|     img { | ||||
|       flex: 0 0 36px; | ||||
|       margin-right: 5px; | ||||
|     } | ||||
|     &.failure { | ||||
|       color: inherit; | ||||
|       background: #ffdddd; | ||||
|       border: 1px solid #6e0000; | ||||
| 
 | ||||
|     h3 { | ||||
|       flex: 1; | ||||
|       margin: 0; | ||||
|       line-height: 36px; | ||||
|       color: $colorOlive; | ||||
|       &:hover { | ||||
|         color: #111; | ||||
|         text-shadow: 0 0 10px red; | ||||
|       } | ||||
|     } | ||||
|     &.critical { | ||||
|       color: green; | ||||
|   } | ||||
| 
 | ||||
|   .card-content { | ||||
|     margin: 5px 0; | ||||
| 
 | ||||
|     h3 { | ||||
|       font-size: 12px; | ||||
|       margin: 0; | ||||
|       font-weight: bold; | ||||
|     } | ||||
|     &.fumble { | ||||
|       color: red; | ||||
| 
 | ||||
|     > * { | ||||
|       -webkit-user-select: text; | ||||
|       -moz-user-select: text; | ||||
|       -ms-user-select: text; | ||||
|       user-select: text; | ||||
|     } | ||||
|   } | ||||
|   } | ||||
| 
 | ||||
|   .card-buttons { | ||||
|     margin: 5px 0; | ||||
| 
 | ||||
|     span { | ||||
|       display: block; | ||||
|       line-height: 28px; | ||||
|       text-align: center; | ||||
|       border: 1px solid $colorTan; | ||||
|     } | ||||
| 
 | ||||
|     button { | ||||
|       font-size: 12px; | ||||
|       height: 24px; | ||||
|       line-height: 20px; | ||||
|       margin: 2px 0; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .card-footer { | ||||
|     padding: 3px 0 0; | ||||
|     border-top: 2px groove #fff; | ||||
| 
 | ||||
|     span { | ||||
|       border-right: 2px groove #fff; | ||||
|       padding: 0 5px 0 0; | ||||
|       font-size: 10px; | ||||
| 
 | ||||
|       &:last-child { | ||||
|         border-right: none; | ||||
|         padding-right: 0; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .dice-roll .dice-total { | ||||
|   &.success { | ||||
|     color: inherit; | ||||
|     background: #c7d0c0; | ||||
|     border: 1px solid #006c00; | ||||
|   } | ||||
|   &.failure { | ||||
|     color: inherit; | ||||
|     background: #ffdddd; | ||||
|     border: 1px solid #6e0000; | ||||
|   } | ||||
|   &.critical { | ||||
|     color: green; | ||||
|   } | ||||
|   &.fumble { | ||||
|     color: red; | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -37,4 +37,4 @@ | |||
|   .resizable { | ||||
|     overflow: auto; | ||||
|   } | ||||
| } | ||||
| } | ||||
|  | @ -24,6 +24,7 @@ | |||
|         }, | ||||
|         "thac0": { | ||||
|           "value": 19, | ||||
|           "bba": 0, | ||||
|           "mod": { | ||||
|             "missile": 0, | ||||
|             "melee": 0 | ||||
|  |  | |||
|  | @ -114,15 +114,32 @@ | |||
|         <div class="flexrow"> | ||||
|             <ul class="attributes flexrow"> | ||||
|                 <li class="attribute attribute-secondaries attack" data-attack="Melee"> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.Melee'}}"><a>{{localize 'OSE.MeleeShort'}}</a></h4> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.Melee'}}"> | ||||
|                         <a>{{localize 'OSE.MeleeShort'}}</a></h4> | ||||
|                     <div class="flexrow"> | ||||
|                         <div class="attribute-value"> | ||||
|                             {{#if config.ascendingAC}} | ||||
|                             {{add data.thac0.mod.melee (add mods.str data.thac0.bba)}} | ||||
|                             {{else}} | ||||
|                             {{subtract data.thac0.mod.melee (subtract mods.str data.thac0.value)}} | ||||
|                             {{/if}} | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </li> | ||||
|                 <li class="attribute attack" data-attack="Attack"> | ||||
|                     <h4 class="attribute-name box-title" title="{{ localize 'OSE.Thac0' }}"><a>{{ localize "OSE.Thac0" }}</a> | ||||
|                 {{#if config.ascendingAC}} | ||||
|                 <li class="attribute"> | ||||
|                     <h4 class="attribute-name box-title" title="{{ localize 'OSE.AB' }}">{{ localize "OSE.ABShort"}} | ||||
|                     </h4> | ||||
|                     <div class="flexrow"> | ||||
|                         <div class="attribute-value"> | ||||
|                             <input name="data.thac0.bba" type="text" value="{{data.thac0.bba}}" placeholder="0" | ||||
|                                 data-dtype="Number" /> | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </li> | ||||
|                 {{else}} | ||||
|                 <li class="attribute"> | ||||
|                     <h4 class="attribute-name box-title" title="{{ localize 'OSE.Thac0' }}">{{ localize "OSE.Thac0"}} | ||||
|                     </h4> | ||||
|                     <div class="flexrow"> | ||||
|                         <div class="attribute-value"> | ||||
|  | @ -131,11 +148,17 @@ | |||
|                         </div> | ||||
|                     </div> | ||||
|                 </li> | ||||
|                 {{/if}} | ||||
|                 <li class="attribute attribute-secondaries attack" data-attack="Missile"> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.Missile'}}"><a>{{localize 'OSE.MissileShort'}}</a></h4> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.Missile'}}"> | ||||
|                         <a>{{localize 'OSE.MissileShort'}}</a></h4> | ||||
|                     <div class="flexrow"> | ||||
|                         <div class="attribute-value"> | ||||
|                             {{#if config.ascendingAC}} | ||||
|                             {{add data.thac0.mod.missile (add mods.dex data.thac0.bba)}} | ||||
|                             {{else}} | ||||
|                             {{subtract data.thac0.mod.missile (subtract mods.dex data.thac0.value)}} | ||||
|                             {{/if}} | ||||
|                         </div> | ||||
|                     </div> | ||||
|                 </li> | ||||
|  | @ -144,7 +167,8 @@ | |||
|         <div class="flexrow"> | ||||
|             <ul class="attributes flexrow"> | ||||
|                 <li class="attribute attribute-secondaries"> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.MovementEncounter'}}">{{localize 'OSE.MovementEncounterShort'}}</h4> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.MovementEncounter'}}"> | ||||
|                         {{localize 'OSE.MovementEncounterShort'}}</h4> | ||||
|                     <div class="flexrow"> | ||||
|                         <div class="attribute-value"> | ||||
|                             {{divide data.movement.base 3}} | ||||
|  | @ -160,7 +184,8 @@ | |||
|                     </div> | ||||
|                 </li> | ||||
|                 <li class="attribute attribute-secondaries"> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.MovementOverland'}}">{{localize 'OSE.MovementOverlandShort'}}</h4> | ||||
|                     <h4 class="attribute-name box-title" title="{{localize 'OSE.MovementOverland'}}"> | ||||
|                         {{localize 'OSE.MovementOverlandShort'}}</h4> | ||||
|                     <div class="flexrow"> | ||||
|                         <div class="attribute-value"> | ||||
|                             {{divide data.movement.base 5}} | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| <section class="ose chat-message"> | ||||
|     <div class="ose chat-block"> | ||||
|         <h2 class="chat-title">{{title}}</h2> | ||||
|         {{#if details}}<div class="chat-details">{{{details}}}</div>{{/if}} | ||||
|         {{#if rollOSE}}<div>{{{rollOSE}}}</div>{{/if}} | ||||
|     </div> | ||||
| </section> | ||||
		Loading…
	
		Reference in New Issue