Of imports and other mysteries

I really hope you aren't anything like me[1].

Whenever I'm left alone with new programming stuff, I always get easily frustrated, my blood boils and I always feel desperation and agony[2] that is quite physically unpleasant.

Then, after stubbornly dealing with the quite real physical pain, I finally get the hang of it and things start to go smooth.
Only then I can relax and continue with my life as usual; social life, specially.

I don't know why the hell people say I'm crazy. I told Poseidon about that when I was in the shower and he agreed with me I'm quite normal.

Now that I think about it, I suppose a great deal of that frustration comes from the fact that I know I'm usually alone in these things and there's no one I could ever ask for help.

But you are not like me. You actually have me to guide you ❤️.
And so, I hope I can save you from much of the pain I've endured to get up to this point.

What are imports?

Sure, I can send you right away to the official documentation, but then again, some context is always helpful.

You see, all "general purpose programming languages"[3] I know of have some kind of "module importing".

In the case of Typescript, you import modules with import.
Like this:

import { Actor, Game, ObjectReference } from "skyrimPlatform"

import * as JDB from "JContainers/JDB"

You still haven't told me shit

I know, bear with me.
These are actually quite easy to understand concepts, but I've always liked to give an overview of things before I can get into details.

Anyway, here's what a module is.

What's a module

A file that contains code.

Really, that's all there's to it.

In the case of Typescript:

What's a Typescript module

A *.ts file that contains Typescript code.

Some programming languages call them by other names, I think. But right now I don't remember about one that doesn't call them "modules"[4].
All of the languages I remember[5], call them like that.

Sometimes humans call them by other names, like "source files" or "libraries", but all of those names refer to the same thing: code spread along some files.

Oh, speaking of libraries...

What are modules useful for

Two main things:

  1. Spread code along many files
  2. Reuse code by making libraries

Spread code along many files

Most of the time, programs are so big it's impractical to write all of them inside just one file.

Sure, you can do that, but it's a pain in the ass to our human minds to keep track of things when a file is bigger than a couple hundreds of lines[6].

So, you can spread all your code along many little and easy to manage files with hopefully helpful names telling you the content of those files.

Reuse code by making libraries

We have a concrete example of this right in front of our noses: skyrimPlatform.ts.

If that file didn't exist, everytime you wanted to do something to an Actor you would need to add all this code to your file:

declare class PapyrusObject {
  static from(papyrusObject: PapyrusObject | null): PapyrusObject | null;
}

export declare class Form extends PapyrusObject{
    static from(papyrusObject: PapyrusObject | null) : Form| null;
    getFormID(): number;
    getGoldValue(): number;
    getKeywords(): PapyrusObject[] | null;
    getName(): string;
    getNthKeyword(index: number): Keyword | null;
    getNumKeywords(): number;
    getType(): number;
    getWeight(): number;
    getWorldModelNthTextureSet(n: number): TextureSet | null;
    getWorldModelNumTextureSets(): number;
    getWorldModelPath(): string;
    hasKeyword(akKeyword: Keyword | null): boolean;
    hasWorldModel(): boolean;
    isPlayable(): boolean;
    playerKnows(): boolean;
    registerForActorAction(actionType: number): void;
    registerForAnimationEvent(akSender: ObjectReference | null, asEventName: string): boolean;
    registerForCameraState(): void;
    registerForControl(control: string): void;
    registerForCrosshairRef(): void;
    registerForKey(keyCode: number): void;
    registerForLOS(akViewer: Actor | null, akTarget: ObjectReference | null): void;
    registerForMenu(menuName: string): void;
    registerForModEvent(eventName: string, callbackName: string): void;
    registerForNiNodeUpdate(): void;
    registerForSingleLOSGain(akViewer: Actor | null, akTarget: ObjectReference | null): void;
    registerForSingleLOSLost(akViewer: Actor | null, akTarget: ObjectReference | null): void;
    registerForSingleUpdate(afInterval: number): void;
    registerForSingleUpdateGameTime(afInterval: number): void;
    registerForSleep(): void;
    registerForTrackedStatsEvent(): void;
    registerForUpdate(afInterval: number): void;
    registerForUpdateGameTime(afInterval: number): void;
    sendModEvent(eventName: string, strArg: string, numArg: number): void;
    setGoldValue(value: number): void;
    setName(name: string): void;
    setPlayerKnows(knows: boolean): void;
    setWeight(weight: number): void;
    setWorldModelNthTextureSet(nSet: TextureSet | null, n: number): void;
    setWorldModelPath(path: string): void;
    startObjectProfiling(): void;
    stopObjectProfiling(): void;
    tempClone(): Form | null;
    unregisterForActorAction(actionType: number): void;
    unregisterForAllControls(): void;
    unregisterForAllKeys(): void;
    unregisterForAllMenus(): void;
    unregisterForAllModEvents(): void;
    unregisterForAnimationEvent(akSender: ObjectReference | null, asEventName: string): void;
    unregisterForCameraState(): void;
    unregisterForControl(control: string): void;
    unregisterForCrosshairRef(): void;
    unregisterForKey(keyCode: number): void;
    unregisterForLOS(akViewer: Actor | null, akTarget: ObjectReference | null): void;
    unregisterForMenu(menuName: string): void;
    unregisterForModEvent(eventName: string): void;
    unregisterForNiNodeUpdate(): void;
    unregisterForSleep(): void;
    unregisterForTrackedStatsEvent(): void;
    unregisterForUpdate(): void;
    unregisterForUpdateGameTime(): void;
}

