Roll20 uses cookies to improve your experience on our site. Cookies enable you to enjoy certain features, social sharing functionality, and tailor message and display ads to your interests on our site and others. They also help us understand how our site is being used. By continuing to use our site, you consent to our use of cookies. Update your cookie preferences .
×
Create a free account

New Warhammer 4e Character Sheet

1556755814
Victor B.
Pro
Sheet Author
API Scripter
I've taken the wonderful 2e Warhammer sheet and upgraded it to 4e including critical hit tables/oops tables, wrath table and miscasts.&nbsp; This is version 1 but I've been using it for a couple of months now so it's fairly battle tested.&nbsp; I'd like to have people use it and get some feedback before committing to Roll20.&nbsp;&nbsp; The bad news is that you'll have to copy your characters over into this and it's doubtful the field names are the same between this new version and any other sheet you may be using.&nbsp; I'd suggest creating a new game and having both old and new up at the same time so you can quickly copy data into this new sheet.&nbsp; Enjoy.&nbsp; Reach out to me if you have questions Here's the link <a href="https://github.com/vicberg/Roll20-Warhammer-4e-Character-Sheet" rel="nofollow">https://github.com/vicberg/Roll20-Warhammer-4e-Character-Sheet</a>
1556756094
Victor B.
Pro
Sheet Author
API Scripter
Characteristics, Skills &amp; Talents Weapons &amp; Armor Background, Spellbook and Inventory
1556777190
vÍnce
Pro
Sheet Author
Nice looking sheet Victor.
1556796477
Victor B.
Pro
Sheet Author
API Scripter
I'm not taking any credit for this.&nbsp; The incredible work at Axel put into 2e version is what made this happen.&nbsp;&nbsp;
1556802007
Andreas J.
Forum Champion
Sheet Author
Translator
Victor B. said: The bad news is that you'll have to copy your characters over into this and it's doubtful the field names are the same between this new version and any other sheet you may be using.&nbsp; I'd suggest creating a new game and having both old and new up at the same time so you can quickly copy data into this new sheet. There could be created sheetworkers to to migrate stats from the old attributes to new ones if you changed the names. Any particular reason why you changed name of stats? Trying to search the forums I didn't immediately find an example on how to migrate attributes, sure be great if any such examples would exist as there are many great sheets like this that would benefit from that, shrinking the work of people to migrate from one sheet to another.
1556802905
Finderski
Pro
Sheet Author
Compendium Curator
Andreas J. said: Victor B. said: The bad news is that you'll have to copy your characters over into this and it's doubtful the field names are the same between this new version and any other sheet you may be using.&nbsp; I'd suggest creating a new game and having both old and new up at the same time so you can quickly copy data into this new sheet. There could be created sheetworkers to to migrate stats from the old attributes to new ones if you changed the names. Any particular reason why you changed name of stats? Trying to search the forums I didn't immediately find an example on how to migrate attributes, sure be great if any such examples would exist as there are many great sheets like this that would benefit from that, shrinking the work of people to migrate from one sheet to another. Well....here's how I do it for the Savage Worlds Tabbed sheet. &nbsp;I'm sure GiGs could make this more efficient/cleaner... :) function convertRS (originSection, destinationSection, ofieldArray, dfieldArray, daddlfieldArray, defaultValues) { /*------ example of call ------ || &nbsp;originSection = section (without repeating_) fields will be migrated FROM || &nbsp;destinationSection = section (without repeating_) fields will be migrated TO || &nbsp;ofieldArray = Array of Fields that need to be migrated || &nbsp;dfieldArray = Array of Fields the ofieldArray will be mapped to || &nbsp;NOTE: The number of fields in the ofieldArray and dfieldArray should be the same || &nbsp;daddlfieldArray = Additional fields in the destination section that may not be in the original section || &nbsp;defaultValues = Default values for list of fields in daddlfieldArray || &nbsp;NOTE: The number of fields in the daddlfieldArray and defaultValues should be the same || || &nbsp;Example: || &nbsp;var hindranceFields = ["hindrance","hindrancedescription"]; || &nbsp;convertRS("mhindrances","hindrances",hindranceFields,hindranceFields); --------*/ console.log("originSection: " + originSection); console.log("destinationSection: " + destinationSection); console.log("ofieldArray: " + ofieldArray); console.log("dfieldarray: " + dfieldArray); console.log("Number of Entries in ofieldArray: " + ofieldArray.length); console.log("Number of Entries in dfieldArray: " + dfieldArray.length); let originRS = "repeating_"+originSection; getSectionIDs(originRS, function(idArray) { if (idArray.length &gt; 0) { console.log("originSection IDs: " + idArray); let oldAttrs = []; for (let a=0; a &lt; idArray.length; a++) { for (let b=0; b &lt; ofieldArray.length; b++) { oldAttrs[oldAttrs.length] = originRS + "_" + idArray[a] + "_" + ofieldArray[b]; } } getAttrs(oldAttrs, function(v) { let sattrs = {}; for(let i=0; i &lt; idArray.length; i++) { let newrowid = generateRowID(); console.log("~~~~ ConvertRS Iteration " + i + ":: for Section: "+ originSection + ":: id generated: " + newrowid +" ~~~~"); let originRF = "repeating_" + originSection + "_" + idArray[i] + "_"; let destinationRF = "repeating_" + destinationSection + "_" + newrowid + "_"; //migrate original fields to new fields for(var r=0; r &lt; ofieldArray.length; r++) { let getORF = '', putdestinationRF = ''; console.log("********************"); console.log("** originRF: "+originRF+ofieldArray[r]); console.log("** destinationRF: "+destinationRF+dfieldArray[r]); console.log("********************"); getORF = originRF+ofieldArray[r]; console.log(i+"-"+r+") Current value of getORF: "+getORF+": "+v[getORF]); putdestinationRF = destinationRF+dfieldArray[r]; if (ofieldArray[r] === "DmgType" || ofieldArray[r] === "RDmgType") { if (typeof v[getORF] === 'undefined') { sattrs[putdestinationRF]="d4!" } else { sattrs[putdestinationRF]="d"+v[getORF]; } } else { if (typeof v[getORF] === 'undefined') { sattrs[putdestinationRF]=''; } else { sattrs[putdestinationRF]=v[getORF]; } } } //Check to see if we need to set any default values console.log("*** Checking for Additional Fields to set Defaults ***"); if(typeof daddlfieldArray === 'undefined') { console.log("*** No Additional Fields to set...undefined ***"); } else { if (daddlfieldArray.length &gt; 0 &amp;&amp; daddlfieldArray.length === defaultValues.length) { console.log("!! Need to set some additional fields !!"); //Set default value for other fields for(let r=0; r &lt; daddlfieldArray.length; r++) { let getORF = '', putdestinationRF = ''; console.log("********************"); console.log("** destinationRF: "+destinationRF+daddlfieldArray[r]); console.log("********************"); putdestinationRF = destinationRF+daddlfieldArray[r]; sattrs[putdestinationRF] = defaultValues[r]; } } else { console.log("!! No additional fields to set OR the number of fields doesn't match the number of values !!"); } } console.log("##### sattrs: " + JSON.stringify(sattrs) + " #####"); setAttrs(sattrs); sattrs = {}; } }); } else { console.log("&lt;===== "+originSection+" had no rows =====&gt;"); } }); } const upgradeSheet = function(version) { if (version &lt; 3.0) { //migrate old fields to new const oldAtts = ["agMod","smMod","spMod","stMod","viMod","UnskilledEncumbrance","FightingMod","Academicsskill","AcademicsskillMod","Athletics","AthleticsMod","Battle","BattleMod","BoatingMod","Climbing","ClimbingMod","Commonknowledge","CommonknowledgeMod","Driving","DrivingMod","Electronics","ElectronicsMod","Faith","FaithMod","Focus","FocusMod","Gambling","GamblingMod","Guts","GutsMod","Hacking","HackingMod","Healing","HealingMod","Intimidation","IntimidationMod","Investigation","InvestigationMod","Language","LanguageMod","Lockpicking","LockpickingMod","Notice","NoticeMod","Occult","OccultMod","Performance","PerformanceMod","Persuasion","PersuasionMod","Piloting","PilotingMod","Psionics","PsionicsMod","Repair","RepairMod","Research","ResearchMod","Riding","RidingMod","Ritual","RitualMod","Science","ScienceMod","Shooting","ShootingMod","Spellcasting","SpellcastingMod","Stealth","StealthMod","Streetwise","StreetwiseMod","Survival","SurvivalMod","Swimming","SwimmingMod","Taunt","TauntMod","Thievery","ThieveryMod","Throwing","ThrowingMod","Tracking","TrackingMod","WeirdScience","WeirdScienceMod","LinkedPsionicsAtt","LinkedRitualAttScore","LinkedSpellcastingAttScore","LinkedWeirdScienceAttScore","PAstrength","PAstMod","PAstrollMod"]; const newAtts = ["agilitymod","smartsmod","spiritmod","strengthmod","vigormod","unskilledencumbrance","fightingmod","academicsskill","academicsskillmod","athletics","athleticsmod","battle","battlemod","boatingmod","climbing","climbingmod","commonknowledge","commonknowledgemod","driving","drivingmod","electronics","electronicsmod","faith","faithmod","focus","focusmod","gambling","gamblingmod","guts","gutsmod","hacking","hackingmod","healing","healingmod","intimidation","intimidationmod","investigation","investigationmod","language","languagemod","lockpicking","lockpickingmod","notice","noticemod","occult","occultmod","performance","performancemod","persuasion","persuasionmod","piloting","pilotingmod","psionics","psionicsmod","repair","repairmod","research","researchmod","riding","ridingmod","ritual","ritualmod","science","sciencemod","shooting","shootingmod","spellcasting","spellcastingmod","stealth","stealthmod","streetwise","streetwisemod","survival","survivalmod","swimming","swimmingmod","taunt","tauntmod","thievery","thieverymod","throwing","throwingmod","tracking","trackingmod","weirdscience","weirdsciencemod","linkedpsionicsatt","linkedritualattscore","linkedspellcastingattscore","linkedweirdscienceattscore","pastrength","pastmod","pastrollmod","rastrength","rastmod","rastrollmod"]; let sAttrs = {}; getAttrs(oldAtts, function(v) { //Convert from Old Trait Mod Format (e.g. agMod) to the new format (e.g. agilitymod) for (let i=0; i&lt;oldAtts.length; i++) { console.log(newAtts[i] + " = " + v[oldAtts[i]]); sAttrs[newAtts[i]] = v[oldAtts[i]]; } setAttrs(sAttrs); //Need to create rank and display values for existing sheets const otraits = ['agility','smarts','spirit','strength','vigor','fighting','academicsskill','athletics','battle','boating','climbing','commonknowledge','driving','electronics','faith','focus','gambling','guts','hacking','healing','intimidation','investigation','language','lockpicking','notice','occult','performance','persuasion','piloting','psionics','repair','research','riding','ritual','science','shooting','spellcasting','stealth','streetwise','survival','swimming','taunt','throwing','thievery','tracking','weirdscience','repeating_skills:skillnamerank','pastrength','rastrength']; otraits.forEach(otraits =&gt; { &nbsp; rankNdisplay(otraits); }); }); //migrate old repeating skills fields to new getSectionIDs("skills", function(idArray) { if (idArray.length &gt; 0) { console.log("originSection IDs: " + idArray); let oldAttrs = []; let newAttrs = []; for (let a=0; a &lt; idArray.length; a++) { oldAttrs[oldAttrs.length] = "repeating_skills_" + idArray[a] + "_" + "skillnamemod"; newAttrs[newAttrs.length] = "repeating_skills_" + idArray[a] + "_" + "skillnamerankmod"; } getAttrs(oldAttrs, function(v) { const sattrs = {}; for(let i=0; i &lt; idArray.length; i++) { console.log(newAttrs[i]+" == "+oldAttrs[i]); console.log(newAttrs[i]+" == "+ v[oldAttrs[i]]); if (typeof v[oldAttrs[i]] !== 'undefined') { sattrs[newAttrs[i]]=v[oldAttrs[i]]; } else { sattrs[newAttrs[i]] = 0; } } console.log("##### sattrs: " + JSON.stringify(sattrs) + " #####"); setAttrs(sattrs); }); setAttrs({staticKnowledgeOther: "on"}); } else { console.log("&lt;===== had no rows =====&gt;"); } }); //Migrate Melee Weapons to Weapons let meleeFields = ["WeaponType","MeleeDieNum","DmgType","MDmgMod","WeaponWeight","meleeWNotes"]; let weaponFields = ["weapon","dmgnumdice","dmgdietype","dmgbonusnum","weaponweight","weaponnotes"]; let moreweaponFields = ["weaponcarried"]; let weaponDefaultValues = ["on"]; convertRS("meleeweapons","weapons",meleeFields,weaponFields,moreweaponFields,weaponDefaultValues); //Migrate Ranged Weapons to Weapons let rangedFields = ["RWeaponType","WeaponRange","WeaponRoF","NoDmgDice","RDmgType","RDmgMod","RWeaponWeight","rangeWNotes"]; let weaponFieldsII =["weapon","range","rof","dmgnumdice","dmgdietype","dmgbonusnum","weaponweight","weaponnotes"]; let morerangedweaponFields = ["damageatt","weaponcarried"]; let rangedweaponDefaultValues = ["na","on"]; convertRS ("rangeweapons", "weapons", rangedFields, weaponFieldsII,morerangedweaponFields,rangedweaponDefaultValues); //Migrate Power Armor Ranged Weapons to Power Armor Weapons let parangedFields = ["PAWeaponType","PAWeaponRange","PAWeaponRoF","PANoDmgDice","PARDmgType","PARDmgMod","PAWeaponNotes"]; let paweaponFieldsII =["paweapon","parange","parof","padmgnumdice","padmgdietype","padmgbonusnum","paweaponnotes"]; let pamorerangedweaponFields = ["padamageatt"]; let parangedweaponDefaultValues = ["na"]; convertRS ("PAweapons", "paweapons", parangedFields, paweaponFieldsII, pamorerangedweaponFields, parangedweaponDefaultValues); //Migrate Power Armor Melee Weapons to Power Armor Weapons let pameleeFields = ["PAMWeaponType","PAMMeleeDieNum","PAMDmgType","PAMMDmgMod","PAMmeleeWNotes"]; let paweaponFields = ["paweapon","padmgnumdice","padmgdietype","padmgbonusnum","paweaponnotes"]; let pamoreweaponFields = ["padamageatt"]; let paweaponDefaultValues = ["strength"]; convertRS("PAmeleeweapons","paweapons", pameleeFields, paweaponFields, pamoreweaponFields, paweaponDefaultValues); //Migrate Robot Armor Ranged Weapons to Robot Armor Weapons let rarangedFields = ["RAWeaponType","RAWeaponRange","RAWeaponRoF","RANoDmgDice","RARDmgType","RARDmgMod","RAWeaponNotes"]; let raweaponFieldsII =["raweapon","rarange","rarof","radmgnumdice","radmgdietype","radmgbonusnum","raweaponnotes"]; let ramorerangedweaponFields = ["radamageatt"]; let rarangedweaponDefaultValues = ["na"]; convertRS ("RAweapons", "raweapons", rarangedFields, raweaponFieldsII, ramorerangedweaponFields, rarangedweaponDefaultValues); //Migrate Robot Armor Melee Weapons to Robot Armor Weapons let rameleeFields = ["RAMWeaponType","RAMMeleeDieNum","RAMDmgType","RAMMDmgMod","RAMmeleeWNotes"]; let raweaponFields = ["raweapon","radmgnumdice","radmgdietype","radmgbonusnum","raweaponnotes"]; let ramoreweaponFields = ["radamageatt"]; let raweaponDefaultValues = ["strength"]; convertRS("RAmeleeweapons","raweapons", rameleeFields, raweaponFields, ramoreweaponFields, raweaponDefaultValues); //Migrate Damaging Spells to powers let dmgSpells = ["Spell","SpellCost","SpellRange","SpellDuration","NoSpllDmgDice","SDmgType","SDmgMod","DmgSpellNotes"]; let powerFields =["power","pp","powerrange","powerduration","spelldmgnumdice","spelldmgdietype","spelldmgbonusnum","spellnotes"]; let moredmgspellFields = ["spelldamageatt"]; let moredmgspellDefaultValues = ["na"]; convertRS ("dmgspells", "spells", dmgSpells, powerFields, moredmgspellFields, moredmgspellDefaultValues); //Migrate Non-Damaging Spells to Powers let nondmgSpells = ["NDSpell","NDSpellCost","NDSpellRange","NDSpellDuration","ndSpellNotes"]; let powerFieldsII = ["power","pp","powerrange","powerduration","spellnotes"]; let morespellFields = ["spelldamageatt","spellbutton","spelldmgnumdice"]; let morespellDefaultValues = ["na","2","0"]; convertRS ("ndspells", "spells", nondmgSpells, powerFieldsII, morespellFields, morespellDefaultValues); //Migrate Ground Vehicles Crew let crewfields = ["GVCrewName","GVCrewType","GVCrewPos","GVCrewSalary","GVCrewShares","GVCrewNotes"]; convertRS ("FShipCrew", "GShipCrew", crewfields, crewfields); //Migrate Walker Vehicles Crew let wcrewfields = ["WalkerCrewName","WalkerCrewType","WalkerCrewPos","WalkerCrewSalary","WalkerCrewShares","WalkerCrewNotes"]; convertRS ("FShipCrew", "WShipCrew", wcrewfields, wcrewfields); } if (version &lt; 3.1) { console.log(`!@!@! UPGRADING TO VERSION 3.1 !@!@!`) //Set up Multi Attack option stepwiseFaux("weapons","rof"); //Migrate Nerves of Steel getAttrs(["NoS"], function(v) { if(v.NoS === "-1") { console.log("!!! Nerves of Steel !!!"); let nerves = getTranslationByKey('nerves-of-steel'); setAttrs({ reducewoundsby: "-1", reducereason: nerves }); } else if(v.NoS === "-2") { console.log("!!! Nerves of Steel !!!"); let nerves = getTranslationByKey('improved-nerves-of-steel'); setAttrs({ reducewoundsby: "-2", reducereason: nerves }); } else { console.log("No Nerves of Steel to care for"); } }); } else { console.log("!!! Sheet Already Upgraded !!!"); } //Update Sheet Version Number console.log("**** Updating Sheet Version ****"); setAttrs({ character_sheet: sheetVersionPrefix + sheetVersion }); console.log("&lt;===== End Sheet Upgrade Conversion Process =====&gt;"); };
1556803051

Edited 1556803492
Victor B.
Pro
Sheet Author
API Scripter
Stat names haven't changed from 2e to 4e so it's a pretty simple upgrade.&nbsp; Stats have changed from Arioch's current 4e sheet to this one because I maintained the 2e names.&nbsp; &nbsp;For people using the 2e character sheet, you don't need to do anything special other than take a screen shot of your 2e weapons and skills.&nbsp; You'll need to roll up Initiative and Dexterity because they don't exist in 2e and re-enter your weapons because I made weapons into repeating sections and re-enter your skills because skills changed significantly from 2e to 4e.&nbsp; Talents will come across from 2e to 4e but even there, talents have changed significantly and will require review. I'll reach out to Arioch on a sheet worker from his to this one.&nbsp;&nbsp;