diff --git a/foundry/availability.csv b/foundry/availability.csv new file mode 100644 index 0000000..20515ed --- /dev/null +++ b/foundry/availability.csv @@ -0,0 +1,9 @@ +Type,SP Tier,Availability +Supplemental,1,Scarce +Essential,Base,Scarce +Supplemental,2,Rare +Essential,Base+1 SP,Rare +Supplemental,3,Very Rare +Essential,Base+2 SP,Very Rare +Archeotech,Any,Extremely Rare +Xenotech,Any,Near Unique diff --git a/foundry/components_supplemental_xenos_archeotech.csv b/foundry/components_supplemental_xenos_archeotech.csv index a2748f3..aeb1837 100644 --- a/foundry/components_supplemental_xenos_archeotech.csv +++ b/foundry/components_supplemental_xenos_archeotech.csv @@ -65,3 +65,106 @@ Archeotech,Cogitator Interlink,All (unless noted),UNKN,UNKN,UNKN,Crew Rating +5 Archeotech,Energistic Conversion Matrix,All (unless noted),UNKN,UNKN,UNKN,Divert Power to Speed as Free Action (limit +5 Speed); can depower Components.,Into the Storm (Archeotech Components) Archeotech,Empyrean Mantle,All (unless noted),UNKN,0,UNKN,External; Silent Running detection is two steps harder; +50 EP to Criminal objectives.,Into the Storm (Archeotech Components) Archeotech,Teleportarium (Teleportation Array),All (unless noted),1,1,1,Enables Hit & Run vs targets with Void Shields down; per ItS usage rules.,Into the Storm (Archeotech Components) +Category,Component,Appropriate Hull Types,Power,Space,SP,Notes / Effects,Source +Cargo Holds and Passenger Compartments,Cargo Hold and Lighter Bay,Raider/Frigate/Light Cruiser/Cruiser,1,2,1,Standard cargo + lighter bay,Table 8-5 +Cargo Holds and Passenger Compartments,Compartmentalized Cargo Hold,Raider/Frigate/Light Cruiser/Cruiser,2,5,1,Segmented holds; safer cargo,Table 8-5 +Cargo Holds and Passenger Compartments,Main Cargo Hold,Transports,2,4,1,Primary cargo storage,Table 8-5 +Cargo Holds and Passenger Compartments,Luxury Passenger Quarters,All Ships,2,1,1,Passenger comfort/morale,Table 8-5 +Cargo Holds and Passenger Compartments,Barracks,All Ships,2,4,2,Troop capacity; boarding bonus,Table 8-5 + +Augments and Enhancements,Augmented Retro-thrusters,Raider/Frigate,3,0,2,Improved decel/turn,Table 8-5 +Augments and Enhancements,Augmented Retro-thrusters,Transports/Light Cruisers,4,0,2,Improved decel/turn,Table 8-5 +Augments and Enhancements,Augmented Retro-thrusters,Cruisers,5,0,2,Improved decel/turn,Table 8-5 +Augments and Enhancements,Reinforced Interior Bulkheads,Transports/Raiders/Frigates,0,2,2,Structural survivability,Table 8-5 +Augments and Enhancements,Reinforced Interior Bulkheads,Light Cruisers/Cruisers,0,3,2,Structural survivability,Table 8-5 +Augments and Enhancements,Armour Plating†,Transports/Raiders/Frigates,0,1,2,"† Once per vessel",Table 8-5 +Augments and Enhancements,Armour Plating†,Light Cruisers/Cruisers,0,2,2,"† Once per vessel",Table 8-5 +Augments and Enhancements,Armoured Prow†,Cruisers,0,4,2,"† Once per vessel",Table 8-5 +Augments and Enhancements,Tenebro-Maze†,Transports/Raiders/Frigates,1,2,2,"† Once per vessel",Table 8-5 +Augments and Enhancements,Tenebro-Maze†,Light Cruisers/Cruisers,2,3,2,"† Once per vessel",Table 8-5 + +Additional Facilities,Extended Supply Vaults,All Ships,1,4,2,Extended endurance,Table 8-5 +Additional Facilities,Crew Reclamation Facility,All Ships,1,1,1,Reclaims crew losses,Table 8-5 +Additional Facilities,Munitorium,Transports/Raiders/Frigates,2,3,2,Ordnance logistics,Table 8-5 +Additional Facilities,Munitorium,Light Cruisers/Cruisers,3,4,2,Ordnance logistics,Table 8-5 +Additional Facilities,Temple-shrine to the God Emperor,All Ships,1,1,1,Morale/Creed benefits,Table 8-5 +Additional Facilities,Librarium Vault,All Ships,1,1,1,Archives/intel,Table 8-5 +Additional Facilities,Trophy Room,All Ships,1,1,1,Social/renown,Table 8-5 +Additional Facilities,Observation Dome,All Ships,0,1,1,Observation/social,Table 8-5 +Additional Facilities,Murder-Servitors,All Ships,1,1,2,Boarding bonus,Table 8-5 + +Cargo and Passenger Compartments,Brig,All Ships,1,1,1,Prison cells,Table 1-10 +Cargo and Passenger Compartments,Ship’s Stores,Light Cruisers/Cruisers,1,5,2,Spares & supplies,Table 1-10 +Cargo and Passenger Compartments,Ship’s Stores,Transports/Grand Cruisers,1,10,2,Spares & supplies,Table 1-10 + +Augments and Enhancements,Defensive Countermeasures†,All Ships,1,1,2,"† Once per vessel",Table 1-10 +Augments and Enhancements,Flak Turrets†,All Ships,1,1,1,"† Once per vessel",Table 1-10 +Augments and Enhancements,Field Bracing,All Ships,"1-3",1,2,Variable power by scope,Table 1-10 +Augments and Enhancements,Fire Suppression System†,Transports/Raiders/Frigates,1,1,2,"† Once per vessel",Table 1-10 +Augments and Enhancements,Fire Suppression System†,Light Cruisers/Cruisers,2,2,2,"† Once per vessel",Table 1-10 +Augments and Enhancements,Fire Suppression System†,Grand Cruisers,3,3,2,"† Once per vessel",Table 1-10 +Augments and Enhancements,Lux Net,All Ships,"10 generated",2,2,Generates Power; special rules,Table 1-10 +Augments and Enhancements,Minelayer Bay,Transports/Light Cruisers/Cruisers,1,4,1,Deploys mines,Table 1-10 +Augments and Enhancements,Power Ram†,Light Cruisers/Cruisers,2,0,2,"† Once per vessel",Table 1-10 + +Additional Facilities,Cloudmining Facility†,Transports,3,4,1,"† Once per vessel",Table 1-10 +Additional Facilities,Hydraphuran Jamming System†,All Ships,4,0,1,"† Once per vessel",Table 1-10 +Additional Facilities,Manufactorum†,Light Cruisers/Cruisers,2,1,2,"† Once per vessel",Table 1-10 +Additional Facilities,Medicae Deck†,All Ships,2,1,1,"† Once per vessel",Table 1-10 +Additional Facilities,Melodium†,All Ships,1,1,1,"† Once per vessel",Table 1-10 +Additional Facilities,Plasma Scoop†,Raiders/Frigates,2,3,3,"† Once per vessel",Table 1-10 +Additional Facilities,Plasma Scoop†,Light Cruisers/Cruisers,3,4,3,"† Once per vessel",Table 1-10 +Additional Facilities,Pilot’s Chamber†,All Ships,1,1,1,"† Once per vessel",Table 1-10 +Additional Facilities,Salvage Systems†,Transport/Light Cruiser/Cruiser,5,3,3,"† Once per vessel",Table 1-10 +Additional Facilities,Small Craft Repair Deck†,Light Cruisers/Cruisers,2,2,1,"† Once per vessel",Table 1-10 +Additional Facilities,Spacedock Piers†,Transports (50+ Space)/Grand Cruisers,7,14,4,"† Once per vessel",Table 1-10 + +Cargo Holds and Passenger Compartments,Shadowblind Bays,All Ships,3,4,2,Stealth berths,Table 4-3 +Cargo Holds and Passenger Compartments,Xenos Habitats,All Ships,2,1,1,Accommodations for xenos,Table 4-3 + +Augments and Enhancements,Auxiliary Plasma Banks,Transports/Raiders/Frigates,"8 generated",5,1,Generates Power; special rules,Table 4-3 +Augments and Enhancements,Auxiliary Plasma Banks,Light Cruisers/Cruisers,"10 generated",6,1,Generates Power; special rules,Table 4-3 +Augments and Enhancements,Drop Pod Launch Bays,All Ships,1,3,2,Assault insertion bays,Table 4-3 +Augments and Enhancements,Empyrean Mantle,Transports/Raiders/Frigates,3,0,2,External; stealth vs detection,Table 4-3 +Augments and Enhancements,Empyrean Mantle,Light Cruisers/Cruisers,5,0,2,External; stealth vs detection,Table 4-3 +Augments and Enhancements,Reinforced Prow,Transports/Raiders/Frigates,0,2,1,Strengthened prow,Table 4-3 +Augments and Enhancements,Reinforced Prow,Light Cruisers/Cruisers,0,3,1,Strengthened prow,Table 4-3 + +Additional Facilities,Arboretum,Transports/Raiders/Frigates,2,2,1,Life-supporting gardens,Table 4-3 +Additional Facilities,Arboretum,Light Cruisers/Cruisers,2,3,1,Life-supporting gardens,Table 4-3 +Additional Facilities,Asteroid Mining Facility,All Ships,6,10,3,Onboard mining/refinery,Table 4-3 +Additional Facilities,Astropathic Choir-chambers,All Ships,1,1,1,Astropathic communications,Table 4-3 +Additional Facilities,Broadband Hymn-casters,All Ships,3,0,1,Long-range vox-hymn projectors,Table 4-3 +Category,Component,Appropriate Hull Types,Power,Space,SP,Notes / Effects,Source +Archeotech,Castellan Shield,All Ships,5,1,2,Archeotech void-shield variant.,Table 4-4 +Archeotech,Castellan Shield Array,Cruisers,7,2,2,Counts as multiple shields; archeotech array.,Table 4-4 +Archeotech,Cogitator Interlink†,All Ships,1,1,2,"† Once per vessel; advanced cogitator network.",Table 4-4 +Archeotech,Energistic Conversion Matrix†,All Ships,1,1,1,"† Once per vessel; convert Power to Speed per rules.",Table 4-4 +Archeotech,Gyro-stabilisation Matrix†,All Ships,1,1,1,"† Once per vessel; enhanced targeting stabilisation.",Table 4-4 +Archeotech,Staravar Laser Macrobattery,All Ships,4,4,2,Archeotech laser macrobattery (see weapon statline in Table 4-5),Table 4-4 +Archeotech,Star-flare Lance,Light Cruisers/Cruisers,12,6,3,Archeotech lance (see weapon statline in Table 4-5),Table 4-4 + +Archeotech,Cypra-pattern Class 1 Drive,Transports,"30 Generated",10,2,Archeotech plasma drive.,Table 1-11 +Archeotech,Cypra-pattern Class 2 Drive,Raiders/Frigates,"40 Generated",12,2,Archeotech plasma drive.,Table 1-11 +Archeotech,Jovian-pattern Nova Cannon,Cruisers,6,7,5,Component entry for Nova Cannon mount (see weapon on Table 1-12),Table 1-11 +Archeotech,Plasma Accelerated Torpedo Tubes,All Ships,2,6,2,Archeotech torpedo tubes (see weapon on Table 1-12),Table 1-11 +Archeotech,Warp Antenna,All Ships,1,1,2,Archeotech warp comms augment.,Table 1-11 +Archeotech,Warp Sextant,All Ships,4,1,2,Archeotech warp navigation aid.,Table 1-11 + +Archeotech,Ancient Life Sustainer,Transports/Raiders/Frigates,2,1,2,Archeotech life support.,Table 8-6 +Archeotech,Ancient Life Sustainer,Light Cruisers/Cruisers,2,2,2,Archeotech life support.,Table 8-6 +Archeotech,Modifified Jovian Pattern Class 1 Drive,Transports,"35 Generated",4,3,Archeotech/modified drive.,Table 8-6 +Archeotech,Modifified Lathe Pattern Class 1 Drive,Transports,"40 Generated",8,3,Archeotech/modified drive.,Table 8-6 +Archeotech,Modifified Jovian Pattern Class 2 Drive,Raiders/Frigates,"45 Generated",6,3,Archeotech/modified drive.,Table 8-6 +Archeotech,Modifified Jovian Pattern Class 3 Drive,Light Cruisers,"60 Generated",8,3,Archeotech/modified drive.,Table 8-6 +Archeotech,Modifified Jovian Pattern Class 4 Drive,Cruisers,"75 Generated",10,3,Archeotech/modified drive.,Table 8-6 +Archeotech,Bridge of Antiquity,Transports/Raiders/Frigates,1,1,2,Archeotech bridge.,Table 8-6 +Archeotech,Bridge of Antiquity,Light Cruisers/Cruisers,2,1,2,Archeotech bridge.,Table 8-6 +Archeotech,Auto-stabilised Logis-targeter,All ships,5,0,2,Archeotech targeting augmentation.,Table 8-6 +Archeotech,Teleportarium,All ships,1,1,1,Archeotech teleport array (component entry),Table 8-6 +Xenotech,Ghost Field,All ships,8,4,3,Xenos equivalent of advanced stealth shielding.,Table 8-7 +Xenotech,Shard Cannon Battery,All ships,0,3,2,Xenos macrobattery analogue.,Table 8-7 +Xenotech,Runecaster,All ships,0,1,2,Xenos augury/warding device.,Table 8-7 +Xenotech,Micro Laser Defence Grid,All ships,2,0,2,Xenos PD grid.,Table 8-7 +Xenotech,Gravity Sails,Transports/Raiders/Frigates,3,0,3,Ancient gravity sail rig.,Table 8-7 +Xenotech,Gravity Sails,Light cruisers/Cruisers,5,0,3,Ancient gravity sail rig.,Table 8-7 diff --git a/foundry/starship_weapons.csv b/foundry/starship_weapons.csv index e765bca..7dc8789 100644 --- a/foundry/starship_weapons.csv +++ b/foundry/starship_weapons.csv @@ -1,13 +1,49 @@ component_name,category,mount,scale,strength,damage,crit_rating,range_vu,power,space,sp,availability,special_rules_notes,book_sources,needs_review -Mars-pattern Macrocannon Broadside (Frigate Scale),Macrocannon,Dorsal,Raider/Frigate,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Standard macrobattery for escorts; broadside-equivalent on dorsal for small hulls.,Core,check_table;check_desc;check_presence -Mars-pattern Macrocannon Broadside (Cruiser Scale),Macrocannon,Port/Starboard,Cruiser/Battlecruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Standard cruiser macrobroadside component.,Core,check_table;check_desc;check_presence -Sunsear Laser Battery (Frigate Scale),Laser Battery,Dorsal,Raider/Frigate,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,High accuracy laser macrobattery used by escorts.,Core,check_table;check_desc;check_presence -Sunsear Laser Battery (Cruiser Scale),Laser Battery,Port/Starboard,Cruiser/Battlecruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Cruiser-grade laser macrobattery.,Core,check_table;check_desc;check_presence -Dorsal Lance Battery (Frigate Grade),Lance,Dorsal,Frigate,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Single/double lance mount for escorts.,Core,check_table;check_desc;check_presence -Prow Lance Battery (Light Cruiser),Lance,Prow,Light Cruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Iconic Dauntless prow lance fit.,Core,check_table;check_desc;check_presence -Port/Starboard Lance Battery (Cruiser),Lance,Port/Starboard,Cruiser/Battlecruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Heavy lance broadside for line ships.,Core,check_table;check_desc;check_presence -Voss-pattern Torpedo Tubes,Torpedo Tubes,Prow,Escort/Light Cruiser/Cruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,"As seen on Falchion (prow slot pre-equipped; half ammo, +1 Power).",BFK/Core,check_table -Standard Torpedo Tubes (Cruiser Pattern),Torpedo Tubes,Prow,Cruiser/Battlecruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Generic torpedo tube component for cruiser-sized hulls.,Core,check_table;check_desc;check_presence -Jovian-pattern Launch Bay,Launch Bay,Port/Starboard,Light Cruiser/Cruiser/Battlecruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Carrier component for attack craft (Fury/Starhawk wings).,Core/BFK,check_table;check_presence -Nova Cannon,Nova Cannon,Prow,Cruiser/Battlecruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,Superheavy prow weapon; unique ammo rules.,Core/BFK,check_table;check_desc;check_presence -Plasma Battery (Cruiser Grade),Plasma Battery,Port/Starboard,Cruiser/Battlecruiser,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,UNKN,If present in BFK as a distinct macrobattery type; verify exact name/statline.,BFK,check_table;check_desc;check_presence +Mars-pattern Macrocannon Broadside (Frigate Scale),Macrocannon,Dorsal,Raider/Frigate,3,1d10+2,5,6,4,2,1,UNKN,"Standard macrobattery for escorts; broadside-equivalent on dorsal for small hulls.",Core (Table 8-4), +Mars-pattern Macrocannon Broadside (Cruiser Scale),Macrocannon,Port/Starboard,Cruiser/Battlecruiser,6,1d10+2,5,6,4,5,1,UNKN,Standard cruiser macrobroadside component.,Core (Table 8-4), +Sunsear Laser Battery (Frigate Scale),Laser Battery,Dorsal,Raider/Frigate,4,1d10+2,4,9,6,4,1,UNKN,High accuracy laser macrobattery used by escorts.,Core (Table 8-4), +Sunsear Laser Battery (Cruiser Scale),Laser Battery,Port/Starboard,Cruiser/Battlecruiser,6,1d10+2,4,9,9,6,1,UNKN,Cruiser-grade laser macrobattery.,Supplemental Table 4-2, +Dorsal Lance Battery (Frigate Grade),Lance,Dorsal,Frigate,1,1d10+2,3,5,6,4,2,UNKN,Single/double lance mount for escorts.,Core (Table 8-4), +Prow Lance Battery (Light Cruiser),Lance,Prow,Light Cruiser,1,1d10+4,3,6,9,4,2,UNKN,Iconic Dauntless prow lance fit.,Core (Table 8-4), +Port/Starboard Lance Battery (Cruiser),Lance,Port/Starboard,Cruiser/Battlecruiser,2,1d10+4,3,6,13,6,2,UNKN,Heavy lance broadside for line ships.,Core (Table 8-4), +Voss-pattern Torpedo Tubes,Torpedo Tubes,Prow,Escort/Light Cruiser/Cruiser,2,UNKN,UNKN,UNKN,1,5,1,UNKN,"As seen on Falchion (prow slot pre-equipped; half ammo, +1 Power).",Core (Table 1-9), +Standard Torpedo Tubes (Cruiser Pattern),Torpedo Tubes,Prow,Cruiser/Battlecruiser,6,UNKN,UNKN,UNKN,2,8,2,UNKN,Generic torpedo tube component for cruiser-sized hulls.,Core (Table 1-9), +Jovian-pattern Launch Bay,Launch Bay,Port/Starboard,Light Cruiser/Cruiser/Battlecruiser,2,UNKN,UNKN,UNKN,1,6,2,UNKN,"Carrier component for attack craft (Fury/Starhawk wings).",Core (Table 1-9), +Nova Cannon,Nova Cannon,Prow,Cruiser/Battlecruiser,SPECIAL,2d5+4,SPECIAL,6–40,3,7,3,UNKN,"Superheavy prow weapon; unique ammo rules.",Core (Table 1-9),check_table +Plasma Battery (Cruiser Grade),Plasma Battery,Port/Starboard,Cruiser/Battlecruiser,4,1d10+4,4,5,7,4,2,UNKN,"If present in BFK as a distinct macrobattery type; verify exact name/statline.",Core (Table 8-4), +Bombardment Cannons,Macrocannon,Port/Starboard,Light Cruiser/Cruiser,3,1d10+6,2,4,5,5,3,UNKN,Heavy bombardment macrobattery.,Core (Table 1-9), +Disruption Macrocannons,Macrocannon,Any,All Ships,3,1d10+1,0,5,4,2,2,UNKN,"Mount varies by hull: Dorsal on escorts; Port/Starboard on larger hulls.",Core (Table 1-9), +Disruption Macrocannon Broadside,Macrocannon,Port/Starboard,Light Cruiser/Cruiser,6,1d10+1,0,5,6,5,2,UNKN,Broadside variant.,Core (Table 1-9), +Stygies-pattern Macrocannons,Macrocannon,Any,All Ships,3,1d10+2,5,5,4,3,1,UNKN,"Mount varies by hull: Dorsal on escorts; Port/Starboard on larger hulls.",Core (Table 1-9), +Hecutor-pattern Plasma Battery,Plasma Battery,Port/Starboard,Light Cruiser/Cruiser,3,1d10+2,4,11,8,3,2,UNKN,High-velocity plasma battery.,Core (Table 1-9), +Hecutor-pattern Plasma Broadside,Plasma Battery,Port/Starboard,Battlecruiser/Grand Cruiser,5,1d10+2,4,11,12,5,2,UNKN,Heavy plasma broadside.,Core (Table 1-9), + +Godsbane Lance,Lance,Any,Battlecruiser/Grand Cruiser,1,1d10+2,3,12,9,4,3,UNKN,Single long-range lance.,Core (Table 1-9), +Godsbane Lance Battery,Lance,Port/Starboard,Battlecruiser/Grand Cruiser,2,1d10+2,3,12,13,6,3,UNKN,Broadside lance battery.,Core (Table 1-9), +Las-burner,Lance,Any,All Ships,2,1d5+1,3,3,7,3,2,UNKN,Short-range cutting lance.,Core (Table 1-9), +Mezoa Lance Weapon,Lance,Any,All Ships,1,1d10+5,4,4,9,4,3,UNKN,High-output lance emitter.,Core (Table 1-9), +Mezoa Lance Battery,Lance,Port/Starboard,Light Cruiser/Cruiser,2,1d10+5,4,4,13,6,3,UNKN,Heavy lance battery.,Core (Table 1-9), +Voidsunder Lance Battery,Lance,Port/Starboard,Light Cruiser/Cruiser,1,1d10+4,3,6,15,8,3,UNKN,Shield-piercing lance battery.,Core (Table 1-9), + +Ryza-pattern Nova Cannon,Nova Cannon,Prow,Cruisers,SPECIAL,2d5+5,SPECIAL,6–36,4,7,4,UNKN,"Nova weapon; special blast/aiming rules apply.",Core (Table 1-9),check_table + +Fortis-pattern Torpedo Tubes,Torpedo Tubes,Prow,Light Cruiser/Cruiser,6,UNKN,UNKN,UNKN,2,8,3,UNKN,Standard cruiser/LC torpedo system.,Core (Table 1-9), +Gryphonne-pattern Torpedo Tubes,Torpedo Tubes,Prow,Raider/Frigate/Light Cruiser/Cruiser,4,UNKN,UNKN,UNKN,2,6,1,UNKN,Escort-through-cruiser compatible tubes.,Core (Table 1-9), +Mars-pattern Torpedo Tubes,Torpedo Tubes,Prow,Light Cruiser/Cruiser,6,UNKN,UNKN,UNKN,2,8,2,UNKN,Standard Navy cruiser torpedo fit.,Core (Table 1-9), + +Hold Landing Bay,Launch Bay,Internal,Transports,2,UNKN,UNKN,UNKN,1,0,2,UNKN,"Cargo-hold conversion; Space shown as 0 for '—' per CSV convention.",Core (Table 1-9), +Jovian-pattern Escort Bay,Launch Bay,Port/Starboard,Light Cruiser/Cruiser,1,UNKN,UNKN,UNKN,1,4,1,UNKN,Escort-grade launch bay.,Core (Table 1-9), +Lathe-pattern Landing Bay,Launch Bay,Port/Starboard,Cruiser,2,UNKN,UNKN,UNKN,1,5,2,UNKN,Cruiser-grade launch bay (Lathe),Core (Table 1-9), + +Jovian Missile Battery,Macrocannon,Any,All Ships,5,1d10+1,6,6,3,1,1,UNKN,"Missile-based macrobattery; mount varies by hull.",Supplemental Table 4-2, +Lathe Grav-culverin Broadside,Macrocannon,Port/Starboard,Light Cruiser/Cruiser,6,1d10+3,6,5,5,5,1,UNKN,High-velocity gravitic broadside.,Supplemental Table 4-2, +Mezoa Macrocannons,Macrocannon,Any,All Ships,4,1d10+3,5,5,4,4,1,UNKN,"Mount varies by hull: Dorsal on escorts; Port/Starboard on larger hulls.",Supplemental Table 4-2, +Pyros Melta-cannons,Macrocannon,Any,All Ships,3,1d10+4,4,4,4,3,2,UNKN,Short-range melta macrobattery.,Supplemental Table 4-2, +Sunhammer Lance,Lance,Any,All Ships,1,1d10+3,3,9,9,4,2,UNKN,Long-range lance emitter.,Supplemental Table 4-2, +Sunhammer Lance Battery,Lance,Port/Starboard,All Ships,2,1d10+3,3,9,13,6,2,UNKN,Long-range lance battery.,Supplemental Table 4-2, +Mars-pattern Nova Cannon,Nova Cannon,Prow,Cruisers,SPECIAL,2d5+4,SPECIAL,6–40,3,7,3,UNKN,"Nova weapon; special blast/aiming rules apply.",Core (Table 1-9),check_table +Staravar Laser Macrobattery,Macrocannon,Any,All Ships,4,1d10+2,4,12,4,4,2,Extremely Rare,"Archeotech laser macro; mount depends on hull (Dorsal/Port-Starboard).",Table 4-5, +Star-flare Lance,Lance,Any,Light Cruiser/Cruiser,3,1d10+3,3,7,12,6,3,Extremely Rare,Archeotech lance emitter.,Table 4-5, + +Jovian-pattern Nova Cannon†,Nova Cannon,Prow,Cruisers,SPECIAL,2d5+7,SPECIAL,6–35,5,7,5,Extremely Rare,"† Special rules for Strength/Crit (see page 16).",Table 1-12,check_table +Plasma Accelerated Torpedo Tubes (Archeotech Weapon),Torpedo Tubes,Prow,All Ships,4,UNKN,UNKN,UNKN,1,4,4,Extremely Rare,"Archeotech torpedo mount; torpedo warhead stats handled separately.",Table 1-12, diff --git a/foundry/version.nfo b/foundry/version.nfo index 49d5957..3b04cfb 100644 --- a/foundry/version.nfo +++ b/foundry/version.nfo @@ -1 +1 @@ -0.1 +0.2 diff --git a/help_scripts/generate_manifests.py b/help_scripts/generate_manifests.py new file mode 100644 index 0000000..f1febc3 --- /dev/null +++ b/help_scripts/generate_manifests.py @@ -0,0 +1,118 @@ +# generate_manifests.py +# Fortel Trappa — pełne manifesty ładunkowe (HOLD I/II/III) w ZIP +# Uruchom: python generate_manifests.py -> powstaną dwa pliki ZIP w bieżącym katalogu. + +import random, zipfile, datetime + +random.seed(40000) # deterministyczny „smak” + +WORLDS = ["Ostia","Avachrus","Enoch","Gilead Prime","Nethreus","Ryza","Vostroya","Scintilla","Footfall"] +INN_NOTES = [ + "Zwykły wyrób, zgodny z normą diecezjalną.", + "Do użytku cywilnego; dokumentacja pełna.", + "Na potrzeby konserwacji/edukacji polowej.", + "Brak zasilania, brak funkcji specjalnych.", + "Wersja eksportowa; gwinty zgodne z PL-3.", + "Na morale i porządek; bez cech bojowych.", + "Próbki kontrolne — wynik negatywny na scrapcode.", + "Rekwizyty katechetyczne; brak statusu kultowego.", + "Puste, czyste, opieczętowane.", + "Zabezpieczone audytem Ministorum.", + "Składane; bez ostrych krawędzi w papierach.", + "Zwykła jakość magazynowa; brak niespodzianek.", + "Brak reakcji psionicznej (pomiar: tło).", + "Standard przemysłowy; kompatybilność wysoka.", + "Do ekspozycji; nieotwierane przy inspekcji.", + "Zestaw muzealny; kojąco nudny.", + "Oznaczenie ‘cywilne’; instrukcja w komplecie.", + "Zgodne z przepisem P-77 (ładownie ogólne).", + "Pieczęć diecezjalna aktualna; datowanie bieżące.", + "Bezpieczne dla chóru i czytników vox." +] +INN_NAMES = [ + "Ziarno kultywacyjne ‘Święty Oset’","Mąka ‘Hostia’","Kasza jęczmienna ‘Pielgrzym’","Groch ‘Kocioł’","Fasola ‘Pilgrim’", + "Sól żywnościowa","Olej lampowy","Smary maszynowe ‘Stałość’","Farba kadłubowa ‘Mgła’","Pasy mocujące ‘Ordo Cargo’", + "Złącza heksowe","Zawory nadciśnieniowe","Łożyska baryłkowe ‘Wytrwałość’","Łożyska kulkowe precyzyjne", + "Taśmy stalowe ‘Krzyżowe’","Plandeki światłochłonne ‘Cień’","Pudła ciszy (obudowy wygłuszające)","Lampiony procesyjne", + "Witraże polowe (składane)","Zwoje pergaminowe ‘Sztywna Karta’","Maski nabożne ‘Neutral’","Relikwie podręczne (skrzynki)", + "Szkatuły dewocyjne (stal miękka)","Szkła ochronne ‘Błękit Służbowy’","Barwniki okienne (błękit/purpura)", + "Kity hermetyczne ‘Głuchy Spaw’","Rury segmentowe PL-7","Kable PL-3 ‘Uniwersalne’","Siatki zabezpieczające ‘Drobne Oczko’", + "Zasuwy grodziowe PL-10","Zaczepy stalowe ‘Cierpliwość’","Kątowniki ciężkie ‘Prosto’","Nity kadłubowe ‘Amen’", + "Skrzynki na dźwięk (wygłuszenie)","Szkła różnobarwne (mix)","Amfory gliniane ‘E-Seria’","Szaty ‘Pokora’ (robocze)", + "Kopułki modlitewne (mini)","Płaszcze przeciwdeszczowe ‘Wachta’","Świece czarne ‘Negocjacje’","Świece woskowe ‘Cisza Nocna’", + "Zwoje kabli heksowych (ekranowane)","Sprężyny powrotne","Zawiesia wielokrążkowe","Przekaźniki proste ‘Miedź’", + "Łożyska stożkowe ‘Kierunek’","Szkatuły na krople (apteczne)","Ołtarze polowe (składane)","Figurki pobożne", + "Piety miniaturowe","Popiół posypny (liturgiczny)","Kleje statkowe ‘Głuchy Spaw’","Klemy zasilające ‘Moc’", + "Kołnierze izolacyjne PL-5","Beczki puste ‘Gotowość’","Rolki transportowe ‘Gładkie’","Słód jasny ‘Nocny’", + "Skrzydła drzwiowe ‘Jedność’","Skarbonki parafialne","Księgi wizualne (albumy)","Zegary harmoniczne (niski ton)", + "Baldachimy małe ‘Krem’","Puchar kapłański (rekwizyt)","Księgi zakładkowe","Pasy transmisyjne ‘Ciągłość’", + "Pudła akustyczne (puste)","Rękawice robocze ‘Skóra+’", +] +XENO_REAL = [ + "fragmenty wraithbone Aeldari (martwe, obrobione ‘na szkło’)", + "odpryski spirit-stone (pozbawione ładunku)", + "ukryty vox-scrambler xeno (obudowa cywilna)", + "mikrofiolki po jadu Lhamaean (dokładnie wyparzone)", + "moduł holo-emisyjny Aeldari (matryca wycięta, nieaktywny)", + "kaseta rozliczeń cold trade (podwójne dno)", + "nekrotech — fragment stele ‘bezcielesnej’ (kamień ‘ozdobny’)", + "obudowy tłumiące sygnał (technologia nieidentyfikowana)", + "orb psioniczny — tłumik nastroju (źródło Aeldari)", + "halo-device: element oprawy (niekompletne, bez rdzenia)", + "resztki proscribed tomes (okładki, wkład wymieniony na blanki)", + "zegar-pikietaż dla transakcji cold trade (ukryty znacznik czasu)", + "ampułki po obscura klasy ceremonialnej (umyte; testy czyste)", + "fiolki po ‘ichorze’ drukhari (sterylnie czyste)", + "masker polowy Drukhari — tkanina z włóknem ziggurat (zdezaktywowana)", +] + +def inn_note(): return random.choice(INN_NOTES) +def name(): return random.choice(INN_NAMES) +def world(): return random.choice(WORLDS) +def qty(): return random.randint(1, 8) + +def make_line(idx, suspicious_ratio=0.34): + q = qty(); nm = name(); ow = world(); note = inn_note() + if random.random() < suspicious_ratio: + paren = f"({random.choice(XENO_REAL)})" + else: + paren = f"({random.choice(['zwykły ładunek cywilny','materiały szkoleniowe/rekonstrukcja','zestaw konserwatorski','komplet części serwisowych','puste i czyste','standard magazynowy','produkt spożywczy','wyposażenie pomocnicze','materiały dekoracyjne','narzędzia warsztatowe'])})" + return f"{idx}) {q}× — {nm} — {ow} — [{note}] {paren}", q + +def make_section_text(hold_label, s_idx, s_cnt): + header = f"=== FORTEL TRAPPA — MANIFEST ŁADUNKOWY ===\n{hold_label} / SEKCJA {s_idx} z {s_cnt}\n(Format: [Liczba 1–10] × Standardowy Imperialny Kontener — Nazwa — Pochodzenie — [Uwaga kapitańska] (co to naprawdę jest))\n\n" + total = 0; entries = [] + for i in range(1, 124): + line, q = make_line(i, suspicious_ratio=0.34) + entries.append([line, q]); total += q + if total > 1000: + excess = total - 1000 + for i in range(len(entries)-1, -1, -1): + if excess <= 0: break + q = entries[i][1] + if q > 1: + red = min(q-1, excess); entries[i][1] -= red; excess -= red + prefix, rest = entries[i][0].split("× —", 1) + idx_str = prefix.split(")")[0] + ")" + entries[i][0] = f"{idx_str} {entries[i][1]}× —{rest}" + body = "\n".join(e[0] for e in entries) + footer = f"\n\n— KONIEC: {hold_label} / SEKCJA {s_idx}/{s_cnt} —" + return header + body + footer + +def write_full_zip(path_full, path_hold1): + with zipfile.ZipFile(path_full, "w", compression=zipfile.ZIP_DEFLATED) as z: + for s in range(4, 17): # HOLD I 04–16 + z.writestr(f"HOLD_I_SEKCJA_{s:02d}.txt", make_section_text("HOLD I", s, 16)) + for s in range(1, 16): # HOLD II 01–15 + z.writestr(f"HOLD_II_SEKCJA_{s:02d}.txt", make_section_text("HOLD II", s, 15)) + for s in range(1, 16): # HOLD III 01–15 + z.writestr(f"HOLD_III_SEKCJA_{s:02d}.txt", make_section_text("HOLD III", s, 15)) + z.writestr("README.txt", f"FORTEL TRAPPA — MANIFESTY ({datetime.datetime.utcnow().isoformat()}Z)\nHOLD I: 04–16; HOLD II: 01–15; HOLD III: 01–15\n~1/3 pozycji w ( ) ma real-tożsamość (xeno/kontrabanda), [ ] — noty ‘urzędowo niewinne’.\n") + with zipfile.ZipFile(path_hold1, "w", compression=zipfile.ZIP_DEFLATED) as z: + for s in range(4, 17): + z.writestr(f"HOLD_I_SEKCJA_{s:02d}.txt", make_section_text("HOLD I", s, 16)) + z.writestr("README_HOLD_I.txt", f"HOLD I (04–16) — {datetime.datetime.utcnow().isoformat()}Z\n") + +if __name__ == "__main__": + write_full_zip("FORTEL_HOLD_I_II_III_MANIFESTS.zip", "FORTEL_HOLD_I_SEKCJE_04-16.zip") + print("OK: FORTEL_HOLD_I_II_III_MANIFESTS.zip | FORTEL_HOLD_I_SEKCJE_04-16.zip") diff --git a/help_scripts/generate_manifestsv2.py b/help_scripts/generate_manifestsv2.py new file mode 100644 index 0000000..8fa13c8 --- /dev/null +++ b/help_scripts/generate_manifestsv2.py @@ -0,0 +1,182 @@ +# generate_manifests_v2.py +# Fortel Trappa — manifesty o wysokiej różnorodności (HOLD I/II/III) w dwóch ZIP-ach. +# Uruchom: python generate_manifests_v2.py +# Wynik: FORTEL_MANIFESTS_v2_FULL.zip oraz FORTEL_MANIFESTS_v2_HOLD_I.zip + +import random, zipfile, datetime + +# ===== USTAWIENIA ===== +SEED = None # ustaw np. na liczbę, jeśli chcesz powtarzalny wynik +SUSP_RATIO = 0.34 # ~1/3 pozycji ma „prawdę” w ( ) o xeno/kontrabandzie +QTY_MIN, QTY_MAX = 1, 10 +LINES_PER_SECTION = 123 +CAP_PER_SECTION = 1000 + +if SEED is None: + random.seed() # świeża losowość +else: + random.seed(SEED) + +# ===== PULI I KOMPONENTY NAZW ===== +WORLDS = [ + "Ostia","Avachrus","Enoch","Gilead Prime","Nethreus","Ryza","Vostroya","Scintilla","Footfall", + "Hydraphur","Malfi","Port Wander","Voss Prime","Lucius","Graia","Metalica","Sollex","Sibellus" +] + +FOODS = [ + "Ziarno kultywacyjne","Pszenica","Owies","Jęczmień","Mąka","Kasza jęczmienna","Kasza owsiana", + "Groch","Fasola","Ciecierzyca","Słód jasny","Słód ciemny","Suszone jarzyny","Sól żywnościowa","Olej lampowy" +] +INDUSTRY = [ + "Złącza heksowe","Zawory nadciśnieniowe","Łożyska baryłkowe","Łożyska kulkowe","Łożyska stożkowe", + "Taśmy stalowe","Plandeki światłochłonne","Pudła ciszy (wygłuszenie)","Rury segmentowe PL","Kable PL", + "Siatki zabezpieczające","Zasuwy grodziowe","Zaczepy stalowe","Kątowniki ciężkie","Nity kadłubowe", + "Skrzynki na dźwięk (wygłuszenie)","Filtry rebreatherów","Transpondery nawigacyjne","Przekaźniki proste", + "Zawiesia wielokrążkowe","Sprężyny powrotne","Kołnierze izolacyjne","Kleje statkowe","Klemy zasilające", + "Rolki transportowe","Farba kadłubowa","Żywica naprawcza","Kity hermetyczne","Szkła ochronne", + "Panele grodziowe","Uszczelki retencyjne","Siłowniki serwo (obudowy)" +] +DEVOTION = [ + "Lampiony procesyjne","Witraże polowe (składane)","Zwoje pergaminowe","Maski nabożne", + "Relikwie podręczne (skrzynki)","Szkatuły dewocyjne (stal miękka)","Amfory gliniane", + "Szaty ‘Pokora’ (robocze)","Kopułki modlitewne (mini)","Płaszcze ‘Wachta’", + "Świece czarne ‘Negocjacje’","Świece woskowe ‘Cisza Nocna’","Figurki pobożne", + "Piety miniaturowe","Popiół posypny (liturgiczny)","Ołtarze polowe (składane)", + "Skarbonki parafialne","Księgi wizualne (albumy)","Księgi zakładkowe", + "Ikony ścienne (odlewy)","Reliefy miedziane","Baldachimy ‘Krem’","Puchary kapłańskie (rekwizyt)" +] + +ADJ = [ + "‘Hostia’","‘Pielgrzym’","‘Kocioł’","‘Pilgrim’","‘Stałość’","‘Ordo Cargo’","‘Mgła’","‘Cień’", + "‘Błękit Służbowy’","‘Sztywna Karta’","‘Jedność’","‘Głuchy Spaw’","‘Kierunek’","‘Gotowość’","‘Neutral’", + "‘Mk II’","‘Mk III’","‘Mk IV’","‘Mk V’","‘Plus’","‘Premium’","‘Export’","‘Seria V’","‘Seria IX’","‘Nocny’" +] + +INN_NOTES = [ + "Zwykły wyrób, zgodny z normą diecezjalną.", + "Do użytku cywilnego; dokumentacja pełna.", + "Na potrzeby konserwacji/edukacji polowej.", + "Brak zasilania, brak funkcji specjalnych.", + "Wersja eksportowa; gwinty zgodne z PL-3.", + "Na morale i porządek; bez cech bojowych.", + "Próbki kontrolne — wynik negatywny na scrapcode.", + "Rekwizyty katechetyczne; brak statusu kultowego.", + "Puste, czyste, opieczętowane.", + "Zabezpieczone audytem Ministorum.", + "Składane; bez ostrych krawędzi w papierach.", + "Zwykła jakość magazynowa; brak niespodzianek.", + "Brak reakcji psionicznej (pomiar: tło).", + "Standard przemysłowy; kompatybilność wysoka.", + "Do ekspozycji; nieotwierane przy inspekcji.", + "Zestaw muzealny; kojąco nudny.", + "Oznaczenie ‘cywilne’; instrukcja w komplecie.", + "Zgodne z przepisem P-77 (ładownie ogólne).", + "Pieczęć diecezjalna aktualna; datowanie bieżące.", + "Bezpieczne dla chóru i czytników vox." +] + +HARMLESS = [ + "zwykły ładunek cywilny","materiały szkoleniowe/rekonstrukcja","zestaw konserwatorski", + "komplet części serwisowych","puste i czyste","standard magazynowy","produkt spożywczy", + "wyposażenie pomocnicze","materiały dekoracyjne","narzędzia warsztatowe" +] + +XENO_REAL = [ + "fragmenty wraithbone Aeldari (martwe, obrobione ‘na szkło’)", + "odpryski spirit-stone (pozbawione ładunku)", + "ukryty vox-scrambler xeno (obudowa cywilna)", + "mikrofiolki po jadu Lhamaean (dokładnie wyparzone)", + "moduł holo-emisyjny Aeldari (matryca wycięta, nieaktywny)", + "kaseta rozliczeń cold trade (podwójne dno)", + "nekrotech — fragment stele ‘bezcielesnej’ (kamień ‘ozdobny’)", + "obudowy tłumiące sygnał (technologia nieidentyfikowana)", + "orb psioniczny — tłumik nastroju (źródło Aeldari)", + "halo-device: element oprawy (niekompletne, bez rdzenia)", + "resztki proscribed tomes (okładki, wkład wymieniony na blanki)", + "zegar-pikietaż dla transakcji cold trade (ukryty znacznik czasu)", + "ampułki po obscura klasy ceremonialnej (umyte; testy czyste)", + "fiolki po ‘ichorze’ drukhari (sterylnie czyste)", + "masker polowy Drukhari — tkanina z włóknem ziggurat (zdezaktywowana)", + "matryca soniczna Laer (obudowana ‘pudłem ciszy’)", + "półprzezroczysta łuska necron — fragment dekoracyjny (brak reakcji)", +] + +def compose_name(): + # wybór kategorii z wagami + genre = random.choices(["food","industry","devotion"], [4,5,3])[0] + base = random.choice({"food":FOODS, "industry":INDUSTRY, "devotion":DEVOTION}[genre]) + # modyfikacje wersji/serii + if "PL" in base: + base = base.replace("PL", f"PL-{random.randint(3,12)}") + if "Kable PL" in base and random.random()<0.7: + base = base.replace("Kable PL", f"Kable PL-{random.randint(2,8)}") + flair=[] + if random.random()<0.7: flair.append(random.choice(ADJ)) + if random.random()<0.25: flair.append(f"‘Seria {random.randint(2,12)}’") + return base if not flair else f"{base} " + " ".join(flair) + +def make_line(idx, suspicious_ratio=SUSP_RATIO): + q = random.randint(QTY_MIN, QTY_MAX) + nm = compose_name() + ow = random.choice(WORLDS) + note = random.choice(INN_NOTES) + paren = f"({random.choice(XENO_REAL)})" if random.random()7: + seen.add(name_part) + break + tries+=1 + entries.append([line,q]); total+=q + # przytnij sumę kontenerów do CAP_PER_SECTION + if total>CAP_PER_SECTION: + excess=total-CAP_PER_SECTION + for i in range(len(entries)-1,-1,-1): + if excess<=0: break + q=entries[i][1] + if q>1: + red=min(q-1,excess); entries[i][1]-=red; excess-=red + prefix,rest=entries[i][0].split("× —",1) + idx_str=prefix.split(")")[0]+")" + entries[i][0]=f"{idx_str} {entries[i][1]}× —{rest}" + body="\n".join(e[0] for e in entries) + footer=f"\n\n— KONIEC: {hold_label} / SEKCJA {s_idx}/{s_cnt} —" + return header+body+footer + +def build_zip(full_path, hold1_path): + # FULL: HOLD I 04–16; HOLD II 01–15; HOLD III 01–15 + with zipfile.ZipFile(full_path,"w",compression=zipfile.ZIP_DEFLATED) as z: + for s in range(4,17): # HOLD I + z.writestr(f"HOLD_I_SEKCJA_{s:02d}.txt", make_section_text("HOLD I", s, 16)) + for s in range(1,16): # HOLD II + z.writestr(f"HOLD_II_SEKCJA_{s:02d}.txt", make_section_text("HOLD II", s, 15)) + for s in range(1,16): # HOLD III + z.writestr(f"HOLD_III_SEKCJA_{s:02d}.txt", make_section_text("HOLD III", s, 15)) + z.writestr("README.txt", + f"FORTEL TRAPPA — MANIFESTY v2 ({datetime.datetime.utcnow().isoformat()}Z)\n" + "HOLD I: 04–16; HOLD II: 01–15; HOLD III: 01–15\n" + f"~{int(SUSP_RATIO*100)}% pozycji w ( ) ujawnia real-tożsamość (xeno/kontrabanda); [ ] — noty urzędowo niewinne.\n" + "123 pozycji/sekcję; 1–10 kontenerów/pozycję; suma ≤1000/sekcję.\n") + # HOLD I only + with zipfile.ZipFile(hold1_path,"w",compression=zipfile.ZIP_DEFLATED) as z: + for s in range(4,17): + z.writestr(f"HOLD_I_SEKCJA_{s:02d}.txt", make_section_text("HOLD I", s, 16)) + z.writestr("README_HOLD_I.txt", f"HOLD I (04–16) v2 — {datetime.datetime.utcnow().isoformat()}Z\n") + +if __name__ == "__main__": + build_zip("FORTEL_MANIFESTS_v2_FULL.zip","FORTEL_MANIFESTS_v2_HOLD_I.zip") + print("OK -> FORTEL_MANIFESTS_v2_FULL.zip | FORTEL_MANIFESTS_v2_HOLD_I.zip") diff --git a/investigation-board_v13/investigation-board/assets/bluePin.webp b/investigation-board_v13/investigation-board/assets/bluePin.webp new file mode 100644 index 0000000..68f73e3 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/bluePin.webp differ diff --git a/investigation-board_v13/investigation-board/assets/custom_photoFrame.webp b/investigation-board_v13/investigation-board/assets/custom_photoFrame.webp new file mode 100644 index 0000000..80f553c Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/custom_photoFrame.webp differ diff --git a/investigation-board_v13/investigation-board/assets/fonts/rock_salt.ttf b/investigation-board_v13/investigation-board/assets/fonts/rock_salt.ttf new file mode 100644 index 0000000..0f72fe6 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/fonts/rock_salt.ttf differ diff --git a/investigation-board_v13/investigation-board/assets/futuristic_photoFrame.webp b/investigation-board_v13/investigation-board/assets/futuristic_photoFrame.webp new file mode 100644 index 0000000..3ffed36 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/futuristic_photoFrame.webp differ diff --git a/investigation-board_v13/investigation-board/assets/greenPin.webp b/investigation-board_v13/investigation-board/assets/greenPin.webp new file mode 100644 index 0000000..509f3b6 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/greenPin.webp differ diff --git a/investigation-board_v13/investigation-board/assets/note_index.webp b/investigation-board_v13/investigation-board/assets/note_index.webp new file mode 100644 index 0000000..1528bb1 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/note_index.webp differ diff --git a/investigation-board_v13/investigation-board/assets/note_white.webp b/investigation-board_v13/investigation-board/assets/note_white.webp new file mode 100644 index 0000000..53d23be Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/note_white.webp differ diff --git a/investigation-board_v13/investigation-board/assets/photoFrame.webp b/investigation-board_v13/investigation-board/assets/photoFrame.webp new file mode 100644 index 0000000..997e476 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/photoFrame.webp differ diff --git a/investigation-board_v13/investigation-board/assets/placeholder.webp b/investigation-board_v13/investigation-board/assets/placeholder.webp new file mode 100644 index 0000000..3053e01 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/placeholder.webp differ diff --git a/investigation-board_v13/investigation-board/assets/redPin.webp b/investigation-board_v13/investigation-board/assets/redPin.webp new file mode 100644 index 0000000..5dc4a2f Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/redPin.webp differ diff --git a/investigation-board_v13/investigation-board/assets/yellowPin.webp b/investigation-board_v13/investigation-board/assets/yellowPin.webp new file mode 100644 index 0000000..2dcabd5 Binary files /dev/null and b/investigation-board_v13/investigation-board/assets/yellowPin.webp differ diff --git a/investigation-board_v13/investigation-board/module.json b/investigation-board_v13/investigation-board/module.json new file mode 100644 index 0000000..f9c4c9f --- /dev/null +++ b/investigation-board_v13/investigation-board/module.json @@ -0,0 +1,15 @@ + { + "id": "investigation-board", + "title": "Investigation Board", + "description": "A Foundry VTT module that lets everyone create...photo notes on the scene as collaborative investigation tools.", + "version": "1.3.2", + "authors": [{ "name": "Gus", "url": "", "email": "" }], + "compatibility": { "minimum": "12", "verified": "13" }, + "esmodules": [ + "scripts/settings.js", + "scripts/investigation-board.js" + ], + "styles": ["styles/style.css"], + "packs": [], + "templates": ["templates/drawing-sheet.html"], +} diff --git a/investigation-board_v13/investigation-board/scripts/investigation-board.js b/investigation-board_v13/investigation-board/scripts/investigation-board.js new file mode 100644 index 0000000..c55922c --- /dev/null +++ b/investigation-board_v13/investigation-board/scripts/investigation-board.js @@ -0,0 +1,471 @@ +//investigation-boards.js + +import { registerSettings } from "./settings.js"; + +const MODULE_ID = "investigation-board"; +const BASE_FONT_SIZE = 15; +const PIN_COLORS = ["redPin.webp", "bluePin.webp", "yellowPin.webp", "greenPin.webp"]; + +function getBaseCharacterLimits() { + return game.settings.get(MODULE_ID, "baseCharacterLimits") || { + sticky: 60, + photo: 15, + index: 200, + }; +} + +function getDynamicCharacterLimits(noteType, currentFontSize) { + const baseLimits = getBaseCharacterLimits(); + const scaleFactor = BASE_FONT_SIZE / currentFontSize; + const limits = baseLimits[noteType] || { sticky: 60, photo: 15, index: 200 }; + return { + sticky: Math.round(limits.sticky * scaleFactor), + photo: Math.round(limits.photo * scaleFactor), + index: Math.round(limits.index * scaleFactor), + }; +} + + +class CustomDrawingSheet extends DrawingConfig { + static get defaultOptions() { + return foundry.utils.mergeObject(super.defaultOptions, { + classes: ["custom-drawing-sheet"], + template: "modules/investigation-board/templates/drawing-sheet.html", + width: 400, + height: "auto", + title: "Note Configuration", + }); + } + + getData(options) { + const data = super.getData(options); + data.noteType = this.object.flags[MODULE_ID]?.type || "sticky"; + data.text = this.object.flags[MODULE_ID]?.text || "Default Text"; + data.image = this.object.flags[MODULE_ID]?.image || "modules/investigation-board/assets/placeholder.webp"; + + // Pass along the extra identityName for futuristic photo notes + data.identityName = this.object.flags[MODULE_ID]?.identityName || ""; + + // Include the board mode from settings for conditional display in the template + data.boardMode = game.settings.get(MODULE_ID, "boardMode"); + + data.noteTypes = { + sticky: "Sticky Note", + photo: "Photo Note", + index: "Index Card", + }; + return data; + } + + + async _updateObject(event, formData) { + const updates = { + [`flags.${MODULE_ID}.type`]: formData.noteType, + [`flags.${MODULE_ID}.text`]: formData.text, + [`flags.${MODULE_ID}.image`]: formData.image || "modules/investigation-board/assets/placeholder.webp", + }; + if (formData.identityName !== undefined) { + updates[`flags.${MODULE_ID}.identityName`] = formData.identityName; + } + + + await this.object.update(updates); + const drawing = canvas.drawings.get(this.object.id); + if (drawing) drawing.refresh(); + } + + // Add activateListeners to hook up the file-picker button. + activateListeners(element) { + super.activateListeners(html); + html.find(".file-picker-button").click(ev => { + ev.preventDefault(); + // Open Foundry's FilePicker for images; adjust the "current" directory if needed. + new FilePicker({ + type: "image", + current: "modules/investigation-board/assets/", + callback: path => { + // Update the readonly input with the chosen image path. + const input = element.querySelector("input[name='image']"); if (input) input.value = path; + } + }).browse(); + }); + } + + +} + +class CustomDrawing extends Drawing { + constructor(...args) { + super(...args); + this.bgSprite = null; + this.pinSprite = null; + this.noteText = null; + this.photoImageSprite = null; + this.identityNameText = null; + this.futuristicText = null; + } + + // Ensure sprites are created when the drawing is first rendered. + async draw() { + await super.draw(); + await this._updateSprites(); + return this; + } + + // Ensure sprites update correctly on refresh. + async refresh() { + await super.refresh(); + await this._updateSprites(); + return this; + } + + async _updateSprites() { + const noteData = this.document.flags[MODULE_ID]; + if (!noteData) return; + + const isPhoto = noteData.type === "photo"; + const isIndex = noteData.type === "index"; + const mode = game.settings.get(MODULE_ID, "boardMode"); + + // FUTURISTIC PHOTO NOTE LAYOUT + if (isPhoto && mode === "futuristic") { + const fullWidth = game.settings.get(MODULE_ID, "photoNoteWidth"); + const margin = 10; + const photoImgWidth = fullWidth * 0.4; + const photoImgHeight = photoImgWidth * (4 / 3); + const textAreaX = margin + photoImgWidth + margin; + const fullHeight = photoImgHeight + margin * 2; + + // --- Background Frame --- + if (!this.bgSprite) { + this.bgSprite = new PIXI.Sprite(); + this.addChildAt(this.bgSprite, 0); + } + try { + // Always use the fixed photo frame image. + this.bgSprite.texture = PIXI.Texture.from("modules/investigation-board/assets/photoFrame.webp"); + } catch (err) { + console.error("Failed to load photo frame texture", err); + this.bgSprite.texture = PIXI.Texture.EMPTY; + } + this.bgSprite.width = fullWidth; + this.bgSprite.height = fullHeight; + + // --- Foreground (User-Assigned) Photo --- + if (!this.photoImageSprite) { + this.photoImageSprite = new PIXI.Sprite(); + this.addChild(this.photoImageSprite); + } + try { + // Use a fallback if no image is assigned. + const imagePath = noteData.image || "modules/investigation-board/assets/placeholder.webp"; + this.photoImageSprite.texture = PIXI.Texture.from(imagePath); + } catch (err) { + console.error(`Failed to load user photo: ${noteData.image}`, err); + this.photoImageSprite.texture = PIXI.Texture.EMPTY; + } + // Position the user photo inside the frame. + this.photoImageSprite.width = fullWidth * 0.9; + this.photoImageSprite.height = fullHeight * 0.9; + this.photoImageSprite.position.set(fullWidth * 0.05, fullHeight * 0.05); + + // --- Identity Name and Additional Text (Futuristic) --- + const font = game.settings.get(MODULE_ID, "font"); + const baseFontSize = game.settings.get(MODULE_ID, "baseFontSize"); + const fontSize = (fullWidth / 200) * baseFontSize; + const textStyle = new PIXI.TextStyle({ + fontFamily: font, + fontSize: fontSize, + fill: "#000000", + wordWrap: true, + wordWrapWidth: fullWidth - textAreaX - margin, + align: "left", + }); + if (!this.identityNameText) { + this.identityNameText = new PIXI.Text("", textStyle); + this.addChild(this.identityNameText); + } + this.identityNameText.text = noteData.identityName || "Name"; + this.identityNameText.style = textStyle; + this.identityNameText.position.set(textAreaX, margin); + + if (!this.futuristicText) { + this.futuristicText = new PIXI.Text("", textStyle); + this.addChild(this.futuristicText); + } + this.futuristicText.text = noteData.text || ""; + this.futuristicText.style = textStyle; + this.futuristicText.position.set(textAreaX, margin + this.identityNameText.height + 5); + + // Remove default note text if present. + if (this.noteText) { + this.removeChild(this.noteText); + this.noteText.destroy(); + this.noteText = null; + } + + // --- Pin Handling (Futuristic) --- + const pinSetting = game.settings.get(MODULE_ID, "pinColor"); + if (pinSetting === "none") { + if (this.pinSprite) { + this.removeChild(this.pinSprite); + this.pinSprite.destroy(); + this.pinSprite = null; + } + } else { + if (!this.pinSprite) { + this.pinSprite = new PIXI.Sprite(); + this.addChild(this.pinSprite); + } + let pinColor = noteData.pinColor; + if (!pinColor) { + pinColor = (pinSetting === "random") + ? PIN_COLORS[Math.floor(Math.random() * PIN_COLORS.length)] + : `${pinSetting}Pin.webp`; + if (this.document.isOwner) { + await this.document.update({ [`flags.${MODULE_ID}.pinColor`]: pinColor }); + } + } + const pinImage = `modules/investigation-board/assets/${pinColor}`; + try { + this.pinSprite.texture = PIXI.Texture.from(pinImage); + } catch (err) { + console.error(`Failed to load pin texture: ${pinImage}`, err); + this.pinSprite.texture = PIXI.Texture.EMPTY; + } + this.pinSprite.width = 40; + this.pinSprite.height = 40; + this.pinSprite.position.set(fullWidth / 2 - 20, 3); + } + return; // End early for futuristic photo notes. + } + + // STANDARD LAYOUT (Modern photo notes, sticky, index, etc.) + const width = isPhoto + ? game.settings.get(MODULE_ID, "photoNoteWidth") + : isIndex + ? game.settings.get(MODULE_ID, "indexNoteWidth") || 600 + : game.settings.get(MODULE_ID, "stickyNoteWidth"); + + const height = isPhoto + ? Math.round(width / (225 / 290)) + : isIndex + ? Math.round(width / (600 / 400)) + : width; + + // Background Image based on board mode. + const getBackgroundImage = (noteType, mode) => { + if (mode === "futuristic") { + if (noteType === "photo") return "modules/investigation-board/assets/futuristic_photoFrame.webp"; + if (noteType === "index") return "modules/investigation-board/assets/futuristic_note_index.webp"; + return "modules/investigation-board/assets/futuristic_note_white.webp"; + } else if (mode === "custom") { + if (noteType === "photo") return "modules/investigation-board/assets/custom_photoFrame.webp"; + if (noteType === "index") return "modules/investigation-board/assets/custom_note_index.webp"; + return "modules/investigation-board/assets/custom_note_white.webp"; + } + // Default "modern" mode: + if (noteType === "photo") return "modules/investigation-board/assets/photoFrame.webp"; + if (noteType === "index") return "modules/investigation-board/assets/note_index.webp"; + return "modules/investigation-board/assets/note_white.webp"; + }; + const bgImage = getBackgroundImage(noteData.type, mode); + + if (!this.bgSprite) { + this.bgSprite = new PIXI.Sprite(); + this.addChild(this.bgSprite); + } + try { + this.bgSprite.texture = PIXI.Texture.from(bgImage); + } catch (err) { + console.error(`Failed to load background texture: ${bgImage}`, err); + this.bgSprite.texture = PIXI.Texture.EMPTY; + } + this.bgSprite.width = width; + this.bgSprite.height = height; + + // --- Foreground (User-Assigned) Photo for Modern Mode --- + // (This is the code missing from your current version.) + if (isPhoto) { + const fgImage = noteData.image || "modules/investigation-board/assets/placeholder.webp"; + if (!this.photoImageSprite) { + this.photoImageSprite = new PIXI.Sprite(); + this.addChild(this.photoImageSprite); + } + try { + this.photoImageSprite.texture = PIXI.Texture.from(fgImage); + } catch (err) { + console.error(`Failed to load foreground texture: ${fgImage}`, err); + this.photoImageSprite.texture = PIXI.Texture.EMPTY; + } + // Use offsets similar to your old code. + const widthOffset = width * 0.13333; + const heightOffset = height * 0.30246; + this.photoImageSprite.width = width - widthOffset; + this.photoImageSprite.height = height - heightOffset; + this.photoImageSprite.position.set(widthOffset / 2, heightOffset / 2); + this.photoImageSprite.visible = true; + } else if (this.photoImageSprite) { + this.photoImageSprite.visible = false; + } + + // --- Pin Handling (Standard) --- + { + const pinSetting = game.settings.get(MODULE_ID, "pinColor"); + if (pinSetting === "none") { + if (this.pinSprite) { + this.removeChild(this.pinSprite); + this.pinSprite.destroy(); + this.pinSprite = null; + } + } else { + if (!this.pinSprite) { + this.pinSprite = new PIXI.Sprite(); + this.addChild(this.pinSprite); + } + let pinColor = noteData.pinColor; + if (!pinColor) { + pinColor = (pinSetting === "random") + ? PIN_COLORS[Math.floor(Math.random() * PIN_COLORS.length)] + : `${pinSetting}Pin.webp`; + if (this.document.isOwner) { + await this.document.update({ [`flags.${MODULE_ID}.pinColor`]: pinColor }); + } + } + const pinImage = `modules/investigation-board/assets/${pinColor}`; + try { + this.pinSprite.texture = PIXI.Texture.from(pinImage); + } catch (err) { + console.error(`Failed to load pin texture: ${pinImage}`, err); + this.pinSprite.texture = PIXI.Texture.EMPTY; + } + this.pinSprite.width = 40; + this.pinSprite.height = 40; + this.pinSprite.position.set(width / 2 - 20, 3); + } + } + + // Default text layout for non-futuristic notes. + const font = game.settings.get(MODULE_ID, "font"); + const baseFontSize = game.settings.get(MODULE_ID, "baseFontSize"); + const fontSize = (width / 200) * baseFontSize; + const textStyle = new PIXI.TextStyle({ + fontFamily: font, + fontSize: fontSize, + fill: "#000000", + wordWrap: true, + wordWrapWidth: width - 15, + align: "center", + }); + const truncatedText = this._truncateText(noteData.text || "Default Text", font, noteData.type, fontSize); + if (!this.noteText) { + this.noteText = new PIXI.Text(truncatedText, textStyle); + this.noteText.anchor.set(0.5); + this.addChild(this.noteText); + } else { + this.noteText.style = textStyle; + this.noteText.text = truncatedText; + } + this.noteText.position.set(width / 2, isPhoto ? height - 25 : height / 2); + } + + + + + _truncateText(text, font, noteType, currentFontSize) { + const limits = getDynamicCharacterLimits(font, currentFontSize); + const charLimit = limits[noteType] || 100; + return text.length <= charLimit ? text : text.slice(0, charLimit).trim() + "..."; + } +} + + + +async function createNote(noteType) { + const scene = canvas.scene; + if (!scene) { + console.error("Cannot create note: No active scene."); + return; + } + + // Retrieve width settings (or use defaults) + const stickyW = game.settings.get(MODULE_ID, "stickyNoteWidth") || 200; + const photoW = game.settings.get(MODULE_ID, "photoNoteWidth") || 225; + const indexW = game.settings.get(MODULE_ID, "indexNoteWidth") || 600; + + const width = noteType === "photo" ? photoW + : noteType === "index" ? indexW + : stickyW; + const height = noteType === "photo" ? Math.round(photoW / (225 / 290)) + : noteType === "index" ? Math.round(indexW / (600 / 400)) + : stickyW; + + const dims = canvas.dimensions; + const x = dims.width / 2; + const y = dims.height / 2; + + // Get default text from settings (fallback if missing) + const defaultText = game.settings.get(MODULE_ID, `${noteType}NoteDefaultText`) || "Notes"; + + // Determine board mode and include identityName if note is a futuristic photo note + const boardMode = game.settings.get(MODULE_ID, "boardMode"); + const extraFlags = {}; + if (noteType === "photo" && boardMode === "futuristic") { + extraFlags.identityName = ""; + } + + await canvas.scene.createEmbeddedDocuments("Drawing", [ + { + type: "r", + author: game.user.id, + x, + y, + shape: { width, height }, + fillColor: "#ffffff", + fillAlpha: 1, + strokeColor: "transparent", + strokeAlpha: 0, + locked: false, + flags: { + [MODULE_ID]: { + type: noteType, + text: defaultText, + ...extraFlags + }, + }, + "flags.core.sheetClass": "investigation-board.CustomDrawingSheet", + "ownership": { default: 3 }, + }, + ]); + + canvas.drawings.activate(); +} + + + +Hooks.on("getSceneControlButtons", (controls) => { + const journalControls = controls.find((c) => c.name === "notes"); + if (!journalControls) return; + + journalControls.tools.push( + { name: "createStickyNote", title: "Create Sticky Note", icon: "fas fa-sticky-note", onClick: () => createNote("sticky"), button: true }, + { name: "createPhotoNote", title: "Create Photo Note", icon: "fa-solid fa-camera-polaroid", onClick: () => createNote("photo"), button: true }, + { name: "createIndexCard", title: "Create Index Card", icon: "fa-regular fa-subtitles", onClick: () => createNote("index"), button: true } + ); +}); + +Hooks.once("init", () => { + registerSettings(); + CONFIG.Drawing.objectClass = CustomDrawing; + + DocumentSheetConfig.registerSheet(DrawingDocument, "investigation-board", CustomDrawingSheet, { + label: "Note Drawing Sheet", + types: ["base"], + makeDefault: false, + }); + + console.log("Investigation Board module initialized."); +}); + + +export { CustomDrawing, CustomDrawingSheet }; diff --git a/investigation-board_v13/investigation-board/scripts/settings.js b/investigation-board_v13/investigation-board/scripts/settings.js new file mode 100644 index 0000000..f86bc65 --- /dev/null +++ b/investigation-board_v13/investigation-board/scripts/settings.js @@ -0,0 +1,167 @@ +export const MODULE_ID = "investigation-board"; + +export const registerSettings = function() { + + const refreshAllDrawings = () => { + if (canvas.drawings) { + canvas.drawings.placeables.forEach(drawing => { + if (drawing.document.flags[MODULE_ID]) { + drawing.refresh(); + } + }); + } + }; + + // Update the pinColor setting to include a "No Pins" option. + game.settings.register(MODULE_ID, "pinColor", { + name: "Pin Color", + hint: "Choose the color of the pin for notes. Selecting 'Random' will randomly assign a pin color. Select 'No Pins' to disable pin display.", + scope: "world", + config: true, + type: String, + choices: { + random: "Random", + red: "Red", + blue: "Blue", + yellow: "Yellow", + green: "Green", + none: "No Pins" + }, + default: "random", + onChange: () => { + if (canvas.drawings) { + canvas.drawings.placeables.forEach(drawing => drawing.refresh()); + } + } + }); + + // Register a new setting for board mode selection. + game.settings.register(MODULE_ID, "boardMode", { + name: "Board Mode", + hint: "Select the board mode to change the styling of notes.", + scope: "world", + config: true, + type: String, + choices: { + modern: "Modern", + futuristic: "Futuristic", + custom: "Custom" + }, + default: "modern", + onChange: () => refreshAllDrawings() + }); + + + // Register existing settings + game.settings.register(MODULE_ID, "stickyNoteWidth", { + name: "Sticky Note Width", + hint: "The width (in pixels) for all newly created sticky notes (default: 200).", + scope: "world", + config: true, + type: Number, + default: 200, + onChange: () => refreshAllDrawings() + }); + + game.settings.register(MODULE_ID, "photoNoteWidth", { + name: "Photo Note Width", + hint: "The width (in pixels) for all newly created photo notes (default: 225).", + scope: "world", + config: true, + type: Number, + default: 225, + onChange: () => refreshAllDrawings() + }); + + game.settings.register(MODULE_ID, "indexNoteWidth", { + name: "Index Note Width", + hint: "The width (in pixels) for all newly created index cards (default: 600).", + scope: "world", + config: true, + type: Number, + default: 600, + onChange: () => refreshAllDrawings() + }); + + game.settings.register(MODULE_ID, "baseFontSize", { + name: "Base Font Size", + hint: "The font size (in pixels) for text when the note width is at its default size (default: 16).", + scope: "world", + config: true, + type: Number, + default: 16, + onChange: () => refreshAllDrawings() + }); + + game.settings.register(MODULE_ID, "font", { + name: "Font", + hint: "Choose the font to be used in notes.", + scope: "world", + config: true, + type: String, + choices: { + "Rock Salt": "Rock Salt", + "Courier New": "Courier New", + "Times New Roman": "Times New Roman", + "Signika": "Signika", + "Arial": "Arial" + }, + default: "Rock Salt", + onChange: () => refreshAllDrawings() + }); + + game.settings.register(MODULE_ID, "characterNameKey", { + name: "Character Name Key", + hint: "Specify the key path to retrieve the name (e.g., 'prototypeToken.name' or 'system.alias' for Blades in the Dark). If empty, defaults to 'name'.", + scope: "world", + config: true, + default: "prototypeToken.name", + type: String, + }); + + game.settings.register(MODULE_ID, "stickyNoteDefaultText", { + name: "Default Sticky Note Text", + hint: "The default text to use for new sticky notes.", + scope: "world", + config: true, + type: String, + default: "Clue", + onChange: () => refreshAllDrawings() + }); + + game.settings.register(MODULE_ID, "photoNoteDefaultText", { + name: "Default Photo Note Text", + hint: "The default text to use for new photo notes.", + scope: "world", + config: true, + type: String, + default: "Suspect/Place", + onChange: () => refreshAllDrawings() + }); + + game.settings.register(MODULE_ID, "indexNoteDefaultText", { + name: "Default Index Note Text", + hint: "The default text to use for new index notes.", + scope: "world", + config: true, + type: String, + default: "Notes", + onChange: () => refreshAllDrawings() + }); + + // Register base font size and character limits + game.settings.register(MODULE_ID, "baseCharacterLimits", { + name: "Base Character Limits", + hint: "The base character limits for each font and note type. Edit this JSON to customize.", + scope: "world", + config: false, // Hidden from the settings UI + type: Object, + default: { + "Rock Salt": { sticky: 90, photo: 20, index: 210 }, + "Courier New": { sticky: 250, photo: 30, index: 580 }, + "Times New Roman": { sticky: 200, photo: 30, index: 800 }, + "Signika": { sticky: 200, photo: 30, index: 650 }, + "Arial": { sticky: 200, photo: 30, index: 650 } + } + }); +}; diff --git a/investigation-board_v13/investigation-board/styles/style.css b/investigation-board_v13/investigation-board/styles/style.css new file mode 100644 index 0000000..b72e4d6 --- /dev/null +++ b/investigation-board_v13/investigation-board/styles/style.css @@ -0,0 +1,134 @@ +@font-face { + font-family: 'Rock Salt'; + src: url('../assets/fonts/rock_salt.ttf') format('ttf'); + font-weight: normal; + font-style: normal; +} + +.custom-drawing-text { + font-family: 'Rock Salt', Arial, sans-serif; + color: #000000; /* Black text */ +} + +.sticky-note { + background-image: url("modules/investigation-board/assets/note_white.webp"); + width: 200px; + height: 200px; +} + +.photo-note { + background-image: url("modules/investigation-board/assets/photoFrame.webp"); + width: 250px; + height: 310px; +} + +/* EDIT FORM */ +/* +.note-config-wrapper { + position: relative; + width: 400px; + min-height: 310px; + background-repeat: no-repeat; + background-position: center; + background-size: cover; + box-shadow: 0 0 10px rgba(0,0,0,0.5); + border: none; +} + +.note-config-wrapper.sticky-mode { + background-image: url("../assets/note_white.webp"); + background-repeat: no-repeat; + background-size: contain; +} + +.note-config-wrapper.photo-mode { + background-image: url("../assets/photoFrame.webp"); + background-repeat: no-repeat; + background-size: cover; +} + +.note-config-wrapper .window-content { + background: none; + border: none; +} + +.note-config-wrapper form { + position: relative; + width: 100%; + height: 100%; + overflow: hidden; +} + +.note-type-group { + position: absolute; + top: 10px; + left: 10px; + width: 150px; +} + +.note-text-group { + position: absolute; + left: 20px; + width: 360px; +} + +.note-config-wrapper.sticky-mode .note-text-group { + top: 100px; +} + +.note-config-wrapper.photo-mode .note-text-group { + top: 220px; +} + +.note-image-group { + position: absolute; + top: 50px; + right: 20px; + width: 200px; +} + +.note-save-group { + position: absolute; + bottom: 10px; + right: 10px; +} + +.note-config-wrapper input, +.note-config-wrapper textarea, +.note-config-wrapper select { + background: rgba(255, 255, 255, 0.9); + border: 1px solid #ccc; + border-radius: 2px; + padding: 4px; + font-size: 14px; + font-family: Arial, sans-serif; +} + +.note-config-wrapper label { + font-size: 12px; + font-weight: bold; + color: #333; + margin-bottom: 3px; + display: inline-block; +} + +.file-picker-button { + margin-left: 5px; + background: #eee; + border: 1px solid #ccc; + padding: 2px 4px; + cursor: pointer; +} + +.save-button { + background: #3f51b5; + color: #fff; + border: none; + padding: 5px 10px; + cursor: pointer; + font-size: 14px; + border-radius: 2px; +} +.save-button i { + margin-right: 5px; +} */ diff --git a/investigation-board_v13/investigation-board/templates/drawing-sheet.html b/investigation-board_v13/investigation-board/templates/drawing-sheet.html new file mode 100644 index 0000000..9073631 --- /dev/null +++ b/investigation-board_v13/investigation-board/templates/drawing-sheet.html @@ -0,0 +1,37 @@ +
+
+ + +
+ + {{!-- Only show the Image Path field for photo notes --}} + {{#if (eq noteType "photo")}} +
+ +
+ + +
+
+ {{else}} + + {{/if}} + + {{!-- If boardMode is futuristic and noteType is photo, show the extra Identity Name field --}} + {{#if (eq noteType "photo")}} + {{#if (eq boardMode "futuristic")}} +
+ + +
+ {{/if}} + {{/if}} + +
+ +
+
diff --git a/wg-voidships-builder/sample_universe.json b/wg-voidships-builder/sample_universe.json deleted file mode 100644 index 8652255..0000000 --- a/wg-voidships-builder/sample_universe.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "name": "Universe-class Mass Conveyor", - "type": "vehicle", - "img": "icons/svg/ship.svg", - "folder": null, - "ownership": { "default": 0 }, - "flags": { - "core": { "sheetClass": "wg-voidships-builder.VoidshipSheet" }, - "wg-voidships-builder": { - "isVoidship": true, - "hull": { - "class": "Universe-class Mass Conveyor", - "size": "Cruiser", - "slots": { "prow": 0, "dorsal": 0, "port": 1, "starboard": 1, "keel": 0 }, - "requirements": [ - { "tag": "cargo-bay", "min": 4, "label": "Cargo Holds" }, - { "tag": "aux-plasma", "min": 1, "label": "Auxiliary Plasma Generator" } - ] - }, - "base": { "armour": 12, "integrity": 36, "manoeuvre": -1, "detection": 0, "speed": 8, "space": 60, "power": 55 }, - "crew": { "command": 1, "pilot": 1, "sensors": 1, "engineering": 2, "gunnery": 2, "security": 2, "flightDeck": 0, "passengers": 0 }, - "rtRaw": { - "mnvr": -1, - "notes": "Super-massive hauler for colony loads and crusade materiel; logistics linchpin.", - "combat": { "size": "colossal", "speed": 8, "wounds": { "value": 0, "bonus": 0, "max": 36 }, "resilience": { "bonus": 0, "total": 12 }, "defence": { "bonus": 2 }, "fly": 0 }, - "rt": { - "speed": 8, - "manoeuvrability": -10, - "detection": 0, - "armour": 12, - "hullIntegrity": 36, - "space": 60, - "power": 55, - "weapons": { "prow": 0, "dorsal": 0, "port": 1, "starboard": 1, "keel": 0 }, - "turret": 1, - "source": "Battlefleet Koronus p.31 — to verify" - }, - "source": "Battlefleet Koronus, p. 31" - } - } - }, - "system": {}, - "effects": [], - "items": [ - { - "_id": "ABCD1234EFGH5678", - "name": "Command Bridge (Civilian)", - "type": "gear", - "img": "icons/svg/book.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "bridge", "subtype": "civilian", "tags": ["bridge"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": 2, "power": 2 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 1, "detection": 1, "speed": 0, "space": 0, "power": 0 } } } - }, - { - "_id": "IJKL9012MNOP3456", - "name": "Gellar Field Projector", - "type": "gear", - "img": "icons/svg/temple.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "gellar", "subtype": "standard", "tags": ["gellar"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": 2, "power": 3 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 0, "speed": 0, "space": 0, "power": 0 } } } - }, - { - "_id": "QRST7890UVWX1234", - "name": "Miloslav Warp Drive (Civilian)", - "type": "gear", - "img": "icons/svg/wing.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "drive", "subtype": "warp", "tags": ["drive", "warp"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": 3, "power": 6 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 0, "speed": 0, "space": 0, "power": 0 } } } - }, - { - "_id": "YZAB5678CDEF9012", - "name": "Augur Array (Civilian)", - "type": "gear", - "img": "icons/svg/mystery-man.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "sensor", "subtype": "augur", "tags": ["sensor", "auspex"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Aetherics", "use": { "space": 1, "power": 2 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 2, "speed": 0, "space": 0, "power": 0 } } } - }, - { - "_id": "GHIJ3456KLMN7890", - "name": "Reinforced Hull Plating", - "type": "gear", - "img": "icons/svg/armor.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "armour", "subtype": "plating", "tags": ["hull", "armour"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": 1, "power": 0 }, "mods": { "armour": 2, "integrity": 4, "manoeuvre": -1, "detection": 0, "speed": 0, "space": 0, "power": 0 } } } - }, - { - "_id": "PQRS9012TUVW3456", - "name": "Cargo Hold & Lighter Bay", - "type": "gear", - "img": "icons/svg/crate.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "cargo-bay", "subtype": "standard", "tags": ["cargo-bay", "hold"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": -5, "power": 1 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 0, "speed": 0, "space": -5, "power": 0 } } } - }, - { - "_id": "WXYZ1234ABCD5678", - "name": "Cargo Hold & Lighter Bay", - "type": "gear", - "img": "icons/svg/crate.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "cargo-bay", "subtype": "standard", "tags": ["cargo-bay", "hold"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": -5, "power": 1 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 0, "speed": 0, "space": -5, "power": 0 } } } - }, - { - "_id": "EFGH9012IJKL3456", - "name": "Cargo Hold & Lighter Bay", - "type": "gear", - "img": "icons/svg/crate.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "cargo-bay", "subtype": "standard", "tags": ["cargo-bay", "hold"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": -5, "power": 1 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 0, "speed": 0, "space": -5, "power": 0 } } } - }, - { - "_id": "MNOP5678QRST9012", - "name": "Cargo Hold & Lighter Bay", - "type": "gear", - "img": "icons/svg/crate.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "cargo-bay", "subtype": "standard", "tags": ["cargo-bay", "hold"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": -5, "power": 1 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 0, "speed": 0, "space": -5, "power": 0 } } } - }, - { - "_id": "UVWX3456YZAB7890", - "name": "Auxiliary Plasma Generator", - "type": "gear", - "img": "icons/svg/energy.svg", - "flags": { "wg-voidships-builder": { "component": { "kind": "plasma", "subtype": "aux-generator", "tags": ["aux-plasma"] }, "voidOnly": true } }, - "system": { "voidship": { "slot": "Internal", "use": { "space": 2, "power": -6 }, "mods": { "armour": 0, "integrity": 0, "manoeuvre": 0, "detection": 0, "speed": 0, "space": 0, "power": 6 } } } - } - ], - "prototypeToken": { "name": "Universe-class Mass Conveyor", "actorLink": true, "texture": { "src": "icons/svg/ship.svg" }, "disposition": 1, "displayName": 0 }, - "_id": "6836e238cce8d9fc", - "systemId": "wrath-and-glory", - "systemVersion": "7.1.1" -}