Ë
    ¬“ÒjÒ  ã            	       ó  — d Z ddlZddlZddlZddlZej
                  j                  ej
                  j                  ej
                  j                  ej
                  j                  e«      «      «      «      Z	ej
                  j                  dej
                  j                  e	d«      «       ej
                  j                  dej
                  j                  e	dd«      «       ddlZddlmZmZ dZd„ Zd	„ Zdd
„Zedk(  r‡ eej
                  j                  ej*                  d   «       eej*                  «      dkD  rej*                  d   nd«      Z e ej2                  ed¬«      «        ej4                  ed   rdnd«       yy)a7  Contract check of a spacecraft/v1 or hover/v1 GLB (jobs written by tools/space/make_jobs_space.py) before it becomes a card.
usage: python3 tools/space/check_space.py <job_dir> [<glb>]   -> prints JSON {ok, errors, warnings, facts}; exit 1 on errors.
Errors stop the card (things a game would break on): no Hull, no Paint material, a moving part whose pivot is not on the
part (it would orbit instead of turning), a Gun not under its Turret, a size far from the brief. Everything else is a warning
the card keeps (missing thrusters or lights, merged turrets, budget).é    NÚlibÚtoolsÚvehicles)Úworld_nodesÚ_descz™(Turret_\d+|Gun_\d+|Canopy|Wing_[LR]|Radiator_[LR]|BayDoor_[LR]|Ring(_\d+)?|Radar|Core|Arm_\w+|Drill|Ramp|Hatch|Door_[LR]|Fan_\w+|Pod_\w+|Vane_[LR]|Yoke)c           	      óz  — g }t        j                  d| «      D ]ˆ  }t        j                  d|«      D ]=  \  }}}|t        t        |«      t        |«      dz   «      D �cg c]	  }|› d|› �‘Œ c}z  }Œ? t        j                  dd|«      }|t        j                  d|«      z  }ŒŠ t        t        |«      «      S c c}w )zSnode names the job's 'Extra moving parts' sentence promised (Arm_1..Arm_6 expanded)zExtra moving parts?: (.*?\))\. z!\b([A-Z][a-z]+)_(\d+)\.\.\1_(\d+)é   Ú_z\b([A-Z][a-z]+)_\d+\.\.\1_\d+Ú zS\b(?:[A-Z][a-z]+_(?:[A-Z]{1,2}|\d+)|Ramp|Hatch|Ring|Radar|Core|Drill|Yoke|Canopy)\b)ÚreÚfindallÚrangeÚintÚsubÚsortedÚset)ÚtextÚoutÚsentÚaÚbÚcÚks          úF/home/claude-agent/forge-build/forge-assets/tools/space/check_space.pyÚexpected_movingr      sº   € à
€CÜ—
‘
Ð=¸tÓDò xˆÜ—z‘zÐ"FÈÓMò 	C‰GˆAˆq�!Ø¬¬c°!«f´c¸!³f¸q±jÓ(AÖB 1�q�c˜˜1˜#’JÒBÑB‰Cð	Cä�v‰vÐ6¸¸DÓAˆØŒr�z‰zÐpÐrvÓwÑw‰ð	xô
 ”#�c“(ÓÐùò Cs   ÁB8
c           	      ój   ‡ — ˆ fd„}t         |d«       |d«       |d«       |d«       |d«      ¬«      S )Nc                 ó>   •—  d„ t        j                  | ‰«      «      S )Nc                 ó>   — | rt        | j                  d«      «      S d S )Nr	   )ÚfloatÚgroup)Úms    r   ú<lambda>z.size_asked.<locals>.<lambda>.<locals>.<lambda>   s   € ±1œe A§G¡G¨A£JÓ/€ ¸$€ ó    )r   Úsearch)Úrxr   s    €r   r"   zsize_asked.<locals>.<lambda>   s   ø€ Ò>ÄÇ	Á	È"ÈdÓ@SÓT€ r#   z([\d.]+) m longz%([\d.]+) m (?:wide|wingspan|fin span)z([\d.]+) m highz([\d.]+) m diameterzhovering ([\d.]+) m)ÚlÚwÚhÚdÚhover)Údict)r   Úgs   ` r   Ú
size_askedr-      sA   ø€ ÛT€AÜ‘!Ð&Ó'©1Ð-UÓ+VÑZ[Ð\nÓZoÙÐ*Ó+±1Ð5KÓ3LôNð Nr#   c                 ó$  ‡‡+‡,‡-‡.‡/‡0‡1‡2‡3— t        j                  t        | › d�«      «      }|xs | › d|d   › d�}t        j                  |«      d   Š+t        j
                  |«      }t        ‰+«      Š1t        ‰1«      Š0|j                  d«      dk(  }|j                  d«      }g g cŠ,Š3ˆ+ˆ1fd	„Š/d