export declare class ObjectReference extends Form{
    static from(papyrusObject: PapyrusObject | null) : ObjectReference| null;
    activate(akActivator: ObjectReference | null, abDefaultProcessingOnly: boolean): boolean;
    addDependentAnimatedObjectReference(akDependent: ObjectReference | null): boolean;
    addInventoryEventFilter(akFilter: Form | null): void;
    addItem(akItemToAdd: Form | null, aiCount: number, abSilent: boolean): void;
    addToMap(abAllowFastTravel: boolean): void;
    applyHavokImpulse(afX: number, afY: number, afZ: number, afMagnitude: number): Promise<void>;
    blockActivation(abBlocked: boolean): void;
    calculateEncounterLevel(aiDifficulty: number): number;
    canFastTravelToMarker(): boolean;
    clearDestruction(): void;
    createDetectionEvent(akOwner: Actor | null, aiSoundLevel: number): void;
    createEnchantment(maxCharge: number, effects: PapyrusObject[] | null, magnitudes: number[] | null, areas: number[] | null, durations: number[] | null): void;
    damageObject(afDamage: number): Promise<void>;
    delete(): Promise<void>;
    disable(abFadeOut: boolean): Promise<void>;
    disableNoWait(abFadeOut: boolean): void;
    dropObject(akObject: Form | null, aiCount: number): Promise<ObjectReference | null>;
    enable(abFadeIn: boolean): Promise<void>;
    enableFastTravel(abEnable: boolean): void;
    enableNoWait(abFadeIn: boolean): void;
    forceAddRagdollToWorld(): Promise<void>;
    forceRemoveRagdollFromWorld(): Promise<void>;
    getActorOwner(): ActorBase | null;
    getAllForms(toFill: FormList | null): void;
    getAngleX(): number;
    getAngleY(): number;
    getAngleZ(): number;
    getAnimationVariableBool(arVariableName: string): boolean;
    getAnimationVariableFloat(arVariableName: string): number;
    getAnimationVariableInt(arVariableName: string): number;
    getBaseObject(): Form | null;
    getContainerForms(): PapyrusObject[] | null;
    getCurrentDestructionStage(): number;
    getCurrentLocation(): Location | null;
    getCurrentScene(): Scene | null;
    getDisplayName(): string;
    getEditorLocation(): Location | null;
    getEnableParent(): ObjectReference | null;
    getEnchantment(): Enchantment | null;
    getFactionOwner(): Faction | null;
    getHeadingAngle(akOther: ObjectReference | null): number;
    getHeight(): number;
    getItemCharge(): number;
    getItemCount(akItem: Form | null): number;
    getItemHealthPercent(): number;
    getItemMaxCharge(): number;
    getKey(): Key | null;
    getLength(): number;
    getLinkedRef(apKeyword: Keyword | null): ObjectReference | null;
    getLockLevel(): number;
    getMass(): number;
    getNthForm(index: number): Form | null;
    getNthLinkedRef(aiLinkedRef: number): ObjectReference | null;
    getNthReferenceAlias(n: number): ReferenceAlias | null;
    getNumItems(): number;
    getNumReferenceAliases(): number;
    getOpenState(): number;
    getParentCell(): Cell | null;
    getPoison(): Potion | null;
    getPositionX(): number;
    getPositionY(): number;
    getPositionZ(): number;
    getReferenceAliases(): PapyrusObject[] | null;
    getScale(): number;
    getTotalArmorWeight(): number;
    getTotalItemWeight(): number;
    getTriggerObjectCount(): number;
    getVoiceType(): VoiceType | null;
    getWidth(): number;
    getWorldSpace(): WorldSpace | null;
    hasEffectKeyword(akKeyword: Keyword | null): boolean;
    hasNode(asNodeName: string): boolean;
    hasRefType(akRefType: LocationRefType | null): boolean;
    ignoreFriendlyHits(abIgnore: boolean): void;
    interruptCast(): void;
    is3DLoaded(): boolean;
    isActivateChild(akChild: ObjectReference | null): boolean;
    isActivationBlocked(): boolean;
    isDeleted(): boolean;
    isDisabled(): boolean;
    isFurnitureInUse(abIgnoreReserved: boolean): boolean;
    isFurnitureMarkerInUse(aiMarker: number, abIgnoreReserved: boolean): boolean;
    isHarvested(): boolean;
    isIgnoringFriendlyHits(): boolean;
    isInDialogueWithPlayer(): boolean;
    isLockBroken(): boolean;
    isLocked(): boolean;
    isMapMarkerVisible(): boolean;
    isOffLimits(): boolean;
    knockAreaEffect(afMagnitude: number, afRadius: number): void;
    lock(abLock: boolean, abAsOwner: boolean): void;
    moveTo(akTarget: ObjectReference | null, afXOffset: number, afYOffset: number, afZOffset: number, abMatchRotation: boolean): Promise<void>;
    moveToInteractionLocation(akTarget: ObjectReference | null): Promise<void>;
    moveToMyEditorLocation(): Promise<void>;
    moveToNode(akTarget: ObjectReference | null, asNodeName: string): Promise<void>;
    placeActorAtMe(akActorToPlace: ActorBase | null, aiLevelMod: number, akZone: EncounterZone | null): Actor | null;
    placeAtMe(akFormToPlace: Form | null, aiCount: number, abForcePersist: boolean, abInitiallyDisabled: boolean): ObjectReference | null;
    playAnimation(asAnimation: string): boolean;
    playAnimationAndWait(asAnimation: string, asEventName: string): Promise<boolean>;
    playGamebryoAnimation(asAnimation: string, abStartOver: boolean, afEaseInTime: number): boolean;
    playImpactEffect(akImpactEffect: ImpactDataSet | null, asNodeName: string, afPickDirX: number, afPickDirY: number, afPickDirZ: number, afPickLength: number, abApplyNodeRotation: boolean, abUseNodeLocalRotation: boolean): boolean;
    playSyncedAnimationAndWaitSS(asAnimation1: string, asEvent1: string, akObj2: ObjectReference | null, asAnimation2: string, asEvent2: string): Promise<boolean>;
    playSyncedAnimationSS(asAnimation1: string, akObj2: ObjectReference | null, asAnimation2: string): boolean;
    playTerrainEffect(asEffectModelName: string, asAttachBoneName: string): void;
    processTrapHit(akTrap: ObjectReference | null, afDamage: number, afPushback: number, afXVel: number, afYVel: number, afZVel: number, afXPos: number, afYPos: number, afZPos: number, aeMaterial: number, afStagger: number): void;
    pushActorAway(akActorToPush: Actor | null, aiKnockbackForce: number): void;
    removeAllInventoryEventFilters(): void;
    removeAllItems(akTransferTo: ObjectReference | null, abKeepOwnership: boolean, abRemoveQuestItems: boolean): void;
    removeDependentAnimatedObjectReference(akDependent: ObjectReference | null): boolean;
    removeInventoryEventFilter(akFilter: Form | null): void;
    removeItem(akItemToRemove: Form | null, aiCount: number, abSilent: boolean, akOtherContainer: ObjectReference | null): void;
    reset(akTarget: ObjectReference | null): Promise<void>;
    resetInventory(): void;
    say(akTopicToSay: Topic | null, akActorToSpeakAs: Actor | null, abSpeakInPlayersHead: boolean): void;
    sendStealAlarm(akThief: Actor | null): void;
    setActorCause(akActor: Actor | null): void;
    setActorOwner(akActorBase: ActorBase | null): void;
    setAngle(afXAngle: number, afYAngle: number, afZAngle: number): Promise<void>;
    setAnimationVariableBool(arVariableName: string, abNewValue: boolean): void;
    setAnimationVariableFloat(arVariableName: string, afNewValue: number): void;
    setAnimationVariableInt(arVariableName: string, aiNewValue: number): void;
    setDestroyed(abDestroyed: boolean): void;
    setDisplayName(name: string, force: boolean): boolean;
    setEnchantment(source: Enchantment | null, maxCharge: number): void;
    setFactionOwner(akFaction: Faction | null): void;
    setHarvested(harvested: boolean): void;
    setItemCharge(charge: number): void;
    setItemHealthPercent(health: number): void;
    setItemMaxCharge(maxCharge: number): void;
    setLockLevel(aiLockLevel: number): void;
    setMotionType(aeMotionType: MotionType, abAllowActivate: boolean): Promise<void>;
    setNoFavorAllowed(abNoFavor: boolean): void;
    setOpen(abOpen: boolean): void;
    setPosition(afX: number, afY: number, afZ: number): Promise<void>;
    setScale(afScale: number): Promise<void>;
    splineTranslateTo(afX: number, afY: number, afZ: number, afXAngle: number, afYAngle: number, afZAngle: number, afTangentMagnitude: number, afSpeed: number, afMaxRotationSpeed: number): void;
    splineTranslateToRefNode(arTarget: ObjectReference | null, arNodeName: string, afTangentMagnitude: number, afSpeed: number, afMaxRotationSpeed: number): void;
    stopTranslation(): void;
    tetherToHorse(akHorse: ObjectReference | null): void;
    translateTo(afX: number, afY: number, afZ: number, afXAngle: number, afYAngle: number, afZAngle: number, afSpeed: number, afMaxRotationSpeed: number): void;
    waitForAnimationEvent(asEventName: string): Promise<boolean>;
    getDistance(akOther: ObjectReference | null): number;
}

