Preserve approved pixel tower prototype as development baseline

This commit is contained in:
Codex
2026-09-09 07:51:16 +09:00
commit 423fd6dd2e
915 changed files with 95193 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
class_name PixelTowerArt
extends RefCounted
## Small, code-native room furnishings. Coordinates use the same pixel size as
## Kenney's 16 px sprites. This renderer never changes simulation state.
const INK := Color("34413b")
const WOOD := Color("8e6348")
const WOOD_LIGHT := Color("c39665")
const CREAM := Color("eee5c9")
const TEAL := Color("608d84")
static func tile(canvas: CanvasItem, texture: Texture2D, index: int, target: Rect2, tint := Color.WHITE) -> void:
if texture == null:
return
canvas.draw_texture_rect_region(texture, target, Rect2((index % 12) * 16, (index / 12) * 16, 16, 16), tint)
static func room(canvas: CanvasItem, rect: Rect2, id: StringName, pixel: float, dungeon: Texture2D, town: Texture2D, back: bool, decoration_seed := 0) -> void:
var p := pixel
if id in [&"guest_corridor", &"service_corridor"]:
canvas.draw_rect(rect, Color("c6b999") if not back else Color("a6b6a0"))
canvas.draw_rect(Rect2(rect.position.x,rect.end.y-4*p,rect.size.x,3*p),WOOD_LIGHT)
canvas.draw_rect(Rect2(rect.position.x,rect.end.y-p,rect.size.x,p),INK)
canvas.draw_rect(Rect2(rect.position.x,rect.position.y,rect.size.x,p),WOOD)
if posmod(decoration_seed,3)==0:
window(canvas,Vector2(rect.get_center().x-3*p,rect.position.y+5*p),p)
return
var dark := id in [&"tavern", &"adult_private_room", &"massage_room"]
var wall := Color("d4c7a5") if not back else Color("acb9a5")
if dark:
wall = Color("897487")
canvas.draw_rect(rect, INK)
var inner := rect.grow(-p)
canvas.draw_rect(inner, wall)
# Plaster, dado rail and floor boards read as interiors, even at a distance.
canvas.draw_rect(Rect2(inner.position, Vector2(inner.size.x, p)), Color("f1e9cb"))
var floor_y := rect.end.y - 4.0 * p
canvas.draw_rect(Rect2(Vector2(inner.position.x, floor_y - 4.0 * p), Vector2(inner.size.x, 4.0 * p)), Color("b39b77") if not dark else Color("615563"))
canvas.draw_rect(Rect2(Vector2(inner.position.x, floor_y), Vector2(inner.size.x, 3.0 * p)), WOOD)
canvas.draw_line(Vector2(inner.position.x, floor_y), Vector2(inner.end.x, floor_y), WOOD_LIGHT, p)
for x in range(int(inner.position.x + 2 * p), int(inner.end.x), maxi(int(8 * p), 1)):
canvas.draw_rect(Rect2(x, floor_y + p, 5 * p, p), Color("a57852"))
var mid := rect.get_center().x
var w := rect.size.x
var top := rect.position.y
if id in [&"guest_elevator", &"service_elevator"]:
canvas.draw_rect(inner, Color("455855") if not back else Color("506277"))
canvas.draw_rect(Rect2(mid - p, top + p, 2 * p, rect.size.y - 2 * p), Color("243c3c"))
canvas.draw_rect(Rect2(inner.position.x + 2*p, top + 4*p, inner.size.x - 4*p, inner.size.y - 6*p), Color("84978b"))
canvas.draw_line(Vector2(mid, top+4*p), Vector2(mid, rect.end.y-3*p), INK, p)
canvas.draw_rect(Rect2(mid-2*p, top+2*p, 4*p, p), Color("f1c774"))
return
if id in [&"guest_corridor", &"service_corridor", &"staff_door"]:
if id == &"staff_door":
door(canvas, Vector2(mid-4*p, floor_y-14*p), p, Color("587f7b"))
else:
window(canvas, Vector2(mid-3*p, top+5*p), p)
return
# A window and small wall lamp leave the lower part clear for live actors.
if w >= 28*p:
window(canvas, Vector2(rect.position.x+5*p, top+5*p), p)
window(canvas, Vector2(rect.end.x-12*p, top+5*p), p)
else:
window(canvas, Vector2(mid-3*p, top+4*p), p)
var lamp_x := mid if w > 32*p else rect.end.x-4*p
canvas.draw_rect(Rect2(lamp_x-p, top+3*p, 2*p, 3*p), WOOD)
canvas.draw_rect(Rect2(lamp_x-2*p, top+5*p, 4*p, 2*p), Color("edc97c"))
match id:
&"reception":
counter(canvas, Vector2(mid-9*p, floor_y-6*p), 18*p, p)
canvas.draw_rect(Rect2(mid-6*p, floor_y-8*p, 4*p, 2*p), CREAM)
canvas.draw_rect(Rect2(mid+4*p, floor_y-8*p, 2*p, 2*p), Color("d6ad52"))
&"standard_room", &"adult_private_room":
bed(canvas, Vector2(rect.position.x+2*p, floor_y-9*p), p, Color("709b94") if not dark else Color("a37b91"),13)
if w >= 28*p:
bed(canvas, Vector2(rect.end.x-15*p, floor_y-9*p), p, Color("709b94") if not dark else Color("a37b91"),13)
&"cafeteria", &"tavern":
for x in range(int(rect.position.x+5*p), int(rect.end.x-13*p), int(20*p)):
table(canvas, Vector2(x, floor_y-7*p), p)
if rect.size.y > 28*p:
counter(canvas, Vector2(rect.position.x+7*p, top+13*p), w-14*p, p)
for x in range(int(rect.position.x+10*p), int(rect.end.x-8*p), int(12*p)):
canvas.draw_rect(Rect2(x, top+11*p, 3*p, 2*p), CREAM)
&"public_bath":
canvas.draw_rect(Rect2(rect.position.x+4*p, floor_y-7*p, w-8*p, 7*p), CREAM)
canvas.draw_rect(Rect2(rect.position.x+6*p, floor_y-6*p, w-12*p, 4*p), Color("72b6b8"))
canvas.draw_line(Vector2(rect.position.x+8*p,floor_y-5*p), Vector2(rect.end.x-10*p,floor_y-5*p), Color("c1e0d4"), p)
&"kitchen":
counter(canvas, Vector2(rect.position.x+3*p, floor_y-7*p), w-6*p, p)
for x in range(int(rect.position.x+5*p), int(rect.end.x-9*p), int(12*p)):
canvas.draw_rect(Rect2(x, floor_y-9*p, 7*p, 2*p), Color("445652"))
canvas.draw_rect(Rect2(x+2*p, floor_y-11*p, 4*p, 3*p), Color("a2b2a3"))
&"warehouse":
for x in range(int(rect.position.x+2*p), int(rect.end.x-12*p), int(13*p)):
tile(canvas, dungeon, 63, Rect2(x, floor_y-12*p, 12*p, 12*p))
&"staff_waiting_room":
table(canvas, Vector2(mid-6*p, floor_y-7*p), p)
tile(canvas, dungeon, 89, Rect2(rect.position.x+2*p, floor_y-12*p, 12*p, 12*p))
&"security_station":
counter(canvas, Vector2(mid-8*p, floor_y-6*p), 16*p, p)
tile(canvas, dungeon, 105, Rect2(rect.position.x+3*p, floor_y-17*p, 12*p, 12*p))
&"massage_room":
bed(canvas, Vector2(mid-8*p, floor_y-9*p), p, Color("d3aa8f"))
_:
table(canvas, Vector2(mid-6*p, floor_y-7*p), p)
static func window(canvas: CanvasItem, at: Vector2, p: float) -> void:
canvas.draw_rect(Rect2(at, Vector2(7,9)*p), WOOD)
canvas.draw_rect(Rect2(at+Vector2.ONE*p, Vector2(5,6)*p), Color("9ab9b3"))
canvas.draw_rect(Rect2(at+Vector2(2,1)*p, Vector2(2,2)*p), Color("d5e3d0"))
canvas.draw_rect(Rect2(at+Vector2(3,1)*p, Vector2(1,6)*p), WOOD_LIGHT)
canvas.draw_rect(Rect2(at+Vector2(1,4)*p, Vector2(5,1)*p), WOOD_LIGHT)
canvas.draw_rect(Rect2(at+Vector2(-1,8)*p, Vector2(9,1)*p), WOOD_LIGHT)
static func door(canvas: CanvasItem, at: Vector2, p: float, color: Color) -> void:
canvas.draw_rect(Rect2(at, Vector2(8,14)*p), INK)
canvas.draw_rect(Rect2(at+Vector2.ONE*p, Vector2(6,13)*p), color)
canvas.draw_rect(Rect2(at+Vector2(5,8)*p, Vector2.ONE*p), Color("f2ca81"))
static func bed(canvas: CanvasItem, at: Vector2, p: float, cover: Color, width := 17) -> void:
canvas.draw_rect(Rect2(at+Vector2(0,2)*p, Vector2(width,7)*p), WOOD)
canvas.draw_rect(Rect2(at+Vector2(1,2)*p, Vector2(width-2,4)*p), CREAM)
canvas.draw_rect(Rect2(at+Vector2(5,2)*p, Vector2(width-6,5)*p), cover)
canvas.draw_rect(Rect2(at, Vector2(1,10)*p), WOOD)
canvas.draw_rect(Rect2(at+Vector2(width-1,5)*p, Vector2(1,5)*p), WOOD)
canvas.draw_rect(Rect2(at+Vector2(2,2)*p, Vector2(3,2)*p), Color("fff0d6"))
static func counter(canvas: CanvasItem, at: Vector2, width: float, p: float) -> void:
canvas.draw_rect(Rect2(at, Vector2(width, 6*p)), WOOD)
canvas.draw_rect(Rect2(at, Vector2(width, p)), WOOD_LIGHT)
canvas.draw_rect(Rect2(at+Vector2(2,2)*p, Vector2(width-4*p, 3*p)), Color("a67c53"))
static func table(canvas: CanvasItem, at: Vector2, p: float) -> void:
canvas.draw_rect(Rect2(at+Vector2(2,1)*p, Vector2(9,2)*p), WOOD_LIGHT)
canvas.draw_rect(Rect2(at+Vector2(3,3)*p, Vector2(1,4)*p), WOOD)
canvas.draw_rect(Rect2(at+Vector2(9,3)*p, Vector2(1,4)*p), WOOD)
canvas.draw_rect(Rect2(at+Vector2(0,3)*p, Vector2(2,4)*p), TEAL)
canvas.draw_rect(Rect2(at+Vector2(12,3)*p, Vector2(2,4)*p), TEAL)
canvas.draw_rect(Rect2(at+Vector2(5,0)*p, Vector2(3,1)*p), CREAM)
+1
View File
@@ -0,0 +1 @@
uid://cq3ssdj06ljk0
+141
View File
@@ -0,0 +1,141 @@
extends RefCounted
## Reproducible, disposable viewing scenario. All buildings, employees, inventory,
## and jobs go through Runtime commands; visitors come from production arrivals.
## The normal new-game state and the player's saves are never changed by this file.
var failure := ""
var facility_ids: Dictionary = {}
var _runtime: Variant
var _clock: Variant
var _tree: SceneTree
var _sequence := 0
func prepare(runtime: Variant, clock: Variant, tree: SceneTree) -> bool:
_runtime = runtime
_clock = clock
_tree = tree
_clock.set_paused(true)
var operations: Dictionary = _runtime.enable_production_operations()
if not bool(operations.get(&"ok", false)):
return _fail("Could not enable production operations: %s" % operations)
for row: Array in [
[&"reception", Vector3i(-8, 0, 0)],
[&"cafeteria", Vector3i(2, 0, 0)],
[&"kitchen", Vector3i(2, 0, 1)],
]:
if not _build(row[0], row[1]):
return false
if not await advance(180):
return false
for facility: Dictionary in _runtime.construction_service.get_facilities():
facility_ids[facility[&"definition_id"]] = facility[&"instance_id"]
# Four rooms sit on the completed reception and dining structure. Corridor
# cells are ordinary supported construction; unfinished elevator topology is
# deliberately excluded from this scenario.
for x: int in [-8, -6, 2, 4]:
if not _build(&"standard_room", Vector3i(x, 2, 0)):
return false
for x: int in range(-4, 2):
if not _build(&"guest_corridor", Vector3i(x, 1, 0)):
return false
if not await advance(60):
return false
for x: int in range(-4, 2):
if not _build(&"guest_corridor", Vector3i(x, 2, 0)):
return false
if not await advance(10):
return false
# This walkway follows the upper side of the rooms. Its authoritative Path
# projection allows real guests and workers to move between the three levels.
for x: int in range(-8, 6):
if not _build(&"guest_corridor", Vector3i(x, 3, 0)):
return false
if not await advance(10):
return false
for row: Array in [
[&"staff_akane", facility_ids[&"reception"], Vector3i(-8, 0, 0)],
[&"staff_chika", facility_ids[&"cafeteria"], Vector3i(2, 0, 0)],
[&"staff_iris", facility_ids[&"cafeteria"], Vector3i(4, 0, 0)],
[&"staff_eri", &"", Vector3i(0, 0, 0)],
]:
if not _command(&"hire_staff_candidate", {
&"candidate_id": row[0], &"facility_id": row[1], &"current_cell": row[2],
}):
return false
# A small explicitly seeded pantry is part of the disposable scenario; its
# stacks are real and get consumed by the same service pipeline as gameplay.
var cafeteria: Variant = _runtime.facility_service.get_instance(facility_ids[&"cafeteria"])
if not _command(&"add_inventory_stack", {
&"owner_inventory_id": cafeteria.local_inventory_id,
&"item_id": &"ingredient/basic_meal", &"quantity": 32,
}):
return false
if not await advance(1):
return false
# The cleaner actually accepts, walks to, and completes these queued tasks.
for index: int in 6:
var target := Vector3i(-8 if index % 2 == 0 else 5, 3, 0)
if not _command(&"create_staff_task", {
&"task_id": StringName("pixel_view.clean.%02d" % index),
&"task_type": &"clean", &"priority": 70 - index,
&"options": {
&"target_entity_id": facility_ids[&"reception"] if index % 2 == 0 else facility_ids[&"cafeteria"],
&"target_cell": target, &"required_role_tags": [&"cleaner"],
&"task_tags": [&"cleaning"], &"estimated_minutes": 8,
},
}):
return false
return await advance(1)
func advance(minutes: int) -> bool:
for index: int in minutes:
var before: int = _clock.absolute_minute
_clock.advance_minutes(1)
if _clock.absolute_minute != before + 1:
return _fail("Scenario stopped at %s: %s" % [_clock.formatted_time(), _runtime.get_operations_report()])
var report: Dictionary = _runtime.get_operations_report()
if bool(report.get(&"runtime_failure_latched", false)):
return _fail("Operations failed: %s" % report)
if index % 30 == 29:
await _tree.process_frame
return true
func describe() -> Dictionary:
var facilities: Array[Dictionary] = []
for facility: Variant in _runtime.facility_service.get_instances():
facilities.append({
"definition": String(facility.definition_id), "state": int(facility.state),
"origin": [facility.origin.x, facility.origin.y, facility.origin.z],
})
var actors: Array[Dictionary] = []
for guest: Variant in _runtime.guest_service.get_guests():
actors.append({"id": String(guest.guest_id), "kind": "guest", "state": int(guest.state),
"cell": [guest.current_cell.x, guest.current_cell.y, guest.current_cell.z]})
for staff: Variant in _runtime.staff_service.get_staff_members():
actors.append({"id": String(staff.staff_id), "kind": "staff", "state": int(staff.state),
"cell": [staff.current_cell.x, staff.current_cell.y, staff.current_cell.z]})
return {"time": _clock.formatted_time(), "minute": _clock.absolute_minute,
"facilities": facilities, "actors": actors, "pantry_seed_meals": 32}
func _build(definition_id: StringName, cell: Vector3i) -> bool:
return _command(&"build_facility", {&"definition_id": definition_id, &"origin": cell})
func _command(type: StringName, payload: Dictionary) -> bool:
_sequence += 1
var result: Variant = _runtime.submit_command(type, payload, &"player", &"", 50, -1,
StringName("pixel_view.command.%04d" % _sequence))
if not result.is_success():
return _fail("%s failed for %s: %s %s" % [type, payload, result.message, result.details])
return true
func _fail(message: String) -> bool:
failure = message
push_error(message)
return false
+1
View File
@@ -0,0 +1 @@
uid://l58jbi52mqdl
+90
View File
@@ -0,0 +1,90 @@
class_name VisualThemeProfile
extends Resource
## Replaceable visual-only configuration. Gameplay code must not branch on
## this resource, so changing art never changes simulation or save data.
@export_group("Pixel Art")
@export var pixel_dungeon_tiles: Texture2D
@export var pixel_town_tiles: Texture2D
@export_group("World Art")
@export var front_world_background: Texture2D
@export var back_world_background: Texture2D
@export_range(0.0, 1.0, 0.01) var world_art_opacity := 0.94
@export_range(0.0, 1.0, 0.01) var grid_overlay_opacity := 0.24
@export_group("Interface Icons")
@export var facility_icons: Dictionary = {}
@export var navigation_icons: Dictionary = {}
@export var resource_icons: Dictionary = {}
@export var control_icons: Dictionary = {}
@export var guest_actor_markers: Dictionary = {}
@export var staff_actor_markers: Dictionary = {}
@export_group("Interface Skins")
@export var ui_skin_textures: Dictionary = {}
@export_group("Interface Palette")
@export var window_background := Color("24170f")
@export var dark_panel := Color("6b3e1d")
@export var dark_panel_alt := Color("513018")
@export var light_panel := Color("f4e3bd")
@export var border_gold := Color("d5a552")
@export var button_normal := Color("75451f")
@export var button_hover := Color("8d5628")
@export var button_pressed := Color("496528")
@export var button_disabled := Color("574334")
@export var text_light := Color("fff0c8")
@export var text_dark := Color("55371e")
@export var success := Color("4f8a45")
@export var warning := Color("d39a2d")
@export var danger := Color("c84632")
func world_background_for_layer(layer: int) -> Texture2D:
return front_world_background if layer == 0 else back_world_background
func facility_icon(definition_id: StringName) -> Texture2D:
return _icon_from(facility_icons, definition_id)
func navigation_icon(mode: StringName) -> Texture2D:
return _icon_from(navigation_icons, mode)
func resource_icon(resource_name: StringName) -> Texture2D:
return _icon_from(resource_icons, resource_name)
func control_icon(control_name: StringName) -> Texture2D:
return _icon_from(control_icons, control_name)
func guest_actor_marker(guest_type_id: StringName) -> Texture2D:
var direct := _icon_from(guest_actor_markers, guest_type_id)
if direct != null:
return direct
var type_text := String(guest_type_id)
for raw_key: Variant in guest_actor_markers:
var class_id := String(raw_key)
if class_id == "default":
continue
if type_text == class_id or type_text.begins_with(class_id + "_"):
return guest_actor_markers[raw_key] as Texture2D
return _icon_from(guest_actor_markers, &"default")
func staff_actor_marker(role_id: StringName) -> Texture2D:
var marker := _icon_from(staff_actor_markers, role_id)
return marker if marker != null else _icon_from(staff_actor_markers, &"default")
func ui_skin(skin_id: StringName) -> Texture2D:
return _icon_from(ui_skin_textures, skin_id)
func _icon_from(collection: Dictionary, key: StringName) -> Texture2D:
var value: Variant = collection.get(key, collection.get(String(key), null))
return value as Texture2D
+1
View File
@@ -0,0 +1 @@
uid://bmcsg0aclydnb