modify scripts

This commit is contained in:
2025-10-17 10:59:23 +08:00
parent 9336ed0d6f
commit 4f782a638e
131 changed files with 79880 additions and 3549 deletions

View File

@ -721,17 +721,27 @@ namespace WordsToolkit.Scripts.Levels.Editor
// Handle selection from LevelHierarchyTreeView
if (selectedItem != null && selectedItem.type == LevelHierarchyItem.ItemType.Level && selectedItem.levelAsset != null)
{
// Try to find any available language data in the level
string foundLanguage = FindAvailableLanguage(selectedItem.levelAsset);
// Use the same language selection logic as the "Open Grid" button
// This preserves the user's currently selected language tab
string languageCode = LevelEditorUtility.GetLanguageCodeForLevel(selectedItem.levelAsset);
if (!string.IsNullOrEmpty(foundLanguage))
if (!string.IsNullOrEmpty(languageCode))
{
SetCurrentLevel(selectedItem.levelAsset, foundLanguage);
SetCurrentLevel(selectedItem.levelAsset, languageCode);
}
else
{
// Set the level anyway but with a default language
SetCurrentLevel(selectedItem.levelAsset, "en");
// Fallback: try to find any available language data in the level
string foundLanguage = FindAvailableLanguage(selectedItem.levelAsset);
if (!string.IsNullOrEmpty(foundLanguage))
{
SetCurrentLevel(selectedItem.levelAsset, foundLanguage);
}
else
{
// Set the level anyway but with a default language
SetCurrentLevel(selectedItem.levelAsset, "en");
}
}
}
else if (selectedItem == null || selectedItem.type != LevelHierarchyItem.ItemType.Level)
@ -767,13 +777,25 @@ namespace WordsToolkit.Scripts.Levels.Editor
Level latestLevel = AssetDatabase.LoadAssetAtPath<Level>(path);
if (latestLevel != null)
{
string foundLanguage = FindAvailableLanguage(latestLevel);
if (!string.IsNullOrEmpty(foundLanguage))
// Use the same language selection logic as the rest of the system
string languageCode = LevelEditorUtility.GetLanguageCodeForLevel(latestLevel);
if (!string.IsNullOrEmpty(languageCode))
{
SetCurrentLevel(latestLevel, foundLanguage);
Debug.Log($"CrosswordGridWindow: Loaded latest level '{latestLevel.name}' with language '{foundLanguage}' from EditorPrefs");
SetCurrentLevel(latestLevel, languageCode);
Debug.Log($"CrosswordGridWindow: Loaded latest level '{latestLevel.name}' with language '{languageCode}' from EditorPrefs");
return;
}
else
{
// Fallback
string foundLanguage = FindAvailableLanguage(latestLevel);
if (!string.IsNullOrEmpty(foundLanguage))
{
SetCurrentLevel(latestLevel, foundLanguage);
Debug.Log($"CrosswordGridWindow: Loaded latest level '{latestLevel.name}' with fallback language '{foundLanguage}' from EditorPrefs");
return;
}
}
}
}
}
@ -784,17 +806,26 @@ namespace WordsToolkit.Scripts.Levels.Editor
Level selectedLevel = Selection.activeObject as Level;
if (selectedLevel != null)
{
// Try to find any available language data in the level
string foundLanguage = FindAvailableLanguage(selectedLevel);
if (!string.IsNullOrEmpty(foundLanguage))
// Use the same language selection logic as the rest of the system
string languageCode = LevelEditorUtility.GetLanguageCodeForLevel(selectedLevel);
if (!string.IsNullOrEmpty(languageCode))
{
SetCurrentLevel(selectedLevel, foundLanguage);
Debug.Log($"CrosswordGridWindow: Loaded level '{selectedLevel.name}' with language '{foundLanguage}' from project selection");
SetCurrentLevel(selectedLevel, languageCode);
Debug.Log($"CrosswordGridWindow: Loaded level '{selectedLevel.name}' with language '{languageCode}' from project selection");
}
else
{
Debug.LogWarning($"Level '{selectedLevel.name}' has no language data available.");
// Fallback
string foundLanguage = FindAvailableLanguage(selectedLevel);
if (!string.IsNullOrEmpty(foundLanguage))
{
SetCurrentLevel(selectedLevel, foundLanguage);
Debug.Log($"CrosswordGridWindow: Loaded level '{selectedLevel.name}' with fallback language '{foundLanguage}' from project selection");
}
else
{
Debug.LogWarning($"Level '{selectedLevel.name}' has no language data available.");
}
}
}
}
@ -823,23 +854,30 @@ namespace WordsToolkit.Scripts.Levels.Editor
private string FindAvailableLanguage(Level level)
{
if (level == null) return null;
// Try common language codes in order of preference
string[] languageCodes = { "en", "eng", "english", "en-US", "en-GB", "es", "fr", "de", "ru", "zh" };
foreach (string code in languageCodes)
if (level == null || level.languages == null || level.languages.Count == 0)
return null;
// First, try to use the currently selected language tab (same as LevelEditorUtility.GetLanguageCodeForLevel)
int selectedTabIndex = EditorPrefs.GetInt("WordsToolkit_SelectedLanguageTab", 0);
if (selectedTabIndex >= 0 && selectedTabIndex < level.languages.Count)
{
var languageData = level.GetLanguageData(code);
if (languageData != null)
var selectedLanguage = level.languages[selectedTabIndex];
if (selectedLanguage != null && !string.IsNullOrEmpty(selectedLanguage.language))
{
return code;
return selectedLanguage.language;
}
}
// Fallback: return the first available language
for (int i = 0; i < level.languages.Count; i++)
{
var languageData = level.languages[i];
if (languageData != null && !string.IsNullOrEmpty(languageData.language))
{
return languageData.language;
}
}
// If no common languages found, this would require reflection or other means
// to get all available languages from the Level object
// For now, we'll return null and let the user manually specify
return null;
}
@ -851,19 +889,28 @@ namespace WordsToolkit.Scripts.Levels.Editor
Level selectedLevel = Selection.activeObject as Level;
if (selectedLevel != null)
{
// Try to find any available language data in the level
string foundLanguage = FindAvailableLanguage(selectedLevel);
if (!string.IsNullOrEmpty(foundLanguage))
// Use the same language selection logic as the rest of the system
string languageCode = LevelEditorUtility.GetLanguageCodeForLevel(selectedLevel);
if (!string.IsNullOrEmpty(languageCode))
{
SetCurrentLevel(selectedLevel, foundLanguage);
Debug.Log($"Loaded level: {selectedLevel.name} with language: {foundLanguage}");
SetCurrentLevel(selectedLevel, languageCode);
Debug.Log($"Loaded level: {selectedLevel.name} with language: {languageCode}");
}
else
{
// Set the level anyway but with a default language
SetCurrentLevel(selectedLevel, "en");
Debug.LogWarning($"Level '{selectedLevel.name}' has no language data. Using default language 'en'.");
// Fallback: try to find any available language data in the level
string foundLanguage = FindAvailableLanguage(selectedLevel);
if (!string.IsNullOrEmpty(foundLanguage))
{
SetCurrentLevel(selectedLevel, foundLanguage);
Debug.Log($"Loaded level: {selectedLevel.name} with fallback language: {foundLanguage}");
}
else
{
// Set the level anyway but with a default language
SetCurrentLevel(selectedLevel, "en");
Debug.LogWarning($"Level '{selectedLevel.name}' has no language data. Using default language 'en'.");
}
}
}
else
@ -1023,148 +1070,142 @@ namespace WordsToolkit.Scripts.Levels.Editor
if (_currentLevel == null) return;
var serializedObject = new SerializedObject(_currentLevel);
// Bind letters field (TextField with label)
var lettersField = paletteRoot.Q<TextField>("letters-field");
if (lettersField != null)
var initialValue = languageData.letters ?? "";
lettersField.RegisterValueChangedCallback(evt =>
{
var initialValue = languageData.letters ?? "";
lettersField.RegisterValueChangedCallback(evt =>
// Don't trigger on initial setup (when previous value is empty and new value matches initial data)
if (evt.newValue != evt.previousValue &&
!(string.IsNullOrEmpty(evt.previousValue) && evt.newValue == initialValue))
{
// Don't trigger on initial setup (when previous value is empty and new value matches initial data)
if (evt.newValue != evt.previousValue &&
!(string.IsNullOrEmpty(evt.previousValue) && evt.newValue == initialValue))
// Record undo before changing letters
if (_currentLevel != null)
{
// Record undo before changing letters
if (_currentLevel != null)
// Ensure grid data is serialized before recording undo
var langData = _currentLevel.GetLanguageData(_currentLanguageCode);
if (langData?.crosswordData != null)
{
// Ensure grid data is serialized before recording undo
var langData = _currentLevel.GetLanguageData(_currentLanguageCode);
if (langData?.crosswordData != null)
{
langData.crosswordData.SerializeGrid();
}
Undo.RecordObject(_currentLevel, "Change Letters");
}
languageData.letters = evt.newValue;
var lettersLength = paletteRoot.Q<IntegerField>("letters-length");
if (lettersLength != null)
{
lettersLength.value = evt.newValue?.Length ?? 0;
langData.crosswordData.SerializeGrid();
}
_currentLevel.letters = evt.newValue?.Length ?? 0;
EditorUtility.SetDirty(_currentLevel);
// Generate new words for the updated letters
if (!string.IsNullOrEmpty(evt.newValue))
{
LevelDataEditor.UpdateAvailableWordsForLevel(_currentLevel);
RefreshPreviewData();
}
UpdateGridDisplay();
Undo.RecordObject(_currentLevel, "Change Letters");
}
});
lettersField.value = initialValue;
}
languageData.letters = evt.newValue;
var lettersLength = paletteRoot.Q<IntegerField>("letters-length");
if (lettersLength != null)
{
lettersLength.value = evt.newValue?.Length ?? 0;
}
_currentLevel.letters = evt.newValue?.Length ?? 0;
EditorUtility.SetDirty(_currentLevel);
// Generate new words for the updated letters
if (!string.IsNullOrEmpty(evt.newValue))
{
LevelDataEditor.UpdateAvailableWordsForLevel(_currentLevel);
RefreshPreviewData();
}
UpdateGridDisplay();
}
});
lettersField.value = initialValue;
// Bind letters length field
var lettersLength = paletteRoot.Q<TextField>("letters-length");
if (lettersLength != null)
lettersLength.value = _currentLevel.letters.ToString();
lettersLength.RegisterValueChangedCallback(evt =>
{
lettersLength.value = _currentLevel.letters.ToString();
lettersLength.RegisterValueChangedCallback(evt =>
if (int.TryParse(evt.newValue, out int value))
{
if (int.TryParse(evt.newValue, out int value))
{
_currentLevel.letters = value;
EditorUtility.SetDirty(_currentLevel);
EditorPrefs.SetInt("WordsToolkit_LettersAmount", value);
}
});
}
_currentLevel.letters = value;
EditorUtility.SetDirty(_currentLevel);
EditorPrefs.SetInt("WordsToolkit_LettersAmount", value);
}
});
var generateLettersCheck = paletteRoot.Q<Toggle>("generate-letters-check");
generateLettersCheck.value = EditorPrefs.GetBool("WordsToolkit_GenerateLetters", true);
generateLettersCheck.RegisterValueChangedCallback(evt => {
if (_currentLevel != null)
{
EditorUtility.SetDirty(_currentLevel);
EditorPrefs.SetBool("WordsToolkit_GenerateLetters", evt.newValue);
}
});
// Bind generate button
var generateButton = paletteRoot.Q<Button>("generate-button");
if (generateButton != null)
{
generateButton.clicked += () => {
if (_currentLevel != null && languageData != null)
generateButton.clicked += () => {
if (_currentLevel != null && languageData != null)
{
// Record undo before generating new letters
if (_currentLevel != null)
{
// Record undo before generating new letters
if (_currentLevel != null)
// Ensure grid data is serialized before recording undo
var langData = _currentLevel.GetLanguageData(_currentLanguageCode);
if (langData?.crosswordData != null)
{
// Ensure grid data is serialized before recording undo
var langData = _currentLevel.GetLanguageData(_currentLanguageCode);
if (langData?.crosswordData != null)
{
langData.crosswordData.SerializeGrid();
}
Undo.RecordObject(_currentLevel, "Generate Random Letters");
langData.crosswordData.SerializeGrid();
}
var modelController = EditorScope.Resolve<IModelController>();
string letters = LevelEditorServices.GenerateRandomLetters(languageData, languageData.wordsAmount, _currentLevel.letters);
languageData.letters = letters;
// Update the letters field in the UI
if (lettersField != null) lettersField.value = languageData.letters;
if (lettersLength != null) lettersLength.value = (languageData.letters?.Length ?? 0).ToString();
languageData.words = new string[0];
EditorUtility.SetDirty(_currentLevel);
LevelEditorServices.GenerateAvailableWords(_currentLevel, modelController, languageData);
UpdateCrossword(_currentLevel, languageData);
LevelDataEditor.NotifyWordsListNeedsUpdate(_currentLevel, languageData.language);
RefreshPreviewData();
UpdateLetterPalette();
UpdateGridDisplay();
UpdateStatusBar();
Undo.RecordObject(_currentLevel, "Generate Random Letters");
}
};
}
var modelController = EditorScope.Resolve<IModelController>();
string letters = LevelEditorServices.GenerateRandomLetters(languageData, languageData.wordsAmount, _currentLevel.letters, generateLettersCheck.value );
languageData.letters = letters;
// Update the letters field in the UI
if (lettersField != null) lettersField.value = languageData.letters;
if (lettersLength != null) lettersLength.value = (languageData.letters?.Length ?? 0).ToString();
languageData.words = new string[0];
EditorUtility.SetDirty(_currentLevel);
LevelEditorServices.GenerateAvailableWords(_currentLevel, modelController, languageData);
UpdateCrossword(_currentLevel, languageData);
LevelDataEditor.NotifyWordsListNeedsUpdate(_currentLevel, languageData.language);
RefreshPreviewData();
UpdateLetterPalette();
UpdateGridDisplay();
UpdateStatusBar();
}
};
// Bind grid size controls - columns field
var columnsField = paletteRoot.Q<TextField>("columns-field");
if (columnsField != null)
{
columnsField.value = (_previewData?.columns ?? 10).ToString();
columnsField.RegisterValueChangedCallback(evt => {
if (_previewData != null && int.TryParse(evt.newValue, out int value) && value >= 5 && value <= 50)
{
_previewData.columns = value;
CrosswordPreviewHandler.SavePreviewToLevel(_currentLevel, _currentLanguageCode, _previewData);
EditorUtility.SetDirty(_currentLevel);
EditorPrefs.SetInt("WordsToolkit_grid_x", value);
UpdateGridDisplay();
UpdateStatusBar();
}
});
}
columnsField.value = (_previewData?.columns ?? 10).ToString();
columnsField.RegisterValueChangedCallback(evt => {
if (_previewData != null && int.TryParse(evt.newValue, out int value) && value >= 5 && value <= 50)
{
_previewData.columns = value;
CrosswordPreviewHandler.SavePreviewToLevel(_currentLevel, _currentLanguageCode, _previewData);
EditorUtility.SetDirty(_currentLevel);
EditorPrefs.SetInt("WordsToolkit_grid_x", value);
UpdateGridDisplay();
UpdateStatusBar();
}
});
// Bind grid size controls - rows field
var rowsField = paletteRoot.Q<TextField>("rows-field");
if (rowsField != null)
{
rowsField.value = (_previewData?.rows ?? 7).ToString();
rowsField.RegisterValueChangedCallback(evt => {
if (_previewData != null && int.TryParse(evt.newValue, out int value) && value >= 5 && value <= 50)
{
_previewData.rows = value;
CrosswordPreviewHandler.SavePreviewToLevel(_currentLevel, _currentLanguageCode, _previewData);
EditorUtility.SetDirty(_currentLevel);
EditorPrefs.SetInt("WordsToolkit_grid_y", value);
UpdateGridDisplay();
UpdateStatusBar();
}
});
}
rowsField.value = (_previewData?.rows ?? 7).ToString();
rowsField.RegisterValueChangedCallback(evt => {
if (_previewData != null && int.TryParse(evt.newValue, out int value) && value >= 5 && value <= 50)
{
_previewData.rows = value;
CrosswordPreviewHandler.SavePreviewToLevel(_currentLevel, _currentLanguageCode, _previewData);
EditorUtility.SetDirty(_currentLevel);
EditorPrefs.SetInt("WordsToolkit_grid_y", value);
UpdateGridDisplay();
UpdateStatusBar();
}
});
// Bind Test Level button
var testLevelButton = paletteRoot.Q<Button>("test-level-button");
@ -1319,8 +1360,11 @@ namespace WordsToolkit.Scripts.Levels.Editor
if (_enableEditing && _currentLevel != null)
{
var buttonsRow = CreateLetterButtonsRow();
buttonsRow.style.alignSelf = Align.FlexEnd;
letterButtonsContainer.Add(buttonsRow);
if (buttonsRow != null)
{
buttonsRow.style.alignSelf = Align.FlexEnd;
letterButtonsContainer.Add(buttonsRow);
}
}
}