export declare class Actor extends ObjectReference{
    static from(papyrusObject: PapyrusObject | null) : Actor| null;
    addPerk(akPerk: Perk | null): void;
    addShout(akShout: Shout | null): boolean;
    addSpell(akSpell: Spell | null, abVerbose: boolean): boolean;
    allowBleedoutDialogue(abCanTalk: boolean): void;
    allowPCDialogue(abTalk: boolean): void;
    attachAshPile(akAshPileBase: Form | null): void;
    canFlyHere(): boolean;
    changeHeadPart(hPart: HeadPart | null): void;
    clearArrested(): void;
    clearExpressionOverride(): void;
    clearExtraArrows(): void;
    clearForcedMovement(): void;
    clearKeepOffsetFromActor(): void;
    clearLookAt(): void;
    damageActorValue(asValueName: string, afDamage: number): void;
    dismount(): boolean;
    dispelAllSpells(): void;
    dispelSpell(akSpell: Spell | null): boolean;
    doCombatSpellApply(akSpell: Spell | null, akTarget: ObjectReference | null): void;
    drawWeapon(): void;
    enableAI(abEnable: boolean): void;
    endDeferredKill(): void;
    equipItem(akItem: Form | null, abPreventRemoval: boolean, abSilent: boolean): void;
    equipItemById(item: Form | null, itemId: number, equipSlot: number, preventUnequip: boolean, equipSound: boolean): void;
    equipItemEx(item: Form | null, equipSlot: number, preventUnequip: boolean, equipSound: boolean): void;
    equipShout(akShout: Shout | null): void;
    equipSpell(akSpell: Spell | null, aiSource: number): void;
    evaluatePackage(): void;
    forceActorValue(asValueName: string, afNewValue: number): void;
    forceMovementDirection(afXAngle: number, afYAngle: number, afZAngle: number): void;
    forceMovementDirectionRamp(afXAngle: number, afYAngle: number, afZAngle: number, afRampTime: number): void;
    forceMovementRotationSpeed(afXMult: number, afYMult: number, afZMult: number): void;
    forceMovementRotationSpeedRamp(afXMult: number, afYMult: number, afZMult: number, afRampTime: number): void;
    forceMovementSpeed(afSpeedMult: number): void;
    forceMovementSpeedRamp(afSpeedMult: number, afRampTime: number): void;
    forceTargetAngle(afXAngle: number, afYAngle: number, afZAngle: number): void;
    forceTargetDirection(afXAngle: number, afYAngle: number, afZAngle: number): void;
    forceTargetSpeed(afSpeed: number): void;
    getActorValue(asValueName: string): number;
    getActorValueMax(asValueName: string): number;
    getActorValuePercentage(asValueName: string): number;
    getBaseActorValue(asValueName: string): number;
    getBribeAmount(): number;
    getCombatState(): number;
    getCombatTarget(): Actor | null;
    getCrimeFaction(): Faction | null;
    getCurrentPackage(): Package | null;
    getDialogueTarget(): Actor | null;
    getEquippedArmorInSlot(aiSlot: number): Armor | null;
    getEquippedItemId(Location: number): number;
    getEquippedItemType(aiHand: number): number;
    getEquippedObject(Location: number): Form | null;
    getEquippedShield(): Armor | null;
    getEquippedShout(): Shout | null;
    getEquippedSpell(aiSource: number): Spell | null;
    getEquippedWeapon(abLeftHand: boolean): Weapon | null;
    getFactionRank(akFaction: Faction | null): number;
    getFactionReaction(akOther: Actor | null): number;
    getFactions(minRank: number, maxRank: number): PapyrusObject[] | null;
    getFlyingState(): number;
    getForcedLandingMarker(): ObjectReference | null;
    getFurnitureReference(): ObjectReference | null;
    getGoldAmount(): number;
    getHighestRelationshipRank(): number;
    getKiller(): Actor | null;
    getLevel(): number;
    getLeveledActorBase(): ActorBase | null;
    getLightLevel(): number;
    getLowestRelationshipRank(): number;
    getNoBleedoutRecovery(): boolean;
    getNthSpell(n: number): Spell | null;
    getPlayerControls(): boolean;
    getRace(): Race | null;
    getRelationshipRank(akOther: Actor | null): number;
    getSitState(): number;
    getSleepState(): number;
    getSpellCount(): number;
    getVoiceRecoveryTime(): number;
    getWarmthRating(): number;
    getWornForm(slotMask: number): Form | null;
    getWornItemId(slotMask: number): number;
    hasAssociation(akAssociation: AssociationType | null, akOther: Actor | null): boolean;
    hasFamilyRelationship(akOther: Actor | null): boolean;
    hasLOS(akOther: ObjectReference | null): boolean;
    hasMagicEffect(akEffect: MagicEffect | null): boolean;
    hasMagicEffectWithKeyword(akKeyword: Keyword | null): boolean;
    hasParentRelationship(akOther: Actor | null): boolean;
    hasPerk(akPerk: Perk | null): boolean;
    hasSpell(akForm: Form | null): boolean;
    isAIEnabled(): boolean;
    isAlarmed(): boolean;
    isAlerted(): boolean;
    isAllowedToFly(): boolean;
    isArrested(): boolean;
    isArrestingTarget(): boolean;
    isBeingRidden(): boolean;
    isBleedingOut(): boolean;
    isBribed(): boolean;
    isChild(): boolean;
    isCommandedActor(): boolean;
    isDead(): boolean;
    isDetectedBy(akOther: Actor | null): boolean;
    isDoingFavor(): boolean;
    isEquipped(akItem: Form | null): boolean;
    isEssential(): boolean;
    isFlying(): boolean;
    isGhost(): boolean;
    isGuard(): boolean;
    isHostileToActor(akActor: Actor | null): boolean;
    isInCombat(): boolean;
    isInFaction(akFaction: Faction | null): boolean;
    isInKillMove(): boolean;
    isIntimidated(): boolean;
    isOnMount(): boolean;
    isOverEncumbered(): boolean;
    isPlayerTeammate(): boolean;
    isPlayersLastRiddenHorse(): boolean;
    isRunning(): boolean;
    isSneaking(): boolean;
    isSprinting(): boolean;
    isSwimming(): boolean;
    isTrespassing(): boolean;
    isUnconscious(): boolean;
    isWeaponDrawn(): boolean;
    keepOffsetFromActor(arTarget: Actor | null, afOffsetX: number, afOffsetY: number, afOffsetZ: number, afOffsetAngleX: number, afOffsetAngleY: number, afOffsetAngleZ: number, afCatchUpRadius: number, afFollowRadius: number): void;
    kill(akKiller: Actor | null): void;
    killSilent(akKiller: Actor | null): void;
    modActorValue(asValueName: string, afAmount: number): void;
    modFactionRank(akFaction: Faction | null, aiMod: number): void;
    moveToPackageLocation(): Promise<void>;
    openInventory(abForceOpen: boolean): void;
    pathToReference(aTarget: ObjectReference | null, afWalkRunPercent: number): Promise<boolean>;
    playIdle(akIdle: Idle | null): boolean;
    playIdleWithTarget(akIdle: Idle | null, akTarget: ObjectReference | null): boolean;
    playSubGraphAnimation(asEventName: string): void;
    queueNiNodeUpdate(): void;
    regenerateHead(): void;
    removeFromAllFactions(): void;
    removeFromFaction(akFaction: Faction | null): void;
    removePerk(akPerk: Perk | null): void;
    removeShout(akShout: Shout | null): boolean;
    removeSpell(akSpell: Spell | null): boolean;
    replaceHeadPart(oPart: HeadPart | null, newPart: HeadPart | null): void;
    resetAI(): void;
    resetExpressionOverrides(): void;
    resetHealthAndLimbs(): void;
    restoreActorValue(asValueName: string, afAmount: number): void;
    resurrect(): Promise<void>;
    sendAssaultAlarm(): void;
    sendLycanthropyStateChanged(abIsWerewolf: boolean): void;
    sendTrespassAlarm(akCriminal: Actor | null): void;
    sendVampirismStateChanged(abIsVampire: boolean): void;
    setActorValue(asValueName: string, afValue: number): void;
    setAlert(abAlerted: boolean): void;
    setAllowFlying(abAllowed: boolean): void;
    setAllowFlyingEx(abAllowed: boolean, abAllowCrash: boolean, abAllowSearch: boolean): void;
    setAlpha(afTargetAlpha: number, abFade: boolean): void;
    setAttackActorOnSight(abAttackOnSight: boolean): void;
    setBribed(abBribe: boolean): void;
    setCrimeFaction(akFaction: Faction | null): void;
    setCriticalStage(aiStage: number): void;
    setDoingFavor(abDoingFavor: boolean): void;
    setDontMove(abDontMove: boolean): void;
    setExpressionModifier(index: number, value: number): void;
    setExpressionOverride(aiMood: number, aiStrength: number): void;
    setExpressionPhoneme(index: number, value: number): void;
    setEyeTexture(akNewTexture: TextureSet | null): void;
    setFactionRank(akFaction: Faction | null, aiRank: number): void;
    setForcedLandingMarker(aMarker: ObjectReference | null): void;
    setGhost(abIsGhost: boolean): void;
    setHeadTracking(abEnable: boolean): void;
    setIntimidated(abIntimidate: boolean): void;
    setLookAt(akTarget: ObjectReference | null, abPathingLookAt: boolean): void;
    setNoBleedoutRecovery(abAllowed: boolean): void;
    setNotShowOnStealthMeter(abNotShow: boolean): void;
    setOutfit(akOutfit: Outfit | null, abSleepOutfit: boolean): void;
    setPlayerControls(abControls: boolean): void;
    setPlayerResistingArrest(): void;
    setPlayerTeammate(abTeammate: boolean, abCanDoFavor: boolean): void;
    setRace(akRace: Race | null): void;
    setRelationshipRank(akOther: Actor | null, aiRank: number): void;
    setRestrained(abRestrained: boolean): void;
    setSubGraphFloatVariable(asVariableName: string, afValue: number): void;
    setUnconscious(abUnconscious: boolean): void;
    setVehicle(akVehicle: ObjectReference | null): void;
    setVoiceRecoveryTime(afTime: number): void;
    sheatheWeapon(): void;
    showBarterMenu(): void;
    showGiftMenu(abGivingGift: boolean, apFilterList: FormList | null, abShowStolenItems: boolean, abUseFavorPoints: boolean): Promise<number>;
    startCannibal(akTarget: Actor | null): void;
    startCombat(akTarget: Actor | null): void;
    startDeferredKill(): void;
    startSneaking(): void;
    startVampireFeed(akTarget: Actor | null): void;
    stopCombat(): void;
    stopCombatAlarm(): void;
    trapSoul(akTarget: Actor | null): boolean;
    unLockOwnedDoorsInCell(): void;
    unequipAll(): void;
    unequipItem(akItem: Form | null, abPreventEquip: boolean, abSilent: boolean): void;
    unequipItemEx(item: Form | null, equipSlot: number, preventEquip: boolean): void;
    unequipItemSlot(aiSlot: number): void;
    unequipShout(akShout: Shout | null): void;
    unequipSpell(akSpell: Spell | null, aiSource: number): void;
    updateWeight(neckDelta: number): void;
    willIntimidateSucceed(): boolean;
    wornHasKeyword(akKeyword: Keyword | null): boolean;
}

