148 lines
7.8 KiB
PowerShell
148 lines
7.8 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Copie les clips d'animation convertis dans un mod d'assets, renommes a la convention du jeu (lot AP5).
|
|
|
|
.DESCRIPTION
|
|
/Game/Quaternius/UAL/B_<Clip> -> /<Nom>/Anims/Female/qua_<clip>_F ; M_<Clip> -> /<Nom>/Anims/Male/qua_<clip>_M.
|
|
Le jeu appelle Actor.animate(<cle>) et cherche <cle>_F et <cle>_M (cle = ce qui precede le dernier _).
|
|
1. controle bloquant : rapport.json existe et valide = vrai (les a_revoir sont affiches, non bloquants) ;
|
|
2. controles : editeur ferme, aucun autre plugin dans Mods\ ;
|
|
3. table cle / sources / destinations calculee depuis Content\Quaternius\UAL\B_*.uasset et M_*.uasset ;
|
|
4. -DryRun : affiche la table et s'arrete (rien n'est lance ni cree) ;
|
|
5. sinon : cree ModSources\<Nom> s'il manque (jamais d'ecrasement), monte le mod, lance Tools\anim\DeliverAnims.py
|
|
dans UnrealEditor-Cmd (journal Saved\Logs\portage\AP5_DeliverAnims_<horodatage>.log), remet le mod en place,
|
|
controle la sortie (LIVRAISON copies 2n erreurs 0, n fichiers .uasset par sexe). Code de sortie 1 au moindre ecart.
|
|
|
|
.EXAMPLE
|
|
.\Tools\anim\DeliverAnims.ps1 -DryRun
|
|
.\Tools\anim\DeliverAnims.ps1
|
|
#>
|
|
param(
|
|
[string] $Name = 'lpr_anim_quaternius',
|
|
[string] $Source = '/Game/Quaternius/UAL',
|
|
[string] $Prefix = 'qua',
|
|
[string] $Exclude = 'A_TPose',
|
|
[string] $Report = '',
|
|
[switch] $DryRun
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$Root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
|
|
. (Join-Path $Root 'Tools\ModSource.ps1')
|
|
$Editor = 'D:\Game\Launcher\Engine\UE_5.5\Engine\Binaries\Win64\UnrealEditor-Cmd.exe'
|
|
if (-not $Report) { $Report = Join-Path $Root 'Saved\AnimPipeline\quaternius\rapport.json' }
|
|
|
|
function New-IfMissing([string] $Path, [string] $Text) {
|
|
if (Test-Path $Path) { return }
|
|
New-Item -ItemType Directory -Force (Split-Path $Path -Parent) | Out-Null
|
|
[IO.File]::WriteAllText($Path, $Text, (New-Object Text.UTF8Encoding $false))
|
|
Write-Host " cree : $Path"
|
|
}
|
|
|
|
try {
|
|
# 1. Verdict des controles qualite.
|
|
if (-not (Test-Path $Report)) { throw "rapport.json introuvable : $Report" }
|
|
$rapport = Get-Content $Report -Raw -Encoding utf8 | ConvertFrom-Json
|
|
if ($rapport.valide -ne $true) {
|
|
$echecs = @($rapport.echecs) -join '; '
|
|
throw "Rapport non valide ($Report). Echecs : $echecs"
|
|
}
|
|
foreach ($r in @($rapport.a_revoir)) { Write-Warning "a revoir : $r" }
|
|
|
|
# 2. Controles d'environnement.
|
|
$running = @(Get-Process UnrealEditor, UnrealEditor-Cmd -ErrorAction SilentlyContinue)
|
|
if ($running) { throw "UnrealEditor est ouvert (pid $($running[0].Id)) : le fermer avant la livraison" }
|
|
$stray = Get-StrayMods $Root
|
|
if ($stray) { throw "Autres plugins dans Mods\ ($($stray -join ', ')) : les remettre dans ModSources" }
|
|
|
|
# 3. Table des cles.
|
|
if ($Source -notmatch '^/Game/(.+)$') { throw "-Source doit commencer par /Game/ (recu : $Source)" }
|
|
$srcDir = Join-Path $Root ('Content\' + ($Matches[1] -replace '/', '\'))
|
|
if (-not (Test-Path $srcDir)) { throw "Dossier source introuvable : $srcDir" }
|
|
$excluded = @($Exclude -split ',' | Where-Object { $_ })
|
|
$fem = @{}; $mal = @{}
|
|
foreach ($f in Get-ChildItem $srcDir -File -Filter '*.uasset') {
|
|
$base = $f.BaseName
|
|
if ($base -like 'B_*') { $fem[$base.Substring(2)] = $Source.TrimEnd('/') + '/' + $base }
|
|
elseif ($base -like 'M_*') { $mal[$base.Substring(2)] = $Source.TrimEnd('/') + '/' + $base }
|
|
}
|
|
$clips = @(($fem.Keys + $mal.Keys) | Sort-Object -Unique | Where-Object { $_ -notin $excluded })
|
|
$table = @()
|
|
$seen = @{}
|
|
foreach ($clip in $clips) {
|
|
if (-not $fem.ContainsKey($clip)) { throw "Clip $clip : source B_ absente" }
|
|
if (-not $mal.ContainsKey($clip)) { throw "Clip $clip : source M_ absente" }
|
|
$cle = ($Prefix + '_' + $clip).ToLowerInvariant()
|
|
if ($cle -notmatch '^[a-z0-9_]+$') { throw "Cle invalide (caracteres hors [a-z0-9_]) : $cle" }
|
|
if ($seen.ContainsKey($cle)) { throw "Cle en double : $cle ($clip et $($seen[$cle]))" }
|
|
$seen[$cle] = $clip
|
|
$table += [pscustomobject]@{
|
|
Cle = $cle; SourceF = $fem[$clip]; SourceM = $mal[$clip]
|
|
AssetF = "/$Name/Anims/Female/${cle}_F"; AssetM = "/$Name/Anims/Male/${cle}_M"
|
|
}
|
|
}
|
|
if ($table.Count -eq 0) { throw "Aucun clip a livrer dans $srcDir" }
|
|
Write-Host "Table : $($table.Count) cle(s) pour /$Name/ (exclus : $($excluded -join ', '))"
|
|
$table | Select-Object -First 5 | ForEach-Object { Write-Host (" {0} : {1} -> {2} ; {3} -> {4}" -f $_.Cle, $_.SourceF, $_.AssetF, $_.SourceM, $_.AssetM) }
|
|
|
|
if ($DryRun) {
|
|
Write-Host "A blanc : rien lance, rien cree"
|
|
exit 0
|
|
}
|
|
|
|
# 5. Sources du mod (jamais d'ecrasement).
|
|
$paths = Get-ModSourcePaths $Root $Name
|
|
$src = $paths.Source
|
|
$desc = 'Free animation clips from the Quaternius Universal Animation Library (CC0), retargeted for LifePlay'
|
|
$author = 'LifePlay Revival (animations by Quaternius)'
|
|
$uplugin = [ordered]@{
|
|
FileVersion = 3; Version = 1; VersionName = '1.0'; FriendlyName = 'Quaternius Animations'
|
|
Description = $desc; Category = 'LifePlay Mods'; CreatedBy = $author
|
|
CanContainContent = $true; IsBetaVersion = $false; Installed = $false
|
|
CreatedByURL = 'https://quaternius.com'
|
|
}
|
|
New-IfMissing (Join-Path $src "$Name.uplugin") (($uplugin | ConvertTo-Json) + "`r`n")
|
|
$lpmod = @(
|
|
"MODULE_UNIQUEID: $Name", 'MODULE_NAME: Quaternius Animations', "MODULE_AUTHOR: $author",
|
|
'MODULE_LINK: https://github.com/edujiro5972/LifePlay-Revival', "MODULE_DESCRIPTION: $desc",
|
|
'MODULE_REQUIREMENTS: vin_Base', 'MODULE_VERSION: 1.0', "MODULE_CREATED: $(Get-Date -Format 'yyyy-MM-dd')",
|
|
'MODULE_LICENSE: CC0', 'MODULE_AUTHOR_LINK: https://quaternius.com', 'MODULE_PRICE: Free', 'MODULE_DEFAULT_ENABLED: true')
|
|
New-IfMissing (Join-Path $src "Module\$Name.lpmod") (($lpmod -join "`r`n") + "`r`n")
|
|
$credits = "Credits and licenses of the assets in Quaternius Animations (one line per asset: title, author, license, source URL, changes).`r`n" +
|
|
"- Universal Animation Library by Quaternius, CC0 1.0, https://quaternius.com . Changes: retargeted to the Genesis 8 skeletons (Bridget, Michael), renamed qua_<clip>_F / qua_<clip>_M.`r`n"
|
|
New-IfMissing (Join-Path $src 'Module\CREDITS.txt') $credits
|
|
New-Item -ItemType Directory -Force (Join-Path $src 'Content') | Out-Null
|
|
|
|
# Lancement d'Unreal, mod monte.
|
|
$logDir = Join-Path $Root 'Saved\Logs\portage'
|
|
New-Item -ItemType Directory -Force $logDir | Out-Null
|
|
$log = Join-Path $logDir ('AP5_DeliverAnims_{0}.log' -f (Get-Date -Format 'yyyyMMdd-HHmmss'))
|
|
$py = (Join-Path $Root 'Tools\anim\DeliverAnims.py') -replace '\\', '/'
|
|
$mounted = Mount-ModSource $Root $Name
|
|
try {
|
|
$script = "-script=$py mod=$Name src=$Source prefix=$Prefix exclude=$Exclude"
|
|
Write-Host "Livraison dans /$Name/ (journal : $log)"
|
|
& $Editor (Join-Path $Root 'LifePlay.uproject') -run=pythonscript $script -unattended -nopause -nosplash -stdout -FullStdOutLogOutput *> $log
|
|
}
|
|
finally {
|
|
Dismount-ModSource $Root $Name
|
|
}
|
|
|
|
# Controle de sortie.
|
|
$n = $table.Count
|
|
if (-not (Select-String -Path $log -Pattern "LIVRAISON copies $($n * 2) erreurs 0\b" -Quiet)) {
|
|
$last = Select-String -Path $log -Pattern 'LIVRAISON' | Select-Object -Last 3 | ForEach-Object { $_.Line }
|
|
throw "Ligne 'LIVRAISON copies $($n * 2) erreurs 0' absente : $($last -join ' | ') (voir $log)"
|
|
}
|
|
foreach ($sexe in 'Female', 'Male') {
|
|
$dir = Join-Path $src "Content\Anims\$sexe"
|
|
$count = @(Get-ChildItem $dir -Filter '*.uasset' -ErrorAction SilentlyContinue).Count
|
|
if ($count -ne $n) { throw "$dir : $count fichier(s) .uasset pour $n cle(s)" }
|
|
}
|
|
Write-Host "Livraison terminee : $n cle(s), $($n * 2) copies"
|
|
exit 0
|
|
}
|
|
catch {
|
|
Write-Host "ECHEC : $($_.Exception.Message)" -ForegroundColor Red
|
|
exit 1
|
|
}
|