‰0vr‰,j                  d«       t        d„ ‰0D «       «      }|s‰3j                  d«       |D ]  } ‰/|«      sŒ‰3j                  |› d�«       Œ! ‰1j                  «       D �]  \  }\  }	}
‰+d   |
   }t        j                  t        |«      r|j                  d«      €Œ>‰+d   |d      d   D �cg c]  }d|d   v sŒ‰+d   |d   d      ‘Œ }}t        d«      D �‡cg c]  Št!        ˆfd„|D «       «      ‘Œ c}Š.t        d«      D �‡cg c]  Št#        ˆfd„|D «       «      ‘Œ c}Š-dt#        d„ t%        ‰-‰.«      D «       «      z  dz   Š2t'        ˆ-ˆ.ˆ2fd„t        d«      D «       «      r�Œ‰,j                  |› d�«       �Œ t        d„ ‰0D «       «      D ]A  }d|d d  z   }|‰0v sŒ‰1|   d!   t)        ‰+‰1|   d!   «      vsŒ+‰,j                  |› d"|› d#�«       ŒC t+        |j                  dd$«      «      }|D �cg c]	  }|‰0vsŒ|‘Œ }}|r#‰3j                  d%d&j-                  |«      › �«       t        d'„ ‰0D «       «      }t        j.                  d(|d)   t        j0                  «      r|s‰3j                  d*«       ‰+j                  d+g «      D �ci c]  }|j                  d)d$«      |“Œ }}d,|vr‰,j                  d-«       nB|d,   j                  d.i «      j                  d/g d0¢«      }|d d g d1¢k(  r‰3j                  d2«       d3D ]  }||vsŒ‰3j                  d4|› �«       Œ |d5v r)d6|j                  d7g «      vrd8|vr‰3j                  d9«       t3        |«      |d:   d;z   kD  r#‰3j                  t3        |«      › d<|d:   › d=�«       |rd>d?gnd@g}|dAv r,t5        ˆ0fdB„|D «       «      s‰3j                  dC|d   › dD�«       dEg|rg ndFdGgz   D ]  }|‰0vsŒ‰3j                  dC|› �«       Œ |dH   \  }}}t7        |dI   «      }ˆ,ˆ3fdJ„} |dK||dL   dMdN«        |dO||dP   dQdR«        |dS||dT   dQdU«       |dV   r* |dW|dL   rt#        ||«      nt#        ||«      |dV   dMdN«       |dX   }|r`|dY   d!   }|dk  r‰3j                  dZ|d[›d\|d]   › d^�«       nµ|d]   r°t9        ||d]   z
  «      d_kD  rœ‰3j                  d`|d[›da|d]   › d^�«       nt        d«      D �cg c]  }|dY   |   |db   |   z   d;z  ‘Œ } }t#        |dH   «      }!t#        dc„ | D «       «      |!z  }"|"ddkD  r-‰3j                  de| D �#cg c]  }#t;        |#d;«      ‘Œ c}#› d=�«       t=        t        j>                  df|dg   «      dh   «      diz  }$|dg   |$djz  kD  r‰3j                  |dg   › dk|dg   › �«       |dl   D �cg c]4  }t#        |j                  dPd«      |j                  dTd«      «      dmkD  sŒ3|‘Œ6 }%}|%r‰3j                  t3        |%«      › dn�«       i }&t@        jB                  jE                  | › do�«      r"	 t        j                  t        | › do�«      «      }&tI        |&tJ        «      r|&j                  dq«      xs g ng }(t3        |(«      d k  r‰3j                  t3        |(«      › dr�«       t        ˆ/fds„‰0D «       «      })|)r2tI        |&tJ        «      r|&j                  dt«      s‰3j                  du«       tK        t;        |d«      t;        |d«      t;        |d«      g|dg   t        |«      t3        |dl   «      t;        |dv   d!«      t;        |dw   dxz  d;«      t3        |«      t3        |«      |)|t3        |(«      t        ˆ+ˆ1fdy„‰0D «       «      ¬z«      }*tK        |d   ‰, ‰,‰3|*¬{«      S c c}w c c}w c c}w c c}w c c}w c c}w c c}#w c c}w # tF        $ r}'‰3j                  dp|'› �«       Y d }'~'�Œ”d }'~'ww xY w)|Nz	/job.jsonú/Úslugz.glbr   Úcontractzhover/v1Úfamilyc                 óB   •— ‰d   ‰|    d      j                  d«      d uS )NÚnodesr	   Úmesh©Úget)ÚnÚdocr4   s    €€r   r"   zcheck.<locals>.<lambda>+   s(   ø€ �S˜‘\ %¨¡(¨1¡+Ñ.×2Ñ2°6Ó:À$ÐF€ r#   ÚHullzmissing node Hullc              3   óN   K  — | ]  }t        j                  d |«      sŒ|–— Œ y­w)zThruster_\d+N©r   Ú	fullmatch©Ú.0r8   s     r   ú	<genexpr>zcheck.<locals>.<genexpr>.   s   è ø€ ÒF�q¤R§\¡\°/À1Õ%E”ÑFùó   ‚%ž%z;no Thruster_1..n empties (games cannot place engine flames)z  has a mesh (should be an empty)r4   r5   ÚmeshesÚ
primitivesÚPOSITIONÚ
attributesÚ	accessorsé   c              3   ó.   •K  — | ]  }|d    ‰   –— Œ y­w)ÚminN© ©r?   r   r   s     €r   r@   zcheck.<locals>.<genexpr>:   s   øè ø€ Ò+ !�!�E‘(˜1•+Ñ+ùó   ƒc              3   ó.   •K  — | ]  }|d    ‰   –— Œ y­w)ÚmaxNrJ   rK   s     €r   r@   zcheck.<locals>.<genexpr>:   s   øè ø€ ÒIcÐZ[È!ÈEÉ(ÐSTÍ+ÑIcùrL   g{®Gáz´?c              3   ó,   K  — | ]  \  }}||z
  –— Œ y ­w©NrJ   )r?   r(   r&   s      r   r@   zcheck.<locals>.<genexpr>;   s   è ø€ Ò7¡4 1 a˜˜Q�Ñ7ùs   ‚gš™™™™™©?c              3   óV   •K  — | ]   }‰|   ‰z
  d cxk  xr ‰|   ‰z   k  nc –— Œ" y­w)r   NrJ   )r?   r   ÚhiÚloÚpads     €€€r   r@   zcheck.<locals>.<genexpr><   s.   øè ø€ ÒE°q�2�a‘5˜3‘; !Ö2 r¨!¡u¨s¡{×2Ð2ÑEùs   ƒ&)z] origin is outside its own geometry (pivot not on the part: it will orbit instead of turning)c              3   óN   K  — | ]  }t        j                  d |«      sŒ|–— Œ y­w)zGun_\d+Nr<   r>   s     r   r@   zcheck.<locals>.<genexpr>>   s   è ø€ ÒD˜!¬¯©°ZÀÕ(C”AÑDùrA   ÚTurret_é   r	   z is not under z- (turret traverse would leave the gun behind)r   z moving parts merged or missing: z, c              3   óN   K  — | ]  }t        j                  d |«      sŒ|–— Œ y­w)z
Turret_\d+Nr<   r>   s     r   r@   zcheck.<locals>.<genexpr>E   s   è ø€ ÒH˜1¬¯©°mÀQÕ)G”QÑHùrA   ÚturretÚnamezQthe brief has turrets but there is no Turret_ node (turrets merged into the hull)Ú	materialsÚPaintz+no material named Paint (recolour contract)ÚpbrMetallicRoughnessÚbaseColorFactor)r	   r	   r	   r	   )r	   r	   r	   zDPaint baseColorFactor is white: default colour baked in the texture?)ÚEnginesÚLampszno material named )ÚfightersÚshipsÚspeedersÚalienÚtagsÚGlasszno material named GlassÚmatsé   z materials (budget ú)ÚSeat_DriverÚ
Seat_RiderÚ
Seat_Pilot)ra   rc   c              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­wrP   rJ   )r?   ÚsÚnamess     €r   r@   zcheck.<locals>.<genexpr>T   s   øè ø€ Ò2LÀ!°1¸´:Ñ2Lùs   ƒzno z emptyÚCamera_ChaseÚLight_Nav_LÚLight_Nav_RÚsize_xyzÚsizec                 ó²   •— |sy t        ||z
  «      |z  }||kD  r‰j                  | › d|d›d|› d�«       y ||kD  r‰j                  | › d|d›d|› d�«       y y )Nú ú.2fz m vs asked z m)ÚabsÚappend)ÚlabelÚgotÚwantÚerr_atÚwarn_atÚoffÚerrÚwarns         €€r   Úcmpzcheck.<locals>.cmp[   sj   ø€ Ù�VÜ�#˜‘*‹o Ñ$ˆØ�Š<˜Ÿ™ u g¨Q¨s°3¨i°|ÀDÀ6ÈÐ$LÕMØ�7Š]˜DŸK™K¨5¨'°°3°s°)¸<ÈÀvÈRÐ(PÕQˆ]r#   Úlengthr&   g333333Ó?gš™™™™™¹?Úwidthr'   gÍÌÌÌÌÌÜ?g333333Ã?Úheightr(   gš™™™™™É?r)   ÚdiameterÚboundsrI   zlowest point y=rw   z m: it should hover (r*   z m)gffffffÖ?z
hovers at z
 m (asked rN   c              3   ó2   K  — | ]  }t        |«      –— Œ y ­wrP   )rx   )r?   Úxs     r   r@   zcheck.<locals>.<genexpr>m   s   è ø€ Ò$˜Q”#�a—&Ñ$ùs   ‚g      Ð?z4origin far from the middle of the ship (bbox centre z(\d+)kÚtriséÿÿÿÿiè  gÍÌÌÌÌÌô?z tris over budget Útexturesi   z( textures above 1024 px (pack caps them)z/nodes.jsonznodes.json unreadable: Úpaintsz paints in nodes.json (asked 6)c              3   ój   •K  — | ]*  }t        j                  t        |«      sŒ ‰|«      sŒ'|–— Œ, y ­wrP   )r   r=   ÚMOVING)r?   r8   r5   s     €r   r@   zcheck.<locals>.<genexpr>{   s$   øè ø€ ÒL˜!¬¯©´V¸QÕ(?ÁDÈÅG”AÑLùs   ƒ3£3¬3Úaxesz,no "axes" in nodes.json for the moving partsÚgpu_mb_decodedÚbytesg    €„.Ac              3   ó^   •K  — | ]$  }‰d    ‰|   d      j                  d«      �Œ!|–— Œ& y­w)r4   r	   r5   Nr6   )r?   r8   r9   r4   s     €€r   r@   zcheck.<locals>.<genexpr>€   s3   øè ø€ Òb a°C¸±LÀÀqÁÈ!ÁÑ4M×4QÑ4QÐRXÓ4YÑ4a¤Ñbùs   ƒ"-¦-)rt   rŠ   r[   rŒ   Úgpu_mbÚmbÚ	thrustersÚturretsÚmovingÚpromisedr�   Úempties)r0   ÚokÚerrorsÚwarningsÚfacts)&ÚjsonÚloadÚopenÚglbÚreadÚmeasurer   r   r7   ry   r   Úitemsr   r=   r�   r   rI   rN   ÚzipÚallr   r   Újoinr$   ÚIÚlenÚanyr-   rx   Úroundr   r   ÚosÚpathÚexistsÚ	ExceptionÚ
isinstancer+   )4ÚjdÚfÚjr!   r*   ÚfamÚthrr8   Únmr
   ÚiÚndÚprÚaccr   r,   Útr™   Úmissingr—   Úmtrg   ÚfacÚneedÚseatÚWÚHÚLr   r‚   r   Úlowr   Úspanr   r‰   ÚtmaxÚbigÚinfoÚer�   r˜   rž   r9   r€   rR   rS   r5   ro   r4   rT   r�   s4                 `                            @@@@@@@@@r   ÚcheckrË   !   sÃ  ÿù€ Ü�	‰	”$˜"˜˜YÐ'Ó(Ó)€AØ	Ò$�ˆt�1�Q�v‘Y�K˜tÐ$€AÜ
