Upload 88 files
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- LICENSE +21 -0
- STRUCTURE.md +101 -0
- TECHSTACK.md +12 -0
- aftman.toml +7 -0
- default.project.json +36 -0
- mcp.json +10 -0
- roblox-studio-antigravity-mcp-guide.md +74 -0
- robloxstudio-mcp-system-mechanics.md +78 -0
- src/ReplicatedStorage/Events/ChopEvent.model.json +3 -0
- src/ReplicatedStorage/Events/ClaimPlot.model.json +3 -0
- src/ReplicatedStorage/Events/CraftEvent.model.json +3 -0
- src/ReplicatedStorage/Events/DialogueEvent.model.json +3 -0
- src/ReplicatedStorage/Events/DragEvent.model.json +3 -0
- src/ReplicatedStorage/Events/DropEvent.model.json +3 -0
- src/ReplicatedStorage/Events/EquipToolEvent.model.json +3 -0
- src/ReplicatedStorage/Events/FillBlueprintEvent.model.json +3 -0
- src/ReplicatedStorage/Events/MarketUpdateEvent.model.json +3 -0
- src/ReplicatedStorage/Events/NotificationEvent.model.json +3 -0
- src/ReplicatedStorage/Events/PlaceBlueprintEvent.model.json +3 -0
- src/ReplicatedStorage/Events/PurchaseEvent.model.json +3 -0
- src/ReplicatedStorage/Events/QuestUpdateEvent.model.json +3 -0
- src/ReplicatedStorage/Events/SettingsEvent.model.json +3 -0
- src/ReplicatedStorage/Events/ShopEvent.model.json +3 -0
- src/ReplicatedStorage/Events/SoundEvent.model.json +3 -0
- src/ReplicatedStorage/Events/WireConnectEvent.model.json +3 -0
- src/ReplicatedStorage/Shared/AutomationConfig.lua +20 -0
- src/ReplicatedStorage/Shared/BiomeConfig.lua +105 -0
- src/ReplicatedStorage/Shared/BuildingConfig.lua +98 -0
- src/ReplicatedStorage/Shared/ChoppingConfig.lua +115 -0
- src/ReplicatedStorage/Shared/Constants.lua +63 -0
- src/ReplicatedStorage/Shared/CraftingConfig.lua +74 -0
- src/ReplicatedStorage/Shared/DraggingConfig.lua +15 -0
- src/ReplicatedStorage/Shared/EconomyConfig.lua +40 -0
- src/ReplicatedStorage/Shared/ExplorationConfig.lua +42 -0
- src/ReplicatedStorage/Shared/GameConfig.lua +57 -0
- src/ReplicatedStorage/Shared/MutationConfig.lua +40 -0
- src/ReplicatedStorage/Shared/NPCConfig.lua +86 -0
- src/ReplicatedStorage/Shared/PlotConfig.lua +10 -0
- src/ReplicatedStorage/Shared/QuestConfig.lua +107 -0
- src/ReplicatedStorage/Shared/ShopConfig.lua +112 -0
- src/ReplicatedStorage/Shared/SoundConfig.lua +39 -0
- src/ReplicatedStorage/Shared/TreeModelConfig.lua +119 -0
- src/ReplicatedStorage/Shared/Utility.lua +85 -0
- src/ReplicatedStorage/Shared/VehicleConfig.lua +14 -0
- src/ReplicatedStorage/Shared/WeatherConfig.lua +17 -0
- src/ReplicatedStorage/Shared/WireLogicConfig.lua +14 -0
- src/ServerScriptService/AchievementManager.server.lua +82 -0
- src/ServerScriptService/AntiCheatManager.server.lua +73 -0
- src/ServerScriptService/BiomeGeneratorManager.server.lua +195 -0
- src/ServerScriptService/ConstructionManager.server.lua +88 -0
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 callmerem
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
STRUCTURE.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Project Structure
|
| 2 |
+
|
| 3 |
+
```text
|
| 4 |
+
TimberWoods/
|
| 5 |
+
├── references/
|
| 6 |
+
├── src/
|
| 7 |
+
│ ├── ReplicatedStorage/
|
| 8 |
+
│ │ ├── Events/
|
| 9 |
+
│ │ │ ├── ChopEvent.model.json
|
| 10 |
+
│ │ │ ├── ClaimPlot.model.json
|
| 11 |
+
│ │ │ ├── CraftEvent.model.json
|
| 12 |
+
│ │ │ ├── DialogueEvent.model.json
|
| 13 |
+
│ │ │ ├── DragEvent.model.json
|
| 14 |
+
│ │ │ ├── DropEvent.model.json
|
| 15 |
+
│ │ │ ├── EquipToolEvent.model.json
|
| 16 |
+
│ │ │ ├── FillBlueprintEvent.model.json
|
| 17 |
+
│ │ │ ├── MarketUpdateEvent.model.json
|
| 18 |
+
│ │ │ ├── NotificationEvent.model.json
|
| 19 |
+
│ │ │ ├── PlaceBlueprintEvent.model.json
|
| 20 |
+
│ │ │ ├── PurchaseEvent.model.json
|
| 21 |
+
│ │ │ ├── QuestUpdateEvent.model.json
|
| 22 |
+
│ │ │ ├── SettingsEvent.model.json
|
| 23 |
+
│ │ │ ├── ShopEvent.model.json
|
| 24 |
+
│ │ │ ├── SoundEvent.model.json
|
| 25 |
+
│ │ │ └── WireConnectEvent.model.json
|
| 26 |
+
│ │ └── Shared/
|
| 27 |
+
│ │ ├── AutomationConfig.lua
|
| 28 |
+
│ │ ├── BiomeConfig.lua
|
| 29 |
+
│ │ ├── BuildingConfig.lua
|
| 30 |
+
│ │ ├── ChoppingConfig.lua
|
| 31 |
+
│ │ ├── Constants.lua
|
| 32 |
+
│ │ ├── CraftingConfig.lua
|
| 33 |
+
│ │ ├── DraggingConfig.lua
|
| 34 |
+
│ │ ├── EconomyConfig.lua
|
| 35 |
+
│ │ ├── ExplorationConfig.lua
|
| 36 |
+
│ │ ├── GameConfig.lua
|
| 37 |
+
│ │ ├── MutationConfig.lua
|
| 38 |
+
│ │ ├── NPCConfig.lua
|
| 39 |
+
│ │ ├── PlotConfig.lua
|
| 40 |
+
│ │ ├── QuestConfig.lua
|
| 41 |
+
│ │ ├── ShopConfig.lua
|
| 42 |
+
│ │ ├── SoundConfig.lua
|
| 43 |
+
│ │ ├── TreeModelConfig.lua
|
| 44 |
+
│ │ ├── Utility.lua
|
| 45 |
+
│ │ ├── VehicleConfig.lua
|
| 46 |
+
│ │ ├── WeatherConfig.lua
|
| 47 |
+
│ │ └── WireLogicConfig.lua
|
| 48 |
+
│ ├── ServerScriptService/
|
| 49 |
+
│ │ ├── AchievementManager.server.lua
|
| 50 |
+
│ │ ├── AntiCheatManager.server.lua
|
| 51 |
+
│ │ ├── BiomeGeneratorManager.server.lua
|
| 52 |
+
│ │ ├── ConstructionManager.server.lua
|
| 53 |
+
│ │ ├── CraftingManager.server.lua
|
| 54 |
+
│ │ ├── DatastoreManager.server.lua
|
| 55 |
+
│ │ ├── DayNightManager.server.lua
|
| 56 |
+
│ │ ├── DragManager.server.lua
|
| 57 |
+
│ │ ├── DroneAutomationManager.server.lua
|
| 58 |
+
│ │ ├── EnvironmentalPuzzleManager.server.lua
|
| 59 |
+
│ │ ├── InventoryManager.server.lua
|
| 60 |
+
│ │ ├── LeaderboardManager.server.lua
|
| 61 |
+
│ │ ├── MapManager.server.lua
|
| 62 |
+
│ │ ├── MarketManager.server.lua
|
| 63 |
+
│ │ ├── MutationManager.server.lua
|
| 64 |
+
│ │ ├── NPCManager.server.lua
|
| 65 |
+
│ │ ├── PlayerSetupManager.server.lua
|
| 66 |
+
│ │ ├── PlotManager.server.lua
|
| 67 |
+
│ │ ├── QuestManager.server.lua
|
| 68 |
+
│ │ ├── RespawnManager.server.lua
|
| 69 |
+
│ │ ├── SawmillManager.server.lua
|
| 70 |
+
│ │ ├── ShopManager.server.lua
|
| 71 |
+
│ │ ├── SoundscapeManager.server.lua
|
| 72 |
+
│ │ ├── TreeManager.server.lua
|
| 73 |
+
│ │ ├── TreeSpawnerManager.server.lua
|
| 74 |
+
│ │ ├── VehicleLogisticsManager.server.lua
|
| 75 |
+
│ │ ├── WeatherManager.server.lua
|
| 76 |
+
│ │ └── WireLogicManager.server.lua
|
| 77 |
+
│ ├── StarterGui/
|
| 78 |
+
│ │ ├── CraftingGUI.client.lua
|
| 79 |
+
│ │ ├── DialogueGUI.client.lua
|
| 80 |
+
│ │ ├── InventoryGUI.client.lua
|
| 81 |
+
│ │ ├── MainHUD.client.lua
|
| 82 |
+
│ │ ├── MarketGUI.client.lua
|
| 83 |
+
│ │ ├── QuestGUI.client.lua
|
| 84 |
+
│ │ ├── SettingsGUI.client.lua
|
| 85 |
+
│ │ └── ShopGUI.client.lua
|
| 86 |
+
│ └── StarterPlayer/
|
| 87 |
+
│ └── StarterCharacterScripts/
|
| 88 |
+
│ ├── AxeController.client.lua
|
| 89 |
+
│ ├── BuildController.client.lua
|
| 90 |
+
│ ├── DragController.client.lua
|
| 91 |
+
│ └── SoundController.client.lua
|
| 92 |
+
├── aftman.toml
|
| 93 |
+
├── default.project.json
|
| 94 |
+
├── LICENSE
|
| 95 |
+
├── mcp.json
|
| 96 |
+
├── roblox-studio-antigravity-mcp-guide.md
|
| 97 |
+
├── robloxstudio-mcp-system-mechanics.md
|
| 98 |
+
├── studio-rust-mcp-server-system-mechanics.md
|
| 99 |
+
├── studio_setup_guide.md
|
| 100 |
+
└── TECHSTACK.md
|
| 101 |
+
```
|
TECHSTACK.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Techstack
|
| 2 |
+
|
| 3 |
+
Audit of **TimberWoods** project files (excluding environment and cache):
|
| 4 |
+
|
| 5 |
+
| File Type | Count | Size (KB) |
|
| 6 |
+
| :--- | :--- | :--- |
|
| 7 |
+
| Lua (.lua) | 61 | 212.2 |
|
| 8 |
+
| JSON (.json) | 19 | 1.8 |
|
| 9 |
+
| Markdown (.md) | 4 | 17.8 |
|
| 10 |
+
| (no extension) | 1 | 1.1 |
|
| 11 |
+
| TOML (.toml) | 1 | 0.3 |
|
| 12 |
+
| **Total** | **86** | **233.1** |
|
aftman.toml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file lists tools managed by Aftman, a cross-platform toolchain manager.
|
| 2 |
+
# For more information, see https://github.com/LPGhatguy/aftman
|
| 3 |
+
|
| 4 |
+
# To add a new tool, add an entry to this table.
|
| 5 |
+
[tools]
|
| 6 |
+
rojo = "rojo-rbx/rojo@7.7.0-rc.1"
|
| 7 |
+
# rojo = "rojo-rbx/rojo@6.2.0"
|
default.project.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "TimberboundExpeditions",
|
| 3 |
+
"tree": {
|
| 4 |
+
"$className": "DataModel",
|
| 5 |
+
"ReplicatedStorage": {
|
| 6 |
+
"$className": "ReplicatedStorage",
|
| 7 |
+
"Shared": {
|
| 8 |
+
"$path": "src/ReplicatedStorage/Shared"
|
| 9 |
+
},
|
| 10 |
+
"Events": {
|
| 11 |
+
"$path": "src/ReplicatedStorage/Events"
|
| 12 |
+
}
|
| 13 |
+
},
|
| 14 |
+
"ServerScriptService": {
|
| 15 |
+
"$className": "ServerScriptService",
|
| 16 |
+
"TimberboundServer": {
|
| 17 |
+
"$path": "src/ServerScriptService"
|
| 18 |
+
}
|
| 19 |
+
},
|
| 20 |
+
"StarterPlayer": {
|
| 21 |
+
"$className": "StarterPlayer",
|
| 22 |
+
"StarterCharacterScripts": {
|
| 23 |
+
"$className": "StarterCharacterScripts",
|
| 24 |
+
"TimberboundClient": {
|
| 25 |
+
"$path": "src/StarterPlayer/StarterCharacterScripts"
|
| 26 |
+
}
|
| 27 |
+
}
|
| 28 |
+
},
|
| 29 |
+
"StarterGui": {
|
| 30 |
+
"$className": "StarterGui",
|
| 31 |
+
"TimberboundGUI": {
|
| 32 |
+
"$path": "src/StarterGui"
|
| 33 |
+
}
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
}
|
mcp.json
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"mcpServers": {
|
| 3 |
+
"Roblox Studio": {
|
| 4 |
+
"command": "C:\\Users\\User\\Desktop\\VSCode2\\RobloxStudio-MCP-GoogleAntigravity\\references\\studio-rust-mcp-server-main\\target\\release\\rbx-studio-mcp.exe",
|
| 5 |
+
"args": [
|
| 6 |
+
"--stdio"
|
| 7 |
+
]
|
| 8 |
+
}
|
| 9 |
+
}
|
| 10 |
+
}
|
roblox-studio-antigravity-mcp-guide.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Integrating Google Antigravity with Roblox Studio via Rust MCP
|
| 2 |
+
|
| 3 |
+
This guide outlines exactly how to connect the Google Antigravity system (running within VS Code) natively to your Roblox Studio instance utilizing the high-performance **Rust Model Context Protocol (MCP)** implementation.
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## Step 1: Prepare the Rust Environment
|
| 8 |
+
|
| 9 |
+
The `studio-rust-mcp-server-main` reference must first be built into a local binary.
|
| 10 |
+
|
| 11 |
+
1. Open your terminal inside the `studio-rust-mcp-server-main` repository directory.
|
| 12 |
+
2. Ensure you have Rust and Cargo installed via rustup.
|
| 13 |
+
3. Build the server for release:
|
| 14 |
+
```bash
|
| 15 |
+
cargo build --release
|
| 16 |
+
```
|
| 17 |
+
4. This will output a binary executable located at `target\release\rbx-studio-mcp.exe`.
|
| 18 |
+
|
| 19 |
+
## Step 2: Configure Google Antigravity (VS Code)
|
| 20 |
+
|
| 21 |
+
Google Antigravity leverages a configuration file named `mcp.json` inside your VS Code user data directory to identify connected AI bridging servers.
|
| 22 |
+
|
| 23 |
+
1. Navigate to your VS Code data folder. On Windows, this is typically located at:
|
| 24 |
+
`C:\Users\User\AppData\Roaming\Code\User\mcp.json`
|
| 25 |
+
*(If the file does not exist, create it).*
|
| 26 |
+
2. Add the `Roblox Studio` entry to the `mcpServers` object, directing it to your compiled executable and passing the required `--stdio` flag. The file should look like this:
|
| 27 |
+
|
| 28 |
+
```json
|
| 29 |
+
{
|
| 30 |
+
"mcpServers": {
|
| 31 |
+
"Roblox Studio": {
|
| 32 |
+
"command": "C:\\Users\\User\\Desktop\\VSCode2\\RobloxStudio-MCP-GoogleAntigravity\\references\\studio-rust-mcp-server-main\\target\\release\\rbx-studio-mcp.exe",
|
| 33 |
+
"args": [
|
| 34 |
+
"--stdio"
|
| 35 |
+
]
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
```
|
| 40 |
+
|
| 41 |
+
## Step 3: Install the Studio Component Plugin
|
| 42 |
+
|
| 43 |
+
The Rust MCP server utilizes a native Roblox Studio plugin (`MCPStudioPlugin.rbxm`) to communicate over local ports via long polling. The compiled server binary can install this plugin automatically.
|
| 44 |
+
|
| 45 |
+
1. Run the `.exe` directly **without any arguments** from your command line:
|
| 46 |
+
```bash
|
| 47 |
+
.\target\release\rbx-studio-mcp.exe
|
| 48 |
+
```
|
| 49 |
+
2. The installer will automatically extract and inject the plugin directly into your local Roblox plugins directory (`%LOCALAPPDATA%\Roblox\Plugins`).
|
| 50 |
+
|
| 51 |
+
## Step 4: Finalizing the Integration
|
| 52 |
+
|
| 53 |
+
You must make both systems aware of the new bridging connections.
|
| 54 |
+
|
| 55 |
+
1. **Reload VS Code**: Because you just created/modified `mcp.json`, the Antigravity system hasn't loaded it yet. Run the VS Code command `Developer: Reload Window` (Ctrl+Shift+P) or simply restart VS Code entirely.
|
| 56 |
+
2. **Restart Roblox Studio**: Open any local place or project file to load your plugins.
|
| 57 |
+
3. **Activate the Plugin**: In the Studio `Plugins` toolbar, click the MCP icon to toggle it ON. Look at your Studio `Output` window—you should see the message:
|
| 58 |
+
> `"The MCP Studio plugin is ready for prompts."`
|
| 59 |
+
|
| 60 |
+
---
|
| 61 |
+
|
| 62 |
+
## Step 5: Test the Workspace Link!
|
| 63 |
+
|
| 64 |
+
The setup is complete! Since Google Antigravity relies on an internal network of autonomous tools (and doesn't require jumping through an external Claude/Cursor GUI), you can simply begin prompting directly in the Antigravity chat:
|
| 65 |
+
|
| 66 |
+
- *"List the child elements of my Workspace."*
|
| 67 |
+
- *"Create a red part at the world spawn."*
|
| 68 |
+
- *"Find and report any LocalScripts currently running in ReplicatedStorage."*
|
| 69 |
+
|
| 70 |
+
Antigravity will instantly pipe instructions sequentially through `rbx-studio-mcp.exe`, execute natively inside Roblox Studio's DataModel, and retrieve responses securely back into your agent interface.
|
| 71 |
+
|
| 72 |
+
# others
|
| 73 |
+
|
| 74 |
+
cat C:\Users\User\AppData\Roaming\Code\User\mcp.json
|
robloxstudio-mcp-system-mechanics.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# NOTE: This is from 'C:\Users\User\Desktop\VSCode2\RobloxStudio-MCP-GoogleAntigravity\references\robloxstudio-mcp-main'.
|
| 2 |
+
|
| 3 |
+
# Roblox Studio MCP Server (Node.js/TypeScript) System Mechanics
|
| 4 |
+
|
| 5 |
+
This document provides a detailed breakdown of the system architecture and mechanics for the `robloxstudio-mcp` project, which facilitates communication between an LLM Client (like Claude or Cursor) and Roblox Studio.
|
| 6 |
+
|
| 7 |
+
## Architecture Diagram
|
| 8 |
+
|
| 9 |
+
Below is a detailed Mermaid sequence diagram illustrating the lifecycle of a tool request in this environment.
|
| 10 |
+
|
| 11 |
+
```mermaid
|
| 12 |
+
sequenceDiagram
|
| 13 |
+
participant LLM as LLM Client (Claude/Cursor)
|
| 14 |
+
participant MCP as Node.js MCP Server (stdio)
|
| 15 |
+
participant Bridge as BridgeService (Memory)
|
| 16 |
+
participant HTTP as Express HTTP Server (:port)
|
| 17 |
+
participant Plugin as Roblox Studio Plugin (Luau)
|
| 18 |
+
|
| 19 |
+
note over LLM,Plugin: 1. Setup Phase
|
| 20 |
+
HTTP-->>Plugin: Setup Endpoints (/health, /poll, /response)
|
| 21 |
+
Plugin->>HTTP: POST /ready (Initialize Connection)
|
| 22 |
+
HTTP->>Bridge: bridge.clearAllPendingRequests()
|
| 23 |
+
|
| 24 |
+
note over LLM,Plugin: 2. Long Polling Loop
|
| 25 |
+
loop Every few seconds or immediately after a response
|
| 26 |
+
Plugin->>HTTP: GET /poll
|
| 27 |
+
HTTP->>Bridge: getPendingRequest()
|
| 28 |
+
alt No pending request
|
| 29 |
+
HTTP-->>Plugin: empty response (after delay/timeout)
|
| 30 |
+
else Has pending request
|
| 31 |
+
HTTP-->>Plugin: JSON { request, requestId }
|
| 32 |
+
end
|
| 33 |
+
end
|
| 34 |
+
|
| 35 |
+
note over LLM,Plugin: 3. Tool Execution Lifecycle
|
| 36 |
+
LLM->>MCP: CallTool (e.g., get_file_tree)
|
| 37 |
+
MCP->>Bridge: sendRequest(endpoint, data)
|
| 38 |
+
Bridge->>Bridge: generate UUID & store in pendingRequests Map
|
| 39 |
+
|
| 40 |
+
note right of HTTP: The next long poll from Plugin hits...
|
| 41 |
+
Plugin->>HTTP: GET /poll
|
| 42 |
+
HTTP->>Bridge: getPendingRequest()
|
| 43 |
+
Bridge-->>HTTP: oldest pending request
|
| 44 |
+
HTTP-->>Plugin: Return Request Payload (requestId, tool data)
|
| 45 |
+
|
| 46 |
+
note over Plugin: Evaluates request & interacts with Roblox DataModel
|
| 47 |
+
|
| 48 |
+
Plugin->>HTTP: POST /response { requestId, response, error }
|
| 49 |
+
HTTP->>Bridge: resolveRequest(requestId, response)
|
| 50 |
+
Bridge->>Bridge: Match UUID, trigger promise resolve
|
| 51 |
+
Bridge-->>MCP: Return result
|
| 52 |
+
MCP-->>LLM: Return CallToolResult
|
| 53 |
+
```
|
| 54 |
+
|
| 55 |
+
## Detailed Discussion
|
| 56 |
+
|
| 57 |
+
### Components Overview
|
| 58 |
+
|
| 59 |
+
1. **MCP Server over stdio (`src/index.ts`)**:
|
| 60 |
+
The core entry point utilizes the `@modelcontextprotocol/sdk` to expose Roblox Studio capabilities to AI assistants via Standard AI standard input/output streams (`stdio`). It initializes various available tools (like `get_file_tree`, `search_files`, `edit_script_lines`, etc.) and maps them to functions in `RobloxStudioTools`.
|
| 61 |
+
|
| 62 |
+
2. **BridgeService (`src/bridge-service.ts`)**:
|
| 63 |
+
Since standard LLMs cannot execute HTTP requests directly into the Roblox Studio execution context, a "Bridge" is required. The `BridgeService` stores requests originating from the LLM in memory utilizing a `Map<string, PendingRequest>`. It assigns a `uuidv4` identifier to each request, resolving or rejecting Javascript Promises when Roblox Studio responds or naturally timing out after 30 seconds.
|
| 64 |
+
|
| 65 |
+
3. **Express HTTP Server (`src/http-server.ts`)**:
|
| 66 |
+
Exposed locally, this server creates standard REST API endpoints mapping to the bridge context:
|
| 67 |
+
- `/ready` and `/disconnect`: Manage lifecycle awareness for the connected plugin.
|
| 68 |
+
- `/poll`: The crucial endpoint for the long-polling architecture. The Roblox Studio plugin continuously sends GET requests here. If a request exists, it returns immediately; otherwise, it waits/cycles to keep the network open without overwhelming the LLM client.
|
| 69 |
+
- `/response`: Where the plugin submits the results of executed queries natively run inside Studio.
|
| 70 |
+
- Various `/mcp/*` endpoints exist natively simulating the tools routing.
|
| 71 |
+
|
| 72 |
+
4. **Roblox Studio Plugin (`studio-plugin/src/...`)**:
|
| 73 |
+
Written in Luau. Because Roblox restricts incoming connections for security, the plugin utilizes `HttpService` to make outgoing requests to the local Express server. It intercepts the HTTP Polling results, runs internal Studio SDK functions (e.g., getting hierarchical Place structures, updating script source code), and uses `/response` to fire the feedback back up the chain.
|
| 74 |
+
|
| 75 |
+
### Core Mechanics
|
| 76 |
+
|
| 77 |
+
- **Asynchronous Execution Gap Check**: The Node.js application is cleanly divided between the immediate `stdio` processing required by AI environments and the delayed networking constraints of `HttpService` in Roblox. The Promise architecture in the `BridgeService` forces the LLM to patiently await a complete trip through the local REST API and back.
|
| 78 |
+
- **Robust Toolset**: Uniquely among implementations, the TS-based server opts to fully define over 30 distinct granular tools (e.g., `insert_script_lines`, `mass_create_objects`) rather than primarily depending on evaluating raw Luau injected as strings. This gives the AI safer, strictly structured constraints to work within instead of writing custom engine scripts consistently over REST.
|
src/ReplicatedStorage/Events/ChopEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/ClaimPlot.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/CraftEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/DialogueEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/DragEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/DropEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/EquipToolEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/FillBlueprintEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/MarketUpdateEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/NotificationEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/PlaceBlueprintEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/PurchaseEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/QuestUpdateEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/SettingsEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/ShopEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/SoundEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Events/WireConnectEvent.model.json
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"ClassName": "RemoteEvent"
|
| 3 |
+
}
|
src/ReplicatedStorage/Shared/AutomationConfig.lua
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/AutomationConfig.lua
|
| 2 |
+
|
| 3 |
+
local AutomationConfig = {
|
| 4 |
+
Drones = {
|
| 5 |
+
BasicDrone = {
|
| 6 |
+
Speed = 20,
|
| 7 |
+
MaxCarryWeight = 50, -- Volume limit
|
| 8 |
+
EnergyCapacity = 100,
|
| 9 |
+
EnergyDrainPerStud = 0.1
|
| 10 |
+
},
|
| 11 |
+
AdvancedDrone = {
|
| 12 |
+
Speed = 40,
|
| 13 |
+
MaxCarryWeight = 150,
|
| 14 |
+
EnergyCapacity = 300,
|
| 15 |
+
EnergyDrainPerStud = 0.05
|
| 16 |
+
}
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
return AutomationConfig
|
src/ReplicatedStorage/Shared/BiomeConfig.lua
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/BiomeConfig.lua
|
| 2 |
+
|
| 3 |
+
local BiomeConfig = {
|
| 4 |
+
Biomes = {
|
| 5 |
+
Forest = {
|
| 6 |
+
TerrainMaterial = Enum.Material.Grass,
|
| 7 |
+
GroundColor = Color3.fromRGB(85, 120, 60),
|
| 8 |
+
TreeTypes = {"Oak", "Birch", "Elm"},
|
| 9 |
+
TreeDensity = 0.08, -- trees per stud^2 (in spawn region)
|
| 10 |
+
ElevationRange = {0, 5},
|
| 11 |
+
HazardType = nil,
|
| 12 |
+
FogEnd = 1500,
|
| 13 |
+
FogColor = Color3.fromRGB(180, 200, 180),
|
| 14 |
+
Region = {MinX = -500, MaxX = -1, MinZ = 1, MaxZ = 500},
|
| 15 |
+
},
|
| 16 |
+
PineWoods = {
|
| 17 |
+
TerrainMaterial = Enum.Material.Grass,
|
| 18 |
+
GroundColor = Color3.fromRGB(60, 95, 45),
|
| 19 |
+
TreeTypes = {"Pine", "Birch"},
|
| 20 |
+
TreeDensity = 0.10,
|
| 21 |
+
ElevationRange = {5, 20},
|
| 22 |
+
HazardType = nil,
|
| 23 |
+
FogEnd = 1200,
|
| 24 |
+
FogColor = Color3.fromRGB(160, 180, 160),
|
| 25 |
+
Region = {MinX = 1, MaxX = 500, MinZ = 1, MaxZ = 500},
|
| 26 |
+
},
|
| 27 |
+
Swamp = {
|
| 28 |
+
TerrainMaterial = Enum.Material.Mud,
|
| 29 |
+
GroundColor = Color3.fromRGB(70, 80, 50),
|
| 30 |
+
TreeTypes = {"Oak", "Elm"},
|
| 31 |
+
TreeDensity = 0.04,
|
| 32 |
+
ElevationRange = {-3, 0},
|
| 33 |
+
HazardType = "SwampZone",
|
| 34 |
+
FogEnd = 600,
|
| 35 |
+
FogColor = Color3.fromRGB(120, 140, 100),
|
| 36 |
+
Region = {MinX = -500, MaxX = -1, MinZ = -500, MaxZ = -1},
|
| 37 |
+
},
|
| 38 |
+
Desert = {
|
| 39 |
+
TerrainMaterial = Enum.Material.Sand,
|
| 40 |
+
GroundColor = Color3.fromRGB(210, 190, 140),
|
| 41 |
+
TreeTypes = {"Elm"},
|
| 42 |
+
TreeDensity = 0.01,
|
| 43 |
+
ElevationRange = {0, 10},
|
| 44 |
+
HazardType = nil,
|
| 45 |
+
FogEnd = 2000,
|
| 46 |
+
FogColor = Color3.fromRGB(230, 220, 180),
|
| 47 |
+
Region = {MinX = 501, MaxX = 1000, MinZ = -500, MaxZ = -1},
|
| 48 |
+
},
|
| 49 |
+
Volcanic = {
|
| 50 |
+
TerrainMaterial = Enum.Material.Basalt,
|
| 51 |
+
GroundColor = Color3.fromRGB(60, 40, 35),
|
| 52 |
+
TreeTypes = {"LavaWood"},
|
| 53 |
+
TreeDensity = 0.02,
|
| 54 |
+
ElevationRange = {10, 40},
|
| 55 |
+
HazardType = "HazardZone",
|
| 56 |
+
HazardAttribute = "Lava",
|
| 57 |
+
FogEnd = 800,
|
| 58 |
+
FogColor = Color3.fromRGB(180, 100, 60),
|
| 59 |
+
Region = {MinX = 501, MaxX = 1000, MinZ = 1, MaxZ = 500},
|
| 60 |
+
},
|
| 61 |
+
IcePeak = {
|
| 62 |
+
TerrainMaterial = Enum.Material.Glacier,
|
| 63 |
+
GroundColor = Color3.fromRGB(200, 220, 240),
|
| 64 |
+
TreeTypes = {"Pine", "Birch"},
|
| 65 |
+
TreeDensity = 0.03,
|
| 66 |
+
ElevationRange = {20, 60},
|
| 67 |
+
HazardType = nil,
|
| 68 |
+
FogEnd = 900,
|
| 69 |
+
FogColor = Color3.fromRGB(210, 220, 240),
|
| 70 |
+
Region = {MinX = -1000, MaxX = -501, MinZ = -500, MaxZ = -1},
|
| 71 |
+
},
|
| 72 |
+
TropicalRainforest = {
|
| 73 |
+
TerrainMaterial = Enum.Material.LeafyGrass,
|
| 74 |
+
GroundColor = Color3.fromRGB(40, 100, 35),
|
| 75 |
+
TreeTypes = {"Mahogany", "Walnut", "GlowWood"},
|
| 76 |
+
TreeDensity = 0.12,
|
| 77 |
+
ElevationRange = {0, 8},
|
| 78 |
+
HazardType = nil,
|
| 79 |
+
FogEnd = 500,
|
| 80 |
+
FogColor = Color3.fromRGB(100, 150, 100),
|
| 81 |
+
Region = {MinX = -1000, MaxX = -501, MinZ = 1, MaxZ = 500},
|
| 82 |
+
},
|
| 83 |
+
Meadow = {
|
| 84 |
+
TerrainMaterial = Enum.Material.Grass,
|
| 85 |
+
GroundColor = Color3.fromRGB(130, 170, 80),
|
| 86 |
+
TreeTypes = {"Oak", "Birch"},
|
| 87 |
+
TreeDensity = 0.02,
|
| 88 |
+
ElevationRange = {0, 3},
|
| 89 |
+
HazardType = nil,
|
| 90 |
+
FogEnd = 2000,
|
| 91 |
+
FogColor = Color3.fromRGB(200, 210, 200),
|
| 92 |
+
Region = {MinX = 1, MaxX = 500, MinZ = -500, MaxZ = -1},
|
| 93 |
+
},
|
| 94 |
+
},
|
| 95 |
+
|
| 96 |
+
-- Terrain generation
|
| 97 |
+
TerrainChunkSize = 64, -- studs per terrain block
|
| 98 |
+
TerrainResolution = 4, -- voxel resolution
|
| 99 |
+
WaterLevel = -2,
|
| 100 |
+
|
| 101 |
+
-- Tree spacing
|
| 102 |
+
MinTreeSpacing = 12, -- minimum studs between trees
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
return BiomeConfig
|
src/ReplicatedStorage/Shared/BuildingConfig.lua
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/BuildingConfig.lua
|
| 2 |
+
|
| 3 |
+
local BuildingConfig = {
|
| 4 |
+
GridSnap = 2,
|
| 5 |
+
MaxPlacementDistance = 30,
|
| 6 |
+
|
| 7 |
+
Structures = {
|
| 8 |
+
WoodWall = {
|
| 9 |
+
Name = "Wooden Wall",
|
| 10 |
+
Size = Vector3.new(10, 10, 1),
|
| 11 |
+
Cost = {
|
| 12 |
+
WoodVolume = 20,
|
| 13 |
+
SpecificMaterial = "Plank",
|
| 14 |
+
},
|
| 15 |
+
},
|
| 16 |
+
WoodFloor = {
|
| 17 |
+
Name = "Wooden Floor",
|
| 18 |
+
Size = Vector3.new(10, 1, 10),
|
| 19 |
+
Cost = {
|
| 20 |
+
WoodVolume = 25,
|
| 21 |
+
SpecificMaterial = "Plank",
|
| 22 |
+
},
|
| 23 |
+
},
|
| 24 |
+
WoodRoof = {
|
| 25 |
+
Name = "Wooden Roof",
|
| 26 |
+
Size = Vector3.new(12, 1, 12),
|
| 27 |
+
Cost = {
|
| 28 |
+
WoodVolume = 15,
|
| 29 |
+
SpecificMaterial = "Plank",
|
| 30 |
+
},
|
| 31 |
+
},
|
| 32 |
+
WoodDoor = {
|
| 33 |
+
Name = "Wooden Door",
|
| 34 |
+
Size = Vector3.new(4, 8, 1),
|
| 35 |
+
Cost = {
|
| 36 |
+
WoodVolume = 10,
|
| 37 |
+
SpecificMaterial = "Plank",
|
| 38 |
+
},
|
| 39 |
+
},
|
| 40 |
+
WoodStairs = {
|
| 41 |
+
Name = "Wooden Stairs",
|
| 42 |
+
Size = Vector3.new(4, 10, 10),
|
| 43 |
+
Cost = {
|
| 44 |
+
WoodVolume = 30,
|
| 45 |
+
SpecificMaterial = "Plank",
|
| 46 |
+
},
|
| 47 |
+
},
|
| 48 |
+
WoodWindow = {
|
| 49 |
+
Name = "Wooden Window Frame",
|
| 50 |
+
Size = Vector3.new(6, 6, 1),
|
| 51 |
+
Cost = {
|
| 52 |
+
WoodVolume = 12,
|
| 53 |
+
SpecificMaterial = "Plank",
|
| 54 |
+
},
|
| 55 |
+
},
|
| 56 |
+
WoodFence = {
|
| 57 |
+
Name = "Wooden Fence",
|
| 58 |
+
Size = Vector3.new(10, 4, 1),
|
| 59 |
+
Cost = {
|
| 60 |
+
WoodVolume = 8,
|
| 61 |
+
SpecificMaterial = "Stripped",
|
| 62 |
+
},
|
| 63 |
+
},
|
| 64 |
+
WoodBridge = {
|
| 65 |
+
Name = "Wooden Bridge",
|
| 66 |
+
Size = Vector3.new(6, 1, 16),
|
| 67 |
+
Cost = {
|
| 68 |
+
WoodVolume = 40,
|
| 69 |
+
SpecificMaterial = "Plank",
|
| 70 |
+
},
|
| 71 |
+
},
|
| 72 |
+
StonePillar = {
|
| 73 |
+
Name = "Stone Pillar",
|
| 74 |
+
Size = Vector3.new(3, 12, 3),
|
| 75 |
+
Cost = {
|
| 76 |
+
WoodVolume = 5, -- Mortar binding
|
| 77 |
+
SpecificMaterial = "Raw",
|
| 78 |
+
},
|
| 79 |
+
},
|
| 80 |
+
WoodShelter = {
|
| 81 |
+
Name = "Rain Shelter",
|
| 82 |
+
Size = Vector3.new(14, 8, 14),
|
| 83 |
+
Cost = {
|
| 84 |
+
WoodVolume = 50,
|
| 85 |
+
SpecificMaterial = "Plank",
|
| 86 |
+
},
|
| 87 |
+
},
|
| 88 |
+
},
|
| 89 |
+
|
| 90 |
+
-- Structure type cycle order for BuildController
|
| 91 |
+
StructureOrder = {
|
| 92 |
+
"WoodWall", "WoodFloor", "WoodRoof", "WoodDoor",
|
| 93 |
+
"WoodStairs", "WoodWindow", "WoodFence", "WoodBridge",
|
| 94 |
+
"StonePillar", "WoodShelter",
|
| 95 |
+
},
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
return BuildingConfig
|
src/ReplicatedStorage/Shared/ChoppingConfig.lua
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/ChoppingConfig.lua
|
| 2 |
+
local ChoppingConfig = {
|
| 3 |
+
-- Tree configuration
|
| 4 |
+
TreeTypes = {
|
| 5 |
+
Oak = {
|
| 6 |
+
HealthPerSegment = 100,
|
| 7 |
+
LogColor = Color3.fromRGB(133, 94, 66),
|
| 8 |
+
Density = 0.7,
|
| 9 |
+
ValueMult = 1.0,
|
| 10 |
+
},
|
| 11 |
+
Pine = {
|
| 12 |
+
HealthPerSegment = 80,
|
| 13 |
+
LogColor = Color3.fromRGB(158, 127, 98),
|
| 14 |
+
Density = 0.5,
|
| 15 |
+
ValueMult = 0.8,
|
| 16 |
+
},
|
| 17 |
+
Birch = {
|
| 18 |
+
HealthPerSegment = 60,
|
| 19 |
+
LogColor = Color3.fromRGB(220, 215, 200),
|
| 20 |
+
Density = 0.4,
|
| 21 |
+
ValueMult = 1.2,
|
| 22 |
+
},
|
| 23 |
+
Walnut = {
|
| 24 |
+
HealthPerSegment = 120,
|
| 25 |
+
LogColor = Color3.fromRGB(80, 50, 30),
|
| 26 |
+
Density = 0.8,
|
| 27 |
+
ValueMult = 1.8,
|
| 28 |
+
},
|
| 29 |
+
Mahogany = {
|
| 30 |
+
HealthPerSegment = 150,
|
| 31 |
+
LogColor = Color3.fromRGB(100, 40, 20),
|
| 32 |
+
Density = 0.9,
|
| 33 |
+
ValueMult = 2.5,
|
| 34 |
+
},
|
| 35 |
+
Elm = {
|
| 36 |
+
HealthPerSegment = 90,
|
| 37 |
+
LogColor = Color3.fromRGB(110, 80, 55),
|
| 38 |
+
Density = 0.6,
|
| 39 |
+
ValueMult = 1.0,
|
| 40 |
+
},
|
| 41 |
+
Redwood = {
|
| 42 |
+
HealthPerSegment = 200,
|
| 43 |
+
LogColor = Color3.fromRGB(120, 50, 30),
|
| 44 |
+
Density = 1.0,
|
| 45 |
+
ValueMult = 3.0,
|
| 46 |
+
},
|
| 47 |
+
GlowWood = {
|
| 48 |
+
HealthPerSegment = 70,
|
| 49 |
+
LogColor = Color3.fromRGB(60, 180, 80),
|
| 50 |
+
Density = 0.3,
|
| 51 |
+
ValueMult = 4.0,
|
| 52 |
+
},
|
| 53 |
+
LavaWood = {
|
| 54 |
+
HealthPerSegment = 130,
|
| 55 |
+
LogColor = Color3.fromRGB(50, 30, 20),
|
| 56 |
+
Density = 1.2,
|
| 57 |
+
ValueMult = 5.0,
|
| 58 |
+
},
|
| 59 |
+
-- Mutation types
|
| 60 |
+
Fireproof = {
|
| 61 |
+
HealthPerSegment = 180,
|
| 62 |
+
LogColor = Color3.fromRGB(255, 100, 0),
|
| 63 |
+
Density = 1.1,
|
| 64 |
+
ValueMult = 6.0,
|
| 65 |
+
},
|
| 66 |
+
Glowing = {
|
| 67 |
+
HealthPerSegment = 90,
|
| 68 |
+
LogColor = Color3.fromRGB(0, 255, 100),
|
| 69 |
+
Density = 0.5,
|
| 70 |
+
ValueMult = 7.0,
|
| 71 |
+
},
|
| 72 |
+
Frozen = {
|
| 73 |
+
HealthPerSegment = 50,
|
| 74 |
+
LogColor = Color3.fromRGB(150, 200, 255),
|
| 75 |
+
Density = 0.9,
|
| 76 |
+
ValueMult = 4.5,
|
| 77 |
+
},
|
| 78 |
+
Crystalline = {
|
| 79 |
+
HealthPerSegment = 250,
|
| 80 |
+
LogColor = Color3.fromRGB(200, 160, 255),
|
| 81 |
+
Density = 1.5,
|
| 82 |
+
ValueMult = 10.0,
|
| 83 |
+
},
|
| 84 |
+
},
|
| 85 |
+
|
| 86 |
+
-- Axe configuration
|
| 87 |
+
AxeTypes = {
|
| 88 |
+
BasicAxe = {
|
| 89 |
+
Damage = 25,
|
| 90 |
+
SwingCooldown = 0.8,
|
| 91 |
+
Range = 6,
|
| 92 |
+
},
|
| 93 |
+
SteelAxe = {
|
| 94 |
+
Damage = 50,
|
| 95 |
+
SwingCooldown = 0.6,
|
| 96 |
+
Range = 7,
|
| 97 |
+
},
|
| 98 |
+
GoldAxe = {
|
| 99 |
+
Damage = 80,
|
| 100 |
+
SwingCooldown = 0.5,
|
| 101 |
+
Range = 8,
|
| 102 |
+
},
|
| 103 |
+
DiamondAxe = {
|
| 104 |
+
Damage = 120,
|
| 105 |
+
SwingCooldown = 0.4,
|
| 106 |
+
Range = 9,
|
| 107 |
+
},
|
| 108 |
+
},
|
| 109 |
+
|
| 110 |
+
-- General Physics logic limits
|
| 111 |
+
MinSegmentSizeY = 1.5,
|
| 112 |
+
MaxSegmentSizeY = 8,
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
return ChoppingConfig
|
src/ReplicatedStorage/Shared/Constants.lua
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/Constants.lua
|
| 2 |
+
|
| 3 |
+
local Constants = {}
|
| 4 |
+
|
| 5 |
+
-- CollectionService Tags
|
| 6 |
+
Constants.Tags = {
|
| 7 |
+
TreeSegment = "TreeSegment",
|
| 8 |
+
TreeModel = "TreeModel",
|
| 9 |
+
Draggable = "Draggable",
|
| 10 |
+
Blueprint = "Blueprint",
|
| 11 |
+
ConstructedPart = "ConstructedPart",
|
| 12 |
+
EmptyPlot = "EmptyPlot",
|
| 13 |
+
ClaimedPlot = "ClaimedPlot",
|
| 14 |
+
MarketDropoff = "MarketDropoff",
|
| 15 |
+
ShopCounter = "ShopCounter",
|
| 16 |
+
BoxedItem = "BoxedItem",
|
| 17 |
+
ProcessingMachine = "ProcessingMachine",
|
| 18 |
+
Vehicle = "Vehicle",
|
| 19 |
+
DronePad = "DronePad",
|
| 20 |
+
HazardZone = "HazardZone",
|
| 21 |
+
SwampZone = "SwampZone",
|
| 22 |
+
WeightSwitch = "WeightSwitch",
|
| 23 |
+
LogicSource = "LogicSource",
|
| 24 |
+
SwampTires = "SwampTires",
|
| 25 |
+
NPC = "NPC",
|
| 26 |
+
BiomeZone = "BiomeZone",
|
| 27 |
+
WorldBoundary = "WorldBoundary",
|
| 28 |
+
SpawnPlatform = "SpawnPlatform",
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
-- Attributes
|
| 32 |
+
Constants.Attributes = {
|
| 33 |
+
TreeType = "TreeType",
|
| 34 |
+
Health = "Health",
|
| 35 |
+
ProcessState = "ProcessState",
|
| 36 |
+
OwnerId = "OwnerId",
|
| 37 |
+
ItemId = "ItemId",
|
| 38 |
+
FillLevel = "FillLevel",
|
| 39 |
+
WoodFilled = "WoodFilled",
|
| 40 |
+
WoodRequired = "WoodRequired",
|
| 41 |
+
StructureType = "StructureType",
|
| 42 |
+
MaterialRequired = "MaterialRequired",
|
| 43 |
+
MachineType = "MachineType",
|
| 44 |
+
DegradedMult = "DegradedMult",
|
| 45 |
+
Mutated = "Mutated",
|
| 46 |
+
HazardType = "HazardType",
|
| 47 |
+
ComponentType = "ComponentType",
|
| 48 |
+
SourceType = "SourceType",
|
| 49 |
+
TargetFunction = "TargetFunction",
|
| 50 |
+
ConveyorSpeed = "ConveyorSpeed",
|
| 51 |
+
Purchased = "Purchased",
|
| 52 |
+
EquippedAxe = "EquippedAxe",
|
| 53 |
+
BiomeName = "BiomeName",
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
-- Process States
|
| 57 |
+
Constants.ProcessStates = {
|
| 58 |
+
Raw = "Raw",
|
| 59 |
+
Stripped = "Stripped",
|
| 60 |
+
Plank = "Plank",
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
return Constants
|
src/ReplicatedStorage/Shared/CraftingConfig.lua
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/CraftingConfig.lua
|
| 2 |
+
|
| 3 |
+
local CraftingConfig = {
|
| 4 |
+
-- Punch (fist) stats
|
| 5 |
+
Fist = {
|
| 6 |
+
Damage = 5,
|
| 7 |
+
SwingCooldown = 1.2,
|
| 8 |
+
Range = 5,
|
| 9 |
+
},
|
| 10 |
+
|
| 11 |
+
-- Resources dropped per chop hit
|
| 12 |
+
WoodDropChance = 0.6, -- 60% chance to drop resource per hit
|
| 13 |
+
WoodDropAmount = {1, 3}, -- random amount per drop
|
| 14 |
+
|
| 15 |
+
-- Crafting recipes
|
| 16 |
+
Recipes = {
|
| 17 |
+
BasicAxe = {
|
| 18 |
+
Name = "Basic Axe",
|
| 19 |
+
Materials = {
|
| 20 |
+
Wood = 10,
|
| 21 |
+
},
|
| 22 |
+
CraftTime = 2,
|
| 23 |
+
},
|
| 24 |
+
SteelAxe = {
|
| 25 |
+
Name = "Steel Axe",
|
| 26 |
+
Materials = {
|
| 27 |
+
Wood = 30,
|
| 28 |
+
Stone = 5,
|
| 29 |
+
},
|
| 30 |
+
CraftTime = 4,
|
| 31 |
+
},
|
| 32 |
+
GoldAxe = {
|
| 33 |
+
Name = "Gold Axe",
|
| 34 |
+
Materials = {
|
| 35 |
+
Wood = 50,
|
| 36 |
+
Stone = 15,
|
| 37 |
+
GoldOre = 10,
|
| 38 |
+
},
|
| 39 |
+
CraftTime = 6,
|
| 40 |
+
},
|
| 41 |
+
DiamondAxe = {
|
| 42 |
+
Name = "Diamond Axe",
|
| 43 |
+
Materials = {
|
| 44 |
+
Wood = 80,
|
| 45 |
+
Stone = 30,
|
| 46 |
+
GoldOre = 20,
|
| 47 |
+
Diamond = 5,
|
| 48 |
+
},
|
| 49 |
+
CraftTime = 10,
|
| 50 |
+
},
|
| 51 |
+
},
|
| 52 |
+
|
| 53 |
+
-- Resource types and how they are gathered
|
| 54 |
+
Resources = {
|
| 55 |
+
Wood = {
|
| 56 |
+
Source = "TreeSegment",
|
| 57 |
+
Color = Color3.fromRGB(160, 120, 80),
|
| 58 |
+
},
|
| 59 |
+
Stone = {
|
| 60 |
+
Source = "Rock",
|
| 61 |
+
Color = Color3.fromRGB(140, 140, 140),
|
| 62 |
+
},
|
| 63 |
+
GoldOre = {
|
| 64 |
+
Source = "GoldVein",
|
| 65 |
+
Color = Color3.fromRGB(255, 200, 50),
|
| 66 |
+
},
|
| 67 |
+
Diamond = {
|
| 68 |
+
Source = "DiamondVein",
|
| 69 |
+
Color = Color3.fromRGB(100, 200, 255),
|
| 70 |
+
},
|
| 71 |
+
},
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
return CraftingConfig
|
src/ReplicatedStorage/Shared/DraggingConfig.lua
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/DraggingConfig.lua
|
| 2 |
+
local DraggingConfig = {
|
| 3 |
+
MaxGrabDistance = 15, -- How far away the player can grab an object
|
| 4 |
+
HoldDistance = 6, -- The distance at which the object is held in front of the player
|
| 5 |
+
|
| 6 |
+
-- AlignPosition Properties
|
| 7 |
+
AlignPositionMaxForce = 20000,
|
| 8 |
+
AlignPositionResponsiveness = 15,
|
| 9 |
+
|
| 10 |
+
-- AlignOrientation Properties
|
| 11 |
+
AlignOrientationMaxTorque = 20000,
|
| 12 |
+
AlignOrientationResponsiveness = 20,
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
return DraggingConfig
|
src/ReplicatedStorage/Shared/EconomyConfig.lua
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/EconomyConfig.lua
|
| 2 |
+
|
| 3 |
+
local EconomyConfig = {
|
| 4 |
+
-- Base values per unit (Stud^3 roughly) of raw wood
|
| 5 |
+
WoodBaseValues = {
|
| 6 |
+
Oak = 15,
|
| 7 |
+
Pine = 10,
|
| 8 |
+
Birch = 18,
|
| 9 |
+
Walnut = 25,
|
| 10 |
+
Mahogany = 35,
|
| 11 |
+
Elm = 12,
|
| 12 |
+
Redwood = 45,
|
| 13 |
+
GlowWood = 60,
|
| 14 |
+
LavaWood = 80,
|
| 15 |
+
Fireproof = 100,
|
| 16 |
+
Glowing = 120,
|
| 17 |
+
Frozen = 70,
|
| 18 |
+
Crystalline = 200,
|
| 19 |
+
},
|
| 20 |
+
|
| 21 |
+
-- Processing multipliers
|
| 22 |
+
ProcessingMultipliers = {
|
| 23 |
+
Raw = 1.0,
|
| 24 |
+
Stripped = 1.5,
|
| 25 |
+
Plank = 2.5,
|
| 26 |
+
},
|
| 27 |
+
|
| 28 |
+
-- Market fluctuation settings
|
| 29 |
+
MarketFluctuations = {
|
| 30 |
+
Enabled = true,
|
| 31 |
+
UpdateInterval = 300,
|
| 32 |
+
MaxIncrease = 1.3,
|
| 33 |
+
MaxDecrease = 0.7,
|
| 34 |
+
},
|
| 35 |
+
|
| 36 |
+
-- Starter cash amount
|
| 37 |
+
StarterCash = 500,
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
return EconomyConfig
|
src/ReplicatedStorage/Shared/ExplorationConfig.lua
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/ExplorationConfig.lua
|
| 2 |
+
|
| 3 |
+
local ExplorationConfig = {
|
| 4 |
+
Puzzles = {
|
| 5 |
+
WeightSwitch = {
|
| 6 |
+
RequiredWeight = 500,
|
| 7 |
+
ActiveTime = 0,
|
| 8 |
+
},
|
| 9 |
+
LightReceptor = {
|
| 10 |
+
RequiredIntensity = 0.8,
|
| 11 |
+
},
|
| 12 |
+
},
|
| 13 |
+
|
| 14 |
+
Biomes = {
|
| 15 |
+
Swamp = {
|
| 16 |
+
SinkingSpeed = 2,
|
| 17 |
+
DamagePerSecond = 5,
|
| 18 |
+
},
|
| 19 |
+
IcePeak = {
|
| 20 |
+
SlipFriction = 0.05,
|
| 21 |
+
FreezeDamage = 2,
|
| 22 |
+
},
|
| 23 |
+
Desert = {
|
| 24 |
+
HeatDamagePerSecond = 1,
|
| 25 |
+
StaminaDrainMult = 1.5,
|
| 26 |
+
},
|
| 27 |
+
Volcanic = {
|
| 28 |
+
LavaDamagePerSecond = 20,
|
| 29 |
+
SafeDistance = 10,
|
| 30 |
+
},
|
| 31 |
+
TropicalRainforest = {
|
| 32 |
+
VisibilityRange = 100,
|
| 33 |
+
PoisonChance = 0.1,
|
| 34 |
+
},
|
| 35 |
+
Tundra = {
|
| 36 |
+
FreezeDamage = 3,
|
| 37 |
+
SlowMult = 0.7,
|
| 38 |
+
},
|
| 39 |
+
},
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
return ExplorationConfig
|
src/ReplicatedStorage/Shared/GameConfig.lua
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/GameConfig.lua
|
| 2 |
+
|
| 3 |
+
local GameConfig = {
|
| 4 |
+
-- Starting values
|
| 5 |
+
StarterCash = 500,
|
| 6 |
+
StarterAxe = "BasicAxe",
|
| 7 |
+
|
| 8 |
+
-- Leaderstats
|
| 9 |
+
Stats = {
|
| 10 |
+
Cash = "Cash",
|
| 11 |
+
WoodChopped = "WoodChopped",
|
| 12 |
+
},
|
| 13 |
+
|
| 14 |
+
-- World boundaries
|
| 15 |
+
WorldBounds = {
|
| 16 |
+
MinX = -1500,
|
| 17 |
+
MaxX = 1500,
|
| 18 |
+
MinZ = -1500,
|
| 19 |
+
MaxZ = 1500,
|
| 20 |
+
},
|
| 21 |
+
|
| 22 |
+
-- Spawn area
|
| 23 |
+
SpawnPosition = Vector3.new(0, 5, 0),
|
| 24 |
+
SpawnPlatformSize = Vector3.new(60, 1, 60),
|
| 25 |
+
|
| 26 |
+
-- Auto-save
|
| 27 |
+
AutoSaveInterval = 120, -- seconds
|
| 28 |
+
|
| 29 |
+
-- Max players per server
|
| 30 |
+
MaxPlayers = 12,
|
| 31 |
+
|
| 32 |
+
-- Plot spawn positions (evenly spaced)
|
| 33 |
+
PlotPositions = {
|
| 34 |
+
Vector3.new(120, 0.5, 0),
|
| 35 |
+
Vector3.new(240, 0.5, 0),
|
| 36 |
+
Vector3.new(120, 0.5, 220),
|
| 37 |
+
Vector3.new(240, 0.5, 220),
|
| 38 |
+
Vector3.new(-120, 0.5, 0),
|
| 39 |
+
Vector3.new(-240, 0.5, 0),
|
| 40 |
+
Vector3.new(-120, 0.5, 220),
|
| 41 |
+
Vector3.new(-240, 0.5, 220),
|
| 42 |
+
Vector3.new(120, 0.5, -220),
|
| 43 |
+
Vector3.new(240, 0.5, -220),
|
| 44 |
+
Vector3.new(-120, 0.5, -220),
|
| 45 |
+
Vector3.new(-240, 0.5, -220),
|
| 46 |
+
},
|
| 47 |
+
|
| 48 |
+
-- Market area
|
| 49 |
+
MarketPosition = Vector3.new(0, 0.5, -100),
|
| 50 |
+
MarketSize = Vector3.new(40, 10, 40),
|
| 51 |
+
|
| 52 |
+
-- Shop area
|
| 53 |
+
ShopPosition = Vector3.new(60, 0.5, -100),
|
| 54 |
+
ShopSize = Vector3.new(30, 12, 30),
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
return GameConfig
|
src/ReplicatedStorage/Shared/MutationConfig.lua
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/MutationConfig.lua
|
| 2 |
+
|
| 3 |
+
local MutationConfig = {
|
| 4 |
+
Hazards = {
|
| 5 |
+
Lava = {
|
| 6 |
+
Radius = 50,
|
| 7 |
+
MutationChance = 0.5,
|
| 8 |
+
ModifiedType = "Fireproof",
|
| 9 |
+
ValueMult = 2.5,
|
| 10 |
+
Material = Enum.Material.Neon,
|
| 11 |
+
Color = Color3.fromRGB(255, 100, 0),
|
| 12 |
+
},
|
| 13 |
+
Toxic = {
|
| 14 |
+
Radius = 60,
|
| 15 |
+
MutationChance = 0.3,
|
| 16 |
+
ModifiedType = "Glowing",
|
| 17 |
+
ValueMult = 3.0,
|
| 18 |
+
Material = Enum.Material.Neon,
|
| 19 |
+
Color = Color3.fromRGB(0, 255, 100),
|
| 20 |
+
},
|
| 21 |
+
Frozen = {
|
| 22 |
+
Radius = 45,
|
| 23 |
+
MutationChance = 0.4,
|
| 24 |
+
ModifiedType = "Frozen",
|
| 25 |
+
ValueMult = 2.0,
|
| 26 |
+
Material = Enum.Material.Ice,
|
| 27 |
+
Color = Color3.fromRGB(150, 200, 255),
|
| 28 |
+
},
|
| 29 |
+
Crystalline = {
|
| 30 |
+
Radius = 30,
|
| 31 |
+
MutationChance = 0.15,
|
| 32 |
+
ModifiedType = "Crystalline",
|
| 33 |
+
ValueMult = 5.0,
|
| 34 |
+
Material = Enum.Material.Glass,
|
| 35 |
+
Color = Color3.fromRGB(200, 160, 255),
|
| 36 |
+
},
|
| 37 |
+
},
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
return MutationConfig
|
src/ReplicatedStorage/Shared/NPCConfig.lua
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/NPCConfig.lua
|
| 2 |
+
|
| 3 |
+
local NPCConfig = {
|
| 4 |
+
NPCs = {
|
| 5 |
+
Lumberjack_Larry = {
|
| 6 |
+
DisplayName = "Larry the Lumberjack",
|
| 7 |
+
Position = Vector3.new(10, 3, -80),
|
| 8 |
+
BodyColors = {
|
| 9 |
+
HeadColor = Color3.fromRGB(234, 184, 146),
|
| 10 |
+
TorsoColor = Color3.fromRGB(180, 40, 40),
|
| 11 |
+
LeftArmColor = Color3.fromRGB(234, 184, 146),
|
| 12 |
+
RightArmColor = Color3.fromRGB(234, 184, 146),
|
| 13 |
+
LeftLegColor = Color3.fromRGB(50, 50, 130),
|
| 14 |
+
RightLegColor = Color3.fromRGB(50, 50, 130),
|
| 15 |
+
},
|
| 16 |
+
Dialogue = {
|
| 17 |
+
"Welcome to Timberbound Expeditions, partner!",
|
| 18 |
+
"Grab your axe and start choppin' trees!",
|
| 19 |
+
"Process logs at the sawmill for better prices.",
|
| 20 |
+
"Watch out for the rain -- it ruins unprotected wood!",
|
| 21 |
+
},
|
| 22 |
+
QuestIds = {"ChopFirstTree", "Chop10Trees"},
|
| 23 |
+
Role = "Guide",
|
| 24 |
+
},
|
| 25 |
+
Shopkeeper_Sue = {
|
| 26 |
+
DisplayName = "Sue's Supply Shop",
|
| 27 |
+
Position = Vector3.new(60, 3, -90),
|
| 28 |
+
BodyColors = {
|
| 29 |
+
HeadColor = Color3.fromRGB(234, 184, 146),
|
| 30 |
+
TorsoColor = Color3.fromRGB(50, 150, 50),
|
| 31 |
+
LeftArmColor = Color3.fromRGB(234, 184, 146),
|
| 32 |
+
RightArmColor = Color3.fromRGB(234, 184, 146),
|
| 33 |
+
LeftLegColor = Color3.fromRGB(90, 70, 50),
|
| 34 |
+
RightLegColor = Color3.fromRGB(90, 70, 50),
|
| 35 |
+
},
|
| 36 |
+
Dialogue = {
|
| 37 |
+
"Welcome to my shop! Take a look around.",
|
| 38 |
+
"I've got axes, sawmills, and more!",
|
| 39 |
+
"Drag items to the counter and talk to me to buy.",
|
| 40 |
+
},
|
| 41 |
+
QuestIds = {"BuyFirstItem"},
|
| 42 |
+
Role = "Shopkeeper",
|
| 43 |
+
},
|
| 44 |
+
Market_Mike = {
|
| 45 |
+
DisplayName = "Market Mike",
|
| 46 |
+
Position = Vector3.new(0, 3, -110),
|
| 47 |
+
BodyColors = {
|
| 48 |
+
HeadColor = Color3.fromRGB(180, 140, 100),
|
| 49 |
+
TorsoColor = Color3.fromRGB(200, 180, 50),
|
| 50 |
+
LeftArmColor = Color3.fromRGB(180, 140, 100),
|
| 51 |
+
RightArmColor = Color3.fromRGB(180, 140, 100),
|
| 52 |
+
LeftLegColor = Color3.fromRGB(40, 40, 40),
|
| 53 |
+
RightLegColor = Color3.fromRGB(40, 40, 40),
|
| 54 |
+
},
|
| 55 |
+
Dialogue = {
|
| 56 |
+
"Bring your wood here and I'll buy it!",
|
| 57 |
+
"Planks are worth more than raw logs.",
|
| 58 |
+
"Market prices shift every few minutes. Sell smart!",
|
| 59 |
+
},
|
| 60 |
+
QuestIds = {"SellFirstWood", "Earn5000"},
|
| 61 |
+
Role = "Buyer",
|
| 62 |
+
},
|
| 63 |
+
Explorer_Eva = {
|
| 64 |
+
DisplayName = "Explorer Eva",
|
| 65 |
+
Position = Vector3.new(-30, 3, -80),
|
| 66 |
+
BodyColors = {
|
| 67 |
+
HeadColor = Color3.fromRGB(210, 170, 130),
|
| 68 |
+
TorsoColor = Color3.fromRGB(100, 70, 40),
|
| 69 |
+
LeftArmColor = Color3.fromRGB(210, 170, 130),
|
| 70 |
+
RightArmColor = Color3.fromRGB(210, 170, 130),
|
| 71 |
+
LeftLegColor = Color3.fromRGB(60, 80, 50),
|
| 72 |
+
RightLegColor = Color3.fromRGB(60, 80, 50),
|
| 73 |
+
},
|
| 74 |
+
Dialogue = {
|
| 75 |
+
"This world is full of secrets!",
|
| 76 |
+
"Different biomes have different tree types.",
|
| 77 |
+
"Some rare woods glow in the dark... and they're worth a fortune!",
|
| 78 |
+
"Have you found the volcanic region yet?",
|
| 79 |
+
},
|
| 80 |
+
QuestIds = {"ExploreAllBiomes"},
|
| 81 |
+
Role = "Explorer",
|
| 82 |
+
},
|
| 83 |
+
},
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
return NPCConfig
|
src/ReplicatedStorage/Shared/PlotConfig.lua
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/PlotConfig.lua
|
| 2 |
+
local PlotConfig = {
|
| 3 |
+
GridSize = 2, -- Grid snapping increment in studs
|
| 4 |
+
MaxItemsPerPlot = 1000, -- Limit for performance
|
| 5 |
+
|
| 6 |
+
-- Dimensions of the claimable land
|
| 7 |
+
PlotDimensions = Vector3.new(200, 1, 200),
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
return PlotConfig
|
src/ReplicatedStorage/Shared/QuestConfig.lua
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/QuestConfig.lua
|
| 2 |
+
|
| 3 |
+
local QuestConfig = {
|
| 4 |
+
Quests = {
|
| 5 |
+
ChopFirstTree = {
|
| 6 |
+
Title = "First Timber",
|
| 7 |
+
Description = "Chop down your first tree segment.",
|
| 8 |
+
Type = "Chop",
|
| 9 |
+
Target = 1,
|
| 10 |
+
Reward = {Cash = 100},
|
| 11 |
+
Icon = "rbxassetid://0", -- placeholder
|
| 12 |
+
},
|
| 13 |
+
Chop10Trees = {
|
| 14 |
+
Title = "Getting Started",
|
| 15 |
+
Description = "Chop 10 tree segments.",
|
| 16 |
+
Type = "Chop",
|
| 17 |
+
Target = 10,
|
| 18 |
+
Reward = {Cash = 500},
|
| 19 |
+
Icon = "rbxassetid://0",
|
| 20 |
+
},
|
| 21 |
+
Chop50Trees = {
|
| 22 |
+
Title = "Lumberjack",
|
| 23 |
+
Description = "Chop 50 tree segments.",
|
| 24 |
+
Type = "Chop",
|
| 25 |
+
Target = 50,
|
| 26 |
+
Reward = {Cash = 2500},
|
| 27 |
+
Icon = "rbxassetid://0",
|
| 28 |
+
},
|
| 29 |
+
Chop200Trees = {
|
| 30 |
+
Title = "Master Forester",
|
| 31 |
+
Description = "Chop 200 tree segments.",
|
| 32 |
+
Type = "Chop",
|
| 33 |
+
Target = 200,
|
| 34 |
+
Reward = {Cash = 10000},
|
| 35 |
+
Icon = "rbxassetid://0",
|
| 36 |
+
},
|
| 37 |
+
SellFirstWood = {
|
| 38 |
+
Title = "First Sale",
|
| 39 |
+
Description = "Sell wood at the market for the first time.",
|
| 40 |
+
Type = "Sell",
|
| 41 |
+
Target = 1,
|
| 42 |
+
Reward = {Cash = 200},
|
| 43 |
+
Icon = "rbxassetid://0",
|
| 44 |
+
},
|
| 45 |
+
Earn5000 = {
|
| 46 |
+
Title = "Money Maker",
|
| 47 |
+
Description = "Earn a total of $5,000 from selling wood.",
|
| 48 |
+
Type = "EarnCash",
|
| 49 |
+
Target = 5000,
|
| 50 |
+
Reward = {Cash = 1000},
|
| 51 |
+
Icon = "rbxassetid://0",
|
| 52 |
+
},
|
| 53 |
+
Earn50000 = {
|
| 54 |
+
Title = "Timber Tycoon",
|
| 55 |
+
Description = "Earn a total of $50,000.",
|
| 56 |
+
Type = "EarnCash",
|
| 57 |
+
Target = 50000,
|
| 58 |
+
Reward = {Cash = 10000},
|
| 59 |
+
Icon = "rbxassetid://0",
|
| 60 |
+
},
|
| 61 |
+
BuildFirstStructure = {
|
| 62 |
+
Title = "Builder",
|
| 63 |
+
Description = "Construct your first building.",
|
| 64 |
+
Type = "Build",
|
| 65 |
+
Target = 1,
|
| 66 |
+
Reward = {Cash = 300},
|
| 67 |
+
Icon = "rbxassetid://0",
|
| 68 |
+
},
|
| 69 |
+
Build10Structures = {
|
| 70 |
+
Title = "Architect",
|
| 71 |
+
Description = "Construct 10 buildings.",
|
| 72 |
+
Type = "Build",
|
| 73 |
+
Target = 10,
|
| 74 |
+
Reward = {Cash = 3000},
|
| 75 |
+
Icon = "rbxassetid://0",
|
| 76 |
+
},
|
| 77 |
+
ExploreAllBiomes = {
|
| 78 |
+
Title = "Explorer",
|
| 79 |
+
Description = "Visit every biome in the world.",
|
| 80 |
+
Type = "Explore",
|
| 81 |
+
Target = 8, -- number of biomes
|
| 82 |
+
Reward = {Cash = 5000},
|
| 83 |
+
Icon = "rbxassetid://0",
|
| 84 |
+
},
|
| 85 |
+
ProcessFirstPlank = {
|
| 86 |
+
Title = "Mill Worker",
|
| 87 |
+
Description = "Process a log into a plank at the sawmill.",
|
| 88 |
+
Type = "Process",
|
| 89 |
+
Target = 1,
|
| 90 |
+
Reward = {Cash = 150},
|
| 91 |
+
Icon = "rbxassetid://0",
|
| 92 |
+
},
|
| 93 |
+
BuyFirstItem = {
|
| 94 |
+
Title = "First Purchase",
|
| 95 |
+
Description = "Buy an item from the shop.",
|
| 96 |
+
Type = "Purchase",
|
| 97 |
+
Target = 1,
|
| 98 |
+
Reward = {Cash = 100},
|
| 99 |
+
Icon = "rbxassetid://0",
|
| 100 |
+
},
|
| 101 |
+
},
|
| 102 |
+
|
| 103 |
+
-- Quest ordering (which quests unlock first)
|
| 104 |
+
StarterQuests = {"ChopFirstTree", "SellFirstWood", "BuildFirstStructure", "BuyFirstItem"},
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
return QuestConfig
|
src/ReplicatedStorage/Shared/ShopConfig.lua
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/ShopConfig.lua
|
| 2 |
+
|
| 3 |
+
local ShopConfig = {
|
| 4 |
+
Items = {
|
| 5 |
+
BasicAxe = {
|
| 6 |
+
Price = 0, -- Free starter tool
|
| 7 |
+
Name = "Basic Axe",
|
| 8 |
+
Type = "Tool",
|
| 9 |
+
Description = "A simple axe for chopping trees.",
|
| 10 |
+
},
|
| 11 |
+
SteelAxe = {
|
| 12 |
+
Price = 1200,
|
| 13 |
+
Name = "Steel Axe",
|
| 14 |
+
Type = "Tool",
|
| 15 |
+
Description = "Stronger than basic. Faster swings.",
|
| 16 |
+
},
|
| 17 |
+
GoldAxe = {
|
| 18 |
+
Price = 5000,
|
| 19 |
+
Name = "Gold Axe",
|
| 20 |
+
Type = "Tool",
|
| 21 |
+
Description = "High damage with quick recovery.",
|
| 22 |
+
},
|
| 23 |
+
DiamondAxe = {
|
| 24 |
+
Price = 25000,
|
| 25 |
+
Name = "Diamond Axe",
|
| 26 |
+
Type = "Tool",
|
| 27 |
+
Description = "The ultimate axe. Cuts anything fast.",
|
| 28 |
+
},
|
| 29 |
+
BarkStripper = {
|
| 30 |
+
Price = 800,
|
| 31 |
+
Name = "Bark Stripper Machine",
|
| 32 |
+
Type = "Machine",
|
| 33 |
+
Description = "Strips bark from raw logs.",
|
| 34 |
+
},
|
| 35 |
+
Sawmill = {
|
| 36 |
+
Price = 3500,
|
| 37 |
+
Name = "Basic Sawmill",
|
| 38 |
+
Type = "Machine",
|
| 39 |
+
Description = "Converts logs into valuable planks.",
|
| 40 |
+
},
|
| 41 |
+
AdvancedSawmill = {
|
| 42 |
+
Price = 15000,
|
| 43 |
+
Name = "Advanced Sawmill",
|
| 44 |
+
Type = "Machine",
|
| 45 |
+
Description = "Processes logs faster and cleaner.",
|
| 46 |
+
},
|
| 47 |
+
Conveyor = {
|
| 48 |
+
Price = 500,
|
| 49 |
+
Name = "Conveyor Belt",
|
| 50 |
+
Type = "Machine",
|
| 51 |
+
Description = "Moves items automatically.",
|
| 52 |
+
},
|
| 53 |
+
Flatbed = {
|
| 54 |
+
Price = 8000,
|
| 55 |
+
Name = "Flatbed Truck",
|
| 56 |
+
Type = "Vehicle",
|
| 57 |
+
Description = "Transport large loads of lumber.",
|
| 58 |
+
},
|
| 59 |
+
BasicDrone = {
|
| 60 |
+
Price = 12000,
|
| 61 |
+
Name = "Basic Delivery Drone",
|
| 62 |
+
Type = "Automation",
|
| 63 |
+
Description = "Automatically carries small loads.",
|
| 64 |
+
},
|
| 65 |
+
AdvancedDrone = {
|
| 66 |
+
Price = 35000,
|
| 67 |
+
Name = "Advanced Delivery Drone",
|
| 68 |
+
Type = "Automation",
|
| 69 |
+
Description = "Carries heavy loads at high speed.",
|
| 70 |
+
},
|
| 71 |
+
WoodShelter = {
|
| 72 |
+
Price = 2000,
|
| 73 |
+
Name = "Rain Shelter Kit",
|
| 74 |
+
Type = "Structure",
|
| 75 |
+
Description = "Protects wood from rain degradation.",
|
| 76 |
+
},
|
| 77 |
+
WireKit = {
|
| 78 |
+
Price = 300,
|
| 79 |
+
Name = "Wire Kit",
|
| 80 |
+
Type = "Utility",
|
| 81 |
+
Description = "Connect components with wires.",
|
| 82 |
+
},
|
| 83 |
+
Button = {
|
| 84 |
+
Price = 200,
|
| 85 |
+
Name = "Logic Button",
|
| 86 |
+
Type = "Component",
|
| 87 |
+
Description = "Wire source: sends pulse signal.",
|
| 88 |
+
},
|
| 89 |
+
Lever = {
|
| 90 |
+
Price = 250,
|
| 91 |
+
Name = "Logic Lever",
|
| 92 |
+
Type = "Component",
|
| 93 |
+
Description = "Wire source: toggles on/off.",
|
| 94 |
+
},
|
| 95 |
+
},
|
| 96 |
+
|
| 97 |
+
-- Category ordering for ship GUI
|
| 98 |
+
Categories = {"Tool", "Machine", "Vehicle", "Automation", "Structure", "Utility", "Component"},
|
| 99 |
+
|
| 100 |
+
-- Items per category (for display ordering)
|
| 101 |
+
CategoryItems = {
|
| 102 |
+
Tool = {"BasicAxe", "SteelAxe", "GoldAxe", "DiamondAxe"},
|
| 103 |
+
Machine = {"BarkStripper", "Sawmill", "AdvancedSawmill", "Conveyor"},
|
| 104 |
+
Vehicle = {"Flatbed"},
|
| 105 |
+
Automation = {"BasicDrone", "AdvancedDrone"},
|
| 106 |
+
Structure = {"WoodShelter"},
|
| 107 |
+
Utility = {"WireKit"},
|
| 108 |
+
Component = {"Button", "Lever"},
|
| 109 |
+
},
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
return ShopConfig
|
src/ReplicatedStorage/Shared/SoundConfig.lua
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/SoundConfig.lua
|
| 2 |
+
-- Sound asset IDs (using Roblox default content where available)
|
| 3 |
+
|
| 4 |
+
local SoundConfig = {
|
| 5 |
+
-- Tool Sounds
|
| 6 |
+
AxeSwing = "rbxasset://sounds/snap.mp3",
|
| 7 |
+
AxeHitWood = "rbxasset://sounds/impact_wood.mp3",
|
| 8 |
+
TreeFall = "rbxasset://sounds/bass.mp3",
|
| 9 |
+
|
| 10 |
+
-- Economy
|
| 11 |
+
CashRegister = "rbxasset://sounds/electronicpingshort.wav",
|
| 12 |
+
PurchaseSuccess = "rbxasset://sounds/electronicpingshort.wav",
|
| 13 |
+
PurchaseFail = "rbxasset://sounds/bass.mp3",
|
| 14 |
+
|
| 15 |
+
-- Building
|
| 16 |
+
PlaceBlueprint = "rbxasset://sounds/snap.mp3",
|
| 17 |
+
ConstructComplete = "rbxasset://sounds/electronicpingshort.wav",
|
| 18 |
+
|
| 19 |
+
-- UI
|
| 20 |
+
ButtonClick = "rbxasset://sounds/snap.mp3",
|
| 21 |
+
QuestComplete = "rbxasset://sounds/electronicpingshort.wav",
|
| 22 |
+
Notification = "rbxasset://sounds/electronicpingshort.wav",
|
| 23 |
+
|
| 24 |
+
-- Weather ambient
|
| 25 |
+
RainLoop = "rbxasset://sounds/bass.mp3",
|
| 26 |
+
WindLoop = "rbxasset://sounds/snap.mp3",
|
| 27 |
+
|
| 28 |
+
-- General ambient
|
| 29 |
+
ForestAmbient = "rbxasset://sounds/snap.mp3",
|
| 30 |
+
SwampAmbient = "rbxasset://sounds/bass.mp3",
|
| 31 |
+
|
| 32 |
+
-- Volume levels
|
| 33 |
+
MasterVolume = 0.8,
|
| 34 |
+
SFXVolume = 1.0,
|
| 35 |
+
MusicVolume = 0.5,
|
| 36 |
+
AmbientVolume = 0.3,
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
return SoundConfig
|
src/ReplicatedStorage/Shared/TreeModelConfig.lua
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/TreeModelConfig.lua
|
| 2 |
+
|
| 3 |
+
local TreeModelConfig = {
|
| 4 |
+
Templates = {
|
| 5 |
+
Oak = {
|
| 6 |
+
TrunkDiameter = {2.5, 3.5}, -- min, max
|
| 7 |
+
TrunkHeight = {12, 20},
|
| 8 |
+
SegmentCount = {3, 5},
|
| 9 |
+
CanopyRadius = {6, 10},
|
| 10 |
+
CanopySegments = 5,
|
| 11 |
+
LeafColor = Color3.fromRGB(50, 120, 40),
|
| 12 |
+
LeafMaterial = Enum.Material.LeafyGrass,
|
| 13 |
+
BarkColor = Color3.fromRGB(133, 94, 66),
|
| 14 |
+
BarkMaterial = Enum.Material.Wood,
|
| 15 |
+
HasLeaves = true,
|
| 16 |
+
},
|
| 17 |
+
Pine = {
|
| 18 |
+
TrunkDiameter = {1.5, 2.5},
|
| 19 |
+
TrunkHeight = {15, 25},
|
| 20 |
+
SegmentCount = {4, 6},
|
| 21 |
+
CanopyRadius = {3, 5},
|
| 22 |
+
CanopySegments = 6,
|
| 23 |
+
LeafColor = Color3.fromRGB(30, 80, 30),
|
| 24 |
+
LeafMaterial = Enum.Material.LeafyGrass,
|
| 25 |
+
BarkColor = Color3.fromRGB(158, 127, 98),
|
| 26 |
+
BarkMaterial = Enum.Material.Wood,
|
| 27 |
+
HasLeaves = true,
|
| 28 |
+
ConicalCanopy = true,
|
| 29 |
+
},
|
| 30 |
+
Birch = {
|
| 31 |
+
TrunkDiameter = {1.5, 2.0},
|
| 32 |
+
TrunkHeight = {14, 22},
|
| 33 |
+
SegmentCount = {4, 6},
|
| 34 |
+
CanopyRadius = {4, 7},
|
| 35 |
+
CanopySegments = 4,
|
| 36 |
+
LeafColor = Color3.fromRGB(100, 170, 60),
|
| 37 |
+
LeafMaterial = Enum.Material.LeafyGrass,
|
| 38 |
+
BarkColor = Color3.fromRGB(220, 215, 200),
|
| 39 |
+
BarkMaterial = Enum.Material.Marble,
|
| 40 |
+
HasLeaves = true,
|
| 41 |
+
},
|
| 42 |
+
Walnut = {
|
| 43 |
+
TrunkDiameter = {2.0, 3.0},
|
| 44 |
+
TrunkHeight = {10, 16},
|
| 45 |
+
SegmentCount = {3, 4},
|
| 46 |
+
CanopyRadius = {5, 8},
|
| 47 |
+
CanopySegments = 5,
|
| 48 |
+
LeafColor = Color3.fromRGB(60, 100, 30),
|
| 49 |
+
LeafMaterial = Enum.Material.LeafyGrass,
|
| 50 |
+
BarkColor = Color3.fromRGB(80, 50, 30),
|
| 51 |
+
BarkMaterial = Enum.Material.Wood,
|
| 52 |
+
HasLeaves = true,
|
| 53 |
+
},
|
| 54 |
+
Mahogany = {
|
| 55 |
+
TrunkDiameter = {3.0, 4.5},
|
| 56 |
+
TrunkHeight = {18, 28},
|
| 57 |
+
SegmentCount = {5, 7},
|
| 58 |
+
CanopyRadius = {8, 12},
|
| 59 |
+
CanopySegments = 6,
|
| 60 |
+
LeafColor = Color3.fromRGB(40, 90, 25),
|
| 61 |
+
LeafMaterial = Enum.Material.LeafyGrass,
|
| 62 |
+
BarkColor = Color3.fromRGB(100, 40, 20),
|
| 63 |
+
BarkMaterial = Enum.Material.Wood,
|
| 64 |
+
HasLeaves = true,
|
| 65 |
+
},
|
| 66 |
+
Elm = {
|
| 67 |
+
TrunkDiameter = {2.0, 3.0},
|
| 68 |
+
TrunkHeight = {12, 18},
|
| 69 |
+
SegmentCount = {3, 5},
|
| 70 |
+
CanopyRadius = {5, 9},
|
| 71 |
+
CanopySegments = 5,
|
| 72 |
+
LeafColor = Color3.fromRGB(80, 140, 50),
|
| 73 |
+
LeafMaterial = Enum.Material.LeafyGrass,
|
| 74 |
+
BarkColor = Color3.fromRGB(110, 80, 55),
|
| 75 |
+
BarkMaterial = Enum.Material.Wood,
|
| 76 |
+
HasLeaves = true,
|
| 77 |
+
},
|
| 78 |
+
Redwood = {
|
| 79 |
+
TrunkDiameter = {4.0, 6.0},
|
| 80 |
+
TrunkHeight = {30, 50},
|
| 81 |
+
SegmentCount = {6, 10},
|
| 82 |
+
CanopyRadius = {6, 10},
|
| 83 |
+
CanopySegments = 4,
|
| 84 |
+
LeafColor = Color3.fromRGB(35, 70, 30),
|
| 85 |
+
LeafMaterial = Enum.Material.LeafyGrass,
|
| 86 |
+
BarkColor = Color3.fromRGB(120, 50, 30),
|
| 87 |
+
BarkMaterial = Enum.Material.Wood,
|
| 88 |
+
HasLeaves = true,
|
| 89 |
+
},
|
| 90 |
+
GlowWood = {
|
| 91 |
+
TrunkDiameter = {1.5, 2.5},
|
| 92 |
+
TrunkHeight = {8, 14},
|
| 93 |
+
SegmentCount = {3, 4},
|
| 94 |
+
CanopyRadius = {4, 6},
|
| 95 |
+
CanopySegments = 4,
|
| 96 |
+
LeafColor = Color3.fromRGB(0, 255, 100),
|
| 97 |
+
LeafMaterial = Enum.Material.Neon,
|
| 98 |
+
BarkColor = Color3.fromRGB(60, 180, 80),
|
| 99 |
+
BarkMaterial = Enum.Material.SmoothPlastic,
|
| 100 |
+
HasLeaves = true,
|
| 101 |
+
GlowTrunk = true,
|
| 102 |
+
},
|
| 103 |
+
LavaWood = {
|
| 104 |
+
TrunkDiameter = {2.0, 3.5},
|
| 105 |
+
TrunkHeight = {8, 14},
|
| 106 |
+
SegmentCount = {3, 4},
|
| 107 |
+
CanopyRadius = {3, 5},
|
| 108 |
+
CanopySegments = 3,
|
| 109 |
+
LeafColor = Color3.fromRGB(255, 120, 0),
|
| 110 |
+
LeafMaterial = Enum.Material.Neon,
|
| 111 |
+
BarkColor = Color3.fromRGB(50, 30, 20),
|
| 112 |
+
BarkMaterial = Enum.Material.CrackedLava,
|
| 113 |
+
HasLeaves = true,
|
| 114 |
+
GlowTrunk = true,
|
| 115 |
+
},
|
| 116 |
+
},
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
return TreeModelConfig
|
src/ReplicatedStorage/Shared/Utility.lua
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/Utility.lua
|
| 2 |
+
|
| 3 |
+
local Utility = {}
|
| 4 |
+
|
| 5 |
+
function Utility.formatCash(amount)
|
| 6 |
+
if amount >= 1000000 then
|
| 7 |
+
return string.format("$%.1fM", amount / 1000000)
|
| 8 |
+
elseif amount >= 1000 then
|
| 9 |
+
return string.format("$%.1fK", amount / 1000)
|
| 10 |
+
else
|
| 11 |
+
return "$" .. tostring(math.floor(amount))
|
| 12 |
+
end
|
| 13 |
+
end
|
| 14 |
+
|
| 15 |
+
function Utility.formatTime(seconds)
|
| 16 |
+
local min = math.floor(seconds / 60)
|
| 17 |
+
local sec = math.floor(seconds % 60)
|
| 18 |
+
return string.format("%d:%02d", min, sec)
|
| 19 |
+
end
|
| 20 |
+
|
| 21 |
+
function Utility.lerp(a, b, t)
|
| 22 |
+
return a + (b - a) * t
|
| 23 |
+
end
|
| 24 |
+
|
| 25 |
+
function Utility.getPartVolume(part)
|
| 26 |
+
if not part or not part:IsA("BasePart") then return 0 end
|
| 27 |
+
return part.Size.X * part.Size.Y * part.Size.Z
|
| 28 |
+
end
|
| 29 |
+
|
| 30 |
+
function Utility.randomInRange(min, max)
|
| 31 |
+
return min + math.random() * (max - min)
|
| 32 |
+
end
|
| 33 |
+
|
| 34 |
+
function Utility.randomIntInRange(min, max)
|
| 35 |
+
return math.random(min, max)
|
| 36 |
+
end
|
| 37 |
+
|
| 38 |
+
function Utility.clampVector3(vec, minBounds, maxBounds)
|
| 39 |
+
return Vector3.new(
|
| 40 |
+
math.clamp(vec.X, minBounds.X, maxBounds.X),
|
| 41 |
+
math.clamp(vec.Y, minBounds.Y, maxBounds.Y),
|
| 42 |
+
math.clamp(vec.Z, minBounds.Z, maxBounds.Z)
|
| 43 |
+
)
|
| 44 |
+
end
|
| 45 |
+
|
| 46 |
+
function Utility.shuffleTable(tbl)
|
| 47 |
+
local shuffled = {}
|
| 48 |
+
for _, v in ipairs(tbl) do
|
| 49 |
+
table.insert(shuffled, v)
|
| 50 |
+
end
|
| 51 |
+
for i = #shuffled, 2, -1 do
|
| 52 |
+
local j = math.random(1, i)
|
| 53 |
+
shuffled[i], shuffled[j] = shuffled[j], shuffled[i]
|
| 54 |
+
end
|
| 55 |
+
return shuffled
|
| 56 |
+
end
|
| 57 |
+
|
| 58 |
+
function Utility.tableContains(tbl, value)
|
| 59 |
+
for _, v in ipairs(tbl) do
|
| 60 |
+
if v == value then return true end
|
| 61 |
+
end
|
| 62 |
+
return false
|
| 63 |
+
end
|
| 64 |
+
|
| 65 |
+
function Utility.tableKeys(tbl)
|
| 66 |
+
local keys = {}
|
| 67 |
+
for k, _ in pairs(tbl) do
|
| 68 |
+
table.insert(keys, k)
|
| 69 |
+
end
|
| 70 |
+
return keys
|
| 71 |
+
end
|
| 72 |
+
|
| 73 |
+
function Utility.deepCopy(original)
|
| 74 |
+
local copy = {}
|
| 75 |
+
for k, v in pairs(original) do
|
| 76 |
+
if type(v) == "table" then
|
| 77 |
+
copy[k] = Utility.deepCopy(v)
|
| 78 |
+
else
|
| 79 |
+
copy[k] = v
|
| 80 |
+
end
|
| 81 |
+
end
|
| 82 |
+
return copy
|
| 83 |
+
end
|
| 84 |
+
|
| 85 |
+
return Utility
|
src/ReplicatedStorage/Shared/VehicleConfig.lua
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/VehicleConfig.lua
|
| 2 |
+
local VehicleConfig = {
|
| 3 |
+
-- Physical properties for the truck bed to prevent logs from sliding off easily while driving
|
| 4 |
+
-- Density, Friction, Elasticity, FrictionWeight, ElasticityWeight
|
| 5 |
+
-- Extremely high Friction and FrictionWeight to lock the logs to the bed
|
| 6 |
+
FlatbedPhysicalProperties = PhysicalProperties.new(1.0, 2.0, 0, 100, 100),
|
| 7 |
+
|
| 8 |
+
CollisionGroups = {
|
| 9 |
+
Logs = "Logs",
|
| 10 |
+
Vehicles = "Vehicles",
|
| 11 |
+
}
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
return VehicleConfig
|
src/ReplicatedStorage/Shared/WeatherConfig.lua
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/WeatherConfig.lua
|
| 2 |
+
|
| 3 |
+
local WeatherConfig = {
|
| 4 |
+
CycleDurationMin = 300, -- 5 mins of clear weather
|
| 5 |
+
CycleDurationMax = 600, -- 10 mins
|
| 6 |
+
|
| 7 |
+
RainDurationMin = 60, -- 1 minute of rain
|
| 8 |
+
RainDurationMax = 180, -- 3 minutes of rain
|
| 9 |
+
|
| 10 |
+
DegradationConfig = {
|
| 11 |
+
WoodValueLossPerTick = 0.05, -- 5% loss per tick
|
| 12 |
+
TickInterval = 3, -- Every 3 seconds in rain
|
| 13 |
+
MaxValueLoss = 0.5, -- Logs won't lose more than 50% of value
|
| 14 |
+
}
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
return WeatherConfig
|
src/ReplicatedStorage/Shared/WireLogicConfig.lua
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ReplicatedStorage/Shared/WireLogicConfig.lua
|
| 2 |
+
|
| 3 |
+
local WireLogicConfig = {
|
| 4 |
+
MaxWireLength = 50, -- Maximum distance between two connected nodes
|
| 5 |
+
|
| 6 |
+
-- Defining the kinds of components available
|
| 7 |
+
ComponentTypes = {
|
| 8 |
+
Source = {"Button", "Lever", "PressurePlate"},
|
| 9 |
+
Logic = {"AND", "OR", "NOT", "Delay", "Timer"},
|
| 10 |
+
Target = {"Door", "Sawmill", "Conveyor", "Light"}
|
| 11 |
+
}
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
return WireLogicConfig
|
src/ServerScriptService/AchievementManager.server.lua
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ServerScriptService/AchievementManager.server.lua
|
| 2 |
+
|
| 3 |
+
local Players = game:GetService("Players")
|
| 4 |
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
| 5 |
+
|
| 6 |
+
local NotificationEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("NotificationEvent")
|
| 7 |
+
|
| 8 |
+
-- Achievement definitions
|
| 9 |
+
local Achievements = {
|
| 10 |
+
{id = "first_chop", title = "First Swing", description = "Chop your first tree segment", stat = "WoodChopped", target = 1},
|
| 11 |
+
{id = "chop_100", title = "Century Cutter", description = "Chop 100 tree segments", stat = "WoodChopped", target = 100},
|
| 12 |
+
{id = "chop_500", title = "Lumber Legend", description = "Chop 500 tree segments", stat = "WoodChopped", target = 500},
|
| 13 |
+
{id = "chop_1000", title = "Timber Titan", description = "Chop 1000 tree segments", stat = "WoodChopped", target = 1000},
|
| 14 |
+
{id = "earn_1000", title = "First Thousand", description = "Earn $1,000 total", stat = "TotalEarned", target = 1000},
|
| 15 |
+
{id = "earn_10000", title = "Money Bags", description = "Earn $10,000 total", stat = "TotalEarned", target = 10000},
|
| 16 |
+
{id = "earn_100000", title = "Timber Mogul", description = "Earn $100,000 total", stat = "TotalEarned", target = 100000},
|
| 17 |
+
{id = "build_1", title = "First Foundation", description = "Build your first structure", stat = "TotalBuilt", target = 1},
|
| 18 |
+
{id = "build_25", title = "Master Builder", description = "Build 25 structures", stat = "TotalBuilt", target = 25},
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
-- Track awarded achievements per player
|
| 22 |
+
local playerAchievements = {} -- [userId] = {achievementId = true, ...}
|
| 23 |
+
|
| 24 |
+
local function checkAchievements(player)
|
| 25 |
+
local data = _G.GetPlayerData(player)
|
| 26 |
+
if not data then return end
|
| 27 |
+
|
| 28 |
+
if not playerAchievements[player.UserId] then
|
| 29 |
+
playerAchievements[player.UserId] = {}
|
| 30 |
+
end
|
| 31 |
+
|
| 32 |
+
local leaderstats = player:FindFirstChild("leaderstats")
|
| 33 |
+
|
| 34 |
+
for _, achievement in ipairs(Achievements) do
|
| 35 |
+
if playerAchievements[player.UserId][achievement.id] then continue end
|
| 36 |
+
|
| 37 |
+
local currentValue = 0
|
| 38 |
+
|
| 39 |
+
-- Check leaderstats
|
| 40 |
+
if leaderstats then
|
| 41 |
+
local stat = leaderstats:FindFirstChild(achievement.stat)
|
| 42 |
+
if stat then
|
| 43 |
+
currentValue = stat.Value
|
| 44 |
+
end
|
| 45 |
+
end
|
| 46 |
+
|
| 47 |
+
-- Check player data
|
| 48 |
+
if data[achievement.stat] then
|
| 49 |
+
currentValue = data[achievement.stat]
|
| 50 |
+
end
|
| 51 |
+
|
| 52 |
+
if currentValue >= achievement.target then
|
| 53 |
+
playerAchievements[player.UserId][achievement.id] = true
|
| 54 |
+
NotificationEvent:FireClient(player, "Achievement", achievement.title .. " - " .. achievement.description)
|
| 55 |
+
end
|
| 56 |
+
end
|
| 57 |
+
end
|
| 58 |
+
|
| 59 |
+
-- Expose for other managers
|
| 60 |
+
_G.CheckAchievements = function(player)
|
| 61 |
+
checkAchievements(player)
|
| 62 |
+
end
|
| 63 |
+
|
| 64 |
+
_G.GetPlayerAchievements = function(player)
|
| 65 |
+
return playerAchievements[player.UserId] or {}
|
| 66 |
+
end
|
| 67 |
+
|
| 68 |
+
-- Periodic check
|
| 69 |
+
task.spawn(function()
|
| 70 |
+
while true do
|
| 71 |
+
task.wait(10)
|
| 72 |
+
for _, player in ipairs(Players:GetPlayers()) do
|
| 73 |
+
pcall(function()
|
| 74 |
+
checkAchievements(player)
|
| 75 |
+
end)
|
| 76 |
+
end
|
| 77 |
+
end
|
| 78 |
+
end)
|
| 79 |
+
|
| 80 |
+
Players.PlayerRemoving:Connect(function(player)
|
| 81 |
+
playerAchievements[player.UserId] = nil
|
| 82 |
+
end)
|
src/ServerScriptService/AntiCheatManager.server.lua
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ServerScriptService/AntiCheatManager.server.lua
|
| 2 |
+
|
| 3 |
+
local Players = game:GetService("Players")
|
| 4 |
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
| 5 |
+
|
| 6 |
+
-- Rate limiting tracker
|
| 7 |
+
local rateLimits = {} -- [userId] = { [eventName] = { count, lastReset } }
|
| 8 |
+
|
| 9 |
+
local MAX_EVENTS_PER_SECOND = 10
|
| 10 |
+
local RATE_WINDOW = 1 -- seconds
|
| 11 |
+
|
| 12 |
+
local function getRateData(player, eventName)
|
| 13 |
+
if not rateLimits[player.UserId] then
|
| 14 |
+
rateLimits[player.UserId] = {}
|
| 15 |
+
end
|
| 16 |
+
if not rateLimits[player.UserId][eventName] then
|
| 17 |
+
rateLimits[player.UserId][eventName] = {
|
| 18 |
+
count = 0,
|
| 19 |
+
lastReset = tick(),
|
| 20 |
+
}
|
| 21 |
+
end
|
| 22 |
+
return rateLimits[player.UserId][eventName]
|
| 23 |
+
end
|
| 24 |
+
|
| 25 |
+
-- Check if a player is rate limited
|
| 26 |
+
_G.IsRateLimited = function(player, eventName)
|
| 27 |
+
local data = getRateData(player, eventName)
|
| 28 |
+
local now = tick()
|
| 29 |
+
|
| 30 |
+
if now - data.lastReset > RATE_WINDOW then
|
| 31 |
+
data.count = 0
|
| 32 |
+
data.lastReset = now
|
| 33 |
+
end
|
| 34 |
+
|
| 35 |
+
data.count = data.count + 1
|
| 36 |
+
|
| 37 |
+
if data.count > MAX_EVENTS_PER_SECOND then
|
| 38 |
+
warn("Rate limit exceeded for " .. player.Name .. " on event " .. eventName)
|
| 39 |
+
return true
|
| 40 |
+
end
|
| 41 |
+
|
| 42 |
+
return false
|
| 43 |
+
end
|
| 44 |
+
|
| 45 |
+
-- Validate distance between player and target
|
| 46 |
+
_G.ValidateDistance = function(player, targetPosition, maxDistance)
|
| 47 |
+
local character = player.Character
|
| 48 |
+
if not character then return false end
|
| 49 |
+
|
| 50 |
+
local rootPart = character:FindFirstChild("HumanoidRootPart")
|
| 51 |
+
if not rootPart then return false end
|
| 52 |
+
|
| 53 |
+
local distance = (rootPart.Position - targetPosition).Magnitude
|
| 54 |
+
return distance <= maxDistance
|
| 55 |
+
end
|
| 56 |
+
|
| 57 |
+
-- Validate player has enough cash
|
| 58 |
+
_G.ValidateCash = function(player, amount)
|
| 59 |
+
local leaderstats = player:FindFirstChild("leaderstats")
|
| 60 |
+
if not leaderstats then return false end
|
| 61 |
+
|
| 62 |
+
local cash = leaderstats:FindFirstChild("Cash")
|
| 63 |
+
if not cash then return false end
|
| 64 |
+
|
| 65 |
+
return cash.Value >= amount
|
| 66 |
+
end
|
| 67 |
+
|
| 68 |
+
-- Cleanup on player leave
|
| 69 |
+
Players.PlayerRemoving:Connect(function(player)
|
| 70 |
+
rateLimits[player.UserId] = nil
|
| 71 |
+
end)
|
| 72 |
+
|
| 73 |
+
print("Anti-Cheat Manager initialized")
|
src/ServerScriptService/BiomeGeneratorManager.server.lua
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ServerScriptService/BiomeGeneratorManager.server.lua
|
| 2 |
+
-- Simple flat baseplate-style world with colored biome regions
|
| 3 |
+
|
| 4 |
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
| 5 |
+
local CollectionService = game:GetService("CollectionService")
|
| 6 |
+
|
| 7 |
+
local BiomeConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("BiomeConfig"))
|
| 8 |
+
local GameConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("GameConfig"))
|
| 9 |
+
|
| 10 |
+
-- Create folders
|
| 11 |
+
local biomeZonesFolder = Instance.new("Folder")
|
| 12 |
+
biomeZonesFolder.Name = "BiomeZones"
|
| 13 |
+
biomeZonesFolder.Parent = workspace
|
| 14 |
+
|
| 15 |
+
local worldFolder = Instance.new("Folder")
|
| 16 |
+
worldFolder.Name = "WorldStructures"
|
| 17 |
+
worldFolder.Parent = workspace
|
| 18 |
+
|
| 19 |
+
-- Biome color map (distinct flat colors, no grass)
|
| 20 |
+
local BiomeColors = {
|
| 21 |
+
Forest = Color3.fromRGB(80, 130, 60),
|
| 22 |
+
PineWoods = Color3.fromRGB(50, 100, 45),
|
| 23 |
+
Swamp = Color3.fromRGB(65, 85, 50),
|
| 24 |
+
Desert = Color3.fromRGB(210, 190, 130),
|
| 25 |
+
Volcanic = Color3.fromRGB(70, 40, 35),
|
| 26 |
+
IcePeak = Color3.fromRGB(190, 210, 230),
|
| 27 |
+
TropicalRainforest = Color3.fromRGB(35, 110, 40),
|
| 28 |
+
Meadow = Color3.fromRGB(140, 180, 80),
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
local function createBiomePlate(biomeName, biomeData)
|
| 32 |
+
local region = biomeData.Region
|
| 33 |
+
local regionWidth = region.MaxX - region.MinX
|
| 34 |
+
local regionDepth = region.MaxZ - region.MinZ
|
| 35 |
+
local centerX = (region.MinX + region.MaxX) / 2
|
| 36 |
+
local centerZ = (region.MinZ + region.MaxZ) / 2
|
| 37 |
+
|
| 38 |
+
-- Flat baseplate for this biome
|
| 39 |
+
local plate = Instance.new("Part")
|
| 40 |
+
plate.Name = biomeName .. "_Plate"
|
| 41 |
+
plate.Size = Vector3.new(regionWidth, 1, regionDepth)
|
| 42 |
+
plate.Position = Vector3.new(centerX, -0.5, centerZ) -- Top surface at Y=0
|
| 43 |
+
plate.Anchored = true
|
| 44 |
+
plate.Material = Enum.Material.SmoothPlastic
|
| 45 |
+
plate.Color = BiomeColors[biomeName] or Color3.fromRGB(100, 100, 100)
|
| 46 |
+
plate.TopSurface = Enum.SurfaceType.Smooth
|
| 47 |
+
plate.BottomSurface = Enum.SurfaceType.Smooth
|
| 48 |
+
plate.Parent = biomeZonesFolder
|
| 49 |
+
|
| 50 |
+
-- Invisible detection zone (taller, for biome detection)
|
| 51 |
+
local zonePart = Instance.new("Part")
|
| 52 |
+
zonePart.Name = biomeName .. "_Zone"
|
| 53 |
+
zonePart.Size = Vector3.new(regionWidth, 50, regionDepth)
|
| 54 |
+
zonePart.Position = Vector3.new(centerX, 25, centerZ)
|
| 55 |
+
zonePart.Anchored = true
|
| 56 |
+
zonePart.CanCollide = false
|
| 57 |
+
zonePart.Transparency = 1
|
| 58 |
+
zonePart:SetAttribute("BiomeName", biomeName)
|
| 59 |
+
CollectionService:AddTag(zonePart, "BiomeZone")
|
| 60 |
+
zonePart.Parent = biomeZonesFolder
|
| 61 |
+
|
| 62 |
+
-- Hazard zone if applicable
|
| 63 |
+
if biomeData.HazardType then
|
| 64 |
+
local hazardZone = Instance.new("Part")
|
| 65 |
+
hazardZone.Name = biomeName .. "_Hazard"
|
| 66 |
+
hazardZone.Size = Vector3.new(regionWidth * 0.8, 20, regionDepth * 0.8)
|
| 67 |
+
hazardZone.Position = Vector3.new(centerX, 10, centerZ)
|
| 68 |
+
hazardZone.Anchored = true
|
| 69 |
+
hazardZone.CanCollide = false
|
| 70 |
+
hazardZone.Transparency = 0.95
|
| 71 |
+
hazardZone.BrickColor = BrickColor.new("Bright red")
|
| 72 |
+
|
| 73 |
+
if biomeData.HazardAttribute then
|
| 74 |
+
hazardZone:SetAttribute("HazardType", biomeData.HazardAttribute)
|
| 75 |
+
end
|
| 76 |
+
|
| 77 |
+
CollectionService:AddTag(hazardZone, biomeData.HazardType)
|
| 78 |
+
hazardZone.Parent = biomeZonesFolder
|
| 79 |
+
end
|
| 80 |
+
|
| 81 |
+
-- Biome label sign at center
|
| 82 |
+
local signPart = Instance.new("Part")
|
| 83 |
+
signPart.Name = biomeName .. "_Sign"
|
| 84 |
+
signPart.Size = Vector3.new(8, 4, 0.5)
|
| 85 |
+
signPart.Position = Vector3.new(centerX, 4, centerZ)
|
| 86 |
+
signPart.Anchored = true
|
| 87 |
+
signPart.Material = Enum.Material.Wood
|
| 88 |
+
signPart.BrickColor = BrickColor.new("Reddish brown")
|
| 89 |
+
signPart.Parent = biomeZonesFolder
|
| 90 |
+
|
| 91 |
+
local signGui = Instance.new("SurfaceGui")
|
| 92 |
+
signGui.Face = Enum.NormalId.Front
|
| 93 |
+
signGui.Parent = signPart
|
| 94 |
+
|
| 95 |
+
local signLabel = Instance.new("TextLabel")
|
| 96 |
+
signLabel.Size = UDim2.new(1, 0, 1, 0)
|
| 97 |
+
signLabel.BackgroundTransparency = 1
|
| 98 |
+
signLabel.Text = biomeName
|
| 99 |
+
signLabel.TextColor3 = Color3.new(1, 1, 1)
|
| 100 |
+
signLabel.TextScaled = true
|
| 101 |
+
signLabel.Font = Enum.Font.GothamBold
|
| 102 |
+
signLabel.Parent = signGui
|
| 103 |
+
|
| 104 |
+
print("Biome plate created:", biomeName)
|
| 105 |
+
end
|
| 106 |
+
|
| 107 |
+
-- Create spawn platform (elevated slightly so it is distinct)
|
| 108 |
+
local function createSpawnPlatform()
|
| 109 |
+
local platform = Instance.new("Part")
|
| 110 |
+
platform.Name = "SpawnPlatform"
|
| 111 |
+
platform.Size = Vector3.new(GameConfig.SpawnPlatformSize.X, 0.2, GameConfig.SpawnPlatformSize.Z)
|
| 112 |
+
platform.Position = Vector3.new(GameConfig.SpawnPosition.X, 0.1, GameConfig.SpawnPosition.Z) -- rests slightly above biome plate
|
| 113 |
+
platform.Anchored = true
|
| 114 |
+
platform.Material = Enum.Material.SmoothPlastic
|
| 115 |
+
platform.Color = Color3.fromRGB(160, 160, 165)
|
| 116 |
+
platform.TopSurface = Enum.SurfaceType.Smooth
|
| 117 |
+
platform.BottomSurface = Enum.SurfaceType.Smooth
|
| 118 |
+
CollectionService:AddTag(platform, "SpawnPlatform")
|
| 119 |
+
platform.Parent = worldFolder
|
| 120 |
+
|
| 121 |
+
local spawn = Instance.new("SpawnLocation")
|
| 122 |
+
spawn.Size = Vector3.new(6, 1, 6)
|
| 123 |
+
spawn.Position = Vector3.new(GameConfig.SpawnPosition.X, 0.5, GameConfig.SpawnPosition.Z)
|
| 124 |
+
spawn.Anchored = true
|
| 125 |
+
spawn.CanCollide = false
|
| 126 |
+
spawn.Transparency = 1
|
| 127 |
+
spawn.Parent = worldFolder
|
| 128 |
+
end
|
| 129 |
+
|
| 130 |
+
-- World boundaries
|
| 131 |
+
local function createWorldBoundaries()
|
| 132 |
+
local bounds = GameConfig.WorldBounds
|
| 133 |
+
local height = 200
|
| 134 |
+
local thickness = 5
|
| 135 |
+
|
| 136 |
+
local walls = {
|
| 137 |
+
{pos = Vector3.new(0, height/2, bounds.MinZ), size = Vector3.new(bounds.MaxX - bounds.MinX, height, thickness)},
|
| 138 |
+
{pos = Vector3.new(0, height/2, bounds.MaxZ), size = Vector3.new(bounds.MaxX - bounds.MinX, height, thickness)},
|
| 139 |
+
{pos = Vector3.new(bounds.MinX, height/2, 0), size = Vector3.new(thickness, height, bounds.MaxZ - bounds.MinZ)},
|
| 140 |
+
{pos = Vector3.new(bounds.MaxX, height/2, 0), size = Vector3.new(thickness, height, bounds.MaxZ - bounds.MinZ)},
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
for i, wallData in ipairs(walls) do
|
| 144 |
+
local wall = Instance.new("Part")
|
| 145 |
+
wall.Name = "WorldBoundary_" .. i
|
| 146 |
+
wall.Size = wallData.size
|
| 147 |
+
wall.Position = wallData.pos
|
| 148 |
+
wall.Anchored = true
|
| 149 |
+
wall.Transparency = 1
|
| 150 |
+
wall.CanCollide = true
|
| 151 |
+
CollectionService:AddTag(wall, "WorldBoundary")
|
| 152 |
+
wall.Parent = worldFolder
|
| 153 |
+
end
|
| 154 |
+
end
|
| 155 |
+
|
| 156 |
+
-- Plot areas
|
| 157 |
+
local function createPlots()
|
| 158 |
+
local plotsFolder = Instance.new("Folder")
|
| 159 |
+
plotsFolder.Name = "Plots"
|
| 160 |
+
plotsFolder.Parent = worldFolder
|
| 161 |
+
|
| 162 |
+
for i, pos in ipairs(GameConfig.PlotPositions) do
|
| 163 |
+
local plot = Instance.new("Part")
|
| 164 |
+
plot.Name = "Plot_" .. i
|
| 165 |
+
plot.Size = Vector3.new(200, 0.2, 200)
|
| 166 |
+
plot.Position = Vector3.new(pos.X, 0.1, pos.Z) -- elevate slightly to prevent z-fighting
|
| 167 |
+
plot.Anchored = true
|
| 168 |
+
plot.Material = Enum.Material.SmoothPlastic
|
| 169 |
+
plot.Color = Color3.fromRGB(90, 90, 95)
|
| 170 |
+
plot.TopSurface = Enum.SurfaceType.Smooth
|
| 171 |
+
plot.BottomSurface = Enum.SurfaceType.Smooth
|
| 172 |
+
plot.Transparency = 1 -- fully invisible as requested
|
| 173 |
+
CollectionService:AddTag(plot, "EmptyPlot")
|
| 174 |
+
plot.Parent = plotsFolder
|
| 175 |
+
|
| 176 |
+
|
| 177 |
+
end
|
| 178 |
+
end
|
| 179 |
+
|
| 180 |
+
-- Execute world generation
|
| 181 |
+
task.spawn(function()
|
| 182 |
+
print("=== Timberbound Expeditions: World Generation Starting ===")
|
| 183 |
+
|
| 184 |
+
createSpawnPlatform()
|
| 185 |
+
createWorldBoundaries()
|
| 186 |
+
createPlots()
|
| 187 |
+
|
| 188 |
+
-- Create flat biome plates (no terrain, no elevation)
|
| 189 |
+
for biomeName, biomeData in pairs(BiomeConfig.Biomes) do
|
| 190 |
+
createBiomePlate(biomeName, biomeData)
|
| 191 |
+
end
|
| 192 |
+
|
| 193 |
+
print("=== World Generation Complete ===")
|
| 194 |
+
_G.BiomeGenerationComplete = true
|
| 195 |
+
end)
|
src/ServerScriptService/ConstructionManager.server.lua
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-- src/ServerScriptService/ConstructionManager.server.lua
|
| 2 |
+
|
| 3 |
+
local CollectionService = game:GetService("CollectionService")
|
| 4 |
+
local ReplicatedStorage = game:GetService("ReplicatedStorage")
|
| 5 |
+
local Players = game:GetService("Players")
|
| 6 |
+
|
| 7 |
+
local BuildingConfig = require(ReplicatedStorage:WaitForChild("Shared"):WaitForChild("BuildingConfig"))
|
| 8 |
+
local PlaceBlueprintEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("PlaceBlueprintEvent")
|
| 9 |
+
local FillBlueprintEvent = ReplicatedStorage:WaitForChild("Events"):WaitForChild("FillBlueprintEvent")
|
| 10 |
+
|
| 11 |
+
local function getPartVolume(part)
|
| 12 |
+
return part.Size.X * part.Size.Y * part.Size.Z
|
| 13 |
+
end
|
| 14 |
+
|
| 15 |
+
-- Validate and place a blueprint
|
| 16 |
+
local function onPlaceBlueprint(player, structureType, cframe)
|
| 17 |
+
local config = BuildingConfig.Structures[structureType]
|
| 18 |
+
if not config then return end
|
| 19 |
+
|
| 20 |
+
-- In a real game, you would verify if `cframe` is inside the player's Claimed Plot
|
| 21 |
+
-- Verification skipped here for brevity
|
| 22 |
+
|
| 23 |
+
local blueprintModel = Instance.new("Part")
|
| 24 |
+
blueprintModel.Size = Vector3.new(10, 10, 1) -- Example dimensions
|
| 25 |
+
blueprintModel.CFrame = cframe
|
| 26 |
+
|
| 27 |
+
-- Visual Blueprint state
|
| 28 |
+
blueprintModel.Transparency = 0.5
|
| 29 |
+
blueprintModel.BrickColor = BrickColor.new("Neon blue")
|
| 30 |
+
blueprintModel.Material = Enum.Material.ForceField
|
| 31 |
+
blueprintModel.Anchored = true
|
| 32 |
+
|
| 33 |
+
-- Attributes for tracking
|
| 34 |
+
blueprintModel:SetAttribute("StructureType", structureType)
|
| 35 |
+
blueprintModel:SetAttribute("WoodFilled", 0)
|
| 36 |
+
blueprintModel:SetAttribute("WoodRequired", config.Cost.WoodVolume)
|
| 37 |
+
blueprintModel:SetAttribute("MaterialRequired", config.Cost.SpecificMaterial)
|
| 38 |
+
blueprintModel:SetAttribute("OwnerId", player.UserId)
|
| 39 |
+
|
| 40 |
+
CollectionService:AddTag(blueprintModel, "Blueprint")
|
| 41 |
+
|
| 42 |
+
blueprintModel.Parent = workspace.Terrain
|
| 43 |
+
|
| 44 |
+
-- Bind touched event so players can "throw" wood into the blueprint to fill it
|
| 45 |
+
local debounce = {}
|
| 46 |
+
blueprintModel.Touched:Connect(function(hit)
|
| 47 |
+
-- Make sure it's fully processed wood touching it
|
| 48 |
+
if CollectionService:HasTag(hit, "TreeSegment") and not debounce[hit] then
|
| 49 |
+
debounce[hit] = true
|
| 50 |
+
|
| 51 |
+
local requiredMaterial = blueprintModel:GetAttribute("MaterialRequired")
|
| 52 |
+
local hitMaterial = hit:GetAttribute("ProcessState") or "Raw"
|
| 53 |
+
|
| 54 |
+
if hitMaterial == requiredMaterial then
|
| 55 |
+
local volume = getPartVolume(hit)
|
| 56 |
+
|
| 57 |
+
local currentFill = blueprintModel:GetAttribute("WoodFilled")
|
| 58 |
+
local required = blueprintModel:GetAttribute("WoodRequired")
|
| 59 |
+
|
| 60 |
+
local newFill = currentFill + volume
|
| 61 |
+
blueprintModel:SetAttribute("WoodFilled", newFill)
|
| 62 |
+
|
| 63 |
+
-- Visual feedback: change transparency based on fill %
|
| 64 |
+
local fillPercent = math.clamp(newFill / required, 0, 1)
|
| 65 |
+
blueprintModel.Transparency = 0.5 - (0.5 * fillPercent)
|
| 66 |
+
|
| 67 |
+
hit:Destroy()
|
| 68 |
+
|
| 69 |
+
if newFill >= required then
|
| 70 |
+
-- Construct!
|
| 71 |
+
blueprintModel.Transparency = 0
|
| 72 |
+
blueprintModel.BrickColor = BrickColor.new("Burlap")
|
| 73 |
+
blueprintModel.Material = Enum.Material.WoodPlanks
|
| 74 |
+
blueprintModel.CanCollide = true
|
| 75 |
+
|
| 76 |
+
CollectionService:RemoveTag(blueprintModel, "Blueprint")
|
| 77 |
+
CollectionService:AddTag(blueprintModel, "ConstructedPart")
|
| 78 |
+
end
|
| 79 |
+
end
|
| 80 |
+
|
| 81 |
+
task.delay(0.5, function()
|
| 82 |
+
debounce[hit] = nil
|
| 83 |
+
end)
|
| 84 |
+
end
|
| 85 |
+
end)
|
| 86 |
+
end
|
| 87 |
+
|
| 88 |
+
PlaceBlueprintEvent.OnServerEvent:Connect(onPlaceBlueprint)
|