```python
import bpy, math
from mathutils import Vector
from math import radians

# ---------- reset ----------
bpy.ops.object.select_all(action='SELECT')
bpy.ops.object.delete(use_global=False)
for datablocks in (bpy.data.meshes, bpy.data.curves, bpy.data.materials, bpy.data.cameras, bpy.data.lights):
    pass

# ---------- materials ----------
def mat(name, color, rough=0.5, metallic=0.0, emission=None, transmission=0.0):
    m = bpy.data.materials.new(name)
    m.diffuse_color = (*color, 1)
    m.use_nodes = True
    bs = m.node_tree.nodes.get("Principled BSDF")
    bs.inputs["Base Color"].default_value = (*color, 1)
    bs.inputs["Roughness"].default_value = rough
    bs.inputs["Metallic"].default_value = metallic
    if "Transmission Weight" in bs.inputs:
        bs.inputs["Transmission Weight"].default_value = transmission
    if emission:
        bs.inputs["Emission Color"].default_value = (*emission, 1)
        bs.inputs["Emission Strength"].default_value = 3.0
    return m

dust = mat("sun baked dust", (0.27,0.18,0.10), .95)
dust2 = mat("grit", (0.10,0.065,0.035), 1)
wood = mat("weathered timber", (0.18,0.075,0.028), .82)
wood2 = mat("old porch boards", (0.32,0.13,0.045), .88)
rust = mat("oxidized red metal", (0.25,0.055,0.025), .72, .35)
wallmat = mat("corrugated colony wall", (0.22,0.24,0.22), .72, .42)
walllight = mat("aged wall panel", (0.38,0.36,0.29), .82)
black = mat("rubber polymer", (0.018,0.022,0.025), .38)
leather = mat("dark leather", (0.10,0.027,0.012), .63)
coatmat = mat("dusty duster cloth", (0.16,0.07,0.035), .94)
coatedge = mat("coat leather trim", (0.055,0.018,0.008), .6)
suit = mat("vac suit fabric", (0.08,0.13,0.15), .72)
metal = mat("brushed steel", (0.25,0.29,0.30), .28, .82)
gold = mat("brass fittings", (0.48,0.22,0.045), .3, .75)
glass = mat("helmet glass", (0.035,0.18,0.23), .12, .15, transmission=.35)
skin = mat("sun worn skin", (0.40,0.17,0.09), .7)
skinlight = mat("face highlights", (0.62,0.29,0.15), .62)
red = mat("scarf", (0.36,0.035,0.018), .72)
blueglow = mat("airlock cyan", (0.02,0.22,0.26), .25, .1, emission=(0.02,0.32,0.38))
warmglow = mat("saloon interior glow", (.8,.16,.025), .3, 0, emission=(1,.09,.015))
white = mat("sign lettering", (.8,.47,.12), .45, .2)

# ---------- helpers ----------
def apply_mat(o, m):
    if o.data and hasattr(o.data, "materials"): o.data.materials.append(m)
    return o

def cube(n, loc, scale, material, bevel=0.0, rot=(0,0,0)):
    bpy.ops.mesh.primitive_cube_add(location=loc, rotation=rot)
    o=bpy.context.object; o.name=n; o.dimensions=scale
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
    apply_mat(o,material)
    if bevel:
        mod=o.modifiers.new("soft worn edges","BEVEL"); mod.width=bevel; mod.segments=2
    return o

def uv(n, loc, scale, material):
    bpy.ops.mesh.primitive_uv_sphere_add(segments=24, ring_count=16, location=loc)
    o=bpy.context.object; o.name=n; o.scale=scale
    bpy.ops.object.transform_apply(location=False, rotation=False, scale=True); apply_mat(o,material)
    return o

def cyl(n, a, b, r, material, verts=16):
    a,b=Vector(a),Vector(b); d=b-a
    bpy.ops.mesh.primitive_cylinder_add(vertices=verts, radius=r, depth=d.length, location=(a+b)/2)
    o=bpy.context.object; o.name=n
    o.rotation_mode='QUATERNION'; o.rotation_quaternion=d.to_track_quat('Z','Y')
    apply_mat(o,material); return o

def torus(n, loc, major, minor, material, rot=(0,0,0)):
    bpy.ops.mesh.primitive_torus_add(major_radius=major, minor_radius=minor, major_segments=24, minor_segments=8, location=loc, rotation=rot)
    o=bpy.context.object; o.name=n; apply_mat(o,material); return o

def text_obj(n, text, loc, size, material, extrude=.025):
    cu=bpy.data.curves.new(n,'FONT'); cu.body=text; cu.align_x='CENTER'; cu.align_y='CENTER'
    cu.size=size; cu.extrude=extrude; cu.bevel_depth=.008
    o=bpy.data.objects.new(n,cu); bpy.context.collection.objects.link(o); o.location=loc; o.rotation_euler=(radians(90),0,0); apply_mat(o,material); return o

# ---------- ground ----------
cube("dusty ground", (0,1,0), (13,11,.18), dust, .08)
for i,(x,y,s) in enumerate([(-3,-1,.18),(-2.4,-.4,.12),(2,-1.2,.15),(3.7,.2,.2),(-4,2,.13),(3,2.5,.1)]):
    uv("scattered stone", (x,y,.12), (s,s*.65,s*.45), dust2)
# footprints leading toward door
for i in range(7):
    x=1.1 + .25*(i%2); y=-2.1+i*.58
    uv("boot footprint", (x,y,.105), (.18,.34,.018), dust2)
# scrap
for i,(x,y,z,rz) in enumerate([(3,-.2,.25,.3),(3.5,.8,.18,1.2),(2.7,1.4,.16,.1)]):
    cube("discarded colony scrap",(x,y,z),(.55,.12,.12),rust,.025,(0,rz,.2))

# ---------- saloon exterior ----------
# main back facade and side corner
cube("saloon front wall",(0,2.55,3.35),(7.4,.28,6.7),wallmat,.04)
cube("saloon right return",(3.65,4.0,3.35),(.28,3.1,6.7),walllight,.04)
# lower paneling
for x in [-3.3,-2.2,-1.1,0,1.1,2.2,3.3]:
    cube("vertical timber", (x,2.34,3.2),(.13,.20,6.2),wood,.025)
for z in [1.0,2.25,5.75]:
    cube("horizontal timber",(0,2.29,z),(7.2,.22,.16),wood,.025)
# corner post
cube("heavy corner post",(3.42,2.45,3.25),(.34,.42,6.5),wood2,.04)
# porch floor and roof
cube("covered porch floor",(0,1.55,.35),(7.0,2.2,.22),wood2,.04)
cube("porch roof",(0,1.45,6.55),(7.5,2.5,.26),rust,.05, (radians(-2),0,0))
for x in [-3.0,2.8]:
    cyl("porch support post",(x,.75,.45),(x,.75,6.45),.13,wood2)
    torus("post collar",(x,.75,5.95),.18,.035,gold)
# entrance frame, dark interior
cube("entrance black opening",(1.25,2.12,3.0),(2.25,.12,4.35),black,.03)
cube("door lintel",(1.25,2.00,5.25),(2.7,.28,.3),gold,.04)
for x in [.15,2.35]:
    cube("door jamb",(x,2.0,3.0),(.25,.28,4.5),rust,.03)
# swinging airlock doors, angled
for x,ang in [(0.72,-radians(14)),(1.78,radians(14))]:
    cube("swinging airlock door",(x,1.95,3.0),(1.0,.14,3.55),metal,.05,(0,ang,0))
    cube("door window",(x,1.84,3.65),(.55,.035,1.15),glass,.02,(0,ang,0))
    for z in [1.55,2.95,4.45]:
        cyl("door rib",(x-.35,1.82,z),(x+.35,1.82,z),.035,gold)
# dusty windows
for x in [-2.15,-.65]:
    cube("dusty saloon window",(x,2.08,3.65),(1.05,.06,1.35),glass,.03)
    for xx in [-.18,.18]:
        cube("window crossbar",(x+xx,2.03,3.65),(.045,.06,1.3),rust)
    cube("window sill",(x,2.0,2.92),(1.35,.22,.15),wood2)
# sign backing + letters
cube("large weathered sign",(0,2.02,6.02),(4.5,.22,.92),wood,.05,(radians(-3),0,0))
for x in [-1.9,-.95,0,.95,1.9]:
    cyl("sign rivet",(x,1.88,6.03),(x,1.82,6.03),.055,gold,12)
text_obj("SALOON sign","SALOON",(0,1.84,6.04),.63,white,.035)
# conduits
for x in [-2.9,2.85]:
    cyl("exposed utility conduit",(x,2.05,1.0),(x,2.05,5.55),.055,metal)
    for z in [1.6,3.0,4.4,5.2]: torus("conduit clamp",(x,2.05,z),.09,.018,gold)
cyl("horizontal cable",(2.8,2.05,5.2),(-1.8,2.05,5.2),.045,black)
# warm interior lamps
for x in [.5,2.0]:
    uv("warm light behind door",(x,1.75,4.6),(.16,.05,.16),warmglow)

# ---------- character ----------
# base positions, leaning against wall
# boots / legs, crossed braced pose
cyl("left straight leg",(-1.05,1.55,.62),(-1.05,1.58,2.55),.27,suit)
cyl("left boot",(-1.05,1.48,.18),(-1.05,1.48,.72),.31,black)
cube("left magnetic sole",(-1.05,1.42,.10),(.75,.85,.16),metal,.06)
# bent crossed right leg
cyl("right thigh",(-.62,1.48,2.3),(-.30,1.42,1.35),.29,coatmat)
cyl("right shin",(-.30,1.42,1.35),(-.02,1.34,.58),.23,suit)
cyl("right boot",(-.02,1.28,.17),(.43,1.25,.22),.30,black)
cube("right magnetic sole",(.30,1.20,.10),(.85,.7,.16),metal,.06,(0,0,radians(-8)))
# boot details
for x in [-1.05,.28]:
    torus("boot ankle ring",(x,1.35,.65),.25,.035,gold,rot=(radians(90),0,0))
# torso and coat
body=cube("fitted vac suit torso",(-.78,1.62,3.45),(1.05,.62,1.85),suit,.18,(0,radians(-7),0))
cube("long duster left panel",(-1.12,1.69,2.35),(.48,.42,2.75),coatmat,.10,(0,radians(-5),0))
cube("long duster right panel",(-.47,1.63,2.35),(.42,.4,2.65),coatmat,.10,(0,radians(-5),0))
cube("coat center opening",(-.78,1.28,3.38),(.12,.08,1.55),coatedge,.02)
# coat folds
for x in [-1.28,-1.0,-.62,-.38]:
    cyl("coat seam",(x,1.43,1.45),(x-.03,1.43,3.25),.025,coatedge)
# shoulders
uv("left shoulder",(-1.34,1.63,4.18),(.38,.38,.38),coatmat)
uv("right shoulder",(-.23,1.61,4.18),(.38,.38,.38),coatmat)
# left arm resting toward wall
cyl("left upper arm",(-1.38,1.63,4.08),(-1.18,1.93,3.35),.22,coatmat)
cyl("left forearm",(-1.18,1.93,3.35),(-.95,2.03,2.72),.19,suit)
uv("left sealed glove",(-.94,2.04,2.58),(.22,.19,.25),black)
# right gun arm bent, hand hovering
cyl("right upper arm",(-.18,1.62,4.08),(.18,1.39,3.55),.22,coatmat)
cyl("right forearm",(.18,1.39,3.55),(.38,1.30,3.02),.18,suit)
uv("right open glove",(.40,1.28,2.91),(.22,.15,.2),black)
# individually posed fingers, open a few cm over revolver
for i,(dy,dz,dx) in enumerate([(-.10,.16,-.13),(-.04,.20,-.04),(.04,.19,.05),(.11,.14,.13)]):
    cyl("individual trigger hand finger",(.40+dx*.3,1.22+dy,2.94),(.40+dx,1.20+dy,2.94+dz),.035,black,12)
# neck collar and head
torus("sealed collar ring",(-.78,1.62,4.48),.38,.10,metal)
cyl("neck",(-.78,1.62,4.4),(-.78,1.62,4.72),.20,skin)
uv("readable face",(-.78,1.58,5.08),(.43,.34,.52),skin)
# nose, ears, eyes, mustache
uv("nose",(-.78,1.22,5.10),(.09,.12,.13),skinlight)
for x in [-.94,-.62]:
    uv("eye",(x,1.20,5.22),(.045,.025,.04),black)
cyl("western mustache",(-.91,1.20,4.93),(-.78,1.18,4.97),.035,black,10)
cyl("western mustache",(-.78,1.18,4.97),(-.65,1.20,4.93),.035,black,10)
# helmet dome, transparent and rim
bpy.ops.mesh.primitive_uv_sphere_add(segments=32, ring_count=18, location=(-.78,1.62,5.18))
dome=bpy.context.object; dome.name="transparent helmet dome"; dome.scale=(.58,.48,.72); bpy.ops.object.transform_apply(location=False,rotation=False,scale=True); apply_mat(dome,glass)
torus("helmet lower rim",(-.78,1.62,4.72),.43,.055,metal)
# hat brim and crown
cyl("wide hat brim",(-.78,1.62,5.78),(-.78,1.62,5.88),.88,leather,32)
cyl("hat crown",(-.78,1.62,6.10),(-.78,1.62,5.76),.50,leather,24)
torus("hat band",(-.78,1.62,5.84),.49,.055,gold)
# life support chest hardware
cube("chest life support housing",(-.80,1.22,3.82),(.52,.18,.62),metal,.06)
for x in [-.95,-.78,-.61]:
    cyl("life support gauge",(x,1.10,3.9),(x,1.02,3.9),.055,blueglow,12)
# backpack
cube("backpack unit",(-.78,1.98,3.65),(.72,.28,1.2),black,.1)
cube("backpack metal plate",(-.78,2.14,3.7),(.42,.06,.7),metal,.03)
# hoses
cyl("life support hose",(-.53,1.55,4.0),(-.18,1.48,3.1),.055,blueglow)
cyl("life support hose",(-1.02,1.58,4.0),(-1.35,1.72,3.0),.055,red)
# belt and buckle
torus("gun belt",(-.78,1.6,3.0),.62,.09,leather,rot=(radians(90),0,0))
cube("brass belt buckle",(-.78,1.07,3.03),(.24,.08,.25),gold,.025)
# belt cells
for i in range(5):
    x=-1.55+i*.28
    cube("energy cell loop",(x,1.07,3.06),(.16,.12,.30),gold,.025)
    cyl("energy cell",(x,1.05,3.18),(x,1.05,3.39),.055,blueglow,12)
# holster and revolver on right hip
cube("open leather holster",(.05,1.36,2.75),(.34,.32,.82),leather,.07,(0,radians(-8),0))
cube("holster mouth",(.05,1.17,3.12),(.43,.12,.13),coatedge,.025)
# revolver lifted visible
cyl("energy revolver barrel",(.12,1.12,3.12),(.12,.98,3.95),.09,metal,16)
cyl("revolver cylinder",(.12,1.12,3.18),(.12,1.02,3.45),.18,metal,16)
cube("revolver grip",(.12,1.12,2.87),(.14,.22,.52),leather,.04,(radians(-12),0,0))
torus("revolver cylinder ring",(.12,1.0,3.32),.15,.025,gold,rot=(radians(90),0,0))
# coat shoulder contact patch at wall
cube("wall contact shoulder pad",(-1.25,2.08,4.12),(.42,.12,.55),coatedge,.05)

# ---------- lighting ----------
world=bpy.context.scene.world
world.use_nodes=True
world.node_tree.nodes["Background"].inputs["Color"].default_value=(.012,.022,.04,1)
world.node_tree.nodes["Background"].inputs["Strength"].default_value=.22

def area(name, loc, energy, color, size, target):
    bpy.ops.object.light_add(type='AREA', location=loc)
    l=bpy.context.object; l.name=name; l.data.energy=energy; l.data.color=color; l.data.shape='DISK'; l.data.size=size
    direction=Vector(target)-l.location; l.rotation_euler=direction.to_track_quat('-Z','Y').to_euler(); return l
area("hard low sunset key",(-6,-6,7),1250,(1.0,.30,.10),3.0,(-.5,1.5,2.8))
area("cool lunar fill",(5,-3,6),650,(.12,.28,1.0),5.0,(0,1.8,3))
area("saloon warm spill",(1.2,0.5,4.0),500,(1.0,.12,.025),1.5,(1.2,2.0,3))

# ---------- camera ----------
bpy.ops.object.camera_add(location=(9,-14,6.1))
cam=bpy.context.object; cam.name="presentation camera"; cam.data.lens=58
target=Vector((-.2,1.45,3.25)); cam.rotation_euler=(target-cam.location).to_track_quat('-Z','Y').to_euler()
bpy.context.scene.camera=cam

# ---------- render settings ----------
scene=bpy.context.scene
scene.render.engine='BLENDER_EEVEE_NEXT'
scene.render.resolution_x=1536; scene.render.resolution_y=1536; scene.render.resolution_percentage=100
scene.render.image_settings.file_format='PNG'
scene.render.film_transparent=False
scene.render.filepath='/tmp/space_cowboy_saloon.png'
scene.view_settings.look='AgX - Medium High Contrast'
# ground contact shadows and clean framing
scene.camera.data.dof.use_dof=False
# set units and save
scene.unit_settings.system='METRIC'
bpy.ops.wm.save_as_mainfile(filepath='/tmp/space_cowboy_saloon.blend')
```