2026-10-03 07:06:48 +00:00
<#
. SYNOPSIS
Cuit un mod d 'assets et l' assemble en archive installable ( lot H3b , Docs / Migration / 16_H3_Mods_Paquets . md ) .
. DESCRIPTION
Sources : ModSources \ < Nom > \ ( < Nom > . uplugin , Content \ , Module \ ; voir Tools / ModSource . ps1 ) .
1 . controles : plugin et module ( MODULE_UNIQUEID = < Nom > ) , version de reference Releases \ < Version > \ Windows , aucun
autre plugin dans Mods \ ;
2026-10-06 19:10:53 +00:00
1b . -Retarget bake | rigs : recopie les clips Quaternius sur Bridget et Michael ( Tools / RetargetQuaternius . py
dans UnrealEditor . exe , Slate requis ) . L 'editeur doit etre ferme. -RetargetOnly s' arrete avant la cuisson ;
1c . -Target sandbox | game ecrit Saved \ Config \ Windows \ LPAnimTarget . ini . -Open lance cette cible
( sandbox : chambre d ' essai ; game : partie , carte par defaut ) et ne cuit pas ;
2026-10-03 07:06:48 +00:00
2 . montage du mod dans Mods \ < Nom > , cuisson DLC contre la version de reference ( BuildCookRun -dlcname
-basedonreleaseversion , via cmd / c : appelee directement depuis PowerShell , la version est coupee en deux ) ,
journal dans Saved \ Logs \ portage \ mods ; remise en place dans ModSources \ < Nom > dans tous les cas ;
3 . controle du conteneur : chaque paquet est sous Mods / < Nom > / ( aucun asset du jeu embarque ) ;
4 . assemblage < Out > \ < Nom > -for -LifePlay - < Version > \ LifePlay \ { Mods \ < Nom > , Content \ Modules \ < Nom > } + notice bilingue ,
archive . zip testee ( 7 -Zip ) ; un assemblage precedent du meme nom est deplace ( precedent_ < date > ) , jamais supprime ;
5 . -Install < dossier du jeu > : copie le mod dans un jeu de test ( dossier contenant LifePlay . exe ) .
. EXAMPLE
H3d : metadonnees des mods, avertissement de version, paquet lpr_hair_pocolov (12 coiffures)
- LPModInfoSubsystem : metadonnees des .lpmod ; fenetre d'avertissement quand un mod actif
a ete construit pour une autre version du jeu (Slate, sans Blueprint).
- NewMod/BuildMod : MODULE_VERSION, _CREATED, _LICENSE, _AUTHOR_LINK, _PRICE, _PACKAGED,
_GAME_VERSION ; ordre des 3 premieres lignes controle ; prix et licences controles.
- lpr_hair_pocolov 1.0 : les 12 coiffures Pocolov CC BY ; lp_Revival et les 6 Pocolov
retires du jeu de base ; mod d'essai LPHairSample retire.
Valide en jeu le 2026-10-03 (CHG-106, CHG-107).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:14:37 +00:00
. \ Tools \ BuildMod . ps1 -Name lpr_hair_pocolov
2026-10-06 19:10:53 +00:00
. \ Tools \ BuildMod . ps1 -Name lpr_hair_pocolov -Retarget bake
. \ Tools \ BuildMod . ps1 -Name lpr_hair_pocolov -Retarget bake -RetargetOnly
. \ Tools \ BuildMod . ps1 -Target sandbox -Open
. \ Tools \ BuildMod . ps1 -Target game -Open
H3d : metadonnees des mods, avertissement de version, paquet lpr_hair_pocolov (12 coiffures)
- LPModInfoSubsystem : metadonnees des .lpmod ; fenetre d'avertissement quand un mod actif
a ete construit pour une autre version du jeu (Slate, sans Blueprint).
- NewMod/BuildMod : MODULE_VERSION, _CREATED, _LICENSE, _AUTHOR_LINK, _PRICE, _PACKAGED,
_GAME_VERSION ; ordre des 3 premieres lignes controle ; prix et licences controles.
- lpr_hair_pocolov 1.0 : les 12 coiffures Pocolov CC BY ; lp_Revival et les 6 Pocolov
retires du jeu de base ; mod d'essai LPHairSample retire.
Valide en jeu le 2026-10-03 (CHG-106, CHG-107).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:14:37 +00:00
. \ Tools \ BuildMod . ps1 -Name lpr_hair_pocolov -Install D: \ Game \ LifePlay_Build_5 . 5_H3d \ Windows
2026-10-03 07:06:48 +00:00
#>
param (
2026-10-06 19:10:53 +00:00
[ string ] $Name = '' ,
2026-10-03 07:06:48 +00:00
[ string ] $Version = '' ,
[ string ] $Out = 'D:\Game\LifePlay_Publication\Mods' ,
2026-10-06 19:10:53 +00:00
[ string ] $Install = '' ,
[ string ] $Retarget = '' ,
[ switch ] $RetargetOnly ,
[ string ] $Target = '' ,
[ switch ] $Open
2026-10-03 07:06:48 +00:00
)
$ErrorActionPreference = 'Stop'
$Root = Split-Path $PSScriptRoot -Parent
. ( Join-Path $PSScriptRoot 'ModSource.ps1' )
$UE = 'D:\Game\Launcher\Engine\UE_5.5\Engine'
$SevenZip = 'C:\Program Files\7-Zip\7z.exe'
2026-10-06 19:10:53 +00:00
function Invoke-AnimToGenesis([string ] $ProjectRoot , [ string ] $Mode ) {
$running = @ ( Get-Process UnrealEditor , UnrealEditor-Cmd -ErrorAction SilentlyContinue )
if ( $running ) { throw " UnrealEditor est ouvert (pid $( $running [ 0 ] . Id ) ) : le fermer avant la conversion " }
$stepFile = Join-Path $ProjectRoot 'Saved\anim_to_genesis_step.txt'
New-Item -ItemType Directory -Force ( Split-Path $stepFile ) | Out-Null
[ IO.File ] :: WriteAllText ( $stepFile , ( $Mode . Trim ( ) . ToLower ( ) + " `r `n " ) , ( New-Object Text . UTF8Encoding $false ) )
$editor = Join-Path $UE 'Binaries\Win64\UnrealEditor.exe'
$project = Join-Path $ProjectRoot 'LifePlay.uproject'
$script = Join-Path $ProjectRoot 'Tools\RetargetQuaternius.py'
$logDir = Join-Path $ProjectRoot 'Saved\Logs'
$success = if ( $Mode -eq 'rigs' ) { 'RETARGET rigs prets' } else { 'RETARGET bridget \d+ michael \d+ deplaces \d+' }
$before = @ { }
Get-ChildItem $logDir -Filter 'LifePlay*.log' -ErrorAction SilentlyContinue | ForEach-Object { $before [ $_ . FullName ] = $_ . Length }
Write-Host " Conversion des clips Quaternius vers Bridget et Michael ( $Mode ) "
$proc = Start-Process -FilePath $editor -ArgumentList @ ( $project , " -ExecutePythonScript= $script " , '-nosplash' , '-unattended' ) -WorkingDirectory $ProjectRoot -PassThru
$deadline = ( Get-Date ) . AddMinutes ( 25 )
$hit = $null
$failure = $null
while ( ( Get-Date ) -lt $deadline ) {
Start-Sleep -Seconds 5
$logs = @ ( Get-ChildItem $logDir -Filter 'LifePlay*.log' -ErrorAction SilentlyContinue )
foreach ( $file in $logs ) {
$offset = 0
if ( $before . ContainsKey ( $file . FullName ) ) { $offset = $before [ $file . FullName ] }
if ( $file . Length -le $offset ) { continue }
try {
$stream = [ IO.File ] :: Open ( $file . FullName , [ IO.FileMode ] :: Open , [ IO.FileAccess ] :: Read , [ IO.FileShare ] :: ReadWrite )
}
catch [ System.IO.IOException ] { continue }
try {
$stream . Seek ( $offset , [ IO.SeekOrigin ] :: Begin ) | Out-Null
$reader = New-Object IO . StreamReader ( $stream )
$text = $reader . ReadToEnd ( )
}
finally { $stream . Close ( ) }
$ok = [ regex ] :: Match ( $text , $success )
if ( $ok . Success ) { $hit = $ok . Value }
$bad = [ regex ] :: Match ( $text , 'chaine impossible[^\r\n]*|RETARGET deplacement rate[^\r\n]*|etape inconnue[^\r\n]*|LogPython: Error:[^\r\n]*' )
if ( $bad . Success ) { $failure = $bad . Value . Trim ( ) }
}
if ( $hit ) { break }
$editorGone = -not ( Get-Process UnrealEditor -ErrorAction SilentlyContinue )
if ( $editorGone -and $proc . HasExited ) { break }
}
if ( $failure -and -not $hit ) { throw " Conversion en echec : $failure " }
if ( -not $hit ) { throw " Conversion sans ligne RETARGET : voir Saved\Logs\LifePlay.log " }
$quiet = ( Get-Date ) . AddMinutes ( 2 )
while ( ( Get-Date ) -lt $quiet -and ( Get-Process -Id $proc . Id -ErrorAction SilentlyContinue ) ) { Start-Sleep -Seconds 2 }
$still = @ ( Get-Process UnrealEditor -ErrorAction SilentlyContinue )
if ( $still ) { throw " Conversion terminee ( $hit ) mais UnrealEditor est encore ouvert : le fermer avant la cuisson " }
Write-Host " $hit "
}
function Write-AnimTarget([string ] $ProjectRoot , [ string ] $Which ) {
$dir = Join-Path $ProjectRoot 'Saved\Config\Windows'
New-Item -ItemType Directory -Force $dir | Out-Null
$ini = Join-Path $dir 'LPAnimTarget.ini'
[ IO.File ] :: WriteAllText ( $ini , " [LifePlay.Anim] `r `n Target= $Which `r `n " , ( New-Object Text . UTF8Encoding $false ) )
Write-Host " Cible des animations : $Which ( $ini ) "
}
function Open-AnimTarget([string ] $ProjectRoot , [ string ] $Which ) {
$running = @ ( Get-Process UnrealEditor , UnrealEditor-Cmd -ErrorAction SilentlyContinue )
if ( $running ) { throw " UnrealEditor est ouvert (pid $( $running [ 0 ] . Id ) ) : le fermer avant d'ouvrir la cible " }
$editor = Join-Path $UE 'Binaries\Win64\UnrealEditor.exe'
$project = Join-Path $ProjectRoot 'LifePlay.uproject'
if ( $Which -eq 'sandbox' ) {
# Le .bat quote l'argument -ini. Start-Process le coupe sur les deux-points.
& ( Join-Path $ProjectRoot 'Tools\debug\LifePlay_interaction_sandbox.bat' )
}
else {
Start-Process -FilePath $editor -ArgumentList @ ( $project , '-game' , '-windowed' , '-ResX=1600' , '-ResY=900' ) -WorkingDirectory $ProjectRoot | Out-Null
}
Write-Host " Ouvert : $Which "
}
# 1. Controles. Cuisson seulement : -Target -Open peut partir sans mod.
if ( $Open -and -not $Target ) { throw " -Open demande -Target sandbox ou -Target game " }
if ( $Target -and $Target -notin 'sandbox' , 'game' ) { throw " -Target vaut sandbox ou game (recu : $Target ) " }
$bCook = -not $RetargetOnly -and -not $Open
if ( $bCook -and -not $Name ) { throw " Preciser -Name du mod " }
if ( -not $bCook -and -not $Target -and -not $Retarget ) { throw " Preciser -Name du mod, ou -Target sandbox|game " }
if ( $bCook ) {
2026-10-03 07:06:48 +00:00
if ( -not $Version ) {
$line = Select-String -Path ( Join-Path $Root 'Config\DefaultGame.ini' ) -Pattern '^ProjectVersion=(.+)$' | Select-Object -First 1
if ( -not $line ) { throw 'ProjectVersion absent de Config\DefaultGame.ini : preciser -Version' }
$Version = $line . Matches [ 0 ] . Groups [ 1 ] . Value . Trim ( )
}
$src = Join-Path $Root " ModSources\ $Name "
$lpmod = Join-Path $src " Module\ $Name .lpmod "
if ( -not ( Test-Path ( Join-Path $src " $Name .uplugin " ) ) ) { throw " ModSources\ $Name \ $Name .uplugin introuvable " }
if ( -not ( Test-Path $lpmod ) ) { throw " ModSources\ $Name \Module\ $Name .lpmod introuvable " }
$id = ( Select-String -Path $lpmod -Pattern '^MODULE_UNIQUEID:\s*(.+)$' | Select-Object -First 1 ) . Matches [ 0 ] . Groups [ 1 ] . Value . Trim ( )
if ( $id -ne $Name ) { throw " MODULE_UNIQUEID ( $id ) different du nom du mod ( $Name ) " }
$title = ( Select-String -Path $lpmod -Pattern '^MODULE_NAME:\s*(.+)$' | Select-Object -First 1 ) . Matches [ 0 ] . Groups [ 1 ] . Value . Trim ( )
H3d : metadonnees des mods, avertissement de version, paquet lpr_hair_pocolov (12 coiffures)
- LPModInfoSubsystem : metadonnees des .lpmod ; fenetre d'avertissement quand un mod actif
a ete construit pour une autre version du jeu (Slate, sans Blueprint).
- NewMod/BuildMod : MODULE_VERSION, _CREATED, _LICENSE, _AUTHOR_LINK, _PRICE, _PACKAGED,
_GAME_VERSION ; ordre des 3 premieres lignes controle ; prix et licences controles.
- lpr_hair_pocolov 1.0 : les 12 coiffures Pocolov CC BY ; lp_Revival et les 6 Pocolov
retires du jeu de base ; mod d'essai LPHairSample retire.
Valide en jeu le 2026-10-03 (CHG-106, CHG-107).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:14:37 +00:00
# Metadonnees (H3d) : le jeu lit les trois premieres lignes par position.
$lpmodLines = @ ( Get-Content $lpmod -Encoding utf8 )
$expected = 'MODULE_UNIQUEID:' , 'MODULE_NAME:' , 'MODULE_AUTHOR:'
for ( $i = 0 ; $i -lt 3 ; $i + + ) {
if ( $lpmodLines . Count -le $i -or -not $lpmodLines [ $i ] . StartsWith ( $expected [ $i ] ) ) { throw " $Name .lpmod : la ligne $( $i + 1 ) doit commencer par $( $expected [ $i ] ) (le jeu lit ces lignes par position) " }
}
function Get-LpmodValue([string ] $Key ) {
$m = $lpmodLines | Select-String -Pattern " ^ $( [ regex ] :: Escape ( $Key ) ) :\s*(.*) $ " | Select-Object -First 1
if ( $m ) { $m . Matches [ 0 ] . Groups [ 1 ] . Value . Trim ( ) } else { '' }
}
$modVersion = Get-LpmodValue 'MODULE_VERSION'
# Mod payant : aucune licence d'asset non commerciale ou de magasin dans les credits.
$price = Get-LpmodValue 'MODULE_PRICE'
if ( $price -and $price -notmatch '^(free|gratuit|0+([.,]0+)?)$' ) {
$blocking = Select-String -Path ( Join-Path $src 'Module\CREDITS.txt' ) -Pattern '\bNC\b|NonCommercial|Non-Commercial|Standard License|Personal' -ErrorAction SilentlyContinue
if ( $blocking ) { throw " MODULE_PRICE ' $price ' : licence(s) incompatible(s) avec la vente dans CREDITS.txt ( $( $blocking [ 0 ] . Line . Trim ( ) ) ) " }
}
2026-10-03 07:06:48 +00:00
$release = Join-Path $Root " Releases\ $Version \Windows "
if ( -not ( Test-Path ( Join-Path $release 'AssetRegistry.bin' ) ) ) { throw " Version de reference absente : $release (build de publication avec -createreleaseversion= $Version ) " }
$stray = Get-StrayMods $Root
if ( $stray ) { throw " Autres plugins dans Mods\ ( $( $stray -join ', ' ) ) : les remettre dans ModSources avant de cuire " }
2026-10-06 19:10:53 +00:00
}
# 1b. Conversion, puis redirecteur sandbox ou game.
if ( $RetargetOnly -and -not $Retarget ) { $Retarget = 'bake' }
if ( $Retarget -and $Retarget -notin 'bake' , 'rigs' ) { throw " -Retarget vaut bake ou rigs (recu : $Retarget ) " }
if ( $Target ) { Write-AnimTarget $Root $Target }
if ( $Retarget ) { Invoke-AnimToGenesis $Root $Retarget }
if ( $Open ) {
Open-AnimTarget $Root $Target
exit 0
}
if ( -not $bCook ) {
Write-Host " Cuisson non lancee "
exit 0
}
2026-10-03 07:06:48 +00:00
# 2. Cuisson DLC.
$logDir = Join-Path $Root 'Saved\Logs\portage\mods'
New-Item -ItemType Directory -Force $logDir | Out-Null
$log = Join-Path $logDir ( " {0}_{1}.log " -f $Name , ( Get-Date -Format 'yyyyMMdd_HHmmss' ) )
$mounted = Mount-ModSource $Root $Name
try {
$uplugin = Join-Path $mounted " $Name .uplugin "
# Commande ecrite dans un .cmd a cote du journal (guillemets intacts, commande exacte conservee).
$batch = [ IO.Path ] :: ChangeExtension ( $log , '.cmd' )
$cmd = " @call `" $UE \Build\BatchFiles\RunUAT.bat `" BuildCookRun -project= `" $Root \LifePlay.uproject `" -platform=Win64 -clientconfig=Shipping -cook -stage -pak -iostore -compressed -dlcname= `" $uplugin `" -basedonreleaseversion= $Version -nodebuginfo -unattended -utf8output -nop4 > `" $log `" 2>&1 "
[ IO.File ] :: WriteAllText ( $batch , $cmd + " `r `n @exit /b %ERRORLEVEL% `r `n " , ( New-Object Text . UTF8Encoding $false ) )
Write-Host " Cuisson DLC de $Name contre $Version (journal : $log ) "
& cmd / c $batch
$exit = $LASTEXITCODE
$cooked = Select-String -Path $log -Pattern 'Packages Cooked: (\d+)' | Select-Object -Last 1
if ( $exit -ne 0 -or -not $cooked ) { throw " Cuisson en echec (code $exit ) : voir $log " }
Write-Host " $( $cooked . Matches [ 0 ] . Groups [ 1 ] . Value ) paquet(s) cuit(s) "
$paks = Join-Path $mounted " Saved\StagedBuilds\Windows\LifePlay\Mods\ $Name \Content\Paks\Windows "
$files = @ ( Get-ChildItem $paks -File | Where-Object { $_ . LastWriteTime -gt ( Get-Item $log ) . CreationTime } )
if ( $files . Count -lt 3 ) { throw " Paquets du mod absents ou anciens dans $paks " }
# 3. Contenu du conteneur : rien hors de Mods/<Nom>/.
$utoc = ( $files | Where-Object Extension -eq '.utoc' | Select-Object -First 1 ) . FullName
$entries = @ ( & " $UE \Binaries\Win64\UnrealPak.exe " $utoc -List 2 > & 1 | ForEach-Object { " $_ " } | Where-Object { $_ -match '"\.\./\.\./\.\./' } | ForEach-Object { ( $_ -split '"' ) [ 1 ] } )
$outside = @ ( $entries | Where-Object { $_ -notlike " ../../../LifePlay/Mods/ $Name /* " } )
if ( -not $entries -or $outside ) { throw " Conteneur : $( $entries . Count ) entree(s), $( $outside . Count ) hors de Mods/ $Name ( $( $outside | Select-Object -First 3 ) ) " }
Write-Host " conteneur : $( $entries . Count ) fichier(s), tous sous Mods/ $Name / "
# 4. Assemblage.
New-Item -ItemType Directory -Force $Out | Out-Null
H3d : metadonnees des mods, avertissement de version, paquet lpr_hair_pocolov (12 coiffures)
- LPModInfoSubsystem : metadonnees des .lpmod ; fenetre d'avertissement quand un mod actif
a ete construit pour une autre version du jeu (Slate, sans Blueprint).
- NewMod/BuildMod : MODULE_VERSION, _CREATED, _LICENSE, _AUTHOR_LINK, _PRICE, _PACKAGED,
_GAME_VERSION ; ordre des 3 premieres lignes controle ; prix et licences controles.
- lpr_hair_pocolov 1.0 : les 12 coiffures Pocolov CC BY ; lp_Revival et les 6 Pocolov
retires du jeu de base ; mod d'essai LPHairSample retire.
Valide en jeu le 2026-10-03 (CHG-106, CHG-107).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:14:37 +00:00
$base = if ( $modVersion ) { " $Name - $modVersion -for-LifePlay- $Version " } else { " $Name -for-LifePlay- $Version " }
2026-10-03 07:06:48 +00:00
$stage = Join-Path $Out $base
$zip = " $stage .zip "
$stamp = Get-Date -Format 'yyyyMMdd_HHmmss'
if ( Test-Path $stage ) { Move-Item $stage " $stage .precedent_ $stamp " }
if ( Test-Path $zip ) { Move-Item $zip " $stage .precedent_ $stamp .zip " }
$modDir = Join-Path $stage " LifePlay\Mods\ $Name "
New-Item -ItemType Directory -Force ( Join-Path $modDir 'Content\Paks\Windows' ) | Out-Null
Copy-Item $uplugin $modDir
$files | Copy-Item -Destination ( Join-Path $modDir 'Content\Paks\Windows' )
Copy-Item ( Join-Path $mounted 'Module' ) ( Join-Path $stage " LifePlay\Content\Modules\ $Name " ) -Recurse
H3d : metadonnees des mods, avertissement de version, paquet lpr_hair_pocolov (12 coiffures)
- LPModInfoSubsystem : metadonnees des .lpmod ; fenetre d'avertissement quand un mod actif
a ete construit pour une autre version du jeu (Slate, sans Blueprint).
- NewMod/BuildMod : MODULE_VERSION, _CREATED, _LICENSE, _AUTHOR_LINK, _PRICE, _PACKAGED,
_GAME_VERSION ; ordre des 3 premieres lignes controle ; prix et licences controles.
- lpr_hair_pocolov 1.0 : les 12 coiffures Pocolov CC BY ; lp_Revival et les 6 Pocolov
retires du jeu de base ; mod d'essai LPHairSample retire.
Valide en jeu le 2026-10-03 (CHG-106, CHG-107).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:14:37 +00:00
# Copie livree (pas les sources) : date de paquet et version du jeu visee (avertissement en jeu si elle differe).
$stagedLpmod = Join-Path $stage " LifePlay\Content\Modules\ $Name \ $Name .lpmod "
$stamped = @ ( $lpmodLines | Where-Object { $_ -notmatch '^MODULE_(PACKAGED|GAME_VERSION):' } ) + " MODULE_PACKAGED: $( Get-Date -Format 'yyyy-MM-dd' ) " + " MODULE_GAME_VERSION: $Version "
[ IO.File ] :: WriteAllText ( $stagedLpmod , ( $stamped -join " `r `n " ) + " `r `n " , ( New-Object Text . UTF8Encoding $false ) )
# Descripteur livre : version du mod et lien de l'auteur dans les champs natifs du plugin.
$stagedPlugin = Join-Path $modDir " $Name .uplugin "
$desc = Get-Content $stagedPlugin -Raw | ConvertFrom-Json
if ( $modVersion ) { $desc . VersionName = $modVersion }
$authorLink = Get-LpmodValue 'MODULE_AUTHOR_LINK'
if ( $authorLink ) { $desc | Add-Member CreatedByURL $authorLink -Force }
[ IO.File ] :: WriteAllText ( $stagedPlugin , ( $desc | ConvertTo-Json ) + " `r `n " , ( New-Object Text . UTF8Encoding $false ) )
2026-10-03 07:06:48 +00:00
$readme = @ (
H3d : metadonnees des mods, avertissement de version, paquet lpr_hair_pocolov (12 coiffures)
- LPModInfoSubsystem : metadonnees des .lpmod ; fenetre d'avertissement quand un mod actif
a ete construit pour une autre version du jeu (Slate, sans Blueprint).
- NewMod/BuildMod : MODULE_VERSION, _CREATED, _LICENSE, _AUTHOR_LINK, _PRICE, _PACKAGED,
_GAME_VERSION ; ordre des 3 premieres lignes controle ; prix et licences controles.
- lpr_hair_pocolov 1.0 : les 12 coiffures Pocolov CC BY ; lp_Revival et les 6 Pocolov
retires du jeu de base ; mod d'essai LPHairSample retire.
Valide en jeu le 2026-10-03 (CHG-106, CHG-107).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:14:37 +00:00
" $title ( $Name ) $( if ( $modVersion ) { " $modVersion " } ) - built for LifePlay $Version " ,
2026-10-03 07:06:48 +00:00
'' ,
'INSTALL: extract this archive into the game folder (the one containing LifePlay.exe), merging the LifePlay folder.' ,
" Then open `" Mod Manager `" on the title screen and enable the mod. To uninstall, delete LifePlay\Mods\ $Name " ,
" and LifePlay\Content\Modules\ $Name . Credits and licenses: LifePlay\Content\Modules\ $Name \CREDITS.txt. " ,
" This mod contains cooked assets: it is made for LifePlay $Version and may need a rebuild for other versions. " ,
'' ,
'INSTALLATION : extraire cette archive dans le dossier du jeu (celui de LifePlay.exe) en fusionnant le dossier' ,
'LifePlay, puis activer le mod dans "Mod Manager" sur l''ecran titre. Desinstallation : supprimer' ,
" LifePlay\Mods\ $Name et LifePlay\Content\Modules\ $Name . Credits et licences : CREDITS.txt du module. " ,
" Ce mod contient des assets cuits pour LifePlay $Version : une autre version du jeu peut demander de le recuire. " )
[ IO.File ] :: WriteAllText ( ( Join-Path $stage 'README.txt' ) , ( $readme -join " `r `n " ) + " `r `n " , ( New-Object Text . UTF8Encoding $false ) )
}
finally {
Dismount-ModSource $Root $Name
}
Push-Location $stage
try {
& $SevenZip a -tzip $zip '.\*' | Out-Null
& $SevenZip t $zip | Out-Null
if ( $LASTEXITCODE -ne 0 ) { throw " Archive $zip invalide " }
}
finally { Pop-Location }
$staged = @ ( Get-ChildItem $stage -Recurse -File ) . Count
$listed = @ ( & $SevenZip l -slt $zip | Where-Object { $_ -match '^Attributes = [^D]*$' } ) . Count
if ( $staged -ne $listed ) { throw " Archive : $listed fichier(s) pour $staged assembles " }
$hash = ( Get-FileHash $zip -Algorithm SHA256 ) . Hash
Write-Host ( " Archive : {0} ({1:N0} octets, {2} fichiers, SHA-256 {3}) " -f $zip , ( Get-Item $zip ) . Length , $listed , $hash )
2026-10-03 09:19:44 +00:00
# Index de diffusion (Tools/SplitModules.ps1, mode per_module) : ligne du mod ajoutee ou remplacee.
$index = Join-Path $Out 'INDEX.txt'
if ( Test-Path $index ) {
$entry = '{0} ({1}) : {2}, {3:N0} bytes, SHA-256 {4}' -f $title , $Name , ( Split-Path $zip -Leaf ) , ( Get-Item $zip ) . Length , $hash . ToLower ( )
$kept = @ ( Get-Content $index -Encoding utf8 | Where-Object { $_ -notlike " * ( $Name ) : * " } )
[ IO.File ] :: WriteAllText ( $index , ( ( $kept + $entry ) -join " `r `n " ) + " `r `n " , ( New-Object Text . UTF8Encoding $false ) )
Write-Host " Index mis a jour : $index "
}
2026-10-03 07:06:48 +00:00
# 5. Installation dans un jeu de test.
if ( $Install ) {
if ( -not ( Test-Path ( Join-Path $Install 'LifePlay.exe' ) ) ) { throw " $Install ne contient pas LifePlay.exe " }
robocopy ( Join-Path $stage 'LifePlay' ) ( Join-Path $Install 'LifePlay' ) / E / NFL / NDL / NJH / NJS / NP | Out-Null
if ( $LASTEXITCODE -ge 8 ) { throw " Copie vers $Install en echec (robocopy $LASTEXITCODE ) " }
Write-Host " Installe dans $Install "
}
H3d : metadonnees des mods, avertissement de version, paquet lpr_hair_pocolov (12 coiffures)
- LPModInfoSubsystem : metadonnees des .lpmod ; fenetre d'avertissement quand un mod actif
a ete construit pour une autre version du jeu (Slate, sans Blueprint).
- NewMod/BuildMod : MODULE_VERSION, _CREATED, _LICENSE, _AUTHOR_LINK, _PRICE, _PACKAGED,
_GAME_VERSION ; ordre des 3 premieres lignes controle ; prix et licences controles.
- lpr_hair_pocolov 1.0 : les 12 coiffures Pocolov CC BY ; lp_Revival et les 6 Pocolov
retires du jeu de base ; mod d'essai LPHairSample retire.
Valide en jeu le 2026-10-03 (CHG-106, CHG-107).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 08:14:37 +00:00
# Code de sortie : succes (robocopy renvoie 1 a 7 quand il a copie).
exit 0