mirror of
https://github.com/cliffe/BreakEscape.git
synced 2026-02-20 13:50:46 +00:00
- Deleted unused character images: woman_in_science_lab_coat.png and woman_with_black_long_hair_bow_in_hair_long_sleeve_(1).png. - Added new padlock icon asset for UI. - Introduced player_preferences.css for styling the player preferences configuration screen. - Updated game.js to load new character atlases with simplified filenames. - Enhanced player.js to create custom idle animations for characters. - Implemented sprite-grid.js for sprite selection UI, including a preview feature. - Updated database schema to include break_escape_player_preferences table for storing player settings. - Modified convert_pixellab_to_spritesheet.py to map character names to simplified filenames and extract headshots from character images.
102 lines
3.0 KiB
Ruby
102 lines
3.0 KiB
Ruby
module BreakEscape
|
|
class PlayerPreference < ApplicationRecord
|
|
self.table_name = 'break_escape_player_preferences'
|
|
|
|
# Associations
|
|
belongs_to :player, polymorphic: true
|
|
|
|
# Constants - Available sprite sheets (must match game.js preload and assets on disk)
|
|
AVAILABLE_SPRITES = %w[
|
|
female_hacker_hood
|
|
female_hacker_hood_down
|
|
female_office_worker
|
|
female_security_guard
|
|
female_telecom
|
|
female_spy
|
|
female_scientist
|
|
woman_bow
|
|
male_hacker_hood
|
|
male_hacker_hood_down
|
|
male_office_worker
|
|
male_security_guard
|
|
male_telecom
|
|
male_spy
|
|
male_scientist
|
|
male_nerd
|
|
].freeze
|
|
|
|
# Mapping from UI key to game texture key (game.js loads these atlas keys)
|
|
# woman_bow -> woman_blowse (filename typo in assets); others are identity
|
|
SPRITE_FILE_MAPPING = {
|
|
'woman_bow' => 'woman_blowse'
|
|
}.freeze
|
|
|
|
# Get the texture key for game injection (must match game.js preload keys)
|
|
def self.sprite_filename(sprite_name)
|
|
SPRITE_FILE_MAPPING[sprite_name] || sprite_name
|
|
end
|
|
|
|
# Validations
|
|
validates :player, presence: true
|
|
validates :selected_sprite, inclusion: { in: AVAILABLE_SPRITES }, allow_nil: true
|
|
validates :in_game_name, presence: true, length: { in: 1..20 }, format: {
|
|
with: /\A[a-zA-Z0-9_ ]+\z/,
|
|
message: 'only allows letters, numbers, spaces, and underscores'
|
|
}
|
|
|
|
# Callbacks
|
|
before_validation :set_defaults, on: :create
|
|
|
|
# Check if selected sprite is valid for a given scenario
|
|
def sprite_valid_for_scenario?(scenario_data)
|
|
# If no sprite selected, invalid (player must choose)
|
|
return false if selected_sprite.blank?
|
|
|
|
# If scenario has no restrictions, any sprite is valid
|
|
return true unless scenario_data['validSprites'].present?
|
|
|
|
valid_sprites = Array(scenario_data['validSprites'])
|
|
|
|
# Check if sprite matches any pattern
|
|
valid_sprites.any? do |pattern|
|
|
sprite_matches_pattern?(selected_sprite, pattern)
|
|
end
|
|
end
|
|
|
|
# Check if player has selected a sprite
|
|
def sprite_selected?
|
|
selected_sprite.present?
|
|
end
|
|
|
|
private
|
|
|
|
def set_defaults
|
|
# Seed in_game_name from player.handle if available
|
|
if in_game_name.blank? && player.respond_to?(:handle) && player.handle.present?
|
|
self.in_game_name = player.handle
|
|
end
|
|
|
|
# Fallback to 'Zero' if still blank
|
|
self.in_game_name = 'Zero' if in_game_name.blank?
|
|
|
|
# NOTE: selected_sprite left NULL - player MUST choose before first game
|
|
end
|
|
|
|
# Pattern matching for sprite validation
|
|
# Supports:
|
|
# - Exact match: "female_hacker"
|
|
# - Wildcard: "female_*" (all female sprites)
|
|
# - Wildcard: "*_hacker" (all hacker sprites)
|
|
# - Wildcard: "*" (all sprites)
|
|
def sprite_matches_pattern?(sprite, pattern)
|
|
return true if pattern == '*'
|
|
|
|
# Convert wildcard pattern to regex
|
|
regex_pattern = Regexp.escape(pattern).gsub('\*', '.*')
|
|
regex = /\A#{regex_pattern}\z/
|
|
|
|
sprite.match?(regex)
|
|
end
|
|
end
|
|
end
|