If you thought it was annoying to skip all of this, imagine how annoying it would have been to make changes to that Actor code.

You would've need to change it in every single place you have ever used it (good luck remembering all of those places).

It's better to just use this, don't you agree?

import { Actor } from "skyrimPlatform"

What's a library?

A library is a file that contains code that you can use over an over again.

Whenever that code changes, places that have called things inside that library will automatically take things from the new version.

Let's revisit our barebones project.

If you open src\example.ts, you will see this:

import { printConsole } from "skyrimPlatform"

export let main = () => {
  printConsole("Barebones mod is now working")
}

You can picture that first line as a conversation between two neighbors.
One wants to borrow something from the other:

– Hey, dude! I know you have a tool named printConsole. May I borrow it?
– Sure. Just don't break it.
– Nah, I just wanted to use it for saying "Barebones mod is now working".

You see: if possible, you should always think about these kind of bullshit situations when dealing with new programming and mathemathics concepts, since that's the way our human minds better undestand the world around them.

Seeing complex mathematical concepts explained with drawings even a kindergartener would have understood has certainly helped me way better than all university classes I've ever had about those same topics.

Wrapping up

I wanted to talk here about "module not found" type of errors and a nifty trick to get rid of them without having to resort to changing tsconfig files, but thanks to my little jest of adding hundreds of lines from skyrimPlatform.ts, this is already so long I will better leave that for another time.

Cheers.


  1. And if you have read whatever else I've written here, you know you are on a good track if you aren't anything like me. ↩︎

  2. The only reason I've never made a C++ dll is because those sensations get physically unbereable whenever I try to read anything written for it.
    I wish that was a joke. ↩︎

  3. You could say Papyrus is a "specialist programming language" specifically tailored for Bethesda games. ↩︎

  4. I'm almost totally sure the close relatives of the C family (C, C++, Java...) call them by other names.
    But I have so much steem for that family as the steem I have for the Black Briar family. That is, I only kind of like one of them and hate and avoid the rest as much as possible.

    And I don't know Ingun... errr... C or C# well enough to remember how that family names their "modules".

    By the way, I'm aware Javascript (and Typescript by extension) are technically part of that family, but they are so different to the other members they may very well be a cool and chill family branch living in Hammerfell; renowned for hunting Thalmor assholes. ↩︎

  5. Right on top of my head: Delphi/Pascal, Python, Lua, Typescript, Fortran... ↩︎

  6. If you have ever done MCM menus in Papyrus you know exactly what I'm talking about. ↩︎