�(‰(�1‹+�a‰.€CÜ�‰�A‹€AÜ˜Ó€EÜ�‹J€EØ�E‰E�*Ó Ñ+€EØ
�%‰%�‹/€CØ�B€I€CˆÜF€DØ�UÑØ�
‰
Ð&Ô'Ü
ÑF˜EÔFÓ
F€CÙØ�‰ÐQÔRØò HˆÙ��7�D—K‘K 1 #Ð%EÐ FÕGðHð —k‘k“mó }‰
ˆ‰FˆQ�Ø�‰\˜!‰_ˆÜ�|‰|œF BÔ'¨2¯6©6°&«>Ð+AØØHKÈHÉÐVXÐY_ÑV`ÑHaÐbnÑHoö  SÀ"Ðs}ð  BDð  EQñ  BRò  tRˆs�;Ñ  <Ñ 0°Ñ <Ó=ð  Sˆð  SÜ5:¸1³X×>°ŒcÓ+ sÔ+Õ+Ò>ˆÔmrÐstÓmu×EvÐhiÄcÓIcÐ_bÔIcÕFcÒEvÀØ”SÑ7¬3¨r°2«;Ô7Ó7Ñ7¸$Ñ>ˆÜÕE¼EÀ!»HÔEÖEØ�J‰J˜"˜ÐzÐ{Ö|ð}ô ÑD˜uÔDÓDò ]ˆØ˜˜!˜"˜ÑˆØ�Š:˜% ™( 1™+¬U°3¸¸a¹À¹Ó-DÒDØ�J‰J˜!˜˜N¨1¨#Ð-ZÐ[Õ\ð]ô ˜qŸu™u W¨bÓ1Ó2€HØ"Ö5�Q a¨u¢nŠqÐ5€GÐ5Ù�—‘Ð>¸t¿y¹yÈÓ?QÐ>RÐSÔTÜÑH ÔHÓH€GÜ	‡y�y�˜A˜f™I¤r§t¡tÔ,±WØ�‰ÐgÔhØ-0¯W©W°[À"Ó-EÖF rˆB�F‰F�6˜2Ó Ñ"ÐF€DÐFØ�dÑØ�
‰
Ð@ÕAà�7‰m×ÑÐ 6¸Ó;×?Ñ?Ð@QÒS_Ó`ˆØˆr�ˆ7’iÒ §¡Ð-sÔ!tØ$ò FˆØ�tÒ˜TŸ[™[Ð+=¸d¸VÐ)DÕEðFà
Ð/Ñ/°GÀ1Ç5Á5ÈÐQSÓCTÑ4TÐY`ÐhlÑYlØ�‰Ð-Ô.Ü
ˆ4ƒy�1�V‘9˜q‘=Ò  $§+¡+´°T³°Ð;NÈqÐQWÉyÈkÐYZÐ.[Ô"\Ù,1ˆM˜<Ñ(¸°~€DØ
Ð&Ñ&¬sÓ2LÀtÔ2LÔ/LØ�‰�c˜$˜q™'˜ &Ð)Ô*ØÐ©¡"°MÀ=Ð3QÑRò 2ˆØ�EŠ>˜4Ÿ;™;¨¨Q¨C yÕ1ð2ð �
‰m�G€A€qˆ!Ü�1�V‘9Ó€AõRñ
 ˆ�!�Q�s‘V˜T 4Ô(Ùˆ��A�c‘F˜D $Ô'Ùˆ�!�Q�s‘V˜T 4Ô(Øˆ‚vÙˆJ Q s¢Vœ˜A˜qœ	´°Q¸³¸A¸c¹FÀDÈ$ÔOØ	ˆ(‰€AÙØ�‰h�q‰kˆØ�Š:�t—{‘{ _°S¸°IÐ=RÐSTÐU\ÑS]ÐR^Ð^aÐ#bÕcØˆwŠZœC  a¨¡jÑ 0Ó1°DÒ8¸$¿+¹+È
ÐSVÐWZÐR[Ð[eÐfgÐhoÑfpÐeqÐqtÐFuÕ:vä6;¸A³hÖ?°ˆa�‰h�q‰k˜A˜e™H Q™KÑ'¨1Ó,Ð?ˆÐ?Ü�1�Z‘=Ó!ˆÜÑ$ !Ô$Ó$ tÑ+ˆØ�Š:�t—{‘{Ð%YÐpqÖZrÐklÔ[`ÐabÐdeÕ[fÒZrÐYsÐstÐ#uÔvÜŒr�z‰z˜) Q v¡YÓ/°Ñ3Ó4°tÑ;€DØˆ�y�4˜#‘:Ò˜tŸ{™{¨a°©i¨[Ð8JÈ1ÈVÉ9È+Ð+VÔWØ˜
‘mÖ
P�¤s¨1¯5©5°°a«=¸!¿%¹%ÀÀQ»-Ó'HÈ4Ó'OŠ1Ð
P€CÐ
PÙ
ˆD�K‰Kœ3˜s›8˜*Ð$LÐMÔNØ€DÜ	‡w�w‡~�~˜˜˜KÐ(Ô)ð	7Ü—9‘9œT R D¨Ð"4Ó5Ó6ˆDô *4°D¼$Ô)?ˆT�X‰X�hÓÒ%¡2ÀR€FÜ
ˆ6ƒ{�Q‚˜Ÿ™¤s¨6£{ mÐ3RÐ$SÔTÜÓL˜uÔLÓL€FÙ”z $¬Ô-°$·(±(¸6Ô2BÀTÇ[Á[ÐQô  FAÜ”u˜Q “{¤E¨!¨Q£K´°q¸!³Ð=ÀAÀfÁIÔY_Ð`dÓYeÔpsÐtuð  wAñ  uBó  qCÜ˜aÐ 0Ñ1°1Ó5¼%ÀÀ'Á
ÈSÑ@PÐRSÓ:TÔ`cÐdgÓ`hÔruÐv}Ór~Ø¨¼#¸f»+ÜÔb¨5ÔbÓbôd€Eô �Q�v‘Y s 7°3ÀÈUÔSÐSùòQ SùÚ>ùÒEvùò 6ùò
 GùòF @ùò [sùò Qøô ò 	7Ø�K‰KÐ1°!°Ð5×6Ò6ûð	7úsZ   Å*`?Å7`?ÆaÇa	Ê0	aÊ:aÍaÖa×#a
Ù4a"Úa"Û!a' á'	bá0b
â
bÚ__main__r	   rh   )Úindentr›   rP   )Ú__doc__rŸ   r­   r   Úsysr®   ÚdirnameÚabspathÚ__file__ÚROOTÚinsertr¨   r¢   Úcheck_vehicler   r   r�   r   r-   rË   Ú__name__Úargvrª   ÚrÚprintÚdumpsÚexitrJ   r#   r   ú<module>rÜ      s&  ðñI÷
 × Ø	‡w�w‡��r—w‘w—‘ r§w¡w§¡°r·w±w·±ÀxÓ7PÓ'QÓRÓS€Ø ‡�‡���2—7‘7—<‘<  eÓ,Ô -¨s¯x©x¯©¸qÀ"Ç'Á'Ç,Á,ÈtÐU\Ð^hÓBiÔ/jÛ 
ß ,ð7€òòNó`TðF ˆzÒÙˆb�g‰g�o‰o˜cŸh™h q™kÓ*¹3¸s¿x¹x»=È1Ò;L¨C¯H©H°QªKÐRVÓW€AÙ	ˆ*ˆ$�*‰*�Q˜qÔ
!Ô"Ø€C‡H�H�!�D’'‰Q˜qÕ!ð r#   