Skip to content

Commit

Permalink
feat: Support for Cepheus Universal (#1632)
Browse files Browse the repository at this point in the history
This release adds support for the Cepheus Universal rules published by Zozer Games (used with permission).  There is a basic compendium (personal weapons, armor and skills) included with this release.  More specific information can be found on the wiki here: https://github.com/xdy/twodsix-foundryvtt/wiki/System-Configuration-Guide#cepheus-universal

Warning: Please test this release before using in live play - even if you don't use the Cepheus Universal ruleset.  There has been multiple changes in the code to accommodate these new rules and there may be some bugs still.
  • Loading branch information
marvin9257 authored Oct 16, 2024
1 parent 38ad063 commit 143df93
Show file tree
Hide file tree
Showing 90 changed files with 864 additions and 370 deletions.
7 changes: 4 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 3 additions & 4 deletions src/migrations/2023_01_20_09_48_00_refactor_conditions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-nocheck This turns off *all* typechecking, make sure to remove this once foundry-vtt-types are updated to cover v10.

import { TWODSIX } from "../module/config";
import { applyToAllActors } from "../module/utils/migration-utils";
import { effectType } from "../module/utils/showStatusIcons";

async function refactorConditions (actor: TwodsixActor): Promise<void> {

const encumberedEffect = actor.effects.find(eff => [game.i18n.localize(effectType.encumbered), "Encumbered"].includes(eff.name));
const encumberedEffect = actor.effects.find(eff => [game.i18n.localize(TWODSIX.effectType.encumbered), "Encumbered"].includes(eff.name));
if (encumberedEffect) {
const changeData = { key: "system.conditions.encumberedEffect", mode: CONST.ACTIVE_EFFECT_MODES.ADD, value: encumberedEffect.changes[0].value };
await actor.updateEmbeddedDocuments('ActiveEffect', [{ _id: encumberedEffect.id, changes: [changeData] }]);
}

const woundedEffect = actor.effects.find(eff => [game.i18n.localize(effectType.wounded), "Wounded"].includes(eff.name));
const woundedEffect = actor.effects.find(eff => [game.i18n.localize(TWODSIX.effectType.wounded), "Wounded"].includes(eff.name));
if (woundedEffect) {
const changeData = { key: "system.conditions.woundedEffect", mode: CONST.ACTIVE_EFFECT_MODES.ADD, value: woundedEffect.changes[0].value };
await actor.updateEmbeddedDocuments('ActiveEffect', [{ _id: woundedEffect.id, changes: [changeData] }]);
Expand Down
142 changes: 134 additions & 8 deletions src/module/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Namespace TWODSIX Configuration Values


const CHARACTERISTICS = Object.freeze({
"strength": "STR",
"dexterity": "DEX",
Expand All @@ -24,7 +23,8 @@ const CHARACTERISTICS = Object.freeze({
const DIFFICULTY_VARIANTS = Object.freeze({
"CE": "CE",
"CEL": "CEL",
"AC": "AC"
"AC": "AC",
"CU": "CU"
});

/**
Expand All @@ -35,7 +35,8 @@ const DIFFICULTY_VARIANTS = Object.freeze({
const AUTOFIRE_VARIANTS = Object.freeze({
"CE": "CE",
"CEL": "CEL",
"CT": "CT"
"CT": "CT",
"CU": "CU"
});

//TODO VARIANTS and RULESETS should really be combined/refactored.
Expand Down Expand Up @@ -634,6 +635,58 @@ const RULESETS = Object.freeze({
chainBonus: "0, 0, 0, 0, 0, 0"
}
},
CU: {
key: "CU",
name: "Cepheus Universal",
settings: {
initiativeFormula: "2d6 + min( 1, max( @skills.Tactics, 0)) + min( 1, max( @skills.Recon, 0))",
difficultyListUsed: "CU",
difficultiesAsTargetNumber: false,
autofireRulesUsed: "CU",
modifierForZeroCharacteristic: -2,
termForAdvantage: "advantage",
termForDisadvantage: "disadvantage",
absoluteBonusValueForEachTimeIncrement: 1,
criticalNaturalAffectsEffect: false,
absoluteCriticalEffectValue: 99,
ShowLawLevel: false,
rangeModifierType: "CU_Bands",
ShowWeaponType: true,
ShowDamageType: true,
ShowRateOfFire: true,
ShowRecoil: false,
ShowDoubleTap: false,
showLifebloodStamina: false,
lifebloodInsteadOfCharacteristics: false,
minorWoundsRollModifier: 0,
seriousWoundsRollModifier: 0,
mortgagePayment: 240,
massProductionDiscount: "0.10",
maxEncumbrance: "6 * @characteristics.strength.current",
defaultMovement: 6,
defaultMovementUnits: "m",
addEffectForShipDamage: false,
unarmedDamage: "1d6 + min( 1, max( -1, @characteristics.strength.mod))",
showTimeframe: true,
showHullAndArmor: "armorHullStruc",
showSpells: false,
useNationality: false,
animalsUseHits: false,
robotsUseHits: false,
animalsUseLocations: false,
displayReactionMorale: true,
showComponentRating: true,
showComponentDM: true,
encumbranceFraction: "0.3334",
encumbranceModifier: -1,
useDegreesOfSuccess: 'CE',
targetDMList: "Aiming +1, Aiming w/scope +2, Cover (partial) -3, Movement -1, Dodges -1, Frenzy Fire -2, Into Hand-to-Hand -2",
armorDamageFormula: "@damage - @effectiveArmor",
addEffectToDamage: true,
weightModifierForWornArmor: "1",
chainBonus: "-2, -1, -1, 1, 1, 2"
}
},
OTHER: {
key: "OTHER",
name: "Other",
Expand Down Expand Up @@ -667,9 +720,10 @@ const CONSUMABLES = Object.freeze({
});

export type CE_DIFFICULTIES = { Formidable:{ mod:number; target:number }; Easy:{ mod:number; target:number }; Difficult:{ mod:number; target:number }; Average:{ mod:number; target:number }; VeryDifficult:{ mod:number; target:number }; Routine:{ mod:number; target:number }; Impossible:{ mod:number; target:number }; Simple:{ mod:number; target:number } };
export type CU_DIFFICULTIES = { Formidable:{ mod:number; target:number }; Easy:{ mod:number; target:number }; Difficult:{ mod:number; target:number }; Average:{ mod:number; target:number }; VeryDifficult:{ mod:number; target:number }; Routine:{ mod:number; target:number }};
export type CEL_DIFFICULTIES = { Formidable:{ mod:number; target:number }; Difficult:{ mod:number; target:number }; Average:{ mod:number; target:number }; VeryDifficult:{ mod:number; target:number }; Routine:{ mod:number; target:number } };
export type AC_DIFFICULTIES = { Impossible:{ mod:number; target:number }; Formidable:{ mod:number; target:number }; VeryDifficult:{ mod:number; target:number }; Difficult:{ mod:number; target:number }; Average:{ mod:number; target:number }; Simple:{ mod:number; target:number }; Routine:{ mod:number; target:number } };
const DIFFICULTIES:Readonly<{ CE:CE_DIFFICULTIES; CEL:CEL_DIFFICULTIES, AC: AC_DIFFICULTIES }> = Object.freeze({
const DIFFICULTIES:Readonly<{ CE:CE_DIFFICULTIES; CEL:CEL_DIFFICULTIES, AC: AC_DIFFICULTIES, CU:CU_DIFFICULTIES }> = Object.freeze({
CE: {
Simple: {mod: 6, target: 2},
Easy: {mod: 4, target: 4},
Expand All @@ -678,14 +732,14 @@ const DIFFICULTIES:Readonly<{ CE:CE_DIFFICULTIES; CEL:CEL_DIFFICULTIES, AC: AC_D
Difficult: {mod: -2, target: 10},
VeryDifficult: {mod: -4, target: 12},
Formidable: {mod: -6, target: 14},
Impossible: {mod: -8, target: 16},
Impossible: {mod: -8, target: 16}
},
CEL: {
Routine: {mod: 2, target: 4},
Average: {mod: 0, target: 6},
Difficult: {mod: -2, target: 8},
VeryDifficult: {mod: -4, target: 10},
Formidable: {mod: -6, target: 12},
Formidable: {mod: -6, target: 12}
},
AC: {
Routine: {mod: 2, target: 4},
Expand All @@ -695,6 +749,14 @@ const DIFFICULTIES:Readonly<{ CE:CE_DIFFICULTIES; CEL:CEL_DIFFICULTIES, AC: AC_D
VeryDifficult: {mod: -6, target: 12},
Formidable: {mod: -8, target: 14},
Impossible: {mod: -10, target: 16}
},
CU: {
Easy: {mod: 4, target: 4},
Routine: {mod: 2, target: 6},
Average: {mod: 0, target: 8},
Difficult: {mod: -2, target: 10},
VeryDifficult: {mod: -4, target: 12},
Formidable: {mod: -6, target: 14}
}
});

Expand Down Expand Up @@ -942,6 +1004,7 @@ export const RANGE_MODIFIERS_TYPES = {
none: "TWODSIX.Chat.Roll.RangeModifierTypes.none",
CT_Bands: "TWODSIX.Chat.Roll.RangeModifierTypes.CT_Bands",
CE_Bands: "TWODSIX.Chat.Roll.RangeModifierTypes.CE_Bands",
CU_Bands: "TWODSIX.Chat.Roll.RangeModifierTypes.CU_Bands",
singleBand: "TWODSIX.Chat.Roll.RangeModifierTypes.singleBand",
doubleBand: "TWODSIX.Chat.Roll.RangeModifierTypes.doubleBand"
};
Expand Down Expand Up @@ -971,6 +1034,33 @@ export const CE_WEAPON_RANGE_TYPES = {
}
};

export const CU_WEAPON_RANGE_TYPES = {
long: {
personal: "TWODSIX.Chat.Roll.WeaponRangeTypes.personal",
close: "TWODSIX.Chat.Roll.WeaponRangeTypes.close",
short: "TWODSIX.Chat.Roll.WeaponRangeTypes.short",
medium: "TWODSIX.Chat.Roll.WeaponRangeTypes.medium",
shotgun: "TWODSIX.Chat.Roll.WeaponRangeTypes.shotgun",
thrown: "TWODSIX.Chat.Roll.WeaponRangeTypes.thrown",
long: "TWODSIX.Chat.Roll.WeaponRangeTypes.long",
veryLong: "TWODSIX.Chat.Roll.WeaponRangeTypes.veryLong",
distant: "TWODSIX.Chat.Roll.WeaponRangeTypes.distant",
none: "TWODSIX.Chat.Roll.WeaponRangeTypes.none"
},
short: {
personal: "TWODSIX.Chat.Roll.WeaponRangeTypes.pers",
close: "TWODSIX.Chat.Roll.WeaponRangeTypes.close",
short: "TWODSIX.Chat.Roll.WeaponRangeTypes.short",
medium: "TWODSIX.Chat.Roll.WeaponRangeTypes.medium",
shotgun: "TWODSIX.Chat.Roll.WeaponRangeTypes.shotgun",
thrown: "TWODSIX.Chat.Roll.WeaponRangeTypes.thr",
long: "TWODSIX.Chat.Roll.WeaponRangeTypes.long",
veryLong: "TWODSIX.Chat.Roll.WeaponRangeTypes.vLong",
distant: "TWODSIX.Chat.Roll.WeaponRangeTypes.dist",
none: "TWODSIX.Chat.Roll.WeaponRangeTypes.none"
}
};

export const CT_WEAPON_RANGE_TYPES = {
long: {
hands: "TWODSIX.Chat.Roll.WeaponRangeTypes.hands",
Expand Down Expand Up @@ -1073,6 +1163,34 @@ export const ITEM_TYPE_SELECT = {
storage: "TWODSIX.Items.Items.MoveStorage"
};

export const CU_DAMAGE_TYPES = {
acid: "TWODSIX.DamageType.Acid",
ballistic: "TWODSIX.DamageType.Ballistic",
disintegrate: "TWODSIX.DamageType.Disintegrate",
electrical: "TWODSIX.DamageType.Electrical",
entangle: "TWODSIX.DamageType.Entangle",
fire: "TWODSIX.DamageType.Fire",
laser: "TWODSIX.DamageType.Laser",
melee: "TWODSIX.DamageType.Melee",
plasma: "TWODSIX.DamageType.Plasma",
poison: "TWODSIX.DamageType.Poison",
stun: "TWODSIX.DamageType.Stun"
};

export const DAMAGECOLORS = Object.freeze({
minorWoundTint: '#ffff00', // Yellow
seriousWoundTint: '#ff0000', // Red
deadTint: '#ffffff' // White
});

export const effectType = Object.freeze({
dead: 'EFFECT.StatusDead',
wounded: 'EFFECT.StatusWounded',
unconscious: 'EFFECT.StatusUnconscious',
encumbered: 'EFFECT.StatusEncumbered'
});


export type TWODSIX = {
CHARACTERISTICS: typeof CHARACTERISTICS,
CONSUMABLES: typeof CONSUMABLES,
Expand Down Expand Up @@ -1105,11 +1223,15 @@ export type TWODSIX = {
EQUIPPED_TOGGLE_OPTIONS: typeof EQUIPPED_TOGGLE_OPTIONS,
RANGE_MODIFIERS_TYPES: typeof RANGE_MODIFIERS_TYPES,
CE_WEAPON_RANGE_TYPES: typeof CE_WEAPON_RANGE_TYPES,
CU_WEAPON_RANGE_TYPES: typeof CU_WEAPON_RANGE_TYPES,
CT_WEAPON_RANGE_TYPES: typeof CT_WEAPON_RANGE_TYPES,
CT_ARMOR_TYPES: typeof CT_ARMOR_TYPES,
TARGET_DM: object,
AUG_LOCATIONS: typeof AUG_LOCATIONS,
ITEM_TYPE_SELECT: typeof ITEM_TYPE_SELECT
ITEM_TYPE_SELECT: typeof ITEM_TYPE_SELECT,
CU_DAMAGE_TYPES: typeof CU_DAMAGE_TYPES,
effectType: typeof effectType,
DAMAGECOLORS: typeof DAMAGECOLORS
};

export const TWODSIX = {
Expand Down Expand Up @@ -1144,9 +1266,13 @@ export const TWODSIX = {
EQUIPPED_TOGGLE_OPTIONS: EQUIPPED_TOGGLE_OPTIONS,
RANGE_MODIFIERS_TYPES: RANGE_MODIFIERS_TYPES,
CE_WEAPON_RANGE_TYPES: CE_WEAPON_RANGE_TYPES,
CU_WEAPON_RANGE_TYPES: CU_WEAPON_RANGE_TYPES,
CT_WEAPON_RANGE_TYPES: CT_WEAPON_RANGE_TYPES,
CT_ARMOR_TYPES: CT_ARMOR_TYPES,
TARGET_DM: TARGET_DM,
AUG_LOCATIONS: AUG_LOCATIONS,
ITEM_TYPE_SELECT: ITEM_TYPE_SELECT
ITEM_TYPE_SELECT: ITEM_TYPE_SELECT,
CU_DAMAGE_TYPES: CU_DAMAGE_TYPES,
effectType: effectType,
DAMAGECOLORS: DAMAGECOLORS
};
2 changes: 2 additions & 0 deletions src/module/data/item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export class WeaponData extends GearData {
schema.recoil = new fields.BooleanField({ required: true, initial: false});
schema.features = new fields.StringField({...requiredBlankString});
schema.armorPiercing = new fields.NumberField({...requiredInteger, initial: 0});
schema.parryAV = new fields.NumberField({...requiredInteger, initial: 0});
schema.isShield = new fields.BooleanField({ required: true, initial: false});
schema.handlingModifiers = new fields.StringField({...requiredBlankString});
schema.meleeRangeModifier = new fields.StringField({ required: true, blank: true, initial: "0"});
schema.customCT = new fields.SchemaField({
Expand Down
4 changes: 4 additions & 0 deletions src/module/data/vehicles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,10 @@ export class VehicleData extends TwodsixVehicleBaseData {
schema.traits = new fields.StringField({...requiredBlankString});
schema.weight = new fields.StringField({...requiredBlankString});
schema.shippingSize = new fields.StringField({...requiredBlankString});
schema.spaces = new fields.SchemaField({
value: new fields.NumberField({...requiredInteger, initial: 0 }),
max: new fields.NumberField({...requiredInteger, initial: 0 })
});
return schema;
}
}
Expand Down
Loading

0 comments on commit 143df93

Please sign in to comment.