Preserve approved pixel tower prototype as development baseline
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
class_name FacilityCatalog
|
||||
extends RefCounted
|
||||
|
||||
## Canonical MVP order from GAME_SPECIFICATION section 7.6.
|
||||
##
|
||||
## DefinitionRegistry deliberately sorts its query results by stable ID. This
|
||||
## list instead preserves the design-table order for tools that present the
|
||||
## baseline catalog to designers or players.
|
||||
const BASELINE_FACILITY_IDS: Array[StringName] = [
|
||||
&"reception",
|
||||
&"standard_room",
|
||||
&"cafeteria",
|
||||
&"kitchen",
|
||||
&"public_bath",
|
||||
&"warehouse",
|
||||
&"staff_waiting_room",
|
||||
&"security_station",
|
||||
&"tavern",
|
||||
&"massage_room",
|
||||
&"guest_elevator",
|
||||
&"service_elevator",
|
||||
&"staff_door",
|
||||
&"guest_corridor",
|
||||
&"service_corridor",
|
||||
&"adult_private_room",
|
||||
]
|
||||
|
||||
const FACILITY_PATHS: Array[String] = [
|
||||
"res://data/facilities/reception.tres",
|
||||
"res://data/facilities/standard_room.tres",
|
||||
"res://data/facilities/cafeteria.tres",
|
||||
"res://data/facilities/kitchen.tres",
|
||||
"res://data/facilities/public_bath.tres",
|
||||
"res://data/facilities/warehouse.tres",
|
||||
"res://data/facilities/staff_waiting_room.tres",
|
||||
"res://data/facilities/security_station.tres",
|
||||
"res://data/facilities/tavern.tres",
|
||||
"res://data/facilities/massage_room.tres",
|
||||
"res://data/facilities/guest_elevator.tres",
|
||||
"res://data/facilities/service_elevator.tres",
|
||||
"res://data/facilities/staff_door.tres",
|
||||
"res://data/facilities/guest_corridor.tres",
|
||||
"res://data/facilities/service_corridor.tres",
|
||||
"res://data/facilities/adult_private_room.tres",
|
||||
]
|
||||
|
||||
## Optional post-MVP expansion definitions. They live below a subdirectory so
|
||||
## DefinitionRegistry's baseline directory scan remains exactly compatible with
|
||||
## existing saves and the sixteen-facility acceptance fixture.
|
||||
const EXPANSION_FACILITY_IDS: Array[StringName] = [
|
||||
&"premium_room",
|
||||
&"tea_lounge",
|
||||
&"general_store",
|
||||
&"clinic",
|
||||
&"guild_counter",
|
||||
&"underground_guest_room",
|
||||
&"private_bath",
|
||||
&"secret_tavern",
|
||||
&"black_market",
|
||||
&"vip_private_salon",
|
||||
&"laundry",
|
||||
&"cold_storage",
|
||||
&"mana_control_room",
|
||||
&"maintenance_workshop",
|
||||
&"staff_dormitory",
|
||||
&"waste_processing",
|
||||
]
|
||||
|
||||
const EXPANSION_FACILITY_PATHS: Array[String] = [
|
||||
"res://data/facilities/expansion/premium_room.tres",
|
||||
"res://data/facilities/expansion/tea_lounge.tres",
|
||||
"res://data/facilities/expansion/general_store.tres",
|
||||
"res://data/facilities/expansion/clinic.tres",
|
||||
"res://data/facilities/expansion/guild_counter.tres",
|
||||
"res://data/facilities/expansion/underground_guest_room.tres",
|
||||
"res://data/facilities/expansion/private_bath.tres",
|
||||
"res://data/facilities/expansion/secret_tavern.tres",
|
||||
"res://data/facilities/expansion/black_market.tres",
|
||||
"res://data/facilities/expansion/vip_private_salon.tres",
|
||||
"res://data/facilities/expansion/laundry.tres",
|
||||
"res://data/facilities/expansion/cold_storage.tres",
|
||||
"res://data/facilities/expansion/mana_control_room.tres",
|
||||
"res://data/facilities/expansion/maintenance_workshop.tres",
|
||||
"res://data/facilities/expansion/staff_dormitory.tres",
|
||||
"res://data/facilities/expansion/waste_processing.tres",
|
||||
]
|
||||
|
||||
static func load_all() -> Array[FacilityDefinition]:
|
||||
var result: Array[FacilityDefinition] = []
|
||||
for path in FACILITY_PATHS:
|
||||
var definition := load(path) as FacilityDefinition
|
||||
if definition != null:
|
||||
result.append(definition)
|
||||
return result
|
||||
|
||||
static func load_expansion() -> Array[FacilityDefinition]:
|
||||
var result: Array[FacilityDefinition] = []
|
||||
for path in EXPANSION_FACILITY_PATHS:
|
||||
var definition := load(path) as FacilityDefinition
|
||||
if definition != null:
|
||||
result.append(definition)
|
||||
return result
|
||||
|
||||
static func load_available(include_expansion := false) -> Array[FacilityDefinition]:
|
||||
var result := load_all()
|
||||
if include_expansion:
|
||||
result.append_array(load_expansion())
|
||||
return result
|
||||
|
||||
static func by_id(facility_id: StringName) -> FacilityDefinition:
|
||||
for definition in load_available(true):
|
||||
if definition.id == facility_id:
|
||||
return definition
|
||||
return null
|
||||
@@ -0,0 +1 @@
|
||||
uid://c61tnpw8yjyx7
|
||||
@@ -0,0 +1,143 @@
|
||||
class_name FacilityDefinition
|
||||
extends Resource
|
||||
|
||||
const LEGACY_SCHEMA_VERSION := 1
|
||||
const DETAILED_SCHEMA_VERSION := 2
|
||||
|
||||
enum MapZone {
|
||||
ANY,
|
||||
ABOVE_GROUND,
|
||||
BOUNDARY,
|
||||
UNDERGROUND,
|
||||
}
|
||||
|
||||
enum ViewLayer {
|
||||
FRONT,
|
||||
BACK,
|
||||
}
|
||||
|
||||
@export_group("Identity")
|
||||
@export_range(LEGACY_SCHEMA_VERSION, DETAILED_SCHEMA_VERSION, 1) var definition_schema_version := LEGACY_SCHEMA_VERSION
|
||||
@export var id: StringName
|
||||
@export var display_name := ""
|
||||
@export_multiline var description := ""
|
||||
@export var category: StringName = &"uncategorized"
|
||||
@export var service_types: Array[StringName] = []
|
||||
@export var default_service_type: StringName = &""
|
||||
@export var service_profiles: Dictionary = {}
|
||||
|
||||
@export_group("Placement")
|
||||
@export var zone := MapZone.ANY
|
||||
@export var layer := ViewLayer.FRONT
|
||||
@export var footprint := Vector2i.ONE
|
||||
@export var allowed_rotations: Array[int] = [0, 90, 180, 270]
|
||||
@export var guest_access_offsets: Array[Vector2i] = []
|
||||
@export var staff_access_offsets: Array[Vector2i] = []
|
||||
@export var required_connection_tags: Array[StringName] = []
|
||||
@export var preferred_adjacency_tags: Array[StringName] = []
|
||||
@export var forbidden_adjacency_tags: Array[StringName] = []
|
||||
|
||||
@export_group("Construction")
|
||||
@export var construction_cost := {&"gold": 100, &"materials": 1}
|
||||
@export var build_minutes := 60
|
||||
@export_range(1, 3, 1) var weight_per_cell := 1
|
||||
@export var pass_through := false
|
||||
@export_range(0, 100, 1) var demolition_refund_percent := 25
|
||||
@export_range(1, 5, 1) var max_level := 3
|
||||
@export var upgrade_costs: Array[Dictionary] = []
|
||||
|
||||
@export_group("Upkeep and Power")
|
||||
@export var maintenance_gold := 0
|
||||
@export var maintenance_mana := 0
|
||||
@export_range(0, 100, 1) var power_priority := 50
|
||||
|
||||
@export_group("Staffing")
|
||||
@export var staff_min := 0
|
||||
@export var staff_max := 0
|
||||
@export var required_roles: Array[StringName] = []
|
||||
@export var primary_skill: StringName = &"service"
|
||||
|
||||
@export_group("Service")
|
||||
@export var capacity := 0
|
||||
@export var queue_capacity := 0
|
||||
@export var base_service_minutes := 1
|
||||
@export var base_quality := 50
|
||||
@export var base_price := 0
|
||||
@export var inventory_inputs: Dictionary = {}
|
||||
@export var inventory_outputs: Dictionary = {}
|
||||
@export var opening_schedule := {&"morning": true, &"day": true, &"night": true}
|
||||
@export var policy_schema: Dictionary = {}
|
||||
|
||||
@export_group("Environment and Risk")
|
||||
@export_range(0, 100, 1) var cleanliness_decay_per_service := 0
|
||||
@export_range(0, 100, 1) var noise_per_service := 0
|
||||
@export_range(-100, 100, 1) var beauty_effect := 0
|
||||
@export_range(-100, 100, 1) var security_effect := 0
|
||||
@export_range(-100, 100, 1) var concealment_effect := 0
|
||||
@export_range(0, 100, 1) var heat_per_service := 0
|
||||
@export var incident_weights: Dictionary = {}
|
||||
|
||||
@export_group("Audience and Content")
|
||||
@export var allowed_guest_tags: Array[StringName] = []
|
||||
@export var forbidden_guest_tags: Array[StringName] = []
|
||||
@export var adult_content := false
|
||||
@export var content_replacement_id: StringName = &""
|
||||
|
||||
@export_group("Progression")
|
||||
@export_range(1, 7, 1) var unlock_rank := 1
|
||||
@export var research_requirements: Array[StringName] = []
|
||||
|
||||
@export_group("Analytics")
|
||||
@export var statistics_keys: Array[StringName] = []
|
||||
|
||||
@export_group("Classification")
|
||||
@export var tags: Array[StringName] = []
|
||||
|
||||
func is_valid() -> bool:
|
||||
return not id.is_empty() \
|
||||
and not display_name.is_empty() \
|
||||
and footprint.x > 0 \
|
||||
and footprint.y > 0 \
|
||||
and build_minutes > 0 \
|
||||
and weight_per_cell in [1, 2, 3] \
|
||||
and not allowed_rotations.is_empty() \
|
||||
and maintenance_gold >= 0 \
|
||||
and maintenance_mana >= 0 \
|
||||
and staff_min >= 0 \
|
||||
and staff_max >= staff_min \
|
||||
and capacity >= 0 \
|
||||
and queue_capacity >= 0 \
|
||||
and base_service_minutes > 0 \
|
||||
and base_price >= 0 \
|
||||
and detailed_profile_is_valid()
|
||||
|
||||
|
||||
func has_detailed_profile() -> bool:
|
||||
return definition_schema_version == DETAILED_SCHEMA_VERSION
|
||||
|
||||
|
||||
func detailed_profile_is_valid() -> bool:
|
||||
if definition_schema_version == LEGACY_SCHEMA_VERSION:
|
||||
return true
|
||||
if definition_schema_version != DETAILED_SCHEMA_VERSION:
|
||||
return false
|
||||
if category.is_empty() or service_types.is_empty() or default_service_type.is_empty():
|
||||
return false
|
||||
if not service_types.has(default_service_type) or not service_profiles.has(default_service_type):
|
||||
return false
|
||||
if guest_access_offsets.is_empty() and staff_access_offsets.is_empty() and not pass_through:
|
||||
return false
|
||||
if demolition_refund_percent < 0 or demolition_refund_percent > 100:
|
||||
return false
|
||||
if max_level < 1 or max_level > 5 or upgrade_costs.size() != max_level - 1:
|
||||
return false
|
||||
if unlock_rank < 1 or unlock_rank > 7:
|
||||
return false
|
||||
for time_band: StringName in [&"morning", &"day", &"night"]:
|
||||
if not opening_schedule.has(time_band) or not opening_schedule[time_band] is bool:
|
||||
return false
|
||||
if opening_schedule.size() != 3:
|
||||
return false
|
||||
if adult_content and (not tags.has(&"adult_only") or content_replacement_id.is_empty()):
|
||||
return false
|
||||
return true
|
||||
@@ -0,0 +1 @@
|
||||
uid://cpmiimqx2vb58
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
uid://nw4dttch8rt4
|
||||
Reference in New Issue
Block a user