View File

@ -616,12 +616,16 @@ namespace WordsToolkit.Scripts.Levels.Editor
if (candidate != null && candidate.isValid)
{
// Check for problematic overlaps (both position overlaps and missing words)
bool hasProblematicOverlaps = LevelEditorServices.CheckForOverlappingWords(candidate.placements, wordsToUse);
// Create a string representation of the grid for uniqueness checking
string gridSignature = GridToString(candidate.grid);
// Check if this grid layout is unique and has a good word count
// Check if this grid layout is unique, has a good word count, and no problematic overlaps
if (!generatedGrids.Contains(gridSignature) &&
candidate.placements.Count > bestWordCount)
candidate.placements.Count > bestWordCount &&
!hasProblematicOverlaps)
{
bestVariant = candidate;
bestWordCount = candidate.placements.Count;

View File

@ -1037,6 +1037,7 @@ namespace WordsToolkit.Scripts.Levels.Editor
// Word text field
var wordField = new TextField();
wordField.name = "word-field";
wordField.isReadOnly = true;
wordField.style.flexGrow = 1;
wordField.style.flexShrink = 1;
wordField.style.minWidth = 100;
@ -1677,7 +1678,7 @@ namespace WordsToolkit.Scripts.Levels.Editor
if (string.IsNullOrEmpty(letters))
{
// Generate random letters based on language if needed
letters = LevelEditorServices.GenerateRandomLetters(languageData, languageData.wordsAmount, lettersAmount);
letters = LevelEditorServices.GenerateRandomLetters(languageData, languageData.wordsAmount, lettersAmount, false);
lettersProp.stringValue = letters;
serializedObject.ApplyModifiedProperties();
}
@ -1778,7 +1779,7 @@ namespace WordsToolkit.Scripts.Levels.Editor
private void GenerateWordsForLanguage(Level level, LanguageData languageData, IModelController Controller)
{
string letters = LevelEditorServices.GenerateRandomLetters(languageData, languageData.wordsAmount, level.letters);
string letters = LevelEditorServices.GenerateRandomLetters(languageData, languageData.wordsAmount, level.letters, false);
languageData.letters = letters;
LevelEditorServices.GenerateWordsForLanguage(level, languageData, Controller, false);
UpdateCrossword(level, languageData);
@ -1970,7 +1971,7 @@ namespace WordsToolkit.Scripts.Levels.Editor
{
if (!AssetDatabase.IsValidFolder("Assets/WordConnectGameToolkit/Resources"))
{
AssetDatabase.CreateFolder("Assets/WordsToolkit", "Resources");
AssetDatabase.CreateFolder("Assets/WordConnectGameToolkit", "Resources");
}
AssetDatabase.CreateFolder("Assets/WordConnectGameToolkit/Resources", "Settings");
}

View File

@ -95,7 +95,7 @@ namespace WordsToolkit.Scripts.Levels.Editor
$"Processing language: {languageData.language}",
(float)processedLanguages / totalLanguages);
string letters = GenerateRandomLetters(languageData, languageData.wordsAmount, level.letters);
string letters = GenerateRandomLetters(languageData, languageData.wordsAmount, level.letters, false);
if (!string.IsNullOrEmpty(letters))
{
languageData.letters = letters;
@ -165,10 +165,10 @@ namespace WordsToolkit.Scripts.Levels.Editor
if (selectedWords.Count == 0)
{
Debug.LogWarning($"No valid words with {minLettersInWord} to {maxLettersInWord} letters could be generated from '{languageData.letters}' for language {languageData.language}");
return;
}
Debug.Log($"Generated words for language {languageData.language} in level {level.number}");
// Shuffle the final list
var words = selectedWords.OrderBy(x => UnityEngine.Random.value).ToList();
@ -192,7 +192,7 @@ namespace WordsToolkit.Scripts.Levels.Editor
return model.GetWordsFromSymbols(languageData.letters, languageData.language);
}
public static string GenerateRandomLetters(LanguageData languageData, int count, int lettersAmount)
public static string GenerateRandomLetters(LanguageData languageData, int count, int lettersAmount, bool generateLetters)
{
var controller = EditorScope.Resolve<IModelController>();
var bannedWordsService = EditorScope.Resolve<IBannedWordsService>();
@ -204,6 +204,19 @@ namespace WordsToolkit.Scripts.Levels.Editor
return "";
}
// 30% chance to generate actual random letters instead of finding optimal words
if (generateLetters)
{
string randomLetters = GenerateActualRandomLetters(languageData.language, lettersAmount);
// Check if these random letters can generate any words
var wordsFromRandomLetters = controller.GetWordsFromSymbols(randomLetters, languageData.language);
if (wordsFromRandomLetters != null && wordsFromRandomLetters.Count() > 0)
{
return randomLetters;
}
}
var usedWords = LevelEditorServices.GetUsedWords(languageData.language);
// Try getting words with specified length
@ -212,28 +225,38 @@ namespace WordsToolkit.Scripts.Levels.Editor
// If no words found with the specified length, try with other lengths
if (words.Count == 0)
{
Debug.LogWarning($"No words with length {lettersAmount} found for language {languageData.language}. Trying other lengths...");
// Try with smaller lengths first, then larger
for (int offset = 1; offset <= 3; offset++)
// Check if model is loaded for this language
if (!controller.IsModelLoaded(languageData.language))
{
// Try smaller length
if (lettersAmount - offset > 2)
controller.LoadModels();
// Try again after reloading
words = controller.GetWordsWithLength(lettersAmount, languageData.language).Where(w => !usedWords.Contains(w)).ToList();
}
// If still no words found after reloading, try with other lengths
if (words.Count == 0)
{
// Try with smaller lengths first, then larger
for (int offset = 1; offset <= 3; offset++)
{
words = controller.GetWordsWithLength(lettersAmount - offset, languageData.language);
// Try smaller length
if (lettersAmount - offset > 2)
{
words = controller.GetWordsWithLength(lettersAmount - offset, languageData.language);
if (words.Count > 0) break;
}
// Try larger length
words = controller.GetWordsWithLength(lettersAmount + offset, languageData.language);
if (words.Count > 0) break;
}
// Try larger length
words = controller.GetWordsWithLength(lettersAmount + offset, languageData.language);
if (words.Count > 0) break;
}
}
// If still no words found, use some default letters
if (words.Count == 0)
{
Debug.LogWarning($"No suitable words found for language {languageData.language}. Using default letters.");
// Default letter sets by language
Dictionary<string, string> defaultLetters = new Dictionary<string, string>
{
@ -248,7 +271,7 @@ namespace WordsToolkit.Scripts.Levels.Editor
}
else
{
return "".Substring(0, Mathf.Min(lettersAmount, 26));
return new string('a', lettersAmount);
}
}
@ -270,7 +293,6 @@ namespace WordsToolkit.Scripts.Levels.Editor
if (wordsCount >= count)
{
Debug.Log($"Found optimal word '{words[i]}' that can generate {wordsCount} words (excluding banned words)");
return words[i];
}
@ -281,10 +303,73 @@ namespace WordsToolkit.Scripts.Levels.Editor
}
}
Debug.Log($"Using best word '{bestWord}' which can generate {maxWordsGenerated} words (excluding banned words)");
return bestWord; // Return the word that generated the most derived words
}
private static string GenerateActualRandomLetters(string language, int length)
{
// Define Latin languages that use vowel/consonant distinction
HashSet<string> latinLanguages = new HashSet<string> { "en", "es", "fr", "de", "it", "pt", "nl", "da", "sv", "no" };
if (latinLanguages.Contains(language))
{
// For Latin languages, use vowel/consonant distribution
string vowels = "aeiou";
string consonants = "bcdfghjklmnpqrstvwxyz";
var result = new List<char>();
int vowelCount = Mathf.Max(1, length / 2); // About 1/2 vowels
int consonantCount = length - vowelCount;
// Add vowels
for (int i = 0; i < vowelCount; i++)
{
result.Add(vowels[UnityEngine.Random.Range(0, vowels.Length)]);
}
// Add consonants
for (int i = 0; i < consonantCount; i++)
{
result.Add(consonants[UnityEngine.Random.Range(0, consonants.Length)]);
}
// Shuffle the letters
var chars = result.ToArray();
for (int i = 0; i < chars.Length; i++)
{
int randomIndex = UnityEngine.Random.Range(i, chars.Length);
(chars[i], chars[randomIndex]) = (chars[randomIndex], chars[i]);
}
return new string(chars);
}
else
{
// For non-Latin languages, use default letters without vowel distinction
Dictionary<string, string> defaultLetters = new Dictionary<string, string>
{
{ "ru", "оеаинтсрвлкмдпуяыьгзбчйхжшюцщэфъ" },
{ "zh", "一二三四五六七八九十百千万亿" },
{ "ja", "あいうえおかきくけこさしすせそたちつてとなにぬねの" },
{ "ar", "ابتثجحخدذرزسشصضطظعغفقكلمنهوي" }
};
string letters;
if (!defaultLetters.TryGetValue(language, out letters))
{
letters = "abcdefghijklmnopqrstuvwxyz"; // Fallback to Latin alphabet
}
var result = new List<char>();
for (int i = 0; i < length; i++)
{
result.Add(letters[UnityEngine.Random.Range(0, letters.Length)]);
}
return new string(result.ToArray());
}
}
private static List<string> GetUsedWords(string languageDataLanguage)
{
// Get all levels in the project
@ -503,5 +588,77 @@ namespace WordsToolkit.Scripts.Levels.Editor
}
};
}
// Check for problematic overlapping words
public static bool CheckForOverlappingWords(List<WordPlacement> placements, string[] originalWords = null)
{
// Check for words starting at the same position
var sameStartGroups = placements
.GroupBy(p => p.startPosition)
.Where(g => g.Count() > 1)
.ToList();
bool hasPositionOverlaps = sameStartGroups.Count > 0;
// Check for same-orientation overlaps (words that overlap along their length)
bool hasSameOrientationOverlaps = false;
for (int i = 0; i < placements.Count; i++)
{
for (int j = i + 1; j < placements.Count; j++)
{
var word1 = placements[i];
var word2 = placements[j];
// Only check words with the same orientation
if (word1.isHorizontal == word2.isHorizontal)
{
if (word1.isHorizontal)
{
// Both horizontal - check if they're on the same row and overlap
if (word1.startPosition.y == word2.startPosition.y)
{
int word1End = word1.startPosition.x + word1.word.Length - 1;
int word2End = word2.startPosition.x + word2.word.Length - 1;
// Check for overlap
bool overlaps = !(word1End < word2.startPosition.x || word2End < word1.startPosition.x);
if (overlaps)
{
hasSameOrientationOverlaps = true;
break;
}
}
}
else
{
// Both vertical - check if they're on the same column and overlap
if (word1.startPosition.x == word2.startPosition.x)
{
int word1End = word1.startPosition.y + word1.word.Length - 1;
int word2End = word2.startPosition.y + word2.word.Length - 1;
// Check for overlap
bool overlaps = !(word1End < word2.startPosition.y || word2End < word1.startPosition.y);
if (overlaps)
{
hasSameOrientationOverlaps = true;
break;
}
}
}
}
}
if (hasSameOrientationOverlaps) break;
}
// Check if some words are missing from placements (indicating overlaps prevented placement)
bool hasMissingWords = false;
if (originalWords != null)
{
hasMissingWords = originalWords.Length > placements.Count;
}
return hasPositionOverlaps || hasSameOrientationOverlaps || hasMissingWords;
}
}
}

View File

@ -4,11 +4,7 @@
"references": [
"GUID:343deaaf83e0cee4ca978e7df0b80d21",
"GUID:d3bf71b33c0c04eb9bc1a8d6513d76bb",
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc",
"GUID:00dd4a7ac8c24c898083910c81898ecc",
"GUID:ac6e78962cfc743b9a5fc5f5a808aa72",
"GUID:b25ad8286798741e3b2cc3883283e669",
"GUID:75bdbcf23199f4cfb86c610d1d946666"
"GUID:b0214a6008ed146ff8f122a6a9c2f6cc"
],
"includePlatforms": [
"Editor"