init
This commit is contained in:
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Normalize EOL for all files that Git considers text files.
|
||||
* text=auto eol=lf
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# Godot 4+ specific ignores
|
||||
.godot/
|
||||
/android/
|
||||
3
docs/Action Rog/.obsidian/app.json
vendored
Normal file
3
docs/Action Rog/.obsidian/app.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"alwaysUpdateLinks": true
|
||||
}
|
||||
1
docs/Action Rog/.obsidian/appearance.json
vendored
Normal file
1
docs/Action Rog/.obsidian/appearance.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{}
|
||||
3
docs/Action Rog/.obsidian/community-plugins.json
vendored
Normal file
3
docs/Action Rog/.obsidian/community-plugins.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[
|
||||
"table-editor-obsidian"
|
||||
]
|
||||
33
docs/Action Rog/.obsidian/core-plugins.json
vendored
Normal file
33
docs/Action Rog/.obsidian/core-plugins.json
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"file-explorer": true,
|
||||
"global-search": true,
|
||||
"switcher": true,
|
||||
"graph": true,
|
||||
"backlink": true,
|
||||
"canvas": true,
|
||||
"outgoing-link": true,
|
||||
"tag-pane": true,
|
||||
"footnotes": false,
|
||||
"properties": true,
|
||||
"page-preview": true,
|
||||
"daily-notes": true,
|
||||
"templates": true,
|
||||
"note-composer": true,
|
||||
"command-palette": true,
|
||||
"slash-command": false,
|
||||
"editor-status": true,
|
||||
"bookmarks": true,
|
||||
"markdown-importer": false,
|
||||
"zk-prefixer": false,
|
||||
"random-note": false,
|
||||
"outline": true,
|
||||
"word-count": true,
|
||||
"slides": false,
|
||||
"audio-recorder": false,
|
||||
"workspaces": false,
|
||||
"file-recovery": true,
|
||||
"publish": false,
|
||||
"sync": true,
|
||||
"bases": true,
|
||||
"webviewer": false
|
||||
}
|
||||
22
docs/Action Rog/.obsidian/graph.json
vendored
Normal file
22
docs/Action Rog/.obsidian/graph.json
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"collapse-filter": false,
|
||||
"search": "",
|
||||
"showTags": true,
|
||||
"showAttachments": true,
|
||||
"hideUnresolved": false,
|
||||
"showOrphans": true,
|
||||
"collapse-color-groups": false,
|
||||
"colorGroups": [],
|
||||
"collapse-display": false,
|
||||
"showArrow": false,
|
||||
"textFadeMultiplier": 0,
|
||||
"nodeSizeMultiplier": 1,
|
||||
"lineSizeMultiplier": 1,
|
||||
"collapse-forces": false,
|
||||
"centerStrength": 0.518713248970312,
|
||||
"repelStrength": 10,
|
||||
"linkStrength": 1,
|
||||
"linkDistance": 250,
|
||||
"scale": 1,
|
||||
"close": true
|
||||
}
|
||||
504
docs/Action Rog/.obsidian/plugins/obsidian-tasks-plugin/main.js
vendored
Normal file
504
docs/Action Rog/.obsidian/plugins/obsidian-tasks-plugin/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
12
docs/Action Rog/.obsidian/plugins/obsidian-tasks-plugin/manifest.json
vendored
Normal file
12
docs/Action Rog/.obsidian/plugins/obsidian-tasks-plugin/manifest.json
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"id": "obsidian-tasks-plugin",
|
||||
"name": "Tasks",
|
||||
"version": "7.22.0",
|
||||
"minAppVersion": "1.4.0",
|
||||
"description": "Track tasks across your vault. Supports due dates, recurring tasks, done dates, sub-set of checklist items, and filtering.",
|
||||
"helpUrl": "https://publish.obsidian.md/tasks/",
|
||||
"author": "Clare Macrae and Ilyas Landikov (created by Martin Schenck)",
|
||||
"authorUrl": "https://github.com/obsidian-tasks-group",
|
||||
"fundingUrl": "https://github.com/sponsors/claremacrae",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
1
docs/Action Rog/.obsidian/plugins/obsidian-tasks-plugin/styles.css
vendored
Normal file
1
docs/Action Rog/.obsidian/plugins/obsidian-tasks-plugin/styles.css
vendored
Normal file
File diff suppressed because one or more lines are too long
6
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/data.json
vendored
Normal file
6
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/data.json
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"formatType": "normal",
|
||||
"showRibbonIcon": true,
|
||||
"bindEnter": true,
|
||||
"bindTab": true
|
||||
}
|
||||
236
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/main.js
vendored
Normal file
236
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
17
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/manifest.json
vendored
Normal file
17
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/manifest.json
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"id": "table-editor-obsidian",
|
||||
"name": "Advanced Tables",
|
||||
"author": "Tony Grosinger",
|
||||
"authorUrl": "https://grosinger.net",
|
||||
"description": "Improved table navigation, formatting, manipulation, and formulas",
|
||||
"isDesktopOnly": false,
|
||||
"minAppVersion": "1.0.0",
|
||||
"version": "0.22.1",
|
||||
"js": "main.js",
|
||||
"fundingUrl": {
|
||||
"Github Sponsor": "https://github.com/sponsors/tgrosinger",
|
||||
"Buy me a Coffee": "https://buymeacoffee.com/tgrosinger",
|
||||
"Paypal": "https://paypal.me/tgrosinger"
|
||||
},
|
||||
"donation": "https://buymeacoffee.com/tgrosinger"
|
||||
}
|
||||
78
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/styles.css
vendored
Normal file
78
docs/Action Rog/.obsidian/plugins/table-editor-obsidian/styles.css
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
:root {
|
||||
--advanced-tables-helper-size: 28px;
|
||||
}
|
||||
|
||||
.HyperMD-table-row span.cm-inline-code {
|
||||
font-size: 100%;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.advanced-tables-buttons>div>.title {
|
||||
font-weight: var(--font-medium);
|
||||
font-size: var(--nav-item-size);
|
||||
color: var(--nav-item-color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
[data-type="advanced-tables-toolbar"] .nav-buttons-container {
|
||||
column-gap: 0.2rem;
|
||||
margin: 0.2rem 0 0.2rem 0;
|
||||
justify-content: start;
|
||||
}
|
||||
|
||||
[data-type="advanced-tables-toolbar"] .nav-buttons-container::before {
|
||||
min-width: 2.6rem;
|
||||
line-height: var(--advanced-tables-helper-size);
|
||||
font-size: var(--nav-item-size);
|
||||
font-weight: var(--nav-item-weight);
|
||||
color: var(--nav-item-color);
|
||||
}
|
||||
|
||||
[data-type="advanced-tables-toolbar"] .nav-buttons-container>* {
|
||||
height: var(--advanced-tables-helper-size);
|
||||
line-height: var(--advanced-tables-helper-size);
|
||||
}
|
||||
|
||||
[data-type="advanced-tables-toolbar"] .nav-buttons-container .nav-action-button {
|
||||
width: var(--advanced-tables-helper-size);
|
||||
height: var(--advanced-tables-helper-size);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-radius: var(--radius-s);
|
||||
}
|
||||
|
||||
[data-type="advanced-tables-toolbar"] .nav-buttons-container .nav-action-button:hover {
|
||||
background-color: var(--nav-item-background-hover);
|
||||
color: var(--nav-item-color-hover);
|
||||
font-weight: var(--nav-item-weight-hover);
|
||||
}
|
||||
|
||||
.advanced-tables-row-label {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.widget-icon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
fill: var(--text-muted);
|
||||
}
|
||||
|
||||
.widget-icon:hover {
|
||||
fill: var(--text-normal);
|
||||
}
|
||||
|
||||
.advanced-tables-csv-export textarea {
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.advanced-tables-donation {
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.advanced-tables-donate-button {
|
||||
margin: 10px;
|
||||
}
|
||||
224
docs/Action Rog/.obsidian/workspace.json
vendored
Normal file
224
docs/Action Rog/.obsidian/workspace.json
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
{
|
||||
"main": {
|
||||
"id": "9d84761fdc521e48",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "56870b1a5abc8bc3",
|
||||
"type": "tabs",
|
||||
"dimension": 52.76162790697675,
|
||||
"children": [
|
||||
{
|
||||
"id": "fae3dd60da6746c9",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "markdown",
|
||||
"state": {
|
||||
"file": "Спелы/Начинания/Начинания.md",
|
||||
"mode": "source",
|
||||
"source": false
|
||||
},
|
||||
"icon": "lucide-file",
|
||||
"title": "Начинания"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "693c8197b2393835",
|
||||
"type": "tabs",
|
||||
"dimension": 47.23837209302326,
|
||||
"children": [
|
||||
{
|
||||
"id": "41a43fa3fff1d73f",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "graph",
|
||||
"state": {},
|
||||
"icon": "lucide-git-fork",
|
||||
"title": "Граф"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "vertical"
|
||||
},
|
||||
"left": {
|
||||
"id": "c0e70ec8604f67da",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "b11845e247b44591",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "9a81516dc6c65404",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "file-explorer",
|
||||
"state": {
|
||||
"sortOrder": "alphabetical",
|
||||
"autoReveal": false
|
||||
},
|
||||
"icon": "lucide-folder-closed",
|
||||
"title": "Файловый менеджер"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "6f4075d7a5920333",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "search",
|
||||
"state": {
|
||||
"query": "",
|
||||
"matchingCase": false,
|
||||
"explainSearch": false,
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical"
|
||||
},
|
||||
"icon": "lucide-search",
|
||||
"title": "Поиск"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "f0a6202fc12fc4af",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "bookmarks",
|
||||
"state": {},
|
||||
"icon": "lucide-bookmark",
|
||||
"title": "Закладки"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300
|
||||
},
|
||||
"right": {
|
||||
"id": "feabda6c3d6801d3",
|
||||
"type": "split",
|
||||
"children": [
|
||||
{
|
||||
"id": "cd27a52bffd0262b",
|
||||
"type": "tabs",
|
||||
"children": [
|
||||
{
|
||||
"id": "b0383d0fcbd61458",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "backlink",
|
||||
"state": {
|
||||
"file": "Спелы/Начинания/Начинания.md",
|
||||
"collapseAll": false,
|
||||
"extraContext": false,
|
||||
"sortOrder": "alphabetical",
|
||||
"showSearch": false,
|
||||
"searchQuery": "",
|
||||
"backlinkCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-coming-in",
|
||||
"title": "Обратные ссылки для Начинания"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "609b4502709daf5e",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outgoing-link",
|
||||
"state": {
|
||||
"file": "Спелы/Начинания/Начинания.md",
|
||||
"linksCollapsed": false,
|
||||
"unlinkedCollapsed": true
|
||||
},
|
||||
"icon": "links-going-out",
|
||||
"title": "Исходящие ссылки из Начинания"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "2d8db340c9660aae",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "tag",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"useHierarchy": true,
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-tags",
|
||||
"title": "Теги"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "dff9a1d15096e8ab",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "all-properties",
|
||||
"state": {
|
||||
"sortOrder": "frequency",
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-archive",
|
||||
"title": "Все свойства"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "1591bf739b0cab13",
|
||||
"type": "leaf",
|
||||
"state": {
|
||||
"type": "outline",
|
||||
"state": {
|
||||
"file": "Спелы/Начинания/Начинания.md",
|
||||
"followCursor": false,
|
||||
"showSearch": false,
|
||||
"searchQuery": ""
|
||||
},
|
||||
"icon": "lucide-list",
|
||||
"title": "Структура Начинания"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"direction": "horizontal",
|
||||
"width": 300,
|
||||
"collapsed": true
|
||||
},
|
||||
"left-ribbon": {
|
||||
"hiddenItems": {
|
||||
"switcher:Меню быстрого перехода": false,
|
||||
"graph:Граф": false,
|
||||
"canvas:Создать новый холст": false,
|
||||
"daily-notes:Сегодняшняя заметка": false,
|
||||
"templates:Вставить шаблон": false,
|
||||
"command-palette:Открыть палитру команд": false,
|
||||
"bases:Создать новую базу": false,
|
||||
"table-editor-obsidian:Advanced Tables Toolbar": false
|
||||
}
|
||||
},
|
||||
"active": "fae3dd60da6746c9",
|
||||
"lastOpenFiles": [
|
||||
"Спелы/Начинания/Первичные/Вода.md",
|
||||
"Вода.md",
|
||||
"Спелы/Начинания/Начинания.md",
|
||||
"Спелы/Начинания/Первичные/Огонь.md",
|
||||
"Спелы/Начинания/Вторичные",
|
||||
"Спелы/Начинания/Первичные",
|
||||
"Спелы/Лужа.md",
|
||||
"Спелы/Начинания/Первичные/Смерть.md",
|
||||
"Спелы/Начинания/Первичные/Жизнь.md",
|
||||
"Спелы/Начинания/Первичные/Тьма.md",
|
||||
"Спелы/Начинания/Первичные/Свет.md",
|
||||
"Спелы/Начинания/Первичные/Воздух.md",
|
||||
"Спелы/Начинания/Первичные/Земля.md",
|
||||
"Спелы/Начинания",
|
||||
"Добро пожаловать.md",
|
||||
"Спелы"
|
||||
]
|
||||
}
|
||||
0
docs/Action Rog/Вода.md
Normal file
0
docs/Action Rog/Вода.md
Normal file
1
docs/Action Rog/Спелы/Лужа.md
Normal file
1
docs/Action Rog/Спелы/Лужа.md
Normal file
@@ -0,0 +1 @@
|
||||
#мокрый
|
||||
31
docs/Action Rog/Спелы/Начинания/Начинания.md
Normal file
31
docs/Action Rog/Спелы/Начинания/Начинания.md
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
|
||||
# Общее
|
||||
Есть первичные начинания и вторичные.
|
||||
Первичные доступны сразу, до вторичных нужно дойти.
|
||||
Начинания - кирпичики из которых складываются спелы.
|
||||
|
||||
# Первичные начинания
|
||||
1. [[Огонь]]
|
||||
2. [[Вода]]
|
||||
3. [[Земля]]
|
||||
4. [[Воздух]]
|
||||
5. [[Свет]]
|
||||
6. [[Тьма]]
|
||||
7. [[Жизнь]]
|
||||
8. [[Смерть]]
|
||||
|
||||
Первичные начинания доступны сразу и могут
|
||||
|
||||
# Вторичные
|
||||
Доступны как комбинации первичных
|
||||
|
||||
1. Паладин ([[Огонь]] + [[Свет]])
|
||||
2. Бладмаг ([[Огонь]] + [[Жизнь]])
|
||||
3. Растения ([[Жизнь]] + [[Земля]])
|
||||
4. Сталь ([[Земля]] + [[Тьма]])
|
||||
5. Лед ([[Тьма]] + [[Спелы/Начинания/Первичные/Вода]])
|
||||
6. Яд ([[Спелы/Начинания/Первичные/Вода]] + [[Смерть]])
|
||||
7. Чума ([[Смерть]] + [[Воздух]])
|
||||
8. Молния ([[Воздух]] + [[Свет]])
|
||||
|
||||
5
docs/Action Rog/Спелы/Начинания/Первичные/Вода.md
Normal file
5
docs/Action Rog/Спелы/Начинания/Первичные/Вода.md
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
|
||||
|
||||
#мокрый
|
||||
|
||||
0
docs/Action Rog/Спелы/Начинания/Первичные/Воздух.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Воздух.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Жизнь.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Жизнь.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Земля.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Земля.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Огонь.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Огонь.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Свет.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Свет.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Смерть.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Смерть.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Тьма.md
Normal file
0
docs/Action Rog/Спелы/Начинания/Первичные/Тьма.md
Normal file
1
icon.svg
Normal file
1
icon.svg
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>
|
||||
|
After Width: | Height: | Size: 995 B |
43
icon.svg.import
Normal file
43
icon.svg.import
Normal file
@@ -0,0 +1,43 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dn3m6ec8rak21"
|
||||
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://icon.svg"
|
||||
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
||||
@@ -0,0 +1,12 @@
|
||||
# Blender 4.4.0 MTL File: 'None'
|
||||
# www.blender.org
|
||||
|
||||
newmtl Material.001
|
||||
Ns 250.000000
|
||||
Ka 1.000000 1.000000 1.000000
|
||||
Ks 0.500000 0.500000 0.500000
|
||||
Ke 0.000000 0.000000 0.000000
|
||||
Ni 1.500000
|
||||
d 1.000000
|
||||
illum 2
|
||||
map_Kd Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.png
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,25 @@
|
||||
[remap]
|
||||
|
||||
importer="wavefront_obj"
|
||||
importer_version=1
|
||||
type="Mesh"
|
||||
uid="uid://c82de86x4ho25"
|
||||
path="res://.godot/imported/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.obj-272c3e26cc8a388ddaff3599002a085b.mesh"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res://.godot/imported/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.obj-272c3e26cc8a388ddaff3599002a085b.mesh"]
|
||||
|
||||
source_file="res://models/Crystal Golem Behemot/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.obj"
|
||||
dest_files=["res://.godot/imported/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.obj-272c3e26cc8a388ddaff3599002a085b.mesh", "res://.godot/imported/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.obj-272c3e26cc8a388ddaff3599002a085b.mesh"]
|
||||
|
||||
[params]
|
||||
|
||||
generate_tangents=true
|
||||
generate_lods=true
|
||||
generate_shadow_mesh=true
|
||||
generate_lightmap_uv2=false
|
||||
generate_lightmap_uv2_texel_size=0.2
|
||||
scale_mesh=Vector3(1, 1, 1)
|
||||
offset_mesh=Vector3(0, 0, 0)
|
||||
force_disable_mesh_compression=false
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.2 MiB |
@@ -0,0 +1,41 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://jyix4ilr67uk"
|
||||
path.s3tc="res://.godot/imported/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.png-68db2fcac524e74c59fbe9360f7bc6f0.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://models/Crystal Golem Behemot/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.png"
|
||||
dest_files=["res://.godot/imported/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.png-68db2fcac524e74c59fbe9360f7bc6f0.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.8 MiB |
@@ -0,0 +1,44 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://x4734mds7b74"
|
||||
path.s3tc="res://.godot/imported/Meshy_AI_Meshy_Merged_Animations_0.png-afda3fedb942079159108d0b652b7e94.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "7bd9bbed46ac9e0d5bf50f774da896f3"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://models/Crystal Golem Behemot/Meshy_AI_Meshy_Merged_Animations_0.png"
|
||||
dest_files=["res://.godot/imported/Meshy_AI_Meshy_Merged_Animations_0.png-afda3fedb942079159108d0b652b7e94.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
BIN
models/Crystal Golem Behemot/Mutant Walking.fbx
Normal file
BIN
models/Crystal Golem Behemot/Mutant Walking.fbx
Normal file
Binary file not shown.
50
models/Crystal Golem Behemot/Mutant Walking.fbx.import
Normal file
50
models/Crystal Golem Behemot/Mutant Walking.fbx.import
Normal file
@@ -0,0 +1,50 @@
|
||||
[remap]
|
||||
|
||||
importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://fruqpocnluu4"
|
||||
path="res://.godot/imported/Mutant Walking.fbx-044ace83349eaaf9d031cd571d61e9b3.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://models/Crystal Golem Behemot/Mutant Walking.fbx"
|
||||
dest_files=["res://.godot/imported/Mutant Walking.fbx-044ace83349eaaf9d031cd571d61e9b3.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
nodes/root_type=""
|
||||
nodes/root_name=""
|
||||
nodes/root_script=null
|
||||
nodes/apply_root_scale=true
|
||||
nodes/root_scale=1.0
|
||||
nodes/import_as_skeleton_bones=false
|
||||
nodes/use_name_suffixes=true
|
||||
nodes/use_node_type_suffixes=true
|
||||
meshes/ensure_tangents=true
|
||||
meshes/generate_lods=true
|
||||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
animation/trimming=true
|
||||
animation/remove_immutable_tracks=true
|
||||
animation/import_rest_as_RESET=false
|
||||
import_script/path=""
|
||||
materials/extract=0
|
||||
materials/extract_format=0
|
||||
materials/extract_path=""
|
||||
_subresources={
|
||||
"nodes": {
|
||||
"PATH:Skeleton3D": {
|
||||
"rest_pose/selected_animation": "mixamo_com"
|
||||
}
|
||||
}
|
||||
}
|
||||
fbx/importer=0
|
||||
fbx/allow_geometry_helper_nodes=false
|
||||
fbx/embedded_image_handling=1
|
||||
fbx/naming_version=2
|
||||
BIN
models/Crystal Golem Behemot/Mutant Walking_0.png
Normal file
BIN
models/Crystal Golem Behemot/Mutant Walking_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.0 MiB |
44
models/Crystal Golem Behemot/Mutant Walking_0.png.import
Normal file
44
models/Crystal Golem Behemot/Mutant Walking_0.png.import
Normal file
@@ -0,0 +1,44 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cgxy3uivyomg"
|
||||
path.s3tc="res://.godot/imported/Mutant Walking_0.png-5ddbf6c8fdd4c9c6cc7d81ed05a731ad.s3tc.ctex"
|
||||
metadata={
|
||||
"imported_formats": ["s3tc_bptc"],
|
||||
"vram_texture": true
|
||||
}
|
||||
generator_parameters={
|
||||
"md5": "9f199a31179af6f4a0995373751c7d0f"
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://models/Crystal Golem Behemot/Mutant Walking_0.png"
|
||||
dest_files=["res://.godot/imported/Mutant Walking_0.png-5ddbf6c8fdd4c9c6cc7d81ed05a731ad.s3tc.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=2
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/uastc_level=0
|
||||
compress/rdo_quality_loss=0.0
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=true
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/channel_remap/red=0
|
||||
process/channel_remap/green=1
|
||||
process/channel_remap/blue=2
|
||||
process/channel_remap/alpha=3
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=0
|
||||
44
project.godot
Normal file
44
project.godot
Normal file
@@ -0,0 +1,44 @@
|
||||
; Engine configuration file.
|
||||
; It's best edited using the editor UI and not directly,
|
||||
; since the parameters that go here are not all obvious.
|
||||
;
|
||||
; Format:
|
||||
; [section] ; section goes between []
|
||||
; param=value ; assign values to parameters
|
||||
|
||||
config_version=5
|
||||
|
||||
[application]
|
||||
|
||||
config/name="ActionRog"
|
||||
run/main_scene="res://scenes/main_menu.tscn"
|
||||
config/features=PackedStringArray("4.5", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[input]
|
||||
|
||||
move_forward={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_back={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
dash={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
61
scenes/enemy.tscn
Normal file
61
scenes/enemy.tscn
Normal file
@@ -0,0 +1,61 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://dcyxfspptvuqn"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://8vuywuv7ebsl" path="res://scripts/enemy.gd" id="1_enemy"]
|
||||
[ext_resource type="Script" path="res://scripts/enemy_health_bar.gd" id="2_health_bar"]
|
||||
[ext_resource type="ArrayMesh" uid="uid://c82de86x4ho25" path="res://models/Crystal Golem Behemot/Meshy_AI_Crystal_Golem_Behemot_0110152107_texture.obj" id="3_enemy"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1"]
|
||||
radius = 1.5
|
||||
height = 5.0
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_1"]
|
||||
size = Vector3(3, 0.2, 0.05)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1"]
|
||||
albedo_color = Color(0.5, 0, 0, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(0.8, 0, 0, 1)
|
||||
emission_energy_multiplier = 2.0
|
||||
shading_mode = 0
|
||||
flags_unshaded = true
|
||||
flags_transparent = false
|
||||
depth_draw_mode = 2
|
||||
render_priority = 1
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_2"]
|
||||
size = Vector3(3, 0.2, 0.05)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2"]
|
||||
albedo_color = Color(0, 1, 0, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(0, 1, 0, 1)
|
||||
emission_energy_multiplier = 2.0
|
||||
shading_mode = 0
|
||||
flags_unshaded = true
|
||||
flags_transparent = false
|
||||
depth_draw_mode = 2
|
||||
render_priority = 2
|
||||
|
||||
[node name="Enemy" type="CharacterBody3D"]
|
||||
script = ExtResource("1_enemy")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(2.5, 0, 0, 0, 2.5, 0, 0, 0, 2.5, 0, 2.6208835, 0)
|
||||
mesh = ExtResource("3_enemy")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0)
|
||||
shape = SubResource("CapsuleShape3D_1")
|
||||
|
||||
[node name="HealthBarContainer" type="Node3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 5.5, 0)
|
||||
script = ExtResource("2_health_bar")
|
||||
|
||||
[node name="BackgroundBar" type="MeshInstance3D" parent="HealthBarContainer"]
|
||||
mesh = SubResource("BoxMesh_1")
|
||||
material_override = SubResource("StandardMaterial3D_1")
|
||||
|
||||
[node name="HealthBar" type="MeshInstance3D" parent="HealthBarContainer"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.01)
|
||||
mesh = SubResource("BoxMesh_2")
|
||||
material_override = SubResource("StandardMaterial3D_2")
|
||||
104
scenes/hud.tscn
Normal file
104
scenes/hud.tscn
Normal file
@@ -0,0 +1,104 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://hud"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/hud.gd" id="1_hud"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1"]
|
||||
bg_color = Color(0.2, 0, 0, 0.8)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2"]
|
||||
bg_color = Color(0, 0.8, 0, 0.8)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3"]
|
||||
bg_color = Color(0.2, 0.2, 0.2, 0.8)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_4"]
|
||||
bg_color = Color(0.2, 0.6, 1, 0.8)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[node name="HUD" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("1_hud")
|
||||
|
||||
[node name="HealthBar" type="ProgressBar" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 2
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 20.0
|
||||
offset_top = -60.0
|
||||
offset_right = 320.0
|
||||
offset_bottom = -20.0
|
||||
grow_vertical = 0
|
||||
max_value = 100.0
|
||||
value = 100.0
|
||||
show_percentage = false
|
||||
theme_override_styles/background = SubResource("StyleBoxFlat_1")
|
||||
theme_override_styles/fill = SubResource("StyleBoxFlat_2")
|
||||
|
||||
[node name="HealthLabel" type="Label" parent="HealthBar"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/shadow_offset_x = 2
|
||||
theme_override_constants/shadow_offset_y = 2
|
||||
text = "HP: 100 / 100"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="DashCooldownBar" type="ProgressBar" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 2
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 20.0
|
||||
offset_top = -120.0
|
||||
offset_right = 320.0
|
||||
offset_bottom = -80.0
|
||||
grow_vertical = 0
|
||||
max_value = 100.0
|
||||
value = 100.0
|
||||
show_percentage = false
|
||||
theme_override_styles/background = SubResource("StyleBoxFlat_3")
|
||||
theme_override_styles/fill = SubResource("StyleBoxFlat_4")
|
||||
|
||||
[node name="DashCooldownLabel" type="Label" parent="DashCooldownBar"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_colors/font_shadow_color = Color(0, 0, 0, 1)
|
||||
theme_override_constants/shadow_offset_x = 2
|
||||
theme_override_constants/shadow_offset_y = 2
|
||||
text = "Рывок: готов (Пробел)"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
60
scenes/main.tscn
Normal file
60
scenes/main.tscn
Normal file
@@ -0,0 +1,60 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://c64bxmqeaaaku"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://player" path="res://scenes/player.tscn" id="1_main"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/enemy.tscn" id="2_main"]
|
||||
[ext_resource type="Script" uid="uid://28onm6d36qbo" path="res://scripts/camera_controller.gd" id="3_main"]
|
||||
[ext_resource type="PackedScene" path="res://scenes/hud.tscn" id="4_main"]
|
||||
|
||||
[sub_resource type="PlaneMesh" id="PlaneMesh_1"]
|
||||
size = Vector2(50, 50)
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1"]
|
||||
albedo_color = Color(0.2, 0.2, 0.2, 1)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_1"]
|
||||
size = Vector3(50, 0.1, 50)
|
||||
|
||||
[node name="Main" type="Node3D"]
|
||||
|
||||
[node name="Ground" type="StaticBody3D" parent="."]
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
|
||||
mesh = SubResource("PlaneMesh_1")
|
||||
surface_material_override/0 = SubResource("StandardMaterial3D_1")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Ground"]
|
||||
shape = SubResource("BoxShape3D_1")
|
||||
|
||||
[node name="Player" parent="." instance=ExtResource("1_main")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5.2521453, 1, 12.966181)
|
||||
|
||||
[node name="Enemies" type="Node3D" parent="."]
|
||||
|
||||
[node name="Enemy1" parent="Enemies" instance=ExtResource("2_main")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13.720226, 1, 5)
|
||||
|
||||
[node name="Enemy2" parent="Enemies" instance=ExtResource("2_main")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 1, 5)
|
||||
|
||||
[node name="Enemy3" parent="Enemies" instance=ExtResource("2_main")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 1, -5)
|
||||
|
||||
[node name="Enemy4" parent="Enemies" instance=ExtResource("2_main")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 1, -5)
|
||||
|
||||
[node name="Enemy5" parent="Enemies" instance=ExtResource("2_main")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 8)
|
||||
|
||||
[node name="CameraController" type="Node3D" parent="."]
|
||||
script = ExtResource("3_main")
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="CameraController"]
|
||||
transform = Transform3D(0.707107, -0.5, 0.5, 0, 0.707107, 0.707107, -0.707107, -0.5, 0.5, 0, 15, 15)
|
||||
current = true
|
||||
fov = 60.0
|
||||
|
||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||
transform = Transform3D(0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, -0.75, 0.433013, 0, 10, 0)
|
||||
shadow_enabled = true
|
||||
|
||||
[node name="HUD" parent="." instance=ExtResource("4_main")]
|
||||
99
scenes/main_menu.tscn
Normal file
99
scenes/main_menu.tscn
Normal file
@@ -0,0 +1,99 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://main_menu"]
|
||||
|
||||
[ext_resource type="Script" path="res://scripts/main_menu.gd" id="1_menu"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1"]
|
||||
bg_color = Color(0.2, 0.2, 0.2, 0.8)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_2"]
|
||||
bg_color = Color(0.3, 0.3, 0.4, 0.9)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3"]
|
||||
bg_color = Color(0.15, 0.15, 0.25, 0.9)
|
||||
corner_radius_top_left = 5
|
||||
corner_radius_top_right = 5
|
||||
corner_radius_bottom_right = 5
|
||||
corner_radius_bottom_left = 5
|
||||
|
||||
[node name="MainMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_menu")
|
||||
|
||||
[node name="Background" type="ColorRect" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
color = Color(0.1, 0.1, 0.15, 1)
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -150.0
|
||||
offset_top = -120.0
|
||||
offset_right = 150.0
|
||||
offset_bottom = 120.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="Title" type="Label" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_font_sizes/font_size = 48
|
||||
text = "Action Rog"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="StartButton" type="Button" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(200, 40)
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_1")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_2")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxFlat_3")
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_colors/font_hover_color = Color(0.9, 0.9, 1, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.7, 0.7, 0.9, 1)
|
||||
text = "Начать игру"
|
||||
|
||||
[node name="SettingsButton" type="Button" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(200, 40)
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_1")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_2")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxFlat_3")
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_colors/font_hover_color = Color(0.9, 0.9, 1, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.7, 0.7, 0.9, 1)
|
||||
text = "Настройки"
|
||||
|
||||
[node name="QuitButton" type="Button" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(200, 40)
|
||||
theme_override_styles/normal = SubResource("StyleBoxFlat_1")
|
||||
theme_override_styles/hover = SubResource("StyleBoxFlat_2")
|
||||
theme_override_styles/pressed = SubResource("StyleBoxFlat_3")
|
||||
theme_override_colors/font_color = Color(1, 1, 1, 1)
|
||||
theme_override_colors/font_hover_color = Color(0.9, 0.9, 1, 1)
|
||||
theme_override_colors/font_pressed_color = Color(0.7, 0.7, 0.9, 1)
|
||||
text = "Выход"
|
||||
|
||||
113
scenes/parabolic_projectile.tscn
Normal file
113
scenes/parabolic_projectile.tscn
Normal file
@@ -0,0 +1,113 @@
|
||||
[gd_scene load_steps=10 format=3 uid="uid://cwte6nc4bxak0"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://d3c5g160kh3vv" path="res://scripts/parabolic_projectile.gd" id="1_parabolic"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1"]
|
||||
albedo_color = Color(1, 0.4, 0.1, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(1, 0.3, 0, 1)
|
||||
emission_energy_multiplier = 4.0
|
||||
|
||||
[sub_resource type="SphereMesh" id="SphereMesh_1"]
|
||||
radius = 0.3
|
||||
height = 0.6
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1"]
|
||||
radius = 0.3
|
||||
height = 0.6
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_1"]
|
||||
direction = Vector3(0, 0, -1)
|
||||
initial_velocity_min = 2.0
|
||||
initial_velocity_max = 5.0
|
||||
gravity = Vector3(0, -2, 0)
|
||||
scale_min = 0.15
|
||||
scale_max = 0.25
|
||||
color = Color(1, 0.5, 0, 1)
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_2"]
|
||||
direction = Vector3(0, 0, -1)
|
||||
initial_velocity_min = 1.0
|
||||
initial_velocity_max = 3.0
|
||||
gravity = Vector3(0, -1, 0)
|
||||
scale_min = 0.08
|
||||
scale_max = 0.18
|
||||
color = Color(1, 0.3, 0, 0.8)
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_3"]
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 180.0
|
||||
initial_velocity_min = 5.0
|
||||
initial_velocity_max = 10.0
|
||||
gravity = Vector3(0, -5, 0)
|
||||
scale_min = 0.1
|
||||
scale_max = 0.25
|
||||
scale_over_velocity_min = 0.05
|
||||
scale_over_velocity_max = 0.15
|
||||
color = Color(1, 0.6, 0.1, 1)
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_4"]
|
||||
direction = Vector3(0, 1, 0)
|
||||
initial_velocity_min = 1.5
|
||||
initial_velocity_max = 3.0
|
||||
gravity = Vector3(0, -2, 0)
|
||||
scale_min = 0.15
|
||||
scale_max = 0.3
|
||||
color = Color(1, 0.8, 0.2, 1)
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_5"]
|
||||
direction = Vector3(0, 1, 0)
|
||||
spread = 90.0
|
||||
initial_velocity_min = 2.0
|
||||
initial_velocity_max = 4.0
|
||||
gravity = Vector3(0, -1, 0)
|
||||
scale_min = 0.2
|
||||
scale_max = 0.4
|
||||
color = Color(0.3, 0.3, 0.3, 0.6)
|
||||
|
||||
[node name="ParabolicProjectile" type="Area3D"]
|
||||
script = ExtResource("1_parabolic")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
material_override = SubResource("StandardMaterial3D_1")
|
||||
mesh = SubResource("SphereMesh_1")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("CapsuleShape3D_1")
|
||||
|
||||
[node name="FireParticles" type="GPUParticles3D" parent="."]
|
||||
amount = 40
|
||||
lifetime = 0.4
|
||||
process_material = SubResource("ParticleProcessMaterial_1")
|
||||
draw_pass_1 = SubResource("SphereMesh_1")
|
||||
|
||||
[node name="TrailParticles" type="GPUParticles3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.4)
|
||||
amount = 60
|
||||
lifetime = 0.6
|
||||
process_material = SubResource("ParticleProcessMaterial_2")
|
||||
draw_pass_1 = SubResource("SphereMesh_1")
|
||||
|
||||
[node name="ExplosionParticles" type="GPUParticles3D" parent="."]
|
||||
emitting = false
|
||||
amount = 80
|
||||
lifetime = 0.5
|
||||
one_shot = true
|
||||
process_material = SubResource("ParticleProcessMaterial_3")
|
||||
draw_pass_1 = SubResource("SphereMesh_1")
|
||||
|
||||
[node name="ExplosionCore" type="GPUParticles3D" parent="."]
|
||||
emitting = false
|
||||
amount = 30
|
||||
lifetime = 0.4
|
||||
one_shot = true
|
||||
process_material = SubResource("ParticleProcessMaterial_4")
|
||||
draw_pass_1 = SubResource("SphereMesh_1")
|
||||
|
||||
[node name="ExplosionSmoke" type="GPUParticles3D" parent="."]
|
||||
emitting = false
|
||||
amount = 40
|
||||
lifetime = 0.5
|
||||
one_shot = true
|
||||
process_material = SubResource("ParticleProcessMaterial_5")
|
||||
draw_pass_1 = SubResource("SphereMesh_1")
|
||||
23
scenes/player.tscn
Normal file
23
scenes/player.tscn
Normal file
@@ -0,0 +1,23 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://player"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bt4nfjnngh21i" path="res://scripts/player.gd" id="1_player"]
|
||||
[ext_resource type="PackedScene" uid="uid://q46fjntx43e2" path="res://scenes/straight_projectile.tscn" id="2_player"]
|
||||
[ext_resource type="PackedScene" uid="uid://cwte6nc4bxak0" path="res://scenes/parabolic_projectile.tscn" id="3_player"]
|
||||
|
||||
[sub_resource type="BoxMesh" id="BoxMesh_1"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_1"]
|
||||
|
||||
[node name="Player" type="CharacterBody3D" groups=["player"]]
|
||||
script = ExtResource("1_player")
|
||||
straight_projectile_scene = ExtResource("2_player")
|
||||
parabolic_projectile_scene = ExtResource("3_player")
|
||||
speed = 15.0
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
mesh = SubResource("BoxMesh_1")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||
shape = SubResource("BoxShape3D_1")
|
||||
57
scenes/straight_projectile.tscn
Normal file
57
scenes/straight_projectile.tscn
Normal file
@@ -0,0 +1,57 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://q46fjntx43e2"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://crsltf0vxq1xd" path="res://scripts/straight_projectile.gd" id="1_straight"]
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_1"]
|
||||
albedo_color = Color(1, 0.6, 0.2, 1)
|
||||
emission_enabled = true
|
||||
emission = Color(1, 0.4, 0, 1)
|
||||
emission_energy_multiplier = 3.0
|
||||
|
||||
[sub_resource type="SphereMesh" id="SphereMesh_1"]
|
||||
radius = 0.2
|
||||
height = 0.6
|
||||
|
||||
[sub_resource type="SphereShape3D" id="SphereShape3D_1"]
|
||||
radius = 0.2
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_1"]
|
||||
direction = Vector3(0, 0, -1)
|
||||
initial_velocity_min = 2.0
|
||||
initial_velocity_max = 4.0
|
||||
gravity = Vector3(0, -2, 0)
|
||||
scale_min = 0.1
|
||||
scale_max = 0.2
|
||||
color = Color(1, 0.5, 0, 1)
|
||||
|
||||
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_2"]
|
||||
direction = Vector3(0, 0, -1)
|
||||
initial_velocity_min = 1.0
|
||||
initial_velocity_max = 2.0
|
||||
gravity = Vector3(0, -1, 0)
|
||||
scale_min = 0.05
|
||||
scale_max = 0.15
|
||||
color = Color(1, 0.3, 0, 0.8)
|
||||
|
||||
[node name="StraightProjectile" type="Area3D"]
|
||||
script = ExtResource("1_straight")
|
||||
|
||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||
material_override = SubResource("StandardMaterial3D_1")
|
||||
mesh = SubResource("SphereMesh_1")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
shape = SubResource("SphereShape3D_1")
|
||||
|
||||
[node name="FireParticles" type="GPUParticles3D" parent="."]
|
||||
amount = 30
|
||||
lifetime = 0.3
|
||||
process_material = SubResource("ParticleProcessMaterial_1")
|
||||
draw_pass_1 = SubResource("SphereMesh_1")
|
||||
|
||||
[node name="TrailParticles" type="GPUParticles3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.3)
|
||||
amount = 50
|
||||
lifetime = 0.5
|
||||
process_material = SubResource("ParticleProcessMaterial_2")
|
||||
draw_pass_1 = SubResource("SphereMesh_1")
|
||||
46
scripts/camera_controller.gd
Normal file
46
scripts/camera_controller.gd
Normal file
@@ -0,0 +1,46 @@
|
||||
extends Node3D
|
||||
class_name CameraController
|
||||
|
||||
# Контроллер камеры в стиле Path of Exile 2
|
||||
# Изометрическая камера, которая следует за игроком
|
||||
|
||||
@export var target: Node3D = null
|
||||
@export var camera_distance: float = 20.0
|
||||
@export var camera_height: float = 15.0
|
||||
@export var camera_angle: float = 45.0 # Угол наклона камеры
|
||||
@export var follow_speed: float = 5.0
|
||||
|
||||
var camera: Camera3D
|
||||
|
||||
func _ready():
|
||||
camera = get_node_or_null("Camera3D")
|
||||
if not camera:
|
||||
camera = Camera3D.new()
|
||||
add_child(camera)
|
||||
|
||||
# Находим игрока если цель не установлена
|
||||
if not target:
|
||||
var players = get_tree().get_nodes_in_group("player")
|
||||
if players.size() > 0:
|
||||
target = players[0]
|
||||
|
||||
func _process(delta):
|
||||
if not target or not is_instance_valid(target):
|
||||
return
|
||||
|
||||
# Вычисляем позицию камеры
|
||||
var target_pos = target.global_position
|
||||
var angle_rad = deg_to_rad(camera_angle)
|
||||
|
||||
# Изометрическая позиция камеры (диагональный вид сверху, как в POE 2)
|
||||
# Камера находится под углом 45 градусов по диагонали
|
||||
var offset_x = camera_distance * 0.707 # cos(45) = 0.707
|
||||
var offset_z = camera_distance * 0.707 # sin(45) = 0.707
|
||||
var offset_y = camera_height
|
||||
|
||||
# Плавное следование за игроком
|
||||
var desired_pos = target_pos + Vector3(offset_x, offset_y, offset_z)
|
||||
global_position = global_position.lerp(desired_pos, follow_speed * delta)
|
||||
|
||||
# Камера всегда смотрит на игрока
|
||||
camera.look_at(target_pos, Vector3.UP)
|
||||
1
scripts/camera_controller.gd.uid
Normal file
1
scripts/camera_controller.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://28onm6d36qbo
|
||||
77
scripts/enemy.gd
Normal file
77
scripts/enemy.gd
Normal file
@@ -0,0 +1,77 @@
|
||||
extends Unit
|
||||
class_name Enemy
|
||||
|
||||
# Класс противника
|
||||
|
||||
@export var detection_range: float = 10.0
|
||||
@export var attack_range: float = 2.0
|
||||
@export var attack_damage: float = 10.0
|
||||
@export var attack_cooldown: float = 1.0
|
||||
|
||||
var target: Node3D = null
|
||||
var attack_timer: float = 0.0
|
||||
|
||||
func _ready():
|
||||
super._ready()
|
||||
|
||||
func _physics_process(delta):
|
||||
attack_timer -= delta
|
||||
|
||||
# Поиск цели (игрока)
|
||||
if target == null or not is_instance_valid(target):
|
||||
find_target()
|
||||
|
||||
# Движение к цели
|
||||
if target and is_instance_valid(target):
|
||||
var distance = global_position.distance_to(target.global_position)
|
||||
|
||||
if distance <= attack_range:
|
||||
# Атака - поворачиваемся к цели
|
||||
var look_direction = (target.global_position - global_position)
|
||||
look_direction.y = 0
|
||||
if look_direction.length() > 0.1:
|
||||
var angle = atan2(look_direction.x, look_direction.z)
|
||||
rotation.y = angle
|
||||
|
||||
if attack_timer <= 0:
|
||||
attack(target)
|
||||
attack_timer = attack_cooldown
|
||||
elif distance <= detection_range:
|
||||
# Преследование - поворачиваемся и бежим к цели
|
||||
var direction = (target.global_position - global_position).normalized()
|
||||
velocity.x = direction.x * speed
|
||||
velocity.z = direction.z * speed
|
||||
|
||||
# Поворачиваем врага в сторону цели
|
||||
var look_direction = (target.global_position - global_position)
|
||||
look_direction.y = 0
|
||||
if look_direction.length() > 0.1:
|
||||
var angle = atan2(look_direction.x, look_direction.z)
|
||||
rotation.y = angle
|
||||
else:
|
||||
# Остановка - не поворачиваемся
|
||||
velocity.x = move_toward(velocity.x, 0, speed)
|
||||
velocity.z = move_toward(velocity.z, 0, speed)
|
||||
else:
|
||||
# Остановка если нет цели
|
||||
velocity.x = move_toward(velocity.x, 0, speed)
|
||||
velocity.z = move_toward(velocity.z, 0, speed)
|
||||
|
||||
# Применяем гравитацию
|
||||
if not is_on_floor():
|
||||
velocity.y -= 9.8 * delta
|
||||
else:
|
||||
velocity.y = 0
|
||||
|
||||
super._physics_process(delta)
|
||||
|
||||
func find_target():
|
||||
# Ищем игрока в сцене
|
||||
var players = get_tree().get_nodes_in_group("player")
|
||||
if players.size() > 0:
|
||||
target = players[0]
|
||||
|
||||
func attack(target_unit: Node3D):
|
||||
# Атака цели
|
||||
if target_unit.has_method("take_damage"):
|
||||
target_unit.take_damage(attack_damage)
|
||||
1
scripts/enemy.gd.uid
Normal file
1
scripts/enemy.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://8vuywuv7ebsl
|
||||
87
scripts/enemy_health_bar.gd
Normal file
87
scripts/enemy_health_bar.gd
Normal file
@@ -0,0 +1,87 @@
|
||||
extends Node3D
|
||||
class_name EnemyHealthBar
|
||||
|
||||
# Скрипт для отображения полоски здоровья над врагом
|
||||
|
||||
@onready var background_bar: MeshInstance3D = $BackgroundBar
|
||||
@onready var health_bar: MeshInstance3D = $HealthBar
|
||||
|
||||
var unit: Unit = null
|
||||
var bar_width: float = 3.0
|
||||
var bar_height: float = 0.2
|
||||
|
||||
func _ready():
|
||||
# Находим родительский Unit (враг)
|
||||
unit = get_parent() as Unit
|
||||
if unit:
|
||||
# Подключаемся к сигналу изменения здоровья
|
||||
unit.health_changed.connect(_on_health_changed)
|
||||
# Изначально скрываем полоску (если здоровье 100%)
|
||||
update_visibility()
|
||||
update_health_bar()
|
||||
else:
|
||||
print("EnemyHealthBar: Не найден Unit!")
|
||||
|
||||
# Убеждаемся, что узлы найдены
|
||||
if not background_bar:
|
||||
print("EnemyHealthBar: Не найден BackgroundBar!")
|
||||
if not health_bar:
|
||||
print("EnemyHealthBar: Не найден HealthBar!")
|
||||
|
||||
func _process(_delta):
|
||||
# Поворачиваем полоску к камере
|
||||
var camera = get_viewport().get_camera_3d()
|
||||
if camera and visible:
|
||||
# BoxMesh по умолчанию ориентирован правильно
|
||||
# Поворачиваем полоску так, чтобы она была обращена к камере
|
||||
# Используем только горизонтальный поворот (по оси Y), чтобы полоска оставалась горизонтальной
|
||||
var direction_to_camera = (camera.global_position - global_position).normalized()
|
||||
if direction_to_camera.length() > 0.01:
|
||||
var horizontal_direction = direction_to_camera
|
||||
horizontal_direction.y = 0
|
||||
horizontal_direction = horizontal_direction.normalized()
|
||||
if horizontal_direction.length() > 0.01:
|
||||
# Поворачиваем только по оси Y
|
||||
var angle = atan2(horizontal_direction.x, horizontal_direction.z)
|
||||
rotation.y = angle
|
||||
|
||||
# Обновляем полоску после поворота (чтобы позиция была правильной)
|
||||
if unit:
|
||||
update_health_bar()
|
||||
|
||||
func _on_health_changed(new_health: float):
|
||||
update_health_bar()
|
||||
update_visibility()
|
||||
|
||||
func update_health_bar():
|
||||
if not unit or not health_bar:
|
||||
return
|
||||
|
||||
var health_percent = clamp(unit.health / unit.max_health, 0.0, 1.0)
|
||||
|
||||
# Масштабируем полоску здоровья в зависимости от процента
|
||||
# Убеждаемся, что масштаб всегда положительный и достаточно большой для видимости
|
||||
var scale_x = max(health_percent, 0.05) # Минимум 0.05 для лучшей видимости
|
||||
health_bar.scale.x = scale_x
|
||||
|
||||
# Смещаем полоску влево, чтобы она уменьшалась справа налево
|
||||
# Используем локальные координаты относительно центра
|
||||
# Центр полоски должен быть в центре, поэтому смещаем на половину разницы
|
||||
var offset = -(bar_width * (1.0 - health_percent)) / 2.0
|
||||
health_bar.position.x = offset
|
||||
# Убеждаемся, что зеленая полоска немного впереди красной для правильного отображения
|
||||
health_bar.position.z = 0.01
|
||||
|
||||
func update_visibility():
|
||||
if not unit:
|
||||
visible = false
|
||||
return
|
||||
|
||||
# Показываем полоску только если здоровье меньше 100%
|
||||
var should_show = unit.health < unit.max_health
|
||||
visible = should_show
|
||||
if background_bar:
|
||||
background_bar.visible = should_show
|
||||
if health_bar:
|
||||
health_bar.visible = should_show
|
||||
|
||||
1
scripts/enemy_health_bar.gd.uid
Normal file
1
scripts/enemy_health_bar.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://c7bnw4xa03juj
|
||||
53
scripts/hud.gd
Normal file
53
scripts/hud.gd
Normal file
@@ -0,0 +1,53 @@
|
||||
extends Control
|
||||
class_name HUD
|
||||
|
||||
# HUD для отображения информации об игроке
|
||||
|
||||
@onready var health_bar: ProgressBar = $HealthBar
|
||||
@onready var health_label: Label = $HealthBar/HealthLabel
|
||||
@onready var dash_cooldown_bar: ProgressBar = $DashCooldownBar
|
||||
@onready var dash_cooldown_label: Label = $DashCooldownBar/DashCooldownLabel
|
||||
|
||||
var player: Player = null
|
||||
|
||||
func _ready():
|
||||
# Находим игрока в сцене
|
||||
call_deferred("find_player")
|
||||
|
||||
func find_player():
|
||||
var players = get_tree().get_nodes_in_group("player")
|
||||
if players.size() > 0:
|
||||
player = players[0] as Player
|
||||
if player:
|
||||
# Подключаемся к сигналам
|
||||
player.health_changed.connect(_on_player_health_changed)
|
||||
player.dash_cooldown_changed.connect(_on_dash_cooldown_changed)
|
||||
# Обновляем отображение
|
||||
update_health_display()
|
||||
update_dash_cooldown_display()
|
||||
|
||||
func _on_player_health_changed(new_health: float):
|
||||
update_health_display()
|
||||
|
||||
func _on_dash_cooldown_changed(remaining_time: float):
|
||||
update_dash_cooldown_display()
|
||||
|
||||
func update_health_display():
|
||||
if not player:
|
||||
return
|
||||
|
||||
var health_percent = (player.health / player.max_health) * 100.0
|
||||
health_bar.value = health_percent
|
||||
health_label.text = "HP: %d / %d" % [int(player.health), int(player.max_health)]
|
||||
|
||||
func update_dash_cooldown_display():
|
||||
if not player:
|
||||
return
|
||||
|
||||
var cooldown_percent = (player.dash_cooldown_timer / player.dash_cooldown) * 100.0
|
||||
dash_cooldown_bar.value = cooldown_percent
|
||||
|
||||
if player.dash_cooldown_timer > 0.0:
|
||||
dash_cooldown_label.text = "Рывок: %.1f" % player.dash_cooldown_timer
|
||||
else:
|
||||
dash_cooldown_label.text = "Рывок: готов (Пробел)"
|
||||
1
scripts/hud.gd.uid
Normal file
1
scripts/hud.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://buc8nflhbd2kx
|
||||
29
scripts/main_menu.gd
Normal file
29
scripts/main_menu.gd
Normal file
@@ -0,0 +1,29 @@
|
||||
extends Control
|
||||
|
||||
# Скрипт главного меню
|
||||
|
||||
func _ready():
|
||||
# Подключаем сигналы кнопок
|
||||
var start_button = get_node_or_null("VBoxContainer/StartButton")
|
||||
var settings_button = get_node_or_null("VBoxContainer/SettingsButton")
|
||||
var quit_button = get_node_or_null("VBoxContainer/QuitButton")
|
||||
|
||||
if start_button:
|
||||
start_button.pressed.connect(_on_start_button_pressed)
|
||||
if settings_button:
|
||||
settings_button.pressed.connect(_on_settings_button_pressed)
|
||||
if quit_button:
|
||||
quit_button.pressed.connect(_on_quit_button_pressed)
|
||||
|
||||
func _on_start_button_pressed():
|
||||
# Переход к игре
|
||||
get_tree().change_scene_to_file("res://scenes/main.tscn")
|
||||
|
||||
func _on_settings_button_pressed():
|
||||
# TODO: Открыть меню настроек
|
||||
print("Настройки (пока не реализовано)")
|
||||
|
||||
func _on_quit_button_pressed():
|
||||
# Выход из игры
|
||||
get_tree().quit()
|
||||
|
||||
1
scripts/main_menu.gd.uid
Normal file
1
scripts/main_menu.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dpfj2k2b4353w
|
||||
124
scripts/parabolic_projectile.gd
Normal file
124
scripts/parabolic_projectile.gd
Normal file
@@ -0,0 +1,124 @@
|
||||
extends Projectile
|
||||
class_name ParabolicProjectile
|
||||
|
||||
# Параболический снаряд для броска
|
||||
|
||||
@export var projectile_gravity: float = 200
|
||||
@export var arc_height: float = 15.0 # Увеличена высота параболы
|
||||
|
||||
var target_position: Vector3 = Vector3.ZERO
|
||||
var start_position: Vector3 = Vector3.ZERO
|
||||
var travel_time: float = 0.0
|
||||
var total_time: float = 0.0
|
||||
|
||||
func _ready():
|
||||
start_position = global_position
|
||||
|
||||
# Проверяем, что target_position установлен
|
||||
if target_position == Vector3.ZERO:
|
||||
# Если не установлен, используем направление вперед
|
||||
target_position = start_position + Vector3(0, 0, -10.0)
|
||||
target_position.y = start_position.y
|
||||
|
||||
# Вычисляем горизонтальное расстояние
|
||||
var horizontal_distance = Vector3(start_position.x, 0, start_position.z).distance_to(Vector3(target_position.x, 0, target_position.z))
|
||||
|
||||
# Вычисляем правильную траекторию для попадания в цель
|
||||
var horizontal_dir = (target_position - start_position)
|
||||
horizontal_dir.y = 0
|
||||
|
||||
# Если цель слишком близко (включая клик на самого игрока), устанавливаем минимальное расстояние и направление
|
||||
if horizontal_distance < 1.0:
|
||||
# Если направление нулевое или очень маленькое, используем направление вперед от игрока
|
||||
if horizontal_dir.length() < 0.1:
|
||||
# Используем направление камеры или направление вперед
|
||||
var camera = get_viewport().get_camera_3d()
|
||||
if camera:
|
||||
var forward = -camera.global_transform.basis.z
|
||||
forward.y = 0
|
||||
horizontal_dir = forward.normalized()
|
||||
else:
|
||||
horizontal_dir = Vector3(0, 0, -1) # Направление по умолчанию
|
||||
else:
|
||||
horizontal_dir = horizontal_dir.normalized()
|
||||
|
||||
# Устанавливаем минимальное расстояние
|
||||
horizontal_distance = 1.0
|
||||
# Обновляем target_position на минимальном расстоянии от стартовой позиции
|
||||
target_position = start_position + horizontal_dir * horizontal_distance
|
||||
target_position.y = 0.0
|
||||
else:
|
||||
horizontal_dir = horizontal_dir.normalized()
|
||||
|
||||
# Время подъема до максимальной высоты
|
||||
var time_to_peak = sqrt(2 * arc_height / projectile_gravity)
|
||||
# Время полета вниз (примерно такое же)
|
||||
var time_to_fall = time_to_peak
|
||||
# Общее время полета по вертикали
|
||||
var vertical_time = time_to_peak + time_to_fall
|
||||
|
||||
# Горизонтальная скорость должна обеспечить попадание в цель за время полета
|
||||
var horizontal_speed = horizontal_distance / vertical_time
|
||||
|
||||
# Начальная вертикальная скорость для достижения нужной высоты
|
||||
var vertical_speed = sqrt(2 * projectile_gravity * arc_height)
|
||||
|
||||
# Общее время полета
|
||||
total_time = vertical_time
|
||||
|
||||
# Устанавливаем начальную скорость
|
||||
velocity = horizontal_dir * horizontal_speed + Vector3.UP * vertical_speed
|
||||
|
||||
# Вызываем super._ready() после установки velocity
|
||||
super._ready()
|
||||
|
||||
# Отключаем мониторинг коллизий при полете
|
||||
monitoring = false
|
||||
|
||||
func _physics_process(delta):
|
||||
if has_hit:
|
||||
return
|
||||
|
||||
# Если velocity не установлена, не двигаемся
|
||||
if velocity == Vector3.ZERO:
|
||||
return
|
||||
|
||||
# Проверяем время жизни
|
||||
lifetime_timer -= delta
|
||||
if lifetime_timer <= 0:
|
||||
if is_explosive:
|
||||
explode(global_position)
|
||||
else:
|
||||
queue_free()
|
||||
return
|
||||
|
||||
travel_time += delta
|
||||
|
||||
# Сохраняем предыдущую позицию для raycast (если понадобится)
|
||||
previous_position = global_position
|
||||
|
||||
# Применяем гравитацию
|
||||
velocity.y -= projectile_gravity * delta
|
||||
|
||||
# Движение снаряда
|
||||
global_position += velocity * delta
|
||||
|
||||
# Проверяем, достигли ли цели
|
||||
# Проверяем по Y координате (приземление) и по времени полета
|
||||
# НЕ проверяем горизонтальное расстояние, так как это может сработать сразу при близких целях
|
||||
if (global_position.y <= target_position.y + 0.3) and (travel_time >= total_time * 0.5):
|
||||
# Включаем коллизию только при приземлении
|
||||
monitoring = true
|
||||
# Устанавливаем позицию точно в целевую точку
|
||||
global_position = target_position
|
||||
on_hit(target_position)
|
||||
return
|
||||
|
||||
# Дополнительная проверка по времени полета (на случай, если снаряд не достиг земли)
|
||||
if travel_time >= total_time * 1.2:
|
||||
monitoring = true
|
||||
global_position = target_position
|
||||
on_hit(target_position)
|
||||
return
|
||||
|
||||
# Не проверяем столкновения при полете - только движение
|
||||
1
scripts/parabolic_projectile.gd.uid
Normal file
1
scripts/parabolic_projectile.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d3c5g160kh3vv
|
||||
249
scripts/player.gd
Normal file
249
scripts/player.gd
Normal file
@@ -0,0 +1,249 @@
|
||||
extends Unit
|
||||
class_name Player
|
||||
|
||||
# Класс игрока с управлением WASD
|
||||
|
||||
@export var mouse_sensitivity: float = 0.003
|
||||
@export var dash_speed: float = 25.0 # Скорость рывка
|
||||
@export var dash_duration: float = 0.2 # Длительность рывка
|
||||
@export var dash_cooldown: float = 1.0 # Перезарядка рывка
|
||||
@export var straight_projectile_scene: PackedScene = null # Сцена прямого снаряда
|
||||
@export var parabolic_projectile_scene: PackedScene = null # Сцена параболического снаряда
|
||||
@export var projectile_damage: float = 20.0
|
||||
@export var projectile_speed: float = 20.0
|
||||
@export var explosion_radius: float = 3.0
|
||||
|
||||
var camera_angle: float = 0.0
|
||||
var is_dashing: bool = false
|
||||
var dash_timer: float = 0.0
|
||||
var dash_cooldown_timer: float = 0.0
|
||||
var dash_direction: Vector3 = Vector3.ZERO
|
||||
|
||||
signal dash_started
|
||||
signal dash_ended
|
||||
signal dash_cooldown_changed(remaining_time: float)
|
||||
|
||||
func _ready():
|
||||
super._ready()
|
||||
# Игрок белый
|
||||
var mesh_instance = get_node_or_null("MeshInstance3D")
|
||||
if mesh_instance:
|
||||
var material = StandardMaterial3D.new()
|
||||
material.albedo_color = Color.WHITE
|
||||
mesh_instance.material_override = material
|
||||
|
||||
func _input(event):
|
||||
# Обработка рывка
|
||||
if event.is_action_pressed("dash") and dash_cooldown_timer <= 0.0 and not is_dashing:
|
||||
perform_dash()
|
||||
|
||||
# Обработка стрельбы (левая кнопка мыши)
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||
shoot_straight_projectile()
|
||||
|
||||
# Обработка броска (правая кнопка мыши)
|
||||
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
|
||||
throw_parabolic_projectile()
|
||||
|
||||
func _physics_process(delta):
|
||||
# Обновляем таймеры
|
||||
var previous_cooldown = dash_cooldown_timer
|
||||
if dash_cooldown_timer > 0.0:
|
||||
dash_cooldown_timer -= delta
|
||||
dash_cooldown_timer = max(0.0, dash_cooldown_timer)
|
||||
# Эмитим сигнал только если значение изменилось
|
||||
if abs(previous_cooldown - dash_cooldown_timer) > 0.01:
|
||||
dash_cooldown_changed.emit(dash_cooldown_timer)
|
||||
|
||||
# Поворачиваем игрока в сторону курсора мыши
|
||||
update_rotation_to_mouse()
|
||||
|
||||
# Получаем направление движения от клавиатуры
|
||||
var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
|
||||
var direction = Vector3(input_dir.x, 0, input_dir.y)
|
||||
|
||||
# Обработка рывка
|
||||
if is_dashing:
|
||||
dash_timer -= delta
|
||||
if dash_timer <= 0.0:
|
||||
end_dash()
|
||||
else:
|
||||
# Во время рывка двигаемся с увеличенной скоростью
|
||||
velocity.x = dash_direction.x * dash_speed
|
||||
velocity.z = dash_direction.z * dash_speed
|
||||
else:
|
||||
# Обычное движение
|
||||
if direction.length() > 0:
|
||||
velocity.x = direction.x * speed
|
||||
velocity.z = direction.z * speed
|
||||
else:
|
||||
velocity.x = move_toward(velocity.x, 0, speed)
|
||||
velocity.z = move_toward(velocity.z, 0, speed)
|
||||
|
||||
# Применяем гравитацию
|
||||
if not is_on_floor():
|
||||
velocity.y -= 9.8 * delta
|
||||
else:
|
||||
velocity.y = 0
|
||||
|
||||
super._physics_process(delta)
|
||||
|
||||
func update_rotation_to_mouse():
|
||||
# Получаем камеру из сцены
|
||||
var camera = get_viewport().get_camera_3d()
|
||||
if not camera:
|
||||
return
|
||||
|
||||
# Получаем позицию курсора мыши на экране
|
||||
var mouse_pos = get_viewport().get_mouse_position()
|
||||
|
||||
# Создаем луч от камеры через позицию курсора
|
||||
var from = camera.project_ray_origin(mouse_pos)
|
||||
var to = from + camera.project_ray_normal(mouse_pos) * 1000.0
|
||||
|
||||
# Создаем запрос для raycast
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var query = PhysicsRayQueryParameters3D.create(from, to)
|
||||
query.collision_mask = 1 # Слой земли
|
||||
|
||||
# Выполняем raycast
|
||||
var result = space_state.intersect_ray(query)
|
||||
|
||||
if result:
|
||||
# Получаем точку пересечения
|
||||
var target_point = result.position
|
||||
# Поворачиваем игрока в сторону курсора (только по оси Y)
|
||||
var look_direction = (target_point - global_position)
|
||||
look_direction.y = 0 # Игнорируем вертикальную составляющую
|
||||
if look_direction.length() > 0.1:
|
||||
look_at(global_position + look_direction, Vector3.UP)
|
||||
|
||||
func perform_dash():
|
||||
# Получаем направление движения
|
||||
var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
|
||||
var direction = Vector3(input_dir.x, 0, input_dir.y)
|
||||
|
||||
# Если игрок не двигается, рывок вперед (по направлению взгляда или вверх)
|
||||
if direction.length() < 0.1:
|
||||
direction = Vector3(0, 0, -1) # Вперед по умолчанию
|
||||
|
||||
# Нормализуем направление
|
||||
dash_direction = direction.normalized()
|
||||
|
||||
# Запускаем рывок
|
||||
is_dashing = true
|
||||
dash_timer = dash_duration
|
||||
dash_cooldown_timer = dash_cooldown
|
||||
dash_started.emit()
|
||||
|
||||
func end_dash():
|
||||
is_dashing = false
|
||||
dash_timer = 0.0
|
||||
dash_ended.emit()
|
||||
|
||||
func shoot_straight_projectile():
|
||||
if not straight_projectile_scene:
|
||||
return
|
||||
|
||||
# Получаем точку прицеливания через raycast
|
||||
var target_point = get_mouse_target_point()
|
||||
if not target_point:
|
||||
return
|
||||
|
||||
# Создаем снаряд
|
||||
var projectile = straight_projectile_scene.instantiate() as StraightProjectile
|
||||
if not projectile:
|
||||
return
|
||||
|
||||
# Устанавливаем параметры
|
||||
projectile.owner_unit = self
|
||||
projectile.damage = projectile_damage
|
||||
projectile.speed = projectile_speed
|
||||
projectile.is_explosive = false
|
||||
|
||||
# Направление полета
|
||||
var shoot_position = global_position + Vector3.UP * 1.0 # Немного выше игрока
|
||||
var direction = (target_point - shoot_position).normalized()
|
||||
projectile.direction = direction
|
||||
|
||||
# Позиция и поворот
|
||||
projectile.global_position = shoot_position
|
||||
projectile.look_at(shoot_position + direction, Vector3.UP)
|
||||
|
||||
# Добавляем в сцену
|
||||
var scene_root = get_tree().current_scene
|
||||
if not scene_root:
|
||||
scene_root = get_tree().root.get_child(get_tree().root.get_child_count() - 1)
|
||||
scene_root.add_child(projectile)
|
||||
|
||||
func throw_parabolic_projectile():
|
||||
if not parabolic_projectile_scene:
|
||||
# Пробуем загрузить вручную
|
||||
parabolic_projectile_scene = load("res://scenes/parabolic_projectile.tscn") as PackedScene
|
||||
if not parabolic_projectile_scene:
|
||||
return
|
||||
|
||||
# Получаем точку прицеливания через raycast (на плоскости)
|
||||
var target_point = get_mouse_target_point()
|
||||
|
||||
# Убеждаемся, что целевая точка на уровне земли (Y = 0)
|
||||
# Если кликнули на противника или другой объект, используем его X и Z, но Y ставим на землю
|
||||
target_point.y = 0.0
|
||||
|
||||
# Позиция запуска
|
||||
var throw_position = global_position + Vector3.UP * 1.0
|
||||
|
||||
# Создаем снаряд
|
||||
var projectile = parabolic_projectile_scene.instantiate()
|
||||
if not projectile:
|
||||
return
|
||||
|
||||
# Устанавливаем параметры ДО добавления в сцену
|
||||
projectile.owner_unit = self
|
||||
projectile.damage = projectile_damage
|
||||
projectile.speed = projectile_speed
|
||||
projectile.is_explosive = true
|
||||
projectile.explosion_radius = explosion_radius
|
||||
|
||||
# Устанавливаем позицию
|
||||
projectile.global_position = throw_position
|
||||
|
||||
# Целевая позиция (на уровне земли) - устанавливаем ДО _ready()
|
||||
projectile.target_position = target_point
|
||||
|
||||
# Добавляем в сцену (после установки всех параметров)
|
||||
var scene_root = get_tree().current_scene
|
||||
if not scene_root:
|
||||
scene_root = get_tree().root.get_child(get_tree().root.get_child_count() - 1)
|
||||
scene_root.add_child(projectile)
|
||||
|
||||
func get_mouse_target_point() -> Vector3:
|
||||
# Получаем камеру
|
||||
var camera = get_viewport().get_camera_3d()
|
||||
if not camera:
|
||||
return global_position + transform.basis.z * -10.0
|
||||
|
||||
# Получаем позицию курсора мыши
|
||||
var mouse_pos = get_viewport().get_mouse_position()
|
||||
|
||||
# Создаем луч от камеры
|
||||
var from = camera.project_ray_origin(mouse_pos)
|
||||
var ray_dir = camera.project_ray_normal(mouse_pos)
|
||||
var to = from + ray_dir * 1000.0
|
||||
|
||||
# Raycast
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var query = PhysicsRayQueryParameters3D.create(from, to)
|
||||
query.collision_mask = 1
|
||||
|
||||
var result = space_state.intersect_ray(query)
|
||||
if result:
|
||||
return result.position
|
||||
|
||||
# Если не попали ни во что, вычисляем точку на плоскости земли
|
||||
if ray_dir.y < -0.01: # Луч направлен вниз
|
||||
var t = -from.y / ray_dir.y
|
||||
return from + ray_dir * t
|
||||
|
||||
# Если луч не направлен вниз, используем точку перед игроком
|
||||
return global_position + transform.basis.z * -10.0
|
||||
1
scripts/player.gd.uid
Normal file
1
scripts/player.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bt4nfjnngh21i
|
||||
182
scripts/projectile.gd
Normal file
182
scripts/projectile.gd
Normal file
@@ -0,0 +1,182 @@
|
||||
extends Area3D
|
||||
class_name Projectile
|
||||
|
||||
# Базовый класс для снарядов
|
||||
|
||||
@export var speed: float = 120.0
|
||||
@export var damage: float = 20.0
|
||||
@export var lifetime: float = 5.0
|
||||
@export var is_explosive: bool = false
|
||||
@export var explosion_radius: float = 3.0
|
||||
|
||||
var direction: Vector3 = Vector3.ZERO
|
||||
var velocity: Vector3 = Vector3.ZERO
|
||||
var owner_unit: Node3D = null
|
||||
var lifetime_timer: float = 0.0
|
||||
var has_hit: bool = false
|
||||
var previous_position: Vector3 = Vector3.ZERO
|
||||
|
||||
signal hit(target: Node3D, position: Vector3)
|
||||
signal exploded(position: Vector3)
|
||||
|
||||
func _ready():
|
||||
lifetime_timer = lifetime
|
||||
previous_position = global_position
|
||||
# Настраиваем Area3D для обнаружения целей
|
||||
body_entered.connect(_on_body_entered)
|
||||
# Отключаем мониторинг по умолчанию для параболических
|
||||
monitoring = true
|
||||
monitorable = false
|
||||
|
||||
func _physics_process(delta):
|
||||
if has_hit:
|
||||
return
|
||||
|
||||
lifetime_timer -= delta
|
||||
if lifetime_timer <= 0:
|
||||
if is_explosive:
|
||||
explode(global_position)
|
||||
else:
|
||||
queue_free()
|
||||
return
|
||||
|
||||
# Сохраняем предыдущую позицию для raycast
|
||||
previous_position = global_position
|
||||
|
||||
# Движение снаряда
|
||||
global_position += velocity * delta
|
||||
|
||||
# Проверка столкновений через raycast
|
||||
check_collisions_raycast()
|
||||
|
||||
func check_collisions_raycast():
|
||||
# Используем raycast для проверки столкновений
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var query = PhysicsRayQueryParameters3D.create(previous_position, global_position)
|
||||
query.exclude = [self, owner_unit] if owner_unit else [self]
|
||||
query.collision_mask = 1 # Только слой земли и объектов
|
||||
|
||||
var result = space_state.intersect_ray(query)
|
||||
if result:
|
||||
var collider = result.collider
|
||||
var hit_position = result.position
|
||||
|
||||
# Если это враг, наносим урон
|
||||
if collider.has_method("take_damage"):
|
||||
collider.take_damage(damage)
|
||||
hit.emit(collider, hit_position)
|
||||
|
||||
on_hit(hit_position)
|
||||
|
||||
func _on_body_entered(body: Node3D):
|
||||
# Дополнительная проверка через Area3D
|
||||
if has_hit:
|
||||
return
|
||||
|
||||
if body == owner_unit:
|
||||
return
|
||||
|
||||
if body is Projectile:
|
||||
return
|
||||
|
||||
if body.has_method("take_damage"):
|
||||
body.take_damage(damage)
|
||||
hit.emit(body, global_position)
|
||||
|
||||
on_hit(global_position)
|
||||
|
||||
func on_hit(position: Vector3):
|
||||
if has_hit:
|
||||
return
|
||||
|
||||
has_hit = true
|
||||
velocity = Vector3.ZERO
|
||||
# Останавливаем частицы
|
||||
var fire_particles = get_node_or_null("FireParticles")
|
||||
var trail_particles = get_node_or_null("TrailParticles")
|
||||
if fire_particles:
|
||||
fire_particles.emitting = false
|
||||
if trail_particles:
|
||||
trail_particles.emitting = false
|
||||
|
||||
if is_explosive:
|
||||
explode(position)
|
||||
else:
|
||||
# Простое попадание
|
||||
hit.emit(null, position)
|
||||
# Воспроизводим анимацию попадания
|
||||
play_hit_animation()
|
||||
# Удаляем через небольшую задержку
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
queue_free()
|
||||
|
||||
func explode(position: Vector3):
|
||||
# Находим все цели в радиусе взрыва
|
||||
var space_state = get_world_3d().direct_space_state
|
||||
var query = PhysicsShapeQueryParameters3D.new()
|
||||
var sphere_shape = SphereShape3D.new()
|
||||
sphere_shape.radius = explosion_radius
|
||||
query.shape = sphere_shape
|
||||
query.transform.origin = position
|
||||
query.collision_mask = 1
|
||||
|
||||
var results = space_state.intersect_shape(query)
|
||||
|
||||
# Наносим урон всем в радиусе
|
||||
for result in results:
|
||||
var collider = result.collider
|
||||
if collider != owner_unit and collider.has_method("take_damage"):
|
||||
var distance = position.distance_to(collider.global_position)
|
||||
var damage_multiplier = 1.0 - (distance / explosion_radius)
|
||||
collider.take_damage(damage * damage_multiplier)
|
||||
|
||||
exploded.emit(position)
|
||||
play_explosion_animation()
|
||||
# Ждем, пока частицы взрыва отыграют (0.5 секунды)
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
queue_free()
|
||||
|
||||
func play_hit_animation():
|
||||
# Создаем простой визуальный эффект попадания
|
||||
var mesh = get_node_or_null("MeshInstance3D")
|
||||
if mesh:
|
||||
# Увеличиваем размер и скрываем
|
||||
var tween = create_tween()
|
||||
tween.tween_property(mesh, "scale", Vector3(2.0, 2.0, 2.0), 0.2)
|
||||
tween.tween_callback(func(): mesh.visible = false)
|
||||
|
||||
func play_explosion_animation():
|
||||
# Скрываем основной меш
|
||||
var mesh = get_node_or_null("MeshInstance3D")
|
||||
if mesh:
|
||||
mesh.visible = false
|
||||
|
||||
# Останавливаем частицы полета
|
||||
var fire_particles = get_node_or_null("FireParticles")
|
||||
var trail_particles = get_node_or_null("TrailParticles")
|
||||
if fire_particles:
|
||||
fire_particles.emitting = false
|
||||
if trail_particles:
|
||||
trail_particles.emitting = false
|
||||
|
||||
# Воспроизводим частицы взрыва
|
||||
var explosion_particles = get_node_or_null("ExplosionParticles")
|
||||
var explosion_core = get_node_or_null("ExplosionCore")
|
||||
var explosion_smoke = get_node_or_null("ExplosionSmoke")
|
||||
|
||||
# Масштабируем узлы частиц в зависимости от радиуса взрыва
|
||||
# Это создаст визуальный эффект взрыва нужного размера
|
||||
var scale_factor = explosion_radius / 3.0 # 3.0 - базовый радиус взрыва
|
||||
|
||||
if explosion_particles:
|
||||
explosion_particles.scale = Vector3.ONE * scale_factor
|
||||
explosion_particles.restart()
|
||||
explosion_particles.emitting = true
|
||||
if explosion_core:
|
||||
explosion_core.scale = Vector3.ONE * scale_factor * 0.6
|
||||
explosion_core.restart()
|
||||
explosion_core.emitting = true
|
||||
if explosion_smoke:
|
||||
explosion_smoke.scale = Vector3.ONE * scale_factor * 1.2
|
||||
explosion_smoke.restart()
|
||||
explosion_smoke.emitting = true
|
||||
1
scripts/projectile.gd.uid
Normal file
1
scripts/projectile.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bg1bpdny7eta6
|
||||
13
scripts/straight_projectile.gd
Normal file
13
scripts/straight_projectile.gd
Normal file
@@ -0,0 +1,13 @@
|
||||
extends Projectile
|
||||
class_name StraightProjectile
|
||||
|
||||
# Прямой снаряд для стрельбы
|
||||
|
||||
func _ready():
|
||||
super._ready()
|
||||
# Устанавливаем скорость только горизонтально (параллельно земле)
|
||||
var horizontal_dir = direction
|
||||
horizontal_dir.y = 0
|
||||
horizontal_dir = horizontal_dir.normalized()
|
||||
velocity = horizontal_dir * speed
|
||||
|
||||
1
scripts/straight_projectile.gd.uid
Normal file
1
scripts/straight_projectile.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://crsltf0vxq1xd
|
||||
30
scripts/unit.gd
Normal file
30
scripts/unit.gd
Normal file
@@ -0,0 +1,30 @@
|
||||
extends CharacterBody3D
|
||||
class_name Unit
|
||||
|
||||
# Базовый класс для всех юнитов (игрок и противники)
|
||||
|
||||
@export var speed: float = 5.0
|
||||
@export var health: float = 100.0
|
||||
@export var max_health: float = 100.0
|
||||
|
||||
signal health_changed(new_health: float)
|
||||
signal died
|
||||
|
||||
func _ready():
|
||||
health = max_health
|
||||
|
||||
func take_damage(amount: float):
|
||||
health -= amount
|
||||
health = max(0, health)
|
||||
health_changed.emit(health)
|
||||
|
||||
if health <= 0:
|
||||
die()
|
||||
|
||||
func die():
|
||||
died.emit()
|
||||
queue_free()
|
||||
|
||||
func _physics_process(delta):
|
||||
# Базовая физика движения
|
||||
move_and_slide()
|
||||
1
scripts/unit.gd.uid
Normal file
1
scripts/unit.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cok6ep4d2e6mf
|
||||
Reference in New Issue
Block a user